@timurproko/a1 0.2.6-dev.727 → 0.2.6-dev.734

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 (104) hide show
  1. package/dist/app/session-shell/clipboard-services.d.ts +19 -0
  2. package/dist/app/session-shell/clipboard-services.js +7 -0
  3. package/dist/app/session-shell/exit-notice.d.ts +9 -5
  4. package/dist/app/session-shell/exit-notice.js +5 -5
  5. package/dist/app/session-shell/index.d.ts +3 -1
  6. package/dist/app/session-shell/index.js +1 -0
  7. package/dist/app/session-shell/paste-preparation-client.d.ts +3 -0
  8. package/dist/app/session-shell/paste-preparation-client.js +5 -2
  9. package/dist/app/session-shell/path-chip-presentation.d.ts +10 -3
  10. package/dist/app/session-shell/path-chip-presentation.js +48 -6
  11. package/dist/app/session-shell/prompt-chips.js +30 -4
  12. package/dist/app/session-shell/quit-outro.d.ts +3 -1
  13. package/dist/app/session-shell/quit-outro.js +23 -8
  14. package/dist/app/session-shell/response-copy-transport.d.ts +6 -1
  15. package/dist/app/session-shell/response-copy-transport.js +8 -2
  16. package/dist/app/session-shell/session-shell-root.d.ts +20 -10
  17. package/dist/app/session-shell/session-shell-root.js +6 -6
  18. package/dist/app/session-shell/session-shell.d.ts +19 -21
  19. package/dist/app/session-shell/session-shell.js +656 -860
  20. package/dist/composition/owned-ui.d.ts +20 -2
  21. package/dist/composition/owned-ui.js +66 -35
  22. package/dist/composition/session-info-reference.d.ts +2 -2
  23. package/dist/composition/session-info-reference.js +2 -2
  24. package/dist/contracts/agent-engine/capability-ports.d.ts +1 -85
  25. package/dist/contracts/agent-engine/capability-ports.js +1 -35
  26. package/dist/contracts/agent-engine/index.d.ts +1 -7
  27. package/dist/contracts/agent-engine/index.js +0 -4
  28. package/dist/contracts/owned-ui/index.d.ts +5 -1
  29. package/dist/contracts/owned-ui/index.js +1 -0
  30. package/dist/contracts/owned-ui/model.d.ts +6 -0
  31. package/dist/contracts/owned-ui/session-backend.d.ts +119 -0
  32. package/dist/contracts/owned-ui/session-presenters.d.ts +55 -0
  33. package/dist/contracts/owned-ui/session-presenters.js +1 -0
  34. package/dist/contracts/owned-ui/session-workflows.d.ts +342 -0
  35. package/dist/contracts/owned-ui/session-workflows.js +30 -0
  36. package/dist/features/owned-ui/settings-app.js +35 -15
  37. package/dist/foundation/release/bootstrap.js +1 -2
  38. package/dist/foundation/release/release-gc.js +1 -1
  39. package/dist/foundation/release/update.js +0 -4
  40. package/dist/foundation/storage/control-store.js +0 -12
  41. package/dist/integrations/pi/components/components.js +0 -16
  42. package/dist/integrations/pi/components/index.d.ts +3 -3
  43. package/dist/integrations/pi/components/index.js +2 -2
  44. package/dist/integrations/pi/components/models-dialog.js +1 -30
  45. package/dist/integrations/pi/components/shell-editor-autocomplete.js +11 -9
  46. package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -3
  47. package/dist/integrations/pi/components/shell-extension-ui.js +6 -2
  48. package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -4
  49. package/dist/integrations/pi/components/shell-shared-facade.d.ts +21 -1
  50. package/dist/integrations/pi/components/shell-shared-facade.js +18 -2
  51. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -1
  52. package/dist/integrations/pi/components/upstream/theme/theme.js +20 -2
  53. package/dist/integrations/pi/engine/adapter.d.ts +44 -24
  54. package/dist/integrations/pi/engine/adapter.js +130 -22
  55. package/dist/integrations/pi/engine/command-dispatch.d.ts +2 -5
  56. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -11
  57. package/dist/integrations/pi/engine/host-ports.d.ts +20 -0
  58. package/dist/integrations/pi/engine/host-ports.js +13 -0
  59. package/dist/integrations/pi/engine/host.d.ts +70 -0
  60. package/dist/integrations/pi/engine/host.js +147 -0
  61. package/dist/integrations/pi/engine/index.d.ts +8 -4
  62. package/dist/integrations/pi/engine/index.js +3 -1
  63. package/dist/integrations/pi/engine/resource-catalog.d.ts +4 -23
  64. package/dist/integrations/pi/engine/runtime-integration.d.ts +8 -2
  65. package/dist/integrations/pi/engine/runtime-integration.js +12 -7
  66. package/dist/integrations/pi/engine/session-integration.d.ts +3 -8
  67. package/dist/integrations/pi/engine/session-integration.js +0 -49
  68. package/dist/integrations/pi/engine/session-runtime.d.ts +18 -3
  69. package/dist/integrations/pi/engine/session-runtime.js +18 -7
  70. package/dist/integrations/pi/engine/settings-bridge.d.ts +2 -2
  71. package/dist/integrations/pi/engine/settings-bridge.js +0 -2
  72. package/dist/integrations/pi/engine/settings-port.d.ts +2 -0
  73. package/dist/integrations/pi/engine/settings-port.js +3 -5
  74. package/dist/integrations/pi/engine/workflows.d.ts +33 -260
  75. package/dist/integrations/pi/engine/workflows.js +6 -28
  76. package/dist/integrations/pi/session-presenters/index.d.ts +2 -0
  77. package/dist/integrations/pi/session-presenters/index.js +1 -0
  78. package/dist/integrations/pi/session-presenters/presenters.d.ts +16 -0
  79. package/dist/integrations/pi/session-presenters/presenters.js +341 -0
  80. package/dist/integrations/pi/tui-runtime/adapter.js +0 -2
  81. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +7 -0
  82. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +66 -1
  83. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
  84. package/dist/integrations/pi/tui-runtime/index.js +1 -1
  85. package/dist/native/darwin-arm64/manifest.json +1 -1
  86. package/dist/native/linux-x64/manifest.json +1 -1
  87. package/dist/native/win32-x64/manifest.json +2 -2
  88. package/dist/native/win32-x64/process-guardian.exe +0 -0
  89. package/dist/ui/settings/declarations.d.ts +13 -1
  90. package/dist/ui/settings/declarations.js +12 -1
  91. package/dist/ui/settings/migrations.js +7 -0
  92. package/docs/architecture/boundaries.md +9 -1
  93. package/docs/architecture/project-structure.md +3 -2
  94. package/package.json +1 -1
  95. package/dist/contracts/agent-engine/model.d.ts +0 -78
  96. package/dist/contracts/agent-engine/model.js +0 -1
  97. package/dist/contracts/agent-engine/ports.d.ts +0 -18
  98. package/dist/contracts/agent-engine/serialization.d.ts +0 -7
  99. package/dist/contracts/agent-engine/serialization.js +0 -58
  100. package/dist/contracts/agent-engine/validation.d.ts +0 -5
  101. package/dist/contracts/agent-engine/validation.js +0 -128
  102. package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +0 -61
  103. package/dist/integrations/pi/components/upstream/theme/system-theme.js +0 -709
  104. /package/dist/contracts/{agent-engine/ports.js → owned-ui/session-backend.js} +0 -0
@@ -0,0 +1,19 @@
1
+ import type { HelperPool } from "./helper-pool.js";
2
+ /**
3
+ * Process-wide clipboard helper spares. Composition creates one and every session shell borrows it, so N
4
+ * sessions keep at most one spare paste helper and one spare copy helper instead of 2N. Shells warm the
5
+ * pools after their first frame; only the creator disposes them.
6
+ */
7
+ export interface OwnedUiClipboardServices {
8
+ readonly paste: HelperPool;
9
+ readonly copy: HelperPool;
10
+ dispose(): void;
11
+ }
12
+ export interface OwnedUiClipboardServicesOptions {
13
+ /** Package/fault-test seams for the forked helpers' entries. */
14
+ readonly pasteHelper?: URL;
15
+ readonly copyHelper?: URL;
16
+ /** Idle bound for both spares; production keeps the default. */
17
+ readonly spareIdleMs?: number;
18
+ }
19
+ export declare function createOwnedUiClipboardServices(options?: OwnedUiClipboardServicesOptions): OwnedUiClipboardServices;
@@ -0,0 +1,7 @@
1
+ import { createPasteHelperPool } from "./paste-executor.js";
2
+ import { createCopyHelperPool } from "./response-copy-transport.js";
3
+ export function createOwnedUiClipboardServices(options = {}) {
4
+ const paste = createPasteHelperPool(options.pasteHelper, options.spareIdleMs);
5
+ const copy = createCopyHelperPool(options.copyHelper, options.spareIdleMs);
6
+ return { paste, copy, dispose: () => { paste.dispose(); copy.dispose(); } };
7
+ }
@@ -1,10 +1,14 @@
1
1
  import { type SessionResumeCommandMetadata } from "./session-shell.js";
2
2
  export interface ExitNoticeSource {
3
- readonly sessionGeneration: number;
4
- currentSessionResumeMetadata(): SessionResumeCommandMetadata | null;
5
- onEvent(listener: (event: {
6
- readonly type: string;
7
- }) => void): () => void;
3
+ readonly identity: {
4
+ readonly sessionGeneration: number;
5
+ currentSessionResumeMetadata(): SessionResumeCommandMetadata | null;
6
+ };
7
+ readonly session: {
8
+ onEvent(listener: (event: {
9
+ readonly type: string;
10
+ }) => void): () => void;
11
+ };
8
12
  }
9
13
  export interface ArmedExitNotice {
10
14
  clear(): void;
@@ -17,12 +17,12 @@ export function uncleanExitNoticeLines(resume) {
17
17
  export function armExitNotice(source, path) {
18
18
  let current = null;
19
19
  let cleared = false;
20
- let generation = source.sessionGeneration;
20
+ let generation = source.identity.sessionGeneration;
21
21
  const write = () => {
22
22
  if (cleared)
23
23
  return;
24
24
  // Protocol: plain text only; the guardian strips control characters, so styling would be lost.
25
- const text = `${uncleanExitNoticeLines(source.currentSessionResumeMetadata()).join("\n")}\n`;
25
+ const text = `${uncleanExitNoticeLines(source.identity.currentSessionResumeMetadata()).join("\n")}\n`;
26
26
  if (text === current)
27
27
  return;
28
28
  try {
@@ -35,13 +35,13 @@ export function armExitNotice(source, path) {
35
35
  catch { }
36
36
  };
37
37
  write();
38
- const unsubscribe = source.onEvent(event => {
38
+ const unsubscribe = source.session.onEvent(event => {
39
39
  // Rationale: a session is persisted by its first response and replaced by /new or /resume;
40
40
  // these events cover both without touching the per-chunk stream.
41
- const replaced = source.sessionGeneration !== generation;
41
+ const replaced = source.identity.sessionGeneration !== generation;
42
42
  if (!replaced && event.type !== "session-lifecycle" && event.type !== "assistant-message-completed")
43
43
  return;
44
- generation = source.sessionGeneration;
44
+ generation = source.identity.sessionGeneration;
45
45
  write();
46
46
  });
47
47
  return {
@@ -1,7 +1,9 @@
1
1
  export { ClipboardDiagnosticCapture } from "./clipboard-diagnostics.js";
2
+ export { createOwnedUiClipboardServices } from "./clipboard-services.js";
3
+ export type { OwnedUiClipboardServices, OwnedUiClipboardServicesOptions } from "./clipboard-services.js";
2
4
  export { ContextualPromptSuggestionController, samePromptSuggestionIdentity } from "./prompt-suggestion-controller.js";
3
5
  export type { ContextualPromptSuggestionControllerOptions, ContextualPromptSuggestionSurface } from "./prompt-suggestion-controller.js";
4
6
  export { OwnedUiSessionShell, OwnedUiSessionShellRoot, formatSessionResumeCommand, quoteCommandArgument } from "./session-shell.js";
5
- export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellPromptImagesOptions, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
7
+ export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellPromptImagesOptions, OwnedUiShellSharedServices, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
6
8
  export { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer } from "./stream-presentation-coalescer.js";
7
9
  export type { StreamPresentationScheduler } from "./stream-presentation-coalescer.js";
@@ -1,4 +1,5 @@
1
1
  export { ClipboardDiagnosticCapture } from "./clipboard-diagnostics.js";
2
+ export { createOwnedUiClipboardServices } from "./clipboard-services.js";
2
3
  export { ContextualPromptSuggestionController, samePromptSuggestionIdentity } from "./prompt-suggestion-controller.js";
3
4
  export { OwnedUiSessionShell, OwnedUiSessionShellRoot, formatSessionResumeCommand, quoteCommandArgument } from "./session-shell.js";
4
5
  export { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer } from "./stream-presentation-coalescer.js";
@@ -1,4 +1,5 @@
1
1
  import { startPasteExecutor } from "./paste-executor.js";
2
+ import type { HelperPool } from "./helper-pool.js";
2
3
  import { type PasteEvent, type PasteSource, type PreparedPaste } from "./paste-protocol.js";
3
4
  export interface PreparedPasteJob<T> {
4
5
  readonly result: Promise<T>;
@@ -16,6 +17,8 @@ export interface PastePreparationClientOptions {
16
17
  readonly helper?: URL;
17
18
  /** Idle bound for the spare helper; production keeps the default. */
18
19
  readonly spareIdleMs?: number;
20
+ /** Process-wide spare pool shared across sessions; the client warms it but never disposes it. */
21
+ readonly pool?: HelperPool;
19
22
  }
20
23
  /** Keeps eight distinct paste transactions alive through insertion; canceled executors retain capacity until fenced. */
21
24
  export declare class PastePreparationClient {
@@ -8,13 +8,15 @@ export class PastePreparationClient {
8
8
  #execute;
9
9
  #helper;
10
10
  #pool;
11
+ #ownsPool;
11
12
  #sequence = 0;
12
13
  #disposed = false;
13
14
  constructor(options = {}) {
14
15
  this.#onEvent = options.onEvent;
15
16
  this.#execute = options.execute ?? startPasteExecutor;
16
17
  this.#helper = options.helper;
17
- this.#pool = options.execute === undefined ? createPasteHelperPool(options.helper, options.spareIdleMs) : undefined;
18
+ this.#pool = options.execute === undefined ? options.pool ?? createPasteHelperPool(options.helper, options.spareIdleMs) : undefined;
19
+ this.#ownsPool = options.pool === undefined;
18
20
  }
19
21
  /** Forks the spare helper now so the first paste takes a live child; later pastes rewarm it themselves. */
20
22
  warm() { this.#pool?.warm(); }
@@ -160,7 +162,8 @@ export class PastePreparationClient {
160
162
  async dispose() {
161
163
  this.#disposed = true;
162
164
  this.reset();
163
- this.#pool?.dispose();
165
+ if (this.#ownsPool)
166
+ this.#pool?.dispose();
164
167
  let timer;
165
168
  await Promise.race([Promise.all([...this.#active].map(active => active.stopped)),
166
169
  new Promise(resolve => { timer = setTimeout(resolve, PASTE_STOP_MS); timer.unref(); })]);
@@ -1,7 +1,14 @@
1
1
  import type { ClipboardPath, PreparedPasteText } from "./paste-types.js";
2
2
  export declare const PATH_CHIP_PRESENTATION_UNITS = 4096;
3
- export declare const PATH_CHIP_SUFFIX_UNITS = 20;
4
- /** Shared base tag for presentation budgeting and actual chip adoption, including image-file spacing. */
5
- export declare function pathChipTag(item: ClipboardPath): string;
3
+ /** Normalizes a path for host-filesystem identity comparisons. */
4
+ export declare function normalizedPathIdentity(fullPath: string): string;
5
+ /** Yields labels from the basename through the normalized rooted path, shortest first. */
6
+ export declare function pathChipLabelCandidates(fullPath: string): Iterable<string>;
7
+ /** Formats one path label with the established folder/file/image-file icon. */
8
+ export declare function pathChipTag(item: ClipboardPath, label?: string): string;
9
+ /** Yields complete deterministic tags lazily for path collision adoption. */
10
+ export declare function pathChipTagCandidates(item: ClipboardPath): Iterable<string>;
11
+ /** Returns the conservative UTF-16 presentation cost for one path occurrence. */
12
+ export declare function pathChipPresentationUnits(item: ClipboardPath): number;
6
13
  /** Run in the isolated preparer: compact before path arrays become an oversized editor presentation. */
7
14
  export declare function preparePathPresentation(paths: readonly ClipboardPath[]): PreparedPasteText;
@@ -1,19 +1,52 @@
1
1
  import path from "node:path";
2
2
  export const PATH_CHIP_PRESENTATION_UNITS = 4096;
3
- // Invariant: a collision suffix containing any safe-integer index fits this per-occurrence reserve.
4
- export const PATH_CHIP_SUFFIX_UNITS = 20;
5
3
  const IMAGE_EXTENSION = /\.(?:jpe?g|png|webp|gif|bmp|tiff?)$/iu;
6
- /** Shared base tag for presentation budgeting and actual chip adoption, including image-file spacing. */
7
- export function pathChipTag(item) {
8
- const label = path.basename(item.fullPath) || path.parse(item.fullPath).root.replace(/[\\/]+$/u, "") || item.fullPath;
4
+ /** Normalizes a path for host-filesystem identity comparisons. */
5
+ export function normalizedPathIdentity(fullPath) {
6
+ const normalized = path.normalize(fullPath.trim());
7
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
8
+ }
9
+ /** Yields labels from the basename through the normalized rooted path, shortest first. */
10
+ export function* pathChipLabelCandidates(fullPath) {
11
+ const normalized = path.normalize(fullPath.trim());
12
+ const parsed = path.parse(normalized);
13
+ const relative = normalized.slice(parsed.root.length);
14
+ const segments = relative.split(path.sep).filter(segment => segment.length > 0);
15
+ let previous = "";
16
+ for (let depth = 1; depth <= segments.length; depth++) {
17
+ previous = segments.slice(-depth).join("/");
18
+ yield previous;
19
+ }
20
+ const rooted = displayPath(normalized);
21
+ if (segments.length === 0) {
22
+ const root = displayPath(parsed.root).replace(/\/$/u, "");
23
+ if (root.length > 0) {
24
+ previous = root;
25
+ yield root;
26
+ }
27
+ }
28
+ if (rooted.length > 0 && rooted !== previous)
29
+ yield rooted;
30
+ }
31
+ /** Formats one path label with the established folder/file/image-file icon. */
32
+ export function pathChipTag(item, label = basePathLabel(item.fullPath)) {
9
33
  const icon = item.kind === "folder" ? "📁" : IMAGE_EXTENSION.test(item.fullPath) ? "🖼 " : "📄";
10
34
  return `[${icon} ${label}]`;
11
35
  }
36
+ /** Yields complete deterministic tags lazily for path collision adoption. */
37
+ export function* pathChipTagCandidates(item) {
38
+ for (const label of pathChipLabelCandidates(item.fullPath))
39
+ yield pathChipTag(item, label);
40
+ }
41
+ /** Returns the conservative UTF-16 presentation cost for one path occurrence. */
42
+ export function pathChipPresentationUnits(item) {
43
+ return pathChipTag(item, displayPath(path.normalize(item.fullPath.trim()))).length;
44
+ }
12
45
  /** Run in the isolated preparer: compact before path arrays become an oversized editor presentation. */
13
46
  export function preparePathPresentation(paths) {
14
47
  let units = 0;
15
48
  for (const item of paths) {
16
- units += pathChipTag(item).length + PATH_CHIP_SUFFIX_UNITS;
49
+ units += pathChipPresentationUnits(item);
17
50
  if (units > PATH_CHIP_PRESENTATION_UNITS) {
18
51
  // Compatibility: individual adjacent chips expand without inserted separators or text re-normalization.
19
52
  const text = paths.map(value => value.fullPath).join("");
@@ -22,3 +55,12 @@ export function preparePathPresentation(paths) {
22
55
  }
23
56
  return { kind: "paths", paths };
24
57
  }
58
+ function basePathLabel(fullPath) {
59
+ const normalized = path.normalize(fullPath.trim());
60
+ return path.basename(normalized)
61
+ || displayPath(path.parse(normalized).root).replace(/\/$/u, "")
62
+ || displayPath(normalized);
63
+ }
64
+ function displayPath(value) {
65
+ return path.sep === "\\" ? value.replaceAll("\\", "/") : value;
66
+ }
@@ -1,10 +1,9 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import path from "node:path";
3
2
  import { canonicalizeClipboardImage } from "./clipboard-image.js";
4
3
  import { assertImageEncodedSize, assertPromptImages, canonicalPromptChipMatches, DEFAULT_PROMPT_IMAGE_LIMIT, ImageAttachmentError, replaceCanonicalPromptChips, } from "../../contracts/owned-ui/index.js";
5
4
  import { ImagePreparationClient } from "./image-preparation-client.js";
6
5
  import { preparePasteText } from "./paste-text-preparation.js";
7
- import { pathChipTag } from "./path-chip-presentation.js";
6
+ import { normalizedPathIdentity, pathChipTagCandidates } from "./path-chip-presentation.js";
8
7
  import { PastePreparationClient } from "./paste-preparation-client.js";
9
8
  const URL_SUBSTRING_PATTERN = /https?:\/\/[^\s\u0000-\u001f\u007f\]]+/giu;
10
9
  const IMAGE_CHIP_IDENTIFIER_PATTERN = /^\[📷 screenshot-([a-f0-9]+)(?:-resized)?\]$/u;
@@ -23,6 +22,7 @@ function referencesPasteMarker(text, entry) {
23
22
  /** Owns semantic chips and bounded pending paste references; cancels background work on reset or disposal. */
24
23
  export class PromptChipStore {
25
24
  #chips = new Map();
25
+ #pathTags = new Map();
26
26
  // Invariant: clearing the editor never recycles a recoverable text chip's identity.
27
27
  #textCounter = 0;
28
28
  #pending = new Map();
@@ -199,6 +199,7 @@ export class PromptChipStore {
199
199
  async dispose() {
200
200
  await Promise.all([this.#preparation.dispose(), this.#isolated?.dispose(), ...this.#stopping]);
201
201
  this.#chips.clear();
202
+ this.#pathTags.clear();
202
203
  this.#pending.clear();
203
204
  this.#countRejectedImages.clear();
204
205
  this.#provisionalOwners.clear();
@@ -269,7 +270,26 @@ export class PromptChipStore {
269
270
  this.#claimChip(tag, owner, true);
270
271
  return tag;
271
272
  }
272
- return paste.paths.map(item => this.#recordUnique({ kind: item.kind, tag: pathChipTag(item), path: item.fullPath }, owner)).join("");
273
+ return paste.paths.map(item => this.#recordPath(item, owner)).join("");
274
+ }
275
+ #recordPath(item, owner) {
276
+ const key = pathChipKey(item);
277
+ const establishedTag = this.#pathTags.get(key);
278
+ if (establishedTag !== undefined && this.#chips.has(establishedTag)) {
279
+ this.#claimChip(establishedTag, owner, false);
280
+ return establishedTag;
281
+ }
282
+ for (const tag of pathChipTagCandidates(item)) {
283
+ const chip = { kind: item.kind, tag, path: item.fullPath };
284
+ const existing = this.#chips.get(tag);
285
+ if (existing !== undefined && !sameChipValue(existing, chip))
286
+ continue;
287
+ this.#chips.set(tag, chip);
288
+ this.#pathTags.set(key, tag);
289
+ this.#claimChip(tag, owner, existing === undefined);
290
+ return tag;
291
+ }
292
+ throw new Error("Path chip labels could not distinguish normalized paths");
273
293
  }
274
294
  /** Hide provisional clipboard identities until the read identifies an actual image. */
275
295
  hiddenRanges(line) {
@@ -501,7 +521,10 @@ export class PromptChipStore {
501
521
  owners.delete(owner);
502
522
  if (owners.size === 0) {
503
523
  this.#provisionalOwners.delete(tag);
524
+ const chip = this.#chips.get(tag);
504
525
  this.#chips.delete(tag);
526
+ if (chip?.kind === "file" || chip?.kind === "folder")
527
+ this.#pathTags.delete(pathChipKey(chip));
505
528
  }
506
529
  }
507
530
  }
@@ -522,13 +545,16 @@ export class PromptChipStore {
522
545
  return uniqueTag;
523
546
  }
524
547
  }
548
+ function pathChipKey(item) {
549
+ return `${item.kind}\0${normalizedPathIdentity("fullPath" in item ? item.fullPath : item.path)}`;
550
+ }
525
551
  function sameChipValue(left, right) {
526
552
  if (left.kind !== right.kind)
527
553
  return false;
528
554
  if (left.kind === "url" && right.kind === "url")
529
555
  return left.url === right.url;
530
556
  if ((left.kind === "file" || left.kind === "folder") && (right.kind === "file" || right.kind === "folder")) {
531
- return path.normalize(left.path) === path.normalize(right.path);
557
+ return normalizedPathIdentity(left.path) === normalizedPathIdentity(right.path);
532
558
  }
533
559
  if (left.kind === "image" && right.kind === "image")
534
560
  return left.image.data === right.image.data;
@@ -15,6 +15,8 @@ export interface QuitOutroPlayback {
15
15
  sleep?(ms: number): Promise<void>;
16
16
  /** Plan seed; defaults to a time- and geometry-derived value. */
17
17
  readonly seed?: number;
18
+ /** Active bare-A1 canvas; omitted preserves transparent playback bytes. */
19
+ readonly canvasBackgroundAnsi?: string;
18
20
  }
19
21
  export declare const QUIT_OUTRO_MIN_MS = 300;
20
22
  export declare const QUIT_OUTRO_MAX_MS = 2000;
@@ -29,7 +31,7 @@ export declare function clampQuitOutroDuration(durationMs: number): number;
29
31
  */
30
32
  export declare function captureQuitOutroFrame(presented: readonly string[], columns: number, rows: number): QuitOutroFrame | null;
31
33
  /** The clear-and-repaint block that seeds the animation surface with the captured frame. */
32
- export declare function createQuitOutroSurfaceFrame(frame: QuitOutroFrame): string;
34
+ export declare function createQuitOutroSurfaceFrame(frame: QuitOutroFrame, canvasBackgroundAnsi?: string): string;
33
35
  /**
34
36
  * Plays the effect over the frame. Resolves true when the plan was played to
35
37
  * completion or abandoned at its guard, false when there was nothing to play.
@@ -6,6 +6,7 @@
6
6
  * terminal cannot delay restoration past the configured clamp.
7
7
  */
8
8
  import { displayWidth, truncateToWidth } from "../../ui/components/index.js";
9
+ import { rebaseTerminalDefaultBackground } from "../../integrations/pi/tui-runtime/index.js";
9
10
  import { createQuitOutroPlan } from "./quit-outro-effects.js";
10
11
  export const QUIT_OUTRO_MIN_MS = 300;
11
12
  export const QUIT_OUTRO_MAX_MS = 2000;
@@ -44,14 +45,25 @@ export function captureQuitOutroFrame(presented, columns, rows) {
44
45
  return { lines, columns: width, rows: height, rowWidths };
45
46
  }
46
47
  /** The clear-and-repaint block that seeds the animation surface with the captured frame. */
47
- export function createQuitOutroSurfaceFrame(frame) {
48
- let output = `${SYNC_BEGIN}${HIDE_CURSOR}${CLEAR_SCREEN}${HOME}${RESET}`;
48
+ export function createQuitOutroSurfaceFrame(frame, canvasBackgroundAnsi) {
49
+ if (canvasBackgroundAnsi === undefined) {
50
+ let output = `${SYNC_BEGIN}${HIDE_CURSOR}${CLEAR_SCREEN}${HOME}${RESET}`;
51
+ for (let row = 0; row < frame.rows; row++) {
52
+ const line = frame.lines[row] ?? "";
53
+ if (frame.rowWidths[row] > 0)
54
+ output += `${cursorAt(row, 0)}${line}${RESET}`;
55
+ }
56
+ return `${output}${SYNC_END}`;
57
+ }
58
+ let output = `${SYNC_BEGIN}${HIDE_CURSOR}${RESET}${canvasBackgroundAnsi}${CLEAR_SCREEN}${HOME}`;
49
59
  for (let row = 0; row < frame.rows; row++) {
50
60
  const line = frame.lines[row] ?? "";
51
- if (frame.rowWidths[row] > 0)
52
- output += `${cursorAt(row, 0)}${line}${RESET}`;
61
+ if (frame.rowWidths[row] > 0) {
62
+ output += `${cursorAt(row, 0)}${canvasBackgroundAnsi}`
63
+ + `${rebaseTerminalDefaultBackground(line, canvasBackgroundAnsi)}${RESET}${canvasBackgroundAnsi}`;
64
+ }
53
65
  }
54
- return `${output}${SYNC_END}`;
66
+ return `${output}${RESET}${SYNC_END}`;
55
67
  }
56
68
  /**
57
69
  * Plays the effect over the frame. Resolves true when the plan was played to
@@ -69,20 +81,23 @@ export async function playQuitOutro(frame, effect, durationMs, playback) {
69
81
  let sparkleIndex = 0;
70
82
  let clearIndex = 0;
71
83
  let ticks = 0;
72
- playback.write(createQuitOutroSurfaceFrame(frame));
84
+ const canvas = playback.canvasBackgroundAnsi;
85
+ playback.write(createQuitOutroSurfaceFrame(frame, canvas));
73
86
  const startedAt = now();
74
87
  for (;;) {
75
88
  ticks += 1;
76
89
  const elapsed = now() - startedAt;
77
90
  const progress = Math.min(1, elapsed / duration);
78
- let output = `${SYNC_BEGIN}${RESET}`;
91
+ let output = canvas === undefined ? `${SYNC_BEGIN}${RESET}` : `${SYNC_BEGIN}${RESET}${canvas}`;
79
92
  while (sparkleIndex < plan.sparkles.length && plan.sparkles[sparkleIndex].start <= progress) {
80
93
  const cell = plan.sparkles[sparkleIndex++];
81
94
  output += `${cursorAt(cell.row, cell.col)}${cell.color}${cell.glyph}`;
82
95
  }
83
96
  while (clearIndex < plan.clears.length && plan.clears[clearIndex].end <= progress) {
84
97
  const cell = plan.clears[clearIndex++];
85
- output += `${cursorAt(cell.row, cell.col)}${RESET} `;
98
+ output += canvas === undefined
99
+ ? `${cursorAt(cell.row, cell.col)}${RESET} `
100
+ : `${cursorAt(cell.row, cell.col)}${RESET}${canvas} `;
86
101
  }
87
102
  const finished = clearIndex >= plan.clears.length || progress >= 1
88
103
  || elapsed > duration + QUIT_OUTRO_GUARD_MS || ticks >= QUIT_OUTRO_MAX_TICKS;
@@ -1,3 +1,4 @@
1
+ import { HelperPool } from "./helper-pool.js";
1
2
  import type { SelectionCopySnapshot } from "../../ui/components/selection-copy.js";
2
3
  import { type CopyResult } from "./response-copy-protocol.js";
3
4
  export interface ResponseCopyJob {
@@ -8,7 +9,7 @@ export interface ResponseCopyJob {
8
9
  }
9
10
  export type CopyPhaseObserver = (phase: "extracted" | "encoded" | "submitting", bytes: number, transport: "native" | "terminal" | "injected") => void;
10
11
  export type ResponseCopyExecutor = (snapshot: SelectionCopySnapshot, phase: CopyPhaseObserver) => ResponseCopyJob;
11
- /** The production executor also owns a spare helper; the owner warms it at start and disposes it at shutdown. */
12
+ /** The production executor also holds a spare helper; the owner warms it at start and disposes it at shutdown unless the pool is shared. */
12
13
  export interface OwnedResponseCopyExecutor extends ResponseCopyExecutor {
13
14
  warm(): void;
14
15
  dispose(): void;
@@ -36,4 +37,8 @@ export declare function createResponseCopyExecutor(options?: {
36
37
  readonly helper?: URL;
37
38
  /** Idle bound for the spare helper; production keeps the default. */
38
39
  readonly spareIdleMs?: number;
40
+ /** Process-wide spare pool shared across sessions; the executor warms it but never disposes it. */
41
+ readonly pool?: HelperPool;
39
42
  }): OwnedResponseCopyExecutor;
43
+ /** A spare-of-one pool of copy helpers; `createResponseCopyExecutor` takes its child and warms the next one after each copy. */
44
+ export declare function createCopyHelperPool(helper?: URL, idleMs?: number): HelperPool;
@@ -23,7 +23,8 @@ export function hasAsyncClipboardOutput(platform = process.platform, stdout = pr
23
23
  }
24
24
  /** Isolates preparation and native clipboard access; only the owner may submit terminal controls. */
25
25
  export function createResponseCopyExecutor(options = {}) {
26
- const pool = new HelperPool({ fork: () => forkCopyHelper(options.helper), stop: stopCopyHelper, ...(options.spareIdleMs === undefined ? {} : { idleMs: options.spareIdleMs }) });
26
+ const pool = options.pool ?? createCopyHelperPool(options.helper, options.spareIdleMs);
27
+ const ownsPool = options.pool === undefined;
27
28
  const execute = (snapshot, phase) => {
28
29
  const controller = new AbortController();
29
30
  let active;
@@ -70,10 +71,15 @@ export function createResponseCopyExecutor(options = {}) {
70
71
  // Rationale: `Object.assign` would copy the getter's value once; the accessor must read the pool each time.
71
72
  return Object.defineProperties(execute, {
72
73
  warm: { value: () => pool.warm() },
73
- dispose: { value: () => pool.dispose() },
74
+ dispose: { value: () => { if (ownsPool)
75
+ pool.dispose(); } },
74
76
  warmed: { get: () => pool.warmed },
75
77
  });
76
78
  }
79
+ /** A spare-of-one pool of copy helpers; `createResponseCopyExecutor` takes its child and warms the next one after each copy. */
80
+ export function createCopyHelperPool(helper, idleMs) {
81
+ return new HelperPool({ fork: () => forkCopyHelper(helper), stop: stopCopyHelper, ...(idleMs === undefined ? {} : { idleMs }) });
82
+ }
77
83
  /** Forks one copy helper: bounded IPC only, no clipboard payload in argv or temp files, no inherited terminal descriptors. */
78
84
  function forkCopyHelper(helper) {
79
85
  const source = new URL(import.meta.url).pathname.endsWith(".ts");
@@ -1,23 +1,21 @@
1
1
  import { ImageAttachmentError } from "../../contracts/owned-ui/index.js";
2
- import type { OwnedUiPromptSuggestionGeneratorPort, OwnedUiSessionViewModel, OwnedUiViewportSettings, OwnedUiViewportSettingsPort, OwnedUiQuitOutroSettingsPort, SuggestionDecision, SuggestionDiagnosticObserver } from "../../contracts/owned-ui/index.js";
2
+ import type { OwnedUiBackgroundSettingsPort, OwnedUiPromptSuggestionGeneratorPort, OwnedUiSessionBackend, OwnedUiSessionPresenters, OwnedUiSessionViewModel, OwnedUiTranscriptRendererPort, OwnedUiWorkflowMessage, OwnedUiWorkflowResult, OwnedUiViewportSettings, OwnedUiViewportSettingsPort, OwnedUiQuitOutroSettingsPort, SuggestionDecision, SuggestionDiagnosticObserver } from "../../contracts/owned-ui/index.js";
3
3
  import type { PiTuiPointerSurface } from "../../integrations/pi/tui-runtime/contracts.js";
4
4
  import type { UiRouteHost, UiRouteInput } from "../../ui/apps/contracts.js";
5
5
  import type { TranscriptViewportFrame, TranscriptViewportFrameDescriptor } from "../../ui/components/transcript-viewport.js";
6
- import type { PiEngineAdapter } from "../../integrations/pi/engine/adapter.js";
7
- import type { PiWorkflowMessage, PiWorkflowResult } from "../../integrations/pi/engine/workflows.js";
8
6
  import type { PiShellLazySelectorLoader } from "../../integrations/pi/components/lazy-selectors.js";
9
7
  import { type PiShellHotkeysPresentation } from "../../integrations/pi/components/shell-presenters-info.js";
10
- import { type PiShellClipboardContent, type PiShellComponentPort, type PiShellEditorPort, type PiShellExtensionRendererResolver, type PiShellHeaderOptions, type PiShellHeaderPort, type PiShellImageAssetResolver, type PiShellLoadedResourcesPort, type PiShellResourceEntry, type PiShellTranscriptComponentPort } from "../../integrations/pi/components/shell-shared-facade.js";
8
+ import { type PiShellClipboardContent, type PiShellComponentPort, type PiKeybindingsHost, type PiShellEditorPort, type PiShellHeaderOptions, type PiShellHeaderPort, type PiShellImageAssetResolver, type PiShellLoadedResourcesPort, type PiShellResourceEntry, type PiShellTranscriptComponentPort } from "../../integrations/pi/components/shell-shared-facade.js";
11
9
  import { type PiTuiInputCoordinationScheduler, type PiTuiInputSurfaceKind } from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
12
10
  import type { PiTuiComponentPort, PiTuiInputDiagnosticsEvent, PiTuiLayoutNode, PiTuiTerminalPort } from "../../integrations/pi/tui-runtime/contracts.js";
13
11
  import { type PreparedPrompt } from "./prompt-chips.js";
14
12
  import type { PastePreparationClientOptions } from "./paste-preparation-client.js";
13
+ import type { OwnedUiClipboardServices } from "./clipboard-services.js";
15
14
  import { type SessionViewportInputResult } from "./session-viewport-controller.js";
16
15
  import type { ResponseCopyExecutor } from "./response-copy-transport.js";
17
16
  import type { ResponseCopyEvent } from "./response-copy-protocol.js";
18
17
  import type { PasteEvent, PasteSource } from "./paste-protocol.js";
19
18
  import type { StreamPresentationScheduler } from "./stream-presentation-coalescer.js";
20
- export type OwnedUiBackendPort = PiEngineAdapter;
21
19
  export type OwnedUiTerminalPort = PiTuiTerminalPort;
22
20
  type OwnedUiStartupOptions = PiShellHeaderOptions;
23
21
  export interface OwnedUiClipboardPort {
@@ -30,7 +28,7 @@ export interface OwnedUiClipboardPort {
30
28
  }
31
29
  /** The engine session the shell presents and the composition's decisions about it. */
32
30
  export interface OwnedUiShellEngineOptions {
33
- readonly backend: OwnedUiBackendPort;
31
+ readonly backend: OwnedUiSessionBackend;
34
32
  readonly cwd: string;
35
33
  /**
36
34
  * Declared A1-owned routes. A route this host claims resolves to its app;
@@ -52,6 +50,8 @@ export interface OwnedUiShellPresentationOptions {
52
50
  readonly startup?: OwnedUiStartupOptions;
53
51
  /** Live profile-local settings, supplied only to the bare-A1 composition. */
54
52
  readonly viewportSettings?: OwnedUiViewportSettingsPort;
53
+ /** Live fullscreen canvas choice, supplied only to the bare-A1 composition. */
54
+ readonly backgroundSettings?: OwnedUiBackgroundSettingsPort;
55
55
  /** Deterministic scheduling seam for presentation-cadence tests. */
56
56
  readonly stream?: {
57
57
  readonly intervalMs?: number;
@@ -130,9 +130,17 @@ export interface OwnedUiShellSkillsOptions {
130
130
  readonly presentation: () => "collapse" | "expand";
131
131
  readonly onChange: (listener: () => void) => () => void;
132
132
  }
133
+ /** Process-wide services composition creates once and every session shell borrows; absent, a shell keeps private ones. */
134
+ export interface OwnedUiShellSharedServices {
135
+ readonly clipboard?: OwnedUiClipboardServices;
136
+ /** Must match the shell's layout: the owned-input profile for the custom viewport, Pi's otherwise. */
137
+ readonly keybindings?: PiKeybindingsHost;
138
+ }
133
139
  /** What composes an owned session shell, grouped by the collaborator that provides each part. */
134
140
  export interface OwnedUiSessionShellOptions {
135
141
  readonly engine: OwnedUiShellEngineOptions;
142
+ /** Opens the selectors and resolves the transcript renderers whose inputs are engine objects. */
143
+ readonly presenters: OwnedUiSessionPresenters;
136
144
  readonly presentation?: OwnedUiShellPresentationOptions;
137
145
  readonly history?: OwnedUiShellHistoryOptions;
138
146
  readonly suggestions?: OwnedUiShellSuggestionOptions;
@@ -141,6 +149,7 @@ export interface OwnedUiSessionShellOptions {
141
149
  /** Supplied only to the bare-A1 composition; absent keeps the pinned per-skill command list. */
142
150
  readonly skills?: OwnedUiShellSkillsOptions;
143
151
  readonly diagnostics?: OwnedUiShellDiagnosticOptions;
152
+ readonly shared?: OwnedUiShellSharedServices;
144
153
  }
145
154
  /** Composes backend session state into the owned transcript, viewport, editor, and dock presentation. */
146
155
  export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
@@ -178,9 +187,10 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
178
187
  readonly captureClipboardPaste?: () => PasteSource;
179
188
  readonly pasteDiagnostics?: (event: PasteEvent) => void;
180
189
  readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent">;
190
+ readonly keybindings?: PiKeybindingsHost;
181
191
  readonly promptImageLimit?: () => number;
182
192
  readonly skillsPresentation?: () => "collapse" | "expand";
183
- }, startup?: PiShellHeaderOptions, agentDir?: string, extensionRenderers?: PiShellExtensionRendererResolver, sessionLayout?: "pinned" | "custom-viewport", imageAssets?: PiShellImageAssetResolver);
193
+ }, startup?: PiShellHeaderOptions, agentDir?: string, extensionRenderers?: OwnedUiTranscriptRendererPort, sessionLayout?: "pinned" | "custom-viewport", imageAssets?: PiShellImageAssetResolver);
184
194
  setEditorPaddingX(padding: number): void;
185
195
  setAutocompleteMaxVisible(maxVisible: number): void;
186
196
  setOutputPad(padding: 0 | 1): void;
@@ -266,8 +276,8 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
266
276
  transcriptComponent(id: string): PiShellTranscriptComponentPort | undefined;
267
277
  exitTranscript(width: number): string;
268
278
  appendWorkflowStatus(message: string): void;
269
- appendWorkflowMessage(message: PiWorkflowMessage, errorCode?: ImageAttachmentError["code"]): void;
270
- appendWorkflowResult(result: PiWorkflowResult, errorCode?: ImageAttachmentError["code"]): void;
279
+ appendWorkflowMessage(message: OwnedUiWorkflowMessage, errorCode?: ImageAttachmentError["code"]): void;
280
+ appendWorkflowResult(result: OwnedUiWorkflowResult, errorCode?: ImageAttachmentError["code"]): void;
271
281
  toggleThinkingVisibility(): void;
272
282
  get toolsExpanded(): boolean;
273
283
  setToolsExpanded(expanded: boolean): void;
@@ -296,5 +306,5 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
296
306
  setFocused(focused: boolean): void;
297
307
  dispose(): void;
298
308
  }
299
- export declare function shellResourceEntries(backend: OwnedUiBackendPort): readonly PiShellResourceEntry[];
309
+ export declare function shellResourceEntries(backend: OwnedUiSessionBackend): readonly PiShellResourceEntry[];
300
310
  export {};
@@ -5,11 +5,9 @@ import { composeSubmittedPromptRows, formatSubmittedPromptTime, submittedPromptL
5
5
  import { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, } from "../../ui/components/spans.js";
6
6
  import { progressStatusFrame, progressStatusText } from "../../ui/components/progress-status.js";
7
7
  import { displayWidth, faint, stripAnsi } from "../../ui/components/text.js";
8
- import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES } from "../../integrations/pi/engine/workflows.js";
9
- import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
10
8
  import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
11
9
  import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
12
- import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
10
+ import { createPiShellArmin, createPiShellEarendilAnnouncement, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
13
11
  import { createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellHotkeys, createPiShellSessionInfo, renderPiShellCommandMessage, renderPiShellPackageUpdateNotice, renderPiShellReleaseUpdateBanner, renderPiShellReleaseUpdateNotice, renderPiShellStatusText, } from "../../integrations/pi/components/shell-presenters-info.js";
14
12
  import { createPiShellTranscriptComponent, isPiPromptStyleCompaction, paintPiSubmittedPromptTimestamp, renderPiShellStartupDiagnostic, renderPiShellTranscriptBlock, } from "../../integrations/pi/components/shell-presenters-transcript.js";
15
13
  import { onPiThemeChange, PINNED_PI_LAYOUT, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
@@ -88,6 +86,7 @@ export class OwnedUiSessionShellRoot {
88
86
  constructor(view, cwd, handlers, startup = {}, agentDir, extensionRenderers = {
89
87
  getMessageRenderer: () => undefined,
90
88
  getToolRenderers: () => undefined,
89
+ getShortcuts: () => [],
91
90
  }, sessionLayout = "pinned", imageAssets) {
92
91
  this.#view = view;
93
92
  this.#customViewport = sessionLayout === "custom-viewport";
@@ -124,6 +123,7 @@ export class OwnedUiSessionShellRoot {
124
123
  this.editor = createPiShellEditor({
125
124
  ...handlers,
126
125
  keybindingProfile: this.#customViewport ? "a1" : "pi",
126
+ ...(handlers.keybindings === undefined ? {} : { keybindings: handlers.keybindings }),
127
127
  paintEditorSelection: (line, from, to, atomic) => backgroundSgrSpan(line, from, to, atomic ? "\u001b[7m" : "\u001b[27m\u001b[48;2;38;79;120m", atomic ? "\u001b[27m" : "\u001b[49m", piShellVisibleWidth),
128
128
  transformPastedContent: content => {
129
129
  try {
@@ -1071,7 +1071,7 @@ export class OwnedUiSessionShellRoot {
1071
1071
  hotkeysPresentation() {
1072
1072
  return {
1073
1073
  bindings: this.editor.keybindingConfig(),
1074
- getShortcuts: bindings => this.#extensionRenderers.getShortcuts?.(bindings) ?? [],
1074
+ getShortcuts: bindings => this.#extensionRenderers.getShortcuts(bindings),
1075
1075
  profile: this.#customViewport ? "a1" : "pi",
1076
1076
  };
1077
1077
  }
@@ -1381,7 +1381,7 @@ function layoutPort(render, invalidate, handleInput) {
1381
1381
  };
1382
1382
  }
1383
1383
  export function shellResourceEntries(backend) {
1384
- const resources = backend.nonVisualResources().map(resource => ({
1384
+ const resources = backend.extensions.nonVisualResources().map(resource => ({
1385
1385
  section: resource.kind === "skill"
1386
1386
  ? "Skills"
1387
1387
  : resource.kind === "prompt-template"
@@ -1397,7 +1397,7 @@ export function shellResourceEntries(backend) {
1397
1397
  sourcePath: resource.sourcePath,
1398
1398
  diagnostic: resource.diagnostic,
1399
1399
  }));
1400
- const extensions = backend.extensionResources().filter(extension => !extension.hidden);
1400
+ const extensions = backend.extensions.extensionResources().filter(extension => !extension.hidden);
1401
1401
  const loadedExtensions = extensions.filter(extension => extension.diagnostic === null);
1402
1402
  const extensionLabels = compactExtensionLabels(loadedExtensions);
1403
1403
  for (const extension of extensions) {