@gajae-code/coding-agent 0.10.1 → 0.11.0

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 (474) hide show
  1. package/CHANGELOG.md +70 -2
  2. package/dist/types/append-only-mode.d.ts +1 -1
  3. package/dist/types/cli/args.d.ts +4 -5
  4. package/dist/types/cli/daemon-cli.d.ts +12 -1
  5. package/dist/types/cli/notify-cli.d.ts +21 -1
  6. package/dist/types/cli.d.ts +3 -0
  7. package/dist/types/commands/daemon.d.ts +39 -0
  8. package/dist/types/commands/launch.d.ts +3 -0
  9. package/dist/types/commands/notify.d.ts +28 -1
  10. package/dist/types/commands/sdk.d.ts +33 -0
  11. package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
  12. package/dist/types/config/model-profile-activation.d.ts +35 -15
  13. package/dist/types/config/model-profiles.d.ts +11 -5
  14. package/dist/types/config/model-registry.d.ts +3 -3
  15. package/dist/types/config/model-resolver.d.ts +39 -6
  16. package/dist/types/config/model-selector-value.d.ts +17 -0
  17. package/dist/types/config/models-config-schema.d.ts +10 -6
  18. package/dist/types/config/settings-schema.d.ts +133 -6
  19. package/dist/types/config/settings.d.ts +63 -35
  20. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
  21. package/dist/types/coordinator-mcp/server.d.ts +6 -49
  22. package/dist/types/daemon/builtin.d.ts +6 -5
  23. package/dist/types/daemon/control-types.d.ts +1 -1
  24. package/dist/types/discovery/mcp-json.d.ts +12 -1
  25. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  26. package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
  27. package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
  28. package/dist/types/extensibility/extensions/loader.d.ts +10 -1
  29. package/dist/types/extensibility/extensions/runner.d.ts +3 -3
  30. package/dist/types/extensibility/extensions/types.d.ts +141 -16
  31. package/dist/types/extensibility/hooks/types.d.ts +1 -1
  32. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
  33. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  34. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
  35. package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
  36. package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
  37. package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
  38. package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
  39. package/dist/types/harness-control-plane/operate.d.ts +5 -5
  40. package/dist/types/harness-control-plane/owner.d.ts +18 -4
  41. package/dist/types/harness-control-plane/receipts.d.ts +0 -1
  42. package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
  43. package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
  44. package/dist/types/harness-control-plane/types.d.ts +3 -4
  45. package/dist/types/lsp/index.d.ts +1 -1
  46. package/dist/types/main.d.ts +32 -38
  47. package/dist/types/modes/acp/acp-agent.d.ts +224 -53
  48. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
  49. package/dist/types/modes/acp/acp-mode.d.ts +6 -15
  50. package/dist/types/modes/acp/permission-mode.d.ts +4 -0
  51. package/dist/types/modes/acp/startup-options.d.ts +6 -0
  52. package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
  53. package/dist/types/modes/components/gajae-pet-widget.d.ts +1 -0
  54. package/dist/types/modes/components/model-selector.d.ts +1 -0
  55. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  56. package/dist/types/modes/components/pet-capability.d.ts +37 -0
  57. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  58. package/dist/types/modes/components/settings-selector.d.ts +15 -4
  59. package/dist/types/modes/components/welcome.d.ts +1 -1
  60. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  61. package/dist/types/modes/index.d.ts +0 -4
  62. package/dist/types/modes/interactive-mode.d.ts +5 -1
  63. package/dist/types/modes/runtime-init.d.ts +2 -2
  64. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  65. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  66. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  67. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  68. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  69. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
  70. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  71. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  72. package/dist/types/modes/types.d.ts +16 -1
  73. package/dist/types/runtime-mcp/client.d.ts +2 -0
  74. package/dist/types/runtime-mcp/config.d.ts +5 -1
  75. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  76. package/dist/types/runtime-mcp/types.d.ts +4 -0
  77. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  78. package/dist/types/sdk/acp/index.d.ts +2 -0
  79. package/dist/types/sdk/broker/broker.d.ts +53 -0
  80. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  81. package/dist/types/sdk/broker/ensure.d.ts +33 -0
  82. package/dist/types/sdk/broker/identity.d.ts +3 -0
  83. package/dist/types/sdk/broker/index.d.ts +8 -0
  84. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  85. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  86. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  87. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  88. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  89. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  90. package/dist/types/sdk/broker/transport.d.ts +9 -0
  91. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  92. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  93. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  94. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  95. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  96. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  97. package/dist/types/sdk/bus/config.d.ts +274 -0
  98. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  99. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  100. package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
  101. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  102. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  103. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  104. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  105. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  106. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  107. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  108. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  109. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  110. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  111. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  112. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  113. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  114. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  115. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  116. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  117. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  118. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  119. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  120. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
  121. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  122. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  123. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  124. package/dist/types/sdk/cli/index.d.ts +1 -0
  125. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  126. package/dist/types/sdk/client/client.d.ts +1 -0
  127. package/dist/types/sdk/client/discovery.d.ts +27 -0
  128. package/dist/types/sdk/client/index.d.ts +2 -0
  129. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  130. package/dist/types/sdk/host/control/index.d.ts +2 -0
  131. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  132. package/dist/types/sdk/host/events.d.ts +35 -0
  133. package/dist/types/sdk/host/host.d.ts +35 -0
  134. package/dist/types/sdk/host/index.d.ts +4 -0
  135. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  136. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  137. package/dist/types/sdk/host/query/index.d.ts +3 -0
  138. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  139. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  140. package/dist/types/sdk/host/types.d.ts +32 -0
  141. package/dist/types/sdk/index.d.ts +6 -0
  142. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  143. package/dist/types/sdk/mcp/index.d.ts +1 -0
  144. package/dist/types/sdk/mcp/server.d.ts +37 -0
  145. package/dist/types/sdk/models.d.ts +44 -0
  146. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  147. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  148. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  149. package/dist/types/sdk/startup-capability.d.ts +50 -0
  150. package/dist/types/session/agent-session.d.ts +114 -21
  151. package/dist/types/session/default-model-selection.d.ts +25 -0
  152. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  153. package/dist/types/session/session-manager.d.ts +58 -1
  154. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  155. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  156. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  157. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  158. package/dist/types/tools/browser/launch.d.ts +22 -0
  159. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  160. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  161. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  162. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  163. package/dist/types/tools/browser/registry.d.ts +19 -0
  164. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  165. package/dist/types/tools/index.d.ts +8 -8
  166. package/package.json +34 -13
  167. package/scripts/build-sdk-package-smoke.ts +94 -0
  168. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  169. package/scripts/compile-args.ts +4 -2
  170. package/scripts/g005-daemon-smoke.ts +4 -5
  171. package/scripts/g011-daemon-path-smoke.ts +5 -5
  172. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  173. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  174. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  175. package/scripts/manifest-schema.ts +124 -0
  176. package/scripts/run-test-manifest.ts +153 -0
  177. package/scripts/run-threat-model-check.ts +157 -0
  178. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  179. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  180. package/src/append-only-mode.ts +1 -1
  181. package/src/cli/args.ts +43 -12
  182. package/src/cli/auth-gateway-cli.ts +2 -2
  183. package/src/cli/config-cli.ts +2 -1
  184. package/src/cli/daemon-cli.ts +63 -14
  185. package/src/cli/list-models.ts +1 -1
  186. package/src/cli/notify-cli.ts +288 -286
  187. package/src/cli.ts +21 -2
  188. package/src/commands/daemon.ts +50 -6
  189. package/src/commands/harness.ts +93 -36
  190. package/src/commands/launch.ts +5 -2
  191. package/src/commands/mcp-serve.ts +19 -12
  192. package/src/commands/notify.ts +36 -3
  193. package/src/commands/sdk.ts +426 -0
  194. package/src/commit/agentic/agent.ts +1 -0
  195. package/src/config/atomic-yaml-patch.ts +423 -0
  196. package/src/config/model-profile-activation.ts +240 -89
  197. package/src/config/model-profiles.ts +37 -34
  198. package/src/config/model-registry.ts +51 -26
  199. package/src/config/model-resolver.ts +138 -51
  200. package/src/config/model-selector-value.ts +50 -0
  201. package/src/config/models-config-schema.ts +14 -21
  202. package/src/config/settings-schema.ts +128 -10
  203. package/src/config/settings.ts +1120 -158
  204. package/src/coordinator-mcp/model-preset.ts +74 -20
  205. package/src/coordinator-mcp/server.ts +1597 -3052
  206. package/src/daemon/builtin.ts +8 -5
  207. package/src/daemon/control-types.ts +1 -1
  208. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  209. package/src/discovery/mcp-json.ts +327 -20
  210. package/src/extensibility/custom-tools/types.ts +1 -1
  211. package/src/extensibility/extensions/compact-handler.ts +3 -3
  212. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  213. package/src/extensibility/extensions/loader.ts +69 -5
  214. package/src/extensibility/extensions/runner.ts +112 -4
  215. package/src/extensibility/extensions/types.ts +176 -17
  216. package/src/extensibility/hooks/types.ts +1 -1
  217. package/src/extensibility/slash-commands.ts +3 -3
  218. package/src/gjc-runtime/launch-tmux.ts +170 -86
  219. package/src/gjc-runtime/psmux-detect.ts +82 -4
  220. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  221. package/src/gjc-runtime/team-runtime.ts +54 -35
  222. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  223. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -2
  224. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  225. package/src/gjc-runtime/ultragoal-guard.ts +53 -6
  226. package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
  227. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  228. package/src/harness-control-plane/operate.ts +9 -9
  229. package/src/harness-control-plane/owner.ts +283 -39
  230. package/src/harness-control-plane/receipts.ts +0 -1
  231. package/src/harness-control-plane/sdk-transport.ts +395 -0
  232. package/src/harness-control-plane/session-transport.ts +94 -0
  233. package/src/harness-control-plane/types.ts +1 -1
  234. package/src/internal-urls/docs-index.generated.ts +36 -37
  235. package/src/lsp/client.ts +42 -6
  236. package/src/lsp/index.ts +2 -2
  237. package/src/main.ts +299 -204
  238. package/src/modes/DESIGN.md +258 -0
  239. package/src/modes/acp/acp-agent.ts +1228 -2612
  240. package/src/modes/acp/acp-client-bridge.ts +2 -20
  241. package/src/modes/acp/acp-event-mapper.ts +18 -2
  242. package/src/modes/acp/acp-mode.ts +8 -36
  243. package/src/modes/acp/permission-mode.ts +25 -0
  244. package/src/modes/acp/startup-options.ts +6 -0
  245. package/src/modes/components/agent-dashboard.ts +37 -17
  246. package/src/modes/components/custom-editor.ts +1 -0
  247. package/src/modes/components/gajae-pet-widget.ts +169 -20
  248. package/src/modes/components/model-selector.ts +93 -35
  249. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  250. package/src/modes/components/pet-capability.ts +115 -0
  251. package/src/modes/components/pet-selector.ts +4 -6
  252. package/src/modes/components/settings-selector.ts +86 -11
  253. package/src/modes/components/welcome.ts +4 -4
  254. package/src/modes/controllers/command-controller.ts +1 -2
  255. package/src/modes/controllers/event-controller.ts +11 -16
  256. package/src/modes/controllers/extension-ui-controller.ts +357 -12
  257. package/src/modes/controllers/input-controller.ts +43 -13
  258. package/src/modes/controllers/selector-controller.ts +671 -44
  259. package/src/modes/index.ts +0 -22
  260. package/src/modes/interactive-mode.ts +108 -24
  261. package/src/modes/runtime-init.ts +298 -6
  262. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  263. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  264. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  265. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  266. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  267. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
  268. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  269. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  270. package/src/modes/types.ts +16 -1
  271. package/src/runtime-mcp/client.ts +94 -19
  272. package/src/runtime-mcp/config.ts +27 -15
  273. package/src/runtime-mcp/manager.ts +343 -105
  274. package/src/runtime-mcp/oauth-flow.ts +42 -12
  275. package/src/runtime-mcp/transports/http.ts +49 -19
  276. package/src/runtime-mcp/transports/stdio.ts +47 -28
  277. package/src/runtime-mcp/types.ts +10 -0
  278. package/src/sdk/acp/adapter.ts +418 -0
  279. package/src/sdk/acp/index.ts +2 -0
  280. package/src/sdk/broker/broker.ts +642 -0
  281. package/src/sdk/broker/discovery.ts +94 -0
  282. package/src/sdk/broker/ensure.ts +249 -0
  283. package/src/sdk/broker/identity.ts +42 -0
  284. package/src/sdk/broker/index.ts +8 -0
  285. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  286. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  287. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  288. package/src/sdk/broker/lifecycle.ts +2157 -0
  289. package/src/sdk/broker/process-incarnation.ts +159 -0
  290. package/src/sdk/broker/runtime.ts +164 -0
  291. package/src/sdk/broker/session-index.ts +318 -0
  292. package/src/sdk/broker/state-version.ts +23 -0
  293. package/src/sdk/broker/transport.ts +125 -0
  294. package/src/sdk/bus/chat-command-policy.ts +107 -0
  295. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  296. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  297. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  298. package/src/sdk/bus/chat-effect-journal.ts +353 -0
  299. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  300. package/src/sdk/bus/config.ts +433 -0
  301. package/src/sdk/bus/conversation-store.ts +391 -0
  302. package/src/sdk/bus/discord-conversation.ts +85 -0
  303. package/src/sdk/bus/discord-daemon.ts +2275 -0
  304. package/src/sdk/bus/discord-live-provider.ts +521 -0
  305. package/src/sdk/bus/discord-provider.ts +56 -0
  306. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  307. package/src/sdk/bus/index.ts +3943 -0
  308. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  309. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  310. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  311. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  312. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  313. package/src/sdk/bus/natives-augment.d.ts +38 -0
  314. package/src/sdk/bus/notification-orchestration.ts +535 -0
  315. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  316. package/src/sdk/bus/session-control.ts +333 -0
  317. package/src/sdk/bus/slack-conversation.ts +74 -0
  318. package/src/sdk/bus/slack-daemon.ts +2144 -0
  319. package/src/sdk/bus/slack-live-provider.ts +380 -0
  320. package/src/sdk/bus/slack-provider.ts +76 -0
  321. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  322. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  323. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  324. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  325. package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
  326. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  327. package/src/sdk/bus/telegram-setup.ts +663 -0
  328. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  329. package/src/sdk/cli/index.ts +1 -0
  330. package/src/sdk/cli/session-cli.ts +275 -0
  331. package/src/sdk/client/client.ts +1 -0
  332. package/src/sdk/client/discovery.ts +108 -0
  333. package/src/sdk/client/index.ts +2 -0
  334. package/src/sdk/host/control/dispatch.ts +367 -0
  335. package/src/sdk/host/control/index.ts +10 -0
  336. package/src/sdk/host/control/operations.ts +63 -0
  337. package/src/sdk/host/events.ts +91 -0
  338. package/src/sdk/host/host.ts +296 -0
  339. package/src/sdk/host/index.ts +4 -0
  340. package/src/sdk/host/query/cursor.ts +189 -0
  341. package/src/sdk/host/query/handlers.ts +533 -0
  342. package/src/sdk/host/query/index.ts +3 -0
  343. package/src/sdk/host/query/revision-store.ts +1126 -0
  344. package/src/sdk/host/reverse-leases.ts +325 -0
  345. package/src/sdk/host/types.ts +29 -0
  346. package/src/sdk/index.ts +13 -0
  347. package/src/sdk/lifecycle-session.ts +39 -0
  348. package/src/sdk/mcp/index.ts +1 -0
  349. package/src/sdk/mcp/server.ts +332 -0
  350. package/src/sdk/models.ts +168 -0
  351. package/src/sdk/protocol/adapter-validation.ts +48 -0
  352. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  353. package/src/sdk/protocol/operation-registry.ts +312 -0
  354. package/src/{sdk.ts → sdk/session.ts} +339 -212
  355. package/src/sdk/startup-capability.ts +189 -0
  356. package/src/session/agent-session.ts +2470 -866
  357. package/src/session/default-model-selection.ts +93 -0
  358. package/src/session/fallback-chain-controller.ts +136 -0
  359. package/src/session/session-manager.ts +447 -13
  360. package/src/session/session-storage.ts +8 -1
  361. package/src/setup/credential-auto-import.ts +290 -35
  362. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  363. package/src/slash-commands/builtin-registry.ts +33 -30
  364. package/src/task/executor.ts +62 -13
  365. package/src/tools/ask-answer-registry.ts +1 -1
  366. package/src/tools/ask.ts +13 -8
  367. package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
  368. package/src/tools/browser/benchmark/detector-report.ts +157 -0
  369. package/src/tools/browser/benchmark/run-suite.ts +39 -0
  370. package/src/tools/browser/launch.ts +124 -19
  371. package/src/tools/browser/profile-discovery.ts +63 -0
  372. package/src/tools/browser/profile-posture.ts +59 -0
  373. package/src/tools/browser/profile-reuse.ts +102 -0
  374. package/src/tools/browser/profile-warmup.ts +109 -0
  375. package/src/tools/browser/registry.ts +87 -11
  376. package/src/tools/browser/tab-protocol.ts +1 -0
  377. package/src/tools/browser/tab-supervisor.ts +1 -0
  378. package/src/tools/browser/tab-worker.ts +1 -1
  379. package/src/tools/browser.ts +6 -0
  380. package/src/tools/index.ts +8 -8
  381. package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
  382. package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
  383. package/src/tools/telegram-send.ts +2 -2
  384. package/vendor/insane-search/MANIFEST.json +2 -1
  385. package/vendor/insane-search/engine/bias_check.py +4 -1
  386. package/vendor/insane-search/engine/phase0.py +26 -1
  387. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  388. package/dist/types/modes/bridge/auth.d.ts +0 -12
  389. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  390. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  391. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  392. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  393. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  394. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  395. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  396. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  397. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  398. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  399. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  400. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  401. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  402. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  403. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  404. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  405. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  406. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  407. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  408. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  409. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  410. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  411. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  412. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  413. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  414. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  415. package/dist/types/notifications/config.d.ts +0 -132
  416. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  417. package/src/modes/bridge/auth.ts +0 -41
  418. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  419. package/src/modes/bridge/bridge-mode.ts +0 -675
  420. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  421. package/src/modes/bridge/event-stream.ts +0 -70
  422. package/src/modes/rpc/host-tools.ts +0 -1
  423. package/src/modes/rpc/host-uris.ts +0 -1
  424. package/src/modes/rpc/rpc-client.ts +0 -1166
  425. package/src/modes/rpc/rpc-mode.ts +0 -1029
  426. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  427. package/src/modes/rpc/rpc-types.ts +0 -637
  428. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  429. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  430. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  431. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  432. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  433. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  434. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  435. package/src/modes/shared/agent-wire/responses.ts +0 -17
  436. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  437. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  438. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  439. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  440. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  441. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  442. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  443. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  444. package/src/notifications/config.ts +0 -219
  445. package/src/notifications/index.ts +0 -1835
  446. package/src/notifications/telegram-daemon-cli.ts +0 -243
  447. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  448. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  449. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  450. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  453. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  454. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  455. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  456. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  457. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  458. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  459. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  460. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  461. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  462. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  463. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  466. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  467. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  468. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  469. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  470. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  471. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  472. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  473. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  474. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -21,27 +21,9 @@ import type {
21
21
  ClientBridgePermissionToolCall,
22
22
  ClientBridgeTerminalHandle,
23
23
  } from "../../session/client-bridge";
24
+ import { resolveAcpPermissionMode } from "./permission-mode";
24
25
 
25
- type AcpPermissionMode = "auto" | "prompt" | "always-allow";
26
-
27
- const ACP_PERMISSION_MODE_ENV = "GJC_ACP_PERMISSION_MODE";
28
-
29
- function parseAcpPermissionMode(value: unknown): AcpPermissionMode {
30
- if (value === "auto" || value === "prompt" || value === "always-allow") return value;
31
- return "prompt";
32
- }
33
-
34
- /** Client metadata is authoritative; the process environment is only a fallback when that field is absent. */
35
- function resolveAcpPermissionMode(clientCapabilities: ClientCapabilities | undefined): AcpPermissionMode {
36
- const meta = clientCapabilities?._meta;
37
- if (typeof meta === "object" && meta !== null) {
38
- const gjc = (meta as { gjc?: unknown }).gjc;
39
- if (typeof gjc === "object" && gjc !== null && "permissionHandling" in gjc) {
40
- return parseAcpPermissionMode((gjc as { permissionHandling?: unknown }).permissionHandling);
41
- }
42
- }
43
- return parseAcpPermissionMode(process.env[ACP_PERMISSION_MODE_ENV]);
44
- }
26
+ export { type AcpPermissionMode, resolveAcpPermissionMode } from "./permission-mode";
45
27
 
46
28
  export function createAcpClientBridge(
47
29
  connection: AgentSideConnection,
@@ -232,6 +232,24 @@ export function mapAgentSessionEventToAcpSessionUpdates(
232
232
  }
233
233
  case "todo_auto_clear":
234
234
  return [toSessionNotification(sessionId, { sessionUpdate: "plan", entries: [] })];
235
+ case "model_fallback_switched":
236
+ return [
237
+ toSessionNotification(sessionId, {
238
+ sessionUpdate: "session_info_update",
239
+ _meta: {
240
+ gjcModelFallbackSwitched: true,
241
+ gjcModelFallbackEventId: event.eventId,
242
+ gjcModelFallbackFrom: event.from,
243
+ gjcModelFallbackTo: event.to,
244
+ gjcModelFallbackReason: event.reason,
245
+ gjcModelFallbackRole: event.role,
246
+ gjcModelFallbackScope: event.scope,
247
+ gjcModelFallbackActiveIndex: event.activeIndex,
248
+ gjcModelFallbackChainLength: event.chainLength,
249
+ gjcModelFallbackAttemptsUsed: event.attemptsUsed,
250
+ },
251
+ }),
252
+ ];
235
253
  // These event types are intentionally not represented as ACP session updates.
236
254
  case "agent_start":
237
255
  case "agent_end":
@@ -278,8 +296,6 @@ export function mapAgentSessionEventToAcpSessionUpdates(
278
296
  }
279
297
  case "auto_retry_start":
280
298
  case "auto_retry_end":
281
- case "retry_fallback_applied":
282
- case "retry_fallback_succeeded":
283
299
  case "ttsr_triggered":
284
300
  case "irc_message":
285
301
  case "subagent_steer_message":
@@ -1,50 +1,22 @@
1
1
  import * as stream from "node:stream";
2
2
  import { AgentSideConnection, ndJsonStream, type Stream } from "@agentclientprotocol/sdk";
3
- import type { AgentSession } from "../../session/agent-session";
4
3
  import { AcpAgent } from "./acp-agent";
4
+ import type { AcpStartupOptions } from "./startup-options";
5
5
 
6
- export type AcpSessionFactory = (cwd: string) => Promise<AgentSession>;
7
-
8
- export interface AcpConnectionHandle {
9
- connection: AgentSideConnection;
10
- agent: AcpAgent;
11
- }
12
-
13
- export function createAcpConnection(
14
- transport: Stream,
15
- createSession: AcpSessionFactory,
16
- initialSession?: AgentSession,
17
- ): AgentSideConnection {
18
- return createAcpConnectionWithAgent(transport, createSession, initialSession).connection;
6
+ export interface AcpModeOptions {
7
+ agentDir?: string;
8
+ startupOptions?: AcpStartupOptions;
19
9
  }
20
10
 
21
- /**
22
- * Create an ACP connection and return both the {@link AgentSideConnection} and the
23
- * underlying {@link AcpAgent}. Callers that need to await agent shutdown (terminal
24
- * delete/dispose work) after transport closure should use this instead of
25
- * {@link createAcpConnection}.
26
- */
27
- export function createAcpConnectionWithAgent(
28
- transport: Stream,
29
- createSession: AcpSessionFactory,
30
- initialSession?: AgentSession,
31
- ): AcpConnectionHandle {
32
- let agent: AcpAgent | undefined;
33
- const connection = new AgentSideConnection(conn => {
34
- agent = new AcpAgent(conn, createSession, initialSession);
35
- return agent;
36
- }, transport);
37
- return { connection, agent: agent! };
11
+ export function createAcpConnection(transport: Stream, options: AcpModeOptions = {}): AgentSideConnection {
12
+ return new AgentSideConnection(conn => new AcpAgent(conn, options), transport);
38
13
  }
39
14
 
40
- export async function runAcpMode(createSession: AcpSessionFactory, initialSession?: AgentSession): Promise<never> {
15
+ export async function runAcpMode(options: AcpModeOptions = {}): Promise<never> {
41
16
  const input = stream.Writable.toWeb(process.stdout);
42
17
  const output = stream.Readable.toWeb(process.stdin);
43
18
  const transport = ndJsonStream(input, output);
44
- const { connection, agent } = createAcpConnectionWithAgent(transport, createSession, initialSession);
19
+ const connection = createAcpConnection(transport, options);
45
20
  await connection.closed;
46
- // Await connection-local terminal work (active deletes, disposal) so
47
- // `process.exit` never kills in-flight deletion/cleanup.
48
- await agent.shutdownPromise;
49
21
  process.exit(0);
50
22
  }
@@ -0,0 +1,25 @@
1
+ import type { ClientCapabilities } from "@agentclientprotocol/sdk";
2
+
3
+ export type AcpPermissionMode = "auto" | "prompt" | "always-allow";
4
+
5
+ const ACP_PERMISSION_MODE_ENV = "GJC_ACP_PERMISSION_MODE";
6
+
7
+ function parseAcpPermissionMode(value: unknown): AcpPermissionMode {
8
+ if (value === "auto" || value === "prompt" || value === "always-allow") return value;
9
+ return "prompt";
10
+ }
11
+
12
+ /** Client metadata is authoritative; the process environment is only a fallback when that field is absent. */
13
+ export function resolveAcpPermissionMode(
14
+ clientCapabilities: ClientCapabilities | undefined,
15
+ env: NodeJS.ProcessEnv = process.env,
16
+ ): AcpPermissionMode {
17
+ const meta = clientCapabilities?._meta;
18
+ if (typeof meta === "object" && meta !== null) {
19
+ const gjc = (meta as { gjc?: unknown }).gjc;
20
+ if (typeof gjc === "object" && gjc !== null && "permissionHandling" in gjc) {
21
+ return parseAcpPermissionMode((gjc as { permissionHandling?: unknown }).permissionHandling);
22
+ }
23
+ }
24
+ return parseAcpPermissionMode(env[ACP_PERMISSION_MODE_ENV]);
25
+ }
@@ -0,0 +1,6 @@
1
+ /** Startup settings that ACP can apply through the canonical SDK control surface. */
2
+ export interface AcpStartupOptions {
3
+ modelId?: string;
4
+ modelPreset?: string;
5
+ thinkingLevel?: string;
6
+ }
@@ -40,6 +40,7 @@ import {
40
40
  resolveConfiguredModelPatterns,
41
41
  resolveModelOverride,
42
42
  } from "../../config/model-resolver";
43
+ import { type ModelSelectorValue, selectorHead } from "../../config/model-selector-value";
43
44
  import { Settings } from "../../config/settings";
44
45
  import agentCreationArchitectPrompt from "../../prompts/system/agent-creation-architect.md" with { type: "text" };
45
46
  import agentCreationUserPrompt from "../../prompts/system/agent-creation-user.md" with { type: "text" };
@@ -84,6 +85,35 @@ interface AgentDashboardModelContext {
84
85
  defaultModelPattern?: string;
85
86
  }
86
87
 
88
+ export function resolveAgentCreationModel(
89
+ modelPatterns: string[],
90
+ modelRegistry: ModelRegistry,
91
+ settings: Settings | undefined,
92
+ ) {
93
+ const { model } = resolveModelOverride(modelPatterns, modelRegistry, settings);
94
+ if (model) return model;
95
+ if (modelPatterns.length > 0) {
96
+ throw new Error(`Configured model selector(s) did not resolve: ${modelPatterns.join(", ")}.`);
97
+ }
98
+
99
+ const fallbackModel = modelRegistry.getAvailable()[0];
100
+ if (!fallbackModel) {
101
+ throw new Error("No available model to generate agent specification.");
102
+ }
103
+ return fallbackModel;
104
+ }
105
+
106
+ export function updateAgentModelOverride(
107
+ overrides: Record<string, ModelSelectorValue>,
108
+ agentName: string,
109
+ rawValue: string,
110
+ ): Record<string, ModelSelectorValue> {
111
+ const value = rawValue.trim();
112
+ if (value) return { ...overrides, [agentName]: value };
113
+ const { [agentName]: _, ...remainingOverrides } = overrides;
114
+ return remainingOverrides;
115
+ }
116
+
87
117
  const SOURCE_ORDER: Record<AgentSource, number> = {
88
118
  project: 0,
89
119
  user: 1,
@@ -370,7 +400,7 @@ export class AgentDashboard extends Container {
370
400
  .map(agent => ({
371
401
  ...agent,
372
402
  disabled: disabled.has(agent.name),
373
- overrideModel: overrides[agent.name]?.trim() || undefined,
403
+ overrideModel: selectorHead(overrides[agent.name])?.trim() || undefined,
374
404
  }));
375
405
 
376
406
  this.#tabs = this.#buildTabs(this.#allAgents);
@@ -465,16 +495,10 @@ export class AgentDashboard extends Container {
465
495
  this.#settingsManager.set("task.disabledAgents", disabled);
466
496
  }
467
497
 
468
- #persistModelOverrides(): void {
498
+ #persistModelOverride(agentName: string, rawValue: string): void {
469
499
  if (!this.#settingsManager) return;
470
- const overrides: Record<string, string> = {};
471
- for (const agent of this.#allAgents) {
472
- const value = agent.overrideModel?.trim();
473
- if (value) {
474
- overrides[agent.name] = value;
475
- }
476
- }
477
- this.#settingsManager.set("task.agentModelOverrides", overrides);
500
+ const overrides = this.#settingsManager.get("task.agentModelOverrides");
501
+ this.#settingsManager.set("task.agentModelOverrides", updateAgentModelOverride(overrides, agentName, rawValue));
478
502
  }
479
503
 
480
504
  #toggleSelectedAgent(): void {
@@ -506,7 +530,7 @@ export class AgentDashboard extends Container {
506
530
  if (!selected) return;
507
531
  const value = rawValue.trim();
508
532
  selected.overrideModel = value || undefined;
509
- this.#persistModelOverrides();
533
+ this.#persistModelOverride(selected.name, value);
510
534
  this.#editingAgentName = null;
511
535
  this.#editInput = null;
512
536
  this.#applyFilters();
@@ -587,12 +611,7 @@ export class AgentDashboard extends Container {
587
611
  settings?.getModelRole("default"),
588
612
  settings,
589
613
  );
590
- const { model } = resolveModelOverride(modelPatterns, modelRegistry, settings);
591
- const fallbackModel = modelRegistry.getAvailable()[0];
592
- const selectedModel = model ?? fallbackModel;
593
- if (!selectedModel) {
594
- throw new Error("No available model to generate agent specification.");
595
- }
614
+ const selectedModel = resolveAgentCreationModel(modelPatterns, modelRegistry, settings);
596
615
 
597
616
  const systemPrompt = prompt.render(agentCreationArchitectPrompt, { TASK_TOOL_NAME: "task" });
598
617
  const userPrompt = prompt.render(agentCreationUserPrompt, { request: description });
@@ -605,6 +624,7 @@ export class AgentDashboard extends Container {
605
624
  model: selectedModel,
606
625
  systemPrompt: [systemPrompt],
607
626
  hasUI: false,
627
+ notificationHostModeSupported: false,
608
628
  enableLsp: false,
609
629
  enableMCP: false,
610
630
  disableExtensionDiscovery: true,
@@ -248,6 +248,7 @@ export class CustomEditor extends Editor {
248
248
  const paste = this.#pasteHandler.process(data);
249
249
  if (paste.handled) {
250
250
  if (paste.pasteContent !== undefined) {
251
+ this.onViewportFollowLive?.();
251
252
  this.#handleBracketedPaste(paste.pasteContent, paste.remaining);
252
253
  }
253
254
  return;
@@ -6,7 +6,6 @@ import {
6
6
  type GajaePixelFrameName,
7
7
  type GajaePixelFrames,
8
8
  getCellDimensions,
9
- ImageProtocol,
10
9
  PARA_PARA_STEPS,
11
10
  PET_SKINS,
12
11
  type PetMode,
@@ -14,10 +13,10 @@ import {
14
13
  petBurstDurationMs,
15
14
  petBurstFrame,
16
15
  registerAnimationCallback,
17
- TERMINAL,
18
16
  type TUI,
19
17
  } from "@gajae-code/tui";
20
18
  import type { CustomEditor } from "./custom-editor";
19
+ import { getPetPixelProtocol } from "./pet-capability";
21
20
 
22
21
  /** Re-exported from the tui skin registry so widget-relative imports stay valid. */
23
22
  export type { PetMode, PetSkinId };
@@ -38,6 +37,35 @@ const KITTY_DROP_FRACTION = 0.45;
38
37
  const petKittyDropPx = (cellHeightPx: number): number =>
39
38
  Math.min(Math.max(0, cellHeightPx - 1), Math.floor(cellHeightPx * KITTY_DROP_FRACTION));
40
39
  const PET_RAISE_ROWS = 1;
40
+ const allocatedPetKittyImageIds = new Set<number>();
41
+
42
+ function allocatePetKittyImageId(): number {
43
+ let id = 0;
44
+ while (id === 0 || allocatedPetKittyImageIds.has(id)) {
45
+ id = crypto.getRandomValues(new Uint32Array(1))[0] ?? 0;
46
+ }
47
+ allocatedPetKittyImageIds.add(id);
48
+ return id;
49
+ }
50
+
51
+ interface SixelFootprint {
52
+ x: number;
53
+ y: number;
54
+ columns: number;
55
+ rows: number;
56
+ }
57
+
58
+ function sameFootprint(left: SixelFootprint, right: SixelFootprint): boolean {
59
+ return left.x === right.x && left.y === right.y && left.columns === right.columns && left.rows === right.rows;
60
+ }
61
+
62
+ /**
63
+ * Which widget currently owns each TUI's single shared post-render emitter
64
+ * slot. A stale or repeated dispose (or off-switch) of a predecessor widget
65
+ * must never clear a successor's overlay authority.
66
+ */
67
+ const petOverlayEmitterOwners = new WeakMap<TUI, GajaePetWidget>();
68
+
41
69
  /** Working animation: the shared para-para beats looped end to end. */
42
70
  const WORK_LOOP_TOTAL = PARA_PARA_STEPS.reduce((sum, [, ms]) => sum + ms, 0);
43
71
  /** Random gap between automatic claw flexes (fires while idle AND working). */
@@ -127,6 +155,20 @@ export class GajaePetWidget {
127
155
  /** Cell metrics the current frames were built for; a change triggers a rebuild. */
128
156
  #builtCellW = 0;
129
157
  #builtCellH = 0;
158
+ #kittyImageId: number | undefined;
159
+ /** True while a kitty placement may exist on screen; cleared only after the delete escape is delivered. */
160
+ #kittyCleanupPending = false;
161
+ /** Last emitted Sixel raster position; retained until an erase is actually delivered. */
162
+ #lastSixelFootprint: SixelFootprint | undefined;
163
+ /** Terminal state: a disposed widget never touches the TUI or shared slots again. */
164
+ #disposed = false;
165
+ /**
166
+ * True while the previous overlay frame carried the cleanup payload. The
167
+ * TUI writes the frame after the emitter returns, so delivery is
168
+ * acknowledged only on the next emitter pass — and only while the terminal
169
+ * stayed available, since a failed render write drops availability.
170
+ */
171
+ #frameCleanupAwaitingAck = false;
130
172
 
131
173
  constructor(options: {
132
174
  ui: TUI;
@@ -154,9 +196,7 @@ export class GajaePetWidget {
154
196
 
155
197
  /** Protocol available for the real-pixel pet, if any. */
156
198
  static pixelProtocol(): "sixel" | "kitty" | null {
157
- if (TERMINAL.imageProtocol === ImageProtocol.Kitty) return "kitty";
158
- if (TERMINAL.imageProtocol === ImageProtocol.Sixel) return "sixel";
159
- return null;
199
+ return getPetPixelProtocol();
160
200
  }
161
201
 
162
202
  get mode(): PetMode {
@@ -182,14 +222,19 @@ export class GajaePetWidget {
182
222
  }
183
223
  }
184
224
 
225
+ commitPreviewMode(mode: PetMode): void {
226
+ this.#applyMode(mode, false);
227
+ }
228
+
185
229
  #applyMode(mode: PetMode, mountComposer: boolean): void {
186
- if (mode === this.#mode) return;
230
+ if (this.#disposed || mode === this.#mode) return;
187
231
 
188
232
  if (mode === "off") {
233
+ this.#writeImageCleanup();
189
234
  this.#mode = "off";
190
235
  this.#animation?.unregister();
191
236
  this.#animation = undefined;
192
- this.#ui.setPostRenderEmitter(undefined);
237
+ this.#releaseOverlayEmitter();
193
238
  this.#floorContainer.clear();
194
239
  this.#pixel = undefined;
195
240
  this.#framedEditor.setReserve(0);
@@ -200,6 +245,7 @@ export class GajaePetWidget {
200
245
 
201
246
  const protocol = this.#forcedProtocol ?? GajaePetWidget.pixelProtocol();
202
247
  if (!protocol) return;
248
+ if (this.#mode !== "off") this.#writeImageCleanup();
203
249
  this.#mode = mode;
204
250
  this.#frame = "base";
205
251
  this.#flexUntil = 0;
@@ -210,6 +256,7 @@ export class GajaePetWidget {
210
256
  // the composer stays pinned to the terminal bottom.
211
257
  this.#floorContainer.clear();
212
258
  this.#ui.setPostRenderEmitter(() => this.#overlayPayload());
259
+ petOverlayEmitterOwners.set(this.#ui, this);
213
260
  this.#animation ??= registerAnimationCallback(now => this.#tick(now), 80);
214
261
  this.#ui.requestRender(true);
215
262
  }
@@ -220,6 +267,10 @@ export class GajaePetWidget {
220
267
  this.#builtCellW = cell.widthPx;
221
268
  this.#builtCellH = cell.heightPx;
222
269
  const skin: PetSkinId = this.#mode === "off" ? "red" : this.#mode;
270
+ if (protocol === "kitty") {
271
+ this.#kittyImageId ??= allocatePetKittyImageId();
272
+ this.#kittyCleanupPending = true;
273
+ }
223
274
  this.#pixel = buildGajaePixelFrames({
224
275
  protocol,
225
276
  skin,
@@ -228,17 +279,49 @@ export class GajaePetWidget {
228
279
  targetRows: 2,
229
280
  sixelTopPaddingPx: protocol === "sixel" ? PET_SIXEL_DROP_PX : 0,
230
281
  kittyCellYOffsetPx: protocol === "kitty" ? petKittyDropPx(cell.heightPx) : 0,
282
+ kittyImageId: protocol === "kitty" ? this.#kittyImageId : undefined,
231
283
  });
232
284
  this.#framedEditor.setReserve(this.#pixel.columns + PET_SIDE_MARGIN);
233
285
  }
234
286
 
235
287
  dispose(): void {
236
- this.#animation?.unregister();
237
- this.#animation = undefined;
238
- this.#ui.setPostRenderEmitter(undefined);
239
- this.#floorContainer.clear();
240
- this.#framedEditor.setReserve(0);
241
- this.#mountEditor(false);
288
+ if (this.#disposed) return;
289
+ this.#disposed = true;
290
+ const kittyImageId = this.#kittyImageId;
291
+ const cleanupPayload = this.#imageCleanupPayload();
292
+ try {
293
+ if (cleanupPayload) {
294
+ this.#ui.queueTerminalCleanup(
295
+ `\x1b[?2026h\x1b7${cleanupPayload}\x1b8\x1b[?2026l`,
296
+ kittyImageId === undefined ? undefined : () => allocatedPetKittyImageIds.delete(kittyImageId),
297
+ );
298
+ } else if (kittyImageId !== undefined) {
299
+ allocatedPetKittyImageIds.delete(kittyImageId);
300
+ }
301
+ this.#consumeCleanupAuthority();
302
+ this.#kittyImageId = undefined;
303
+ } finally {
304
+ this.#animation?.unregister();
305
+ this.#animation = undefined;
306
+ this.#releaseOverlayEmitter();
307
+ this.#mode = "off";
308
+ this.#pixel = undefined;
309
+ this.#floorContainer.clear();
310
+ this.#framedEditor.setReserve(0);
311
+ // Restore the plain composer only while our framed wrapper is still
312
+ // mounted; a successor widget may already own the editor container.
313
+ if (this.#editorContainer.children.includes(this.#framedEditor)) {
314
+ this.#mountEditor(false);
315
+ }
316
+ }
317
+ }
318
+
319
+ /** Clear the shared post-render slot only while this widget still owns it. */
320
+ #releaseOverlayEmitter(): void {
321
+ if (petOverlayEmitterOwners.get(this.#ui) === this) {
322
+ this.#ui.setPostRenderEmitter(undefined);
323
+ petOverlayEmitterOwners.delete(this.#ui);
324
+ }
242
325
  }
243
326
 
244
327
  #mountEditor(framed: boolean): void {
@@ -335,25 +418,91 @@ export class GajaePetWidget {
335
418
  return { x, y };
336
419
  }
337
420
 
338
- #clearPetPayload(x: number, y: number): string {
339
- const pixel = this.#pixel;
340
- if (pixel?.protocol !== "sixel") return "";
421
+ #clearSixelFootprint(footprint: SixelFootprint): string {
341
422
  let out = "\x1b[0m";
342
- for (let row = 0; row < pixel.rasterRows; row++) {
343
- out += `\x1b[${y + row + 1};${x + 1}H\x1b[${pixel.columns}X`;
423
+ for (let row = 0; row < footprint.rows; row++) {
424
+ out += `\x1b[${footprint.y + row + 1};${footprint.x + 1}H\x1b[${footprint.columns}X`;
425
+ }
426
+ return out;
427
+ }
428
+
429
+ /** Pending on-screen image cleanup. Pure: authority is consumed separately, on delivery. */
430
+ #imageCleanupPayload(): string {
431
+ let out = "";
432
+ if (this.#kittyCleanupPending && this.#kittyImageId !== undefined) {
433
+ out += `\x1b_Ga=d,d=I,i=${this.#kittyImageId},q=2\x1b\\`;
434
+ }
435
+ if (this.#lastSixelFootprint) {
436
+ out += this.#clearSixelFootprint(this.#lastSixelFootprint);
344
437
  }
345
438
  return out;
346
439
  }
347
440
 
441
+ #consumeCleanupAuthority(): void {
442
+ this.#kittyCleanupPending = false;
443
+ this.#lastSixelFootprint = undefined;
444
+ }
445
+
446
+ /**
447
+ * Best-effort direct erase of the on-screen pet image. Cleanup authority is
448
+ * consumed only after the write is actually delivered: an unavailable
449
+ * terminal or a throwing write keeps the erase pending so a later mode
450
+ * switch or dispose can retry it.
451
+ */
452
+ #writeImageCleanup(): void {
453
+ if (!this.#ui.terminalAvailable) return;
454
+ const payload = this.#imageCleanupPayload();
455
+ if (!payload) return;
456
+ try {
457
+ this.#ui.terminal.write(`\x1b[?2026h\x1b7${payload}\x1b8\x1b[?2026l`);
458
+ } catch {
459
+ // Keep the footprint/placement authority; the terminal write layer
460
+ // reports availability separately and callers retry on the next
461
+ // lifecycle transition.
462
+ return;
463
+ }
464
+ this.#consumeCleanupAuthority();
465
+ }
466
+
348
467
  /** Draw escape payload at the pet's absolute position. */
349
468
  #overlayPayload(clearPet = false): string | null {
350
469
  const pixel = this.#pixel;
351
470
  if (!pixel) return null;
352
471
  const pos = this.#petPosition();
353
- if (!pos) return null;
472
+ if (!pos) {
473
+ // Deferred delivery acknowledgement: the TUI writes the frame after
474
+ // this emitter returns, and that write can fail. Consume the cleanup
475
+ // authority only once a later pass observes the terminal survived
476
+ // the frame that carried the payload; otherwise retain it so a later
477
+ // lifecycle cleanup retries the erase/delete.
478
+ if (this.#frameCleanupAwaitingAck && this.#ui.terminalAvailable) {
479
+ this.#consumeCleanupAuthority();
480
+ }
481
+ this.#frameCleanupAwaitingAck = false;
482
+ if (!this.#ui.terminalAvailable) return null;
483
+ const cleanup = this.#imageCleanupPayload();
484
+ if (!cleanup) return null;
485
+ this.#frameCleanupAwaitingAck = true;
486
+ return cleanup;
487
+ }
488
+ // A full frame supersedes any cleanup-only frame still awaiting ack.
489
+ this.#frameCleanupAwaitingAck = false;
354
490
  const { x, y } = pos;
491
+ let out = "";
492
+
493
+ if (pixel.protocol === "sixel") {
494
+ const footprint = { x, y, columns: pixel.columns, rows: pixel.rasterRows };
495
+ if (this.#lastSixelFootprint && !sameFootprint(this.#lastSixelFootprint, footprint)) {
496
+ out += this.#clearSixelFootprint(this.#lastSixelFootprint);
497
+ }
498
+ if (clearPet) out += this.#clearSixelFootprint(footprint);
499
+ this.#lastSixelFootprint = footprint;
500
+ } else {
501
+ // A kitty frame emitted below (re)places the image, so cleanup is
502
+ // pending again even if a narrow-terminal pass consumed it earlier.
503
+ this.#kittyCleanupPending = true;
504
+ }
355
505
 
356
- let out = clearPet ? this.#clearPetPayload(x, y) : "";
357
506
  out += `\x1b[${y + 1};${x + 1}H${pixel.frames[this.#frame]}`;
358
507
  return out;
359
508
  }