@agentclientprotocol/claude-agent-acp 0.95.1-preview.1 → 0.95.1-preview.3
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/dist/acp-agent.d.ts +5 -0
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +70 -18
- package/dist/native-subagents.d.ts +71 -6
- package/dist/native-subagents.d.ts.map +1 -1
- package/dist/native-subagents.js +141 -44
- package/dist/session-model.d.ts +23 -0
- package/dist/session-model.d.ts.map +1 -1
- package/dist/session-model.js +43 -0
- package/dist/subagent-replay.d.ts +42 -8
- package/dist/subagent-replay.d.ts.map +1 -1
- package/dist/subagent-replay.js +101 -22
- package/package.json +1 -1
package/dist/subagent-replay.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { HANDBACK_TOOL, handbackMessage, isNativeSubagentControlTool, launchReply, linkedSessionMessages, nativeSubagentState, rfdEndState, rfdLabels, } from "./native-subagents.js";
|
|
1
|
+
import { childModelOption, HANDBACK_TOOL, handbackMessage, isNativeSubagentControlTool, launchReply, linkedSessionMessages, nativeSubagentState, responseModelEntry, rfdEndState, rfdLabels, } from "./native-subagents.js";
|
|
2
2
|
import { noticeUpdate } from "./session-notices.js";
|
|
3
3
|
export class RfdSubagentReplay {
|
|
4
4
|
history;
|
|
5
5
|
publish;
|
|
6
|
+
models;
|
|
6
7
|
/** The children by the id of the tool use that launched each. */
|
|
7
8
|
children = new Map();
|
|
8
9
|
byAgentId = new Map();
|
|
@@ -13,9 +14,10 @@ export class RfdSubagentReplay {
|
|
|
13
14
|
withoutHistory = 0;
|
|
14
15
|
/** The SubagentHandback calls, with their report and whether their result failed. */
|
|
15
16
|
handbacks = new Map();
|
|
16
|
-
constructor(history, publish) {
|
|
17
|
+
constructor(history, publish, models) {
|
|
17
18
|
this.history = history;
|
|
18
19
|
this.publish = publish;
|
|
20
|
+
this.models = models;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Registers the launches, the launch results, and the SendMessage calls in
|
|
@@ -41,7 +43,8 @@ export class RfdSubagentReplay {
|
|
|
41
43
|
to: input.to,
|
|
42
44
|
message: input.message,
|
|
43
45
|
senderSessionId: ownerSessionId,
|
|
44
|
-
|
|
46
|
+
shownAsToolCall: false,
|
|
47
|
+
outgoingSent: false,
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -63,8 +66,12 @@ export class RfdSubagentReplay {
|
|
|
63
66
|
childOfLaunch(toolUseId) {
|
|
64
67
|
return this.children.get(toolUseId);
|
|
65
68
|
}
|
|
66
|
-
/**
|
|
67
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Announces `child` on its parent, as live: running, with the parent's
|
|
71
|
+
* labels. Then it sends `firstModel`, the model of the child's first
|
|
72
|
+
* response, when the transcript has one.
|
|
73
|
+
*/
|
|
74
|
+
async announce(child, firstModel) {
|
|
68
75
|
if (child.announced)
|
|
69
76
|
return;
|
|
70
77
|
const labels = rfdLabels(child.record.name ?? child.input.name, child.record.agentType ?? child.input.subagent_type, child.record.description ?? child.input.description);
|
|
@@ -79,6 +86,30 @@ export class RfdSubagentReplay {
|
|
|
79
86
|
},
|
|
80
87
|
});
|
|
81
88
|
child.announced = true;
|
|
89
|
+
await this.responseModel(child, firstModel);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Takes the model of a replayed response of the child `owner`. A child
|
|
93
|
+
* whose model changes gets its `model` option again.
|
|
94
|
+
*/
|
|
95
|
+
async responseModel(owner, model) {
|
|
96
|
+
const entry = responseModelEntry(model, this.models);
|
|
97
|
+
if (!entry || !owner.announced)
|
|
98
|
+
return;
|
|
99
|
+
owner.responseModel = model;
|
|
100
|
+
await this.sendModel(owner, entry);
|
|
101
|
+
}
|
|
102
|
+
async sendModel(child, model) {
|
|
103
|
+
if (child.model?.value === model.value && child.model.name === model.name)
|
|
104
|
+
return;
|
|
105
|
+
child.model = model;
|
|
106
|
+
await this.publish({
|
|
107
|
+
sessionId: child.sessionId,
|
|
108
|
+
update: {
|
|
109
|
+
sessionUpdate: "config_option_update",
|
|
110
|
+
configOptions: [childModelOption(model)],
|
|
111
|
+
},
|
|
112
|
+
});
|
|
82
113
|
}
|
|
83
114
|
/**
|
|
84
115
|
* The rest of a child's user message to replay, or `null` when nothing is
|
|
@@ -119,15 +150,30 @@ export class RfdSubagentReplay {
|
|
|
119
150
|
/**
|
|
120
151
|
* Takes a replayed update of a tool call, from the transcript of `owner`
|
|
121
152
|
* when it is a child's. Returns whether the client is not shown it: an
|
|
122
|
-
* update of the launch of a child that the replay shows,
|
|
153
|
+
* update of the launch of a child that the replay shows, of a SendMessage
|
|
154
|
+
* call whose message the replay already delivered, or of a successful
|
|
123
155
|
* SubagentHandback call of `owner`. A foreground launch's completed result
|
|
124
|
-
* goes out as the child's reply,
|
|
125
|
-
* its parent.
|
|
156
|
+
* goes out as the child's reply, a delivered SendMessage as the sender's
|
|
157
|
+
* outgoing message, and a hand-back as the child's message to its parent.
|
|
126
158
|
*/
|
|
127
159
|
async toolCallUpdate(update, owner) {
|
|
128
160
|
if (update.sessionUpdate !== "tool_call" && update.sessionUpdate !== "tool_call_update") {
|
|
129
161
|
return false;
|
|
130
162
|
}
|
|
163
|
+
const sendMessage = this.sendMessages.find((call) => call.toolUseId === update.toolCallId);
|
|
164
|
+
if (sendMessage) {
|
|
165
|
+
if (sendMessage.recipientSessionId === undefined) {
|
|
166
|
+
sendMessage.shownAsToolCall = true;
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (!sendMessage.outgoingSent) {
|
|
170
|
+
sendMessage.outgoingSent = true;
|
|
171
|
+
const [outgoing] = linkedSessionMessages(sendMessage.toolUseId, sendMessage.senderSessionId, sendMessage.recipientSessionId, sendMessage.message);
|
|
172
|
+
if (outgoing)
|
|
173
|
+
await this.publish(outgoing);
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
131
177
|
const handback = owner ? this.handbacks.get(update.toolCallId) : undefined;
|
|
132
178
|
if (owner && handback && !handback.failed) {
|
|
133
179
|
if (!handback.sent) {
|
|
@@ -205,21 +251,37 @@ export class RfdSubagentReplay {
|
|
|
205
251
|
* can be cancelled, as live. Any other child cannot be running, because the
|
|
206
252
|
* process that ran it is gone, so its replayed end is its current state.
|
|
207
253
|
*/
|
|
208
|
-
currentStates(live) {
|
|
254
|
+
currentStates(live, liveConfiguration) {
|
|
209
255
|
return [...this.children.values()]
|
|
210
256
|
.filter((child) => child.announced)
|
|
211
|
-
.
|
|
257
|
+
.flatMap((child) => {
|
|
212
258
|
const liveState = live(child.sessionId);
|
|
213
259
|
const state = liveState === undefined || liveState === "idle" ? endState(child) : { state: liveState };
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
260
|
+
// The session's model list may be known only now, after the load response.
|
|
261
|
+
const entry = responseModelEntry(child.responseModel, this.models);
|
|
262
|
+
const replayed = child.model ? JSON.stringify([childModelOption(child.model)]) : undefined;
|
|
263
|
+
const current = liveConfiguration?.(child.sessionId) ?? (entry ? [childModelOption(entry)] : undefined);
|
|
264
|
+
// A configuration that the replay already sent does not go again.
|
|
265
|
+
const configOptions = current && JSON.stringify(current) !== replayed ? current : undefined;
|
|
266
|
+
return [
|
|
267
|
+
{
|
|
268
|
+
sessionId: child.parentSessionId,
|
|
269
|
+
update: {
|
|
270
|
+
sessionUpdate: "subagent_update",
|
|
271
|
+
sessionId: child.sessionId,
|
|
272
|
+
...(liveState !== undefined ? { capabilities: { cancel: {} } } : {}),
|
|
273
|
+
state,
|
|
274
|
+
},
|
|
221
275
|
},
|
|
222
|
-
|
|
276
|
+
...(configOptions
|
|
277
|
+
? [
|
|
278
|
+
{
|
|
279
|
+
sessionId: child.sessionId,
|
|
280
|
+
update: { sessionUpdate: "config_option_update", configOptions },
|
|
281
|
+
},
|
|
282
|
+
]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
223
285
|
});
|
|
224
286
|
}
|
|
225
287
|
registerLaunch(toolUseId, input, parentSessionId) {
|
|
@@ -256,21 +318,26 @@ export class RfdSubagentReplay {
|
|
|
256
318
|
* first undelivered SendMessage to the child whose message the delivered
|
|
257
319
|
* text contains. With that call, the message has the call's id and text and
|
|
258
320
|
* its sender, as live; without it, its transcript id and the delivered text.
|
|
321
|
+
* Unless the replay already showed the call as a tool call, the message
|
|
322
|
+
* replaces it, so this view names the sender's view, which goes out at the
|
|
323
|
+
* call (see {@link toolCallUpdate}).
|
|
259
324
|
*/
|
|
260
325
|
async sendDelivery(child, message, text) {
|
|
261
326
|
const names = [child.sessionId, child.record.name, child.input.name];
|
|
262
|
-
const call = this.sendMessages.find((candidate) =>
|
|
327
|
+
const call = this.sendMessages.find((candidate) => candidate.recipientSessionId === undefined &&
|
|
263
328
|
names.includes(candidate.to) &&
|
|
264
329
|
text.includes(candidate.message.trim()));
|
|
265
330
|
if (call) {
|
|
266
|
-
call.
|
|
267
|
-
await this.sendMessage(child, call.toolUseId, call.message, call.senderSessionId
|
|
331
|
+
call.recipientSessionId = child.sessionId;
|
|
332
|
+
await this.sendMessage(child, call.toolUseId, call.message, call.senderSessionId, {
|
|
333
|
+
linked: !call.shownAsToolCall,
|
|
334
|
+
});
|
|
268
335
|
}
|
|
269
336
|
else {
|
|
270
337
|
await this.sendMessage(child, message.uuid, text, undefined);
|
|
271
338
|
}
|
|
272
339
|
}
|
|
273
|
-
async sendMessage(child, messageId, text, sender) {
|
|
340
|
+
async sendMessage(child, messageId, text, sender, { linked = false } = {}) {
|
|
274
341
|
await this.publish({
|
|
275
342
|
sessionId: child.sessionId,
|
|
276
343
|
update: {
|
|
@@ -278,11 +345,23 @@ export class RfdSubagentReplay {
|
|
|
278
345
|
messageId,
|
|
279
346
|
...(sender ? { senderSessionId: sender } : {}),
|
|
280
347
|
recipientSessionId: child.sessionId,
|
|
348
|
+
...(linked ? { senderMessageId: messageId } : {}),
|
|
281
349
|
content: [{ type: "text", text }],
|
|
282
350
|
},
|
|
283
351
|
});
|
|
284
352
|
}
|
|
285
353
|
}
|
|
354
|
+
/** The model of the first response in `messages`, a subagent's transcript. */
|
|
355
|
+
export function firstResponseModel(messages) {
|
|
356
|
+
for (const message of messages) {
|
|
357
|
+
if (message.type !== "assistant")
|
|
358
|
+
continue;
|
|
359
|
+
const model = message.message?.model;
|
|
360
|
+
if (typeof model === "string" && model && model !== "<synthetic>")
|
|
361
|
+
return model;
|
|
362
|
+
}
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
286
365
|
/**
|
|
287
366
|
* The end of a foreground launch from its tool result: an error result that
|
|
288
367
|
* names a stop is a cancellation, any other error a failure.
|
package/package.json
CHANGED