macro-agent 0.1.0 → 0.1.1
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/.claude/settings.local.json +3 -1
- package/.sudocode/specs.jsonl +4 -0
- package/CLAUDE.md +16 -14
- package/README.md +11 -29
- package/dist/acp/macro-agent.d.ts +15 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +131 -35
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +32 -1
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +65 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +464 -183
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +37 -6
- package/dist/api/server.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token.d.ts +41 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +73 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli/acp.d.ts +2 -23
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +127 -61
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +147 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +268 -181
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/parse-args.d.ts +20 -0
- package/dist/cli/parse-args.d.ts.map +1 -0
- package/dist/cli/parse-args.js +43 -0
- package/dist/cli/parse-args.js.map +1 -0
- package/dist/cli/stable-instance-id.d.ts +8 -0
- package/dist/cli/stable-instance-id.d.ts.map +1 -0
- package/dist/cli/stable-instance-id.js +14 -0
- package/dist/cli/stable-instance-id.js.map +1 -0
- package/dist/config/project-config.d.ts +74 -7
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +123 -20
- package/dist/config/project-config.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +17 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +384 -23
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/connection-manager.d.ts.map +1 -1
- package/dist/map/adapter/connection-manager.js +3 -0
- package/dist/map/adapter/connection-manager.js.map +1 -1
- package/dist/map/adapter/event-log.d.ts +87 -0
- package/dist/map/adapter/event-log.d.ts.map +1 -0
- package/dist/map/adapter/event-log.js +122 -0
- package/dist/map/adapter/event-log.js.map +1 -0
- package/dist/map/adapter/event-translator.js +6 -6
- package/dist/map/adapter/event-translator.js.map +1 -1
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
- package/dist/map/adapter/extensions/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +34 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
- package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
- package/dist/map/adapter/extensions/rename.d.ts +29 -0
- package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
- package/dist/map/adapter/extensions/rename.js +49 -0
- package/dist/map/adapter/extensions/rename.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -1
- package/dist/map/adapter/extensions/task.js +10 -0
- package/dist/map/adapter/extensions/task.js.map +1 -1
- package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.js +67 -0
- package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
- package/dist/map/adapter/index.d.ts +2 -1
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +8 -2
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +2 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +3 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +258 -35
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
- package/dist/map/adapter/subscription-manager.js +5 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/map/adapter/types.d.ts +2 -0
- package/dist/map/adapter/types.d.ts.map +1 -1
- package/dist/mcp/map-client.d.ts +39 -0
- package/dist/mcp/map-client.d.ts.map +1 -0
- package/dist/mcp/map-client.js +129 -0
- package/dist/mcp/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +14 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +113 -85
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/types.d.ts +9 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/metrics/metrics.js +1 -1
- package/dist/metrics/metrics.js.map +1 -1
- package/dist/roles/capabilities.d.ts +3 -1
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +17 -7
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/config-loader.d.ts +6 -6
- package/dist/roles/config-loader.d.ts.map +1 -1
- package/dist/roles/config-loader.js +6 -6
- package/dist/roles/config-loader.js.map +1 -1
- package/dist/roles/registry.d.ts +2 -2
- package/dist/roles/registry.js +2 -2
- package/dist/server/combined-server.d.ts +20 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +107 -8
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +2 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +69 -20
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +18 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/dist/store/types/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/task/backend/index.d.ts +47 -29
- package/dist/task/backend/index.d.ts.map +1 -1
- package/dist/task/backend/index.js +109 -71
- package/dist/task/backend/index.js.map +1 -1
- package/dist/task/backend/memory.d.ts +1 -0
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +3 -0
- package/dist/task/backend/memory.js.map +1 -1
- package/dist/task/backend/opentasks/backend.d.ts +140 -0
- package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
- package/dist/task/backend/opentasks/backend.js +1023 -0
- package/dist/task/backend/opentasks/backend.js.map +1 -0
- package/dist/task/backend/opentasks/client.d.ts +337 -0
- package/dist/task/backend/opentasks/client.d.ts.map +1 -0
- package/dist/task/backend/opentasks/client.js +225 -0
- package/dist/task/backend/opentasks/client.js.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.js +195 -0
- package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
- package/dist/task/backend/opentasks/index.d.ts +21 -0
- package/dist/task/backend/opentasks/index.d.ts.map +1 -0
- package/dist/task/backend/opentasks/index.js +21 -0
- package/dist/task/backend/opentasks/index.js.map +1 -0
- package/dist/task/backend/opentasks/mapping.d.ts +48 -0
- package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
- package/dist/task/backend/opentasks/mapping.js +77 -0
- package/dist/task/backend/opentasks/mapping.js.map +1 -0
- package/dist/task/backend/types.d.ts +33 -53
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js +7 -11
- package/dist/task/backend/types.js.map +1 -1
- package/dist/task/backend/unified-tool-provider.d.ts +57 -0
- package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
- package/dist/task/backend/unified-tool-provider.js +623 -0
- package/dist/task/backend/unified-tool-provider.js.map +1 -0
- package/dist/teams/team-loader.d.ts +2 -2
- package/dist/teams/team-loader.js +3 -3
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-runtime.d.ts.map +1 -1
- package/dist/teams/team-runtime.js +2 -0
- package/dist/teams/team-runtime.js.map +1 -1
- package/docs/architecture.md +7 -6
- package/docs/configuration.md +26 -62
- package/docs/implementation-details.md +5 -5
- package/docs/implementation-summary.md +17 -17
- package/docs/plan-self-driving-support.md +4 -4
- package/docs/spec-self-driving-support.md +10 -10
- package/docs/team-templates.md +2 -2
- package/docs/teams.md +3 -3
- package/docs/troubleshooting.md +10 -11
- package/package.json +6 -4
- package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
- package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
- package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
- package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
- package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
- package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
- package/src/acp/__tests__/integration.test.ts +56 -31
- package/src/acp/__tests__/macro-agent.test.ts +16 -7
- package/src/acp/macro-agent.ts +170 -36
- package/src/acp/types.ts +46 -1
- package/src/agent/__tests__/agent-manager.test.ts +228 -2
- package/src/agent/agent-manager.ts +714 -261
- package/src/agent/types.ts +3 -1
- package/src/api/server.ts +41 -7
- package/src/auth/__tests__/token.test.ts +100 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/token.ts +82 -0
- package/src/cli/__tests__/acp.test.ts +1 -1
- package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
- package/src/cli/acp.ts +130 -72
- package/src/cli/index.ts +120 -14
- package/src/cli/mcp.ts +311 -207
- package/src/cli/parse-args.ts +54 -0
- package/src/cli/stable-instance-id.ts +14 -0
- package/src/config/project-config.ts +190 -27
- package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
- package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
- package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
- package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
- package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
- package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
- package/src/map/adapter/__tests__/event-log.test.ts +527 -0
- package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
- package/src/map/adapter/__tests__/extensions.test.ts +408 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
- package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
- package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
- package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
- package/src/map/adapter/acp-over-map.ts +678 -66
- package/src/map/adapter/connection-manager.ts +3 -0
- package/src/map/adapter/event-log.ts +208 -0
- package/src/map/adapter/event-translator.ts +6 -6
- package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
- package/src/map/adapter/extensions/index.ts +60 -0
- package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
- package/src/map/adapter/extensions/task.ts +11 -0
- package/src/map/adapter/extensions/update-metadata.ts +126 -0
- package/src/map/adapter/index.ts +28 -0
- package/src/map/adapter/interface.ts +2 -0
- package/src/map/adapter/map-adapter.ts +312 -47
- package/src/map/adapter/subscription-manager.ts +5 -1
- package/src/map/adapter/types.ts +2 -0
- package/src/mcp/__tests__/map-client.test.ts +386 -0
- package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
- package/src/mcp/__tests__/mcp-server.test.ts +100 -1
- package/src/mcp/map-client.ts +177 -0
- package/src/mcp/mcp-server.ts +191 -100
- package/src/mcp/types.ts +6 -1
- package/src/metrics/metrics.ts +1 -1
- package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
- package/src/roles/__tests__/config-loader.test.ts +7 -7
- package/src/roles/capabilities.ts +17 -7
- package/src/roles/config-loader.ts +6 -6
- package/src/roles/registry.ts +2 -2
- package/src/server/__tests__/combined-server.test.ts +94 -21
- package/src/server/combined-server.ts +189 -33
- package/src/steering/__tests__/steering-integration.test.ts +1 -1
- package/src/store/__tests__/event-store.test.ts +196 -1
- package/src/store/__tests__/instance.test.ts +3 -3
- package/src/store/event-store.ts +80 -21
- package/src/store/types/agents.ts +15 -0
- package/src/store/types/events.ts +1 -1
- package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
- package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
- package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
- package/src/task/backend/index.ts +156 -106
- package/src/task/backend/memory.ts +4 -0
- package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
- package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
- package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
- package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
- package/src/task/backend/opentasks/backend.ts +1323 -0
- package/src/task/backend/opentasks/client.ts +652 -0
- package/src/task/backend/opentasks/daemon-manager.ts +253 -0
- package/src/task/backend/opentasks/index.ts +69 -0
- package/src/task/backend/opentasks/mapping.ts +94 -0
- package/src/task/backend/types.ts +42 -66
- package/src/task/backend/unified-tool-provider.ts +779 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
- package/src/teams/team-loader.ts +3 -3
- package/src/teams/team-runtime.ts +2 -0
- package/test_fixtures/README.md +2 -3
- package/test_fixtures/fixtures/index.ts +0 -3
- package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
- package/test_fixtures/fixtures/repos/index.ts +1 -3
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
- package/test_fixtures/fixtures/repos/types.ts +0 -11
- package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
- package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
- package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
- package/vitest.config.ts +1 -1
- package/vitest.e2e.config.ts +1 -1
- package/vitest.setup.ts +1 -30
- package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
- package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
- package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
- package/.macro-agent/teams/self-driving/team.yaml +0 -103
- package/.macro-agent/teams/structured/prompts/developer.md +0 -26
- package/.macro-agent/teams/structured/prompts/lead.md +0 -25
- package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
- package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
- package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
- package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
- package/.macro-agent/teams/structured/team.yaml +0 -89
- package/docs/sudocode-integration.md +0 -383
- package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
- package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
- package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
- package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
- package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
- package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
- package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
- package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
- package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
- package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
- package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
- package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
- package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
- package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
- package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
- package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
- package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
- package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
- package/src/task/backend/sudocode/backend.ts +0 -1237
- package/src/task/backend/sudocode/client.ts +0 -515
- package/src/task/backend/sudocode/index.ts +0 -120
- package/src/task/backend/sudocode/mapping.ts +0 -93
- package/src/task/backend/sudocode/server-client.ts +0 -522
- package/src/task/backend/sudocode/standalone-client.ts +0 -623
- package/src/task/backend/sudocode/sync-policy.ts +0 -387
- package/src/task/backend/sudocode/tools.ts +0 -896
- package/src/task/backend/tool-provider.ts +0 -506
- package/test_fixtures/fixtures/sudocode/index.ts +0 -29
- package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
- package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
package/src/agent/types.ts
CHANGED
package/src/api/server.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type InjectionDeps,
|
|
57
57
|
} from "../steering/index.js";
|
|
58
58
|
import type { AgentId } from "../store/types/index.js";
|
|
59
|
+
import { secureCompare } from "../auth/token.js";
|
|
59
60
|
|
|
60
61
|
// ─────────────────────────────────────────────────────────────────
|
|
61
62
|
// Server Configuration
|
|
@@ -73,6 +74,9 @@ export interface APIServerConfig {
|
|
|
73
74
|
|
|
74
75
|
/** Grace period in milliseconds for in-flight work during shutdown (default: 5000) */
|
|
75
76
|
shutdownGracePeriodMs?: number;
|
|
77
|
+
|
|
78
|
+
/** Server token for Bearer auth on API routes. When set, all routes except /health require auth. */
|
|
79
|
+
serverToken?: string;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export interface APIServices {
|
|
@@ -351,7 +355,7 @@ export function createAPIServer(
|
|
|
351
355
|
services: APIServices,
|
|
352
356
|
config: APIServerConfig = {}
|
|
353
357
|
): APIServer {
|
|
354
|
-
const { port = 3000, host = "localhost", cors = true, shutdownGracePeriodMs = 5000 } = config;
|
|
358
|
+
const { port = 3000, host = "localhost", cors = true, shutdownGracePeriodMs = 5000, serverToken } = config;
|
|
355
359
|
const { eventStore, agentManager, taskManager } = services;
|
|
356
360
|
|
|
357
361
|
// Server state
|
|
@@ -374,12 +378,27 @@ export function createAPIServer(
|
|
|
374
378
|
if (cors) {
|
|
375
379
|
app.use((_req: Request, res: Response, next: NextFunction) => {
|
|
376
380
|
res.header("Access-Control-Allow-Origin", "*");
|
|
377
|
-
res.header("Access-Control-Allow-Headers", "Content-Type");
|
|
381
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
378
382
|
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
|
379
383
|
next();
|
|
380
384
|
});
|
|
381
385
|
}
|
|
382
386
|
|
|
387
|
+
// Bearer token auth middleware (skip /health)
|
|
388
|
+
if (serverToken) {
|
|
389
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
390
|
+
if (req.path === "/health") return next();
|
|
391
|
+
const authHeader = req.headers.authorization;
|
|
392
|
+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : undefined;
|
|
393
|
+
if (!token || !secureCompare(token, serverToken)) {
|
|
394
|
+
const error: APIError = { error: "Unauthorized", code: "AUTH_REQUIRED" };
|
|
395
|
+
res.status(401).json(error);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
next();
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
383
402
|
// ─────────────────────────────────────────────────────────────────
|
|
384
403
|
// Helper Functions
|
|
385
404
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -438,7 +457,7 @@ export function createAPIServer(
|
|
|
438
457
|
case "spawn":
|
|
439
458
|
summary = `Agent ${event.payload.agent_id} spawned`;
|
|
440
459
|
break;
|
|
441
|
-
case "
|
|
460
|
+
case "stop":
|
|
442
461
|
summary = `Agent terminated: ${event.payload.reason}`;
|
|
443
462
|
break;
|
|
444
463
|
case "status":
|
|
@@ -1241,9 +1260,9 @@ export function createAPIServer(
|
|
|
1241
1260
|
*/
|
|
1242
1261
|
export function createAPIApp(
|
|
1243
1262
|
services: Pick<APIServices, "eventStore" | "agentManager" | "taskManager" | "messageRouter"> & Pick<Partial<APIServices>, "mailService" | "conversationMap">,
|
|
1244
|
-
config: { cors?: boolean } = {}
|
|
1263
|
+
config: { cors?: boolean; serverToken?: string } = {}
|
|
1245
1264
|
): Express {
|
|
1246
|
-
const { cors = true } = config;
|
|
1265
|
+
const { cors = true, serverToken } = config;
|
|
1247
1266
|
const { agentManager, taskManager, messageRouter } = services;
|
|
1248
1267
|
|
|
1249
1268
|
// Create shared state
|
|
@@ -1262,12 +1281,27 @@ export function createAPIApp(
|
|
|
1262
1281
|
if (cors) {
|
|
1263
1282
|
app.use((_req: Request, res: Response, next: NextFunction) => {
|
|
1264
1283
|
res.header("Access-Control-Allow-Origin", "*");
|
|
1265
|
-
res.header("Access-Control-Allow-Headers", "Content-Type");
|
|
1284
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
1266
1285
|
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
|
1267
1286
|
next();
|
|
1268
1287
|
});
|
|
1269
1288
|
}
|
|
1270
1289
|
|
|
1290
|
+
// Bearer token auth middleware (skip /health)
|
|
1291
|
+
if (serverToken) {
|
|
1292
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
1293
|
+
if (req.path === "/health") return next();
|
|
1294
|
+
const authHeader = req.headers.authorization;
|
|
1295
|
+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : undefined;
|
|
1296
|
+
if (!token || !secureCompare(token, serverToken)) {
|
|
1297
|
+
const error: APIError = { error: "Unauthorized", code: "AUTH_REQUIRED" };
|
|
1298
|
+
res.status(401).json(error);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
next();
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1271
1305
|
// ─────────────────────────────────────────────────────────────────
|
|
1272
1306
|
// Helper Functions
|
|
1273
1307
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -1326,7 +1360,7 @@ export function createAPIApp(
|
|
|
1326
1360
|
case "spawn":
|
|
1327
1361
|
summary = `Agent ${event.payload.agent_id} spawned`;
|
|
1328
1362
|
break;
|
|
1329
|
-
case "
|
|
1363
|
+
case "stop":
|
|
1330
1364
|
summary = `Agent terminated: ${event.payload.reason}`;
|
|
1331
1365
|
break;
|
|
1332
1366
|
case "status":
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { generateToken, secureCompare, AgentTokenManager } from "../token.js";
|
|
3
|
+
|
|
4
|
+
describe("generateToken", () => {
|
|
5
|
+
it("generates a hex string of expected length", () => {
|
|
6
|
+
const token = generateToken();
|
|
7
|
+
// 32 bytes = 64 hex chars
|
|
8
|
+
expect(token).toHaveLength(64);
|
|
9
|
+
expect(token).toMatch(/^[0-9a-f]+$/);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("generates different tokens each time", () => {
|
|
13
|
+
const a = generateToken();
|
|
14
|
+
const b = generateToken();
|
|
15
|
+
expect(a).not.toBe(b);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("respects custom byte length", () => {
|
|
19
|
+
const token = generateToken(16);
|
|
20
|
+
expect(token).toHaveLength(32); // 16 bytes = 32 hex chars
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("secureCompare", () => {
|
|
25
|
+
it("returns true for identical strings", () => {
|
|
26
|
+
expect(secureCompare("abc123", "abc123")).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns false for different strings of same length", () => {
|
|
30
|
+
expect(secureCompare("abc123", "xyz789")).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns false for different length strings", () => {
|
|
34
|
+
expect(secureCompare("short", "longer-string")).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns true for empty strings", () => {
|
|
38
|
+
expect(secureCompare("", "")).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("AgentTokenManager", () => {
|
|
43
|
+
it("creates and verifies a token", () => {
|
|
44
|
+
const mgr = new AgentTokenManager();
|
|
45
|
+
const token = mgr.createToken("agent-1");
|
|
46
|
+
expect(token).toHaveLength(64);
|
|
47
|
+
expect(mgr.verifyToken("agent-1", token)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("rejects wrong token", () => {
|
|
51
|
+
const mgr = new AgentTokenManager();
|
|
52
|
+
mgr.createToken("agent-1");
|
|
53
|
+
expect(mgr.verifyToken("agent-1", "wrong-token")).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("rejects unknown agent", () => {
|
|
57
|
+
const mgr = new AgentTokenManager();
|
|
58
|
+
expect(mgr.verifyToken("unknown", "any-token")).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("revokes a token", () => {
|
|
62
|
+
const mgr = new AgentTokenManager();
|
|
63
|
+
const token = mgr.createToken("agent-1");
|
|
64
|
+
expect(mgr.revokeToken("agent-1")).toBe(true);
|
|
65
|
+
expect(mgr.verifyToken("agent-1", token)).toBe(false);
|
|
66
|
+
expect(mgr.hasToken("agent-1")).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("revoke returns false for unknown agent", () => {
|
|
70
|
+
const mgr = new AgentTokenManager();
|
|
71
|
+
expect(mgr.revokeToken("unknown")).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("hasToken returns correct state", () => {
|
|
75
|
+
const mgr = new AgentTokenManager();
|
|
76
|
+
expect(mgr.hasToken("agent-1")).toBe(false);
|
|
77
|
+
mgr.createToken("agent-1");
|
|
78
|
+
expect(mgr.hasToken("agent-1")).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("createToken overwrites previous token", () => {
|
|
82
|
+
const mgr = new AgentTokenManager();
|
|
83
|
+
const token1 = mgr.createToken("agent-1");
|
|
84
|
+
const token2 = mgr.createToken("agent-1");
|
|
85
|
+
expect(token1).not.toBe(token2);
|
|
86
|
+
expect(mgr.verifyToken("agent-1", token1)).toBe(false);
|
|
87
|
+
expect(mgr.verifyToken("agent-1", token2)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("manages multiple agents independently", () => {
|
|
91
|
+
const mgr = new AgentTokenManager();
|
|
92
|
+
const t1 = mgr.createToken("agent-1");
|
|
93
|
+
const t2 = mgr.createToken("agent-2");
|
|
94
|
+
expect(mgr.verifyToken("agent-1", t1)).toBe(true);
|
|
95
|
+
expect(mgr.verifyToken("agent-2", t2)).toBe(true);
|
|
96
|
+
// Cross-validation fails
|
|
97
|
+
expect(mgr.verifyToken("agent-1", t2)).toBe(false);
|
|
98
|
+
expect(mgr.verifyToken("agent-2", t1)).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generateToken, secureCompare, AgentTokenManager } from "./token.js";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Token Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides token generation, secure comparison, and per-agent
|
|
5
|
+
* token management for the macro-agent server.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as crypto from "crypto";
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Token Generation
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate a cryptographically random hex token.
|
|
16
|
+
*/
|
|
17
|
+
export function generateToken(bytes = 32): string {
|
|
18
|
+
return crypto.randomBytes(bytes).toString("hex");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Secure Comparison
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Constant-time string comparison to prevent timing attacks.
|
|
27
|
+
*/
|
|
28
|
+
export function secureCompare(a: string, b: string): boolean {
|
|
29
|
+
if (a.length !== b.length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const bufA = Buffer.from(a);
|
|
33
|
+
const bufB = Buffer.from(b);
|
|
34
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Agent Token Manager
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Manages per-agent authentication tokens.
|
|
43
|
+
*
|
|
44
|
+
* Each spawned agent gets a unique token at spawn time. The token is
|
|
45
|
+
* passed to the subprocess via environment variable and validated on
|
|
46
|
+
* every MCP bridge RPC call.
|
|
47
|
+
*/
|
|
48
|
+
export class AgentTokenManager {
|
|
49
|
+
private tokens = new Map<string, string>();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create and store a token for an agent. Returns the generated token.
|
|
53
|
+
*/
|
|
54
|
+
createToken(agentId: string): string {
|
|
55
|
+
const token = generateToken();
|
|
56
|
+
this.tokens.set(agentId, token);
|
|
57
|
+
return token;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Verify that a token matches the one stored for an agent.
|
|
62
|
+
*/
|
|
63
|
+
verifyToken(agentId: string, token: string): boolean {
|
|
64
|
+
const stored = this.tokens.get(agentId);
|
|
65
|
+
if (!stored) return false;
|
|
66
|
+
return secureCompare(stored, token);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Revoke an agent's token (e.g., on terminate).
|
|
71
|
+
*/
|
|
72
|
+
revokeToken(agentId: string): boolean {
|
|
73
|
+
return this.tokens.delete(agentId);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if an agent has a registered token.
|
|
78
|
+
*/
|
|
79
|
+
hasToken(agentId: string): boolean {
|
|
80
|
+
return this.tokens.has(agentId);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { describe, it, expect } from "vitest";
|
|
9
|
-
import { parseArgs, type ACPServerOptions } from "../
|
|
9
|
+
import { parseArgs, type ACPServerOptions } from "../parse-args.js";
|
|
10
10
|
|
|
11
11
|
// ─────────────────────────────────────────────────────────────────
|
|
12
12
|
// parseArgs Tests
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { describe, it, expect } from "vitest";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
-
import { getStableInstanceId } from "../
|
|
10
|
+
import { getStableInstanceId } from "../stable-instance-id.js";
|
|
11
11
|
|
|
12
12
|
describe("getStableInstanceId", () => {
|
|
13
13
|
it("should return the same ID for the same path", () => {
|
package/src/cli/acp.ts
CHANGED
|
@@ -38,11 +38,7 @@
|
|
|
38
38
|
* });
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import { readFileSync } from "node:fs";
|
|
42
|
-
import { createHash } from "node:crypto";
|
|
43
|
-
import { dirname, join, resolve } from "node:path";
|
|
44
41
|
import { Readable } from "node:stream";
|
|
45
|
-
import { fileURLToPath } from "node:url";
|
|
46
42
|
import {
|
|
47
43
|
AgentSideConnection,
|
|
48
44
|
ndJsonStream,
|
|
@@ -73,63 +69,13 @@ import type { AgentId, EventId } from "../store/types/index.js";
|
|
|
73
69
|
// Configuration
|
|
74
70
|
// ─────────────────────────────────────────────────────────────────
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
/** Stdio ACP-only mode (for embedded use with acp-factory) */
|
|
80
|
-
acp?: boolean;
|
|
81
|
-
/** Port for server (default: 3001) */
|
|
82
|
-
port?: number;
|
|
83
|
-
/** Host for server (default: localhost) */
|
|
84
|
-
host?: string;
|
|
85
|
-
/** Instance ID to reuse an existing event store (omit for new instance) */
|
|
86
|
-
instanceId?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Parse command line arguments.
|
|
91
|
-
* @param argv Optional array of arguments (defaults to process.argv.slice(2))
|
|
92
|
-
*/
|
|
93
|
-
export function parseArgs(argv?: string[]): ACPServerOptions {
|
|
94
|
-
const args = argv ?? process.argv.slice(2);
|
|
95
|
-
const options: ACPServerOptions = {};
|
|
96
|
-
|
|
97
|
-
for (let i = 0; i < args.length; i++) {
|
|
98
|
-
if (args[i] === "--version" || args[i] === "-v") {
|
|
99
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
100
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
101
|
-
console.log(pkg.version);
|
|
102
|
-
process.exit(0);
|
|
103
|
-
} else if (args[i] === "--cwd" && args[i + 1]) {
|
|
104
|
-
options.cwd = args[i + 1];
|
|
105
|
-
i++;
|
|
106
|
-
} else if (args[i] === "--acp") {
|
|
107
|
-
options.acp = true;
|
|
108
|
-
} else if (args[i] === "--port" && args[i + 1]) {
|
|
109
|
-
options.port = parseInt(args[i + 1], 10);
|
|
110
|
-
i++;
|
|
111
|
-
} else if (args[i] === "--host" && args[i + 1]) {
|
|
112
|
-
options.host = args[i + 1];
|
|
113
|
-
i++;
|
|
114
|
-
} else if (args[i] === "--instance-id" && args[i + 1]) {
|
|
115
|
-
options.instanceId = args[i + 1];
|
|
116
|
-
i++;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return options;
|
|
121
|
-
}
|
|
72
|
+
import { parseArgs } from "./parse-args.js";
|
|
73
|
+
import { getStableInstanceId } from "./stable-instance-id.js";
|
|
74
|
+
import { loadMergedConfig } from "../config/project-config.js";
|
|
122
75
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
* so agents and sessions persist across server restarts.
|
|
127
|
-
*/
|
|
128
|
-
export function getStableInstanceId(cwd: string): string {
|
|
129
|
-
const normalizedPath = resolve(cwd);
|
|
130
|
-
const hash = createHash("sha256").update(normalizedPath).digest("hex").slice(0, 12);
|
|
131
|
-
return `inst_${hash}`;
|
|
132
|
-
}
|
|
76
|
+
// Re-export utilities for backwards compatibility
|
|
77
|
+
export { parseArgs, type ACPServerOptions } from "./parse-args.js";
|
|
78
|
+
export { getStableInstanceId } from "./stable-instance-id.js";
|
|
133
79
|
|
|
134
80
|
// ─────────────────────────────────────────────────────────────────
|
|
135
81
|
// Stream Setup
|
|
@@ -272,10 +218,92 @@ async function main() {
|
|
|
272
218
|
wakeHandler: routerWakeHandler,
|
|
273
219
|
});
|
|
274
220
|
|
|
221
|
+
// Load merged config (global → project → env vars)
|
|
222
|
+
const mergedConfig = loadMergedConfig(defaultCwd);
|
|
223
|
+
|
|
224
|
+
// Compute server URL for thin-client MCP mode (only in server mode, not stdio ACP)
|
|
225
|
+
const serverUrl = options.acp
|
|
226
|
+
? undefined
|
|
227
|
+
: `http://${options.host ?? mergedConfig.host ?? "localhost"}:${options.port ?? mergedConfig.port ?? 3001}`;
|
|
228
|
+
|
|
229
|
+
// Set up authentication tokens from merged config
|
|
230
|
+
const noAuth = options.noAuth || mergedConfig.auth?.disabled === true;
|
|
231
|
+
let serverToken: string | undefined;
|
|
232
|
+
let agentTokenManager: import("../auth/token.js").AgentTokenManager | undefined;
|
|
233
|
+
|
|
234
|
+
if (!noAuth && mergedConfig.auth?.secret) {
|
|
235
|
+
const { AgentTokenManager } = await import("../auth/token.js");
|
|
236
|
+
serverToken = mergedConfig.auth.secret;
|
|
237
|
+
agentTokenManager = new AgentTokenManager();
|
|
238
|
+
}
|
|
239
|
+
|
|
275
240
|
// Now create the agentManager with the real router
|
|
276
|
-
agentManager = createAgentManager(eventStore, messageRouter
|
|
241
|
+
agentManager = createAgentManager(eventStore, messageRouter, {
|
|
242
|
+
serverUrl,
|
|
243
|
+
serverToken: serverUrl ? serverToken : undefined,
|
|
244
|
+
agentTokenManager: serverUrl ? agentTokenManager : undefined,
|
|
245
|
+
taskBackend: mergedConfig.task?.backend,
|
|
246
|
+
openTasksSocketPath: mergedConfig.task?.opentasks?.socket_path,
|
|
247
|
+
});
|
|
277
248
|
const taskManager = createTaskManager(eventStore);
|
|
278
249
|
|
|
250
|
+
// Create task backend for dynamic task tools (create_task, get_task, etc.)
|
|
251
|
+
const { createTaskBackend, loadTaskConfigFromMerged } = await import("../task/backend/index.js");
|
|
252
|
+
const { UnifiedTaskToolProvider } = await import("../task/backend/unified-tool-provider.js");
|
|
253
|
+
|
|
254
|
+
let taskBackend: import("../task/backend/types.js").TaskBackend | undefined;
|
|
255
|
+
let taskToolProvider: import("../task/backend/types.js").TaskToolProvider | undefined;
|
|
256
|
+
let taskBackendShutdown: (() => Promise<void>) | undefined;
|
|
257
|
+
|
|
258
|
+
// Mutable context holder for the task tool provider. Bridge handlers set
|
|
259
|
+
// this before each tool call so the provider uses the calling agent's ID.
|
|
260
|
+
// Safe because Node.js is single-threaded.
|
|
261
|
+
const taskToolContext = { agent_id: "" as string };
|
|
262
|
+
|
|
263
|
+
// Connect-on-spawn callback: auto-connect project .opentasks/ dirs to central daemon
|
|
264
|
+
let connectProject: ((projectPath: string) => Promise<void>) | undefined;
|
|
265
|
+
let getConnectedProjects: (() => string[]) | undefined;
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const taskConfig = loadTaskConfigFromMerged(mergedConfig);
|
|
269
|
+
const result = await createTaskBackend(taskConfig, eventStore);
|
|
270
|
+
taskBackend = result.backend;
|
|
271
|
+
taskBackendShutdown = result.shutdown;
|
|
272
|
+
connectProject = result.connectProject;
|
|
273
|
+
getConnectedProjects = result.getConnectedProjects;
|
|
274
|
+
|
|
275
|
+
taskToolProvider = new UnifiedTaskToolProvider(
|
|
276
|
+
taskBackend,
|
|
277
|
+
() => taskToolContext,
|
|
278
|
+
result.openTasksClient
|
|
279
|
+
);
|
|
280
|
+
console.error(`[acp] Task backend created: ${taskConfig.backend.type}`);
|
|
281
|
+
|
|
282
|
+
// Propagate runtime socket path to child agents so they skip daemon discovery
|
|
283
|
+
if (result.socketPath) {
|
|
284
|
+
agentManager.setOpenTasksSocketPath(result.socketPath);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Auto-connect the server's own project directory on startup
|
|
288
|
+
if (connectProject) {
|
|
289
|
+
connectProject(defaultCwd).catch(() => {});
|
|
290
|
+
}
|
|
291
|
+
} catch (err) {
|
|
292
|
+
// Fall back to in-memory backend if opentasks connection fails
|
|
293
|
+
console.error(`[acp] Failed to create task backend (${err}), falling back to memory`);
|
|
294
|
+
try {
|
|
295
|
+
const fallbackResult = await createTaskBackend({ backend: { type: "memory" } }, eventStore);
|
|
296
|
+
taskBackend = fallbackResult.backend;
|
|
297
|
+
taskToolProvider = new UnifiedTaskToolProvider(
|
|
298
|
+
taskBackend,
|
|
299
|
+
() => taskToolContext,
|
|
300
|
+
);
|
|
301
|
+
console.error(`[acp] Task backend created: memory (fallback)`);
|
|
302
|
+
} catch (fallbackErr) {
|
|
303
|
+
console.error(`[acp] Memory fallback also failed: ${fallbackErr}. Task tools will be unavailable.`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
279
307
|
// Create ActivityWatcher for event-driven agent waking
|
|
280
308
|
const sessionProvider = createSessionProviderFromAgentManager(agentManager);
|
|
281
309
|
const wakeHandler = createWakeHandler(sessionProvider, agentManager);
|
|
@@ -332,9 +360,11 @@ async function main() {
|
|
|
332
360
|
activityWatcher.start();
|
|
333
361
|
|
|
334
362
|
// Auto-subscribe Monitor agents to health events when they spawn
|
|
363
|
+
// and auto-connect project .opentasks/ directories to central daemon
|
|
335
364
|
agentManager.onLifecycleEvent((event) => {
|
|
336
365
|
if (event.type === "spawned") {
|
|
337
366
|
const agent = event.agent;
|
|
367
|
+
|
|
338
368
|
// Check if this is a Monitor agent
|
|
339
369
|
if (agent.role === "monitor" || agent.role?.startsWith("monitor.")) {
|
|
340
370
|
subscribeAgentToEvents(
|
|
@@ -346,23 +376,44 @@ async function main() {
|
|
|
346
376
|
);
|
|
347
377
|
console.error(`[acp] Auto-subscribed Monitor ${agent.id} to health events`);
|
|
348
378
|
}
|
|
379
|
+
|
|
380
|
+
// Connect-on-spawn: auto-connect project .opentasks/ to central daemon
|
|
381
|
+
if (connectProject && agent.cwd) {
|
|
382
|
+
connectProject(agent.cwd).catch(() => {
|
|
383
|
+
// Non-fatal — logged inside connectProject
|
|
384
|
+
});
|
|
385
|
+
}
|
|
349
386
|
}
|
|
350
387
|
});
|
|
351
388
|
|
|
352
389
|
// Combined server (when --ws is enabled)
|
|
353
390
|
let combinedServer: CombinedServer | undefined;
|
|
354
391
|
|
|
355
|
-
// Cleanup function
|
|
392
|
+
// Cleanup function — best-effort, each step isolated
|
|
356
393
|
const cleanup = async () => {
|
|
357
|
-
|
|
358
|
-
|
|
394
|
+
try { activityWatcher.stop(); } catch (err) {
|
|
395
|
+
console.error(`[cleanup] ActivityWatcher stop failed: ${err}`);
|
|
396
|
+
}
|
|
359
397
|
|
|
360
|
-
// Stop combined server if running
|
|
361
398
|
if (combinedServer) {
|
|
362
|
-
await combinedServer.stop();
|
|
399
|
+
try { await combinedServer.stop(); } catch (err) {
|
|
400
|
+
console.error(`[cleanup] Combined server stop failed: ${err}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try { await agentManager.close(); } catch (err) {
|
|
405
|
+
console.error(`[cleanup] AgentManager close failed: ${err}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (taskBackendShutdown) {
|
|
409
|
+
try { await taskBackendShutdown(); } catch (err) {
|
|
410
|
+
console.error(`[cleanup] Task backend shutdown failed: ${err}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
try { await eventStore.close(); } catch (err) {
|
|
415
|
+
console.error(`[cleanup] EventStore close failed: ${err}`);
|
|
363
416
|
}
|
|
364
|
-
await agentManager.close();
|
|
365
|
-
await eventStore.close();
|
|
366
417
|
};
|
|
367
418
|
|
|
368
419
|
try {
|
|
@@ -401,15 +452,22 @@ async function main() {
|
|
|
401
452
|
await cleanup();
|
|
402
453
|
} else {
|
|
403
454
|
// Full server mode (default): WebSocket ACP + MAP + REST API
|
|
404
|
-
const host = options.host ?? "localhost";
|
|
405
|
-
const port = options.port ?? 3001;
|
|
455
|
+
const host = options.host ?? mergedConfig.host ?? "localhost";
|
|
456
|
+
const port = options.port ?? mergedConfig.port ?? 3001;
|
|
406
457
|
|
|
407
458
|
combinedServer = createCombinedServer(
|
|
408
|
-
{ eventStore, agentManager, taskManager, messageRouter, activityWatcher },
|
|
409
|
-
{ port, host, defaultCwd }
|
|
459
|
+
{ eventStore, agentManager, taskManager, messageRouter, activityWatcher, taskBackend, taskToolProvider, taskToolContext, agentTokenManager, getConnectedProjects },
|
|
460
|
+
{ port, host, defaultCwd, serverToken, noAuth }
|
|
410
461
|
);
|
|
411
462
|
|
|
412
463
|
await combinedServer.start();
|
|
464
|
+
if (serverToken) {
|
|
465
|
+
console.error(`[acp] Server token: ${serverToken.substring(0, 8)}...`);
|
|
466
|
+
} else if (noAuth) {
|
|
467
|
+
console.error(`[acp] Auth: explicitly disabled`);
|
|
468
|
+
} else {
|
|
469
|
+
console.error(`[acp] Auth: off (set auth.secret in config or MACRO_SERVER_SECRET to enable)`);
|
|
470
|
+
}
|
|
413
471
|
|
|
414
472
|
// Keep process alive - will exit via SIGINT/SIGTERM handlers
|
|
415
473
|
await new Promise<void>(() => {
|