macro-agent 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +128 -1
- package/.sessionlog/settings.json +4 -0
- package/CLAUDE.md +125 -10
- package/README.md +93 -31
- package/dist/boot-v2-DMTHPQ4i.d.ts +2672 -0
- package/dist/cognitive/workspace-handler.d.ts +17 -9
- package/dist/cognitive/workspace-handler.d.ts.map +1 -1
- package/dist/cognitive/workspace-handler.js +10 -11
- package/dist/cognitive/workspace-handler.js.map +1 -1
- package/dist/map/coordination-handler.d.ts +7 -23
- package/dist/map/coordination-handler.d.ts.map +1 -1
- package/dist/map/coordination-handler.js +124 -100
- package/dist/map/coordination-handler.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +7 -15
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/trajectory-reporter.d.ts +4 -9
- package/dist/map/trajectory-reporter.d.ts.map +1 -1
- package/dist/map/trajectory-reporter.js +15 -129
- package/dist/map/trajectory-reporter.js.map +1 -1
- package/dist/map/types.d.ts +37 -0
- package/dist/map/types.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/__tests__/e2e/cognitive-workspace.e2e.test.ts +1 -1
- package/src/cognitive/__tests__/workspace-handler.test.ts +2 -10
- package/src/cognitive/workspace-handler.ts +18 -15
- package/src/map/__tests__/trajectory-reporter.test.ts +2 -254
- package/src/map/coordination-handler.ts +137 -120
- package/src/map/sidecar.ts +7 -20
- package/src/map/trajectory-reporter.ts +16 -154
- package/src/map/types.ts +40 -2
- package/src/__tests__/e2e/trajectory-content.e2e.test.ts +0 -708
- package/src/map/__tests__/coordination-handler.test.ts +0 -598
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Coordination Handler — dispatches inbound coordination messages from the MAP hub.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Context sharing and messaging use agent-inbox (not MAP).
|
|
7
|
-
* Workspace execution uses x-workspace/* notifications.
|
|
4
|
+
* Handles x-openhive/* JSON-RPC notifications for task assignment, status updates,
|
|
5
|
+
* context sharing, messaging, and workspace execution.
|
|
8
6
|
*
|
|
9
7
|
* @module map/coordination-handler
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
|
-
import {
|
|
10
|
+
import type { AgentManager } from "../agent/agent-manager.js";
|
|
13
11
|
import type { InboxAdapter, TasksAdapter } from "../adapters/types.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
payload?: Record<string, unknown>;
|
|
22
|
-
meta?: Record<string, unknown>;
|
|
23
|
-
}
|
|
12
|
+
import type {
|
|
13
|
+
CoordinationTaskAssign,
|
|
14
|
+
CoordinationTaskStatus,
|
|
15
|
+
CoordinationContextShare,
|
|
16
|
+
CoordinationMessage,
|
|
17
|
+
TrajectoryReporter,
|
|
18
|
+
} from "./types.js";
|
|
24
19
|
|
|
25
20
|
export interface CoordinationConnection {
|
|
26
21
|
onNotification(
|
|
@@ -32,15 +27,14 @@ export interface CoordinationConnection {
|
|
|
32
27
|
handler: (params: unknown) => void | Promise<void>,
|
|
33
28
|
): void;
|
|
34
29
|
sendNotification(method: string, params: unknown): Promise<void>;
|
|
35
|
-
onMessage(handler: (message: MAPMessage) => void | Promise<void>): void;
|
|
36
|
-
offMessage(handler: (message: MAPMessage) => void | Promise<void>): void;
|
|
37
30
|
}
|
|
38
31
|
|
|
39
32
|
export interface CoordinationDeps {
|
|
40
33
|
connection: CoordinationConnection;
|
|
41
|
-
|
|
34
|
+
agentManager: AgentManager;
|
|
42
35
|
inboxAdapter: InboxAdapter;
|
|
43
36
|
tasksAdapter: TasksAdapter;
|
|
37
|
+
trajectoryReporter?: TrajectoryReporter;
|
|
44
38
|
/** Workspace handler from cognitive module (if available) */
|
|
45
39
|
workspaceHandler?: {
|
|
46
40
|
handleWorkspaceExecute(params: unknown): Promise<void>;
|
|
@@ -48,138 +42,164 @@ export interface CoordinationDeps {
|
|
|
48
42
|
};
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
/**
|
|
45
|
+
/** Coordination method constants */
|
|
52
46
|
const METHODS = {
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
TASK_ASSIGN: "x-openhive/task.assign",
|
|
48
|
+
TASK_STATUS: "x-openhive/task.status",
|
|
49
|
+
CONTEXT_SHARE: "x-openhive/context.share",
|
|
50
|
+
MESSAGE_SEND: "x-openhive/message.send",
|
|
51
|
+
WORKSPACE_EXECUTE: "x-openhive/learning.workspace.execute",
|
|
55
52
|
} as const;
|
|
56
53
|
|
|
57
54
|
/**
|
|
58
|
-
* Register coordination handlers on the MAP connection.
|
|
59
|
-
*
|
|
60
|
-
* Task operations are handled via MAP scope messages (onMessage).
|
|
61
|
-
* Context sharing and messaging are handled by agent-inbox (not here).
|
|
62
|
-
* Workspace execution uses x-workspace/* notifications.
|
|
55
|
+
* Register coordination notification handlers on the MAP connection.
|
|
63
56
|
* Returns a cleanup function that removes all handlers.
|
|
64
57
|
*/
|
|
65
58
|
export function setupCoordinationHandlers(
|
|
66
59
|
deps: CoordinationDeps,
|
|
67
60
|
): () => void {
|
|
68
|
-
const { connection, inboxAdapter, tasksAdapter } = deps;
|
|
69
|
-
const
|
|
61
|
+
const { connection, agentManager, inboxAdapter, tasksAdapter } = deps;
|
|
62
|
+
const handlers: Array<{ method: string; handler: (params: unknown) => void | Promise<void> }> = [];
|
|
70
63
|
|
|
71
64
|
const register = (
|
|
72
65
|
method: string,
|
|
73
66
|
handler: (params: unknown) => void | Promise<void>,
|
|
74
67
|
): void => {
|
|
75
68
|
connection.onNotification(method, handler);
|
|
76
|
-
|
|
69
|
+
handlers.push({ method, handler });
|
|
77
70
|
};
|
|
78
71
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// { type: "task.assigned", taskId, assignee }
|
|
84
|
-
// { type: "task.status", taskId, previous, current }
|
|
85
|
-
// =========================================================================
|
|
86
|
-
|
|
87
|
-
const messageHandler = async (message: MAPMessage): Promise<void> => {
|
|
88
|
-
const payload = message.payload;
|
|
89
|
-
if (!payload || typeof payload.type !== "string") return;
|
|
90
|
-
|
|
91
|
-
// Skip messages we originated (echo prevention)
|
|
92
|
-
const origin = payload._origin as string | undefined;
|
|
93
|
-
if (origin === "macro-agent") return;
|
|
72
|
+
// --- Task Assignment ---
|
|
73
|
+
register(METHODS.TASK_ASSIGN, async (params: unknown) => {
|
|
74
|
+
const p = params as CoordinationTaskAssign;
|
|
75
|
+
if (!p?.title) return;
|
|
94
76
|
|
|
95
77
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
78
|
+
// Create task in opentasks
|
|
79
|
+
const taskId = await tasksAdapter.createTask({
|
|
80
|
+
title: p.title,
|
|
81
|
+
content: p.description,
|
|
82
|
+
assignee: p.assigned_to,
|
|
83
|
+
priority: p.priority === "critical" ? 1 : p.priority === "high" ? 2 : p.priority === "low" ? 4 : 3,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Optionally spawn an agent to work on the task
|
|
87
|
+
if (p.assigned_to) {
|
|
88
|
+
try {
|
|
89
|
+
await inboxAdapter.send("system", p.assigned_to, {
|
|
90
|
+
type: "event",
|
|
91
|
+
event: "TASK_ASSIGNED",
|
|
92
|
+
data: { taskId, title: p.title, description: p.description },
|
|
107
93
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
await inboxAdapter
|
|
111
|
-
.send("system", task.assignee, {
|
|
112
|
-
type: "event",
|
|
113
|
-
event: "TASK_ASSIGNED",
|
|
114
|
-
data: { taskId, title: task.title },
|
|
115
|
-
})
|
|
116
|
-
.catch(() => {});
|
|
117
|
-
}
|
|
118
|
-
break;
|
|
94
|
+
} catch {
|
|
95
|
+
// Agent may not exist locally — that's fine
|
|
119
96
|
}
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.warn(
|
|
100
|
+
`[map-sidecar] Failed to handle task.assign: ${(err as Error).message}`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
120
104
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
await tasksAdapter.assignTask(taskId, assignee);
|
|
127
|
-
|
|
128
|
-
await inboxAdapter
|
|
129
|
-
.send("system", assignee, {
|
|
130
|
-
type: "event",
|
|
131
|
-
event: "TASK_ASSIGNED",
|
|
132
|
-
data: { taskId },
|
|
133
|
-
})
|
|
134
|
-
.catch(() => {});
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
105
|
+
// --- Task Status ---
|
|
106
|
+
register(METHODS.TASK_STATUS, async (params: unknown) => {
|
|
107
|
+
const p = params as CoordinationTaskStatus;
|
|
108
|
+
if (!p?.task_id || !p?.status) return;
|
|
137
109
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
110
|
+
try {
|
|
111
|
+
const actionMap: Record<string, string> = {
|
|
112
|
+
in_progress: "start",
|
|
113
|
+
completed: "complete",
|
|
114
|
+
closed: "complete",
|
|
115
|
+
failed: "fail",
|
|
116
|
+
blocked: "block",
|
|
117
|
+
open: "reopen",
|
|
118
|
+
};
|
|
119
|
+
const action = actionMap[p.status];
|
|
120
|
+
if (action) {
|
|
121
|
+
await tasksAdapter.transitionTask(p.task_id, action as any);
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.warn(
|
|
125
|
+
`[map-sidecar] Failed to handle task.status: ${(err as Error).message}`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
157
129
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
130
|
+
// --- Context Share ---
|
|
131
|
+
register(METHODS.CONTEXT_SHARE, async (params: unknown) => {
|
|
132
|
+
const p = params as CoordinationContextShare;
|
|
133
|
+
if (!p?.context_type || !p?.data) return;
|
|
161
134
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
135
|
+
try {
|
|
136
|
+
// Deliver context to all running agents via inbox
|
|
137
|
+
const agents = agentManager.list()
|
|
138
|
+
.filter((a: any) => a.state === "running");
|
|
139
|
+
for (const agent of agents) {
|
|
140
|
+
await inboxAdapter
|
|
141
|
+
.send("system", agent.id, {
|
|
142
|
+
type: "event",
|
|
143
|
+
event: "CONTEXT_SHARED",
|
|
144
|
+
data: {
|
|
145
|
+
context_type: p.context_type,
|
|
146
|
+
data: p.data,
|
|
147
|
+
source: p.source_swarm_id,
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
.catch(() => {});
|
|
165
151
|
}
|
|
166
152
|
} catch (err) {
|
|
167
153
|
console.warn(
|
|
168
|
-
`[map-sidecar] Failed to handle
|
|
154
|
+
`[map-sidecar] Failed to handle context.share: ${(err as Error).message}`,
|
|
169
155
|
);
|
|
170
156
|
}
|
|
171
|
-
};
|
|
157
|
+
});
|
|
172
158
|
|
|
173
|
-
|
|
159
|
+
// --- Message Send ---
|
|
160
|
+
register(METHODS.MESSAGE_SEND, async (params: unknown) => {
|
|
161
|
+
const p = params as CoordinationMessage;
|
|
162
|
+
if (!p?.content) return;
|
|
174
163
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
164
|
+
try {
|
|
165
|
+
const agents = agentManager.list()
|
|
166
|
+
.filter((a: any) => a.state === "running");
|
|
167
|
+
if (agents.length === 0) return;
|
|
168
|
+
|
|
169
|
+
// Route to the best target:
|
|
170
|
+
// 1. If to_swarm_id matches a local agent ID, send directly
|
|
171
|
+
// 2. If metadata has a target_agent hint, use it
|
|
172
|
+
// 3. Otherwise, send to the coordinator/head manager (parentless agent)
|
|
173
|
+
// 4. Fallback: first running agent
|
|
174
|
+
const targetId = p.to_swarm_id;
|
|
175
|
+
const directTarget = targetId
|
|
176
|
+
? agents.find((a: any) => a.id === targetId)
|
|
177
|
+
: undefined;
|
|
178
|
+
const coordinator = agents.find((a: any) => !a.parent);
|
|
179
|
+
const target = directTarget ?? coordinator ?? agents[0];
|
|
180
|
+
|
|
181
|
+
await inboxAdapter.send("system", target.id, {
|
|
182
|
+
type: "event",
|
|
183
|
+
event: "EXTERNAL_MESSAGE",
|
|
184
|
+
data: {
|
|
185
|
+
from: p.from_swarm_id,
|
|
186
|
+
content_type: p.content_type,
|
|
187
|
+
content: p.content,
|
|
188
|
+
reply_to: p.reply_to,
|
|
189
|
+
metadata: p.metadata,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
} catch (err) {
|
|
193
|
+
console.warn(
|
|
194
|
+
`[map-sidecar] Failed to handle message.send: ${(err as Error).message}`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
178
198
|
|
|
179
199
|
// --- Workspace Execute (delegate to cognitive module) ---
|
|
180
200
|
if (deps.workspaceHandler) {
|
|
181
201
|
const wh = deps.workspaceHandler;
|
|
182
|
-
|
|
202
|
+
register(METHODS.WORKSPACE_EXECUTE, async (params: unknown) => {
|
|
183
203
|
try {
|
|
184
204
|
await wh.handleWorkspaceExecute(params);
|
|
185
205
|
} catch (err) {
|
|
@@ -187,17 +207,14 @@ export function setupCoordinationHandlers(
|
|
|
187
207
|
`[map-sidecar] Failed to handle workspace.execute: ${(err as Error).message}`,
|
|
188
208
|
);
|
|
189
209
|
}
|
|
190
|
-
};
|
|
191
|
-
register(METHODS.WORKSPACE_EXECUTE, workspaceHandler);
|
|
192
|
-
register(METHODS.WORKSPACE_EXECUTE_LEGACY, workspaceHandler);
|
|
210
|
+
});
|
|
193
211
|
}
|
|
194
212
|
|
|
195
213
|
// Return cleanup function
|
|
196
214
|
return () => {
|
|
197
|
-
|
|
198
|
-
for (const { method, handler } of notificationHandlers) {
|
|
215
|
+
for (const { method, handler } of handlers) {
|
|
199
216
|
connection.offNotification(method, handler);
|
|
200
217
|
}
|
|
201
|
-
|
|
218
|
+
handlers.length = 0;
|
|
202
219
|
};
|
|
203
220
|
}
|
package/src/map/sidecar.ts
CHANGED
|
@@ -107,6 +107,10 @@ export function createMAPSidecar(
|
|
|
107
107
|
role: "sidecar",
|
|
108
108
|
scopes: [scope],
|
|
109
109
|
capabilities: {
|
|
110
|
+
messaging: { canSend: true, canReceive: true },
|
|
111
|
+
mail: { canCreate: true, canJoin: true, canViewHistory: true },
|
|
112
|
+
protocols: ['acp'],
|
|
113
|
+
acp: { version: '2024-10-07' },
|
|
110
114
|
trajectory: { canReport: true, canServeContent: false },
|
|
111
115
|
tasks: {
|
|
112
116
|
canCreate: true,
|
|
@@ -253,31 +257,14 @@ export function createMAPSidecar(
|
|
|
253
257
|
const { createTrajectoryReporter } = await import("./trajectory-reporter.js");
|
|
254
258
|
trajectoryReporter = createTrajectoryReporter(connection, config);
|
|
255
259
|
|
|
256
|
-
// 4.
|
|
257
|
-
const { MacroAgentBackend } = await import("../cognitive/macro-agent-backend.js");
|
|
258
|
-
const { handleWorkspaceExecute, isWorkspaceExecuteMessage } = await import("../cognitive/workspace-handler.js");
|
|
259
|
-
const workspaceBackend = new MacroAgentBackend(agentManager, {});
|
|
260
|
-
const sendToHub = (msg: { method?: string; params?: unknown }) => {
|
|
261
|
-
if (msg.method && msg.params) {
|
|
262
|
-
connection.sendNotification(msg.method, msg.params as Record<string, unknown>);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
const workspaceHandler = {
|
|
266
|
-
handleWorkspaceExecute: (params: unknown) =>
|
|
267
|
-
handleWorkspaceExecute(
|
|
268
|
-
{ backend: workspaceBackend, sendToHub },
|
|
269
|
-
params as import("agent-workspace").WorkspaceExecuteParams,
|
|
270
|
-
),
|
|
271
|
-
isWorkspaceExecuteMessage,
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// 5. Coordination Handler
|
|
260
|
+
// 4. Coordination Handler
|
|
275
261
|
const { setupCoordinationHandlers } = await import("./coordination-handler.js");
|
|
276
262
|
coordinationCleanup = setupCoordinationHandlers({
|
|
277
263
|
connection,
|
|
264
|
+
agentManager,
|
|
278
265
|
inboxAdapter,
|
|
279
266
|
tasksAdapter,
|
|
280
|
-
|
|
267
|
+
trajectoryReporter,
|
|
281
268
|
});
|
|
282
269
|
}
|
|
283
270
|
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* Trajectory Reporter — builds and reports trajectory checkpoints to the MAP hub.
|
|
3
3
|
*
|
|
4
4
|
* Sends checkpoints via the `trajectory/checkpoint` JSON-RPC extension.
|
|
5
|
-
*
|
|
6
|
-
* session
|
|
7
|
-
* Supports all agent types (Claude Code, Codex, Gemini, etc.) through
|
|
8
|
-
* sessionlog's adapter system.
|
|
5
|
+
* Also handles inbound `trajectory/content.request` notifications by serving
|
|
6
|
+
* session transcript data.
|
|
9
7
|
*
|
|
10
8
|
* @module map/trajectory-reporter
|
|
11
9
|
*/
|
|
@@ -36,173 +34,37 @@ export interface TrajectoryConnection {
|
|
|
36
34
|
get isConnected(): boolean;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
/**
|
|
40
|
-
* Resolve transcript content for a checkpoint ID using sessionlog.
|
|
41
|
-
*
|
|
42
|
-
* Two-source strategy (matching cc-swarm):
|
|
43
|
-
* 1. Live session — use sessionlog's SessionStore to find a session
|
|
44
|
-
* matching the checkpoint ID, then read its transcript from disk.
|
|
45
|
-
* 2. Committed checkpoint — use sessionlog's CheckpointStore to read
|
|
46
|
-
* content from the git history.
|
|
47
|
-
*
|
|
48
|
-
* Returns null if no content is found or sessionlog is unavailable.
|
|
49
|
-
*/
|
|
50
|
-
async function resolveContent(
|
|
51
|
-
checkpointId: string,
|
|
52
|
-
sessionDirs: string[],
|
|
53
|
-
): Promise<{
|
|
54
|
-
transcript: string;
|
|
55
|
-
metadata: Record<string, unknown>;
|
|
56
|
-
prompts: string;
|
|
57
|
-
context: string;
|
|
58
|
-
} | null> {
|
|
59
|
-
let sessionlog: typeof import("sessionlog") | null = null;
|
|
60
|
-
try {
|
|
61
|
-
sessionlog = await import("sessionlog");
|
|
62
|
-
} catch {
|
|
63
|
-
return null; // sessionlog not available
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const { readFileSync, existsSync } = await import("node:fs");
|
|
67
|
-
|
|
68
|
-
// Derive the session ID from checkpoint ID (e.g., "sess-abc-step3" → "sess-abc")
|
|
69
|
-
const sessionId = checkpointId.replace(/-step\d+$/, "");
|
|
70
|
-
|
|
71
|
-
// ── 1. Live session lookup via SessionStore ────────────────────────────
|
|
72
|
-
for (const sessionsDir of sessionDirs) {
|
|
73
|
-
if (!existsSync(sessionsDir)) continue;
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const store = sessionlog.createSessionStore(undefined, sessionsDir);
|
|
77
|
-
// Try loading by derived session ID first, then by raw checkpoint ID
|
|
78
|
-
let state = await store.load(sessionId);
|
|
79
|
-
if (!state) state = await store.load(checkpointId);
|
|
80
|
-
|
|
81
|
-
// If not found by ID, scan all sessions for checkpoint match
|
|
82
|
-
if (!state) {
|
|
83
|
-
const allSessions = await store.list();
|
|
84
|
-
state = allSessions.find((s) =>
|
|
85
|
-
s.lastCheckpointID === checkpointId ||
|
|
86
|
-
(s.turnCheckpointIDs || []).includes(checkpointId),
|
|
87
|
-
) ?? null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!state?.transcriptPath || !existsSync(state.transcriptPath)) continue;
|
|
91
|
-
|
|
92
|
-
const transcript = readFileSync(state.transcriptPath, "utf-8");
|
|
93
|
-
|
|
94
|
-
// Use sessionlog's prompt extraction if the agent has a TranscriptAnalyzer,
|
|
95
|
-
// otherwise fall back to the prompts stored in state
|
|
96
|
-
let prompts = "";
|
|
97
|
-
if (state.firstPrompt) {
|
|
98
|
-
// Collect from promptAttributions if available, otherwise use firstPrompt
|
|
99
|
-
const attrs = state.promptAttributions;
|
|
100
|
-
if (attrs && attrs.length > 0) {
|
|
101
|
-
prompts = attrs.map((a) => a.prompt).join("\n---\n");
|
|
102
|
-
} else {
|
|
103
|
-
prompts = state.firstPrompt;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
transcript,
|
|
109
|
-
prompts,
|
|
110
|
-
metadata: {
|
|
111
|
-
sessionID: state.sessionID,
|
|
112
|
-
phase: state.phase,
|
|
113
|
-
agentType: state.agentType,
|
|
114
|
-
stepCount: state.stepCount || 0,
|
|
115
|
-
filesTouched: state.filesTouched || [],
|
|
116
|
-
tokenUsage: state.tokenUsage || {},
|
|
117
|
-
startedAt: state.startedAt,
|
|
118
|
-
endedAt: state.endedAt,
|
|
119
|
-
source: "live",
|
|
120
|
-
},
|
|
121
|
-
context: `Session ${state.sessionID} (${state.phase})`,
|
|
122
|
-
};
|
|
123
|
-
} catch {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ── 2. Committed checkpoint via CheckpointStore ────────────────────────
|
|
129
|
-
try {
|
|
130
|
-
if (sessionlog.createCheckpointStore) {
|
|
131
|
-
const store = sessionlog.createCheckpointStore();
|
|
132
|
-
const content = await store.readSessionContent(checkpointId, 0);
|
|
133
|
-
if (content) {
|
|
134
|
-
return {
|
|
135
|
-
transcript: content.transcript,
|
|
136
|
-
prompts: content.prompts,
|
|
137
|
-
metadata: { ...content.metadata, source: "committed" },
|
|
138
|
-
context: content.context,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch {
|
|
143
|
-
// Checkpoint not found or store unavailable
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
37
|
/**
|
|
150
38
|
* Create a trajectory reporter that sends checkpoints to the MAP hub
|
|
151
|
-
* and serves
|
|
39
|
+
* and serves content on demand.
|
|
152
40
|
*/
|
|
153
41
|
export function createTrajectoryReporter(
|
|
154
42
|
connection: TrajectoryConnection,
|
|
155
|
-
config: Pick<MAPSidecarConfig, "trajectorySyncLevel"
|
|
156
|
-
/** Additional session directories to search for transcripts */
|
|
157
|
-
sessionDirs?: string[];
|
|
158
|
-
},
|
|
43
|
+
config: Pick<MAPSidecarConfig, "trajectorySyncLevel">,
|
|
159
44
|
): TrajectoryReporter {
|
|
160
45
|
// Cache the resource_id from the first checkpoint response
|
|
161
46
|
// so subsequent calls reuse it (avoids creating duplicate session resources)
|
|
162
47
|
let cachedResourceId: string | undefined;
|
|
163
48
|
|
|
164
|
-
// Build session directory search list
|
|
165
|
-
const defaultDirs: string[] = [];
|
|
166
|
-
try {
|
|
167
|
-
const cwd = process.cwd();
|
|
168
|
-
defaultDirs.push(
|
|
169
|
-
`${cwd}/.git/sessionlog-sessions`,
|
|
170
|
-
`${cwd}/.swarm/sessionlog/sessions`,
|
|
171
|
-
);
|
|
172
|
-
} catch {
|
|
173
|
-
// Can't resolve paths — will use config dirs only
|
|
174
|
-
}
|
|
175
|
-
const sessionDirs = [...(config.sessionDirs ?? []), ...defaultDirs];
|
|
176
|
-
|
|
177
49
|
// Handler for inbound content requests
|
|
178
50
|
const contentHandler = async (params: unknown): Promise<void> => {
|
|
179
51
|
const req = params as TrajectoryContentRequest;
|
|
180
52
|
if (!req?.request_id) return;
|
|
181
53
|
|
|
182
54
|
try {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
context: content.context,
|
|
195
|
-
});
|
|
196
|
-
} else {
|
|
197
|
-
await connection.sendNotification("trajectory/content.response", {
|
|
198
|
-
request_id: req.request_id,
|
|
199
|
-
transcript: "",
|
|
200
|
-
metadata: { source: "macro-agent" },
|
|
201
|
-
prompts: "",
|
|
202
|
-
context: "",
|
|
203
|
-
});
|
|
204
|
-
}
|
|
55
|
+
// Respond with what we have — macro-agent doesn't store full transcripts
|
|
56
|
+
// like sessionlog does, so we send a minimal response.
|
|
57
|
+
// Future: integrate with ACP session history for richer content.
|
|
58
|
+
await connection.sendNotification("trajectory/content.response", {
|
|
59
|
+
request_id: req.request_id,
|
|
60
|
+
transcript: null,
|
|
61
|
+
metadata: {
|
|
62
|
+
source: "macro-agent",
|
|
63
|
+
note: "Full transcript serving not yet implemented",
|
|
64
|
+
},
|
|
65
|
+
});
|
|
205
66
|
} catch {
|
|
67
|
+
// Best effort
|
|
206
68
|
try {
|
|
207
69
|
await connection.sendNotification("trajectory/content.response", {
|
|
208
70
|
request_id: req.request_id,
|
package/src/map/types.ts
CHANGED
|
@@ -145,8 +145,46 @@ export interface TrajectoryContentRequest {
|
|
|
145
145
|
// Coordination Wire Format
|
|
146
146
|
// =============================================================================
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
/** Inbound task assignment from hub */
|
|
149
|
+
export interface CoordinationTaskAssign {
|
|
150
|
+
title: string;
|
|
151
|
+
description?: string;
|
|
152
|
+
assigned_to?: string;
|
|
153
|
+
assigned_by: string;
|
|
154
|
+
priority?: string;
|
|
155
|
+
context?: Record<string, unknown>;
|
|
156
|
+
deadline?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Inbound task status update from hub */
|
|
160
|
+
export interface CoordinationTaskStatus {
|
|
161
|
+
task_id: string;
|
|
162
|
+
status: string;
|
|
163
|
+
progress?: number;
|
|
164
|
+
result?: unknown;
|
|
165
|
+
error?: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Inbound context share from hub */
|
|
169
|
+
export interface CoordinationContextShare {
|
|
170
|
+
hive_id?: string;
|
|
171
|
+
source_swarm_id: string;
|
|
172
|
+
context_type: string;
|
|
173
|
+
data: unknown;
|
|
174
|
+
target_swarm_ids?: string[];
|
|
175
|
+
ttl_seconds?: number;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Inbound message from hub */
|
|
179
|
+
export interface CoordinationMessage {
|
|
180
|
+
hive_id?: string;
|
|
181
|
+
from_swarm_id: string;
|
|
182
|
+
to_swarm_id: string;
|
|
183
|
+
content_type: string;
|
|
184
|
+
content: unknown;
|
|
185
|
+
reply_to?: string;
|
|
186
|
+
metadata?: Record<string, unknown>;
|
|
187
|
+
}
|
|
150
188
|
|
|
151
189
|
// =============================================================================
|
|
152
190
|
// Internal Bridge Types
|