@sayknow-cli/coding-agent 0.4.7 → 0.5.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 (428) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +20 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -1,4 +1,6 @@
1
+ import type { WindowsJobMemoryProbeResult } from "@sayknow-cli/natives";
1
2
  import type { Settings } from "../config/settings";
3
+ import type { MemoryGuardWorkerSample } from "../runtime/memory-guard-contract";
2
4
  import { type TabGcSnapshot } from "./browser/tab-supervisor";
3
5
  export interface BrowserGcPolicy {
4
6
  enabled: boolean;
@@ -16,7 +18,10 @@ export declare function resolveSweepIntervalMs(settings: Settings): number;
16
18
  /** Injectable seams so the controller is fully testable without real browsers/filesystem/RSS. */
17
19
  export interface ResourceGcDeps {
18
20
  now: () => number;
21
+ monotonicNow: () => number;
19
22
  rssBytes: () => number;
23
+ memorySnapshot: () => Promise<MemoryPressureSnapshot>;
24
+ runGc: () => void;
20
25
  logWarn: (msg: string, meta?: Record<string, unknown>) => void;
21
26
  listTabs: () => TabGcSnapshot[];
22
27
  releaseTab: (name: string, policy: {
@@ -31,10 +36,13 @@ export interface ResourceGcDeps {
31
36
  removed: number;
32
37
  }>;
33
38
  screenshotArmed: () => boolean;
39
+ listTeamWorkers?: (cwd: string, sessionId: string) => Promise<MemoryGuardWorkerSample[]>;
40
+ applyTeamWorkerGuard?: (cwd: string, sessionId: string, workerId: string, excessBytes: number, incidentId: string) => Promise<void>;
34
41
  }
35
42
  export interface ResourceGcRegistration {
36
43
  sessionId: string;
37
44
  settings: Settings;
45
+ cwd?: string | (() => string);
38
46
  }
39
47
  /**
40
48
  * Register a session with the resource GC. Starts the single shared timer on the first
@@ -43,6 +51,21 @@ export interface ResourceGcRegistration {
43
51
  */
44
52
  export declare function registerResourceGcSession(reg: ResourceGcRegistration): () => void;
45
53
  export declare function sweepOnce(d?: ResourceGcDeps): Promise<void>;
54
+ export interface MemoryPressureDomain {
55
+ hardCapBytes: number;
56
+ totalUsageBytes: number;
57
+ source: MemoryPressureSnapshot["source"];
58
+ }
59
+ export interface MemoryPressureSnapshot {
60
+ hardCapBytes: number;
61
+ totalUsageBytes: number;
62
+ parentBytes: number;
63
+ source: "host" | "linux_cgroup_v2" | "linux_cgroup_v1" | "windows_job" | "windows_process_job_limit";
64
+ domains?: MemoryPressureDomain[];
65
+ }
66
+ export declare function __sampleLinuxCgroupHierarchyForTest(mountInfo: string, membership: string, fsType: "cgroup" | "cgroup2", hostBytes: number, parentBytes: number): Promise<MemoryPressureSnapshot | null>;
67
+ export declare function __sampleWindowsJobMemoryForTest(hostBytes: number, parentBytes: number, probeResult?: WindowsJobMemoryProbeResult): MemoryPressureSnapshot | null;
68
+ export declare function __selectMemoryPressureDomainForTest(snapshot: MemoryPressureSnapshot, policyLimitBytes: number | null): MemoryPressureSnapshot;
46
69
  export declare function __setResourceGcDepsForTest(overrides: Partial<ResourceGcDeps>): void;
47
70
  export declare function __setResourceGcSchedulerNowForTest(now: () => number): void;
48
71
  export declare function __runResourceGcTickForTest(): Promise<void>;
@@ -15,6 +15,13 @@ export type SkillDiscoveryToolInput = z.infer<typeof skillDiscoverySchema>;
15
15
  export interface SkillDiscoveryToolDetails {
16
16
  candidates: RuntimeSkillDiscoveryCandidate[];
17
17
  count: number;
18
+ /**
19
+ * Present only when zero candidates were returned AND discovery config gates
20
+ * (`skills.enabled` / `skills.enablePiProject` / `skills.enablePiUser`)
21
+ * prevented some or all of the requested scope from being searched. Without
22
+ * this, a disabled config is indistinguishable from "no skills exist".
23
+ */
24
+ notice?: string;
18
25
  }
19
26
  export declare class SkillDiscoveryTool implements AgentTool<typeof skillDiscoverySchema, SkillDiscoveryToolDetails> {
20
27
  #private;
@@ -32,6 +32,13 @@ export type SqliteRowLookup = {
32
32
  } | {
33
33
  kind: "rowid";
34
34
  };
35
+ /**
36
+ * Keep raw reads deliberately narrower than SQLite's full grammar. Comments are
37
+ * rejected, and a statement terminator is accepted only as the final
38
+ * non-whitespace character. Quoted semicolons remain ordinary query data.
39
+ */
40
+ export declare function validateSqliteReadQuery(sql: string): string;
41
+ export declare function enforceSqliteQueryOnly(db: Database): void;
35
42
  export declare function parseSqlitePathCandidates(filePath: string): SqlitePathCandidate[];
36
43
  export declare function isSqliteFile(absolutePath: string): Promise<boolean>;
37
44
  export declare function parseSqliteSelector(subPath: string, queryString: string): SqliteSelector;
@@ -1,6 +1,6 @@
1
1
  import type { AgentToolResult } from "@sayknow-cli/agent-core";
2
2
  import type { ImageContent, TextContent } from "@sayknow-cli/ai";
3
- import type { OutputSummary, TruncationResult } from "../session/streaming-output";
3
+ import type { OutputSummary, ReadWindow, TruncationResult } from "../session/streaming-output";
4
4
  import type { OutputMeta, TruncationOptions, TruncationSummaryOptions, TruncationTextOptions } from "./output-meta";
5
5
  type ToolContent = Array<TextContent | ImageContent>;
6
6
  type DetailsWithMeta = {
@@ -12,6 +12,12 @@ export declare class ToolResultBuilder<TDetails extends DetailsWithMeta> {
12
12
  text(text: string): this;
13
13
  content(content: ToolContent): this;
14
14
  truncation(result: TruncationResult, options: TruncationOptions): this;
15
+ truncationWindows(windows: ReadWindow, options?: {
16
+ artifactId?: string;
17
+ noticeOwner?: "body";
18
+ maxBytes?: number;
19
+ rangeBase?: "file" | "window";
20
+ }): this;
15
21
  truncationFromSummary(summary: OutputSummary, options: TruncationSummaryOptions): this;
16
22
  truncationFromText(text: string, options: TruncationTextOptions): this;
17
23
  limits(limits: {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Predict the user's likely next prompt after an agent turn using a smol,
3
+ * fast model. Native SKC port of the ghost-text prompt-suggestion behavior
4
+ * from Claude Code: the prediction renders as dim ghost text in the empty
5
+ * composer and Tab accepts it.
6
+ */
7
+ import type { AgentMessage } from "@sayknow-cli/agent-core";
8
+ import { type Api, type Model } from "@sayknow-cli/ai";
9
+ import type { ModelRegistry } from "../config/model-registry";
10
+ import type { Settings } from "../config/settings";
11
+ export declare const PROMPT_SUGGESTION_MAX_WORDS = 12;
12
+ export declare const PROMPT_SUGGESTION_MAX_CHARS = 100;
13
+ /**
14
+ * Strip wrapper tags and label prefixes a model sometimes adds despite being
15
+ * told to reply with only the suggestion.
16
+ */
17
+ export declare function sanitizePromptSuggestion(raw: string): string;
18
+ /**
19
+ * Heuristic gate rejecting model output that would make a bad ghost-text
20
+ * suggestion. Returns the rejection reason, or null when the text is usable.
21
+ */
22
+ export declare function suppressPromptSuggestionReason(text: string): string | null;
23
+ /**
24
+ * Build a compact recent-conversation transcript for the prediction request.
25
+ * Returns null when there is no user message to predict from.
26
+ */
27
+ export declare function buildPromptSuggestionContext(messages: AgentMessage[]): string | null;
28
+ /**
29
+ * Generate a predicted next user prompt from the recent conversation.
30
+ * Returns null when no usable suggestion is produced (silence is the
31
+ * expected steady state).
32
+ */
33
+ export declare function generatePromptSuggestion(messages: AgentMessage[], registry: ModelRegistry, settings: Settings, sessionId?: string, currentModel?: Model<Api>, metadataResolver?: (provider: string) => Record<string, unknown> | undefined): Promise<string | null>;
@@ -1,5 +1,17 @@
1
+ interface ShellSnapshotCacheOptions {
2
+ tempRoot: string;
3
+ home: string | (() => string);
4
+ platform: NodeJS.Platform;
5
+ }
6
+ declare function createShellSnapshotCache(options: ShellSnapshotCacheOptions): {
7
+ getOrCreateSnapshot(shell: string, env: Record<string, string | undefined>): Promise<string | null>;
8
+ cleanup(): Promise<void>;
9
+ };
10
+ /** @internal Test-only factory for isolated filesystem and platform assertions. */
11
+ export declare const createShellSnapshotCacheForTesting: typeof createShellSnapshotCache;
1
12
  /**
2
13
  * Create a shell snapshot, caching the result.
3
14
  * Returns the path to the snapshot file, or null if creation failed.
4
15
  */
5
16
  export declare function getOrCreateSnapshot(shell: string, env: Record<string, string | undefined>): Promise<string | null>;
17
+ export {};
@@ -16,6 +16,7 @@ import { spawn as nodeSpawn } from "node:child_process";
16
16
  export declare const INSANE_VENDOR_DIR: string;
17
17
  /** Stable note prefixes — tests assert on these without depending on full stderr. */
18
18
  export declare const INSANE_NOTES: {
19
+ readonly securityDisabled: "insane fallback disabled: remote renderer cannot preserve validated network routing";
19
20
  readonly guardBlocked: (reason: string) => string;
20
21
  readonly vendorMissing: "insane fallback unavailable: vendor engine missing at packages/coding-agent/vendor/insane-search";
21
22
  readonly noPython: "insane fallback unavailable: python3 not found; install python3 and curl_cffi, then retry with web.insaneFallback=true";
@@ -10,6 +10,18 @@ export interface PublicUrlRejected {
10
10
  export type PublicUrlResult = PublicUrlAccepted | PublicUrlRejected;
11
11
  /** Resolver seam so tests can inject DNS results without real lookups. */
12
12
  export type AddressResolver = (hostname: string) => Promise<string[]>;
13
+ type ProxyEnvironment = Record<string, string | undefined>;
14
+ export type GuardedPublicFetchResult = {
15
+ ok: true;
16
+ response: Response;
17
+ logicalUrl: URL;
18
+ wireUrl: URL;
19
+ } | {
20
+ ok: false;
21
+ reason: string;
22
+ logicalUrl: string;
23
+ };
24
+ export declare function hasConfiguredProxy(env: ProxyEnvironment): boolean;
13
25
  /** True for any address that is not a routable public unicast address. */
14
26
  export declare function isPrivateOrSpecialAddress(address: string): boolean;
15
27
  /**
@@ -19,7 +31,12 @@ export declare function isPrivateOrSpecialAddress(address: string): boolean;
19
31
  */
20
32
  export declare function validatePublicHttpUrl(rawUrl: string, options?: {
21
33
  resolver?: AddressResolver;
34
+ signal?: AbortSignal;
22
35
  }): Promise<PublicUrlResult>;
36
+ export declare function guardedPublicFetch(rawUrl: string, init?: BunFetchRequestInit, options?: {
37
+ resolver?: AddressResolver;
38
+ }): Promise<GuardedPublicFetchResult>;
23
39
  export declare function validatePublicHttpUrlForInsane(rawUrl: string, options?: {
24
40
  resolver?: AddressResolver;
25
41
  }): Promise<PublicUrlResult>;
42
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/coding-agent",
4
- "version": "0.4.7",
4
+ "version": "0.5.0",
5
5
  "description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://sayknow-cli.com",
7
7
  "author": "jaybeyond",
@@ -49,15 +49,15 @@
49
49
  "verify:insane-vendor": "bun scripts/verify-insane-vendor.ts"
50
50
  },
51
51
  "dependencies": {
52
- "@agentclientprotocol/sdk": "1.2.1",
52
+ "@agentclientprotocol/sdk": "1.3.0",
53
53
  "@babel/parser": "^7.29.3",
54
54
  "@mozilla/readability": "^0.6.0",
55
- "@sayknow-cli/stats": "0.4.7",
56
- "@sayknow-cli/agent-core": "0.4.7",
57
- "@sayknow-cli/ai": "0.4.7",
58
- "@sayknow-cli/natives": "0.4.7",
59
- "@sayknow-cli/tui": "0.4.7",
60
- "@sayknow-cli/utils": "0.4.7",
55
+ "@sayknow-cli/stats": "0.5.0",
56
+ "@sayknow-cli/agent-core": "0.5.0",
57
+ "@sayknow-cli/ai": "0.5.0",
58
+ "@sayknow-cli/natives": "0.5.0",
59
+ "@sayknow-cli/tui": "0.5.0",
60
+ "@sayknow-cli/utils": "0.5.0",
61
61
  "@puppeteer/browsers": "^2.13.0",
62
62
  "@types/turndown": "5.0.6",
63
63
  "@xterm/headless": "^6.0.0",
@@ -308,10 +308,6 @@
308
308
  "types": "./dist/types/extensibility/custom-commands/bundled/ci-green/index.d.ts",
309
309
  "import": "./src/extensibility/custom-commands/bundled/ci-green/index.ts"
310
310
  },
311
- "./extensibility/custom-commands/bundled/review": {
312
- "types": "./dist/types/extensibility/custom-commands/bundled/review/index.d.ts",
313
- "import": "./src/extensibility/custom-commands/bundled/review/index.ts"
314
- },
315
311
  "./extensibility/custom-tools": {
316
312
  "types": "./dist/types/extensibility/custom-tools/index.d.ts",
317
313
  "import": "./src/extensibility/custom-tools/index.ts"
@@ -0,0 +1,239 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { Container, renderMetrics, Text, TUI } from "@sayknow-cli/tui";
4
+ import { VirtualTerminal } from "../../tui/test/virtual-terminal";
5
+ import {
6
+ __ircSidebarPerfCounters,
7
+ type IrcSidebarTheme,
8
+ IrcSplitViewComponent,
9
+ } from "../src/modes/components/irc-sidebar";
10
+ import { __ircLedgerPerfCounters, IrcObservationLedger } from "../src/modes/irc-observation-ledger";
11
+
12
+ const SCHEMA_VERSION = 1;
13
+ const WIDTH = 80;
14
+ const HEIGHTS = [1, 3, 10] as const;
15
+ const TRANSCRIPT_ROWS = [10_000, 100_000] as const;
16
+ const STICKY_SUFFIX_WORKLOAD_NAMES = [
17
+ "sticky-suffix-10000-rows-height-1",
18
+ "sticky-suffix-10000-rows-height-3",
19
+ "sticky-suffix-10000-rows-height-10",
20
+ "sticky-suffix-100000-rows-height-1",
21
+ "sticky-suffix-100000-rows-height-3",
22
+ "sticky-suffix-100000-rows-height-10",
23
+ ] as const;
24
+
25
+ const stickySuffixMatrix = TRANSCRIPT_ROWS.flatMap(transcriptRows =>
26
+ HEIGHTS.map(height => ({
27
+ transcriptRows,
28
+ height,
29
+ name: `sticky-suffix-${transcriptRows}-rows-height-${height}`,
30
+ })),
31
+ );
32
+
33
+ function assertStickySuffixMatrix(): void {
34
+ const names = stickySuffixMatrix.map(workload => workload.name);
35
+ if (JSON.stringify(names) !== JSON.stringify(STICKY_SUFFIX_WORKLOAD_NAMES))
36
+ throw new Error("sticky suffix workload matrix changed");
37
+ }
38
+
39
+ const sidebarTheme = {
40
+ fg: (_color: "dim" | "accent", text: string) => text,
41
+ bold: (text: string) => text,
42
+ boxSharp: { vertical: "|" },
43
+ } satisfies IrcSidebarTheme;
44
+
45
+ type HardCounters = Readonly<Record<string, number | boolean>>;
46
+ type WorkloadResult = Readonly<{ name: string; hard: HardCounters; advisory: Record<string, number> }>;
47
+ export type StickyViewportPr1Benchmark = Readonly<{
48
+ schemaVersion: number;
49
+ workloads: readonly WorkloadResult[];
50
+ advisory: {
51
+ readonly timingAndMemoryOnly: true;
52
+ readonly wallMs: number;
53
+ readonly cpuUserMicros: number;
54
+ readonly cpuSystemMicros: number;
55
+ readonly rssBytes: number;
56
+ };
57
+ }>;
58
+
59
+ function rows(count: number, prefix: string): Text[] {
60
+ return Array.from({ length: count }, (_, index) => new Text(`${prefix}-${index}`, 0, 0));
61
+ }
62
+
63
+ async function settle(terminal: VirtualTerminal): Promise<void> {
64
+ await terminal.waitForRender();
65
+ await Promise.resolve();
66
+ }
67
+
68
+ async function stickySuffixWorkload(transcriptRows: number, height: number): Promise<WorkloadResult> {
69
+ renderMetrics.reset();
70
+ const terminal = new VirtualTerminal(WIDTH, height, { isProcessTerminal: true });
71
+ const tui = new TUI(terminal);
72
+ const transcript = new Container();
73
+ for (const row of rows(transcriptRows, "transcript")) transcript.addChild(row);
74
+ const status = new Text("status: pinned", 0, 0);
75
+ const suffix = Array.from({ length: height + 3 }, (_value, index) => new Text(`suffix-${index}`, 0, 0));
76
+ tui.addChild(transcript);
77
+ tui.addChild(status);
78
+ for (const row of suffix) tui.addChild(row);
79
+ tui.setBottomPinnedComponent(status);
80
+ const flatFrameRows = transcriptRows + suffix.length + 1;
81
+ const originalSlice = Array.prototype.slice;
82
+ let flatFrameSlices = 0;
83
+ let negativeControlSlices = 0;
84
+ const isLargeFlatFrame = (receiver: unknown[]): boolean =>
85
+ receiver.length === flatFrameRows &&
86
+ receiver[0] === "transcript-0" &&
87
+ receiver[transcriptRows - 1] === `transcript-${transcriptRows - 1}`;
88
+ try {
89
+ Array.prototype.slice = function <T>(this: T[], start?: number, end?: number): T[] {
90
+ if (isLargeFlatFrame(this)) flatFrameSlices++;
91
+ return originalSlice.call(this, start, end);
92
+ };
93
+ const negativeControl = Array.from({ length: flatFrameRows }, (_value, index) =>
94
+ index < transcriptRows ? `transcript-${index}` : "suffix",
95
+ );
96
+ negativeControl.slice(0, 1);
97
+ negativeControlSlices = flatFrameSlices;
98
+ flatFrameSlices = 0;
99
+ tui.start();
100
+ await settle(terminal);
101
+ const structural = renderMetrics.snapshot().structuralCounters;
102
+ const selected = structural.pinnedSuffixSelectedRows ?? 0;
103
+ const overflowFrames = structural.pinnedSuffixOverflowFrames ?? 0;
104
+ if (flatFrameSlices !== 0) throw new Error("pinned suffix sliced the large transcript frame");
105
+ if (negativeControlSlices !== 1) throw new Error("large-frame slice detector did not count its negative control");
106
+ if (selected > height) throw new Error("pinned suffix selected rows exceed terminal height");
107
+ if (overflowFrames !== 1 || selected === 0) throw new Error("pinned suffix overflow workload did not execute");
108
+ return {
109
+ name: `sticky-suffix-${transcriptRows}-rows-height-${height}`,
110
+ hard: {
111
+ largeFlatFrameSliceCalls: flatFrameSlices,
112
+ pinnedSuffixOverflowFrames: overflowFrames,
113
+ pinnedSuffixSelectedRows: selected,
114
+ },
115
+ advisory: { renderCount: renderMetrics.snapshot().renderCount },
116
+ };
117
+ } finally {
118
+ Array.prototype.slice = originalSlice;
119
+ tui.stop();
120
+ }
121
+ }
122
+
123
+ async function equalOutputSourceWorkload(): Promise<WorkloadResult> {
124
+ renderMetrics.reset();
125
+ const terminal = new VirtualTerminal(WIDTH, 10);
126
+ const tui = new TUI(terminal);
127
+ try {
128
+ tui.addChild(new Text("output", 0, 0));
129
+ tui.setViewportOutputSource({ identity: "pr1-equal", revision: 1n });
130
+ tui.start();
131
+ await settle(terminal);
132
+ const before = renderMetrics.snapshot().renderCount;
133
+ for (let index = 0; index < 1_000; index++) tui.setViewportOutputSource({ identity: "pr1-equal", revision: 1n });
134
+ await Promise.resolve();
135
+ const snapshot = renderMetrics.snapshot();
136
+ const equalNoops = snapshot.structuralCounters.viewportOutputSourceEqualNoops ?? 0;
137
+ if (snapshot.renderCount !== before || equalNoops !== 1_000)
138
+ throw new Error("equal output source requested a render");
139
+ return {
140
+ name: "equal-output-source-1000",
141
+ hard: { equalNoops, renderRequests: snapshot.renderCount - before },
142
+ advisory: {},
143
+ };
144
+ } finally {
145
+ tui.stop();
146
+ }
147
+ }
148
+
149
+ function addObservation(ledger: IrcObservationLedger, id: string): void {
150
+ ledger.observe(
151
+ { observationId: id, kind: "incoming", from: "peer", to: "you", text: `observation ${id}`, timestamp: 0 },
152
+ false,
153
+ );
154
+ }
155
+
156
+ function sidebarCacheWorkload(): WorkloadResult {
157
+ __ircSidebarPerfCounters.reset();
158
+ __ircSidebarPerfCounters.enable();
159
+ __ircLedgerPerfCounters.reset();
160
+ __ircLedgerPerfCounters.enable();
161
+ const ledger = new IrcObservationLedger();
162
+ for (let index = 0; index < 9_999; index++) addObservation(ledger, `history-${index}`);
163
+ const split = new IrcSplitViewComponent(new Text("left pane", 0, 0), ledger, sidebarTheme);
164
+ split.setVisible(true);
165
+ for (let index = 0; index < 100; index++) split.render(WIDTH);
166
+ const stable = __ircSidebarPerfCounters.snapshot();
167
+ addObservation(ledger, "mutation");
168
+ split.render(WIDTH);
169
+ const final = __ircSidebarPerfCounters.snapshot();
170
+ const ledgerCounters = __ircLedgerPerfCounters.snapshot();
171
+ if (stable.projectionMemoMisses !== 1 || stable.styledCacheMisses !== 1 || stable.wrapCalls === 0)
172
+ throw new Error("unchanged sidebar did not reuse caches");
173
+ if (stable.projectionMemoHits !== 99 || stable.styledCacheHits !== 99)
174
+ throw new Error("unchanged sidebar cache counts changed");
175
+ if (final.wrapCalls <= stable.wrapCalls || final.projectionMemoMisses !== 2 || final.styledCacheMisses !== 2)
176
+ throw new Error("sidebar mutation did not reproject once");
177
+ return {
178
+ name: "irc-sidebar-near-cap-cache",
179
+ hard: {
180
+ ledgerEpochAdvances: ledgerCounters.epochAdvances,
181
+ unchangedProjectionMisses: stable.projectionMemoMisses,
182
+ unchangedProjectionHits: stable.projectionMemoHits,
183
+ unchangedStyledMisses: stable.styledCacheMisses,
184
+ unchangedStyledHits: stable.styledCacheHits,
185
+ unchangedWrapCalls: stable.wrapCalls,
186
+ mutationProjectionMisses: final.projectionMemoMisses,
187
+ mutationStyledMisses: final.styledCacheMisses,
188
+ mutationWrapCalls: final.wrapCalls,
189
+ },
190
+ advisory: {},
191
+ };
192
+ }
193
+
194
+ export async function runStickyViewportPr1Benchmark(): Promise<StickyViewportPr1Benchmark> {
195
+ const cpu = process.cpuUsage();
196
+ const started = performance.now();
197
+ const wasEnabled = renderMetrics.enabled;
198
+ renderMetrics.enable();
199
+ try {
200
+ assertStickySuffixMatrix();
201
+ const workloads: WorkloadResult[] = [];
202
+ for (const workload of stickySuffixMatrix)
203
+ workloads.push(await stickySuffixWorkload(workload.transcriptRows, workload.height));
204
+ workloads.push(await equalOutputSourceWorkload(), sidebarCacheWorkload());
205
+ const usage = process.cpuUsage(cpu);
206
+ return {
207
+ schemaVersion: SCHEMA_VERSION,
208
+ workloads,
209
+ advisory: {
210
+ timingAndMemoryOnly: true,
211
+ wallMs: performance.now() - started,
212
+ cpuUserMicros: usage.user,
213
+ cpuSystemMicros: usage.system,
214
+ rssBytes: process.memoryUsage().rss,
215
+ },
216
+ };
217
+ } finally {
218
+ renderMetrics.reset();
219
+ __ircSidebarPerfCounters.disable();
220
+ __ircLedgerPerfCounters.disable();
221
+ if (!wasEnabled) renderMetrics.disable();
222
+ }
223
+ }
224
+
225
+ function outputPath(args: readonly string[]): string | undefined {
226
+ const index = args.indexOf("--out");
227
+ if (index < 0 || !args[index + 1]) return undefined;
228
+ return args[index + 1];
229
+ }
230
+
231
+ if (import.meta.main) {
232
+ const benchmark = await runStickyViewportPr1Benchmark();
233
+ const output = `${JSON.stringify(benchmark, null, 2)}\n`;
234
+ const destination = outputPath(process.argv.slice(2));
235
+ if (destination) {
236
+ await fs.mkdir(path.dirname(destination), { recursive: true });
237
+ await Bun.write(destination, output);
238
+ } else process.stdout.write(output);
239
+ }