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/store/event-store.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
EventFilter,
|
|
24
24
|
Agent,
|
|
25
25
|
AgentState,
|
|
26
|
+
AgentMetadataUpdate,
|
|
26
27
|
Task,
|
|
27
28
|
TaskStatus,
|
|
28
29
|
QueuedMessage,
|
|
@@ -131,8 +132,8 @@ function createTabularBetterSqlite3Persister(
|
|
|
131
132
|
undefined,
|
|
132
133
|
// onIgnoredError
|
|
133
134
|
(error: any) => console.warn('[EventStore] Persister error:', error),
|
|
134
|
-
// destroy
|
|
135
|
-
() =>
|
|
135
|
+
// destroy — do NOT close DB here; close() handles it after the persister drains
|
|
136
|
+
() => {},
|
|
136
137
|
// persist mode (1 = StoreOnly)
|
|
137
138
|
1 as any,
|
|
138
139
|
// thing (the db instance)
|
|
@@ -204,6 +205,7 @@ export interface EventStore {
|
|
|
204
205
|
getAgent(agentId: AgentId): Agent | null;
|
|
205
206
|
listAgents(filter?: { state?: AgentState; parent?: AgentId | null }): Agent[];
|
|
206
207
|
updateAgentPlan(agentId: AgentId, plan: Array<{ content: string; priority: string; status: string }>): void;
|
|
208
|
+
updateAgentMetadata(agentId: AgentId, updates: AgentMetadataUpdate): void;
|
|
207
209
|
|
|
208
210
|
// Task view
|
|
209
211
|
getTask(taskId: TaskId): Task | null;
|
|
@@ -521,24 +523,50 @@ export async function createEventStore(config: StoreConfig = {}): Promise<EventS
|
|
|
521
523
|
}
|
|
522
524
|
|
|
523
525
|
/**
|
|
524
|
-
* Update
|
|
526
|
+
* Update agent metadata fields (name, plan, metadata).
|
|
527
|
+
* Only provided fields are updated. Metadata is shallow-merged with existing.
|
|
525
528
|
*/
|
|
526
|
-
function
|
|
529
|
+
function updateAgentMetadata(
|
|
527
530
|
agentId: AgentId,
|
|
528
|
-
|
|
531
|
+
updates: AgentMetadataUpdate,
|
|
529
532
|
): void {
|
|
530
533
|
const row = store.getRow('agents', agentId);
|
|
531
534
|
if (!row.id) return;
|
|
532
535
|
|
|
533
|
-
|
|
534
|
-
plan: JSON.stringify(plan),
|
|
536
|
+
const partial: Record<string, string | number | boolean> = {
|
|
535
537
|
last_activity_at: Date.now(),
|
|
536
|
-
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
if (updates.name !== undefined) {
|
|
541
|
+
partial.name = updates.name;
|
|
542
|
+
}
|
|
543
|
+
if (updates.plan !== undefined) {
|
|
544
|
+
partial.plan = JSON.stringify(updates.plan);
|
|
545
|
+
}
|
|
546
|
+
if (updates.metadata !== undefined) {
|
|
547
|
+
// Shallow merge with existing metadata
|
|
548
|
+
const existing = row.metadata ? JSON.parse(row.metadata as string) : {};
|
|
549
|
+
partial.metadata = JSON.stringify({ ...existing, ...updates.metadata });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
store.setPartialRow('agents', agentId, partial);
|
|
537
553
|
|
|
538
554
|
const agent = rowToAgent(store.getRow('agents', agentId));
|
|
539
555
|
notifyAgentChange(agentId, agent);
|
|
540
556
|
}
|
|
541
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Update an agent's plan entries (persisted to SQLite via TinyBase).
|
|
560
|
+
* Convenience wrapper around updateAgentMetadata.
|
|
561
|
+
*/
|
|
562
|
+
function updateAgentPlan(
|
|
563
|
+
agentId: AgentId,
|
|
564
|
+
plan: Array<{ content: string; priority: string; status: string }>,
|
|
565
|
+
): void {
|
|
566
|
+
updateAgentMetadata(agentId, { plan });
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
|
|
542
570
|
/**
|
|
543
571
|
* Get task by ID
|
|
544
572
|
*/
|
|
@@ -947,8 +975,18 @@ export async function createEventStore(config: StoreConfig = {}): Promise<EventS
|
|
|
947
975
|
*/
|
|
948
976
|
async function close(): Promise<void> {
|
|
949
977
|
if (persister) {
|
|
978
|
+
// Stop auto-save first to prevent race conditions between
|
|
979
|
+
// auto-save callbacks and the explicit save/destroy sequence.
|
|
980
|
+
await persister.stopAutoSave();
|
|
950
981
|
await persister.save();
|
|
982
|
+
// Destroy the persister (removes store listeners) BEFORE closing the DB.
|
|
983
|
+
// The destroy callback is a no-op — we close the DB ourselves below
|
|
984
|
+
// after giving TinyBase's internal async queues time to drain.
|
|
951
985
|
persister.destroy();
|
|
986
|
+
// Allow any in-flight TinyBase microtasks to settle before closing
|
|
987
|
+
// the database connection. Without this, pending writes from store
|
|
988
|
+
// change listeners can race against db.close().
|
|
989
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
952
990
|
}
|
|
953
991
|
if (db) {
|
|
954
992
|
db.close();
|
|
@@ -1224,6 +1262,7 @@ export async function createEventStore(config: StoreConfig = {}): Promise<EventS
|
|
|
1224
1262
|
getAgent,
|
|
1225
1263
|
listAgents,
|
|
1226
1264
|
updateAgentPlan,
|
|
1265
|
+
updateAgentMetadata,
|
|
1227
1266
|
getTask,
|
|
1228
1267
|
listTasks,
|
|
1229
1268
|
getMessages,
|
|
@@ -1291,13 +1330,21 @@ function initializeTables(store: Store): void {
|
|
|
1291
1330
|
*/
|
|
1292
1331
|
function rebuildViews(store: Store): void {
|
|
1293
1332
|
// Preserve out-of-band agent fields that aren't derived from events.
|
|
1294
|
-
//
|
|
1295
|
-
// so
|
|
1296
|
-
const
|
|
1333
|
+
// These fields are written directly (not through events),
|
|
1334
|
+
// so they would be lost when we clear and replay.
|
|
1335
|
+
const OUT_OF_BAND_FIELDS = ['plan', 'name', 'metadata'] as const;
|
|
1336
|
+
const savedOutOfBand = new Map<string, Record<string, string>>();
|
|
1297
1337
|
for (const rowId of store.getRowIds('agents')) {
|
|
1298
1338
|
const row = store.getRow('agents', rowId);
|
|
1299
|
-
|
|
1300
|
-
|
|
1339
|
+
const saved: Record<string, string> = {};
|
|
1340
|
+
for (const field of OUT_OF_BAND_FIELDS) {
|
|
1341
|
+
const val = row[field] as string | undefined;
|
|
1342
|
+
if (val && val !== '' && val !== '[]') {
|
|
1343
|
+
saved[field] = val;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
if (Object.keys(saved).length > 0) {
|
|
1347
|
+
savedOutOfBand.set(rowId, saved);
|
|
1301
1348
|
}
|
|
1302
1349
|
}
|
|
1303
1350
|
|
|
@@ -1358,10 +1405,10 @@ function rebuildViews(store: Store): void {
|
|
|
1358
1405
|
}
|
|
1359
1406
|
|
|
1360
1407
|
// Restore out-of-band agent fields preserved before the wipe
|
|
1361
|
-
for (const [agentId,
|
|
1408
|
+
for (const [agentId, fields] of savedOutOfBand) {
|
|
1362
1409
|
const row = store.getRow('agents', agentId);
|
|
1363
1410
|
if (row.id) {
|
|
1364
|
-
store.setPartialRow('agents', agentId,
|
|
1411
|
+
store.setPartialRow('agents', agentId, fields);
|
|
1365
1412
|
}
|
|
1366
1413
|
}
|
|
1367
1414
|
}
|
|
@@ -1383,8 +1430,8 @@ function applyEventToViews(
|
|
|
1383
1430
|
case 'spawn':
|
|
1384
1431
|
applySpawnEvent(store, event, notifyAgentChange);
|
|
1385
1432
|
break;
|
|
1386
|
-
case '
|
|
1387
|
-
|
|
1433
|
+
case 'stop':
|
|
1434
|
+
applyStopEvent(store, event, notifyAgentChange);
|
|
1388
1435
|
break;
|
|
1389
1436
|
case 'status':
|
|
1390
1437
|
applyStatusEvent(store, event, notifyAgentChange);
|
|
@@ -1451,6 +1498,7 @@ function applySpawnEvent(
|
|
|
1451
1498
|
|
|
1452
1499
|
store.setRow('agents', agentId, {
|
|
1453
1500
|
id: agentId,
|
|
1501
|
+
name: '',
|
|
1454
1502
|
session_id: payload.session_id,
|
|
1455
1503
|
provider_session_id: '',
|
|
1456
1504
|
parent: parent ?? '',
|
|
@@ -1463,6 +1511,7 @@ function applySpawnEvent(
|
|
|
1463
1511
|
config: JSON.stringify(payload.config ?? {}),
|
|
1464
1512
|
cwd: payload.cwd ?? process.cwd(),
|
|
1465
1513
|
plan: '[]',
|
|
1514
|
+
metadata: '',
|
|
1466
1515
|
created_at: event.timestamp,
|
|
1467
1516
|
started_at: 0,
|
|
1468
1517
|
stopped_at: 0,
|
|
@@ -1474,9 +1523,9 @@ function applySpawnEvent(
|
|
|
1474
1523
|
}
|
|
1475
1524
|
|
|
1476
1525
|
/**
|
|
1477
|
-
* Apply
|
|
1526
|
+
* Apply stop event to agents view
|
|
1478
1527
|
*/
|
|
1479
|
-
function
|
|
1528
|
+
function applyStopEvent(
|
|
1480
1529
|
store: Store,
|
|
1481
1530
|
event: Event,
|
|
1482
1531
|
notify: (agentId: AgentId, agent: Agent | null) => void,
|
|
@@ -1618,6 +1667,7 @@ function applyTaskEvent(
|
|
|
1618
1667
|
description: string;
|
|
1619
1668
|
parent_task?: TaskId;
|
|
1620
1669
|
inputs?: Record<string, unknown>;
|
|
1670
|
+
tags?: string[];
|
|
1621
1671
|
retryPolicy?: unknown;
|
|
1622
1672
|
};
|
|
1623
1673
|
store.setRow('tasks', taskId, {
|
|
@@ -1628,6 +1678,7 @@ function applyTaskEvent(
|
|
|
1628
1678
|
parent_task: details.parent_task ?? '',
|
|
1629
1679
|
subtasks: JSON.stringify([]),
|
|
1630
1680
|
blockers: JSON.stringify([]),
|
|
1681
|
+
tags: details.tags ? JSON.stringify(details.tags) : '',
|
|
1631
1682
|
created_at: event.timestamp,
|
|
1632
1683
|
started_at: 0,
|
|
1633
1684
|
completed_at: 0,
|
|
@@ -1674,10 +1725,15 @@ function applyTaskEvent(
|
|
|
1674
1725
|
break;
|
|
1675
1726
|
}
|
|
1676
1727
|
}
|
|
1677
|
-
|
|
1728
|
+
const updates: Record<string, string | number | boolean> = {
|
|
1678
1729
|
assigned_agent: '',
|
|
1679
1730
|
agent_history: JSON.stringify(history),
|
|
1680
|
-
}
|
|
1731
|
+
};
|
|
1732
|
+
// Reset to pending if task was only assigned (not yet started)
|
|
1733
|
+
if (existing.status === 'assigned') {
|
|
1734
|
+
updates.status = 'pending';
|
|
1735
|
+
}
|
|
1736
|
+
store.setPartialRow('tasks', taskId, updates);
|
|
1681
1737
|
break;
|
|
1682
1738
|
}
|
|
1683
1739
|
case 'status_change': {
|
|
@@ -1800,6 +1856,7 @@ function rowToAgent(row: Record<string, unknown>): Agent {
|
|
|
1800
1856
|
const stopReason = row.stop_reason as string;
|
|
1801
1857
|
return {
|
|
1802
1858
|
id: row.id as AgentId,
|
|
1859
|
+
name: (row.name as string) || undefined,
|
|
1803
1860
|
session_id: row.session_id as string,
|
|
1804
1861
|
provider_session_id: (row.provider_session_id as string) || undefined,
|
|
1805
1862
|
parent: (row.parent as string) || null,
|
|
@@ -1812,6 +1869,7 @@ function rowToAgent(row: Record<string, unknown>): Agent {
|
|
|
1812
1869
|
config: row.config ? JSON.parse(row.config as string) : {},
|
|
1813
1870
|
cwd: (row.cwd as string) || process.cwd(),
|
|
1814
1871
|
plan: row.plan ? JSON.parse(row.plan as string) : [],
|
|
1872
|
+
metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,
|
|
1815
1873
|
created_at: row.created_at as Timestamp,
|
|
1816
1874
|
started_at: (row.started_at as number) || undefined,
|
|
1817
1875
|
stopped_at: (row.stopped_at as number) || undefined,
|
|
@@ -1839,6 +1897,7 @@ function rowToTask(row: Record<string, unknown>): Task {
|
|
|
1839
1897
|
outputs: row.outputs ? JSON.parse(row.outputs as string) : undefined,
|
|
1840
1898
|
artifacts: row.artifacts ? JSON.parse(row.artifacts as string) : undefined,
|
|
1841
1899
|
agent_history: row.agent_history ? JSON.parse(row.agent_history as string) : undefined,
|
|
1900
|
+
tags: row.tags ? JSON.parse(row.tags as string) : undefined,
|
|
1842
1901
|
retryPolicy: row.retry_policy ? JSON.parse(row.retry_policy as string) : undefined,
|
|
1843
1902
|
retryState: row.retry_state ? JSON.parse(row.retry_state as string) : undefined,
|
|
1844
1903
|
};
|
|
@@ -28,6 +28,8 @@ export interface AgentConfig {
|
|
|
28
28
|
// Agent record in materialized view
|
|
29
29
|
export interface Agent {
|
|
30
30
|
id: AgentId;
|
|
31
|
+
/** Optional display name (set via rename, not derived from events) */
|
|
32
|
+
name?: string;
|
|
31
33
|
session_id: SessionId;
|
|
32
34
|
/** Session ID from the underlying agent provider (e.g., Claude Code UUID for --resume) */
|
|
33
35
|
provider_session_id?: string;
|
|
@@ -41,9 +43,22 @@ export interface Agent {
|
|
|
41
43
|
config: AgentConfig;
|
|
42
44
|
cwd: string;
|
|
43
45
|
plan: Array<{ content: string; priority: string; status: string }>;
|
|
46
|
+
/** Arbitrary metadata (persisted out-of-band, not derived from events) */
|
|
47
|
+
metadata?: Record<string, unknown>;
|
|
44
48
|
created_at: Timestamp;
|
|
45
49
|
started_at?: Timestamp;
|
|
46
50
|
stopped_at?: Timestamp;
|
|
47
51
|
/** Last time this agent emitted an event (for health monitoring) */
|
|
48
52
|
last_activity_at?: Timestamp;
|
|
49
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Partial update for agent metadata fields.
|
|
57
|
+
* All fields are optional — only provided fields are updated.
|
|
58
|
+
* `metadata` is merged (shallow) with existing metadata.
|
|
59
|
+
*/
|
|
60
|
+
export interface AgentMetadataUpdate {
|
|
61
|
+
name?: string;
|
|
62
|
+
plan?: Array<{ content: string; priority: string; status: string }>;
|
|
63
|
+
metadata?: Record<string, unknown>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for createTaskBackend factory function
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - Memory backend returns no socketPath
|
|
6
|
+
* - OpenTasks backend with explicit socketPath returns it
|
|
7
|
+
* - OpenTasks backend with autoStart returns daemon's socketPath
|
|
8
|
+
* - Unknown backend type throws
|
|
9
|
+
*
|
|
10
|
+
* @module task/backend/__tests__/create-task-backend.test
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
14
|
+
import { createEventStore, type EventStore } from "../../../store/event-store.js";
|
|
15
|
+
|
|
16
|
+
// Mock opentasks to avoid real daemon operations
|
|
17
|
+
vi.mock("opentasks", () => ({
|
|
18
|
+
checkExistingDaemon: vi.fn(),
|
|
19
|
+
createDaemonWithStore: vi.fn(),
|
|
20
|
+
OpenTasksClient: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
// Mock the opentasks client module
|
|
24
|
+
vi.mock("../opentasks/client.js", () => ({
|
|
25
|
+
IPCOpenTasksClient: vi.fn().mockImplementation(() => ({
|
|
26
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
27
|
+
disconnect: vi.fn(),
|
|
28
|
+
isConnected: vi.fn().mockReturnValue(true),
|
|
29
|
+
call: vi.fn().mockResolvedValue({}),
|
|
30
|
+
createIssue: vi.fn(),
|
|
31
|
+
getIssue: vi.fn(),
|
|
32
|
+
updateIssue: vi.fn(),
|
|
33
|
+
deleteIssue: vi.fn(),
|
|
34
|
+
listIssues: vi.fn(),
|
|
35
|
+
getReadyIssues: vi.fn(),
|
|
36
|
+
createEdge: vi.fn(),
|
|
37
|
+
removeEdge: vi.fn(),
|
|
38
|
+
getBlockers: vi.fn(),
|
|
39
|
+
getBlocking: vi.fn(),
|
|
40
|
+
task: vi.fn(),
|
|
41
|
+
taskTransition: vi.fn(),
|
|
42
|
+
taskReady: vi.fn(),
|
|
43
|
+
taskAssign: vi.fn(),
|
|
44
|
+
taskValidActions: vi.fn(),
|
|
45
|
+
listProviders: vi.fn(),
|
|
46
|
+
})),
|
|
47
|
+
createOpenTasksClient: vi.fn().mockImplementation(async () => ({
|
|
48
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
49
|
+
disconnect: vi.fn(),
|
|
50
|
+
isConnected: vi.fn().mockReturnValue(true),
|
|
51
|
+
call: vi.fn().mockResolvedValue({}),
|
|
52
|
+
createIssue: vi.fn(),
|
|
53
|
+
getIssue: vi.fn(),
|
|
54
|
+
updateIssue: vi.fn(),
|
|
55
|
+
deleteIssue: vi.fn(),
|
|
56
|
+
listIssues: vi.fn(),
|
|
57
|
+
getReadyIssues: vi.fn(),
|
|
58
|
+
createEdge: vi.fn(),
|
|
59
|
+
removeEdge: vi.fn(),
|
|
60
|
+
getBlockers: vi.fn(),
|
|
61
|
+
getBlocking: vi.fn(),
|
|
62
|
+
task: vi.fn(),
|
|
63
|
+
taskTransition: vi.fn(),
|
|
64
|
+
taskReady: vi.fn(),
|
|
65
|
+
taskAssign: vi.fn(),
|
|
66
|
+
taskValidActions: vi.fn(),
|
|
67
|
+
listProviders: vi.fn(),
|
|
68
|
+
})),
|
|
69
|
+
OpenTasksClientError: class extends Error {
|
|
70
|
+
code: string;
|
|
71
|
+
constructor(msg: string, code: string) {
|
|
72
|
+
super(msg);
|
|
73
|
+
this.code = code;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Mock the opentasks backend
|
|
79
|
+
vi.mock("../opentasks/backend.js", () => ({
|
|
80
|
+
OpenTasksTaskBackend: vi.fn().mockImplementation(() => ({
|
|
81
|
+
create: vi.fn(),
|
|
82
|
+
get: vi.fn(),
|
|
83
|
+
update: vi.fn(),
|
|
84
|
+
list: vi.fn(),
|
|
85
|
+
delete: vi.fn(),
|
|
86
|
+
onChange: vi.fn(),
|
|
87
|
+
})),
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
// Mock daemon-manager
|
|
91
|
+
vi.mock("../opentasks/daemon-manager.js", () => ({
|
|
92
|
+
DaemonManager: vi.fn().mockImplementation(() => ({
|
|
93
|
+
ensureDaemon: vi.fn().mockResolvedValue({
|
|
94
|
+
client: {
|
|
95
|
+
connect: vi.fn(),
|
|
96
|
+
disconnect: vi.fn(),
|
|
97
|
+
isConnected: vi.fn().mockReturnValue(true),
|
|
98
|
+
},
|
|
99
|
+
socketPath: "/tmp/auto-daemon.sock",
|
|
100
|
+
ownsDaemon: true,
|
|
101
|
+
}),
|
|
102
|
+
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
103
|
+
connectProject: vi.fn().mockResolvedValue(undefined),
|
|
104
|
+
getConnectedProjects: vi.fn().mockReturnValue([]),
|
|
105
|
+
})),
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
describe("createTaskBackend", () => {
|
|
109
|
+
let eventStore: EventStore;
|
|
110
|
+
|
|
111
|
+
beforeEach(async () => {
|
|
112
|
+
eventStore = await createEventStore({ inMemory: true });
|
|
113
|
+
vi.clearAllMocks();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
afterEach(async () => {
|
|
117
|
+
await eventStore.close();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return no socketPath for memory backend", async () => {
|
|
121
|
+
const { createTaskBackend } = await import("../index.js");
|
|
122
|
+
|
|
123
|
+
const result = await createTaskBackend(
|
|
124
|
+
{ backend: { type: "memory" } },
|
|
125
|
+
eventStore
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(result.backend).toBeDefined();
|
|
129
|
+
expect(result.socketPath).toBeUndefined();
|
|
130
|
+
expect(result.shutdown).toBeUndefined();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should return explicit socketPath for opentasks with socketPath config", async () => {
|
|
134
|
+
const { createTaskBackend } = await import("../index.js");
|
|
135
|
+
|
|
136
|
+
const result = await createTaskBackend(
|
|
137
|
+
{ backend: { type: "opentasks", socketPath: "/tmp/explicit.sock" } },
|
|
138
|
+
eventStore
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(result.backend).toBeDefined();
|
|
142
|
+
expect(result.socketPath).toBe("/tmp/explicit.sock");
|
|
143
|
+
expect(result.openTasksClient).toBeDefined();
|
|
144
|
+
expect(result.shutdown).toBeDefined();
|
|
145
|
+
// No connectProject when using direct socket (no DaemonManager)
|
|
146
|
+
expect(result.connectProject).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should return daemon socketPath for opentasks with autoStart", async () => {
|
|
150
|
+
const { createTaskBackend } = await import("../index.js");
|
|
151
|
+
|
|
152
|
+
const result = await createTaskBackend(
|
|
153
|
+
{ backend: { type: "opentasks" } }, // autoStart defaults to true
|
|
154
|
+
eventStore
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(result.backend).toBeDefined();
|
|
158
|
+
expect(result.socketPath).toBe("/tmp/auto-daemon.sock");
|
|
159
|
+
expect(result.openTasksClient).toBeDefined();
|
|
160
|
+
expect(result.shutdown).toBeDefined();
|
|
161
|
+
expect(result.connectProject).toBeDefined();
|
|
162
|
+
expect(result.getConnectedProjects).toBeDefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should return no socketPath for opentasks with autoStart disabled", async () => {
|
|
166
|
+
const { createTaskBackend } = await import("../index.js");
|
|
167
|
+
|
|
168
|
+
const result = await createTaskBackend(
|
|
169
|
+
{ backend: { type: "opentasks", autoStart: false } },
|
|
170
|
+
eventStore
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(result.backend).toBeDefined();
|
|
174
|
+
// autoStart=false, no socketPath in config → socket path unknown
|
|
175
|
+
expect(result.socketPath).toBeUndefined();
|
|
176
|
+
expect(result.openTasksClient).toBeDefined();
|
|
177
|
+
expect(result.shutdown).toBeDefined();
|
|
178
|
+
// No DaemonManager when autoStart is false
|
|
179
|
+
expect(result.connectProject).toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should throw for unknown backend type", async () => {
|
|
183
|
+
const { createTaskBackend } = await import("../index.js");
|
|
184
|
+
|
|
185
|
+
await expect(
|
|
186
|
+
createTaskBackend(
|
|
187
|
+
{ backend: { type: "unknown" as any } },
|
|
188
|
+
eventStore
|
|
189
|
+
)
|
|
190
|
+
).rejects.toThrow("Unknown backend type");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should call backend.close() before daemon shutdown", async () => {
|
|
194
|
+
const { createTaskBackend } = await import("../index.js");
|
|
195
|
+
|
|
196
|
+
const result = await createTaskBackend(
|
|
197
|
+
{ backend: { type: "opentasks" } }, // autoStart defaults to true
|
|
198
|
+
eventStore
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(result.shutdown).toBeDefined();
|
|
202
|
+
|
|
203
|
+
// Track call order
|
|
204
|
+
const callOrder: string[] = [];
|
|
205
|
+
const { DaemonManager } = await import("../opentasks/daemon-manager.js");
|
|
206
|
+
const mockInstance = vi.mocked(DaemonManager).mock.results[0]?.value;
|
|
207
|
+
if (mockInstance) {
|
|
208
|
+
mockInstance.shutdown.mockImplementation(async () => {
|
|
209
|
+
callOrder.push("daemon_shutdown");
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// backend.close() is called within the shutdown wrapper
|
|
214
|
+
const backend = result.backend as any;
|
|
215
|
+
const originalClose = backend.close?.bind(backend);
|
|
216
|
+
backend.close = vi.fn(async () => {
|
|
217
|
+
callOrder.push("backend_close");
|
|
218
|
+
if (originalClose) await originalClose();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await result.shutdown!();
|
|
222
|
+
|
|
223
|
+
expect(callOrder).toEqual(["backend_close", "daemon_shutdown"]);
|
|
224
|
+
});
|
|
225
|
+
});
|