macro-agent 0.1.8 → 0.1.11
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.md +263 -33
- package/README.md +781 -131
- package/dist/acp/claude-code-replay.d.ts +11 -0
- package/dist/acp/claude-code-replay.d.ts.map +1 -0
- package/dist/acp/claude-code-replay.js +190 -0
- package/dist/acp/claude-code-replay.js.map +1 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +192 -7
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +9 -0
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/adapters/tasks-adapter.d.ts.map +1 -1
- package/dist/adapters/tasks-adapter.js +3 -0
- package/dist/adapters/tasks-adapter.js.map +1 -1
- package/dist/adapters/types.d.ts +1 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/agent/agent-manager-v2.d.ts +21 -0
- package/dist/agent/agent-manager-v2.d.ts.map +1 -1
- package/dist/agent/agent-manager-v2.js +308 -54
- package/dist/agent/agent-manager-v2.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +12 -0
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/agent-store.d.ts +10 -0
- package/dist/agent/agent-store.d.ts.map +1 -1
- package/dist/agent/agent-store.js +22 -0
- package/dist/agent/agent-store.js.map +1 -1
- package/dist/agent/types.d.ts +15 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/boot-v2.d.ts +129 -1
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +359 -8
- package/dist/boot-v2.js.map +1 -1
- package/dist/cli/acp.js +4 -0
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +56 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cognitive/macro-agent-backend.d.ts.map +1 -1
- package/dist/cognitive/macro-agent-backend.js +40 -22
- package/dist/cognitive/macro-agent-backend.js.map +1 -1
- package/dist/integrations/skilltree.d.ts.map +1 -1
- package/dist/integrations/skilltree.js +1 -0
- package/dist/integrations/skilltree.js.map +1 -1
- package/dist/lifecycle/cascade.d.ts +25 -2
- package/dist/lifecycle/cascade.d.ts.map +1 -1
- package/dist/lifecycle/cascade.js +70 -2
- package/dist/lifecycle/cascade.js.map +1 -1
- package/dist/lifecycle/cleanup.d.ts +33 -2
- package/dist/lifecycle/cleanup.d.ts.map +1 -1
- package/dist/lifecycle/cleanup.js +28 -6
- package/dist/lifecycle/cleanup.js.map +1 -1
- package/dist/lifecycle/handlers-v2.d.ts +7 -0
- package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
- package/dist/lifecycle/handlers-v2.js +28 -2
- package/dist/lifecycle/handlers-v2.js.map +1 -1
- package/dist/lifecycle/types.d.ts +11 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/dist/map/acp-bridge.d.ts +9 -0
- package/dist/map/acp-bridge.d.ts.map +1 -1
- package/dist/map/acp-bridge.js +15 -2
- package/dist/map/acp-bridge.js.map +1 -1
- package/dist/map/cascade-action-handler.d.ts +24 -0
- package/dist/map/cascade-action-handler.d.ts.map +1 -0
- package/dist/map/cascade-action-handler.js +170 -0
- package/dist/map/cascade-action-handler.js.map +1 -0
- package/dist/map/cascade-bridge.d.ts +44 -0
- package/dist/map/cascade-bridge.d.ts.map +1 -0
- package/dist/map/cascade-bridge.js +294 -0
- package/dist/map/cascade-bridge.js.map +1 -0
- package/dist/map/coordination-handler.d.ts.map +1 -1
- package/dist/map/coordination-handler.js +12 -1
- package/dist/map/coordination-handler.js.map +1 -1
- package/dist/map/lifecycle-bridge.d.ts +1 -1
- package/dist/map/lifecycle-bridge.d.ts.map +1 -1
- package/dist/map/lifecycle-bridge.js +58 -23
- package/dist/map/lifecycle-bridge.js.map +1 -1
- package/dist/map/server.d.ts.map +1 -1
- package/dist/map/server.js +219 -7
- package/dist/map/server.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +49 -2
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/types.d.ts +22 -0
- package/dist/map/types.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.js +8 -0
- package/dist/mcp/tools/done-v2.js.map +1 -1
- package/dist/teams/team-manager-v2.d.ts.map +1 -1
- package/dist/teams/team-manager-v2.js +26 -0
- package/dist/teams/team-manager-v2.js.map +1 -1
- package/dist/teams/team-runtime-v2.d.ts.map +1 -1
- package/dist/teams/team-runtime-v2.js +16 -3
- package/dist/teams/team-runtime-v2.js.map +1 -1
- package/dist/workspace/config.d.ts +10 -10
- package/dist/workspace/config.d.ts.map +1 -1
- package/dist/workspace/config.js +4 -4
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/git-cascade-adapter.d.ts +510 -0
- package/dist/workspace/git-cascade-adapter.d.ts.map +1 -0
- package/dist/workspace/git-cascade-adapter.js +934 -0
- package/dist/workspace/git-cascade-adapter.js.map +1 -0
- package/dist/workspace/index.d.ts +3 -3
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +4 -4
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/landing/direct-push.d.ts +20 -0
- package/dist/workspace/landing/direct-push.d.ts.map +1 -0
- package/dist/workspace/landing/direct-push.js +74 -0
- package/dist/workspace/landing/direct-push.js.map +1 -0
- package/dist/workspace/landing/index.d.ts +29 -0
- package/dist/workspace/landing/index.d.ts.map +1 -0
- package/dist/workspace/landing/index.js +37 -0
- package/dist/workspace/landing/index.js.map +1 -0
- package/dist/workspace/landing/merge-to-parent.d.ts +41 -0
- package/dist/workspace/landing/merge-to-parent.d.ts.map +1 -0
- package/dist/workspace/landing/merge-to-parent.js +186 -0
- package/dist/workspace/landing/merge-to-parent.js.map +1 -0
- package/dist/workspace/landing/optimistic-push.d.ts +16 -0
- package/dist/workspace/landing/optimistic-push.d.ts.map +1 -0
- package/dist/workspace/landing/optimistic-push.js +27 -0
- package/dist/workspace/landing/optimistic-push.js.map +1 -0
- package/dist/workspace/landing/queue-to-branch.d.ts +24 -0
- package/dist/workspace/landing/queue-to-branch.d.ts.map +1 -0
- package/dist/workspace/landing/queue-to-branch.js +79 -0
- package/dist/workspace/landing/queue-to-branch.js.map +1 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts +10 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts.map +1 -1
- package/dist/workspace/merge-queue/merge-queue.js +10 -0
- package/dist/workspace/merge-queue/merge-queue.js.map +1 -1
- package/dist/workspace/merge-queue/types.d.ts +16 -2
- package/dist/workspace/merge-queue/types.d.ts.map +1 -1
- package/dist/workspace/merge-queue/types.js +9 -0
- package/dist/workspace/merge-queue/types.js.map +1 -1
- package/dist/workspace/pool/types.d.ts +1 -0
- package/dist/workspace/pool/types.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.js +1 -0
- package/dist/workspace/pool/worktree-pool.js.map +1 -1
- package/dist/workspace/recovery/abandon.d.ts +15 -0
- package/dist/workspace/recovery/abandon.d.ts.map +1 -0
- package/dist/workspace/recovery/abandon.js +45 -0
- package/dist/workspace/recovery/abandon.js.map +1 -0
- package/dist/workspace/recovery/auto-resolve.d.ts +27 -0
- package/dist/workspace/recovery/auto-resolve.d.ts.map +1 -0
- package/dist/workspace/recovery/auto-resolve.js +99 -0
- package/dist/workspace/recovery/auto-resolve.js.map +1 -0
- package/dist/workspace/recovery/defer.d.ts +15 -0
- package/dist/workspace/recovery/defer.d.ts.map +1 -0
- package/dist/workspace/recovery/defer.js +16 -0
- package/dist/workspace/recovery/defer.js.map +1 -0
- package/dist/workspace/recovery/escalate.d.ts +16 -0
- package/dist/workspace/recovery/escalate.d.ts.map +1 -0
- package/dist/workspace/recovery/escalate.js +24 -0
- package/dist/workspace/recovery/escalate.js.map +1 -0
- package/dist/workspace/recovery/index.d.ts +32 -0
- package/dist/workspace/recovery/index.d.ts.map +1 -0
- package/dist/workspace/recovery/index.js +45 -0
- package/dist/workspace/recovery/index.js.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts +45 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.js +118 -0
- package/dist/workspace/recovery/spawn-resolver.js.map +1 -0
- package/dist/workspace/recovery/types.d.ts +63 -0
- package/dist/workspace/recovery/types.d.ts.map +1 -0
- package/dist/workspace/recovery/types.js +12 -0
- package/dist/workspace/recovery/types.js.map +1 -0
- package/dist/workspace/topology/index.d.ts +9 -0
- package/dist/workspace/topology/index.d.ts.map +1 -0
- package/dist/workspace/topology/index.js +8 -0
- package/dist/workspace/topology/index.js.map +1 -0
- package/dist/workspace/topology/no-workspace.d.ts +18 -0
- package/dist/workspace/topology/no-workspace.d.ts.map +1 -0
- package/dist/workspace/topology/no-workspace.js +25 -0
- package/dist/workspace/topology/no-workspace.js.map +1 -0
- package/dist/workspace/topology/types.d.ts +97 -0
- package/dist/workspace/topology/types.d.ts.map +1 -0
- package/dist/workspace/topology/types.js +20 -0
- package/dist/workspace/topology/types.js.map +1 -0
- package/dist/workspace/topology/yaml-driven.d.ts +69 -0
- package/dist/workspace/topology/yaml-driven.d.ts.map +1 -0
- package/dist/workspace/topology/yaml-driven.js +273 -0
- package/dist/workspace/topology/yaml-driven.js.map +1 -0
- package/dist/workspace/types-v3.d.ts +117 -0
- package/dist/workspace/types-v3.d.ts.map +1 -0
- package/dist/workspace/types-v3.js +20 -0
- package/dist/workspace/types-v3.js.map +1 -0
- package/dist/workspace/types.d.ts +162 -17
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +101 -13
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +416 -13
- package/dist/workspace/workspace-manager.js.map +1 -1
- package/dist/workspace/yaml-schema.d.ts +254 -0
- package/dist/workspace/yaml-schema.d.ts.map +1 -0
- package/dist/workspace/yaml-schema.js +170 -0
- package/dist/workspace/yaml-schema.js.map +1 -0
- package/docs/conflict-recovery.md +472 -0
- package/docs/design/task-dispatcher.md +880 -0
- package/docs/git-cascade-integration-gaps.md +678 -0
- package/docs/workspace-interfaces.md +731 -0
- package/docs/workspace-redesign-plan.md +302 -0
- package/package.json +6 -5
- package/src/__tests__/boot-v2.test.ts +435 -0
- package/src/__tests__/e2e/acp-over-map.e2e.test.ts +92 -0
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -0
- package/src/__tests__/e2e/bootstrap.e2e.test.ts +319 -0
- package/src/__tests__/e2e/cascade-rebase.e2e.test.ts +254 -0
- package/src/__tests__/e2e/cli-run.e2e.test.ts +167 -0
- package/src/__tests__/e2e/dispatch-coordination.e2e.test.ts +495 -0
- package/src/__tests__/e2e/dispatch-live.e2e.test.ts +564 -0
- package/src/__tests__/e2e/dispatch-opentasks.e2e.test.ts +496 -0
- package/src/__tests__/e2e/dispatch-phase2-live.e2e.test.ts +456 -0
- package/src/__tests__/e2e/dispatch-phase2.e2e.test.ts +386 -0
- package/src/__tests__/e2e/dispatch.e2e.test.ts +376 -0
- package/src/__tests__/e2e/self-driving-v3.e2e.test.ts +197 -0
- package/src/__tests__/e2e/spawn-resolver.e2e.test.ts +200 -0
- package/src/__tests__/e2e/workspace-lifecycle.e2e.test.ts +30 -22
- package/src/__tests__/e2e/workspace-v3.e2e.test.ts +413 -0
- package/src/acp/__tests__/claude-code-replay.test.ts +225 -0
- package/src/acp/__tests__/macro-agent.test.ts +39 -1
- package/src/acp/claude-code-replay.ts +208 -0
- package/src/acp/macro-agent.ts +203 -10
- package/src/acp/types.ts +10 -0
- package/src/adapters/__tests__/tasks-adapter.test.ts +1 -0
- package/src/adapters/tasks-adapter.ts +3 -0
- package/src/adapters/types.ts +1 -0
- package/src/agent/__tests__/agent-manager-topology.test.ts +73 -0
- package/src/agent/__tests__/agent-manager-v2.test.ts +66 -0
- package/src/agent/__tests__/agent-store.test.ts +52 -0
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +372 -59
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/agent-store.ts +24 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +589 -35
- package/src/cli/acp.ts +4 -0
- package/src/cli/index.ts +61 -0
- package/src/cognitive/macro-agent-backend.ts +45 -29
- package/src/integrations/skilltree.ts +1 -0
- package/src/lifecycle/__tests__/cascade-consolidation.test.ts +240 -0
- package/src/lifecycle/cascade.ts +77 -2
- package/src/lifecycle/cleanup.ts +52 -3
- package/src/lifecycle/handlers-v2.ts +40 -3
- package/src/lifecycle/types.ts +12 -0
- package/src/map/__tests__/cascade-bridge.test.ts +229 -0
- package/src/map/__tests__/emit-event.test.ts +71 -0
- package/src/map/__tests__/lifecycle-bridge.test.ts +86 -10
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-action-handler.ts +205 -0
- package/src/map/cascade-bridge.ts +339 -0
- package/src/map/coordination-handler.ts +13 -1
- package/src/map/lifecycle-bridge.ts +52 -17
- package/src/map/server.ts +225 -7
- package/src/map/sidecar.ts +48 -1
- package/src/map/types.ts +23 -0
- package/src/mcp/tools/done-v2.ts +9 -0
- package/src/teams/team-manager-v2.ts +37 -0
- package/src/teams/team-runtime-v2.ts +23 -3
- package/src/workspace/__tests__/{dataplane-adapter.test.ts → git-cascade-adapter.test.ts} +209 -14
- package/src/workspace/__tests__/land-dispatch.test.ts +214 -0
- package/src/workspace/__tests__/self-driving-yaml.test.ts +114 -0
- package/src/workspace/__tests__/shared-worktree-refcount.test.ts +154 -0
- package/src/workspace/__tests__/standalone-mode.test.ts +118 -0
- package/src/workspace/__tests__/workspace-manager-v3.test.ts +245 -0
- package/src/workspace/__tests__/yaml-schema.test.ts +210 -0
- package/src/workspace/config.ts +11 -11
- package/src/workspace/git-cascade-adapter.ts +1213 -0
- package/src/workspace/index.ts +11 -11
- package/src/workspace/landing/__tests__/strategies.test.ts +184 -0
- package/src/workspace/landing/direct-push.ts +91 -0
- package/src/workspace/landing/index.ts +40 -0
- package/src/workspace/landing/merge-to-parent.ts +229 -0
- package/src/workspace/landing/optimistic-push.ts +36 -0
- package/src/workspace/landing/queue-to-branch.ts +108 -0
- package/src/workspace/merge-queue/merge-queue.ts +10 -0
- package/src/workspace/merge-queue/types.ts +16 -2
- package/src/workspace/pool/__tests__/worktree-pool.integration.test.ts +5 -5
- package/src/workspace/pool/types.ts +1 -0
- package/src/workspace/pool/worktree-pool.ts +1 -0
- package/src/workspace/recovery/__tests__/auto-resolve-integration.test.ts +127 -0
- package/src/workspace/recovery/__tests__/spawn-resolver.test.ts +139 -0
- package/src/workspace/recovery/__tests__/strategies.test.ts +145 -0
- package/src/workspace/recovery/abandon.ts +51 -0
- package/src/workspace/recovery/auto-resolve.ts +119 -0
- package/src/workspace/recovery/defer.ts +23 -0
- package/src/workspace/recovery/escalate.ts +30 -0
- package/src/workspace/recovery/index.ts +58 -0
- package/src/workspace/recovery/spawn-resolver.ts +152 -0
- package/src/workspace/recovery/types.ts +54 -0
- package/src/workspace/topology/__tests__/yaml-driven.test.ts +345 -0
- package/src/workspace/topology/index.ts +18 -0
- package/src/workspace/topology/no-workspace.ts +39 -0
- package/src/workspace/topology/types.ts +116 -0
- package/src/workspace/topology/yaml-driven.ts +316 -0
- package/src/workspace/types-v3.ts +162 -0
- package/src/workspace/types.ts +211 -20
- package/src/workspace/workspace-manager.ts +533 -19
- package/src/workspace/yaml-schema.ts +216 -0
- package/dist/workspace/dataplane-adapter.d.ts +0 -260
- package/dist/workspace/dataplane-adapter.d.ts.map +0 -1
- package/dist/workspace/dataplane-adapter.js +0 -416
- package/dist/workspace/dataplane-adapter.js.map +0 -1
- package/src/workspace/dataplane-adapter.ts +0 -546
|
@@ -116,6 +116,29 @@ export interface AgentManagerV2Config {
|
|
|
116
116
|
serverToken?: string;
|
|
117
117
|
/** Control socket path for MCP subprocess lifecycle RPC */
|
|
118
118
|
controlSocketPath?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Default opentasks resource ID hosted on the OpenHive hub. When set,
|
|
121
|
+
* spawn paths build `taskRef = { resource_id: <this>, node_id: task_id }`
|
|
122
|
+
* automatically from `SpawnAgentOptions.task_id` (for any spawn where
|
|
123
|
+
* `resolveTaskRef` returned undefined AND the caller didn't supply an
|
|
124
|
+
* explicit `taskRef`).
|
|
125
|
+
*
|
|
126
|
+
* Operators set this once at swarm registration for the common
|
|
127
|
+
* single-graph case. Multi-graph deployments should use `resolveTaskRef`
|
|
128
|
+
* instead.
|
|
129
|
+
*/
|
|
130
|
+
taskResourceId?: string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Multi-graph resolver. Called at every spawn; return a `TaskRef` to set
|
|
134
|
+
* the binding or `undefined` to fall through to the `taskResourceId`
|
|
135
|
+
* default. Explicit `SpawnAgentOptions.taskRef` always wins over both.
|
|
136
|
+
*
|
|
137
|
+
* Keep cheap — runs per-spawn.
|
|
138
|
+
*/
|
|
139
|
+
resolveTaskRef?: (
|
|
140
|
+
spawnOptions: SpawnAgentOptions
|
|
141
|
+
) => import("git-cascade/events").TaskRef | undefined;
|
|
119
142
|
}
|
|
120
143
|
|
|
121
144
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -139,6 +162,8 @@ export function createAgentManagerV2(
|
|
|
139
162
|
serverToken,
|
|
140
163
|
agentTokenManager,
|
|
141
164
|
controlSocketPath,
|
|
165
|
+
taskResourceId,
|
|
166
|
+
resolveTaskRef,
|
|
142
167
|
} = config;
|
|
143
168
|
|
|
144
169
|
// In-memory state
|
|
@@ -158,6 +183,12 @@ export function createAgentManagerV2(
|
|
|
158
183
|
// MAP sidecar reference for trajectory reporting (set via setSidecar)
|
|
159
184
|
let sidecarRef: { connected: boolean; reportCheckpoint(cp: any): Promise<any> } | null = null;
|
|
160
185
|
|
|
186
|
+
// TopologyPolicy for workspace allocation (Phase 3+); set via setTopologyPolicy.
|
|
187
|
+
// When null, createWorkspaceForRole falls back to legacy role-name dispatch.
|
|
188
|
+
let topologyPolicy:
|
|
189
|
+
| import('../workspace/topology/types.js').TopologyPolicy
|
|
190
|
+
| null = null;
|
|
191
|
+
|
|
161
192
|
// ── Helpers ──────────────────────────────────────────────────
|
|
162
193
|
|
|
163
194
|
function notifyLifecycle(event: AgentLifecycleEvent): void {
|
|
@@ -247,6 +278,109 @@ export function createAgentManagerV2(
|
|
|
247
278
|
|
|
248
279
|
// ── Workspace Helper ─────────────────────────────────────────
|
|
249
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Execute a TopologyPolicy decision against the WorkspaceManager.
|
|
283
|
+
*
|
|
284
|
+
* Translates declarative `WorkspaceDecision` into concrete workspace
|
|
285
|
+
* allocations. Returns a `Workspace` compatible with the legacy shape
|
|
286
|
+
* so the rest of AgentManagerV2 doesn't need to change.
|
|
287
|
+
*/
|
|
288
|
+
async function executeWorkspaceDecision(
|
|
289
|
+
agentId: AgentId,
|
|
290
|
+
decision: import('../workspace/topology/types.js').WorkspaceDecision,
|
|
291
|
+
role?: string,
|
|
292
|
+
spawnOptions?: SpawnAgentOptions
|
|
293
|
+
): Promise<Workspace | undefined> {
|
|
294
|
+
if (!workspaceManager) return undefined;
|
|
295
|
+
|
|
296
|
+
switch (decision.kind) {
|
|
297
|
+
case 'none':
|
|
298
|
+
case 'share-parent-cwd':
|
|
299
|
+
return undefined;
|
|
300
|
+
|
|
301
|
+
case 'share-with-agent': {
|
|
302
|
+
const worktree = workspaceManager.allocateWorktree({
|
|
303
|
+
agentId,
|
|
304
|
+
sharedWithAgent: decision.agentId,
|
|
305
|
+
});
|
|
306
|
+
return {
|
|
307
|
+
agentId,
|
|
308
|
+
path: worktree.path,
|
|
309
|
+
branch: worktree.currentStream
|
|
310
|
+
? `stream/${worktree.currentStream}`
|
|
311
|
+
: 'unknown',
|
|
312
|
+
streamId: worktree.currentStream ?? '',
|
|
313
|
+
role: 'v3', // V3 path — bypass legacy worker task/merge-queue flows
|
|
314
|
+
createdAt: worktree.createdAt,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
case 'attach-to-stream': {
|
|
319
|
+
// Record even if no worktree — the topology needs the stream↔role
|
|
320
|
+
// mapping for event-driven features like on_parent_advanced.
|
|
321
|
+
const attachPolicy = topologyPolicy as unknown as {
|
|
322
|
+
recordAgentStream?: (a: string, s: string, role?: string) => void;
|
|
323
|
+
};
|
|
324
|
+
attachPolicy.recordAgentStream?.(agentId, decision.streamId, role);
|
|
325
|
+
|
|
326
|
+
if (!decision.allocateWorktree) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
const worktree = workspaceManager.allocateWorktree({
|
|
330
|
+
agentId,
|
|
331
|
+
streamId: decision.streamId,
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
agentId,
|
|
335
|
+
path: worktree.path,
|
|
336
|
+
branch: `stream/${decision.streamId}`,
|
|
337
|
+
streamId: decision.streamId,
|
|
338
|
+
role: 'v3', // V3 path — attach-to-team-root
|
|
339
|
+
createdAt: worktree.createdAt,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
case 'new-stream': {
|
|
344
|
+
// If the spawning agent has a taskRef and the streamSpec doesn't
|
|
345
|
+
// already carry one, weave it into metadata so the resulting stream
|
|
346
|
+
// binds to the OpenTasks node. Explicit streamSpec.metadata.task_ref
|
|
347
|
+
// wins.
|
|
348
|
+
const taskRef = spawnOptions?.taskRef;
|
|
349
|
+
const existingMeta = decision.streamSpec.metadata as
|
|
350
|
+
| Record<string, unknown>
|
|
351
|
+
| undefined;
|
|
352
|
+
const streamSpec = taskRef && !existingMeta?.task_ref
|
|
353
|
+
? {
|
|
354
|
+
...decision.streamSpec,
|
|
355
|
+
metadata: { ...(existingMeta ?? {}), task_ref: taskRef },
|
|
356
|
+
}
|
|
357
|
+
: decision.streamSpec;
|
|
358
|
+
const streamId = workspaceManager.createStreamV3(streamSpec);
|
|
359
|
+
// Record the mapping in the topology if it supports it (for share-with lookup).
|
|
360
|
+
const policy = topologyPolicy as unknown as {
|
|
361
|
+
recordAgentStream?: (a: string, s: string, role?: string) => void;
|
|
362
|
+
};
|
|
363
|
+
policy.recordAgentStream?.(agentId, streamId, role);
|
|
364
|
+
|
|
365
|
+
if (!decision.allocateWorktree) {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
const worktree = workspaceManager.allocateWorktree({
|
|
369
|
+
agentId,
|
|
370
|
+
streamId,
|
|
371
|
+
});
|
|
372
|
+
return {
|
|
373
|
+
agentId,
|
|
374
|
+
path: worktree.path,
|
|
375
|
+
branch: `stream/${streamId}`,
|
|
376
|
+
streamId,
|
|
377
|
+
role: 'v3', // V3 path — new-stream
|
|
378
|
+
createdAt: worktree.createdAt,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
250
384
|
async function createWorkspaceForRole(
|
|
251
385
|
agentId: AgentId,
|
|
252
386
|
role: string,
|
|
@@ -254,56 +388,92 @@ export function createAgentManagerV2(
|
|
|
254
388
|
): Promise<Workspace | undefined> {
|
|
255
389
|
if (!workspaceManager) return undefined;
|
|
256
390
|
|
|
391
|
+
// V3 path — TopologyPolicy-driven. Set by boot-v2 when team YAML has
|
|
392
|
+
// `macro_agent.workspace`. When set, this takes precedence over the legacy
|
|
393
|
+
// capability/role-name dispatch below.
|
|
394
|
+
if (topologyPolicy) {
|
|
395
|
+
const decision = await topologyPolicy.onAgentSpawn({
|
|
396
|
+
agentId,
|
|
397
|
+
role,
|
|
398
|
+
parentAgentId: options.parent ?? undefined,
|
|
399
|
+
parentStreamId: options.streamId,
|
|
400
|
+
teamStreamId: (() => {
|
|
401
|
+
const stream = (
|
|
402
|
+
topologyPolicy as { getAgentStream?: (a: AgentId) => string | null }
|
|
403
|
+
).getAgentStream?.(agentId);
|
|
404
|
+
return stream ?? undefined;
|
|
405
|
+
})(),
|
|
406
|
+
workspaceManager,
|
|
407
|
+
getAgentByRole: (r: string) => {
|
|
408
|
+
for (const [aid, ws] of agentWorkspaces) {
|
|
409
|
+
const rec = agentStore.getAgent(aid);
|
|
410
|
+
if (rec?.role === r) return aid;
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
return executeWorkspaceDecision(agentId, decision, role, options);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Capability-based dispatch for programmatic callers that don't use
|
|
419
|
+
// team YAML. This is the supported path for libraries that construct
|
|
420
|
+
// WorkspaceManager + GitCascadeAdapter directly and spawn agents with
|
|
421
|
+
// explicit `capabilities` + `streamId` arguments. It coexists with the
|
|
422
|
+
// V3 topology path above.
|
|
423
|
+
return capabilityBasedDispatch(agentId, options, workspaceManager);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Capability-based workspace allocation for programmatic callers.
|
|
428
|
+
*
|
|
429
|
+
* Matches on `workspace.stream` / `workspace.integrate` / `workspace.worktree`
|
|
430
|
+
* capabilities + corresponding streamId/streamConfig args. Delegates to the
|
|
431
|
+
* role-shaped WorkspaceManager methods (createWorkerWorkspace,
|
|
432
|
+
* createIntegratorWorkspace, createCoordinatorWorkspace).
|
|
433
|
+
*
|
|
434
|
+
* Not used by team-YAML-driven teams — those go through TopologyPolicy above.
|
|
435
|
+
*/
|
|
436
|
+
async function capabilityBasedDispatch(
|
|
437
|
+
agentId: AgentId,
|
|
438
|
+
options: SpawnAgentOptions,
|
|
439
|
+
ws: WorkspaceManager
|
|
440
|
+
): Promise<Workspace | undefined> {
|
|
257
441
|
const capabilities = options.capabilities ?? [];
|
|
258
442
|
const streamId = options.streamId;
|
|
259
|
-
|
|
260
|
-
|
|
443
|
+
// Merge taskRef (if set at spawn time) into streamConfig.metadata so that
|
|
444
|
+
// adapter.createStream → x-cascade/stream.opened carries the binding to
|
|
445
|
+
// OpenTasks. Explicit streamConfig.metadata.task_ref wins if already set.
|
|
446
|
+
const streamConfig = options.streamConfig
|
|
447
|
+
? options.taskRef &&
|
|
448
|
+
!(options.streamConfig.metadata &&
|
|
449
|
+
(options.streamConfig.metadata as Record<string, unknown>).task_ref)
|
|
450
|
+
? {
|
|
451
|
+
...options.streamConfig,
|
|
452
|
+
metadata: {
|
|
453
|
+
...(options.streamConfig.metadata ?? {}),
|
|
454
|
+
task_ref: options.taskRef,
|
|
455
|
+
},
|
|
456
|
+
}
|
|
457
|
+
: options.streamConfig
|
|
458
|
+
: undefined;
|
|
459
|
+
const gitCascadeTaskId = options.gitCascadeTaskId;
|
|
261
460
|
|
|
262
|
-
// Capability-based dispatch
|
|
263
461
|
if (capabilities.includes("workspace.stream") && streamConfig) {
|
|
264
|
-
const newStreamId =
|
|
265
|
-
|
|
266
|
-
streamConfig
|
|
267
|
-
);
|
|
268
|
-
return workspaceManager.createCoordinatorWorkspace(agentId, newStreamId);
|
|
462
|
+
const newStreamId = ws.createIntegrationStream(agentId, streamConfig);
|
|
463
|
+
return ws.createCoordinatorWorkspace(agentId, newStreamId);
|
|
269
464
|
}
|
|
270
465
|
|
|
271
466
|
if (capabilities.includes("workspace.integrate") && streamId) {
|
|
272
|
-
return
|
|
467
|
+
return ws.createIntegratorWorkspace(agentId, streamId);
|
|
273
468
|
}
|
|
274
469
|
|
|
275
470
|
if (capabilities.includes("workspace.worktree") && streamId) {
|
|
276
|
-
const taskId =
|
|
277
|
-
return
|
|
471
|
+
const taskId = gitCascadeTaskId ?? agentId;
|
|
472
|
+
return ws.createWorkerWorkspace(agentId, taskId, streamId);
|
|
278
473
|
}
|
|
279
474
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
case "coordinator":
|
|
283
|
-
if (streamConfig) {
|
|
284
|
-
const sid = workspaceManager.createIntegrationStream(
|
|
285
|
-
agentId,
|
|
286
|
-
streamConfig
|
|
287
|
-
);
|
|
288
|
-
return workspaceManager.createCoordinatorWorkspace(agentId, sid);
|
|
289
|
-
}
|
|
290
|
-
return undefined;
|
|
291
|
-
case "integrator":
|
|
292
|
-
if (streamId) {
|
|
293
|
-
return workspaceManager.createIntegratorWorkspace(agentId, streamId);
|
|
294
|
-
}
|
|
295
|
-
return undefined;
|
|
296
|
-
case "worker":
|
|
297
|
-
case "worker.resolver": {
|
|
298
|
-
if (streamId) {
|
|
299
|
-
const tid = dataplaneTaskId ?? agentId;
|
|
300
|
-
return workspaceManager.createWorkerWorkspace(agentId, tid, streamId);
|
|
301
|
-
}
|
|
302
|
-
return undefined;
|
|
303
|
-
}
|
|
304
|
-
default:
|
|
305
|
-
return undefined;
|
|
306
|
-
}
|
|
475
|
+
// No matching capability — agent inherits parent cwd (no workspace)
|
|
476
|
+
return undefined;
|
|
307
477
|
}
|
|
308
478
|
|
|
309
479
|
// ── Core Lifecycle ───────────────────────────────────────────
|
|
@@ -317,10 +487,40 @@ export function createAgentManagerV2(
|
|
|
317
487
|
}
|
|
318
488
|
|
|
319
489
|
// Apply spawn interceptor (set by TeamRuntime)
|
|
320
|
-
const
|
|
490
|
+
const interceptedOptions = spawnInterceptor
|
|
321
491
|
? await spawnInterceptor(rawOptions)
|
|
322
492
|
: rawOptions;
|
|
323
493
|
|
|
494
|
+
// Resolve taskRef with three-level precedence:
|
|
495
|
+
// 1. Explicit `options.taskRef` (caller knows exactly what graph).
|
|
496
|
+
// 2. `resolveTaskRef(opts)` (multi-graph deployments decide per spawn).
|
|
497
|
+
// 3. `taskResourceId` + `options.task_id` (single-graph default).
|
|
498
|
+
// If none resolves, spawn proceeds with no taskRef — cascade events
|
|
499
|
+
// land without a task binding (hub back-fills from first commit that
|
|
500
|
+
// carries one, if any).
|
|
501
|
+
let resolvedTaskRef = interceptedOptions.taskRef;
|
|
502
|
+
if (!resolvedTaskRef && resolveTaskRef) {
|
|
503
|
+
try {
|
|
504
|
+
resolvedTaskRef = resolveTaskRef(interceptedOptions);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
// Resolver failures must not block spawn. Log + fall through.
|
|
507
|
+
// eslint-disable-next-line no-console
|
|
508
|
+
console.warn(
|
|
509
|
+
"[agent-manager-v2] resolveTaskRef threw; falling back to taskResourceId default:",
|
|
510
|
+
err instanceof Error ? err.message : err
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (!resolvedTaskRef && taskResourceId && interceptedOptions.task_id) {
|
|
515
|
+
resolvedTaskRef = {
|
|
516
|
+
resource_id: taskResourceId,
|
|
517
|
+
node_id: String(interceptedOptions.task_id),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
const options = resolvedTaskRef === interceptedOptions.taskRef
|
|
521
|
+
? interceptedOptions
|
|
522
|
+
: { ...interceptedOptions, taskRef: resolvedTaskRef };
|
|
523
|
+
|
|
324
524
|
const {
|
|
325
525
|
task,
|
|
326
526
|
task_id,
|
|
@@ -404,7 +604,9 @@ export function createAgentManagerV2(
|
|
|
404
604
|
systemPrompt += `\n\n${interactionPatterns.join("\n\n")}`;
|
|
405
605
|
}
|
|
406
606
|
|
|
407
|
-
// Persist agent in store
|
|
607
|
+
// Persist agent in store. Stash taskRef in metadata so done()'s
|
|
608
|
+
// lifecycle context can read it without separate plumbing — this is the
|
|
609
|
+
// path that makes per-commit task_ref binding work end-to-end.
|
|
408
610
|
const now = Date.now() as Timestamp;
|
|
409
611
|
const agentRecord: AgentRecord = {
|
|
410
612
|
id: agentId,
|
|
@@ -422,7 +624,7 @@ export function createAgentManagerV2(
|
|
|
422
624
|
created_at: now,
|
|
423
625
|
started_at: now,
|
|
424
626
|
config: agentConfig as Record<string, unknown>,
|
|
425
|
-
metadata: {},
|
|
627
|
+
metadata: options.taskRef ? { task_ref: options.taskRef } : {},
|
|
426
628
|
};
|
|
427
629
|
agentStore.putAgent(agentRecord);
|
|
428
630
|
|
|
@@ -456,13 +658,13 @@ export function createAgentManagerV2(
|
|
|
456
658
|
if (workspace) {
|
|
457
659
|
agentWorkspaces.set(agentId, workspace);
|
|
458
660
|
|
|
459
|
-
// Create and claim
|
|
661
|
+
// Create and claim git-cascade task for workers
|
|
460
662
|
if (
|
|
461
663
|
workspace.role === "worker" &&
|
|
462
664
|
workspace.streamId &&
|
|
463
665
|
workspaceManager
|
|
464
666
|
) {
|
|
465
|
-
const dpTaskId = options.
|
|
667
|
+
const dpTaskId = options.gitCascadeTaskId ?? agentId;
|
|
466
668
|
workspaceManager.createTask(workspace.streamId, {
|
|
467
669
|
title: task ?? `Task for ${agentId}`,
|
|
468
670
|
});
|
|
@@ -588,9 +790,11 @@ export function createAgentManagerV2(
|
|
|
588
790
|
created_at: now,
|
|
589
791
|
});
|
|
590
792
|
|
|
591
|
-
// Update agent with provider session ID
|
|
793
|
+
// Update agent with provider session ID. Merge with existing metadata
|
|
794
|
+
// so fields set at spawn time (e.g. task_ref) aren't clobbered.
|
|
795
|
+
const existingMeta = agentStore.getAgent(agentId)?.metadata ?? {};
|
|
592
796
|
agentStore.updateAgent(agentId, {
|
|
593
|
-
metadata: { provider_session_id: session.id },
|
|
797
|
+
metadata: { ...existingMeta, provider_session_id: session.id },
|
|
594
798
|
});
|
|
595
799
|
|
|
596
800
|
// Register agent in inbox
|
|
@@ -675,7 +879,16 @@ export function createAgentManagerV2(
|
|
|
675
879
|
healthCheckService.stopForCoordinator(agentId);
|
|
676
880
|
}
|
|
677
881
|
|
|
678
|
-
//
|
|
882
|
+
// Land the worker's work if completed with a workspace.
|
|
883
|
+
//
|
|
884
|
+
// V3 path (preferred): look up the role's YAML landing strategy via
|
|
885
|
+
// TopologyPolicy.getRoleConfig and dispatch through
|
|
886
|
+
// WorkspaceManager.land(). This fires cascade events (stream.merged or
|
|
887
|
+
// queue.added) so the hub sees the work. Landing = 'none' short-circuits.
|
|
888
|
+
//
|
|
889
|
+
// Legacy fallback: if no TopologyPolicy is wired or it can't resolve a
|
|
890
|
+
// landing for this role, submit to the legacy MergeQueue as before.
|
|
891
|
+
// Keeps pre-V3 programmatic callers + tests that bypass YAML working.
|
|
679
892
|
if (
|
|
680
893
|
workspaceManager &&
|
|
681
894
|
agentWorkspaces.has(agentId) &&
|
|
@@ -683,18 +896,45 @@ export function createAgentManagerV2(
|
|
|
683
896
|
) {
|
|
684
897
|
const ws = agentWorkspaces.get(agentId)!;
|
|
685
898
|
if (ws.role === "worker" && ws.streamId) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
899
|
+
const roleConfig = topologyPolicy?.getRoleConfig?.(record.role);
|
|
900
|
+
const yamlLandingName = roleConfig?.landing;
|
|
901
|
+
const usingV3Landing =
|
|
902
|
+
typeof yamlLandingName === "string" && yamlLandingName.length > 0;
|
|
903
|
+
|
|
904
|
+
if (usingV3Landing) {
|
|
905
|
+
try {
|
|
906
|
+
const taskRef = (record.metadata as Record<string, unknown> | undefined)
|
|
907
|
+
?.task_ref as { resource_id: string; node_id: string } | undefined;
|
|
908
|
+
await workspaceManager.land({
|
|
909
|
+
agentId,
|
|
690
910
|
streamId: ws.streamId,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
911
|
+
sourceWorktree: ws.path,
|
|
912
|
+
strategyName: yamlLandingName,
|
|
913
|
+
strategyConfig: roleConfig?.landing_config,
|
|
914
|
+
taskRef,
|
|
915
|
+
// Dispatcher overwrites this with `this`; placeholder keeps the
|
|
916
|
+
// type satisfied without a cast.
|
|
917
|
+
workspaceManager,
|
|
694
918
|
});
|
|
919
|
+
} catch {
|
|
920
|
+
// Non-fatal landing failure — agent still terminates; conflicts
|
|
921
|
+
// and strategy errors surface via WorkspaceEvent emission and
|
|
922
|
+
// the strategy's own logs.
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
try {
|
|
926
|
+
const mergeQueue = workspaceManager.getMergeQueue();
|
|
927
|
+
if (mergeQueue) {
|
|
928
|
+
mergeQueue.submit({
|
|
929
|
+
streamId: ws.streamId,
|
|
930
|
+
workerBranch: ws.branch,
|
|
931
|
+
taskId: record.task_id ?? agentId,
|
|
932
|
+
workerAgentId: agentId,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
} catch {
|
|
936
|
+
// Non-fatal merge queue submission failure
|
|
695
937
|
}
|
|
696
|
-
} catch {
|
|
697
|
-
// Non-fatal merge queue submission failure
|
|
698
938
|
}
|
|
699
939
|
}
|
|
700
940
|
}
|
|
@@ -783,11 +1023,16 @@ export function createAgentManagerV2(
|
|
|
783
1023
|
.map((r) => agentRecordToAgent(r)),
|
|
784
1024
|
terminate: (id: AgentId, r: AgentStopReason) => terminate(id, r),
|
|
785
1025
|
};
|
|
1026
|
+
const parentTaskRef = (record.metadata as Record<string, unknown> | undefined)
|
|
1027
|
+
?.task_ref as { resource_id: string; node_id: string } | undefined;
|
|
786
1028
|
await terminateWithChangeConsolidation(
|
|
787
1029
|
child.id as AgentId,
|
|
788
1030
|
agentId,
|
|
789
1031
|
cascadeAdapter,
|
|
790
|
-
wsProvider
|
|
1032
|
+
wsProvider,
|
|
1033
|
+
undefined,
|
|
1034
|
+
workspaceManager ?? undefined,
|
|
1035
|
+
parentTaskRef
|
|
791
1036
|
);
|
|
792
1037
|
}
|
|
793
1038
|
}
|
|
@@ -893,6 +1138,21 @@ export function createAgentManagerV2(
|
|
|
893
1138
|
});
|
|
894
1139
|
|
|
895
1140
|
const agent = agentRecordToAgent(agentStore.getAgent(agentId)!);
|
|
1141
|
+
|
|
1142
|
+
// Re-publish the agent to subscribers (local MAP server, hub lifecycle
|
|
1143
|
+
// bridge, team auto-join listeners) so a resumed agent is a first-class
|
|
1144
|
+
// registered agent — not just an in-memory handle. Without this, the hub
|
|
1145
|
+
// never re-registers the agent after cold-start; ACP routing works but
|
|
1146
|
+
// the hub's "Registered Agents" view stays empty and capabilities never
|
|
1147
|
+
// propagate back through `map/agents/register`.
|
|
1148
|
+
//
|
|
1149
|
+
// Spawn semantics are correct here: the process is new, the session is
|
|
1150
|
+
// (re)loaded, and subscribers treat it as a fresh registration. Paired
|
|
1151
|
+
// with the `stopped` event that fired on the prior termination, this
|
|
1152
|
+
// keeps the bridge's `registered` map consistent.
|
|
1153
|
+
notifyLifecycle({ type: "spawned", agent });
|
|
1154
|
+
notifyLifecycle({ type: "started", agent });
|
|
1155
|
+
|
|
896
1156
|
return {
|
|
897
1157
|
id: agentId,
|
|
898
1158
|
session_id: sessionRecord?.session_id ?? "",
|
|
@@ -1134,12 +1394,21 @@ export function createAgentManagerV2(
|
|
|
1134
1394
|
async function getOrCreateHeadManager(
|
|
1135
1395
|
options: HeadManagerOptions
|
|
1136
1396
|
): Promise<SpawnedAgent> {
|
|
1137
|
-
// Check for existing head manager
|
|
1397
|
+
// Check for an existing head manager matching this cwd that ALSO has a
|
|
1398
|
+
// live session in this process. The activeSessions check has to be inside
|
|
1399
|
+
// the predicate (not after .find) — the agentStore is persistent across
|
|
1400
|
+
// process restarts, so without this filter we'd match stale "running"
|
|
1401
|
+
// records from previous processes whose sessions are gone, then fall
|
|
1402
|
+
// through to spawn() and create a duplicate coordinator.
|
|
1138
1403
|
const existing = agentStore
|
|
1139
1404
|
.listAgents({ parent_id: null, state: "running" })
|
|
1140
|
-
.find(
|
|
1405
|
+
.find(
|
|
1406
|
+
(a) =>
|
|
1407
|
+
a.cwd === options.cwd &&
|
|
1408
|
+
activeSessions.has(a.id as AgentId),
|
|
1409
|
+
);
|
|
1141
1410
|
|
|
1142
|
-
if (existing
|
|
1411
|
+
if (existing) {
|
|
1143
1412
|
const sessionEntry = activeSessions.get(existing.id as AgentId)!;
|
|
1144
1413
|
const storedSession = agentStore.getSession(existing.id as AgentId);
|
|
1145
1414
|
return {
|
|
@@ -1167,6 +1436,30 @@ export function createAgentManagerV2(
|
|
|
1167
1436
|
.map(agentRecordToAgent);
|
|
1168
1437
|
}
|
|
1169
1438
|
|
|
1439
|
+
/**
|
|
1440
|
+
* Look up the spawned-agent shape for any agent that's still alive in this
|
|
1441
|
+
* process (any role, not just coordinators). Returns null if the agent
|
|
1442
|
+
* doesn't exist, isn't running, or has no live session in `activeSessions`.
|
|
1443
|
+
*
|
|
1444
|
+
* Used by the ACP layer to bind a session to a specific agent when the MAP
|
|
1445
|
+
* stream targets one explicitly — preserving the routing intent that
|
|
1446
|
+
* cwd-based head-manager lookup would otherwise lose in multi-coordinator
|
|
1447
|
+
* scenarios.
|
|
1448
|
+
*/
|
|
1449
|
+
function getActiveAgentSession(agentId: AgentId): SpawnedAgent | null {
|
|
1450
|
+
if (!activeSessions.has(agentId)) return null;
|
|
1451
|
+
const record = agentStore.getAgent(agentId);
|
|
1452
|
+
if (!record || record.state !== "running") return null;
|
|
1453
|
+
const sessionEntry = activeSessions.get(agentId)!;
|
|
1454
|
+
const storedSession = agentStore.getSession(agentId);
|
|
1455
|
+
return {
|
|
1456
|
+
id: agentId,
|
|
1457
|
+
session_id: storedSession?.session_id ?? sessionEntry.session.id ?? "",
|
|
1458
|
+
agent: agentRecordToAgent(record),
|
|
1459
|
+
session: sessionEntry.session,
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1170
1463
|
// ── Session Interaction ──────────────────────────────────────
|
|
1171
1464
|
|
|
1172
1465
|
async function* prompt(
|
|
@@ -1263,6 +1556,18 @@ export function createAgentManagerV2(
|
|
|
1263
1556
|
}
|
|
1264
1557
|
}
|
|
1265
1558
|
|
|
1559
|
+
// Auto-terminate when done() was called and the handler signaled shouldTerminate.
|
|
1560
|
+
// This closes the lifecycle gap: without this, agents stay in "running" state
|
|
1561
|
+
// after calling done() because nothing triggers terminate().
|
|
1562
|
+
if (doneCalled) {
|
|
1563
|
+
const reason = doneStatus === "completed" ? "completed" : (doneStatus ?? "failed");
|
|
1564
|
+
try {
|
|
1565
|
+
await terminate(agentId, reason as any);
|
|
1566
|
+
} catch {
|
|
1567
|
+
// Best effort — agent may already be stopping
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1266
1571
|
return { doneCalled, doneStatus, updates: allUpdates };
|
|
1267
1572
|
}
|
|
1268
1573
|
|
|
@@ -1380,6 +1685,12 @@ export function createAgentManagerV2(
|
|
|
1380
1685
|
sidecarRef = sidecar;
|
|
1381
1686
|
}
|
|
1382
1687
|
|
|
1688
|
+
function setTopologyPolicyFn(
|
|
1689
|
+
policy: import('../workspace/topology/types.js').TopologyPolicy | null
|
|
1690
|
+
): void {
|
|
1691
|
+
topologyPolicy = policy;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1383
1694
|
function setMailServices(): void {
|
|
1384
1695
|
// No-op: agent-inbox handles conversation tracking
|
|
1385
1696
|
}
|
|
@@ -1422,6 +1733,7 @@ export function createAgentManagerV2(
|
|
|
1422
1733
|
getHierarchy,
|
|
1423
1734
|
getOrCreateHeadManager,
|
|
1424
1735
|
listHeadManagers,
|
|
1736
|
+
getActiveAgentSession,
|
|
1425
1737
|
prompt,
|
|
1426
1738
|
promptUntilDone,
|
|
1427
1739
|
getSession,
|
|
@@ -1441,6 +1753,7 @@ export function createAgentManagerV2(
|
|
|
1441
1753
|
setIntegrationConfigs,
|
|
1442
1754
|
setSkillLoadout,
|
|
1443
1755
|
setSidecar,
|
|
1756
|
+
setTopologyPolicy: setTopologyPolicyFn,
|
|
1444
1757
|
setMailServices,
|
|
1445
1758
|
close,
|
|
1446
1759
|
} as AgentManager;
|
|
@@ -131,6 +131,12 @@ export interface AgentManager {
|
|
|
131
131
|
*/
|
|
132
132
|
listHeadManagers(): Agent[];
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Look up the SpawnedAgent shape for any agent (any role) that's running
|
|
136
|
+
* AND has a live session in this process. Returns null otherwise.
|
|
137
|
+
*/
|
|
138
|
+
getActiveAgentSession(agentId: AgentId): SpawnedAgent | null;
|
|
139
|
+
|
|
134
140
|
// ── Session Interaction ────────────────────────────────────────
|
|
135
141
|
|
|
136
142
|
/**
|
|
@@ -296,6 +302,14 @@ export interface AgentManager {
|
|
|
296
302
|
*/
|
|
297
303
|
setSidecar(sidecar: { connected: boolean; reportCheckpoint(cp: any): Promise<any> } | null): void;
|
|
298
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Set a TopologyPolicy for workspace allocation decisions.
|
|
307
|
+
* When set, AgentManagerV2 delegates `createWorkspaceForRole` to the policy
|
|
308
|
+
* before falling back to role-name dispatch. Set by boot-v2 when team YAML
|
|
309
|
+
* contains `macro_agent.workspace`.
|
|
310
|
+
*/
|
|
311
|
+
setTopologyPolicy(policy: import('../workspace/topology/types.js').TopologyPolicy | null): void;
|
|
312
|
+
|
|
299
313
|
// ── Cleanup ────────────────────────────────────────────────────
|
|
300
314
|
|
|
301
315
|
/**
|
package/src/agent/agent-store.ts
CHANGED
|
@@ -340,6 +340,30 @@ export class AgentStore {
|
|
|
340
340
|
};
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Reverse lookup: find the session row whose provider_session_id matches.
|
|
345
|
+
* Used by `_macro/resumeAgent` to resolve session → agent when only the
|
|
346
|
+
* Claude Code session UUID is known (e.g. OpenHive asks to resume a session
|
|
347
|
+
* by its persisted provider_session_id).
|
|
348
|
+
*
|
|
349
|
+
* Returns the most recently created session if multiple agents ever held
|
|
350
|
+
* the same provider_session_id — shouldn't happen, but defensive.
|
|
351
|
+
*/
|
|
352
|
+
findSessionByProviderSessionId(providerSessionId: string): SessionRecord | null {
|
|
353
|
+
const row = this.db
|
|
354
|
+
.prepare(
|
|
355
|
+
"SELECT * FROM sessions WHERE provider_session_id = ? ORDER BY created_at DESC LIMIT 1"
|
|
356
|
+
)
|
|
357
|
+
.get(providerSessionId) as Record<string, unknown> | undefined;
|
|
358
|
+
if (!row) return null;
|
|
359
|
+
return {
|
|
360
|
+
agent_id: row.agent_id as string,
|
|
361
|
+
session_id: row.session_id as string,
|
|
362
|
+
provider_session_id: (row.provider_session_id as string) || undefined,
|
|
363
|
+
created_at: row.created_at as number,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
343
367
|
removeSession(agentId: AgentId): void {
|
|
344
368
|
this.db.prepare("DELETE FROM sessions WHERE agent_id = ?").run(agentId);
|
|
345
369
|
}
|