docs(01-daemon-decomposition): create phase plans — 3 plans in 2 waves
This commit is contained in:
@@ -18,6 +18,14 @@ Three phases, each delivering one complete capability. Phase 1 decomposes the mo
|
|||||||
|
|
||||||
**Requirements:** DECO-01, DECO-02, DECO-03, DECO-04, DECO-05, DECO-06, DECO-07, DECO-08
|
**Requirements:** DECO-01, DECO-02, DECO-03, DECO-04, DECO-05, DECO-06, DECO-07, DECO-08
|
||||||
|
|
||||||
|
**Plans:** 3 plans in 2 waves
|
||||||
|
|
||||||
|
| Plan | Wave | Objective | Tasks |
|
||||||
|
|------|------|-----------|-------|
|
||||||
|
| 01-01 | 1 | Extract models.ts, memory.ts, tools.ts | 3 |
|
||||||
|
| 01-02 | 1 | Extract channels.ts, agents.ts, routing.ts | 3 |
|
||||||
|
| 01-03 | 2 | Finalize thin composition root | 2 |
|
||||||
|
|
||||||
**Success Criteria:**
|
**Success Criteria:**
|
||||||
1. Running `pnpm test:run` passes 1077+ tests with zero regressions
|
1. Running `pnpm test:run` passes 1077+ tests with zero regressions
|
||||||
2. daemon/index.ts is under 200 lines and contains only imports, wiring, and lifecycle — no business logic
|
2. daemon/index.ts is under 200 lines and contains only imports, wiring, and lifecycle — no business logic
|
||||||
@@ -55,7 +63,7 @@ Three phases, each delivering one complete capability. Phase 1 decomposes the mo
|
|||||||
|
|
||||||
| Phase | Status | Requirements |
|
| Phase | Status | Requirements |
|
||||||
|-------|--------|--------------|
|
|-------|--------|--------------|
|
||||||
| 1 — Daemon Decomposition | not_started | DECO-01..08 (8) |
|
| 1 — Daemon Decomposition | planned | DECO-01..08 (8) — 3 plans, 2 waves |
|
||||||
| 2 — Config Overlays | not_started | CONF-01..03 (3) |
|
| 2 — Config Overlays | not_started | CONF-01..03 (3) |
|
||||||
| 3 — Live Ops Dashboard | not_started | DASH-01..05 (5) |
|
| 3 — Live Ops Dashboard | not_started | DASH-01..05 (5) |
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
---
|
||||||
|
phase: 01-daemon-decomposition
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- src/daemon/models.ts
|
||||||
|
- src/daemon/memory.ts
|
||||||
|
- src/daemon/tools.ts
|
||||||
|
- src/daemon/index.ts
|
||||||
|
autonomous: true
|
||||||
|
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, and createModelRouter are importable from src/daemon/models.ts with identical signatures"
|
||||||
|
- "Memory store, vector store, hybrid search, and background indexer initialization works identically when imported from src/daemon/memory.ts"
|
||||||
|
- "Tool registration, web search, process tools, browser tools, tool executor, and tool policy setup works identically when imported from src/daemon/tools.ts"
|
||||||
|
- "All 1077+ existing tests pass with zero regressions"
|
||||||
|
- "clientFactory.test.ts continues to pass without modification"
|
||||||
|
artifacts:
|
||||||
|
- path: "src/daemon/models.ts"
|
||||||
|
provides: "Model client factory, GitHub model mapping, auto-fallback, model router creation"
|
||||||
|
exports: ["createClientFromConfig", "anthropicToGitHubModel", "createAutoFallbackClient", "createModelRouter"]
|
||||||
|
min_lines: 200
|
||||||
|
- path: "src/daemon/memory.ts"
|
||||||
|
provides: "Memory store, vector store, hybrid search, and background indexer initialization"
|
||||||
|
exports: ["initMemory"]
|
||||||
|
min_lines: 60
|
||||||
|
- path: "src/daemon/tools.ts"
|
||||||
|
provides: "Tool registration, web search, process tools, browser tools, tool executor, tool policy"
|
||||||
|
exports: ["initTools"]
|
||||||
|
min_lines: 80
|
||||||
|
key_links:
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/models.ts"
|
||||||
|
via: "import and call createModelRouter"
|
||||||
|
pattern: "import.*from.*['\"]\\./models\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/memory.ts"
|
||||||
|
via: "import and call initMemory"
|
||||||
|
pattern: "import.*from.*['\"]\\./memory\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/tools.ts"
|
||||||
|
via: "import and call initTools"
|
||||||
|
pattern: "import.*from.*['\"]\\./tools\\.js['\"]"
|
||||||
|
- from: "src/daemon/clientFactory.test.ts"
|
||||||
|
to: "src/daemon/index.ts"
|
||||||
|
via: "re-exports from models.ts"
|
||||||
|
pattern: "import.*from.*['\"]\\./index\\.js['\"]"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Extract model client logic, memory initialization, and tool registration from daemon/index.ts into dedicated modules.
|
||||||
|
|
||||||
|
Purpose: These three extractions are the largest code movements (models ~240 lines, memory ~90 lines, tools ~70 lines) and are independent of channel/routing logic. Doing them first reduces daemon/index.ts by ~400 lines.
|
||||||
|
|
||||||
|
Output: Three new files (models.ts, memory.ts, tools.ts) with re-exports from daemon/index.ts to preserve the existing public interface.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/will/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/will/.config/opencode/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
|
||||||
|
@src/daemon/index.ts
|
||||||
|
@src/daemon/clientFactory.test.ts
|
||||||
|
@src/daemon/lifecycle.ts
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Extract model client logic into src/daemon/models.ts</name>
|
||||||
|
<files>src/daemon/models.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/models.ts` containing these functions moved verbatim from `daemon/index.ts`:
|
||||||
|
|
||||||
|
1. **`createClientFromConfig`** (lines 75-146) — the provider switch-case factory
|
||||||
|
2. **`anthropicToGitHubModel`** (lines 155-184) — Anthropic-to-GitHub model name mapping
|
||||||
|
3. **`createAutoFallbackClient`** (lines 191-207) — auto fallback for Anthropic tiers
|
||||||
|
4. **`createModelRouter`** (lines 209-316) — full model router setup with tiers, fallbacks, retry
|
||||||
|
|
||||||
|
Move the necessary imports from `daemon/index.ts` to `models.ts`:
|
||||||
|
- `import type { Config, ModelConfig } from '../config/index.js';`
|
||||||
|
- `import { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, GeminiClient, BedrockClient, GitHubModelsClient, ModelRouter, DEFAULT_RETRY_CONFIG } from '../models/index.js';`
|
||||||
|
- `import type { ModelClient, RetryConfig, ModelTier } from '../models/index.js';`
|
||||||
|
|
||||||
|
All four functions must be `export`ed with the exact same signatures. JSDoc comments must be preserved.
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Remove lines 70-316 (the four functions and their imports that are now only used in models.ts)
|
||||||
|
- Add `import { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js';`
|
||||||
|
- Add re-exports at the bottom: `export { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js';`
|
||||||
|
|
||||||
|
The re-exports are CRITICAL — `clientFactory.test.ts` imports from `./index.js` and must continue to work without modification.
|
||||||
|
|
||||||
|
Keep imports in `daemon/index.ts` that are still needed by other code in that file (e.g., `ModelRouter` type for `DaemonContext`).
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run src/daemon/clientFactory.test.ts` — all tests pass.
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/models.ts` exists with all four exported functions
|
||||||
|
- `createClientFromConfig`, `anthropicToGitHubModel`, `createAutoFallbackClient`, `createModelRouter` are importable from both `./models.js` and `./index.js`
|
||||||
|
- clientFactory.test.ts passes without changes
|
||||||
|
- No type errors
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Extract memory initialization into src/daemon/memory.ts</name>
|
||||||
|
<files>src/daemon/memory.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/memory.ts` with a single factory function that encapsulates memory/vector/hybrid-search setup.
|
||||||
|
|
||||||
|
Extract from `startDaemon()` lines 549-653 (memory store init, vector store, hybrid search, background indexer, memory tools registration) into a function:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface MemoryDeps {
|
||||||
|
config: Config;
|
||||||
|
dataDir: string;
|
||||||
|
lifecycle: Lifecycle;
|
||||||
|
toolRegistry: ToolRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MemoryResult {
|
||||||
|
memoryStore?: MemoryStore;
|
||||||
|
hybridSearch?: HybridSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initMemory(deps: MemoryDeps): Promise<MemoryResult>
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body should contain the exact logic from lines 549-653:
|
||||||
|
- Create memoryDir, mkdirSync
|
||||||
|
- Conditionally create MemoryStore
|
||||||
|
- If embedding enabled: create embedding provider, VectorStore, HybridSearch, background indexer interval, lifecycle shutdown handlers
|
||||||
|
- Register memory tools via createMemoryTools
|
||||||
|
- Return `{ memoryStore, hybridSearch }`
|
||||||
|
|
||||||
|
Move only the imports that are exclusively used by memory logic:
|
||||||
|
- `import { MemoryStore } from '../memory/index.js';`
|
||||||
|
- `import { VectorStore, HybridSearch, createEmbeddingProvider, chunkText, contentHash } from '../memory/index.js';`
|
||||||
|
- `import type { EmbeddingProvider as EmbeddingProviderInterface } from '../memory/index.js';`
|
||||||
|
- `import { createMemoryTools } from '../tools/builtin/index.js';`
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Replace lines 549-653 with: `const { memoryStore, hybridSearch } = await initMemory({ config, dataDir, lifecycle, toolRegistry });`
|
||||||
|
- Remove the imports that moved to memory.ts (MemoryStore stays if still referenced in DaemonContext or createMessageRouter types)
|
||||||
|
- Add `import { initMemory } from './memory.js';`
|
||||||
|
|
||||||
|
Note: `MemoryStore` type is used in `createMessageRouter` deps interface. Keep the type import in `daemon/index.ts` if needed, or import it from memory index.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run` — all tests pass (memory is integration-tested via daemon tests).
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/memory.ts` exists with `initMemory` exported
|
||||||
|
- Memory initialization logic removed from `startDaemon()` body
|
||||||
|
- All tests pass
|
||||||
|
- No type errors
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Extract tool registration into src/daemon/tools.ts</name>
|
||||||
|
<files>src/daemon/tools.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/tools.ts` with a factory function that encapsulates tool registry setup, web search, process tools, browser tools, tool executor, and tool policy.
|
||||||
|
|
||||||
|
Extract from `startDaemon()` lines 585-714 (tool registry init, web search, process manager, browser manager, tool executor, tool policy) into:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface ToolsDeps {
|
||||||
|
config: Config;
|
||||||
|
lifecycle: Lifecycle;
|
||||||
|
hookEngine: HookEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolsResult {
|
||||||
|
toolRegistry: ToolRegistry;
|
||||||
|
toolExecutor: ToolExecutor;
|
||||||
|
browserManager?: BrowserManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initTools(deps: ToolsDeps): ToolsResult
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** The function is NOT async because none of the tool setup is async (process manager, browser manager, tool policy are all synchronous). Only the shutdown handlers are async but those are callbacks registered on lifecycle.
|
||||||
|
|
||||||
|
The function body contains the exact logic from lines 585-714:
|
||||||
|
- Create ToolRegistry, register allBuiltinTools
|
||||||
|
- Register web search tools (if configured)
|
||||||
|
- Create ProcessManager, register process tools, lifecycle shutdown
|
||||||
|
- Create BrowserManager (if enabled), register browser tools, lifecycle shutdown
|
||||||
|
- Create ToolExecutor with hookEngine
|
||||||
|
- Create ToolPolicy from config, set on registry, log profile
|
||||||
|
|
||||||
|
Move the imports used exclusively by tool setup:
|
||||||
|
- `import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools } from '../tools/index.js';`
|
||||||
|
|
||||||
|
Keep in `daemon/index.ts` any tool imports still needed (e.g., `ToolRegistry` type for DaemonContext, `createSessionTools`, `createAgentsListTool`, `createMessageSendTool`, `createCronTools`, `createMediaSendTool` for later in startDaemon).
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Replace lines 585-714 with: `const { toolRegistry, toolExecutor, browserManager } = initTools({ config, lifecycle, hookEngine });`
|
||||||
|
- Remove the moved imports
|
||||||
|
- Add `import { initTools } from './tools.js';`
|
||||||
|
|
||||||
|
Note: Do NOT move the "Register Tier 1 agent tools" section (lines 975-993) — those depend on sessionManager, agentConfigRegistry, channelRegistry, and cronScheduler which are created later in startDaemon. They stay in daemon/index.ts for now.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run` — all tests pass.
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
Count lines in daemon/index.ts — should be ~400 lines shorter than baseline (1088).
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/tools.ts` exists with `initTools` exported
|
||||||
|
- Tool registration logic removed from `startDaemon()` body
|
||||||
|
- Tier 1 agent tools (session, agents list, message send, cron) remain in daemon/index.ts
|
||||||
|
- All tests pass
|
||||||
|
- No type errors
|
||||||
|
- daemon/index.ts is ~650-700 lines (down from 1088)
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `pnpm typecheck` passes with zero errors
|
||||||
|
- [ ] `pnpm test:run` passes all 1077+ tests
|
||||||
|
- [ ] `pnpm test:run src/daemon/clientFactory.test.ts` passes (re-export verification)
|
||||||
|
- [ ] `src/daemon/models.ts` exports: createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter
|
||||||
|
- [ ] `src/daemon/memory.ts` exports: initMemory
|
||||||
|
- [ ] `src/daemon/tools.ts` exports: initTools
|
||||||
|
- [ ] daemon/index.ts re-exports model functions for backward compatibility
|
||||||
|
- [ ] daemon/index.ts is ~650-700 lines (reduced by ~400 lines)
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- All tasks completed
|
||||||
|
- All verification checks pass
|
||||||
|
- No errors or warnings introduced
|
||||||
|
- Each new module file can be read and understood in isolation
|
||||||
|
- daemon/index.ts is measurably shorter with model/memory/tool logic extracted
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-daemon-decomposition/01-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
---
|
||||||
|
phase: 01-daemon-decomposition
|
||||||
|
plan: 02
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- src/daemon/channels.ts
|
||||||
|
- src/daemon/agents.ts
|
||||||
|
- src/daemon/routing.ts
|
||||||
|
- src/daemon/index.ts
|
||||||
|
autonomous: true
|
||||||
|
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "Channel adapter registration (Telegram, Discord, Slack, WhatsApp, WebChat, Cron, Webhooks, Gmail) works identically when imported from src/daemon/channels.ts"
|
||||||
|
- "Agent cache and factory logic (getOrCreateAgent, sandbox wiring, attachment collector) works identically when imported from src/daemon/routing.ts"
|
||||||
|
- "Agent config registry and router initialization works identically when imported from src/daemon/agents.ts"
|
||||||
|
- "All 1077+ existing tests pass with zero regressions"
|
||||||
|
- "routing.test.ts continues to pass without modification"
|
||||||
|
artifacts:
|
||||||
|
- path: "src/daemon/channels.ts"
|
||||||
|
provides: "Channel adapter registration for all adapters plus automation (cron, webhooks, Gmail)"
|
||||||
|
exports: ["registerChannels"]
|
||||||
|
min_lines: 80
|
||||||
|
- path: "src/daemon/agents.ts"
|
||||||
|
provides: "Agent config registry loading, agent router creation, sandbox manager init"
|
||||||
|
exports: ["initAgents"]
|
||||||
|
min_lines: 30
|
||||||
|
- path: "src/daemon/routing.ts"
|
||||||
|
provides: "Message router with agent cache, sandbox wiring, audio transcription, command handling"
|
||||||
|
exports: ["createMessageRouter"]
|
||||||
|
min_lines: 180
|
||||||
|
key_links:
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/channels.ts"
|
||||||
|
via: "import and call registerChannels"
|
||||||
|
pattern: "import.*from.*['\"]\\./channels\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/agents.ts"
|
||||||
|
via: "import and call initAgents"
|
||||||
|
pattern: "import.*from.*['\"]\\./agents\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/routing.ts"
|
||||||
|
via: "import and call createMessageRouter"
|
||||||
|
pattern: "import.*from.*['\"]\\./routing\\.js['\"]"
|
||||||
|
- from: "src/daemon/routing.test.ts"
|
||||||
|
to: "src/daemon/routing.ts"
|
||||||
|
via: "tests use AgentRouter and AgentConfigRegistry from agents module"
|
||||||
|
pattern: "import.*from.*['\"]\\.\\./(agents|daemon)"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Extract channel adapter setup, agent configuration, and message routing from daemon/index.ts into dedicated modules.
|
||||||
|
|
||||||
|
Purpose: These three extractions handle the remaining business logic in daemon/index.ts — channel wiring (~100 lines), agent setup (~40 lines), and message routing (~220 lines). After this plan, daemon/index.ts contains only infrastructure wiring.
|
||||||
|
|
||||||
|
Output: Three new files (channels.ts, agents.ts, routing.ts) that encapsulate channel, agent, and routing concerns.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/will/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/will/.config/opencode/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
|
||||||
|
@src/daemon/index.ts
|
||||||
|
@src/daemon/routing.test.ts
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Extract message routing into src/daemon/routing.ts</name>
|
||||||
|
<files>src/daemon/routing.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/routing.ts` by moving the `createMessageRouter` function (lines 325-540) verbatim from `daemon/index.ts`.
|
||||||
|
|
||||||
|
This is a direct function move — the function signature, parameter types, return type, and body are unchanged. Move the entire function including:
|
||||||
|
- The `deps` parameter interface (inline or named)
|
||||||
|
- The `agents` Map
|
||||||
|
- The `getOrCreateAgent` inner function (agent cache, sandbox wiring, media send tool, orchestrator creation)
|
||||||
|
- The `handler` async function (command handling: reset, compact, usage; audio transcription; agent.process; error handling)
|
||||||
|
- The return `{ handler, agents }`
|
||||||
|
|
||||||
|
Move necessary imports to `routing.ts`:
|
||||||
|
- `import type { AudioTranscriptionConfig } from '../models/media.js';`
|
||||||
|
- `import type { Attachment } from '../channels/types.js';`
|
||||||
|
- `import { isSupportedAudio, transcribeAudio } from '../models/media.js';`
|
||||||
|
- `import { AgentOrchestrator, type DelegationConfig } from '../backends/index.js';`
|
||||||
|
- `import { OutboundAttachmentCollector } from '../backends/native/attachments.js';`
|
||||||
|
- `import type { InboundMessage, OutboundMessage } from '../channels/index.js';`
|
||||||
|
- `import { MemoryStore } from '../memory/index.js';`
|
||||||
|
- `import type { Tool } from '../tools/types.js';`
|
||||||
|
- `import { createMediaSendTool } from '../tools/index.js';`
|
||||||
|
- `import { createSandboxedShellTool, createSandboxedProcessStartTool, SandboxManager } from '../sandbox/index.js';`
|
||||||
|
- `import type { Config } from '../config/index.js';`
|
||||||
|
- `import { ModelRouter } from '../models/index.js';`
|
||||||
|
- `import { ToolRegistry, ToolExecutor } from '../tools/index.js';`
|
||||||
|
- `import { SessionManager } from '../session/index.js';`
|
||||||
|
- `import { AgentConfigRegistry, AgentRouter } from '../agents/index.js';`
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Remove the `createMessageRouter` function (lines 318-540)
|
||||||
|
- Remove imports that only served that function
|
||||||
|
- Add `import { createMessageRouter } from './routing.js';`
|
||||||
|
|
||||||
|
IMPORTANT: The `createMessageRouter` function is NOT part of the public API (it's not exported from daemon/index.ts currently — it's a private `function`, not `export function`). So no re-export needed.
|
||||||
|
|
||||||
|
Note: `routing.test.ts` tests `AgentRouter` and `AgentConfigRegistry` from `../agents/` — it does NOT import `createMessageRouter` from `./index.js`. So this move doesn't affect that test file.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run src/daemon/routing.test.ts` — passes (doesn't import createMessageRouter).
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/routing.ts` exists with `createMessageRouter` exported
|
||||||
|
- createMessageRouter removed from daemon/index.ts
|
||||||
|
- routing.test.ts passes without changes
|
||||||
|
- No type errors
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Extract agent config and sandbox setup into src/daemon/agents.ts</name>
|
||||||
|
<files>src/daemon/agents.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/agents.ts` with a factory function that encapsulates agent config registry loading, agent router creation, and sandbox manager initialization.
|
||||||
|
|
||||||
|
Extract from `startDaemon()` lines 749-774 (agent config registry, agent router, sandbox manager) into:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Config } from '../config/index.js';
|
||||||
|
import { AgentConfigRegistry, AgentRouter } from '../agents/index.js';
|
||||||
|
import { DockerSandbox, SandboxManager } from '../sandbox/index.js';
|
||||||
|
import type { Lifecycle } from './lifecycle.js';
|
||||||
|
|
||||||
|
export interface AgentsDeps {
|
||||||
|
config: Config;
|
||||||
|
lifecycle: Lifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentsResult {
|
||||||
|
agentConfigRegistry: AgentConfigRegistry;
|
||||||
|
agentRouter: AgentRouter;
|
||||||
|
sandboxManager?: SandboxManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initAgents(deps: AgentsDeps): Promise<AgentsResult>
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body contains the exact logic from lines 749-774:
|
||||||
|
- Create AgentConfigRegistry, load from config if present
|
||||||
|
- Create AgentRouter from config.routing
|
||||||
|
- Check DockerSandbox.isAvailable(), create SandboxManager if enabled
|
||||||
|
- Register lifecycle shutdown for sandbox cleanup
|
||||||
|
- Return all three
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Replace lines 749-774 with: `const { agentConfigRegistry, agentRouter, sandboxManager } = await initAgents({ config, lifecycle });`
|
||||||
|
- Remove imports that moved (AgentConfigRegistry, AgentRouter, DockerSandbox, SandboxManager — but keep SandboxManager type if needed for DaemonContext)
|
||||||
|
- Add `import { initAgents } from './agents.js';`
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run src/daemon/routing.test.ts` — passes (tests AgentRouter from ../agents/).
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/agents.ts` exists with `initAgents` exported
|
||||||
|
- Agent config, router, and sandbox logic removed from `startDaemon()`
|
||||||
|
- All tests pass
|
||||||
|
- No type errors
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Extract channel adapter registration into src/daemon/channels.ts</name>
|
||||||
|
<files>src/daemon/channels.ts, src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
Create `src/daemon/channels.ts` with a factory function that encapsulates all channel adapter creation and registration.
|
||||||
|
|
||||||
|
Extract from `startDaemon()` lines 898-973 (Telegram, Discord, Slack, WhatsApp, WebChat adapters, cron scheduler, webhook handler, Gmail watcher) into:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Config } from '../config/index.js';
|
||||||
|
import type { HookEngine } from '../hooks/index.js';
|
||||||
|
import type { PairingManager, ChannelRegistry } from '../channels/index.js';
|
||||||
|
import { TelegramAdapter, WebChatAdapter, DiscordAdapter, SlackAdapter, WhatsAppAdapter } from '../channels/index.js';
|
||||||
|
import { CronScheduler, WebhookHandler, GmailWatcher } from '../automation/index.js';
|
||||||
|
import type { GatewayServer } from '../gateway/index.js';
|
||||||
|
|
||||||
|
export interface ChannelsDeps {
|
||||||
|
config: Config;
|
||||||
|
channelRegistry: ChannelRegistry;
|
||||||
|
hookEngine: HookEngine;
|
||||||
|
pairingManager?: PairingManager;
|
||||||
|
gateway: GatewayServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChannelsResult {
|
||||||
|
cronScheduler?: CronScheduler;
|
||||||
|
webhookHandler?: WebhookHandler;
|
||||||
|
gmailWatcher?: GmailWatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerChannels(deps: ChannelsDeps): ChannelsResult
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body contains the exact logic from lines 898-973:
|
||||||
|
- Create and register TelegramAdapter (always)
|
||||||
|
- Create and register DiscordAdapter (if config.discord)
|
||||||
|
- Create and register SlackAdapter (if config.slack)
|
||||||
|
- Create and register WhatsAppAdapter (if config.whatsapp)
|
||||||
|
- Create and register WebChatAdapter (wraps gateway)
|
||||||
|
- Create and register CronScheduler (if cron jobs configured)
|
||||||
|
- Create and register WebhookHandler (if webhooks configured), set on gateway
|
||||||
|
- Create and register GmailWatcher (if configured), set on gateway
|
||||||
|
- Return { cronScheduler, webhookHandler, gmailWatcher }
|
||||||
|
|
||||||
|
In `daemon/index.ts`:
|
||||||
|
- Replace lines 898-973 with: `const { cronScheduler, webhookHandler, gmailWatcher } = registerChannels({ config, channelRegistry, hookEngine, pairingManager, gateway });`
|
||||||
|
- Remove the adapter imports that moved (TelegramAdapter, DiscordAdapter, SlackAdapter, WhatsAppAdapter, WebChatAdapter, CronScheduler, WebhookHandler, GmailWatcher)
|
||||||
|
- Add `import { registerChannels } from './channels.js';`
|
||||||
|
|
||||||
|
Note: `gmailWatcher` and `webhookHandler` variables may still be referenced below (e.g., no further usage after registration). Check and remove any dead references.
|
||||||
|
|
||||||
|
The `cronScheduler` is still needed in startDaemon for the Tier 1 cron tools registration (lines 989-993). So the return value is important.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `pnpm test:run` — all tests pass.
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
Count lines in daemon/index.ts — should be ~300-350 lines shorter than after Plan 01 completed.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- `src/daemon/channels.ts` exists with `registerChannels` exported
|
||||||
|
- All channel adapter registration logic removed from `startDaemon()`
|
||||||
|
- cronScheduler returned so Tier 1 cron tools can still be registered
|
||||||
|
- All tests pass
|
||||||
|
- No type errors
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `pnpm typecheck` passes with zero errors
|
||||||
|
- [ ] `pnpm test:run` passes all 1077+ tests
|
||||||
|
- [ ] `pnpm test:run src/daemon/routing.test.ts` passes (agent routing tests)
|
||||||
|
- [ ] `src/daemon/routing.ts` exports: createMessageRouter
|
||||||
|
- [ ] `src/daemon/agents.ts` exports: initAgents
|
||||||
|
- [ ] `src/daemon/channels.ts` exports: registerChannels
|
||||||
|
- [ ] daemon/index.ts no longer contains createMessageRouter function body
|
||||||
|
- [ ] daemon/index.ts no longer contains individual adapter creation code
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- All tasks completed
|
||||||
|
- All verification checks pass
|
||||||
|
- No errors or warnings introduced
|
||||||
|
- Each new module file can be read and understood in isolation
|
||||||
|
- Channel adapter additions would only require editing channels.ts
|
||||||
|
- Agent config additions would only require editing agents.ts
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-daemon-decomposition/01-02-SUMMARY.md`
|
||||||
|
</output>
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
---
|
||||||
|
phase: 01-daemon-decomposition
|
||||||
|
plan: 03
|
||||||
|
type: execute
|
||||||
|
wave: 2
|
||||||
|
depends_on: ["01-01", "01-02"]
|
||||||
|
files_modified:
|
||||||
|
- src/daemon/index.ts
|
||||||
|
autonomous: true
|
||||||
|
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "daemon/index.ts is under 200 lines and contains only imports, wiring calls, lifecycle, and the DaemonContext return"
|
||||||
|
- "daemon/index.ts has no business logic — no switch statements, no conditional adapter creation, no tool registration loops"
|
||||||
|
- "All 1077+ existing tests pass with zero regressions"
|
||||||
|
- "Adding a new model provider requires editing only src/daemon/models.ts"
|
||||||
|
- "Adding a new channel adapter requires editing only src/daemon/channels.ts"
|
||||||
|
- "The DaemonContext interface is unchanged"
|
||||||
|
- "startDaemon() function signature and return type are unchanged"
|
||||||
|
artifacts:
|
||||||
|
- path: "src/daemon/index.ts"
|
||||||
|
provides: "Thin composition root — imports, wiring, lifecycle, DaemonContext"
|
||||||
|
exports: ["DaemonContext", "startDaemon", "Lifecycle", "createClientFromConfig", "anthropicToGitHubModel", "createAutoFallbackClient", "createModelRouter"]
|
||||||
|
min_lines: 100
|
||||||
|
max_lines: 200
|
||||||
|
key_links:
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/models.ts"
|
||||||
|
via: "re-export for backward compatibility"
|
||||||
|
pattern: "export.*from.*['\"]\\./models\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/memory.ts"
|
||||||
|
via: "import initMemory"
|
||||||
|
pattern: "import.*initMemory.*from.*['\"]\\./memory\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/tools.ts"
|
||||||
|
via: "import initTools"
|
||||||
|
pattern: "import.*initTools.*from.*['\"]\\./tools\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/channels.ts"
|
||||||
|
via: "import registerChannels"
|
||||||
|
pattern: "import.*registerChannels.*from.*['\"]\\./channels\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/agents.ts"
|
||||||
|
via: "import initAgents"
|
||||||
|
pattern: "import.*initAgents.*from.*['\"]\\./agents\\.js['\"]"
|
||||||
|
- from: "src/daemon/index.ts"
|
||||||
|
to: "src/daemon/routing.ts"
|
||||||
|
via: "import createMessageRouter"
|
||||||
|
pattern: "import.*createMessageRouter.*from.*['\"]\\./routing\\.js['\"]"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Finalize daemon/index.ts as a thin composition root under 200 lines.
|
||||||
|
|
||||||
|
Purpose: After Plans 01 and 02 extracted ~700 lines of business logic into 6 modules, daemon/index.ts should already be significantly shorter. This plan cleans up any remaining inline logic, ensures consistent import structure, removes dead imports, adds section comments, and verifies the final line count meets the <200 line target.
|
||||||
|
|
||||||
|
Output: A clean daemon/index.ts that reads as a wiring manifest — you can understand the entire daemon startup by reading one short file.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/will/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/will/.config/opencode/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
|
||||||
|
@.planning/phases/01-daemon-decomposition/01-01-SUMMARY.md
|
||||||
|
@.planning/phases/01-daemon-decomposition/01-02-SUMMARY.md
|
||||||
|
|
||||||
|
@src/daemon/index.ts
|
||||||
|
@src/daemon/models.ts
|
||||||
|
@src/daemon/memory.ts
|
||||||
|
@src/daemon/tools.ts
|
||||||
|
@src/daemon/channels.ts
|
||||||
|
@src/daemon/agents.ts
|
||||||
|
@src/daemon/routing.ts
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Clean up daemon/index.ts — remove dead imports, extract remaining inline logic</name>
|
||||||
|
<files>src/daemon/index.ts</files>
|
||||||
|
<action>
|
||||||
|
After Plans 01 and 02 completed, daemon/index.ts should be around 300-400 lines. Review and clean up:
|
||||||
|
|
||||||
|
1. **Remove dead imports** — Any imports from `../models/`, `../tools/`, `../channels/`, `../automation/`, `../sandbox/`, `../agents/`, `../memory/` that are no longer referenced in daemon/index.ts should be removed. The extracted modules have their own imports now.
|
||||||
|
|
||||||
|
2. **Audit remaining inline logic in startDaemon()**. The following should still be in daemon/index.ts (they are pure wiring, not business logic):
|
||||||
|
- Data directory creation (2 lines)
|
||||||
|
- Session store + manager creation (3 lines)
|
||||||
|
- Session pruning timer (10 lines) — this is lifecycle wiring, acceptable
|
||||||
|
- Hook engine creation (1 line)
|
||||||
|
- initMemory() call (1 line)
|
||||||
|
- initTools() call (1 line)
|
||||||
|
- MCP manager setup (6 lines)
|
||||||
|
- Skills loading (12 lines)
|
||||||
|
- initAgents() call (1 line)
|
||||||
|
- Audio config (5 lines)
|
||||||
|
- Model router creation (1 line)
|
||||||
|
- System prompt loading (5 lines)
|
||||||
|
- Channel registry creation (1 line)
|
||||||
|
- Pairing manager setup (10 lines)
|
||||||
|
- Gateway server creation (large config block ~50 lines) — this stays, it's wiring
|
||||||
|
- createMessageRouter() call (14 lines)
|
||||||
|
- registerChannels() call (1 line)
|
||||||
|
- Tier 1 agent tools (15 lines) — these stay (they need deps from multiple init functions)
|
||||||
|
- Signal handlers (10 lines)
|
||||||
|
- Service startup (channels, gateway, Tailscale, heartbeat) (35 lines)
|
||||||
|
- Return DaemonContext
|
||||||
|
|
||||||
|
3. **Extract loadSystemPrompt** into its own section or keep it — it's only 15 lines and used once. If daemon/index.ts is still over 200 lines, consider extracting the Tier 1 agent tools block and/or the gateway config block. However, these are wiring code, not business logic — so keeping them is acceptable if under 200 lines.
|
||||||
|
|
||||||
|
4. **Organize imports** into clear groups with section comments:
|
||||||
|
```
|
||||||
|
// ── External ──
|
||||||
|
// ── Config & Types ──
|
||||||
|
// ── Daemon Modules ──
|
||||||
|
// ── Infrastructure ──
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add section dividers** in startDaemon() body:
|
||||||
|
```
|
||||||
|
// ── Data & Sessions ──
|
||||||
|
// ── Core Services ──
|
||||||
|
// ── Model & Prompt ──
|
||||||
|
// ── Gateway & Channels ──
|
||||||
|
// ── Tier 1 Tools ──
|
||||||
|
// ── Lifecycle ──
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Verify DaemonContext** interface is unchanged — same fields, same types.
|
||||||
|
|
||||||
|
7. **Verify all re-exports** at the bottom of the file:
|
||||||
|
```typescript
|
||||||
|
export { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js';
|
||||||
|
export { Lifecycle } from './lifecycle.js';
|
||||||
|
```
|
||||||
|
|
||||||
|
If the file is still over 200 lines after cleanup, identify what can move:
|
||||||
|
- The `loadSystemPrompt` function (15 lines) could move to a prompt.ts or stay
|
||||||
|
- The pairing manager setup (10 lines) could move to channels.ts
|
||||||
|
- The gateway config block is the largest remaining section — but it's pure wiring
|
||||||
|
|
||||||
|
Target: daemon/index.ts ≤ 200 lines.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
Run `wc -l src/daemon/index.ts` — must be ≤ 200 lines.
|
||||||
|
Run `pnpm typecheck` — no type errors.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- daemon/index.ts is under 200 lines
|
||||||
|
- All imports are clean — no unused imports
|
||||||
|
- Section dividers make the file scannable
|
||||||
|
- DaemonContext interface is unchanged
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Final validation — full test suite and line count audit</name>
|
||||||
|
<files>src/daemon/index.ts, src/daemon/models.ts, src/daemon/memory.ts, src/daemon/tools.ts, src/daemon/channels.ts, src/daemon/agents.ts, src/daemon/routing.ts</files>
|
||||||
|
<action>
|
||||||
|
Run the full validation suite to confirm Phase 1 success criteria:
|
||||||
|
|
||||||
|
1. **Run full test suite**: `pnpm test:run` — all 1077+ tests must pass
|
||||||
|
2. **Run typecheck**: `pnpm typecheck` — zero errors
|
||||||
|
3. **Run specific daemon tests**: `pnpm test:run src/daemon/` — all daemon tests pass
|
||||||
|
4. **Line count audit**:
|
||||||
|
- `wc -l src/daemon/index.ts` — must be ≤ 200
|
||||||
|
- `wc -l src/daemon/models.ts` — should be ~200-250 (model factory + router)
|
||||||
|
- `wc -l src/daemon/routing.ts` — should be ~180-220 (message router)
|
||||||
|
- `wc -l src/daemon/channels.ts` — should be ~80-100 (adapter registration)
|
||||||
|
- `wc -l src/daemon/agents.ts` — should be ~40-60 (agent config + sandbox)
|
||||||
|
- `wc -l src/daemon/memory.ts` — should be ~70-100 (memory + vector init)
|
||||||
|
- `wc -l src/daemon/tools.ts` — should be ~80-130 (tool registry setup)
|
||||||
|
|
||||||
|
5. **Verify module isolation**: Each extracted module imports only from external modules (../models, ../tools, etc.) and from ./lifecycle.js — never from ./index.js (no circular deps).
|
||||||
|
|
||||||
|
6. **Verify backward compatibility**: Grep for all imports of `from '../daemon/index.js'` or `from './index.js'` in the test files — all must still resolve.
|
||||||
|
|
||||||
|
If any test fails, fix the issue. Common issues:
|
||||||
|
- Missing re-export in index.ts
|
||||||
|
- Import path changed
|
||||||
|
- Type not exported from new module
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
`pnpm test:run` — all tests pass.
|
||||||
|
`pnpm typecheck` — zero errors.
|
||||||
|
`wc -l src/daemon/index.ts` ≤ 200.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- All 1077+ tests pass
|
||||||
|
- Zero type errors
|
||||||
|
- daemon/index.ts ≤ 200 lines
|
||||||
|
- All 7 daemon module files exist and are correctly sized
|
||||||
|
- No circular dependencies between daemon modules
|
||||||
|
- Phase 1 success criteria fully met
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `pnpm typecheck` passes with zero errors
|
||||||
|
- [ ] `pnpm test:run` passes all 1077+ tests
|
||||||
|
- [ ] `pnpm test:run src/daemon/clientFactory.test.ts` passes
|
||||||
|
- [ ] `pnpm test:run src/daemon/routing.test.ts` passes
|
||||||
|
- [ ] `pnpm test:run src/daemon/lifecycle.test.ts` passes
|
||||||
|
- [ ] `wc -l src/daemon/index.ts` shows ≤ 200 lines
|
||||||
|
- [ ] daemon/index.ts contains NO switch statements
|
||||||
|
- [ ] daemon/index.ts contains NO conditional adapter creation (`if (config.discord)`)
|
||||||
|
- [ ] daemon/index.ts contains NO tool registration loops (`for (const tool of ...)`)
|
||||||
|
- [ ] DaemonContext interface is unchanged from baseline
|
||||||
|
- [ ] All re-exports present for backward compatibility
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- All tasks completed
|
||||||
|
- All verification checks pass
|
||||||
|
- No errors or warnings introduced
|
||||||
|
- daemon/index.ts is a thin composition root: imports → init calls → wire → lifecycle → return
|
||||||
|
- Every concern (models, memory, tools, channels, agents, routing) lives in its own module
|
||||||
|
- DECO-01 through DECO-08 all satisfied
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-daemon-decomposition/01-03-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user