agent-relay 2.0.28 → 2.0.32
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/README.md +19 -0
- package/dist/index.cjs +85691 -0
- package/dist/src/bridge/index.d.ts.map +1 -0
- package/dist/src/bridge/index.js.map +1 -0
- package/dist/src/cli/commands/doctor.d.ts +2 -0
- package/dist/src/cli/commands/doctor.d.ts.map +1 -0
- package/dist/src/cli/commands/doctor.js +451 -0
- package/dist/src/cli/commands/doctor.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +29 -1
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/config/relay-config.d.ts.map +1 -0
- package/dist/src/config/relay-config.js.map +1 -0
- package/dist/src/continuity/index.d.ts.map +1 -0
- package/dist/src/continuity/index.js.map +1 -0
- package/dist/src/daemon/index.d.ts.map +1 -0
- package/dist/src/daemon/index.js.map +1 -0
- package/dist/src/health-worker-manager.d.ts.map +1 -0
- package/dist/src/health-worker-manager.js.map +1 -0
- package/dist/src/health-worker.d.ts.map +1 -0
- package/dist/src/health-worker.js.map +1 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/memory/index.d.ts.map +1 -0
- package/dist/src/memory/index.js.map +1 -0
- package/dist/src/policy/index.d.ts.map +1 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/protocol/index.d.ts.map +1 -0
- package/dist/src/protocol/index.js.map +1 -0
- package/dist/src/resiliency/index.d.ts.map +1 -0
- package/dist/src/resiliency/index.js.map +1 -0
- package/dist/src/shared/cli-auth-config.d.ts.map +1 -0
- package/dist/src/shared/cli-auth-config.js.map +1 -0
- package/dist/src/state/index.d.ts.map +1 -0
- package/dist/src/state/index.js.map +1 -0
- package/dist/src/storage/index.d.ts.map +1 -0
- package/dist/src/storage/index.js.map +1 -0
- package/dist/src/trajectory/index.d.ts.map +1 -0
- package/dist/src/trajectory/index.js.map +1 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/wrapper/index.d.ts.map +1 -0
- package/dist/src/wrapper/index.js.map +1 -0
- package/package.json +83 -20
- package/packages/api-types/dist/index.d.ts.map +1 -0
- package/packages/api-types/dist/index.js.map +1 -0
- package/packages/api-types/dist/schemas/agent.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/agent.js.map +1 -0
- package/packages/api-types/dist/schemas/api.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/api.js.map +1 -0
- package/packages/api-types/dist/schemas/decision.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/decision.js.map +1 -0
- package/packages/api-types/dist/schemas/fleet.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/fleet.js.map +1 -0
- package/packages/api-types/dist/schemas/history.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/history.js.map +1 -0
- package/packages/api-types/dist/schemas/index.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/index.js.map +1 -0
- package/packages/api-types/dist/schemas/message.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/message.js.map +1 -0
- package/packages/api-types/dist/schemas/session.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/session.js.map +1 -0
- package/packages/api-types/dist/schemas/task.d.ts.map +1 -0
- package/packages/api-types/dist/schemas/task.js.map +1 -0
- package/packages/api-types/package.json +1 -1
- package/packages/api-types/src/index.ts +22 -0
- package/packages/api-types/src/schemas/agent.test.ts +164 -0
- package/packages/api-types/src/schemas/agent.ts +110 -0
- package/packages/api-types/src/schemas/api.test.ts +372 -0
- package/packages/api-types/src/schemas/api.ts +194 -0
- package/packages/api-types/src/schemas/decision.test.ts +324 -0
- package/packages/api-types/src/schemas/decision.ts +136 -0
- package/packages/api-types/src/schemas/fleet.test.ts +212 -0
- package/packages/api-types/src/schemas/fleet.ts +83 -0
- package/packages/api-types/src/schemas/history.test.ts +242 -0
- package/packages/api-types/src/schemas/history.ts +84 -0
- package/packages/api-types/src/schemas/index.ts +148 -0
- package/packages/api-types/src/schemas/message.test.ts +192 -0
- package/packages/api-types/src/schemas/message.ts +98 -0
- package/packages/api-types/src/schemas/session.test.ts +104 -0
- package/packages/api-types/src/schemas/session.ts +40 -0
- package/packages/api-types/src/schemas/task.test.ts +192 -0
- package/packages/api-types/src/schemas/task.ts +78 -0
- package/packages/api-types/tsconfig.json +19 -0
- package/packages/api-types/vitest.config.ts +9 -0
- package/packages/benchmark/README.md +200 -0
- package/packages/benchmark/datasets/coding-tasks.yaml +127 -0
- package/packages/benchmark/datasets/coordination-tasks.yaml +122 -0
- package/packages/benchmark/dist/benchmark.d.ts +47 -0
- package/packages/benchmark/dist/benchmark.d.ts.map +1 -0
- package/packages/benchmark/dist/benchmark.js +224 -0
- package/packages/benchmark/dist/benchmark.js.map +1 -0
- package/packages/benchmark/dist/cli.d.ts +8 -0
- package/packages/benchmark/dist/cli.d.ts.map +1 -0
- package/packages/benchmark/dist/cli.js +185 -0
- package/packages/benchmark/dist/cli.js.map +1 -0
- package/packages/benchmark/dist/harbor.d.ts +53 -0
- package/packages/benchmark/dist/harbor.d.ts.map +1 -0
- package/packages/benchmark/dist/harbor.js +127 -0
- package/packages/benchmark/dist/harbor.js.map +1 -0
- package/packages/benchmark/dist/index.d.ts +48 -0
- package/packages/benchmark/dist/index.d.ts.map +1 -0
- package/packages/benchmark/dist/index.js +50 -0
- package/packages/benchmark/dist/index.js.map +1 -0
- package/packages/benchmark/dist/runners/base.d.ts +63 -0
- package/packages/benchmark/dist/runners/base.d.ts.map +1 -0
- package/packages/benchmark/dist/runners/base.js +155 -0
- package/packages/benchmark/dist/runners/base.js.map +1 -0
- package/packages/benchmark/dist/runners/index.d.ts +10 -0
- package/packages/benchmark/dist/runners/index.d.ts.map +1 -0
- package/packages/benchmark/dist/runners/index.js +10 -0
- package/packages/benchmark/dist/runners/index.js.map +1 -0
- package/packages/benchmark/dist/runners/single.d.ts +19 -0
- package/packages/benchmark/dist/runners/single.d.ts.map +1 -0
- package/packages/benchmark/dist/runners/single.js +111 -0
- package/packages/benchmark/dist/runners/single.js.map +1 -0
- package/packages/benchmark/dist/runners/subagent.d.ts +32 -0
- package/packages/benchmark/dist/runners/subagent.d.ts.map +1 -0
- package/packages/benchmark/dist/runners/subagent.js +212 -0
- package/packages/benchmark/dist/runners/subagent.js.map +1 -0
- package/packages/benchmark/dist/runners/swarm.d.ts +36 -0
- package/packages/benchmark/dist/runners/swarm.d.ts.map +1 -0
- package/packages/benchmark/dist/runners/swarm.js +273 -0
- package/packages/benchmark/dist/runners/swarm.js.map +1 -0
- package/packages/benchmark/dist/types.d.ts +178 -0
- package/packages/benchmark/dist/types.d.ts.map +1 -0
- package/packages/benchmark/dist/types.js +16 -0
- package/packages/benchmark/dist/types.js.map +1 -0
- package/packages/benchmark/package.json +80 -0
- package/packages/benchmark/src/benchmark.ts +298 -0
- package/packages/benchmark/src/cli.ts +240 -0
- package/packages/benchmark/src/harbor.ts +170 -0
- package/packages/benchmark/src/index.ts +73 -0
- package/packages/benchmark/src/runners/base.ts +204 -0
- package/packages/benchmark/src/runners/index.ts +10 -0
- package/packages/benchmark/src/runners/single.ts +121 -0
- package/packages/benchmark/src/runners/subagent.ts +240 -0
- package/packages/benchmark/src/runners/swarm.ts +326 -0
- package/packages/benchmark/src/types.ts +205 -0
- package/packages/benchmark/tsconfig.json +20 -0
- package/packages/bridge/dist/index.d.ts.map +1 -0
- package/packages/bridge/dist/index.js.map +1 -0
- package/packages/bridge/dist/multi-project-client.d.ts.map +1 -0
- package/packages/bridge/dist/multi-project-client.js.map +1 -0
- package/packages/bridge/dist/shadow-cli.d.ts.map +1 -0
- package/packages/bridge/dist/shadow-cli.js.map +1 -0
- package/packages/bridge/dist/spawner.d.ts.map +1 -0
- package/packages/bridge/dist/spawner.js +15 -2
- package/packages/bridge/dist/spawner.js.map +1 -0
- package/packages/bridge/dist/types.d.ts.map +1 -0
- package/packages/bridge/dist/types.js.map +1 -0
- package/packages/bridge/dist/utils.d.ts.map +1 -0
- package/packages/bridge/dist/utils.js.map +1 -0
- package/packages/bridge/package.json +8 -8
- package/packages/bridge/src/index.ts +25 -0
- package/packages/bridge/src/multi-project-client.test.ts +340 -0
- package/packages/bridge/src/multi-project-client.ts +469 -0
- package/packages/bridge/src/shadow-cli.ts +95 -0
- package/packages/bridge/src/spawner-mcp.test.ts +505 -0
- package/packages/bridge/src/spawner.ts +1724 -0
- package/packages/bridge/src/types.ts +145 -0
- package/packages/bridge/src/utils.test.ts +98 -0
- package/packages/bridge/src/utils.ts +67 -0
- package/packages/bridge/tsconfig.json +29 -0
- package/packages/bridge/vitest.config.ts +9 -0
- package/packages/cli-tester/dist/index.d.ts.map +1 -0
- package/packages/cli-tester/dist/index.js.map +1 -0
- package/packages/cli-tester/dist/utils/credential-check.d.ts.map +1 -0
- package/packages/cli-tester/dist/utils/credential-check.js.map +1 -0
- package/packages/cli-tester/dist/utils/socket-client.d.ts.map +1 -0
- package/packages/cli-tester/dist/utils/socket-client.js.map +1 -0
- package/packages/cli-tester/docker/Dockerfile +61 -0
- package/packages/cli-tester/docker/docker-compose.yml +71 -0
- package/packages/cli-tester/package.json +1 -1
- package/packages/cli-tester/src/index.ts +40 -0
- package/packages/cli-tester/src/utils/credential-check.ts +284 -0
- package/packages/cli-tester/src/utils/socket-client.ts +211 -0
- package/packages/cli-tester/tests/credential-check.test.ts +56 -0
- package/packages/cli-tester/tsconfig.json +11 -0
- package/packages/config/dist/agent-config.d.ts.map +1 -0
- package/packages/config/dist/agent-config.js.map +1 -0
- package/packages/config/dist/bridge-config.d.ts.map +1 -0
- package/packages/config/dist/bridge-config.js.map +1 -0
- package/packages/config/dist/bridge-utils.d.ts.map +1 -0
- package/packages/config/dist/bridge-utils.js.map +1 -0
- package/packages/config/dist/cli-auth-config.d.ts.map +1 -0
- package/packages/config/dist/cli-auth-config.js.map +1 -0
- package/packages/config/dist/cloud-config.d.ts.map +1 -0
- package/packages/config/dist/cloud-config.js.map +1 -0
- package/packages/config/dist/index.d.ts.map +1 -0
- package/packages/config/dist/index.js.map +1 -0
- package/packages/config/dist/project-namespace.d.ts.map +1 -0
- package/packages/config/dist/project-namespace.js.map +1 -0
- package/packages/config/dist/relay-config.d.ts.map +1 -0
- package/packages/config/dist/relay-config.js.map +1 -0
- package/packages/config/dist/relay-file-writer.d.ts.map +1 -0
- package/packages/config/dist/relay-file-writer.js.map +1 -0
- package/packages/config/dist/schemas.d.ts.map +1 -0
- package/packages/config/dist/schemas.js.map +1 -0
- package/packages/config/dist/shadow-config.d.ts.map +1 -0
- package/packages/config/dist/shadow-config.js.map +1 -0
- package/packages/config/dist/teams-config.d.ts.map +1 -0
- package/packages/config/dist/teams-config.js.map +1 -0
- package/packages/config/dist/trajectory-config.d.ts.map +1 -0
- package/packages/config/dist/trajectory-config.js.map +1 -0
- package/packages/config/package.json +2 -2
- package/packages/config/src/agent-config.test.ts +245 -0
- package/packages/config/src/agent-config.ts +160 -0
- package/packages/config/src/bridge-config.test.ts +132 -0
- package/packages/config/src/bridge-config.ts +189 -0
- package/packages/config/src/bridge-utils.ts +59 -0
- package/packages/config/src/cli-auth-config.ts +548 -0
- package/packages/config/src/cloud-config.ts +208 -0
- package/packages/config/src/index.ts +12 -0
- package/packages/config/src/project-namespace.ts +344 -0
- package/packages/config/src/relay-config.test.ts +51 -0
- package/packages/config/src/relay-config.ts +36 -0
- package/packages/config/src/relay-file-writer.test.ts +351 -0
- package/packages/config/src/relay-file-writer.ts +508 -0
- package/packages/config/src/schemas.test.ts +59 -0
- package/packages/config/src/schemas.ts +201 -0
- package/packages/config/src/shadow-config.ts +205 -0
- package/packages/config/src/teams-config.ts +135 -0
- package/packages/config/src/trajectory-config.ts +222 -0
- package/packages/config/tsconfig.json +21 -0
- package/packages/config/vitest.config.ts +9 -0
- package/packages/continuity/dist/formatter.d.ts.map +1 -0
- package/packages/continuity/dist/formatter.js.map +1 -0
- package/packages/continuity/dist/handoff-store.d.ts.map +1 -0
- package/packages/continuity/dist/handoff-store.js.map +1 -0
- package/packages/continuity/dist/index.d.ts.map +1 -0
- package/packages/continuity/dist/index.js.map +1 -0
- package/packages/continuity/dist/ledger-store.d.ts.map +1 -0
- package/packages/continuity/dist/ledger-store.js.map +1 -0
- package/packages/continuity/dist/manager.d.ts.map +1 -0
- package/packages/continuity/dist/manager.js.map +1 -0
- package/packages/continuity/dist/parser.d.ts.map +1 -0
- package/packages/continuity/dist/parser.js.map +1 -0
- package/packages/continuity/dist/types.d.ts.map +1 -0
- package/packages/continuity/dist/types.js.map +1 -0
- package/packages/continuity/package.json +1 -1
- package/packages/continuity/src/formatter.ts +371 -0
- package/packages/continuity/src/handoff-store.ts +523 -0
- package/packages/continuity/src/index.ts +9 -0
- package/packages/continuity/src/ledger-store.ts +594 -0
- package/packages/continuity/src/manager.test.ts +291 -0
- package/packages/continuity/src/manager.ts +774 -0
- package/packages/continuity/src/parser.test.ts +292 -0
- package/packages/continuity/src/parser.ts +680 -0
- package/packages/continuity/src/types.ts +211 -0
- package/packages/continuity/tsconfig.json +21 -0
- package/packages/continuity/vitest.config.ts +9 -0
- package/packages/daemon/dist/agent-manager.d.ts.map +1 -0
- package/packages/daemon/dist/agent-manager.js.map +1 -0
- package/packages/daemon/dist/agent-registry.d.ts.map +1 -0
- package/packages/daemon/dist/agent-registry.js.map +1 -0
- package/packages/daemon/dist/agent-signing.d.ts.map +1 -0
- package/packages/daemon/dist/agent-signing.js.map +1 -0
- package/packages/daemon/dist/api.d.ts.map +1 -0
- package/packages/daemon/dist/api.js.map +1 -0
- package/packages/daemon/dist/auth.d.ts.map +1 -0
- package/packages/daemon/dist/auth.js.map +1 -0
- package/packages/daemon/dist/channel-membership-store.d.ts.map +1 -0
- package/packages/daemon/dist/channel-membership-store.js.map +1 -0
- package/packages/daemon/dist/cli-auth.d.ts.map +1 -0
- package/packages/daemon/dist/cli-auth.js.map +1 -0
- package/packages/daemon/dist/cloud-sync.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-sync.js.map +1 -0
- package/packages/daemon/dist/connection.d.ts.map +1 -0
- package/packages/daemon/dist/connection.js.map +1 -0
- package/packages/daemon/dist/consensus-integration.d.ts.map +1 -0
- package/packages/daemon/dist/consensus-integration.js.map +1 -0
- package/packages/daemon/dist/consensus.d.ts.map +1 -0
- package/packages/daemon/dist/consensus.js.map +1 -0
- package/packages/daemon/dist/delivery-tracker.d.ts.map +1 -0
- package/packages/daemon/dist/delivery-tracker.js.map +1 -0
- package/packages/daemon/dist/enhanced-features.d.ts.map +1 -0
- package/packages/daemon/dist/enhanced-features.js.map +1 -0
- package/packages/daemon/dist/index.d.ts.map +1 -0
- package/packages/daemon/dist/index.js.map +1 -0
- package/packages/daemon/dist/migrations/index.d.ts.map +1 -0
- package/packages/daemon/dist/migrations/index.js.map +1 -0
- package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator.js.map +1 -0
- package/packages/daemon/dist/rate-limiter.d.ts.map +1 -0
- package/packages/daemon/dist/rate-limiter.js.map +1 -0
- package/packages/daemon/dist/registry.d.ts.map +1 -0
- package/packages/daemon/dist/registry.js.map +1 -0
- package/packages/daemon/dist/relay-ledger.d.ts.map +1 -0
- package/packages/daemon/dist/relay-ledger.js.map +1 -0
- package/packages/daemon/dist/relay-watchdog.d.ts.map +1 -0
- package/packages/daemon/dist/relay-watchdog.js.map +1 -0
- package/packages/daemon/dist/repo-manager.d.ts.map +1 -0
- package/packages/daemon/dist/repo-manager.js.map +1 -0
- package/packages/daemon/dist/router.d.ts.map +1 -0
- package/packages/daemon/dist/router.js.map +1 -0
- package/packages/daemon/dist/server.d.ts +1 -0
- package/packages/daemon/dist/server.d.ts.map +1 -0
- package/packages/daemon/dist/server.js +46 -16
- package/packages/daemon/dist/server.js.map +1 -0
- package/packages/daemon/dist/spawn-manager.d.ts.map +1 -0
- package/packages/daemon/dist/spawn-manager.js.map +1 -0
- package/packages/daemon/dist/sync-queue.d.ts.map +1 -0
- package/packages/daemon/dist/sync-queue.js.map +1 -0
- package/packages/daemon/dist/types.d.ts.map +1 -0
- package/packages/daemon/dist/types.js.map +1 -0
- package/packages/daemon/dist/workspace-manager.d.ts.map +1 -0
- package/packages/daemon/dist/workspace-manager.js.map +1 -0
- package/packages/daemon/package.json +12 -12
- package/packages/daemon/src/agent-manager.ts +679 -0
- package/packages/daemon/src/agent-registry.ts +284 -0
- package/packages/daemon/src/agent-signing.ts +707 -0
- package/packages/daemon/src/api.ts +1012 -0
- package/packages/daemon/src/auth.ts +276 -0
- package/packages/daemon/src/channel-membership-store.ts +217 -0
- package/packages/daemon/src/cli-auth.ts +906 -0
- package/packages/daemon/src/cloud-sync.ts +902 -0
- package/packages/daemon/src/connection.ts +534 -0
- package/packages/daemon/src/consensus-integration.ts +510 -0
- package/packages/daemon/src/consensus.ts +848 -0
- package/packages/daemon/src/delivery-tracker.ts +145 -0
- package/packages/daemon/src/enhanced-features.ts +390 -0
- package/packages/daemon/src/index.ts +52 -0
- package/packages/daemon/src/migrations/0001_initial.sql +72 -0
- package/packages/daemon/src/migrations/index.test.ts +195 -0
- package/packages/daemon/src/migrations/index.ts +286 -0
- package/packages/daemon/src/orchestrator.test.ts +231 -0
- package/packages/daemon/src/orchestrator.ts +1376 -0
- package/packages/daemon/src/rate-limiter.ts +172 -0
- package/packages/daemon/src/registry.ts +8 -0
- package/packages/daemon/src/relay-ledger.test.ts +358 -0
- package/packages/daemon/src/relay-ledger.ts +713 -0
- package/packages/daemon/src/relay-watchdog.test.ts +881 -0
- package/packages/daemon/src/relay-watchdog.ts +785 -0
- package/packages/daemon/src/repo-manager.ts +468 -0
- package/packages/daemon/src/router.test.ts +149 -0
- package/packages/daemon/src/router.ts +1885 -0
- package/packages/daemon/src/server.ts +1871 -0
- package/packages/daemon/src/spawn-manager.ts +275 -0
- package/packages/daemon/src/sync-queue.ts +477 -0
- package/packages/daemon/src/types.ts +158 -0
- package/packages/daemon/src/workspace-manager.ts +371 -0
- package/packages/daemon/tsconfig.json +21 -0
- package/packages/hooks/dist/browser.d.ts.map +1 -0
- package/packages/hooks/dist/browser.js.map +1 -0
- package/packages/hooks/dist/emitter.d.ts.map +1 -0
- package/packages/hooks/dist/emitter.js.map +1 -0
- package/packages/hooks/dist/inbox-check/hook.d.ts.map +1 -0
- package/packages/hooks/dist/inbox-check/hook.js.map +1 -0
- package/packages/hooks/dist/inbox-check/index.d.ts.map +1 -0
- package/packages/hooks/dist/inbox-check/index.js.map +1 -0
- package/packages/hooks/dist/inbox-check/types.d.ts.map +1 -0
- package/packages/hooks/dist/inbox-check/types.js.map +1 -0
- package/packages/hooks/dist/inbox-check/utils.d.ts.map +1 -0
- package/packages/hooks/dist/inbox-check/utils.js.map +1 -0
- package/packages/hooks/dist/index.d.ts.map +1 -0
- package/packages/hooks/dist/index.js.map +1 -0
- package/packages/hooks/dist/registry.d.ts.map +1 -0
- package/packages/hooks/dist/registry.js.map +1 -0
- package/packages/hooks/dist/trajectory-hooks.d.ts.map +1 -0
- package/packages/hooks/dist/trajectory-hooks.js.map +1 -0
- package/packages/hooks/dist/types.d.ts.map +1 -0
- package/packages/hooks/dist/types.js.map +1 -0
- package/packages/hooks/package.json +4 -4
- package/packages/hooks/src/browser.ts +2 -0
- package/packages/hooks/src/emitter.ts +84 -0
- package/packages/hooks/src/inbox-check/hook.ts +114 -0
- package/packages/hooks/src/inbox-check/index.ts +8 -0
- package/packages/hooks/src/inbox-check/types.ts +39 -0
- package/packages/hooks/src/inbox-check/utils.test.ts +287 -0
- package/packages/hooks/src/inbox-check/utils.ts +125 -0
- package/packages/hooks/src/index.ts +11 -0
- package/packages/hooks/src/registry.ts +614 -0
- package/packages/hooks/src/shims.d.ts +3 -0
- package/packages/hooks/src/trajectory-hooks.ts +251 -0
- package/packages/hooks/src/types.ts +342 -0
- package/packages/hooks/tsconfig.json +21 -0
- package/packages/hooks/vitest.config.ts +9 -0
- package/packages/mcp/dist/bin.d.ts.map +1 -0
- package/packages/mcp/dist/bin.js.map +1 -0
- package/packages/mcp/dist/client.d.ts +9 -15
- package/packages/mcp/dist/client.d.ts.map +1 -0
- package/packages/mcp/dist/client.js +42 -74
- package/packages/mcp/dist/client.js.map +1 -0
- package/packages/mcp/dist/cloud.d.ts.map +1 -0
- package/packages/mcp/dist/cloud.js.map +1 -0
- package/packages/mcp/dist/errors.d.ts.map +1 -0
- package/packages/mcp/dist/errors.js.map +1 -0
- package/packages/mcp/dist/file-transport.d.ts.map +1 -0
- package/packages/mcp/dist/file-transport.js.map +1 -0
- package/packages/mcp/dist/hybrid-client.d.ts.map +1 -0
- package/packages/mcp/dist/hybrid-client.js.map +1 -0
- package/packages/mcp/dist/index.d.ts.map +1 -0
- package/packages/mcp/dist/index.js.map +1 -0
- package/packages/mcp/dist/install-cli.d.ts.map +1 -0
- package/packages/mcp/dist/install-cli.js.map +1 -0
- package/packages/mcp/dist/install.d.ts.map +1 -0
- package/packages/mcp/dist/install.js.map +1 -0
- package/packages/mcp/dist/prompts/index.d.ts.map +1 -0
- package/packages/mcp/dist/prompts/index.js.map +1 -0
- package/packages/mcp/dist/prompts/protocol.d.ts.map +1 -0
- package/packages/mcp/dist/prompts/protocol.js.map +1 -0
- package/packages/mcp/dist/resources/agents.d.ts.map +1 -0
- package/packages/mcp/dist/resources/agents.js.map +1 -0
- package/packages/mcp/dist/resources/inbox.d.ts.map +1 -0
- package/packages/mcp/dist/resources/inbox.js.map +1 -0
- package/packages/mcp/dist/resources/index.d.ts.map +1 -0
- package/packages/mcp/dist/resources/index.js.map +1 -0
- package/packages/mcp/dist/resources/project.d.ts.map +1 -0
- package/packages/mcp/dist/resources/project.js.map +1 -0
- package/packages/mcp/dist/server.d.ts.map +1 -0
- package/packages/mcp/dist/server.js.map +1 -0
- package/packages/mcp/dist/simple.d.ts +2 -5
- package/packages/mcp/dist/simple.d.ts.map +1 -0
- package/packages/mcp/dist/simple.js.map +1 -0
- package/packages/mcp/dist/tools/index.d.ts.map +1 -0
- package/packages/mcp/dist/tools/index.js.map +1 -0
- package/packages/mcp/dist/tools/relay-broadcast.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-broadcast.js.map +1 -0
- package/packages/mcp/dist/tools/relay-channel.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-channel.js.map +1 -0
- package/packages/mcp/dist/tools/relay-connected.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-connected.js.map +1 -0
- package/packages/mcp/dist/tools/relay-consensus.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-consensus.js.map +1 -0
- package/packages/mcp/dist/tools/relay-continuity.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-continuity.js.map +1 -0
- package/packages/mcp/dist/tools/relay-health.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-health.js.map +1 -0
- package/packages/mcp/dist/tools/relay-inbox.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-inbox.js.map +1 -0
- package/packages/mcp/dist/tools/relay-logs.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-logs.js.map +1 -0
- package/packages/mcp/dist/tools/relay-metrics.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-metrics.js.map +1 -0
- package/packages/mcp/dist/tools/relay-release.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-release.js.map +1 -0
- package/packages/mcp/dist/tools/relay-remove-agent.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-remove-agent.js.map +1 -0
- package/packages/mcp/dist/tools/relay-send.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-send.js +4 -2
- package/packages/mcp/dist/tools/relay-send.js.map +1 -0
- package/packages/mcp/dist/tools/relay-shadow.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-shadow.js.map +1 -0
- package/packages/mcp/dist/tools/relay-spawn.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-spawn.js.map +1 -0
- package/packages/mcp/dist/tools/relay-status.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-status.js.map +1 -0
- package/packages/mcp/dist/tools/relay-subscribe.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-subscribe.js.map +1 -0
- package/packages/mcp/dist/tools/relay-who.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-who.js.map +1 -0
- package/packages/mcp/package.json +3 -3
- package/packages/mcp/src/bin.ts +149 -0
- package/packages/mcp/src/client.ts +400 -0
- package/packages/mcp/src/cloud.ts +523 -0
- package/packages/mcp/src/errors.ts +54 -0
- package/packages/mcp/src/file-transport.ts +268 -0
- package/packages/mcp/src/hybrid-client.ts +209 -0
- package/packages/mcp/src/index.ts +122 -0
- package/packages/mcp/src/install-cli.ts +210 -0
- package/packages/mcp/src/install.ts +745 -0
- package/packages/mcp/src/prompts/index.ts +1 -0
- package/packages/mcp/src/prompts/protocol.ts +164 -0
- package/packages/mcp/src/resources/agents.ts +21 -0
- package/packages/mcp/src/resources/inbox.ts +21 -0
- package/packages/mcp/src/resources/index.ts +3 -0
- package/packages/mcp/src/resources/project.ts +29 -0
- package/packages/mcp/src/server.ts +431 -0
- package/packages/mcp/src/simple.ts +214 -0
- package/packages/mcp/src/tools/index.ts +133 -0
- package/packages/mcp/src/tools/relay-broadcast.ts +32 -0
- package/packages/mcp/src/tools/relay-channel.ts +93 -0
- package/packages/mcp/src/tools/relay-connected.ts +52 -0
- package/packages/mcp/src/tools/relay-consensus.ts +92 -0
- package/packages/mcp/src/tools/relay-continuity.ts +127 -0
- package/packages/mcp/src/tools/relay-health.ts +148 -0
- package/packages/mcp/src/tools/relay-inbox.ts +70 -0
- package/packages/mcp/src/tools/relay-logs.ts +106 -0
- package/packages/mcp/src/tools/relay-metrics.ts +140 -0
- package/packages/mcp/src/tools/relay-release.ts +54 -0
- package/packages/mcp/src/tools/relay-remove-agent.ts +58 -0
- package/packages/mcp/src/tools/relay-send.ts +84 -0
- package/packages/mcp/src/tools/relay-shadow.ts +67 -0
- package/packages/mcp/src/tools/relay-spawn.ts +87 -0
- package/packages/mcp/src/tools/relay-status.ts +57 -0
- package/packages/mcp/src/tools/relay-subscribe.ts +61 -0
- package/packages/mcp/src/tools/relay-who.ts +59 -0
- package/packages/mcp/tests/client.test.ts +476 -0
- package/packages/mcp/tests/discover.test.ts +195 -0
- package/packages/mcp/tests/install.test.ts +123 -0
- package/packages/mcp/tests/prompts.test.ts +12 -0
- package/packages/mcp/tests/resources.test.ts +53 -0
- package/packages/mcp/tests/tools.test.ts +1242 -0
- package/packages/mcp/tsconfig.json +22 -0
- package/packages/mcp/vitest.config.ts +9 -0
- package/packages/memory/dist/adapters/index.d.ts.map +1 -0
- package/packages/memory/dist/adapters/index.js.map +1 -0
- package/packages/memory/dist/adapters/inmemory.d.ts.map +1 -0
- package/packages/memory/dist/adapters/inmemory.js.map +1 -0
- package/packages/memory/dist/adapters/supermemory.d.ts.map +1 -0
- package/packages/memory/dist/adapters/supermemory.js.map +1 -0
- package/packages/memory/dist/context-compaction.d.ts.map +1 -0
- package/packages/memory/dist/context-compaction.js.map +1 -0
- package/packages/memory/dist/factory.d.ts.map +1 -0
- package/packages/memory/dist/factory.js.map +1 -0
- package/packages/memory/dist/index.d.ts.map +1 -0
- package/packages/memory/dist/index.js.map +1 -0
- package/packages/memory/dist/memory-hooks.d.ts.map +1 -0
- package/packages/memory/dist/memory-hooks.js.map +1 -0
- package/packages/memory/dist/service.d.ts.map +1 -0
- package/packages/memory/dist/service.js.map +1 -0
- package/packages/memory/dist/types.d.ts.map +1 -0
- package/packages/memory/dist/types.js.map +1 -0
- package/packages/memory/package.json +2 -2
- package/packages/memory/src/adapters/index.ts +8 -0
- package/packages/memory/src/adapters/inmemory.ts +265 -0
- package/packages/memory/src/adapters/supermemory.ts +449 -0
- package/packages/memory/src/context-compaction.test.ts +660 -0
- package/packages/memory/src/context-compaction.ts +612 -0
- package/packages/memory/src/factory.ts +170 -0
- package/packages/memory/src/index.ts +33 -0
- package/packages/memory/src/memory-hooks.ts +410 -0
- package/packages/memory/src/service.ts +194 -0
- package/packages/memory/src/types.ts +211 -0
- package/packages/memory/tsconfig.json +21 -0
- package/packages/memory/vitest.config.ts +9 -0
- package/packages/policy/dist/agent-policy.d.ts.map +1 -0
- package/packages/policy/dist/agent-policy.js.map +1 -0
- package/packages/policy/dist/cloud-policy-fetcher.d.ts.map +1 -0
- package/packages/policy/dist/cloud-policy-fetcher.js.map +1 -0
- package/packages/policy/dist/index.d.ts.map +1 -0
- package/packages/policy/dist/index.js.map +1 -0
- package/packages/policy/package.json +2 -2
- package/packages/policy/src/agent-policy.ts +866 -0
- package/packages/policy/src/cloud-policy-fetcher.ts +78 -0
- package/packages/policy/src/index.ts +21 -0
- package/packages/policy/tsconfig.json +21 -0
- package/packages/policy/vitest.config.ts +9 -0
- package/packages/protocol/dist/channels.d.ts.map +1 -0
- package/packages/protocol/dist/channels.js.map +1 -0
- package/packages/protocol/dist/framing.d.ts.map +1 -0
- package/packages/protocol/dist/framing.js.map +1 -0
- package/packages/protocol/dist/id-generator.d.ts.map +1 -0
- package/packages/protocol/dist/id-generator.js.map +1 -0
- package/packages/protocol/dist/index.d.ts.map +1 -0
- package/packages/protocol/dist/index.js.map +1 -0
- package/packages/protocol/dist/relay-pty-schemas.d.ts +70 -2
- package/packages/protocol/dist/relay-pty-schemas.d.ts.map +1 -0
- package/packages/protocol/dist/relay-pty-schemas.js.map +1 -0
- package/packages/protocol/dist/types.d.ts +8 -0
- package/packages/protocol/dist/types.d.ts.map +1 -0
- package/packages/protocol/dist/types.js.map +1 -0
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/channels.test.ts +330 -0
- package/packages/protocol/src/channels.ts +270 -0
- package/packages/protocol/src/framing.test.ts +164 -0
- package/packages/protocol/src/framing.ts +242 -0
- package/packages/protocol/src/id-generator.ts +69 -0
- package/packages/protocol/src/index.ts +4 -0
- package/packages/protocol/src/relay-pty-schemas.ts +400 -0
- package/packages/protocol/src/types.test.ts +271 -0
- package/packages/protocol/src/types.ts +846 -0
- package/packages/protocol/tsconfig.json +21 -0
- package/packages/protocol/vitest.config.ts +9 -0
- package/packages/resiliency/dist/cgroup-manager.d.ts.map +1 -0
- package/packages/resiliency/dist/cgroup-manager.js.map +1 -0
- package/packages/resiliency/dist/context-persistence.d.ts.map +1 -0
- package/packages/resiliency/dist/context-persistence.js.map +1 -0
- package/packages/resiliency/dist/crash-insights.d.ts.map +1 -0
- package/packages/resiliency/dist/crash-insights.js.map +1 -0
- package/packages/resiliency/dist/gossip-health.d.ts.map +1 -0
- package/packages/resiliency/dist/gossip-health.js.map +1 -0
- package/packages/resiliency/dist/health-monitor.d.ts.map +1 -0
- package/packages/resiliency/dist/health-monitor.js.map +1 -0
- package/packages/resiliency/dist/index.d.ts.map +1 -0
- package/packages/resiliency/dist/index.js.map +1 -0
- package/packages/resiliency/dist/leader-watchdog.d.ts.map +1 -0
- package/packages/resiliency/dist/leader-watchdog.js.map +1 -0
- package/packages/resiliency/dist/logger.d.ts.map +1 -0
- package/packages/resiliency/dist/logger.js.map +1 -0
- package/packages/resiliency/dist/memory-monitor.d.ts.map +1 -0
- package/packages/resiliency/dist/memory-monitor.js.map +1 -0
- package/packages/resiliency/dist/metrics.d.ts.map +1 -0
- package/packages/resiliency/dist/metrics.js.map +1 -0
- package/packages/resiliency/dist/provider-context.d.ts.map +1 -0
- package/packages/resiliency/dist/provider-context.js.map +1 -0
- package/packages/resiliency/dist/stateless-lead.d.ts.map +1 -0
- package/packages/resiliency/dist/stateless-lead.js.map +1 -0
- package/packages/resiliency/dist/supervisor.d.ts.map +1 -0
- package/packages/resiliency/dist/supervisor.js.map +1 -0
- package/packages/resiliency/package.json +1 -1
- package/packages/resiliency/src/cgroup-manager.ts +468 -0
- package/packages/resiliency/src/context-persistence.ts +538 -0
- package/packages/resiliency/src/crash-insights.test.ts +620 -0
- package/packages/resiliency/src/crash-insights.ts +660 -0
- package/packages/resiliency/src/gossip-health.ts +333 -0
- package/packages/resiliency/src/health-monitor.ts +371 -0
- package/packages/resiliency/src/index.ts +157 -0
- package/packages/resiliency/src/leader-watchdog.ts +260 -0
- package/packages/resiliency/src/logger.ts +320 -0
- package/packages/resiliency/src/memory-monitor.test.ts +637 -0
- package/packages/resiliency/src/memory-monitor.ts +740 -0
- package/packages/resiliency/src/metrics.ts +311 -0
- package/packages/resiliency/src/provider-context.ts +452 -0
- package/packages/resiliency/src/stateless-lead.ts +408 -0
- package/packages/resiliency/src/supervisor.ts +578 -0
- package/packages/resiliency/tsconfig.json +21 -0
- package/packages/resiliency/vitest.config.ts +9 -0
- package/packages/sdk/dist/client.d.ts.map +1 -0
- package/packages/sdk/dist/client.js.map +1 -0
- package/packages/sdk/dist/index.d.ts.map +1 -0
- package/packages/sdk/dist/index.js.map +1 -0
- package/packages/sdk/dist/logs.d.ts.map +1 -0
- package/packages/sdk/dist/logs.js.map +1 -0
- package/packages/sdk/dist/protocol/index.d.ts.map +1 -0
- package/packages/sdk/dist/protocol/index.js.map +1 -0
- package/packages/sdk/dist/standalone.d.ts.map +1 -0
- package/packages/sdk/dist/standalone.js.map +1 -0
- package/packages/sdk/examples/SWARM_CAPABILITIES.md +498 -0
- package/packages/sdk/examples/SWARM_PATTERNS.md +541 -0
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/client.test.ts +568 -0
- package/packages/sdk/src/client.ts +1418 -0
- package/packages/sdk/src/index.ts +103 -0
- package/packages/sdk/src/logs.test.ts +98 -0
- package/packages/sdk/src/logs.ts +126 -0
- package/packages/sdk/src/protocol/framing.test.ts +164 -0
- package/packages/sdk/src/protocol/index.ts +8 -0
- package/packages/sdk/src/standalone.ts +176 -0
- package/packages/sdk/tsconfig.json +22 -0
- package/packages/sdk/vitest.config.ts +9 -0
- package/packages/spawner/.trajectories/index.json +5 -0
- package/packages/spawner/dist/index.d.ts.map +1 -0
- package/packages/spawner/dist/index.js.map +1 -0
- package/packages/spawner/dist/types.d.ts.map +1 -0
- package/packages/spawner/dist/types.js.map +1 -0
- package/packages/spawner/package.json +1 -1
- package/packages/spawner/src/index.ts +8 -0
- package/packages/spawner/src/types.test.ts +385 -0
- package/packages/spawner/src/types.ts +228 -0
- package/packages/spawner/tsconfig.json +19 -0
- package/packages/spawner/vitest.config.ts +9 -0
- package/packages/state/dist/agent-state.d.ts.map +1 -0
- package/packages/state/dist/agent-state.js.map +1 -0
- package/packages/state/dist/index.d.ts.map +1 -0
- package/packages/state/dist/index.js.map +1 -0
- package/packages/state/package.json +1 -1
- package/packages/state/src/agent-state.test.ts +335 -0
- package/packages/state/src/agent-state.ts +153 -0
- package/packages/state/src/index.ts +12 -0
- package/packages/state/tsconfig.json +21 -0
- package/packages/state/vitest.config.ts +9 -0
- package/packages/storage/dist/adapter.d.ts +28 -1
- package/packages/storage/dist/adapter.d.ts.map +1 -0
- package/packages/storage/dist/adapter.js +104 -10
- package/packages/storage/dist/adapter.js.map +1 -0
- package/packages/storage/dist/batched-sqlite-adapter.d.ts.map +1 -0
- package/packages/storage/dist/batched-sqlite-adapter.js.map +1 -0
- package/packages/storage/dist/dead-letter-queue.d.ts.map +1 -0
- package/packages/storage/dist/dead-letter-queue.js.map +1 -0
- package/packages/storage/dist/dlq-adapter.d.ts.map +1 -0
- package/packages/storage/dist/dlq-adapter.js.map +1 -0
- package/packages/storage/dist/index.d.ts +1 -0
- package/packages/storage/dist/index.d.ts.map +1 -0
- package/packages/storage/dist/index.js +1 -0
- package/packages/storage/dist/index.js.map +1 -0
- package/packages/storage/dist/jsonl-adapter.d.ts +77 -0
- package/packages/storage/dist/jsonl-adapter.d.ts.map +1 -0
- package/packages/storage/dist/jsonl-adapter.js +505 -0
- package/packages/storage/dist/jsonl-adapter.js.map +1 -0
- package/packages/storage/dist/sqlite-adapter.d.ts +6 -1
- package/packages/storage/dist/sqlite-adapter.d.ts.map +1 -0
- package/packages/storage/dist/sqlite-adapter.js +47 -0
- package/packages/storage/dist/sqlite-adapter.js.map +1 -0
- package/packages/storage/package.json +2 -2
- package/packages/storage/src/adapter.ts +438 -0
- package/packages/storage/src/batched-sqlite-adapter.test.ts +240 -0
- package/packages/storage/src/batched-sqlite-adapter.ts +239 -0
- package/packages/storage/src/dead-letter-queue.ts +643 -0
- package/packages/storage/src/dlq-adapter.test.ts +492 -0
- package/packages/storage/src/dlq-adapter.ts +954 -0
- package/packages/storage/src/index.ts +6 -0
- package/packages/storage/src/jsonl-adapter.test.ts +200 -0
- package/packages/storage/src/jsonl-adapter.ts +618 -0
- package/packages/storage/src/memory-adapter.test.ts +36 -0
- package/packages/storage/src/sqlite-adapter.test.ts +562 -0
- package/packages/storage/src/sqlite-adapter.ts +1058 -0
- package/packages/storage/tsconfig.json +21 -0
- package/packages/storage/vitest.config.ts +9 -0
- package/packages/telemetry/dist/client.d.ts.map +1 -0
- package/packages/telemetry/dist/client.js.map +1 -0
- package/packages/telemetry/dist/config.d.ts.map +1 -0
- package/packages/telemetry/dist/config.js.map +1 -0
- package/packages/telemetry/dist/events.d.ts.map +1 -0
- package/packages/telemetry/dist/events.js.map +1 -0
- package/packages/telemetry/dist/index.d.ts.map +1 -0
- package/packages/telemetry/dist/index.js.map +1 -0
- package/packages/telemetry/dist/machine-id.d.ts.map +1 -0
- package/packages/telemetry/dist/machine-id.js.map +1 -0
- package/packages/telemetry/dist/posthog-config.d.ts.map +1 -0
- package/packages/telemetry/dist/posthog-config.js.map +1 -0
- package/packages/telemetry/package.json +1 -1
- package/packages/telemetry/src/client.ts +158 -0
- package/packages/telemetry/src/config.ts +110 -0
- package/packages/telemetry/src/events.ts +137 -0
- package/packages/telemetry/src/index.ts +46 -0
- package/packages/telemetry/src/machine-id.ts +63 -0
- package/packages/telemetry/src/posthog-config.ts +39 -0
- package/packages/telemetry/tsconfig.json +21 -0
- package/packages/trajectory/dist/index.d.ts.map +1 -0
- package/packages/trajectory/dist/index.js.map +1 -0
- package/packages/trajectory/dist/integration.d.ts.map +1 -0
- package/packages/trajectory/dist/integration.js.map +1 -0
- package/packages/trajectory/package.json +2 -2
- package/packages/trajectory/src/index.ts +1 -0
- package/packages/trajectory/src/integration.ts +1268 -0
- package/packages/trajectory/tsconfig.json +21 -0
- package/packages/trajectory/vitest.config.ts +9 -0
- package/packages/user-directory/dist/index.d.ts.map +1 -0
- package/packages/user-directory/dist/index.js.map +1 -0
- package/packages/user-directory/dist/user-directory.d.ts.map +1 -0
- package/packages/user-directory/dist/user-directory.js.map +1 -0
- package/packages/user-directory/package.json +2 -2
- package/packages/user-directory/src/index.ts +12 -0
- package/packages/user-directory/src/user-directory.ts +393 -0
- package/packages/user-directory/tsconfig.json +21 -0
- package/packages/user-directory/vitest.config.ts +9 -0
- package/packages/utils/dist/cjs/client-helpers.js +127 -0
- package/packages/utils/dist/cjs/command-resolver.js +89 -0
- package/packages/utils/dist/cjs/error-tracking.js +106 -0
- package/packages/utils/dist/cjs/git-remote.js +120 -0
- package/packages/utils/dist/cjs/index.js +40 -0
- package/packages/utils/dist/cjs/logger.js +105 -0
- package/packages/utils/dist/cjs/model-mapping.js +54 -0
- package/packages/utils/dist/cjs/name-generator.js +179 -0
- package/packages/utils/dist/cjs/package.json +3 -0
- package/packages/utils/dist/cjs/precompiled-patterns.js +271 -0
- package/packages/utils/dist/cjs/relay-pty-path.js +143 -0
- package/packages/utils/dist/cjs/update-checker.js +185 -0
- package/packages/utils/dist/client-helpers.d.ts +73 -0
- package/packages/utils/dist/client-helpers.d.ts.map +1 -0
- package/packages/utils/dist/client-helpers.js +130 -0
- package/packages/utils/dist/client-helpers.js.map +1 -0
- package/packages/utils/dist/command-resolver.d.ts.map +1 -0
- package/packages/utils/dist/command-resolver.js.map +1 -0
- package/packages/utils/dist/error-tracking.d.ts.map +1 -0
- package/packages/utils/dist/error-tracking.js.map +1 -0
- package/packages/utils/dist/git-remote.d.ts.map +1 -0
- package/packages/utils/dist/git-remote.js.map +1 -0
- package/packages/utils/dist/index.d.ts +1 -0
- package/packages/utils/dist/index.d.ts.map +1 -0
- package/packages/utils/dist/index.js +1 -0
- package/packages/utils/dist/index.js.map +1 -0
- package/packages/utils/dist/logger.d.ts.map +1 -0
- package/packages/utils/dist/logger.js.map +1 -0
- package/packages/utils/dist/model-mapping.d.ts.map +1 -0
- package/packages/utils/dist/model-mapping.js.map +1 -0
- package/packages/utils/dist/name-generator.d.ts.map +1 -0
- package/packages/utils/dist/name-generator.js.map +1 -0
- package/packages/utils/dist/precompiled-patterns.d.ts.map +1 -0
- package/packages/utils/dist/precompiled-patterns.js.map +1 -0
- package/packages/utils/dist/relay-pty-path.d.ts +11 -5
- package/packages/utils/dist/relay-pty-path.d.ts.map +1 -0
- package/packages/utils/dist/relay-pty-path.js +60 -5
- package/packages/utils/dist/relay-pty-path.js.map +1 -0
- package/packages/utils/dist/update-checker.d.ts.map +1 -0
- package/packages/utils/dist/update-checker.js.map +1 -0
- package/packages/utils/package.json +37 -14
- package/packages/utils/scripts/build-cjs.mjs +24 -0
- package/packages/utils/src/client-helpers.ts +221 -0
- package/packages/utils/src/command-resolver.ts +82 -0
- package/packages/utils/src/error-tracking.ts +189 -0
- package/packages/utils/src/git-remote.ts +143 -0
- package/packages/utils/src/index.ts +10 -0
- package/packages/utils/src/logger.ts +107 -0
- package/packages/utils/src/model-mapping.test.ts +122 -0
- package/packages/utils/src/model-mapping.ts +58 -0
- package/packages/utils/src/name-generator.test.ts +259 -0
- package/packages/utils/src/name-generator.ts +56 -0
- package/packages/utils/src/precompiled-patterns.test.ts +452 -0
- package/packages/utils/src/precompiled-patterns.ts +395 -0
- package/packages/utils/src/relay-pty-path.ts +196 -0
- package/packages/utils/src/update-checker.test.ts +260 -0
- package/packages/utils/src/update-checker.ts +211 -0
- package/packages/utils/tsconfig.json +21 -0
- package/packages/utils/vitest.config.ts +9 -0
- package/packages/wrapper/dist/__fixtures__/claude-outputs.d.ts.map +1 -0
- package/packages/wrapper/dist/__fixtures__/claude-outputs.js.map +1 -0
- package/packages/wrapper/dist/__fixtures__/codex-outputs.d.ts.map +1 -0
- package/packages/wrapper/dist/__fixtures__/codex-outputs.js.map +1 -0
- package/packages/wrapper/dist/__fixtures__/gemini-outputs.d.ts.map +1 -0
- package/packages/wrapper/dist/__fixtures__/gemini-outputs.js.map +1 -0
- package/packages/wrapper/dist/__fixtures__/index.d.ts.map +1 -0
- package/packages/wrapper/dist/__fixtures__/index.js.map +1 -0
- package/packages/wrapper/dist/auth-detection.d.ts.map +1 -0
- package/packages/wrapper/dist/auth-detection.js.map +1 -0
- package/packages/wrapper/dist/base-wrapper.d.ts.map +1 -0
- package/packages/wrapper/dist/base-wrapper.js.map +1 -0
- package/packages/wrapper/dist/client.d.ts.map +1 -0
- package/packages/wrapper/dist/client.js.map +1 -0
- package/packages/wrapper/dist/id-generator.d.ts.map +1 -0
- package/packages/wrapper/dist/id-generator.js.map +1 -0
- package/packages/wrapper/dist/idle-detector.d.ts.map +1 -0
- package/packages/wrapper/dist/idle-detector.js.map +1 -0
- package/packages/wrapper/dist/inbox.d.ts.map +1 -0
- package/packages/wrapper/dist/inbox.js.map +1 -0
- package/packages/wrapper/dist/index.d.ts.map +1 -0
- package/packages/wrapper/dist/index.js.map +1 -0
- package/packages/wrapper/dist/parser.d.ts.map +1 -0
- package/packages/wrapper/dist/parser.js.map +1 -0
- package/packages/wrapper/dist/prompt-composer.d.ts.map +1 -0
- package/packages/wrapper/dist/prompt-composer.js.map +1 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +10 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.js +69 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -0
- package/packages/wrapper/dist/shared.d.ts.map +1 -0
- package/packages/wrapper/dist/shared.js.map +1 -0
- package/packages/wrapper/dist/stuck-detector.d.ts.map +1 -0
- package/packages/wrapper/dist/stuck-detector.js.map +1 -0
- package/packages/wrapper/dist/tmux-resolver.d.ts.map +1 -0
- package/packages/wrapper/dist/tmux-resolver.js.map +1 -0
- package/packages/wrapper/dist/tmux-wrapper.d.ts.map +1 -0
- package/packages/wrapper/dist/tmux-wrapper.js.map +1 -0
- package/packages/wrapper/dist/trajectory-integration.d.ts.map +1 -0
- package/packages/wrapper/dist/trajectory-integration.js.map +1 -0
- package/packages/wrapper/dist/wrapper-types.d.ts.map +1 -0
- package/packages/wrapper/dist/wrapper-types.js.map +1 -0
- package/packages/wrapper/package.json +6 -9
- package/packages/wrapper/src/__fixtures__/claude-outputs.ts +471 -0
- package/packages/wrapper/src/__fixtures__/codex-outputs.ts +99 -0
- package/packages/wrapper/src/__fixtures__/gemini-outputs.ts +151 -0
- package/packages/wrapper/src/__fixtures__/index.ts +47 -0
- package/packages/wrapper/src/auth-detection.ts +244 -0
- package/packages/wrapper/src/base-wrapper.test.ts +589 -0
- package/packages/wrapper/src/base-wrapper.ts +810 -0
- package/packages/wrapper/src/client.test.ts +262 -0
- package/packages/wrapper/src/client.ts +984 -0
- package/packages/wrapper/src/id-generator.test.ts +71 -0
- package/packages/wrapper/src/id-generator.ts +69 -0
- package/packages/wrapper/src/idle-detector.test.ts +418 -0
- package/packages/wrapper/src/idle-detector.ts +384 -0
- package/packages/wrapper/src/inbox.test.ts +233 -0
- package/packages/wrapper/src/inbox.ts +89 -0
- package/packages/wrapper/src/index.ts +170 -0
- package/packages/wrapper/src/parser.regression.test.ts +251 -0
- package/packages/wrapper/src/parser.test.ts +1359 -0
- package/packages/wrapper/src/parser.ts +1477 -0
- package/packages/wrapper/src/prompt-composer.test.ts +219 -0
- package/packages/wrapper/src/prompt-composer.ts +231 -0
- package/packages/wrapper/src/relay-pty-orchestrator.test.ts +1204 -0
- package/packages/wrapper/src/relay-pty-orchestrator.ts +2626 -0
- package/packages/wrapper/src/shared.test.ts +322 -0
- package/packages/wrapper/src/shared.ts +495 -0
- package/packages/wrapper/src/stuck-detector.test.ts +303 -0
- package/packages/wrapper/src/stuck-detector.ts +511 -0
- package/packages/wrapper/src/tmux-resolver.test.ts +104 -0
- package/packages/wrapper/src/tmux-resolver.ts +207 -0
- package/packages/wrapper/src/tmux-wrapper.test.ts +316 -0
- package/packages/wrapper/src/tmux-wrapper.ts +2095 -0
- package/packages/wrapper/src/trajectory-detection.test.ts +151 -0
- package/packages/wrapper/src/trajectory-integration.ts +1261 -0
- package/packages/wrapper/src/wrapper-types.ts +45 -0
- package/packages/wrapper/tsconfig.json +19 -0
- package/packages/wrapper/vitest.config.ts +9 -0
- package/scripts/build-cjs.mjs +23 -0
- package/scripts/postinstall.js +132 -0
- package/.cursor/mcp.json +0 -11
- package/.gitattributes +0 -3
- package/.gitleaks.toml +0 -26
- package/.mcp.json +0 -11
- package/.nvmrc +0 -1
- package/ARCHITECTURE.md +0 -1245
- package/CHANGELOG.md +0 -231
- package/TESTING.md +0 -278
- package/TRAIL_GIT_AUTH_FIX.md +0 -113
- package/scripts/demos/README.md +0 -79
- package/scripts/demos/server-capacity.sh +0 -69
- package/scripts/demos/sprint-planning.sh +0 -73
- package/scripts/hooks/install.sh +0 -16
- package/scripts/hooks/pre-commit +0 -60
- package/scripts/post-publish-verify/README.md +0 -80
- package/scripts/post-publish-verify/run-verify.sh +0 -127
- package/scripts/post-publish-verify/verify-install.sh +0 -249
- package/scripts/stress-test-orchestrator-integration.mts +0 -1366
- package/scripts/stress-test-orchestrator.mjs +0 -584
- package/scripts/stress-test-relay-pty.sh +0 -452
- package/scripts/test-interactive-terminal.sh +0 -248
- package/specs/PRIMITIVES_ROADMAP.md +0 -2154
- package/tests/benchmarks/protocol.bench.ts +0 -310
- package/turbo.json +0 -37
|
@@ -0,0 +1,1376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Manages multiple workspace daemons and provides a unified API for the dashboard.
|
|
5
|
+
* This is the top-level service that runs by default, handling workspace switching
|
|
6
|
+
* and agent management across all connected repositories.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as http from 'http';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import { EventEmitter } from 'events';
|
|
13
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
14
|
+
import {
|
|
15
|
+
createLogger,
|
|
16
|
+
metrics,
|
|
17
|
+
getSupervisor,
|
|
18
|
+
getMemoryMonitor,
|
|
19
|
+
formatBytes,
|
|
20
|
+
type MemoryAlert,
|
|
21
|
+
type MemorySnapshot,
|
|
22
|
+
} from '@agent-relay/resiliency';
|
|
23
|
+
import { Daemon } from './server.js';
|
|
24
|
+
import { AgentSpawner } from '@agent-relay/bridge';
|
|
25
|
+
import { getProjectPaths } from '@agent-relay/config';
|
|
26
|
+
import { getCloudSync, createCloudPersistenceHandler } from './cloud-sync.js';
|
|
27
|
+
import type {
|
|
28
|
+
Workspace,
|
|
29
|
+
Agent,
|
|
30
|
+
DaemonEvent,
|
|
31
|
+
UserSession,
|
|
32
|
+
ProviderType,
|
|
33
|
+
AddWorkspaceRequest,
|
|
34
|
+
SpawnAgentRequest,
|
|
35
|
+
} from './types.js';
|
|
36
|
+
|
|
37
|
+
const logger = createLogger('orchestrator');
|
|
38
|
+
|
|
39
|
+
function generateId(): string {
|
|
40
|
+
return Math.random().toString(36).substring(2, 15);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface OrchestratorConfig {
|
|
44
|
+
/** Port for HTTP/WebSocket API */
|
|
45
|
+
port: number;
|
|
46
|
+
/** Host to bind to */
|
|
47
|
+
host: string;
|
|
48
|
+
/** Data directory for persistence */
|
|
49
|
+
dataDir: string;
|
|
50
|
+
/** Auto-start daemons for workspaces */
|
|
51
|
+
autoStartDaemons: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Determine the default host binding.
|
|
56
|
+
* - In cloud environments, bind to '::' for IPv6+IPv4 dual-stack (required for Fly.io 6PN)
|
|
57
|
+
* - Locally, bind to localhost for security
|
|
58
|
+
* - Can be overridden with AGENT_RELAY_API_HOST env var
|
|
59
|
+
*/
|
|
60
|
+
function getDefaultHost(): string {
|
|
61
|
+
// Explicit override
|
|
62
|
+
if (process.env.AGENT_RELAY_API_HOST) {
|
|
63
|
+
return process.env.AGENT_RELAY_API_HOST;
|
|
64
|
+
}
|
|
65
|
+
// Cloud environment detection - bind to :: for IPv6 + IPv4 dual-stack
|
|
66
|
+
// Fly.io internal network uses IPv6 (fdaa:...), so 0.0.0.0 won't work
|
|
67
|
+
const isCloudEnvironment =
|
|
68
|
+
process.env.FLY_APP_NAME || // Fly.io
|
|
69
|
+
process.env.WORKSPACE_ID || // Agent Relay workspace
|
|
70
|
+
process.env.RELAY_WORKSPACE_ID || // Alternative workspace ID
|
|
71
|
+
process.env.RUNNING_IN_DOCKER === 'true'; // Docker container
|
|
72
|
+
return isCloudEnvironment ? '::' : 'localhost';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const DEFAULT_CONFIG: OrchestratorConfig = {
|
|
76
|
+
port: 3456,
|
|
77
|
+
host: getDefaultHost(),
|
|
78
|
+
dataDir: path.join(process.env.HOME || '', '.agent-relay', 'orchestrator'),
|
|
79
|
+
autoStartDaemons: true,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
interface ManagedWorkspace extends Workspace {
|
|
83
|
+
daemon?: Daemon;
|
|
84
|
+
spawner?: AgentSpawner;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface AgentHealthState {
|
|
88
|
+
key: string;
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
agentName: string;
|
|
91
|
+
pid: number;
|
|
92
|
+
lastHeartbeatAt?: Date;
|
|
93
|
+
lastSampleAt?: Date;
|
|
94
|
+
lastRssBytes?: number;
|
|
95
|
+
lastCpuPercent?: number;
|
|
96
|
+
releasing?: boolean;
|
|
97
|
+
lastCpuAlertAt?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const HEARTBEAT_INTERVAL_MS = 10_000;
|
|
101
|
+
const RESOURCE_ALERT_COOLDOWN_MS = 60_000;
|
|
102
|
+
const parsedCpuThreshold = parseFloat(process.env.AGENT_CPU_ALERT_THRESHOLD || '300');
|
|
103
|
+
const CPU_ALERT_THRESHOLD = Number.isFinite(parsedCpuThreshold) ? parsedCpuThreshold : 300;
|
|
104
|
+
|
|
105
|
+
export class Orchestrator extends EventEmitter {
|
|
106
|
+
private config: OrchestratorConfig;
|
|
107
|
+
private workspaces = new Map<string, ManagedWorkspace>();
|
|
108
|
+
private activeWorkspaceId?: string;
|
|
109
|
+
private server?: http.Server;
|
|
110
|
+
private wss?: WebSocketServer;
|
|
111
|
+
private sessions = new Map<WebSocket, UserSession>();
|
|
112
|
+
private supervisor = getSupervisor({
|
|
113
|
+
autoRestart: true,
|
|
114
|
+
maxRestarts: 5,
|
|
115
|
+
contextPersistence: { enabled: true, autoInjectOnRestart: true },
|
|
116
|
+
});
|
|
117
|
+
private workspacesFile: string;
|
|
118
|
+
|
|
119
|
+
// Track alive status for ping/pong keepalive
|
|
120
|
+
private clientAlive = new WeakMap<WebSocket, boolean>();
|
|
121
|
+
private pingInterval?: NodeJS.Timeout;
|
|
122
|
+
private heartbeatInterval?: NodeJS.Timeout;
|
|
123
|
+
private memoryMonitor = getMemoryMonitor({ checkIntervalMs: 10_000 });
|
|
124
|
+
private agentHealth = new Map<string, AgentHealthState>();
|
|
125
|
+
|
|
126
|
+
// Event handler references for cleanup
|
|
127
|
+
private memorySampleHandler?: (event: { name: string; snapshot: MemorySnapshot }) => void;
|
|
128
|
+
private memoryAlertHandler?: (alert: MemoryAlert) => void;
|
|
129
|
+
|
|
130
|
+
constructor(config: Partial<OrchestratorConfig> = {}) {
|
|
131
|
+
super();
|
|
132
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
133
|
+
this.workspacesFile = path.join(this.config.dataDir, 'workspaces.json');
|
|
134
|
+
|
|
135
|
+
// Ensure data directory exists
|
|
136
|
+
if (!fs.existsSync(this.config.dataDir)) {
|
|
137
|
+
fs.mkdirSync(this.config.dataDir, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Load existing workspaces
|
|
141
|
+
this.loadWorkspaces();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Start the orchestrator
|
|
146
|
+
*/
|
|
147
|
+
async start(): Promise<void> {
|
|
148
|
+
logger.info('Starting orchestrator', {
|
|
149
|
+
port: this.config.port,
|
|
150
|
+
host: this.config.host,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Start supervisor
|
|
154
|
+
this.supervisor.start();
|
|
155
|
+
|
|
156
|
+
// Auto-start daemons for workspaces
|
|
157
|
+
if (this.config.autoStartDaemons) {
|
|
158
|
+
for (const [id, workspace] of this.workspaces) {
|
|
159
|
+
if (fs.existsSync(workspace.path)) {
|
|
160
|
+
await this.startWorkspaceDaemon(id);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Start HTTP server
|
|
166
|
+
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
167
|
+
|
|
168
|
+
// Setup WebSocket
|
|
169
|
+
this.wss = new WebSocketServer({ server: this.server });
|
|
170
|
+
this.wss.on('connection', (ws, req) => this.handleWebSocket(ws, req));
|
|
171
|
+
|
|
172
|
+
// Setup ping/pong keepalive (30 second interval)
|
|
173
|
+
this.pingInterval = setInterval(() => {
|
|
174
|
+
this.wss?.clients.forEach((ws) => {
|
|
175
|
+
if (this.clientAlive.get(ws) === false) {
|
|
176
|
+
logger.info('WebSocket client unresponsive, closing');
|
|
177
|
+
ws.terminate();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.clientAlive.set(ws, false);
|
|
181
|
+
ws.ping();
|
|
182
|
+
});
|
|
183
|
+
}, 30000);
|
|
184
|
+
|
|
185
|
+
this.startHealthMonitoring();
|
|
186
|
+
|
|
187
|
+
return new Promise((resolve) => {
|
|
188
|
+
this.server!.listen(this.config.port, this.config.host, () => {
|
|
189
|
+
logger.info('Orchestrator started', {
|
|
190
|
+
url: `http://${this.config.host}:${this.config.port}`,
|
|
191
|
+
});
|
|
192
|
+
resolve();
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Stop the orchestrator
|
|
199
|
+
*/
|
|
200
|
+
async stop(): Promise<void> {
|
|
201
|
+
logger.info('Stopping orchestrator');
|
|
202
|
+
|
|
203
|
+
// Clear ping interval
|
|
204
|
+
if (this.pingInterval) {
|
|
205
|
+
clearInterval(this.pingInterval);
|
|
206
|
+
this.pingInterval = undefined;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (this.heartbeatInterval) {
|
|
210
|
+
clearInterval(this.heartbeatInterval);
|
|
211
|
+
this.heartbeatInterval = undefined;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Clean up memory monitor event handlers before stopping
|
|
215
|
+
if (this.memorySampleHandler) {
|
|
216
|
+
this.memoryMonitor.off('sample', this.memorySampleHandler);
|
|
217
|
+
this.memorySampleHandler = undefined;
|
|
218
|
+
}
|
|
219
|
+
if (this.memoryAlertHandler) {
|
|
220
|
+
this.memoryMonitor.off('alert', this.memoryAlertHandler);
|
|
221
|
+
this.memoryAlertHandler = undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.memoryMonitor.stop();
|
|
225
|
+
|
|
226
|
+
// Stop all workspace daemons
|
|
227
|
+
for (const [id] of this.workspaces) {
|
|
228
|
+
await this.stopWorkspaceDaemon(id);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Stop supervisor
|
|
232
|
+
this.supervisor.stop();
|
|
233
|
+
|
|
234
|
+
// Close WebSocket connections
|
|
235
|
+
if (this.wss) {
|
|
236
|
+
for (const ws of this.wss.clients) {
|
|
237
|
+
ws.close();
|
|
238
|
+
}
|
|
239
|
+
this.wss.close();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Close HTTP server
|
|
243
|
+
if (this.server) {
|
|
244
|
+
return new Promise((resolve) => {
|
|
245
|
+
this.server!.close(() => {
|
|
246
|
+
logger.info('Orchestrator stopped');
|
|
247
|
+
resolve();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// === Workspace Management ===
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Add a workspace
|
|
257
|
+
*/
|
|
258
|
+
addWorkspace(request: AddWorkspaceRequest): Workspace {
|
|
259
|
+
const resolvedPath = this.resolvePath(request.path);
|
|
260
|
+
|
|
261
|
+
// Check if already exists
|
|
262
|
+
const existing = this.findWorkspaceByPath(resolvedPath);
|
|
263
|
+
if (existing) {
|
|
264
|
+
return existing;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Validate path exists
|
|
268
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
269
|
+
throw new Error(`Path does not exist: ${resolvedPath}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const workspace: ManagedWorkspace = {
|
|
273
|
+
id: generateId(),
|
|
274
|
+
name: request.name || path.basename(resolvedPath),
|
|
275
|
+
path: resolvedPath,
|
|
276
|
+
status: 'inactive',
|
|
277
|
+
provider: request.provider || this.detectProvider(resolvedPath),
|
|
278
|
+
createdAt: new Date(),
|
|
279
|
+
lastActiveAt: new Date(),
|
|
280
|
+
...this.getGitInfo(resolvedPath),
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
this.workspaces.set(workspace.id, workspace);
|
|
284
|
+
this.saveWorkspaces();
|
|
285
|
+
|
|
286
|
+
logger.info('Workspace added', { id: workspace.id, name: workspace.name });
|
|
287
|
+
|
|
288
|
+
this.broadcastEvent({
|
|
289
|
+
type: 'workspace:added',
|
|
290
|
+
workspaceId: workspace.id,
|
|
291
|
+
data: this.toPublicWorkspace(workspace),
|
|
292
|
+
timestamp: new Date(),
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Auto-start daemon
|
|
296
|
+
if (this.config.autoStartDaemons) {
|
|
297
|
+
this.startWorkspaceDaemon(workspace.id).catch((err) => {
|
|
298
|
+
logger.error('Failed to start workspace daemon', { id: workspace.id, error: String(err) });
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return this.toPublicWorkspace(workspace);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Remove a workspace
|
|
307
|
+
*/
|
|
308
|
+
async removeWorkspace(workspaceId: string): Promise<boolean> {
|
|
309
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
310
|
+
if (!workspace) return false;
|
|
311
|
+
|
|
312
|
+
// Stop daemon if running
|
|
313
|
+
await this.stopWorkspaceDaemon(workspaceId);
|
|
314
|
+
|
|
315
|
+
// Clear active if this was active
|
|
316
|
+
if (this.activeWorkspaceId === workspaceId) {
|
|
317
|
+
this.activeWorkspaceId = undefined;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
this.workspaces.delete(workspaceId);
|
|
321
|
+
this.saveWorkspaces();
|
|
322
|
+
|
|
323
|
+
logger.info('Workspace removed', { id: workspaceId });
|
|
324
|
+
|
|
325
|
+
this.broadcastEvent({
|
|
326
|
+
type: 'workspace:removed',
|
|
327
|
+
workspaceId,
|
|
328
|
+
data: { id: workspaceId },
|
|
329
|
+
timestamp: new Date(),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Switch to a workspace
|
|
337
|
+
*/
|
|
338
|
+
async switchWorkspace(workspaceId: string): Promise<Workspace> {
|
|
339
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
340
|
+
if (!workspace) {
|
|
341
|
+
throw new Error(`Workspace not found: ${workspaceId}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const previousId = this.activeWorkspaceId;
|
|
345
|
+
|
|
346
|
+
// Update status
|
|
347
|
+
if (previousId && previousId !== workspaceId) {
|
|
348
|
+
const prev = this.workspaces.get(previousId);
|
|
349
|
+
if (prev) {
|
|
350
|
+
prev.status = 'inactive';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
workspace.status = 'active';
|
|
355
|
+
workspace.lastActiveAt = new Date();
|
|
356
|
+
this.activeWorkspaceId = workspaceId;
|
|
357
|
+
|
|
358
|
+
// Ensure daemon is running
|
|
359
|
+
if (!workspace.daemon?.isRunning) {
|
|
360
|
+
await this.startWorkspaceDaemon(workspaceId);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this.saveWorkspaces();
|
|
364
|
+
|
|
365
|
+
logger.info('Switched workspace', { id: workspaceId, name: workspace.name });
|
|
366
|
+
|
|
367
|
+
this.broadcastEvent({
|
|
368
|
+
type: 'workspace:switched',
|
|
369
|
+
workspaceId,
|
|
370
|
+
data: { previousId, currentId: workspaceId },
|
|
371
|
+
timestamp: new Date(),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
return this.toPublicWorkspace(workspace);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get all workspaces
|
|
379
|
+
*/
|
|
380
|
+
getWorkspaces(): Workspace[] {
|
|
381
|
+
return Array.from(this.workspaces.values()).map((w) => this.toPublicWorkspace(w));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get workspace by ID
|
|
386
|
+
*/
|
|
387
|
+
getWorkspace(workspaceId: string): Workspace | undefined {
|
|
388
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
389
|
+
return workspace ? this.toPublicWorkspace(workspace) : undefined;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get active workspace
|
|
394
|
+
*/
|
|
395
|
+
getActiveWorkspace(): Workspace | undefined {
|
|
396
|
+
if (!this.activeWorkspaceId) return undefined;
|
|
397
|
+
return this.getWorkspace(this.activeWorkspaceId);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// === Agent Management ===
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Spawn an agent in a workspace
|
|
404
|
+
*/
|
|
405
|
+
async spawnAgent(workspaceId: string, request: SpawnAgentRequest): Promise<Agent> {
|
|
406
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
407
|
+
if (!workspace) {
|
|
408
|
+
throw new Error(`Workspace not found: ${workspaceId}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Ensure daemon is running
|
|
412
|
+
if (!workspace.daemon?.isRunning) {
|
|
413
|
+
await this.startWorkspaceDaemon(workspaceId);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Ensure spawner exists
|
|
417
|
+
if (!workspace.spawner) {
|
|
418
|
+
workspace.spawner = new AgentSpawner({
|
|
419
|
+
projectRoot: workspace.path,
|
|
420
|
+
onMarkSpawning: (name) => workspace.daemon?.markSpawning(name),
|
|
421
|
+
onClearSpawning: (name) => workspace.daemon?.clearSpawning(name),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const result = await workspace.spawner.spawn({
|
|
426
|
+
name: request.name,
|
|
427
|
+
cli: this.getCliForProvider(request.provider || workspace.provider),
|
|
428
|
+
task: request.task || '',
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
if (!result.success) {
|
|
432
|
+
throw new Error(result.error || 'Failed to spawn agent');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const agent: Agent = {
|
|
436
|
+
id: generateId(),
|
|
437
|
+
name: request.name,
|
|
438
|
+
workspaceId,
|
|
439
|
+
provider: request.provider || workspace.provider,
|
|
440
|
+
status: 'running',
|
|
441
|
+
pid: result.pid,
|
|
442
|
+
task: request.task,
|
|
443
|
+
spawnedAt: new Date(),
|
|
444
|
+
restartCount: 0,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// Register for health monitoring if we have a PID
|
|
448
|
+
if (result.pid) {
|
|
449
|
+
this.registerAgentHealth(workspaceId, request.name, result.pid);
|
|
450
|
+
} else {
|
|
451
|
+
logger.warn('Agent spawned without PID - health monitoring disabled', {
|
|
452
|
+
workspaceId,
|
|
453
|
+
agentName: request.name,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
logger.info('Agent spawned', { id: agent.id, name: agent.name, workspaceId, pid: result.pid });
|
|
458
|
+
|
|
459
|
+
this.broadcastEvent({
|
|
460
|
+
type: 'agent:spawned',
|
|
461
|
+
workspaceId,
|
|
462
|
+
agentId: agent.id,
|
|
463
|
+
data: agent,
|
|
464
|
+
timestamp: new Date(),
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return agent;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Stop an agent
|
|
472
|
+
*/
|
|
473
|
+
async stopAgent(workspaceId: string, agentName: string): Promise<boolean> {
|
|
474
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
475
|
+
if (!workspace?.spawner) return false;
|
|
476
|
+
|
|
477
|
+
// Mark as releasing BEFORE stopping to prevent crash announcement
|
|
478
|
+
this.markAgentReleasing(workspaceId, agentName);
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
const released = await workspace.spawner.release(agentName);
|
|
482
|
+
|
|
483
|
+
if (released) {
|
|
484
|
+
// Unregister from health monitoring after successful release
|
|
485
|
+
this.unregisterAgentHealth(workspaceId, agentName);
|
|
486
|
+
|
|
487
|
+
this.broadcastEvent({
|
|
488
|
+
type: 'agent:stopped',
|
|
489
|
+
workspaceId,
|
|
490
|
+
data: { name: agentName },
|
|
491
|
+
timestamp: new Date(),
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
logger.info('Agent stopped gracefully', { workspaceId, agentName });
|
|
495
|
+
} else {
|
|
496
|
+
// Release failed - clear the releasing flag
|
|
497
|
+
const health = this.getAgentHealth(workspaceId, agentName);
|
|
498
|
+
if (health) {
|
|
499
|
+
health.releasing = false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return released;
|
|
504
|
+
} catch (err) {
|
|
505
|
+
// Release threw an exception - clean up health tracking to avoid stuck state
|
|
506
|
+
this.unregisterAgentHealth(workspaceId, agentName);
|
|
507
|
+
logger.error('Agent release failed with exception', {
|
|
508
|
+
workspaceId,
|
|
509
|
+
agentName,
|
|
510
|
+
error: String(err),
|
|
511
|
+
});
|
|
512
|
+
throw err;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Get agents in a workspace
|
|
518
|
+
*/
|
|
519
|
+
getAgents(workspaceId: string): Agent[] {
|
|
520
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
521
|
+
if (!workspace?.spawner) return [];
|
|
522
|
+
|
|
523
|
+
return workspace.spawner.getActiveWorkers().map((w) => {
|
|
524
|
+
// Get health data for this agent
|
|
525
|
+
const health = this.getAgentHealth(workspaceId, w.name);
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
id: w.name,
|
|
529
|
+
name: w.name,
|
|
530
|
+
workspaceId,
|
|
531
|
+
provider: this.detectProviderFromCli(w.cli),
|
|
532
|
+
status: 'running' as const,
|
|
533
|
+
pid: w.pid,
|
|
534
|
+
task: w.task,
|
|
535
|
+
spawnedAt: new Date(w.spawnedAt),
|
|
536
|
+
lastHealthCheck: health?.lastHeartbeatAt,
|
|
537
|
+
rssBytes: health?.lastRssBytes,
|
|
538
|
+
cpuPercent: health?.lastCpuPercent,
|
|
539
|
+
restartCount: 0,
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// === Private Methods ===
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Start daemon for a workspace
|
|
548
|
+
*/
|
|
549
|
+
private async startWorkspaceDaemon(workspaceId: string): Promise<void> {
|
|
550
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
551
|
+
if (!workspace) return;
|
|
552
|
+
|
|
553
|
+
if (workspace.daemon?.isRunning) return;
|
|
554
|
+
|
|
555
|
+
try {
|
|
556
|
+
const paths = getProjectPaths(workspace.path);
|
|
557
|
+
|
|
558
|
+
workspace.daemon = new Daemon({
|
|
559
|
+
socketPath: paths.socketPath,
|
|
560
|
+
teamDir: paths.teamDir,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
await workspace.daemon.start();
|
|
564
|
+
workspace.status = 'active';
|
|
565
|
+
|
|
566
|
+
// Create spawner
|
|
567
|
+
workspace.spawner = new AgentSpawner({
|
|
568
|
+
projectRoot: workspace.path,
|
|
569
|
+
onMarkSpawning: (name) => workspace.daemon?.markSpawning(name),
|
|
570
|
+
onClearSpawning: (name) => workspace.daemon?.clearSpawning(name),
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// Set up cloud persistence for session tracking (if cloud sync is enabled)
|
|
574
|
+
const cloudSync = getCloudSync();
|
|
575
|
+
if (cloudSync.isConnected()) {
|
|
576
|
+
const persistenceHandler = createCloudPersistenceHandler(cloudSync, workspace.cloudId);
|
|
577
|
+
workspace.spawner.setCloudPersistence(persistenceHandler);
|
|
578
|
+
logger.info('Cloud persistence enabled for workspace', { id: workspaceId });
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Set up agent death notifications
|
|
582
|
+
workspace.spawner.setOnAgentDeath((info) => {
|
|
583
|
+
// Broadcast to dashboard via WebSocket
|
|
584
|
+
this.broadcastEvent({
|
|
585
|
+
type: 'agent:crashed',
|
|
586
|
+
workspaceId,
|
|
587
|
+
data: {
|
|
588
|
+
name: info.name,
|
|
589
|
+
exitCode: info.exitCode,
|
|
590
|
+
continuityAgentId: info.agentId,
|
|
591
|
+
resumeInstructions: info.resumeInstructions,
|
|
592
|
+
},
|
|
593
|
+
timestamp: new Date(),
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// Broadcast to all connected agents via relay
|
|
597
|
+
const message = info.agentId
|
|
598
|
+
? `AGENT DIED: "${info.name}" has crashed (exit code: ${info.exitCode}). Agent ID: ${info.agentId}. ${info.resumeInstructions}`
|
|
599
|
+
: `AGENT DIED: "${info.name}" has crashed (exit code: ${info.exitCode}).`;
|
|
600
|
+
|
|
601
|
+
workspace.daemon?.broadcastSystemMessage(message, {
|
|
602
|
+
agentName: info.name,
|
|
603
|
+
exitCode: info.exitCode,
|
|
604
|
+
agentId: info.agentId,
|
|
605
|
+
resumeInstructions: info.resumeInstructions,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
logger.warn('Agent died', {
|
|
609
|
+
name: info.name,
|
|
610
|
+
exitCode: info.exitCode,
|
|
611
|
+
agentId: info.agentId,
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
logger.info('Workspace daemon started', { id: workspaceId, socket: paths.socketPath });
|
|
616
|
+
} catch (err) {
|
|
617
|
+
workspace.status = 'error';
|
|
618
|
+
logger.error('Failed to start workspace daemon', { id: workspaceId, error: String(err) });
|
|
619
|
+
throw err;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Stop daemon for a workspace
|
|
625
|
+
*/
|
|
626
|
+
private async stopWorkspaceDaemon(workspaceId: string): Promise<void> {
|
|
627
|
+
const workspace = this.workspaces.get(workspaceId);
|
|
628
|
+
if (!workspace) return;
|
|
629
|
+
|
|
630
|
+
// Mark all agents as releasing to prevent crash announcements
|
|
631
|
+
const workspaceHealth = this.getWorkspaceAgentHealth(workspaceId);
|
|
632
|
+
for (const health of workspaceHealth) {
|
|
633
|
+
this.markAgentReleasing(workspaceId, health.agentName);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Release all agents first
|
|
637
|
+
if (workspace.spawner) {
|
|
638
|
+
await workspace.spawner.releaseAll();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Clean up health monitoring for all agents in this workspace
|
|
642
|
+
for (const health of workspaceHealth) {
|
|
643
|
+
this.unregisterAgentHealth(workspaceId, health.agentName);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Stop daemon
|
|
647
|
+
if (workspace.daemon) {
|
|
648
|
+
await workspace.daemon.stop();
|
|
649
|
+
workspace.daemon = undefined;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
workspace.spawner = undefined;
|
|
653
|
+
workspace.status = 'inactive';
|
|
654
|
+
|
|
655
|
+
logger.info('Workspace daemon stopped', { id: workspaceId });
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Handle HTTP request
|
|
660
|
+
*/
|
|
661
|
+
private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
662
|
+
// CORS
|
|
663
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
664
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
665
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
666
|
+
|
|
667
|
+
if (req.method === 'OPTIONS') {
|
|
668
|
+
res.writeHead(204);
|
|
669
|
+
res.end();
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const url = new URL(req.url || '/', `http://${req.headers.host}`);
|
|
674
|
+
const pathname = url.pathname;
|
|
675
|
+
const method = req.method || 'GET';
|
|
676
|
+
|
|
677
|
+
try {
|
|
678
|
+
let response: { status: number; body: unknown };
|
|
679
|
+
|
|
680
|
+
// Health check
|
|
681
|
+
if (pathname === '/' && method === 'GET') {
|
|
682
|
+
response = { status: 200, body: { status: 'ok', version: '1.0.0' } };
|
|
683
|
+
}
|
|
684
|
+
// Metrics
|
|
685
|
+
else if (pathname === '/metrics' && method === 'GET') {
|
|
686
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
687
|
+
res.writeHead(200);
|
|
688
|
+
res.end(metrics.toPrometheus());
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
// List workspaces
|
|
692
|
+
else if (pathname === '/workspaces' && method === 'GET') {
|
|
693
|
+
response = {
|
|
694
|
+
status: 200,
|
|
695
|
+
body: {
|
|
696
|
+
workspaces: this.getWorkspaces(),
|
|
697
|
+
activeWorkspaceId: this.activeWorkspaceId,
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
// Add workspace
|
|
702
|
+
else if (pathname === '/workspaces' && method === 'POST') {
|
|
703
|
+
const body = await this.parseBody(req);
|
|
704
|
+
const workspace = this.addWorkspace(body as AddWorkspaceRequest);
|
|
705
|
+
response = { status: 201, body: workspace };
|
|
706
|
+
}
|
|
707
|
+
// Get workspace
|
|
708
|
+
else if (pathname.match(/^\/workspaces\/[^/]+$/) && method === 'GET') {
|
|
709
|
+
const id = pathname.split('/')[2];
|
|
710
|
+
const workspace = this.getWorkspace(id);
|
|
711
|
+
response = workspace
|
|
712
|
+
? { status: 200, body: workspace }
|
|
713
|
+
: { status: 404, body: { error: 'Not found' } };
|
|
714
|
+
}
|
|
715
|
+
// Delete workspace
|
|
716
|
+
else if (pathname.match(/^\/workspaces\/[^/]+$/) && method === 'DELETE') {
|
|
717
|
+
const id = pathname.split('/')[2];
|
|
718
|
+
const removed = await this.removeWorkspace(id);
|
|
719
|
+
response = removed
|
|
720
|
+
? { status: 204, body: null }
|
|
721
|
+
: { status: 404, body: { error: 'Not found' } };
|
|
722
|
+
}
|
|
723
|
+
// Switch workspace
|
|
724
|
+
else if (pathname.match(/^\/workspaces\/[^/]+\/switch$/) && method === 'POST') {
|
|
725
|
+
const id = pathname.split('/')[2];
|
|
726
|
+
const workspace = await this.switchWorkspace(id);
|
|
727
|
+
response = { status: 200, body: workspace };
|
|
728
|
+
}
|
|
729
|
+
// List agents in workspace
|
|
730
|
+
else if (pathname.match(/^\/workspaces\/[^/]+\/agents$/) && method === 'GET') {
|
|
731
|
+
const id = pathname.split('/')[2];
|
|
732
|
+
const agents = this.getAgents(id);
|
|
733
|
+
response = { status: 200, body: { agents, workspaceId: id } };
|
|
734
|
+
}
|
|
735
|
+
// Spawn agent
|
|
736
|
+
else if (pathname.match(/^\/workspaces\/[^/]+\/agents$/) && method === 'POST') {
|
|
737
|
+
const id = pathname.split('/')[2];
|
|
738
|
+
const body = await this.parseBody(req);
|
|
739
|
+
const agent = await this.spawnAgent(id, body as SpawnAgentRequest);
|
|
740
|
+
response = { status: 201, body: agent };
|
|
741
|
+
}
|
|
742
|
+
// Stop agent
|
|
743
|
+
else if (pathname.match(/^\/workspaces\/[^/]+\/agents\/[^/]+$/) && method === 'DELETE') {
|
|
744
|
+
const parts = pathname.split('/');
|
|
745
|
+
const workspaceId = parts[2];
|
|
746
|
+
const agentName = parts[4];
|
|
747
|
+
const stopped = await this.stopAgent(workspaceId, agentName);
|
|
748
|
+
response = stopped
|
|
749
|
+
? { status: 204, body: null }
|
|
750
|
+
: { status: 404, body: { error: 'Not found' } };
|
|
751
|
+
}
|
|
752
|
+
// Not found
|
|
753
|
+
else {
|
|
754
|
+
response = { status: 404, body: { error: 'Not found' } };
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
res.setHeader('Content-Type', 'application/json');
|
|
758
|
+
res.writeHead(response.status);
|
|
759
|
+
res.end(response.body ? JSON.stringify(response.body) : '');
|
|
760
|
+
} catch (err) {
|
|
761
|
+
logger.error('Request error', { error: String(err) });
|
|
762
|
+
res.setHeader('Content-Type', 'application/json');
|
|
763
|
+
res.writeHead(500);
|
|
764
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Handle WebSocket connection
|
|
770
|
+
*/
|
|
771
|
+
private handleWebSocket(ws: WebSocket, _req: http.IncomingMessage): void {
|
|
772
|
+
logger.info('WebSocket client connected');
|
|
773
|
+
|
|
774
|
+
// Mark client as alive for ping/pong keepalive
|
|
775
|
+
this.clientAlive.set(ws, true);
|
|
776
|
+
|
|
777
|
+
// Handle pong responses
|
|
778
|
+
ws.on('pong', () => {
|
|
779
|
+
this.clientAlive.set(ws, true);
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
const session: UserSession = {
|
|
783
|
+
userId: 'anonymous',
|
|
784
|
+
githubUsername: 'anonymous',
|
|
785
|
+
connectedAt: new Date(),
|
|
786
|
+
activeWorkspaceId: this.activeWorkspaceId,
|
|
787
|
+
};
|
|
788
|
+
this.sessions.set(ws, session);
|
|
789
|
+
|
|
790
|
+
// Send initial state
|
|
791
|
+
this.sendToClient(ws, {
|
|
792
|
+
type: 'init',
|
|
793
|
+
data: {
|
|
794
|
+
workspaces: this.getWorkspaces(),
|
|
795
|
+
activeWorkspaceId: this.activeWorkspaceId,
|
|
796
|
+
agents: this.activeWorkspaceId ? this.getAgents(this.activeWorkspaceId) : [],
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
ws.on('message', (data) => {
|
|
801
|
+
try {
|
|
802
|
+
const msg = JSON.parse(data.toString());
|
|
803
|
+
this.handleWebSocketMessage(ws, session, msg);
|
|
804
|
+
} catch (err) {
|
|
805
|
+
logger.error('WebSocket message error', { error: String(err) });
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
ws.on('close', () => {
|
|
810
|
+
this.sessions.delete(ws);
|
|
811
|
+
logger.info('WebSocket client disconnected');
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Handle WebSocket message
|
|
817
|
+
*/
|
|
818
|
+
private handleWebSocketMessage(
|
|
819
|
+
ws: WebSocket,
|
|
820
|
+
session: UserSession,
|
|
821
|
+
msg: { type: string; data?: unknown }
|
|
822
|
+
): void {
|
|
823
|
+
switch (msg.type) {
|
|
824
|
+
case 'switch_workspace':
|
|
825
|
+
if (typeof msg.data === 'string') {
|
|
826
|
+
this.switchWorkspace(msg.data)
|
|
827
|
+
.then((workspace) => {
|
|
828
|
+
session.activeWorkspaceId = workspace.id;
|
|
829
|
+
})
|
|
830
|
+
.catch((err) => {
|
|
831
|
+
this.sendToClient(ws, { type: 'error', data: String(err) });
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
break;
|
|
835
|
+
case 'ping':
|
|
836
|
+
this.sendToClient(ws, { type: 'pong' });
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Send to WebSocket client
|
|
843
|
+
*/
|
|
844
|
+
private sendToClient(ws: WebSocket, msg: unknown): void {
|
|
845
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
846
|
+
ws.send(JSON.stringify(msg));
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Broadcast event to all clients
|
|
852
|
+
*/
|
|
853
|
+
private broadcastEvent(event: DaemonEvent): void {
|
|
854
|
+
if (!this.wss) return;
|
|
855
|
+
const msg = JSON.stringify({ type: 'event', data: event });
|
|
856
|
+
for (const ws of this.wss.clients) {
|
|
857
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
858
|
+
ws.send(msg);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Parse request body
|
|
865
|
+
*/
|
|
866
|
+
private parseBody(req: http.IncomingMessage): Promise<unknown> {
|
|
867
|
+
return new Promise((resolve, reject) => {
|
|
868
|
+
let data = '';
|
|
869
|
+
req.on('data', (chunk) => (data += chunk));
|
|
870
|
+
req.on('end', () => {
|
|
871
|
+
try {
|
|
872
|
+
resolve(data ? JSON.parse(data) : {});
|
|
873
|
+
} catch {
|
|
874
|
+
reject(new Error('Invalid JSON'));
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Load workspaces from disk
|
|
882
|
+
*/
|
|
883
|
+
private loadWorkspaces(): void {
|
|
884
|
+
if (!fs.existsSync(this.workspacesFile)) return;
|
|
885
|
+
try {
|
|
886
|
+
const data = JSON.parse(fs.readFileSync(this.workspacesFile, 'utf8'));
|
|
887
|
+
for (const w of data.workspaces || []) {
|
|
888
|
+
this.workspaces.set(w.id, {
|
|
889
|
+
...w,
|
|
890
|
+
createdAt: new Date(w.createdAt),
|
|
891
|
+
lastActiveAt: new Date(w.lastActiveAt),
|
|
892
|
+
status: 'inactive',
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
this.activeWorkspaceId = data.activeWorkspaceId;
|
|
896
|
+
logger.info('Loaded workspaces', { count: this.workspaces.size });
|
|
897
|
+
} catch (err) {
|
|
898
|
+
logger.error('Failed to load workspaces', { error: String(err) });
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Save workspaces to disk
|
|
904
|
+
*/
|
|
905
|
+
private saveWorkspaces(): void {
|
|
906
|
+
try {
|
|
907
|
+
const data = {
|
|
908
|
+
workspaces: Array.from(this.workspaces.values()).map((w) => this.toPublicWorkspace(w)),
|
|
909
|
+
activeWorkspaceId: this.activeWorkspaceId,
|
|
910
|
+
};
|
|
911
|
+
fs.writeFileSync(this.workspacesFile, JSON.stringify(data, null, 2));
|
|
912
|
+
} catch (err) {
|
|
913
|
+
logger.error('Failed to save workspaces', { error: String(err) });
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Find workspace by path
|
|
919
|
+
*/
|
|
920
|
+
private findWorkspaceByPath(path: string): Workspace | undefined {
|
|
921
|
+
const resolved = this.resolvePath(path);
|
|
922
|
+
const workspace = Array.from(this.workspaces.values()).find((w) => w.path === resolved);
|
|
923
|
+
return workspace ? this.toPublicWorkspace(workspace) : undefined;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Resolve path
|
|
928
|
+
*/
|
|
929
|
+
private resolvePath(p: string): string {
|
|
930
|
+
if (p.startsWith('~')) {
|
|
931
|
+
p = path.join(process.env.HOME || '', p.slice(1));
|
|
932
|
+
}
|
|
933
|
+
return path.resolve(p);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Detect provider from workspace
|
|
938
|
+
*/
|
|
939
|
+
private detectProvider(workspacePath: string): ProviderType {
|
|
940
|
+
if (
|
|
941
|
+
fs.existsSync(path.join(workspacePath, 'CLAUDE.md')) ||
|
|
942
|
+
fs.existsSync(path.join(workspacePath, '.claude'))
|
|
943
|
+
) {
|
|
944
|
+
return 'claude';
|
|
945
|
+
}
|
|
946
|
+
if (fs.existsSync(path.join(workspacePath, '.codex'))) {
|
|
947
|
+
return 'codex';
|
|
948
|
+
}
|
|
949
|
+
if (fs.existsSync(path.join(workspacePath, '.gemini'))) {
|
|
950
|
+
return 'gemini';
|
|
951
|
+
}
|
|
952
|
+
return 'generic';
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Detect provider from CLI command
|
|
957
|
+
*/
|
|
958
|
+
private detectProviderFromCli(cli: string): ProviderType {
|
|
959
|
+
if (cli.includes('claude')) return 'claude';
|
|
960
|
+
if (cli.includes('codex')) return 'codex';
|
|
961
|
+
if (cli.includes('gemini')) return 'gemini';
|
|
962
|
+
return 'generic';
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Get CLI command for provider
|
|
967
|
+
*/
|
|
968
|
+
private getCliForProvider(provider: ProviderType): string {
|
|
969
|
+
switch (provider) {
|
|
970
|
+
case 'claude':
|
|
971
|
+
return 'claude';
|
|
972
|
+
case 'codex':
|
|
973
|
+
return 'codex';
|
|
974
|
+
case 'gemini':
|
|
975
|
+
return 'gemini';
|
|
976
|
+
default:
|
|
977
|
+
return 'claude';
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Get git info
|
|
983
|
+
*/
|
|
984
|
+
private getGitInfo(workspacePath: string): { gitRemote?: string; gitBranch?: string } {
|
|
985
|
+
try {
|
|
986
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
987
|
+
const { execSync } = require('child_process');
|
|
988
|
+
const branch = execSync('git branch --show-current', {
|
|
989
|
+
cwd: workspacePath,
|
|
990
|
+
encoding: 'utf8',
|
|
991
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
992
|
+
}).trim();
|
|
993
|
+
let remote: string | undefined;
|
|
994
|
+
try {
|
|
995
|
+
remote = execSync('git remote get-url origin', {
|
|
996
|
+
cwd: workspacePath,
|
|
997
|
+
encoding: 'utf8',
|
|
998
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
999
|
+
}).trim();
|
|
1000
|
+
} catch {
|
|
1001
|
+
// No remote
|
|
1002
|
+
}
|
|
1003
|
+
return { gitRemote: remote, gitBranch: branch };
|
|
1004
|
+
} catch {
|
|
1005
|
+
return {};
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Convert to public workspace (without internal references)
|
|
1011
|
+
*/
|
|
1012
|
+
private toPublicWorkspace(w: ManagedWorkspace): Workspace {
|
|
1013
|
+
return {
|
|
1014
|
+
id: w.id,
|
|
1015
|
+
name: w.name,
|
|
1016
|
+
path: w.path,
|
|
1017
|
+
status: w.status,
|
|
1018
|
+
provider: w.provider,
|
|
1019
|
+
createdAt: w.createdAt,
|
|
1020
|
+
lastActiveAt: w.lastActiveAt,
|
|
1021
|
+
cloudId: w.cloudId,
|
|
1022
|
+
customDomain: w.customDomain,
|
|
1023
|
+
gitRemote: w.gitRemote,
|
|
1024
|
+
gitBranch: w.gitBranch,
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// === Health Monitoring ===
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Start agent health monitoring.
|
|
1032
|
+
* Monitors PIDs for liveness and tracks memory/CPU usage.
|
|
1033
|
+
*/
|
|
1034
|
+
private startHealthMonitoring(): void {
|
|
1035
|
+
// Start the memory monitor
|
|
1036
|
+
this.memoryMonitor.start();
|
|
1037
|
+
|
|
1038
|
+
// Listen for memory samples to update health state
|
|
1039
|
+
// Store handler reference for cleanup
|
|
1040
|
+
this.memorySampleHandler = (event: { name: string; snapshot: MemorySnapshot }) => {
|
|
1041
|
+
const health = this.agentHealth.get(event.name);
|
|
1042
|
+
if (health) {
|
|
1043
|
+
health.lastSampleAt = new Date();
|
|
1044
|
+
health.lastRssBytes = event.snapshot.rssBytes;
|
|
1045
|
+
health.lastCpuPercent = event.snapshot.cpuPercent;
|
|
1046
|
+
|
|
1047
|
+
// Check for high CPU usage and broadcast alert
|
|
1048
|
+
if (event.snapshot.cpuPercent >= CPU_ALERT_THRESHOLD) {
|
|
1049
|
+
this.broadcastResourceAlert(health, 'cpu', event.snapshot.cpuPercent);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
this.memoryMonitor.on('sample', this.memorySampleHandler);
|
|
1054
|
+
|
|
1055
|
+
// Listen for memory alerts and broadcast to agents
|
|
1056
|
+
// Store handler reference for cleanup
|
|
1057
|
+
this.memoryAlertHandler = (alert: MemoryAlert) => {
|
|
1058
|
+
const health = this.agentHealth.get(alert.agentName);
|
|
1059
|
+
if (health && alert.type !== 'recovered') {
|
|
1060
|
+
this.broadcastResourceAlert(health, 'memory', alert.currentRss, alert);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
this.memoryMonitor.on('alert', this.memoryAlertHandler);
|
|
1064
|
+
|
|
1065
|
+
// Start heartbeat interval to check PIDs are alive
|
|
1066
|
+
this.heartbeatInterval = setInterval(() => {
|
|
1067
|
+
this.checkAgentHeartbeats();
|
|
1068
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
1069
|
+
|
|
1070
|
+
logger.info('Health monitoring started', {
|
|
1071
|
+
heartbeatIntervalMs: HEARTBEAT_INTERVAL_MS,
|
|
1072
|
+
cpuAlertThreshold: CPU_ALERT_THRESHOLD,
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Check all registered agents' PIDs are still alive.
|
|
1078
|
+
* If a PID has died unexpectedly, broadcast a crash notification.
|
|
1079
|
+
*/
|
|
1080
|
+
private checkAgentHeartbeats(): void {
|
|
1081
|
+
// Collect crashed agents first to avoid modifying map during iteration
|
|
1082
|
+
const crashedAgents: AgentHealthState[] = [];
|
|
1083
|
+
|
|
1084
|
+
for (const [key, health] of this.agentHealth) {
|
|
1085
|
+
const isAlive = this.isProcessAlive(health.pid);
|
|
1086
|
+
|
|
1087
|
+
if (isAlive) {
|
|
1088
|
+
// Only update heartbeat timestamp for alive processes
|
|
1089
|
+
health.lastHeartbeatAt = new Date();
|
|
1090
|
+
} else if (!health.releasing) {
|
|
1091
|
+
// Agent died unexpectedly - mark for crash handling
|
|
1092
|
+
// Immediately remove from map to prevent duplicate handling on next interval
|
|
1093
|
+
this.agentHealth.delete(key);
|
|
1094
|
+
crashedAgents.push(health);
|
|
1095
|
+
}
|
|
1096
|
+
// If !isAlive && health.releasing, agent is being gracefully stopped - skip
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// Now handle crashes outside the iteration
|
|
1100
|
+
for (const health of crashedAgents) {
|
|
1101
|
+
logger.warn('Agent heartbeat failed - process died', {
|
|
1102
|
+
workspaceId: health.workspaceId,
|
|
1103
|
+
agentName: health.agentName,
|
|
1104
|
+
pid: health.pid,
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
this.handleAgentCrash(health);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Check if a process is alive by sending signal 0.
|
|
1113
|
+
*/
|
|
1114
|
+
private isProcessAlive(pid: number): boolean {
|
|
1115
|
+
try {
|
|
1116
|
+
process.kill(pid, 0);
|
|
1117
|
+
return true;
|
|
1118
|
+
} catch {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Handle an agent crash - unregister and broadcast to other agents.
|
|
1125
|
+
* Note: Agent is already removed from agentHealth map before this is called.
|
|
1126
|
+
*/
|
|
1127
|
+
private handleAgentCrash(health: AgentHealthState): void {
|
|
1128
|
+
const workspace = this.workspaces.get(health.workspaceId);
|
|
1129
|
+
|
|
1130
|
+
// Get crash context from memory monitor for analysis
|
|
1131
|
+
const crashContext = this.memoryMonitor.getCrashContext(health.agentName);
|
|
1132
|
+
|
|
1133
|
+
// Unregister from memory monitor (agent already removed from agentHealth map)
|
|
1134
|
+
this.memoryMonitor.unregister(health.agentName);
|
|
1135
|
+
|
|
1136
|
+
// Broadcast crash to dashboard via WebSocket
|
|
1137
|
+
this.broadcastEvent({
|
|
1138
|
+
type: 'agent:crashed',
|
|
1139
|
+
workspaceId: health.workspaceId,
|
|
1140
|
+
data: {
|
|
1141
|
+
name: health.agentName,
|
|
1142
|
+
pid: health.pid,
|
|
1143
|
+
crashContext: {
|
|
1144
|
+
likelyCause: crashContext.likelyCause,
|
|
1145
|
+
peakMemory: crashContext.peakMemory,
|
|
1146
|
+
averageMemory: crashContext.averageMemory,
|
|
1147
|
+
memoryTrend: crashContext.memoryTrend,
|
|
1148
|
+
analysisNotes: crashContext.analysisNotes,
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
timestamp: new Date(),
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
// Broadcast to all connected agents in the workspace via relay
|
|
1155
|
+
const message = crashContext.likelyCause !== 'unknown'
|
|
1156
|
+
? `AGENT CRASHED: "${health.agentName}" has died unexpectedly (PID: ${health.pid}). Likely cause: ${crashContext.likelyCause}. ${crashContext.analysisNotes.slice(0, 2).join('. ')}`
|
|
1157
|
+
: `AGENT CRASHED: "${health.agentName}" has died unexpectedly (PID: ${health.pid}).`;
|
|
1158
|
+
|
|
1159
|
+
workspace?.daemon?.broadcastSystemMessage(message, {
|
|
1160
|
+
agentName: health.agentName,
|
|
1161
|
+
pid: health.pid,
|
|
1162
|
+
likelyCause: crashContext.likelyCause,
|
|
1163
|
+
crashType: 'heartbeat_failure',
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// Remove the stale agent from the router so connected-agents.json is accurate
|
|
1167
|
+
workspace?.daemon?.removeStaleAgent(health.agentName);
|
|
1168
|
+
|
|
1169
|
+
logger.error('Agent crashed', {
|
|
1170
|
+
workspaceId: health.workspaceId,
|
|
1171
|
+
agentName: health.agentName,
|
|
1172
|
+
pid: health.pid,
|
|
1173
|
+
likelyCause: crashContext.likelyCause,
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Broadcast a resource alert (memory or CPU) to agents.
|
|
1179
|
+
*/
|
|
1180
|
+
private broadcastResourceAlert(
|
|
1181
|
+
health: AgentHealthState,
|
|
1182
|
+
resourceType: 'memory' | 'cpu',
|
|
1183
|
+
currentValue: number,
|
|
1184
|
+
memoryAlert?: MemoryAlert
|
|
1185
|
+
): void {
|
|
1186
|
+
// CPU alert cooldown to avoid spamming
|
|
1187
|
+
if (resourceType === 'cpu') {
|
|
1188
|
+
const now = Date.now();
|
|
1189
|
+
if (health.lastCpuAlertAt && now - health.lastCpuAlertAt < RESOURCE_ALERT_COOLDOWN_MS) {
|
|
1190
|
+
return; // Still in cooldown
|
|
1191
|
+
}
|
|
1192
|
+
health.lastCpuAlertAt = now;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
const workspace = this.workspaces.get(health.workspaceId);
|
|
1196
|
+
|
|
1197
|
+
// Broadcast to dashboard
|
|
1198
|
+
this.broadcastEvent({
|
|
1199
|
+
type: 'agent:resource-alert',
|
|
1200
|
+
workspaceId: health.workspaceId,
|
|
1201
|
+
agentId: health.agentName,
|
|
1202
|
+
data: {
|
|
1203
|
+
name: health.agentName,
|
|
1204
|
+
resourceType,
|
|
1205
|
+
currentValue,
|
|
1206
|
+
alertLevel: memoryAlert?.type ?? 'high_cpu',
|
|
1207
|
+
message: memoryAlert?.message ??
|
|
1208
|
+
`Agent "${health.agentName}" is running at ${currentValue.toFixed(1)}% CPU`,
|
|
1209
|
+
recommendation: memoryAlert?.recommendation ??
|
|
1210
|
+
'Consider reducing workload or checking for runaway processes',
|
|
1211
|
+
},
|
|
1212
|
+
timestamp: new Date(),
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
// Broadcast to agents
|
|
1216
|
+
const message = resourceType === 'memory'
|
|
1217
|
+
? `RESOURCE ALERT: "${health.agentName}" memory usage is ${memoryAlert?.type ?? 'high'} (${formatBytes(currentValue)}). ${memoryAlert?.recommendation ?? ''}`
|
|
1218
|
+
: `RESOURCE ALERT: "${health.agentName}" is running at ${currentValue.toFixed(1)}% CPU. Consider reducing workload.`;
|
|
1219
|
+
|
|
1220
|
+
workspace?.daemon?.broadcastSystemMessage(message, {
|
|
1221
|
+
agentName: health.agentName,
|
|
1222
|
+
resourceType,
|
|
1223
|
+
alertLevel: memoryAlert?.type ?? 'high_cpu',
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
logger.warn('Resource alert', {
|
|
1227
|
+
workspaceId: health.workspaceId,
|
|
1228
|
+
agentName: health.agentName,
|
|
1229
|
+
resourceType,
|
|
1230
|
+
currentValue: resourceType === 'memory' ? formatBytes(currentValue) : `${currentValue.toFixed(1)}%`,
|
|
1231
|
+
alertLevel: memoryAlert?.type ?? 'high_cpu',
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Register an agent for health monitoring.
|
|
1237
|
+
*/
|
|
1238
|
+
private registerAgentHealth(workspaceId: string, agentName: string, pid: number): void {
|
|
1239
|
+
const key = `${workspaceId}:${agentName}`;
|
|
1240
|
+
|
|
1241
|
+
// Guard against double-registration - update PID instead
|
|
1242
|
+
if (this.agentHealth.has(key)) {
|
|
1243
|
+
logger.warn('Agent already registered for health monitoring, updating PID', {
|
|
1244
|
+
workspaceId,
|
|
1245
|
+
agentName,
|
|
1246
|
+
newPid: pid,
|
|
1247
|
+
});
|
|
1248
|
+
this.updateAgentHealthPid(workspaceId, agentName, pid);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
this.agentHealth.set(key, {
|
|
1253
|
+
key,
|
|
1254
|
+
workspaceId,
|
|
1255
|
+
agentName,
|
|
1256
|
+
pid,
|
|
1257
|
+
lastHeartbeatAt: new Date(),
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
// Register with memory monitor
|
|
1261
|
+
this.memoryMonitor.register(agentName, pid);
|
|
1262
|
+
|
|
1263
|
+
logger.info('Agent registered for health monitoring', {
|
|
1264
|
+
workspaceId,
|
|
1265
|
+
agentName,
|
|
1266
|
+
pid,
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/**
|
|
1271
|
+
* Update PID for an agent (after restart).
|
|
1272
|
+
*
|
|
1273
|
+
* This method is intended for agent restart scenarios where the agent process
|
|
1274
|
+
* is restarted with a new PID but should maintain continuity in health tracking.
|
|
1275
|
+
* Currently unused but reserved for future auto-restart functionality.
|
|
1276
|
+
*
|
|
1277
|
+
* @param workspaceId - The workspace ID
|
|
1278
|
+
* @param agentName - The agent name
|
|
1279
|
+
* @param newPid - The new process ID after restart
|
|
1280
|
+
*/
|
|
1281
|
+
private updateAgentHealthPid(workspaceId: string, agentName: string, newPid: number): void {
|
|
1282
|
+
const key = `${workspaceId}:${agentName}`;
|
|
1283
|
+
const health = this.agentHealth.get(key);
|
|
1284
|
+
|
|
1285
|
+
if (health) {
|
|
1286
|
+
health.pid = newPid;
|
|
1287
|
+
health.releasing = false;
|
|
1288
|
+
health.lastHeartbeatAt = new Date();
|
|
1289
|
+
this.memoryMonitor.updatePid(agentName, newPid);
|
|
1290
|
+
|
|
1291
|
+
logger.info('Agent health PID updated', {
|
|
1292
|
+
workspaceId,
|
|
1293
|
+
agentName,
|
|
1294
|
+
newPid,
|
|
1295
|
+
});
|
|
1296
|
+
} else {
|
|
1297
|
+
// Register new
|
|
1298
|
+
this.registerAgentHealth(workspaceId, agentName, newPid);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
/**
|
|
1303
|
+
* Mark an agent as releasing (to avoid crash announcement).
|
|
1304
|
+
*/
|
|
1305
|
+
private markAgentReleasing(workspaceId: string, agentName: string): void {
|
|
1306
|
+
const key = `${workspaceId}:${agentName}`;
|
|
1307
|
+
const health = this.agentHealth.get(key);
|
|
1308
|
+
|
|
1309
|
+
if (health) {
|
|
1310
|
+
health.releasing = true;
|
|
1311
|
+
logger.debug('Agent marked as releasing', { workspaceId, agentName });
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* Unregister an agent from health monitoring.
|
|
1317
|
+
*/
|
|
1318
|
+
private unregisterAgentHealth(workspaceId: string, agentName: string): void {
|
|
1319
|
+
const key = `${workspaceId}:${agentName}`;
|
|
1320
|
+
this.agentHealth.delete(key);
|
|
1321
|
+
this.memoryMonitor.unregister(agentName);
|
|
1322
|
+
|
|
1323
|
+
logger.debug('Agent unregistered from health monitoring', {
|
|
1324
|
+
workspaceId,
|
|
1325
|
+
agentName,
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Get health state for an agent.
|
|
1331
|
+
*/
|
|
1332
|
+
private getAgentHealth(workspaceId: string, agentName: string): AgentHealthState | undefined {
|
|
1333
|
+
return this.agentHealth.get(`${workspaceId}:${agentName}`);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* Get health states for all agents in a workspace.
|
|
1338
|
+
*/
|
|
1339
|
+
private getWorkspaceAgentHealth(workspaceId: string): AgentHealthState[] {
|
|
1340
|
+
return Array.from(this.agentHealth.values()).filter((h) => h.workspaceId === workspaceId);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
let orchestratorInstance: Orchestrator | undefined;
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Start the orchestrator
|
|
1348
|
+
*/
|
|
1349
|
+
export async function startOrchestrator(
|
|
1350
|
+
config: Partial<OrchestratorConfig> = {}
|
|
1351
|
+
): Promise<Orchestrator> {
|
|
1352
|
+
if (orchestratorInstance) {
|
|
1353
|
+
return orchestratorInstance;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
orchestratorInstance = new Orchestrator(config);
|
|
1357
|
+
await orchestratorInstance.start();
|
|
1358
|
+
return orchestratorInstance;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Stop the orchestrator
|
|
1363
|
+
*/
|
|
1364
|
+
export async function stopOrchestrator(): Promise<void> {
|
|
1365
|
+
if (orchestratorInstance) {
|
|
1366
|
+
await orchestratorInstance.stop();
|
|
1367
|
+
orchestratorInstance = undefined;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Get orchestrator instance
|
|
1373
|
+
*/
|
|
1374
|
+
export function getOrchestrator(): Orchestrator | undefined {
|
|
1375
|
+
return orchestratorInstance;
|
|
1376
|
+
}
|