lsd-pi 1.3.7 → 1.3.10

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 (92) hide show
  1. package/README.md +82 -0
  2. package/dist/resources/extensions/mcp-client/index.js +230 -54
  3. package/dist/resources/extensions/mcp-client/mcp-manager-component.js +220 -0
  4. package/dist/resources/extensions/slash-commands/plan.js +72 -18
  5. package/dist/resources/extensions/subagent/agents.js +7 -0
  6. package/dist/resources/extensions/subagent/index.js +25 -8
  7. package/dist/resources/extensions/subagent/model-resolution.js +1 -0
  8. package/dist/resources/extensions/usage/index.js +34 -2
  9. package/dist/resources/extensions/voice/index.js +1 -0
  10. package/dist/resources/extensions/voice/push-to-talk.js +2 -0
  11. package/package.json +1 -1
  12. package/packages/pi-coding-agent/dist/core/agent-session.context-usage.test.d.ts +2 -0
  13. package/packages/pi-coding-agent/dist/core/agent-session.context-usage.test.d.ts.map +1 -0
  14. package/packages/pi-coding-agent/dist/core/agent-session.context-usage.test.js +72 -0
  15. package/packages/pi-coding-agent/dist/core/agent-session.context-usage.test.js.map +1 -0
  16. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +4 -0
  17. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  18. package/packages/pi-coding-agent/dist/core/agent-session.js +29 -2
  19. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  20. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  21. package/packages/pi-coding-agent/dist/core/extensions/runner.js +1 -0
  22. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  23. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  24. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  25. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  26. package/packages/pi-coding-agent/dist/core/tool-priority.js +1 -1
  27. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -1
  28. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  29. package/packages/pi-coding-agent/dist/main.js +1 -0
  30. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  31. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +104 -2
  32. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -1
  33. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +39 -2
  34. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  35. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +135 -18
  36. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  37. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  38. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  39. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -1
  40. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  41. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +21 -2
  42. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -1
  43. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +147 -9
  44. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -1
  45. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +51 -13
  46. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -1
  47. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  48. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +112 -18
  49. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  50. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
  51. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
  52. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  53. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
  54. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  55. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  56. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  57. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  58. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +34 -4
  59. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  60. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  61. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +3 -0
  62. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  63. package/packages/pi-coding-agent/package.json +1 -1
  64. package/packages/pi-coding-agent/src/core/agent-session.context-usage.test.ts +87 -0
  65. package/packages/pi-coding-agent/src/core/agent-session.ts +40 -2
  66. package/packages/pi-coding-agent/src/core/extensions/runner.ts +1 -0
  67. package/packages/pi-coding-agent/src/core/extensions/types.ts +3 -0
  68. package/packages/pi-coding-agent/src/core/tool-priority.ts +1 -1
  69. package/packages/pi-coding-agent/src/main.ts +1 -0
  70. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +129 -2
  71. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +158 -18
  72. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -1
  73. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +164 -10
  74. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +60 -13
  75. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +123 -20
  76. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
  77. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
  78. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +34 -4
  79. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +4 -0
  80. package/pkg/package.json +1 -1
  81. package/src/resources/extensions/mcp-client/index.ts +259 -58
  82. package/src/resources/extensions/mcp-client/mcp-manager-component.ts +256 -0
  83. package/src/resources/extensions/mcp-client/tests/mcp-manager-component.test.ts +141 -0
  84. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +32 -0
  85. package/src/resources/extensions/slash-commands/plan.ts +76 -19
  86. package/src/resources/extensions/subagent/agents.ts +9 -0
  87. package/src/resources/extensions/subagent/index.ts +30 -8
  88. package/src/resources/extensions/subagent/model-resolution.ts +1 -0
  89. package/src/resources/extensions/usage/index.ts +40 -2
  90. package/src/resources/extensions/voice/index.ts +1 -0
  91. package/src/resources/extensions/voice/push-to-talk.ts +3 -0
  92. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +6 -0
@@ -1,7 +1,7 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import stripAnsi from "strip-ansi";
4
- import { ToolSummaryLine } from "../tool-summary-line.js";
4
+ import { ToolSummaryLine, extractToolLabel } from "../tool-summary-line.js";
5
5
  import { initTheme } from "../../theme/theme.js";
6
6
  initTheme("dark");
7
7
  describe("ToolSummaryLine", () => {
@@ -13,7 +13,8 @@ describe("ToolSummaryLine", () => {
13
13
  assert.match(rendered, /^ ● /);
14
14
  assert.ok(rendered.includes("reading 2 files · 0.8s"));
15
15
  assert.equal(summary.canGroupWith("read"), true);
16
- assert.equal(summary.canGroupWith("find"), false);
16
+ assert.equal(summary.canGroupWith("find"), true);
17
+ assert.equal(summary.canGroupWith("bash"), false);
17
18
  assert.equal(rendered.includes("collapsed tools"), false);
18
19
  assert.equal(rendered.includes("⎯"), false);
19
20
  });
@@ -30,5 +31,106 @@ describe("ToolSummaryLine", () => {
30
31
  summary.setHidden(true);
31
32
  assert.deepEqual(summary.render(80), []);
32
33
  });
34
+ it("renders spinner and label line when pending tools exist", () => {
35
+ const summary = new ToolSummaryLine();
36
+ summary.addPendingTool("t1", "read", { path: "src/foo/bar.ts" });
37
+ const rendered = stripAnsi(summary.render(160).join("\n"));
38
+ assert.ok(!rendered.startsWith(" ●"));
39
+ assert.ok(rendered.includes(" └ bar.ts"));
40
+ assert.ok(rendered.includes("bar.ts"));
41
+ });
42
+ it("keeps last tool label after pending tool completes", () => {
43
+ const summary = new ToolSummaryLine();
44
+ summary.addPendingTool("t1", "read", { path: "file.ts" });
45
+ assert.equal(summary.hasPendingTools(), true);
46
+ summary.removePendingTool("t1");
47
+ summary.addTool("read", 500);
48
+ assert.equal(summary.hasPendingTools(), false);
49
+ const rendered = stripAnsi(summary.render(160).join("\n"));
50
+ assert.ok(rendered.includes("●"));
51
+ assert.ok(rendered.includes("0.5s"));
52
+ assert.ok(rendered.includes("└ file.ts"));
53
+ });
54
+ it("aggregates completed and pending tools in summary text", () => {
55
+ const summary = new ToolSummaryLine();
56
+ summary.addTool("read", 300);
57
+ summary.addPendingTool("t1", "grep", { pattern: "TODO" });
58
+ const rendered = stripAnsi(summary.render(160).join("\n"));
59
+ assert.ok(rendered.includes("1 file"));
60
+ assert.ok(rendered.includes("1 pattern"));
61
+ assert.ok(rendered.includes("…"));
62
+ assert.ok(rendered.includes("TODO"));
63
+ });
64
+ it("shows expand hint when set and tools are pending", () => {
65
+ const summary = new ToolSummaryLine();
66
+ summary.setExpandHint("(ctrl+o to expand)");
67
+ summary.addPendingTool("t1", "read", { path: "test.ts" });
68
+ const rendered = stripAnsi(summary.render(160).join("\n"));
69
+ assert.ok(rendered.includes("ctrl+o to expand"));
70
+ });
71
+ it("does not show expand hint when no pending tools", () => {
72
+ const summary = new ToolSummaryLine();
73
+ summary.setExpandHint("(ctrl+o to expand)");
74
+ summary.addTool("read", 300);
75
+ const rendered = stripAnsi(summary.render(160).join("\n"));
76
+ assert.ok(!rendered.includes("ctrl+o to expand"));
77
+ });
78
+ it("updates label when pending tool args change", () => {
79
+ const summary = new ToolSummaryLine();
80
+ summary.addPendingTool("t1", "read", { path: "old.ts" });
81
+ let rendered = stripAnsi(summary.render(160).join("\n"));
82
+ assert.ok(rendered.includes("old.ts"));
83
+ summary.updatePendingToolArgs("t1", { path: "new.ts" });
84
+ rendered = stripAnsi(summary.render(160).join("\n"));
85
+ assert.ok(rendered.includes("new.ts"));
86
+ });
87
+ it("clears pending spinner without removing last label", () => {
88
+ const summary = new ToolSummaryLine();
89
+ summary.addPendingTool("t1", "read", { path: "file.ts" });
90
+ summary.clearPendingTools();
91
+ const rendered = stripAnsi(summary.render(160).join("\n"));
92
+ assert.ok(!rendered.includes("◯"));
93
+ assert.ok(!rendered.includes("◔"));
94
+ assert.ok(!rendered.includes("◑"));
95
+ assert.ok(!rendered.includes("◕"));
96
+ assert.ok(!rendered.includes("● Listing"));
97
+ assert.ok(!rendered.includes("…"));
98
+ assert.ok(rendered === "" || rendered.includes("file.ts"));
99
+ });
100
+ it("canGroupWith considers pending tools", () => {
101
+ const summary = new ToolSummaryLine();
102
+ summary.addPendingTool("t1", "read", { path: "a.ts" });
103
+ assert.equal(summary.canGroupWith("read"), true);
104
+ assert.equal(summary.canGroupWith("grep"), true);
105
+ assert.equal(summary.canGroupWith("bash"), false);
106
+ });
107
+ });
108
+ describe("extractToolLabel", () => {
109
+ it("extracts basename for read tool", () => {
110
+ assert.equal(extractToolLabel("read", { path: "src/foo/bar.ts" }), "bar.ts");
111
+ assert.equal(extractToolLabel("read", { file_path: "/abs/path/file.json" }), "file.json");
112
+ assert.equal(extractToolLabel("read", {}), "read");
113
+ });
114
+ it("extracts pattern for grep tool", () => {
115
+ assert.equal(extractToolLabel("grep", { pattern: "TODO" }), "TODO");
116
+ assert.equal(extractToolLabel("grep", {}), "grep");
117
+ });
118
+ it("extracts pattern for find tool", () => {
119
+ assert.equal(extractToolLabel("find", { pattern: "*.ts" }), "*.ts");
120
+ assert.equal(extractToolLabel("find", {}), "find");
121
+ });
122
+ it("extracts path for ls tool", () => {
123
+ assert.equal(extractToolLabel("ls", { path: "src/components" }), "components");
124
+ assert.equal(extractToolLabel("ls", {}), ".");
125
+ });
126
+ it("extracts symbol or file for lsp tool", () => {
127
+ assert.equal(extractToolLabel("lsp", { symbol: "MyClass" }), "MyClass");
128
+ assert.equal(extractToolLabel("lsp", { file: "src/index.ts" }), "index.ts");
129
+ assert.equal(extractToolLabel("lsp", { symbol: "foo", file: "bar.ts" }), "foo");
130
+ assert.equal(extractToolLabel("lsp", {}), "lsp");
131
+ });
132
+ it("returns tool name for unknown tools", () => {
133
+ assert.equal(extractToolLabel("custom_tool", { whatever: "value" }), "custom_tool");
134
+ });
33
135
  });
34
136
  //# sourceMappingURL=tool-summary-line.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-summary-line.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-summary-line.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,SAAS,CAAC,MAAM,CAAC,CAAC;AAElB,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE7B,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAClD,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAEpC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC/C,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAEzC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport stripAnsi from \"strip-ansi\";\n\nimport { ToolSummaryLine } from \"../tool-summary-line.js\";\nimport { initTheme } from \"../../theme/theme.js\";\n\ninitTheme(\"dark\");\n\ndescribe(\"ToolSummaryLine\", () => {\n\tit(\"renders action-based summaries for grouped identical tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 600);\n\t\tsummary.addTool(\"read\", 150);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.match(rendered, /^ ● /);\n\t\tassert.ok(rendered.includes(\"reading 2 files · 0.8s\"));\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"find\"), false);\n\t\tassert.equal(rendered.includes(\"collapsed tools\"), false);\n\t\tassert.equal(rendered.includes(\"⎯\"), false);\n\t});\n\n\tit(\"keeps fallback format for unknown tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"custom_tool\", 100);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"custom_tool · 0.1s\"));\n\t});\n\n\tit(\"renders nothing when empty or hidden\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tassert.deepEqual(summary.render(80), []);\n\n\t\tsummary.addTool(\"grep\", 100);\n\t\tsummary.setHidden(true);\n\t\tassert.deepEqual(summary.render(80), []);\n\t});\n});\n"]}
1
+ {"version":3,"file":"tool-summary-line.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-summary-line.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport stripAnsi from \"strip-ansi\";\n\nimport { ToolSummaryLine, extractToolLabel } from \"../tool-summary-line.js\";\nimport { initTheme } from \"../../theme/theme.js\";\n\ninitTheme(\"dark\");\n\ndescribe(\"ToolSummaryLine\", () => {\n\tit(\"renders action-based summaries for grouped identical tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 600);\n\t\tsummary.addTool(\"read\", 150);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.match(rendered, /^ ● /);\n\t\tassert.ok(rendered.includes(\"reading 2 files · 0.8s\"));\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"find\"), true);\n\t\tassert.equal(summary.canGroupWith(\"bash\"), false);\n\t\tassert.equal(rendered.includes(\"collapsed tools\"), false);\n\t\tassert.equal(rendered.includes(\"⎯\"), false);\n\t});\n\n\tit(\"keeps fallback format for unknown tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"custom_tool\", 100);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"custom_tool · 0.1s\"));\n\t});\n\n\tit(\"renders nothing when empty or hidden\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tassert.deepEqual(summary.render(80), []);\n\n\t\tsummary.addTool(\"grep\", 100);\n\t\tsummary.setHidden(true);\n\t\tassert.deepEqual(summary.render(80), []);\n\t});\n\n\tit(\"renders spinner and label line when pending tools exist\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"src/foo/bar.ts\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.startsWith(\" ●\"));\n\t\tassert.ok(rendered.includes(\" └ bar.ts\"));\n\t\tassert.ok(rendered.includes(\"bar.ts\"));\n\t});\n\n\tit(\"keeps last tool label after pending tool completes\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"file.ts\" });\n\t\tassert.equal(summary.hasPendingTools(), true);\n\n\t\tsummary.removePendingTool(\"t1\");\n\t\tsummary.addTool(\"read\", 500);\n\t\tassert.equal(summary.hasPendingTools(), false);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"●\"));\n\t\tassert.ok(rendered.includes(\"0.5s\"));\n\t\tassert.ok(rendered.includes(\"└ file.ts\"));\n\t});\n\n\tit(\"aggregates completed and pending tools in summary text\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 300);\n\t\tsummary.addPendingTool(\"t1\", \"grep\", { pattern: \"TODO\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"1 file\"));\n\t\tassert.ok(rendered.includes(\"1 pattern\"));\n\t\tassert.ok(rendered.includes(\"…\"));\n\t\tassert.ok(rendered.includes(\"TODO\"));\n\t});\n\n\tit(\"shows expand hint when set and tools are pending\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.setExpandHint(\"(ctrl+o to expand)\");\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"test.ts\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"ctrl+o to expand\"));\n\t});\n\n\tit(\"does not show expand hint when no pending tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.setExpandHint(\"(ctrl+o to expand)\");\n\t\tsummary.addTool(\"read\", 300);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.includes(\"ctrl+o to expand\"));\n\t});\n\n\tit(\"updates label when pending tool args change\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"old.ts\" });\n\n\t\tlet rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"old.ts\"));\n\n\t\tsummary.updatePendingToolArgs(\"t1\", { path: \"new.ts\" });\n\t\trendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"new.ts\"));\n\t});\n\n\tit(\"clears pending spinner without removing last label\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"file.ts\" });\n\n\t\tsummary.clearPendingTools();\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.includes(\"◯\"));\n\t\tassert.ok(!rendered.includes(\"◔\"));\n\t\tassert.ok(!rendered.includes(\"◑\"));\n\t\tassert.ok(!rendered.includes(\"◕\"));\n\t\tassert.ok(!rendered.includes(\"● Listing\"));\n\t\tassert.ok(!rendered.includes(\"…\"));\n\t\tassert.ok(rendered === \"\" || rendered.includes(\"file.ts\"));\n\t});\n\n\tit(\"canGroupWith considers pending tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"a.ts\" });\n\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"grep\"), true);\n\t\tassert.equal(summary.canGroupWith(\"bash\"), false);\n\t});\n});\n\ndescribe(\"extractToolLabel\", () => {\n\tit(\"extracts basename for read tool\", () => {\n\t\tassert.equal(extractToolLabel(\"read\", { path: \"src/foo/bar.ts\" }), \"bar.ts\");\n\t\tassert.equal(extractToolLabel(\"read\", { file_path: \"/abs/path/file.json\" }), \"file.json\");\n\t\tassert.equal(extractToolLabel(\"read\", {}), \"read\");\n\t});\n\n\tit(\"extracts pattern for grep tool\", () => {\n\t\tassert.equal(extractToolLabel(\"grep\", { pattern: \"TODO\" }), \"TODO\");\n\t\tassert.equal(extractToolLabel(\"grep\", {}), \"grep\");\n\t});\n\n\tit(\"extracts pattern for find tool\", () => {\n\t\tassert.equal(extractToolLabel(\"find\", { pattern: \"*.ts\" }), \"*.ts\");\n\t\tassert.equal(extractToolLabel(\"find\", {}), \"find\");\n\t});\n\n\tit(\"extracts path for ls tool\", () => {\n\t\tassert.equal(extractToolLabel(\"ls\", { path: \"src/components\" }), \"components\");\n\t\tassert.equal(extractToolLabel(\"ls\", {}), \".\");\n\t});\n\n\tit(\"extracts symbol or file for lsp tool\", () => {\n\t\tassert.equal(extractToolLabel(\"lsp\", { symbol: \"MyClass\" }), \"MyClass\");\n\t\tassert.equal(extractToolLabel(\"lsp\", { file: \"src/index.ts\" }), \"index.ts\");\n\t\tassert.equal(extractToolLabel(\"lsp\", { symbol: \"foo\", file: \"bar.ts\" }), \"foo\");\n\t\tassert.equal(extractToolLabel(\"lsp\", {}), \"lsp\");\n\t});\n\n\tit(\"returns tool name for unknown tools\", () => {\n\t\tassert.equal(extractToolLabel(\"custom_tool\", { whatever: \"value\" }), \"custom_tool\");\n\t});\n});\n"]}
@@ -1,8 +1,30 @@
1
1
  import type { AssistantMessage } from "@gsd/pi-ai";
2
- import { Container, type MarkdownTheme } from "@gsd/pi-tui";
2
+ import type { Component } from "@gsd/pi-tui";
3
+ import { Container, Markdown, type MarkdownTheme, Text } from "@gsd/pi-tui";
3
4
  import { type TimestampFormat } from "./timestamp.js";
4
5
  /**
5
- * Component that renders a complete assistant message
6
+ * Create a Markdown component for an assistant text block.
7
+ * @param text - Text content (should be trimmed by caller)
8
+ * @param withMarker - Whether to prefix with the response marker
9
+ * @param markdownTheme - Markdown theme
10
+ */
11
+ export declare function createTextMarkdown(text: string, withMarker: boolean, markdownTheme: MarkdownTheme): Markdown;
12
+ /**
13
+ * Create a Markdown component for a thinking block.
14
+ */
15
+ export declare function createThinkingMarkdown(thinking: string, markdownTheme: MarkdownTheme): Markdown;
16
+ /**
17
+ * Create an error/abort Text component.
18
+ */
19
+ export declare function createErrorText(message: string): Text;
20
+ /**
21
+ * Component that renders a complete assistant message.
22
+ *
23
+ * Supports two rendering modes:
24
+ * 1. Legacy: `updateContent(message)` renders all text/thinking into a contentContainer.
25
+ * Tool rows are expected to be added as siblings in the parent container.
26
+ * 2. Interleaved: `updateContentOrdered(message, toolComponents)` renders text/thinking
27
+ * AND tool components in content order. Tool components become children of this container.
6
28
  */
7
29
  export declare class AssistantMessageComponent extends Container {
8
30
  private contentContainer;
@@ -15,6 +37,21 @@ export declare class AssistantMessageComponent extends Container {
15
37
  invalidate(): void;
16
38
  setHideThinkingBlock(hide: boolean): void;
17
39
  setThinkingLevel(level: string): void;
40
+ /**
41
+ * Legacy rendering: renders text/thinking blocks into contentContainer.
42
+ * Stops rendering at the first tool-type block (toolCall/serverToolUse).
43
+ * Post-tool text blocks are handled by the chat-controller to preserve
44
+ * content ordering relative to tool rows.
45
+ */
18
46
  updateContent(message: AssistantMessage): void;
47
+ /**
48
+ * Interleaved rendering: renders text/thinking AND tool components in content order.
49
+ * Tool components become children of this container, preserving visual ordering.
50
+ *
51
+ * @param message - The assistant message
52
+ * @param toolComponents - Map of content block ID → pre-created Component (ToolExecutionComponent etc.)
53
+ * @returns Map of content block ID → the tool Component that was placed (for pending tool tracking)
54
+ */
55
+ updateContentOrdered(message: AssistantMessage, toolComponents?: Map<string, Component>): Map<string, Component>;
19
56
  }
20
57
  //# sourceMappingURL=assistant-message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,aAAa,CAAC;AAEpF,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,eAAe,CAAkB;gBAGxC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,eAAe,GAAE,eAAiC,EAClD,aAAa,SAAQ;IAkBb,UAAU,IAAI,IAAI;IAO3B,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIzC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;CAoF9C"}
1
+ {"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAU,IAAI,EAAE,MAAM,aAAa,CAAC;AAEpF,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAUvE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,OAAO,EACnB,aAAa,EAAE,aAAa,GAC1B,QAAQ,CAGV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,GAC1B,QAAQ,CAKV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAErD;AAED;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,eAAe,CAAkB;gBAGxC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,eAAe,GAAE,eAAiC,EAClD,aAAa,SAAQ;IAkBb,UAAU,IAAI,IAAI;IAO3B,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIzC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;;OAKG;IACH,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA8E9C;;;;;;;OAOG;IACH,oBAAoB,CACnB,OAAO,EAAE,gBAAgB,EACzB,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GACrC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;CA+EzB"}
@@ -1,7 +1,45 @@
1
1
  import { Container, Markdown, Spacer, Text } from "@gsd/pi-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
3
  /**
4
- * Component that renders a complete assistant message
4
+ * Create the response marker prefixed to the first visible text block.
5
+ * Lazy to avoid calling theme.fg() at module load time (fails in tests).
6
+ */
7
+ function getResponseMarker() {
8
+ return `${theme.fg("accent", "●")} `;
9
+ }
10
+ /**
11
+ * Create a Markdown component for an assistant text block.
12
+ * @param text - Text content (should be trimmed by caller)
13
+ * @param withMarker - Whether to prefix with the response marker
14
+ * @param markdownTheme - Markdown theme
15
+ */
16
+ export function createTextMarkdown(text, withMarker, markdownTheme) {
17
+ const withMarker_ = withMarker ? `${getResponseMarker()}${text}` : text;
18
+ return new Markdown(withMarker_, 1, 0, markdownTheme);
19
+ }
20
+ /**
21
+ * Create a Markdown component for a thinking block.
22
+ */
23
+ export function createThinkingMarkdown(thinking, markdownTheme) {
24
+ return new Markdown(thinking.trim(), 1, 0, markdownTheme, {
25
+ color: (text) => theme.fg("thinkingText", text),
26
+ italic: true,
27
+ });
28
+ }
29
+ /**
30
+ * Create an error/abort Text component.
31
+ */
32
+ export function createErrorText(message) {
33
+ return new Text(theme.fg("error", message), 1, 0);
34
+ }
35
+ /**
36
+ * Component that renders a complete assistant message.
37
+ *
38
+ * Supports two rendering modes:
39
+ * 1. Legacy: `updateContent(message)` renders all text/thinking into a contentContainer.
40
+ * Tool rows are expected to be added as siblings in the parent container.
41
+ * 2. Interleaved: `updateContentOrdered(message, toolComponents)` renders text/thinking
42
+ * AND tool components in content order. Tool components become children of this container.
5
43
  */
6
44
  export class AssistantMessageComponent extends Container {
7
45
  constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme(), timestampFormat = "date-time-iso", thinkingLevel = "off") {
@@ -29,6 +67,12 @@ export class AssistantMessageComponent extends Container {
29
67
  setThinkingLevel(level) {
30
68
  this.thinkingLevel = level;
31
69
  }
70
+ /**
71
+ * Legacy rendering: renders text/thinking blocks into contentContainer.
72
+ * Stops rendering at the first tool-type block (toolCall/serverToolUse).
73
+ * Post-tool text blocks are handled by the chat-controller to preserve
74
+ * content ordering relative to tool rows.
75
+ */
32
76
  updateContent(message) {
33
77
  this.lastMessage = message;
34
78
  // Clear content container
@@ -43,26 +87,26 @@ export class AssistantMessageComponent extends Container {
43
87
  if (hasVisibleContent) {
44
88
  this.contentContainer.addChild(new Spacer(1));
45
89
  }
46
- // Render content in order
90
+ // Render content blocks up to (but not including) the first tool block.
91
+ // Text blocks after tools are rendered by the chat-controller as separate
92
+ // components to maintain correct visual ordering with tool rows.
47
93
  let markerAdded = false;
48
- const responseMarker = `${theme.fg("accent", "●")} `;
49
94
  for (let i = 0; i < message.content.length; i++) {
50
95
  const content = message.content[i];
96
+ // Stop at the first tool-type block — post-tool content is handled externally
97
+ if (content.type === "toolCall" || content.type === "serverToolUse") {
98
+ break;
99
+ }
51
100
  if (content.type === "text" && content.text.trim()) {
52
- // Assistant text messages with no background - trim the text
53
- // Set paddingY=0 to avoid extra spacing before tool executions
54
101
  const text = content.text.trim();
55
- const withMarker = markerAdded ? text : `${responseMarker}${text}`;
102
+ const withMarker = markerAdded ? text : `${getResponseMarker()}${text}`;
56
103
  this.contentContainer.addChild(new Markdown(withMarker, 1, 0, this.markdownTheme));
57
104
  markerAdded = true;
58
105
  }
59
106
  else if (content.type === "thinking" && content.thinking.trim()) {
60
107
  if (this.hideThinkingBlock) {
61
- // Hide thinking content entirely when hide-thinking is enabled.
62
108
  continue;
63
109
  }
64
- // Add spacing only when another visible assistant content block follows.
65
- // This avoids a superfluous blank line before separately-rendered tool execution blocks.
66
110
  const hasVisibleContentAfter = message.content.slice(i + 1).some((c) => {
67
111
  if (c.type === "text")
68
112
  return Boolean(c.text.trim());
@@ -70,7 +114,6 @@ export class AssistantMessageComponent extends Container {
70
114
  return !this.hideThinkingBlock && Boolean(c.thinking.trim());
71
115
  return false;
72
116
  });
73
- // Thinking traces in thinkingText color, italic
74
117
  this.contentContainer.addChild(new Markdown(content.thinking.trim(), 1, 0, this.markdownTheme, {
75
118
  color: (text) => theme.fg("thinkingText", text),
76
119
  italic: true,
@@ -81,8 +124,7 @@ export class AssistantMessageComponent extends Container {
81
124
  }
82
125
  }
83
126
  // Check if aborted - show after partial content
84
- // But only if there are no tool calls (tool execution components will show the error)
85
- const hasToolCalls = message.content.some((c) => c.type === "toolCall");
127
+ const hasToolCalls = message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
86
128
  if (!hasToolCalls) {
87
129
  if (message.stopReason === "aborted") {
88
130
  const abortMessage = message.errorMessage && message.errorMessage !== "Request was aborted"
@@ -102,12 +144,87 @@ export class AssistantMessageComponent extends Container {
102
144
  this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
103
145
  }
104
146
  }
105
- // Timestamp display removed
106
- // Show timestamp when the message is complete (has a stop reason)
107
- // if (message.stopReason && message.timestamp) {
108
- // const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
109
- // this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
110
- // }
147
+ }
148
+ /**
149
+ * Interleaved rendering: renders text/thinking AND tool components in content order.
150
+ * Tool components become children of this container, preserving visual ordering.
151
+ *
152
+ * @param message - The assistant message
153
+ * @param toolComponents - Map of content block ID → pre-created Component (ToolExecutionComponent etc.)
154
+ * @returns Map of content block ID → the tool Component that was placed (for pending tool tracking)
155
+ */
156
+ updateContentOrdered(message, toolComponents) {
157
+ this.lastMessage = message;
158
+ // Clear contentContainer so we can re-render all blocks in order
159
+ this.contentContainer.clear();
160
+ const placedTools = new Map();
161
+ // Check if there's any visible content at all
162
+ const hasVisibleContent = message.content.some((c) => {
163
+ if (c.type === "text")
164
+ return Boolean(c.text.trim());
165
+ if (c.type === "thinking")
166
+ return !this.hideThinkingBlock && Boolean(c.thinking.trim());
167
+ return false;
168
+ });
169
+ if (hasVisibleContent) {
170
+ this.contentContainer.addChild(new Spacer(1));
171
+ }
172
+ // Render all content blocks in order
173
+ let markerAdded = false;
174
+ for (let i = 0; i < message.content.length; i++) {
175
+ const block = message.content[i];
176
+ if (block.type === "text" && block.text.trim()) {
177
+ const text = block.text.trim();
178
+ const withMarker = markerAdded ? text : `${getResponseMarker()}${text}`;
179
+ this.contentContainer.addChild(new Markdown(withMarker, 1, 0, this.markdownTheme));
180
+ markerAdded = true;
181
+ }
182
+ else if (block.type === "thinking" && block.thinking.trim()) {
183
+ if (this.hideThinkingBlock) {
184
+ continue;
185
+ }
186
+ // Add spacing only when another visible assistant content block follows.
187
+ const hasVisibleContentAfter = message.content.slice(i + 1).some((c) => {
188
+ if (c.type === "text")
189
+ return Boolean(c.text.trim());
190
+ if (c.type === "thinking")
191
+ return !this.hideThinkingBlock && Boolean(c.thinking.trim());
192
+ return false;
193
+ });
194
+ this.contentContainer.addChild(new Markdown(block.thinking.trim(), 1, 0, this.markdownTheme, {
195
+ color: (text) => theme.fg("thinkingText", text),
196
+ italic: true,
197
+ }));
198
+ if (hasVisibleContentAfter) {
199
+ this.contentContainer.addChild(new Spacer(1));
200
+ }
201
+ }
202
+ else if ((block.type === "toolCall" || block.type === "serverToolUse") && toolComponents?.has(block.id)) {
203
+ // Place the pre-created tool component in content order
204
+ const toolComponent = toolComponents.get(block.id);
205
+ this.contentContainer.addChild(toolComponent);
206
+ placedTools.set(block.id, toolComponent);
207
+ }
208
+ // webSearchResult blocks don't produce their own component;
209
+ // they update the matching serverToolUse component via updateResult()
210
+ }
211
+ // Handle abort/error after content (only if no tool calls)
212
+ const hasToolCalls = message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
213
+ if (!hasToolCalls) {
214
+ if (message.stopReason === "aborted") {
215
+ const abortMessage = message.errorMessage && message.errorMessage !== "Request was aborted"
216
+ ? message.errorMessage
217
+ : "Operation aborted";
218
+ this.contentContainer.addChild(new Spacer(1));
219
+ this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
220
+ }
221
+ else if (message.stopReason === "error") {
222
+ const errorMsg = message.errorMessage || "Unknown error";
223
+ this.contentContainer.addChild(new Spacer(1));
224
+ this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
225
+ }
226
+ }
227
+ return placedTools;
111
228
  }
112
229
  }
113
230
  //# sourceMappingURL=assistant-message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@gsd/pi-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@gsd/pi-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\nimport { formatTimestamp, type TimestampFormat } from \"./timestamp.js\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate thinkingLevel: string;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate timestampFormat: TimestampFormat;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\ttimestampFormat: TimestampFormat = \"date-time-iso\",\n\t\tthinkingLevel = \"off\",\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingLevel = thinkingLevel;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.timestampFormat = timestampFormat;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t}\n\n\tsetThinkingLevel(level: string): void {\n\t\tthis.thinkingLevel = level;\n\t}\n\n\tupdateContent(message: AssistantMessage): void {\n\t\tthis.lastMessage = message;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasVisibleContent = message.content.some((c) => {\n\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\treturn false;\n\t\t});\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet markerAdded = false;\n\t\tconst responseMarker = `${theme.fg(\"accent\", \"●\")} `;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\tconst text = content.text.trim();\n\t\t\t\tconst withMarker = markerAdded ? text : `${responseMarker}${text}`;\n\t\t\t\tthis.contentContainer.addChild(new Markdown(withMarker, 1, 0, this.markdownTheme));\n\t\t\t\tmarkerAdded = true;\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tif (this.hideThinkingBlock) {\n\t\t\t\t\t// Hide thinking content entirely when hide-thinking is enabled.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content.slice(i + 1).some((c) => {\n\t\t\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\t// Thinking traces in thinkingText color, italic\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.thinking.trim(), 1, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if aborted - show after partial content\n\t\t// But only if there are no tool calls (tool execution components will show the error)\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tif (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tif (hasVisibleContent) {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t} else {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t}\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), 1, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), 1, 0));\n\t\t\t}\n\t\t}\n\n\t\t// Timestamp display removed\n\t\t// Show timestamp when the message is complete (has a stop reason)\n\t\t// if (message.stopReason && message.timestamp) {\n\t\t// \tconst timeStr = formatTimestamp(message.timestamp, this.timestampFormat);\n\t\t// \tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", timeStr), 1, 0));\n\t\t// }\n\t}\n}\n"]}
1
+ {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@gsd/pi-ai\";\nimport type { Component } from \"@gsd/pi-tui\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@gsd/pi-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\nimport { formatTimestamp, type TimestampFormat } from \"./timestamp.js\";\n\n/**\n * Create the response marker prefixed to the first visible text block.\n * Lazy to avoid calling theme.fg() at module load time (fails in tests).\n */\nfunction getResponseMarker(): string {\n\treturn `${theme.fg(\"accent\", \"●\")} `;\n}\n\n/**\n * Create a Markdown component for an assistant text block.\n * @param text - Text content (should be trimmed by caller)\n * @param withMarker - Whether to prefix with the response marker\n * @param markdownTheme - Markdown theme\n */\nexport function createTextMarkdown(\n\ttext: string,\n\twithMarker: boolean,\n\tmarkdownTheme: MarkdownTheme,\n): Markdown {\n\tconst withMarker_ = withMarker ? `${getResponseMarker()}${text}` : text;\n\treturn new Markdown(withMarker_, 1, 0, markdownTheme);\n}\n\n/**\n * Create a Markdown component for a thinking block.\n */\nexport function createThinkingMarkdown(\n\tthinking: string,\n\tmarkdownTheme: MarkdownTheme,\n): Markdown {\n\treturn new Markdown(thinking.trim(), 1, 0, markdownTheme, {\n\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\titalic: true,\n\t});\n}\n\n/**\n * Create an error/abort Text component.\n */\nexport function createErrorText(message: string): Text {\n\treturn new Text(theme.fg(\"error\", message), 1, 0);\n}\n\n/**\n * Component that renders a complete assistant message.\n *\n * Supports two rendering modes:\n * 1. Legacy: `updateContent(message)` renders all text/thinking into a contentContainer.\n * Tool rows are expected to be added as siblings in the parent container.\n * 2. Interleaved: `updateContentOrdered(message, toolComponents)` renders text/thinking\n * AND tool components in content order. Tool components become children of this container.\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate thinkingLevel: string;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate timestampFormat: TimestampFormat;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\ttimestampFormat: TimestampFormat = \"date-time-iso\",\n\t\tthinkingLevel = \"off\",\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingLevel = thinkingLevel;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.timestampFormat = timestampFormat;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t}\n\n\tsetThinkingLevel(level: string): void {\n\t\tthis.thinkingLevel = level;\n\t}\n\n\t/**\n\t * Legacy rendering: renders text/thinking blocks into contentContainer.\n\t * Stops rendering at the first tool-type block (toolCall/serverToolUse).\n\t * Post-tool text blocks are handled by the chat-controller to preserve\n\t * content ordering relative to tool rows.\n\t */\n\tupdateContent(message: AssistantMessage): void {\n\t\tthis.lastMessage = message;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasVisibleContent = message.content.some((c) => {\n\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\treturn false;\n\t\t});\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content blocks up to (but not including) the first tool block.\n\t\t// Text blocks after tools are rendered by the chat-controller as separate\n\t\t// components to maintain correct visual ordering with tool rows.\n\t\tlet markerAdded = false;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\n\t\t\t// Stop at the first tool-type block — post-tool content is handled externally\n\t\t\tif (content.type === \"toolCall\" || content.type === \"serverToolUse\") {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tconst text = content.text.trim();\n\t\t\t\tconst withMarker = markerAdded ? text : `${getResponseMarker()}${text}`;\n\t\t\t\tthis.contentContainer.addChild(new Markdown(withMarker, 1, 0, this.markdownTheme));\n\t\t\t\tmarkerAdded = true;\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tif (this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst hasVisibleContentAfter = message.content.slice(i + 1).some((c) => {\n\t\t\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.thinking.trim(), 1, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if aborted - show after partial content\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\" || c.type === \"serverToolUse\");\n\t\tif (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tif (hasVisibleContent) {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t} else {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t}\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), 1, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), 1, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Interleaved rendering: renders text/thinking AND tool components in content order.\n\t * Tool components become children of this container, preserving visual ordering.\n\t *\n\t * @param message - The assistant message\n\t * @param toolComponents - Map of content block ID → pre-created Component (ToolExecutionComponent etc.)\n\t * @returns Map of content block ID → the tool Component that was placed (for pending tool tracking)\n\t */\n\tupdateContentOrdered(\n\t\tmessage: AssistantMessage,\n\t\ttoolComponents?: Map<string, Component>,\n\t): Map<string, Component> {\n\t\tthis.lastMessage = message;\n\n\t\t// Clear contentContainer so we can re-render all blocks in order\n\t\tthis.contentContainer.clear();\n\n\t\tconst placedTools = new Map<string, Component>();\n\n\t\t// Check if there's any visible content at all\n\t\tconst hasVisibleContent = message.content.some((c) => {\n\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\treturn false;\n\t\t});\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render all content blocks in order\n\t\tlet markerAdded = false;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst block = message.content[i];\n\n\t\t\tif (block.type === \"text\" && block.text.trim()) {\n\t\t\t\tconst text = block.text.trim();\n\t\t\t\tconst withMarker = markerAdded ? text : `${getResponseMarker()}${text}`;\n\t\t\t\tthis.contentContainer.addChild(new Markdown(withMarker, 1, 0, this.markdownTheme));\n\t\t\t\tmarkerAdded = true;\n\t\t\t} else if (block.type === \"thinking\" && block.thinking.trim()) {\n\t\t\t\tif (this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\tconst hasVisibleContentAfter = message.content.slice(i + 1).some((c) => {\n\t\t\t\t\tif (c.type === \"text\") return Boolean(c.text.trim());\n\t\t\t\t\tif (c.type === \"thinking\") return !this.hideThinkingBlock && Boolean(c.thinking.trim());\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(block.thinking.trim(), 1, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t}\n\t\t\t} else if ((block.type === \"toolCall\" || block.type === \"serverToolUse\") && toolComponents?.has(block.id)) {\n\t\t\t\t// Place the pre-created tool component in content order\n\t\t\t\tconst toolComponent = toolComponents.get(block.id)!;\n\t\t\t\tthis.contentContainer.addChild(toolComponent);\n\t\t\t\tplacedTools.set(block.id, toolComponent);\n\t\t\t}\n\t\t\t// webSearchResult blocks don't produce their own component;\n\t\t\t// they update the matching serverToolUse component via updateResult()\n\t\t}\n\n\t\t// Handle abort/error after content (only if no tool calls)\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\" || c.type === \"serverToolUse\");\n\t\tif (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), 1, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), 1, 0));\n\t\t\t}\n\t\t}\n\n\t\treturn placedTools;\n\t}\n}\n"]}
@@ -33,6 +33,7 @@ export declare class ToolExecutionComponent extends Container {
33
33
  private writeHighlightCache?;
34
34
  private hideComponent;
35
35
  private manuallyHidden;
36
+ private indented;
36
37
  private startTime;
37
38
  private spinnerTimer;
38
39
  private spinnerFrame;
@@ -82,6 +83,7 @@ export declare class ToolExecutionComponent extends Container {
82
83
  private maybeConvertImagesForKitty;
83
84
  setExpanded(expanded: boolean): void;
84
85
  setHidden(hidden: boolean): void;
86
+ setIndented(indented: boolean): void;
85
87
  isHidden(): boolean;
86
88
  getElapsed(): number;
87
89
  shouldHideWhenCollapsed(collapseToolCalls?: boolean): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"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"}
@@ -82,6 +82,7 @@ export class ToolExecutionComponent extends Container {
82
82
  // When true, this component intentionally renders no lines
83
83
  this.hideComponent = false;
84
84
  this.manuallyHidden = false;
85
+ this.indented = false;
85
86
  this.startTime = Date.now();
86
87
  // Tool status spinner state
87
88
  this.spinnerTimer = null;
@@ -356,6 +357,10 @@ export class ToolExecutionComponent extends Container {
356
357
  this.manuallyHidden = hidden;
357
358
  this.updateDisplay();
358
359
  }
360
+ setIndented(indented) {
361
+ this.indented = indented;
362
+ this.updateDisplay();
363
+ }
359
364
  isHidden() {
360
365
  return this.hideComponent;
361
366
  }
@@ -393,7 +398,18 @@ export class ToolExecutionComponent extends Container {
393
398
  if (this.hideComponent) {
394
399
  return [];
395
400
  }
396
- return [...super.render(width), ""];
401
+ const lines = super.render(width);
402
+ if (this.indented) {
403
+ const gutter = theme.fg("dim", "│");
404
+ return lines.map((line, i) => {
405
+ if (i === lines.length - 1 && line === "") {
406
+ // Trailing empty spacer line → gutter only
407
+ return gutter;
408
+ }
409
+ return gutter + " " + theme.fg("dim", line);
410
+ });
411
+ }
412
+ return [...lines, ""];
397
413
  }
398
414
  updateDisplay() {
399
415
  // Status indicator with circle