@timurproko/a1 0.2.5-dev.683 → 0.2.5-dev.704

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 (76) hide show
  1. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +2 -0
  2. package/dist/app/session-shell/prompt-suggestion-controller.js +10 -0
  3. package/dist/app/session-shell/session-shell-root.js +15 -2
  4. package/dist/app/session-shell/session-shell.js +80 -23
  5. package/dist/app/session-shell/share-operation.d.ts +3 -0
  6. package/dist/app/session-shell/share-operation.js +11 -0
  7. package/dist/composition/owned-ui.js +2 -0
  8. package/dist/composition/settings-route-host.js +1 -1
  9. package/dist/contracts/presentation/index.d.ts +11 -1
  10. package/dist/contracts/presentation/index.js +6 -2
  11. package/dist/features/owned-ui/project-trust-dialog.js +7 -5
  12. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -2
  13. package/dist/features/owned-ui/reference-screen-app.js +10 -6
  14. package/dist/features/owned-ui/settings-app.d.ts +1 -1
  15. package/dist/features/owned-ui/settings-app.js +342 -66
  16. package/dist/integrations/pi/components/index.d.ts +3 -2
  17. package/dist/integrations/pi/components/index.js +2 -1
  18. package/dist/integrations/pi/components/modal-frame.d.ts +11 -1
  19. package/dist/integrations/pi/components/modal-frame.js +29 -1
  20. package/dist/integrations/pi/components/models-dialog.js +19 -19
  21. package/dist/integrations/pi/components/share-operation-dialog.d.ts +6 -0
  22. package/dist/integrations/pi/components/share-operation-dialog.js +40 -0
  23. package/dist/integrations/pi/components/shell-editor-autocomplete.js +55 -8
  24. package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -0
  25. package/dist/integrations/pi/components/shell-extension-ui.js +8 -4
  26. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +0 -4
  27. package/dist/integrations/pi/components/shell-selectors-dialogs.js +56 -34
  28. package/dist/integrations/pi/components/shell-session-info-reference.js +35 -2
  29. package/dist/integrations/pi/components/shell-shared-facade.d.ts +3 -1
  30. package/dist/integrations/pi/components/shell-shared-facade.js +13 -1
  31. package/dist/integrations/pi/components/skills-dialog.js +21 -8
  32. package/dist/integrations/pi/components/theme.d.ts +8 -1
  33. package/dist/integrations/pi/components/theme.js +28 -1
  34. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +10 -2
  35. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +20 -6
  36. package/dist/integrations/pi/components/upstream/components/extension-editor.js +2 -2
  37. package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -5
  38. package/dist/integrations/pi/components/upstream/components/extension-selector.js +2 -3
  39. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +4 -2
  40. package/dist/integrations/pi/components/upstream/components/owned-editor.js +48 -12
  41. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -2
  42. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +4 -15
  43. package/dist/integrations/pi/components/upstream/components/session-selector.d.ts +2 -0
  44. package/dist/integrations/pi/components/upstream/components/session-selector.js +137 -110
  45. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +9 -5
  46. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +65 -26
  47. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +31 -21
  48. package/dist/integrations/pi/components/upstream/components/tree-selector.js +255 -214
  49. package/dist/integrations/pi/components/upstream/components/trust-selector.js +13 -15
  50. package/dist/integrations/pi/engine/adapter.js +21 -2
  51. package/dist/integrations/pi/engine/workflow-runner.js +2 -1
  52. package/dist/native/darwin-arm64/manifest.json +1 -1
  53. package/dist/native/linux-x64/manifest.json +1 -1
  54. package/dist/native/win32-x64/manifest.json +2 -2
  55. package/dist/native/win32-x64/process-guardian.exe +0 -0
  56. package/dist/ui/apps/host.d.ts +4 -3
  57. package/dist/ui/apps/host.js +9 -4
  58. package/dist/ui/components/dialog-panel.d.ts +15 -0
  59. package/dist/ui/components/dialog-panel.js +55 -9
  60. package/dist/ui/components/index.d.ts +3 -3
  61. package/dist/ui/components/index.js +2 -2
  62. package/dist/ui/components/line-input.d.ts +2 -0
  63. package/dist/ui/components/line-input.js +2 -2
  64. package/dist/ui/components/list-view.d.ts +3 -3
  65. package/dist/ui/components/list-view.js +8 -9
  66. package/dist/ui/components/prompt-input.d.ts +2 -2
  67. package/dist/ui/components/prompt-input.js +5 -5
  68. package/dist/ui/components/shortcut-hints.d.ts +10 -3
  69. package/dist/ui/components/shortcut-hints.js +42 -6
  70. package/dist/ui/components/shortcuts.d.ts +1 -1
  71. package/dist/ui/components/shortcuts.js +4 -0
  72. package/dist/ui/components/theme.d.ts +1 -1
  73. package/dist/ui/components/value-menu.js +3 -1
  74. package/dist/ui/settings/manager.d.ts +1 -0
  75. package/dist/ui/settings/manager.js +9 -3
  76. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, dialogValueColumn, numericValues, RAIL_COLUMNS, renderDialogPanel, renderEmptyState, renderGroupHeader, renderInputRow, renderListRow, renderNote, renderShortcutHints, dialogRowAt, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, isThumbRow, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
1
+ import { DIALOG_BACK_SHORTCUT_HINT, DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
+ import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, numericValues, RAIL_COLUMNS, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderInputRow, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, isThumbRow, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
2
3
  import { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
3
4
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
4
5
  const SCOPE = SETTINGS_APP_ID;
@@ -10,6 +11,8 @@ const SETTINGS_STATUS_ROWS = 1;
10
11
  const SETTINGS_CONTENT_INSET = 1;
11
12
  /** The panel a setting with parts opens: its own keys, its own hint. */
12
13
  const DIALOG_SCOPE = `${SETTINGS_APP_ID}-parts`;
14
+ const MODEL_SCOPE = `${SETTINGS_APP_ID}-models`;
15
+ const MODEL_LEVEL_SCOPE = `${SETTINGS_APP_ID}-model-levels`;
13
16
  const SCROLLBAR_TOP_INSET = 0;
14
17
  /** Identity of the settings list rail in the shared rail state. */
15
18
  const RAIL_KEY = "settings";
@@ -20,29 +23,43 @@ const SEARCH_PLACEHOLDER = "search settings";
20
23
  /** What a structured value offers instead of printing itself. */
21
24
  const CONFIGURE = "configure";
22
25
  export const SETTINGS_SHORTCUTS = new ShortcutRegistry();
23
- SETTINGS_SHORTCUTS.declare({ key: "/", scope: SCOPE, description: "Search settings", section: "Change", hint: { keys: "/", does: "to search" } }, "open-filter");
24
- SETTINGS_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Previous setting", section: "Navigate", hint: { keys: "↑↓", does: "to navigate" } }, "move-up");
25
- SETTINGS_SHORTCUTS.declare({ key: "down", scope: SCOPE, description: "Next setting", section: "Navigate", hint: { keys: "↑↓", does: "to navigate" } }, "move-down");
26
- SETTINGS_SHORTCUTS.declare({ key: "shift+up", scope: SCOPE, description: "Previous section", section: "Navigate", hint: { keys: "Shift+↑↓", does: "to jump" } }, "block-up");
27
- SETTINGS_SHORTCUTS.declare({ key: "shift+down", scope: SCOPE, description: "Next section", section: "Navigate", hint: { keys: "Shift+↑↓", does: "to jump" } }, "block-down");
26
+ SETTINGS_SHORTCUTS.declare({ key: "/", scope: SCOPE, description: "Search settings", section: "Change", hint: { keys: "/", does: "search" } }, "open-filter");
27
+ SETTINGS_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Previous setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-up");
28
+ SETTINGS_SHORTCUTS.declare({ key: "down", scope: SCOPE, description: "Next setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-down");
29
+ SETTINGS_SHORTCUTS.declare({ key: "shift+up", scope: SCOPE, description: "Previous section", section: "Navigate", hint: { keys: "Shift+↑↓", does: "jump" } }, "block-up");
30
+ SETTINGS_SHORTCUTS.declare({ key: "shift+down", scope: SCOPE, description: "Next section", section: "Navigate", hint: { keys: "Shift+↑↓", does: "jump" } }, "block-down");
28
31
  SETTINGS_SHORTCUTS.declare({ key: "pageUp", scope: SCOPE, description: "Up a page", section: "Navigate" }, "page-up");
29
32
  SETTINGS_SHORTCUTS.declare({ key: "pageDown", scope: SCOPE, description: "Down a page", section: "Navigate" }, "page-down");
30
33
  // Compatibility: the same chords the transcript uses for its content boundaries; plain
31
34
  // Home and End stay with the search input's cursor.
32
35
  SETTINGS_SHORTCUTS.declare({ key: "ctrl+home", scope: SCOPE, description: "First setting", section: "Navigate" }, "first");
33
36
  SETTINGS_SHORTCUTS.declare({ key: "ctrl+end", scope: SCOPE, description: "Last setting", section: "Navigate" }, "last");
34
- SETTINGS_SHORTCUTS.declare({ key: "enter", scope: SCOPE, description: "Change value", section: "Change", hint: { keys: "Enter/Space", does: "to change" } }, "activate");
35
- SETTINGS_SHORTCUTS.declare({ key: "space", scope: SCOPE, description: "Change value", section: "Change", hint: { keys: "Enter/Space", does: "to change" } }, "activate");
36
- SETTINGS_SHORTCUTS.declare({ key: "left", scope: SCOPE, description: "Previous value", section: "Change", hint: { keys: "←→", does: "to adjust" } }, "previous-value");
37
- SETTINGS_SHORTCUTS.declare({ key: "right", scope: SCOPE, description: "Next value", section: "Change", hint: { keys: "←→", does: "to adjust" } }, "next-value");
38
- SETTINGS_SHORTCUTS.declare({ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen", hint: { keys: "Esc", does: "to cancel" } }, "close");
39
- SETTINGS_SHORTCUTS.declare({ key: "enter", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts", hint: { keys: "Enter/Space", does: "to change" } }, "part-change");
40
- SETTINGS_SHORTCUTS.declare({ key: "space", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts", hint: { keys: "Enter/Space", does: "to change" } }, "part-change");
37
+ SETTINGS_SHORTCUTS.declare({ key: "enter", scope: SCOPE, description: "Change value", section: "Change", hint: { keys: "Enter", does: "change" } }, "activate");
38
+ SETTINGS_SHORTCUTS.declare({ key: "left", scope: SCOPE, description: "Previous value", section: "Change", hint: { keys: "←→", does: "adjust" } }, "previous-value");
39
+ SETTINGS_SHORTCUTS.declare({ key: "right", scope: SCOPE, description: "Next value", section: "Change", hint: { keys: "←→", does: "adjust" } }, "next-value");
40
+ SETTINGS_SHORTCUTS.declare({ key: "ctrl+z", scope: SCOPE, description: "Undo setting change", section: "Change", hint: { keys: "Ctrl+Z", does: "undo" } }, "undo");
41
+ SETTINGS_SHORTCUTS.declare({ key: "enter", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts", hint: { keys: "Enter", does: "change" } }, "part-change");
41
42
  SETTINGS_SHORTCUTS.declare({ key: "left", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts" }, "part-change");
42
43
  SETTINGS_SHORTCUTS.declare({ key: "right", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts" }, "part-change");
43
44
  SETTINGS_SHORTCUTS.declare({ key: "up", scope: DIALOG_SCOPE, description: "Previous part", section: "Parts" }, "part-previous");
44
45
  SETTINGS_SHORTCUTS.declare({ key: "down", scope: DIALOG_SCOPE, description: "Next part", section: "Parts" }, "part-next");
46
+ SETTINGS_SHORTCUTS.declare({ key: "ctrl+z", scope: DIALOG_SCOPE, description: "Undo setting change", section: "Parts", hint: { keys: "Ctrl+Z", does: "undo" } }, "undo");
47
+ SETTINGS_SHORTCUTS.declare({
48
+ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen",
49
+ hint: { keys: DIALOG_CLOSE_SHORTCUT_HINT.key, does: DIALOG_CLOSE_SHORTCUT_HINT.action },
50
+ }, "close");
45
51
  assertNoShortcutConflicts(SETTINGS_SHORTCUTS.assemble());
52
+ const MODEL_SHORTCUTS = new ShortcutRegistry();
53
+ MODEL_SHORTCUTS.declare({ key: "edit", scope: MODEL_SCOPE, description: "Search models", section: "Choose", hint: { keys: "Type", does: "search" } }, "edit-filter");
54
+ MODEL_SHORTCUTS.declare({ key: "up", scope: MODEL_SCOPE, description: "Previous model", section: "Choose" }, "previous");
55
+ MODEL_SHORTCUTS.declare({ key: "down", scope: MODEL_SCOPE, description: "Next model", section: "Choose" }, "next");
56
+ MODEL_SHORTCUTS.declare({ key: "enter", scope: MODEL_SCOPE, description: "Select model", section: "Choose", hint: { keys: "Enter", does: "select" } }, "select");
57
+ MODEL_SHORTCUTS.declare({ key: "escape", scope: MODEL_SCOPE, description: "Close selector", section: "Choose", hint: { keys: DIALOG_CLOSE_SHORTCUT_HINT.key, does: DIALOG_CLOSE_SHORTCUT_HINT.action } }, "close");
58
+ MODEL_SHORTCUTS.declare({ key: "up", scope: MODEL_LEVEL_SCOPE, description: "Previous level", section: "Choose", hint: { keys: "↑↓", does: "navigate" } }, "previous");
59
+ MODEL_SHORTCUTS.declare({ key: "down", scope: MODEL_LEVEL_SCOPE, description: "Next level", section: "Choose", hint: { keys: "↑↓", does: "navigate" } }, "next");
60
+ MODEL_SHORTCUTS.declare({ key: "enter", scope: MODEL_LEVEL_SCOPE, description: "Select level", section: "Choose", hint: { keys: "Enter", does: "select" } }, "select");
61
+ MODEL_SHORTCUTS.declare({ key: "escape", scope: MODEL_LEVEL_SCOPE, description: "Return to models", section: "Choose", hint: { keys: DIALOG_BACK_SHORTCUT_HINT.key, does: DIALOG_BACK_SHORTCUT_HINT.action } }, "back");
62
+ assertNoShortcutConflicts(MODEL_SHORTCUTS.assemble());
46
63
  const KEYS = {
47
64
  "\u001b[A": "up",
48
65
  "\u001b[B": "down",
@@ -62,6 +79,7 @@ const KEYS = {
62
79
  "\n": "enter",
63
80
  " ": "space",
64
81
  "/": "/",
82
+ "\u001a": "ctrl+z",
65
83
  };
66
84
  /**
67
85
  * The settings screen. Rows, sticky headers, scrolling, and the keymap come from
@@ -84,10 +102,13 @@ export class SettingsApp {
84
102
  #interruptArmed = false;
85
103
  // Invariant: pending values remain visible until the source reflects them.
86
104
  #pending = new Map();
87
- #dialogValueColumn = 0;
105
+ #undoHistory = [];
106
+ #editSequence = 0;
107
+ #undoRequests = 0;
108
+ #undoActive = false;
109
+ #closed = false;
88
110
  #bodyTopForFrame = SETTINGS_TOP_RULE_ROWS + SETTINGS_TITLE_ROWS;
89
111
  #bodyHeightForFrame = 0;
90
- #panelTop = 0;
91
112
  #panelTopForFrame = 0;
92
113
  #hoverKey = null;
93
114
  #hoverRegion = "label";
@@ -105,9 +126,13 @@ export class SettingsApp {
105
126
  this.#session = session;
106
127
  }
107
128
  onActivate(host) {
129
+ this.#closed = false;
108
130
  void this.#session.load().then(() => host.requestRender());
109
131
  }
110
132
  onClose(_host) {
133
+ this.#closed = true;
134
+ this.#undoHistory.length = 0;
135
+ this.#undoRequests = 0;
111
136
  this.#clearActivityTimer();
112
137
  this.#rails.clear();
113
138
  }
@@ -117,7 +142,9 @@ export class SettingsApp {
117
142
  const rows = this.#rows();
118
143
  const selected = indexOfKey(rows, this.#selectedKey);
119
144
  const footer = this.#footerLines(rect.width, theme);
120
- const dividerRows = this.#filter === null ? SETTINGS_FOOTER_DIVIDER_ROWS : 0;
145
+ // Invariant: the shared search input and structured panel each begin with their own standard rule.
146
+ // That rule replaces the ordinary content/footer divider instead of stacking beneath it.
147
+ const dividerRows = this.#filter === null && this.#structured === null ? SETTINGS_FOOTER_DIVIDER_ROWS : 0;
121
148
  const topRows = Math.min(SETTINGS_TOP_RULE_ROWS, rect.height);
122
149
  const contentHeight = Math.max(0, rect.height - topRows - dividerRows - footer.length);
123
150
  let titleRows = this.#scroll <= 0 ? Math.min(SETTINGS_TITLE_ROWS, contentHeight) : 0;
@@ -140,7 +167,6 @@ export class SettingsApp {
140
167
  this.#bodyTopForFrame = topRows + titleRows;
141
168
  this.#bodyHeightForFrame = bodyHeight;
142
169
  this.#panelTopForFrame = topRows + contentHeight + dividerRows;
143
- this.#panelTop = this.#panelTopForFrame;
144
170
  this.#scroll = layout.scroll;
145
171
  const now = Date.now();
146
172
  // Rationale: every way of scrolling ends in this frame, so a moved list is noticed here
@@ -274,6 +300,9 @@ export class SettingsApp {
274
300
  case "next-value":
275
301
  this.#cycle(rows, selected, action === "next-value" ? 1 : -1);
276
302
  return { consumed: true };
303
+ case "undo":
304
+ this.#undo();
305
+ return { consumed: true };
277
306
  default:
278
307
  return { consumed: false };
279
308
  }
@@ -314,30 +343,9 @@ export class SettingsApp {
314
343
  this.#apply(menu.entry, value);
315
344
  return { consumed: true };
316
345
  }
317
- const open = this.#structured;
318
- if (open !== null) {
319
- // Invariant: the panel owns the pointer while it is open; its flag rows are the targets.
320
- // The panel's rows begin one line below its rule.
321
- const panel = { firstRow: this.#panelTop + 1, rows: open.flags.length, valueColumn: this.#dialogValueColumn };
322
- const row = event.row - 1 - panel.firstRow;
323
- if (row < 0 || row >= panel.rows)
324
- return { consumed: true, render: false };
325
- if (event.kind === "motion") {
326
- if (open.index === row)
327
- return { consumed: true, render: false };
328
- open.index = row;
329
- return { consumed: true, render: true };
330
- }
331
- if (event.kind === "press") {
332
- open.index = row;
333
- // Rationale: pointing at the label picks the row; the value is what changes it,
334
- // exactly as in the list behind the dialog.
335
- const key = open.flags[row] ?? "";
336
- const width = displayWidth((open.record[key] ?? false) ? "true" : "false");
337
- if (dialogRowAt(panel, event.row - 1, event.column, width) !== null)
338
- this.#toggleFlag(row);
339
- return { consumed: true };
340
- }
346
+ if (this.#structured !== null) {
347
+ // Invariant: the structured surface is keyboard-only. It owns every pointer report so
348
+ // neither its selection nor the Settings list behind it can react.
341
349
  return { consumed: true, render: false };
342
350
  }
343
351
  if (event.kind === "wheel-up" || event.kind === "wheel-down") {
@@ -451,8 +459,9 @@ export class SettingsApp {
451
459
  return;
452
460
  }
453
461
  const current = shown === null ? 0 : Math.max(0, entry.choices.indexOf(shown));
454
- // Compatibility: pinned SelectList opens on the value currently in effect.
455
- this.#menu = { entry, current, anchorKey: `${entry.backend}:${entry.id}`, choices: entry.choices, index: current };
462
+ // Invariant: the opening press targeted the setting value, not an option. Keep the
463
+ // effective value as the keyboard origin without painting it as pointer-picked.
464
+ this.#menu = { entry, current, anchorKey: `${entry.backend}:${entry.id}`, choices: entry.choices, index: -1 };
456
465
  }
457
466
  // Rationale: a structured setting opens as its own flag list rather than a value menu.
458
467
  #openStructured(entry) {
@@ -477,12 +486,25 @@ export class SettingsApp {
477
486
  this.#hoverKey = null;
478
487
  this.#hoverRegion = "label";
479
488
  this.#rails.clear();
480
- this.#structured = { entry, flags: entry.flags.map(flag => flag.key), index: 0, record };
489
+ const modelDialog = entry.backend === "agent" && entry.id === "modelThinkingLevels";
490
+ this.#structured = {
491
+ entry,
492
+ flags: entry.flags.map(flag => flag.key),
493
+ modelDialog,
494
+ filter: modelDialog ? new LineInput("") : null,
495
+ step: "model",
496
+ modelKey: null,
497
+ modelIndex: 0,
498
+ index: 0,
499
+ record,
500
+ };
481
501
  }
482
502
  #structuredKey(data) {
483
503
  const open = this.#structured;
484
504
  if (open === null)
485
505
  return { consumed: false };
506
+ if (open.modelDialog)
507
+ return this.#modelDialogKey(open, data);
486
508
  switch (SETTINGS_SHORTCUTS.resolve(KEYS[data] ?? data, DIALOG_SCOPE)) {
487
509
  case "close":
488
510
  this.#structured = null;
@@ -496,15 +518,94 @@ export class SettingsApp {
496
518
  case "part-change":
497
519
  this.#toggleFlag(open.index);
498
520
  return { consumed: true };
521
+ case "undo":
522
+ this.#undo();
523
+ return { consumed: true };
499
524
  default:
500
525
  return { consumed: true, render: false };
501
526
  }
502
527
  }
528
+ #modelDialogKey(open, data) {
529
+ const key = KEYS[data] ?? data;
530
+ if (open.step === "model") {
531
+ const flags = this.#filteredModelFlags(open);
532
+ const action = MODEL_SHORTCUTS.resolve(key, MODEL_SCOPE) ?? "edit-filter";
533
+ if (action === "close") {
534
+ this.#structured = null;
535
+ return { consumed: true };
536
+ }
537
+ if (action === "previous" || action === "next") {
538
+ open.index = Math.min(Math.max(0, flags.length - 1), Math.max(0, open.index + (action === "next" ? 1 : -1)));
539
+ return { consumed: true };
540
+ }
541
+ if (action === "select") {
542
+ const selected = flags[open.index];
543
+ if (selected === undefined)
544
+ return { consumed: true, render: false };
545
+ open.modelIndex = open.index;
546
+ open.modelKey = selected.key;
547
+ open.step = "level";
548
+ const options = this.#modelLevelOptions(open, selected);
549
+ const current = structuredValue(open.entry, open.record)[selected.key];
550
+ open.index = Math.max(0, options.findIndex(option => option.value === current));
551
+ return { consumed: true };
552
+ }
553
+ const input = open.filter;
554
+ if (input === null)
555
+ return { consumed: true, render: false };
556
+ handleLineInputKey(input, data);
557
+ open.index = 0;
558
+ return { consumed: true };
559
+ }
560
+ const selected = open.entry.flags.find(flag => flag.key === open.modelKey);
561
+ const choices = selected === undefined ? [] : this.#modelLevelOptions(open, selected);
562
+ const action = MODEL_SHORTCUTS.resolve(key, MODEL_LEVEL_SCOPE);
563
+ if (action === "back") {
564
+ open.step = "model";
565
+ open.modelKey = null;
566
+ open.index = open.modelIndex;
567
+ return { consumed: true };
568
+ }
569
+ if (action === "previous" || action === "next") {
570
+ open.index = Math.min(Math.max(0, choices.length - 1), Math.max(0, open.index + (action === "next" ? 1 : -1)));
571
+ return { consumed: true };
572
+ }
573
+ if (action === "select") {
574
+ const choice = choices[open.index];
575
+ if (selected === undefined || choice === undefined)
576
+ return { consumed: true, render: false };
577
+ const previous = { ...open.record };
578
+ open.record[selected.key] = choice.value;
579
+ const forward = { ...open.record };
580
+ this.#saveStructured(open, previous, forward);
581
+ open.step = "model";
582
+ open.modelKey = null;
583
+ open.index = open.modelIndex;
584
+ return { consumed: true };
585
+ }
586
+ return { consumed: true, render: false };
587
+ }
588
+ #filteredModelFlags(open) {
589
+ const needle = open.filter?.value.trim().toLowerCase() ?? "";
590
+ return open.entry.flags.filter(flag => needle.length === 0
591
+ || (flag.label ?? humanizeLabel(flag.key)).toLowerCase().includes(needle)
592
+ || flag.key.toLowerCase().includes(needle));
593
+ }
594
+ #modelLevelOptions(open, selected) {
595
+ const fallback = String(selected.fallback);
596
+ const supported = (selected.choices ?? [])
597
+ .filter(choice => choice !== fallback)
598
+ .map(choice => ({ value: choice, label: choice, description: thinkingLevelDescription(choice) }));
599
+ return structuredValue(open.entry, open.record)[selected.key] === undefined
600
+ ? supported
601
+ : [...supported, { value: fallback, label: "(clear override)", description: "Revert to global default" }];
602
+ }
503
603
  #toggleFlag(index) {
504
604
  const open = this.#structured;
505
605
  const flag = open?.flags[index];
506
606
  if (open === null || open === undefined || flag === undefined)
507
607
  return;
608
+ const previous = { ...open.record };
508
609
  const declared = open.entry.flags.find(candidate => candidate.key === flag);
509
610
  if (declared?.choices === undefined)
510
611
  open.record[flag] = !(open.record[flag] ?? false);
@@ -513,13 +614,24 @@ export class SettingsApp {
513
614
  const at = declared.choices.indexOf(String(open.record[flag] ?? declared.fallback));
514
615
  open.record[flag] = declared.choices[(at + 1) % declared.choices.length] ?? declared.fallback;
515
616
  }
516
- // Invariant: a choice part at its fallback is unset: it is not written, so the engine's default applies.
517
- const next = Object.fromEntries(Object.entries(open.record).filter(([key, value]) => {
518
- const part = open.entry.flags.find(candidate => candidate.key === key);
519
- return part?.choices === undefined || value !== part.fallback;
520
- }));
521
- void this.#session.changeStructured(open.entry.backend, open.entry.id, next).then(outcome => {
522
- this.#notice = outcome.failure === null ? null : `Could not save ${labelOf(open.entry)}: ${outcome.failure}`;
617
+ const forward = { ...open.record };
618
+ this.#saveStructured(open, previous, forward);
619
+ }
620
+ #saveStructured(open, previous, forward) {
621
+ const sequence = ++this.#editSequence;
622
+ void this.#session.changeStructured(open.entry.backend, open.entry.id, structuredValue(open.entry, forward)).then(outcome => {
623
+ const failure = changeFailure(outcome);
624
+ if (failure === null) {
625
+ this.#recordUndo({ kind: "structured", sequence, entry: open.entry, previous, forward });
626
+ this.#notice = outcome.status === "deferred" && outcome.application !== null
627
+ ? `${labelOf(open.entry)} is stored and applies ${applicationLabel(outcome.application)}`
628
+ : null;
629
+ return;
630
+ }
631
+ // Concurrency: only roll back the dialog if no later press has moved the record on.
632
+ if (this.#structured === open && sameRecord(open.record, forward))
633
+ replaceRecord(open.record, previous);
634
+ this.#notice = `Could not save ${labelOf(open.entry)}: ${failure}`;
523
635
  });
524
636
  }
525
637
  #menuKey(data) {
@@ -527,6 +639,11 @@ export class SettingsApp {
527
639
  if (menu === null)
528
640
  return { consumed: false };
529
641
  const key = KEYS[data] ?? data;
642
+ if (SETTINGS_SHORTCUTS.resolve(key, SCOPE) === "undo") {
643
+ this.#menu = null;
644
+ this.#undo();
645
+ return { consumed: true };
646
+ }
530
647
  if (key === "escape") {
531
648
  this.#menu = null;
532
649
  return { consumed: true };
@@ -550,6 +667,10 @@ export class SettingsApp {
550
667
  if (input === null)
551
668
  return { consumed: false };
552
669
  const key = KEYS[data];
670
+ if (SETTINGS_SHORTCUTS.resolve(key ?? data, SCOPE) === "undo") {
671
+ this.#undo();
672
+ return { consumed: true };
673
+ }
553
674
  // Rationale: the boundary chords jump through the results; plain Home and End stay with
554
675
  // the search cursor, which the shared line input moves below.
555
676
  if (key === "ctrl+home" || key === "ctrl+end") {
@@ -627,20 +748,21 @@ export class SettingsApp {
627
748
  }
628
749
  #apply(entry, value) {
629
750
  const key = `${entry.backend}:${entry.id}`;
751
+ const previous = this.#shownValue(entry);
752
+ const sequence = ++this.#editSequence;
630
753
  // Invariant: shown immediately so the row never lags a keypress, and so the next press
631
754
  // steps from here rather than from a value the source has not caught up to.
632
755
  this.#pending.set(key, value);
633
756
  void this.#session.change(entry.backend, entry.id, value).then(outcome => {
634
- if (outcome.failure !== null || outcome.status === "failed") {
635
- this.#pending.delete(key);
636
- this.#notice = `Could not save ${labelOf(entry)}: ${outcome.failure ?? "the effect failed"}`;
637
- return;
638
- }
639
- if (outcome.status === "unavailable" || outcome.limitationReason !== null) {
640
- this.#pending.delete(key);
641
- this.#notice = outcome.limitationReason ?? `${labelOf(entry)} is unavailable`;
757
+ const failure = changeFailure(outcome);
758
+ if (failure !== null) {
759
+ if (this.#pending.get(key) === value)
760
+ this.#pending.delete(key);
761
+ this.#notice = `Could not save ${labelOf(entry)}: ${failure}`;
642
762
  return;
643
763
  }
764
+ if (previous !== null && previous !== value)
765
+ this.#recordUndo({ kind: "scalar", sequence, entry, previous });
644
766
  // Concurrency: a later press may have moved on; only the last request clears itself.
645
767
  if (this.#pending.get(key) === value)
646
768
  this.#pending.delete(key);
@@ -652,6 +774,64 @@ export class SettingsApp {
652
774
  #shownValue(entry) {
653
775
  return this.#pending.get(`${entry.backend}:${entry.id}`) ?? entry.value;
654
776
  }
777
+ #recordUndo(record) {
778
+ if (this.#closed)
779
+ return;
780
+ const after = this.#undoHistory.findIndex(candidate => candidate.sequence > record.sequence);
781
+ if (after < 0)
782
+ this.#undoHistory.push(record);
783
+ else
784
+ this.#undoHistory.splice(after, 0, record);
785
+ }
786
+ #undo() {
787
+ this.#undoRequests++;
788
+ this.#drainUndo();
789
+ }
790
+ #drainUndo() {
791
+ if (this.#undoActive || this.#undoRequests === 0)
792
+ return;
793
+ const record = this.#undoHistory.pop();
794
+ if (record === undefined) {
795
+ this.#undoRequests = 0;
796
+ return;
797
+ }
798
+ this.#undoRequests--;
799
+ this.#undoActive = true;
800
+ if (record.kind === "scalar") {
801
+ const key = `${record.entry.backend}:${record.entry.id}`;
802
+ this.#pending.set(key, record.previous);
803
+ void this.#session.change(record.entry.backend, record.entry.id, record.previous).then(outcome => {
804
+ if (this.#pending.get(key) === record.previous)
805
+ this.#pending.delete(key);
806
+ this.#finishUndo(record, outcome);
807
+ });
808
+ return;
809
+ }
810
+ const open = this.#structured;
811
+ if (open !== null && sameEntry(open.entry, record.entry))
812
+ replaceRecord(open.record, record.previous);
813
+ void this.#session.changeStructured(record.entry.backend, record.entry.id, structuredValue(record.entry, record.previous)).then(outcome => {
814
+ const failure = changeFailure(outcome);
815
+ if (failure !== null && this.#structured === open && open !== null && sameRecord(open.record, record.previous)) {
816
+ replaceRecord(open.record, record.forward);
817
+ }
818
+ this.#finishUndo(record, outcome);
819
+ });
820
+ }
821
+ #finishUndo(record, outcome) {
822
+ const failure = changeFailure(outcome);
823
+ if (failure !== null) {
824
+ this.#recordUndo(record);
825
+ this.#notice = `Could not restore ${labelOf(record.entry)}: ${failure}`;
826
+ }
827
+ else {
828
+ this.#notice = outcome.status === "deferred" && outcome.application !== null
829
+ ? `${labelOf(record.entry)} is restored and applies ${applicationLabel(outcome.application)}`
830
+ : null;
831
+ }
832
+ this.#undoActive = false;
833
+ this.#drainUndo();
834
+ }
655
835
  #scrollbarSpeed() {
656
836
  const value = this.#scrollSetting("scrollbarSpeed");
657
837
  return isScrollbarSpeed(value) ? value : "normal";
@@ -739,15 +919,16 @@ export class SettingsApp {
739
919
  return ` ${renderListRow(this.#viewRow(entry), { selected, hovered, region: this.#hoverRegion }, valueColumn, innerWidth, theme)}`;
740
920
  }
741
921
  #viewRow(entry) {
922
+ const key = `${entry.backend}:${entry.id}`;
742
923
  const shown = this.#shownValue(entry);
743
924
  const value = entry.structured
744
925
  ? CONFIGURE
745
926
  : shown === null
746
927
  ? describeRaw(entry.rawValue)
747
- : effectiveDisplay(entry, shown);
928
+ : this.#pending.has(key) ? displayValue(shown) : effectiveDisplay(entry, shown);
748
929
  const range = rangeOf(entry);
749
930
  return {
750
- key: `${entry.backend}:${entry.id}`,
931
+ key,
751
932
  label: labelOf(entry),
752
933
  value,
753
934
  ...(typeof shown === "number" && entry.editable ? { stepper: stepperEnds(range, shown) } : {}),
@@ -765,7 +946,10 @@ export class SettingsApp {
765
946
  current: this.#shownValue(menu.entry) === null ? null : displayValue(this.#shownValue(menu.entry)),
766
947
  index: menu.index,
767
948
  };
768
- const frame = valueMenuFrame(state, { screenRow: anchor.screenRow, valueColumn }, {
949
+ const frame = valueMenuFrame(state, {
950
+ screenRow: anchor.screenRow,
951
+ valueColumn: valueColumn + SETTINGS_CONTENT_INSET,
952
+ }, {
769
953
  bodyTop: this.#bodyTopForFrame,
770
954
  bodyHeight: this.#bodyHeightForFrame,
771
955
  surfaceWidth: rect.width,
@@ -775,6 +959,8 @@ export class SettingsApp {
775
959
  return renderValueMenu(lines, state, frame, theme);
776
960
  }
777
961
  #dialogLines(open, width, theme) {
962
+ if (open.modelDialog)
963
+ return this.#modelDialogLines(open, width, theme);
778
964
  const rows = open.flags.map(key => {
779
965
  const declared = open.entry.flags.find(flag => flag.key === key);
780
966
  return {
@@ -784,9 +970,46 @@ export class SettingsApp {
784
970
  ...(declared?.description === undefined ? {} : { description: declared.description }),
785
971
  };
786
972
  });
787
- this.#dialogValueColumn = dialogValueColumn(rows);
788
- this.#panelTop = this.#panelTopForFrame;
789
- return renderDialogPanel({ rows, index: open.index, hint: SETTINGS_SHORTCUTS.hintEntries(DIALOG_SCOPE) }, width, theme);
973
+ return renderDialogPanel({ title: labelOf(open.entry), rows, index: open.index, hint: SETTINGS_SHORTCUTS.hintEntries(DIALOG_SCOPE) }, width, theme);
974
+ }
975
+ #modelDialogLines(open, width, theme) {
976
+ if (open.step === "model") {
977
+ const flags = this.#filteredModelFlags(open);
978
+ const stored = structuredValue(open.entry, open.record);
979
+ const allRows = flags.length === 0
980
+ ? [{ label: "No models found", value: "" }]
981
+ : flags.map(flag => ({
982
+ ...modelLabelParts(flag.label ?? humanizeLabel(flag.key)),
983
+ value: stored[flag.key] === undefined ? "" : String(stored[flag.key]),
984
+ }));
985
+ const visible = dialogWindow(allRows, open.index, 10);
986
+ return renderSteppedDialogPanel({
987
+ title: "Thinking Level",
988
+ step: 1,
989
+ steps: 2,
990
+ description: "Select a model to configure",
991
+ ...(open.filter === null ? {} : { input: open.filter }),
992
+ rows: visible.rows,
993
+ index: visible.index,
994
+ hint: MODEL_SHORTCUTS.hintEntries(MODEL_SCOPE),
995
+ }, width, theme);
996
+ }
997
+ const selected = open.entry.flags.find(flag => flag.key === open.modelKey);
998
+ const current = selected === undefined ? undefined : structuredValue(open.entry, open.record)[selected.key];
999
+ const allRows = (selected === undefined ? [] : this.#modelLevelOptions(open, selected)).map(choice => ({
1000
+ label: choice.value === current ? `✓ ${choice.label}` : choice.label,
1001
+ value: choice.description,
1002
+ }));
1003
+ const visible = dialogWindow(allRows, open.index, 10);
1004
+ return renderSteppedDialogPanel({
1005
+ title: "Thinking Level",
1006
+ step: 2,
1007
+ steps: 2,
1008
+ description: `Select default thinking level for ${selected?.label ?? open.modelKey ?? "model"}`,
1009
+ rows: visible.rows,
1010
+ index: visible.index,
1011
+ hint: MODEL_SHORTCUTS.hintEntries(MODEL_LEVEL_SCOPE),
1012
+ }, width, theme);
790
1013
  }
791
1014
  #footerLines(width, theme) {
792
1015
  const open = this.#structured;
@@ -797,7 +1020,7 @@ export class SettingsApp {
797
1020
  ? notice
798
1021
  : this.#interruptArmed ? "press ctrl+c again to exit a1" : null;
799
1022
  const status = plainStatus === null
800
- ? truncateToWidth(renderShortcutHints(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width > 0 ? 1 : 0), width)
1023
+ ? renderShortcutHintsWithClose(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width, width > 0 ? 1 : 0)
801
1024
  : truncateToWidth(`${width > 0 ? " " : ""}${theme.fg("dim", plainStatus)}`, width);
802
1025
  const input = this.#filter;
803
1026
  if (input === null)
@@ -805,6 +1028,59 @@ export class SettingsApp {
805
1028
  return [...renderInputRow(input, width, { placeholder: SEARCH_PLACEHOLDER, theme }).lines, status];
806
1029
  }
807
1030
  }
1031
+ function modelLabelParts(label) {
1032
+ const match = /^(.*?)(\s+\[[^\]]+\])$/u.exec(label);
1033
+ return match === null ? { label } : { label: match[1] ?? label, labelSuffix: match[2] ?? "" };
1034
+ }
1035
+ function dialogWindow(rows, index, maximum) {
1036
+ if (rows.length <= maximum)
1037
+ return { rows, index: Math.min(Math.max(0, rows.length - 1), Math.max(0, index)) };
1038
+ const start = Math.min(rows.length - maximum, Math.max(0, index - Math.floor(maximum / 2)));
1039
+ return { rows: rows.slice(start, start + maximum), index: index - start };
1040
+ }
1041
+ function thinkingLevelDescription(level) {
1042
+ switch (level) {
1043
+ case "default": return "global default";
1044
+ case "off": return "No reasoning";
1045
+ case "minimal": return "Very brief reasoning (~1k tokens)";
1046
+ case "low": return "Light reasoning (~2k tokens)";
1047
+ case "medium": return "Moderate reasoning (~8k tokens)";
1048
+ case "high": return "Deep reasoning (~16k tokens)";
1049
+ case "xhigh": return "Extra-high reasoning (~32k tokens)";
1050
+ case "max": return "Maximum reasoning";
1051
+ default: return "";
1052
+ }
1053
+ }
1054
+ function structuredValue(entry, record) {
1055
+ // Invariant: a choice part at its fallback is unset, so the engine's default applies.
1056
+ return Object.fromEntries(Object.entries(record).filter(([key, value]) => {
1057
+ const part = entry.flags.find(candidate => candidate.key === key);
1058
+ return part?.choices === undefined || value !== part.fallback;
1059
+ }));
1060
+ }
1061
+ function sameEntry(left, right) {
1062
+ return left.backend === right.backend && left.id === right.id;
1063
+ }
1064
+ function sameRecord(left, right) {
1065
+ const leftEntries = Object.entries(left);
1066
+ const rightEntries = Object.entries(right);
1067
+ return leftEntries.length === rightEntries.length
1068
+ && leftEntries.every(([key, value]) => right[key] === value);
1069
+ }
1070
+ function replaceRecord(target, source) {
1071
+ for (const key of Object.keys(target))
1072
+ delete target[key];
1073
+ Object.assign(target, source);
1074
+ }
1075
+ function changeFailure(outcome) {
1076
+ if (outcome.failure !== null)
1077
+ return outcome.failure;
1078
+ if (outcome.status === "failed")
1079
+ return "the effect failed";
1080
+ if (outcome.status === "unavailable")
1081
+ return outcome.limitationReason ?? "the setting is unavailable";
1082
+ return outcome.limitationReason;
1083
+ }
808
1084
  /** The source's own wording when it has one, otherwise the id made readable. */
809
1085
  function labelOf(entry) {
810
1086
  return entry.label ?? humanizeLabel(entry.id);
@@ -2,10 +2,11 @@ export { adaptPiAssistantMessage, adaptPiToolExecution, adaptPiUserMessage } fro
2
2
  export { loadHistoryEditor, type HistoryEditorConstructor } from "./history-editor-loader.js";
3
3
  export { PiComponentConformanceError, runPiComponentConformance } from "./conformance.js";
4
4
  export type { PiComponentConformanceReport, PiComponentConformanceResult } from "./conformance.js";
5
- export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellReleaseUpdateBanner, renderPiShellReleaseUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
5
+ export { createPiShellOperationLoader, createPiShellShareOperationDialog, type PiShellOperationLoaderPort, } from "./share-operation-dialog.js";
6
+ export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellReleaseUpdateBanner, renderPiShellReleaseUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
6
7
  export { renderPiShellHotkeySections } from "./shell-hotkey-sections.js";
7
8
  export type { PiShellHotkeySection } from "./shell-hotkey-sections.js";
8
- export type { PiExtensionUiBridge, PiExtensionUiBridgeHost, PiShellAuthProviderOption, PiShellAutocompleteCommand, PiShellClipboardContent, PiShellCommandMessagePresentation, PiShellComponentPort, PiShellEditorBodyGeometry, PiShellEditorOptions, PiShellEditorPointerEvent, PiShellEditorPort, PiShellEditorTextRange, PiShellExtensionRendererResolver, PiShellHeaderOptions, PiShellHeaderPort, PiShellHotkeysPresentation, PiShellImageAssetResolver, PiShellLoadedResourcesPort, PiShellLoginDialogPort, PiShellModelSelectorOptions, PiShellModelsDialogOptions, PiShellModelsDialogPort, PiShellOperationLoaderPort, PiShellPasteReservation, PiShellQueuedInputPort, PiShellResourceEntry, PiShellResourceSection, PiShellScopedModelDescriptor, PiShellScopedModelsSelectorOptions, PiShellScopedModelsSelectorPort, PiShellSelectorOption, PiShellSelectorOptions, PiShellSessionInfoPresentation, PiShellSettingsSelectorOptions, PiShellStartupNotice, PiShellStatusPlacement, PiShellStatusPort, PiShellSubmittedPromptComposer, PiShellTranscriptComponentPort, PiShellViewComponentPort, } from "./shell-components.js";
9
+ export type { PiExtensionUiBridge, PiExtensionUiBridgeHost, PiShellAuthProviderOption, PiShellAutocompleteCommand, PiShellClipboardContent, PiShellCommandMessagePresentation, PiShellComponentPort, PiShellEditorBodyGeometry, PiShellEditorOptions, PiShellEditorPointerEvent, PiShellEditorPort, PiShellEditorTextRange, PiShellExtensionRendererResolver, PiShellHeaderOptions, PiShellHeaderPort, PiShellHotkeysPresentation, PiShellImageAssetResolver, PiShellLoadedResourcesPort, PiShellLoginDialogPort, PiShellModelSelectorOptions, PiShellModelsDialogOptions, PiShellModelsDialogPort, PiShellPasteReservation, PiShellQueuedInputPort, PiShellResourceEntry, PiShellResourceSection, PiShellScopedModelDescriptor, PiShellScopedModelsSelectorOptions, PiShellScopedModelsSelectorPort, PiShellSelectorOption, PiShellSelectorOptions, PiShellSessionInfoPresentation, PiShellSettingsSelectorOptions, PiShellStartupNotice, PiShellStatusPlacement, PiShellStatusPort, PiShellSubmittedPromptComposer, PiShellTranscriptComponentPort, PiShellViewComponentPort, } from "./shell-components.js";
9
10
  export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeInstance, currentPiThemeName, detectPiTerminalBackgroundFromEnv, detectPiTerminalBackgroundTheme, detectPiTerminalThemeForAuto, ensurePiTheme, getAvailablePiThemes, loadPiTheme, onPiThemeChange, parsePiAutoThemeSetting, piTheme, resolvePiThemeSetting, stopPiThemeWatcher, } from "./theme.js";
10
11
  export type { PiColorMode, PiTerminalTheme, PiTerminalThemeDetection, PiTerminalThemeDetector, PiThemeBackground, PiThemeResult } from "./theme.js";
11
12
  export { OwnedPiThemeController } from "./upstream/theme/theme-controller.js";