-
Notifications
You must be signed in to change notification settings - Fork 61
🤖 refactor: decompose src/node/ god services — Phase 0+1+2a+3a #2270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ammar-agent
wants to merge
14
commits into
main
Choose a base branch
from
refactor/node-cleanup-phase0
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Replace 214 inline `error instanceof Error ? error.message : String(error)` patterns with the existing `getErrorMessage()` utility from `src/common/utils/errors.ts`. This eliminates the most pervasive DRY violation in the codebase (172+ occurrences across 30+ files). Heaviest adopters: - workspaceService.ts: 32 replacements - projectService.ts: 13 replacements - orpc/router.ts: 8 replacements - historyService.ts: 8 replacements
Replace ~30-line manual service field spread with services.toORPCContext(), which was added specifically to centralize this mapping. Remove the now-unused ORPCContext type import.
Move archiveChildSessionArtifactsIntoParentSessionDir and its supporting helper functions (isErrnoWithCode, isPathInsideDir, copyFileBestEffort, copyDirIfMissingBestEffort, coerceUpdatedAtMs, rollUpAncestorWorkspaceIds) from workspaceService.ts to a new subagentArtifactArchival.ts module. This reduces workspaceService.ts by ~450 lines with no logic changes. The class method that calls archiveChildSessionArtifactsIntoParentSessionDir now imports it from the new module.
Extract 7 workspace AI settings methods from WorkspaceService into standalone functions in src/node/services/workspaceAISettings.ts: - normalizeWorkspaceAISettings: validate + normalize model/thinking - normalizeSendMessageAgentId: normalize agentId in options - extractWorkspaceAISettingsFromSendOptions: extract AI settings from send opts - maybePersistAISettingsFromOptions: best-effort settings persistence - persistWorkspaceAISettingsForAgent: persist settings to config + emit - updateAgentAISettings: validate + persist AI settings WorkspaceService retains the same public API (updateModeAISettings, updateAgentAISettings) and private helpers, all delegating to the new module. An EmitMetadataFn callback bridges the session/event emission. Zero logic changes — pure extraction.
Extract all task hierarchy/indexing query methods from taskService.ts into a new taskHierarchyIndex.ts module. These are pure functions operating on config data, making them the cleanest extraction target with zero circular dependency risk. Extracted functions: - buildAgentTaskIndex, listAgentTaskWorkspaces (index building) - listDescendantAgentTaskIdsFromIndex, isDescendantAgentTaskUsingParentById, listAncestorWorkspaceIdsUsingParentById (tree traversal) - getTaskDepth, getTaskDepthFromParentById (depth helpers) - countActiveAgentTasks, hasActiveDescendantAgentTasks, listActiveDescendantAgentTaskIds, listDescendantAgentTasks (active queries) - getAgentTaskStatus (status query) TaskService's public API remains unchanged — methods now delegate to the standalone functions. countActiveAgentTasks takes isForegroundAwaiting and isStreaming callbacks to avoid service-level dependencies.
Move 7 post-compaction state management methods into dedicated PostCompactionService class: - getPostCompactionState, getPostCompactionExclusions, setPostCompactionExclusion - getPersistedPostCompactionDiffPaths, deletePlanFilesForWorkspace - schedulePostCompactionMetadataRefresh, emitPostCompactionMetadata WorkspaceService delegates to the new service via composition. ~230 lines moved to focused module.
Extract all context-exceeded retry logic (~600 lines + 8 state fields) from AgentSession into a dedicated ContextExceededRetryHandler class. The handler owns: - 8 state fields (retry attempt sets, active stream context/flags) - 11 methods: handleStreamError, three retry strategies (compaction, post-compaction, hard restart exec subagent), and supporting helpers AgentSession interacts with the handler via: - initStreamState/markStreamHadDelta/etc. for state updates - handleStreamError as the error-handling entry point - Callbacks (emitChatEvent, clearQueue, streamWithHistory, etc.) for handler->session communication Zero logic changes - pure extraction with preserved test compatibility. All 31 agentSession tests pass including postCompactionRetry suite.
…Settings - Debounce test patches PostCompactionService directly instead of WorkspaceService delegation wrappers - AI settings tests use saveConfig mock (the real side effect) instead of monkey-patching the now-standalone persistWorkspaceAISettingsForAgent
1fd2812 to
9d5620a
Compare
Deduplicates ~50 lines of identical guard checks between sendMessage and resumeStream (renaming/removing/missing/queued state checks).
Move materializeFileAtMentionsSnapshot and materializeAgentSkillSnapshot to src/node/services/snapshotMaterializer.ts as stateless free functions. AgentSession delegates with explicit dependency parameters. agentSession.ts: 1680 → 1527 LoC (-153)
Move post-compaction attachment assembly (plan refs, TODOs, edited-file diffs, turn-counter state) to src/node/services/postCompactionAttachments.ts. Also deduplicates two near-identical 50-line attachment assembly blocks into a shared buildAttachments() method. agentSession.ts: 1527 → 1323 LoC (-204)
Extract handleEditTruncation (~80 LoC) and validateModelAndFiles (~45 LoC) as private methods, reducing sendMessage from 310 to ~219 lines. The method now reads as a high-level orchestrator with clearly named sub-steps.
…eMuxTypes Removing preGenerateMuxTypes (dead export) left ToolBridge with only type-level usage, triggering consistent-type-imports lint rule.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Systematic decomposition of the three largest
src/node/service files, reducing god-object complexity and improving SoC. Part of the multi-phase cleanup plan following AIService decomposition (PR #2243).Background
After merging AIService decomposition, the next targets are the remaining god objects:
workspaceService.ts(3,488 LoC → 2,607)agentSession.ts(2,303 LoC → 1,356)taskService.ts(2,680 LoC → 2,469)Implementation
Phase 0: Cross-Cutting Cleanup
error instanceof Error ? error.message : String(error)withgetErrorMessage()across 78 filesagentPresets.tsentirelytests/ipc/setup.tswithservices.toORPCContext()Phase 1: WorkspaceService Decomposition
subagentArtifactArchival.ts(467 LoC) — pure functions for archiving child session artifactsPostCompactionService(325 LoC) — plan files, tracked paths, exclusions, debounced metadata emissionworkspaceAISettings.ts(231 LoC) — 7 pure functions for normalizing/persisting AI settingsguardStreamingAllowedhelper — deduplicates ~50 lines of identical guard checks betweensendMessageandresumeStreamPhase 2: AgentSession Decomposition
ContextExceededRetryHandler(806 LoC) — entire retry logic for context limits with 3 strategies + 8 state fieldsPostCompactionAttachmentBuilder(207 LoC) — post-compaction context assembly (plan refs, TODOs, edited-file diffs). Also deduplicates two near-identical 50-line attachment assembly blocksSnapshotMaterializer(199 LoC) — stateless @file mention and agent skill snapshot functionssendMessagegod function — extractedhandleEditTruncationandvalidateModelAndFilesas private methods, reducingsendMessagefrom 310 to ~219 linesPhase 3: TaskService Decomposition
TaskHierarchyIndex(322 LoC) — 15 pure functions for building/querying ancestor/descendant hierarchyLoC Impact
workspaceService.tsagentSession.tstaskService.tsValidation
make typecheckcleanmake lintclean (1 pre-existing error incode_execution.ts, unrelated)Risks
Generated with
mux• Model:anthropic:claude-opus-4-6• Thinking:xhigh• Cost:$293.89