273 lines
12 KiB
Markdown
273 lines
12 KiB
Markdown
---
|
|
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>
|