linkshell-cli 0.2.96 → 0.2.98
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/cli/src/runtime/acp/agent-workspace.js +10 -5
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js +44 -6
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/runtime/acp/agent-workspace.ts +10 -5
- package/src/runtime/acp/claude-stream-json-client.ts +44 -6
|
@@ -114,8 +114,10 @@ export class ClaudeStreamJsonClient {
|
|
|
114
114
|
if (this.claudeSessionId) {
|
|
115
115
|
args.push("--resume", this.claudeSessionId);
|
|
116
116
|
}
|
|
117
|
-
//
|
|
118
|
-
|
|
117
|
+
// Allow up to 10 turns so Claude can execute tools and continue responding.
|
|
118
|
+
// With bypassPermissions, tools auto-execute — a generous limit prevents
|
|
119
|
+
// infinite loops while still allowing complex multi-step workflows.
|
|
120
|
+
args.push("--max-turns", "10");
|
|
119
121
|
|
|
120
122
|
if (input.model) {
|
|
121
123
|
args.push("--model", input.model);
|
|
@@ -180,6 +182,10 @@ export class ClaudeStreamJsonClient {
|
|
|
180
182
|
const rl = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
181
183
|
let currentToolId: string | undefined;
|
|
182
184
|
let currentToolName: string | undefined;
|
|
185
|
+
let currentMessageId: string | undefined;
|
|
186
|
+
// Map tool_use_id → tool_name so tool_result can look up the correct name
|
|
187
|
+
// even when multiple tools are in flight
|
|
188
|
+
const toolNames = new Map<string, string>();
|
|
183
189
|
|
|
184
190
|
rl.on("line", (line: string) => {
|
|
185
191
|
if (this.pendingCancel) {
|
|
@@ -225,11 +231,15 @@ export class ClaudeStreamJsonClient {
|
|
|
225
231
|
const message = event.message;
|
|
226
232
|
if (!message) break;
|
|
227
233
|
const content = (message.content ?? []) as ClaudeContentBlock[];
|
|
234
|
+
// Reset per-message tracking — each assistant message starts fresh
|
|
235
|
+
currentMessageId = undefined;
|
|
228
236
|
|
|
229
237
|
for (const block of content) {
|
|
230
238
|
switch (block.type) {
|
|
231
239
|
case "thinking":
|
|
232
|
-
|
|
240
|
+
// Use item/completed since thinking blocks arrive complete (not streaming deltas)
|
|
241
|
+
// item/started would leave isStreaming=true forever with no matching item/completed
|
|
242
|
+
this.input.onNotification("item/completed", {
|
|
233
243
|
sessionId: this.claudeSessionId,
|
|
234
244
|
item: {
|
|
235
245
|
id: event.uuid ?? id("thinking"),
|
|
@@ -241,9 +251,10 @@ export class ClaudeStreamJsonClient {
|
|
|
241
251
|
break;
|
|
242
252
|
|
|
243
253
|
case "text":
|
|
254
|
+
currentMessageId = (typeof message.id === "string" ? message.id : undefined) ?? event.uuid ?? id("msg");
|
|
244
255
|
this.input.onNotification("item/agentMessage/delta", {
|
|
245
256
|
sessionId: this.claudeSessionId,
|
|
246
|
-
itemId:
|
|
257
|
+
itemId: currentMessageId,
|
|
247
258
|
delta: block.text,
|
|
248
259
|
});
|
|
249
260
|
break;
|
|
@@ -251,6 +262,7 @@ export class ClaudeStreamJsonClient {
|
|
|
251
262
|
case "tool_use": {
|
|
252
263
|
currentToolId = block.id;
|
|
253
264
|
currentToolName = block.name ?? "tool";
|
|
265
|
+
if (block.id) toolNames.set(block.id, block.name ?? "tool");
|
|
254
266
|
const toolName = block.name ?? "tool";
|
|
255
267
|
this.input.onNotification("item/started", {
|
|
256
268
|
sessionId: this.claudeSessionId,
|
|
@@ -273,6 +285,20 @@ export class ClaudeStreamJsonClient {
|
|
|
273
285
|
break;
|
|
274
286
|
}
|
|
275
287
|
}
|
|
288
|
+
|
|
289
|
+
// Mark this assistant message as complete — the full message has arrived.
|
|
290
|
+
// Each text block was streamed via item/agentMessage/delta; now we signal
|
|
291
|
+
// that streaming is done so the UI stops showing "正在生成".
|
|
292
|
+
if (currentMessageId) {
|
|
293
|
+
this.input.onNotification("item/completed", {
|
|
294
|
+
sessionId: this.claudeSessionId,
|
|
295
|
+
item: {
|
|
296
|
+
id: currentMessageId,
|
|
297
|
+
type: "agentMessage",
|
|
298
|
+
status: "completed",
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
276
302
|
break;
|
|
277
303
|
}
|
|
278
304
|
|
|
@@ -285,14 +311,15 @@ export class ClaudeStreamJsonClient {
|
|
|
285
311
|
for (const block of content) {
|
|
286
312
|
if (block.type === "tool_result") {
|
|
287
313
|
const toolId = block.tool_use_id ?? currentToolId;
|
|
314
|
+
const toolName = (toolId ? toolNames.get(toolId) : undefined) ?? currentToolName;
|
|
288
315
|
const isError = block.is_error === true;
|
|
289
316
|
this.input.onNotification("item/completed", {
|
|
290
317
|
sessionId: this.claudeSessionId,
|
|
291
318
|
item: {
|
|
292
319
|
id: toolId ?? id("tool"),
|
|
293
320
|
type: "toolCall",
|
|
294
|
-
toolName
|
|
295
|
-
tool:
|
|
321
|
+
toolName,
|
|
322
|
+
tool: toolName,
|
|
296
323
|
status: isError ? "failed" : "completed",
|
|
297
324
|
output: typeof block.content === "string" ? block.content : JSON.stringify(block.content),
|
|
298
325
|
aggregatedOutput: typeof block.content === "string" ? block.content : undefined,
|
|
@@ -305,6 +332,17 @@ export class ClaudeStreamJsonClient {
|
|
|
305
332
|
}
|
|
306
333
|
|
|
307
334
|
case "result": {
|
|
335
|
+
// Mark the last agent message as complete so isStreaming flips to false
|
|
336
|
+
if (currentMessageId) {
|
|
337
|
+
this.input.onNotification("item/completed", {
|
|
338
|
+
sessionId: this.claudeSessionId,
|
|
339
|
+
item: {
|
|
340
|
+
id: currentMessageId,
|
|
341
|
+
type: "agentMessage",
|
|
342
|
+
status: "completed",
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
}
|
|
308
346
|
// Turn complete
|
|
309
347
|
const isError = event.subtype === "error" || event.is_error === true;
|
|
310
348
|
this.input.onNotification("turn/completed", {
|