@f5xc-salesdemos/xcsh 18.54.0 → 18.55.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/xcsh",
4
- "version": "18.54.0",
4
+ "version": "18.55.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5xc-salesdemos/xcsh",
7
7
  "author": "Can Boluk",
@@ -48,12 +48,12 @@
48
48
  "dependencies": {
49
49
  "@agentclientprotocol/sdk": "0.16.1",
50
50
  "@mozilla/readability": "^0.6",
51
- "@f5xc-salesdemos/xcsh-stats": "18.54.0",
52
- "@f5xc-salesdemos/pi-agent-core": "18.54.0",
53
- "@f5xc-salesdemos/pi-ai": "18.54.0",
54
- "@f5xc-salesdemos/pi-natives": "18.54.0",
55
- "@f5xc-salesdemos/pi-tui": "18.54.0",
56
- "@f5xc-salesdemos/pi-utils": "18.54.0",
51
+ "@f5xc-salesdemos/xcsh-stats": "18.55.0",
52
+ "@f5xc-salesdemos/pi-agent-core": "18.55.0",
53
+ "@f5xc-salesdemos/pi-ai": "18.55.0",
54
+ "@f5xc-salesdemos/pi-natives": "18.55.0",
55
+ "@f5xc-salesdemos/pi-tui": "18.55.0",
56
+ "@f5xc-salesdemos/pi-utils": "18.55.0",
57
57
  "@sinclair/typebox": "^0.34",
58
58
  "@xterm/headless": "^6.0",
59
59
  "ajv": "^8.18",
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.54.0",
21
- "commit": "cc0ce8cb044cde5a7ccfcdbd4ba82603fb8dbf66",
22
- "shortCommit": "cc0ce8c",
20
+ "version": "18.55.0",
21
+ "commit": "c87217beee0abc34c5d21baa7a6ecbd3e1109630",
22
+ "shortCommit": "c87217b",
23
23
  "branch": "main",
24
- "tag": "v18.54.0",
25
- "commitDate": "2026-05-09T17:34:09Z",
26
- "buildDate": "2026-05-09T17:58:06.189Z",
24
+ "tag": "v18.55.0",
25
+ "commitDate": "2026-05-09T19:47:18Z",
26
+ "buildDate": "2026-05-09T20:10:12.336Z",
27
27
  "dirty": false,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5xc-salesdemos/xcsh",
30
30
  "repoSlug": "f5xc-salesdemos/xcsh",
31
- "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/cc0ce8cb044cde5a7ccfcdbd4ba82603fb8dbf66",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.54.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/c87217beee0abc34c5d21baa7a6ecbd3e1109630",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.55.0"
33
33
  };
@@ -292,14 +292,14 @@ function repairToolResultOrdering(messages: Message[]): Message[] {
292
292
  }
293
293
  }
294
294
 
295
- // Track which toolResult messages have been placed by repair
296
- const placedToolResultIndices = new Set<number>();
295
+ // Track which message indices have been consumed (placed or displaced) by repair
296
+ const consumedIndices = new Set<number>();
297
297
 
298
298
  for (let i = 0; i < messages.length; i++) {
299
299
  const msg = messages[i];
300
300
 
301
- // Skip toolResult messages that were already placed by repair
302
- if (msg.role === "toolResult" && placedToolResultIndices.has(i)) {
301
+ // Skip toolResult messages that were already consumed (placed elsewhere or displaced)
302
+ if (msg.role === "toolResult" && consumedIndices.has(i)) {
303
303
  continue;
304
304
  }
305
305
 
@@ -326,7 +326,7 @@ function repairToolResultOrdering(messages: Message[]): Message[] {
326
326
  if (requiredIds.has(trMsg.toolCallId)) {
327
327
  // This tool_result belongs here — place it
328
328
  result.push(next);
329
- placedToolResultIndices.add(j);
329
+ consumedIndices.add(j);
330
330
  requiredIds.delete(trMsg.toolCallId);
331
331
  if (displaced.length > 0) repaired = true;
332
332
  j++;
@@ -335,7 +335,7 @@ function repairToolResultOrdering(messages: Message[]): Message[] {
335
335
  }
336
336
  // Non-matching message between tool_use and tool_result — displace it
337
337
  displaced.push(next);
338
- placedToolResultIndices.add(j); // Mark original index as consumed
338
+ consumedIndices.add(j); // Mark original index as consumed
339
339
  j++;
340
340
  }
341
341
 
@@ -345,9 +345,9 @@ function repairToolResultOrdering(messages: Message[]): Message[] {
345
345
  // Any remaining required IDs: find them later in the array or synthesize
346
346
  for (const id of requiredIds) {
347
347
  const found = toolResultsByCallId.get(id);
348
- if (found && !placedToolResultIndices.has(found.originalIndex)) {
348
+ if (found && !consumedIndices.has(found.originalIndex)) {
349
349
  result.push(found.message);
350
- placedToolResultIndices.add(found.originalIndex);
350
+ consumedIndices.add(found.originalIndex);
351
351
  repaired = true;
352
352
  } else {
353
353
  // Missing tool_result entirely — inject synthetic error result
@@ -370,6 +370,55 @@ function repairToolResultOrdering(messages: Message[]): Message[] {
370
370
  }
371
371
  }
372
372
 
373
+ // Second pass: repair displaced assistant messages whose tool calls were never processed.
374
+ // When an assistant-with-tool-calls gets displaced (wedged between another assistant's
375
+ // tool_use and its tool_result), the first pass pushes it to result but the outer loop
376
+ // jumps past it — so its own tool_results are never resolved.
377
+ for (let i = 0; i < result.length; i++) {
378
+ const msg = result[i];
379
+ if (msg.role !== "assistant") continue;
380
+ const assistantMsg = msg as AssistantMessage;
381
+ const toolCalls = assistantMsg.content.filter((c): c is ToolCall => c.type === "toolCall");
382
+ if (toolCalls.length === 0) continue;
383
+
384
+ // Check if every tool call has a toolResult immediately following
385
+ const expectedIds = new Set(toolCalls.map(tc => tc.id));
386
+ let j = i + 1;
387
+ while (j < result.length && result[j].role === "toolResult") {
388
+ expectedIds.delete((result[j] as ToolResultMessage).toolCallId);
389
+ j++;
390
+ }
391
+
392
+ if (expectedIds.size === 0) continue;
393
+
394
+ // For missing tool calls: relocate existing result from later in array, or synthesize
395
+ const toInsert: ToolResultMessage[] = [];
396
+ for (const id of expectedIds) {
397
+ // Check if a toolResult for this ID exists later in result
398
+ const laterIndex = result.findIndex(
399
+ (m, idx) => idx > j && m.role === "toolResult" && (m as ToolResultMessage).toolCallId === id,
400
+ );
401
+ if (laterIndex !== -1) {
402
+ // Relocate the existing result to right after the assistant
403
+ const [relocated] = result.splice(laterIndex, 1);
404
+ toInsert.push(relocated as ToolResultMessage);
405
+ } else {
406
+ const toolCall = toolCalls.find(tc => tc.id === id);
407
+ toInsert.push({
408
+ role: "toolResult",
409
+ toolCallId: id,
410
+ toolName: toolCall?.name ?? "unknown",
411
+ content: [{ type: "text", text: "Tool execution was interrupted (session recovery)." }],
412
+ isError: true,
413
+ timestamp: Date.now(),
414
+ } as ToolResultMessage);
415
+ }
416
+ }
417
+ result.splice(i + 1, 0, ...toInsert);
418
+ i += toInsert.length; // Skip past inserted results
419
+ repaired = true;
420
+ }
421
+
373
422
  if (repaired) {
374
423
  logger.warn("Repaired tool_use/tool_result ordering in conversation history");
375
424
  }