@code-yeongyu/senpi 2026.5.14 → 2026.5.15-3

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 (175) hide show
  1. package/CHANGELOG.md +1110 -1175
  2. package/README.md +1 -2
  3. package/dist/core/agent-session.d.ts +9 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +109 -7
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/dynamic-prompt/verification.d.ts +31 -0
  8. package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
  9. package/dist/core/dynamic-prompt/verification.js +41 -0
  10. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  11. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
  12. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
  13. package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
  14. package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
  15. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/compaction/index.js +168 -31
  17. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  18. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
  19. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
  20. package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
  21. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
  22. package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
  23. package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
  25. package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
  26. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
  27. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
  29. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
  30. package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
  31. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/compaction/speculative.js +82 -33
  33. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
  35. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
  37. package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
  38. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/index.js +0 -2
  40. package/dist/core/extensions/builtin/index.js.map +1 -1
  41. package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
  42. package/dist/core/extensions/builtin/openai-web-search/index.js +26 -1
  43. package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
  44. package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
  46. package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
  47. package/dist/core/extensions/builtin/system-messages.d.ts +7 -7
  48. package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/system-messages.js +10 -10
  50. package/dist/core/extensions/builtin/system-messages.js.map +1 -1
  51. package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts +1 -1
  52. package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/todotools/continuation/prompt.js +1 -1
  54. package/dist/core/extensions/builtin/todotools/continuation/prompt.js.map +1 -1
  55. package/dist/core/extensions/builtin/todotools/state.d.ts +1 -1
  56. package/dist/core/extensions/builtin/todotools/state.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/todotools/state.js +1 -1
  58. package/dist/core/extensions/builtin/todotools/state.js.map +1 -1
  59. package/dist/core/extensions/builtin/todotools/system-messages.d.ts +3 -3
  60. package/dist/core/extensions/builtin/todotools/system-messages.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/todotools/system-messages.js +6 -6
  62. package/dist/core/extensions/builtin/todotools/system-messages.js.map +1 -1
  63. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
  64. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
  66. package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
  67. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
  68. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
  69. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
  70. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
  71. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
  72. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
  74. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
  75. package/dist/core/extensions/loader.d.ts.map +1 -1
  76. package/dist/core/extensions/loader.js +2 -0
  77. package/dist/core/extensions/loader.js.map +1 -1
  78. package/dist/core/extensions/runner.d.ts +3 -0
  79. package/dist/core/extensions/runner.d.ts.map +1 -1
  80. package/dist/core/extensions/runner.js +18 -0
  81. package/dist/core/extensions/runner.js.map +1 -1
  82. package/dist/core/extensions/types.d.ts +22 -0
  83. package/dist/core/extensions/types.d.ts.map +1 -1
  84. package/dist/core/extensions/types.js.map +1 -1
  85. package/dist/core/messages.d.ts +3 -3
  86. package/dist/core/messages.d.ts.map +1 -1
  87. package/dist/core/messages.js +5 -10
  88. package/dist/core/messages.js.map +1 -1
  89. package/dist/core/model-registry.d.ts.map +1 -1
  90. package/dist/core/model-registry.js +1 -0
  91. package/dist/core/model-registry.js.map +1 -1
  92. package/dist/core/sdk.d.ts +1 -1
  93. package/dist/core/sdk.d.ts.map +1 -1
  94. package/dist/core/sdk.js +7 -22
  95. package/dist/core/sdk.js.map +1 -1
  96. package/dist/core/session-manager.d.ts.map +1 -1
  97. package/dist/core/session-manager.js +1 -1
  98. package/dist/core/session-manager.js.map +1 -1
  99. package/dist/core/settings-manager.d.ts +0 -5
  100. package/dist/core/settings-manager.d.ts.map +1 -1
  101. package/dist/core/settings-manager.js.map +1 -1
  102. package/dist/core/thinking-levels.d.ts +6 -0
  103. package/dist/core/thinking-levels.d.ts.map +1 -0
  104. package/dist/core/thinking-levels.js +36 -0
  105. package/dist/core/thinking-levels.js.map +1 -0
  106. package/dist/core/tools/bash.d.ts.map +1 -1
  107. package/dist/core/tools/bash.js +15 -1
  108. package/dist/core/tools/bash.js.map +1 -1
  109. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
  111. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  112. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/keybinding-hints.js +3 -1
  114. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  115. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  116. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  117. package/dist/modes/interactive/interactive-mode.js +137 -49
  118. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  119. package/dist/modes/interactive/working-status.d.ts +15 -0
  120. package/dist/modes/interactive/working-status.d.ts.map +1 -0
  121. package/dist/modes/interactive/working-status.js +60 -0
  122. package/dist/modes/interactive/working-status.js.map +1 -0
  123. package/docs/extensions.md +0 -1
  124. package/docs/index.md +0 -1
  125. package/docs/sdk.md +0 -1
  126. package/docs/settings.md +1 -29
  127. package/docs/termux.md +2 -2
  128. package/docs/usage.md +1 -1
  129. package/examples/README.md +1 -1
  130. package/examples/extensions/README.md +0 -1
  131. package/examples/extensions/overlay-qa-tests.ts +1 -1
  132. package/package.json +4 -4
  133. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
  134. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
  135. package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
  136. package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
  137. package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
  138. package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
  139. package/dist/core/extensions/builtin/background-task/index.js +0 -207
  140. package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
  141. package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
  142. package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
  143. package/dist/core/extensions/builtin/background-task/manager.js +0 -114
  144. package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
  145. package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
  146. package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
  147. package/dist/core/extensions/builtin/background-task/notification.js +0 -105
  148. package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
  149. package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
  150. package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
  151. package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
  152. package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
  153. package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
  154. package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
  155. package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
  156. package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
  157. package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
  158. package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
  159. package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
  160. package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
  161. package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
  162. package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
  163. package/dist/core/extensions/builtin/background-task/types.js +0 -32
  164. package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
  165. package/docs/agents.md +0 -348
  166. package/examples/extensions/subagent/README.md +0 -172
  167. package/examples/extensions/subagent/agents/planner.md +0 -37
  168. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  169. package/examples/extensions/subagent/agents/scout.md +0 -50
  170. package/examples/extensions/subagent/agents/worker.md +0 -24
  171. package/examples/extensions/subagent/agents.ts +0 -126
  172. package/examples/extensions/subagent/index.ts +0 -987
  173. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  174. package/examples/extensions/subagent/prompts/implement.md +0 -10
  175. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
@@ -0,0 +1,15 @@
1
+ type WorkingStatusTextFrameStyle = {
2
+ base: (text: string) => string;
3
+ glow: (text: string) => string;
4
+ highlight: (text: string) => string;
5
+ shimmer?: (text: string, intensity: number) => string;
6
+ };
7
+ type WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {
8
+ suffix: (text: string) => string;
9
+ };
10
+ export declare function formatWorkingElapsedSeconds(elapsedSeconds: number): string;
11
+ export declare function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string): string;
12
+ export declare function formatWorkingStatusTextFrame(statusMessage: string, animationElapsedMs: number, style: WorkingStatusTextFrameStyle): string;
13
+ export declare function formatWorkingStatusMessageFrame(message: string, elapsedSeconds: number, interruptKey: string, animationElapsedMs: number, style: WorkingStatusMessageFrameStyle): string;
14
+ export {};
15
+ //# sourceMappingURL=working-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-status.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"AAIA,KAAK,2BAA2B,GAAG;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CACtD,CAAC;AAEF,KAAK,8BAA8B,GAAG,2BAA2B,GAAG;IACnE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhH;AAED,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,2BAA2B,GAChC,MAAM,CAsCR;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,8BAA8B,GACnC,MAAM,CAGR","sourcesContent":["const WORKING_STATUS_MESSAGE_SHIMMER_PADDING = 10;\nconst WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH = 5;\nconst WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS = 2_000;\n\ntype WorkingStatusTextFrameStyle = {\n\tbase: (text: string) => string;\n\tglow: (text: string) => string;\n\thighlight: (text: string) => string;\n\tshimmer?: (text: string, intensity: number) => string;\n};\n\ntype WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {\n\tsuffix: (text: string) => string;\n};\n\nexport function formatWorkingElapsedSeconds(elapsedSeconds: number): string {\n\tconst totalSeconds = Math.max(0, Math.floor(elapsedSeconds));\n\tconst seconds = totalSeconds % 60;\n\tconst totalMinutes = Math.floor(totalSeconds / 60);\n\tif (totalSeconds < 60) {\n\t\treturn `${totalSeconds}s`;\n\t}\n\tif (totalSeconds < 3600) {\n\t\treturn `${totalMinutes}m ${seconds.toString().padStart(2, \"0\")}s`;\n\t}\n\tconst hours = Math.floor(totalMinutes / 60);\n\tconst minutes = totalMinutes % 60;\n\treturn `${hours}h ${minutes.toString().padStart(2, \"0\")}m ${seconds.toString().padStart(2, \"0\")}s`;\n}\n\nexport function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string): string {\n\treturn `${message} (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n}\n\nexport function formatWorkingStatusTextFrame(\n\tstatusMessage: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusTextFrameStyle,\n): string {\n\tconst chars = Array.from(statusMessage);\n\tif (chars.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst period = chars.length + WORKING_STATUS_MESSAGE_SHIMMER_PADDING * 2;\n\tconst sweepProgress =\n\t\t((Math.max(0, animationElapsedMs) % WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) /\n\t\t\tWORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) *\n\t\tperiod;\n\n\treturn chars\n\t\t.map((char, index) => {\n\t\t\tif (char === \" \") {\n\t\t\t\treturn char;\n\t\t\t}\n\t\t\tconst distance = Math.abs(index + WORKING_STATUS_MESSAGE_SHIMMER_PADDING - sweepProgress);\n\t\t\tif (distance > WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH) {\n\t\t\t\tif (style.shimmer) {\n\t\t\t\t\treturn style.shimmer(char, 0);\n\t\t\t\t}\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\n\t\t\tconst intensity = 0.5 * (1 + Math.cos(Math.PI * (distance / WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH)));\n\t\t\tif (style.shimmer) {\n\t\t\t\treturn style.shimmer(char, intensity);\n\t\t\t}\n\t\t\tif (intensity < 0.2) {\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\t\t\tif (intensity < 0.6) {\n\t\t\t\treturn style.glow(char);\n\t\t\t}\n\t\t\treturn style.highlight(char);\n\t\t})\n\t\t.join(\"\");\n}\n\nexport function formatWorkingStatusMessageFrame(\n\tmessage: string,\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n): string {\n\tconst suffix = ` (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n\treturn `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n"]}
@@ -0,0 +1,60 @@
1
+ const WORKING_STATUS_MESSAGE_SHIMMER_PADDING = 10;
2
+ const WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH = 5;
3
+ const WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS = 2_000;
4
+ export function formatWorkingElapsedSeconds(elapsedSeconds) {
5
+ const totalSeconds = Math.max(0, Math.floor(elapsedSeconds));
6
+ const seconds = totalSeconds % 60;
7
+ const totalMinutes = Math.floor(totalSeconds / 60);
8
+ if (totalSeconds < 60) {
9
+ return `${totalSeconds}s`;
10
+ }
11
+ if (totalSeconds < 3600) {
12
+ return `${totalMinutes}m ${seconds.toString().padStart(2, "0")}s`;
13
+ }
14
+ const hours = Math.floor(totalMinutes / 60);
15
+ const minutes = totalMinutes % 60;
16
+ return `${hours}h ${minutes.toString().padStart(2, "0")}m ${seconds.toString().padStart(2, "0")}s`;
17
+ }
18
+ export function formatWorkingStatusMessage(message, elapsedSeconds, interruptKey) {
19
+ return `${message} (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;
20
+ }
21
+ export function formatWorkingStatusTextFrame(statusMessage, animationElapsedMs, style) {
22
+ const chars = Array.from(statusMessage);
23
+ if (chars.length === 0) {
24
+ return "";
25
+ }
26
+ const period = chars.length + WORKING_STATUS_MESSAGE_SHIMMER_PADDING * 2;
27
+ const sweepProgress = ((Math.max(0, animationElapsedMs) % WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) /
28
+ WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) *
29
+ period;
30
+ return chars
31
+ .map((char, index) => {
32
+ if (char === " ") {
33
+ return char;
34
+ }
35
+ const distance = Math.abs(index + WORKING_STATUS_MESSAGE_SHIMMER_PADDING - sweepProgress);
36
+ if (distance > WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH) {
37
+ if (style.shimmer) {
38
+ return style.shimmer(char, 0);
39
+ }
40
+ return style.base(char);
41
+ }
42
+ const intensity = 0.5 * (1 + Math.cos(Math.PI * (distance / WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH)));
43
+ if (style.shimmer) {
44
+ return style.shimmer(char, intensity);
45
+ }
46
+ if (intensity < 0.2) {
47
+ return style.base(char);
48
+ }
49
+ if (intensity < 0.6) {
50
+ return style.glow(char);
51
+ }
52
+ return style.highlight(char);
53
+ })
54
+ .join("");
55
+ }
56
+ export function formatWorkingStatusMessageFrame(message, elapsedSeconds, interruptKey, animationElapsedMs, style) {
57
+ const suffix = ` (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;
58
+ return `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;
59
+ }
60
+ //# sourceMappingURL=working-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-status.js","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"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","sourcesContent":["const WORKING_STATUS_MESSAGE_SHIMMER_PADDING = 10;\nconst WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH = 5;\nconst WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS = 2_000;\n\ntype WorkingStatusTextFrameStyle = {\n\tbase: (text: string) => string;\n\tglow: (text: string) => string;\n\thighlight: (text: string) => string;\n\tshimmer?: (text: string, intensity: number) => string;\n};\n\ntype WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {\n\tsuffix: (text: string) => string;\n};\n\nexport function formatWorkingElapsedSeconds(elapsedSeconds: number): string {\n\tconst totalSeconds = Math.max(0, Math.floor(elapsedSeconds));\n\tconst seconds = totalSeconds % 60;\n\tconst totalMinutes = Math.floor(totalSeconds / 60);\n\tif (totalSeconds < 60) {\n\t\treturn `${totalSeconds}s`;\n\t}\n\tif (totalSeconds < 3600) {\n\t\treturn `${totalMinutes}m ${seconds.toString().padStart(2, \"0\")}s`;\n\t}\n\tconst hours = Math.floor(totalMinutes / 60);\n\tconst minutes = totalMinutes % 60;\n\treturn `${hours}h ${minutes.toString().padStart(2, \"0\")}m ${seconds.toString().padStart(2, \"0\")}s`;\n}\n\nexport function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string): string {\n\treturn `${message} (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n}\n\nexport function formatWorkingStatusTextFrame(\n\tstatusMessage: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusTextFrameStyle,\n): string {\n\tconst chars = Array.from(statusMessage);\n\tif (chars.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst period = chars.length + WORKING_STATUS_MESSAGE_SHIMMER_PADDING * 2;\n\tconst sweepProgress =\n\t\t((Math.max(0, animationElapsedMs) % WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) /\n\t\t\tWORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) *\n\t\tperiod;\n\n\treturn chars\n\t\t.map((char, index) => {\n\t\t\tif (char === \" \") {\n\t\t\t\treturn char;\n\t\t\t}\n\t\t\tconst distance = Math.abs(index + WORKING_STATUS_MESSAGE_SHIMMER_PADDING - sweepProgress);\n\t\t\tif (distance > WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH) {\n\t\t\t\tif (style.shimmer) {\n\t\t\t\t\treturn style.shimmer(char, 0);\n\t\t\t\t}\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\n\t\t\tconst intensity = 0.5 * (1 + Math.cos(Math.PI * (distance / WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH)));\n\t\t\tif (style.shimmer) {\n\t\t\t\treturn style.shimmer(char, intensity);\n\t\t\t}\n\t\t\tif (intensity < 0.2) {\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\t\t\tif (intensity < 0.6) {\n\t\t\t\treturn style.glow(char);\n\t\t\t}\n\t\t\treturn style.highlight(char);\n\t\t})\n\t\t.join(\"\");\n}\n\nexport function formatWorkingStatusMessageFrame(\n\tmessage: string,\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n): string {\n\tconst suffix = ` (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n\treturn `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n"]}
@@ -2576,7 +2576,6 @@ All examples in [examples/extensions/](../examples/extensions/).
2576
2576
  | `ssh.ts` | SSH remote execution | `registerFlag`, `on("user_bash")`, `on("before_agent_start")`, tool operations |
2577
2577
  | `interactive-shell.ts` | Persistent shell session | `on("user_bash")` |
2578
2578
  | `sandbox/` | Sandboxed tool execution | Tool operations |
2579
- | `subagent/` | Spawn sub-agents | `registerTool`, `exec` |
2580
2579
  | **Games** |||
2581
2580
  | `snake.ts` | Snake game | `registerCommand`, `ui.custom`, keyboard handling |
2582
2581
  | `space-invaders.ts` | Space Invaders game | `registerCommand`, `ui.custom` |
package/docs/index.md CHANGED
@@ -51,7 +51,6 @@ For the full first-run flow, see [Quickstart](quickstart.md).
51
51
  ## Reference
52
52
 
53
53
  - [Session format](session-format.md) - JSONL session file format, entry types, and SessionManager API.
54
- - [Agents](agents.md) - subagent profiles, custom agent definitions, and permission rules.
55
54
 
56
55
  ## Platform setup
57
56
 
package/docs/sdk.md CHANGED
@@ -8,7 +8,6 @@ The SDK provides programmatic access to pi's agent capabilities. Use it to embed
8
8
  - Build a custom UI (web, desktop, mobile)
9
9
  - Integrate agent capabilities into existing applications
10
10
  - Create automated pipelines with agent reasoning
11
- - Build custom tools that spawn sub-agents
12
11
  - Test agent behavior programmatically
13
12
 
14
13
  See [examples/sdk/](../examples/sdk/) for working examples from minimal to full control.
package/docs/settings.md CHANGED
@@ -266,28 +266,6 @@ Object form filters which resources to load:
266
266
 
267
267
  See [packages.md](packages.md) for package management details.
268
268
 
269
- ### Agent Defaults
270
-
271
- | Setting | Type | Default | Description |
272
- |---------|------|---------|-------------|
273
- | `agentDefaults.permission` | object | `{}` | Default tool permissions applied to all agents (lowest priority) |
274
- | `agentDefaults.model` | string | - | Default model ID for agents spawned via `task()` |
275
-
276
- Permission values: `"allow"`, `"deny"`, `"ask"`. See [agents.md](agents.md) for details.
277
-
278
- ```json
279
- {
280
- "agentDefaults": {
281
- "permission": {
282
- "edit": "ask",
283
- "write": "ask",
284
- "bash": "allow"
285
- },
286
- "model": "anthropic/claude-haiku-4-5"
287
- }
288
- }
289
- ```
290
-
291
269
  ## Example
292
270
 
293
271
  ```json
@@ -310,13 +288,7 @@ Permission values: `"allow"`, `"deny"`, `"ask"`. See [agents.md](agents.md) for
310
288
  "warnings": {
311
289
  "anthropicExtraUsage": true
312
290
  },
313
- "packages": ["pi-skills"],
314
- "agentDefaults": {
315
- "permission": {
316
- "edit": "ask",
317
- "write": "ask"
318
- }
319
- }
291
+ "packages": ["pi-skills"]
320
292
  }
321
293
  ```
322
294
 
package/docs/termux.md CHANGED
@@ -36,7 +36,7 @@ Image clipboard is not supported on Termux (the `ctrl+v` image paste feature wil
36
36
 
37
37
  Create `~/.senpi/agent/AGENTS.md` to help the agent understand the Termux environment:
38
38
 
39
- ```markdown
39
+ ````markdown
40
40
  # Agent Environment: Termux on Android
41
41
 
42
42
  ## Location
@@ -91,7 +91,7 @@ termux-camera-photo out.jpg # Take photo
91
91
  - Termux:API app must be installed for `termux-*` commands
92
92
  - Use `pkg install termux-api` for the command-line tools
93
93
  - Storage permission needed for `/storage/emulated/0` access
94
- ```
94
+ ````
95
95
 
96
96
  ## Limitations
97
97
 
package/docs/usage.md CHANGED
@@ -272,6 +272,6 @@ pi --tools read,grep,find,ls -p "Review the code"
272
272
 
273
273
  Pi keeps the core small and pushes workflow-specific behavior into extensions, skills, prompt templates, and packages.
274
274
 
275
- It intentionally does not include built-in MCP, sub-agents, permission popups, plan mode, to-dos, or background bash. You can build or install those workflows as extensions or packages, or use external tools such as containers and tmux.
275
+ It intentionally does not include built-in MCP, permission popups, plan mode, to-dos, or long-running shell orchestration. You can build or install those workflows as extensions or packages, or use external tools such as containers and tmux.
276
276
 
277
277
  For the full rationale, read the [blog post](https://mariozechner.at/posts/2025-11-30-pi-coding-agent/).
@@ -10,7 +10,7 @@ Programmatic usage via `createAgentSession()`. Shows how to customize models, pr
10
10
  ### [extensions/](extensions/)
11
11
  Example extensions demonstrating:
12
12
  - Lifecycle event handlers (tool interception, safety gates, context modifications)
13
- - Custom tools (todo lists, questions, subagents, output truncation)
13
+ - Custom tools (todo lists, questions, output truncation)
14
14
  - Commands and keyboard shortcuts
15
15
  - Custom UI (footers, headers, editors, overlays)
16
16
  - Git integration (checkpoints, auto-commit)
@@ -39,7 +39,6 @@ cp permission-gate.ts ~/.senpi/agent/extensions/
39
39
  | `minimal-mode.ts` | Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode) |
40
40
  | `truncated-tool.ts` | Wraps ripgrep with proper output truncation (50KB/2000 lines) |
41
41
  | `ssh.ts` | Delegate all tools to a remote machine via SSH using pluggable operations |
42
- | `subagent/` | Delegate tasks to specialized subagents with isolated context windows |
43
42
 
44
43
  ### Commands & UI
45
44
 
@@ -469,7 +469,7 @@ class StreamingOverflowComponent extends BaseOverlay {
469
469
  "-c",
470
470
  `
471
471
  echo "Starting streaming overflow test (30+ seconds)..."
472
- echo "This simulates subagent output with colors, hyperlinks, and long paths"
472
+ echo "This simulates streaming output with colors, hyperlinks, and long paths"
473
473
  echo ""
474
474
  for i in $(seq 1 100); do
475
475
  # Simulate long file paths with OSC 8 hyperlinks (clickable) - tests width overflow
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.5.14",
3
+ "version": "2026.5.15-3",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -40,9 +40,9 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@anthropic-ai/sdk": "^0.91.1",
43
- "@earendil-works/pi-agent-core": "^2026.5.14",
44
- "@earendil-works/pi-ai": "^2026.5.14",
45
- "@earendil-works/pi-tui": "^2026.5.14",
43
+ "@earendil-works/pi-agent-core": "^2026.5.15-3",
44
+ "@earendil-works/pi-ai": "^2026.5.15-3",
45
+ "@earendil-works/pi-tui": "^2026.5.15-3",
46
46
  "@silvia-odwyer/photon-node": "^0.3.4",
47
47
  "chalk": "^5.5.0",
48
48
  "diff": "^8.0.2",
@@ -1,10 +0,0 @@
1
- import { Type } from "typebox";
2
- import type { ToolDefinition } from "../../types.js";
3
- import type { BackgroundManager } from "./manager.js";
4
- declare const BackgroundCancelParams: Type.TObject<{
5
- taskId: Type.TOptional<Type.TString>;
6
- all: Type.TOptional<Type.TBoolean>;
7
- }>;
8
- export declare function createBackgroundCancelTool(manager: BackgroundManager): ToolDefinition<typeof BackgroundCancelParams>;
9
- export {};
10
- //# sourceMappingURL=cancel-tool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cancel-tool.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/cancel-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,QAAA,MAAM,sBAAsB;;;EAG1B,CAAC;AAEH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAmHpH","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../types.js\";\nimport type { BackgroundManager } from \"./manager.js\";\n\nconst BackgroundCancelParams = Type.Object({\n\ttaskId: Type.Optional(Type.String({ description: \"Task ID to cancel (required if all=false)\" })),\n\tall: Type.Optional(Type.Boolean({ description: \"Cancel all running background tasks (default: false)\" })),\n});\n\nexport function createBackgroundCancelTool(manager: BackgroundManager): ToolDefinition<typeof BackgroundCancelParams> {\n\treturn {\n\t\tname: \"background_cancel\",\n\t\tlabel: \"BackgroundCancel\",\n\t\tdescription: \"Cancel a background task by taskId, or cancel all running tasks with all=true.\",\n\t\tpromptSnippet: \"Cancel background tasks by ID or cancel all running tasks.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use this tool to cancel pending or running background tasks.\",\n\t\t\t\"Provide taskId to cancel a specific task, or set all=true to cancel all active tasks.\",\n\t\t\t\"Cancelled tasks will be marked as 'cancelled' and cannot be resumed.\",\n\t\t],\n\t\tparameters: BackgroundCancelParams,\n\t\tasync execute(_toolCallId, params, _signal, _onUpdate, _ctx) {\n\t\t\tif (!params.taskId && !params.all) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Error: Provide taskId or set all=true\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.all) {\n\t\t\t\tconst cancelled = manager.cancelAll();\n\n\t\t\t\tif (cancelled.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"No active tasks to cancel\" }],\n\t\t\t\t\t\tdetails: { cancelledTasks: [] },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tfor (const task of cancelled) {\n\t\t\t\t\tif (task.pid !== undefined) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tprocess.kill(task.pid, \"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* process may already be dead */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst lines: string[] = [`Cancelled ${cancelled.length} task(s):`];\n\t\t\t\tfor (const task of cancelled) {\n\t\t\t\t\tlines.push(`- ${task.id}: ${task.description}`);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: lines.join(\"\\n\") }],\n\t\t\t\t\tdetails: { cancelledTasks: cancelled },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.taskId) {\n\t\t\t\tconst task = manager.getTask(params.taskId);\n\t\t\t\tif (!task) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Error: Task not found: ${params.taskId}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst wasCancelled = manager.cancelTask(params.taskId);\n\n\t\t\t\tif (!wasCancelled) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Task ${params.taskId} is not active (status: ${task.status})` }],\n\t\t\t\t\t\tdetails: { task },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (task.pid !== undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(task.pid, \"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* process may already be dead */\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst cancelledTask = manager.getTask(params.taskId);\n\t\t\t\tif (!cancelledTask) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Cancelled task ${params.taskId}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Cancelled task ${params.taskId}: ${cancelledTask.description}` }],\n\t\t\t\t\tdetails: { task: cancelledTask },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: \"Error: Provide taskId or set all=true\" }],\n\t\t\t\tdetails: undefined,\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\tif (args.all) {\n\t\t\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(\"BackgroundCancel \")) + theme.fg(\"accent\", \"[all]\"), 0, 0);\n\t\t\t}\n\t\t\treturn new Text(\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"BackgroundCancel \")) + theme.fg(\"accent\", args.taskId ?? \"unknown\"),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t},\n\t\trenderResult(result, _options, theme) {\n\t\t\tconst firstContent = result.content[0];\n\t\t\tconst text = firstContent?.type === \"text\" ? firstContent.text : \"(no output)\";\n\t\t\treturn new Text(theme.fg(\"muted\", text), 0, 0);\n\t\t},\n\t};\n}\n"]}
@@ -1,109 +0,0 @@
1
- import { Text } from "@earendil-works/pi-tui";
2
- import { Type } from "typebox";
3
- const BackgroundCancelParams = Type.Object({
4
- taskId: Type.Optional(Type.String({ description: "Task ID to cancel (required if all=false)" })),
5
- all: Type.Optional(Type.Boolean({ description: "Cancel all running background tasks (default: false)" })),
6
- });
7
- export function createBackgroundCancelTool(manager) {
8
- return {
9
- name: "background_cancel",
10
- label: "BackgroundCancel",
11
- description: "Cancel a background task by taskId, or cancel all running tasks with all=true.",
12
- promptSnippet: "Cancel background tasks by ID or cancel all running tasks.",
13
- promptGuidelines: [
14
- "Use this tool to cancel pending or running background tasks.",
15
- "Provide taskId to cancel a specific task, or set all=true to cancel all active tasks.",
16
- "Cancelled tasks will be marked as 'cancelled' and cannot be resumed.",
17
- ],
18
- parameters: BackgroundCancelParams,
19
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
20
- if (!params.taskId && !params.all) {
21
- return {
22
- content: [{ type: "text", text: "Error: Provide taskId or set all=true" }],
23
- details: undefined,
24
- isError: true,
25
- };
26
- }
27
- if (params.all) {
28
- const cancelled = manager.cancelAll();
29
- if (cancelled.length === 0) {
30
- return {
31
- content: [{ type: "text", text: "No active tasks to cancel" }],
32
- details: { cancelledTasks: [] },
33
- };
34
- }
35
- for (const task of cancelled) {
36
- if (task.pid !== undefined) {
37
- try {
38
- process.kill(task.pid, "SIGTERM");
39
- }
40
- catch {
41
- /* process may already be dead */
42
- }
43
- }
44
- }
45
- const lines = [`Cancelled ${cancelled.length} task(s):`];
46
- for (const task of cancelled) {
47
- lines.push(`- ${task.id}: ${task.description}`);
48
- }
49
- return {
50
- content: [{ type: "text", text: lines.join("\n") }],
51
- details: { cancelledTasks: cancelled },
52
- };
53
- }
54
- if (params.taskId) {
55
- const task = manager.getTask(params.taskId);
56
- if (!task) {
57
- return {
58
- content: [{ type: "text", text: `Error: Task not found: ${params.taskId}` }],
59
- details: undefined,
60
- isError: true,
61
- };
62
- }
63
- const wasCancelled = manager.cancelTask(params.taskId);
64
- if (!wasCancelled) {
65
- return {
66
- content: [{ type: "text", text: `Task ${params.taskId} is not active (status: ${task.status})` }],
67
- details: { task },
68
- };
69
- }
70
- if (task.pid !== undefined) {
71
- try {
72
- process.kill(task.pid, "SIGTERM");
73
- }
74
- catch {
75
- /* process may already be dead */
76
- }
77
- }
78
- const cancelledTask = manager.getTask(params.taskId);
79
- if (!cancelledTask) {
80
- return {
81
- content: [{ type: "text", text: `Cancelled task ${params.taskId}` }],
82
- details: undefined,
83
- };
84
- }
85
- return {
86
- content: [{ type: "text", text: `Cancelled task ${params.taskId}: ${cancelledTask.description}` }],
87
- details: { task: cancelledTask },
88
- };
89
- }
90
- return {
91
- content: [{ type: "text", text: "Error: Provide taskId or set all=true" }],
92
- details: undefined,
93
- isError: true,
94
- };
95
- },
96
- renderCall(args, theme) {
97
- if (args.all) {
98
- return new Text(theme.fg("toolTitle", theme.bold("BackgroundCancel ")) + theme.fg("accent", "[all]"), 0, 0);
99
- }
100
- return new Text(theme.fg("toolTitle", theme.bold("BackgroundCancel ")) + theme.fg("accent", args.taskId ?? "unknown"), 0, 0);
101
- },
102
- renderResult(result, _options, theme) {
103
- const firstContent = result.content[0];
104
- const text = firstContent?.type === "text" ? firstContent.text : "(no output)";
105
- return new Text(theme.fg("muted", text), 0, 0);
106
- },
107
- };
108
- }
109
- //# sourceMappingURL=cancel-tool.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cancel-tool.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/cancel-tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../types.js\";\nimport type { BackgroundManager } from \"./manager.js\";\n\nconst BackgroundCancelParams = Type.Object({\n\ttaskId: Type.Optional(Type.String({ description: \"Task ID to cancel (required if all=false)\" })),\n\tall: Type.Optional(Type.Boolean({ description: \"Cancel all running background tasks (default: false)\" })),\n});\n\nexport function createBackgroundCancelTool(manager: BackgroundManager): ToolDefinition<typeof BackgroundCancelParams> {\n\treturn {\n\t\tname: \"background_cancel\",\n\t\tlabel: \"BackgroundCancel\",\n\t\tdescription: \"Cancel a background task by taskId, or cancel all running tasks with all=true.\",\n\t\tpromptSnippet: \"Cancel background tasks by ID or cancel all running tasks.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use this tool to cancel pending or running background tasks.\",\n\t\t\t\"Provide taskId to cancel a specific task, or set all=true to cancel all active tasks.\",\n\t\t\t\"Cancelled tasks will be marked as 'cancelled' and cannot be resumed.\",\n\t\t],\n\t\tparameters: BackgroundCancelParams,\n\t\tasync execute(_toolCallId, params, _signal, _onUpdate, _ctx) {\n\t\t\tif (!params.taskId && !params.all) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Error: Provide taskId or set all=true\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.all) {\n\t\t\t\tconst cancelled = manager.cancelAll();\n\n\t\t\t\tif (cancelled.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"No active tasks to cancel\" }],\n\t\t\t\t\t\tdetails: { cancelledTasks: [] },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tfor (const task of cancelled) {\n\t\t\t\t\tif (task.pid !== undefined) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tprocess.kill(task.pid, \"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* process may already be dead */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst lines: string[] = [`Cancelled ${cancelled.length} task(s):`];\n\t\t\t\tfor (const task of cancelled) {\n\t\t\t\t\tlines.push(`- ${task.id}: ${task.description}`);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: lines.join(\"\\n\") }],\n\t\t\t\t\tdetails: { cancelledTasks: cancelled },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.taskId) {\n\t\t\t\tconst task = manager.getTask(params.taskId);\n\t\t\t\tif (!task) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Error: Task not found: ${params.taskId}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst wasCancelled = manager.cancelTask(params.taskId);\n\n\t\t\t\tif (!wasCancelled) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Task ${params.taskId} is not active (status: ${task.status})` }],\n\t\t\t\t\t\tdetails: { task },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (task.pid !== undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(task.pid, \"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* process may already be dead */\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst cancelledTask = manager.getTask(params.taskId);\n\t\t\t\tif (!cancelledTask) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Cancelled task ${params.taskId}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Cancelled task ${params.taskId}: ${cancelledTask.description}` }],\n\t\t\t\t\tdetails: { task: cancelledTask },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: \"Error: Provide taskId or set all=true\" }],\n\t\t\t\tdetails: undefined,\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\tif (args.all) {\n\t\t\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(\"BackgroundCancel \")) + theme.fg(\"accent\", \"[all]\"), 0, 0);\n\t\t\t}\n\t\t\treturn new Text(\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"BackgroundCancel \")) + theme.fg(\"accent\", args.taskId ?? \"unknown\"),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t},\n\t\trenderResult(result, _options, theme) {\n\t\t\tconst firstContent = result.content[0];\n\t\t\tconst text = firstContent?.type === \"text\" ? firstContent.text : \"(no output)\";\n\t\t\treturn new Text(theme.fg(\"muted\", text), 0, 0);\n\t\t},\n\t};\n}\n"]}
@@ -1,3 +0,0 @@
1
- import type { ExtensionAPI } from "../../types.js";
2
- export default function backgroundTaskExtension(pi: ExtensionAPI): void;
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AA8LrE,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAiEtE","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.js\";\nimport { sendBuiltinCustomMessage } from \"../system-messages.js\";\nimport { createBackgroundCancelTool } from \"./cancel-tool.js\";\nimport { BackgroundManager, getWidgetLines } from \"./manager.js\";\nimport { sendCompletionNotification } from \"./notification.js\";\nimport { createBackgroundOutputTool } from \"./output-tool.js\";\nimport { spawnSubagent } from \"./spawner.js\";\nimport { createTaskTool } from \"./task-tool.js\";\nimport { type BackgroundTask, TASK_ENTRY_TYPE } from \"./types.js\";\n\ntype SessionEntry = {\n\ttype: string;\n\tcustomType?: string;\n\tdata?: unknown;\n\tmessage?: unknown;\n};\n\nfunction isTaskStatus(value: unknown): value is BackgroundTask[\"status\"] {\n\treturn (\n\t\tvalue === \"pending\" || value === \"running\" || value === \"completed\" || value === \"error\" || value === \"cancelled\"\n\t);\n}\n\nfunction parseDate(value: unknown): Date | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (value instanceof Date) {\n\t\treturn Number.isNaN(value.getTime()) ? undefined : value;\n\t}\n\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\n\tconst parsedDate = new Date(value);\n\treturn Number.isNaN(parsedDate.getTime()) ? undefined : parsedDate;\n}\n\nfunction parseBackgroundTask(value: unknown): BackgroundTask | undefined {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn undefined;\n\t}\n\n\tconst taskRecord = value as Record<string, unknown>;\n\tconst startedAt = parseDate(taskRecord.startedAt);\n\tconst completedAt = parseDate(taskRecord.completedAt);\n\n\tif (\n\t\ttypeof taskRecord.id !== \"string\" ||\n\t\ttypeof taskRecord.description !== \"string\" ||\n\t\ttypeof taskRecord.prompt !== \"string\" ||\n\t\t!isTaskStatus(taskRecord.status) ||\n\t\t(taskRecord.model !== undefined && typeof taskRecord.model !== \"string\") ||\n\t\t(taskRecord.agentType !== undefined && typeof taskRecord.agentType !== \"string\") ||\n\t\t(taskRecord.pid !== undefined && typeof taskRecord.pid !== \"number\") ||\n\t\t(taskRecord.sessionPath !== undefined && typeof taskRecord.sessionPath !== \"string\") ||\n\t\t(taskRecord.activeToolNames !== undefined &&\n\t\t\t(!Array.isArray(taskRecord.activeToolNames) ||\n\t\t\t\ttaskRecord.activeToolNames.some((toolName) => typeof toolName !== \"string\"))) ||\n\t\tstartedAt === undefined ||\n\t\t(taskRecord.completedAt !== undefined && completedAt === undefined) ||\n\t\t(taskRecord.result !== undefined && typeof taskRecord.result !== \"string\") ||\n\t\t(taskRecord.error !== undefined && typeof taskRecord.error !== \"string\") ||\n\t\ttypeof taskRecord.parentSessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tid: taskRecord.id,\n\t\tdescription: taskRecord.description,\n\t\tprompt: taskRecord.prompt,\n\t\tmodel: taskRecord.model,\n\t\tagentType: taskRecord.agentType,\n\t\tstatus: taskRecord.status,\n\t\tpid: taskRecord.pid,\n\t\tsessionPath: taskRecord.sessionPath,\n\t\tactiveToolNames: Array.isArray(taskRecord.activeToolNames) ? taskRecord.activeToolNames : [],\n\t\tstartedAt,\n\t\tcompletedAt,\n\t\tresult: taskRecord.result,\n\t\terror: taskRecord.error,\n\t\tparentSessionId: taskRecord.parentSessionId,\n\t};\n}\n\nfunction isTerminalTask(task: BackgroundTask): boolean {\n\treturn task.status === \"completed\" || task.status === \"error\" || task.status === \"cancelled\";\n}\n\nfunction getRestoredTasks(entries: SessionEntry[]): BackgroundTask[] {\n\tconst restoredTasks = new Map<string, BackgroundTask>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== TASK_ENTRY_TYPE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst task = parseBackgroundTask(entry.data);\n\t\tif (!task) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isTerminalTask(task)) {\n\t\t\trestoredTasks.set(task.id, task);\n\t\t} else {\n\t\t\trestoredTasks.delete(task.id);\n\t\t}\n\t}\n\n\treturn Array.from(restoredTasks.values());\n}\n\nfunction getCompletionTaskId(message: { content?: unknown }): string | undefined {\n\tif (!Array.isArray(message.content)) {\n\t\treturn undefined;\n\t}\n\n\tfor (const part of message.content) {\n\t\tif (typeof part !== \"object\" || part === null) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst contentPart = part as Record<string, unknown>;\n\t\tif (contentPart.type !== \"text\" || typeof contentPart.text !== \"string\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst match = contentPart.text.match(/\\b(bg_[0-9a-f]{8})\\b/);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1];\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nclass ObservableBackgroundManager extends BackgroundManager {\n\tconstructor(\n\t\tprivate readonly onChange: () => void,\n\t\tprivate readonly persistTask: (task: BackgroundTask) => void,\n\t) {\n\t\tsuper();\n\t}\n\n\toverride launch(task: Omit<BackgroundTask, \"id\" | \"status\" | \"startedAt\">): BackgroundTask {\n\t\tconst launchedTask = super.launch(task);\n\t\tthis.onChange();\n\t\treturn launchedTask;\n\t}\n\n\toverride updateTask(id: string, updates: Partial<BackgroundTask>): void {\n\t\tsuper.updateTask(id, updates);\n\t\tthis.onChange();\n\t}\n\n\toverride cancelTask(id: string): boolean {\n\t\tconst wasCancelled = super.cancelTask(id);\n\t\tif (wasCancelled) {\n\t\t\tconst cancelledTask = this.getTask(id);\n\t\t\tif (cancelledTask) {\n\t\t\t\tthis.persistTask(cancelledTask);\n\t\t\t}\n\t\t\tthis.onChange();\n\t\t}\n\t\treturn wasCancelled;\n\t}\n\n\toverride cancelAll(): BackgroundTask[] {\n\t\tconst cancelledTasks = super.cancelAll();\n\t\tfor (const task of cancelledTasks) {\n\t\t\tthis.persistTask(task);\n\t\t}\n\t\tif (cancelledTasks.length > 0) {\n\t\t\tthis.onChange();\n\t\t}\n\t\treturn cancelledTasks;\n\t}\n\n\treplaceTasks(tasks: BackgroundTask[]): void {\n\t\tthis.clearTasks();\n\t\tfor (const task of tasks) {\n\t\t\tthis.restoreTask(task);\n\t\t}\n\t\tthis.onChange();\n\t}\n}\n\nexport default function backgroundTaskExtension(pi: ExtensionAPI): void {\n\tlet currentContext: ExtensionContext | undefined;\n\n\tconst syncWidget = (): void => {\n\t\tcurrentContext?.ui.setWidget(\"background-tasks\", getWidgetLines(manager));\n\t};\n\n\tconst manager = new ObservableBackgroundManager(\n\t\t() => {\n\t\t\tsyncWidget();\n\t\t},\n\t\t(task) => {\n\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, task);\n\t\t},\n\t);\n\n\tconst syncFromSession = (ctx: ExtensionContext): void => {\n\t\tcurrentContext = ctx;\n\t\tmanager.replaceTasks(getRestoredTasks(ctx.sessionManager.getBranch() as SessionEntry[]));\n\t};\n\n\tconst notifyingPi = {\n\t\t...pi,\n\t\tsendMessage(message, options) {\n\t\t\tif (message.customType === \"background-task.complete\") {\n\t\t\t\tconst taskId = getCompletionTaskId(message);\n\t\t\t\tif (taskId) {\n\t\t\t\t\tconst task = manager.getTask(taskId);\n\t\t\t\t\tif (task && isTerminalTask(task)) {\n\t\t\t\t\t\tsendCompletionNotification(pi, task, manager);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tsendBuiltinCustomMessage(pi, \"background-task.notification\", message, options);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tpi.sendMessage(message, options);\n\t\t},\n\t} satisfies ExtensionAPI;\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsyncFromSession(ctx);\n\t});\n\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tsyncFromSession(ctx);\n\t});\n\n\tpi.registerTool(createTaskTool(manager, spawnSubagent, notifyingPi));\n\tpi.registerTool(createBackgroundOutputTool(manager));\n\tpi.registerTool(createBackgroundCancelTool(manager));\n\n\tprocess.on(\"exit\", () => {\n\t\tfor (const task of manager.getActiveTasks()) {\n\t\t\tif (task.pid === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tprocess.kill(task.pid, \"SIGTERM\");\n\t\t\t} catch {}\n\t\t}\n\t});\n}\n"]}
@@ -1,207 +0,0 @@
1
- import { sendBuiltinCustomMessage } from "../system-messages.js";
2
- import { createBackgroundCancelTool } from "./cancel-tool.js";
3
- import { BackgroundManager, getWidgetLines } from "./manager.js";
4
- import { sendCompletionNotification } from "./notification.js";
5
- import { createBackgroundOutputTool } from "./output-tool.js";
6
- import { spawnSubagent } from "./spawner.js";
7
- import { createTaskTool } from "./task-tool.js";
8
- import { TASK_ENTRY_TYPE } from "./types.js";
9
- function isTaskStatus(value) {
10
- return (value === "pending" || value === "running" || value === "completed" || value === "error" || value === "cancelled");
11
- }
12
- function parseDate(value) {
13
- if (value === undefined) {
14
- return undefined;
15
- }
16
- if (value instanceof Date) {
17
- return Number.isNaN(value.getTime()) ? undefined : value;
18
- }
19
- if (typeof value !== "string") {
20
- return undefined;
21
- }
22
- const parsedDate = new Date(value);
23
- return Number.isNaN(parsedDate.getTime()) ? undefined : parsedDate;
24
- }
25
- function parseBackgroundTask(value) {
26
- if (typeof value !== "object" || value === null) {
27
- return undefined;
28
- }
29
- const taskRecord = value;
30
- const startedAt = parseDate(taskRecord.startedAt);
31
- const completedAt = parseDate(taskRecord.completedAt);
32
- if (typeof taskRecord.id !== "string" ||
33
- typeof taskRecord.description !== "string" ||
34
- typeof taskRecord.prompt !== "string" ||
35
- !isTaskStatus(taskRecord.status) ||
36
- (taskRecord.model !== undefined && typeof taskRecord.model !== "string") ||
37
- (taskRecord.agentType !== undefined && typeof taskRecord.agentType !== "string") ||
38
- (taskRecord.pid !== undefined && typeof taskRecord.pid !== "number") ||
39
- (taskRecord.sessionPath !== undefined && typeof taskRecord.sessionPath !== "string") ||
40
- (taskRecord.activeToolNames !== undefined &&
41
- (!Array.isArray(taskRecord.activeToolNames) ||
42
- taskRecord.activeToolNames.some((toolName) => typeof toolName !== "string"))) ||
43
- startedAt === undefined ||
44
- (taskRecord.completedAt !== undefined && completedAt === undefined) ||
45
- (taskRecord.result !== undefined && typeof taskRecord.result !== "string") ||
46
- (taskRecord.error !== undefined && typeof taskRecord.error !== "string") ||
47
- typeof taskRecord.parentSessionId !== "string") {
48
- return undefined;
49
- }
50
- return {
51
- id: taskRecord.id,
52
- description: taskRecord.description,
53
- prompt: taskRecord.prompt,
54
- model: taskRecord.model,
55
- agentType: taskRecord.agentType,
56
- status: taskRecord.status,
57
- pid: taskRecord.pid,
58
- sessionPath: taskRecord.sessionPath,
59
- activeToolNames: Array.isArray(taskRecord.activeToolNames) ? taskRecord.activeToolNames : [],
60
- startedAt,
61
- completedAt,
62
- result: taskRecord.result,
63
- error: taskRecord.error,
64
- parentSessionId: taskRecord.parentSessionId,
65
- };
66
- }
67
- function isTerminalTask(task) {
68
- return task.status === "completed" || task.status === "error" || task.status === "cancelled";
69
- }
70
- function getRestoredTasks(entries) {
71
- const restoredTasks = new Map();
72
- for (const entry of entries) {
73
- if (entry.type !== "custom" || entry.customType !== TASK_ENTRY_TYPE) {
74
- continue;
75
- }
76
- const task = parseBackgroundTask(entry.data);
77
- if (!task) {
78
- continue;
79
- }
80
- if (isTerminalTask(task)) {
81
- restoredTasks.set(task.id, task);
82
- }
83
- else {
84
- restoredTasks.delete(task.id);
85
- }
86
- }
87
- return Array.from(restoredTasks.values());
88
- }
89
- function getCompletionTaskId(message) {
90
- if (!Array.isArray(message.content)) {
91
- return undefined;
92
- }
93
- for (const part of message.content) {
94
- if (typeof part !== "object" || part === null) {
95
- continue;
96
- }
97
- const contentPart = part;
98
- if (contentPart.type !== "text" || typeof contentPart.text !== "string") {
99
- continue;
100
- }
101
- const match = contentPart.text.match(/\b(bg_[0-9a-f]{8})\b/);
102
- if (match?.[1]) {
103
- return match[1];
104
- }
105
- }
106
- return undefined;
107
- }
108
- class ObservableBackgroundManager extends BackgroundManager {
109
- onChange;
110
- persistTask;
111
- constructor(onChange, persistTask) {
112
- super();
113
- this.onChange = onChange;
114
- this.persistTask = persistTask;
115
- }
116
- launch(task) {
117
- const launchedTask = super.launch(task);
118
- this.onChange();
119
- return launchedTask;
120
- }
121
- updateTask(id, updates) {
122
- super.updateTask(id, updates);
123
- this.onChange();
124
- }
125
- cancelTask(id) {
126
- const wasCancelled = super.cancelTask(id);
127
- if (wasCancelled) {
128
- const cancelledTask = this.getTask(id);
129
- if (cancelledTask) {
130
- this.persistTask(cancelledTask);
131
- }
132
- this.onChange();
133
- }
134
- return wasCancelled;
135
- }
136
- cancelAll() {
137
- const cancelledTasks = super.cancelAll();
138
- for (const task of cancelledTasks) {
139
- this.persistTask(task);
140
- }
141
- if (cancelledTasks.length > 0) {
142
- this.onChange();
143
- }
144
- return cancelledTasks;
145
- }
146
- replaceTasks(tasks) {
147
- this.clearTasks();
148
- for (const task of tasks) {
149
- this.restoreTask(task);
150
- }
151
- this.onChange();
152
- }
153
- }
154
- export default function backgroundTaskExtension(pi) {
155
- let currentContext;
156
- const syncWidget = () => {
157
- currentContext?.ui.setWidget("background-tasks", getWidgetLines(manager));
158
- };
159
- const manager = new ObservableBackgroundManager(() => {
160
- syncWidget();
161
- }, (task) => {
162
- pi.appendEntry(TASK_ENTRY_TYPE, task);
163
- });
164
- const syncFromSession = (ctx) => {
165
- currentContext = ctx;
166
- manager.replaceTasks(getRestoredTasks(ctx.sessionManager.getBranch()));
167
- };
168
- const notifyingPi = {
169
- ...pi,
170
- sendMessage(message, options) {
171
- if (message.customType === "background-task.complete") {
172
- const taskId = getCompletionTaskId(message);
173
- if (taskId) {
174
- const task = manager.getTask(taskId);
175
- if (task && isTerminalTask(task)) {
176
- sendCompletionNotification(pi, task, manager);
177
- return;
178
- }
179
- }
180
- sendBuiltinCustomMessage(pi, "background-task.notification", message, options);
181
- return;
182
- }
183
- pi.sendMessage(message, options);
184
- },
185
- };
186
- pi.on("session_start", async (_event, ctx) => {
187
- syncFromSession(ctx);
188
- });
189
- pi.on("session_tree", async (_event, ctx) => {
190
- syncFromSession(ctx);
191
- });
192
- pi.registerTool(createTaskTool(manager, spawnSubagent, notifyingPi));
193
- pi.registerTool(createBackgroundOutputTool(manager));
194
- pi.registerTool(createBackgroundCancelTool(manager));
195
- process.on("exit", () => {
196
- for (const task of manager.getActiveTasks()) {
197
- if (task.pid === undefined) {
198
- continue;
199
- }
200
- try {
201
- process.kill(task.pid, "SIGTERM");
202
- }
203
- catch { }
204
- }
205
- });
206
- }
207
- //# sourceMappingURL=index.js.map