@timurproko/a1 0.2.7-dev.1255 → 0.2.7-dev.1264

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 (78) hide show
  1. package/dist/app/session-shell/prompt-submission.d.ts +1 -1
  2. package/dist/app/session-shell/prompt-submission.js +9 -9
  3. package/dist/app/session-shell/resource-labels.d.ts +2 -0
  4. package/dist/app/session-shell/resource-labels.js +158 -0
  5. package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
  6. package/dist/app/session-shell/session-prompt-history.js +80 -0
  7. package/dist/app/session-shell/session-selectors.d.ts +46 -0
  8. package/dist/app/session-shell/session-selectors.js +269 -0
  9. package/dist/app/session-shell/session-shell-root.d.ts +6 -28
  10. package/dist/app/session-shell/session-shell-root.js +26 -220
  11. package/dist/app/session-shell/session-shell.d.ts +3 -23
  12. package/dist/app/session-shell/session-shell.js +54 -365
  13. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -10
  14. package/dist/app/session-shell/session-viewport-controller.js +40 -318
  15. package/dist/app/session-shell/settings-bindings.d.ts +1 -1
  16. package/dist/app/session-shell/settings-bindings.js +2 -2
  17. package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
  18. package/dist/app/session-shell/viewport-pointer.js +361 -0
  19. package/dist/composition/owned-ui.js +63 -59
  20. package/dist/contracts/owned-ui/index.d.ts +1 -1
  21. package/dist/contracts/owned-ui/index.js +1 -1
  22. package/dist/contracts/owned-ui/validation.d.ts +1 -11
  23. package/dist/contracts/owned-ui/validation.js +8 -104
  24. package/dist/contracts/presentation/index.d.ts +1 -1
  25. package/dist/contracts/presentation/terminal.d.ts +0 -6
  26. package/dist/features/owned-ui/settings-app.js +13 -208
  27. package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
  28. package/dist/features/owned-ui/settings-model-dialog.js +194 -0
  29. package/dist/foundation/release/automatic-update.js +1 -8
  30. package/dist/foundation/release/dependency-layer.js +3 -3
  31. package/dist/foundation/release/latest-release.js +1 -1
  32. package/dist/foundation/release/managed-path.d.ts +10 -0
  33. package/dist/foundation/release/managed-path.js +26 -0
  34. package/dist/foundation/release/release-gc.js +6 -8
  35. package/dist/foundation/release/restart-certification.js +3 -4
  36. package/dist/foundation/release/update-activation.js +1 -1
  37. package/dist/foundation/release/update-execution-lease.js +1 -7
  38. package/dist/foundation/release/update-installer.d.ts +45 -0
  39. package/dist/foundation/release/update-installer.js +57 -0
  40. package/dist/foundation/release/update-recovery.js +2 -9
  41. package/dist/foundation/release/update.d.ts +4 -23
  42. package/dist/foundation/release/update.js +160 -179
  43. package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
  44. package/dist/integrations/pi/components/bracketed-paste.js +123 -0
  45. package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
  46. package/dist/integrations/pi/engine/adapter.d.ts +1 -8
  47. package/dist/integrations/pi/engine/adapter.js +6 -49
  48. package/dist/integrations/pi/engine/host.js +1 -1
  49. package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
  50. package/dist/integrations/pi/engine/repository-metadata.js +189 -0
  51. package/dist/integrations/pi/engine/session-runtime.d.ts +6 -12
  52. package/dist/integrations/pi/engine/session-runtime.js +21 -178
  53. package/dist/integrations/pi/engine/view-state.d.ts +21 -2
  54. package/dist/integrations/pi/engine/view-state.js +129 -1
  55. package/dist/integrations/pi/engine/workflow-runner.d.ts +0 -1
  56. package/dist/integrations/pi/engine/workflow-runner.js +254 -365
  57. package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
  58. package/dist/integrations/pi/engine/workflow-share.js +168 -0
  59. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -14
  60. package/dist/integrations/pi/engine/workflow-support.js +0 -47
  61. package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
  62. package/dist/integrations/pi/tui-runtime/adapter.js +35 -144
  63. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +4 -4
  64. package/dist/integrations/pi/tui-runtime/index.d.ts +0 -1
  65. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -2
  66. package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
  67. package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
  68. package/dist/ui/components/transcript-viewport-frame.js +314 -0
  69. package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
  70. package/dist/ui/components/transcript-viewport.d.ts +2 -147
  71. package/dist/ui/components/transcript-viewport.js +87 -315
  72. package/package.json +3 -3
  73. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
  74. package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
  75. package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
  76. package/dist/integrations/pi/engine/usage-view.js +0 -56
  77. package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -22
  78. /package/dist/{integrations/pi/tui-runtime/contracts.js → ui/components/transcript-viewport-types.js} +0 -0
@@ -44,7 +44,6 @@ const SLOT_IDS = new Set([
44
44
  "overlay",
45
45
  "layout",
46
46
  ]);
47
- const OUTCOMES = new Set(["accepted", "rejected", "completed", "failed", "timed-out", "cancelled"]);
48
47
  const FOCUSED_REGIONS = new Set(["transcript", "editor", "dialog", "overlay", "status"]);
49
48
  const DIALOG_KINDS = new Set(["choice", "editor", "settings", "confirmation"]);
50
49
  const OVERLAY_PLACEMENTS = new Set(["bottom", "center"]);
@@ -94,89 +93,7 @@ export function assertOwnedUiCommand(command) {
94
93
  throw new TypeError("owned-UI command type is unknown");
95
94
  }
96
95
  }
97
- export function assertOwnedUiEvent(event) {
98
- assertId(event.sessionId, "owned-UI event session id");
99
- assertNonNegativeInteger(event.sequence, "owned-UI event sequence");
100
- switch (event.type) {
101
- case "session-lifecycle":
102
- assertEnum(event.lifecycle, LIFECYCLES, "owned-UI lifecycle");
103
- assertOptionalText(event.reason, "owned-UI lifecycle reason", MAX_MESSAGE_LENGTH);
104
- return;
105
- case "session-view":
106
- assertOwnedUiSessionViewModel(event.view);
107
- assertViewSession(event.view, event.sessionId);
108
- return;
109
- case "transcript-block":
110
- assertOwnedUiTranscriptBlock(event.block);
111
- return;
112
- case "assistant-message-completed":
113
- assertNonNegativeInteger(event.sessionGeneration, "owned-UI assistant response session generation");
114
- assertNonNegativeInteger(event.runSequence, "owned-UI assistant response run sequence");
115
- assertNonNegativeInteger(event.responseSequence, "owned-UI assistant response sequence");
116
- assertNonNegativeInteger(event.assistantMessageCount, "owned-UI assistant response count");
117
- if (event.model !== null)
118
- assertOwnedUiModelInfo(event.model);
119
- if (typeof event.successful !== "boolean")
120
- throw new TypeError("owned-UI assistant response success state is invalid");
121
- assertOptionalText(event.stopReason, "owned-UI assistant response stop reason", MAX_LABEL_LENGTH);
122
- if (typeof event.toolContinuation !== "boolean")
123
- throw new TypeError("owned-UI assistant response tool-continuation state is invalid");
124
- return;
125
- case "agent-run-started":
126
- return;
127
- case "agent-run-settled":
128
- assertNonNegativeInteger(event.sessionGeneration, "owned-UI settlement session generation");
129
- assertNonNegativeInteger(event.runSequence, "owned-UI settlement run sequence");
130
- assertNonNegativeInteger(event.responseSequence, "owned-UI settlement response sequence");
131
- assertNonNegativeInteger(event.assistantMessageCount, "owned-UI settlement assistant message count");
132
- if (event.model !== null)
133
- assertOwnedUiModelInfo(event.model);
134
- if (typeof event.successful !== "boolean")
135
- throw new TypeError("owned-UI settlement success state is invalid");
136
- if (event.stopReason === undefined)
137
- throw new TypeError("owned-UI settlement stop reason is missing");
138
- assertOptionalText(event.stopReason, "owned-UI settlement stop reason", MAX_LABEL_LENGTH);
139
- if (typeof event.toolContinuation !== "boolean")
140
- throw new TypeError("owned-UI settlement tool-continuation state is invalid");
141
- if (typeof event.aborted !== "boolean")
142
- throw new TypeError("owned-UI settlement aborted state is invalid");
143
- return;
144
- case "editor-state":
145
- assertOwnedUiEditorState(event.editor);
146
- return;
147
- case "status":
148
- assertOwnedUiStatusView(event.status);
149
- return;
150
- case "dialog":
151
- if (event.dialog !== null)
152
- assertOwnedUiDialog(event.dialog);
153
- return;
154
- case "overlay":
155
- if (event.overlay !== null)
156
- assertOwnedUiOverlay(event.overlay);
157
- return;
158
- case "command-outcome":
159
- assertId(event.correlationId, "owned-UI outcome correlation id");
160
- assertEnum(event.outcome, OUTCOMES, "owned-UI command outcome");
161
- assertOptionalText(event.diagnostic, "owned-UI command diagnostic", MAX_MESSAGE_LENGTH);
162
- return;
163
- case "customization":
164
- assertCollection(event.customizations, "owned-UI customizations", MAX_CUSTOMIZATIONS);
165
- assertUniqueCustomizations(event.customizations);
166
- for (const customization of event.customizations)
167
- assertOwnedUiCustomization(customization);
168
- return;
169
- case "terminal-surface":
170
- assertOwnedUiTerminalSurface(event.surface);
171
- return;
172
- case "diagnostic":
173
- assertOwnedUiDiagnostics(event.diagnostic);
174
- return;
175
- default:
176
- throw new TypeError("owned-UI event type is unknown");
177
- }
178
- }
179
- export function assertOwnedUiPromptSuggestionIdentity(identity) {
96
+ function assertOwnedUiPromptSuggestionIdentity(identity) {
180
97
  assertId(identity.sessionId, "prompt-suggestion session id");
181
98
  assertNonNegativeInteger(identity.sessionGeneration, "prompt-suggestion session generation");
182
99
  assertNonNegativeInteger(identity.runSequence, "prompt-suggestion run sequence");
@@ -202,20 +119,7 @@ export function assertOwnedUiPromptSuggestionResult(result) {
202
119
  throw new TypeError("prompt-suggestion outcome is invalid");
203
120
  }
204
121
  }
205
- export function assertOwnedUiPromptSuggestionState(state) {
206
- if (state.status === "idle")
207
- return;
208
- if (state.status !== "generating" && state.status !== "prepared" && state.status !== "available") {
209
- throw new TypeError("prompt-suggestion state is invalid");
210
- }
211
- assertOwnedUiPromptSuggestionIdentity(state.identity);
212
- if (state.status === "generating" && typeof state.settled !== "boolean") {
213
- throw new TypeError("prompt-suggestion settlement state is invalid");
214
- }
215
- if (state.status === "prepared" || state.status === "available")
216
- assertPromptSuggestionText(state.text, false);
217
- }
218
- export function assertOwnedUiSessionViewModel(view) {
122
+ function assertOwnedUiSessionViewModel(view) {
219
123
  if (view.contractVersion !== OWNED_UI_CONTRACT_VERSION) {
220
124
  throw new TypeError("unsupported owned-UI contract version");
221
125
  }
@@ -270,7 +174,7 @@ export function assertOwnedUiSnapshot(snapshot) {
270
174
  assertViewSession(snapshot.view, snapshot.sessionId);
271
175
  assertJsonBytes(snapshot, "owned-UI snapshot", MAX_SESSION_VIEW_BYTES);
272
176
  }
273
- export function assertOwnedUiCustomization(customization) {
177
+ function assertOwnedUiCustomization(customization) {
274
178
  assertId(customization.id, "owned-UI customization id");
275
179
  assertEnum(customization.slot, SLOT_IDS, "owned-UI customization slot");
276
180
  assertIntegerInRange(customization.version, 1, Number.MAX_SAFE_INTEGER, "owned-UI customization version");
@@ -278,7 +182,7 @@ export function assertOwnedUiCustomization(customization) {
278
182
  assertBoundedText(customization.label, "owned-UI customization label", MAX_LABEL_LENGTH);
279
183
  assertJsonObject(customization.payload, "owned-UI customization payload", MAX_PAYLOAD_BYTES);
280
184
  }
281
- export function assertOwnedUiTranscriptBlock(block) {
185
+ function assertOwnedUiTranscriptBlock(block) {
282
186
  assertId(block.id, "owned-UI transcript block id");
283
187
  assertEnum(block.kind, BLOCK_KINDS, "owned-UI transcript block kind");
284
188
  assertEnum(block.status, BLOCK_STATUSES, "owned-UI transcript block status");
@@ -337,7 +241,7 @@ export function assertOwnedUiTranscriptBlock(block) {
337
241
  }
338
242
  }
339
243
  }
340
- export function assertOwnedUiEditorState(editor) {
244
+ function assertOwnedUiEditorState(editor) {
341
245
  assertPossiblyEmptyText(editor.text, "owned-UI editor text", MAX_TEXT_BYTES);
342
246
  assertCollection(editor.queuedSubmissions, "owned-UI queued submissions", MAX_QUEUE);
343
247
  let queuedBytes = 0;
@@ -356,7 +260,7 @@ export function assertOwnedUiEditorState(editor) {
356
260
  assertIntegerInRange(editor.selection.end, editor.selection.start, editor.text.length, "owned-UI selection end");
357
261
  }
358
262
  }
359
- export function assertOwnedUiStatusView(status) {
263
+ function assertOwnedUiStatusView(status) {
360
264
  assertBoundedText(status.title, "owned-UI status title", MAX_LABEL_LENGTH);
361
265
  assertOptionalText(status.workingMessage, "owned-UI working message", MAX_MESSAGE_LENGTH);
362
266
  if (status.workingProgress !== undefined && status.workingProgress !== null) {
@@ -427,14 +331,14 @@ export function assertOwnedUiStatusView(status) {
427
331
  }
428
332
  }
429
333
  }
430
- export function assertOwnedUiTerminalSurface(surface) {
334
+ function assertOwnedUiTerminalSurface(surface) {
431
335
  assertIntegerInRange(surface.columns, 20, 500, "owned-UI terminal columns");
432
336
  assertIntegerInRange(surface.rows, 8, 300, "owned-UI terminal rows");
433
337
  assertEnum(surface.focusedRegion, FOCUSED_REGIONS, "owned-UI focused region");
434
338
  if (typeof surface.hardwareCursor !== "boolean")
435
339
  throw new TypeError("owned-UI hardware-cursor state is invalid");
436
340
  }
437
- export function assertOwnedUiDiagnostics(diagnostic) {
341
+ function assertOwnedUiDiagnostics(diagnostic) {
438
342
  assertNonNegativeInteger(diagnostic.sequence, "owned-UI diagnostic sequence");
439
343
  assertId(diagnostic.code, "owned-UI diagnostic code");
440
344
  assertEnum(diagnostic.severity, SEVERITIES, "owned-UI diagnostic severity");
@@ -2,7 +2,7 @@ import type { PresentationComponentPort } from "./component.js";
2
2
  export type { PresentationComponentPort, PresentationPointerSurface } from "./component.js";
3
3
  export { samePointerSurfaces } from "./component.js";
4
4
  export type { CanvasBackgroundStyle, ShellPresentationPorts, ThemeSource, UiTheme, UiThemeBackground, UiThemeToken } from "./theme.js";
5
- export type { TerminalComponentPort, TerminalDamageDecision, TerminalDamageDecisionReason, TerminalDamageFrameDescriptor, TerminalDamageFrameSafety, TerminalDamageOptions, TerminalDamagePort, TerminalHyperlinkInspection, TerminalInputCoordinationDecision, TerminalInputCoordinationScheduler, TerminalInputDiagnosticsEvent, TerminalInputDiagnosticsPhase, TerminalInputListener, TerminalInputListenerResult, TerminalInputSurfaceKind, TerminalLayoutEntry, TerminalLayoutNode, TerminalOverlayAnchor, TerminalOverlayHandle, TerminalOverlayMargin, TerminalOverlayOptions, TerminalOverlayUnfocusOptions, TerminalPointerSurface, TerminalPort, TerminalPreInputListener, TerminalProgramStatus, TerminalRuntimeFactory, TerminalRuntimeOptions, TerminalRuntimePort, TerminalRuntimeState, TerminalScrollState, TerminalSizeValue, TerminalStopOptions, TerminalViewport, TerminalWheelScrollLines, } from "./terminal.js";
5
+ export type { TerminalComponentPort, TerminalDamageDecision, TerminalDamageDecisionReason, TerminalDamageFrameDescriptor, TerminalDamageFrameSafety, TerminalDamageOptions, TerminalDamagePort, TerminalHyperlinkInspection, TerminalInputCoordinationDecision, TerminalInputCoordinationScheduler, TerminalInputDiagnosticsEvent, TerminalInputDiagnosticsPhase, TerminalInputListener, TerminalInputListenerResult, TerminalInputSurfaceKind, TerminalLayoutEntry, TerminalLayoutNode, TerminalOverlayAnchor, TerminalOverlayHandle, TerminalOverlayMargin, TerminalOverlayOptions, TerminalOverlayUnfocusOptions, TerminalPointerSurface, TerminalPort, TerminalPreInputListener, TerminalProgramStatus, TerminalRuntimeFactory, TerminalRuntimeOptions, TerminalRuntimePort, TerminalRuntimeState, TerminalSizeValue, TerminalStopOptions, TerminalViewport, TerminalWheelScrollLines, } from "./terminal.js";
6
6
  export { SHARED_PROGRESS_ACCENT_ANSI, SHARED_PROGRESS_PALETTE } from "./progress-palette.generated.js";
7
7
  export type PresentationRuntimeState = "idle" | "running" | "stopping" | "stopped" | "failed";
8
8
  export interface SemanticShortcutHint {
@@ -20,12 +20,6 @@ export interface TerminalViewport {
20
20
  readonly columns: number;
21
21
  readonly rows: number;
22
22
  }
23
- export interface TerminalScrollState {
24
- readonly scrollTop: number;
25
- readonly viewportHeight: number;
26
- readonly followingEnd: boolean;
27
- readonly scrollbarVisible: boolean;
28
- }
29
23
  export type TerminalPointerSurface = PresentationPointerSurface;
30
24
  export interface TerminalComponentPort extends PresentationComponentPort {
31
25
  readonly wantsKeyRelease?: boolean;
@@ -1,8 +1,9 @@
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, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollbarGeometry, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
1
+ import { DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
+ import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollbarGeometry, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
3
3
  import { SettingsScrollRail } from "./settings-scroll-rail.js";
4
- import { SettingsEdits, changeFailure, replaceRecord, sameRecord, structuredValue } from "./settings-edits.js";
4
+ import { SettingsEdits, replaceRecord, sameRecord } from "./settings-edits.js";
5
5
  import { SETTINGS_APP_ID } from "./settings-route.js";
6
+ import { dialogWindow, modelDialogKey, modelDialogLines } from "./settings-model-dialog.js";
6
7
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
7
8
  const SCOPE = SETTINGS_APP_ID;
8
9
  const SETTINGS_TOP_RULE_ROWS = 1;
@@ -13,8 +14,6 @@ const SETTINGS_STATUS_ROWS = 1;
13
14
  const SETTINGS_CONTENT_INSET = 1;
14
15
  /** The panel a setting with parts opens: its own keys, its own hint. */
15
16
  const DIALOG_SCOPE = `${SETTINGS_APP_ID}-parts`;
16
- const MODEL_SCOPE = `${SETTINGS_APP_ID}-models`;
17
- const MODEL_LEVEL_SCOPE = `${SETTINGS_APP_ID}-model-levels`;
18
17
  const SCROLLBAR_TOP_INSET = 0;
19
18
  /** What a structured value offers instead of printing itself. */
20
19
  const CONFIGURE = "configure";
@@ -45,17 +44,6 @@ SETTINGS_SHORTCUTS.declare({
45
44
  hint: { keys: DIALOG_CLOSE_SHORTCUT_HINT.key, does: DIALOG_CLOSE_SHORTCUT_HINT.action },
46
45
  }, "close");
47
46
  assertNoShortcutConflicts(SETTINGS_SHORTCUTS.assemble());
48
- const MODEL_SHORTCUTS = new ShortcutRegistry();
49
- MODEL_SHORTCUTS.declare({ key: "edit", scope: MODEL_SCOPE, description: "Search models", section: "Choose", hint: { keys: "Type", does: "search" } }, "edit-filter");
50
- MODEL_SHORTCUTS.declare({ key: "up", scope: MODEL_SCOPE, description: "Previous model", section: "Choose" }, "previous");
51
- MODEL_SHORTCUTS.declare({ key: "down", scope: MODEL_SCOPE, description: "Next model", section: "Choose" }, "next");
52
- MODEL_SHORTCUTS.declare({ key: "enter", scope: MODEL_SCOPE, description: "Select model", section: "Choose", hint: { keys: "Enter", does: "select" } }, "select");
53
- 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");
54
- MODEL_SHORTCUTS.declare({ key: "up", scope: MODEL_LEVEL_SCOPE, description: "Previous level", section: "Choose", hint: { keys: "↑↓", does: "navigate" } }, "previous");
55
- MODEL_SHORTCUTS.declare({ key: "down", scope: MODEL_LEVEL_SCOPE, description: "Next level", section: "Choose", hint: { keys: "↑↓", does: "navigate" } }, "next");
56
- MODEL_SHORTCUTS.declare({ key: "enter", scope: MODEL_LEVEL_SCOPE, description: "Select level", section: "Choose", hint: { keys: "Enter", does: "select" } }, "select");
57
- 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");
58
- assertNoShortcutConflicts(MODEL_SHORTCUTS.assemble());
59
47
  const KEYS = {
60
48
  "\u001b[A": "up",
61
49
  "\u001b[B": "down",
@@ -471,8 +459,14 @@ export class SettingsApp {
471
459
  const open = this.#structured;
472
460
  if (open === null)
473
461
  return { consumed: false };
474
- if (open.modelDialog !== null)
475
- return this.#modelDialogKey(open, data);
462
+ if (open.modelDialog !== null) {
463
+ return modelDialogKey(open, data, KEYS[data] ?? data, {
464
+ session: this.#session,
465
+ close: () => { this.#structured = null; },
466
+ reopen: reopened => { this.#structured = reopened; },
467
+ save: (edit, previous, forward) => { this.#saveStructured(edit, previous, forward); },
468
+ });
469
+ }
476
470
  switch (SETTINGS_SHORTCUTS.resolve(KEYS[data] ?? data, DIALOG_SCOPE)) {
477
471
  case "close":
478
472
  this.#structured = null;
@@ -493,114 +487,6 @@ export class SettingsApp {
493
487
  return { consumed: true, render: false };
494
488
  }
495
489
  }
496
- #modelDialogKey(open, data) {
497
- const key = KEYS[data] ?? data;
498
- if (open.modelDialog === "default") {
499
- const flags = this.#filteredModelFlags(open);
500
- const action = MODEL_SHORTCUTS.resolve(key, MODEL_SCOPE) ?? "edit-filter";
501
- if (action === "close") {
502
- this.#structured = null;
503
- return { consumed: true };
504
- }
505
- if (action === "previous" || action === "next") {
506
- open.index = Math.min(Math.max(0, flags.length - 1), Math.max(0, open.index + (action === "next" ? 1 : -1)));
507
- return { consumed: true };
508
- }
509
- if (action === "select") {
510
- const selected = flags[open.index];
511
- if (selected === undefined)
512
- return { consumed: true, render: false };
513
- open.error = null;
514
- this.#structured = null;
515
- void this.#session.change(open.entry.backend, open.entry.id, selected.key).then(outcome => {
516
- const failure = changeFailure(outcome);
517
- if (failure !== null) {
518
- open.error = `Could not save: ${failure}`;
519
- this.#structured = open;
520
- }
521
- });
522
- return { consumed: true };
523
- }
524
- if (open.filter === null)
525
- return { consumed: true, render: false };
526
- handleLineInputKey(open.filter, data);
527
- open.index = 0;
528
- return { consumed: true };
529
- }
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.description.toLowerCase().includes(needle)
593
- || flag.key.toLowerCase().includes(needle));
594
- }
595
- #modelLevelOptions(open, selected) {
596
- const fallback = String(selected.fallback);
597
- const supported = (selected.choices ?? [])
598
- .filter(choice => choice !== fallback)
599
- .map(choice => ({ value: choice, label: choice, description: thinkingLevelDescription(choice) }));
600
- return structuredValue(open.entry, open.record)[selected.key] === undefined
601
- ? supported
602
- : [...supported, { value: fallback, label: "(clear override)", description: "Revert to global default" }];
603
- }
604
490
  #toggleFlag(index) {
605
491
  const open = this.#structured;
606
492
  const flag = open?.flags[index];
@@ -859,7 +745,7 @@ export class SettingsApp {
859
745
  }
860
746
  #dialogLines(open, width, height, theme) {
861
747
  if (open.modelDialog !== null)
862
- return this.#modelDialogLines(open, width, height, theme);
748
+ return modelDialogLines(open, width, height, theme);
863
749
  const rows = open.flags.map(key => {
864
750
  const declared = open.entry.flags.find(flag => flag.key === key);
865
751
  return {
@@ -872,64 +758,6 @@ export class SettingsApp {
872
758
  const visible = dialogWindow(rows, open.index, configureDialogChoiceCapacity(height, false, configureDialogTextRows(rows[open.index]?.description ?? "", width)));
873
759
  return renderDialogPanel({ title: labelOf(open.entry), rows: visible.rows, index: visible.index, hint: SETTINGS_SHORTCUTS.hintEntries(DIALOG_SCOPE) }, width, theme);
874
760
  }
875
- #modelDialogLines(open, width, height, theme) {
876
- if (open.modelDialog === "default") {
877
- const flags = this.#filteredModelFlags(open);
878
- const current = typeof open.entry.rawValue === "string" ? open.entry.rawValue : null;
879
- const rows = flags.length === 0
880
- ? [{ label: "No models found", value: "" }]
881
- : flags.map(flag => ({
882
- ...modelLabelParts(flag.label ?? humanizeLabel(flag.key)),
883
- ...(flag.key === current ? { successSuffix: "✓" } : {}),
884
- value: "",
885
- }));
886
- const description = open.error ?? "Select the model used for new sessions";
887
- const visible = dialogWindow(rows, open.index, configureDialogChoiceCapacity(height, true, configureDialogTextRows(description, width)));
888
- return renderSteppedDialogPanel({
889
- title: "Default Model", description, input: open.filter ?? new LineInput(""),
890
- rows: visible.rows, index: visible.index, hint: MODEL_SHORTCUTS.hintEntries(MODEL_SCOPE),
891
- }, width, theme);
892
- }
893
- if (open.step === "model") {
894
- const flags = this.#filteredModelFlags(open);
895
- const stored = structuredValue(open.entry, open.record);
896
- const allRows = flags.length === 0
897
- ? [{ label: "No models found", value: "" }]
898
- : flags.map(flag => ({
899
- ...modelLabelParts(flag.label ?? humanizeLabel(flag.key)),
900
- value: stored[flag.key] === undefined ? "" : String(stored[flag.key]),
901
- }));
902
- const description = "Select a model to configure";
903
- const visible = dialogWindow(allRows, open.index, configureDialogChoiceCapacity(height, true, configureDialogTextRows(description, width)));
904
- return renderSteppedDialogPanel({
905
- title: "Thinking Level",
906
- step: 1,
907
- steps: 2,
908
- description,
909
- ...(open.filter === null ? {} : { input: open.filter }),
910
- rows: visible.rows,
911
- index: visible.index,
912
- hint: MODEL_SHORTCUTS.hintEntries(MODEL_SCOPE),
913
- }, width, theme);
914
- }
915
- const selected = open.entry.flags.find(flag => flag.key === open.modelKey);
916
- const current = selected === undefined ? undefined : structuredValue(open.entry, open.record)[selected.key];
917
- const allRows = (selected === undefined ? [] : this.#modelLevelOptions(open, selected)).map(choice => ({
918
- label: choice.value === current ? `✓ ${choice.label}` : choice.label,
919
- value: choice.description,
920
- }));
921
- const description = `Select default thinking level for ${selected?.label ?? open.modelKey ?? "model"}`;
922
- const visible = dialogWindow(allRows, open.index, configureDialogChoiceCapacity(height, false, configureDialogTextRows(description, width)));
923
- return renderSteppedDialogPanel({
924
- title: "Thinking Level",
925
- step: 2,
926
- steps: 2,
927
- description,
928
- rows: visible.rows,
929
- index: visible.index,
930
- hint: MODEL_SHORTCUTS.hintEntries(MODEL_LEVEL_SCOPE),
931
- }, width, theme);
932
- }
933
761
  #footerLines(width, theme) {
934
762
  // Rationale: standing guidance is more useful than transient feedback; failures still
935
763
  // roll back without displacing the Settings interaction contract.
@@ -981,29 +809,6 @@ function orderDefaultModelBeforeThinking(entries, sectionId) {
981
809
  ordered.splice(thinking < 0 ? ordered.length : thinking, 0, defaultModel);
982
810
  return ordered;
983
811
  }
984
- function modelLabelParts(label) {
985
- const match = /^(.*?)(\s+\[[^\]]+\])$/u.exec(label);
986
- return match === null ? { label } : { label: match[1] ?? label, labelSuffix: match[2] ?? "" };
987
- }
988
- function dialogWindow(rows, index, maximum) {
989
- if (rows.length <= maximum)
990
- return { rows, index: Math.min(Math.max(0, rows.length - 1), Math.max(0, index)) };
991
- const start = Math.min(rows.length - maximum, Math.max(0, index - Math.floor(maximum / 2)));
992
- return { rows: rows.slice(start, start + maximum), index: index - start };
993
- }
994
- function thinkingLevelDescription(level) {
995
- switch (level) {
996
- case "default": return "global default";
997
- case "off": return "No reasoning";
998
- case "minimal": return "Very brief reasoning (~1k tokens)";
999
- case "low": return "Light reasoning (~2k tokens)";
1000
- case "medium": return "Moderate reasoning (~8k tokens)";
1001
- case "high": return "Deep reasoning (~16k tokens)";
1002
- case "xhigh": return "Extra-high reasoning (~32k tokens)";
1003
- case "max": return "Maximum reasoning";
1004
- default: return "";
1005
- }
1006
- }
1007
812
  function isDefaultModelEntry(entry) {
1008
813
  return entry.backend === "agent" && entry.id === "defaultModel";
1009
814
  }
@@ -0,0 +1,41 @@
1
+ import type { DialogRow } from "../../ui/components/index.js";
2
+ import { LineInput, type PaneInputResult, type UiTheme } from "../../ui/components/index.js";
3
+ import type { OwnedSettingsManager, OwnedUiSettingsEntry } from "../../ui/settings/index.js";
4
+ /**
5
+ * A structured setting being edited: its parts, the row in hand, and their values. A part is a
6
+ * toggle when the declaration offers no choices, and a choice cycle otherwise; a choice part at its
7
+ * fallback is unset and leaves the record.
8
+ */
9
+ export interface StructuredEdit {
10
+ readonly entry: OwnedUiSettingsEntry;
11
+ readonly flags: readonly string[];
12
+ readonly modelDialog: "thinking" | "default" | null;
13
+ /** Searchable flag dialog for owned tab-bar configuration. */
14
+ readonly filter: LineInput | null;
15
+ step: "model" | "level";
16
+ modelKey: string | null;
17
+ modelIndex: number;
18
+ index: number;
19
+ error: string | null;
20
+ readonly record: Record<string, boolean | string>;
21
+ }
22
+ /** What the model dialog asks of the settings screen that owns it. */
23
+ export interface ModelDialogActions {
24
+ readonly session: Pick<OwnedSettingsManager, "change">;
25
+ close(): void;
26
+ /** Show the dialog again, as after a failed save. */
27
+ reopen(open: StructuredEdit): void;
28
+ /** Save a changed record, rolling back if the source refuses it. */
29
+ save(open: StructuredEdit, previous: Readonly<Record<string, boolean | string>>, forward: Readonly<Record<string, boolean | string>>): void;
30
+ }
31
+ /**
32
+ * One key in the default-model selector or the two-step thinking-level dialog. `key` is the
33
+ * named key for `data`; typed text that names no shortcut edits the search.
34
+ */
35
+ export declare function modelDialogKey(open: StructuredEdit, data: string, key: string, actions: ModelDialogActions): PaneInputResult;
36
+ export declare function modelDialogLines(open: StructuredEdit, width: number, height: number, theme: UiTheme): readonly string[];
37
+ /** The rows a dialog of `maximum` rows shows, centred on `index` where the list allows. */
38
+ export declare function dialogWindow(rows: readonly DialogRow[], index: number, maximum: number): {
39
+ readonly rows: readonly DialogRow[];
40
+ readonly index: number;
41
+ };