@timurproko/a1 0.2.7-dev.788 → 0.2.7-dev.792

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 (66) hide show
  1. package/dist/app/session-shell/session-shell-root.d.ts +0 -1
  2. package/dist/app/session-shell/session-shell-root.js +41 -15
  3. package/dist/app/session-shell/session-shell.js +5 -4
  4. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
  5. package/dist/app/session-shell/session-viewport-controller.js +24 -12
  6. package/dist/composition/owned-ui.d.ts +1 -0
  7. package/dist/composition/owned-ui.js +27 -2
  8. package/dist/contracts/owned-ui/index.d.ts +1 -1
  9. package/dist/contracts/owned-ui/model.d.ts +3 -0
  10. package/dist/features/owned-ui/settings-app.js +9 -6
  11. package/dist/features/resident-tab/index.d.ts +3 -1
  12. package/dist/features/resident-tab/index.js +5 -1
  13. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
  14. package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
  15. package/dist/features/resident-tab/{shortcuts.d.ts → shortcut-declarations.d.ts} +5 -2
  16. package/dist/features/resident-tab/{shortcuts.js → shortcut-declarations.js} +5 -17
  17. package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
  18. package/dist/features/resident-tab/shortcut-projection.js +64 -0
  19. package/dist/foundation/release/bootstrap.js +1 -0
  20. package/dist/foundation/resident-tabs/bridge-client.d.ts +5 -0
  21. package/dist/foundation/resident-tabs/bridge-client.js +17 -1
  22. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -2
  23. package/dist/foundation/resident-tabs/prompt-journal.js +0 -1
  24. package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -0
  25. package/dist/integrations/pi/components/owned-editor-ux.js +13 -0
  26. package/dist/integrations/pi/components/shell-editor-autocomplete.js +1 -0
  27. package/dist/integrations/pi/components/shell-presenters-info.d.ts +1 -0
  28. package/dist/integrations/pi/components/shell-presenters-info.js +10 -30
  29. package/dist/integrations/pi/components/shell-shared-facade.d.ts +2 -0
  30. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  31. package/dist/native/darwin-arm64/manifest.json +3 -2
  32. package/dist/native/darwin-arm64/terminal-host +0 -0
  33. package/dist/native/darwin-arm64/terminal-host.manifest.json +7 -6
  34. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  35. package/dist/native/darwin-x64/manifest.json +5 -4
  36. package/dist/native/darwin-x64/process-guardian +0 -0
  37. package/dist/native/darwin-x64/terminal-host +0 -0
  38. package/dist/native/darwin-x64/terminal-host.manifest.json +7 -6
  39. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  40. package/dist/native/linux-x64/manifest.json +3 -2
  41. package/dist/native/linux-x64/terminal-host +0 -0
  42. package/dist/native/linux-x64/terminal-host.manifest.json +7 -6
  43. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  44. package/dist/native/win32-x64/manifest.json +4 -3
  45. package/dist/native/win32-x64/process-guardian.exe +0 -0
  46. package/dist/native/win32-x64/terminal-host.exe +0 -0
  47. package/dist/native/win32-x64/terminal-host.manifest.json +6 -5
  48. package/dist/ui/components/index.d.ts +2 -2
  49. package/dist/ui/components/index.js +1 -1
  50. package/dist/ui/components/selection-copy.d.ts +2 -0
  51. package/dist/ui/components/selection-copy.js +13 -0
  52. package/dist/ui/components/submitted-prompt.d.ts +7 -4
  53. package/dist/ui/components/submitted-prompt.js +18 -10
  54. package/dist/ui/components/transcript-viewport.d.ts +10 -6
  55. package/dist/ui/components/transcript-viewport.js +28 -13
  56. package/dist/ui/settings/declarations.d.ts +13 -1
  57. package/dist/ui/settings/declarations.js +12 -1
  58. package/dist/ui/settings/migrations.js +7 -0
  59. package/docs/architecture/evidence/resident-tabs/macos-x64.md +1 -1
  60. package/docs/architecture/process-guardian-provenance.md +1 -1
  61. package/docs/architecture/project-structure.md +8 -0
  62. package/docs/architecture/resident-tabs.md +18 -2
  63. package/docs/architecture/terminal-host-provenance.md +1 -1
  64. package/docs/architecture/toolchain.md +1 -1
  65. package/docs/validation.md +3 -3
  66. package/package.json +1 -1
@@ -15,6 +15,9 @@ export type TabBridgeCommand = {
15
15
  readonly type: "rename";
16
16
  readonly name: string;
17
17
  readonly nameSource: "default" | "user" | "auto";
18
+ } | {
19
+ readonly type: "route";
20
+ readonly route: "settings";
18
21
  } | {
19
22
  readonly type: "stop";
20
23
  readonly deadlineMs: number;
@@ -48,6 +51,8 @@ export declare class TabBridgeClient {
48
51
  accent(accent: string): void;
49
52
  session(file: string | null, name: string | null, nameSource?: "default" | "user" | "auto"): void;
50
53
  prompt(interrupted: boolean): void;
54
+ /** Publishes the effective tab shortcut assignments so the host dispatches and labels the same keys. */
55
+ shortcuts(assignments: Readonly<Record<string, string | null>>): void;
51
56
  requestDetach(origin?: TabAttachment | null, timeoutMs?: number): Promise<boolean>;
52
57
  requestRestart(origin?: TabAttachment | null, timeoutMs?: number): Promise<boolean>;
53
58
  consumeControllerBoundaryInput(data: string): string;
@@ -15,6 +15,7 @@ export class TabBridgeClient {
15
15
  #latestStatus = null;
16
16
  #accent = null;
17
17
  #latestSession = null;
18
+ #latestShortcuts = null;
18
19
  #heartbeat = null;
19
20
  #pending = new Map();
20
21
  #startWaiter = null;
@@ -63,7 +64,7 @@ export class TabBridgeClient {
63
64
  transport.once("connect", () => {
64
65
  this.#send("hello", {
65
66
  role: "bridge", generation: 1, build: this.#options.build ?? "a1",
66
- features: ["bridge-status", "bridge-detach", "bridge-heartbeat", "bridge-restart", "bridge-visibility", "bridge-rename", "bridge-stop", "controller-boundary", "recovery-journal"],
67
+ features: ["bridge-status", "bridge-detach", "bridge-heartbeat", "bridge-restart", "bridge-visibility", "bridge-rename", "bridge-route", "bridge-stop", "bridge-shortcuts", "controller-boundary", "recovery-journal"],
67
68
  token: [...Buffer.from(this.#options.credentials.token, "base64")], tabId: this.#options.credentials.tabId,
68
69
  pid: this.#options.pid ?? process.pid, startIdentity: this.#options.startIdentity ?? `${process.pid}:node`,
69
70
  }, ++this.#request);
@@ -102,6 +103,16 @@ export class TabBridgeClient {
102
103
  prompt(interrupted) {
103
104
  this.#send("bridge.prompt", { interrupted });
104
105
  }
106
+ /** Publishes the effective tab shortcut assignments so the host dispatches and labels the same keys. */
107
+ shortcuts(assignments) {
108
+ this.#latestShortcuts = Object.entries(assignments).map(([action, key]) => [action, key]);
109
+ this.#sendShortcuts();
110
+ }
111
+ #sendShortcuts() {
112
+ if (this.#latestShortcuts === null || !this.#supports("bridge-shortcuts"))
113
+ return;
114
+ this.#send("bridge.shortcuts", { assignments: this.#latestShortcuts.map(([action, key]) => [action, key]) });
115
+ }
105
116
  requestDetach(origin = this.captureAttachment(), timeoutMs = 1_000) {
106
117
  return this.#requestAction("detach", origin, timeoutMs);
107
118
  }
@@ -191,6 +202,7 @@ export class TabBridgeClient {
191
202
  this.status(this.#latestStatus.state, this.#latestStatus.degraded, this.#latestStatus.pendingRequests, this.#latestStatus.queuedInput);
192
203
  if (this.#latestSession !== null)
193
204
  this.#send("bridge.session", this.#latestSession);
205
+ this.#sendShortcuts();
194
206
  return;
195
207
  }
196
208
  if (message.type === "incompatible-generation") {
@@ -206,6 +218,10 @@ export class TabBridgeClient {
206
218
  this.#emitCommand({ type: "rename", name: message.body["name"], nameSource: source === "auto" || source === "default" ? source : "user" });
207
219
  return;
208
220
  }
221
+ if (message.type === "bridge.route" && message.body["route"] === "settings") {
222
+ this.#emitCommand({ type: "route", route: "settings" });
223
+ return;
224
+ }
209
225
  if (message.type === "bridge.stop" && typeof message.body["deadlineMs"] === "number") {
210
226
  this.#emitCommand({ type: "stop", deadlineMs: message.body["deadlineMs"] });
211
227
  return;
@@ -8,7 +8,6 @@ export interface SubmittedPromptRecord {
8
8
  readonly fileExisted: boolean;
9
9
  readonly kind: "prompt" | "steer" | "follow-up";
10
10
  readonly text: string;
11
- readonly images?: readonly unknown[];
12
11
  readonly at: string;
13
12
  }
14
13
  export interface CorrelatedPromptRecord {
@@ -33,7 +32,6 @@ export interface PromptSubmission {
33
32
  readonly fileExisted: boolean;
34
33
  readonly kind: SubmittedPromptRecord["kind"];
35
34
  readonly text: string;
36
- readonly images?: readonly unknown[];
37
35
  }
38
36
  export interface SessionJournalEntry {
39
37
  readonly entryId: string;
@@ -30,7 +30,6 @@ export class PromptJournal {
30
30
  type: "submitted",
31
31
  id: randomUUID(),
32
32
  ...submission,
33
- ...(submission.images === undefined ? {} : { images: submission.images }),
34
33
  at: new Date().toISOString(),
35
34
  };
36
35
  await this.#append(record, "journal-before-append", "journal-after-append", "journal-after-sync");
@@ -13,6 +13,7 @@ export interface OwnedEditorUxInterceptor {
13
13
  reset(): void;
14
14
  handlePointer?(event: OwnedEditorPointerEvent): boolean;
15
15
  hasSelection?(): boolean;
16
+ selectedText?(): string | undefined;
16
17
  ownsPointer?(): boolean;
17
18
  pasteClipboard?(): boolean;
18
19
  cancelPendingPastes?(): void;
@@ -40,6 +41,7 @@ export declare class OwnedEditorUxInterception {
40
41
  cancelPendingPastes(): void;
41
42
  handlePointer(event: OwnedEditorPointerEvent): boolean;
42
43
  hasSelection(): boolean;
44
+ selectedText(): string | undefined;
43
45
  ownsPointer(): boolean;
44
46
  pasteClipboard(): boolean;
45
47
  }
@@ -39,6 +39,14 @@ export class OwnedEditorUxInterception {
39
39
  hasSelection() {
40
40
  return this.interceptors.some(interceptor => interceptor.hasSelection?.() === true);
41
41
  }
42
+ selectedText() {
43
+ for (const interceptor of this.interceptors) {
44
+ const selected = interceptor.selectedText?.();
45
+ if (selected !== undefined)
46
+ return selected;
47
+ }
48
+ return undefined;
49
+ }
42
50
  ownsPointer() {
43
51
  return this.interceptors.some(interceptor => interceptor.ownsPointer?.() === true);
44
52
  }
@@ -281,6 +289,11 @@ class PromptSelectionInterceptor {
281
289
  hasSelection() {
282
290
  return this.#activeRange() !== undefined;
283
291
  }
292
+ selectedText() {
293
+ return this.#activeRange() === undefined
294
+ ? undefined
295
+ : this.options.expandCopiedText(this.#selectedText());
296
+ }
284
297
  pasteClipboard() {
285
298
  this.#pasteFromClipboard();
286
299
  return true;
@@ -346,6 +346,7 @@ export function createPiShellEditor(options) {
346
346
  return editor.canPresentPromptSuggestion() ? null : "prompt-mode";
347
347
  },
348
348
  hasSelection: () => editorUx?.hasSelection() ?? false,
349
+ selectedText: () => editorUx?.selectedText(),
349
350
  ownsPointer: () => editorUx?.ownsPointer() ?? false,
350
351
  handlePointer: event => editorUx?.handlePointer(event) ?? false,
351
352
  pasteClipboard: () => editorUx?.pasteClipboard() ?? false,
@@ -73,6 +73,7 @@ export interface PiShellHotkeysPresentation {
73
73
  export declare function createPiShellHotkeys(bindings?: KeybindingsConfig, getShortcuts?: NonNullable<PiShellExtensionRendererResolver["getShortcuts"]>, profile?: "pi" | "a1", residentTabs?: boolean): PiShellComponentPort;
74
74
  /** The keyboard-shortcut document rows the feed presenter shows, without its spacer, borders, and heading. */
75
75
  export declare function renderPiShellHotkeysLines(presentation: PiShellHotkeysPresentation, width: number): readonly string[];
76
+ /** The global tab declarations the hotkeys document lists, straight from the feature's declarations. */
76
77
  export declare const TAB_HOTKEYS: readonly (readonly [string, string | null, string])[];
77
78
  export declare function hotkeysMarkdown(bindings: KeybindingsConfig | undefined, getShortcuts: NonNullable<PiShellExtensionRendererResolver["getShortcuts"]>, profile: "pi" | "a1", residentTabs?: boolean): string;
78
79
  /**
@@ -1,6 +1,7 @@
1
1
  import { DynamicBorder } from "../startup-public.js";
2
2
  import { Container, getCapabilities, hyperlink, Markdown, Spacer, Text, visibleWidth } from "@earendil-works/pi-tui";
3
3
  import { PRODUCT_TEXT } from "../../../product-identity.js";
4
+ import { projectResidentTabShortcuts, RESIDENT_TAB_GLOBAL_SHORTCUTS } from "../../../features/resident-tab/index.js";
4
5
  import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
5
6
  import { getPiHotkeysMarkdownTheme, getPiMarkdownTheme, paintPiBorder, PINNED_PI_LAYOUT, piTheme } from "./theme.js";
6
7
  import { componentPort, ensureTheme, formatSessionTokens } from "./shell-shared-facade.js";
@@ -167,14 +168,8 @@ export function renderPiShellHotkeysLines(presentation, width) {
167
168
  function hotkeysMarkdownComponent(markdown) {
168
169
  return new Markdown(markdown, 1, 1, getPiHotkeysMarkdownTheme());
169
170
  }
170
- // Performance: keep startup presentation independent from the registry; parity is contract-tested.
171
- export const TAB_HOTKEYS = [
172
- ["tabs.new", "alt+a", "New A1 tab"], ["tabs.close", "alt+w", "Close A1 tab"],
173
- ["tabs.rename", "f2", "Rename A1 tab"],
174
- ...Array.from({ length: 10 }, (_, index) => [`tabs.jump${index + 1}`, `alt+${(index + 1) % 10}`, `View A1 tab ${index + 1}`]),
175
- ["tabs.next", null, "View next A1 tab"], ["tabs.previous", null, "View previous A1 tab"],
176
- ["tabs.moveRight", null, "Move A1 tab right"], ["tabs.moveLeft", null, "Move A1 tab left"],
177
- ];
171
+ /** The global tab declarations the hotkeys document lists, straight from the feature's declarations. */
172
+ export const TAB_HOTKEYS = RESIDENT_TAB_GLOBAL_SHORTCUTS.map(shortcut => [shortcut.id, shortcut.defaultKey, shortcut.description]);
178
173
  export function hotkeysMarkdown(bindings, getShortcuts, profile, residentTabs = false) {
179
174
  const keys = profile === "a1" ? KeybindingsManager.fromOwnedBindings(bindings) : new KeybindingsManager(bindings);
180
175
  // Compatibility: the owned viewport consumes these physical chords before editor actions.
@@ -189,29 +184,14 @@ export function hotkeysMarkdown(bindings, getShortcuts, profile, residentTabs =
189
184
  }).join(" / ")} | ${description} |`;
190
185
  let markdown = ["**Navigation**", "| Key | Action |", "|-----|--------|", row(["tui.editor.cursorUp", "tui.editor.cursorDown", "tui.editor.cursorLeft", "tui.editor.cursorRight"], "Move cursor / browse history"), row(["tui.editor.cursorWordLeft", "tui.editor.cursorWordRight"], "Move by word"), row(["tui.editor.cursorLineStart"], profile === "a1" ? "Start of prompt line" : "Start of line"), row(["tui.editor.cursorLineEnd"], profile === "a1" ? "End of prompt line" : "End of line"), ...(profile === "a1" ? ["| `Ctrl+Home` | Start of content |", "| `Ctrl+End` | End of content / follow output |"] : []), row(["tui.editor.jumpForward"], "Jump forward to character"), row(["tui.editor.jumpBackward"], "Jump backward to character"), row(["tui.editor.pageUp", "tui.editor.pageDown"], "Scroll by page"), "", "**Editing**", "| Key | Action |", "|-----|--------|", row(["tui.input.submit"], "Send message"), row(["tui.input.newLine"], `New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""}`), row(["tui.editor.deleteWordBackward"], "Delete word backwards"), row(["tui.editor.deleteWordForward"], "Delete word forwards"), row(["tui.editor.deleteToLineStart"], "Delete to start of line"), row(["tui.editor.deleteToLineEnd"], "Delete to end of line"), row(["tui.editor.yank"], "Paste the most-recently-deleted text"), row(["tui.editor.yankPop"], "Cycle through the deleted text after pasting"), row(["tui.editor.undo"], "Undo"), "", "**Other**", "| Key | Action |", "|-----|--------|", row(["tui.input.tab"], "Path completion / accept autocomplete"), row(["app.interrupt"], "Cancel autocomplete / abort streaming"), row(["app.clear"], "Clear editor (first) / exit (second)"), row(["app.exit"], "Exit (when editor is empty)"), row(["app.suspend"], "Suspend to background"), row(["app.thinking.cycle"], "Cycle thinking level"), row(["app.model.cycleForward", "app.model.cycleBackward"], "Cycle models"), row(["app.model.select"], profile === "a1" ? "Open the Models dialog" : "Open model selector"), row(["app.tools.expand"], "Toggle tool output expansion"), row(["app.thinking.toggle"], "Toggle thinking block visibility"), row(["app.editor.external"], "Edit message in external editor"), row(["app.message.copy"], "Copy selection or last assistant message"), row(["app.message.followUp"], "Queue follow-up message"), row(["app.message.dequeue"], "Restore queued messages"), row(["app.clipboard.pasteImage"], profile === "a1" ? "Paste image or text from clipboard" : "Paste files on macOS, images, or text from clipboard"), "| `/` | Slash commands |", "| `!` | Run bash command |", "| `!!` | Run bash command (excluded from context) |", ...(profile === "a1" ? ["", "**Models dialog**", "| Key | Action |", "|-----|--------|", "| `Space` | Toggle the selected model in the cycling scope |", "| `Tab` | Switch the all/scoped filter |", row(["app.models.save"], "Save the scope to settings"), row(["app.models.enableAll"], "Scope every listed model"), row(["app.models.clearAll"], "Clear the listed models from the scope"), row(["app.models.toggleProvider"], "Toggle the selected model's provider"), row(["app.models.reorderUp", "app.models.reorderDown"], "Reorder the cycling scope")] : [])].join("\n");
191
186
  if (profile === "a1" && residentTabs) {
192
- const configured = (bindings ?? {});
187
+ // Invariant: the Tabs section shows the same projection the resident shell dispatches: the
188
+ // effective A1 declarations keep their keys and earlier tab declarations keep duplicates.
189
+ const projection = projectResidentTabShortcuts({ configured: bindings, reserved: keys.getEffectiveConfig() });
193
190
  markdown += "\n\n**Tabs**\n| Key | Action |\n|-----|--------|\n";
194
- const claimed = new Set();
195
- for (const [action, value] of Object.entries(configured)) {
196
- if (action.startsWith("tabs."))
197
- continue;
198
- const keys = typeof value === "string" ? [value] : Array.isArray(value) ? value : [];
199
- for (const key of keys)
200
- if (typeof key === "string")
201
- claimed.add(key.toLowerCase());
202
- }
203
- markdown += TAB_HOTKEYS.map(([id, defaultKey, description]) => {
204
- const value = configured[id];
205
- const assigned = (typeof value === "string" ? [value] : Array.isArray(value) ? value.filter((key) => typeof key === "string") : defaultKey === null ? [] : [defaultKey])
206
- .filter(key => {
207
- const normalized = key.toLowerCase();
208
- if (claimed.has(normalized))
209
- return false;
210
- claimed.add(normalized);
211
- return true;
212
- });
213
- const label = assigned.length === 0 ? "Unbound" : `\`${assigned.map(shortcutDisplay).join("/")}\``;
214
- return `| ${label} | ${description} |`;
191
+ markdown += RESIDENT_TAB_GLOBAL_SHORTCUTS.map(shortcut => {
192
+ const assigned = projection.assignments[shortcut.id];
193
+ const label = assigned === null ? "Unbound" : `\`${shortcutDisplay(assigned)}\``;
194
+ return `| ${label} | ${shortcut.description} |`;
215
195
  }).join("\n");
216
196
  }
217
197
  const shortcuts = getShortcuts(bindings ?? keys.getEffectiveConfig());
@@ -64,6 +64,8 @@ export interface PiShellEditorPort extends PiShellComponentPort {
64
64
  canPresentPromptSuggestion(): boolean;
65
65
  promptSuggestionBlockReason?(): SuggestionDecision;
66
66
  hasSelection(): boolean;
67
+ /** Exact expanded semantic editor selection without mutating or collapsing it. */
68
+ selectedText?(): string | undefined;
67
69
  ownsPointer(): boolean;
68
70
  handlePointer(event: PiShellEditorPointerEvent): boolean;
69
71
  pasteClipboard(): boolean;
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:44.665Z",
8
+ "builtAt": "2026-10-17T13:02:46.502Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "47e9052599983a6bb0078ce6203ba18d71bec0b7b94e6a49019d11d46c3fbeca",
@@ -15,6 +15,7 @@
15
15
  "language": "Rust",
16
16
  "cargoLock": "native/process-guardian/Cargo.lock",
17
17
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
18
+ "signatureStatus": "ci-attestation-required",
19
+ "identityCheck": "executed"
19
20
  }
20
21
  }
@@ -7,16 +7,16 @@
7
7
  "architecture": "arm64",
8
8
  "target": "aarch64-apple-darwin",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:12:28.897Z",
10
+ "builtAt": "2026-10-17T13:07:42.805Z",
11
11
  "artifact": {
12
12
  "filename": "terminal-host",
13
- "sha256": "9996ac4e901bc35df352567d120d6bb7b6a8b3451e2841be27e0b77f71a2f046",
14
- "size": 4893504
13
+ "sha256": "da3e4930eb0249992da208ac2ab8f38f882388e810a7bcc0c48152f03b245129",
14
+ "size": 5337024
15
15
  },
16
16
  "sessionLock": {
17
17
  "filename": "a1_session_lock.node",
18
- "sha256": "3c70e041ed400e8d4f543adcd4910c7681d2498aac4ffc7a06ba8114dab0d290",
19
- "size": 561200
18
+ "sha256": "44682ec58d0de84a447535fa579fa847ffbc332a446cc60fd02e52b73716a03e",
19
+ "size": 577680
20
20
  },
21
21
  "notices": {
22
22
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
@@ -39,6 +39,7 @@
39
39
  "node-addon"
40
40
  ]
41
41
  },
42
- "signatureStatus": "ci-attestation-required"
42
+ "signatureStatus": "ci-attestation-required",
43
+ "identityCheck": "executed"
43
44
  }
44
45
  }
@@ -5,16 +5,17 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:54.598Z",
8
+ "builtAt": "2026-10-17T13:02:52.732Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
- "sha256": "ccc1272de81b06efa5e590a88dbafe30726a0fe33e0808025a6602236e5d2855",
12
- "size": 394776
11
+ "sha256": "80ac279b688fe2642c43a40669ff35afc478fc5a8bda7f6cc2ac88a98431a4ce",
12
+ "size": 394768
13
13
  },
14
14
  "provenance": {
15
15
  "language": "Rust",
16
16
  "cargoLock": "native/process-guardian/Cargo.lock",
17
17
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
18
+ "signatureStatus": "ci-attestation-required",
19
+ "identityCheck": "executed"
19
20
  }
20
21
  }
@@ -7,16 +7,16 @@
7
7
  "architecture": "x64",
8
8
  "target": "x86_64-apple-darwin",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:24:32.295Z",
10
+ "builtAt": "2026-10-17T13:11:38.751Z",
11
11
  "artifact": {
12
12
  "filename": "terminal-host",
13
- "sha256": "b265f3b4ce17f06b984278cffa461ee9d77dd47641b3d66e824251d6d08157b6",
14
- "size": 5335192
13
+ "sha256": "d1b524a5bd2da42a36e40f9b2e7c27b1d0143ecff88982fea9a20ac0367472e8",
14
+ "size": 5815872
15
15
  },
16
16
  "sessionLock": {
17
17
  "filename": "a1_session_lock.node",
18
- "sha256": "059de5460e5333353bc6c541e2ae577d72c201493f7e8081a65730af251d626e",
19
- "size": 516280
18
+ "sha256": "d1418e81a3c8f48139b90aff322d2d860eab3e033cb8d25ff7760cc9e3331555",
19
+ "size": 516344
20
20
  },
21
21
  "notices": {
22
22
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
@@ -39,6 +39,7 @@
39
39
  "node-addon"
40
40
  ]
41
41
  },
42
- "signatureStatus": "ci-attestation-required"
42
+ "signatureStatus": "ci-attestation-required",
43
+ "identityCheck": "executed"
43
44
  }
44
45
  }
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:19.616Z",
8
+ "builtAt": "2026-10-17T13:02:43.567Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "94a96fa9d5178a1949d05c6d6cc9504f3030cd49a096cd9db20dd29860ea5884",
@@ -15,6 +15,7 @@
15
15
  "language": "Rust",
16
16
  "cargoLock": "native/process-guardian/Cargo.lock",
17
17
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
18
+ "signatureStatus": "ci-attestation-required",
19
+ "identityCheck": "executed"
19
20
  }
20
21
  }
Binary file
@@ -7,16 +7,16 @@
7
7
  "architecture": "x64",
8
8
  "target": "x86_64-unknown-linux-gnu",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:11:30.623Z",
10
+ "builtAt": "2026-10-17T13:05:45.852Z",
11
11
  "artifact": {
12
12
  "filename": "terminal-host",
13
- "sha256": "694d149d49e5f0ceaeb429fbfdbd9710bdad32b4932ee754f48400f1514fcabc",
14
- "size": 5901664
13
+ "sha256": "9be29443a6ea4ce4718a93539adf9fb38dd6fdc0beb110485cd01a1fc93e5918",
14
+ "size": 6435192
15
15
  },
16
16
  "sessionLock": {
17
17
  "filename": "a1_session_lock.node",
18
- "sha256": "2c47291fae93329dea2bc774fbcd823e83f16026e43333ee3ed3336c45df7ce2",
19
- "size": 726016
18
+ "sha256": "bae1affd0d1ce906258afbc97f762796630c73ab314846d10b6a5bd0fe63f54f",
19
+ "size": 740560
20
20
  },
21
21
  "notices": {
22
22
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
@@ -39,6 +39,7 @@
39
39
  "node-addon"
40
40
  ]
41
41
  },
42
- "signatureStatus": "ci-attestation-required"
42
+ "signatureStatus": "ci-attestation-required",
43
+ "identityCheck": "executed"
43
44
  }
44
45
  }
@@ -5,16 +5,17 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:09:00.247Z",
8
+ "builtAt": "2026-10-17T13:03:20.189Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "22cad9088f307ea7c98eb992e56f0a9bda111ea14d42d478f1a6ec0216da75d4",
11
+ "sha256": "231de38cbd11f08c493027290c523c589d8ffbfa61d9a8e6b55c2300ed50dbad",
12
12
  "size": 288256
13
13
  },
14
14
  "provenance": {
15
15
  "language": "Rust",
16
16
  "cargoLock": "native/process-guardian/Cargo.lock",
17
17
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
18
+ "signatureStatus": "ci-attestation-required",
19
+ "identityCheck": "executed"
19
20
  }
20
21
  }
@@ -7,15 +7,15 @@
7
7
  "architecture": "x64",
8
8
  "target": "x86_64-pc-windows-msvc",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:13:44.310Z",
10
+ "builtAt": "2026-10-17T13:09:09.866Z",
11
11
  "artifact": {
12
12
  "filename": "terminal-host.exe",
13
- "sha256": "8a75c5c5d1ca1b7e5d1e42eef4342f76037fc6d2962c2ca6f00c78abb6a7693e",
14
- "size": 5219840
13
+ "sha256": "0b6770b00388985dc7e65e6f8fffc93eec60f85621462e388c8b25d30a0bf3c8",
14
+ "size": 5740544
15
15
  },
16
16
  "sessionLock": {
17
17
  "filename": "a1_session_lock.node",
18
- "sha256": "41e23a5a2d65943bfc82fca6f9f3eb0b3169dcbe7181dbc848de015675639790",
18
+ "sha256": "a4f2407b6166aa5e12b07548c286acf2e9a744a1f58fef2bfcab2cddbad1dca1",
19
19
  "size": 1996288
20
20
  },
21
21
  "notices": {
@@ -39,6 +39,7 @@
39
39
  "node-addon"
40
40
  ]
41
41
  },
42
- "signatureStatus": "ci-attestation-required"
42
+ "signatureStatus": "ci-attestation-required",
43
+ "identityCheck": "executed"
43
44
  }
44
45
  }
@@ -32,8 +32,8 @@ export { readVisibleHyperlinks } from "./visible-hyperlinks.js";
32
32
  export type { VisibleHyperlinkRange, VisibleHyperlinkRow } from "./visible-hyperlinks.js";
33
33
  export { renderStatusLine, statusText } from "./status-line.js";
34
34
  export type { StatusLineInput } from "./status-line.js";
35
- export { SUBMITTED_PROMPT_PREFIX_WIDTH, composeSubmittedPromptRows, formatSubmittedPromptTime, submittedPromptLayout } from "./submitted-prompt.js";
36
- export type { SubmittedPromptLayout, SubmittedPromptStyle } from "./submitted-prompt.js";
35
+ export { SUBMITTED_PROMPT_PREFIX_WIDTH, composeSubmittedPromptRows, formatLocalTime, formatSubmittedPromptTime, submittedPromptLayout } from "./submitted-prompt.js";
36
+ export type { SubmittedPromptLayout, SubmittedPromptStyle, UiTimeFormat } from "./submitted-prompt.js";
37
37
  export { numericValues, steppedValue, stepperEnds } from "./stepper.js";
38
38
  export type { NumericRange } from "./stepper.js";
39
39
  export { TranscriptViewport, assertTranscriptViewportFrameDescriptor } from "./transcript-viewport.js";
@@ -16,7 +16,7 @@ export { GLOBAL_SCOPE, ShortcutRegistry, assembleShortcuts, assertNoShortcutConf
16
16
  export { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, hyperlinkTargetAtColumn, nativeHyperlinkStyle, overlaySpan } from "./spans.js";
17
17
  export { readVisibleHyperlinks } from "./visible-hyperlinks.js";
18
18
  export { renderStatusLine, statusText } from "./status-line.js";
19
- export { SUBMITTED_PROMPT_PREFIX_WIDTH, composeSubmittedPromptRows, formatSubmittedPromptTime, submittedPromptLayout } from "./submitted-prompt.js";
19
+ export { SUBMITTED_PROMPT_PREFIX_WIDTH, composeSubmittedPromptRows, formatLocalTime, formatSubmittedPromptTime, submittedPromptLayout } from "./submitted-prompt.js";
20
20
  export { numericValues, steppedValue, stepperEnds } from "./stepper.js";
21
21
  export { TranscriptViewport, assertTranscriptViewportFrameDescriptor } from "./transcript-viewport.js";
22
22
  export { RAIL_COLUMNS, renderEmptyState, withScrollbarRail } from "./surface.js";
@@ -20,5 +20,7 @@ export interface SelectionCopySnapshot {
20
20
  }
21
21
  /** Shares the viewport's semantic chrome exclusions with isolated copy preparation. */
22
22
  export declare function selectionCopyLineContent(text: string, prompt: SelectionCopyPrompt): TextSelectionLineContent;
23
+ /** Builds an immutable exact-text snapshot without changing editor-owned selection state. */
24
+ export declare function literalSelectionCopySnapshot(text: string, revision?: number): SelectionCopySnapshot | null;
23
25
  /** Extracts one snapshot row with the existing grapheme/newline policy; run expensive rows off-thread. */
24
26
  export declare function selectionCopyRowText(snapshot: Pick<SelectionCopySnapshot, "selection" | "literal">, row: SelectionCopyRow, index: number): string;
@@ -19,6 +19,19 @@ export function selectionCopyLineContent(text, prompt) {
19
19
  return { from, to: from + 1 };
20
20
  return usefulTextLineContent(plain, from);
21
21
  }
22
+ /** Builds an immutable exact-text snapshot without changing editor-owned selection state. */
23
+ export function literalSelectionCopySnapshot(text, revision = 0) {
24
+ if (text.trim().length === 0)
25
+ return null;
26
+ const selection = {
27
+ start: { line: 0, column: 0 },
28
+ end: { line: 0, column: Number.MAX_SAFE_INTEGER },
29
+ };
30
+ if (text.length > MAX_COPY_SOURCE_UNITS) {
31
+ return { literal: true, selection, revision, rows: [], sourceUnits: 0, rejected: "size" };
32
+ }
33
+ return { literal: true, selection, revision, rows: [{ text }], sourceUnits: text.length };
34
+ }
22
35
  /** Extracts one snapshot row with the existing grapheme/newline policy; run expensive rows off-thread. */
23
36
  export function selectionCopyRowText(snapshot, row, index) {
24
37
  if (snapshot.literal)
@@ -1,3 +1,6 @@
1
+ export type UiTimeFormat = "20:00" | "8:00 PM";
2
+ /** Deterministic local hour/minute presentation shared by A1-owned time labels. */
3
+ export declare function formatLocalTime(source: Date | number, format?: UiTimeFormat): string | null;
1
4
  /** Visible width of the submitted-prompt prefix (`❯ `). */
2
5
  export declare const SUBMITTED_PROMPT_PREFIX_WIDTH = 2;
3
6
  export interface SubmittedPromptStyle {
@@ -8,16 +11,16 @@ export interface SubmittedPromptLayout {
8
11
  readonly contentWidth: number;
9
12
  readonly timestamp: string | null;
10
13
  }
11
- /** Formats transcript metadata, never render time, as local 24-hour HH:mm. */
12
- export declare function formatSubmittedPromptTime(source: Date | number): string | null;
14
+ /** Formats transcript metadata, never render time, in the active A1 time format. */
15
+ export declare function formatSubmittedPromptTime(source: Date | number, format?: UiTimeFormat): string | null;
13
16
  /**
14
17
  * Computes the width at which the prompt's Markdown body must be rendered.
15
18
  * The timestamp is omitted rather than allowing it to crowd useful content.
16
19
  */
17
- export declare function submittedPromptLayout(width: number, source?: Date | number | null): SubmittedPromptLayout;
20
+ export declare function submittedPromptLayout(width: number, source?: Date | number | null, format?: UiTimeFormat): SubmittedPromptLayout;
18
21
  /**
19
22
  * Composes already-rendered prompt content into the compact submitted bar.
20
23
  * Continuations align below the content, not below the prefix, and no row can
21
24
  * exceed the supplied terminal width even when the content renderer misbehaves.
22
25
  */
23
- export declare function composeSubmittedPromptRows(contentRows: readonly string[], width: number, source?: Date | number | null, style?: SubmittedPromptStyle): readonly string[];
26
+ export declare function composeSubmittedPromptRows(contentRows: readonly string[], width: number, source?: Date | number | null, style?: SubmittedPromptStyle, format?: UiTimeFormat): readonly string[];
@@ -1,22 +1,30 @@
1
1
  import { displayWidth, truncateToWidth } from "./text.js";
2
+ /** Deterministic local hour/minute presentation shared by A1-owned time labels. */
3
+ export function formatLocalTime(source, format = "8:00 PM") {
4
+ const date = source instanceof Date ? source : new Date(source);
5
+ if (!Number.isFinite(date.getTime()))
6
+ return null;
7
+ const hour = date.getHours();
8
+ const minute = String(date.getMinutes()).padStart(2, "0");
9
+ if (format === "20:00")
10
+ return `${String(hour).padStart(2, "0")}:${minute}`;
11
+ return `${hour % 12 || 12}:${minute} ${hour < 12 ? "AM" : "PM"}`;
12
+ }
2
13
  /** Visible width of the submitted-prompt prefix (`❯ `). */
3
14
  export const SUBMITTED_PROMPT_PREFIX_WIDTH = 2;
4
15
  const TIMESTAMP_MARGIN = 3;
5
16
  const MIN_USEFUL_CONTENT_WIDTH = 8;
6
- /** Formats transcript metadata, never render time, as local 24-hour HH:mm. */
7
- export function formatSubmittedPromptTime(source) {
8
- const date = source instanceof Date ? source : new Date(source);
9
- if (!Number.isFinite(date.getTime()))
10
- return null;
11
- return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
17
+ /** Formats transcript metadata, never render time, in the active A1 time format. */
18
+ export function formatSubmittedPromptTime(source, format = "8:00 PM") {
19
+ return formatLocalTime(source, format);
12
20
  }
13
21
  /**
14
22
  * Computes the width at which the prompt's Markdown body must be rendered.
15
23
  * The timestamp is omitted rather than allowing it to crowd useful content.
16
24
  */
17
- export function submittedPromptLayout(width, source) {
25
+ export function submittedPromptLayout(width, source, format = "8:00 PM") {
18
26
  const boundedWidth = Math.max(1, width);
19
- const timestamp = source === undefined || source === null ? null : formatSubmittedPromptTime(source);
27
+ const timestamp = source === undefined || source === null ? null : formatSubmittedPromptTime(source, format);
20
28
  const timestampReserve = timestamp === null ? 0 : displayWidth(timestamp) + TIMESTAMP_MARGIN;
21
29
  const canShowTimestamp = timestamp !== null
22
30
  && boundedWidth - SUBMITTED_PROMPT_PREFIX_WIDTH - timestampReserve >= MIN_USEFUL_CONTENT_WIDTH;
@@ -30,10 +38,10 @@ export function submittedPromptLayout(width, source) {
30
38
  * Continuations align below the content, not below the prefix, and no row can
31
39
  * exceed the supplied terminal width even when the content renderer misbehaves.
32
40
  */
33
- export function composeSubmittedPromptRows(contentRows, width, source, style = {}) {
41
+ export function composeSubmittedPromptRows(contentRows, width, source, style = {}, format = "8:00 PM") {
34
42
  if (width <= 0)
35
43
  return [];
36
- const layout = submittedPromptLayout(width, source);
44
+ const layout = submittedPromptLayout(width, source, format);
37
45
  const prefix = (style.prefix ?? identity)("❯ ");
38
46
  const rows = (contentRows.length === 0 ? [""] : contentRows).map((row, index) => {
39
47
  const content = truncateToWidth(row, layout.contentWidth);