@gajae-code/coding-agent 0.12.21 → 0.13.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 (222) hide show
  1. package/CHANGELOG.md +67 -2
  2. package/dist/types/cli/args.d.ts +5 -2
  3. package/dist/types/cli/root-flags.d.ts +246 -0
  4. package/dist/types/cli.d.ts +11 -19
  5. package/dist/types/commands/daemon.d.ts +0 -30
  6. package/dist/types/commands/launch.d.ts +11 -19
  7. package/dist/types/commands/quick-lane.d.ts +32 -0
  8. package/dist/types/commands/sdk.d.ts +21 -15
  9. package/dist/types/config/file-lock-gc.d.ts +2 -0
  10. package/dist/types/config/model-equivalence.d.ts +20 -0
  11. package/dist/types/config/model-profile-activation.d.ts +62 -17
  12. package/dist/types/config/model-profiles.d.ts +10 -0
  13. package/dist/types/config/model-registry.d.ts +68 -1
  14. package/dist/types/config/model-resolver.d.ts +15 -4
  15. package/dist/types/config/models-config-schema.d.ts +7 -2
  16. package/dist/types/config/provider-selection-policy.d.ts +62 -0
  17. package/dist/types/config/settings-schema.d.ts +36 -78
  18. package/dist/types/config/settings.d.ts +2 -0
  19. package/dist/types/edit/renderer.d.ts +5 -2
  20. package/dist/types/edit/streaming.d.ts +13 -0
  21. package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
  22. package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
  23. package/dist/types/extensibility/extensions/types.d.ts +3 -0
  24. package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
  25. package/dist/types/internal-urls/local-protocol.d.ts +1 -0
  26. package/dist/types/modes/components/model-selector.d.ts +0 -2
  27. package/dist/types/modes/components/provider-order-context.d.ts +45 -0
  28. package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
  29. package/dist/types/modes/components/settings-defs.d.ts +5 -1
  30. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  31. package/dist/types/quick-lane/classify.d.ts +40 -0
  32. package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
  33. package/dist/types/sdk/broker/broker.d.ts +56 -2
  34. package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
  35. package/dist/types/sdk/broker/session-index.d.ts +83 -2
  36. package/dist/types/sdk/broker/state-version.d.ts +1 -1
  37. package/dist/types/sdk/bus/notification-service.d.ts +6 -0
  38. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
  39. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
  40. package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
  41. package/dist/types/sdk/cli/rows.d.ts +98 -0
  42. package/dist/types/sdk/cli/session-cli.d.ts +35 -3
  43. package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
  44. package/dist/types/sdk/elevation/audit.d.ts +93 -0
  45. package/dist/types/sdk/elevation/capability.d.ts +3 -0
  46. package/dist/types/sdk/elevation/digest.d.ts +72 -0
  47. package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
  48. package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
  49. package/dist/types/sdk/elevation/owner.d.ts +48 -0
  50. package/dist/types/sdk/guides/cache.d.ts +98 -0
  51. package/dist/types/sdk/guides/catalog.d.ts +156 -0
  52. package/dist/types/sdk/guides/cli.d.ts +39 -0
  53. package/dist/types/sdk/guides/manifest.d.ts +66 -0
  54. package/dist/types/sdk/guides/verify.d.ts +66 -0
  55. package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
  56. package/dist/types/sdk/host/control/index.d.ts +1 -1
  57. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  58. package/dist/types/sdk/host/query/cursor.d.ts +10 -0
  59. package/dist/types/sdk/host/query/handlers.d.ts +21 -0
  60. package/dist/types/sdk/host/session-runtime.d.ts +9 -0
  61. package/dist/types/sdk/mcp/server.d.ts +2 -2
  62. package/dist/types/sdk/prompt-status.d.ts +11 -0
  63. package/dist/types/sdk/session.d.ts +4 -0
  64. package/dist/types/sdk/transport/index.d.ts +1 -0
  65. package/dist/types/sdk/transport/relay.d.ts +1 -0
  66. package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
  67. package/dist/types/session/agent-session.d.ts +32 -18
  68. package/dist/types/session/blob-store.d.ts +13 -7
  69. package/dist/types/session/fallback-chain-controller.d.ts +15 -0
  70. package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
  71. package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
  72. package/dist/types/session/session-manager.d.ts +139 -10
  73. package/dist/types/session/session-storage.d.ts +48 -0
  74. package/dist/types/setup/provider-onboarding.d.ts +7 -1
  75. package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
  76. package/dist/types/stt/setup.d.ts +2 -0
  77. package/dist/types/task/executor.d.ts +10 -2
  78. package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
  79. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  80. package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
  81. package/dist/types/tools/browser/tab-worker.d.ts +2 -0
  82. package/dist/types/tools/browser.d.ts +2 -0
  83. package/dist/types/tools/image-gen.d.ts +14 -18
  84. package/dist/types/tools/index.d.ts +2 -0
  85. package/dist/types/tools/resource-gc.d.ts +2 -0
  86. package/dist/types/utils/pasted-image-path.d.ts +6 -0
  87. package/package.json +9 -9
  88. package/scripts/generate-sdk-operation-inventory.ts +8 -0
  89. package/scripts/run-test-manifest.ts +61 -21
  90. package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
  91. package/src/cli/args.ts +62 -9
  92. package/src/cli/completion-cli.ts +1 -0
  93. package/src/cli/fast-help.ts +1 -0
  94. package/src/cli/root-flags.ts +100 -0
  95. package/src/cli/setup-cli.ts +6 -2
  96. package/src/cli.ts +42 -65
  97. package/src/commands/acp.ts +1 -1
  98. package/src/commands/daemon.ts +2 -31
  99. package/src/commands/gc.ts +1 -1
  100. package/src/commands/launch.ts +8 -129
  101. package/src/commands/quick-lane.ts +51 -0
  102. package/src/commands/sdk.ts +200 -26
  103. package/src/commands/setup.ts +4 -2
  104. package/src/config/file-lock-gc.ts +4 -1
  105. package/src/config/model-equivalence.ts +76 -7
  106. package/src/config/model-profile-activation.ts +1115 -265
  107. package/src/config/model-profiles.ts +134 -2
  108. package/src/config/model-registry.ts +352 -50
  109. package/src/config/model-resolver.ts +289 -38
  110. package/src/config/models-config-schema.ts +11 -4
  111. package/src/config/provider-selection-policy.ts +146 -0
  112. package/src/config/settings-schema.ts +50 -77
  113. package/src/config/settings.ts +40 -0
  114. package/src/coordinator-mcp/server.ts +32 -2
  115. package/src/edit/renderer.ts +175 -223
  116. package/src/edit/streaming.ts +108 -1
  117. package/src/export/html/index.ts +57 -7
  118. package/src/extensibility/custom-tools/types.ts +2 -0
  119. package/src/extensibility/extensions/compact-handler.ts +3 -2
  120. package/src/extensibility/extensions/runner.ts +6 -0
  121. package/src/extensibility/extensions/types.ts +3 -0
  122. package/src/gjc-runtime/gc-runtime.ts +379 -20
  123. package/src/gjc-runtime/launch-worktree.ts +5 -1
  124. package/src/gjc-runtime/team-runtime.ts +59 -2
  125. package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
  126. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
  127. package/src/internal-urls/docs-index.generated.ts +17 -13
  128. package/src/internal-urls/local-protocol.ts +1 -0
  129. package/src/main.ts +40 -3
  130. package/src/memories/index.ts +4 -2
  131. package/src/modes/acp/acp-agent.ts +235 -91
  132. package/src/modes/components/model-selector.ts +206 -42
  133. package/src/modes/components/provider-order-context.ts +197 -0
  134. package/src/modes/components/provider-order-editor.ts +441 -0
  135. package/src/modes/components/settings-defs.ts +21 -1
  136. package/src/modes/components/settings-selector.ts +45 -2
  137. package/src/modes/components/tool-execution.ts +47 -15
  138. package/src/modes/controllers/command-controller.ts +6 -3
  139. package/src/modes/controllers/extension-ui-controller.ts +2 -0
  140. package/src/modes/controllers/input-controller.ts +73 -2
  141. package/src/modes/controllers/selector-controller.ts +260 -153
  142. package/src/modes/prompt-suggestion-controller.ts +1 -1
  143. package/src/modes/runtime-init.ts +1 -0
  144. package/src/modes/utils/ui-helpers.ts +18 -0
  145. package/src/prompts/tools/browser.md +1 -0
  146. package/src/quick-lane/classify.ts +257 -0
  147. package/src/rlm/index.ts +1 -1
  148. package/src/runtime/stt-settings-setup.ts +57 -0
  149. package/src/sdk/acp/adapter.ts +2 -1
  150. package/src/sdk/broker/broker.ts +780 -12
  151. package/src/sdk/broker/lifecycle.ts +70 -29
  152. package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
  153. package/src/sdk/broker/session-index.ts +463 -48
  154. package/src/sdk/broker/state-version.ts +13 -9
  155. package/src/sdk/broker/transport.ts +14 -1
  156. package/src/sdk/bus/index.ts +34 -13
  157. package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
  158. package/src/sdk/bus/notification-service.ts +47 -1
  159. package/src/sdk/bus/prompt-reconciliation.ts +22 -3
  160. package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
  161. package/src/sdk/bus/telegram-daemon.ts +54 -3
  162. package/src/sdk/cli/rows.ts +247 -0
  163. package/src/sdk/cli/session-cli.ts +930 -79
  164. package/src/sdk/elevation/allowlist.ts +54 -0
  165. package/src/sdk/elevation/audit.ts +208 -0
  166. package/src/sdk/elevation/capability.ts +41 -0
  167. package/src/sdk/elevation/digest.ts +129 -0
  168. package/src/sdk/elevation/dispatch-receipt.ts +184 -0
  169. package/src/sdk/elevation/grant-ledger.ts +1142 -0
  170. package/src/sdk/elevation/owner.ts +109 -0
  171. package/src/sdk/guides/cache.ts +363 -0
  172. package/src/sdk/guides/catalog.ts +554 -0
  173. package/src/sdk/guides/cli.ts +235 -0
  174. package/src/sdk/guides/manifest.ts +152 -0
  175. package/src/sdk/guides/verify.ts +178 -0
  176. package/src/sdk/host/control/dispatch.ts +49 -1
  177. package/src/sdk/host/control/index.ts +1 -0
  178. package/src/sdk/host/control/operations.ts +8 -1
  179. package/src/sdk/host/query/cursor.ts +47 -3
  180. package/src/sdk/host/query/handlers.ts +155 -2
  181. package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
  182. package/src/sdk/host/session-runtime.test.ts +391 -1
  183. package/src/sdk/host/session-runtime.ts +308 -73
  184. package/src/sdk/host/surface-policy.ts +3 -0
  185. package/src/sdk/host/websocket-transport.ts +7 -1
  186. package/src/sdk/mcp/server.ts +88 -15
  187. package/src/sdk/prompt-status.ts +8 -0
  188. package/src/sdk/protocol/operation-inventory.generated.json +62 -0
  189. package/src/sdk/protocol/operation-registry.ts +3 -2
  190. package/src/sdk/session.ts +87 -39
  191. package/src/sdk/transport/index.ts +1 -0
  192. package/src/sdk/transport/relay.ts +3 -0
  193. package/src/sdk/transport/serve-cli.ts +111 -24
  194. package/src/secrets/obfuscator.ts +92 -12
  195. package/src/session/agent-session.ts +322 -93
  196. package/src/session/blob-store.ts +60 -8
  197. package/src/session/fallback-chain-controller.ts +35 -0
  198. package/src/session/internal/managed-session-scope.ts +25 -3
  199. package/src/session/internal/managed-session-storage.ts +574 -73
  200. package/src/session/internal/session-memory-sidecar.ts +19 -29
  201. package/src/session/session-manager.ts +2166 -743
  202. package/src/session/session-storage.ts +715 -48
  203. package/src/setup/provider-onboarding.ts +19 -3
  204. package/src/setup/provider-presets.json +3 -1
  205. package/src/slash-commands/builtin-registry.ts +85 -3
  206. package/src/stt/downloader.ts +2 -3
  207. package/src/stt/setup.ts +29 -4
  208. package/src/task/executor.ts +22 -18
  209. package/src/task/index.ts +24 -1
  210. package/src/tools/archive-reader.ts +11 -2
  211. package/src/tools/browser/runtime-diagnostics.ts +249 -0
  212. package/src/tools/browser/tab-protocol.ts +4 -0
  213. package/src/tools/browser/tab-supervisor.ts +5 -0
  214. package/src/tools/browser/tab-worker.ts +36 -1
  215. package/src/tools/browser.ts +7 -0
  216. package/src/tools/image-gen.ts +110 -166
  217. package/src/tools/index.ts +2 -0
  218. package/src/tools/read.ts +1 -1
  219. package/src/tools/resource-gc.ts +6 -1
  220. package/src/tools/tool-catalog.generated.ts +5 -1
  221. package/src/utils/pasted-image-path.ts +93 -0
  222. package/src/web/search/index.ts +3 -3
@@ -17,6 +17,8 @@ import {
17
17
  } from "@gajae-code/tui";
18
18
  import { getProjectDir, logger, sanitizeText } from "@gajae-code/utils";
19
19
  import { EDIT_MODE_STRATEGIES, type EditMode, type PerFileDiffPreview } from "../../edit";
20
+ import { type EditRenderContext, getPerFileEditRenderArgs, getPerFileEditRenderContext } from "../../edit/renderer";
21
+ import { getEditRequestTargetInventory, orderedDistinctPaths } from "../../edit/streaming";
20
22
  import type { Theme } from "../../modes/theme/theme";
21
23
  import { theme } from "../../modes/theme/theme";
22
24
  import { BASH_DEFAULT_PREVIEW_LINES } from "../../tools/bash";
@@ -81,9 +83,10 @@ function argsCanBeSharedWithRenderer(toolName: string, tool: AgentTool | undefin
81
83
  return !tool?.renderCall && !tool?.renderResult && READONLY_ARG_RENDERER_TOOLS.has(toolName);
82
84
  }
83
85
 
84
- function partialJsonLength(args: unknown): number {
85
- const value = args && typeof args === "object" ? (args as { __partialJson?: unknown }).__partialJson : undefined;
86
- return typeof value === "string" ? value.length : -1;
86
+ function previewPayloadKey(args: unknown): string | undefined {
87
+ const partialJson =
88
+ args && typeof args === "object" ? (args as { __partialJson?: unknown }).__partialJson : undefined;
89
+ return typeof partialJson === "string" ? `${partialJson.length}:${partialJson}` : undefined;
87
90
  }
88
91
 
89
92
  /**
@@ -234,6 +237,7 @@ export class ToolExecutionComponent extends Container {
234
237
  #editDiffLastArgsKey?: string;
235
238
  #argsIdentityVersion = 0;
236
239
  #lastArgsReference: any;
240
+
237
241
  #shareArgsWithRenderer = false;
238
242
  // Cached converted images for Kitty protocol (which requires PNG), keyed by index and source.
239
243
  #convertedImages: Map<number, { data: string; mimeType: string; source: string }> = new Map();
@@ -312,6 +316,7 @@ export class ToolExecutionComponent extends Container {
312
316
  updateArgs(args: any, _toolCallId?: string): void {
313
317
  const argsChanged = !Bun.deepEquals(this.#args, args);
314
318
  if (!argsChanged && !this.#editMode) return;
319
+
315
320
  if (args !== this.#lastArgsReference) {
316
321
  this.#lastArgsReference = args;
317
322
  this.#argsIdentityVersion += 1;
@@ -352,11 +357,12 @@ export class ToolExecutionComponent extends Container {
352
357
  effectiveArgs = args;
353
358
  }
354
359
 
355
- // Coalesce duplicate computes without serializing multi-KB streamed args every delta.
360
+ // The streamed partial JSON is the exact payload snapshot. Its length alone
361
+ // cannot distinguish same-size replacements. Completed calls lack that
362
+ // snapshot, so retain identity invalidation for their distinct arg objects.
356
363
  const argsKey = [
357
364
  this.#toolName,
358
- this.#argsIdentityVersion,
359
- partialJsonLength(args),
365
+ previewPayloadKey(args) ?? `identity:${this.#argsIdentityVersion}`,
360
366
  this.#argsComplete ? 1 : 0,
361
367
  ].join(":");
362
368
  if (argsKey === this.#editDiffLastArgsKey) return;
@@ -498,7 +504,21 @@ export class ToolExecutionComponent extends Container {
498
504
  }
499
505
 
500
506
  #captureLogicalVisibleProjection(): string {
501
- return this.render(10_000).join("\n");
507
+ const rendered = this.render(10_000).join("\n");
508
+ // The sticky-viewport source tracks semantic transcript output, not just
509
+ // the literal pixels. A result-first collapsed edit card intentionally
510
+ // hides in-flight diff previews, so preview resolution changes the
511
+ // rendered text by nothing — yet it is still semantic progress the
512
+ // viewport must re-anchor on (the preview becomes visible the moment the
513
+ // user expands, and downstream snapshot/replay keys off this revision).
514
+ // Fold a compact fingerprint of resolved-preview state into the logical
515
+ // projection so it advances independently of the sparse collapsed render.
516
+ const preview = this.#editDiffPreview;
517
+ if (!preview || preview.length === 0) return rendered;
518
+ const fingerprint = preview
519
+ .map(file => `${file.path}:${file.diff ? "1" : "0"}:${file.firstChangedLine ?? ""}:${file.error ? "1" : "0"}`)
520
+ .join("|");
521
+ return `${rendered}\u0000preview:${fingerprint}`;
502
522
  }
503
523
 
504
524
  #markVisibleMutationIfChanged(notify = false): void {
@@ -697,13 +717,17 @@ export class ToolExecutionComponent extends Container {
697
717
  const perFileResults = this.#result?.details?.perFileResults as
698
718
  | Array<{ path: string; isError?: boolean }>
699
719
  | undefined;
700
- if (perFileResults && perFileResults.length > 1) {
720
+ const requestedEditFiles = isEditLikeToolName(this.#toolName)
721
+ ? getEditRequestTargetInventory(this.#args, this.#editMode, { isPartial: this.#isPartial }).paths.length
722
+ : 0;
723
+ if (perFileResults && (perFileResults.length > 1 || (this.#isPartial && requestedEditFiles > 1))) {
701
724
  // Multi-file: render each file as its own Box (identical to separate tool calls)
702
725
  this.#contentBox.setBgFn(undefined);
703
726
  this.#contentBox.clear();
704
727
 
705
728
  const renderContext = this.#buildRenderContext();
706
729
  this.#renderState.renderContext = renderContext;
730
+ const callArgs = this.#getCallArgsForRender();
707
731
 
708
732
  for (let i = 0; i < perFileResults.length; i++) {
709
733
  const fileResult = perFileResults[i];
@@ -717,10 +741,17 @@ export class ToolExecutionComponent extends Container {
717
741
  : (text: string) => theme.bg("toolSuccessBg", text);
718
742
  const fileBox = new Box(1, 0, fileBgFn);
719
743
  try {
744
+ const fileRenderState = {
745
+ ...this.#renderState,
746
+ renderContext: getPerFileEditRenderContext(renderContext as EditRenderContext, fileResult.path) as
747
+ | Record<string, unknown>
748
+ | undefined,
749
+ };
720
750
  const resultComponent = renderer.renderResult(
721
751
  { content: [], details: fileResult, isError: fileResult.isError },
722
- this.#renderState,
752
+ fileRenderState,
723
753
  theme,
754
+ getPerFileEditRenderArgs(callArgs, fileResult.path, this.#editMode),
724
755
  );
725
756
  if (resultComponent) {
726
757
  fileBox.addChild(ensureInvalidate(resultComponent));
@@ -733,10 +764,11 @@ export class ToolExecutionComponent extends Container {
733
764
  }
734
765
 
735
766
  // Show pending indicator for remaining files
736
- const totalFiles = this.#args?.edits
737
- ? new Set((this.#args.edits as any[]).map((e: any) => e?.path).filter(Boolean)).size
738
- : 0;
739
- const remaining = Math.max(0, totalFiles - perFileResults.length);
767
+ const requestedFiles = getEditRequestTargetInventory(this.#args, this.#editMode, {
768
+ isPartial: this.#isPartial,
769
+ }).paths.length;
770
+ const representedFiles = orderedDistinctPaths(perFileResults.map(file => file.path)).length;
771
+ const remaining = Math.max(0, requestedFiles - representedFiles);
740
772
  if (remaining > 0 && this.#isPartial) {
741
773
  const pendingSpacer = new Spacer(1);
742
774
  this.#multiFileBoxes.push(pendingSpacer);
@@ -906,11 +938,11 @@ export class ToolExecutionComponent extends Container {
906
938
  ? { error: first.error }
907
939
  : { diff: first.diff ?? "", firstChangedLine: first.firstChangedLine };
908
940
  }
909
- if (previews.length > 1) {
941
+ if (previews.some(preview => preview.path.length > 0)) {
910
942
  context.perFileDiffPreview = previews;
911
943
  }
912
944
  }
913
- if (!previews?.some(preview => preview.diff)) {
945
+ if (this.#expanded && !previews?.some(preview => preview.diff)) {
914
946
  const editMode = this.#editMode;
915
947
  const strategy = editMode ? EDIT_MODE_STRATEGIES[editMode] : undefined;
916
948
  const fallback = strategy?.renderStreamingFallback(this.#args, theme);
@@ -478,9 +478,12 @@ export class CommandController {
478
478
  const retirement = memory.coldRetirementActive ? theme.fg("success", "active") : theme.fg("dim", "inactive");
479
479
  info += `\n${theme.bold("Session Memory")}\n`;
480
480
  info += `${theme.fg("dim", "Cold Retirement:")} ${retirement}\n`;
481
- info += `${theme.fg("dim", "Hot Region:")} ${formatBytes(memory.hotRegionBytes)}\n`;
482
- info += `${theme.fg("dim", "Metadata:")} ${formatBytes(memory.metaDescriptorBytes)}\n`;
483
- info += `${theme.fg("dim", "Accounted:")} ${formatBytes(memory.totalAccountedBytes)}\n`;
481
+ info += `${theme.fg("dim", "Hot Resident:")} ${formatBytes(memory.hotResidentBytes)}\n`;
482
+ info += `${theme.fg("dim", "Metadata Resident:")} ${formatBytes(memory.metadataResidentBytes)}\n`;
483
+ info += `${theme.fg("dim", "Allocated Caches:")} ${formatBytes(memory.allocatedCacheBytes)}\n`;
484
+ info += `${theme.fg("dim", "Reserved Budget:")} ${formatBytes(memory.reservedBudgetBytes)}\n`;
485
+ info += `${theme.fg("dim", "Sidecar Files:")} ${formatBytes(memory.sidecarFileBytes)}\n`;
486
+ info += `${theme.fg("dim", "Budget Accounted:")} ${formatBytes(memory.totalAccountedBytes)}\n`;
484
487
  if (memory.coldIndexBytes > 0 || memory.coldIndexBlockCacheBytes > 0 || memory.coldEntryCacheBytes > 0) {
485
488
  info += `${theme.fg("dim", "Cold Index/Caches:")} ${formatBytes(memory.coldIndexBytes)} / ${formatBytes(memory.coldIndexBlockCacheBytes)} / ${formatBytes(memory.coldEntryCacheBytes)}\n`;
486
489
  }
@@ -575,6 +575,7 @@ export class ExtensionUiController {
575
575
  };
576
576
  const contextActions: ExtensionContextActions = {
577
577
  getModel: () => this.ctx.session.model,
578
+ getCredentialSessionId: () => this.ctx.session.credentialSessionId,
578
579
  isIdle: () => !this.ctx.session.isStreaming,
579
580
  getActivePromptHandle: () => this.ctx.session.activePromptHandle,
580
581
  abort: () => this.ctx.session.abort(),
@@ -892,6 +893,7 @@ export class ExtensionUiController {
892
893
  };
893
894
  const contextActions: ExtensionContextActions = {
894
895
  getModel: () => this.ctx.session.model,
896
+ getCredentialSessionId: () => this.ctx.session.credentialSessionId,
895
897
  isIdle: () => !this.ctx.session.isStreaming,
896
898
  getActivePromptHandle: () => this.ctx.session.activePromptHandle,
897
899
  abort: () => this.ctx.session.abort(),
@@ -25,7 +25,11 @@ import { getEditorCommand, openInEditor } from "../../utils/external-editor";
25
25
  import { ensureSupportedImageInput, ImageInputTooLargeError } from "../../utils/image-loading";
26
26
  import { resizeImage } from "../../utils/image-resize";
27
27
  import { loadPastedImageBatch, PastedImageBatchError } from "../../utils/pasted-image-loading";
28
- import { formatPastedImageReference, parsePastedImagePaths } from "../../utils/pasted-image-path";
28
+ import {
29
+ formatPastedImageReference,
30
+ locatePastedImageReferenceAroundCursor,
31
+ parsePastedImagePaths,
32
+ } from "../../utils/pasted-image-path";
29
33
  import { generateSessionTitle, setSessionTerminalTitle } from "../../utils/title-generator";
30
34
  import { ActionRegistry, APP_ACTION_METADATA } from "../action-registry";
31
35
  import { CommandPalette, type CommandPaletteAction, type CommandPaletteEntry } from "../components/command-palette";
@@ -64,6 +68,11 @@ function isExpandable(obj: unknown): obj is Expandable {
64
68
  export class InputController {
65
69
  readonly actionRegistry: ActionRegistry<void>;
66
70
  readonly #loadPastedImageBatch: typeof loadPastedImageBatch;
71
+ #imagePlaceholderDeletionUndo?: {
72
+ beforeText: string;
73
+ afterText: string;
74
+ pendingImages: InteractiveModeContext["pendingImages"];
75
+ };
67
76
 
68
77
  constructor(
69
78
  private ctx: InteractiveModeContext,
@@ -645,6 +654,12 @@ export class InputController {
645
654
  };
646
655
 
647
656
  this.ctx.editor.clearCustomKeyHandlers();
657
+ for (const key of new Set([
658
+ ...this.ctx.keybindings.getKeys("tui.editor.deleteCharBackward"),
659
+ "shift+backspace" as const,
660
+ ])) {
661
+ this.ctx.editor.setCustomKeyHandler(key, () => this.#deleteImagePlaceholderBeforeCursor());
662
+ }
648
663
  // Wire up extension shortcuts
649
664
  this.registerExtensionShortcuts();
650
665
 
@@ -751,6 +766,7 @@ export class InputController {
751
766
  // Tab on an empty composer accepts the pending ghost-text prompt suggestion.
752
767
  this.ctx.editor.onTab = (text: string) => this.ctx.promptSuggestion?.tryAcceptOnTab(text) === true;
753
768
 
769
+ this.ctx.editor.onUndo = text => this.#restorePendingImagesAfterPlaceholderUndo(text);
754
770
  this.ctx.editor.onChange = (text: string) => {
755
771
  this.#resetEscapeGestures();
756
772
  this.ctx.promptSuggestion?.notifyEditorChanged(text);
@@ -777,6 +793,7 @@ export class InputController {
777
793
  }
778
794
 
779
795
  async submitText(text: string, composer: ComposerSubmissionOptions): Promise<void> {
796
+ this.#imagePlaceholderDeletionUndo = undefined;
780
797
  text = text.trim();
781
798
  const submittedBashMode = this.ctx.isBashMode;
782
799
  const submittedBashNoContext = this.ctx.isBashNoContext;
@@ -989,7 +1006,7 @@ export class InputController {
989
1006
  text,
990
1007
  registry,
991
1008
  this.ctx.settings,
992
- this.ctx.session.sessionId,
1009
+ this.ctx.session.credentialSessionId,
993
1010
  this.ctx.session.model,
994
1011
  provider => this.ctx.session.agent.metadataForProvider(provider),
995
1012
  )
@@ -1480,6 +1497,21 @@ export class InputController {
1480
1497
  return;
1481
1498
  }
1482
1499
 
1500
+ // Foreground bash/eval owns the process fence. Queue all prompt text,
1501
+ // including slash skill input, until the process settles instead of
1502
+ // expanding a skill and starting a competing model turn.
1503
+ if (this.ctx.session.isBashRunning || this.ctx.session.isEvalRunning) {
1504
+ this.ctx.editor.addToHistory(text);
1505
+ this.ctx.editor.setText("");
1506
+ await this.ctx.withLocalSubmission(text, () =>
1507
+ this.ctx.session.followUp(text, undefined, {
1508
+ followUpQueuePolicy: "sequential",
1509
+ }),
1510
+ );
1511
+ this.ctx.updatePendingMessagesDisplay();
1512
+ this.ctx.ui.requestRender();
1513
+ return;
1514
+ }
1483
1515
  // Skill commands invoke through the custom-message path regardless of
1484
1516
  // which keybinding submitted them. Enter routes them as `steer`;
1485
1517
  // explicit queue shortcuts route them as `followUp`.
@@ -1736,6 +1768,45 @@ export class InputController {
1736
1768
  }
1737
1769
  }
1738
1770
 
1771
+ #deleteImagePlaceholderBeforeCursor(): boolean {
1772
+ if (this.ctx.pendingImages.length === 0) return false;
1773
+
1774
+ const { line, col } = this.ctx.editor.getCursor();
1775
+ const currentLine = this.ctx.editor.getLines()[line] ?? "";
1776
+ const range = locatePastedImageReferenceAroundCursor(currentLine, col);
1777
+ if (!range) return false;
1778
+ const placeholderNumberText = String(range.imageIndex);
1779
+
1780
+ const imageIndex = Number.parseInt(placeholderNumberText, 10) - 1;
1781
+ if (imageIndex < 0 || imageIndex >= this.ctx.pendingImages.length) return false;
1782
+
1783
+ const beforeText = this.ctx.editor.getText();
1784
+ const pendingImages = this.ctx.pendingImages;
1785
+ const deleted = this.ctx.editor.deleteTextRangeAroundCursor(range.startCol, range.endCol);
1786
+ if (deleted) {
1787
+ this.#imagePlaceholderDeletionUndo = {
1788
+ beforeText,
1789
+ afterText: this.ctx.editor.getText(),
1790
+ pendingImages,
1791
+ };
1792
+ }
1793
+ return deleted;
1794
+ }
1795
+
1796
+ #restorePendingImagesAfterPlaceholderUndo(text: string): void {
1797
+ const deletion = this.#imagePlaceholderDeletionUndo;
1798
+ if (!deletion) return;
1799
+
1800
+ if (text === deletion.beforeText) {
1801
+ if (this.ctx.pendingImages.length === 0) {
1802
+ this.ctx.pendingImages = deletion.pendingImages;
1803
+ }
1804
+ this.#imagePlaceholderDeletionUndo = undefined;
1805
+ } else if (text !== deletion.afterText) {
1806
+ this.#imagePlaceholderDeletionUndo = undefined;
1807
+ }
1808
+ }
1809
+
1739
1810
  #nextImagePlaceholder(): string {
1740
1811
  return `[image ${this.ctx.pendingImages.length}]`;
1741
1812
  }