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

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 (135) hide show
  1. package/dist/app/session-shell/session-shell-root.d.ts +2 -1
  2. package/dist/app/session-shell/session-shell-root.js +41 -15
  3. package/dist/app/session-shell/session-shell.d.ts +1 -0
  4. package/dist/app/session-shell/session-shell.js +15 -4
  5. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
  6. package/dist/app/session-shell/session-viewport-controller.js +24 -12
  7. package/dist/cli/tabs.js +1 -1
  8. package/dist/composition/owned-ui.d.ts +2 -0
  9. package/dist/composition/owned-ui.js +42 -4
  10. package/dist/contracts/owned-ui/index.d.ts +1 -1
  11. package/dist/contracts/owned-ui/model.d.ts +3 -0
  12. package/dist/features/owned-ui/settings-app.js +9 -6
  13. package/dist/features/resident-tab/index.d.ts +3 -1
  14. package/dist/features/resident-tab/index.js +5 -1
  15. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
  16. package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
  17. package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
  18. package/dist/features/resident-tab/{shortcuts.js → shortcut-declarations.js} +6 -17
  19. package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
  20. package/dist/features/resident-tab/shortcut-projection.js +64 -0
  21. package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
  22. package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
  23. package/dist/foundation/launch-guardian/main.d.ts +2 -1
  24. package/dist/foundation/launch-guardian/main.js +58 -6
  25. package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
  26. package/dist/foundation/process-containment/addon-containment.js +139 -0
  27. package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
  28. package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
  29. package/dist/foundation/process-containment/addon.d.ts +42 -0
  30. package/dist/foundation/process-containment/addon.js +57 -0
  31. package/dist/foundation/process-containment/artifact.d.ts +1 -1
  32. package/dist/foundation/process-containment/artifact.js +6 -5
  33. package/dist/foundation/process-containment/contracts.d.ts +2 -0
  34. package/dist/foundation/process-containment/index.d.ts +4 -4
  35. package/dist/foundation/process-containment/index.js +3 -3
  36. package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
  37. package/dist/foundation/process-containment/native-process-inspector.js +10 -10
  38. package/dist/foundation/release/bootstrap.js +54 -16
  39. package/dist/foundation/release/index.d.ts +1 -1
  40. package/dist/foundation/release/index.js +1 -1
  41. package/dist/foundation/release/release-store.d.ts +12 -0
  42. package/dist/foundation/release/release-store.js +124 -2
  43. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +3 -1
  44. package/dist/foundation/resident-tabs/bounded-host-command.js +6 -2
  45. package/dist/foundation/resident-tabs/bridge-client.d.ts +7 -0
  46. package/dist/foundation/resident-tabs/bridge-client.js +30 -1
  47. package/dist/foundation/resident-tabs/doctor.d.ts +0 -1
  48. package/dist/foundation/resident-tabs/doctor.js +4 -4
  49. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +7 -2
  50. package/dist/foundation/resident-tabs/ensure-terminal-host.js +15 -11
  51. package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
  52. package/dist/foundation/resident-tabs/host-entry.js +17 -0
  53. package/dist/foundation/resident-tabs/index.d.ts +2 -2
  54. package/dist/foundation/resident-tabs/index.js +1 -1
  55. package/dist/foundation/resident-tabs/maintenance.d.ts +5 -4
  56. package/dist/foundation/resident-tabs/maintenance.js +32 -17
  57. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -2
  58. package/dist/foundation/resident-tabs/prompt-journal.js +0 -1
  59. package/dist/foundation/resident-tabs/release-holds.d.ts +3 -2
  60. package/dist/foundation/resident-tabs/release-holds.js +16 -7
  61. package/dist/foundation/resident-tabs/settings.d.ts +0 -1
  62. package/dist/foundation/resident-tabs/settings.js +1 -7
  63. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +11 -15
  64. package/dist/foundation/resident-tabs/terminal-host-artifact.js +57 -32
  65. package/dist/foundation/session-writer-lock/index.d.ts +1 -3
  66. package/dist/foundation/session-writer-lock/index.js +8 -6
  67. package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -0
  68. package/dist/integrations/pi/components/owned-editor-ux.js +13 -0
  69. package/dist/integrations/pi/components/shell-editor-autocomplete.js +3 -1
  70. package/dist/integrations/pi/components/shell-presenters-info.d.ts +1 -0
  71. package/dist/integrations/pi/components/shell-presenters-info.js +10 -30
  72. package/dist/integrations/pi/components/shell-shared-facade.d.ts +4 -0
  73. package/dist/native/darwin-arm64/manifest.json +10 -7
  74. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  75. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  76. package/dist/native/darwin-arm64/terminal-host.manifest.json +16 -14
  77. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  78. package/dist/native/darwin-x64/manifest.json +10 -7
  79. package/dist/native/darwin-x64/process-guardian.node +0 -0
  80. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  81. package/dist/native/darwin-x64/terminal-host.manifest.json +16 -14
  82. package/dist/native/darwin-x64/terminal-host.node +0 -0
  83. package/dist/native/linux-x64/manifest.json +10 -7
  84. package/dist/native/linux-x64/process-guardian.node +0 -0
  85. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  86. package/dist/native/linux-x64/terminal-host.manifest.json +16 -14
  87. package/dist/native/linux-x64/terminal-host.node +0 -0
  88. package/dist/native/win32-x64/manifest.json +10 -7
  89. package/dist/native/win32-x64/process-guardian.node +0 -0
  90. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  91. package/dist/native/win32-x64/terminal-host.manifest.json +16 -14
  92. package/dist/native/win32-x64/terminal-host.node +0 -0
  93. package/dist/product-identity.d.ts +1 -1
  94. package/dist/product-identity.js +1 -1
  95. package/dist/product-identity.json +3 -2
  96. package/dist/runtime-payload-inventory.json +1 -0
  97. package/dist/ui/components/index.d.ts +2 -2
  98. package/dist/ui/components/index.js +1 -1
  99. package/dist/ui/components/selection-copy.d.ts +2 -0
  100. package/dist/ui/components/selection-copy.js +13 -0
  101. package/dist/ui/components/submitted-prompt.d.ts +7 -4
  102. package/dist/ui/components/submitted-prompt.js +18 -10
  103. package/dist/ui/components/transcript-viewport.d.ts +10 -6
  104. package/dist/ui/components/transcript-viewport.js +28 -13
  105. package/dist/ui/settings/declarations.d.ts +13 -13
  106. package/dist/ui/settings/declarations.js +12 -11
  107. package/dist/ui/settings/migrations.js +7 -0
  108. package/docs/architecture/boundaries.md +10 -9
  109. package/docs/architecture/evidence/resident-tabs/macos-x64.md +1 -1
  110. package/docs/architecture/process-guardian-provenance.md +17 -11
  111. package/docs/architecture/project-structure.md +8 -0
  112. package/docs/architecture/resident-tabs.md +54 -36
  113. package/docs/architecture/terminal-host-provenance.md +6 -4
  114. package/docs/architecture/toolchain.md +6 -6
  115. package/docs/openspec-archive-automation.md +3 -3
  116. package/docs/validation.md +7 -3
  117. package/package.json +1 -1
  118. package/dist/features/resident-tab/shortcuts.d.ts +0 -10
  119. package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
  120. package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
  121. package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
  122. package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
  123. package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
  124. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  125. package/dist/native/darwin-arm64/process-guardian +0 -0
  126. package/dist/native/darwin-arm64/terminal-host +0 -0
  127. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  128. package/dist/native/darwin-x64/process-guardian +0 -0
  129. package/dist/native/darwin-x64/terminal-host +0 -0
  130. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  131. package/dist/native/linux-x64/process-guardian +0 -0
  132. package/dist/native/linux-x64/terminal-host +0 -0
  133. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  134. package/dist/native/win32-x64/process-guardian.exe +0 -0
  135. package/dist/native/win32-x64/terminal-host.exe +0 -0
@@ -1,53 +1,78 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFile, stat } from "node:fs/promises";
3
3
  import { basename, dirname, resolve } from "node:path";
4
+ import { PRODUCT_IDENTITY } from "../../product-identity.js";
4
5
  export const TERMINAL_HOST_MANIFEST_FILENAME = "terminal-host.manifest.json";
5
- export const TERMINAL_HOST_GENERATION = 1;
6
+ const SCHEMA = "a1-terminal-host-artifact-v2";
7
+ const GENERATION = 1;
8
+ const NODE_API = 9;
9
+ const NOTICES_NAME = "terminal-host-THIRD-PARTY-NOTICES.txt";
6
10
  export class TerminalHostArtifactError extends Error {
7
11
  code;
8
- constructor(message, code) {
12
+ manifestPath;
13
+ constructor(code, manifestPath, message) {
9
14
  super(message);
10
15
  this.name = "TerminalHostArtifactError";
11
16
  this.code = code;
17
+ this.manifestPath = manifestPath;
12
18
  }
13
19
  }
14
- /**
15
- * Verifies a release's terminal host and its session-writer lock against their manifest before
16
- * anything executes or loads them: schema, generation, platform, architecture, file names, sizes,
17
- * and SHA-256 digests.
18
- */
19
- export async function verifyTerminalHostArtifact(executable, platform = process.platform, architecture = process.arch, generation = TERMINAL_HOST_GENERATION) {
20
- const directory = dirname(executable);
20
+ /** Verifies every runtime file certified by the terminal-host v2 manifest before Node loads it. */
21
+ export async function verifyTerminalHostArtifact(addonPath, platform = process.platform, architecture = process.arch) {
22
+ const directory = dirname(addonPath);
23
+ const releaseRoot = resolve(directory, "../../..");
24
+ const manifestPath = resolve(directory, TERMINAL_HOST_MANIFEST_FILENAME);
21
25
  let manifest;
22
26
  try {
23
- manifest = JSON.parse(await readFile(resolve(directory, TERMINAL_HOST_MANIFEST_FILENAME), "utf8"));
27
+ const parsed = JSON.parse(await readFile(manifestPath, "utf8"));
28
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
29
+ throw new TypeError("terminal host manifest is not an object");
30
+ manifest = parsed;
24
31
  }
25
32
  catch (error) {
26
- throw new TerminalHostArtifactError(`terminal host manifest is missing or malformed: ${errorMessage(error)}`, "TERMINAL_HOST_ARTIFACT_MISSING");
33
+ const code = error instanceof Error && "code" in error && error.code === "ENOENT"
34
+ ? "TERMINAL_HOST_ARTIFACT_MISSING"
35
+ : "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE";
36
+ throw new TerminalHostArtifactError(code, manifestPath, "terminal host manifest is missing or invalid");
27
37
  }
28
- if (manifest.schema !== "a1-terminal-host-artifact-v1" || manifest.protocolVersion !== 1 || manifest.generation !== generation
29
- || manifest.platform !== platform || manifest.architecture !== architecture
30
- || manifest.artifact?.filename !== basename(executable) || typeof manifest.sessionLock?.filename !== "string"
31
- || basename(manifest.sessionLock.filename) !== manifest.sessionLock.filename) {
32
- throw new TerminalHostArtifactError("terminal host manifest is incompatible with this platform or generation", "TERMINAL_HOST_ARTIFACT_INCOMPATIBLE");
38
+ if (manifest.capability === "unsupported") {
39
+ throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_UNSUPPORTED", manifestPath, "terminal host is unsupported on this platform");
33
40
  }
34
- if (manifest.capability !== "supported") {
35
- throw new TerminalHostArtifactError(`resident tabs are not supported on ${platform}-${architecture}`, "TERMINAL_HOST_ARTIFACT_UNSUPPORTED");
41
+ if (manifest.schema !== SCHEMA || manifest.kind !== "node-addon" || manifest.nodeApi !== NODE_API
42
+ || manifest.protocolVersion !== 1 || manifest.generation !== GENERATION
43
+ || manifest.platform !== platform || manifest.architecture !== architecture || manifest.capability !== "supported"
44
+ || !validRecord(manifest.artifact) || !validRecord(manifest.entry) || !validRecord(manifest.notices)
45
+ || manifest.artifact.filename !== `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`
46
+ || manifest.entry.filename !== `dist/foundation/resident-tabs/${PRODUCT_IDENTITY.artifacts.terminalHostEntry}.js`
47
+ || manifest.notices.filename !== NOTICES_NAME
48
+ || basename(addonPath) !== manifest.artifact.filename) {
49
+ throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_INCOMPATIBLE", manifestPath, "terminal host manifest is incompatible");
36
50
  }
37
- const sessionLock = resolve(directory, manifest.sessionLock.filename);
38
- await verifyFile(executable, manifest.artifact, "terminal host");
39
- await verifyFile(sessionLock, manifest.sessionLock, "session-writer lock");
40
- return { executable, sessionLock, generation: manifest.generation };
51
+ const entry = resolve(releaseRoot, manifest.entry.filename);
52
+ await verifyFile(addonPath, manifest.artifact, manifestPath);
53
+ await verifyFile(entry, manifest.entry, manifestPath);
54
+ await verifyFile(resolve(directory, manifest.notices.filename), manifest.notices, manifestPath);
55
+ return { addon: addonPath, entry, generation: 1 };
41
56
  }
42
- async function verifyFile(path, expected, label) {
43
- const [bytes, metadata] = await Promise.all([readFile(path), stat(path)]).catch((error) => {
44
- throw new TerminalHostArtifactError(`${label} artifact is unavailable: ${errorMessage(error)}`, "TERMINAL_HOST_ARTIFACT_MISSING");
45
- });
46
- const digest = createHash("sha256").update(bytes).digest("hex");
47
- if (metadata.size !== expected.size || digest !== expected.sha256) {
48
- throw new TerminalHostArtifactError(`${label} artifact integrity verification failed`, "TERMINAL_HOST_ARTIFACT_TAMPERED");
49
- }
57
+ function validRecord(value) {
58
+ return typeof value === "object" && value !== null
59
+ && typeof value.filename === "string"
60
+ && /^[a-f0-9]{64}$/u.test(value.sha256)
61
+ && Number.isSafeInteger(value.size) && value.size >= 0;
50
62
  }
51
- function errorMessage(error) {
52
- return error instanceof Error ? error.message : String(error);
63
+ async function verifyFile(path, record, manifestPath) {
64
+ let bytes;
65
+ let metadata;
66
+ try {
67
+ [bytes, metadata] = await Promise.all([readFile(path), stat(path)]);
68
+ }
69
+ catch (error) {
70
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
71
+ throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_MISSING", manifestPath, `certified terminal host file is missing: ${record.filename}`);
72
+ }
73
+ throw error;
74
+ }
75
+ if (!metadata.isFile() || bytes.byteLength !== record.size || createHash("sha256").update(bytes).digest("hex") !== record.sha256) {
76
+ throw new TerminalHostArtifactError("TERMINAL_HOST_ARTIFACT_TAMPERED", manifestPath, `certified terminal host file failed verification: ${record.filename}`);
77
+ }
53
78
  }
@@ -19,9 +19,7 @@ export interface SessionWriterHandle {
19
19
  bind(createdPath?: string): void;
20
20
  release(): void;
21
21
  }
22
- /** Development-only override naming a native lock addon to load first. */
23
- export declare const SESSION_LOCK_OVERRIDE_ENVIRONMENT = "A1_SESSION_LOCK_PATH";
24
- /** Security: a release that carries its own lock never loads an addon chosen by the environment or cwd. */
22
+ /** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
25
23
  export declare function nativeCandidates(moduleUrl?: string, env?: NodeJS.ProcessEnv, cwd?: string, exists?: typeof existsSync): string[];
26
24
  export declare function acquireExisting(path: string): SessionWriterHandle;
27
25
  export declare function reserveNew(parent: string, name: string): SessionWriterHandle;
@@ -18,15 +18,17 @@ export class SessionWriterConflict extends Error {
18
18
  }
19
19
  let backendOverride;
20
20
  let cachedBackend;
21
- /** Development-only override naming a native lock addon to load first. */
22
- export const SESSION_LOCK_OVERRIDE_ENVIRONMENT = "A1_SESSION_LOCK_PATH";
23
- /** Security: a release that carries its own lock never loads an addon chosen by the environment or cwd. */
21
+ /** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
24
22
  export function nativeCandidates(moduleUrl = import.meta.url, env = process.env, cwd = process.cwd(), exists = existsSync) {
25
- const addon = "a1_session_lock.node";
23
+ const addon = `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`;
26
24
  // Invariant: this module sits at dist/foundation/session-writer-lock/ inside the release root.
27
25
  const own = resolve(fileURLToPath(new URL("../../../", moduleUrl)), "dist", "native", `${process.platform}-${process.arch}`, addon);
28
- const override = env[PRODUCT_IDENTITY.environment.developmentInstanceId] ? env[SESSION_LOCK_OVERRIDE_ENVIRONMENT]?.trim() || undefined : undefined;
29
- const local = exists(own) ? [own] : [resolve(cwd, "dist", "native", `${process.platform}-${process.arch}`, addon), resolve(cwd, ".artifacts", "terminal-host", addon)];
26
+ const override = env[PRODUCT_IDENTITY.environment.developmentInstanceId]
27
+ ? env[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim() || undefined
28
+ : undefined;
29
+ const local = exists(own)
30
+ ? [own]
31
+ : [resolve(cwd, "dist", "native", `${process.platform}-${process.arch}`, addon), resolve(cwd, ".artifacts", "terminal-host", addon)];
30
32
  return override ? [override, ...local] : local;
31
33
  }
32
34
  function loadNativeBackend() {
@@ -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;
@@ -143,7 +143,8 @@ export function createPiShellEditor(options) {
143
143
  tui.requestRender();
144
144
  };
145
145
  let autocompleteProvider;
146
- const builtInCatalog = options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS;
146
+ const builtInCatalog = (options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS)
147
+ .filter(command => !options.hideSettingsCommand || command.name !== "settings");
147
148
  const setAutocompleteCommands = (installed) => {
148
149
  // Invariant: collapse is a bare-A1 presentation; the comparison profile installs the pinned list.
149
150
  const collapsed = options.keybindingProfile === "a1" && options.skillsPresentation?.() === "collapse"
@@ -346,6 +347,7 @@ export function createPiShellEditor(options) {
346
347
  return editor.canPresentPromptSuggestion() ? null : "prompt-mode";
347
348
  },
348
349
  hasSelection: () => editorUx?.hasSelection() ?? false,
350
+ selectedText: () => editorUx?.selectedText(),
349
351
  ownsPointer: () => editorUx?.ownsPointer() ?? false,
350
352
  handlePointer: event => editorUx?.handlePointer(event) ?? false,
351
353
  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;
@@ -162,6 +164,8 @@ export interface PiShellEditorOptions {
162
164
  readonly historyEditor?: HistoryEditorConstructor;
163
165
  /** Bare A1 adds ergonomic aliases while comparison profiles retain Pi defaults. */
164
166
  readonly keybindingProfile?: "pi" | "a1";
167
+ /** Resident Agents expose Settings through the global tab instead of a slash command. */
168
+ readonly hideSettingsCommand?: boolean;
165
169
  /** The process's shared keybindings; its profile must match `keybindingProfile`. */
166
170
  readonly keybindings?: PiKeybindingsHost;
167
171
  readonly getColumns: () => number;
@@ -1,20 +1,23 @@
1
1
  {
2
- "schema": "a1-process-guardian-artifact-v1",
3
- "protocolVersion": 1,
2
+ "schema": "a1-process-guardian-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
5
+ "protocolVersion": 2,
4
6
  "crateVersion": "0.1.0-dev",
5
7
  "platform": "darwin",
6
8
  "architecture": "arm64",
7
9
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:44.665Z",
10
+ "builtAt": "2026-10-18T08:41:09.288Z",
9
11
  "artifact": {
10
- "filename": "process-guardian",
11
- "sha256": "47e9052599983a6bb0078ce6203ba18d71bec0b7b94e6a49019d11d46c3fbeca",
12
- "size": 410368
12
+ "filename": "process-guardian.node",
13
+ "sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
14
+ "size": 456080
13
15
  },
14
16
  "provenance": {
15
17
  "language": "Rust",
16
18
  "cargoLock": "native/process-guardian/Cargo.lock",
17
19
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
20
+ "signatureStatus": "ci-attestation-required",
21
+ "identityCheck": "executed"
19
22
  }
20
23
  }
@@ -1,6 +1,6 @@
1
1
  Third-party notices for the A1 terminal host
2
2
 
3
- The terminal-host executable and its session-writer lock include the following components.
3
+ The terminal-host Node-API addon includes the following components.
4
4
 
5
5
  ==============================================================================
6
6
  anyhow 1.0.104
@@ -1,5 +1,7 @@
1
1
  {
2
- "schema": "a1-terminal-host-artifact-v1",
2
+ "schema": "a1-terminal-host-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
3
5
  "protocolVersion": 1,
4
6
  "generation": 1,
5
7
  "crateVersion": "0.1.0-dev",
@@ -7,21 +9,21 @@
7
9
  "architecture": "arm64",
8
10
  "target": "aarch64-apple-darwin",
9
11
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:12:28.897Z",
12
+ "builtAt": "2026-10-18T08:43:27.368Z",
11
13
  "artifact": {
12
- "filename": "terminal-host",
13
- "sha256": "9996ac4e901bc35df352567d120d6bb7b6a8b3451e2841be27e0b77f71a2f046",
14
- "size": 4893504
14
+ "filename": "terminal-host.node",
15
+ "sha256": "af7b9fee2efa8478c728465031e73fb316b6b7012d11de0364130b0acd1b6e62",
16
+ "size": 5173904
15
17
  },
16
- "sessionLock": {
17
- "filename": "a1_session_lock.node",
18
- "sha256": "3c70e041ed400e8d4f543adcd4910c7681d2498aac4ffc7a06ba8114dab0d290",
19
- "size": 561200
18
+ "entry": {
19
+ "filename": "dist/foundation/resident-tabs/host-entry.js",
20
+ "sha256": "4b1f702cc9980ffa531cf3e9963b884eaa35120d04d5c195ea3308f501d8d557",
21
+ "size": 1315
20
22
  },
21
23
  "notices": {
22
24
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
23
- "sha256": "fca50b98ad080b268a21267295bc8367ffdb0652b9f6a1cb26a81e35f8e58d05",
24
- "size": 540069
25
+ "sha256": "5f18fd5d1863f7282c9c3e249b9a25df1b454d26cc737b92b3360ca877e50120",
26
+ "size": 540046
25
27
  },
26
28
  "provenance": {
27
29
  "language": "Rust",
@@ -34,11 +36,11 @@
34
36
  "zigVersion": "0.15.2",
35
37
  "rustVersion": "rustc 1.99.1 (58cf8f9c3 2026-10-13)",
36
38
  "features": {
37
- "executable": [],
38
- "sessionLock": [
39
+ "addon": [
39
40
  "node-addon"
40
41
  ]
41
42
  },
42
- "signatureStatus": "ci-attestation-required"
43
+ "signatureStatus": "ci-attestation-required",
44
+ "identityCheck": "executed"
43
45
  }
44
46
  }
@@ -1,20 +1,23 @@
1
1
  {
2
- "schema": "a1-process-guardian-artifact-v1",
3
- "protocolVersion": 1,
2
+ "schema": "a1-process-guardian-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
5
+ "protocolVersion": 2,
4
6
  "crateVersion": "0.1.0-dev",
5
7
  "platform": "darwin",
6
8
  "architecture": "x64",
7
9
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:54.598Z",
10
+ "builtAt": "2026-10-18T08:41:16.359Z",
9
11
  "artifact": {
10
- "filename": "process-guardian",
11
- "sha256": "ccc1272de81b06efa5e590a88dbafe30726a0fe33e0808025a6602236e5d2855",
12
- "size": 394776
12
+ "filename": "process-guardian.node",
13
+ "sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
14
+ "size": 444248
13
15
  },
14
16
  "provenance": {
15
17
  "language": "Rust",
16
18
  "cargoLock": "native/process-guardian/Cargo.lock",
17
19
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
20
+ "signatureStatus": "ci-attestation-required",
21
+ "identityCheck": "mach-o-header"
19
22
  }
20
23
  }
@@ -1,6 +1,6 @@
1
1
  Third-party notices for the A1 terminal host
2
2
 
3
- The terminal-host executable and its session-writer lock include the following components.
3
+ The terminal-host Node-API addon includes the following components.
4
4
 
5
5
  ==============================================================================
6
6
  anyhow 1.0.104
@@ -1,5 +1,7 @@
1
1
  {
2
- "schema": "a1-terminal-host-artifact-v1",
2
+ "schema": "a1-terminal-host-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
3
5
  "protocolVersion": 1,
4
6
  "generation": 1,
5
7
  "crateVersion": "0.1.0-dev",
@@ -7,21 +9,21 @@
7
9
  "architecture": "x64",
8
10
  "target": "x86_64-apple-darwin",
9
11
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:24:32.295Z",
12
+ "builtAt": "2026-10-18T08:45:43.907Z",
11
13
  "artifact": {
12
- "filename": "terminal-host",
13
- "sha256": "b265f3b4ce17f06b984278cffa461ee9d77dd47641b3d66e824251d6d08157b6",
14
- "size": 5335192
14
+ "filename": "terminal-host.node",
15
+ "sha256": "1a5465ea12ecfe76d5f0c8fd5c11dee99597b4206a7e9ece01b37fc5c477c211",
16
+ "size": 5656912
15
17
  },
16
- "sessionLock": {
17
- "filename": "a1_session_lock.node",
18
- "sha256": "059de5460e5333353bc6c541e2ae577d72c201493f7e8081a65730af251d626e",
19
- "size": 516280
18
+ "entry": {
19
+ "filename": "dist/foundation/resident-tabs/host-entry.js",
20
+ "sha256": "4b1f702cc9980ffa531cf3e9963b884eaa35120d04d5c195ea3308f501d8d557",
21
+ "size": 1315
20
22
  },
21
23
  "notices": {
22
24
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
23
- "sha256": "4b96f6f99efd3091fad24422c9cfade9b40a96c6b4b9c97b38ec1de05c290991",
24
- "size": 552243
25
+ "sha256": "ebddf265d324c1393ba2f52b5fa411709ae2179bfe1aa7bb5bd84f0adc4ccef0",
26
+ "size": 552220
25
27
  },
26
28
  "provenance": {
27
29
  "language": "Rust",
@@ -34,11 +36,11 @@
34
36
  "zigVersion": "0.15.2",
35
37
  "rustVersion": "rustc 1.99.1 (58cf8f9c3 2026-10-13)",
36
38
  "features": {
37
- "executable": [],
38
- "sessionLock": [
39
+ "addon": [
39
40
  "node-addon"
40
41
  ]
41
42
  },
42
- "signatureStatus": "ci-attestation-required"
43
+ "signatureStatus": "ci-attestation-required",
44
+ "identityCheck": "mach-o-header"
43
45
  }
44
46
  }
@@ -1,20 +1,23 @@
1
1
  {
2
- "schema": "a1-process-guardian-artifact-v1",
3
- "protocolVersion": 1,
2
+ "schema": "a1-process-guardian-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
5
+ "protocolVersion": 2,
4
6
  "crateVersion": "0.1.0-dev",
5
7
  "platform": "linux",
6
8
  "architecture": "x64",
7
9
  "capability": "supported",
8
- "builtAt": "2026-10-16T17:08:19.616Z",
10
+ "builtAt": "2026-10-18T08:40:48.343Z",
9
11
  "artifact": {
10
- "filename": "process-guardian",
11
- "sha256": "94a96fa9d5178a1949d05c6d6cc9504f3030cd49a096cd9db20dd29860ea5884",
12
- "size": 411760
12
+ "filename": "process-guardian.node",
13
+ "sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
14
+ "size": 491520
13
15
  },
14
16
  "provenance": {
15
17
  "language": "Rust",
16
18
  "cargoLock": "native/process-guardian/Cargo.lock",
17
19
  "sourceRoot": "native/process-guardian",
18
- "signatureStatus": "ci-attestation-required"
20
+ "signatureStatus": "ci-attestation-required",
21
+ "identityCheck": "executed"
19
22
  }
20
23
  }
@@ -1,6 +1,6 @@
1
1
  Third-party notices for the A1 terminal host
2
2
 
3
- The terminal-host executable and its session-writer lock include the following components.
3
+ The terminal-host Node-API addon includes the following components.
4
4
 
5
5
  ==============================================================================
6
6
  anyhow 1.0.104
@@ -1,5 +1,7 @@
1
1
  {
2
- "schema": "a1-terminal-host-artifact-v1",
2
+ "schema": "a1-terminal-host-artifact-v2",
3
+ "kind": "node-addon",
4
+ "nodeApi": 9,
3
5
  "protocolVersion": 1,
4
6
  "generation": 1,
5
7
  "crateVersion": "0.1.0-dev",
@@ -7,21 +9,21 @@
7
9
  "architecture": "x64",
8
10
  "target": "x86_64-unknown-linux-gnu",
9
11
  "capability": "supported",
10
- "builtAt": "2026-10-16T17:11:30.623Z",
12
+ "builtAt": "2026-10-18T08:43:45.076Z",
11
13
  "artifact": {
12
- "filename": "terminal-host",
13
- "sha256": "694d149d49e5f0ceaeb429fbfdbd9710bdad32b4932ee754f48400f1514fcabc",
14
- "size": 5901664
14
+ "filename": "terminal-host.node",
15
+ "sha256": "843b10ab29eddf859a74a97875ba646a90917d9ec78445474895bb64f6e0772a",
16
+ "size": 6353496
15
17
  },
16
- "sessionLock": {
17
- "filename": "a1_session_lock.node",
18
- "sha256": "2c47291fae93329dea2bc774fbcd823e83f16026e43333ee3ed3336c45df7ce2",
19
- "size": 726016
18
+ "entry": {
19
+ "filename": "dist/foundation/resident-tabs/host-entry.js",
20
+ "sha256": "4b1f702cc9980ffa531cf3e9963b884eaa35120d04d5c195ea3308f501d8d557",
21
+ "size": 1315
20
22
  },
21
23
  "notices": {
22
24
  "filename": "terminal-host-THIRD-PARTY-NOTICES.txt",
23
- "sha256": "526eb656e7fb47474bcfb07cf922a95a4920d6c4130478902e3c8ea5bb805e04",
24
- "size": 577577
25
+ "sha256": "c3c1874dfdc10608f46e7576486b7eb6f43eb85460ce7b81db02e6db32af1cc2",
26
+ "size": 577554
25
27
  },
26
28
  "provenance": {
27
29
  "language": "Rust",
@@ -34,11 +36,11 @@
34
36
  "zigVersion": "0.15.2",
35
37
  "rustVersion": "rustc 1.99.1 (58cf8f9c3 2026-10-13)",
36
38
  "features": {
37
- "executable": [],
38
- "sessionLock": [
39
+ "addon": [
39
40
  "node-addon"
40
41
  ]
41
42
  },
42
- "signatureStatus": "ci-attestation-required"
43
+ "signatureStatus": "ci-attestation-required",
44
+ "identityCheck": "executed"
43
45
  }
44
46
  }