docs(01-daemon-decomposition): create phase plans — 3 plans in 2 waves
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user