@sayknow-cli/coding-agent 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/dist/types/append-only-mode.d.ts +31 -0
  2. package/dist/types/capability/rule.d.ts +8 -0
  3. package/dist/types/cli/completion-cli.d.ts +41 -0
  4. package/dist/types/cli/config-cli.d.ts +1 -0
  5. package/dist/types/cli/fixture-report.d.ts +20 -0
  6. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  7. package/dist/types/cli/nofile-limit.d.ts +11 -0
  8. package/dist/types/cli/notify-cli.d.ts +7 -0
  9. package/dist/types/cli/update-cli.d.ts +22 -0
  10. package/dist/types/cli.d.ts +124 -1
  11. package/dist/types/commands/completion.d.ts +29 -0
  12. package/dist/types/commands/config.d.ts +3 -0
  13. package/dist/types/commands/deep-interview.d.ts +3 -0
  14. package/dist/types/config/file-lock.d.ts +7 -6
  15. package/dist/types/config/keybindings.d.ts +2 -2
  16. package/dist/types/config/model-profile-activation.d.ts +6 -0
  17. package/dist/types/config/model-profiles.d.ts +1 -0
  18. package/dist/types/config/settings-schema.d.ts +119 -1
  19. package/dist/types/config/settings.d.ts +8 -0
  20. package/dist/types/discovery/index.d.ts +1 -0
  21. package/dist/types/export/html/template.generated.d.ts +1 -1
  22. package/dist/types/export/ttsr.d.ts +14 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  24. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  25. package/dist/types/goals/runtime.d.ts +1 -0
  26. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  27. package/dist/types/main.d.ts +4 -1
  28. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  29. package/dist/types/modes/components/model-selector.d.ts +4 -0
  30. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  31. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  32. package/dist/types/modes/components/status-line.d.ts +9 -0
  33. package/dist/types/modes/components/thinking-selector.d.ts +2 -2
  34. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  35. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  36. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  37. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  38. package/dist/types/modes/interactive-mode.d.ts +4 -0
  39. package/dist/types/modes/print-mode.d.ts +13 -1
  40. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  41. package/dist/types/modes/rpc/rpc-mode.d.ts +1 -3
  42. package/dist/types/modes/rpc/rpc-socket-security.d.ts +3 -0
  43. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  44. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  45. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  46. package/dist/types/modes/types.d.ts +3 -0
  47. package/dist/types/modes/utils/injected-user-submission.d.ts +52 -0
  48. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  49. package/dist/types/notifications/config-commands.d.ts +9 -0
  50. package/dist/types/notifications/config.d.ts +30 -1
  51. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  52. package/dist/types/notifications/index.d.ts +8 -3
  53. package/dist/types/notifications/lifecycle-commands.d.ts +12 -3
  54. package/dist/types/notifications/lifecycle-control-runtime.d.ts +2 -1
  55. package/dist/types/notifications/reply-sent-store.d.ts +53 -0
  56. package/dist/types/notifications/rich-draft.d.ts +68 -0
  57. package/dist/types/notifications/rich-render.d.ts +90 -0
  58. package/dist/types/notifications/telegram-daemon.d.ts +45 -3
  59. package/dist/types/notifications/telegram-reference.d.ts +7 -5
  60. package/dist/types/notifications/threaded-render.d.ts +14 -0
  61. package/dist/types/runtime-api-key.d.ts +7 -0
  62. package/dist/types/sdk.d.ts +3 -0
  63. package/dist/types/session/agent-session.d.ts +61 -6
  64. package/dist/types/session/agent-storage.d.ts +1 -0
  65. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  66. package/dist/types/session/messages.d.ts +4 -0
  67. package/dist/types/session/session-manager.d.ts +17 -1
  68. package/dist/types/skc-runtime/deep-interview-ambiguity.d.ts +39 -0
  69. package/dist/types/skc-runtime/deep-interview-recorder.d.ts +10 -1
  70. package/dist/types/skc-runtime/deep-interview-state.d.ts +17 -2
  71. package/dist/types/skc-runtime/session-state-sidecar.d.ts +7 -0
  72. package/dist/types/skc-runtime/state-writer.d.ts +3 -0
  73. package/dist/types/skc-runtime/tmux-common.d.ts +1 -0
  74. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  75. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +120 -13
  76. package/dist/types/skill-state/active-state.d.ts +18 -1
  77. package/dist/types/system-prompt.d.ts +18 -2
  78. package/dist/types/task/executor.d.ts +3 -0
  79. package/dist/types/task/fork-context-budget.d.ts +7 -0
  80. package/dist/types/task/token-log.d.ts +30 -0
  81. package/dist/types/task/token-log.test.d.ts +1 -0
  82. package/dist/types/tools/bisect.d.ts +81 -0
  83. package/dist/types/tools/computer.d.ts +2 -2
  84. package/dist/types/tools/index.d.ts +1 -0
  85. package/dist/types/tools/search.d.ts +2 -2
  86. package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
  87. package/dist/types/utils/file-mentions.d.ts +14 -0
  88. package/dist/types/utils/git.d.ts +32 -0
  89. package/dist/types/utils/pasted-image-path.d.ts +30 -0
  90. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  91. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  92. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  93. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  94. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  95. package/package.json +8 -8
  96. package/src/append-only-mode.ts +58 -0
  97. package/src/autoresearch/prompt.md +1 -1
  98. package/src/autoresearch/tools/log-experiment.ts +1 -1
  99. package/src/capability/rule.ts +9 -0
  100. package/src/cli/completion-cli.ts +234 -0
  101. package/src/cli/config-cli.ts +61 -9
  102. package/src/cli/fast-help.ts +1 -0
  103. package/src/cli/fixture-report.test.ts +230 -0
  104. package/src/cli/fixture-report.ts +171 -0
  105. package/src/cli/nofile-limit.ts +48 -0
  106. package/src/cli/notify-cli.ts +103 -17
  107. package/src/cli/telegram-cli.ts +1 -1
  108. package/src/cli/update-cli.ts +148 -33
  109. package/src/cli.ts +29 -1
  110. package/src/commands/completion.ts +84 -0
  111. package/src/commands/config.ts +2 -0
  112. package/src/commands/contribution-prep.ts +1 -1
  113. package/src/commands/deep-interview.ts +2 -1
  114. package/src/config/file-lock.ts +73 -34
  115. package/src/config/keybindings.ts +3 -3
  116. package/src/config/model-profile-activation.ts +65 -0
  117. package/src/config/model-profiles.ts +31 -2
  118. package/src/config/settings-schema.ts +76 -1
  119. package/src/config/settings.ts +22 -0
  120. package/src/defaults/skc/skills/team/SKILL.md +17 -14
  121. package/src/defaults/skc/skills/ultragoal/SKILL.md +59 -4
  122. package/src/defaults/skc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  123. package/src/defaults/skc-defaults.ts +9 -0
  124. package/src/discovery/helpers.ts +10 -0
  125. package/src/discovery/index.ts +1 -0
  126. package/src/export/html/template.generated.ts +1 -1
  127. package/src/export/html/template.js +60 -2
  128. package/src/export/ttsr.ts +96 -18
  129. package/src/extensibility/extensions/runner.ts +183 -219
  130. package/src/extensibility/extensions/types.ts +9 -0
  131. package/src/extensibility/skc-plugins/runtime-adapters.ts +156 -44
  132. package/src/goals/runtime.ts +10 -2
  133. package/src/hindsight/backend.ts +1 -3
  134. package/src/hooks/native-skill-hook.ts +124 -4
  135. package/src/internal-urls/docs-index.generated.ts +17 -8
  136. package/src/lsp/client.ts +1 -0
  137. package/src/main.ts +95 -43
  138. package/src/memories/index.ts +12 -1
  139. package/src/modes/components/assistant-message.ts +241 -82
  140. package/src/modes/components/custom-editor.ts +1 -0
  141. package/src/modes/components/footer.ts +7 -2
  142. package/src/modes/components/model-selector.ts +54 -11
  143. package/src/modes/components/queued-message-selector.ts +92 -0
  144. package/src/modes/components/settings-selector.ts +4 -0
  145. package/src/modes/components/status-line.ts +167 -54
  146. package/src/modes/components/thinking-selector.ts +10 -8
  147. package/src/modes/components/tool-execution.ts +44 -19
  148. package/src/modes/components/welcome.ts +181 -44
  149. package/src/modes/controllers/command-controller.ts +40 -4
  150. package/src/modes/controllers/event-controller.ts +77 -49
  151. package/src/modes/controllers/extension-ui-controller.ts +12 -11
  152. package/src/modes/controllers/input-controller.ts +192 -35
  153. package/src/modes/controllers/selector-controller.ts +168 -24
  154. package/src/modes/interactive-mode.ts +56 -16
  155. package/src/modes/print-mode.ts +43 -4
  156. package/src/modes/prompt-action-autocomplete.ts +21 -20
  157. package/src/modes/rpc/rpc-mode.ts +32 -12
  158. package/src/modes/rpc/rpc-socket-security.ts +13 -1
  159. package/src/modes/rpc/rpc-types.ts +9 -0
  160. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  161. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  162. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  163. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  164. package/src/modes/types.ts +3 -0
  165. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  166. package/src/modes/utils/injected-user-submission.ts +94 -0
  167. package/src/modes/utils/ui-helpers.ts +20 -7
  168. package/src/notifications/config-commands.ts +20 -0
  169. package/src/notifications/config.ts +52 -5
  170. package/src/notifications/daemon-paths.ts +2 -0
  171. package/src/notifications/index.ts +144 -18
  172. package/src/notifications/lifecycle-commands.ts +74 -23
  173. package/src/notifications/lifecycle-control-runtime.ts +8 -3
  174. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  175. package/src/notifications/reply-sent-store.ts +134 -0
  176. package/src/notifications/rich-draft.ts +107 -0
  177. package/src/notifications/rich-render.ts +142 -0
  178. package/src/notifications/telegram-daemon-control.ts +5 -3
  179. package/src/notifications/telegram-daemon.ts +606 -98
  180. package/src/notifications/telegram-reference.ts +17 -16
  181. package/src/notifications/threaded-render.ts +45 -2
  182. package/src/prompts/agents/architect.md +19 -10
  183. package/src/prompts/agents/critic.md +16 -18
  184. package/src/prompts/agents/executor.md +14 -11
  185. package/src/prompts/agents/planner.md +18 -13
  186. package/src/prompts/ci-green-request.md +3 -8
  187. package/src/prompts/goals/goal-continuation.md +0 -5
  188. package/src/prompts/goals/goal-mode-active.md +0 -5
  189. package/src/prompts/system/agent-creation-architect.md +2 -2
  190. package/src/prompts/system/plan-mode-active.md +1 -1
  191. package/src/prompts/system/plan-mode-subagent.md +3 -3
  192. package/src/prompts/system/project-prompt.md +0 -13
  193. package/src/prompts/system/subagent-system-prompt.md +3 -2
  194. package/src/prompts/system/system-prompt.md +29 -17
  195. package/src/prompts/system/volatile-project-context.md +10 -0
  196. package/src/prompts/system/web-search.md +4 -7
  197. package/src/prompts/tools/apply-patch.md +6 -0
  198. package/src/prompts/tools/ast-edit.md +1 -0
  199. package/src/prompts/tools/bash.md +1 -1
  200. package/src/prompts/tools/bisect.md +22 -0
  201. package/src/prompts/tools/browser.md +1 -1
  202. package/src/prompts/tools/computer.md +2 -2
  203. package/src/prompts/tools/cron.md +0 -2
  204. package/src/prompts/tools/eval.md +2 -2
  205. package/src/prompts/tools/find.md +1 -1
  206. package/src/prompts/tools/github.md +6 -6
  207. package/src/prompts/tools/goal.md +1 -3
  208. package/src/prompts/tools/image-gen.md +12 -5
  209. package/src/prompts/tools/irc.md +5 -4
  210. package/src/prompts/tools/job.md +2 -2
  211. package/src/prompts/tools/lsp.md +1 -1
  212. package/src/prompts/tools/monitor.md +2 -2
  213. package/src/prompts/tools/patch.md +2 -2
  214. package/src/prompts/tools/replace.md +2 -21
  215. package/src/prompts/tools/rewind.md +1 -1
  216. package/src/prompts/tools/search-tool-bm25.md +6 -11
  217. package/src/prompts/tools/search.md +1 -1
  218. package/src/prompts/tools/skill.md +3 -2
  219. package/src/prompts/tools/ssh.md +2 -1
  220. package/src/prompts/tools/subagent.md +2 -2
  221. package/src/prompts/tools/task.md +4 -5
  222. package/src/prompts/tools/todo-write.md +1 -1
  223. package/src/prompts/tools/vim.md +1 -0
  224. package/src/prompts/tools/web-search.md +2 -0
  225. package/src/prompts/tools/write.md +8 -2
  226. package/src/runtime-api-key.ts +16 -0
  227. package/src/sdk.ts +88 -36
  228. package/src/session/agent-session.ts +1013 -141
  229. package/src/session/agent-storage.ts +4 -0
  230. package/src/session/file-mention-pruning.ts +64 -0
  231. package/src/session/messages.ts +4 -0
  232. package/src/session/session-manager.ts +103 -4
  233. package/src/skc-runtime/deep-interview-ambiguity.ts +206 -0
  234. package/src/skc-runtime/deep-interview-recorder.ts +62 -8
  235. package/src/skc-runtime/deep-interview-state.ts +44 -2
  236. package/src/skc-runtime/session-state-sidecar.ts +263 -20
  237. package/src/skc-runtime/state-runtime.ts +12 -2
  238. package/src/skc-runtime/state-writer.ts +15 -0
  239. package/src/skc-runtime/tmux-common.ts +1 -0
  240. package/src/skc-runtime/ultragoal-guard.ts +55 -66
  241. package/src/skc-runtime/ultragoal-receipt-freshness.ts +315 -0
  242. package/src/skc-runtime/ultragoal-runtime.ts +1672 -159
  243. package/src/skc-runtime/workflow-manifest.generated.json +113 -2
  244. package/src/skc-runtime/workflow-manifest.ts +74 -1
  245. package/src/skill-state/active-state.ts +141 -5
  246. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  247. package/src/slash-commands/builtin-registry.ts +296 -72
  248. package/src/system-prompt.ts +57 -16
  249. package/src/task/executor.ts +132 -24
  250. package/src/task/fork-context-advisory.ts +2 -7
  251. package/src/task/fork-context-budget.ts +9 -0
  252. package/src/task/index.ts +13 -6
  253. package/src/task/output-manager.ts +20 -8
  254. package/src/task/receipt.ts +26 -7
  255. package/src/task/token-log.test.ts +188 -0
  256. package/src/task/token-log.ts +179 -0
  257. package/src/tools/bisect.ts +367 -0
  258. package/src/tools/computer.ts +246 -43
  259. package/src/tools/index.ts +3 -0
  260. package/src/tools/output-meta.ts +116 -4
  261. package/src/tools/search.ts +8 -2
  262. package/src/tools/skill.ts +15 -0
  263. package/src/tools/telegram-send.ts +6 -0
  264. package/src/tools/ultragoal-ask-guard.ts +39 -5
  265. package/src/utils/file-mentions.ts +39 -7
  266. package/src/utils/git.ts +73 -0
  267. package/src/utils/pasted-image-path.ts +170 -0
  268. package/src/web/scrapers/youtube.ts +24 -12
  269. package/src/workflow/workflow-intent-diff.ts +29 -6
  270. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  271. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  272. package/src/workflow/workflow-intent-report.ts +135 -0
@@ -118,12 +118,14 @@ import type { CompactionQueuedMessage, InteractiveModeContext, SubmittedUserInpu
118
118
  import { UiHelpers } from "./utils/ui-helpers";
119
119
 
120
120
  const INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS = 5_000;
121
- const DEFAULT_COMPOSER_PLACEHOLDER = "Type your message...";
121
+ const COMPOSER_NEWLINE_HINT = process.platform === "win32" ? "Alt+Enter/Ctrl+J" : "Shift+Enter/Ctrl+J";
122
+ export const DEFAULT_COMPOSER_PLACEHOLDER = `Type your message... ${COMPOSER_NEWLINE_HINT}: New line · Ctrl+C: Clear · Ctrl+R: Search history · Shift+Tab: Reasoning`;
123
+ const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
122
124
  const FRIENDLY_KEY_PARTS: Record<string, string> = {
123
125
  alt: "Alt",
124
- cmd: "Command",
125
- command: "Command",
126
- ctrl: "Control",
126
+ cmd: "Cmd",
127
+ command: "Cmd",
128
+ ctrl: "Ctrl",
127
129
  enter: "Enter",
128
130
  meta: process.platform === "darwin" ? "Command" : "Meta",
129
131
  option: "Option",
@@ -133,7 +135,7 @@ const FRIENDLY_KEY_PARTS: Record<string, string> = {
133
135
  function formatShortcutForPlaceholder(key: string): string {
134
136
  return key
135
137
  .split("+")
136
- .map(part => FRIENDLY_KEY_PARTS[part.toLowerCase()] ?? part)
138
+ .map(part => FRIENDLY_KEY_PARTS[part.toLowerCase()] ?? (part.length === 1 ? part.toUpperCase() : part))
137
139
  .join("+");
138
140
  }
139
141
 
@@ -162,6 +164,7 @@ function configureDefaultComposerChrome(editor: CustomEditor): void {
162
164
  editor.setInputPrefix(getDefaultInputPrefix());
163
165
  editor.setPlaceholder(DEFAULT_COMPOSER_PLACEHOLDER);
164
166
  editor.setPaddingX(1);
167
+ editor.setRightGutterWidth(1);
165
168
  editor.setTopBorder(undefined);
166
169
  }
167
170
 
@@ -328,6 +331,7 @@ export class InteractiveMode implements InteractiveModeContext {
328
331
  onInputCallback?: (input: SubmittedUserInput) => void;
329
332
  optimisticUserMessageSignature: string | undefined = undefined;
330
333
  locallySubmittedUserSignatures: Set<string> = new Set();
334
+ optimisticInjectedSignatures: Map<string, number> = new Map();
331
335
  #pendingSubmittedInput: SubmittedUserInput | undefined;
332
336
  #pendingSubmissionDispose: (() => void) | undefined;
333
337
  lastSigintTime = 0;
@@ -444,7 +448,6 @@ export class InteractiveMode implements InteractiveModeContext {
444
448
  logger.warn("History storage unavailable", { error: String(error) });
445
449
  }
446
450
  this.hookWidgetContainerAbove = new Container();
447
- this.hookWidgetContainerAbove.addChild(new Spacer(1));
448
451
  this.hookWidgetContainerBelow = new Container();
449
452
  this.editorContainer = new Container();
450
453
  this.editorContainer.addChild(this.editor);
@@ -532,13 +535,7 @@ export class InteractiveMode implements InteractiveModeContext {
532
535
  }
533
536
 
534
537
  if (!startupQuiet) {
535
- const getWelcomeReservedBottomRows = (width: number): number =>
536
- [
537
- this.statusLine,
538
- this.hookWidgetContainerAbove,
539
- this.editorContainer,
540
- this.hookWidgetContainerBelow,
541
- ].reduce((rows, component) => rows + component.render(width).length, 0);
538
+ const getWelcomeReservedBottomRows = (width: number): number => this.#getWelcomeReservedRows(width);
542
539
 
543
540
  // Add welcome header
544
541
  this.#welcomeComponent = new WelcomeComponent(
@@ -941,11 +938,37 @@ export class InteractiveMode implements InteractiveModeContext {
941
938
 
942
939
  #getComposerPlaceholder(): string {
943
940
  if (!this.#isPromptDeliveryBusy()) return DEFAULT_COMPOSER_PLACEHOLDER;
944
- const enterAction = this.settings.get("busyPromptMode") === "steer" ? "Steering" : "Message Queueing";
941
+ const enterAction = this.settings.get("busyPromptMode") === "steer" ? "Steer" : "Queue";
945
942
  const parts = [`Enter: ${enterAction}`];
946
943
  const queueKey = this.#getMessageQueueShortcut();
947
- if (queueKey) parts.push(`${formatShortcutForPlaceholder(queueKey)}: Message Queueing`);
948
- return `${DEFAULT_COMPOSER_PLACEHOLDER} ${parts.join(" · ")}`;
944
+ if (queueKey) parts.push(`${formatShortcutForPlaceholder(queueKey)}: Queue`);
945
+ return `${DEFAULT_COMPOSER_PLACEHOLDER} · ${parts.join(" · ")}`;
946
+ }
947
+
948
+ #getWelcomeReservedRows(width: number): number {
949
+ const transientRows = [
950
+ this.chatContainer,
951
+ this.pendingMessagesContainer,
952
+ this.statusContainer,
953
+ this.todoContainer,
954
+ this.btwContainer,
955
+ ].reduce((rows, container) => rows + this.#renderShortContainerRowsForWelcomeReservation(width, container), 0);
956
+
957
+ const pinnedRows = [
958
+ this.statusLine,
959
+ this.hookWidgetContainerAbove,
960
+ this.editorContainer,
961
+ this.hookWidgetContainerBelow,
962
+ ].reduce((rows, component) => rows + component.render(width).length, 0);
963
+
964
+ return transientRows + pinnedRows;
965
+ }
966
+
967
+ #renderShortContainerRowsForWelcomeReservation(width: number, container: Container): number {
968
+ if (container.children.length === 0 || container.children.length > WELCOME_RESERVED_CONTAINER_CHILD_LIMIT) {
969
+ return 0;
970
+ }
971
+ return container.render(width).length;
949
972
  }
950
973
 
951
974
  updateEditorChrome(): void {
@@ -1352,6 +1375,12 @@ export class InteractiveMode implements InteractiveModeContext {
1352
1375
  return;
1353
1376
  }
1354
1377
 
1378
+ // Leaving plan mode invalidates the plan-mode steering and standing
1379
+ // resolve handler that the in-flight turn may still be acting on. Abort
1380
+ // before clearing that state so the model cannot continue from stale
1381
+ // plan-mode context and retry `resolve` against a retired handler.
1382
+ await this.session.abort({ timeoutMs: INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS });
1383
+
1355
1384
  const previousTools = this.#planModePreviousTools;
1356
1385
  if (previousTools && previousTools.length > 0) {
1357
1386
  await this.session.setActiveToolsByName(previousTools);
@@ -1689,6 +1718,7 @@ export class InteractiveMode implements InteractiveModeContext {
1689
1718
  planContent,
1690
1719
  finalPlanFilePath: options.finalPlanFilePath,
1691
1720
  contextPreserved: options.preserveContext === true,
1721
+ tools: this.session.getActiveToolNames(),
1692
1722
  });
1693
1723
  await this.session.prompt(planModePrompt, { synthetic: true });
1694
1724
  }
@@ -2180,6 +2210,7 @@ export class InteractiveMode implements InteractiveModeContext {
2180
2210
  message => renderWorkingMessage(message, this.#getWorkingMessageAccent()),
2181
2211
  this.#defaultWorkingMessage,
2182
2212
  getSymbolTheme().spinnerFrames,
2213
+ { timeDependentColor: true },
2183
2214
  );
2184
2215
  this.statusContainer.addChild(this.loadingAnimation);
2185
2216
  }
@@ -2337,6 +2368,11 @@ export class InteractiveMode implements InteractiveModeContext {
2337
2368
  return this.#commandController.handleClearCommand();
2338
2369
  }
2339
2370
 
2371
+ handleContextClearCommand(): Promise<void> {
2372
+ this.#prepareSessionSwitch();
2373
+ return this.#commandController.handleContextClearCommand();
2374
+ }
2375
+
2340
2376
  handleDropCommand(): Promise<void> {
2341
2377
  this.#prepareSessionSwitch();
2342
2378
  return this.#commandController.handleDropCommand();
@@ -2526,6 +2562,10 @@ export class InteractiveMode implements InteractiveModeContext {
2526
2562
  this.#selectorController.showModelSelector(options);
2527
2563
  }
2528
2564
 
2565
+ showEffortSelector(): void {
2566
+ this.#selectorController.showEffortSelector();
2567
+ }
2568
+
2529
2569
  showProviderOnboarding(): void {
2530
2570
  this.#selectorController.showProviderOnboarding();
2531
2571
  }
@@ -5,7 +5,7 @@
5
5
  * - `skc -p "prompt"` - text output
6
6
  * - `skc --mode json "prompt"` - JSON event stream
7
7
  */
8
- import type { AssistantMessage, ImageContent } from "@sayknow-cli/ai";
8
+ import { type AssistantMessage, type ImageContent, isContextOverflow } from "@sayknow-cli/ai";
9
9
  import { logger, sanitizeText } from "@sayknow-cli/utils";
10
10
  import type { AgentSession } from "../session/agent-session";
11
11
  import { isSilentAbort } from "../session/messages";
@@ -31,6 +31,33 @@ export interface PrintModeOptions {
31
31
  suppressProcessExit?: boolean;
32
32
  }
33
33
 
34
+ /**
35
+ * Exit code used when a non-interactive **text-mode** run (`skc -p`) terminates
36
+ * because the model context window is exhausted and automatic compaction could
37
+ * not bring the request under the limit. Distinct from the generic failure code
38
+ * (1) so text-mode callers can detect context exhaustion specifically instead of
39
+ * parsing the raw provider error string.
40
+ *
41
+ * Scope: text-mode final-response path only. JSON mode (`--mode json`) streams
42
+ * events from the subscription and does not run this terminal-error branch, so
43
+ * it is intentionally NOT covered by this exit code.
44
+ */
45
+ export const CONTEXT_OVERFLOW_EXIT_CODE = 2;
46
+
47
+ /**
48
+ * Build an actionable stderr diagnostic for a terminal context-overflow error in
49
+ * text mode. The raw provider message is preserved (appended) for debugging, but
50
+ * the leading guidance explains what happened and what the operator can do —
51
+ * tailored to whether auto-compaction was even enabled.
52
+ */
53
+ function formatContextOverflowError(message: AssistantMessage, autoCompactionEnabled: boolean): string {
54
+ const providerDetail = message.errorMessage ? ` (provider error: ${sanitizeText(message.errorMessage)})` : "";
55
+ const guidance = autoCompactionEnabled
56
+ ? "Context window exhausted: automatic compaction ran but could not reduce the request below the model's context limit. Reduce the input size (smaller file reads / tool output), raise the compaction threshold, or switch to a larger-context model."
57
+ : "Context window exhausted and automatic compaction is disabled. Enable it (compaction.enabled=true with a non-off compaction.strategy) so SKC can compact and continue, reduce the input size, or switch to a larger-context model.";
58
+ return `${guidance}${providerDetail}`;
59
+ }
60
+
34
61
  /**
35
62
  * Run in print (single-shot) mode.
36
63
  * Sends prompts to the agent and outputs the result.
@@ -88,15 +115,27 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
88
115
  (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") &&
89
116
  !isSilentAbort(assistantMsg.errorMessage)
90
117
  ) {
91
- const errorLine = sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
118
+ // Context-overflow is an expected, recoverable-in-principle condition — not
119
+ // an opaque crash. Auto-compaction has already run inside session.prompt();
120
+ // if we still land here the request could not be made to fit. In this
121
+ // text-mode final-response path, surface an actionable diagnostic and a
122
+ // distinct exit code so text-mode (`skc -p`) callers can detect context
123
+ // exhaustion instead of parsing the raw provider error string. (JSON mode
124
+ // does not reach this branch and is intentionally out of scope.)
125
+ const isOverflow =
126
+ assistantMsg.stopReason === "error" && isContextOverflow(assistantMsg, session.model?.contextWindow);
127
+ const errorLine = isOverflow
128
+ ? formatContextOverflowError(assistantMsg, session.autoCompactionEnabled)
129
+ : sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
130
+ const exitCode = isOverflow ? CONTEXT_OVERFLOW_EXIT_CODE : 1;
92
131
  const flushed = process.stderr.write(`${errorLine}\n`);
93
132
  // When the caller owns finalization (RLM autonomous), return instead of
94
133
  // exiting so its cleanup runs; the caller surfaces a non-zero exit itself.
95
134
  if (!options.suppressProcessExit) {
96
135
  if (flushed) {
97
- process.exit(1);
136
+ process.exit(exitCode);
98
137
  } else {
99
- process.stderr.once("drain", () => process.exit(1));
138
+ process.stderr.once("drain", () => process.exit(exitCode));
100
139
  }
101
140
  }
102
141
  }
@@ -1,5 +1,12 @@
1
- import type { AutocompleteItem, AutocompleteProvider, SlashCommand } from "@sayknow-cli/tui";
2
- import { CombinedAutocompleteProvider, getKeybindings, getSlashCommandMatchRank } from "@sayknow-cli/tui";
1
+ import {
2
+ type AutocompleteItem,
3
+ type AutocompleteProvider,
4
+ CombinedAutocompleteProvider,
5
+ extractSlashCommandTokenPrefix,
6
+ getKeybindings,
7
+ getSlashCommandMatchRank,
8
+ type SlashCommand,
9
+ } from "@sayknow-cli/tui";
3
10
  import type { KeybindingsManager } from "../config/keybindings";
4
11
  import { formatKeyHints } from "../config/keybindings";
5
12
  import { isSettingsInitialized, settings } from "../config/settings";
@@ -147,6 +154,10 @@ function sortSlashCommandSuggestions(
147
154
  return { ...suggestions, items };
148
155
  }
149
156
 
157
+ function isRootPathSuggestionResult(suggestions: { items: AutocompleteItem[]; prefix: string } | null): boolean {
158
+ return suggestions?.prefix.startsWith("/") === true && suggestions.items.some(item => item.value.startsWith("/"));
159
+ }
160
+
150
161
  function withoutSkillCommandSuggestions(
151
162
  suggestions: { items: AutocompleteItem[]; prefix: string } | null,
152
163
  ): { items: AutocompleteItem[]; prefix: string } | null {
@@ -168,18 +179,9 @@ function getPromptActionPrefix(textBeforeCursor: string): string | null {
168
179
  }
169
180
 
170
181
  function getSlashTokenPrefix(textBeforeCursor: string): string | null {
171
- const slashIndex = textBeforeCursor.lastIndexOf("/");
172
- if (slashIndex === -1) return null;
173
- const beforeSlash = textBeforeCursor.slice(0, slashIndex);
174
- if (beforeSlash && !/\s$/.test(beforeSlash)) return null;
175
- const token = textBeforeCursor.slice(slashIndex);
176
- if (/[\s]/.test(token)) return null;
177
- return token;
182
+ return extractSlashCommandTokenPrefix(textBeforeCursor);
178
183
  }
179
184
 
180
- function isLeadingSlashToken(textBeforeCursor: string, slashPrefix: string): boolean {
181
- return textBeforeCursor.trimStart() === slashPrefix;
182
- }
183
185
  export class PromptActionAutocompleteProvider implements AutocompleteProvider {
184
186
  #baseProvider: CombinedAutocompleteProvider;
185
187
  #actions: PromptActionDefinition[];
@@ -226,14 +228,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
226
228
 
227
229
  const slashPrefix = getSlashTokenPrefix(textBeforeCursor);
228
230
  if (slashPrefix) {
229
- const isLeading = isLeadingSlashToken(textBeforeCursor, slashPrefix);
230
- const baseSuggestions = isLeading
231
- ? withoutSkillCommandSuggestions(await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol))
232
- : null;
233
- const skillCommandSuggestions =
234
- isLeading || slashPrefix.startsWith("/skill")
235
- ? this.#getSkillCommandSuggestions(textBeforeCursor, { includeEmpty: isLeading })
236
- : null;
231
+ const baseSuggestions = withoutSkillCommandSuggestions(
232
+ await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol),
233
+ );
234
+ if (isRootPathSuggestionResult(baseSuggestions)) return baseSuggestions;
235
+ const skillCommandSuggestions = this.#getSkillCommandSuggestions(textBeforeCursor, {
236
+ includeEmpty: false,
237
+ });
237
238
  return sortSlashCommandSuggestions(
238
239
  mergeAutocompleteSuggestions(baseSuggestions, skillCommandSuggestions),
239
240
  this.#commands,
@@ -22,8 +22,13 @@ import type { AgentSession } from "../../session/agent-session";
22
22
  import { workflowGatePath } from "../../skc-runtime/session-layout";
23
23
  import { initializeExtensions } from "../runtime-init";
24
24
  import { dispatchRpcCommand } from "../shared/agent-wire/command-dispatch";
25
- import { AgentWireFrameSequencer, toAgentWireEventFrame } from "../shared/agent-wire/event-envelope";
26
- import { rpcError as error } from "../shared/agent-wire/responses";
25
+ import {
26
+ AgentWireCompactEventEncoder,
27
+ AgentWireFrameSequencer,
28
+ toAgentWireCompactEventFrame,
29
+ toAgentWireEventFrame,
30
+ } from "../shared/agent-wire/event-envelope";
31
+ import { rpcError as error, rpcSuccess } from "../shared/agent-wire/responses";
27
32
  import { registerRpcSession, unregisterRpcSession } from "../shared/agent-wire/session-registry";
28
33
  import { defaultAuditPath, UnattendedAuditLog } from "../shared/agent-wire/unattended-audit";
29
34
  import { modelSupportsTokenCostMetrics, UnattendedSessionControlPlane } from "../shared/agent-wire/unattended-session";
@@ -168,15 +173,9 @@ function auditOutcomeFor(event: string): "accepted" | "rejected" | "denied" | "e
168
173
  return "info";
169
174
  }
170
175
 
171
- export class RpcListenRefusedError extends Error {
172
- constructor(socketPath: string) {
173
- super(
174
- `RPC --listen refused: a live server is already listening on ${socketPath}. ` +
175
- "Stop it first or choose a different --listen path.",
176
- );
177
- this.name = "RpcListenRefusedError";
178
- }
179
- }
176
+ // Thrown by prepareRpcSocketPath when the --listen path has a live owner;
177
+ // re-exported here because main.ts catches it at the RPC launch boundary.
178
+ export { RpcListenRefusedError } from "./rpc-socket-security";
180
179
 
181
180
  /**
182
181
  * Probe whether a unix-domain socket path has a live server accepting
@@ -297,6 +296,7 @@ export async function runRpcMode(
297
296
  output({ type: "ready" });
298
297
  }
299
298
  const emitRpcTitles = shouldEmitRpcTitles();
299
+ const rpcCapabilities = { compactMessageUpdate: false };
300
300
  const decodeError = (err: unknown): string => (err instanceof Error ? err.message : String(err));
301
301
 
302
302
  const pendingExtensionRequests = new Map<string, PendingExtensionRequest>();
@@ -636,8 +636,13 @@ export async function runRpcMode(
636
636
  // Output all agent events as canonical agent-wire `event` frames (docs/rpc.md):
637
637
  // { type:"event", protocol_version, session_id, seq, frame_id, payload:{ event_type, event } }.
638
638
  const eventSequencer = new AgentWireFrameSequencer(session.sessionId);
639
+ const compactEventEncoder = new AgentWireCompactEventEncoder(eventSequencer);
639
640
  session.subscribe(event => {
640
- output(toAgentWireEventFrame(event, eventSequencer));
641
+ output(
642
+ rpcCapabilities.compactMessageUpdate
643
+ ? toAgentWireCompactEventFrame(event, compactEventEncoder)
644
+ : toAgentWireEventFrame(event, eventSequencer),
645
+ );
641
646
  });
642
647
 
643
648
  // Handle a single command through the shared agent-wire dispatcher so RPC
@@ -710,6 +715,21 @@ export async function runRpcMode(
710
715
  hostUriBridge.handleResult(parsed);
711
716
  return;
712
717
  }
718
+ if ((parsed as RpcCommand).type === "set_capabilities") {
719
+ // RPC has no startup handshake; clients negotiate optional event-shape capabilities
720
+ // with this explicit command. Defaults remain legacy/full-frame until accepted here.
721
+ const command = parsed as Extract<RpcCommand, { type: "set_capabilities" }>;
722
+ const requested = Array.isArray(command.capabilities) ? command.capabilities : [];
723
+ const acceptedCapabilities = requested.filter(capability => capability === "compact_message_update");
724
+ rpcCapabilities.compactMessageUpdate = acceptedCapabilities.includes("compact_message_update");
725
+ output(
726
+ rpcSuccess(command.id, "set_capabilities", {
727
+ acceptedCapabilities,
728
+ unsupported: requested.filter(capability => capability !== "compact_message_update"),
729
+ }),
730
+ );
731
+ return;
732
+ }
713
733
  // Ordered commands run through a serial chain to preserve causal order; the
714
734
  // reader never blocks, so cancellation commands stay responsive even while a
715
735
  // long command is in flight (issue 13).
@@ -9,6 +9,18 @@ export class RpcSocketSecurityError extends Error {
9
9
  }
10
10
  }
11
11
 
12
+ // The launch boundary in main.ts catches exactly this class to turn a
13
+ // duplicate --listen into a clean stderr diagnostic + non-zero exit (issue 19).
14
+ export class RpcListenRefusedError extends Error {
15
+ constructor(socketPath: string) {
16
+ super(
17
+ `RPC --listen refused: a live server is already listening on ${socketPath}. ` +
18
+ "Stop it first or choose a different --listen path.",
19
+ );
20
+ this.name = "RpcListenRefusedError";
21
+ }
22
+ }
23
+
12
24
  const unsafeBits = 0o077;
13
25
 
14
26
  function currentUid(): number | undefined {
@@ -53,7 +65,7 @@ export async function prepareRpcSocketPath(socketPath: string): Promise<void> {
53
65
  if (!existing.isSocket()) throw new RpcSocketSecurityError(`RPC socket path is not a socket: ${socketPath}`);
54
66
  assertPrivateMode(existing.mode, socketPath);
55
67
  if (await probeUnixSocketAlive(socketPath)) {
56
- throw new RpcSocketSecurityError(`RPC socket path is live: ${socketPath}`);
68
+ throw new RpcListenRefusedError(socketPath);
57
69
  }
58
70
  await fs.unlink(socketPath);
59
71
  }
@@ -13,6 +13,7 @@ import type { SessionStats } from "../../session/agent-session";
13
13
  import type { TodoPhase } from "../../tools/todo-write";
14
14
 
15
15
  export type RpcGetStateInclude = "tools" | "dumpTools" | "systemPrompt";
16
+ export type RpcCapability = "compact_message_update";
16
17
 
17
18
  // ============================================================================
18
19
  // RPC Commands (stdin)
@@ -33,6 +34,7 @@ export type RpcCommand =
33
34
  | { id?: string; type: "set_host_tools"; tools: RpcHostToolDefinition[] }
34
35
  | { id?: string; type: "set_host_uri_schemes"; schemes: RpcHostUriSchemeDefinition[] }
35
36
  | { id?: string; type: "get_pending_workflow_gates" }
37
+ | { id?: string; type: "set_capabilities"; capabilities: RpcCapability[] }
36
38
 
37
39
  // Model
38
40
  | { id?: string; type: "set_model"; provider: string; modelId: string }
@@ -140,6 +142,13 @@ export type RpcResponse =
140
142
  success: true;
141
143
  data: { gates: RpcWorkflowGate[] };
142
144
  }
145
+ | {
146
+ id?: string;
147
+ type: "response";
148
+ command: "set_capabilities";
149
+ success: true;
150
+ data: { acceptedCapabilities: RpcCapability[]; unsupported: string[] };
151
+ }
143
152
 
144
153
  // Model
145
154
  | {
@@ -130,6 +130,10 @@ export function isRpcCommand(value: unknown): value is RpcCommand {
130
130
  return Array.isArray(value.tools) && value.tools.every(hostToolDefinition);
131
131
  case "set_host_uri_schemes":
132
132
  return Array.isArray(value.schemes) && value.schemes.every(hostUriScheme);
133
+ case "set_capabilities":
134
+ return (
135
+ Array.isArray(value.capabilities) && value.capabilities.every(capability => typeof capability === "string")
136
+ );
133
137
  case "set_model":
134
138
  return stringField(value, "provider") && stringField(value, "modelId");
135
139
  case "set_thinking_level":
@@ -13,6 +13,8 @@
13
13
  * an `AgentSessionEvent` variant fails typecheck until it is registered, and so
14
14
  * conformance tests can assert fixture coverage equals the registry exactly.
15
15
  */
16
+
17
+ import type { AssistantMessageEvent } from "@sayknow-cli/ai";
16
18
  import type { AgentSessionEvent } from "../../../session/agent-session";
17
19
 
18
20
  /** Wire protocol version. Bump on breaking envelope/semantic changes. */
@@ -68,6 +70,25 @@ export interface AgentWireEventPayload {
68
70
  event_type: AgentWireEventType;
69
71
  event: AgentSessionEvent;
70
72
  }
73
+ export type AgentWireCompactAssistantMessageEvent = AssistantMessageEvent extends infer TEvent
74
+ ? TEvent extends { type: string }
75
+ ? Omit<TEvent, "partial" | "message" | "error">
76
+ : never
77
+ : never;
78
+
79
+ export interface AgentWireCompactMessageUpdatePayload {
80
+ event_type: "message_update";
81
+ /** Compact RPC opt-in payload: delta-only update plus minimal routing metadata. */
82
+ compact: true;
83
+ message_id: string | null;
84
+ content_index: number | null;
85
+ assistantMessageEvent: AgentWireCompactAssistantMessageEvent;
86
+ /** Full message checkpoint, snapshotted synchronously when this frame is enqueued. */
87
+ checkpoint_message?: Extract<AgentSessionEvent, { type: "message_update" }>["message"];
88
+ checkpoint_reason?: "periodic";
89
+ }
90
+
91
+ export type AgentWireEventFramePayload = AgentWireEventPayload | AgentWireCompactMessageUpdatePayload;
71
92
 
72
93
  /**
73
94
  * Bounded observed-signal vocabulary surfaced to owner control planes. Mirrors
@@ -146,3 +167,4 @@ export interface AgentWireFrameEnvelope<TType extends AgentWireFrameType = Agent
146
167
 
147
168
  /** An `AgentSessionEvent` serialized into a versioned wire frame. */
148
169
  export type AgentWireEventFrame = AgentWireFrameEnvelope<"event", AgentWireEventPayload>;
170
+ export type AgentWireCompactEventFrame = AgentWireFrameEnvelope<"event", AgentWireEventFramePayload>;
@@ -12,6 +12,9 @@ import { randomUUID } from "node:crypto";
12
12
  import type { AgentSessionEvent } from "../../../session/agent-session";
13
13
  import {
14
14
  AGENT_WIRE_PROTOCOL_VERSION,
15
+ type AgentWireCompactAssistantMessageEvent,
16
+ type AgentWireCompactEventFrame,
17
+ type AgentWireCompactMessageUpdatePayload,
15
18
  type AgentWireEventFrame,
16
19
  type AgentWireEventPayload,
17
20
  type AgentWireEventType,
@@ -19,6 +22,26 @@ import {
19
22
  type AgentWireFrameType,
20
23
  } from "./event-contract";
21
24
 
25
+ export const AGENT_WIRE_COMPACT_MESSAGE_UPDATE_CHECKPOINT_INTERVAL = 32;
26
+
27
+ function recordObject(value: unknown): Record<string, unknown> | undefined {
28
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
29
+ }
30
+
31
+ function messageIdOf(message: unknown): string | null {
32
+ const id = recordObject(message)?.id;
33
+ return typeof id === "string" ? id : null;
34
+ }
35
+
36
+ function contentIndexOf(assistantMessageEvent: unknown): number | null {
37
+ const contentIndex = recordObject(assistantMessageEvent)?.contentIndex;
38
+ return typeof contentIndex === "number" && Number.isInteger(contentIndex) ? contentIndex : null;
39
+ }
40
+
41
+ function snapshotMessage<T>(message: T): T {
42
+ return structuredClone(message);
43
+ }
44
+
22
45
  function assertNever(value: never): never {
23
46
  throw new Error(`Unhandled AgentSessionEvent variant: ${JSON.stringify(value)}`);
24
47
  }
@@ -123,6 +146,65 @@ export function toAgentWireEventFrame(
123
146
  export function toAgentWireEventPayload(event: AgentSessionEvent): AgentWireEventPayload {
124
147
  return { event_type: agentSessionEventType(event), event };
125
148
  }
149
+ export function sanitizeAssistantMessageEventForCompact(
150
+ assistantMessageEvent: Extract<AgentSessionEvent, { type: "message_update" }>["assistantMessageEvent"],
151
+ ): AgentWireCompactAssistantMessageEvent {
152
+ const {
153
+ partial: _partial,
154
+ message: _message,
155
+ error: _error,
156
+ ...deltaOnly
157
+ } = assistantMessageEvent as Record<string, unknown>;
158
+ return deltaOnly as AgentWireCompactAssistantMessageEvent;
159
+ }
160
+
161
+ export interface AgentWireCompactMessageUpdateOptions {
162
+ checkpointInterval?: number;
163
+ }
164
+
165
+ /**
166
+ * Stateful compact event serializer for opt-in RPC clients.
167
+ *
168
+ * Only `message_update` changes shape: it carries the immutable provider delta
169
+ * plus message/content identifiers, with periodic full-message checkpoints that
170
+ * are cloned synchronously before the caller returns to the event loop. All other
171
+ * event variants remain canonical full event frames so legacy bridge/RPC behavior
172
+ * and terminal `message_end` reconstruction are unchanged.
173
+ */
174
+ export class AgentWireCompactEventEncoder {
175
+ readonly #sequencer: AgentWireFrameSequencer;
176
+ readonly #checkpointInterval: number;
177
+ #messageUpdateCount = 0;
178
+
179
+ constructor(sequencer: AgentWireFrameSequencer, options: AgentWireCompactMessageUpdateOptions = {}) {
180
+ this.#sequencer = sequencer;
181
+ this.#checkpointInterval = options.checkpointInterval ?? AGENT_WIRE_COMPACT_MESSAGE_UPDATE_CHECKPOINT_INTERVAL;
182
+ }
183
+
184
+ frame(event: AgentSessionEvent): AgentWireCompactEventFrame {
185
+ if (event.type !== "message_update") return toAgentWireEventFrame(event, this.#sequencer);
186
+ this.#messageUpdateCount += 1;
187
+ const payload: AgentWireCompactMessageUpdatePayload = {
188
+ event_type: "message_update",
189
+ compact: true,
190
+ message_id: messageIdOf(event.message),
191
+ content_index: contentIndexOf(event.assistantMessageEvent),
192
+ assistantMessageEvent: sanitizeAssistantMessageEventForCompact(event.assistantMessageEvent),
193
+ };
194
+ if (this.#checkpointInterval > 0 && this.#messageUpdateCount % this.#checkpointInterval === 0) {
195
+ payload.checkpoint_message = snapshotMessage(event.message);
196
+ payload.checkpoint_reason = "periodic";
197
+ }
198
+ return this.#sequencer.next("event", payload);
199
+ }
200
+ }
201
+
202
+ export function toAgentWireCompactEventFrame(
203
+ event: AgentSessionEvent,
204
+ encoder: AgentWireCompactEventEncoder,
205
+ ): AgentWireCompactEventFrame {
206
+ return encoder.frame(event);
207
+ }
126
208
 
127
209
  /** Back-compat alias for {@link toAgentWireEventFrame}. */
128
210
  export const toBridgeEventFrame = toAgentWireEventFrame;
@@ -47,6 +47,7 @@ const RPC_COMMAND_SCOPE_REGISTRY: Record<RpcCommandType, BridgeCommandScope> = {
47
47
  set_host_tools: "host_tools",
48
48
  set_host_uri_schemes: "host_uri",
49
49
  get_pending_workflow_gates: "message:read",
50
+ set_capabilities: "control",
50
51
  set_model: "model",
51
52
  cycle_model: "model",
52
53
  get_available_models: "model",
@@ -116,6 +116,7 @@ export interface InteractiveModeContext {
116
116
  onInputCallback?: (input: SubmittedUserInput) => void;
117
117
  optimisticUserMessageSignature: string | undefined;
118
118
  locallySubmittedUserSignatures: Set<string>;
119
+ optimisticInjectedSignatures: Map<string, number>;
119
120
  lastSigintTime: number;
120
121
  lastEscapeTime: number;
121
122
  lastComposerClearEscapeTime: number;
@@ -216,6 +217,7 @@ export interface InteractiveModeContext {
216
217
  handleDumpCommand(): void;
217
218
  handleDebugTranscriptCommand(): Promise<void>;
218
219
  handleClearCommand(): Promise<void>;
220
+ handleContextClearCommand(): Promise<void>;
219
221
  handleDropCommand(): Promise<void>;
220
222
  handleForkCommand(): Promise<void>;
221
223
  handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
@@ -243,6 +245,7 @@ export interface InteractiveModeContext {
243
245
  showExtensionsDashboard(): void;
244
246
  showAgentsDashboard(): void;
245
247
  showModelSelector(options?: { temporaryOnly?: boolean }): void;
248
+ showEffortSelector(): void;
246
249
  showProviderOnboarding(): void;
247
250
  showPluginSelector(mode?: "install" | "uninstall"): void;
248
251
  showUserMessageSelector(): void;
@@ -23,6 +23,7 @@ export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string
23
23
  "|-----|--------|",
24
24
  "| `Enter` | Send / queue while busy |",
25
25
  `| \`${appKey(bindings, "app.message.queue")}\` | Queue message for next turn |`,
26
+ `| \`${appKey(bindings, "app.message.dequeue")}\` | Select queued message to edit |`,
26
27
  "| `Shift+Enter` / `Ctrl+J` | New line |",
27
28
  "| `Ctrl+W` / `Option+Backspace` | Delete word backwards |",
28
29
  "| `Ctrl+U` | Delete to start of line |",