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
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cascade Action Handler — receives hub→runtime commands.
|
|
3
|
+
*
|
|
4
|
+
* Listens for `x-cascade/request.*` notifications from the OpenHive hub
|
|
5
|
+
* and dispatches to the GitCascadeAdapter. This is the inbound counterpart
|
|
6
|
+
* to the CascadeBridge (which handles outbound events).
|
|
7
|
+
*
|
|
8
|
+
* Actions are fire-and-forget from the hub's perspective: the hub sends
|
|
9
|
+
* the notification and the UI updates reactively when the resulting
|
|
10
|
+
* `x-cascade/stream.*` event flows back through the bridge.
|
|
11
|
+
*
|
|
12
|
+
* @module map/cascade-action-handler
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { GitCascadeAdapter } from '../workspace/git-cascade-adapter.js';
|
|
16
|
+
|
|
17
|
+
export interface CascadeActionConnection {
|
|
18
|
+
onNotification(
|
|
19
|
+
method: string,
|
|
20
|
+
handler: (params: unknown) => void | Promise<void>,
|
|
21
|
+
): void;
|
|
22
|
+
offNotification(
|
|
23
|
+
method: string,
|
|
24
|
+
handler: (params: unknown) => void | Promise<void>,
|
|
25
|
+
): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const REQUEST_METHODS = {
|
|
29
|
+
MERGE: 'x-cascade/request.merge',
|
|
30
|
+
ABANDON: 'x-cascade/request.abandon',
|
|
31
|
+
PAUSE: 'x-cascade/request.pause',
|
|
32
|
+
RESUME: 'x-cascade/request.resume',
|
|
33
|
+
RESOLVE: 'x-cascade/request.resolve',
|
|
34
|
+
PUSH: 'x-cascade/request.push',
|
|
35
|
+
COMMIT: 'x-cascade/request.commit',
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register cascade action handlers on the MAP connection.
|
|
40
|
+
* Returns a cleanup function that removes all handlers.
|
|
41
|
+
*/
|
|
42
|
+
export function setupCascadeActionHandlers(
|
|
43
|
+
connection: CascadeActionConnection,
|
|
44
|
+
adapter: GitCascadeAdapter,
|
|
45
|
+
): () => void {
|
|
46
|
+
const handlers: Array<{
|
|
47
|
+
method: string;
|
|
48
|
+
handler: (params: unknown) => void | Promise<void>;
|
|
49
|
+
}> = [];
|
|
50
|
+
|
|
51
|
+
const register = (
|
|
52
|
+
method: string,
|
|
53
|
+
handler: (params: unknown) => void | Promise<void>,
|
|
54
|
+
): void => {
|
|
55
|
+
connection.onNotification(method, handler);
|
|
56
|
+
handlers.push({ method, handler });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Find the first worktree checked out on a given stream. */
|
|
60
|
+
function findWorktreeForStream(streamId: string): string | null {
|
|
61
|
+
const wts = adapter.listWorktrees();
|
|
62
|
+
const match = wts.find((wt) => wt.currentStream === streamId);
|
|
63
|
+
return match?.path ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Merge ─────────────────────────────────────────────────────────
|
|
67
|
+
register(REQUEST_METHODS.MERGE, (params: unknown) => {
|
|
68
|
+
const p = params as { stream_id?: string; target_stream_id?: string };
|
|
69
|
+
if (!p?.stream_id) return;
|
|
70
|
+
|
|
71
|
+
const stream = adapter.getStream(p.stream_id);
|
|
72
|
+
const targetStreamId = p.target_stream_id ?? stream?.parentStream;
|
|
73
|
+
if (!targetStreamId) return;
|
|
74
|
+
|
|
75
|
+
const worktreePath = findWorktreeForStream(p.stream_id);
|
|
76
|
+
if (!worktreePath) return;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
adapter.mergeStream({
|
|
80
|
+
sourceStream: p.stream_id,
|
|
81
|
+
targetStream: targetStreamId,
|
|
82
|
+
agentId: 'hub-request',
|
|
83
|
+
worktree: worktreePath,
|
|
84
|
+
});
|
|
85
|
+
} catch {
|
|
86
|
+
// Non-fatal — the resulting event (or conflict) will surface via the bridge
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// ── Abandon ───────────────────────────────────────────────────────
|
|
91
|
+
register(REQUEST_METHODS.ABANDON, (params: unknown) => {
|
|
92
|
+
const p = params as { stream_id?: string; reason?: string };
|
|
93
|
+
if (!p?.stream_id) return;
|
|
94
|
+
try {
|
|
95
|
+
adapter.abandonStream(p.stream_id, { reason: p.reason ?? 'hub-request' });
|
|
96
|
+
} catch { /* non-fatal */ }
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ── Pause ─────────────────────────────────────────────────────────
|
|
100
|
+
register(REQUEST_METHODS.PAUSE, (params: unknown) => {
|
|
101
|
+
const p = params as { stream_id?: string; reason?: string };
|
|
102
|
+
if (!p?.stream_id) return;
|
|
103
|
+
try {
|
|
104
|
+
adapter.pauseStream(p.stream_id, p.reason);
|
|
105
|
+
} catch { /* non-fatal */ }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ── Resume ────────────────────────────────────────────────────────
|
|
109
|
+
register(REQUEST_METHODS.RESUME, (params: unknown) => {
|
|
110
|
+
const p = params as { stream_id?: string };
|
|
111
|
+
if (!p?.stream_id) return;
|
|
112
|
+
try {
|
|
113
|
+
adapter.resumeStream(p.stream_id);
|
|
114
|
+
} catch { /* non-fatal */ }
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ── Resolve conflict ──────────────────────────────────────────────
|
|
118
|
+
register(REQUEST_METHODS.RESOLVE, (params: unknown) => {
|
|
119
|
+
const p = params as {
|
|
120
|
+
stream_id?: string;
|
|
121
|
+
conflict_id?: string;
|
|
122
|
+
strategy?: string;
|
|
123
|
+
};
|
|
124
|
+
if (!p?.stream_id || !p?.conflict_id) return;
|
|
125
|
+
try {
|
|
126
|
+
adapter.resolveConflict({
|
|
127
|
+
conflictId: p.conflict_id,
|
|
128
|
+
resolution: {
|
|
129
|
+
method: (p.strategy as 'ours' | 'theirs') ?? 'ours',
|
|
130
|
+
resolvedBy: 'hub-request',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
} catch { /* non-fatal */ }
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ── Push ───────────────────────────────────────────────────────────
|
|
137
|
+
register(REQUEST_METHODS.PUSH, (params: unknown) => {
|
|
138
|
+
const p = params as {
|
|
139
|
+
stream_id?: string;
|
|
140
|
+
remote?: string;
|
|
141
|
+
target_ref?: string;
|
|
142
|
+
};
|
|
143
|
+
if (!p?.stream_id) return;
|
|
144
|
+
|
|
145
|
+
const worktreePath = findWorktreeForStream(p.stream_id);
|
|
146
|
+
if (!worktreePath) return;
|
|
147
|
+
|
|
148
|
+
const remote = p.remote ?? 'origin';
|
|
149
|
+
const streamBranch = `stream/${p.stream_id}`;
|
|
150
|
+
const targetRef = p.target_ref ?? streamBranch;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const { execSync } = require('child_process');
|
|
154
|
+
execSync(`git push ${remote} ${streamBranch}:refs/heads/${targetRef}`, {
|
|
155
|
+
cwd: worktreePath,
|
|
156
|
+
stdio: 'pipe',
|
|
157
|
+
encoding: 'utf-8',
|
|
158
|
+
});
|
|
159
|
+
// Emit pushed event so the hub records it
|
|
160
|
+
adapter.notifyStreamPushed?.({
|
|
161
|
+
streamId: p.stream_id,
|
|
162
|
+
agentId: 'hub-request',
|
|
163
|
+
pushedCommit: execSync('git rev-parse HEAD', {
|
|
164
|
+
cwd: worktreePath,
|
|
165
|
+
encoding: 'utf-8',
|
|
166
|
+
}).trim(),
|
|
167
|
+
remote,
|
|
168
|
+
remoteRef: targetRef,
|
|
169
|
+
strategy: 'hub-push',
|
|
170
|
+
});
|
|
171
|
+
} catch { /* non-fatal — push failure is reported via absence of pushed event */ }
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ── Commit ────────────────────────────────────────────────────────
|
|
175
|
+
register(REQUEST_METHODS.COMMIT, (params: unknown) => {
|
|
176
|
+
const p = params as {
|
|
177
|
+
stream_id?: string;
|
|
178
|
+
message?: string;
|
|
179
|
+
metadata?: Record<string, unknown>;
|
|
180
|
+
};
|
|
181
|
+
if (!p?.stream_id) return;
|
|
182
|
+
|
|
183
|
+
const worktreePath = findWorktreeForStream(p.stream_id);
|
|
184
|
+
if (!worktreePath) return;
|
|
185
|
+
|
|
186
|
+
const message = p.message ?? 'checkpoint (hub-requested)';
|
|
187
|
+
try {
|
|
188
|
+
adapter.commitChanges({
|
|
189
|
+
streamId: p.stream_id,
|
|
190
|
+
agentId: 'hub-request',
|
|
191
|
+
worktree: worktreePath,
|
|
192
|
+
message,
|
|
193
|
+
metadata: p.metadata,
|
|
194
|
+
});
|
|
195
|
+
} catch { /* non-fatal — nothing to commit, or stream conflicted */ }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// ── Cleanup ───────────────────────────────────────────────────────
|
|
199
|
+
return () => {
|
|
200
|
+
for (const { method, handler } of handlers) {
|
|
201
|
+
connection.offNotification(method, handler);
|
|
202
|
+
}
|
|
203
|
+
handlers.length = 0;
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cascade Bridge — forwards GitCascadeAdapter events to the MAP hub as
|
|
3
|
+
* `x-cascade/*` notifications.
|
|
4
|
+
*
|
|
5
|
+
* macro-agent's GitCascadeAdapter maintains a structured `GitCascadeEvent`
|
|
6
|
+
* stream (translated from git-cascade's native `x-cascade/*` emissions + local
|
|
7
|
+
* adapter events). This bridge subscribes to that stream and translates
|
|
8
|
+
* **back** to `x-cascade/*` MAP method calls so the OpenHive hub receives the
|
|
9
|
+
* canonical event schema.
|
|
10
|
+
*
|
|
11
|
+
* The round-trip (git-cascade → adapter translation → bridge re-translation)
|
|
12
|
+
* is deliberate: it preserves the adapter's internal abstraction (other
|
|
13
|
+
* macro-agent code consumes `GitCascadeEvent`, not raw MAP params) while
|
|
14
|
+
* guaranteeing the hub sees the same schema git-cascade defines. The bridge
|
|
15
|
+
* is the only place that knows about both shapes.
|
|
16
|
+
*
|
|
17
|
+
* Standalone-safe: when `connection.isConnected` is false, events are
|
|
18
|
+
* dropped. macro-agent continues to work without an OpenHive hub.
|
|
19
|
+
*
|
|
20
|
+
* @module map/cascade-bridge
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { CASCADE_METHODS } from 'git-cascade/events';
|
|
24
|
+
|
|
25
|
+
// Fallback method names for events not yet present in the installed
|
|
26
|
+
// git-cascade version (`STREAM_PAUSED`, `STREAM_RESUMED`,
|
|
27
|
+
// `STREAM_ROLLED_BACK` were added after 0.0.7). When the dep is upgraded,
|
|
28
|
+
// collapse these back into `CASCADE_METHODS.*` for a single source of truth.
|
|
29
|
+
const X_CASCADE_STREAM_PAUSED = 'x-cascade/stream.paused' as const;
|
|
30
|
+
const X_CASCADE_STREAM_RESUMED = 'x-cascade/stream.resumed' as const;
|
|
31
|
+
const X_CASCADE_STREAM_ROLLED_BACK = 'x-cascade/stream.rolled_back' as const;
|
|
32
|
+
import type { LifecycleBridgeConnection } from './lifecycle-bridge.js';
|
|
33
|
+
import type {
|
|
34
|
+
GitCascadeAdapter,
|
|
35
|
+
GitCascadeEvent,
|
|
36
|
+
} from '../workspace/git-cascade-adapter.js';
|
|
37
|
+
|
|
38
|
+
export interface CascadeBridgeDisposable {
|
|
39
|
+
/** Unsubscribe from the adapter event stream. Safe to call multiple times. */
|
|
40
|
+
dispose: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CascadeBridgeOptions {
|
|
44
|
+
/** Enable debug logging when events fail to forward. Defaults to false. */
|
|
45
|
+
verbose?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a cascade bridge.
|
|
50
|
+
*
|
|
51
|
+
* Subscribes to `adapter.onEvent()` and forwards a fixed subset of events
|
|
52
|
+
* (the ones that have a canonical `x-cascade/*` method name) as MAP
|
|
53
|
+
* notifications via `connection.callExtension()`. Events with no MAP
|
|
54
|
+
* counterpart (`stream:forked`, `worktree:*`, `task:*`, `mergeQueue:*`, etc.)
|
|
55
|
+
* are silently ignored.
|
|
56
|
+
*
|
|
57
|
+
* @returns A disposable that unsubscribes from the adapter stream.
|
|
58
|
+
*/
|
|
59
|
+
export function createCascadeBridge(
|
|
60
|
+
connection: LifecycleBridgeConnection,
|
|
61
|
+
adapter: GitCascadeAdapter,
|
|
62
|
+
options: CascadeBridgeOptions = {}
|
|
63
|
+
): CascadeBridgeDisposable {
|
|
64
|
+
const verbose = options.verbose ?? false;
|
|
65
|
+
|
|
66
|
+
const unsubscribe = adapter.onEvent((event: GitCascadeEvent) => {
|
|
67
|
+
if (!connection.isConnected) return;
|
|
68
|
+
|
|
69
|
+
const mapped = translate(event);
|
|
70
|
+
if (!mapped) return;
|
|
71
|
+
|
|
72
|
+
// Fire-and-forget: never block the adapter's event loop on a MAP RPC.
|
|
73
|
+
// Errors are swallowed to preserve standalone-safety; they indicate the
|
|
74
|
+
// hub is unreachable or the method isn't registered, neither of which
|
|
75
|
+
// should break local cascade operations.
|
|
76
|
+
void connection
|
|
77
|
+
.callExtension(mapped.method, mapped.params)
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
if (verbose) {
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.warn(
|
|
82
|
+
`[cascade-bridge] failed to forward ${event.type} as ${mapped.method}:`,
|
|
83
|
+
err instanceof Error ? err.message : err
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return { dispose: unsubscribe };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
+
// GitCascadeEvent → x-cascade/* translation
|
|
94
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
interface TranslatedCall {
|
|
97
|
+
method: string;
|
|
98
|
+
params: Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Translate a `GitCascadeEvent` into a MAP method call.
|
|
103
|
+
*
|
|
104
|
+
* Covers the 7 event types that have canonical MAP method names. Returns
|
|
105
|
+
* `null` for events that are macro-agent-internal (worktree/task/mergeQueue
|
|
106
|
+
* lifecycle, local-only forks, etc.).
|
|
107
|
+
*
|
|
108
|
+
* Field names flip from camelCase (macro-agent internal) back to snake_case
|
|
109
|
+
* (MAP wire format). The bridge is intentionally conservative — it only
|
|
110
|
+
* emits fields present on the event, letting the hub back-fill/ignore as
|
|
111
|
+
* needed.
|
|
112
|
+
*/
|
|
113
|
+
function translate(event: GitCascadeEvent): TranslatedCall | null {
|
|
114
|
+
const d = event.data;
|
|
115
|
+
|
|
116
|
+
switch (event.type) {
|
|
117
|
+
case 'stream:created':
|
|
118
|
+
return {
|
|
119
|
+
method: CASCADE_METHODS.STREAM_OPENED,
|
|
120
|
+
params: {
|
|
121
|
+
stream_id: d.streamId,
|
|
122
|
+
name: d.name,
|
|
123
|
+
agent_id: d.agentId,
|
|
124
|
+
base_commit: d.baseCommit,
|
|
125
|
+
parent_stream: d.parentStream,
|
|
126
|
+
branch_name: d.branchName,
|
|
127
|
+
metadata: d.metadata,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
case 'stream:committed':
|
|
132
|
+
return {
|
|
133
|
+
method: CASCADE_METHODS.STREAM_COMMITTED,
|
|
134
|
+
params: {
|
|
135
|
+
stream_id: d.streamId,
|
|
136
|
+
commit_hash: d.commit,
|
|
137
|
+
change_id: d.changeId,
|
|
138
|
+
agent_id: d.agentId,
|
|
139
|
+
message_summary: d.messageSummary,
|
|
140
|
+
files_touched: d.filesTouched,
|
|
141
|
+
parent_commit: d.parentCommit,
|
|
142
|
+
metadata: d.metadata,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
case 'stream:merged':
|
|
147
|
+
return {
|
|
148
|
+
method: CASCADE_METHODS.STREAM_MERGED,
|
|
149
|
+
params: {
|
|
150
|
+
source_stream_id: d.sourceStreamId,
|
|
151
|
+
target_stream_id: d.targetStreamId,
|
|
152
|
+
merge_commit: d.mergeCommit,
|
|
153
|
+
agent_id: d.agentId,
|
|
154
|
+
strategy: d.strategy,
|
|
155
|
+
source_commit: d.sourceCommit,
|
|
156
|
+
metadata: d.metadata,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
case 'stream:conflicted':
|
|
161
|
+
return {
|
|
162
|
+
method: CASCADE_METHODS.STREAM_CONFLICTED,
|
|
163
|
+
params: {
|
|
164
|
+
stream_id: d.streamId,
|
|
165
|
+
conflict_id: d.conflictId,
|
|
166
|
+
conflicted_files: d.conflictedFiles,
|
|
167
|
+
agent_id: d.agentId,
|
|
168
|
+
conflicting_commit: d.conflictingCommit,
|
|
169
|
+
target_commit: d.targetCommit,
|
|
170
|
+
source: d.source,
|
|
171
|
+
metadata: d.metadata,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
case 'conflict:resolved':
|
|
176
|
+
// The adapter emits this from two sources: workspace-manager's local
|
|
177
|
+
// resolveConflict path (carries resolvedBy + resolutionCommit) AND the
|
|
178
|
+
// forwarded git-cascade stream.conflict_resolved (carries the explicit
|
|
179
|
+
// resolution_method). Bridge only forwards events with conflict_id +
|
|
180
|
+
// stream_id present (the cascade-driven shape).
|
|
181
|
+
if (!d.streamId || !d.conflictId) return null;
|
|
182
|
+
return {
|
|
183
|
+
method: CASCADE_METHODS.STREAM_CONFLICT_RESOLVED,
|
|
184
|
+
params: {
|
|
185
|
+
stream_id: d.streamId,
|
|
186
|
+
conflict_id: d.conflictId,
|
|
187
|
+
resolution_method:
|
|
188
|
+
(d.resolutionMethod as string | undefined) ??
|
|
189
|
+
(d.resolvedBy ? 'agent' : 'manual'),
|
|
190
|
+
resolved_by: d.resolvedBy,
|
|
191
|
+
resolution_summary: d.resolutionSummary,
|
|
192
|
+
metadata: d.metadata,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
case 'stream:abandoned':
|
|
197
|
+
return {
|
|
198
|
+
method: CASCADE_METHODS.STREAM_ABANDONED,
|
|
199
|
+
params: {
|
|
200
|
+
stream_id: d.streamId,
|
|
201
|
+
reason: d.reason,
|
|
202
|
+
cascade: d.cascade,
|
|
203
|
+
metadata: d.metadata,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
case 'stream:pushed':
|
|
208
|
+
// Trunk-style push to a remote (direct-push / optimistic-push). Hub
|
|
209
|
+
// sees this as the "merged" equivalent for non-stream targets.
|
|
210
|
+
if (!d.streamId || !d.pushedCommit || !d.remoteRef) return null;
|
|
211
|
+
return {
|
|
212
|
+
method: CASCADE_METHODS.STREAM_PUSHED,
|
|
213
|
+
params: {
|
|
214
|
+
stream_id: d.streamId,
|
|
215
|
+
agent_id: d.agentId,
|
|
216
|
+
pushed_commit: d.pushedCommit,
|
|
217
|
+
remote: d.remote ?? 'origin',
|
|
218
|
+
remote_ref: d.remoteRef,
|
|
219
|
+
strategy: d.strategy,
|
|
220
|
+
metadata: d.metadata,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
case 'cascade:rebased':
|
|
225
|
+
return {
|
|
226
|
+
method: CASCADE_METHODS.CASCADE_REBASED,
|
|
227
|
+
params: {
|
|
228
|
+
stream_id: d.streamId,
|
|
229
|
+
agent_id: d.agentId,
|
|
230
|
+
triggered_by_stream_id: d.triggeredByStreamId,
|
|
231
|
+
triggered_by_agent_id: d.triggeredByAgentId,
|
|
232
|
+
new_base_commit: d.newBaseCommit,
|
|
233
|
+
new_head: d.newHead,
|
|
234
|
+
new_commits: d.newCommits,
|
|
235
|
+
metadata: d.metadata,
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
case 'cascade:completed':
|
|
240
|
+
return {
|
|
241
|
+
method: CASCADE_METHODS.CASCADE_COMPLETED,
|
|
242
|
+
params: {
|
|
243
|
+
root_stream_id: d.rootStreamId,
|
|
244
|
+
agent_id: d.agentId,
|
|
245
|
+
strategy: d.strategy,
|
|
246
|
+
updated_streams: d.updatedStreams,
|
|
247
|
+
failed_streams: d.failedStreams,
|
|
248
|
+
skipped_streams: d.skippedStreams,
|
|
249
|
+
deferred_streams: d.deferredStreams,
|
|
250
|
+
metadata: d.metadata,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
case 'mergeQueue:added':
|
|
255
|
+
if (!d.entryId || !d.streamId) return null;
|
|
256
|
+
return {
|
|
257
|
+
method: CASCADE_METHODS.QUEUE_ADDED,
|
|
258
|
+
params: {
|
|
259
|
+
entry_id: d.entryId,
|
|
260
|
+
stream_id: d.streamId,
|
|
261
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
262
|
+
metadata: d.metadata,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
case 'mergeQueue:ready':
|
|
267
|
+
if (!d.entryId || !d.streamId) return null;
|
|
268
|
+
return {
|
|
269
|
+
method: CASCADE_METHODS.QUEUE_READY,
|
|
270
|
+
params: {
|
|
271
|
+
entry_id: d.entryId,
|
|
272
|
+
stream_id: d.streamId,
|
|
273
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
case 'mergeQueue:cancelled':
|
|
278
|
+
if (!d.entryId || !d.streamId) return null;
|
|
279
|
+
return {
|
|
280
|
+
method: CASCADE_METHODS.QUEUE_CANCELLED,
|
|
281
|
+
params: {
|
|
282
|
+
entry_id: d.entryId,
|
|
283
|
+
stream_id: d.streamId,
|
|
284
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
285
|
+
reason: d.reason,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
case 'mergeQueue:removed':
|
|
290
|
+
if (!d.entryId || !d.streamId) return null;
|
|
291
|
+
return {
|
|
292
|
+
method: CASCADE_METHODS.QUEUE_REMOVED,
|
|
293
|
+
params: {
|
|
294
|
+
entry_id: d.entryId,
|
|
295
|
+
stream_id: d.streamId,
|
|
296
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
297
|
+
outcome: d.outcome,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
case 'stream:paused':
|
|
302
|
+
if (!d.streamId) return null;
|
|
303
|
+
return {
|
|
304
|
+
method: X_CASCADE_STREAM_PAUSED,
|
|
305
|
+
params: {
|
|
306
|
+
stream_id: d.streamId,
|
|
307
|
+
reason: d.reason,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
case 'stream:resumed':
|
|
312
|
+
if (!d.streamId) return null;
|
|
313
|
+
return {
|
|
314
|
+
method: X_CASCADE_STREAM_RESUMED,
|
|
315
|
+
params: {
|
|
316
|
+
stream_id: d.streamId,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
case 'stream:rolled_back':
|
|
321
|
+
if (!d.streamId) return null;
|
|
322
|
+
return {
|
|
323
|
+
method: X_CASCADE_STREAM_ROLLED_BACK,
|
|
324
|
+
params: {
|
|
325
|
+
stream_id: d.streamId,
|
|
326
|
+
strategy: d.strategy,
|
|
327
|
+
target: d.target,
|
|
328
|
+
new_head: d.newHead,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Local-only events with no MAP counterpart.
|
|
333
|
+
// 'stream:updated', 'stream:forked', 'worktree:*', 'task:*',
|
|
334
|
+
// 'change:*', 'conflict:*' (legacy local-only variant — cascade-bridge
|
|
335
|
+
// handles the cascade-driven 'conflict:resolved' separately above)
|
|
336
|
+
default:
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
@@ -75,12 +75,24 @@ export function setupCoordinationHandlers(
|
|
|
75
75
|
if (!p?.title) return;
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
-
//
|
|
78
|
+
// Extract tags and metadata from OpenHive context
|
|
79
|
+
const context = p.context ?? {};
|
|
80
|
+
const tags = Array.isArray(context.tags) ? context.tags as string[] : undefined;
|
|
81
|
+
const metadata: Record<string, unknown> = {
|
|
82
|
+
...context,
|
|
83
|
+
...(p.assigned_by ? { assigned_by: p.assigned_by } : {}),
|
|
84
|
+
...(p.deadline ? { deadline: p.deadline } : {}),
|
|
85
|
+
};
|
|
86
|
+
// Remove tags from metadata (already a top-level field)
|
|
87
|
+
delete metadata.tags;
|
|
88
|
+
|
|
79
89
|
const taskId = await tasksAdapter.createTask({
|
|
80
90
|
title: p.title,
|
|
81
91
|
content: p.description,
|
|
82
92
|
assignee: p.assigned_to,
|
|
93
|
+
tags,
|
|
83
94
|
priority: p.priority === "critical" ? 1 : p.priority === "high" ? 2 : p.priority === "low" ? 4 : 3,
|
|
95
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
84
96
|
});
|
|
85
97
|
|
|
86
98
|
// Optionally spawn an agent to work on the task
|
|
@@ -36,9 +36,27 @@ export function createLifecycleBridge(
|
|
|
36
36
|
agentStore: AgentStore,
|
|
37
37
|
scope: string,
|
|
38
38
|
taskBridge?: TaskBridge,
|
|
39
|
+
getLocalMapId?: (localAgentId: string) => string | undefined,
|
|
39
40
|
): { callback: AgentLifecycleCallback; cleanup: () => Promise<void> } {
|
|
40
41
|
const registered = new Map<string, RegisteredAgent>();
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Poll for the local MAP server's assigned ID for an agent.
|
|
45
|
+
* The local MAP server and the lifecycle bridge both listen to the same
|
|
46
|
+
* lifecycle callback, so they may fire in any order. Poll briefly to handle
|
|
47
|
+
* the race.
|
|
48
|
+
*/
|
|
49
|
+
async function waitForLocalMapId(localAgentId: string, timeoutMs = 500): Promise<string | undefined> {
|
|
50
|
+
if (!getLocalMapId) return undefined;
|
|
51
|
+
const deadline = Date.now() + timeoutMs;
|
|
52
|
+
while (Date.now() < deadline) {
|
|
53
|
+
const id = getLocalMapId(localAgentId);
|
|
54
|
+
if (id) return id;
|
|
55
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
56
|
+
}
|
|
57
|
+
return getLocalMapId(localAgentId);
|
|
58
|
+
}
|
|
59
|
+
|
|
42
60
|
const callback: AgentLifecycleCallback = (event) => {
|
|
43
61
|
if (!connection.isConnected) return;
|
|
44
62
|
|
|
@@ -61,29 +79,46 @@ export function createLifecycleBridge(
|
|
|
61
79
|
}
|
|
62
80
|
|
|
63
81
|
// Register agent with MAP hub (use map/agents/register to preserve
|
|
64
|
-
// per-agent capabilities; map/agents/spawn drops them)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
// per-agent capabilities; map/agents/spawn drops them).
|
|
83
|
+
// Include the local MAP server's ID in metadata so clients can route
|
|
84
|
+
// ACP messages to the correct agent on the macro-agent's own MAP server.
|
|
85
|
+
// Also include provider_session_id so OpenHive can find the underlying
|
|
86
|
+
// Claude Code JSONL transcript on disk for history recovery.
|
|
87
|
+
const agentMetadata = (agent as any).metadata as Record<string, unknown> | undefined;
|
|
88
|
+
const providerSessionId =
|
|
89
|
+
typeof agentMetadata?.provider_session_id === "string"
|
|
90
|
+
? agentMetadata.provider_session_id
|
|
91
|
+
: undefined;
|
|
92
|
+
(async () => {
|
|
93
|
+
const peerMapId = await waitForLocalMapId(agent.id);
|
|
94
|
+
try {
|
|
95
|
+
const result: any = await connection.callExtension("map/agents/register", {
|
|
96
|
+
name,
|
|
97
|
+
role,
|
|
98
|
+
capabilities,
|
|
99
|
+
metadata: {
|
|
100
|
+
// From the hub's perspective these IDs identify this agent on
|
|
101
|
+
// the macro-agent (peer) side. `peerAgentId` is macro-agent's
|
|
102
|
+
// internal store id; `peerMapId` is its local MAP server ULID.
|
|
103
|
+
// Hub callers use these to address the agent in routing
|
|
104
|
+
// (ACP streams target peerMapId, lifecycle ops use peerAgentId).
|
|
105
|
+
peerAgentId: agent.id,
|
|
106
|
+
peerMapId,
|
|
107
|
+
provider_session_id: providerSessionId,
|
|
108
|
+
parent: (agent as any).parent_id ?? undefined,
|
|
109
|
+
team: (agent as any).team ?? undefined,
|
|
110
|
+
cwd: (agent as any).cwd ?? undefined,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
78
113
|
// Track the MAP-assigned agent ID for unregistration
|
|
79
114
|
const mapId = result?.agent?.id ?? result?.id;
|
|
80
115
|
if (mapId) {
|
|
81
116
|
entry.mapId = mapId;
|
|
82
117
|
}
|
|
83
|
-
}
|
|
84
|
-
.catch(() => {
|
|
118
|
+
} catch {
|
|
85
119
|
// Silent — MAP hub may be temporarily unavailable
|
|
86
|
-
}
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
87
122
|
|
|
88
123
|
// Bridge task creation if agent has a task
|
|
89
124
|
if (taskBridge && (agent as any).task_id) {
|