macro-agent 0.1.7 → 0.1.10
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 +179 -38
- 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 +155 -6
- 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/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 +234 -71
- 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/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 +41 -0
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +34 -37
- package/dist/boot-v2.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/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-bridge.d.ts +44 -0
- package/dist/map/cascade-bridge.d.ts.map +1 -0
- package/dist/map/cascade-bridge.js +257 -0
- package/dist/map/cascade-bridge.js.map +1 -0
- package/dist/map/lifecycle-bridge.d.ts +1 -8
- package/dist/map/lifecycle-bridge.d.ts.map +1 -1
- package/dist/map/lifecycle-bridge.js +76 -22
- package/dist/map/lifecycle-bridge.js.map +1 -1
- package/dist/map/server.d.ts.map +1 -1
- package/dist/map/server.js +47 -6
- package/dist/map/server.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +33 -4
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/types.d.ts +20 -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 +908 -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 +185 -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 +111 -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 +110 -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 +145 -17
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +92 -13
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +373 -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/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 +4 -4
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -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/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 +167 -9
- package/src/acp/types.ts +10 -0
- package/src/agent/__tests__/agent-manager-topology.test.ts +73 -0
- package/src/agent/__tests__/agent-manager-v2.test.ts +71 -11
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +293 -77
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +87 -36
- package/src/cli/index.ts +61 -0
- package/src/cognitive/__tests__/macro-agent-backend.test.ts +47 -5
- package/src/cognitive/macro-agent-backend.ts +45 -29
- package/src/integrations/skilltree.ts +1 -0
- 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__/lifecycle-bridge.test.ts +165 -22
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-bridge.ts +301 -0
- package/src/map/lifecycle-bridge.ts +77 -27
- package/src/map/server.ts +47 -6
- package/src/map/sidecar.ts +31 -3
- package/src/map/types.ts +20 -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__/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 +1186 -0
- package/src/workspace/index.ts +11 -11
- package/src/workspace/landing/__tests__/strategies.test.ts +142 -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 +228 -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 +145 -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 +155 -0
- package/src/workspace/types.ts +191 -20
- package/src/workspace/workspace-manager.ts +474 -19
- package/src/workspace/yaml-schema.ts +216 -0
- 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
|
|
@@ -600,20 +804,6 @@ export function createAgentManagerV2(
|
|
|
600
804
|
scope: team_instance ?? "default",
|
|
601
805
|
});
|
|
602
806
|
|
|
603
|
-
// Create task in opentasks
|
|
604
|
-
if (tasksAdapter.connected) {
|
|
605
|
-
try {
|
|
606
|
-
const otTaskId = await tasksAdapter.createTask({
|
|
607
|
-
title: task ?? `Task for ${agentId}`,
|
|
608
|
-
assignee: agentId,
|
|
609
|
-
tags: role ? [role] : [],
|
|
610
|
-
});
|
|
611
|
-
agentStore.updateAgent(agentId, { task_id: otTaskId });
|
|
612
|
-
} catch {
|
|
613
|
-
// Non-fatal — opentasks may not be available
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
807
|
// Track active session
|
|
618
808
|
const activeSession: ActiveSession = {
|
|
619
809
|
agentId,
|
|
@@ -728,21 +918,6 @@ export function createAgentManagerV2(
|
|
|
728
918
|
agentTokenManager.revokeToken(agentId);
|
|
729
919
|
}
|
|
730
920
|
|
|
731
|
-
// Transition task in opentasks
|
|
732
|
-
if (record.task_id && tasksAdapter.connected) {
|
|
733
|
-
try {
|
|
734
|
-
const action =
|
|
735
|
-
reason === "completed"
|
|
736
|
-
? "complete"
|
|
737
|
-
: reason === "failed"
|
|
738
|
-
? "fail"
|
|
739
|
-
: "block";
|
|
740
|
-
await tasksAdapter.transitionTask(record.task_id, action as any);
|
|
741
|
-
} catch {
|
|
742
|
-
// Non-fatal task transition failure
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
921
|
// Notify parent via inbox
|
|
747
922
|
if (record.parent_id) {
|
|
748
923
|
try {
|
|
@@ -1163,12 +1338,21 @@ export function createAgentManagerV2(
|
|
|
1163
1338
|
async function getOrCreateHeadManager(
|
|
1164
1339
|
options: HeadManagerOptions
|
|
1165
1340
|
): Promise<SpawnedAgent> {
|
|
1166
|
-
// Check for existing head manager
|
|
1341
|
+
// Check for an existing head manager matching this cwd that ALSO has a
|
|
1342
|
+
// live session in this process. The activeSessions check has to be inside
|
|
1343
|
+
// the predicate (not after .find) — the agentStore is persistent across
|
|
1344
|
+
// process restarts, so without this filter we'd match stale "running"
|
|
1345
|
+
// records from previous processes whose sessions are gone, then fall
|
|
1346
|
+
// through to spawn() and create a duplicate coordinator.
|
|
1167
1347
|
const existing = agentStore
|
|
1168
1348
|
.listAgents({ parent_id: null, state: "running" })
|
|
1169
|
-
.find(
|
|
1349
|
+
.find(
|
|
1350
|
+
(a) =>
|
|
1351
|
+
a.cwd === options.cwd &&
|
|
1352
|
+
activeSessions.has(a.id as AgentId),
|
|
1353
|
+
);
|
|
1170
1354
|
|
|
1171
|
-
if (existing
|
|
1355
|
+
if (existing) {
|
|
1172
1356
|
const sessionEntry = activeSessions.get(existing.id as AgentId)!;
|
|
1173
1357
|
const storedSession = agentStore.getSession(existing.id as AgentId);
|
|
1174
1358
|
return {
|
|
@@ -1196,6 +1380,30 @@ export function createAgentManagerV2(
|
|
|
1196
1380
|
.map(agentRecordToAgent);
|
|
1197
1381
|
}
|
|
1198
1382
|
|
|
1383
|
+
/**
|
|
1384
|
+
* Look up the spawned-agent shape for any agent that's still alive in this
|
|
1385
|
+
* process (any role, not just coordinators). Returns null if the agent
|
|
1386
|
+
* doesn't exist, isn't running, or has no live session in `activeSessions`.
|
|
1387
|
+
*
|
|
1388
|
+
* Used by the ACP layer to bind a session to a specific agent when the MAP
|
|
1389
|
+
* stream targets one explicitly — preserving the routing intent that
|
|
1390
|
+
* cwd-based head-manager lookup would otherwise lose in multi-coordinator
|
|
1391
|
+
* scenarios.
|
|
1392
|
+
*/
|
|
1393
|
+
function getActiveAgentSession(agentId: AgentId): SpawnedAgent | null {
|
|
1394
|
+
if (!activeSessions.has(agentId)) return null;
|
|
1395
|
+
const record = agentStore.getAgent(agentId);
|
|
1396
|
+
if (!record || record.state !== "running") return null;
|
|
1397
|
+
const sessionEntry = activeSessions.get(agentId)!;
|
|
1398
|
+
const storedSession = agentStore.getSession(agentId);
|
|
1399
|
+
return {
|
|
1400
|
+
id: agentId,
|
|
1401
|
+
session_id: storedSession?.session_id ?? sessionEntry.session.id ?? "",
|
|
1402
|
+
agent: agentRecordToAgent(record),
|
|
1403
|
+
session: sessionEntry.session,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1199
1407
|
// ── Session Interaction ──────────────────────────────────────
|
|
1200
1408
|
|
|
1201
1409
|
async function* prompt(
|
|
@@ -1409,6 +1617,12 @@ export function createAgentManagerV2(
|
|
|
1409
1617
|
sidecarRef = sidecar;
|
|
1410
1618
|
}
|
|
1411
1619
|
|
|
1620
|
+
function setTopologyPolicyFn(
|
|
1621
|
+
policy: import('../workspace/topology/types.js').TopologyPolicy | null
|
|
1622
|
+
): void {
|
|
1623
|
+
topologyPolicy = policy;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1412
1626
|
function setMailServices(): void {
|
|
1413
1627
|
// No-op: agent-inbox handles conversation tracking
|
|
1414
1628
|
}
|
|
@@ -1451,6 +1665,7 @@ export function createAgentManagerV2(
|
|
|
1451
1665
|
getHierarchy,
|
|
1452
1666
|
getOrCreateHeadManager,
|
|
1453
1667
|
listHeadManagers,
|
|
1668
|
+
getActiveAgentSession,
|
|
1454
1669
|
prompt,
|
|
1455
1670
|
promptUntilDone,
|
|
1456
1671
|
getSession,
|
|
@@ -1470,6 +1685,7 @@ export function createAgentManagerV2(
|
|
|
1470
1685
|
setIntegrationConfigs,
|
|
1471
1686
|
setSkillLoadout,
|
|
1472
1687
|
setSidecar,
|
|
1688
|
+
setTopologyPolicy: setTopologyPolicyFn,
|
|
1473
1689
|
setMailServices,
|
|
1474
1690
|
close,
|
|
1475
1691
|
} 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/types.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
StreamConfig,
|
|
16
16
|
Workspace,
|
|
17
17
|
} from "../workspace/types.js";
|
|
18
|
+
import type { TaskRef } from "git-cascade/events";
|
|
18
19
|
|
|
19
20
|
// ─────────────────────────────────────────────────────────────────
|
|
20
21
|
// Spawn Options
|
|
@@ -90,16 +91,29 @@ export interface SpawnAgentOptions {
|
|
|
90
91
|
streamConfig?: StreamConfig;
|
|
91
92
|
|
|
92
93
|
/**
|
|
93
|
-
*
|
|
94
|
+
* git-cascade task ID to claim (for workers).
|
|
94
95
|
* If provided, the worker will claim this task and work on it.
|
|
95
96
|
*/
|
|
96
|
-
|
|
97
|
+
gitCascadeTaskId?: string;
|
|
97
98
|
|
|
98
99
|
/**
|
|
99
100
|
* Resolved capabilities for this agent's role.
|
|
100
101
|
* Injected by TeamRuntime spawn interceptor for capability-based workspace dispatch.
|
|
101
102
|
*/
|
|
102
103
|
capabilities?: string[];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Optional reference to an external task this agent is working on. When
|
|
107
|
+
* set, the reference is woven into cascade event metadata so OpenHive (or
|
|
108
|
+
* any other observer) can bind the agent's commits, merges, and streams
|
|
109
|
+
* to the OpenTasks node. Propagates through:
|
|
110
|
+
* - stream creation: `adapter.createStream({ metadata: { task_ref } })`
|
|
111
|
+
* - commits: `adapter.commitChanges({ metadata: { task_ref } })` (pass
|
|
112
|
+
* per-commit; each commit can carry a distinct sub-task binding)
|
|
113
|
+
* Can also arrive late via pull-mode `claim_task` — workers that claim a
|
|
114
|
+
* task mid-session should include the ref on subsequent commits.
|
|
115
|
+
*/
|
|
116
|
+
taskRef?: TaskRef;
|
|
103
117
|
}
|
|
104
118
|
|
|
105
119
|
/**
|