@selesai/code 0.9.9 → 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 (306) hide show
  1. package/CHANGELOG.md +11 -0
  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 +99 -39
  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/copy-turn.test.ts +24 -1
  43. package/dist/extensions/copy-turn.ts +6 -1
  44. package/dist/extensions/handoff-new.test.ts +15 -2
  45. package/dist/extensions/handoff-new.ts +44 -30
  46. package/dist/extensions/pi-intercom/LICENSE +21 -21
  47. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -83
  48. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -387
  49. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -114
  50. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -153
  51. package/dist/extensions/pi-intercom/broker/paths.ts +134 -134
  52. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -34
  53. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -21
  54. package/dist/extensions/pi-intercom/cwd.test.ts +40 -40
  55. package/dist/extensions/pi-intercom/cwd.ts +31 -31
  56. package/dist/extensions/pi-intercom/extension-api.ts +44 -44
  57. package/dist/extensions/pi-intercom/format-context.test.ts +31 -31
  58. package/dist/extensions/pi-intercom/format-context.ts +32 -32
  59. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -66
  60. package/dist/extensions/pi-intercom/ui/compose.ts +143 -143
  61. package/dist/extensions/pi-intercom/ui/session-list.ts +166 -166
  62. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +286 -286
  63. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +108 -108
  64. package/dist/extensions/pi-powerline-footer/separators.ts +57 -57
  65. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +47 -47
  66. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +39 -39
  67. package/dist/extensions/pi-powerline-footer/theme.example.json +24 -24
  68. package/dist/extensions/pi-powerline-footer/theme.json +12 -12
  69. package/dist/extensions/pi-powerline-footer/tps.ts +345 -345
  70. package/dist/extensions/pi-rewind-hook/README.md +245 -245
  71. package/dist/extensions/pi-rewind-hook/index.ts +1445 -1445
  72. package/dist/extensions/pi-rewind-hook/package.json +29 -29
  73. package/dist/extensions/pi-subagents/install.mjs +0 -0
  74. package/dist/extensions/pi-web-agent/package.json +31 -31
  75. package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -205
  76. package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -136
  77. package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -152
  78. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -25
  79. package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -28
  80. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +136 -136
  81. package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -946
  82. package/dist/extensions/pi-web-agent/src/extension.ts +126 -126
  83. package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -118
  84. package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -199
  85. package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -100
  86. package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -117
  87. package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -67
  88. package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -60
  89. package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -50
  90. package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -52
  91. package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -105
  92. package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -45
  93. package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -28
  94. package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -47
  95. package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -376
  96. package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -64
  97. package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -181
  98. package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -101
  99. package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -81
  100. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +210 -210
  101. package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -75
  102. package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -61
  103. package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -54
  104. package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -41
  105. package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -20
  106. package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -63
  107. package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -114
  108. package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -72
  109. package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -96
  110. package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -71
  111. package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -31
  112. package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -31
  113. package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -157
  114. package/dist/extensions/pi-web-agent/src/types.ts +88 -88
  115. package/dist/extensions/ponytail/package.json +8 -8
  116. package/dist/extensions/question/batch.ts +103 -103
  117. package/dist/extensions/question/constants.ts +30 -30
  118. package/dist/extensions/question/helpers.ts +58 -58
  119. package/dist/extensions/question/navigation.ts +14 -14
  120. package/dist/extensions/question/package.json +19 -19
  121. package/dist/extensions/question/schemas.ts +43 -43
  122. package/dist/extensions/question/selection-mode.ts +46 -46
  123. package/dist/extensions/question/shortcuts.ts +44 -44
  124. package/dist/extensions/question/types.ts +132 -132
  125. package/dist/extensions/test-resolve-hook-impl.mjs +6 -6
  126. package/dist/extensions/test-resolve-hook.mjs +6 -6
  127. package/dist/extensions/web-agent-onboarding.ts +222 -222
  128. package/dist/extensions/workflow/package.json +17 -17
  129. package/dist/package-manager-cli.js +71 -71
  130. package/dist/rpc-entry.js +0 -0
  131. package/dist/skills/agent-browser/SKILL.md +52 -52
  132. package/dist/skills/batch-grill-me/SKILL.md +19 -19
  133. package/dist/skills/grill-me/SKILL.md +10 -10
  134. package/dist/skills/handoff/SKILL.md +16 -16
  135. package/dist/skills/handoff-text/SKILL.md +14 -14
  136. package/dist/skills/implanger/SKILL.md +69 -69
  137. package/dist/skills/improve-codebase/REFERENCE.md +78 -78
  138. package/dist/skills/improve-codebase/SKILL.md +178 -178
  139. package/dist/skills/planger/SKILL.md +166 -166
  140. package/dist/skills/ponytail/SKILL.md +116 -116
  141. package/dist/skills/ponytail-audit/SKILL.md +42 -42
  142. package/dist/skills/ponytail-debt/SKILL.md +45 -45
  143. package/dist/skills/ponytail-gain/SKILL.md +51 -51
  144. package/dist/skills/ponytail-help/SKILL.md +70 -70
  145. package/dist/skills/ponytail-review/SKILL.md +58 -58
  146. package/dist/skills/selesai-handoff/SKILL.md +20 -20
  147. package/dist/skills/workflow-creation/SKILL.md +73 -73
  148. package/dist/themes/powerline-footer/theme.json +33 -33
  149. package/dist/utils/shell.d.ts +3 -0
  150. package/dist/utils/shell.js +17 -5
  151. package/docs/compaction.md +396 -396
  152. package/docs/containerization.md +111 -111
  153. package/docs/development.md +71 -71
  154. package/docs/docs.json +164 -164
  155. package/docs/environment-variables.md +86 -86
  156. package/docs/index.md +83 -83
  157. package/docs/json.md +82 -82
  158. package/docs/models.md +502 -502
  159. package/docs/packages.md +227 -227
  160. package/docs/plans/subagent-delegation/phase-0-correctness.md +264 -264
  161. package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +485 -485
  162. package/docs/plans/subagent-delegation/phase-2-context-controls.md +281 -281
  163. package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +361 -361
  164. package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +380 -380
  165. package/docs/prompt-templates.md +95 -95
  166. package/docs/providers.md +293 -293
  167. package/docs/sdk.md +1144 -1143
  168. package/docs/security.md +59 -59
  169. package/docs/session-format.md +414 -414
  170. package/docs/sessions.md +145 -145
  171. package/docs/shared-host-extensions.md +109 -109
  172. package/docs/shell-aliases.md +13 -13
  173. package/docs/skills.md +231 -231
  174. package/docs/terminal-setup.md +142 -142
  175. package/docs/termux.md +127 -127
  176. package/docs/themes.md +295 -295
  177. package/docs/tmux.md +63 -63
  178. package/docs/tui.md +927 -927
  179. package/docs/windows.md +17 -17
  180. package/examples/README.md +25 -25
  181. package/examples/extensions/README.md +211 -211
  182. package/examples/extensions/auto-commit-on-exit.ts +49 -49
  183. package/examples/extensions/bash-spawn-hook.ts +30 -30
  184. package/examples/extensions/bookmark.ts +50 -50
  185. package/examples/extensions/border-status-editor.ts +150 -150
  186. package/examples/extensions/built-in-tool-renderer.ts +249 -249
  187. package/examples/extensions/claude-rules.ts +86 -86
  188. package/examples/extensions/commands.ts +72 -72
  189. package/examples/extensions/confirm-destructive.ts +59 -59
  190. package/examples/extensions/custom-compaction.ts +130 -130
  191. package/examples/extensions/custom-footer.ts +64 -64
  192. package/examples/extensions/custom-header.ts +73 -73
  193. package/examples/extensions/custom-provider-anthropic/index.ts +610 -610
  194. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -24
  195. package/examples/extensions/custom-provider-anthropic/package.json +19 -19
  196. package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -404
  197. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -16
  198. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -82
  199. package/examples/extensions/dirty-repo-guard.ts +56 -56
  200. package/examples/extensions/doom-overlay/README.md +46 -46
  201. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -21
  202. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  203. package/examples/extensions/doom-overlay/doom/build.sh +152 -152
  204. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -72
  205. package/examples/extensions/doom-overlay/doom-component.ts +132 -132
  206. package/examples/extensions/doom-overlay/doom-engine.ts +173 -173
  207. package/examples/extensions/doom-overlay/doom-keys.ts +104 -104
  208. package/examples/extensions/doom-overlay/index.ts +74 -74
  209. package/examples/extensions/doom-overlay/wad-finder.ts +51 -51
  210. package/examples/extensions/dynamic-resources/SKILL.md +8 -8
  211. package/examples/extensions/dynamic-resources/dynamic.json +79 -79
  212. package/examples/extensions/dynamic-resources/dynamic.md +5 -5
  213. package/examples/extensions/dynamic-resources/index.ts +15 -15
  214. package/examples/extensions/dynamic-tools.ts +74 -74
  215. package/examples/extensions/event-bus.ts +43 -43
  216. package/examples/extensions/file-trigger.ts +41 -41
  217. package/examples/extensions/git-checkpoint.ts +53 -53
  218. package/examples/extensions/git-merge-and-resolve.ts +115 -115
  219. package/examples/extensions/github-issue-autocomplete.ts +185 -185
  220. package/examples/extensions/gondolin/index.ts +531 -531
  221. package/examples/extensions/gondolin/package-lock.json +185 -185
  222. package/examples/extensions/gondolin/package.json +19 -19
  223. package/examples/extensions/handoff.ts +199 -199
  224. package/examples/extensions/hello.ts +26 -26
  225. package/examples/extensions/hidden-thinking-label.ts +53 -53
  226. package/examples/extensions/inline-bash.ts +94 -94
  227. package/examples/extensions/input-transform-streaming.ts +39 -39
  228. package/examples/extensions/input-transform.ts +43 -43
  229. package/examples/extensions/interactive-shell.ts +196 -196
  230. package/examples/extensions/mac-system-theme.ts +47 -47
  231. package/examples/extensions/message-renderer.ts +59 -59
  232. package/examples/extensions/minimal-mode.ts +426 -426
  233. package/examples/extensions/modal-editor.ts +85 -85
  234. package/examples/extensions/model-status.ts +31 -31
  235. package/examples/extensions/notify.ts +55 -55
  236. package/examples/extensions/overlay-qa-tests.ts +1450 -1450
  237. package/examples/extensions/overlay-test.ts +153 -153
  238. package/examples/extensions/permission-gate.ts +34 -34
  239. package/examples/extensions/pirate.ts +47 -47
  240. package/examples/extensions/plan-mode/README.md +66 -66
  241. package/examples/extensions/plan-mode/index.ts +390 -390
  242. package/examples/extensions/plan-mode/utils.ts +168 -168
  243. package/examples/extensions/preset.ts +436 -436
  244. package/examples/extensions/project-trust.ts +64 -64
  245. package/examples/extensions/prompt-customizer.ts +97 -97
  246. package/examples/extensions/protected-paths.ts +30 -30
  247. package/examples/extensions/provider-payload.ts +18 -18
  248. package/examples/extensions/qna.ts +122 -122
  249. package/examples/extensions/question.ts +285 -285
  250. package/examples/extensions/questionnaire.ts +448 -448
  251. package/examples/extensions/rainbow-editor.ts +88 -88
  252. package/examples/extensions/reload-runtime.ts +37 -37
  253. package/examples/extensions/rpc-demo.ts +118 -118
  254. package/examples/extensions/sandbox/index.ts +321 -321
  255. package/examples/extensions/sandbox/package-lock.json +92 -92
  256. package/examples/extensions/sandbox/package.json +19 -19
  257. package/examples/extensions/send-user-message.ts +97 -97
  258. package/examples/extensions/session-name.ts +27 -27
  259. package/examples/extensions/shutdown-command.ts +63 -63
  260. package/examples/extensions/snake.ts +343 -343
  261. package/examples/extensions/space-invaders.ts +560 -560
  262. package/examples/extensions/ssh.ts +220 -220
  263. package/examples/extensions/status-line.ts +32 -32
  264. package/examples/extensions/structured-output.ts +65 -65
  265. package/examples/extensions/subagent/README.md +175 -175
  266. package/examples/extensions/subagent/agents/planner.md +37 -37
  267. package/examples/extensions/subagent/agents/reviewer.md +35 -35
  268. package/examples/extensions/subagent/agents/scout.md +50 -50
  269. package/examples/extensions/subagent/agents/worker.md +24 -24
  270. package/examples/extensions/subagent/agents.ts +126 -126
  271. package/examples/extensions/subagent/index.ts +1015 -1015
  272. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -10
  273. package/examples/extensions/subagent/prompts/implement.md +10 -10
  274. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -9
  275. package/examples/extensions/summarize.ts +209 -209
  276. package/examples/extensions/system-prompt-header.ts +17 -17
  277. package/examples/extensions/tic-tac-toe.ts +1008 -1008
  278. package/examples/extensions/timed-confirm.ts +70 -70
  279. package/examples/extensions/titlebar-spinner.ts +58 -58
  280. package/examples/extensions/todo.ts +297 -297
  281. package/examples/extensions/tool-override.ts +144 -144
  282. package/examples/extensions/tools.ts +146 -146
  283. package/examples/extensions/trigger-compact.ts +50 -50
  284. package/examples/extensions/truncated-tool.ts +195 -195
  285. package/examples/extensions/widget-placement.ts +9 -9
  286. package/examples/extensions/with-deps/index.ts +32 -32
  287. package/examples/extensions/with-deps/package-lock.json +31 -31
  288. package/examples/extensions/with-deps/package.json +22 -22
  289. package/examples/extensions/working-indicator.ts +123 -123
  290. package/examples/extensions/working-message-test.ts +25 -25
  291. package/examples/rpc-extension-ui.ts +632 -632
  292. package/examples/sdk/01-minimal.ts +26 -26
  293. package/examples/sdk/02-custom-model.ts +53 -53
  294. package/examples/sdk/03-custom-prompt.ts +75 -75
  295. package/examples/sdk/04-skills.ts +55 -55
  296. package/examples/sdk/05-tools.ts +48 -48
  297. package/examples/sdk/06-extensions.ts +99 -99
  298. package/examples/sdk/07-context-files.ts +47 -47
  299. package/examples/sdk/08-prompt-templates.ts +51 -51
  300. package/examples/sdk/09-api-keys-and-oauth.ts +52 -52
  301. package/examples/sdk/10-settings.ts +53 -53
  302. package/examples/sdk/11-sessions.ts +52 -52
  303. package/examples/sdk/12-full-control.ts +79 -79
  304. package/examples/sdk/13-session-runtime.ts +67 -67
  305. package/examples/sdk/README.md +144 -144
  306. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to `@selesai/code` will be documented in this file.
4
4
 
5
+ ## [0.9.11] - 2026-08-25
6
+
7
+ ### Fixed
8
+ - **Pi-compatible automatic skill discovery.** Skills under `~/.agents/skills/` and project/ancestor `.agents/skills/` directories, plus automatic skills from `.pi/skills/` and the Selesai agent skill directory, are now enabled by default — matching the Pi coding agent. Explicit `skills` patterns still control overrides and exclusions.
9
+
10
+ ## [0.9.10] - 2026-08-25
11
+
12
+ ### Fixed
13
+ - **Handoff from RPC hosts.** `/handoff-new` now generates and starts a child session in RPC/non-TUI hosts such as VS Code instead of requiring the terminal loader.
14
+ - **Terminal copy markers.** The `copy-turn` extension now adds its `/cp` markers only in the TUI, preventing terminal-only transcript rows from leaking into RPC host conversations and handoffs.
15
+
5
16
  ## [0.9.9] - 2026-08-25
6
17
 
7
18
  ### Fixed
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) => {
@@ -350,10 +353,9 @@ export class AgentSession {
350
353
  if (this._autoHandoffTriggered)
351
354
  return;
352
355
  this._autoHandoffTriggered = true;
353
- // handoff-new requires interactive mode (uses editor/dialog UI).
354
- // ponytail: _extensionMode is set to "tui" by InteractiveMode.bindCurrentSessionExtensions()
355
- // before the first prompt runs, so by the time _emitAgentSettled() calls this the mode is
356
- // reliable. Default "print" only applies before extensions are bound.
356
+ // Auto-handoff is TUI-only: it runs at agent_settled, and headless/print
357
+ // modes should not churn sessions on their own. The handoff-new command
358
+ // itself now also works in non-TUI modes (direct generation, no loader).
357
359
  if (this._extensionMode !== "tui")
358
360
  return;
359
361
  const command = this._extensionRunner.getCommand("handoff-new");
@@ -1436,16 +1438,17 @@ export class AgentSession {
1436
1438
  * Validates that auth is configured, saves to session and settings.
1437
1439
  * @throws Error if no auth is configured for the model
1438
1440
  */
1439
- async setModel(model) {
1441
+ async setModel(model, options = {}) {
1440
1442
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
1441
1443
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1442
1444
  }
1443
1445
  const previousModel = this.model;
1444
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1446
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
1445
1447
  this.agent.state.model = model;
1446
1448
  this.sessionManager.appendModelChange(model.provider, model.id);
1447
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1448
- // Re-clamp thinking level for new model's capabilities
1449
+ if (options.persist) {
1450
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1451
+ }
1449
1452
  this.setThinkingLevel(thinkingLevel);
1450
1453
  await this._emitModelSelect(model, previousModel, "set");
1451
1454
  }
@@ -1455,13 +1458,13 @@ export class AgentSession {
1455
1458
  * @param direction - "forward" (default) or "backward"
1456
1459
  * @returns The new model info, or undefined if only one model available
1457
1460
  */
1458
- async cycleModel(direction = "forward") {
1461
+ async cycleModel(direction = "forward", options = {}) {
1459
1462
  if (this._scopedModels.length > 0) {
1460
- return this._cycleScopedModel(direction);
1463
+ return this._cycleScopedModel(direction, options);
1461
1464
  }
1462
- return this._cycleAvailableModel(direction);
1465
+ return this._cycleAvailableModel(direction, options);
1463
1466
  }
1464
- async _cycleScopedModel(direction) {
1467
+ async _cycleScopedModel(direction, options) {
1465
1468
  const checks = await Promise.all(this._scopedModels.map(async (scoped) => ({
1466
1469
  scoped,
1467
1470
  auth: await this._modelRuntime.checkAuth(scoped.model.provider),
@@ -1476,11 +1479,13 @@ export class AgentSession {
1476
1479
  const len = scopedModels.length;
1477
1480
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1478
1481
  const next = scopedModels[nextIndex];
1479
- const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1482
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
1480
1483
  // Apply model
1481
1484
  this.agent.state.model = next.model;
1482
1485
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1483
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1486
+ if (options.persist) {
1487
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1488
+ }
1484
1489
  // Apply thinking level.
1485
1490
  // - Explicit scoped model thinking level overrides current session level
1486
1491
  // - Undefined scoped model thinking level inherits the current session preference
@@ -1489,7 +1494,7 @@ export class AgentSession {
1489
1494
  await this._emitModelSelect(next.model, currentModel, "cycle");
1490
1495
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1491
1496
  }
1492
- async _cycleAvailableModel(direction) {
1497
+ async _cycleAvailableModel(direction, options) {
1493
1498
  const availableModels = await this._modelRuntime.getAvailable();
1494
1499
  if (availableModels.length <= 1)
1495
1500
  return undefined;
@@ -1500,10 +1505,12 @@ export class AgentSession {
1500
1505
  const len = availableModels.length;
1501
1506
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1502
1507
  const nextModel = availableModels[nextIndex];
1503
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1508
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
1504
1509
  this.agent.state.model = nextModel;
1505
1510
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1506
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1511
+ if (options.persist) {
1512
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1513
+ }
1507
1514
  // Re-clamp thinking level for new model's capabilities
1508
1515
  this.setThinkingLevel(thinkingLevel);
1509
1516
  await this._emitModelSelect(nextModel, currentModel, "cycle");
@@ -1517,18 +1524,18 @@ export class AgentSession {
1517
1524
  * Clamps to model capabilities based on available thinking levels.
1518
1525
  * Saves to session and settings only if the level actually changes.
1519
1526
  */
1520
- setThinkingLevel(level) {
1527
+ setThinkingLevel(level, options = {}) {
1521
1528
  const availableLevels = this.getAvailableThinkingLevels();
1522
1529
  const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
1523
1530
  // Only persist if actually changing
1524
1531
  const previousLevel = this.agent.state.thinkingLevel;
1525
1532
  const isChanging = effectiveLevel !== previousLevel;
1526
1533
  this.agent.state.thinkingLevel = effectiveLevel;
1534
+ if (options.persist) {
1535
+ this.settingsManager.setDefaultThinkingLevel(level);
1536
+ }
1527
1537
  if (isChanging) {
1528
1538
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1529
- if (this.supportsThinking() || effectiveLevel !== "off") {
1530
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1531
- }
1532
1539
  this._emit({ type: "thinking_level_changed", level: effectiveLevel });
1533
1540
  void this._extensionRunner.emit({
1534
1541
  type: "thinking_level_select",
@@ -1541,14 +1548,14 @@ export class AgentSession {
1541
1548
  * Cycle to next thinking level.
1542
1549
  * @returns New level, or undefined if model doesn't support thinking
1543
1550
  */
1544
- cycleThinkingLevel() {
1551
+ cycleThinkingLevel(options = {}) {
1545
1552
  if (!this.supportsThinking())
1546
1553
  return undefined;
1547
1554
  const levels = this.getAvailableThinkingLevels();
1548
1555
  const currentIndex = levels.indexOf(this.thinkingLevel);
1549
1556
  const nextIndex = (currentIndex + 1) % levels.length;
1550
1557
  const nextLevel = levels[nextIndex];
1551
- this.setThinkingLevel(nextLevel);
1558
+ this.setThinkingLevel(nextLevel, options);
1552
1559
  return nextLevel;
1553
1560
  }
1554
1561
  /**
@@ -1557,7 +1564,7 @@ export class AgentSession {
1557
1564
  */
1558
1565
  getAvailableThinkingLevels() {
1559
1566
  if (!this.model)
1560
- return THINKING_LEVELS;
1567
+ return [...THINKING_LEVEL_OPTIONS];
1561
1568
  return getSupportedThinkingLevels(this.model);
1562
1569
  }
1563
1570
  /**
@@ -1566,14 +1573,16 @@ export class AgentSession {
1566
1573
  supportsThinking() {
1567
1574
  return !!this.model?.reasoning;
1568
1575
  }
1569
- _getThinkingLevelForModelSwitch(explicitLevel) {
1576
+ _getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
1570
1577
  if (explicitLevel !== undefined) {
1571
1578
  return explicitLevel;
1572
1579
  }
1573
- if (!this.supportsThinking()) {
1574
- 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;
1575
1584
  }
1576
- return this.thinkingLevel;
1585
+ return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
1577
1586
  }
1578
1587
  _clampThinkingLevel(level, _availableLevels) {
1579
1588
  return this.model ? clampThinkingLevel(this.model, level) : "off";
@@ -1711,13 +1720,21 @@ export class AgentSession {
1711
1720
  catch (error) {
1712
1721
  const message = error instanceof Error ? error.message : String(error);
1713
1722
  const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1723
+ const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1714
1724
  this._emit({
1715
1725
  type: "compaction_end",
1716
1726
  reason: "manual",
1717
1727
  result: undefined,
1718
1728
  aborted,
1719
1729
  willRetry: false,
1720
- 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,
1721
1738
  });
1722
1739
  throw error;
1723
1740
  }
@@ -1836,6 +1853,7 @@ export class AgentSession {
1836
1853
  async _runAutoCompaction(reason, willRetry) {
1837
1854
  const settings = this.settingsManager.getCompactionSettings();
1838
1855
  let started = false;
1856
+ let fromExtension = false;
1839
1857
  try {
1840
1858
  if (!this.model) {
1841
1859
  return false;
@@ -1858,7 +1876,6 @@ export class AgentSession {
1858
1876
  this._autoCompactionAbortController = new AbortController();
1859
1877
  started = true;
1860
1878
  let extensionCompaction;
1861
- let fromExtension = false;
1862
1879
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1863
1880
  const extensionResult = (await this._extensionRunner.emit({
1864
1881
  type: "session_before_compact",
@@ -1877,6 +1894,7 @@ export class AgentSession {
1877
1894
  aborted: true,
1878
1895
  willRetry: false,
1879
1896
  });
1897
+ await this._emitSessionCompactFailed({ reason, aborted: true, willRetry: false, fromExtension: false });
1880
1898
  return false;
1881
1899
  }
1882
1900
  if (extensionResult?.compaction) {
@@ -1914,6 +1932,7 @@ export class AgentSession {
1914
1932
  aborted: true,
1915
1933
  willRetry: false,
1916
1934
  });
1935
+ await this._emitSessionCompactFailed({ reason, aborted: true, willRetry: false, fromExtension });
1917
1936
  return false;
1918
1937
  }
1919
1938
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
@@ -1956,15 +1975,23 @@ export class AgentSession {
1956
1975
  catch (error) {
1957
1976
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1958
1977
  if (started) {
1978
+ const formattedErrorMessage = reason === "overflow"
1979
+ ? `Context overflow recovery failed: ${errorMessage}`
1980
+ : `Auto-compaction failed: ${errorMessage}`;
1959
1981
  this._emit({
1960
1982
  type: "compaction_end",
1961
1983
  reason,
1962
1984
  result: undefined,
1963
1985
  aborted: false,
1964
1986
  willRetry: false,
1965
- errorMessage: reason === "overflow"
1966
- ? `Context overflow recovery failed: ${errorMessage}`
1967
- : `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,
1968
1995
  });
1969
1996
  }
1970
1997
  return false;
@@ -2188,6 +2215,39 @@ export class AgentSession {
2188
2215
  },
2189
2216
  });
2190
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
+ }
2191
2251
  _refreshToolRegistry(options) {
2192
2252
  const previousRegistryNames = new Set(this._toolRegistry.keys());
2193
2253
  const previousActiveToolNames = this.getActiveToolNames();
@@ -2292,6 +2352,7 @@ export class AgentSession {
2292
2352
  bash: { commandPrefix: shellCommandPrefix, shellPath },
2293
2353
  });
2294
2354
  this._baseToolDefinitions = new Map(Object.entries(baseToolDefinitions).map(([name, tool]) => [name, tool]));
2355
+ this._baseToolDefinitions.set("tool_catalog", this._createToolCatalogDefinition());
2295
2356
  const extensionsResult = this._resourceLoader.getExtensions();
2296
2357
  if (options.flagValues) {
2297
2358
  for (const [name, value] of options.flagValues) {
@@ -2306,7 +2367,7 @@ export class AgentSession {
2306
2367
  this._applyExtensionBindings(this._extensionRunner);
2307
2368
  const defaultActiveToolNames = this._baseToolsOverride
2308
2369
  ? Object.keys(this._baseToolsOverride)
2309
- : ["read", "bash", "edit", "write"];
2370
+ : ["read", "bash", "edit", "write", "tool_catalog"];
2310
2371
  const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames;
2311
2372
  this._refreshToolRegistry({
2312
2373
  activeToolNames: baseActiveToolNames,
@@ -2323,7 +2384,6 @@ export class AgentSession {
2323
2384
  this._buildRuntime({
2324
2385
  activeToolNames: this.getActiveToolNames(),
2325
2386
  flagValues: previousFlagValues,
2326
- includeAllExtensionTools: true,
2327
2387
  });
2328
2388
  const hasBindings = this._extensionUIContext ||
2329
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.