@stoneforge/smithy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +114 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +7 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/orchestrator-api.d.ts +153 -0
- package/dist/api/orchestrator-api.d.ts.map +1 -0
- package/dist/api/orchestrator-api.js +374 -0
- package/dist/api/orchestrator-api.js.map +1 -0
- package/dist/bin/sf.d.ts +3 -0
- package/dist/bin/sf.d.ts.map +1 -0
- package/dist/bin/sf.js +10 -0
- package/dist/bin/sf.js.map +1 -0
- package/dist/cli/commands/agent.d.ts +20 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +861 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +14 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +272 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/dispatch.d.ts +9 -0
- package/dist/cli/commands/dispatch.d.ts.map +1 -0
- package/dist/cli/commands/dispatch.js +128 -0
- package/dist/cli/commands/dispatch.js.map +1 -0
- package/dist/cli/commands/merge.d.ts +11 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +246 -0
- package/dist/cli/commands/merge.js.map +1 -0
- package/dist/cli/commands/pool.d.ts +21 -0
- package/dist/cli/commands/pool.d.ts.map +1 -0
- package/dist/cli/commands/pool.js +762 -0
- package/dist/cli/commands/pool.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +54 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +57 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/task.d.ts +36 -0
- package/dist/cli/commands/task.d.ts.map +1 -0
- package/dist/cli/commands/task.js +889 -0
- package/dist/cli/commands/task.js.map +1 -0
- package/dist/cli/commands/test-orchestration.d.ts +32 -0
- package/dist/cli/commands/test-orchestration.d.ts.map +1 -0
- package/dist/cli/commands/test-orchestration.js +392 -0
- package/dist/cli/commands/test-orchestration.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/plugin.d.ts +23 -0
- package/dist/cli/plugin.d.ts.map +1 -0
- package/dist/cli/plugin.js +36 -0
- package/dist/cli/plugin.js.map +1 -0
- package/dist/git/index.d.ts +10 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +12 -0
- package/dist/git/index.js.map +1 -0
- package/dist/git/merge.d.ts +79 -0
- package/dist/git/merge.d.ts.map +1 -0
- package/dist/git/merge.js +254 -0
- package/dist/git/merge.js.map +1 -0
- package/dist/git/worktree-manager.d.ts +299 -0
- package/dist/git/worktree-manager.d.ts.map +1 -0
- package/dist/git/worktree-manager.js +744 -0
- package/dist/git/worktree-manager.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/director.md +272 -0
- package/dist/prompts/index.d.ts +100 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +294 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/message-triage.md +50 -0
- package/dist/prompts/persistent-worker.md +240 -0
- package/dist/prompts/steward-base.md +64 -0
- package/dist/prompts/steward-docs.md +118 -0
- package/dist/prompts/steward-health.md +39 -0
- package/dist/prompts/steward-merge.md +168 -0
- package/dist/prompts/steward-ops.md +28 -0
- package/dist/prompts/steward-reminder.md +26 -0
- package/dist/prompts/worker.md +282 -0
- package/dist/providers/claude/headless.d.ts +18 -0
- package/dist/providers/claude/headless.d.ts.map +1 -0
- package/dist/providers/claude/headless.js +307 -0
- package/dist/providers/claude/headless.js.map +1 -0
- package/dist/providers/claude/index.d.ts +24 -0
- package/dist/providers/claude/index.d.ts.map +1 -0
- package/dist/providers/claude/index.js +80 -0
- package/dist/providers/claude/index.js.map +1 -0
- package/dist/providers/claude/interactive.d.ts +21 -0
- package/dist/providers/claude/interactive.d.ts.map +1 -0
- package/dist/providers/claude/interactive.js +142 -0
- package/dist/providers/claude/interactive.js.map +1 -0
- package/dist/providers/codex/event-mapper.d.ts +91 -0
- package/dist/providers/codex/event-mapper.d.ts.map +1 -0
- package/dist/providers/codex/event-mapper.js +299 -0
- package/dist/providers/codex/event-mapper.js.map +1 -0
- package/dist/providers/codex/headless.d.ts +20 -0
- package/dist/providers/codex/headless.d.ts.map +1 -0
- package/dist/providers/codex/headless.js +174 -0
- package/dist/providers/codex/headless.js.map +1 -0
- package/dist/providers/codex/index.d.ts +30 -0
- package/dist/providers/codex/index.d.ts.map +1 -0
- package/dist/providers/codex/index.js +55 -0
- package/dist/providers/codex/index.js.map +1 -0
- package/dist/providers/codex/interactive.d.ts +21 -0
- package/dist/providers/codex/interactive.d.ts.map +1 -0
- package/dist/providers/codex/interactive.js +141 -0
- package/dist/providers/codex/interactive.js.map +1 -0
- package/dist/providers/codex/jsonrpc-client.d.ts +52 -0
- package/dist/providers/codex/jsonrpc-client.d.ts.map +1 -0
- package/dist/providers/codex/jsonrpc-client.js +141 -0
- package/dist/providers/codex/jsonrpc-client.js.map +1 -0
- package/dist/providers/codex/server-manager.d.ts +100 -0
- package/dist/providers/codex/server-manager.d.ts.map +1 -0
- package/dist/providers/codex/server-manager.js +153 -0
- package/dist/providers/codex/server-manager.js.map +1 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/opencode/async-queue.d.ts +21 -0
- package/dist/providers/opencode/async-queue.d.ts.map +1 -0
- package/dist/providers/opencode/async-queue.js +51 -0
- package/dist/providers/opencode/async-queue.js.map +1 -0
- package/dist/providers/opencode/event-mapper.d.ts +132 -0
- package/dist/providers/opencode/event-mapper.d.ts.map +1 -0
- package/dist/providers/opencode/event-mapper.js +204 -0
- package/dist/providers/opencode/event-mapper.js.map +1 -0
- package/dist/providers/opencode/headless.d.ts +25 -0
- package/dist/providers/opencode/headless.d.ts.map +1 -0
- package/dist/providers/opencode/headless.js +190 -0
- package/dist/providers/opencode/headless.js.map +1 -0
- package/dist/providers/opencode/index.d.ts +33 -0
- package/dist/providers/opencode/index.d.ts.map +1 -0
- package/dist/providers/opencode/index.js +42 -0
- package/dist/providers/opencode/index.js.map +1 -0
- package/dist/providers/opencode/interactive.d.ts +21 -0
- package/dist/providers/opencode/interactive.d.ts.map +1 -0
- package/dist/providers/opencode/interactive.js +135 -0
- package/dist/providers/opencode/interactive.js.map +1 -0
- package/dist/providers/opencode/server-manager.d.ts +145 -0
- package/dist/providers/opencode/server-manager.d.ts.map +1 -0
- package/dist/providers/opencode/server-manager.js +163 -0
- package/dist/providers/opencode/server-manager.js.map +1 -0
- package/dist/providers/registry.d.ts +38 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +82 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +144 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +25 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/runtime/event-utils.d.ts +8 -0
- package/dist/runtime/event-utils.d.ts.map +1 -0
- package/dist/runtime/event-utils.js +23 -0
- package/dist/runtime/event-utils.js.map +1 -0
- package/dist/runtime/handoff.d.ts +195 -0
- package/dist/runtime/handoff.d.ts.map +1 -0
- package/dist/runtime/handoff.js +332 -0
- package/dist/runtime/handoff.js.map +1 -0
- package/dist/runtime/index.d.ts +17 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +60 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/message-mapper.d.ts +99 -0
- package/dist/runtime/message-mapper.d.ts.map +1 -0
- package/dist/runtime/message-mapper.js +202 -0
- package/dist/runtime/message-mapper.js.map +1 -0
- package/dist/runtime/predecessor-query.d.ts +212 -0
- package/dist/runtime/predecessor-query.d.ts.map +1 -0
- package/dist/runtime/predecessor-query.js +283 -0
- package/dist/runtime/predecessor-query.js.map +1 -0
- package/dist/runtime/session-manager.d.ts +466 -0
- package/dist/runtime/session-manager.d.ts.map +1 -0
- package/dist/runtime/session-manager.js +986 -0
- package/dist/runtime/session-manager.js.map +1 -0
- package/dist/runtime/spawner.d.ts +407 -0
- package/dist/runtime/spawner.d.ts.map +1 -0
- package/dist/runtime/spawner.js +781 -0
- package/dist/runtime/spawner.js.map +1 -0
- package/dist/server/config.d.ts +22 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +59 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/daemon-state.d.ts +50 -0
- package/dist/server/daemon-state.d.ts.map +1 -0
- package/dist/server/daemon-state.js +100 -0
- package/dist/server/daemon-state.js.map +1 -0
- package/dist/server/events-websocket.d.ts +32 -0
- package/dist/server/events-websocket.d.ts.map +1 -0
- package/dist/server/events-websocket.js +96 -0
- package/dist/server/events-websocket.js.map +1 -0
- package/dist/server/formatters.d.ts +94 -0
- package/dist/server/formatters.d.ts.map +1 -0
- package/dist/server/formatters.js +142 -0
- package/dist/server/formatters.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +153 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lsp-websocket.d.ts +33 -0
- package/dist/server/lsp-websocket.d.ts.map +1 -0
- package/dist/server/lsp-websocket.js +161 -0
- package/dist/server/lsp-websocket.js.map +1 -0
- package/dist/server/routes/agents.d.ts +9 -0
- package/dist/server/routes/agents.d.ts.map +1 -0
- package/dist/server/routes/agents.js +369 -0
- package/dist/server/routes/agents.js.map +1 -0
- package/dist/server/routes/daemon.d.ts +13 -0
- package/dist/server/routes/daemon.d.ts.map +1 -0
- package/dist/server/routes/daemon.js +187 -0
- package/dist/server/routes/daemon.js.map +1 -0
- package/dist/server/routes/events.d.ts +23 -0
- package/dist/server/routes/events.d.ts.map +1 -0
- package/dist/server/routes/events.js +282 -0
- package/dist/server/routes/events.js.map +1 -0
- package/dist/server/routes/extensions.d.ts +9 -0
- package/dist/server/routes/extensions.d.ts.map +1 -0
- package/dist/server/routes/extensions.js +202 -0
- package/dist/server/routes/extensions.js.map +1 -0
- package/dist/server/routes/health.d.ts +7 -0
- package/dist/server/routes/health.d.ts.map +1 -0
- package/dist/server/routes/health.js +33 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/index.d.ts +21 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +21 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/lsp.d.ts +9 -0
- package/dist/server/routes/lsp.d.ts.map +1 -0
- package/dist/server/routes/lsp.js +50 -0
- package/dist/server/routes/lsp.js.map +1 -0
- package/dist/server/routes/plugins.d.ts +9 -0
- package/dist/server/routes/plugins.d.ts.map +1 -0
- package/dist/server/routes/plugins.js +109 -0
- package/dist/server/routes/plugins.js.map +1 -0
- package/dist/server/routes/pools.d.ts +9 -0
- package/dist/server/routes/pools.d.ts.map +1 -0
- package/dist/server/routes/pools.js +189 -0
- package/dist/server/routes/pools.js.map +1 -0
- package/dist/server/routes/scheduler.d.ts +9 -0
- package/dist/server/routes/scheduler.d.ts.map +1 -0
- package/dist/server/routes/scheduler.js +162 -0
- package/dist/server/routes/scheduler.js.map +1 -0
- package/dist/server/routes/sessions.d.ts +27 -0
- package/dist/server/routes/sessions.d.ts.map +1 -0
- package/dist/server/routes/sessions.js +773 -0
- package/dist/server/routes/sessions.js.map +1 -0
- package/dist/server/routes/tasks.d.ts +9 -0
- package/dist/server/routes/tasks.d.ts.map +1 -0
- package/dist/server/routes/tasks.js +954 -0
- package/dist/server/routes/tasks.js.map +1 -0
- package/dist/server/routes/upload.d.ts +8 -0
- package/dist/server/routes/upload.d.ts.map +1 -0
- package/dist/server/routes/upload.js +40 -0
- package/dist/server/routes/upload.js.map +1 -0
- package/dist/server/routes/workflows.d.ts +9 -0
- package/dist/server/routes/workflows.d.ts.map +1 -0
- package/dist/server/routes/workflows.js +532 -0
- package/dist/server/routes/workflows.js.map +1 -0
- package/dist/server/routes/workspace-files.d.ts +12 -0
- package/dist/server/routes/workspace-files.d.ts.map +1 -0
- package/dist/server/routes/workspace-files.js +520 -0
- package/dist/server/routes/workspace-files.js.map +1 -0
- package/dist/server/routes/worktrees.d.ts +9 -0
- package/dist/server/routes/worktrees.d.ts.map +1 -0
- package/dist/server/routes/worktrees.js +94 -0
- package/dist/server/routes/worktrees.js.map +1 -0
- package/dist/server/server.d.ts +14 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +258 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/services/lsp-manager.d.ts +93 -0
- package/dist/server/services/lsp-manager.d.ts.map +1 -0
- package/dist/server/services/lsp-manager.js +291 -0
- package/dist/server/services/lsp-manager.js.map +1 -0
- package/dist/server/services/session-messages.d.ts +61 -0
- package/dist/server/services/session-messages.d.ts.map +1 -0
- package/dist/server/services/session-messages.js +101 -0
- package/dist/server/services/session-messages.js.map +1 -0
- package/dist/server/services.d.ts +35 -0
- package/dist/server/services.d.ts.map +1 -0
- package/dist/server/services.js +159 -0
- package/dist/server/services.js.map +1 -0
- package/dist/server/static.d.ts +18 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +71 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/types.d.ts +20 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +7 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/websocket.d.ts +16 -0
- package/dist/server/websocket.d.ts.map +1 -0
- package/dist/server/websocket.js +143 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/services/agent-pool-service.d.ts +181 -0
- package/dist/services/agent-pool-service.d.ts.map +1 -0
- package/dist/services/agent-pool-service.js +590 -0
- package/dist/services/agent-pool-service.js.map +1 -0
- package/dist/services/agent-registry.d.ts +185 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +432 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/dispatch-daemon.d.ts +429 -0
- package/dist/services/dispatch-daemon.d.ts.map +1 -0
- package/dist/services/dispatch-daemon.js +1833 -0
- package/dist/services/dispatch-daemon.js.map +1 -0
- package/dist/services/dispatch-service.d.ts +148 -0
- package/dist/services/dispatch-service.d.ts.map +1 -0
- package/dist/services/dispatch-service.js +170 -0
- package/dist/services/dispatch-service.js.map +1 -0
- package/dist/services/docs-steward-service.d.ts +199 -0
- package/dist/services/docs-steward-service.d.ts.map +1 -0
- package/dist/services/docs-steward-service.js +599 -0
- package/dist/services/docs-steward-service.js.map +1 -0
- package/dist/services/health-steward-service.d.ts +446 -0
- package/dist/services/health-steward-service.d.ts.map +1 -0
- package/dist/services/health-steward-service.js +866 -0
- package/dist/services/health-steward-service.js.map +1 -0
- package/dist/services/index.d.ts +26 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +111 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/merge-request-provider.d.ts +59 -0
- package/dist/services/merge-request-provider.d.ts.map +1 -0
- package/dist/services/merge-request-provider.js +89 -0
- package/dist/services/merge-request-provider.js.map +1 -0
- package/dist/services/merge-steward-service.d.ts +268 -0
- package/dist/services/merge-steward-service.d.ts.map +1 -0
- package/dist/services/merge-steward-service.js +568 -0
- package/dist/services/merge-steward-service.js.map +1 -0
- package/dist/services/plugin-executor.d.ts +247 -0
- package/dist/services/plugin-executor.d.ts.map +1 -0
- package/dist/services/plugin-executor.js +451 -0
- package/dist/services/plugin-executor.js.map +1 -0
- package/dist/services/role-definition-service.d.ts +117 -0
- package/dist/services/role-definition-service.d.ts.map +1 -0
- package/dist/services/role-definition-service.js +289 -0
- package/dist/services/role-definition-service.js.map +1 -0
- package/dist/services/steward-scheduler.d.ts +336 -0
- package/dist/services/steward-scheduler.d.ts.map +1 -0
- package/dist/services/steward-scheduler.js +732 -0
- package/dist/services/steward-scheduler.js.map +1 -0
- package/dist/services/task-assignment-service.d.ts +291 -0
- package/dist/services/task-assignment-service.d.ts.map +1 -0
- package/dist/services/task-assignment-service.js +454 -0
- package/dist/services/task-assignment-service.js.map +1 -0
- package/dist/services/worker-task-service.d.ts +202 -0
- package/dist/services/worker-task-service.d.ts.map +1 -0
- package/dist/services/worker-task-service.js +228 -0
- package/dist/services/worker-task-service.js.map +1 -0
- package/dist/testing/index.d.ts +13 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/orchestration-tests.d.ts +62 -0
- package/dist/testing/orchestration-tests.d.ts.map +1 -0
- package/dist/testing/orchestration-tests.js +1115 -0
- package/dist/testing/orchestration-tests.js.map +1 -0
- package/dist/testing/test-context.d.ts +171 -0
- package/dist/testing/test-context.d.ts.map +1 -0
- package/dist/testing/test-context.js +665 -0
- package/dist/testing/test-context.js.map +1 -0
- package/dist/testing/test-prompts.d.ts +46 -0
- package/dist/testing/test-prompts.d.ts.map +1 -0
- package/dist/testing/test-prompts.js +140 -0
- package/dist/testing/test-prompts.js.map +1 -0
- package/dist/testing/test-utils.d.ts +200 -0
- package/dist/testing/test-utils.d.ts.map +1 -0
- package/dist/testing/test-utils.js +378 -0
- package/dist/testing/test-utils.js.map +1 -0
- package/dist/types/agent-pool.d.ts +215 -0
- package/dist/types/agent-pool.d.ts.map +1 -0
- package/dist/types/agent-pool.js +143 -0
- package/dist/types/agent-pool.js.map +1 -0
- package/dist/types/agent.d.ts +265 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +127 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +40 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message-types.d.ts +294 -0
- package/dist/types/message-types.d.ts.map +1 -0
- package/dist/types/message-types.js +354 -0
- package/dist/types/message-types.js.map +1 -0
- package/dist/types/role-definition.d.ts +272 -0
- package/dist/types/role-definition.d.ts.map +1 -0
- package/dist/types/role-definition.js +144 -0
- package/dist/types/role-definition.js.map +1 -0
- package/dist/types/task-meta.d.ts +248 -0
- package/dist/types/task-meta.d.ts.map +1 -0
- package/dist/types/task-meta.js +213 -0
- package/dist/types/task-meta.js.map +1 -0
- package/package.json +120 -0
- package/web/assets/abap-BrgZPUOV.js +6 -0
- package/web/assets/apex-DyP6w7ZV.js +6 -0
- package/web/assets/azcli-BaLxmfj-.js +6 -0
- package/web/assets/bat-CFOPXBzS.js +6 -0
- package/web/assets/bicep-BfEKNvv3.js +7 -0
- package/web/assets/cameligo-BFG1Mk7z.js +6 -0
- package/web/assets/clojure-DTECt2xU.js +6 -0
- package/web/assets/codicon-DCmgc-ay.ttf +0 -0
- package/web/assets/coffee-CDGzqUPQ.js +6 -0
- package/web/assets/cpp-CLLBncYj.js +6 -0
- package/web/assets/csharp-dUCx_-0o.js +6 -0
- package/web/assets/csp-5Rap-vPy.js +6 -0
- package/web/assets/css-D3h14YRZ.js +8 -0
- package/web/assets/cssMode-DMo-5YLA.js +9 -0
- package/web/assets/cypher-DrQuvNYM.js +6 -0
- package/web/assets/dart-CFKIUWau.js +6 -0
- package/web/assets/dockerfile-Zznr-cwX.js +6 -0
- package/web/assets/ecl-Ce3n6wWz.js +6 -0
- package/web/assets/elixir-deUWdS0T.js +6 -0
- package/web/assets/flow9-i9-g7ZhI.js +6 -0
- package/web/assets/freemarker2-D4qgkQzN.js +8 -0
- package/web/assets/fsharp-CzKuDChf.js +6 -0
- package/web/assets/go-Cphgjts3.js +6 -0
- package/web/assets/graphql-Cg7bfA9N.js +6 -0
- package/web/assets/handlebars-CXFvNjQC.js +6 -0
- package/web/assets/hcl-0cvrggvQ.js +6 -0
- package/web/assets/html-oyuB_D-B.js +6 -0
- package/web/assets/htmlMode-iWuZ24-r.js +9 -0
- package/web/assets/index-DqP-_E4F.css +32 -0
- package/web/assets/index-R1cylSgw.js +1665 -0
- package/web/assets/ini-Drc7WvVn.js +6 -0
- package/web/assets/java-B_fMsGYe.js +6 -0
- package/web/assets/javascript-CRIkN2Pg.js +6 -0
- package/web/assets/jsonMode-DVDkDgex.js +15 -0
- package/web/assets/julia-Bqgm2twL.js +6 -0
- package/web/assets/kotlin-BSkB5QuD.js +6 -0
- package/web/assets/less-BsTHnhdd.js +7 -0
- package/web/assets/lexon-YWi4-JPR.js +6 -0
- package/web/assets/liquid-CSfldbB5.js +6 -0
- package/web/assets/lua-nf6ki56Z.js +6 -0
- package/web/assets/m3-Cpb6xl2v.js +6 -0
- package/web/assets/markdown-DSZPf7rp.js +6 -0
- package/web/assets/mdx-Dd58iymR.js +6 -0
- package/web/assets/mips-B_c3zf-v.js +6 -0
- package/web/assets/monaco-editor-B4lwqA13.js +751 -0
- package/web/assets/monaco-editor-CQpyCxOA.css +1 -0
- package/web/assets/msdax-rUNN04Wq.js +6 -0
- package/web/assets/mysql-DDwshQtU.js +6 -0
- package/web/assets/objective-c-B5zXfXm9.js +6 -0
- package/web/assets/pascal-CXOwvkN_.js +6 -0
- package/web/assets/pascaligo-Bc-ZgV77.js +6 -0
- package/web/assets/perl-CwNk8-XU.js +6 -0
- package/web/assets/pgsql-tGk8EFnU.js +6 -0
- package/web/assets/php-CpIb_Oan.js +6 -0
- package/web/assets/pla-B03wrqEc.js +6 -0
- package/web/assets/postiats-BKlk5iyT.js +6 -0
- package/web/assets/powerquery-Bhzvs7bI.js +6 -0
- package/web/assets/powershell-Dd3NCNK9.js +6 -0
- package/web/assets/protobuf-COyEY5Pt.js +7 -0
- package/web/assets/pug-BaJupSGV.js +6 -0
- package/web/assets/python-XWrMqdhO.js +6 -0
- package/web/assets/qsharp-DXyYeYxl.js +6 -0
- package/web/assets/r-CdQndTaG.js +6 -0
- package/web/assets/razor-DPlhCpIs.js +6 -0
- package/web/assets/redis-CVwtpugi.js +6 -0
- package/web/assets/redshift-25W9uPmb.js +6 -0
- package/web/assets/restructuredtext-DfzH4Xui.js +6 -0
- package/web/assets/router-vendor-DHlGizSU.js +41 -0
- package/web/assets/ruby-Cp1zYvxS.js +6 -0
- package/web/assets/rust-D5C2fndG.js +6 -0
- package/web/assets/sb-CDntyWJ8.js +6 -0
- package/web/assets/scala-BoFRg7Ot.js +6 -0
- package/web/assets/scheme-Bio4gycK.js +6 -0
- package/web/assets/scss-4Ik7cdeQ.js +8 -0
- package/web/assets/shell-CX-rkNHf.js +6 -0
- package/web/assets/solidity-Tw7wswEv.js +6 -0
- package/web/assets/sophia-C5WLch3f.js +6 -0
- package/web/assets/sparql-DHaeiCBh.js +6 -0
- package/web/assets/sql-CCSDG5nI.js +6 -0
- package/web/assets/st-pnP8ivHi.js +6 -0
- package/web/assets/swift-DwJ7jVG9.js +8 -0
- package/web/assets/systemverilog-B9Xyijhd.js +6 -0
- package/web/assets/tcl-DnHyzjbg.js +6 -0
- package/web/assets/tsMode-BbA1Jbf3.js +16 -0
- package/web/assets/twig-CPajHgWi.js +6 -0
- package/web/assets/typescript-DcLHYzvH.js +6 -0
- package/web/assets/typespec-D-MeaMDU.js +6 -0
- package/web/assets/ui-vendor-BSco96uv.js +51 -0
- package/web/assets/utils-vendor-DaJ2Dubl.js +911 -0
- package/web/assets/vb-DgyLZaXg.js +6 -0
- package/web/assets/wgsl-DYQUnd45.js +303 -0
- package/web/assets/xml-xKYS3dO6.js +6 -0
- package/web/assets/yaml-CNmlXqzH.js +6 -0
- package/web/favicon.ico +0 -0
- package/web/index.html +22 -0
- package/web/logo.png +0 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawner Service
|
|
3
|
+
*
|
|
4
|
+
* This service manages spawning and lifecycle of agent processes
|
|
5
|
+
* for AI agents in the orchestration system.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Spawn headless agents (ephemeral workers, stewards) via provider abstraction
|
|
9
|
+
* - Spawn interactive agents (Director, persistent workers) via provider abstraction
|
|
10
|
+
* - Resume existing sessions with provider session IDs
|
|
11
|
+
* - Parse stream-json events (assistant, tool_use, tool_result, error)
|
|
12
|
+
* - Track session metadata for cross-restart resumption
|
|
13
|
+
* - Provider-agnostic: supports Claude Code, OpenCode, and future providers
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from 'node:events';
|
|
18
|
+
import { createTimestamp } from '@stoneforge/core';
|
|
19
|
+
import { ClaudeAgentProvider } from '../providers/claude/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Shell-quotes a string for safe inclusion in a bash command.
|
|
22
|
+
* Wraps in single quotes and escapes internal single quotes.
|
|
23
|
+
*/
|
|
24
|
+
export function shellQuote(s) {
|
|
25
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Session state machine transitions
|
|
29
|
+
*/
|
|
30
|
+
export const SessionStatusTransitions = {
|
|
31
|
+
starting: ['running', 'terminated'],
|
|
32
|
+
running: ['suspended', 'terminating', 'terminated'],
|
|
33
|
+
suspended: ['running', 'terminated'],
|
|
34
|
+
terminating: ['terminated'],
|
|
35
|
+
terminated: [],
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Builds the CLI arguments for headless (non-interactive) Claude Code spawning.
|
|
39
|
+
*
|
|
40
|
+
* This function is exported to allow unit testing of the argument construction,
|
|
41
|
+
* which is critical for ensuring Claude Code is invoked correctly.
|
|
42
|
+
*
|
|
43
|
+
* @param options - Options affecting argument construction
|
|
44
|
+
* @returns Array of CLI arguments
|
|
45
|
+
*/
|
|
46
|
+
export function buildHeadlessArgs(options) {
|
|
47
|
+
const args = [
|
|
48
|
+
'-p', // Print mode (non-interactive)
|
|
49
|
+
'--verbose', // Required for stream-json output in print mode
|
|
50
|
+
'--dangerously-skip-permissions',
|
|
51
|
+
'--output-format', 'stream-json',
|
|
52
|
+
'--input-format', 'stream-json',
|
|
53
|
+
];
|
|
54
|
+
if (options?.resumeSessionId) {
|
|
55
|
+
args.push('--resume', options.resumeSessionId);
|
|
56
|
+
}
|
|
57
|
+
// Note: initialPrompt is NOT added as a CLI argument when using --input-format stream-json.
|
|
58
|
+
// Instead, it must be sent via stdin in JSON format after the process starts.
|
|
59
|
+
// This is handled in spawnHeadless().
|
|
60
|
+
return args;
|
|
61
|
+
}
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Spawner Service Implementation
|
|
64
|
+
// ============================================================================
|
|
65
|
+
/**
|
|
66
|
+
* Implementation of the Spawner Service.
|
|
67
|
+
* Uses the provider abstraction to spawn and manage agent processes.
|
|
68
|
+
*/
|
|
69
|
+
export class SpawnerServiceImpl {
|
|
70
|
+
sessions = new Map();
|
|
71
|
+
defaultConfig;
|
|
72
|
+
provider;
|
|
73
|
+
sessionCounter = 0;
|
|
74
|
+
constructor(config) {
|
|
75
|
+
this.defaultConfig = {
|
|
76
|
+
claudePath: config?.claudePath ?? 'claude',
|
|
77
|
+
workingDirectory: config?.workingDirectory ?? process.cwd(),
|
|
78
|
+
timeout: config?.timeout ?? 120000, // 2 minutes default
|
|
79
|
+
stoneforgeRoot: config?.stoneforgeRoot,
|
|
80
|
+
environmentVariables: config?.environmentVariables,
|
|
81
|
+
};
|
|
82
|
+
// Use injected provider or default to Claude
|
|
83
|
+
this.provider = config?.provider ?? new ClaudeAgentProvider(config?.claudePath ?? 'claude');
|
|
84
|
+
}
|
|
85
|
+
// ----------------------------------------
|
|
86
|
+
// Session Lifecycle
|
|
87
|
+
// ----------------------------------------
|
|
88
|
+
async spawn(agentId, agentRole, options) {
|
|
89
|
+
const mode = this.determineSpawnMode(agentRole, options?.mode);
|
|
90
|
+
const now = createTimestamp();
|
|
91
|
+
const sessionId = this.generateSessionId();
|
|
92
|
+
// Create session record
|
|
93
|
+
const session = {
|
|
94
|
+
id: sessionId,
|
|
95
|
+
agentId,
|
|
96
|
+
agentRole,
|
|
97
|
+
workerMode: this.getWorkerMode(agentRole, options?.mode),
|
|
98
|
+
mode,
|
|
99
|
+
status: 'starting',
|
|
100
|
+
workingDirectory: options?.workingDirectory ?? this.defaultConfig.workingDirectory,
|
|
101
|
+
createdAt: now,
|
|
102
|
+
lastActivityAt: now,
|
|
103
|
+
events: new EventEmitter(),
|
|
104
|
+
jsonBuffer: '',
|
|
105
|
+
};
|
|
106
|
+
this.sessions.set(sessionId, session);
|
|
107
|
+
try {
|
|
108
|
+
if (mode === 'headless') {
|
|
109
|
+
await this.spawnHeadless(session, options);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await this.spawnInteractive(session, options);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
session: this.toPublicSession(session),
|
|
116
|
+
events: session.events,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// Clean up on failure
|
|
121
|
+
session.status = 'terminated';
|
|
122
|
+
session.endedAt = createTimestamp();
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async terminate(sessionId, graceful = true) {
|
|
127
|
+
const session = this.sessions.get(sessionId);
|
|
128
|
+
if (!session) {
|
|
129
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
130
|
+
}
|
|
131
|
+
if (session.status === 'terminated' || session.status === 'terminating') {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.transitionStatus(session, 'terminating');
|
|
135
|
+
// Handle provider interactive sessions
|
|
136
|
+
if (session.interactiveSession) {
|
|
137
|
+
if (graceful) {
|
|
138
|
+
session.interactiveSession.write('exit\r');
|
|
139
|
+
await new Promise((resolve) => {
|
|
140
|
+
const timeout = setTimeout(() => {
|
|
141
|
+
clearInterval(checkInterval);
|
|
142
|
+
if (session.interactiveSession) {
|
|
143
|
+
session.interactiveSession.kill();
|
|
144
|
+
}
|
|
145
|
+
resolve();
|
|
146
|
+
}, 5000);
|
|
147
|
+
const checkInterval = setInterval(() => {
|
|
148
|
+
if (session.status === 'terminated') {
|
|
149
|
+
clearInterval(checkInterval);
|
|
150
|
+
clearTimeout(timeout);
|
|
151
|
+
resolve();
|
|
152
|
+
}
|
|
153
|
+
}, 100);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
session.interactiveSession.kill();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Handle provider headless sessions
|
|
161
|
+
if (session.headlessSession) {
|
|
162
|
+
session.headlessSession.close();
|
|
163
|
+
}
|
|
164
|
+
// Handle headless process sessions (legacy)
|
|
165
|
+
if (session.process) {
|
|
166
|
+
if (graceful) {
|
|
167
|
+
session.process.kill('SIGTERM');
|
|
168
|
+
await new Promise((resolve) => {
|
|
169
|
+
const timeout = setTimeout(() => {
|
|
170
|
+
if (session.process && !session.process.killed) {
|
|
171
|
+
session.process.kill('SIGKILL');
|
|
172
|
+
}
|
|
173
|
+
resolve();
|
|
174
|
+
}, 5000);
|
|
175
|
+
session.process?.once('exit', () => {
|
|
176
|
+
clearTimeout(timeout);
|
|
177
|
+
resolve();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
session.process.kill('SIGKILL');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Only transition if not already terminated
|
|
186
|
+
if (session.status !== 'terminated') {
|
|
187
|
+
this.transitionStatus(session, 'terminated');
|
|
188
|
+
session.endedAt = createTimestamp();
|
|
189
|
+
}
|
|
190
|
+
this.scheduleTerminatedSessionCleanup(sessionId);
|
|
191
|
+
}
|
|
192
|
+
async suspend(sessionId) {
|
|
193
|
+
const session = this.sessions.get(sessionId);
|
|
194
|
+
if (!session) {
|
|
195
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
196
|
+
}
|
|
197
|
+
if (session.status !== 'running') {
|
|
198
|
+
throw new Error(`Cannot suspend session in status: ${session.status}`);
|
|
199
|
+
}
|
|
200
|
+
// For headless, close the session but mark as suspended
|
|
201
|
+
// The providerSessionId can be used to resume later
|
|
202
|
+
if (session.headlessSession) {
|
|
203
|
+
session.headlessSession.close();
|
|
204
|
+
}
|
|
205
|
+
if (session.process) {
|
|
206
|
+
session.process.kill('SIGTERM');
|
|
207
|
+
}
|
|
208
|
+
// For interactive, kill the PTY but mark as suspended
|
|
209
|
+
if (session.interactiveSession) {
|
|
210
|
+
session.interactiveSession.kill();
|
|
211
|
+
}
|
|
212
|
+
this.transitionStatus(session, 'suspended');
|
|
213
|
+
session.lastActivityAt = createTimestamp();
|
|
214
|
+
}
|
|
215
|
+
async interrupt(sessionId) {
|
|
216
|
+
const session = this.sessions.get(sessionId);
|
|
217
|
+
if (!session) {
|
|
218
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
219
|
+
}
|
|
220
|
+
if (session.status !== 'running') {
|
|
221
|
+
throw new Error(`Cannot interrupt session in status: ${session.status}`);
|
|
222
|
+
}
|
|
223
|
+
// For provider headless sessions, use interrupt
|
|
224
|
+
if (session.headlessSession) {
|
|
225
|
+
await session.headlessSession.interrupt();
|
|
226
|
+
}
|
|
227
|
+
// For legacy process-based headless sessions, send SIGINT
|
|
228
|
+
else if (session.process) {
|
|
229
|
+
session.process.kill('SIGINT');
|
|
230
|
+
}
|
|
231
|
+
// For interactive PTY sessions, send Escape key
|
|
232
|
+
if (session.interactiveSession) {
|
|
233
|
+
session.interactiveSession.write('\x1b');
|
|
234
|
+
}
|
|
235
|
+
session.lastActivityAt = createTimestamp();
|
|
236
|
+
}
|
|
237
|
+
// ----------------------------------------
|
|
238
|
+
// Session Queries
|
|
239
|
+
// ----------------------------------------
|
|
240
|
+
getSession(sessionId) {
|
|
241
|
+
const session = this.sessions.get(sessionId);
|
|
242
|
+
return session ? this.toPublicSession(session) : undefined;
|
|
243
|
+
}
|
|
244
|
+
listActiveSessions(agentId) {
|
|
245
|
+
const sessions = Array.from(this.sessions.values())
|
|
246
|
+
.filter((s) => s.status !== 'terminated');
|
|
247
|
+
return this.filterAndMapSessions(sessions, agentId);
|
|
248
|
+
}
|
|
249
|
+
listAllSessions(agentId) {
|
|
250
|
+
const sessions = Array.from(this.sessions.values());
|
|
251
|
+
return this.filterAndMapSessions(sessions, agentId);
|
|
252
|
+
}
|
|
253
|
+
getMostRecentSession(agentId) {
|
|
254
|
+
const agentSessions = Array.from(this.sessions.values())
|
|
255
|
+
.filter((s) => s.agentId === agentId)
|
|
256
|
+
.sort((a, b) => {
|
|
257
|
+
const aTime = typeof a.createdAt === 'number' ? a.createdAt : new Date(a.createdAt).getTime();
|
|
258
|
+
const bTime = typeof b.createdAt === 'number' ? b.createdAt : new Date(b.createdAt).getTime();
|
|
259
|
+
return bTime - aTime;
|
|
260
|
+
});
|
|
261
|
+
return agentSessions.length > 0 ? this.toPublicSession(agentSessions[0]) : undefined;
|
|
262
|
+
}
|
|
263
|
+
// ----------------------------------------
|
|
264
|
+
// Headless Agent Communication
|
|
265
|
+
// ----------------------------------------
|
|
266
|
+
async sendInput(sessionId, input, _options) {
|
|
267
|
+
const session = this.sessions.get(sessionId);
|
|
268
|
+
if (!session) {
|
|
269
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
270
|
+
}
|
|
271
|
+
if (session.mode !== 'headless') {
|
|
272
|
+
throw new Error('sendInput is only supported for headless sessions');
|
|
273
|
+
}
|
|
274
|
+
if (session.status !== 'running') {
|
|
275
|
+
throw new Error(`Cannot send input to session in status: ${session.status}`);
|
|
276
|
+
}
|
|
277
|
+
// For provider headless sessions, use sendMessage
|
|
278
|
+
if (session.headlessSession) {
|
|
279
|
+
session.headlessSession.sendMessage(input);
|
|
280
|
+
session.lastActivityAt = createTimestamp();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// For legacy process-based sessions, write to stdin
|
|
284
|
+
if (!session.process?.stdin?.writable) {
|
|
285
|
+
throw new Error('Session stdin is not writable');
|
|
286
|
+
}
|
|
287
|
+
const message = {
|
|
288
|
+
type: 'user',
|
|
289
|
+
message: {
|
|
290
|
+
role: 'user',
|
|
291
|
+
content: input,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
session.process.stdin.write(JSON.stringify(message) + '\n');
|
|
295
|
+
session.lastActivityAt = createTimestamp();
|
|
296
|
+
}
|
|
297
|
+
// ----------------------------------------
|
|
298
|
+
// Interactive PTY Communication
|
|
299
|
+
// ----------------------------------------
|
|
300
|
+
async writeToPty(sessionId, data) {
|
|
301
|
+
const session = this.sessions.get(sessionId);
|
|
302
|
+
if (!session) {
|
|
303
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
304
|
+
}
|
|
305
|
+
if (session.mode !== 'interactive') {
|
|
306
|
+
throw new Error('writeToPty is only supported for interactive sessions');
|
|
307
|
+
}
|
|
308
|
+
if (session.status !== 'running') {
|
|
309
|
+
throw new Error(`Cannot write to PTY in status: ${session.status}`);
|
|
310
|
+
}
|
|
311
|
+
if (!session.interactiveSession) {
|
|
312
|
+
throw new Error('Session PTY is not available');
|
|
313
|
+
}
|
|
314
|
+
session.interactiveSession.write(data);
|
|
315
|
+
session.lastActivityAt = createTimestamp();
|
|
316
|
+
}
|
|
317
|
+
async resize(sessionId, cols, rows) {
|
|
318
|
+
const session = this.sessions.get(sessionId);
|
|
319
|
+
if (!session) {
|
|
320
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
321
|
+
}
|
|
322
|
+
if (session.mode !== 'interactive') {
|
|
323
|
+
throw new Error('resize is only supported for interactive sessions');
|
|
324
|
+
}
|
|
325
|
+
if (!session.interactiveSession) {
|
|
326
|
+
throw new Error('Session PTY is not available');
|
|
327
|
+
}
|
|
328
|
+
if (session.status !== 'running') {
|
|
329
|
+
throw new Error(`Cannot resize session in ${session.status} state`);
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
session.interactiveSession.resize(cols, rows);
|
|
333
|
+
session.cols = cols;
|
|
334
|
+
session.rows = rows;
|
|
335
|
+
session.lastActivityAt = createTimestamp();
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
339
|
+
if (errorMessage.includes('EBADF') || errorMessage.includes('ioctl')) {
|
|
340
|
+
console.warn(`[spawner] Resize failed for session ${sessionId} (PTY may be closed): ${errorMessage}`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// ----------------------------------------
|
|
347
|
+
// Event Subscription
|
|
348
|
+
// ----------------------------------------
|
|
349
|
+
getEventEmitter(sessionId) {
|
|
350
|
+
const session = this.sessions.get(sessionId);
|
|
351
|
+
return session?.events;
|
|
352
|
+
}
|
|
353
|
+
// ----------------------------------------
|
|
354
|
+
// Universal Work Principle (UWP)
|
|
355
|
+
// ----------------------------------------
|
|
356
|
+
async checkReadyQueue(agentId, options) {
|
|
357
|
+
const limit = options?.limit ?? 1;
|
|
358
|
+
if (!options?.getReadyTasks) {
|
|
359
|
+
return {
|
|
360
|
+
hasReadyTask: false,
|
|
361
|
+
autoStarted: false,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const readyTasks = await options.getReadyTasks(agentId, limit);
|
|
365
|
+
if (readyTasks.length === 0) {
|
|
366
|
+
return {
|
|
367
|
+
hasReadyTask: false,
|
|
368
|
+
autoStarted: false,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const task = readyTasks[0];
|
|
372
|
+
const result = {
|
|
373
|
+
hasReadyTask: true,
|
|
374
|
+
taskId: task.id,
|
|
375
|
+
taskTitle: task.title,
|
|
376
|
+
taskPriority: task.priority,
|
|
377
|
+
autoStarted: false,
|
|
378
|
+
};
|
|
379
|
+
if (options.autoStart) {
|
|
380
|
+
result.autoStarted = true;
|
|
381
|
+
}
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
// ----------------------------------------
|
|
385
|
+
// Private Helpers - Spawning
|
|
386
|
+
// ----------------------------------------
|
|
387
|
+
async spawnHeadless(session, options) {
|
|
388
|
+
const headlessProvider = options?.provider?.headless ?? this.provider.headless;
|
|
389
|
+
try {
|
|
390
|
+
const headlessSession = await headlessProvider.spawn({
|
|
391
|
+
workingDirectory: session.workingDirectory,
|
|
392
|
+
initialPrompt: options?.initialPrompt,
|
|
393
|
+
resumeSessionId: options?.resumeSessionId,
|
|
394
|
+
environmentVariables: {
|
|
395
|
+
...this.defaultConfig.environmentVariables,
|
|
396
|
+
...options?.environmentVariables,
|
|
397
|
+
},
|
|
398
|
+
stoneforgeRoot: options?.stoneforgeRoot ?? this.defaultConfig.stoneforgeRoot,
|
|
399
|
+
timeout: options?.timeout ?? this.defaultConfig.timeout,
|
|
400
|
+
model: options?.model,
|
|
401
|
+
});
|
|
402
|
+
session.headlessSession = headlessSession;
|
|
403
|
+
// Process messages from the provider in the background
|
|
404
|
+
this.processProviderMessages(session, headlessSession);
|
|
405
|
+
// Wait for the init event to get the provider session ID
|
|
406
|
+
await this.waitForInit(session, options?.timeout ?? this.defaultConfig.timeout);
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
this.transitionStatus(session, 'terminated');
|
|
410
|
+
session.endedAt = createTimestamp();
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Process provider messages and emit them as session events.
|
|
416
|
+
*/
|
|
417
|
+
async processProviderMessages(session, headlessSession) {
|
|
418
|
+
let resumeErrorDetected = false;
|
|
419
|
+
try {
|
|
420
|
+
for await (const message of headlessSession) {
|
|
421
|
+
if (session.status === 'terminated') {
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
// Check for resume failure
|
|
425
|
+
if (message.type === 'result' && message.subtype === 'error_during_execution') {
|
|
426
|
+
const raw = message.raw;
|
|
427
|
+
const errors = raw?.errors || [];
|
|
428
|
+
const sessionNotFoundError = errors.find((e) => e.includes('No conversation found with session ID'));
|
|
429
|
+
if (sessionNotFoundError) {
|
|
430
|
+
resumeErrorDetected = true;
|
|
431
|
+
session.events.emit('resume_failed', {
|
|
432
|
+
reason: 'session_not_found',
|
|
433
|
+
message: sessionNotFoundError,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// Convert AgentMessage to SpawnedSessionEvent
|
|
438
|
+
const event = this.convertAgentMessageToEvent(message);
|
|
439
|
+
if (event) {
|
|
440
|
+
session.events.emit('event', event);
|
|
441
|
+
// Extract provider session ID from system init message
|
|
442
|
+
if (message.type === 'system' && message.subtype === 'init' && message.sessionId) {
|
|
443
|
+
session.providerSessionId = message.sessionId;
|
|
444
|
+
session.events.emit('provider-session-id', message.sessionId);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
451
|
+
session.events.emit('interrupt');
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (!resumeErrorDetected) {
|
|
455
|
+
session.events.emit('error', error);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
if (session.status !== 'suspended' && session.status !== 'terminated') {
|
|
460
|
+
this.transitionStatus(session, 'terminated');
|
|
461
|
+
}
|
|
462
|
+
if (!session.endedAt) {
|
|
463
|
+
session.endedAt = createTimestamp();
|
|
464
|
+
}
|
|
465
|
+
session.events.emit('exit', resumeErrorDetected ? 1 : 0, null);
|
|
466
|
+
if (session.status === 'terminated') {
|
|
467
|
+
this.scheduleTerminatedSessionCleanup(session.id);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Convert a provider AgentMessage to a SpawnedSessionEvent.
|
|
473
|
+
*/
|
|
474
|
+
convertAgentMessageToEvent(message) {
|
|
475
|
+
const receivedAt = createTimestamp();
|
|
476
|
+
// Build a raw event from the AgentMessage
|
|
477
|
+
const raw = {
|
|
478
|
+
type: message.type,
|
|
479
|
+
subtype: message.subtype,
|
|
480
|
+
session_id: message.sessionId,
|
|
481
|
+
message: message.content,
|
|
482
|
+
...(message.tool ? {
|
|
483
|
+
tool: message.tool.name,
|
|
484
|
+
tool_use_id: message.tool.id,
|
|
485
|
+
tool_input: message.tool.input,
|
|
486
|
+
} : {}),
|
|
487
|
+
};
|
|
488
|
+
return {
|
|
489
|
+
type: message.type,
|
|
490
|
+
subtype: message.subtype,
|
|
491
|
+
receivedAt,
|
|
492
|
+
raw,
|
|
493
|
+
message: message.content,
|
|
494
|
+
tool: message.tool,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
async spawnInteractive(session, options) {
|
|
498
|
+
const interactiveProvider = options?.provider?.interactive ?? this.provider.interactive;
|
|
499
|
+
try {
|
|
500
|
+
const interactiveSession = await interactiveProvider.spawn({
|
|
501
|
+
workingDirectory: session.workingDirectory,
|
|
502
|
+
initialPrompt: options?.initialPrompt,
|
|
503
|
+
resumeSessionId: options?.resumeSessionId,
|
|
504
|
+
environmentVariables: {
|
|
505
|
+
...this.defaultConfig.environmentVariables,
|
|
506
|
+
...options?.environmentVariables,
|
|
507
|
+
},
|
|
508
|
+
stoneforgeRoot: options?.stoneforgeRoot ?? this.defaultConfig.stoneforgeRoot,
|
|
509
|
+
cols: options?.cols,
|
|
510
|
+
rows: options?.rows,
|
|
511
|
+
model: options?.model,
|
|
512
|
+
});
|
|
513
|
+
session.interactiveSession = interactiveSession;
|
|
514
|
+
session.cols = options?.cols ?? 80;
|
|
515
|
+
session.rows = options?.rows ?? 24;
|
|
516
|
+
session.pid = interactiveSession.pid;
|
|
517
|
+
// Set providerSessionId immediately if the provider knows it upfront
|
|
518
|
+
// (e.g., Claude provider generates a UUID before spawning)
|
|
519
|
+
const knownSessionId = interactiveSession.getSessionId();
|
|
520
|
+
if (knownSessionId) {
|
|
521
|
+
session.providerSessionId = knownSessionId;
|
|
522
|
+
}
|
|
523
|
+
// Handle data output
|
|
524
|
+
interactiveSession.onData((data) => {
|
|
525
|
+
session.lastActivityAt = createTimestamp();
|
|
526
|
+
session.events.emit('pty-data', data);
|
|
527
|
+
// Check for provider session ID in output
|
|
528
|
+
if (!session.providerSessionId) {
|
|
529
|
+
const detectedId = interactiveSession.getSessionId();
|
|
530
|
+
if (detectedId) {
|
|
531
|
+
session.providerSessionId = detectedId;
|
|
532
|
+
session.events.emit('provider-session-id', detectedId);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
// Handle exit
|
|
537
|
+
interactiveSession.onExit((code, signal) => {
|
|
538
|
+
if (session.status !== 'suspended' && session.status !== 'terminated') {
|
|
539
|
+
this.transitionStatus(session, 'terminated');
|
|
540
|
+
}
|
|
541
|
+
if (!session.endedAt) {
|
|
542
|
+
session.endedAt = createTimestamp();
|
|
543
|
+
}
|
|
544
|
+
session.events.emit('exit', code, signal);
|
|
545
|
+
if (session.status === 'terminated') {
|
|
546
|
+
this.scheduleTerminatedSessionCleanup(session.id);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
// Transition to running state
|
|
550
|
+
this.transitionStatus(session, 'running');
|
|
551
|
+
session.startedAt = createTimestamp();
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
this.transitionStatus(session, 'terminated');
|
|
555
|
+
session.endedAt = createTimestamp();
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async waitForInit(session, timeout) {
|
|
560
|
+
return new Promise((resolve, reject) => {
|
|
561
|
+
const timer = setTimeout(() => {
|
|
562
|
+
reject(new Error(`Timeout waiting for agent init (${timeout}ms)`));
|
|
563
|
+
}, timeout);
|
|
564
|
+
const onEvent = (event) => {
|
|
565
|
+
if (event.type === 'system' && event.subtype === 'init') {
|
|
566
|
+
clearTimeout(timer);
|
|
567
|
+
session.events.off('event', onEvent);
|
|
568
|
+
// Extract provider session ID from init event
|
|
569
|
+
if (event.raw.session_id) {
|
|
570
|
+
session.providerSessionId = event.raw.session_id;
|
|
571
|
+
session.events.emit('provider-session-id', event.raw.session_id);
|
|
572
|
+
}
|
|
573
|
+
this.transitionStatus(session, 'running');
|
|
574
|
+
session.startedAt = createTimestamp();
|
|
575
|
+
resolve();
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
session.events.on('event', onEvent);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
// ----------------------------------------
|
|
582
|
+
// Private Helpers - Output Parsing (legacy)
|
|
583
|
+
// ----------------------------------------
|
|
584
|
+
handleHeadlessOutput(session, data) {
|
|
585
|
+
const text = data.toString();
|
|
586
|
+
session.jsonBuffer += text;
|
|
587
|
+
session.lastActivityAt = createTimestamp();
|
|
588
|
+
const lines = session.jsonBuffer.split('\n');
|
|
589
|
+
session.jsonBuffer = lines.pop() ?? '';
|
|
590
|
+
for (const line of lines) {
|
|
591
|
+
if (line.trim()) {
|
|
592
|
+
this.parseAndEmitEvent(session, line);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
parseAndEmitEvent(session, line) {
|
|
597
|
+
try {
|
|
598
|
+
const parsed = JSON.parse(line);
|
|
599
|
+
const rawEvent = parsed;
|
|
600
|
+
const parsedMessage = parsed.message;
|
|
601
|
+
let message;
|
|
602
|
+
let toolFromContent;
|
|
603
|
+
let effectiveType = rawEvent.type;
|
|
604
|
+
const rawContent = parsedMessage?.content ?? parsed.message ?? parsed.content ?? parsed.result;
|
|
605
|
+
if (typeof rawContent === 'string') {
|
|
606
|
+
message = rawContent;
|
|
607
|
+
}
|
|
608
|
+
else if (Array.isArray(rawContent)) {
|
|
609
|
+
const textBlocks = [];
|
|
610
|
+
for (const item of rawContent) {
|
|
611
|
+
if (typeof item === 'object' && item !== null && 'type' in item) {
|
|
612
|
+
const block = item;
|
|
613
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
614
|
+
textBlocks.push(block.text);
|
|
615
|
+
}
|
|
616
|
+
else if (block.type === 'tool_use' && block.name) {
|
|
617
|
+
toolFromContent = {
|
|
618
|
+
name: block.name,
|
|
619
|
+
id: block.id,
|
|
620
|
+
input: block.input,
|
|
621
|
+
};
|
|
622
|
+
effectiveType = 'tool_use';
|
|
623
|
+
}
|
|
624
|
+
else if (block.type === 'tool_result') {
|
|
625
|
+
toolFromContent = {
|
|
626
|
+
id: block.tool_use_id,
|
|
627
|
+
};
|
|
628
|
+
message = typeof block.content === 'string' ? block.content : undefined;
|
|
629
|
+
effectiveType = 'tool_result';
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (textBlocks.length > 0 && !message) {
|
|
634
|
+
message = textBlocks.join('\n');
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
else if (typeof rawContent === 'object' && rawContent !== null) {
|
|
638
|
+
const contentObj = rawContent;
|
|
639
|
+
if ('content' in contentObj && typeof contentObj.content === 'string') {
|
|
640
|
+
message = contentObj.content;
|
|
641
|
+
}
|
|
642
|
+
else if ('text' in contentObj && typeof contentObj.text === 'string') {
|
|
643
|
+
message = contentObj.text;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const tool = rawEvent.tool
|
|
647
|
+
? {
|
|
648
|
+
name: rawEvent.tool,
|
|
649
|
+
id: rawEvent.tool_use_id,
|
|
650
|
+
input: rawEvent.tool_input,
|
|
651
|
+
}
|
|
652
|
+
: toolFromContent;
|
|
653
|
+
const event = {
|
|
654
|
+
type: effectiveType,
|
|
655
|
+
subtype: rawEvent.subtype,
|
|
656
|
+
receivedAt: createTimestamp(),
|
|
657
|
+
raw: rawEvent,
|
|
658
|
+
message,
|
|
659
|
+
tool,
|
|
660
|
+
};
|
|
661
|
+
session.events.emit('event', event);
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
session.events.emit('raw', line);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// ----------------------------------------
|
|
668
|
+
// Private Helpers - State Management
|
|
669
|
+
// ----------------------------------------
|
|
670
|
+
generateSessionId() {
|
|
671
|
+
this.sessionCounter++;
|
|
672
|
+
const timestamp = Date.now().toString(36);
|
|
673
|
+
const counter = this.sessionCounter.toString(36).padStart(4, '0');
|
|
674
|
+
const random = Math.random().toString(36).slice(2, 6);
|
|
675
|
+
return `session-${timestamp}-${counter}-${random}`;
|
|
676
|
+
}
|
|
677
|
+
determineSpawnMode(agentRole, requestedMode) {
|
|
678
|
+
if (requestedMode) {
|
|
679
|
+
return requestedMode;
|
|
680
|
+
}
|
|
681
|
+
switch (agentRole) {
|
|
682
|
+
case 'director':
|
|
683
|
+
return 'interactive';
|
|
684
|
+
case 'worker':
|
|
685
|
+
return 'headless';
|
|
686
|
+
case 'steward':
|
|
687
|
+
return 'headless';
|
|
688
|
+
default:
|
|
689
|
+
return 'headless';
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
getWorkerMode(agentRole, requestedMode) {
|
|
693
|
+
if (agentRole !== 'worker') {
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
const mode = requestedMode ?? this.determineSpawnMode(agentRole);
|
|
697
|
+
return mode === 'interactive' ? 'persistent' : 'ephemeral';
|
|
698
|
+
}
|
|
699
|
+
scheduleTerminatedSessionCleanup(sessionId) {
|
|
700
|
+
setTimeout(() => {
|
|
701
|
+
const session = this.sessions.get(sessionId);
|
|
702
|
+
if (session && session.status === 'terminated') {
|
|
703
|
+
this.sessions.delete(sessionId);
|
|
704
|
+
}
|
|
705
|
+
}, 5000);
|
|
706
|
+
}
|
|
707
|
+
transitionStatus(session, newStatus) {
|
|
708
|
+
const allowedTransitions = SessionStatusTransitions[session.status];
|
|
709
|
+
if (!allowedTransitions.includes(newStatus)) {
|
|
710
|
+
throw new Error(`Invalid status transition: ${session.status} -> ${newStatus}`);
|
|
711
|
+
}
|
|
712
|
+
session.status = newStatus;
|
|
713
|
+
}
|
|
714
|
+
filterAndMapSessions(sessions, agentId) {
|
|
715
|
+
const filtered = agentId
|
|
716
|
+
? sessions.filter((s) => s.agentId === agentId)
|
|
717
|
+
: sessions;
|
|
718
|
+
return filtered.map((s) => this.toPublicSession(s));
|
|
719
|
+
}
|
|
720
|
+
toPublicSession(session) {
|
|
721
|
+
return {
|
|
722
|
+
id: session.id,
|
|
723
|
+
providerSessionId: session.providerSessionId,
|
|
724
|
+
agentId: session.agentId,
|
|
725
|
+
agentRole: session.agentRole,
|
|
726
|
+
workerMode: session.workerMode,
|
|
727
|
+
mode: session.mode,
|
|
728
|
+
pid: session.pid,
|
|
729
|
+
status: session.status,
|
|
730
|
+
workingDirectory: session.workingDirectory,
|
|
731
|
+
createdAt: session.createdAt,
|
|
732
|
+
lastActivityAt: session.lastActivityAt,
|
|
733
|
+
startedAt: session.startedAt,
|
|
734
|
+
endedAt: session.endedAt,
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
// ============================================================================
|
|
739
|
+
// Factory Function
|
|
740
|
+
// ============================================================================
|
|
741
|
+
/**
|
|
742
|
+
* Creates a SpawnerService instance
|
|
743
|
+
*/
|
|
744
|
+
export function createSpawnerService(config) {
|
|
745
|
+
return new SpawnerServiceImpl(config);
|
|
746
|
+
}
|
|
747
|
+
// ============================================================================
|
|
748
|
+
// Utility Functions
|
|
749
|
+
// ============================================================================
|
|
750
|
+
/**
|
|
751
|
+
* Checks if a status allows the session to receive input
|
|
752
|
+
*/
|
|
753
|
+
export function canReceiveInput(status) {
|
|
754
|
+
return status === 'running';
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Checks if a status is a terminal state
|
|
758
|
+
*/
|
|
759
|
+
export function isTerminalStatus(status) {
|
|
760
|
+
return status === 'terminated';
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Gets human-readable description of a session status
|
|
764
|
+
*/
|
|
765
|
+
export function getStatusDescription(status) {
|
|
766
|
+
switch (status) {
|
|
767
|
+
case 'starting':
|
|
768
|
+
return 'Starting up';
|
|
769
|
+
case 'running':
|
|
770
|
+
return 'Running';
|
|
771
|
+
case 'suspended':
|
|
772
|
+
return 'Suspended (can be resumed)';
|
|
773
|
+
case 'terminating':
|
|
774
|
+
return 'Shutting down';
|
|
775
|
+
case 'terminated':
|
|
776
|
+
return 'Terminated';
|
|
777
|
+
default:
|
|
778
|
+
return 'Unknown';
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
//# sourceMappingURL=spawner.js.map
|