macro-agent 0.1.8 → 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 +166 -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 +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 -43
- 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 +16 -1
- 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 -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 +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 -2
- 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 +66 -0
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +293 -48
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +68 -1
- 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/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 +86 -10
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-bridge.ts +301 -0
- package/src/map/lifecycle-bridge.ts +52 -17
- package/src/map/server.ts +47 -6
- package/src/map/sidecar.ts +31 -1
- 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
|
@@ -0,0 +1,301 @@
|
|
|
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
|
+
import type { LifecycleBridgeConnection } from './lifecycle-bridge.js';
|
|
25
|
+
import type {
|
|
26
|
+
GitCascadeAdapter,
|
|
27
|
+
GitCascadeEvent,
|
|
28
|
+
} from '../workspace/git-cascade-adapter.js';
|
|
29
|
+
|
|
30
|
+
export interface CascadeBridgeDisposable {
|
|
31
|
+
/** Unsubscribe from the adapter event stream. Safe to call multiple times. */
|
|
32
|
+
dispose: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CascadeBridgeOptions {
|
|
36
|
+
/** Enable debug logging when events fail to forward. Defaults to false. */
|
|
37
|
+
verbose?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a cascade bridge.
|
|
42
|
+
*
|
|
43
|
+
* Subscribes to `adapter.onEvent()` and forwards a fixed subset of events
|
|
44
|
+
* (the ones that have a canonical `x-cascade/*` method name) as MAP
|
|
45
|
+
* notifications via `connection.callExtension()`. Events with no MAP
|
|
46
|
+
* counterpart (`stream:forked`, `worktree:*`, `task:*`, `mergeQueue:*`, etc.)
|
|
47
|
+
* are silently ignored.
|
|
48
|
+
*
|
|
49
|
+
* @returns A disposable that unsubscribes from the adapter stream.
|
|
50
|
+
*/
|
|
51
|
+
export function createCascadeBridge(
|
|
52
|
+
connection: LifecycleBridgeConnection,
|
|
53
|
+
adapter: GitCascadeAdapter,
|
|
54
|
+
options: CascadeBridgeOptions = {}
|
|
55
|
+
): CascadeBridgeDisposable {
|
|
56
|
+
const verbose = options.verbose ?? false;
|
|
57
|
+
|
|
58
|
+
const unsubscribe = adapter.onEvent((event: GitCascadeEvent) => {
|
|
59
|
+
if (!connection.isConnected) return;
|
|
60
|
+
|
|
61
|
+
const mapped = translate(event);
|
|
62
|
+
if (!mapped) return;
|
|
63
|
+
|
|
64
|
+
// Fire-and-forget: never block the adapter's event loop on a MAP RPC.
|
|
65
|
+
// Errors are swallowed to preserve standalone-safety; they indicate the
|
|
66
|
+
// hub is unreachable or the method isn't registered, neither of which
|
|
67
|
+
// should break local cascade operations.
|
|
68
|
+
void connection
|
|
69
|
+
.callExtension(mapped.method, mapped.params)
|
|
70
|
+
.catch((err) => {
|
|
71
|
+
if (verbose) {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.warn(
|
|
74
|
+
`[cascade-bridge] failed to forward ${event.type} as ${mapped.method}:`,
|
|
75
|
+
err instanceof Error ? err.message : err
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return { dispose: unsubscribe };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
// GitCascadeEvent → x-cascade/* translation
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
interface TranslatedCall {
|
|
89
|
+
method: string;
|
|
90
|
+
params: Record<string, unknown>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Translate a `GitCascadeEvent` into a MAP method call.
|
|
95
|
+
*
|
|
96
|
+
* Covers the 7 event types that have canonical MAP method names. Returns
|
|
97
|
+
* `null` for events that are macro-agent-internal (worktree/task/mergeQueue
|
|
98
|
+
* lifecycle, local-only forks, etc.).
|
|
99
|
+
*
|
|
100
|
+
* Field names flip from camelCase (macro-agent internal) back to snake_case
|
|
101
|
+
* (MAP wire format). The bridge is intentionally conservative — it only
|
|
102
|
+
* emits fields present on the event, letting the hub back-fill/ignore as
|
|
103
|
+
* needed.
|
|
104
|
+
*/
|
|
105
|
+
function translate(event: GitCascadeEvent): TranslatedCall | null {
|
|
106
|
+
const d = event.data;
|
|
107
|
+
|
|
108
|
+
switch (event.type) {
|
|
109
|
+
case 'stream:created':
|
|
110
|
+
return {
|
|
111
|
+
method: CASCADE_METHODS.STREAM_OPENED,
|
|
112
|
+
params: {
|
|
113
|
+
stream_id: d.streamId,
|
|
114
|
+
name: d.name,
|
|
115
|
+
agent_id: d.agentId,
|
|
116
|
+
base_commit: d.baseCommit,
|
|
117
|
+
parent_stream: d.parentStream,
|
|
118
|
+
branch_name: d.branchName,
|
|
119
|
+
metadata: d.metadata,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
case 'stream:committed':
|
|
124
|
+
return {
|
|
125
|
+
method: CASCADE_METHODS.STREAM_COMMITTED,
|
|
126
|
+
params: {
|
|
127
|
+
stream_id: d.streamId,
|
|
128
|
+
commit_hash: d.commit,
|
|
129
|
+
change_id: d.changeId,
|
|
130
|
+
agent_id: d.agentId,
|
|
131
|
+
message_summary: d.messageSummary,
|
|
132
|
+
files_touched: d.filesTouched,
|
|
133
|
+
parent_commit: d.parentCommit,
|
|
134
|
+
metadata: d.metadata,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
case 'stream:merged':
|
|
139
|
+
return {
|
|
140
|
+
method: CASCADE_METHODS.STREAM_MERGED,
|
|
141
|
+
params: {
|
|
142
|
+
source_stream_id: d.sourceStreamId,
|
|
143
|
+
target_stream_id: d.targetStreamId,
|
|
144
|
+
merge_commit: d.mergeCommit,
|
|
145
|
+
agent_id: d.agentId,
|
|
146
|
+
strategy: d.strategy,
|
|
147
|
+
source_commit: d.sourceCommit,
|
|
148
|
+
metadata: d.metadata,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
case 'stream:conflicted':
|
|
153
|
+
return {
|
|
154
|
+
method: CASCADE_METHODS.STREAM_CONFLICTED,
|
|
155
|
+
params: {
|
|
156
|
+
stream_id: d.streamId,
|
|
157
|
+
conflict_id: d.conflictId,
|
|
158
|
+
conflicted_files: d.conflictedFiles,
|
|
159
|
+
agent_id: d.agentId,
|
|
160
|
+
conflicting_commit: d.conflictingCommit,
|
|
161
|
+
target_commit: d.targetCommit,
|
|
162
|
+
source: d.source,
|
|
163
|
+
metadata: d.metadata,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
case 'conflict:resolved':
|
|
168
|
+
// The adapter emits this from two sources: workspace-manager's local
|
|
169
|
+
// resolveConflict path (carries resolvedBy + resolutionCommit) AND the
|
|
170
|
+
// forwarded git-cascade stream.conflict_resolved (carries the explicit
|
|
171
|
+
// resolution_method). Bridge only forwards events with conflict_id +
|
|
172
|
+
// stream_id present (the cascade-driven shape).
|
|
173
|
+
if (!d.streamId || !d.conflictId) return null;
|
|
174
|
+
return {
|
|
175
|
+
method: CASCADE_METHODS.STREAM_CONFLICT_RESOLVED,
|
|
176
|
+
params: {
|
|
177
|
+
stream_id: d.streamId,
|
|
178
|
+
conflict_id: d.conflictId,
|
|
179
|
+
resolution_method:
|
|
180
|
+
(d.resolutionMethod as string | undefined) ??
|
|
181
|
+
(d.resolvedBy ? 'agent' : 'manual'),
|
|
182
|
+
resolved_by: d.resolvedBy,
|
|
183
|
+
resolution_summary: d.resolutionSummary,
|
|
184
|
+
metadata: d.metadata,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
case 'stream:abandoned':
|
|
189
|
+
return {
|
|
190
|
+
method: CASCADE_METHODS.STREAM_ABANDONED,
|
|
191
|
+
params: {
|
|
192
|
+
stream_id: d.streamId,
|
|
193
|
+
reason: d.reason,
|
|
194
|
+
cascade: d.cascade,
|
|
195
|
+
metadata: d.metadata,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
case 'stream:pushed':
|
|
200
|
+
// Trunk-style push to a remote (direct-push / optimistic-push). Hub
|
|
201
|
+
// sees this as the "merged" equivalent for non-stream targets.
|
|
202
|
+
if (!d.streamId || !d.pushedCommit || !d.remoteRef) return null;
|
|
203
|
+
return {
|
|
204
|
+
method: CASCADE_METHODS.STREAM_PUSHED,
|
|
205
|
+
params: {
|
|
206
|
+
stream_id: d.streamId,
|
|
207
|
+
agent_id: d.agentId,
|
|
208
|
+
pushed_commit: d.pushedCommit,
|
|
209
|
+
remote: d.remote ?? 'origin',
|
|
210
|
+
remote_ref: d.remoteRef,
|
|
211
|
+
strategy: d.strategy,
|
|
212
|
+
metadata: d.metadata,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
case 'cascade:rebased':
|
|
217
|
+
return {
|
|
218
|
+
method: CASCADE_METHODS.CASCADE_REBASED,
|
|
219
|
+
params: {
|
|
220
|
+
stream_id: d.streamId,
|
|
221
|
+
agent_id: d.agentId,
|
|
222
|
+
triggered_by_stream_id: d.triggeredByStreamId,
|
|
223
|
+
triggered_by_agent_id: d.triggeredByAgentId,
|
|
224
|
+
new_base_commit: d.newBaseCommit,
|
|
225
|
+
new_head: d.newHead,
|
|
226
|
+
new_commits: d.newCommits,
|
|
227
|
+
metadata: d.metadata,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
case 'cascade:completed':
|
|
232
|
+
return {
|
|
233
|
+
method: CASCADE_METHODS.CASCADE_COMPLETED,
|
|
234
|
+
params: {
|
|
235
|
+
root_stream_id: d.rootStreamId,
|
|
236
|
+
agent_id: d.agentId,
|
|
237
|
+
strategy: d.strategy,
|
|
238
|
+
updated_streams: d.updatedStreams,
|
|
239
|
+
failed_streams: d.failedStreams,
|
|
240
|
+
skipped_streams: d.skippedStreams,
|
|
241
|
+
deferred_streams: d.deferredStreams,
|
|
242
|
+
metadata: d.metadata,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
case 'mergeQueue:added':
|
|
247
|
+
if (!d.entryId || !d.streamId) return null;
|
|
248
|
+
return {
|
|
249
|
+
method: CASCADE_METHODS.QUEUE_ADDED,
|
|
250
|
+
params: {
|
|
251
|
+
entry_id: d.entryId,
|
|
252
|
+
stream_id: d.streamId,
|
|
253
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
254
|
+
metadata: d.metadata,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
case 'mergeQueue:ready':
|
|
259
|
+
if (!d.entryId || !d.streamId) return null;
|
|
260
|
+
return {
|
|
261
|
+
method: CASCADE_METHODS.QUEUE_READY,
|
|
262
|
+
params: {
|
|
263
|
+
entry_id: d.entryId,
|
|
264
|
+
stream_id: d.streamId,
|
|
265
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
case 'mergeQueue:cancelled':
|
|
270
|
+
if (!d.entryId || !d.streamId) return null;
|
|
271
|
+
return {
|
|
272
|
+
method: CASCADE_METHODS.QUEUE_CANCELLED,
|
|
273
|
+
params: {
|
|
274
|
+
entry_id: d.entryId,
|
|
275
|
+
stream_id: d.streamId,
|
|
276
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
277
|
+
reason: d.reason,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
case 'mergeQueue:removed':
|
|
282
|
+
if (!d.entryId || !d.streamId) return null;
|
|
283
|
+
return {
|
|
284
|
+
method: CASCADE_METHODS.QUEUE_REMOVED,
|
|
285
|
+
params: {
|
|
286
|
+
entry_id: d.entryId,
|
|
287
|
+
stream_id: d.streamId,
|
|
288
|
+
target_branch: (d.targetBranch as string | undefined) ?? 'main',
|
|
289
|
+
outcome: d.outcome,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Local-only events with no MAP counterpart (Phase 1 scope).
|
|
294
|
+
// 'stream:updated', 'stream:forked', 'stream:paused', 'stream:resumed',
|
|
295
|
+
// 'worktree:*', 'task:*', 'change:*', 'conflict:*' (legacy local-only
|
|
296
|
+
// variant — cascade-bridge handles the cascade-driven 'conflict:resolved'
|
|
297
|
+
// separately above)
|
|
298
|
+
default:
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -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) {
|
package/src/map/server.ts
CHANGED
|
@@ -107,7 +107,7 @@ export function createMAPServerInstance(
|
|
|
107
107
|
role: params.role ?? "worker",
|
|
108
108
|
state: "idle",
|
|
109
109
|
sessionId: ctx?.session?.id,
|
|
110
|
-
metadata: {
|
|
110
|
+
metadata: { peerAgentId: spawned.id, task: params.task },
|
|
111
111
|
});
|
|
112
112
|
if (registered?.id) {
|
|
113
113
|
mapIdToLocalId.set(registered.id, spawned.id);
|
|
@@ -145,6 +145,38 @@ export function createMAPServerInstance(
|
|
|
145
145
|
return { agent: { id: spawned.id } };
|
|
146
146
|
};
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Terminate a running agent. Accepts either the agent's local ID or the
|
|
150
|
+
* MAP-assigned ULID (we resolve back to local via mapIdToLocalId).
|
|
151
|
+
* Reason defaults to "stopped"; use "cancelled" for user-initiated stops.
|
|
152
|
+
*/
|
|
153
|
+
handlers["_macro/terminateAgent"] = async (params) => {
|
|
154
|
+
const agentIdParam = params.agentId as string | undefined;
|
|
155
|
+
const reason = (params.reason as string) ?? "cancelled";
|
|
156
|
+
if (!agentIdParam) {
|
|
157
|
+
return { success: false, error: "agentId is required" };
|
|
158
|
+
}
|
|
159
|
+
// Resolve either a MAP ULID or a local agent ID to our internal ID.
|
|
160
|
+
const localId = mapIdToLocalId.get(agentIdParam) ?? agentIdParam;
|
|
161
|
+
try {
|
|
162
|
+
await agentManager.terminate(localId as any, reason as any);
|
|
163
|
+
return { success: true };
|
|
164
|
+
} catch (err) {
|
|
165
|
+
return { success: false, error: (err as Error).message };
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Inspect ACP stream → peer agent bindings on this MAP server.
|
|
171
|
+
* Each stream carries the peer agent id (macro-agent's internal store id)
|
|
172
|
+
* it was opened against, set by the bridge from MAP routing. Useful for
|
|
173
|
+
* routing tests and debugging multi-coordinator scenarios.
|
|
174
|
+
*/
|
|
175
|
+
handlers["_macro/getAcpStreamBindings"] = async () => {
|
|
176
|
+
if (!acpBridge) return { bindings: [] };
|
|
177
|
+
return { bindings: acpBridge.getStreamBindings() };
|
|
178
|
+
};
|
|
179
|
+
|
|
148
180
|
// ── Task extensions ───────────────────────────────────────────
|
|
149
181
|
handlers["_macro/task/list"] = async () => {
|
|
150
182
|
if (!tasksAdapter.connected) return { tasks: [] };
|
|
@@ -404,16 +436,21 @@ export function createMAPServerInstance(
|
|
|
404
436
|
try {
|
|
405
437
|
if (event.type === "spawned" || event.type === "started") {
|
|
406
438
|
const agent = event.agent;
|
|
407
|
-
// Register agent
|
|
408
|
-
//
|
|
409
|
-
//
|
|
439
|
+
// Register agent ONCE. spawn() fires "spawned" immediately followed
|
|
440
|
+
// by "started", so without this guard the listener re-registers
|
|
441
|
+
// on the second event — generating a fresh MAP ULID and overwriting
|
|
442
|
+
// localIdToMapId. Consumers racing against that overwrite (like the
|
|
443
|
+
// sidecar's lifecycle bridge, which snapshots peerMapId into hub
|
|
444
|
+
// metadata) end up disagreeing with _macro/spawnAgent's return
|
|
445
|
+
// value on which ULID refers to this agent.
|
|
446
|
+
if (localIdToMapId.has(agent.id)) return;
|
|
410
447
|
try {
|
|
411
448
|
const registered = mapServer.agents.register({
|
|
412
449
|
name: agent.name ?? agent.id,
|
|
413
450
|
role: agent.role ?? "worker",
|
|
414
451
|
state: "idle",
|
|
415
452
|
metadata: {
|
|
416
|
-
|
|
453
|
+
peerAgentId: agent.id, // macro-agent's internal store id
|
|
417
454
|
parent: (agent as any).parent ?? null,
|
|
418
455
|
task: (agent as any).task ?? null,
|
|
419
456
|
cwd: (agent as any).cwd ?? null,
|
|
@@ -454,7 +491,7 @@ export function createMAPServerInstance(
|
|
|
454
491
|
role: agent.role ?? "worker",
|
|
455
492
|
state: agent.state === "running" ? "busy" : "idle",
|
|
456
493
|
metadata: {
|
|
457
|
-
|
|
494
|
+
peerAgentId: agent.id,
|
|
458
495
|
parent: agent.parent ?? null,
|
|
459
496
|
task: agent.task ?? null,
|
|
460
497
|
},
|
|
@@ -535,5 +572,9 @@ export function createMAPServerInstance(
|
|
|
535
572
|
getConnectionCount(): number {
|
|
536
573
|
return connectionCount;
|
|
537
574
|
},
|
|
575
|
+
|
|
576
|
+
getLocalMapId(localAgentId: string): string | undefined {
|
|
577
|
+
return localIdToMapId.get(localAgentId);
|
|
578
|
+
},
|
|
538
579
|
};
|
|
539
580
|
}
|
package/src/map/sidecar.ts
CHANGED
|
@@ -35,7 +35,7 @@ export function createMAPSidecar(
|
|
|
35
35
|
deps: MAPSidecarDeps,
|
|
36
36
|
config: MAPSidecarConfig,
|
|
37
37
|
): MAPSidecar {
|
|
38
|
-
const { agentManager, agentStore, inboxAdapter, tasksAdapter } = deps;
|
|
38
|
+
const { agentManager, agentStore, inboxAdapter, tasksAdapter, getLocalMapId, gitCascadeAdapter } = deps;
|
|
39
39
|
const scope = config.scope ?? "swarm:macro-agent";
|
|
40
40
|
const agentName = config.agentName ?? "macro-agent-sidecar";
|
|
41
41
|
|
|
@@ -50,6 +50,7 @@ export function createMAPSidecar(
|
|
|
50
50
|
let trajectoryReporter: TrajectoryReporter | null = null;
|
|
51
51
|
let taskBridge: TaskBridge | null = null;
|
|
52
52
|
let coordinationCleanup: (() => void) | null = null;
|
|
53
|
+
let cascadeBridgeCleanup: (() => void) | null = null;
|
|
53
54
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
54
55
|
|
|
55
56
|
/**
|
|
@@ -82,6 +83,10 @@ export function createMAPSidecar(
|
|
|
82
83
|
coordinationCleanup();
|
|
83
84
|
coordinationCleanup = null;
|
|
84
85
|
}
|
|
86
|
+
if (cascadeBridgeCleanup) {
|
|
87
|
+
try { cascadeBridgeCleanup(); } catch { /* non-critical */ }
|
|
88
|
+
cascadeBridgeCleanup = null;
|
|
89
|
+
}
|
|
85
90
|
if (trajectoryReporter) {
|
|
86
91
|
trajectoryReporter.stop();
|
|
87
92
|
trajectoryReporter = null;
|
|
@@ -126,6 +131,10 @@ export function createMAPSidecar(
|
|
|
126
131
|
metadata: {
|
|
127
132
|
systemId: config.systemId ?? "macro-agent",
|
|
128
133
|
type: "macro-agent-sidecar",
|
|
134
|
+
// Signals that this swarm can spawn ACP-capable coordinators on demand,
|
|
135
|
+
// even before any coordinator has registered. The hub's /sessions/create-acp
|
|
136
|
+
// endpoint handles the spawn via _macro/spawnAgent when no ACP agent exists.
|
|
137
|
+
canHostAcp: true,
|
|
129
138
|
},
|
|
130
139
|
reconnection: {
|
|
131
140
|
enabled: config.reconnection?.enabled ?? true,
|
|
@@ -175,6 +184,19 @@ export function createMAPSidecar(
|
|
|
175
184
|
isConnected = true;
|
|
176
185
|
} // end if (!isConnected)
|
|
177
186
|
|
|
187
|
+
// Publish sidecar metadata to the hub. The MAP SDK's connect()/register()
|
|
188
|
+
// does not propagate the `metadata` field from connect options — it only
|
|
189
|
+
// forwards name/role/capabilities/scopes. Call updateMetadata explicitly
|
|
190
|
+
// so the hub sees canHostAcp (and any other metadata the UI relies on).
|
|
191
|
+
try {
|
|
192
|
+
const metadata = (connectOpts.metadata as Record<string, unknown>) ?? {};
|
|
193
|
+
if (typeof connection.updateMetadata === "function") {
|
|
194
|
+
await connection.updateMetadata(metadata);
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// Non-fatal — metadata is advisory
|
|
198
|
+
}
|
|
199
|
+
|
|
178
200
|
// Monitor connection state
|
|
179
201
|
connection.onStateChange(
|
|
180
202
|
(newState: string, _oldState: string) => {
|
|
@@ -252,6 +274,7 @@ export function createMAPSidecar(
|
|
|
252
274
|
agentStore,
|
|
253
275
|
scope,
|
|
254
276
|
taskBridge,
|
|
277
|
+
getLocalMapId,
|
|
255
278
|
);
|
|
256
279
|
lifecycleCallback = bridge.callback;
|
|
257
280
|
lifecycleCleanup = bridge.cleanup;
|
|
@@ -270,6 +293,13 @@ export function createMAPSidecar(
|
|
|
270
293
|
tasksAdapter,
|
|
271
294
|
trajectoryReporter,
|
|
272
295
|
});
|
|
296
|
+
|
|
297
|
+
// 5. Cascade Bridge (optional — only when a GitCascadeAdapter is available)
|
|
298
|
+
if (gitCascadeAdapter) {
|
|
299
|
+
const { createCascadeBridge } = await import("./cascade-bridge.js");
|
|
300
|
+
const cascadeBridge = createCascadeBridge(connection, gitCascadeAdapter);
|
|
301
|
+
cascadeBridgeCleanup = cascadeBridge.dispose;
|
|
302
|
+
}
|
|
273
303
|
}
|
|
274
304
|
|
|
275
305
|
return {
|
package/src/map/types.ts
CHANGED
|
@@ -68,6 +68,21 @@ export interface MAPSidecarDeps {
|
|
|
68
68
|
agentStore: AgentStore;
|
|
69
69
|
inboxAdapter: InboxAdapter;
|
|
70
70
|
tasksAdapter: TasksAdapter;
|
|
71
|
+
/**
|
|
72
|
+
* Optional lookup for the local MAP server's ULID for a given local agent ID.
|
|
73
|
+
* When provided, the lifecycle bridge includes this ID in hub registration
|
|
74
|
+
* metadata so clients (e.g., SwarmCraft) can target the agent correctly on
|
|
75
|
+
* the macro-agent's own MAP server.
|
|
76
|
+
*/
|
|
77
|
+
getLocalMapId?: (localAgentId: string) => string | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Optional GitCascadeAdapter. When provided, the sidecar wires a cascade
|
|
80
|
+
* bridge that forwards the adapter's event stream to the hub as
|
|
81
|
+
* `x-cascade/*` MAP notifications. Leave undefined to disable cascade
|
|
82
|
+
* event forwarding (macro-agent will still use cascade internally, just
|
|
83
|
+
* without hub observability).
|
|
84
|
+
*/
|
|
85
|
+
gitCascadeAdapter?: import("../workspace/git-cascade-adapter.js").GitCascadeAdapter;
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
// =============================================================================
|
|
@@ -244,4 +259,9 @@ export interface MAPServerInstance {
|
|
|
244
259
|
getUrl(): string;
|
|
245
260
|
/** Get number of active connections */
|
|
246
261
|
getConnectionCount(): number;
|
|
262
|
+
/**
|
|
263
|
+
* Resolve a local agent ID (macro-agent internal) to its MAP server-assigned ULID.
|
|
264
|
+
* Returns undefined if the agent is not registered on the MAP server yet.
|
|
265
|
+
*/
|
|
266
|
+
getLocalMapId(localAgentId: string): string | undefined;
|
|
247
267
|
}
|
package/src/mcp/tools/done-v2.ts
CHANGED
|
@@ -106,6 +106,15 @@ function buildLifecycleContext(
|
|
|
106
106
|
streamId: record?.workspace_stream_id ?? process.env.MACRO_STREAM_ID,
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
// Pull task_ref out of agent metadata if it was stashed there at spawn
|
|
110
|
+
// time. Validates the shape — bad data is silently dropped rather than
|
|
111
|
+
// pushed downstream into cascade payloads.
|
|
112
|
+
const meta = record?.metadata as Record<string, unknown> | undefined;
|
|
113
|
+
const tr = meta?.task_ref as { resource_id?: unknown; node_id?: unknown } | undefined;
|
|
114
|
+
if (tr && typeof tr.resource_id === "string" && typeof tr.node_id === "string") {
|
|
115
|
+
ctx.taskRef = { resource_id: tr.resource_id, node_id: tr.node_id };
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
// Resolve capabilities for dispatch
|
|
110
119
|
if (roleRegistry) {
|
|
111
120
|
try {
|
|
@@ -142,6 +142,43 @@ export class TeamManagerV2 {
|
|
|
142
142
|
basePath ?? process.cwd()
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
+
// V3: auto-wire TopologyPolicy when the team declares
|
|
146
|
+
// `macro_agent.workspace`. Requires a WorkspaceManager to be present.
|
|
147
|
+
if (workspaceManager) {
|
|
148
|
+
try {
|
|
149
|
+
const { extractWorkspaceConfig } = await import(
|
|
150
|
+
"../workspace/yaml-schema.js"
|
|
151
|
+
);
|
|
152
|
+
const workspaceConfig = extractWorkspaceConfig(
|
|
153
|
+
manifest as unknown as { macro_agent?: Record<string, unknown> }
|
|
154
|
+
);
|
|
155
|
+
if (workspaceConfig) {
|
|
156
|
+
const { YamlDrivenTopology } = await import(
|
|
157
|
+
"../workspace/topology/yaml-driven.js"
|
|
158
|
+
);
|
|
159
|
+
const policy = new YamlDrivenTopology(workspaceConfig);
|
|
160
|
+
agentManager.setTopologyPolicy(policy);
|
|
161
|
+
|
|
162
|
+
// Kick the topology's onTeamStart so team-root streams get
|
|
163
|
+
// created before any agents spawn.
|
|
164
|
+
await policy.onTeamStart({
|
|
165
|
+
teamName: name,
|
|
166
|
+
teamInstanceId: `${name}-${this.instanceCounter + 1}`,
|
|
167
|
+
workspaceConfig,
|
|
168
|
+
workspaceManager,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
// Non-fatal: topology wiring is a progressive enhancement. Log and
|
|
173
|
+
// fall through to legacy capability-based dispatch.
|
|
174
|
+
console.warn(
|
|
175
|
+
`[TeamManagerV2] topology auto-wire skipped for team "${name}": ${
|
|
176
|
+
err instanceof Error ? err.message : String(err)
|
|
177
|
+
}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
145
182
|
// Create runtime
|
|
146
183
|
const runtimeServices: TeamServicesV2 = {
|
|
147
184
|
agentManager,
|