12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-daemon-decomposition | 02 | execute | 1 |
|
true |
|
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.
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md@src/daemon/index.ts @src/daemon/routing.test.ts
Task 1: Extract message routing into src/daemon/routing.ts src/daemon/routing.ts, src/daemon/index.ts 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
depsparameter interface (inline or named) - The
agentsMap - The
getOrCreateAgentinner function (agent cache, sandbox wiring, media send tool, orchestrator creation) - The
handlerasync 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
createMessageRouterfunction (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.
Run pnpm test:run src/daemon/routing.test.ts — passes (doesn't import createMessageRouter).
Run pnpm typecheck — no type errors.
src/daemon/routing.tsexists withcreateMessageRouterexported- createMessageRouter removed from daemon/index.ts
- routing.test.ts passes without changes
- No type errors
Extract from startDaemon() lines 749-774 (agent config registry, agent router, sandbox manager) into:
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';Runpnpm test:run src/daemon/routing.test.ts— passes (tests AgentRouter from ../agents/). Runpnpm typecheck— no type errors. src/daemon/agents.tsexists withinitAgentsexported- Agent config, router, and sandbox logic removed from
startDaemon() - All tests pass
- No type errors
Extract from startDaemon() lines 898-973 (Telegram, Discord, Slack, WhatsApp, WebChat adapters, cron scheduler, webhook handler, Gmail watcher) into:
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.
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.
src/daemon/channels.tsexists withregisterChannelsexported- 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
<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>