@selesai/code 0.9.10 → 0.9.11

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 (302) hide show
  1. package/CHANGELOG.md +260 -255
  2. package/dist/cli/args.js +13 -2
  3. package/dist/cli/args.test.js +8 -0
  4. package/dist/cli/credential-print.js +4 -4
  5. package/dist/cli.js +0 -0
  6. package/dist/core/agent-session-auto-handoff.test.js +24 -1
  7. package/dist/core/agent-session.d.ts +11 -4
  8. package/dist/core/agent-session.js +96 -35
  9. package/dist/core/compaction/branch-summarization.js +30 -30
  10. package/dist/core/compaction/compaction.js +81 -81
  11. package/dist/core/compaction/utils.js +2 -2
  12. package/dist/core/defaults.d.ts +1 -0
  13. package/dist/core/defaults.js +9 -0
  14. package/dist/core/export-html/template.css +1066 -1066
  15. package/dist/core/export-html/template.html +55 -55
  16. package/dist/core/export-html/template.js +1864 -1864
  17. package/dist/core/export-html/vendor/highlight.min.js +1212 -1212
  18. package/dist/core/export-html/vendor/marked.min.js +78 -78
  19. package/dist/core/extensions/loader.js +108 -35
  20. package/dist/core/extensions/loader.test.d.ts +1 -0
  21. package/dist/core/extensions/loader.test.js +21 -0
  22. package/dist/core/extensions/types.d.ts +12 -2
  23. package/dist/core/keybindings.d.ts +2 -2
  24. package/dist/core/messages.js +7 -7
  25. package/dist/core/model-resolver.d.ts +1 -0
  26. package/dist/core/model-resolver.js +10 -4
  27. package/dist/core/package-manager.js +4 -4
  28. package/dist/core/sdk.d.ts +5 -5
  29. package/dist/core/sdk.js +16 -4
  30. package/dist/core/settings-manager.d.ts +6 -0
  31. package/dist/core/settings-manager.js +22 -0
  32. package/dist/core/tools/bash.d.ts +18 -1
  33. package/dist/core/tools/bash.js +41 -23
  34. package/dist/core/tools/index.d.ts +4 -1
  35. package/dist/core/tools/index.js +9 -1
  36. package/dist/core/tools/powershell.d.ts +15 -0
  37. package/dist/core/tools/powershell.js +38 -0
  38. package/dist/core/tools/powershell.test.d.ts +1 -0
  39. package/dist/core/tools/powershell.test.js +20 -0
  40. package/dist/extensions/context-compaction-reminder.test.ts +82 -82
  41. package/dist/extensions/context-compaction-reminder.ts +28 -28
  42. package/dist/extensions/pi-intercom/LICENSE +21 -21
  43. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -83
  44. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -387
  45. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -114
  46. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -153
  47. package/dist/extensions/pi-intercom/broker/paths.ts +134 -134
  48. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -34
  49. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -21
  50. package/dist/extensions/pi-intercom/cwd.test.ts +40 -40
  51. package/dist/extensions/pi-intercom/cwd.ts +31 -31
  52. package/dist/extensions/pi-intercom/extension-api.ts +44 -44
  53. package/dist/extensions/pi-intercom/format-context.test.ts +31 -31
  54. package/dist/extensions/pi-intercom/format-context.ts +32 -32
  55. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -66
  56. package/dist/extensions/pi-intercom/ui/compose.ts +143 -143
  57. package/dist/extensions/pi-intercom/ui/session-list.ts +166 -166
  58. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +286 -286
  59. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +108 -108
  60. package/dist/extensions/pi-powerline-footer/separators.ts +57 -57
  61. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +47 -47
  62. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +39 -39
  63. package/dist/extensions/pi-powerline-footer/theme.example.json +24 -24
  64. package/dist/extensions/pi-powerline-footer/theme.json +12 -12
  65. package/dist/extensions/pi-powerline-footer/tps.ts +345 -345
  66. package/dist/extensions/pi-rewind-hook/README.md +245 -245
  67. package/dist/extensions/pi-rewind-hook/index.ts +1445 -1445
  68. package/dist/extensions/pi-rewind-hook/package.json +29 -29
  69. package/dist/extensions/pi-subagents/install.mjs +0 -0
  70. package/dist/extensions/pi-web-agent/package.json +31 -31
  71. package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -205
  72. package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -136
  73. package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -152
  74. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -25
  75. package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -28
  76. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +136 -136
  77. package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -946
  78. package/dist/extensions/pi-web-agent/src/extension.ts +126 -126
  79. package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -118
  80. package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -199
  81. package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -100
  82. package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -117
  83. package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -67
  84. package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -60
  85. package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -50
  86. package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -52
  87. package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -105
  88. package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -45
  89. package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -28
  90. package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -47
  91. package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -376
  92. package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -64
  93. package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -181
  94. package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -101
  95. package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -81
  96. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +210 -210
  97. package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -75
  98. package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -61
  99. package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -54
  100. package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -41
  101. package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -20
  102. package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -63
  103. package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -114
  104. package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -72
  105. package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -96
  106. package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -71
  107. package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -31
  108. package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -31
  109. package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -157
  110. package/dist/extensions/pi-web-agent/src/types.ts +88 -88
  111. package/dist/extensions/ponytail/package.json +8 -8
  112. package/dist/extensions/question/batch.ts +103 -103
  113. package/dist/extensions/question/constants.ts +30 -30
  114. package/dist/extensions/question/helpers.ts +58 -58
  115. package/dist/extensions/question/navigation.ts +14 -14
  116. package/dist/extensions/question/package.json +19 -19
  117. package/dist/extensions/question/schemas.ts +43 -43
  118. package/dist/extensions/question/selection-mode.ts +46 -46
  119. package/dist/extensions/question/shortcuts.ts +44 -44
  120. package/dist/extensions/question/types.ts +132 -132
  121. package/dist/extensions/test-resolve-hook-impl.mjs +6 -6
  122. package/dist/extensions/test-resolve-hook.mjs +6 -6
  123. package/dist/extensions/web-agent-onboarding.ts +222 -222
  124. package/dist/extensions/workflow/package.json +17 -17
  125. package/dist/package-manager-cli.js +71 -71
  126. package/dist/rpc-entry.js +0 -0
  127. package/dist/skills/agent-browser/SKILL.md +52 -52
  128. package/dist/skills/batch-grill-me/SKILL.md +19 -19
  129. package/dist/skills/grill-me/SKILL.md +10 -10
  130. package/dist/skills/handoff/SKILL.md +16 -16
  131. package/dist/skills/handoff-text/SKILL.md +14 -14
  132. package/dist/skills/implanger/SKILL.md +69 -69
  133. package/dist/skills/improve-codebase/REFERENCE.md +78 -78
  134. package/dist/skills/improve-codebase/SKILL.md +178 -178
  135. package/dist/skills/planger/SKILL.md +166 -166
  136. package/dist/skills/ponytail/SKILL.md +116 -116
  137. package/dist/skills/ponytail-audit/SKILL.md +42 -42
  138. package/dist/skills/ponytail-debt/SKILL.md +45 -45
  139. package/dist/skills/ponytail-gain/SKILL.md +51 -51
  140. package/dist/skills/ponytail-help/SKILL.md +70 -70
  141. package/dist/skills/ponytail-review/SKILL.md +58 -58
  142. package/dist/skills/selesai-handoff/SKILL.md +20 -20
  143. package/dist/skills/workflow-creation/SKILL.md +73 -73
  144. package/dist/themes/powerline-footer/theme.json +33 -33
  145. package/dist/utils/shell.d.ts +3 -0
  146. package/dist/utils/shell.js +17 -5
  147. package/docs/compaction.md +396 -396
  148. package/docs/containerization.md +111 -111
  149. package/docs/development.md +71 -71
  150. package/docs/docs.json +164 -164
  151. package/docs/environment-variables.md +86 -86
  152. package/docs/index.md +83 -83
  153. package/docs/json.md +82 -82
  154. package/docs/models.md +502 -502
  155. package/docs/packages.md +227 -227
  156. package/docs/plans/subagent-delegation/phase-0-correctness.md +264 -264
  157. package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +485 -485
  158. package/docs/plans/subagent-delegation/phase-2-context-controls.md +281 -281
  159. package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +361 -361
  160. package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +380 -380
  161. package/docs/prompt-templates.md +95 -95
  162. package/docs/providers.md +293 -293
  163. package/docs/sdk.md +1144 -1143
  164. package/docs/security.md +59 -59
  165. package/docs/session-format.md +414 -414
  166. package/docs/sessions.md +145 -145
  167. package/docs/shared-host-extensions.md +109 -109
  168. package/docs/shell-aliases.md +13 -13
  169. package/docs/skills.md +231 -231
  170. package/docs/terminal-setup.md +142 -142
  171. package/docs/termux.md +127 -127
  172. package/docs/themes.md +295 -295
  173. package/docs/tmux.md +63 -63
  174. package/docs/tui.md +927 -927
  175. package/docs/windows.md +17 -17
  176. package/examples/README.md +25 -25
  177. package/examples/extensions/README.md +211 -211
  178. package/examples/extensions/auto-commit-on-exit.ts +49 -49
  179. package/examples/extensions/bash-spawn-hook.ts +30 -30
  180. package/examples/extensions/bookmark.ts +50 -50
  181. package/examples/extensions/border-status-editor.ts +150 -150
  182. package/examples/extensions/built-in-tool-renderer.ts +249 -249
  183. package/examples/extensions/claude-rules.ts +86 -86
  184. package/examples/extensions/commands.ts +72 -72
  185. package/examples/extensions/confirm-destructive.ts +59 -59
  186. package/examples/extensions/custom-compaction.ts +130 -130
  187. package/examples/extensions/custom-footer.ts +64 -64
  188. package/examples/extensions/custom-header.ts +73 -73
  189. package/examples/extensions/custom-provider-anthropic/index.ts +610 -610
  190. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -24
  191. package/examples/extensions/custom-provider-anthropic/package.json +19 -19
  192. package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -404
  193. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -16
  194. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -82
  195. package/examples/extensions/dirty-repo-guard.ts +56 -56
  196. package/examples/extensions/doom-overlay/README.md +46 -46
  197. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -21
  198. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  199. package/examples/extensions/doom-overlay/doom/build.sh +152 -152
  200. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -72
  201. package/examples/extensions/doom-overlay/doom-component.ts +132 -132
  202. package/examples/extensions/doom-overlay/doom-engine.ts +173 -173
  203. package/examples/extensions/doom-overlay/doom-keys.ts +104 -104
  204. package/examples/extensions/doom-overlay/index.ts +74 -74
  205. package/examples/extensions/doom-overlay/wad-finder.ts +51 -51
  206. package/examples/extensions/dynamic-resources/SKILL.md +8 -8
  207. package/examples/extensions/dynamic-resources/dynamic.json +79 -79
  208. package/examples/extensions/dynamic-resources/dynamic.md +5 -5
  209. package/examples/extensions/dynamic-resources/index.ts +15 -15
  210. package/examples/extensions/dynamic-tools.ts +74 -74
  211. package/examples/extensions/event-bus.ts +43 -43
  212. package/examples/extensions/file-trigger.ts +41 -41
  213. package/examples/extensions/git-checkpoint.ts +53 -53
  214. package/examples/extensions/git-merge-and-resolve.ts +115 -115
  215. package/examples/extensions/github-issue-autocomplete.ts +185 -185
  216. package/examples/extensions/gondolin/index.ts +531 -531
  217. package/examples/extensions/gondolin/package-lock.json +185 -185
  218. package/examples/extensions/gondolin/package.json +19 -19
  219. package/examples/extensions/handoff.ts +199 -199
  220. package/examples/extensions/hello.ts +26 -26
  221. package/examples/extensions/hidden-thinking-label.ts +53 -53
  222. package/examples/extensions/inline-bash.ts +94 -94
  223. package/examples/extensions/input-transform-streaming.ts +39 -39
  224. package/examples/extensions/input-transform.ts +43 -43
  225. package/examples/extensions/interactive-shell.ts +196 -196
  226. package/examples/extensions/mac-system-theme.ts +47 -47
  227. package/examples/extensions/message-renderer.ts +59 -59
  228. package/examples/extensions/minimal-mode.ts +426 -426
  229. package/examples/extensions/modal-editor.ts +85 -85
  230. package/examples/extensions/model-status.ts +31 -31
  231. package/examples/extensions/notify.ts +55 -55
  232. package/examples/extensions/overlay-qa-tests.ts +1450 -1450
  233. package/examples/extensions/overlay-test.ts +153 -153
  234. package/examples/extensions/permission-gate.ts +34 -34
  235. package/examples/extensions/pirate.ts +47 -47
  236. package/examples/extensions/plan-mode/README.md +66 -66
  237. package/examples/extensions/plan-mode/index.ts +390 -390
  238. package/examples/extensions/plan-mode/utils.ts +168 -168
  239. package/examples/extensions/preset.ts +436 -436
  240. package/examples/extensions/project-trust.ts +64 -64
  241. package/examples/extensions/prompt-customizer.ts +97 -97
  242. package/examples/extensions/protected-paths.ts +30 -30
  243. package/examples/extensions/provider-payload.ts +18 -18
  244. package/examples/extensions/qna.ts +122 -122
  245. package/examples/extensions/question.ts +285 -285
  246. package/examples/extensions/questionnaire.ts +448 -448
  247. package/examples/extensions/rainbow-editor.ts +88 -88
  248. package/examples/extensions/reload-runtime.ts +37 -37
  249. package/examples/extensions/rpc-demo.ts +118 -118
  250. package/examples/extensions/sandbox/index.ts +321 -321
  251. package/examples/extensions/sandbox/package-lock.json +92 -92
  252. package/examples/extensions/sandbox/package.json +19 -19
  253. package/examples/extensions/send-user-message.ts +97 -97
  254. package/examples/extensions/session-name.ts +27 -27
  255. package/examples/extensions/shutdown-command.ts +63 -63
  256. package/examples/extensions/snake.ts +343 -343
  257. package/examples/extensions/space-invaders.ts +560 -560
  258. package/examples/extensions/ssh.ts +220 -220
  259. package/examples/extensions/status-line.ts +32 -32
  260. package/examples/extensions/structured-output.ts +65 -65
  261. package/examples/extensions/subagent/README.md +175 -175
  262. package/examples/extensions/subagent/agents/planner.md +37 -37
  263. package/examples/extensions/subagent/agents/reviewer.md +35 -35
  264. package/examples/extensions/subagent/agents/scout.md +50 -50
  265. package/examples/extensions/subagent/agents/worker.md +24 -24
  266. package/examples/extensions/subagent/agents.ts +126 -126
  267. package/examples/extensions/subagent/index.ts +1015 -1015
  268. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -10
  269. package/examples/extensions/subagent/prompts/implement.md +10 -10
  270. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -9
  271. package/examples/extensions/summarize.ts +209 -209
  272. package/examples/extensions/system-prompt-header.ts +17 -17
  273. package/examples/extensions/tic-tac-toe.ts +1008 -1008
  274. package/examples/extensions/timed-confirm.ts +70 -70
  275. package/examples/extensions/titlebar-spinner.ts +58 -58
  276. package/examples/extensions/todo.ts +297 -297
  277. package/examples/extensions/tool-override.ts +144 -144
  278. package/examples/extensions/tools.ts +146 -146
  279. package/examples/extensions/trigger-compact.ts +50 -50
  280. package/examples/extensions/truncated-tool.ts +195 -195
  281. package/examples/extensions/widget-placement.ts +9 -9
  282. package/examples/extensions/with-deps/index.ts +32 -32
  283. package/examples/extensions/with-deps/package-lock.json +31 -31
  284. package/examples/extensions/with-deps/package.json +22 -22
  285. package/examples/extensions/working-indicator.ts +123 -123
  286. package/examples/extensions/working-message-test.ts +25 -25
  287. package/examples/rpc-extension-ui.ts +632 -632
  288. package/examples/sdk/01-minimal.ts +26 -26
  289. package/examples/sdk/02-custom-model.ts +53 -53
  290. package/examples/sdk/03-custom-prompt.ts +75 -75
  291. package/examples/sdk/04-skills.ts +55 -55
  292. package/examples/sdk/05-tools.ts +48 -48
  293. package/examples/sdk/06-extensions.ts +99 -99
  294. package/examples/sdk/07-context-files.ts +47 -47
  295. package/examples/sdk/08-prompt-templates.ts +51 -51
  296. package/examples/sdk/09-api-keys-and-oauth.ts +52 -52
  297. package/examples/sdk/10-settings.ts +53 -53
  298. package/examples/sdk/11-sessions.ts +52 -52
  299. package/examples/sdk/12-full-control.ts +79 -79
  300. package/examples/sdk/13-session-runtime.ts +67 -67
  301. package/examples/sdk/README.md +144 -144
  302. package/package.json +4 -4
package/dist/cli/args.js CHANGED
@@ -16,7 +16,18 @@ export function parseArgs(args) {
16
16
  };
17
17
  for (let i = 0; i < args.length; i++) {
18
18
  const arg = args[i];
19
- if (arg === "--help" || arg === "-h") {
19
+ if (arg === "--") {
20
+ for (const positionalArg of args.slice(i + 1)) {
21
+ if (positionalArg.startsWith("@")) {
22
+ result.fileArgs.push(positionalArg.slice(1));
23
+ }
24
+ else {
25
+ result.messages.push(positionalArg);
26
+ }
27
+ }
28
+ break;
29
+ }
30
+ else if (arg === "--help" || arg === "-h") {
20
31
  result.help = true;
21
32
  }
22
33
  else if (arg === "--version" || arg === "-v") {
@@ -269,7 +280,7 @@ ${chalk.bold("Options:")}
269
280
  --models <patterns> Comma-separated model patterns for Ctrl+P cycling
270
281
  Supports globs (anthropic/*, *sonnet*) and fuzzy matching
271
282
  --no-tools, -nt Disable all tools by default (built-in and extension)
272
- --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled
283
+ --no-builtin-tools, -nbt Disable built-in tools and enable extension/custom tools
273
284
  --tools, -t <tools> Comma-separated allowlist of tool names to enable
274
285
  Applies to built-in, extension, and custom tools
275
286
  --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable
@@ -1,5 +1,13 @@
1
1
  import { describe, expect, test } from "vitest";
2
2
  import { parseArgs } from "./args.js";
3
+ describe("parseArgs end-of-options delimiter", () => {
4
+ test("treats remaining arguments as messages and @files", () => {
5
+ const result = parseArgs(["--no-session", "--", "--provider", "openai", "-c", "@prompt.md"]);
6
+ expect(result.noSession).toBe(true);
7
+ expect(result.messages).toEqual(["--provider", "openai", "-c"]);
8
+ expect(result.fileArgs).toEqual(["prompt.md"]);
9
+ });
10
+ });
3
11
  describe("parseArgs --use-theme", () => {
4
12
  test("parses --use-theme with a theme name", () => {
5
13
  const result = parseArgs(["--use-theme", "nord-border", "hello"]);
@@ -7,10 +7,10 @@ export function isCredentialPrintHelp(args) {
7
7
  return (args[0] === "auth" && (args[1] === undefined || args[1] === "help" || args[1] === "--help" || args[1] === "-h"));
8
8
  }
9
9
  export function printCredentialPrintHelp() {
10
- console.log(`Usage:
11
- ${APP_NAME} auth print-api-key --model <model> [--provider <provider>]
12
- ${APP_NAME} auth print-bearer-token --model <model> [--provider <provider>] [--min-expiry <duration>]
13
-
10
+ console.log(`Usage:
11
+ ${APP_NAME} auth print-api-key --model <model> [--provider <provider>]
12
+ ${APP_NAME} auth print-bearer-token --model <model> [--provider <provider>] [--min-expiry <duration>]
13
+
14
14
  Prints the configured credential alone on stdout. Provider inference uses configured credentials; specify --provider to select explicitly. Bearer tokens have a 30-minute minimum expiry by default. --min-expiry accepts ms, s, m, or h (for example, 30m).`);
15
15
  }
16
16
  /** Parse the auth command surface before normal startup. */
package/dist/cli.js CHANGED
File without changes
@@ -1,7 +1,8 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
+ import { Type } from "typebox";
2
3
  import { AgentSession } from "./agent-session.js";
3
4
  import { SettingsManager } from "./settings-manager.js";
4
- function createMockSession({ enabled, threshold = 128_000, tokens, mode = "tui", command, }) {
5
+ function createMockSession({ enabled, threshold = 128_000, tokens, mode = "tui", command, customTools, allowedToolNames, }) {
5
6
  const settings = SettingsManager.inMemory({
6
7
  autoHandoff: { enabled, thresholdTokens: threshold },
7
8
  });
@@ -45,13 +46,35 @@ function createMockSession({ enabled, threshold = 128_000, tokens, mode = "tui",
45
46
  settingsManager: settings,
46
47
  cwd: "/tmp",
47
48
  resourceLoader: resourceLoader,
49
+ customTools,
48
50
  modelRuntime: modelRegistry,
51
+ allowedToolNames,
49
52
  });
50
53
  session._extensionRunner = extensionRunner;
51
54
  session._extensionMode = mode;
52
55
  session.getContextUsage = () => tokens === null ? null : { tokens, contextWindow: 200_000, percent: tokens / 200_000 };
53
56
  return { session, getCommand, createCommandContext, handler, emitError, settings };
54
57
  }
58
+ describe("AgentSession tools", () => {
59
+ const customTool = {
60
+ name: "custom",
61
+ label: "Custom",
62
+ description: "A custom tool",
63
+ parameters: Type.Object({}),
64
+ execute: async () => ({ content: [{ type: "text", text: "ok" }], details: undefined }),
65
+ };
66
+ it("keeps custom tools inactive by default but activates explicitly allowed ones", () => {
67
+ const defaults = createMockSession({ enabled: false, tokens: null, customTools: [customTool] });
68
+ expect(defaults.session.getActiveToolNames()).not.toContain("custom");
69
+ const allowed = createMockSession({
70
+ enabled: false,
71
+ tokens: null,
72
+ customTools: [customTool],
73
+ allowedToolNames: ["custom"],
74
+ });
75
+ expect(allowed.session.getActiveToolNames()).toEqual(["custom"]);
76
+ });
77
+ });
55
78
  describe("AgentSession auto handoff", () => {
56
79
  it("invokes handoff-new once when enabled and threshold reached in tui mode", async () => {
57
80
  const command = { handler: vi.fn() };
@@ -172,6 +172,11 @@ export interface PromptOptions {
172
172
  preflightResult?: (success: boolean) => void;
173
173
  }
174
174
  /** Result from cycleModel() */
175
+ /** Options for model and thinking mutations. */
176
+ export interface ModelMutationOptions {
177
+ /** Persist the value to global defaults. Defaults to session-only. */
178
+ persist?: boolean;
179
+ }
175
180
  export interface ModelCycleResult {
176
181
  model: Model<any>;
177
182
  thinkingLevel: ThinkingLevel;
@@ -265,6 +270,7 @@ export declare class AgentSession {
265
270
  /** Emit an event to all listeners */
266
271
  private _emit;
267
272
  private _emitQueueUpdate;
273
+ private _emitSessionCompactFailed;
268
274
  private _getIdleWaitPromise;
269
275
  private _resolveIdleWaitIfIdle;
270
276
  private _emitAgentSettled;
@@ -478,14 +484,14 @@ export declare class AgentSession {
478
484
  * Validates that auth is configured, saves to session and settings.
479
485
  * @throws Error if no auth is configured for the model
480
486
  */
481
- setModel(model: Model<any>): Promise<void>;
487
+ setModel(model: Model<any>, options?: ModelMutationOptions): Promise<void>;
482
488
  /**
483
489
  * Cycle to next/previous model.
484
490
  * Uses scoped models (from --models flag) if available, otherwise all available models.
485
491
  * @param direction - "forward" (default) or "backward"
486
492
  * @returns The new model info, or undefined if only one model available
487
493
  */
488
- cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
494
+ cycleModel(direction?: "forward" | "backward", options?: ModelMutationOptions): Promise<ModelCycleResult | undefined>;
489
495
  private _cycleScopedModel;
490
496
  private _cycleAvailableModel;
491
497
  /**
@@ -493,12 +499,12 @@ export declare class AgentSession {
493
499
  * Clamps to model capabilities based on available thinking levels.
494
500
  * Saves to session and settings only if the level actually changes.
495
501
  */
496
- setThinkingLevel(level: ThinkingLevel): void;
502
+ setThinkingLevel(level: ThinkingLevel, options?: ModelMutationOptions): void;
497
503
  /**
498
504
  * Cycle to next thinking level.
499
505
  * @returns New level, or undefined if model doesn't support thinking
500
506
  */
501
- cycleThinkingLevel(): ThinkingLevel | undefined;
507
+ cycleThinkingLevel(options?: ModelMutationOptions): ThinkingLevel | undefined;
502
508
  /**
503
509
  * Get available thinking levels for current model.
504
510
  * The provider will clamp to what the specific model supports internally.
@@ -552,6 +558,7 @@ export declare class AgentSession {
552
558
  private _applyExtensionBindings;
553
559
  private _refreshCurrentModelFromRegistry;
554
560
  private _bindExtensionCore;
561
+ private _createToolCatalogDefinition;
555
562
  private _refreshToolRegistry;
556
563
  private _buildRuntime;
557
564
  reload(options?: {
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
+ import { Type } from "typebox";
17
18
  import { contentText } from "@earendil-works/pi-ai";
18
19
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
19
20
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
@@ -24,7 +25,7 @@ import { normalizeAssistantThinkingTags } from "../utils/thinking-tags.js";
24
25
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
25
26
  import { executeBashWithOperations } from "./bash-executor.js";
26
27
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
27
- import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
28
+ import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
28
29
  import { exportSessionToHtml } from "./export-html/index.js";
29
30
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
30
31
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
@@ -82,8 +83,6 @@ function estimateMessagesTokens(messages) {
82
83
  // ============================================================================
83
84
  // Constants
84
85
  // ============================================================================
85
- /** Standard thinking levels */
86
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
87
86
  // ============================================================================
88
87
  // AgentSession Class
89
88
  // ============================================================================
@@ -168,7 +167,6 @@ export class AgentSession {
168
167
  this._installAgentNextTurnRefresh();
169
168
  this._buildRuntime({
170
169
  activeToolNames: this._initialActiveToolNames,
171
- includeAllExtensionTools: true,
172
170
  });
173
171
  }
174
172
  get modelRuntime() {
@@ -307,6 +305,11 @@ export class AgentSession {
307
305
  followUp: [...this._followUpMessages],
308
306
  });
309
307
  }
308
+ async _emitSessionCompactFailed(event) {
309
+ if (this._extensionRunner.hasHandlers("session_compact_failed")) {
310
+ await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
311
+ }
312
+ }
310
313
  _getIdleWaitPromise() {
311
314
  if (!this._idleWaitPromise) {
312
315
  this._idleWaitPromise = new Promise((resolve) => {
@@ -1435,16 +1438,17 @@ export class AgentSession {
1435
1438
  * Validates that auth is configured, saves to session and settings.
1436
1439
  * @throws Error if no auth is configured for the model
1437
1440
  */
1438
- async setModel(model) {
1441
+ async setModel(model, options = {}) {
1439
1442
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
1440
1443
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1441
1444
  }
1442
1445
  const previousModel = this.model;
1443
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1446
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
1444
1447
  this.agent.state.model = model;
1445
1448
  this.sessionManager.appendModelChange(model.provider, model.id);
1446
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1447
- // Re-clamp thinking level for new model's capabilities
1449
+ if (options.persist) {
1450
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1451
+ }
1448
1452
  this.setThinkingLevel(thinkingLevel);
1449
1453
  await this._emitModelSelect(model, previousModel, "set");
1450
1454
  }
@@ -1454,13 +1458,13 @@ export class AgentSession {
1454
1458
  * @param direction - "forward" (default) or "backward"
1455
1459
  * @returns The new model info, or undefined if only one model available
1456
1460
  */
1457
- async cycleModel(direction = "forward") {
1461
+ async cycleModel(direction = "forward", options = {}) {
1458
1462
  if (this._scopedModels.length > 0) {
1459
- return this._cycleScopedModel(direction);
1463
+ return this._cycleScopedModel(direction, options);
1460
1464
  }
1461
- return this._cycleAvailableModel(direction);
1465
+ return this._cycleAvailableModel(direction, options);
1462
1466
  }
1463
- async _cycleScopedModel(direction) {
1467
+ async _cycleScopedModel(direction, options) {
1464
1468
  const checks = await Promise.all(this._scopedModels.map(async (scoped) => ({
1465
1469
  scoped,
1466
1470
  auth: await this._modelRuntime.checkAuth(scoped.model.provider),
@@ -1475,11 +1479,13 @@ export class AgentSession {
1475
1479
  const len = scopedModels.length;
1476
1480
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1477
1481
  const next = scopedModels[nextIndex];
1478
- const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1482
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
1479
1483
  // Apply model
1480
1484
  this.agent.state.model = next.model;
1481
1485
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1482
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1486
+ if (options.persist) {
1487
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1488
+ }
1483
1489
  // Apply thinking level.
1484
1490
  // - Explicit scoped model thinking level overrides current session level
1485
1491
  // - Undefined scoped model thinking level inherits the current session preference
@@ -1488,7 +1494,7 @@ export class AgentSession {
1488
1494
  await this._emitModelSelect(next.model, currentModel, "cycle");
1489
1495
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1490
1496
  }
1491
- async _cycleAvailableModel(direction) {
1497
+ async _cycleAvailableModel(direction, options) {
1492
1498
  const availableModels = await this._modelRuntime.getAvailable();
1493
1499
  if (availableModels.length <= 1)
1494
1500
  return undefined;
@@ -1499,10 +1505,12 @@ export class AgentSession {
1499
1505
  const len = availableModels.length;
1500
1506
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1501
1507
  const nextModel = availableModels[nextIndex];
1502
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1508
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
1503
1509
  this.agent.state.model = nextModel;
1504
1510
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1505
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1511
+ if (options.persist) {
1512
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1513
+ }
1506
1514
  // Re-clamp thinking level for new model's capabilities
1507
1515
  this.setThinkingLevel(thinkingLevel);
1508
1516
  await this._emitModelSelect(nextModel, currentModel, "cycle");
@@ -1516,18 +1524,18 @@ export class AgentSession {
1516
1524
  * Clamps to model capabilities based on available thinking levels.
1517
1525
  * Saves to session and settings only if the level actually changes.
1518
1526
  */
1519
- setThinkingLevel(level) {
1527
+ setThinkingLevel(level, options = {}) {
1520
1528
  const availableLevels = this.getAvailableThinkingLevels();
1521
1529
  const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
1522
1530
  // Only persist if actually changing
1523
1531
  const previousLevel = this.agent.state.thinkingLevel;
1524
1532
  const isChanging = effectiveLevel !== previousLevel;
1525
1533
  this.agent.state.thinkingLevel = effectiveLevel;
1534
+ if (options.persist) {
1535
+ this.settingsManager.setDefaultThinkingLevel(level);
1536
+ }
1526
1537
  if (isChanging) {
1527
1538
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1528
- if (this.supportsThinking() || effectiveLevel !== "off") {
1529
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1530
- }
1531
1539
  this._emit({ type: "thinking_level_changed", level: effectiveLevel });
1532
1540
  void this._extensionRunner.emit({
1533
1541
  type: "thinking_level_select",
@@ -1540,14 +1548,14 @@ export class AgentSession {
1540
1548
  * Cycle to next thinking level.
1541
1549
  * @returns New level, or undefined if model doesn't support thinking
1542
1550
  */
1543
- cycleThinkingLevel() {
1551
+ cycleThinkingLevel(options = {}) {
1544
1552
  if (!this.supportsThinking())
1545
1553
  return undefined;
1546
1554
  const levels = this.getAvailableThinkingLevels();
1547
1555
  const currentIndex = levels.indexOf(this.thinkingLevel);
1548
1556
  const nextIndex = (currentIndex + 1) % levels.length;
1549
1557
  const nextLevel = levels[nextIndex];
1550
- this.setThinkingLevel(nextLevel);
1558
+ this.setThinkingLevel(nextLevel, options);
1551
1559
  return nextLevel;
1552
1560
  }
1553
1561
  /**
@@ -1556,7 +1564,7 @@ export class AgentSession {
1556
1564
  */
1557
1565
  getAvailableThinkingLevels() {
1558
1566
  if (!this.model)
1559
- return THINKING_LEVELS;
1567
+ return [...THINKING_LEVEL_OPTIONS];
1560
1568
  return getSupportedThinkingLevels(this.model);
1561
1569
  }
1562
1570
  /**
@@ -1565,14 +1573,16 @@ export class AgentSession {
1565
1573
  supportsThinking() {
1566
1574
  return !!this.model?.reasoning;
1567
1575
  }
1568
- _getThinkingLevelForModelSwitch(explicitLevel) {
1576
+ _getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
1569
1577
  if (explicitLevel !== undefined) {
1570
1578
  return explicitLevel;
1571
1579
  }
1572
- if (!this.supportsThinking()) {
1573
- return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
1580
+ if (targetModel) {
1581
+ const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
1582
+ if (perModel !== undefined)
1583
+ return perModel;
1574
1584
  }
1575
- return this.thinkingLevel;
1585
+ return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
1576
1586
  }
1577
1587
  _clampThinkingLevel(level, _availableLevels) {
1578
1588
  return this.model ? clampThinkingLevel(this.model, level) : "off";
@@ -1710,13 +1720,21 @@ export class AgentSession {
1710
1720
  catch (error) {
1711
1721
  const message = error instanceof Error ? error.message : String(error);
1712
1722
  const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1723
+ const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1713
1724
  this._emit({
1714
1725
  type: "compaction_end",
1715
1726
  reason: "manual",
1716
1727
  result: undefined,
1717
1728
  aborted,
1718
1729
  willRetry: false,
1719
- errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
1730
+ errorMessage,
1731
+ });
1732
+ await this._emitSessionCompactFailed({
1733
+ reason: "manual",
1734
+ errorMessage,
1735
+ aborted,
1736
+ willRetry: false,
1737
+ fromExtension: false,
1720
1738
  });
1721
1739
  throw error;
1722
1740
  }
@@ -1835,6 +1853,7 @@ export class AgentSession {
1835
1853
  async _runAutoCompaction(reason, willRetry) {
1836
1854
  const settings = this.settingsManager.getCompactionSettings();
1837
1855
  let started = false;
1856
+ let fromExtension = false;
1838
1857
  try {
1839
1858
  if (!this.model) {
1840
1859
  return false;
@@ -1857,7 +1876,6 @@ export class AgentSession {
1857
1876
  this._autoCompactionAbortController = new AbortController();
1858
1877
  started = true;
1859
1878
  let extensionCompaction;
1860
- let fromExtension = false;
1861
1879
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1862
1880
  const extensionResult = (await this._extensionRunner.emit({
1863
1881
  type: "session_before_compact",
@@ -1876,6 +1894,7 @@ export class AgentSession {
1876
1894
  aborted: true,
1877
1895
  willRetry: false,
1878
1896
  });
1897
+ await this._emitSessionCompactFailed({ reason, aborted: true, willRetry: false, fromExtension: false });
1879
1898
  return false;
1880
1899
  }
1881
1900
  if (extensionResult?.compaction) {
@@ -1913,6 +1932,7 @@ export class AgentSession {
1913
1932
  aborted: true,
1914
1933
  willRetry: false,
1915
1934
  });
1935
+ await this._emitSessionCompactFailed({ reason, aborted: true, willRetry: false, fromExtension });
1916
1936
  return false;
1917
1937
  }
1918
1938
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
@@ -1955,15 +1975,23 @@ export class AgentSession {
1955
1975
  catch (error) {
1956
1976
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1957
1977
  if (started) {
1978
+ const formattedErrorMessage = reason === "overflow"
1979
+ ? `Context overflow recovery failed: ${errorMessage}`
1980
+ : `Auto-compaction failed: ${errorMessage}`;
1958
1981
  this._emit({
1959
1982
  type: "compaction_end",
1960
1983
  reason,
1961
1984
  result: undefined,
1962
1985
  aborted: false,
1963
1986
  willRetry: false,
1964
- errorMessage: reason === "overflow"
1965
- ? `Context overflow recovery failed: ${errorMessage}`
1966
- : `Auto-compaction failed: ${errorMessage}`,
1987
+ errorMessage: formattedErrorMessage,
1988
+ });
1989
+ await this._emitSessionCompactFailed({
1990
+ reason,
1991
+ errorMessage: formattedErrorMessage,
1992
+ aborted: false,
1993
+ willRetry: false,
1994
+ fromExtension,
1967
1995
  });
1968
1996
  }
1969
1997
  return false;
@@ -2187,6 +2215,39 @@ export class AgentSession {
2187
2215
  },
2188
2216
  });
2189
2217
  }
2218
+ _createToolCatalogDefinition() {
2219
+ return {
2220
+ name: "tool_catalog",
2221
+ label: "Tool catalog",
2222
+ description: "Discover inactive tools by capability or activate named tools. Activated tools are available on the next turn.",
2223
+ parameters: Type.Object({
2224
+ query: Type.Optional(Type.String({ description: "Capability or tool name to search for" })),
2225
+ activate: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { description: "Exact inactive tool names to activate" })),
2226
+ }),
2227
+ execute: async (_toolCallId, params) => {
2228
+ const query = params.query?.trim().toLowerCase();
2229
+ const active = new Set(this.getActiveToolNames());
2230
+ const requested = Array.isArray(params.activate)
2231
+ ? Array.from(new Set(params.activate.filter((name) => typeof name === "string")))
2232
+ : [];
2233
+ const available = Array.from(this._toolDefinitions.values()).filter(({ definition }) => definition.name !== "tool_catalog");
2234
+ const known = new Set(available.map(({ definition }) => definition.name));
2235
+ const unknown = requested.filter((name) => !known.has(name));
2236
+ const activate = requested.filter((name) => known.has(name));
2237
+ if (activate.length > 0)
2238
+ this.setActiveToolsByName([...active, ...activate]);
2239
+ const matches = available
2240
+ .filter(({ definition }) => !query || `${definition.name} ${definition.description}`.toLowerCase().includes(query))
2241
+ .map(({ definition }) => `- ${definition.name}: ${definition.description}`);
2242
+ const result = [
2243
+ activate.length > 0 ? `Activated: ${activate.join(", ")}. They are available on the next turn.` : undefined,
2244
+ unknown.length > 0 ? `Unknown or unavailable: ${unknown.join(", ")}.` : undefined,
2245
+ matches.length > 0 ? `Available tools:\n${matches.join("\n")}` : query ? "No matching tools." : "No additional tools available.",
2246
+ ].filter(Boolean).join("\n\n");
2247
+ return { content: [{ type: "text", text: result }], details: { activated: activate, unknown } };
2248
+ },
2249
+ };
2250
+ }
2190
2251
  _refreshToolRegistry(options) {
2191
2252
  const previousRegistryNames = new Set(this._toolRegistry.keys());
2192
2253
  const previousActiveToolNames = this.getActiveToolNames();
@@ -2291,6 +2352,7 @@ export class AgentSession {
2291
2352
  bash: { commandPrefix: shellCommandPrefix, shellPath },
2292
2353
  });
2293
2354
  this._baseToolDefinitions = new Map(Object.entries(baseToolDefinitions).map(([name, tool]) => [name, tool]));
2355
+ this._baseToolDefinitions.set("tool_catalog", this._createToolCatalogDefinition());
2294
2356
  const extensionsResult = this._resourceLoader.getExtensions();
2295
2357
  if (options.flagValues) {
2296
2358
  for (const [name, value] of options.flagValues) {
@@ -2305,7 +2367,7 @@ export class AgentSession {
2305
2367
  this._applyExtensionBindings(this._extensionRunner);
2306
2368
  const defaultActiveToolNames = this._baseToolsOverride
2307
2369
  ? Object.keys(this._baseToolsOverride)
2308
- : ["read", "bash", "edit", "write"];
2370
+ : ["read", "bash", "edit", "write", "tool_catalog"];
2309
2371
  const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames;
2310
2372
  this._refreshToolRegistry({
2311
2373
  activeToolNames: baseActiveToolNames,
@@ -2322,7 +2384,6 @@ export class AgentSession {
2322
2384
  this._buildRuntime({
2323
2385
  activeToolNames: this.getActiveToolNames(),
2324
2386
  flagValues: previousFlagValues,
2325
- includeAllExtensionTools: true,
2326
2387
  });
2327
2388
  const hasBindings = this._extensionUIContext ||
2328
2389
  this._extensionCommandContextActions ||
@@ -146,37 +146,37 @@ export function prepareBranchEntries(entries, tokenBudget = 0) {
146
146
  // ============================================================================
147
147
  // Summary Generation
148
148
  // ============================================================================
149
- const BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.
150
- Summary of that exploration:
151
-
149
+ const BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.
150
+ Summary of that exploration:
151
+
152
152
  `;
153
- const BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.
154
-
155
- Use this EXACT format:
156
-
157
- ## Goal
158
- [What was the user trying to accomplish in this branch?]
159
-
160
- ## Constraints & Preferences
161
- - [Any constraints, preferences, or requirements mentioned]
162
- - [Or "(none)" if none were mentioned]
163
-
164
- ## Progress
165
- ### Done
166
- - [x] [Completed tasks/changes]
167
-
168
- ### In Progress
169
- - [ ] [Work that was started but not finished]
170
-
171
- ### Blocked
172
- - [Issues preventing progress, if any]
173
-
174
- ## Key Decisions
175
- - **[Decision]**: [Brief rationale]
176
-
177
- ## Next Steps
178
- 1. [What should happen next to continue this work]
179
-
153
+ const BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.
154
+
155
+ Use this EXACT format:
156
+
157
+ ## Goal
158
+ [What was the user trying to accomplish in this branch?]
159
+
160
+ ## Constraints & Preferences
161
+ - [Any constraints, preferences, or requirements mentioned]
162
+ - [Or "(none)" if none were mentioned]
163
+
164
+ ## Progress
165
+ ### Done
166
+ - [x] [Completed tasks/changes]
167
+
168
+ ### In Progress
169
+ - [ ] [Work that was started but not finished]
170
+
171
+ ### Blocked
172
+ - [Issues preventing progress, if any]
173
+
174
+ ## Key Decisions
175
+ - **[Decision]**: [Brief rationale]
176
+
177
+ ## Next Steps
178
+ 1. [What should happen next to continue this work]
179
+
180
180
  Keep each section concise. Preserve exact file paths, function names, and error messages.`;
181
181
  /**
182
182
  * Generate a summary of abandoned branch entries.