@gajae-code/coding-agent 0.18.7 → 0.18.9

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 (344) hide show
  1. package/CHANGELOG.md +377 -1
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/job-manager.d.ts +1 -1
  4. package/dist/types/async/support.d.ts +0 -14
  5. package/dist/types/capability/mcp.d.ts +2 -0
  6. package/dist/types/capability/skill.d.ts +2 -0
  7. package/dist/types/capability/types.d.ts +2 -0
  8. package/dist/types/cli/public-command-errors.d.ts +3 -1
  9. package/dist/types/cli/setup-cli.d.ts +1 -1
  10. package/dist/types/commands/deep-interview.d.ts +3 -0
  11. package/dist/types/commands/mcp-serve.d.ts +4 -0
  12. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  13. package/dist/types/config/file-lock.d.ts +1 -0
  14. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  15. package/dist/types/config/model-profile-activation.d.ts +11 -3
  16. package/dist/types/config/model-registry.d.ts +4 -9
  17. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  18. package/dist/types/cursor-delete-path.d.ts +1 -0
  19. package/dist/types/cursor-mcp-name.d.ts +8 -0
  20. package/dist/types/customization/inventory.d.ts +2 -0
  21. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  24. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  25. package/dist/types/discovery/helpers.d.ts +6 -1
  26. package/dist/types/discovery/mcp-json.d.ts +1 -0
  27. package/dist/types/eval/py/kernel.d.ts +18 -0
  28. package/dist/types/export/html/css-color.d.ts +2 -0
  29. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  32. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  33. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  34. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  35. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  36. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  37. package/dist/types/extensibility/skill-management.d.ts +4 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  40. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  41. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  42. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  44. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  45. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  46. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  47. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  48. package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
  49. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  50. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  51. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  52. package/dist/types/lsp/config.d.ts +8 -5
  53. package/dist/types/lsp/workspace-path.d.ts +1 -0
  54. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  55. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  56. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  57. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  58. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  59. package/dist/types/modes/interactive-mode.d.ts +1 -0
  60. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  61. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  62. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  63. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  64. package/dist/types/progress/progress-contract.d.ts +154 -0
  65. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  66. package/dist/types/progress/project-progress.d.ts +141 -0
  67. package/dist/types/progress/render-progress.d.ts +28 -0
  68. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  69. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  70. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
  71. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  72. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  73. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  74. package/dist/types/runtime-mcp/types.d.ts +6 -0
  75. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  76. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  77. package/dist/types/sdk/broker/broker.d.ts +24 -2
  78. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  79. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  80. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  81. package/dist/types/sdk/bus/index.d.ts +12 -1
  82. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  83. package/dist/types/sdk/client/client.d.ts +2 -0
  84. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  85. package/dist/types/sdk/index.d.ts +1 -0
  86. package/dist/types/sdk/router/session-router.d.ts +2 -0
  87. package/dist/types/sdk/session-list.d.ts +1 -1
  88. package/dist/types/sdk/session.d.ts +8 -0
  89. package/dist/types/session/agent-session.d.ts +38 -9
  90. package/dist/types/session/artifacts.d.ts +2 -2
  91. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  92. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  93. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  94. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  95. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  96. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  97. package/dist/types/session/session-manager.d.ts +31 -5
  98. package/dist/types/session/session-retirement.d.ts +31 -7
  99. package/dist/types/session/session-storage.d.ts +69 -2
  100. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  101. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  102. package/dist/types/setup/credential-import.d.ts +6 -4
  103. package/dist/types/skill-state/active-state.d.ts +38 -0
  104. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  105. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  106. package/dist/types/task/executor.d.ts +3 -0
  107. package/dist/types/task/isolation-dir.d.ts +26 -0
  108. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  109. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  110. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  111. package/dist/types/tools/github-side-effect.d.ts +22 -0
  112. package/dist/types/tools/index.d.ts +3 -0
  113. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  114. package/dist/types/tools/python.d.ts +9 -1
  115. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  116. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  117. package/dist/types/tools/todo-write.d.ts +6 -0
  118. package/dist/types/utils/tools-checksum.d.ts +7 -0
  119. package/package.json +7 -7
  120. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  121. package/scripts/verify-managed-empty-stop.ts +28 -0
  122. package/src/async/endpoint-id.ts +26 -0
  123. package/src/async/job-manager.ts +247 -132
  124. package/src/async/support.ts +0 -25
  125. package/src/capability/index.ts +15 -0
  126. package/src/capability/mcp.ts +2 -0
  127. package/src/capability/skill.ts +2 -0
  128. package/src/capability/types.ts +2 -0
  129. package/src/cli/auth-broker-cli.ts +1 -1
  130. package/src/cli/customize-doctor.ts +15 -2
  131. package/src/cli/doctor/repairs.ts +4 -1
  132. package/src/cli/doctor-supervisor.ts +3 -0
  133. package/src/cli/public-command-errors.ts +21 -1
  134. package/src/cli/public-command-registry.ts +28 -6
  135. package/src/cli/setup-cli.ts +4 -4
  136. package/src/cli/skills-cli.ts +5 -0
  137. package/src/commands/deep-interview.ts +47 -8
  138. package/src/commands/mcp-serve.ts +7 -0
  139. package/src/commands/sdk.ts +455 -397
  140. package/src/config/autorouting-tier-map.ts +117 -7
  141. package/src/config/file-lock.ts +316 -79
  142. package/src/config/mcp-schema.json +4 -0
  143. package/src/config/model-bindings-applier.ts +20 -0
  144. package/src/config/model-profile-activation.ts +240 -46
  145. package/src/config/model-registry.ts +47 -153
  146. package/src/config/skill-settings-defaults.ts +7 -0
  147. package/src/coordinator-mcp/policy.ts +3 -2
  148. package/src/coordinator-mcp/server.ts +2 -0
  149. package/src/cursor-delete-path.ts +10 -0
  150. package/src/cursor-mcp-name.ts +15 -0
  151. package/src/cursor.ts +13 -2
  152. package/src/customization/inventory.ts +4 -13
  153. package/src/dap/config.ts +2 -2
  154. package/src/debug/report-bundle-parent.ts +22 -0
  155. package/src/debug/report-bundle.ts +3 -0
  156. package/src/debug/system-info.ts +13 -1
  157. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  158. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  159. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  160. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  161. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  162. package/src/defaults/gjc-defaults.test.ts +18 -1
  163. package/src/defaults/gjc-defaults.ts +3 -0
  164. package/src/defaults/gjc-skills.generated.ts +58 -36
  165. package/src/discovery/agents.ts +6 -1
  166. package/src/discovery/builtin.ts +13 -1
  167. package/src/discovery/helpers.ts +171 -29
  168. package/src/discovery/mcp-json.ts +26 -12
  169. package/src/discovery/ssh.ts +1 -1
  170. package/src/discovery/substitute-plugin-root.ts +3 -1
  171. package/src/edit/file-read-cache.ts +1 -1
  172. package/src/eval/py/executor.ts +796 -154
  173. package/src/eval/py/kernel.ts +79 -13
  174. package/src/eval/py/tool-bridge.ts +4 -0
  175. package/src/export/html/css-color.ts +9 -0
  176. package/src/export/html/html-export-destination.ts +16 -0
  177. package/src/export/html/index.ts +7 -10
  178. package/src/export/html/template.generated.ts +1 -1
  179. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  180. package/src/extensibility/extensions/runner.ts +15 -1
  181. package/src/extensibility/extensions/types.ts +7 -0
  182. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  183. package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -8
  184. package/src/extensibility/plugins/manager.ts +3 -2
  185. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  186. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  187. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  188. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  189. package/src/extensibility/skill-management.ts +9 -2
  190. package/src/extensibility/skills.ts +6 -1
  191. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  192. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  193. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  194. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  195. package/src/gjc-runtime/gc-runtime.ts +206 -9
  196. package/src/gjc-runtime/launch-worktree.ts +70 -9
  197. package/src/gjc-runtime/psmux-detect.ts +28 -4
  198. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  199. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  200. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  201. package/src/gjc-runtime/state-argv.ts +2 -0
  202. package/src/gjc-runtime/state-migrations.ts +91 -29
  203. package/src/gjc-runtime/state-runtime.ts +3716 -135
  204. package/src/gjc-runtime/state-writer.ts +105 -16
  205. package/src/gjc-runtime/tmux-common.ts +8 -5
  206. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  207. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  208. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  209. package/src/harness-control-plane/operate.ts +12 -4
  210. package/src/harness-control-plane/owner.ts +5 -1
  211. package/src/hashline/parser.ts +48 -9
  212. package/src/hashline/recovery.ts +13 -4
  213. package/src/internal-urls/docs-index.generated.ts +20 -19
  214. package/src/internal-urls/skill-path.ts +19 -0
  215. package/src/internal-urls/skill-protocol.ts +2 -0
  216. package/src/lsp/config.ts +12 -63
  217. package/src/lsp/edits.ts +6 -0
  218. package/src/lsp/workspace-path.ts +51 -0
  219. package/src/main.ts +195 -31
  220. package/src/memories/index.ts +88 -7
  221. package/src/modes/acp/acp-agent.ts +88 -4
  222. package/src/modes/components/custom-editor.ts +14 -0
  223. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  224. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  225. package/src/modes/components/extensions/state-manager.ts +15 -5
  226. package/src/modes/components/hook-editor.ts +1 -1
  227. package/src/modes/components/hook-selector.ts +1 -1
  228. package/src/modes/components/model-selector.ts +1 -4
  229. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  230. package/src/modes/components/status-line/segments.ts +4 -0
  231. package/src/modes/controllers/event-controller.ts +11 -4
  232. package/src/modes/controllers/input-controller.ts +76 -31
  233. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
  234. package/src/modes/controllers/selector-controller.ts +24 -15
  235. package/src/modes/controllers/todo-command-controller.ts +1 -1
  236. package/src/modes/interactive-mode.ts +5 -1
  237. package/src/modes/runtime-init.ts +4 -0
  238. package/src/modes/utils/abort-message.ts +3 -1
  239. package/src/modes/utils/interrupt-ownership.ts +12 -0
  240. package/src/modes/utils/keybinding-matchers.ts +4 -0
  241. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  242. package/src/progress/collect-project-progress.ts +153 -0
  243. package/src/progress/progress-contract.ts +172 -0
  244. package/src/progress/progress-snapshot.ts +140 -0
  245. package/src/progress/project-progress.ts +446 -0
  246. package/src/progress/render-progress.ts +209 -0
  247. package/src/prompts/system/master-mode.md +1 -0
  248. package/src/prompts/tools/skill-discovery.md +1 -1
  249. package/src/runtime-mcp/client.ts +4 -2
  250. package/src/runtime-mcp/config-writer.ts +10 -6
  251. package/src/runtime-mcp/config.ts +1 -0
  252. package/src/runtime-mcp/json-rpc.ts +2 -1
  253. package/src/runtime-mcp/manager.ts +29 -11
  254. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  255. package/src/runtime-mcp/oauth-flow.ts +14 -2
  256. package/src/runtime-mcp/oauth-public-url.ts +14 -0
  257. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  258. package/src/runtime-mcp/pool.ts +6 -0
  259. package/src/runtime-mcp/project-host-values.ts +46 -0
  260. package/src/runtime-mcp/smithery-registry.ts +22 -3
  261. package/src/runtime-mcp/transports/http.ts +4 -3
  262. package/src/runtime-mcp/types.ts +6 -0
  263. package/src/sdk/acp/adapter.ts +95 -8
  264. package/src/sdk/broker/broker-exit.ts +52 -23
  265. package/src/sdk/broker/broker.ts +81 -6
  266. package/src/sdk/broker/doctor-restart.ts +4 -0
  267. package/src/sdk/broker/ensure.ts +31 -2
  268. package/src/sdk/broker/lifecycle.ts +711 -66
  269. package/src/sdk/broker/session-index.ts +191 -81
  270. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  271. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  272. package/src/sdk/bus/index.ts +238 -43
  273. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  274. package/src/sdk/cli/session-cli.ts +17 -3
  275. package/src/sdk/client/client.ts +28 -8
  276. package/src/sdk/host/query/handlers.ts +9 -1
  277. package/src/sdk/host/session-runtime.test.ts +2705 -303
  278. package/src/sdk/host/session-runtime.ts +490 -143
  279. package/src/sdk/host/surface-policy.ts +1 -0
  280. package/src/sdk/index.ts +8 -0
  281. package/src/sdk/lifecycle-session.ts +3 -1
  282. package/src/sdk/prompt-deadline-manager.ts +15 -0
  283. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  284. package/src/sdk/protocol/operation-registry.ts +9 -2
  285. package/src/sdk/router/session-router.ts +82 -18
  286. package/src/sdk/session-list.ts +1 -0
  287. package/src/sdk/session.ts +69 -45
  288. package/src/session/agent-session.ts +679 -238
  289. package/src/session/artifacts.ts +176 -71
  290. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  291. package/src/session/internal/managed-session-scope.ts +3779 -462
  292. package/src/session/internal/managed-session-storage.ts +501 -78
  293. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  294. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  295. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  296. package/src/session/session-manager.ts +951 -133
  297. package/src/session/session-retirement.ts +573 -14
  298. package/src/session/session-storage.ts +447 -15
  299. package/src/session/task-artifact-owner-codec.ts +71 -8
  300. package/src/session/task-artifact-owner-retirement.ts +21 -19
  301. package/src/setup/credential-auto-import.ts +12 -3
  302. package/src/setup/credential-import.ts +176 -5
  303. package/src/setup/paseo/announce.ts +6 -1
  304. package/src/skill-state/active-state.ts +145 -2
  305. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  306. package/src/slash-commands/builtin-registry.ts +39 -9
  307. package/src/slash-commands/helpers/mcp.ts +8 -4
  308. package/src/slash-commands/helpers/progress-report.ts +23 -0
  309. package/src/stt/setup.ts +3 -5
  310. package/src/task/commands.ts +3 -1
  311. package/src/task/executor.ts +4 -0
  312. package/src/task/index.ts +6 -0
  313. package/src/task/isolation-dir.ts +143 -0
  314. package/src/task/worktree.ts +16 -7
  315. package/src/tools/ask.ts +205 -6
  316. package/src/tools/ast-edit-write-path.ts +68 -0
  317. package/src/tools/ast-edit.ts +10 -1
  318. package/src/tools/bash-allowed-prefixes.ts +15 -0
  319. package/src/tools/bash-skill-urls.ts +56 -3
  320. package/src/tools/bash.ts +48 -79
  321. package/src/tools/browser/cdp-url.ts +18 -0
  322. package/src/tools/browser.ts +8 -1
  323. package/src/tools/descriptors.ts +5 -1
  324. package/src/tools/eval.ts +56 -36
  325. package/src/tools/gh.ts +10 -0
  326. package/src/tools/github-side-effect.ts +112 -0
  327. package/src/tools/image-gen.ts +1 -1
  328. package/src/tools/index.ts +3 -0
  329. package/src/tools/monitor-mutation.ts +27 -0
  330. package/src/tools/monitor.ts +7 -0
  331. package/src/tools/python.ts +188 -66
  332. package/src/tools/recipe/index.ts +9 -0
  333. package/src/tools/recipe/recipe-guard.ts +19 -0
  334. package/src/tools/restricted-cwd.ts +10 -0
  335. package/src/tools/skill-discovery.ts +5 -0
  336. package/src/tools/skill.ts +28 -1
  337. package/src/tools/todo-write.ts +10 -1
  338. package/src/tools/tool-catalog.generated.ts +1 -1
  339. package/src/tools/write.ts +1 -0
  340. package/src/utils/external-editor.ts +3 -2
  341. package/src/utils/tools-checksum.ts +34 -0
  342. package/src/utils/tools-manager.ts +24 -6
  343. package/src/web/insane/url-guard.ts +2 -1
  344. package/src/web/search/providers/codex.ts +3 -2
@@ -3,7 +3,14 @@ import type * as fsNode from "node:fs";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
5
  import type { AgentMessage } from "@gajae-code/agent-core";
6
- import { completeSimple, Effort, type Model } from "@gajae-code/ai/core";
6
+ import { estimateTextTokensHeuristic } from "@gajae-code/agent-core/compaction";
7
+ import {
8
+ ANTHROPIC_THINKING,
9
+ completeSimple,
10
+ Effort,
11
+ type Model,
12
+ resolveDefaultRequestMaxTokens,
13
+ } from "@gajae-code/ai/core";
7
14
  import { clampThinkingLevelForModel } from "@gajae-code/ai/model-thinking";
8
15
  import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient, prompt } from "@gajae-code/utils";
9
16
  import type { ModelRegistry } from "../config/model-registry";
@@ -74,6 +81,20 @@ const DEFAULTS: MemoryRuntimeConfig = {
74
81
  summaryInjectionTokenLimit: 5_000,
75
82
  };
76
83
 
84
+ const PHASE2_RAW_MEMORIES_TOKEN_LIMIT = 20_000;
85
+ const PHASE2_ROLLOUT_SUMMARIES_TOKEN_LIMIT = 12_000;
86
+ // Ceiling for the phase 2 response. Phase 2 rewrites MEMORY.md, the summary and every
87
+ // skill in one JSON response, and on adaptive-thinking / reasoning-effort models the
88
+ // reasoning shares this budget. Twice the (approximate) input caps is a generous
89
+ // practical ceiling, not a guarantee that every response fits; a response that still
90
+ // overruns it is reported as truncation.
91
+ const PHASE2_MAX_OUTPUT_TOKENS = 2 * (PHASE2_RAW_MEMORIES_TOKEN_LIMIT + PHASE2_ROLLOUT_SUMMARIES_TOKEN_LIMIT);
92
+ // On a small context window phase 2 shrinks its inputs rather than let the response
93
+ // budget drop below the previous fixed budget.
94
+ const PHASE2_MIN_OUTPUT_TOKENS = 8_192;
95
+ // Request framing plus estimator error when fitting input + output into the context window.
96
+ const PHASE2_CONTEXT_SAFETY_TOKENS = 2_048;
97
+
77
98
  interface Stage1Stats {
78
99
  claimed: number;
79
100
  succeeded: number;
@@ -738,10 +759,8 @@ async function runConsolidationModel(options: {
738
759
  const { memoryRoot, model, apiKey } = options;
739
760
  const rawMemories = await Bun.file(path.join(memoryRoot, "raw_memories.md")).text();
740
761
  const rolloutSummaries = await readRolloutSummaries(memoryRoot);
741
- const input = prompt.render(consolidationTemplate, {
742
- raw_memories: truncateByApproxTokens(rawMemories, 20_000),
743
- rollout_summaries: truncateByApproxTokens(rolloutSummaries, 12_000),
744
- });
762
+ const reasoning = clampThinkingLevelForModel(model, Effort.Low);
763
+ const { input, maxTokens } = buildPhase2Request(model, rawMemories, rolloutSummaries, reasoning);
745
764
 
746
765
  const response = await completeSimple(
747
766
  model,
@@ -751,18 +770,27 @@ async function runConsolidationModel(options: {
751
770
  {
752
771
  apiKey,
753
772
  metadata: options.metadata,
754
- maxTokens: 8192,
755
- reasoning: clampThinkingLevelForModel(model, Effort.Medium),
773
+ maxTokens,
774
+ reasoning,
756
775
  },
757
776
  );
758
777
  if (response.stopReason === "error") {
759
778
  throw new Error(response.errorMessage || "phase2 model error");
760
779
  }
780
+ if (response.stopReason === "length") {
781
+ throw new Error(`phase2 output truncated at the max output token limit (${response.usage.output} output tokens)`);
782
+ }
761
783
  const text = response.content
762
784
  .filter((c): c is { type: "text"; text: string } => c.type === "text")
763
785
  .map(c => c.text)
764
786
  .join("\n")
765
787
  .trim();
788
+ if (!text) {
789
+ const thinkingBlocks = response.content.filter(c => c.type === "thinking").length;
790
+ throw new Error(
791
+ `phase2 returned no text (stopReason=${response.stopReason}, ${response.usage.output} output tokens, ${thinkingBlocks} thinking blocks)`,
792
+ );
793
+ }
766
794
  const parsed = parseJsonObject(text);
767
795
  if (!parsed) throw new Error("phase2 JSON parse failure");
768
796
  const schemaOutput = parseConsolidationOutputSchema(parsed);
@@ -1101,6 +1129,59 @@ function truncateByApproxTokens(text: string, tokenLimit: number): string {
1101
1129
  return `${text.slice(0, head)}\n\n...[truncated]...\n\n${text.slice(-tail)}`;
1102
1130
  }
1103
1131
 
1132
+ /**
1133
+ * Render the phase 2 prompt and choose its response budget.
1134
+ *
1135
+ * The budget is the model's output limit capped at PHASE2_MAX_OUTPUT_TOKENS (or the
1136
+ * provider layer's request default when the limit is unknown). When the context window
1137
+ * is known, input + response + any thinking budget the provider adds on top must fit
1138
+ * in it: the response budget is clamped to the remaining room, and the inputs are
1139
+ * shrunk when that room would fall below PHASE2_MIN_OUTPUT_TOKENS.
1140
+ */
1141
+ function buildPhase2Request(
1142
+ model: Model,
1143
+ rawMemories: string,
1144
+ rolloutSummaries: string,
1145
+ reasoning: Effort | undefined,
1146
+ ): { input: string; maxTokens: number | undefined } {
1147
+ const outputLimit =
1148
+ Number.isSafeInteger(model.maxTokens) && model.maxTokens > 0
1149
+ ? Math.min(model.maxTokens, PHASE2_MAX_OUTPUT_TOKENS)
1150
+ : undefined;
1151
+ let rawTokens = Math.min(PHASE2_RAW_MEMORIES_TOKEN_LIMIT, Math.ceil(rawMemories.length / 4));
1152
+ let rolloutTokens = Math.min(PHASE2_ROLLOUT_SUMMARIES_TOKEN_LIMIT, Math.ceil(rolloutSummaries.length / 4));
1153
+ const render = () =>
1154
+ prompt.render(consolidationTemplate, {
1155
+ raw_memories: truncateByApproxTokens(rawMemories, rawTokens),
1156
+ rollout_summaries: truncateByApproxTokens(rolloutSummaries, rolloutTokens),
1157
+ });
1158
+
1159
+ const contextWindow =
1160
+ Number.isSafeInteger(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : undefined;
1161
+ if (contextWindow === undefined) return { input: render(), maxTokens: outputLimit };
1162
+
1163
+ const responseCeiling = resolveDefaultRequestMaxTokens(model, outputLimit);
1164
+ const minResponse = Math.min(PHASE2_MIN_OUTPUT_TOKENS, responseCeiling);
1165
+ // Budget-mode providers may add the thinking budget on top of maxTokens.
1166
+ const addsThinkingBudget = model.thinking?.mode === "budget" || model.thinking?.mode === "anthropic-budget-effort";
1167
+ const thinkingReserve = reasoning && addsThinkingBudget ? ANTHROPIC_THINKING[reasoning] : 0;
1168
+ const inputRoom = contextWindow - PHASE2_CONTEXT_SAFETY_TOKENS - thinkingReserve - minResponse;
1169
+
1170
+ for (let attempt = 0; attempt < 4; attempt++) {
1171
+ const input = render();
1172
+ const inputTokens = estimateTextTokensHeuristic(input);
1173
+ if (inputTokens <= inputRoom) {
1174
+ const room = contextWindow - PHASE2_CONTEXT_SAFETY_TOKENS - thinkingReserve - inputTokens;
1175
+ return { input, maxTokens: Math.min(responseCeiling, room) };
1176
+ }
1177
+ if (inputRoom <= 0 || rawTokens + rolloutTokens === 0) break;
1178
+ const scale = (inputRoom / inputTokens) * 0.9;
1179
+ rawTokens = Math.floor(rawTokens * scale);
1180
+ rolloutTokens = Math.floor(rolloutTokens * scale);
1181
+ }
1182
+ throw new Error(`phase2 input does not fit the model context window (${contextWindow} tokens)`);
1183
+ }
1184
+
1104
1185
  function computeModelTokenBudget(model: Model, config: MemoryRuntimeConfig): number {
1105
1186
  const maxTokens =
1106
1187
  Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : config.fallbackTokenLimit;
@@ -38,6 +38,8 @@ import {
38
38
  } from "@agentclientprotocol/sdk";
39
39
  import { getAgentDir, logger, resolveEquivalentPath } from "@gajae-code/utils";
40
40
  import packageJson from "../../../package.json" with { type: "json" };
41
+ import { PROJECT_PROGRESS_SNAPSHOT_SCHEMA, type ProjectProgressSnapshot } from "../../progress/progress-contract";
42
+ import { PROGRESS_COMMAND_ACP_DESCRIPTION, renderProgressSnapshot } from "../../progress/render-progress";
41
43
  import {
42
44
  ACP_SESSION_RECONNECT,
43
45
  type AcpProviderRegistration,
@@ -258,6 +260,8 @@ type PendingAttachment = { epoch: number; task: Promise<void> };
258
260
 
259
261
  type PromptAdmissionSettlement = { kind: "cancelled" } | { kind: "rejected"; error: AcpSdkAdapterError };
260
262
  type PromptAdmissionReservation = {
263
+ /** Held by a model-free builtin (`/progress`) answered locally; no host turn exists to abort. */
264
+ localCommand?: true;
261
265
  cancelled?: boolean;
262
266
  settlement?: PromptAdmissionSettlement;
263
267
  wake: () => void;
@@ -526,9 +530,12 @@ function pageItems(value: unknown): unknown[] {
526
530
  return Array.isArray(page?.items) ? page.items : [];
527
531
  }
528
532
 
533
+ /** Builtin `/progress`: answered locally from the read-only `session.progress` SDK query, never a model turn. */
534
+ const ACP_PROGRESS_COMMAND: AvailableCommand = { name: "progress", description: PROGRESS_COMMAND_ACP_DESCRIPTION };
535
+
529
536
  /** Build the ACP command palette from the shared builtins and live SDK skill state. */
530
537
  export function acpAvailableCommandsFromSkills(query: unknown): AvailableCommand[] {
531
- const commands = new Map<string, AvailableCommand>();
538
+ const commands = new Map<string, AvailableCommand>([[ACP_PROGRESS_COMMAND.name, ACP_PROGRESS_COMMAND]]);
532
539
  for (const item of pageItems(query)) {
533
540
  const skill = object(item);
534
541
  if (typeof skill?.name !== "string" || !skill.name) continue;
@@ -1508,6 +1515,22 @@ export function acpSkillInvocation(blocks: PromptRequest["prompt"]): { name: str
1508
1515
  return { name: match[1], args: match[2]?.trim() ?? "" };
1509
1516
  }
1510
1517
 
1518
+ /**
1519
+ * Recognize the builtin `/progress` command only when it is the complete, single text
1520
+ * prompt. Like the TUI (`allowArgs: false`), `/progress <args>` is not the command.
1521
+ */
1522
+ export function isAcpProgressCommand(blocks: PromptRequest["prompt"]): boolean {
1523
+ return blocks.length === 1 && blocks[0]?.type === "text" && blocks[0].text.trim() === "/progress";
1524
+ }
1525
+
1526
+ /** Render the `session.progress` query result as the plain-text `/progress` view. */
1527
+ export function acpProgressReportText(query: unknown): string {
1528
+ const snapshot = object(pageItems(query)[0]);
1529
+ if (snapshot?.schema !== PROJECT_PROGRESS_SNAPSHOT_SCHEMA)
1530
+ throw new AcpSdkAdapterError("unavailable", "SDK session.progress response omitted the progress snapshot.");
1531
+ return renderProgressSnapshot(snapshot as unknown as ProjectProgressSnapshot).join("\n");
1532
+ }
1533
+
1511
1534
  /** Convert every ACP prompt block the agent advertises without silently discarding context. */
1512
1535
  export function acpPromptPayload(blocks: PromptRequest["prompt"]): {
1513
1536
  text: string;
@@ -2398,6 +2421,38 @@ export class AcpAgent implements Agent {
2398
2421
  }
2399
2422
  }
2400
2423
 
2424
+ /**
2425
+ * Read-only builtin `/progress`: answered from the session.progress query instead of a
2426
+ * model turn. It owns prompt admission (`pendingPromptAdmission`) from the query until its
2427
+ * response is published, so a concurrent prompt is refused as `conflict` exactly as it is
2428
+ * while a model turn is active. `cancel` and session teardown settle that reservation
2429
+ * through the existing admission paths; this command observes it and stops publishing.
2430
+ */
2431
+ async #runProgressCommand(sessionId: string, record: SessionRecord): Promise<PromptResponse> {
2432
+ const { promise: woken, resolve: wake } = Promise.withResolvers<void>();
2433
+ const reservation: PromptAdmissionReservation = { localCommand: true, wake, woken };
2434
+ // Replaces a follow-up reservation this prompt already consumed; `#submitPrompt` only
2435
+ // clears the reservation it installed, so ownership stays with this command.
2436
+ record.pendingPromptAdmission = reservation;
2437
+ try {
2438
+ const query = record.adapter.query("session.progress");
2439
+ void query.catch(() => undefined);
2440
+ const result = await Promise.race([query.then(value => ({ value })), woken.then(() => undefined)]);
2441
+ const settlement = reservation.settlement;
2442
+ if (settlement?.kind === "rejected") throw settlement.error;
2443
+ if (settlement?.kind === "cancelled" || reservation.cancelled || !result) return { stopReason: "cancelled" };
2444
+ const text = acpProgressReportText(result.value);
2445
+ await this.#publishSessionUpdate(
2446
+ sessionId,
2447
+ { sessionId, update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text } } },
2448
+ record.adapter,
2449
+ );
2450
+ return { stopReason: "end_turn" };
2451
+ } finally {
2452
+ if (record.pendingPromptAdmission === reservation) record.pendingPromptAdmission = undefined;
2453
+ }
2454
+ }
2455
+
2401
2456
  async #waitForPromptSettlement(
2402
2457
  waiter: PromptWaiter,
2403
2458
  admissionReservation: PromptAdmissionReservation,
@@ -2482,6 +2537,7 @@ export class AcpAgent implements Agent {
2482
2537
  );
2483
2538
  return { stopReason: "end_turn" };
2484
2539
  }
2540
+ if (isAcpProgressCommand(params.prompt)) return await this.#runProgressCommand(params.sessionId, record);
2485
2541
  // The SDK transport hard-caps a single request frame at 256 KiB and answers an
2486
2542
  // oversize frame by closing the socket (CloseCode::Size, crates/gjc-sdk/src/server.rs),
2487
2543
  // which surfaces to the client as an opaque `connection_closed` mid-turn. Reject
@@ -2969,6 +3025,7 @@ export class AcpAgent implements Agent {
2969
3025
  // Record the client's intent before awaiting the SDK so a prompt that rejects
2970
3026
  // mid-cancel (e.g. preflight `busy`) can still settle as `cancelled`.
2971
3027
  record.cancelRequested = true;
3028
+ const localCommand = !record.activePrompt && record.pendingPromptAdmission?.localCommand === true;
2972
3029
  this.#settlePendingPromptAdmission(record, { kind: "cancelled" });
2973
3030
  // C04 terminal abort: an external client cancel stops the current turn
2974
3031
  // (`scope:"turn"`, the default, matching the SDK `turn.abort` default and
@@ -2976,6 +3033,12 @@ export class AcpAgent implements Agent {
2976
3033
  // subagents and background tasks stopped opts in with
2977
3034
  // `_meta.gjc.abortScope: "owned"` (or `GJC_ACP_ABORT_SCOPE=owned`).
2978
3035
  const scope = resolveAcpAbortScope(params._meta, process.env);
3036
+ // The current turn is a model-free builtin answered locally: settling its reservation
3037
+ // above cancels it, and the host has no turn to abort.
3038
+ if (localCommand && scope === "turn") {
3039
+ record.cancelRequested = false;
3040
+ return;
3041
+ }
2979
3042
  const waiter = record.activePrompt;
2980
3043
  const waiterWasUnacknowledged = waiter !== undefined && !waiter.acknowledged;
2981
3044
  const waiterWasBeforeActivity = waiter !== undefined && !waiter.observedTurnActivity;
@@ -3051,9 +3114,11 @@ export class AcpAgent implements Agent {
3051
3114
  // publication is advisory and must never gate the settlement above.
3052
3115
  void this.#publishPromptPhaseIdle(params.sessionId, record.adapter);
3053
3116
  } else {
3117
+ if (waiter && record.activePrompt === waiter && waiter.terminal && !waiter.settled)
3118
+ this.#settlePrompt(params.sessionId, record, waiter);
3054
3119
  // The acknowledgement proves the run was aborted, not that its terminal was
3055
3120
  // published. Arm the bounded settlement so the turn cannot outlive the cancel.
3056
- this.#scheduleCancelSettlement(params.sessionId, record);
3121
+ else this.#scheduleCancelSettlement(params.sessionId, record);
3057
3122
  }
3058
3123
  waiter?.cancelAttemptResolve?.(true);
3059
3124
  } catch (error) {
@@ -3069,6 +3134,9 @@ export class AcpAgent implements Agent {
3069
3134
  !waiter.settled
3070
3135
  ) {
3071
3136
  if (waiter.terminalReserved) {
3137
+ await record.frameTail;
3138
+ if (record.activePrompt === waiter && waiter.terminal && !waiter.settled)
3139
+ this.#settlePrompt(params.sessionId, record, waiter);
3072
3140
  waiter.cancelAttemptResolve?.(true);
3073
3141
  return;
3074
3142
  }
@@ -3124,6 +3192,9 @@ export class AcpAgent implements Agent {
3124
3192
  waiter.cancelAttempt = undefined;
3125
3193
  waiter.cancelAttemptResolve = undefined;
3126
3194
  }
3195
+ const cancelWaveSettled = !waiter || waiter.cancelAcknowledged || (waiter.pendingCancelAttempts ?? 0) <= 1;
3196
+ if (cancelWaveSettled && waiter && record.activePrompt === waiter && waiter.terminal && !waiter.settled)
3197
+ this.#settlePrompt(params.sessionId, record, waiter);
3127
3198
  throw cancellationError;
3128
3199
  } finally {
3129
3200
  if (waiter) {
@@ -4885,7 +4956,18 @@ export class AcpAgent implements Agent {
4885
4956
  // Failure diagnostics are useful but advisory. Settle before any mapped
4886
4957
  // session update can await a backpressured client transport; otherwise an
4887
4958
  // already-decided failure can still lose to the inactivity watchdog.
4888
- this.#settlePrompt(id, record, activePrompt);
4959
+ // A terminal that races an in-flight cancel must not settle the public prompt before
4960
+ // the cancel request proves its outcome. Otherwise a follow-up prompt can be admitted
4961
+ // between the stale terminal settlement and the abort acknowledgement, leaving the
4962
+ // cancelled background request reported as `end_turn`.
4963
+ const cancelSettlementPending =
4964
+ activePrompt.cancelAttempt !== undefined &&
4965
+ !activePrompt.cancelAcknowledged &&
4966
+ record.statusRecovery !== activePrompt &&
4967
+ !this.#ownsUncertainAbort(record, activePrompt);
4968
+ if (!cancelSettlementPending) {
4969
+ this.#settlePrompt(id, record, activePrompt);
4970
+ }
4889
4971
  }
4890
4972
  if (!isTerminal && derivedCorrelation === undefined) return;
4891
4973
  if (!isTerminal && hasCorrelation(correlation)) {
@@ -5274,7 +5356,9 @@ export class AcpAgent implements Agent {
5274
5356
  const { outcome } = waiter.terminal;
5275
5357
  this.#rememberSettledPromptCorrelation(id, record, waiter.correlation);
5276
5358
  if (outcome.kind === "stopped") {
5277
- waiter.resolve({ stopReason: outcome.reason });
5359
+ // Once the SDK has acknowledged a client cancel, the cancellation owns the
5360
+ // turn even if a stale terminal frame races in with another stopped reason.
5361
+ waiter.resolve({ stopReason: waiter.cancelAcknowledged ? "cancelled" : outcome.reason });
5278
5362
  return;
5279
5363
  }
5280
5364
  // `phase` is the host's own claim, and a host that omits it leaves `terminalOutcome`
@@ -1,6 +1,7 @@
1
1
  import { Editor, type KeyId, matchesKey, parseKittySequence } from "@gajae-code/tui";
2
2
  import { BracketedPasteHandler } from "@gajae-code/tui/bracketed-paste";
3
3
  import { type AppKeybinding, KEYBINDINGS } from "../../config/keybindings";
4
+ import { macosOptionChordForComposedText } from "../utils/macos-option-forwarding";
4
5
 
5
6
  type ConfigurableEditorAction = Extract<
6
7
  AppKeybinding,
@@ -116,6 +117,12 @@ export class CustomEditor extends Editor {
116
117
  onQueue?: () => void;
117
118
  /** Called when Caps Lock is pressed. */
118
119
  onCapsLock?: () => void;
120
+ /**
121
+ * Called when typed text is what macOS composes for a bound Option chord
122
+ * (for example `œ` for a bound `alt+q`), meaning the terminal sent Option as
123
+ * text. The text is still inserted; the callback only explains the miss.
124
+ */
125
+ onUnforwardedOptionChord?: (key: KeyId, text: string) => void;
119
126
  /** Called when PageUp/PageDown should scroll the transcript viewport instead of prompt history. */
120
127
  onViewportPageScroll?: (direction: -1 | 1) => void;
121
128
  /** Called before regular composer input should return the transcript viewport to the live bottom. */
@@ -473,6 +480,13 @@ export class CustomEditor extends Editor {
473
480
  }
474
481
  }
475
482
 
483
+ if (this.onUnforwardedOptionChord) {
484
+ const optionKey = macosOptionChordForComposedText(data);
485
+ if (optionKey && (this.hasActionKey(optionKey) || this.#customKeyHandlers.has(optionKey))) {
486
+ this.onUnforwardedOptionChord(optionKey, data);
487
+ }
488
+ }
489
+
476
490
  // Pass to parent for normal handling.
477
491
  super.handleInput(data);
478
492
  }
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import { Container, matchesKey, type SelectItem, SelectList, Text } from "@gajae-code/tui";
22
22
  import { sanitizeText } from "@gajae-code/utils";
23
+ import { resolveGlobalUserSkillLinkTrust } from "../../../config/skill-settings-defaults";
23
24
  import { type CustomizationInventory, loadCustomizationInventory } from "../../../customization/inventory";
24
25
  import {
25
26
  removeHookFile,
@@ -47,6 +48,7 @@ import { ImportWizard } from "./import-wizard";
47
48
  /** Minimal settings slice the dashboard reads and writes. */
48
49
  export interface CustomizationSettingsSlice {
49
50
  get(key: string): unknown;
51
+ getGlobal?(key: string): unknown;
50
52
  set?(key: string, value: unknown): void;
51
53
  }
52
54
 
@@ -150,6 +152,16 @@ export class CustomizationDashboard extends Container {
150
152
  cwd: this.#cwd,
151
153
  home: this.#homeDir,
152
154
  policy: this.#skillPolicy(),
155
+ allowExternalUserSkillSymlinks: resolveGlobalUserSkillLinkTrust({
156
+ trustUserSkills:
157
+ typeof this.#settings?.getGlobal === "function"
158
+ ? (this.#settings.getGlobal("skills.trustUserSkills") as boolean | undefined)
159
+ : undefined,
160
+ enablePiUser:
161
+ typeof this.#settings?.getGlobal === "function"
162
+ ? (this.#settings.getGlobal("skills.enablePiUser") as boolean | undefined)
163
+ : undefined,
164
+ }),
153
165
  disabledExtensions: this.#getStringArray("disabledExtensions"),
154
166
  });
155
167
  }
@@ -59,7 +59,7 @@ export class ExtensionDashboard extends Container {
59
59
  async #init(): Promise<void> {
60
60
  const sm = this.settings ?? (await Settings.init());
61
61
  const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
62
- this.#state = await createInitialState(this.cwd, disabledIds);
62
+ this.#state = await createInitialState(this.cwd, disabledIds, sm);
63
63
 
64
64
  // Calculate max visible items based on terminal height
65
65
  // Reserve ~10 lines for header, tabs, help text, borders
@@ -193,7 +193,7 @@ export class ExtensionDashboard extends Container {
193
193
 
194
194
  const sm = this.settings ?? Settings.instance;
195
195
  const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
196
- const nextState = await refreshState(this.#state, this.cwd, disabledIds);
196
+ const nextState = await refreshState(this.#state, this.cwd, disabledIds, sm);
197
197
  if (refreshToken !== this.#refreshToken) return;
198
198
  this.#state = nextState;
199
199
 
@@ -14,6 +14,7 @@ import type { Skill } from "../../../capability/skill";
14
14
  import type { SlashCommand } from "../../../capability/slash-command";
15
15
  import type { CustomTool } from "../../../capability/tool";
16
16
  import type { SourceMeta } from "../../../capability/types";
17
+ import type { Settings } from "../../../config/settings";
17
18
  import {
18
19
  disableProvider,
19
20
  enableProvider,
@@ -43,7 +44,11 @@ export interface ExtensionSettingsManager {
43
44
  /**
44
45
  * Load all extensions from all capabilities.
45
46
  */
46
- export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): Promise<Extension[]> {
47
+ export async function loadAllExtensions(
48
+ cwd?: string,
49
+ disabledIds?: string[],
50
+ settings?: Settings,
51
+ ): Promise<Extension[]> {
47
52
  const extensions: Extension[] = [];
48
53
  const disabledExtensions = new Set<string>(disabledIds ?? []);
49
54
 
@@ -97,7 +102,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P
97
102
  }
98
103
  }
99
104
 
100
- const loadOpts = cwd ? { cwd, includeDisabled: true } : { includeDisabled: true };
105
+ const loadOpts = cwd ? { cwd, includeDisabled: true, settings } : { includeDisabled: true, settings };
101
106
 
102
107
  // Load skills
103
108
  try {
@@ -553,8 +558,12 @@ export function applyDisabledExtensionsToState(state: DashboardState, disabledId
553
558
  /**
554
559
  * Create initial dashboard state.
555
560
  */
556
- export async function createInitialState(cwd?: string, disabledIds?: string[]): Promise<DashboardState> {
557
- const extensions = await loadAllExtensions(cwd, disabledIds);
561
+ export async function createInitialState(
562
+ cwd?: string,
563
+ disabledIds?: string[],
564
+ settings?: Settings,
565
+ ): Promise<DashboardState> {
566
+ const extensions = await loadAllExtensions(cwd, disabledIds, settings);
558
567
  const tabs = buildProviderTabs(extensions);
559
568
  const tabFiltered = extensions; // "all" tab by default
560
569
  const searchFiltered = tabFiltered;
@@ -592,8 +601,9 @@ export async function refreshState(
592
601
  state: DashboardState,
593
602
  cwd?: string,
594
603
  disabledIds?: string[],
604
+ settings?: Settings,
595
605
  ): Promise<DashboardState> {
596
- const extensions = await loadAllExtensions(cwd, disabledIds);
606
+ const extensions = await loadAllExtensions(cwd, disabledIds, settings);
597
607
  const tabs = buildProviderTabs(extensions);
598
608
 
599
609
  // Get current provider from tabs
@@ -146,7 +146,7 @@ export class HookEditorComponent extends Container {
146
146
 
147
147
  const currentText = this.#editor.getExpandedText();
148
148
  try {
149
- this.#tui.stop();
149
+ this.#tui.suspend();
150
150
  const result = await openInEditor(editorCmd, currentText);
151
151
  if (result !== null) {
152
152
  this.#editor.setText(result);
@@ -734,7 +734,7 @@ export class HookSelectorComponent extends Container {
734
734
 
735
735
  const currentText = editor.getExpandedText();
736
736
  try {
737
- this.#tui.stop();
737
+ this.#tui.suspend();
738
738
  const result = await openInEditor(editorCmd, currentText);
739
739
  if (result !== null) {
740
740
  editor.setText(result);
@@ -1369,10 +1369,7 @@ export class ModelSelectorComponent extends Container {
1369
1369
  }
1370
1370
 
1371
1371
  #getProfileAvailableModels(): Model[] {
1372
- const getAvailableForProfileActivation = this.#modelRegistry.getAvailableForProfileActivation;
1373
- return typeof getAvailableForProfileActivation === "function"
1374
- ? getAvailableForProfileActivation.call(this.#modelRegistry)
1375
- : this.#modelRegistry.getAvailable();
1372
+ return this.#modelRegistry.getAvailable();
1376
1373
  }
1377
1374
 
1378
1375
  #createProfileResolutionRegistry(availableModels: readonly Model[]) {
@@ -30,7 +30,7 @@ const PROVIDER_ONBOARDING_OPTIONS: ProviderOnboardingOption[] = [
30
30
  },
31
31
  {
32
32
  label: "Import existing credentials",
33
- description: "Detect and import Claude Code / Codex CLI logins already on this machine.",
33
+ description: "Detect and import Claude Code / Codex CLI / Kiro CLI logins already on this machine.",
34
34
  action: "import-credentials",
35
35
  },
36
36
  ];
@@ -109,6 +109,10 @@ const modelSegment: StatusLineSegment = {
109
109
  const modelName = shortenModelId(state.model?.id ?? state.model?.name);
110
110
 
111
111
  let content = withIcon(theme.icon.model, modelName);
112
+ const unavailableProfile = ctx.session.getUnavailableModelProfile?.();
113
+ if (unavailableProfile) {
114
+ content = `${theme.fg("warning", `profile unavailable: ${sanitizeStatusText(unavailableProfile)}`)} ${content}`;
115
+ }
112
116
 
113
117
  if (ctx.session.isFastModeActive() && theme.icon.fast) {
114
118
  content += ` ${theme.icon.fast}`;
@@ -60,6 +60,8 @@ export const __eventControllerPerfCounters = {
60
60
  const COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS = 10_000;
61
61
  const TEXTLESS_LENGTH_WARNING =
62
62
  'Response ended with stopReason "length" before producing visible text or a tool call. Check the model\'s output and context token limits (maxTokens in models.yml) or lower the reasoning level (/reasoning), then retry.';
63
+ const TEXTLESS_INCOMPLETE_WARNING =
64
+ "Response ended before producing visible text or a tool call. See the provider diagnostic for the reason.";
63
65
 
64
66
  function isTextlessLengthStop(message: AssistantMessage | undefined): boolean {
65
67
  return (
@@ -1090,7 +1092,7 @@ export class EventController {
1090
1092
  event.stopReason !== "maintenance" &&
1091
1093
  isTextlessLengthStop(lastAssistant)
1092
1094
  ) {
1093
- this.ctx.showWarning(TEXTLESS_LENGTH_WARNING);
1095
+ this.ctx.showWarning(lastAssistant?.errorMessage ? TEXTLESS_INCOMPLETE_WARNING : TEXTLESS_LENGTH_WARNING);
1094
1096
  }
1095
1097
  this.sendCompletionNotification();
1096
1098
  this.ctx.promptSuggestion?.onAgentEnd();
@@ -1372,15 +1374,20 @@ export class EventController {
1372
1374
 
1373
1375
  const sessionName = this.ctx.sessionManager.getSessionName();
1374
1376
  const textlessLengthStop = isTextlessLengthStop(last);
1377
+ const incompleteTitle = last?.errorMessage ? "Response incomplete" : "Output limit reached";
1375
1378
  const title = textlessLengthStop
1376
1379
  ? sessionName
1377
- ? `${sessionName}: Output limit reached`
1378
- : "Output limit reached"
1380
+ ? `${sessionName}: ${incompleteTitle}`
1381
+ : incompleteTitle
1379
1382
  : sessionName
1380
1383
  ? `${sessionName}: Complete`
1381
1384
  : "Complete";
1382
1385
  const summary = summaryFromMessage(last, 1000);
1383
- const body = textlessLengthStop ? TEXTLESS_LENGTH_WARNING : (summary ?? "Complete");
1386
+ const body = textlessLengthStop
1387
+ ? last?.errorMessage
1388
+ ? TEXTLESS_INCOMPLETE_WARNING
1389
+ : TEXTLESS_LENGTH_WARNING
1390
+ : (summary ?? "Complete");
1384
1391
  const sessionManager = this.ctx.sessionManager as { getCwd?: () => string; getSessionId?: () => string };
1385
1392
  const payload: CompletionNotifyPayload = {
1386
1393
  type: "agent-turn-complete",