@astralyn/pi 0.84.14 → 0.84.16

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 (185) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cli.js +2435 -18
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +12 -6
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/types.d.ts +4 -1
  7. package/dist/core/extensions/types.d.ts.map +1 -1
  8. package/dist/core/extensions/types.js.map +1 -1
  9. package/dist/core/tools/bash.d.ts +1 -0
  10. package/dist/core/tools/bash.d.ts.map +1 -1
  11. package/dist/core/tools/bash.js +4 -1
  12. package/dist/core/tools/bash.js.map +1 -1
  13. package/dist/core/tools/find.d.ts +2 -0
  14. package/dist/core/tools/find.d.ts.map +1 -1
  15. package/dist/core/tools/find.js +18 -15
  16. package/dist/core/tools/find.js.map +1 -1
  17. package/dist/extensions/background/actions.d.ts.map +1 -1
  18. package/dist/extensions/background/actions.js +3 -2
  19. package/dist/extensions/background/actions.js.map +1 -1
  20. package/dist/extensions/background/index.d.ts +5 -4
  21. package/dist/extensions/background/index.d.ts.map +1 -1
  22. package/dist/extensions/background/index.js +9 -5
  23. package/dist/extensions/background/index.js.map +1 -1
  24. package/dist/extensions/background/manager.d.ts.map +1 -1
  25. package/dist/extensions/background/manager.js +8 -9
  26. package/dist/extensions/background/manager.js.map +1 -1
  27. package/dist/extensions/background/registry.d.ts +4 -1
  28. package/dist/extensions/background/registry.d.ts.map +1 -1
  29. package/dist/extensions/background/registry.js +30 -17
  30. package/dist/extensions/background/registry.js.map +1 -1
  31. package/dist/extensions/background/task-view.d.ts +6 -0
  32. package/dist/extensions/background/task-view.d.ts.map +1 -1
  33. package/dist/extensions/background/task-view.js +11 -6
  34. package/dist/extensions/background/task-view.js.map +1 -1
  35. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  36. package/dist/extensions/deepwiki/client.js +3 -0
  37. package/dist/extensions/deepwiki/client.js.map +1 -1
  38. package/dist/extensions/deepwiki/execute.d.ts +2 -1
  39. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  40. package/dist/extensions/deepwiki/execute.js +17 -2
  41. package/dist/extensions/deepwiki/execute.js.map +1 -1
  42. package/dist/extensions/deepwiki/http.d.ts +1 -0
  43. package/dist/extensions/deepwiki/http.d.ts.map +1 -1
  44. package/dist/extensions/deepwiki/http.js +16 -2
  45. package/dist/extensions/deepwiki/http.js.map +1 -1
  46. package/dist/extensions/deepwiki/schema.d.ts +1 -1
  47. package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
  48. package/dist/extensions/deepwiki/schema.js +8 -2
  49. package/dist/extensions/deepwiki/schema.js.map +1 -1
  50. package/dist/extensions/question/dialog.d.ts.map +1 -1
  51. package/dist/extensions/question/dialog.js +80 -9
  52. package/dist/extensions/question/dialog.js.map +1 -1
  53. package/dist/extensions/question/index.d.ts.map +1 -1
  54. package/dist/extensions/question/index.js +8 -0
  55. package/dist/extensions/question/index.js.map +1 -1
  56. package/dist/extensions/question/schema.d.ts +2 -2
  57. package/dist/extensions/question/schema.d.ts.map +1 -1
  58. package/dist/extensions/question/schema.js +41 -11
  59. package/dist/extensions/question/schema.js.map +1 -1
  60. package/dist/extensions/question/types.d.ts +1 -1
  61. package/dist/extensions/question/types.d.ts.map +1 -1
  62. package/dist/extensions/question/types.js.map +1 -1
  63. package/dist/extensions/router/probe.d.ts.map +1 -1
  64. package/dist/extensions/router/probe.js +10 -29
  65. package/dist/extensions/router/probe.js.map +1 -1
  66. package/dist/extensions/router/register.d.ts.map +1 -1
  67. package/dist/extensions/router/register.js +16 -6
  68. package/dist/extensions/router/register.js.map +1 -1
  69. package/dist/extensions/router/store.d.ts.map +1 -1
  70. package/dist/extensions/router/store.js +108 -51
  71. package/dist/extensions/router/store.js.map +1 -1
  72. package/dist/extensions/router/ui.d.ts +6 -0
  73. package/dist/extensions/router/ui.d.ts.map +1 -1
  74. package/dist/extensions/router/ui.js +14 -6
  75. package/dist/extensions/router/ui.js.map +1 -1
  76. package/dist/extensions/statusline/index.d.ts.map +1 -1
  77. package/dist/extensions/statusline/index.js +40 -43
  78. package/dist/extensions/statusline/index.js.map +1 -1
  79. package/dist/extensions/subagent/activity.d.ts +7 -1
  80. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  81. package/dist/extensions/subagent/activity.js +69 -6
  82. package/dist/extensions/subagent/activity.js.map +1 -1
  83. package/dist/extensions/subagent/agents-command.js +1 -1
  84. package/dist/extensions/subagent/agents-command.js.map +1 -1
  85. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  86. package/dist/extensions/subagent/agents.js +0 -3
  87. package/dist/extensions/subagent/agents.js.map +1 -1
  88. package/dist/extensions/subagent/budget.d.ts.map +1 -1
  89. package/dist/extensions/subagent/budget.js +29 -2
  90. package/dist/extensions/subagent/budget.js.map +1 -1
  91. package/dist/extensions/subagent/cancellation.d.ts +4 -4
  92. package/dist/extensions/subagent/cancellation.d.ts.map +1 -1
  93. package/dist/extensions/subagent/cancellation.js +31 -13
  94. package/dist/extensions/subagent/cancellation.js.map +1 -1
  95. package/dist/extensions/subagent/constants.d.ts +1 -0
  96. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  97. package/dist/extensions/subagent/constants.js +1 -0
  98. package/dist/extensions/subagent/constants.js.map +1 -1
  99. package/dist/extensions/subagent/index.d.ts.map +1 -1
  100. package/dist/extensions/subagent/index.js +1 -0
  101. package/dist/extensions/subagent/index.js.map +1 -1
  102. package/dist/extensions/subagent/render.d.ts +8 -1
  103. package/dist/extensions/subagent/render.d.ts.map +1 -1
  104. package/dist/extensions/subagent/render.js +155 -178
  105. package/dist/extensions/subagent/render.js.map +1 -1
  106. package/dist/extensions/subagent/runner.d.ts +1 -0
  107. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  108. package/dist/extensions/subagent/runner.js +53 -44
  109. package/dist/extensions/subagent/runner.js.map +1 -1
  110. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  111. package/dist/extensions/subagent/sdk-runner.js +0 -3
  112. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  113. package/dist/extensions/subagent/settings.d.ts +1 -1
  114. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  115. package/dist/extensions/subagent/settings.js +74 -25
  116. package/dist/extensions/subagent/settings.js.map +1 -1
  117. package/dist/extensions/subagent/state.d.ts +1 -0
  118. package/dist/extensions/subagent/state.d.ts.map +1 -1
  119. package/dist/extensions/subagent/state.js +46 -16
  120. package/dist/extensions/subagent/state.js.map +1 -1
  121. package/dist/extensions/subagent/types.d.ts +0 -7
  122. package/dist/extensions/subagent/types.d.ts.map +1 -1
  123. package/dist/extensions/subagent/types.js.map +1 -1
  124. package/dist/extensions/subagent/ui/agents-dialog.js +1 -1
  125. package/dist/extensions/subagent/ui/agents-dialog.js.map +1 -1
  126. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  127. package/dist/extensions/subagent/ui/model-list.js +3 -4
  128. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  129. package/dist/extensions/subagent/ui/model-picker.d.ts.map +1 -1
  130. package/dist/extensions/subagent/ui/model-picker.js +2 -1
  131. package/dist/extensions/subagent/ui/model-picker.js.map +1 -1
  132. package/dist/extensions/web-search/format.d.ts.map +1 -1
  133. package/dist/extensions/web-search/format.js +36 -15
  134. package/dist/extensions/web-search/format.js.map +1 -1
  135. package/dist/extensions/web-search/index.d.ts.map +1 -1
  136. package/dist/extensions/web-search/index.js +11 -8
  137. package/dist/extensions/web-search/index.js.map +1 -1
  138. package/dist/extensions/web-search/providers/deepseek.d.ts.map +1 -1
  139. package/dist/extensions/web-search/providers/deepseek.js +36 -5
  140. package/dist/extensions/web-search/providers/deepseek.js.map +1 -1
  141. package/dist/extensions/web-search/providers/http.d.ts +2 -7
  142. package/dist/extensions/web-search/providers/http.d.ts.map +1 -1
  143. package/dist/extensions/web-search/providers/http.js +12 -57
  144. package/dist/extensions/web-search/providers/http.js.map +1 -1
  145. package/dist/extensions/web-search/providers/minimax.d.ts.map +1 -1
  146. package/dist/extensions/web-search/providers/minimax.js +18 -6
  147. package/dist/extensions/web-search/providers/minimax.js.map +1 -1
  148. package/dist/extensions/web-search/render.d.ts.map +1 -1
  149. package/dist/extensions/web-search/render.js +16 -5
  150. package/dist/extensions/web-search/render.js.map +1 -1
  151. package/dist/extensions/web-search/results.d.ts +10 -3
  152. package/dist/extensions/web-search/results.d.ts.map +1 -1
  153. package/dist/extensions/web-search/results.js +25 -7
  154. package/dist/extensions/web-search/results.js.map +1 -1
  155. package/dist/image-resize-worker.js +2 -0
  156. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  157. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/tool-execution.js +7 -0
  159. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  160. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/tool-group.js +10 -0
  162. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  163. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/dist/modes/interactive/interactive-mode.js +3 -40
  165. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/dist/modes/interactive/theme/theme.d.ts +0 -4
  167. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  168. package/dist/modes/interactive/theme/theme.js +16 -23
  169. package/dist/modes/interactive/theme/theme.js.map +1 -1
  170. package/dist/rpc-entry.js +2435 -10
  171. package/dist/utils/http-response.d.ts +10 -0
  172. package/dist/utils/http-response.d.ts.map +1 -0
  173. package/dist/utils/http-response.js +68 -0
  174. package/dist/utils/http-response.js.map +1 -0
  175. package/docs/bundled/extensions/background.md +4 -3
  176. package/docs/bundled/extensions/deepwiki.md +7 -5
  177. package/docs/bundled/extensions/question.md +6 -4
  178. package/docs/bundled/extensions/router.md +1 -1
  179. package/docs/bundled/extensions/statusline.md +1 -0
  180. package/docs/bundled/extensions/subagent.md +29 -39
  181. package/docs/bundled/extensions/web-search.md +3 -3
  182. package/docs/development.md +8 -3
  183. package/docs/extensions.md +2 -2
  184. package/npm-shrinkwrap.json +110 -109
  185. package/package.json +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAK9C,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAGtE,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAI9E,eAAO,MAAM,cAAc,iDAAkD,CAAC;AAE9E,eAAO,MAAM,eAAe,sEAQiB,CAAC;AAE9C,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAI7C,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAGnD,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAG/C,eAAO,MAAM,sBAAsB,KAAK,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAK9C,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAGtE,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAI9E,eAAO,MAAM,cAAc,iDAAkD,CAAC;AAE9E,eAAO,MAAM,eAAe,sEAQiB,CAAC;AAE9C,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAIxC,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAGnD,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAG/C,eAAO,MAAM,sBAAsB,KAAK,CAAC"}
@@ -31,6 +31,7 @@ export const THINKING_LEVELS = [
31
31
  ];
32
32
  export const ACTIVITY_LIMIT = 80;
33
33
  export const ACTIVITY_TEXT_LIMIT = 24 * 1024;
34
+ export const DISPLAY_ACTIVITY_LIMIT = 3;
34
35
  // Auto-compaction enters the activity log as a synthetic entry so the worker's
35
36
  // context maintenance is visible alongside its tool calls. Only one compaction
36
37
  // can run at a time, so a single fixed id is enough to pair start with end.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,yEAAyE;AACzE,0EAA0E;AAC1E,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,4EAA4E;AAC5E,yEAAyE;AACzE,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,SAAS,CAAU,CAAC;AAGrE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAC9E,4EAA4E;AAC5E,sEAAsE;AACtE,mBAAmB;AACnB,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAU,CAAC;AAE9E,MAAM,CAAC,MAAM,eAAe,GAAG;IAC9B,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACuC,CAAC;AAE9C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AACnD,yEAAyE;AACzE,oEAAoE;AACpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC/C,yEAAyE;AACzE,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 6;\n\n// Task-level retry for failures that bypass the session's own auto-retry\n// (preflight throws such as auth checks). Only runs that produced nothing\n// are retried, so a retry never discards partial work.\nexport const TASK_RETRY_LIMIT = 2;\nexport const TASK_RETRY_BASE_DELAY_MS = 1_000;\n\n// The only selectable agents: the schema's agent field restricts task items\n// to these names, resolve.ts defaults an omitted/null agent to explorer.\n// SubagentAgentName is co-located with its source value; types.ts re-imports it.\nexport const SUBAGENT_AGENT_NAMES = [\"explorer\", \"general\"] as const;\nexport type SubagentAgentName = (typeof SUBAGENT_AGENT_NAMES)[number];\n\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// The read-only explorer profile gets the standalone search tools plus bash\n// for read-only inspection (git history, counting); the system prompt\n// constrains bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\", \"bash\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\n// Auto-compaction enters the activity log as a synthetic entry so the worker's\n// context maintenance is visible alongside its tool calls. Only one compaction\n// can run at a time, so a single fixed id is enough to pair start with end.\nexport const COMPACTION_ACTIVITY_ID = \"compaction\";\n// Per-task ceiling for the model-facing report; the total cap below wins\n// when the batch is large enough that a fair share would exceed it.\nexport const TASK_OUTPUT_LIMIT = 32 * 1024;\n// Total cap for the model-facing report across every task section.\nexport const TOTAL_OUTPUT_LIMIT = 48 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const RETRY_ERROR_TEXT_LIMIT = 160;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\n// Bounded per run inside DETAILS_OUTPUT_LIMIT; each activity costs up to\n// ~600 bytes, so 12 keeps an 8-run parallel batch from starving outputs.\nexport const DETAILS_ACTIVITY_LIMIT = 12;\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,yEAAyE;AACzE,0EAA0E;AAC1E,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,4EAA4E;AAC5E,yEAAyE;AACzE,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,SAAS,CAAU,CAAC;AAGrE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAC9E,4EAA4E;AAC5E,sEAAsE;AACtE,mBAAmB;AACnB,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAU,CAAC;AAE9E,MAAM,CAAC,MAAM,eAAe,GAAG;IAC9B,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACuC,CAAC;AAE9C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AACnD,yEAAyE;AACzE,oEAAoE;AACpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC/C,yEAAyE;AACzE,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 6;\n\n// Task-level retry for failures that bypass the session's own auto-retry\n// (preflight throws such as auth checks). Only runs that produced nothing\n// are retried, so a retry never discards partial work.\nexport const TASK_RETRY_LIMIT = 2;\nexport const TASK_RETRY_BASE_DELAY_MS = 1_000;\n\n// The only selectable agents: the schema's agent field restricts task items\n// to these names, resolve.ts defaults an omitted/null agent to explorer.\n// SubagentAgentName is co-located with its source value; types.ts re-imports it.\nexport const SUBAGENT_AGENT_NAMES = [\"explorer\", \"general\"] as const;\nexport type SubagentAgentName = (typeof SUBAGENT_AGENT_NAMES)[number];\n\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// The read-only explorer profile gets the standalone search tools plus bash\n// for read-only inspection (git history, counting); the system prompt\n// constrains bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\", \"bash\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\nexport const DISPLAY_ACTIVITY_LIMIT = 3;\n// Auto-compaction enters the activity log as a synthetic entry so the worker's\n// context maintenance is visible alongside its tool calls. Only one compaction\n// can run at a time, so a single fixed id is enough to pair start with end.\nexport const COMPACTION_ACTIVITY_ID = \"compaction\";\n// Per-task ceiling for the model-facing report; the total cap below wins\n// when the batch is large enough that a fair share would exceed it.\nexport const TASK_OUTPUT_LIMIT = 32 * 1024;\n// Total cap for the model-facing report across every task section.\nexport const TOTAL_OUTPUT_LIMIT = 48 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const RETRY_ERROR_TEXT_LIMIT = 160;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\n// Bounded per run inside DETAILS_OUTPUT_LIMIT; each activity costs up to\n// ~600 bytes, so 12 keeps an 8-run parallel batch from starving outputs.\nexport const DETAILS_ACTIVITY_LIMIT = 12;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYpF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8EvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYpF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA+EvD"}
@@ -49,6 +49,7 @@ export default function subagent(pi) {
49
49
  onUpdate: (details) => {
50
50
  onUpdate?.({ content: [{ type: "text", text: statusSummary(details) }], details });
51
51
  },
52
+ onConfigWarning: (message) => ctx.ui.notify(message, "warning"),
52
53
  registerAbort: (abort) => {
53
54
  activeAborters.add(abort);
54
55
  return () => activeAborters.delete(abort);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAgentDir } from \"../../config.ts\";\nimport type { AgentToolResult, ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { subagentToolDescription } from \"./agents.ts\";\nimport { showAgentsCommand } from \"./agents-command.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME } from \"./constants.ts\";\nimport { renderSubagentCall, renderSubagentResult, type SubagentRenderState, scheduleLiveRefresh } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { statusSummary } from \"./state.ts\";\nimport type { SubagentDetails } from \"./types.ts\";\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\n\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails, SubagentRenderState>({\n\t\tname: SUBAGENT_TOOL_NAME,\n\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\tdescription: subagentToolDescription(),\n\t\tpromptSnippet: \"Delegate bounded work to isolated explorer or general workers\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `subagent` for bounded work that benefits from isolated context or concurrent investigation; do not delegate a task you can finish with one or two direct tool calls.\",\n\t\t],\n\t\tparameters: SubagentParamsSchema,\n\t\tasync execute(toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t// First paint before task preflight so filesystem and model checks\n\t\t\t// cannot leave the transcript card empty.\n\t\t\tonUpdate?.({\n\t\t\t\tcontent: [{ type: \"text\", text: \"Initializing…\" }],\n\t\t\t\tdetails: {\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\truns: [],\n\t\t\t\t\tstartedAt: Date.now(),\n\t\t\t\t\tusage: emptyUsage(),\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst parent: ParentModelContext = {\n\t\t\t\tmodel: ctx.model,\n\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t};\n\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\tparams,\n\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\tparent,\n\t\t\t\tmodelRuntime: ctx.modelRuntime,\n\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\tsignal,\n\t\t\t\tgate,\n\t\t\t\tbatchId: toolCallId,\n\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t},\n\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\tdetails: execution.details,\n\t\t\t\tusage: execution.usage,\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\treturn renderSubagentCall(args, theme);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tscheduleLiveRefresh(context, options.isPartial);\n\t\t\treturn renderSubagentResult(result, options, theme, context.args, context.isError);\n\t\t},\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAgentDir } from \"../../config.ts\";\nimport type { AgentToolResult, ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { subagentToolDescription } from \"./agents.ts\";\nimport { showAgentsCommand } from \"./agents-command.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME } from \"./constants.ts\";\nimport { renderSubagentCall, renderSubagentResult, type SubagentRenderState, scheduleLiveRefresh } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { statusSummary } from \"./state.ts\";\nimport type { SubagentDetails } from \"./types.ts\";\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\n\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails, SubagentRenderState>({\n\t\tname: SUBAGENT_TOOL_NAME,\n\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\tdescription: subagentToolDescription(),\n\t\tpromptSnippet: \"Delegate bounded work to isolated explorer or general workers\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `subagent` for bounded work that benefits from isolated context or concurrent investigation; do not delegate a task you can finish with one or two direct tool calls.\",\n\t\t],\n\t\tparameters: SubagentParamsSchema,\n\t\tasync execute(toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t// First paint before task preflight so filesystem and model checks\n\t\t\t// cannot leave the transcript card empty.\n\t\t\tonUpdate?.({\n\t\t\t\tcontent: [{ type: \"text\", text: \"Initializing…\" }],\n\t\t\t\tdetails: {\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\truns: [],\n\t\t\t\t\tstartedAt: Date.now(),\n\t\t\t\t\tusage: emptyUsage(),\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst parent: ParentModelContext = {\n\t\t\t\tmodel: ctx.model,\n\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t};\n\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\tparams,\n\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\tparent,\n\t\t\t\tmodelRuntime: ctx.modelRuntime,\n\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\tsignal,\n\t\t\t\tgate,\n\t\t\t\tbatchId: toolCallId,\n\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t},\n\t\t\t\tonConfigWarning: (message) => ctx.ui.notify(message, \"warning\"),\n\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\tdetails: execution.details,\n\t\t\t\tusage: execution.usage,\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\treturn renderSubagentCall(args, theme);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tscheduleLiveRefresh(context, options.isPartial);\n\t\t\treturn renderSubagentResult(result, options, theme, context.args, context.isError);\n\t\t},\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
@@ -2,14 +2,21 @@ import { type Component } from "@earendil-works/pi-tui";
2
2
  import type { AgentToolResult, ToolRenderContext, ToolRenderResultOptions } from "../../core/extensions/types.ts";
3
3
  import { type Theme } from "../../modes/interactive/theme/theme.ts";
4
4
  import type { SubagentParams } from "./schema.ts";
5
- import type { SubagentDetails } from "./types.ts";
5
+ import type { SubagentDetails, SubagentRunDetails } from "./types.ts";
6
6
  /** Per-result live-refresh state owned by the shell's render context. */
7
7
  export interface SubagentRenderState {
8
8
  refreshTimer?: ReturnType<typeof setTimeout>;
9
9
  /** Cadence the current timer was created with, used to detect changes. */
10
10
  refreshInterval?: number;
11
+ /** Called by the shell when the transcript row is disposed. */
12
+ dispose?: () => void;
11
13
  }
12
14
  type SubagentToolRenderContext = ToolRenderContext<SubagentRenderState, SubagentParams, SubagentDetails>;
15
+ /** Pure refresh policy: schedule only while the current view contains changing text. */
16
+ export declare function desiredRefreshInterval(runs: readonly SubagentRunDetails[], options: {
17
+ isPartial: boolean;
18
+ expanded: boolean;
19
+ }): number | undefined;
13
20
  export declare function scheduleLiveRefresh(context: SubagentToolRenderContext, isPartial: boolean): void;
14
21
  export declare function renderSubagentCall(args: SubagentParams, theme: Theme): Component;
15
22
  export declare function renderSubagentResult(result: AgentToolResult<SubagentDetails>, options: ToolRenderResultOptions, theme: Theme, args: SubagentParams, isError: boolean): Component;
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/render.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EAOd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAClH,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAGtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,KAAK,EAAE,eAAe,EAAuD,MAAM,YAAY,CAAC;AAgUvG,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,yBAAyB,GAAG,iBAAiB,CAAC,mBAAmB,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;AAazG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,yBAAyB,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CA0BhG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAIhF;AAaD,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EACxC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,OAAO,GACd,SAAS,CAsBX"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/render.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EAOd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAClH,OAAO,EAAoB,KAAK,KAAK,EAAmB,MAAM,wCAAwC,CAAC;AAIvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAmC,MAAM,YAAY,CAAC;AAiUvG,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,KAAK,yBAAyB,GAAG,iBAAiB,CAAC,mBAAmB,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;AAEzG,wFAAwF;AACxF,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,SAAS,kBAAkB,EAAE,EACnC,OAAO,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAChD,MAAM,GAAG,SAAS,CAQpB;AAWD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,yBAAyB,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAwBhG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAIhF;AAED,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EACxC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,OAAO,GACd,SAAS,CAiBX"}
@@ -1,13 +1,12 @@
1
1
  import { Container, Markdown, Spacer, Text, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
2
2
  import { getMarkdownTheme } from "../../modes/interactive/theme/theme.js";
3
+ import { activityCallText, isDisplayableActivity } from "./activity.js";
3
4
  import { AGENT_PROFILE_LABELS } from "./agents.js";
4
- import { COMPACTION_ACTIVITY_ID } from "./constants.js";
5
+ import { COMPACTION_ACTIVITY_ID, DISPLAY_ACTIVITY_LIMIT } from "./constants.js";
5
6
  import { getSubagentRetryView } from "./state.js";
6
7
  import { plainLine, truncate } from "./text.js";
7
- const ACTIVITY_RESULT_LIMIT = 96;
8
8
  const RETRY_ERROR_LIMIT = 160;
9
9
  const FALLBACK_OUTPUT_LIMIT = 4_000;
10
- const ACTIVITY_DURATION_MIN_MS = 10_000;
11
10
  const OUTPUT_TRUNCATION_NOTICE_PATTERN = /\s*\[Output truncated(?:: \d+ bytes omitted)?\.\]\s*$/u;
12
11
  // A breathing dot-to-star bloom: the sequence plays forward to full bloom
13
12
  // and back, holding each extreme for two ticks. The mid frame uses ✼ rather
@@ -75,17 +74,6 @@ function statusMarker(status, theme) {
75
74
  : "○";
76
75
  return theme.fg(statusColor(status), glyph);
77
76
  }
78
- function statusWord(status) {
79
- if (status === "completed")
80
- return "Completed";
81
- if (status === "failed")
82
- return "Failed";
83
- if (status === "aborted")
84
- return "Aborted";
85
- if (status === "running")
86
- return "Thinking...";
87
- return "Queued";
88
- }
89
77
  function displayLine(text) {
90
78
  return plainLine(text).replace(OUTPUT_TRUNCATION_NOTICE_PATTERN, "...").trim();
91
79
  }
@@ -99,72 +87,22 @@ function retryText(run) {
99
87
  const error = truncate(displayLine(retry.error), RETRY_ERROR_LIMIT);
100
88
  return error ? `${state} · ${error}` : state;
101
89
  }
102
- function replaceToolPrefix(text, toolName, label, emptyLabel = label) {
103
- if (text === toolName)
104
- return emptyLabel;
105
- if (text.startsWith(`${toolName} `))
106
- return `${label}${text.slice(toolName.length)}`;
107
- return text;
108
- }
109
- function activitySummary(activity) {
110
- const summary = displayLine(activity.summary);
111
- if (activity.toolName === COMPACTION_ACTIVITY_ID) {
112
- return activity.status === "running" ? "Compacting..." : summary;
113
- }
114
- if (activity.toolName === "bash")
115
- return summary === "bash" ? "Run" : summary;
116
- if (activity.toolName === "read")
117
- return replaceToolPrefix(summary, "read", "Read");
118
- if (activity.toolName === "grep")
119
- return replaceToolPrefix(summary, "grep", "Search");
120
- if (activity.toolName === "find")
121
- return replaceToolPrefix(summary, "find", "Find");
122
- if (activity.toolName === "ls")
123
- return replaceToolPrefix(summary, "ls", "List", "List .");
124
- if (activity.toolName === "edit")
125
- return replaceToolPrefix(summary, "edit", "Edit");
126
- if (activity.toolName === "write")
127
- return replaceToolPrefix(summary, "write", "Write");
128
- return summary || activity.toolName;
129
- }
130
90
  function latestRunningActivity(run, toolName) {
131
91
  for (let index = run.activities.length - 1; index >= 0; index--) {
132
92
  const activity = run.activities[index];
133
- if (activity?.status === "running" && (toolName === undefined || activity.toolName === toolName))
93
+ if (activity?.status === "running" && activity.toolName === toolName)
134
94
  return activity;
135
95
  }
136
96
  return undefined;
137
97
  }
138
- function fallbackCurrentActivity(run) {
139
- if (!run.currentActivity)
140
- return undefined;
141
- const current = displayLine(run.currentActivity);
142
- if (/^Compacting(?: context)?(?:…|\.\.\.)?$/u.test(current))
143
- return "Compacting...";
144
- return current;
145
- }
146
- function runStateText(run) {
147
- if (run.status === "completed" || run.status === "failed" || run.status === "aborted") {
148
- const duration = elapsed(run.startedAt, run.endedAt);
149
- return duration ? `${statusWord(run.status)} · ${duration}` : statusWord(run.status);
150
- }
151
- const retry = retryText(run);
152
- if (retry)
153
- return retry;
154
- if (run.status === "queued")
155
- return "Queued";
156
- const compaction = latestRunningActivity(run, COMPACTION_ACTIVITY_ID);
157
- if (compaction)
158
- return "Compacting...";
159
- const activity = latestRunningActivity(run);
160
- if (activity)
161
- return activitySummary(activity);
162
- const current = fallbackCurrentActivity(run);
163
- if (current)
164
- return current;
165
- if (run.status === "running" && run.usage.turns === 0 && run.usage.toolUses === 0)
166
- return "Starting...";
167
- return statusWord(run.status);
98
+ function formatTotalTokens(value) {
99
+ const count = Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0;
100
+ if (count < 1_000)
101
+ return String(count);
102
+ const divisor = count < 999_950 ? 1_000 : 1_000_000;
103
+ const suffix = divisor === 1_000 ? "k" : "M";
104
+ const rounded = Math.round((count / divisor) * 10) / 10;
105
+ return `${Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toFixed(1)}${suffix}`;
168
106
  }
169
107
  function spinnerGlyph(now) {
170
108
  const frame = Math.floor(now / SPINNER_INTERVAL_MS) % SPINNER_FRAMES.length;
@@ -200,8 +138,10 @@ class CollapsedFlow {
200
138
  const usable = Math.max(1, width);
201
139
  const joined = this.segments.join(FLOW_SEPARATOR);
202
140
  const packed = visibleWidth(joined);
203
- if (this.cellWidth > usable || packed <= usable) {
204
- return [packed <= usable ? joined : truncateToWidth(joined, usable, "...")];
141
+ if (packed <= usable)
142
+ return [joined];
143
+ if (this.cellWidth > usable) {
144
+ return this.segments.map((segment) => truncateToWidth(segment, usable, "..."));
205
145
  }
206
146
  const columnCount = Math.max(1, Math.floor((usable + FLOW_SEPARATOR_WIDTH) / (this.cellWidth + FLOW_SEPARATOR_WIDTH)));
207
147
  const lines = [];
@@ -220,80 +160,123 @@ class CollapsedFlow {
220
160
  }
221
161
  invalidate() { }
222
162
  }
163
+ function toolCallLabel(count) {
164
+ return `${count} tool call${count === 1 ? "" : "s"}`;
165
+ }
223
166
  function runHeader(run, index, theme) {
224
- const metrics = [run.model];
225
- if (run.thinking !== "off")
226
- metrics.push(run.thinking);
227
- if (run.cwd)
228
- metrics.push(`cwd: ${run.cwd}`);
229
- if (run.usage.toolUses)
230
- metrics.push(`${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? "" : "s"}`);
231
- if (run.usage.turns)
232
- metrics.push(`${run.usage.turns} turn${run.usage.turns === 1 ? "" : "s"}`);
233
- const duration = elapsed(run.startedAt, run.endedAt);
234
- if (duration)
235
- metrics.push(duration);
236
- return `${theme.fg("dim", `── #${index + 1} `)}${theme.fg("accent", profileLabel(run.agent))}${theme.fg("dim", ` · ${metrics.join(" · ")}`)}`;
167
+ const separator = theme.fg("dim", " · ");
168
+ const duration = elapsed(run.startedAt, run.endedAt) ?? "0.0s";
169
+ const metrics = [
170
+ run.model,
171
+ run.thinking,
172
+ `${formatTotalTokens(run.usage.totalTokens)} tok`,
173
+ toolCallLabel(run.usage.toolUses),
174
+ duration,
175
+ ].map((metric) => theme.fg("muted", metric));
176
+ return [theme.fg("accent", theme.bold(`#${index + 1} ${profileLabel(run.agent)}`)), ...metrics].join(separator);
177
+ }
178
+ function sectionTitle(title, theme, color, suffix) {
179
+ const heading = theme.fg(color, theme.bold(title));
180
+ return suffix ? `${heading}${theme.fg("dim", ` · ${suffix}`)}` : heading;
181
+ }
182
+ function activityTitleSuffix(total, shown) {
183
+ if (total <= DISPLAY_ACTIVITY_LIMIT && shown >= total)
184
+ return toolCallLabel(total);
185
+ if (shown > 0)
186
+ return `last ${shown} of ${total} tool calls`;
187
+ return toolCallLabel(total);
237
188
  }
238
189
  function activityLine(activity, theme) {
239
- const summary = activitySummary(activity);
240
- let line = activity.status === "failed"
241
- ? `${theme.fg("error", "×")} ${theme.fg("toolOutput", summary)}`
242
- : activity.status === "running"
243
- ? `${theme.fg("accent", "›")} ${theme.fg("toolOutput", summary)}`
244
- : ` ${theme.fg("toolOutput", summary)}`;
245
- if (activity.status === "failed" && activity.resultSummary) {
246
- line += theme.fg("error", ` · ${truncate(displayLine(activity.resultSummary), ACTIVITY_RESULT_LIMIT)}`);
247
- }
248
- if (activity.endedAt !== undefined && activity.endedAt - activity.startedAt >= ACTIVITY_DURATION_MIN_MS) {
249
- line += theme.fg("dim", ` · ${formatDuration((activity.endedAt - activity.startedAt) / 1_000)}`);
250
- }
251
- return line;
190
+ // State color stays on the two-column status marker; the call text itself
191
+ // keeps the quiet toolOutput color so a running row does not flash a whole
192
+ // accent-colored command line on every refresh.
193
+ const text = theme.fg("toolOutput", activityCallText(activity));
194
+ if (activity.status === "failed")
195
+ return `${theme.fg("error", "×")} ${text}`;
196
+ if (activity.status === "running")
197
+ return `${theme.fg("accent", "›")} ${text}`;
198
+ return ` ${text}`;
252
199
  }
253
200
  function addPrompt(container, run, index, args, theme) {
254
- container.addChild(new Spacer(1));
255
- container.addChild(new Text(theme.fg("muted", "Prompt"), 0, 0));
256
- container.addChild(new Text(theme.fg("toolOutput", taskPrompt(args, index, run)), 0, 0));
201
+ container.addChild(new Text(sectionTitle("Prompt", theme, "toolTitle"), 0, 0));
202
+ container.addChild(new Text(theme.fg("toolOutput", taskPrompt(args, index, run)), 2, 0));
257
203
  }
258
- function renderRunningRun(run, index, args, theme) {
259
- const container = new Container();
260
- container.addChild(new SingleLineText(runHeader(run, index, theme)));
261
- addPrompt(container, run, index, args, theme);
262
- container.addChild(new Spacer(1));
263
- const total = Math.max(run.usage.toolUses, run.activities.length);
264
- const label = total > run.activities.length ? `Activity · last ${run.activities.length} of ${total}` : "Activity";
265
- container.addChild(new Text(theme.fg("muted", label), 0, 0));
266
- const retry = retryText(run);
267
- if (retry) {
268
- container.addChild(new SingleLineText(`${statusMarker(run.status, theme)} ${theme.fg("toolOutput", retry)}`));
204
+ function addActivity(container, run, theme) {
205
+ const toolCalls = run.activities.filter(isDisplayableActivity);
206
+ const shown = toolCalls.slice(-DISPLAY_ACTIVITY_LIMIT);
207
+ const total = Math.max(run.usage.toolUses, toolCalls.length);
208
+ container.addChild(new Text(sectionTitle("Activity", theme, "toolTitle", activityTitleSuffix(total, shown.length)), 0, 0));
209
+ if (shown.length === 0) {
210
+ const empty = total === 0 ? "No tool calls yet." : "Activity unavailable.";
211
+ container.addChild(new Text(theme.fg("muted", empty), 2, 0));
212
+ return;
269
213
  }
270
- for (const activity of run.activities)
214
+ for (const activity of shown)
271
215
  container.addChild(new SingleLineText(activityLine(activity, theme)));
272
- if (!retry && run.activities.length === 0) {
273
- container.addChild(new SingleLineText(theme.fg("dim", ` ${runStateText(run)}`)));
216
+ }
217
+ function outcomeColor(status) {
218
+ if (status === "completed")
219
+ return "success";
220
+ if (status === "failed")
221
+ return "error";
222
+ if (status === "aborted")
223
+ return "warning";
224
+ return "accent";
225
+ }
226
+ function outcomeReport(run) {
227
+ if (typeof run.report === "string")
228
+ return run.report;
229
+ const legacyOutput = run.finalOutput;
230
+ return typeof legacyOutput === "string" ? legacyOutput : "";
231
+ }
232
+ function hasOutcome(report) {
233
+ return report.trim().length > 0;
234
+ }
235
+ function addOutcomeMarkdown(container, report, theme) {
236
+ container.addChild(new Markdown(report, 2, 0, getMarkdownTheme(), {
237
+ color: (text) => theme.fg("toolOutput", text),
238
+ }));
239
+ }
240
+ function addOutcome(container, run, theme) {
241
+ const color = outcomeColor(run.status);
242
+ const report = outcomeReport(run);
243
+ container.addChild(new Text(sectionTitle("Outcome", theme, color), 0, 0));
244
+ if (run.status === "queued" || run.status === "running") {
245
+ container.addChild(new Text(theme.fg("muted", "Still running..."), 2, 0));
246
+ const retry = retryText(run);
247
+ if (retry) {
248
+ container.addChild(new Text(theme.fg("warning", retry), 2, 0));
249
+ }
250
+ else if (latestRunningActivity(run, COMPACTION_ACTIVITY_ID)) {
251
+ container.addChild(new Text(theme.fg("accent", "Compacting context..."), 2, 0));
252
+ }
253
+ return;
254
+ }
255
+ if (run.status === "completed") {
256
+ if (hasOutcome(report))
257
+ addOutcomeMarkdown(container, report, theme);
258
+ else
259
+ container.addChild(new Text(theme.fg("muted", "No outcome returned."), 2, 0));
260
+ return;
261
+ }
262
+ const verdict = run.status === "failed" ? "Failed" : "Aborted";
263
+ const reason = run.error ? `${verdict}: ${run.error}` : `${verdict}.`;
264
+ container.addChild(new Text(theme.fg(color, reason), 2, 0));
265
+ if (hasOutcome(report)) {
266
+ container.addChild(new Spacer(1));
267
+ container.addChild(new Text(theme.fg("muted", "Partial outcome:"), 2, 0));
268
+ addOutcomeMarkdown(container, report, theme);
274
269
  }
275
- return container;
276
270
  }
277
- function renderSettledRun(run, index, args, theme) {
271
+ function renderRun(run, index, args, theme) {
278
272
  const container = new Container();
279
273
  container.addChild(new SingleLineText(runHeader(run, index, theme)));
274
+ container.addChild(new Spacer(1));
280
275
  addPrompt(container, run, index, args, theme);
281
- if (run.error) {
282
- container.addChild(new Spacer(1));
283
- container.addChild(new Text(theme.fg("muted", "Error"), 0, 0));
284
- container.addChild(new Text(theme.fg("error", run.error), 0, 0));
285
- }
286
276
  container.addChild(new Spacer(1));
287
- const reportLabel = run.status !== "completed" && run.report ? "Report · partial" : "Report";
288
- container.addChild(new Text(theme.fg("muted", reportLabel), 0, 0));
289
- if (run.report) {
290
- container.addChild(new Markdown(run.report, 0, 0, getMarkdownTheme(), {
291
- color: (text) => theme.fg("toolOutput", text),
292
- }));
293
- }
294
- else {
295
- container.addChild(new Text(theme.fg("muted", "(No report.)"), 0, 0));
296
- }
277
+ addActivity(container, run, theme);
278
+ container.addChild(new Spacer(1));
279
+ addOutcome(container, run, theme);
297
280
  return container;
298
281
  }
299
282
  function fallbackResult(result, theme, isError) {
@@ -301,60 +284,57 @@ function fallbackResult(result, theme, isError) {
301
284
  const value = text?.type === "text" ? truncate(text.text, FALLBACK_OUTPUT_LIMIT) : "(no output)";
302
285
  return new Text(theme.fg(isError ? "error" : "muted", value), 0, 0);
303
286
  }
304
- // Only genuinely running runs justify spinner-cadence repaints; queued runs
305
- // show a static marker and pick the animation back up via runner updates.
306
- function hasRunningRuns(runs) {
307
- return runs.some((run) => run.status === "running");
287
+ /** Pure refresh policy: schedule only while the current view contains changing text. */
288
+ export function desiredRefreshInterval(runs, options) {
289
+ if (!options.isPartial)
290
+ return undefined;
291
+ if (!options.expanded) {
292
+ return runs.some((run) => run.status === "running") ? SPINNER_INTERVAL_MS : undefined;
293
+ }
294
+ return runs.some((run) => run.status === "running" || run.retry !== undefined)
295
+ ? ELAPSED_REFRESH_INTERVAL_MS
296
+ : undefined;
308
297
  }
309
- // Animate a collapsed flow at the spinner cadence while runs are active;
310
- // otherwise re-render timing and retry text once per second. The first
311
- // settled render clears the timer, and a cadence change (expand/collapse or
312
- // the last run finishing) reschedules immediately instead of waiting out
313
- // the previous interval.
298
+ function clearLiveRefresh(state) {
299
+ if (state.refreshTimer !== undefined)
300
+ clearTimeout(state.refreshTimer);
301
+ state.refreshTimer = undefined;
302
+ state.refreshInterval = undefined;
303
+ }
304
+ // One one-shot timer per row. A paint with the same cadence keeps the existing
305
+ // deadline; expansion, settlement, or loss of dynamic content clears or
306
+ // reschedules immediately.
314
307
  export function scheduleLiveRefresh(context, isPartial) {
315
308
  const state = context.state;
316
- if (isPartial) {
317
- const details = context.result?.details;
318
- const interval = !context.expanded && hasRunningRuns(details?.runs ?? []) ? SPINNER_INTERVAL_MS : ELAPSED_REFRESH_INTERVAL_MS;
319
- if (state.refreshTimer !== undefined) {
320
- // Keep a matching timer so unrelated re-renders cannot postpone the
321
- // next tick; only reschedule when the desired cadence changes.
322
- if (state.refreshInterval === interval)
323
- return;
324
- clearTimeout(state.refreshTimer);
325
- }
326
- state.refreshInterval = interval;
327
- state.refreshTimer = setTimeout(() => {
328
- state.refreshTimer = undefined;
329
- state.refreshInterval = undefined;
330
- context.invalidate();
331
- }, interval);
332
- state.refreshTimer.unref?.();
309
+ state.dispose ??= () => clearLiveRefresh(state);
310
+ const interval = desiredRefreshInterval(context.result?.details?.runs ?? [], {
311
+ isPartial,
312
+ expanded: context.expanded,
313
+ });
314
+ if (interval === undefined) {
315
+ clearLiveRefresh(state);
333
316
  return;
334
317
  }
335
318
  if (state.refreshTimer !== undefined) {
319
+ // Keep a matching timer so unrelated re-renders cannot postpone the
320
+ // next tick; only reschedule when the desired cadence changes.
321
+ if (state.refreshInterval === interval)
322
+ return;
336
323
  clearTimeout(state.refreshTimer);
324
+ }
325
+ state.refreshInterval = interval;
326
+ state.refreshTimer = setTimeout(() => {
337
327
  state.refreshTimer = undefined;
338
328
  state.refreshInterval = undefined;
339
- }
329
+ context.invalidate();
330
+ }, interval);
331
+ state.refreshTimer.unref?.();
340
332
  }
341
333
  export function renderSubagentCall(args, theme) {
342
334
  const count = Array.isArray(args.tasks) ? args.tasks.length : 0;
343
335
  const title = theme.fg(count > 0 ? "toolTitle" : "error", theme.bold("Subagent"));
344
336
  return new Text(title, 0, 0);
345
337
  }
346
- /** Batch-level timing and cost live only in the expanded view. */
347
- function batchSummaryLine(details, theme, isPartial) {
348
- const parts = [];
349
- const duration = elapsed(details.startedAt, details.endedAt);
350
- if (duration)
351
- parts.push(duration);
352
- const cost = details.usage?.cost;
353
- if (!isPartial && typeof cost === "number" && Number.isFinite(cost) && cost > 0)
354
- parts.push(`$${cost.toFixed(3)}`);
355
- const tasks = `${details.runs.length} task${details.runs.length === 1 ? "" : "s"}`;
356
- return `${theme.fg("dim", `── Batch · ${tasks}`)}${parts.length > 0 ? `${theme.fg("dim", ` · ${parts.join(" · ")}`)}` : ""}`;
357
- }
358
338
  export function renderSubagentResult(result, options, theme, args, isError) {
359
339
  const details = result.details;
360
340
  if (!details || !Array.isArray(details.runs))
@@ -367,13 +347,10 @@ export function renderSubagentResult(result, options, theme, args, isError) {
367
347
  container.addChild(new CollapsedFlow(details.runs.map((run, index) => flowSegment(run, index, theme, now))));
368
348
  return container;
369
349
  }
370
- container.addChild(new SingleLineText(batchSummaryLine(details, theme, options.isPartial)));
371
- container.addChild(new Spacer(1));
372
350
  for (const [index, run] of details.runs.entries()) {
373
351
  if (index > 0)
374
352
  container.addChild(new Spacer(1));
375
- const settled = run.status === "completed" || run.status === "failed" || run.status === "aborted";
376
- container.addChild(settled ? renderSettledRun(run, index, args, theme) : renderRunningRun(run, index, args, theme));
353
+ container.addChild(renderRun(run, index, args, theme));
377
354
  }
378
355
  return container;
379
356
  }