gsd-pi 2.73.0-dev.e1c09f2 → 2.73.1-dev.1040fb0

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.
Files changed (109) hide show
  1. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +9 -3
  2. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  3. package/dist/resources/extensions/gsd/auto-start.js +20 -6
  4. package/dist/resources/extensions/gsd/auto.js +5 -1
  5. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
  6. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
  7. package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
  8. package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
  9. package/dist/resources/extensions/gsd/gsd-db.js +36 -2
  10. package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
  11. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  12. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  13. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  14. package/dist/update-check.d.ts +1 -0
  15. package/dist/update-check.js +13 -5
  16. package/dist/update-cmd.js +4 -3
  17. package/dist/web/standalone/.next/BUILD_ID +1 -1
  18. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  19. package/dist/web/standalone/.next/build-manifest.json +2 -2
  20. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  21. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.html +1 -1
  38. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  45. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  47. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  48. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  49. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  50. package/package.json +1 -1
  51. package/packages/pi-ai/dist/index.d.ts +1 -0
  52. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  53. package/packages/pi-ai/dist/index.js +1 -0
  54. package/packages/pi-ai/dist/index.js.map +1 -1
  55. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  56. package/packages/pi-ai/dist/utils/overflow.js +12 -0
  57. package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
  58. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
  59. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
  60. package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
  61. package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
  62. package/packages/pi-ai/src/index.ts +4 -0
  63. package/packages/pi-ai/src/utils/overflow.ts +14 -1
  64. package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
  65. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +175 -8
  66. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
  68. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
  70. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
  71. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
  72. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
  73. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  74. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +51 -26
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +73 -12
  79. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  80. package/packages/pi-coding-agent/package.json +1 -1
  81. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +198 -8
  82. package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
  83. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
  84. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +62 -26
  85. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -17
  86. package/pkg/package.json +1 -1
  87. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -4
  88. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +23 -2
  89. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  90. package/src/resources/extensions/gsd/auto-start.ts +27 -6
  91. package/src/resources/extensions/gsd/auto.ts +5 -0
  92. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
  93. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
  94. package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
  95. package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
  96. package/src/resources/extensions/gsd/gsd-db.ts +52 -2
  97. package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
  98. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  99. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  100. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  101. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  102. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
  103. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  104. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
  105. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  106. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
  107. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  108. /package/dist/web/standalone/.next/static/{_XD_gUDcZNBbWV5rI8RgS → 5dzOW4v8Vz23I5xRsiNSk}/_buildManifest.js +0 -0
  109. /package/dist/web/standalone/.next/static/{_XD_gUDcZNBbWV5rI8RgS → 5dzOW4v8Vz23I5xRsiNSk}/_ssgManifest.js +0 -0
@@ -0,0 +1,50 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { isContextOverflow } from "../overflow.js";
4
+ function makeAssistantMessage(overrides = {}) {
5
+ return {
6
+ role: "assistant",
7
+ content: [],
8
+ api: "anthropic-messages",
9
+ provider: "anthropic",
10
+ model: "claude-sonnet-4-6",
11
+ usage: {
12
+ input: 0,
13
+ output: 0,
14
+ cacheRead: 0,
15
+ cacheWrite: 0,
16
+ totalTokens: 0,
17
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
18
+ },
19
+ stopReason: "error",
20
+ timestamp: Date.now(),
21
+ ...overrides,
22
+ };
23
+ }
24
+ describe("isContextOverflow", () => {
25
+ test("detects overflow from provider errorMessage", () => {
26
+ const message = makeAssistantMessage({
27
+ errorMessage: "prompt is too long: 213462 tokens > 200000 maximum",
28
+ });
29
+ assert.equal(isContextOverflow(message, 200000), true);
30
+ });
31
+ test("detects claude-code overflow when text contains the error but errorMessage is generic (#3925)", () => {
32
+ const message = makeAssistantMessage({
33
+ provider: "claude-code",
34
+ api: "anthropic-messages",
35
+ model: "claude-sonnet-4-6",
36
+ errorMessage: "success",
37
+ content: [{ type: "text", text: "Prompt is too long" }],
38
+ });
39
+ assert.equal(isContextOverflow(message, 200000), true);
40
+ });
41
+ test("does not treat normal non-error text as overflow", () => {
42
+ const message = makeAssistantMessage({
43
+ stopReason: "stop",
44
+ errorMessage: undefined,
45
+ content: [{ type: "text", text: "Prompt is too long" }],
46
+ });
47
+ assert.equal(isContextOverflow(message, 200000), false);
48
+ });
49
+ });
50
+ //# sourceMappingURL=overflow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overflow.test.js","sourceRoot":"","sources":["../../../src/utils/tests/overflow.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGnD,SAAS,oBAAoB,CAAC,YAAuC,EAAE;IACtE,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,EAAE;QACX,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,WAAW;QACrB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE;YACN,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;SACpE;QACD,UAAU,EAAE,OAAO;QACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,SAAS;KACZ,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAClC,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACxD,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACpC,YAAY,EAAE,oDAAoD;SAClE,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+FAA+F,EAAE,GAAG,EAAE;QAC1G,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACpC,QAAQ,EAAE,aAAa;YACvB,GAAG,EAAE,oBAAoB;YACzB,KAAK,EAAE,mBAAmB;YAC1B,YAAY,EAAE,SAAS;YACvB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;SACvD,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC7D,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACpC,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,SAAS;YACvB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;SACvD,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\n\nimport { isContextOverflow } from \"../overflow.js\";\nimport type { AssistantMessage } from \"../../types.js\";\n\nfunction makeAssistantMessage(overrides: Partial<AssistantMessage> = {}): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"anthropic\",\n\t\tmodel: \"claude-sonnet-4-6\",\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\ttimestamp: Date.now(),\n\t\t...overrides,\n\t};\n}\n\ndescribe(\"isContextOverflow\", () => {\n\ttest(\"detects overflow from provider errorMessage\", () => {\n\t\tconst message = makeAssistantMessage({\n\t\t\terrorMessage: \"prompt is too long: 213462 tokens > 200000 maximum\",\n\t\t});\n\n\t\tassert.equal(isContextOverflow(message, 200000), true);\n\t});\n\n\ttest(\"detects claude-code overflow when text contains the error but errorMessage is generic (#3925)\", () => {\n\t\tconst message = makeAssistantMessage({\n\t\t\tprovider: \"claude-code\",\n\t\t\tapi: \"anthropic-messages\",\n\t\t\tmodel: \"claude-sonnet-4-6\",\n\t\t\terrorMessage: \"success\",\n\t\t\tcontent: [{ type: \"text\", text: \"Prompt is too long\" }],\n\t\t});\n\n\t\tassert.equal(isContextOverflow(message, 200000), true);\n\t});\n\n\ttest(\"does not treat normal non-error text as overflow\", () => {\n\t\tconst message = makeAssistantMessage({\n\t\t\tstopReason: \"stop\",\n\t\t\terrorMessage: undefined,\n\t\t\tcontent: [{ type: \"text\", text: \"Prompt is too long\" }],\n\t\t});\n\n\t\tassert.equal(isContextOverflow(message, 200000), false);\n\t});\n});\n"]}
@@ -5,6 +5,10 @@ export * from "./api-registry.js";
5
5
  export * from "./env-api-keys.js";
6
6
  export * from "./models.js";
7
7
  export * from "./providers/anthropic.js";
8
+ export {
9
+ mapThinkingLevelToEffort,
10
+ supportsAdaptiveThinking,
11
+ } from "./providers/anthropic-shared.js";
8
12
  export * from "./providers/azure-openai-responses.js";
9
13
  export * from "./providers/google.js";
10
14
  export * from "./providers/google-gemini-cli.js";
@@ -104,6 +104,19 @@ export function isContextOverflow(message: AssistantMessage, contextWindow?: num
104
104
  }
105
105
  }
106
106
 
107
+ // Some providers surface overflow as assistant text while putting a generic
108
+ // classifier value in errorMessage (e.g. claude-code: errorMessage="success",
109
+ // text="Prompt is too long"). Check rendered text as a fallback.
110
+ if (message.stopReason === "error") {
111
+ const assistantText = message.content
112
+ .filter((block) => block.type === "text")
113
+ .map((block) => block.text)
114
+ .join("\n");
115
+ if (assistantText && OVERFLOW_PATTERNS.some((p) => p.test(assistantText))) {
116
+ return true;
117
+ }
118
+ }
119
+
107
120
  // Case 2: Silent overflow (z.ai style) - successful but usage exceeds context
108
121
  if (contextWindow && message.stopReason === "stop") {
109
122
  const inputTokens = message.usage.input + message.usage.cacheRead;
@@ -113,4 +126,4 @@ export function isContextOverflow(message: AssistantMessage, contextWindow?: num
113
126
  }
114
127
 
115
128
  return false;
116
- }
129
+ }
@@ -0,0 +1,58 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import { isContextOverflow } from "../overflow.js";
5
+ import type { AssistantMessage } from "../../types.js";
6
+
7
+ function makeAssistantMessage(overrides: Partial<AssistantMessage> = {}): AssistantMessage {
8
+ return {
9
+ role: "assistant",
10
+ content: [],
11
+ api: "anthropic-messages",
12
+ provider: "anthropic",
13
+ model: "claude-sonnet-4-6",
14
+ usage: {
15
+ input: 0,
16
+ output: 0,
17
+ cacheRead: 0,
18
+ cacheWrite: 0,
19
+ totalTokens: 0,
20
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
21
+ },
22
+ stopReason: "error",
23
+ timestamp: Date.now(),
24
+ ...overrides,
25
+ };
26
+ }
27
+
28
+ describe("isContextOverflow", () => {
29
+ test("detects overflow from provider errorMessage", () => {
30
+ const message = makeAssistantMessage({
31
+ errorMessage: "prompt is too long: 213462 tokens > 200000 maximum",
32
+ });
33
+
34
+ assert.equal(isContextOverflow(message, 200000), true);
35
+ });
36
+
37
+ test("detects claude-code overflow when text contains the error but errorMessage is generic (#3925)", () => {
38
+ const message = makeAssistantMessage({
39
+ provider: "claude-code",
40
+ api: "anthropic-messages",
41
+ model: "claude-sonnet-4-6",
42
+ errorMessage: "success",
43
+ content: [{ type: "text", text: "Prompt is too long" }],
44
+ });
45
+
46
+ assert.equal(isContextOverflow(message, 200000), true);
47
+ });
48
+
49
+ test("does not treat normal non-error text as overflow", () => {
50
+ const message = makeAssistantMessage({
51
+ stopReason: "stop",
52
+ errorMessage: undefined,
53
+ content: [{ type: "text", text: "Prompt is too long" }],
54
+ });
55
+
56
+ assert.equal(isContextOverflow(message, 200000), false);
57
+ });
58
+ });
@@ -88,7 +88,7 @@ function createHost() {
88
88
  };
89
89
  return host;
90
90
  }
91
- test("chat-controller keeps tool output ahead of delayed assistant text for external tool streams", async () => {
91
+ test("chat-controller renders content blocks in content[] index order (tool-first stream)", async () => {
92
92
  // ToolExecutionComponent uses the global theme singleton.
93
93
  // Install a minimal no-op theme implementation for this unit test.
94
94
  globalThis[Symbol.for("@gsd/pi-coding-agent:theme")] = {
@@ -107,7 +107,6 @@ test("chat-controller keeps tool output ahead of delayed assistant text for exte
107
107
  arguments: { cmd: "echo hi" },
108
108
  };
109
109
  await handleAgentEvent(host, { type: "message_start", message: makeAssistant([]) });
110
- assert.equal(host.streamingComponent, undefined, "assistant component should be deferred at message_start");
111
110
  assert.equal(host.chatContainer.children.length, 0, "nothing should render before content arrives");
112
111
  await handleAgentEvent(host, {
113
112
  type: "message_update",
@@ -126,10 +125,9 @@ test("chat-controller keeps tool output ahead of delayed assistant text for exte
126
125
  partial: makeAssistant([toolCall]),
127
126
  },
128
127
  });
129
- assert.equal(host.streamingComponent, undefined, "assistant text container should remain deferred for tool-only updates");
128
+ // content[0] = toolCall ToolExecutionComponent renders first
130
129
  assert.equal(host.chatContainer.children.length, 1, "tool execution block should render immediately");
131
130
  assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolExecutionComponent");
132
- // Re-assert required host method before the text-bearing update path.
133
131
  host.getMarkdownThemeWithSettings = () => ({});
134
132
  await handleAgentEvent(host, {
135
133
  type: "message_update",
@@ -141,11 +139,12 @@ test("chat-controller keeps tool output ahead of delayed assistant text for exte
141
139
  partial: makeAssistant([toolCall, { type: "text", text: "done" }]),
142
140
  },
143
141
  });
144
- assert.equal(host.chatContainer.children.length, 2, "assistant content should render after existing tool output");
142
+ // content[0]=toolCall, content[1]=text order: tool, then text
143
+ assert.equal(host.chatContainer.children.length, 2, "text run should render after tool in content[] order");
145
144
  assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolExecutionComponent");
146
145
  assert.equal(host.chatContainer.children[1]?.constructor?.name, "AssistantMessageComponent");
147
146
  });
148
- test("chat-controller keeps serverToolUse output ahead of assistant text when external results arrive", async () => {
147
+ test("chat-controller renders serverToolUse before trailing text matching content[] index order", async () => {
149
148
  globalThis[Symbol.for("@gsd/pi-coding-agent:theme")] = {
150
149
  fg: (_key, text) => text,
151
150
  bg: (_key, text) => text,
@@ -171,7 +170,7 @@ test("chat-controller keeps serverToolUse output ahead of assistant text when ex
171
170
  partial: makeAssistant([serverToolUse]),
172
171
  },
173
172
  });
174
- assert.equal(host.streamingComponent, undefined, "assistant content should stay deferred while only tool content streams");
173
+ // content[0] = serverToolUse ToolExecutionComponent renders first
175
174
  assert.equal(host.chatContainer.children.length, 1, "server tool block should render immediately");
176
175
  assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolExecutionComponent");
177
176
  host.getMarkdownThemeWithSettings = () => ({});
@@ -195,7 +194,8 @@ test("chat-controller keeps serverToolUse output ahead of assistant text when ex
195
194
  partial: resultMessage,
196
195
  },
197
196
  });
198
- assert.equal(host.chatContainer.children.length, 2, "assistant text should render after existing server tool output");
197
+ // content[0]=serverToolUse, content[1]=text order: tool, then text
198
+ assert.equal(host.chatContainer.children.length, 2, "text run should render after server tool in content[] order");
199
199
  assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolExecutionComponent");
200
200
  assert.equal(host.chatContainer.children[1]?.constructor?.name, "AssistantMessageComponent");
201
201
  });
@@ -385,4 +385,171 @@ test("chat-controller does not pin when there are no tool calls", async () => {
385
385
  });
386
386
  assert.equal(host.pinnedMessageContainer.children.length, 0, "pinned zone should stay empty without tool calls");
387
387
  });
388
+ // Regression test for issue #4144: interleaved text/tool content must render in content[] index order.
389
+ // Stream: [text "A", toolCall T1, text "B", toolCall T2, text "C"]
390
+ // Expected chatContainer order: textRun(A), toolExec(T1), textRun(B), toolExec(T2), textRun(C)
391
+ // Each AssistantMessageComponent must render ONLY its own text — no duplication after message_end.
392
+ test("chat-controller renders interleaved text and tool blocks in content[] index order (#4144)", async () => {
393
+ globalThis[Symbol.for("@gsd/pi-coding-agent:theme")] = {
394
+ fg: (_key, text) => text,
395
+ bg: (_key, text) => text,
396
+ bold: (text) => text,
397
+ italic: (text) => text,
398
+ truncate: (text) => text,
399
+ };
400
+ const host = createHost();
401
+ host.getMarkdownThemeWithSettings = () => ({});
402
+ const t1 = { type: "toolCall", id: "t1", name: "tool_one", arguments: {} };
403
+ const t2 = { type: "toolCall", id: "t2", name: "tool_two", arguments: {} };
404
+ await handleAgentEvent(host, { type: "message_start", message: makeAssistant([]) });
405
+ // Stream text "A" at index 0
406
+ await handleAgentEvent(host, {
407
+ type: "message_update",
408
+ message: makeAssistant([{ type: "text", text: "A" }]),
409
+ assistantMessageEvent: {
410
+ type: "text_delta",
411
+ contentIndex: 0,
412
+ delta: "A",
413
+ partial: makeAssistant([{ type: "text", text: "A" }]),
414
+ },
415
+ });
416
+ // Stream toolCall T1 at index 1
417
+ await handleAgentEvent(host, {
418
+ type: "message_update",
419
+ message: makeAssistant([{ type: "text", text: "A" }, t1]),
420
+ assistantMessageEvent: {
421
+ type: "toolcall_end",
422
+ contentIndex: 1,
423
+ toolCall: {
424
+ ...t1,
425
+ externalResult: { content: [{ type: "text", text: "result1" }], details: {}, isError: false },
426
+ },
427
+ partial: makeAssistant([{ type: "text", text: "A" }, t1]),
428
+ },
429
+ });
430
+ // Stream text "B" at index 2
431
+ await handleAgentEvent(host, {
432
+ type: "message_update",
433
+ message: makeAssistant([{ type: "text", text: "A" }, t1, { type: "text", text: "B" }]),
434
+ assistantMessageEvent: {
435
+ type: "text_delta",
436
+ contentIndex: 2,
437
+ delta: "B",
438
+ partial: makeAssistant([{ type: "text", text: "A" }, t1, { type: "text", text: "B" }]),
439
+ },
440
+ });
441
+ // Stream toolCall T2 at index 3
442
+ await handleAgentEvent(host, {
443
+ type: "message_update",
444
+ message: makeAssistant([{ type: "text", text: "A" }, t1, { type: "text", text: "B" }, t2]),
445
+ assistantMessageEvent: {
446
+ type: "toolcall_end",
447
+ contentIndex: 3,
448
+ toolCall: {
449
+ ...t2,
450
+ externalResult: { content: [{ type: "text", text: "result2" }], details: {}, isError: false },
451
+ },
452
+ partial: makeAssistant([{ type: "text", text: "A" }, t1, { type: "text", text: "B" }, t2]),
453
+ },
454
+ });
455
+ // Stream text "C" at index 4
456
+ const finalContent = [
457
+ { type: "text", text: "A" }, t1, { type: "text", text: "B" }, t2, { type: "text", text: "C" },
458
+ ];
459
+ await handleAgentEvent(host, {
460
+ type: "message_update",
461
+ message: makeAssistant(finalContent),
462
+ assistantMessageEvent: {
463
+ type: "text_delta",
464
+ contentIndex: 4,
465
+ delta: "C",
466
+ partial: makeAssistant(finalContent),
467
+ },
468
+ });
469
+ // Finalize — exercises the message_end path where a buggy setRange(undefined) would cause duplication
470
+ await handleAgentEvent(host, { type: "message_end", message: makeAssistant(finalContent) });
471
+ // Assert interleaved order: textRun(A), toolExec(T1), textRun(B), toolExec(T2), textRun(C)
472
+ assert.equal(host.chatContainer.children.length, 5, "should have 5 children in interleaved order");
473
+ assert.equal(host.chatContainer.children[0]?.constructor?.name, "AssistantMessageComponent", "index 0: text run A");
474
+ assert.equal(host.chatContainer.children[1]?.constructor?.name, "ToolExecutionComponent", "index 1: tool T1");
475
+ assert.equal(host.chatContainer.children[2]?.constructor?.name, "AssistantMessageComponent", "index 2: text run B");
476
+ assert.equal(host.chatContainer.children[3]?.constructor?.name, "ToolExecutionComponent", "index 3: tool T2");
477
+ assert.equal(host.chatContainer.children[4]?.constructor?.name, "AssistantMessageComponent", "index 4: text run C");
478
+ // Helper: collect the text of all Markdown children inside an AssistantMessageComponent.
479
+ // Structure: AssistantMessageComponent (Container) -> contentContainer (children[0]) -> Markdown nodes.
480
+ function getRenderedTexts(comp) {
481
+ const contentContainer = comp.children?.[0];
482
+ if (!contentContainer)
483
+ return [];
484
+ return (contentContainer.children ?? [])
485
+ .filter((c) => c.constructor?.name === "Markdown")
486
+ .map((c) => c.text);
487
+ }
488
+ // Each text-run component must contain only its own text — no cross-contamination after message_end
489
+ assert.deepEqual(getRenderedTexts(host.chatContainer.children[0]), ["A"], "text run A must contain only 'A'");
490
+ assert.deepEqual(getRenderedTexts(host.chatContainer.children[2]), ["B"], "text run B must contain only 'B'");
491
+ assert.deepEqual(getRenderedTexts(host.chatContainer.children[4]), ["C"], "text run C must contain only 'C'");
492
+ });
493
+ test("chat-controller does not duplicate text when content is [text, tool, text] (interleaved stream)", async () => {
494
+ globalThis[Symbol.for("@gsd/pi-coding-agent:theme")] = {
495
+ fg: (_key, text) => text,
496
+ bg: (_key, text) => text,
497
+ bold: (text) => text,
498
+ italic: (text) => text,
499
+ truncate: (text) => text,
500
+ };
501
+ const host = createHost();
502
+ host.getMarkdownThemeWithSettings = () => ({});
503
+ const t1 = { type: "toolCall", id: "t1", name: "tool_one", arguments: {} };
504
+ await handleAgentEvent(host, { type: "message_start", message: makeAssistant([]) });
505
+ // Step 1: text "A" at index 0
506
+ await handleAgentEvent(host, {
507
+ type: "message_update",
508
+ message: makeAssistant([{ type: "text", text: "A" }]),
509
+ assistantMessageEvent: {
510
+ type: "text_delta",
511
+ contentIndex: 0,
512
+ delta: "A",
513
+ partial: makeAssistant([{ type: "text", text: "A" }]),
514
+ },
515
+ });
516
+ // Step 2: toolCall at index 1
517
+ await handleAgentEvent(host, {
518
+ type: "message_update",
519
+ message: makeAssistant([{ type: "text", text: "A" }, t1]),
520
+ assistantMessageEvent: {
521
+ type: "toolcall_end",
522
+ contentIndex: 1,
523
+ toolCall: {
524
+ ...t1,
525
+ externalResult: { content: [{ type: "text", text: "result1" }], details: {}, isError: false },
526
+ },
527
+ partial: makeAssistant([{ type: "text", text: "A" }, t1]),
528
+ },
529
+ });
530
+ // Step 3: text "B" at index 2
531
+ const finalContent = [{ type: "text", text: "A" }, t1, { type: "text", text: "B" }];
532
+ await handleAgentEvent(host, {
533
+ type: "message_update",
534
+ message: makeAssistant(finalContent),
535
+ assistantMessageEvent: {
536
+ type: "text_delta",
537
+ contentIndex: 2,
538
+ delta: "B",
539
+ partial: makeAssistant(finalContent),
540
+ },
541
+ });
542
+ assert.equal(host.chatContainer.children.length, 3);
543
+ assert.equal(host.chatContainer.children[0]?.constructor?.name, "AssistantMessageComponent");
544
+ assert.equal(host.chatContainer.children[1]?.constructor?.name, "ToolExecutionComponent");
545
+ assert.equal(host.chatContainer.children[2]?.constructor?.name, "AssistantMessageComponent");
546
+ const firstText = host.chatContainer.children[0];
547
+ const secondText = host.chatContainer.children[2];
548
+ assert.notEqual(firstText, secondText, "text-before-tool and text-after-tool must be separate component instances");
549
+ assert.deepEqual(firstText.range, { startIndex: 0, endIndex: 0 }, "first text-run covers only content[0]");
550
+ assert.deepEqual(secondText.range, { startIndex: 2, endIndex: 2 }, "second text-run covers only content[2]");
551
+ // Finalize — regression guard: range must NOT be cleared on message_end (would cause duplication)
552
+ await handleAgentEvent(host, { type: "message_end", message: makeAssistant(finalContent) });
553
+ assert.deepEqual(secondText.range, { startIndex: 2, endIndex: 2 }, "range must not be cleared on message_end (would cause duplication)");
554
+ });
388
555
  //# sourceMappingURL=chat-controller-ordering.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-controller-ordering.test.js","sourceRoot":"","sources":["../../src/core/chat-controller-ordering.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { test } from \"node:test\";\n\nimport { handleAgentEvent } from \"../modes/interactive/controllers/chat-controller.js\";\n\nfunction makeUsage() {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t};\n}\n\nfunction makeAssistant(content: any[]) {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent,\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"claude-code\",\n\t\tmodel: \"claude-sonnet-4\",\n\t\tusage: makeUsage(),\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction createHost() {\n\tconst chatContainer = {\n\t\tchildren: [] as any[],\n\t\taddChild(component: any) {\n\t\t\tthis.children.push(component);\n\t\t},\n\t\tremoveChild(component: any) {\n\t\t\tconst idx = this.children.indexOf(component);\n\t\t\tif (idx !== -1) this.children.splice(idx, 1);\n\t\t},\n\t\tclear() {\n\t\t\tthis.children = [];\n\t\t},\n\t};\n\n\tconst pinnedMessageContainer = {\n\t\tchildren: [] as any[],\n\t\taddChild(component: any) {\n\t\t\tthis.children.push(component);\n\t\t},\n\t\tremoveChild(component: any) {\n\t\t\tconst idx = this.children.indexOf(component);\n\t\t\tif (idx !== -1) this.children.splice(idx, 1);\n\t\t},\n\t\tclear() {\n\t\t\tthis.children = [];\n\t\t},\n\t};\n\n\tconst host: any = {\n\t\tisInitialized: true,\n\t\tinit: async () => {},\n\t\tdefaultEditor: { onEscape: undefined },\n\t\teditor: {},\n\t\tsession: { retryAttempt: 0, abortCompaction: () => {}, abortRetry: () => {} },\n\t\tui: { requestRender: () => {}, terminal: { rows: 50 } },\n\t\tfooter: { invalidate: () => {} },\n\t\tkeybindings: {},\n\t\tstatusContainer: { clear: () => {}, addChild: () => {} },\n\t\tchatContainer,\n\t\tsettingsManager: { getTimestampFormat: () => \"date-time-iso\", getShowImages: () => false },\n\t\tpendingTools: new Map(),\n\t\ttoolOutputExpanded: false,\n\t\thideThinkingBlock: false,\n\t\tisBashMode: false,\n\t\tdefaultWorkingMessage: \"Working...\",\n\t\tcompactionQueuedMessages: [],\n\t\teditorContainer: {},\n\t\tpendingMessagesContainer: { clear: () => {} },\n\t\tpinnedMessageContainer,\n\t\taddMessageToChat: () => {},\n\t\tgetMarkdownThemeWithSettings: () => ({}),\n\t\tformatWebSearchResult: () => \"\",\n\t\tgetRegisteredToolDefinition: () => undefined,\n\t\tcheckShutdownRequested: async () => {},\n\t\trebuildChatFromMessages: () => {},\n\t\tflushCompactionQueue: async () => {},\n\t\tshowStatus: () => {},\n\t\tshowError: () => {},\n\t\tupdatePendingMessagesDisplay: () => {},\n\t\tupdateTerminalTitle: () => {},\n\t\tupdateEditorBorderColor: () => {},\n\t};\n\n\treturn host;\n}\n\ntest(\"chat-controller keeps tool output ahead of delayed assistant text for external tool streams\", async () => {\n\t// ToolExecutionComponent uses the global theme singleton.\n\t// Install a minimal no-op theme implementation for this unit test.\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"mcp-tool-1\";\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolId,\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.streamingComponent, undefined, \"assistant component should be deferred at message_start\");\n\tassert.equal(host.chatContainer.children.length, 0, \"nothing should render before content arrives\");\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"tool output\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.streamingComponent, undefined, \"assistant text container should remain deferred for tool-only updates\");\n\tassert.equal(host.chatContainer.children.length, 1, \"tool execution block should render immediately\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\n\t// Re-assert required host method before the text-bearing update path.\n\thost.getMarkdownThemeWithSettings = () => ({});\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([toolCall, { type: \"text\", text: \"done\" }]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"text_delta\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\tdelta: \"done\",\n\t\t\t\tpartial: makeAssistant([toolCall, { type: \"text\", text: \"done\" }]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.chatContainer.children.length, 2, \"assistant content should render after existing tool output\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"AssistantMessageComponent\");\n});\n\ntest(\"chat-controller keeps serverToolUse output ahead of assistant text when external results arrive\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"mcp-secure-1\";\n\tconst serverToolUse = {\n\t\ttype: \"serverToolUse\",\n\t\tid: toolId,\n\t\tname: \"mcp__gsd-workflow__secure_env_collect\",\n\t\tinput: { projectDir: \"/tmp/project\", keys: [{ key: \"SECURE_PASSWORD\" }], destination: \"dotenv\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([serverToolUse]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"server_tool_use\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tpartial: makeAssistant([serverToolUse]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.streamingComponent, undefined, \"assistant content should stay deferred while only tool content streams\");\n\tassert.equal(host.chatContainer.children.length, 1, \"server tool block should render immediately\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tconst resultMessage = makeAssistant([\n\t\t{\n\t\t\t...serverToolUse,\n\t\t\texternalResult: {\n\t\t\t\tcontent: [{ type: \"text\", text: \"secure_env_collect was cancelled by user.\" }],\n\t\t\t\tdetails: {},\n\t\t\t\tisError: true,\n\t\t\t},\n\t\t},\n\t\t{ type: \"text\", text: \"The secure password collection was cancelled.\" },\n\t]);\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: resultMessage,\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"server_tool_use\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tpartial: resultMessage,\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.chatContainer.children.length, 2, \"assistant text should render after existing server tool output\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"AssistantMessageComponent\");\n});\n\ntest(\"chat-controller pins latest assistant text above editor when tool calls are present\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"tool-pin-1\";\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolId,\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should be empty at message_start\");\n\n\t// Send a message with text followed by a tool call\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([\n\t\t\t\t{ type: \"text\", text: \"Looking at the files now.\" },\n\t\t\t\ttoolCall,\n\t\t\t]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"file contents\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Looking at the files now.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// Pinned zone should now have a DynamicBorder and a Markdown component\n\tassert.equal(host.pinnedMessageContainer.children.length, 2, \"pinned zone should have border + markdown\");\n\tassert.equal(host.pinnedMessageContainer.children[0]?.constructor?.name, \"DynamicBorder\");\n\tassert.equal(host.pinnedMessageContainer.children[1]?.constructor?.name, \"Markdown\");\n});\n\ntest(\"chat-controller clears pinned zone when a new assistant message starts\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-clear-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\t// Populate the pinned zone\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated\");\n\n\t// Start a new assistant message — pinned zone should clear\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on new assistant message\");\n});\n\ntest(\"chat-controller clears pinned zone when the agent turn ends\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-clear-on-end-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated before agent_end\");\n\n\tawait handleAgentEvent(host, { type: \"agent_end\" } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on agent_end\");\n});\n\ntest(\"chat-controller clears pinned zone when assistant message ends\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-msg-end-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tconst msgContent = [{ type: \"text\", text: \"Summary after tools.\" }, toolCall];\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant(msgContent),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant(msgContent),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated during streaming\");\n\n\t// End the assistant message (e.g. before form elicitation) — pinned zone should clear\n\tawait handleAgentEvent(host, { type: \"message_end\", message: makeAssistant(msgContent) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on message_end to prevent duplicate display\");\n});\n\ntest(\"chat-controller does not pin when there are no tool calls\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Just some text, no tools.\" }]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"text_delta\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tdelta: \"Just some text, no tools.\",\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Just some text, no tools.\" }]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should stay empty without tool calls\");\n});\n"]}
1
+ {"version":3,"file":"chat-controller-ordering.test.js","sourceRoot":"","sources":["../../src/core/chat-controller-ordering.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { test } from \"node:test\";\n\nimport { handleAgentEvent } from \"../modes/interactive/controllers/chat-controller.js\";\n\nfunction makeUsage() {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t};\n}\n\nfunction makeAssistant(content: any[]) {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent,\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"claude-code\",\n\t\tmodel: \"claude-sonnet-4\",\n\t\tusage: makeUsage(),\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction createHost() {\n\tconst chatContainer = {\n\t\tchildren: [] as any[],\n\t\taddChild(component: any) {\n\t\t\tthis.children.push(component);\n\t\t},\n\t\tremoveChild(component: any) {\n\t\t\tconst idx = this.children.indexOf(component);\n\t\t\tif (idx !== -1) this.children.splice(idx, 1);\n\t\t},\n\t\tclear() {\n\t\t\tthis.children = [];\n\t\t},\n\t};\n\n\tconst pinnedMessageContainer = {\n\t\tchildren: [] as any[],\n\t\taddChild(component: any) {\n\t\t\tthis.children.push(component);\n\t\t},\n\t\tremoveChild(component: any) {\n\t\t\tconst idx = this.children.indexOf(component);\n\t\t\tif (idx !== -1) this.children.splice(idx, 1);\n\t\t},\n\t\tclear() {\n\t\t\tthis.children = [];\n\t\t},\n\t};\n\n\tconst host: any = {\n\t\tisInitialized: true,\n\t\tinit: async () => {},\n\t\tdefaultEditor: { onEscape: undefined },\n\t\teditor: {},\n\t\tsession: { retryAttempt: 0, abortCompaction: () => {}, abortRetry: () => {} },\n\t\tui: { requestRender: () => {}, terminal: { rows: 50 } },\n\t\tfooter: { invalidate: () => {} },\n\t\tkeybindings: {},\n\t\tstatusContainer: { clear: () => {}, addChild: () => {} },\n\t\tchatContainer,\n\t\tsettingsManager: { getTimestampFormat: () => \"date-time-iso\", getShowImages: () => false },\n\t\tpendingTools: new Map(),\n\t\ttoolOutputExpanded: false,\n\t\thideThinkingBlock: false,\n\t\tisBashMode: false,\n\t\tdefaultWorkingMessage: \"Working...\",\n\t\tcompactionQueuedMessages: [],\n\t\teditorContainer: {},\n\t\tpendingMessagesContainer: { clear: () => {} },\n\t\tpinnedMessageContainer,\n\t\taddMessageToChat: () => {},\n\t\tgetMarkdownThemeWithSettings: () => ({}),\n\t\tformatWebSearchResult: () => \"\",\n\t\tgetRegisteredToolDefinition: () => undefined,\n\t\tcheckShutdownRequested: async () => {},\n\t\trebuildChatFromMessages: () => {},\n\t\tflushCompactionQueue: async () => {},\n\t\tshowStatus: () => {},\n\t\tshowError: () => {},\n\t\tupdatePendingMessagesDisplay: () => {},\n\t\tupdateTerminalTitle: () => {},\n\t\tupdateEditorBorderColor: () => {},\n\t};\n\n\treturn host;\n}\n\ntest(\"chat-controller renders content blocks in content[] index order (tool-first stream)\", async () => {\n\t// ToolExecutionComponent uses the global theme singleton.\n\t// Install a minimal no-op theme implementation for this unit test.\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"mcp-tool-1\";\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolId,\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.chatContainer.children.length, 0, \"nothing should render before content arrives\");\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"tool output\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// content[0] = toolCall → ToolExecutionComponent renders first\n\tassert.equal(host.chatContainer.children.length, 1, \"tool execution block should render immediately\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([toolCall, { type: \"text\", text: \"done\" }]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"text_delta\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\tdelta: \"done\",\n\t\t\t\tpartial: makeAssistant([toolCall, { type: \"text\", text: \"done\" }]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// content[0]=toolCall, content[1]=text → order: tool, then text\n\tassert.equal(host.chatContainer.children.length, 2, \"text run should render after tool in content[] order\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"AssistantMessageComponent\");\n});\n\ntest(\"chat-controller renders serverToolUse before trailing text matching content[] index order\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"mcp-secure-1\";\n\tconst serverToolUse = {\n\t\ttype: \"serverToolUse\",\n\t\tid: toolId,\n\t\tname: \"mcp__gsd-workflow__secure_env_collect\",\n\t\tinput: { projectDir: \"/tmp/project\", keys: [{ key: \"SECURE_PASSWORD\" }], destination: \"dotenv\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([serverToolUse]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"server_tool_use\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tpartial: makeAssistant([serverToolUse]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// content[0] = serverToolUse → ToolExecutionComponent renders first\n\tassert.equal(host.chatContainer.children.length, 1, \"server tool block should render immediately\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tconst resultMessage = makeAssistant([\n\t\t{\n\t\t\t...serverToolUse,\n\t\t\texternalResult: {\n\t\t\t\tcontent: [{ type: \"text\", text: \"secure_env_collect was cancelled by user.\" }],\n\t\t\t\tdetails: {},\n\t\t\t\tisError: true,\n\t\t\t},\n\t\t},\n\t\t{ type: \"text\", text: \"The secure password collection was cancelled.\" },\n\t]);\n\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: resultMessage,\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"server_tool_use\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tpartial: resultMessage,\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// content[0]=serverToolUse, content[1]=text → order: tool, then text\n\tassert.equal(host.chatContainer.children.length, 2, \"text run should render after server tool in content[] order\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"ToolExecutionComponent\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"AssistantMessageComponent\");\n});\n\ntest(\"chat-controller pins latest assistant text above editor when tool calls are present\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolId = \"tool-pin-1\";\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolId,\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should be empty at message_start\");\n\n\t// Send a message with text followed by a tool call\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([\n\t\t\t\t{ type: \"text\", text: \"Looking at the files now.\" },\n\t\t\t\ttoolCall,\n\t\t\t]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"file contents\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Looking at the files now.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\t// Pinned zone should now have a DynamicBorder and a Markdown component\n\tassert.equal(host.pinnedMessageContainer.children.length, 2, \"pinned zone should have border + markdown\");\n\tassert.equal(host.pinnedMessageContainer.children[0]?.constructor?.name, \"DynamicBorder\");\n\tassert.equal(host.pinnedMessageContainer.children[1]?.constructor?.name, \"Markdown\");\n});\n\ntest(\"chat-controller clears pinned zone when a new assistant message starts\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-clear-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\t// Populate the pinned zone\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated\");\n\n\t// Start a new assistant message — pinned zone should clear\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on new assistant message\");\n});\n\ntest(\"chat-controller clears pinned zone when the agent turn ends\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-clear-on-end-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Working on it.\" }, toolCall]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated before agent_end\");\n\n\tawait handleAgentEvent(host, { type: \"agent_end\" } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on agent_end\");\n});\n\ntest(\"chat-controller clears pinned zone when assistant message ends\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\tconst toolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: \"tool-msg-end-1\",\n\t\tname: \"exec_command\",\n\t\targuments: { cmd: \"echo hi\" },\n\t};\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tconst msgContent = [{ type: \"text\", text: \"Summary after tools.\" }, toolCall];\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant(msgContent),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\tcontentIndex: 1,\n\t\t\t\ttoolCall: {\n\t\t\t\t\t...toolCall,\n\t\t\t\t\texternalResult: {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"ok\" }],\n\t\t\t\t\t\tdetails: {},\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpartial: makeAssistant(msgContent),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.ok(host.pinnedMessageContainer.children.length > 0, \"pinned zone should be populated during streaming\");\n\n\t// End the assistant message (e.g. before form elicitation) — pinned zone should clear\n\tawait handleAgentEvent(host, { type: \"message_end\", message: makeAssistant(msgContent) } as any);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should clear on message_end to prevent duplicate display\");\n});\n\ntest(\"chat-controller does not pin when there are no tool calls\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\thost.getMarkdownThemeWithSettings = () => ({});\n\tawait handleAgentEvent(\n\t\thost,\n\t\t{\n\t\t\ttype: \"message_update\",\n\t\t\tmessage: makeAssistant([{ type: \"text\", text: \"Just some text, no tools.\" }]),\n\t\t\tassistantMessageEvent: {\n\t\t\t\ttype: \"text_delta\",\n\t\t\t\tcontentIndex: 0,\n\t\t\t\tdelta: \"Just some text, no tools.\",\n\t\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"Just some text, no tools.\" }]),\n\t\t\t},\n\t\t} as any,\n\t);\n\n\tassert.equal(host.pinnedMessageContainer.children.length, 0, \"pinned zone should stay empty without tool calls\");\n});\n\n// Regression test for issue #4144: interleaved text/tool content must render in content[] index order.\n// Stream: [text \"A\", toolCall T1, text \"B\", toolCall T2, text \"C\"]\n// Expected chatContainer order: textRun(A), toolExec(T1), textRun(B), toolExec(T2), textRun(C)\n// Each AssistantMessageComponent must render ONLY its own text — no duplication after message_end.\ntest(\"chat-controller renders interleaved text and tool blocks in content[] index order (#4144)\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\thost.getMarkdownThemeWithSettings = () => ({});\n\n\tconst t1 = { type: \"toolCall\", id: \"t1\", name: \"tool_one\", arguments: {} };\n\tconst t2 = { type: \"toolCall\", id: \"t2\", name: \"tool_two\", arguments: {} };\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\t// Stream text \"A\" at index 0\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"text_delta\",\n\t\t\tcontentIndex: 0,\n\t\t\tdelta: \"A\",\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }]),\n\t\t},\n\t} as any);\n\n\t// Stream toolCall T1 at index 1\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }, t1]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"toolcall_end\",\n\t\t\tcontentIndex: 1,\n\t\t\ttoolCall: {\n\t\t\t\t...t1,\n\t\t\t\texternalResult: { content: [{ type: \"text\", text: \"result1\" }], details: {}, isError: false },\n\t\t\t},\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }, t1]),\n\t\t},\n\t} as any);\n\n\t// Stream text \"B\" at index 2\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"text_delta\",\n\t\t\tcontentIndex: 2,\n\t\t\tdelta: \"B\",\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }]),\n\t\t},\n\t} as any);\n\n\t// Stream toolCall T2 at index 3\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }, t2]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"toolcall_end\",\n\t\t\tcontentIndex: 3,\n\t\t\ttoolCall: {\n\t\t\t\t...t2,\n\t\t\t\texternalResult: { content: [{ type: \"text\", text: \"result2\" }], details: {}, isError: false },\n\t\t\t},\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }, t2]),\n\t\t},\n\t} as any);\n\n\t// Stream text \"C\" at index 4\n\tconst finalContent = [\n\t\t{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }, t2, { type: \"text\", text: \"C\" },\n\t];\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant(finalContent),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"text_delta\",\n\t\t\tcontentIndex: 4,\n\t\t\tdelta: \"C\",\n\t\t\tpartial: makeAssistant(finalContent),\n\t\t},\n\t} as any);\n\n\t// Finalize — exercises the message_end path where a buggy setRange(undefined) would cause duplication\n\tawait handleAgentEvent(host, { type: \"message_end\", message: makeAssistant(finalContent) } as any);\n\n\t// Assert interleaved order: textRun(A), toolExec(T1), textRun(B), toolExec(T2), textRun(C)\n\tassert.equal(host.chatContainer.children.length, 5, \"should have 5 children in interleaved order\");\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"AssistantMessageComponent\", \"index 0: text run A\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"ToolExecutionComponent\", \"index 1: tool T1\");\n\tassert.equal(host.chatContainer.children[2]?.constructor?.name, \"AssistantMessageComponent\", \"index 2: text run B\");\n\tassert.equal(host.chatContainer.children[3]?.constructor?.name, \"ToolExecutionComponent\", \"index 3: tool T2\");\n\tassert.equal(host.chatContainer.children[4]?.constructor?.name, \"AssistantMessageComponent\", \"index 4: text run C\");\n\n\t// Helper: collect the text of all Markdown children inside an AssistantMessageComponent.\n\t// Structure: AssistantMessageComponent (Container) -> contentContainer (children[0]) -> Markdown nodes.\n\tfunction getRenderedTexts(comp: any): string[] {\n\t\tconst contentContainer = comp.children?.[0];\n\t\tif (!contentContainer) return [];\n\t\treturn (contentContainer.children ?? [])\n\t\t\t.filter((c: any) => c.constructor?.name === \"Markdown\")\n\t\t\t.map((c: any) => (c as any).text as string);\n\t}\n\n\t// Each text-run component must contain only its own text — no cross-contamination after message_end\n\tassert.deepEqual(getRenderedTexts(host.chatContainer.children[0]), [\"A\"], \"text run A must contain only 'A'\");\n\tassert.deepEqual(getRenderedTexts(host.chatContainer.children[2]), [\"B\"], \"text run B must contain only 'B'\");\n\tassert.deepEqual(getRenderedTexts(host.chatContainer.children[4]), [\"C\"], \"text run C must contain only 'C'\");\n});\n\ntest(\"chat-controller does not duplicate text when content is [text, tool, text] (interleaved stream)\", async () => {\n\t(globalThis as any)[Symbol.for(\"@gsd/pi-coding-agent:theme\")] = {\n\t\tfg: (_key: string, text: string) => text,\n\t\tbg: (_key: string, text: string) => text,\n\t\tbold: (text: string) => text,\n\t\titalic: (text: string) => text,\n\t\ttruncate: (text: string) => text,\n\t};\n\n\tconst host = createHost();\n\thost.getMarkdownThemeWithSettings = () => ({});\n\n\tconst t1 = { type: \"toolCall\", id: \"t1\", name: \"tool_one\", arguments: {} };\n\n\tawait handleAgentEvent(host, { type: \"message_start\", message: makeAssistant([]) } as any);\n\n\t// Step 1: text \"A\" at index 0\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"text_delta\",\n\t\t\tcontentIndex: 0,\n\t\t\tdelta: \"A\",\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }]),\n\t\t},\n\t} as any);\n\n\t// Step 2: toolCall at index 1\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant([{ type: \"text\", text: \"A\" }, t1]),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"toolcall_end\",\n\t\t\tcontentIndex: 1,\n\t\t\ttoolCall: {\n\t\t\t\t...t1,\n\t\t\t\texternalResult: { content: [{ type: \"text\", text: \"result1\" }], details: {}, isError: false },\n\t\t\t},\n\t\t\tpartial: makeAssistant([{ type: \"text\", text: \"A\" }, t1]),\n\t\t},\n\t} as any);\n\n\t// Step 3: text \"B\" at index 2\n\tconst finalContent = [{ type: \"text\", text: \"A\" }, t1, { type: \"text\", text: \"B\" }];\n\tawait handleAgentEvent(host, {\n\t\ttype: \"message_update\",\n\t\tmessage: makeAssistant(finalContent),\n\t\tassistantMessageEvent: {\n\t\t\ttype: \"text_delta\",\n\t\t\tcontentIndex: 2,\n\t\t\tdelta: \"B\",\n\t\t\tpartial: makeAssistant(finalContent),\n\t\t},\n\t} as any);\n\n\tassert.equal(host.chatContainer.children.length, 3);\n\tassert.equal(host.chatContainer.children[0]?.constructor?.name, \"AssistantMessageComponent\");\n\tassert.equal(host.chatContainer.children[1]?.constructor?.name, \"ToolExecutionComponent\");\n\tassert.equal(host.chatContainer.children[2]?.constructor?.name, \"AssistantMessageComponent\");\n\n\tconst firstText = host.chatContainer.children[0];\n\tconst secondText = host.chatContainer.children[2];\n\tassert.notEqual(firstText, secondText, \"text-before-tool and text-after-tool must be separate component instances\");\n\tassert.deepEqual((firstText as any).range, { startIndex: 0, endIndex: 0 }, \"first text-run covers only content[0]\");\n\tassert.deepEqual((secondText as any).range, { startIndex: 2, endIndex: 2 }, \"second text-run covers only content[2]\");\n\n\t// Finalize — regression guard: range must NOT be cleared on message_end (would cause duplication)\n\tawait handleAgentEvent(host, { type: \"message_end\", message: makeAssistant(finalContent) } as any);\n\n\tassert.deepEqual((secondText as any).range, { startIndex: 2, endIndex: 2 }, \"range must not be cleared on message_end (would cause duplication)\");\n});\n"]}
@@ -179,7 +179,7 @@ export function serializeConversation(messages) {
179
179
  .map((c) => c.text)
180
180
  .join("");
181
181
  if (content)
182
- parts.push(`[User]: ${content}`);
182
+ parts.push(`**User said:** ${content}`);
183
183
  }
184
184
  else if (msg.role === "assistant") {
185
185
  const textParts = [];
@@ -201,13 +201,13 @@ export function serializeConversation(messages) {
201
201
  }
202
202
  }
203
203
  if (thinkingParts.length > 0) {
204
- parts.push(`[Assistant thinking]: ${thinkingParts.join("\n")}`);
204
+ parts.push(`**Assistant thinking:** ${thinkingParts.join("\n")}`);
205
205
  }
206
206
  if (textParts.length > 0) {
207
- parts.push(`[Assistant]: ${textParts.join("\n")}`);
207
+ parts.push(`**Assistant responded:** ${textParts.join("\n")}`);
208
208
  }
209
209
  if (toolCalls.length > 0) {
210
- parts.push(`[Assistant tool calls]: ${toolCalls.join("; ")}`);
210
+ parts.push(`**Assistant tool calls:** ${toolCalls.join("; ")}`);
211
211
  }
212
212
  }
213
213
  else if (msg.role === "toolResult") {
@@ -216,7 +216,7 @@ export function serializeConversation(messages) {
216
216
  .map((c) => c.text)
217
217
  .join("");
218
218
  if (content) {
219
- parts.push(`[Tool result]: ${truncateForSummary(content, TOOL_RESULT_MAX_CHARS)}`);
219
+ parts.push(`**Tool result:** ${truncateForSummary(content, TOOL_RESULT_MAX_CHARS)}`);
220
220
  }
221
221
  }
222
222
  }