@timurproko/a1 0.2.7-dev.1236 → 0.2.7-dev.1238
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.
- package/dist/contracts/owned-ui/session-workflows.d.ts +1 -1
- package/dist/features/owned-ui/settings-app.js +81 -11
- package/dist/integrations/pi/engine/default-model-setting.d.ts +12 -0
- package/dist/integrations/pi/engine/default-model-setting.js +70 -0
- package/dist/integrations/pi/engine/index.d.ts +1 -1
- package/dist/integrations/pi/engine/index.js +1 -1
- package/dist/integrations/pi/engine/offline-settings-port.js +1 -0
- package/dist/integrations/pi/engine/runtime-integration.d.ts +1 -1
- package/dist/integrations/pi/engine/runtime-integration.js +12 -14
- package/dist/integrations/pi/engine/settings-bridge.d.ts +1 -2
- package/dist/integrations/pi/engine/settings-bridge.js +23 -29
- package/dist/integrations/pi/engine/settings-port.js +2 -1
- package/dist/integrations/pi/engine/workflow-runner.js +2 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/ui/components/dialog-panel.d.ts +6 -3
- package/dist/ui/components/dialog-panel.js +10 -7
- package/dist/ui/components/theme.d.ts +1 -1
- package/docs/architecture/ui-reference-provenance.md +3 -3
- package/package.json +3 -3
|
@@ -292,7 +292,7 @@ export type OwnedUiJsonValue = null | boolean | number | string | readonly Owned
|
|
|
292
292
|
/** The layer that installs a setting's live effect. */
|
|
293
293
|
export type OwnedUiSettingOwner = "agent" | "shell" | "terminal" | "startup" | "shutdown" | "installation";
|
|
294
294
|
/** Engine settings whose live effect an owner may bind. */
|
|
295
|
-
export type OwnedUiSessionSettingKey = "autoCompact" | "showImages" | "imageWidthCells" | "autoResizeImages" | "blockImages" | "enableSkillCommands" | "steeringMode" | "followUpMode" | "transport" | "httpIdleTimeoutMs" | "cacheWarmingMode" | "modelThinkingLevels" | "theme" | "hideThinkingBlock" | "mermaidRenderingMode" | "showCacheMissNotices" | "collapseChangelog" | "enableInstallTelemetry" | "quietStartup" | "defaultProjectTrust" | "sessionShareDestination" | "doubleEscapeAction" | "treeFilterMode" | "showHardwareCursor" | "editorPaddingX" | "outputPad" | "autocompleteMaxVisible" | "clearOnShrink" | "showTerminalProgress" | "tuiMode" | "fullscreenExitOutput" | "fullscreenScrollbar" | "fullscreenCopyOnSelect" | "fullscreenWheelScrollLines" | "warnings";
|
|
295
|
+
export type OwnedUiSessionSettingKey = "autoCompact" | "showImages" | "imageWidthCells" | "autoResizeImages" | "blockImages" | "enableSkillCommands" | "steeringMode" | "followUpMode" | "transport" | "httpIdleTimeoutMs" | "cacheWarmingMode" | "defaultModel" | "modelThinkingLevels" | "theme" | "hideThinkingBlock" | "mermaidRenderingMode" | "showCacheMissNotices" | "collapseChangelog" | "enableInstallTelemetry" | "quietStartup" | "defaultProjectTrust" | "sessionShareDestination" | "doubleEscapeAction" | "treeFilterMode" | "showHardwareCursor" | "editorPaddingX" | "outputPad" | "autocompleteMaxVisible" | "clearOnShrink" | "showTerminalProgress" | "tuiMode" | "fullscreenExitOutput" | "fullscreenScrollbar" | "fullscreenCopyOnSelect" | "fullscreenWheelScrollLines" | "warnings";
|
|
296
296
|
export interface OwnedUiSettingEffectHandler {
|
|
297
297
|
/** Install one value in the active owner. Handlers must be idempotent and reversible. */
|
|
298
298
|
apply(value: OwnedUiJsonValue): void | Promise<void>;
|
|
@@ -281,7 +281,7 @@ export class SettingsApp {
|
|
|
281
281
|
return { consumed: false };
|
|
282
282
|
case "activate": {
|
|
283
283
|
const row = rows[selected];
|
|
284
|
-
if (row?.kind === "element" && row.value.structured)
|
|
284
|
+
if (row?.kind === "element" && (row.value.structured || isDefaultModelEntry(row.value)))
|
|
285
285
|
this.#openStructured(row.value);
|
|
286
286
|
else if (row?.kind === "element" && typeof this.#shownValue(row.value) === "number")
|
|
287
287
|
this.#cycle(rows, selected, 1);
|
|
@@ -469,7 +469,7 @@ export class SettingsApp {
|
|
|
469
469
|
if (row === undefined || row.kind !== "element")
|
|
470
470
|
return;
|
|
471
471
|
const entry = row.value;
|
|
472
|
-
if (entry.structured) {
|
|
472
|
+
if (entry.structured || isDefaultModelEntry(entry)) {
|
|
473
473
|
this.#openStructured(entry);
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
@@ -508,9 +508,13 @@ export class SettingsApp {
|
|
|
508
508
|
}
|
|
509
509
|
// Rationale: a structured setting opens as its own flag list rather than a value menu.
|
|
510
510
|
#openStructured(entry) {
|
|
511
|
+
const modelDialog = entry.backend === "agent" && entry.id === "modelThinkingLevels"
|
|
512
|
+
? "thinking"
|
|
513
|
+
: isDefaultModelEntry(entry) ? "default" : null;
|
|
511
514
|
// Invariant: the flags come from the declaration, not from the stored value: an unset
|
|
512
|
-
// flag still has a row, showing the default the source would apply.
|
|
513
|
-
|
|
515
|
+
// flag still has a row, showing the default the source would apply. The default-model
|
|
516
|
+
// selector may open empty so it can truthfully report that no authenticated models exist.
|
|
517
|
+
if (entry.flags.length === 0 && modelDialog !== "default")
|
|
514
518
|
return;
|
|
515
519
|
// Rationale: beside a tab bar, its own Plus Menu dialog configures `+`, so both ways look the same.
|
|
516
520
|
if (entry.backend === "a1" && entry.id === PLUS_MENU_SETTING_ID && this.#options.configurePlusMenu?.() === true)
|
|
@@ -530,17 +534,18 @@ export class SettingsApp {
|
|
|
530
534
|
this.#hoverKey = null;
|
|
531
535
|
this.#hoverRegion = "label";
|
|
532
536
|
this.#rails.clear();
|
|
533
|
-
const
|
|
537
|
+
const currentDefault = typeof entry.rawValue === "string" ? entry.rawValue : null;
|
|
534
538
|
this.#structured = {
|
|
535
539
|
entry,
|
|
536
540
|
flags: entry.flags.map(flag => flag.key),
|
|
537
541
|
modelDialog,
|
|
538
542
|
plusMenu: entry.backend === "a1" && entry.id === PLUS_MENU_SETTING_ID ? new PlusMenuDialog(entry.flags) : null,
|
|
539
|
-
filter: modelDialog ? new LineInput("")
|
|
543
|
+
filter: modelDialog === null ? null : new LineInput(""),
|
|
540
544
|
step: "model",
|
|
541
545
|
modelKey: null,
|
|
542
546
|
modelIndex: 0,
|
|
543
|
-
index: 0,
|
|
547
|
+
index: modelDialog === "default" ? Math.max(0, entry.flags.findIndex(flag => flag.key === currentDefault)) : 0,
|
|
548
|
+
error: null,
|
|
544
549
|
record,
|
|
545
550
|
};
|
|
546
551
|
}
|
|
@@ -548,7 +553,7 @@ export class SettingsApp {
|
|
|
548
553
|
const open = this.#structured;
|
|
549
554
|
if (open === null)
|
|
550
555
|
return { consumed: false };
|
|
551
|
-
if (open.modelDialog)
|
|
556
|
+
if (open.modelDialog !== null)
|
|
552
557
|
return this.#modelDialogKey(open, data);
|
|
553
558
|
if (open.plusMenu !== null) {
|
|
554
559
|
const outcome = open.plusMenu.handle(KEYS[data] ?? data, data, key => this.#toggleFlag(open.flags.indexOf(key)));
|
|
@@ -578,6 +583,38 @@ export class SettingsApp {
|
|
|
578
583
|
}
|
|
579
584
|
#modelDialogKey(open, data) {
|
|
580
585
|
const key = KEYS[data] ?? data;
|
|
586
|
+
if (open.modelDialog === "default") {
|
|
587
|
+
const flags = this.#filteredModelFlags(open);
|
|
588
|
+
const action = MODEL_SHORTCUTS.resolve(key, MODEL_SCOPE) ?? "edit-filter";
|
|
589
|
+
if (action === "close") {
|
|
590
|
+
this.#structured = null;
|
|
591
|
+
return { consumed: true };
|
|
592
|
+
}
|
|
593
|
+
if (action === "previous" || action === "next") {
|
|
594
|
+
open.index = Math.min(Math.max(0, flags.length - 1), Math.max(0, open.index + (action === "next" ? 1 : -1)));
|
|
595
|
+
return { consumed: true };
|
|
596
|
+
}
|
|
597
|
+
if (action === "select") {
|
|
598
|
+
const selected = flags[open.index];
|
|
599
|
+
if (selected === undefined)
|
|
600
|
+
return { consumed: true, render: false };
|
|
601
|
+
open.error = null;
|
|
602
|
+
this.#structured = null;
|
|
603
|
+
void this.#session.change(open.entry.backend, open.entry.id, selected.key).then(outcome => {
|
|
604
|
+
const failure = changeFailure(outcome);
|
|
605
|
+
if (failure !== null) {
|
|
606
|
+
open.error = `Could not save: ${failure}`;
|
|
607
|
+
this.#structured = open;
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
return { consumed: true };
|
|
611
|
+
}
|
|
612
|
+
if (open.filter === null)
|
|
613
|
+
return { consumed: true, render: false };
|
|
614
|
+
handleLineInputKey(open.filter, data);
|
|
615
|
+
open.index = 0;
|
|
616
|
+
return { consumed: true };
|
|
617
|
+
}
|
|
581
618
|
if (open.step === "model") {
|
|
582
619
|
const flags = this.#filteredModelFlags(open);
|
|
583
620
|
const action = MODEL_SHORTCUTS.resolve(key, MODEL_SCOPE) ?? "edit-filter";
|
|
@@ -640,6 +677,7 @@ export class SettingsApp {
|
|
|
640
677
|
const needle = open.filter?.value.trim().toLowerCase() ?? "";
|
|
641
678
|
return open.entry.flags.filter(flag => needle.length === 0
|
|
642
679
|
|| (flag.label ?? humanizeLabel(flag.key)).toLowerCase().includes(needle)
|
|
680
|
+
|| flag.description.toLowerCase().includes(needle)
|
|
643
681
|
|| flag.key.toLowerCase().includes(needle));
|
|
644
682
|
}
|
|
645
683
|
#modelLevelOptions(open, selected) {
|
|
@@ -897,7 +935,8 @@ export class SettingsApp {
|
|
|
897
935
|
// Rationale: a section named by the search is what the reader asked for, so it arrives
|
|
898
936
|
// whole rather than narrowed to the entries that happen to repeat its name.
|
|
899
937
|
const named = needle.length > 0 && section.title.toLowerCase().includes(needle);
|
|
900
|
-
const
|
|
938
|
+
const matching = named ? section.entries : section.entries.filter(matches);
|
|
939
|
+
const entries = orderDefaultModelBeforeThinking(matching, section.id);
|
|
901
940
|
if (needle.length > 0 && entries.length === 0)
|
|
902
941
|
continue;
|
|
903
942
|
if (rows.length > 0)
|
|
@@ -947,7 +986,7 @@ export class SettingsApp {
|
|
|
947
986
|
#viewRow(entry) {
|
|
948
987
|
const key = `${entry.backend}:${entry.id}`;
|
|
949
988
|
const shown = this.#shownValue(entry);
|
|
950
|
-
const value = entry.structured
|
|
989
|
+
const value = entry.structured || isDefaultModelEntry(entry)
|
|
951
990
|
? CONFIGURE
|
|
952
991
|
: shown === null
|
|
953
992
|
? describeRaw(entry.rawValue)
|
|
@@ -988,7 +1027,7 @@ export class SettingsApp {
|
|
|
988
1027
|
return renderValueMenu(lines, state, frame, theme);
|
|
989
1028
|
}
|
|
990
1029
|
#dialogLines(open, width, height, theme) {
|
|
991
|
-
if (open.modelDialog)
|
|
1030
|
+
if (open.modelDialog !== null)
|
|
992
1031
|
return this.#modelDialogLines(open, width, height, theme);
|
|
993
1032
|
if (open.plusMenu !== null)
|
|
994
1033
|
return open.plusMenu.lines(open.record, width, theme);
|
|
@@ -1005,6 +1044,23 @@ export class SettingsApp {
|
|
|
1005
1044
|
return renderDialogPanel({ title: labelOf(open.entry), rows: visible.rows, index: visible.index, hint: SETTINGS_SHORTCUTS.hintEntries(DIALOG_SCOPE) }, width, theme);
|
|
1006
1045
|
}
|
|
1007
1046
|
#modelDialogLines(open, width, height, theme) {
|
|
1047
|
+
if (open.modelDialog === "default") {
|
|
1048
|
+
const flags = this.#filteredModelFlags(open);
|
|
1049
|
+
const current = typeof open.entry.rawValue === "string" ? open.entry.rawValue : null;
|
|
1050
|
+
const rows = flags.length === 0
|
|
1051
|
+
? [{ label: "No models found", value: "" }]
|
|
1052
|
+
: flags.map(flag => ({
|
|
1053
|
+
...modelLabelParts(flag.label ?? humanizeLabel(flag.key)),
|
|
1054
|
+
...(flag.key === current ? { successSuffix: "✓" } : {}),
|
|
1055
|
+
value: "",
|
|
1056
|
+
}));
|
|
1057
|
+
const description = open.error ?? "Select the model used for new sessions";
|
|
1058
|
+
const visible = dialogWindow(rows, open.index, configureDialogChoiceCapacity(height, true, configureDialogTextRows(description, width)));
|
|
1059
|
+
return renderSteppedDialogPanel({
|
|
1060
|
+
title: "Default Model", description, input: open.filter ?? new LineInput(""),
|
|
1061
|
+
rows: visible.rows, index: visible.index, hint: MODEL_SHORTCUTS.hintEntries(MODEL_SCOPE),
|
|
1062
|
+
}, width, theme);
|
|
1063
|
+
}
|
|
1008
1064
|
if (open.step === "model") {
|
|
1009
1065
|
const flags = this.#filteredModelFlags(open);
|
|
1010
1066
|
const stored = structuredValue(open.entry, open.record);
|
|
@@ -1086,6 +1142,17 @@ function handleSettingsSearchPointer(state, event, panelTop) {
|
|
|
1086
1142
|
const handled = input.handlePointer({ kind: event.kind, button: event.button, column: event.column, row });
|
|
1087
1143
|
return { consumed: handled || overInput, render: handled };
|
|
1088
1144
|
}
|
|
1145
|
+
function orderDefaultModelBeforeThinking(entries, sectionId) {
|
|
1146
|
+
if (sectionId !== "agent")
|
|
1147
|
+
return entries;
|
|
1148
|
+
const defaultModel = entries.find(isDefaultModelEntry);
|
|
1149
|
+
if (defaultModel === undefined)
|
|
1150
|
+
return entries;
|
|
1151
|
+
const ordered = entries.filter(entry => entry !== defaultModel);
|
|
1152
|
+
const thinking = ordered.findIndex(entry => entry.backend === "agent" && entry.id === "modelThinkingLevels");
|
|
1153
|
+
ordered.splice(thinking < 0 ? ordered.length : thinking, 0, defaultModel);
|
|
1154
|
+
return ordered;
|
|
1155
|
+
}
|
|
1089
1156
|
function modelLabelParts(label) {
|
|
1090
1157
|
const match = /^(.*?)(\s+\[[^\]]+\])$/u.exec(label);
|
|
1091
1158
|
return match === null ? { label } : { label: match[1] ?? label, labelSuffix: match[2] ?? "" };
|
|
@@ -1139,6 +1206,9 @@ function changeFailure(outcome) {
|
|
|
1139
1206
|
return outcome.limitationReason ?? "the setting is unavailable";
|
|
1140
1207
|
return outcome.limitationReason;
|
|
1141
1208
|
}
|
|
1209
|
+
function isDefaultModelEntry(entry) {
|
|
1210
|
+
return entry.backend === "agent" && entry.id === "defaultModel";
|
|
1211
|
+
}
|
|
1142
1212
|
/** The source's own wording when it has one, otherwise the id made readable. */
|
|
1143
1213
|
function labelOf(entry) {
|
|
1144
1214
|
return entry.label ?? humanizeLabel(entry.id);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SettingsManager } from "../startup-public.js";
|
|
2
|
+
import type { AgentSettingChangeOutcome, AgentSettingDescriptor } from "../../../contracts/agent-engine/index.js";
|
|
3
|
+
interface DefaultModelChoice {
|
|
4
|
+
readonly key: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_MODEL_SETTING_KEY = "defaultModel";
|
|
9
|
+
export declare const SUPPLEMENTAL_SETTING_KEYS: readonly ["defaultModel"];
|
|
10
|
+
export declare function defaultModelDescriptor(settings: SettingsManager, models: readonly DefaultModelChoice[]): AgentSettingDescriptor;
|
|
11
|
+
export declare function writeDefaultModel(settings: SettingsManager, models: readonly DefaultModelChoice[], value: unknown): Promise<AgentSettingChangeOutcome>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const DEFAULT_MODEL_SETTING_KEY = "defaultModel";
|
|
2
|
+
export const SUPPLEMENTAL_SETTING_KEYS = [DEFAULT_MODEL_SETTING_KEY];
|
|
3
|
+
const UNSET = "not set";
|
|
4
|
+
function read(settings) {
|
|
5
|
+
const provider = settings.getDefaultProvider();
|
|
6
|
+
const model = settings.getDefaultModel();
|
|
7
|
+
return provider && model ? `${provider}/${model}` : UNSET;
|
|
8
|
+
}
|
|
9
|
+
export function defaultModelDescriptor(settings, models) {
|
|
10
|
+
const value = read(settings);
|
|
11
|
+
return {
|
|
12
|
+
key: DEFAULT_MODEL_SETTING_KEY,
|
|
13
|
+
valueType: "enum",
|
|
14
|
+
writable: true,
|
|
15
|
+
application: "next-session",
|
|
16
|
+
owner: "startup",
|
|
17
|
+
available: true,
|
|
18
|
+
limitationReason: null,
|
|
19
|
+
storedValue: value,
|
|
20
|
+
effectiveValue: value,
|
|
21
|
+
choices: models.map(model => model.key),
|
|
22
|
+
flags: models.map(model => ({
|
|
23
|
+
key: model.key, label: model.label, description: model.name, fallback: false,
|
|
24
|
+
})),
|
|
25
|
+
label: "Default model",
|
|
26
|
+
description: "Model used when a new session starts",
|
|
27
|
+
resolvedWhenRead: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export async function writeDefaultModel(settings, models, value) {
|
|
31
|
+
if (typeof value !== "string" || !models.some(model => model.key === value)) {
|
|
32
|
+
throw new TypeError("setting value is invalid: defaultModel");
|
|
33
|
+
}
|
|
34
|
+
const previous = read(settings);
|
|
35
|
+
const at = value.indexOf("/");
|
|
36
|
+
settings.setDefaultModelAndProvider(value.slice(0, at), value.slice(at + 1));
|
|
37
|
+
try {
|
|
38
|
+
await settings.flush();
|
|
39
|
+
const errors = settings.drainErrors();
|
|
40
|
+
if (errors.length > 0)
|
|
41
|
+
throw new Error(errors.map(item => item.error.message).join("; "));
|
|
42
|
+
return {
|
|
43
|
+
status: "deferred", application: "next-session", storedValue: value, effectiveValue: previous,
|
|
44
|
+
failure: null, limitationReason: null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
let failure = error instanceof Error ? error.message : String(error);
|
|
49
|
+
try {
|
|
50
|
+
if (previous === UNSET)
|
|
51
|
+
await settings.reload();
|
|
52
|
+
else {
|
|
53
|
+
const split = previous.indexOf("/");
|
|
54
|
+
settings.setDefaultModelAndProvider(previous.slice(0, split), previous.slice(split + 1));
|
|
55
|
+
await settings.flush();
|
|
56
|
+
const errors = settings.drainErrors();
|
|
57
|
+
if (errors.length > 0)
|
|
58
|
+
throw new Error(errors.map(item => item.error.message).join("; "));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (rollbackError) {
|
|
62
|
+
failure += `; persistence rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`;
|
|
63
|
+
}
|
|
64
|
+
const restored = read(settings);
|
|
65
|
+
return {
|
|
66
|
+
status: "failed", application: "next-session", storedValue: restored, effectiveValue: previous,
|
|
67
|
+
failure, limitationReason: null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -10,7 +10,7 @@ export { PiSessionSelectionError, openSelectedPiSession, resolveSessionArgumentP
|
|
|
10
10
|
export type { PiSessionForkPrompt, PiSessionSelection } from "./session-selection.js";
|
|
11
11
|
export { PiSessionCommandIntegration } from "./session-integration.js";
|
|
12
12
|
export type { PiDocumentedSessionCommands, PiSessionCommand, PiSessionCommandResult } from "./session-integration.js";
|
|
13
|
-
export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift,
|
|
13
|
+
export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, } from "./settings-bridge.js";
|
|
14
14
|
export { assertPiSettingsMetadata, loadPiSettingsMetadata, PI_SETTINGS_METADATA_FILE } from "./settings-metadata.js";
|
|
15
15
|
export type { PiSettingBounds, PiSettingPresentation, PiSettingsMetadata } from "./settings-metadata.js";
|
|
16
16
|
export type { PiSettingEffectDefinition, PiSettingEffectHandler, PiSettingKey, PiSettingOwnerHandlers, PiSettingStorageOperation, PiSettingVisualClass, PiSettingVisualEvidence, PiSettingsCoordinatorOptions, PiSettingsModelChoice, PiSettingsProviders, } from "./settings-bridge.js";
|
|
@@ -4,7 +4,7 @@ export { detachedEngineHostPorts } from "./host-ports.js";
|
|
|
4
4
|
export { bindPiRuntimeSession, createPiRuntimeIntegration, createPiRuntimeServicesAfterTrust, disposePiRuntimeIntegration, replacePiRuntimeSession, resolveConfiguredModelScope, } from "./runtime-integration.js";
|
|
5
5
|
export { PiSessionSelectionError, openSelectedPiSession, resolveSessionArgumentPath } from "./session-selection.js";
|
|
6
6
|
export { PiSessionCommandIntegration } from "./session-integration.js";
|
|
7
|
-
export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift,
|
|
7
|
+
export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, } from "./settings-bridge.js";
|
|
8
8
|
export { assertPiSettingsMetadata, loadPiSettingsMetadata, PI_SETTINGS_METADATA_FILE } from "./settings-metadata.js";
|
|
9
9
|
export { resolvePiProjectTrustPreflight } from "./project-trust-preflight.js";
|
|
10
10
|
export { configureOwnedHttpDispatcher, configuredOwnedHttpIdleTimeoutMs } from "./http-dispatcher.js";
|
|
@@ -40,6 +40,7 @@ function modelChoices(models, overrides) {
|
|
|
40
40
|
return {
|
|
41
41
|
key,
|
|
42
42
|
label: `${model.id} [${model.provider}]`,
|
|
43
|
+
name: model.name ?? model.id,
|
|
43
44
|
description: overrides[key] === undefined ? "global default" : `override: ${overrides[key]}`,
|
|
44
45
|
levels: supportedThinkingLevels(model),
|
|
45
46
|
};
|
|
@@ -41,7 +41,7 @@ interface ConfiguredModelScope {
|
|
|
41
41
|
* Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
|
|
42
42
|
* into a scoped model list, keep the resolver's warnings (e.g. "No models match
|
|
43
43
|
* pattern ..."), and pick the same initial model pinned Pi would pick — the
|
|
44
|
-
* saved default
|
|
44
|
+
* saved authenticated default, otherwise the first scoped model.
|
|
45
45
|
*/
|
|
46
46
|
export declare function resolveConfiguredModelScope(services: Pick<AgentSessionServices, "settingsManager" | "modelRuntime">, signal?: AbortSignal): Promise<ConfiguredModelScope>;
|
|
47
47
|
export declare function createPiRuntimeServicesAfterTrust(options: Pick<PiRuntimeIntegrationOptions, "agentDir" | "projectTrustPrompt" | "preflightDependencies" | "markStartupPhase"> & {
|
|
@@ -62,7 +62,7 @@ const runtimeSessionWriters = new WeakMap();
|
|
|
62
62
|
* Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
|
|
63
63
|
* into a scoped model list, keep the resolver's warnings (e.g. "No models match
|
|
64
64
|
* pattern ..."), and pick the same initial model pinned Pi would pick — the
|
|
65
|
-
* saved default
|
|
65
|
+
* saved authenticated default, otherwise the first scoped model.
|
|
66
66
|
*/
|
|
67
67
|
export async function resolveConfiguredModelScope(services, signal) {
|
|
68
68
|
const patterns = services.settingsManager.getEnabledModels();
|
|
@@ -70,21 +70,19 @@ export async function resolveConfiguredModelScope(services, signal) {
|
|
|
70
70
|
return { scopedModels: [], model: undefined, thinkingLevel: undefined, diagnostics: [] };
|
|
71
71
|
}
|
|
72
72
|
const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics([...patterns], services.modelRuntime, { signal: signal === undefined ? AbortSignal.timeout(15_000) : AbortSignal.any([signal, AbortSignal.timeout(15_000)]) });
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
: undefined;
|
|
80
|
-
selected = (savedModel
|
|
81
|
-
? scopedModels.find(scoped => scoped.model.provider === savedModel.provider && scoped.model.id === savedModel.id)
|
|
82
|
-
: undefined) ?? scopedModels[0];
|
|
83
|
-
}
|
|
73
|
+
const savedProvider = services.settingsManager.getDefaultProvider();
|
|
74
|
+
const savedModelId = services.settingsManager.getDefaultModel();
|
|
75
|
+
const savedModel = savedProvider && savedModelId
|
|
76
|
+
? services.modelRuntime.getAvailableSnapshot().find(model => model.provider === savedProvider && model.id === savedModelId)
|
|
77
|
+
: undefined;
|
|
78
|
+
const fallback = scopedModels[0];
|
|
84
79
|
return {
|
|
85
80
|
scopedModels,
|
|
86
|
-
model:
|
|
87
|
-
thinkingLevel:
|
|
81
|
+
model: savedModel ?? fallback?.model,
|
|
82
|
+
thinkingLevel: savedModel
|
|
83
|
+
? services.settingsManager.getModelThinkingLevel(savedModel.provider, savedModel.id)
|
|
84
|
+
?? services.settingsManager.getDefaultThinkingLevel()
|
|
85
|
+
: fallback?.thinkingLevel,
|
|
88
86
|
diagnostics: diagnostics.map(diagnostic => ({ type: diagnostic.type, message: diagnostic.message })),
|
|
89
87
|
};
|
|
90
88
|
}
|
|
@@ -65,11 +65,9 @@ export declare function settingsEffectInventoryDrift(presented: readonly string[
|
|
|
65
65
|
readonly stale: readonly string[];
|
|
66
66
|
readonly duplicated: readonly string[];
|
|
67
67
|
};
|
|
68
|
-
export declare function settingsVisualInventoryViolations(presented: readonly string[], reviewed?: Readonly<Partial<Record<PiSettingKey, PiSettingEffectDefinition>>>): readonly string[];
|
|
69
68
|
/** Values only the running engine knows. */
|
|
70
69
|
export interface PiSettingsProviders {
|
|
71
70
|
readonly themes?: () => readonly string[];
|
|
72
|
-
/** Models a per-model thinking override may name, with the levels each one supports. */
|
|
73
71
|
readonly models?: () => readonly PiSettingsModelChoice[];
|
|
74
72
|
readonly productMode?: "bare" | "comparison";
|
|
75
73
|
}
|
|
@@ -77,6 +75,7 @@ export interface PiSettingsModelChoice {
|
|
|
77
75
|
/** `provider/modelId`, the key the engine stores the override under. */
|
|
78
76
|
readonly key: string;
|
|
79
77
|
readonly label: string;
|
|
78
|
+
readonly name: string;
|
|
80
79
|
readonly description: string;
|
|
81
80
|
readonly levels: readonly string[];
|
|
82
81
|
}
|
|
@@ -15,6 +15,7 @@ export const PI_SETTING_EFFECTS = Object.freeze({
|
|
|
15
15
|
transport: effect("live", "agent", "status-error", "pinned provider request status and failure rows", "settings-effects-provider-parity"),
|
|
16
16
|
httpIdleTimeoutMs: effect("live", "agent", "retry-error", "pinned timeout, retry, and terminal failure rows", "pinned-transcript-lifecycle-parity"),
|
|
17
17
|
cacheWarmingMode: effect("live", "agent", "footer-transcript", "pinned cache warming notices and session usage rows", "pinned-transcript-lifecycle-parity"),
|
|
18
|
+
defaultModel: effect("next-session", "startup", "selector", "pinned default-model selector", "owned-default-model-setting"),
|
|
18
19
|
modelThinkingLevels: effect("live", "agent", "footer-transcript", "pinned per-model thinking override, footer indicator, and thinking rows", "pinned-status-indicator-parity"),
|
|
19
20
|
theme: hiddenEffect("live", "shell", "pinned theme selector and complete themed shell", "pinned-theme-parity"),
|
|
20
21
|
hideThinkingBlock: effect("live", "shell", "transcript", "pinned thinking block presence and spacing", "pinned-transcript-lifecycle-parity"),
|
|
@@ -225,27 +226,6 @@ export function settingsEffectInventoryDrift(presented, reviewed = Object.keys(P
|
|
|
225
226
|
duplicated: [...counts].filter(([, count]) => count > 1).map(([key]) => key),
|
|
226
227
|
};
|
|
227
228
|
}
|
|
228
|
-
export function settingsVisualInventoryViolations(presented, reviewed = PI_SETTING_EFFECTS) {
|
|
229
|
-
const violations = [];
|
|
230
|
-
for (const candidate of presented) {
|
|
231
|
-
const key = candidate;
|
|
232
|
-
const definition = reviewed[key];
|
|
233
|
-
if (definition === undefined) {
|
|
234
|
-
violations.push(`${candidate}: missing visual classification`);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
const visual = definition.visual;
|
|
238
|
-
if (!visual || visual.pinnedSurface.trim().length === 0)
|
|
239
|
-
violations.push(`${candidate}: missing pinned visual source`);
|
|
240
|
-
if (!visual || visual.evidence.trim().length === 0)
|
|
241
|
-
violations.push(`${candidate}: missing independent visual evidence`);
|
|
242
|
-
if (definition.hiddenInBare === true && visual?.class !== "hidden")
|
|
243
|
-
violations.push(`${candidate}: hidden setting declares a visible frame`);
|
|
244
|
-
if (definition.hiddenInBare !== true && visual?.class === "hidden")
|
|
245
|
-
violations.push(`${candidate}: visible setting declares hidden evidence`);
|
|
246
|
-
}
|
|
247
|
-
return violations;
|
|
248
|
-
}
|
|
249
229
|
function outcome(status, application, storedValue, effectiveValue, failure, limitationReason) {
|
|
250
230
|
return { status, application, storedValue, effectiveValue, failure, limitationReason };
|
|
251
231
|
}
|
|
@@ -271,6 +251,7 @@ export const AUTOMATIC_THEME = "automatic";
|
|
|
271
251
|
const LIGHT_APPEARANCE_THEME = "light";
|
|
272
252
|
const DARK_APPEARANCE_THEME = "dark";
|
|
273
253
|
const THEME_KEY = "theme";
|
|
254
|
+
const DEFAULT_MODEL_KEY = "defaultModel";
|
|
274
255
|
const MODEL_THINKING_KEY = "modelThinkingLevels";
|
|
275
256
|
export function parseAutomaticTheme(stored) {
|
|
276
257
|
const at = stored.indexOf("/");
|
|
@@ -307,13 +288,16 @@ export class PiSettingsBridge {
|
|
|
307
288
|
capabilities = { write: true, flush: true };
|
|
308
289
|
#operations;
|
|
309
290
|
#providers;
|
|
291
|
+
#settings;
|
|
310
292
|
#coordinator;
|
|
311
293
|
constructor(settings, providers = {}) {
|
|
312
294
|
this.#providers = providers;
|
|
295
|
+
this.#settings = settings;
|
|
313
296
|
const mapped = operations(settings, providers);
|
|
314
297
|
this.#operations = new Map(mapped.map(operation => [operation.key, operation]));
|
|
315
298
|
const presentationDrift = settingsInventoryDrift(EXPOSED_SETTING_KEYS, mapped.map(operation => operation.key));
|
|
316
|
-
const
|
|
299
|
+
const generatedEffects = Object.keys(PI_SETTING_EFFECTS).filter(key => key !== DEFAULT_MODEL_KEY);
|
|
300
|
+
const effectDrift = settingsEffectInventoryDrift(EXPOSED_SETTING_KEYS, generatedEffects);
|
|
317
301
|
if (presentationDrift.unmapped.length > 0 || presentationDrift.stale.length > 0
|
|
318
302
|
|| effectDrift.unmapped.length > 0 || effectDrift.stale.length > 0 || effectDrift.duplicated.length > 0) {
|
|
319
303
|
throw new Error([
|
|
@@ -331,7 +315,8 @@ export class PiSettingsBridge {
|
|
|
331
315
|
});
|
|
332
316
|
}
|
|
333
317
|
bindOwner(owner, handlers) {
|
|
334
|
-
|
|
318
|
+
const { defaultModel: _defaultModel, ...generated } = handlers;
|
|
319
|
+
return this.#coordinator.bindOwner(owner, generated);
|
|
335
320
|
}
|
|
336
321
|
unbindOwner(owner) {
|
|
337
322
|
this.#coordinator.unbindOwner(owner);
|
|
@@ -341,14 +326,14 @@ export class PiSettingsBridge {
|
|
|
341
326
|
const at = PRESENTATION.order.indexOf(key);
|
|
342
327
|
return at < 0 ? PRESENTATION.order.length : at;
|
|
343
328
|
};
|
|
344
|
-
|
|
329
|
+
const descriptors = [...this.#operations.values()]
|
|
345
330
|
.filter(operation => this.#coordinator.available(operation.key))
|
|
346
331
|
.map(operation => {
|
|
347
332
|
const key = operation.key;
|
|
348
333
|
const wording = PRESENTATION.settings[key];
|
|
349
334
|
const flags = PRESENTATION.dialogs[key];
|
|
350
335
|
const bounds = PRESENTATION.bounds[key];
|
|
351
|
-
|
|
336
|
+
return this.#resolved({
|
|
352
337
|
...operation.descriptor,
|
|
353
338
|
application: PI_SETTING_EFFECTS[key].application,
|
|
354
339
|
owner: PI_SETTING_EFFECTS[key].owner,
|
|
@@ -360,10 +345,11 @@ export class PiSettingsBridge {
|
|
|
360
345
|
...(bounds === undefined ? {} : bounds),
|
|
361
346
|
...(wording === undefined ? {} : { label: wording.label, description: wording.description }),
|
|
362
347
|
...(flags === undefined ? {} : { flags }),
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
})
|
|
366
|
-
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
const { defaultModelDescriptor } = await import("./default-model-setting.js");
|
|
351
|
+
descriptors.push(defaultModelDescriptor(this.#settings, this.#providers.models?.() ?? []));
|
|
352
|
+
return descriptors.sort((left, right) => rank(left.key) - rank(right.key));
|
|
367
353
|
}
|
|
368
354
|
#resolved(descriptor) {
|
|
369
355
|
if (descriptor.key === THEME_KEY) {
|
|
@@ -387,9 +373,17 @@ export class PiSettingsBridge {
|
|
|
387
373
|
return this.#providers.themes?.() ?? [];
|
|
388
374
|
}
|
|
389
375
|
async readSetting(key) {
|
|
376
|
+
if (key === DEFAULT_MODEL_KEY) {
|
|
377
|
+
const { defaultModelDescriptor } = await import("./default-model-setting.js");
|
|
378
|
+
return defaultModelDescriptor(this.#settings, this.#providers.models?.() ?? []).storedValue;
|
|
379
|
+
}
|
|
390
380
|
return isPiSettingKey(key) && this.#operations.has(key) ? this.#coordinator.storedValue(key) : undefined;
|
|
391
381
|
}
|
|
392
382
|
async writeSetting(key, value) {
|
|
383
|
+
if (key === DEFAULT_MODEL_KEY) {
|
|
384
|
+
const { writeDefaultModel } = await import("./default-model-setting.js");
|
|
385
|
+
return await writeDefaultModel(this.#settings, this.#providers.models?.() ?? [], value);
|
|
386
|
+
}
|
|
393
387
|
if (!isPiSettingKey(key) || !this.#operations.has(key))
|
|
394
388
|
throw new Error(`setting is unavailable: ${key}`);
|
|
395
389
|
return await this.#coordinator.apply(key, value);
|
|
@@ -77,7 +77,7 @@ export class PiEngineSettings {
|
|
|
77
77
|
});
|
|
78
78
|
this.#settingsIntegration.bindOwner("startup", {
|
|
79
79
|
// Invariant: deferred application is the owner operation: the next preflight reads
|
|
80
|
-
// the persisted
|
|
80
|
+
// the persisted defaults before constructing project-backed services.
|
|
81
81
|
defaultProjectTrust: { apply() { } },
|
|
82
82
|
collapseChangelog: { apply() { } },
|
|
83
83
|
});
|
|
@@ -167,6 +167,7 @@ export class PiEngineSettings {
|
|
|
167
167
|
return {
|
|
168
168
|
key,
|
|
169
169
|
label: `${model.id} [${model.provider}]`,
|
|
170
|
+
name: model.name,
|
|
170
171
|
description: overrides[key] === undefined ? "global default" : `override: ${overrides[key]}`,
|
|
171
172
|
levels: supportedThinkingLevels(model),
|
|
172
173
|
};
|
|
@@ -333,13 +333,13 @@ export class PiWorkflowRunner {
|
|
|
333
333
|
});
|
|
334
334
|
}
|
|
335
335
|
case "models": {
|
|
336
|
-
// Invariant: the bare dialog seeds its query from the argument; only an explicit selection switches
|
|
336
|
+
// Invariant: the bare dialog seeds its query from the argument; only an explicit selection switches the current session.
|
|
337
337
|
if (!selection)
|
|
338
338
|
return workflowResult(request.command, "requires-selection", "Select a model", argument || undefined, "silent");
|
|
339
339
|
return this.#selectModel(request, selection, {
|
|
340
340
|
pool: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])],
|
|
341
341
|
refreshOnMiss: true,
|
|
342
|
-
persist:
|
|
342
|
+
persist: false,
|
|
343
343
|
onMiss: "failed",
|
|
344
344
|
});
|
|
345
345
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "darwin",
|
|
8
8
|
"architecture": "arm64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T17:03:50.345Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "arm64",
|
|
10
10
|
"target": "aarch64-apple-darwin",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T17:07:06.927Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "1a0b3f286201df724ebed4c0596f0fe7d8ab5d27dea36c588dc4f4a60049995f",
|
|
16
|
+
"size": 6585632
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "darwin",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T17:03:58.940Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-apple-darwin",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T17:09:41.020Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "1d003bf193eb4c54439e711427faef94af2c395dd2a23cfb1497bb2c3ab4ad67",
|
|
16
|
+
"size": 7178048
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "linux",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T17:04:12.745Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-unknown-linux-gnu",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T17:06:54.055Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "fb9f0498986293e8dbed8a2b043313bf78a07777f6a1eb0a4b62e82365a71110",
|
|
16
|
+
"size": 7959248
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"platform": "win32",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T17:04:32.725Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "3fb5f440a2febbb9c84e1d7761a785f0d275db0d689cecf175c6c3de47f6b50d",
|
|
14
14
|
"size": 402432
|
|
15
15
|
},
|
|
16
16
|
"provenance": {
|
|
Binary file
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-pc-windows-msvc",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T17:06:43.680Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "5592caa99ee4c96544abaceeb025f833c1d8566529e3a7a8ecad51f8aa13e752",
|
|
16
|
+
"size": 7077888
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -9,6 +9,8 @@ import type { UiTheme } from "./theme.js";
|
|
|
9
9
|
export interface DialogRow {
|
|
10
10
|
readonly label: string;
|
|
11
11
|
readonly labelSuffix?: string;
|
|
12
|
+
/** Success-colored marker rendered after the complete label. */
|
|
13
|
+
readonly successSuffix?: string;
|
|
12
14
|
readonly value: string;
|
|
13
15
|
/** What this part does, shown while it is the one in hand. */
|
|
14
16
|
readonly description?: string;
|
|
@@ -31,8 +33,9 @@ export interface DialogPanelFrame {
|
|
|
31
33
|
}
|
|
32
34
|
export interface SteppedDialogPanelState {
|
|
33
35
|
readonly title: string;
|
|
34
|
-
|
|
35
|
-
readonly
|
|
36
|
+
/** Omitted for a one-step searchable selector whose title needs no step marker. */
|
|
37
|
+
readonly step?: number;
|
|
38
|
+
readonly steps?: number;
|
|
36
39
|
readonly description: string;
|
|
37
40
|
readonly input?: LineInput;
|
|
38
41
|
readonly rows: readonly DialogRow[];
|
|
@@ -55,7 +58,7 @@ export declare function renderCenteredDialogOverlay(frame: readonly string[], di
|
|
|
55
58
|
export declare function dialogValueColumn(rows: readonly DialogRow[]): number;
|
|
56
59
|
/** The semantic rows for one generic configure modal. */
|
|
57
60
|
export declare function renderDialogPanel(state: DialogPanelState, width: number, theme: UiTheme): readonly string[];
|
|
58
|
-
/** Owned
|
|
61
|
+
/** Owned searchable selector content, with an optional step marker for nested choices. */
|
|
59
62
|
export declare function renderSteppedDialogPanel(state: SteppedDialogPanelState, width: number, theme: UiTheme): readonly string[];
|
|
60
63
|
/** The row a pointer report lands on, or null when it is not on one. */
|
|
61
64
|
export declare function dialogRowAt(frame: DialogPanelFrame, row: number, column: number, valueWidth: number): number | null;
|
|
@@ -99,18 +99,19 @@ export function renderDialogPanel(state, width, theme) {
|
|
|
99
99
|
layout.blank,
|
|
100
100
|
];
|
|
101
101
|
}
|
|
102
|
-
/** Owned
|
|
102
|
+
/** Owned searchable selector content, with an optional step marker for nested choices. */
|
|
103
103
|
export function renderSteppedDialogPanel(state, width, theme) {
|
|
104
104
|
const layout = dialogPanelLayout(width, theme);
|
|
105
|
-
const labelColumn = Math.min(STEPPED_LABEL_COLUMN_CAP, Math.max(0, ...state.rows.map(row => displayWidth(`${row.label}${row.labelSuffix ?? ""}`))));
|
|
105
|
+
const labelColumn = Math.min(STEPPED_LABEL_COLUMN_CAP, Math.max(0, ...state.rows.map(row => displayWidth(`${row.label}${row.labelSuffix ?? ""}${row.successSuffix === undefined ? "" : ` ${row.successSuffix}`}`))));
|
|
106
106
|
const rows = state.rows.map((row, index) => {
|
|
107
107
|
const selected = index === state.index;
|
|
108
|
-
const
|
|
108
|
+
const success = row.successSuffix === undefined ? "" : ` ${row.successSuffix}`;
|
|
109
|
+
const completeLabel = `${row.label}${row.labelSuffix ?? ""}${success}`;
|
|
109
110
|
const padding = " ".repeat(Math.max(0, labelColumn - displayWidth(completeLabel)));
|
|
110
111
|
const suffix = row.value.length === 0 ? "" : ` ${row.value}`;
|
|
111
112
|
const cursor = selected ? "→ " : " ";
|
|
112
113
|
const raw = `${cursor}${completeLabel}${padding}${suffix}`;
|
|
113
|
-
const label = `${theme.fg("text", row.label)}${theme.fg("muted", row.labelSuffix ?? "")}${padding}`;
|
|
114
|
+
const label = `${theme.fg("text", row.label)}${theme.fg("muted", row.labelSuffix ?? "")}${theme.fg("success", success)}${padding}`;
|
|
114
115
|
const content = selected
|
|
115
116
|
? `${theme.fg("accent", cursor)}${label}${theme.fg("muted", suffix)}`
|
|
116
117
|
: `${cursor}${label}${theme.fg("muted", suffix)}`;
|
|
@@ -128,9 +129,11 @@ export function renderSteppedDialogPanel(state, width, theme) {
|
|
|
128
129
|
layout.blank,
|
|
129
130
|
];
|
|
130
131
|
const descriptionRows = wrapText(state.description, layout.contentWidth);
|
|
131
|
-
const step = `(step ${state.step}/${state.steps})`;
|
|
132
|
-
const title = `${state.title} ${step}`;
|
|
133
|
-
const paintedTitle =
|
|
132
|
+
const step = state.step === undefined || state.steps === undefined ? "" : `(step ${state.step}/${state.steps})`;
|
|
133
|
+
const title = step.length === 0 ? state.title : `${state.title} ${step}`;
|
|
134
|
+
const paintedTitle = step.length === 0
|
|
135
|
+
? theme.bold(theme.fg("accent", state.title))
|
|
136
|
+
: `${theme.bold(theme.fg("accent", state.title))} ${theme.fg("dim", step)}`;
|
|
134
137
|
return [
|
|
135
138
|
layout.blank,
|
|
136
139
|
layout.contentRow(paintedTitle, title),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type UiThemeToken = "accent" | "mdHeading" | "text" | "muted" | "dim" | "border" | "error";
|
|
1
|
+
export type UiThemeToken = "accent" | "mdHeading" | "text" | "muted" | "dim" | "border" | "error" | "success";
|
|
2
2
|
export interface UiTheme {
|
|
3
3
|
fg(token: UiThemeToken, text: string): string;
|
|
4
4
|
bold(text: string): string;
|
|
@@ -29,8 +29,8 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
|
|
|
29
29
|
| `ui-components/list-block.ts` — sticky scroll | `settings/impl.ts` — `stickyHeaderGroup`, `topPaddingRows`, `visibleRowCountAt`, `clampScrollForView` | Same reservation arithmetic and two-pass reveal. |
|
|
30
30
|
| `ui-components/mouse.ts` | `core/panes/sgr-mouse.ts` | Same SGR decoding and per-call regex reset; A1 emits its own event shape. |
|
|
31
31
|
| `ui-components/mouse.ts` — tracking sequences | `core/host/pi/providers/host-bridge-surface.ts` | Mouse modes only. A1 does not take the alternate screen, because the Pi TUI owns the screen A1 renders through. |
|
|
32
|
-
| `features/owned-ui/settings-app.ts` — section layout, pointer controls, and
|
|
33
|
-
| `pi-components/models-dialog.ts` — the bare-A1 `/models` dialog | `models/index.ts` and `core/editor/components.ts` — `EditorModal` | Keeps the `all`/`scoped` filters, substring search over id/name/provider, provider-then-id ordering, Enter to switch, Space to scope, Ctrl+S to save, the `(unsaved)` marker, and the `●`/`○` scope markers. A1 renders the marker before the id and the active `✓` after `[provider]`, lists the scoped filter in cycling order (saved-but-removed members last), keeps the pinned bulk/provider/reorder bindings, and hands every switch, scope write, save, and catalog refresh to the engine adapter
|
|
32
|
+
| `features/owned-ui/settings-app.ts` — section layout, pointer controls, scrolling, and Default Model configuration | `settings/impl.ts` — `settingsValueColumn`, block navigation, sticky sections, and pointer hit regions; pinned model selector default state | Setting discovery is A1's own A1/Agent section model. Section navigation and pointer-only numeric controls follow the A1 reference; explicit `/` search, ruled shared input, shortcut-derived hints, hidden description rows, configured wheel cadence, a rail presented through the shared scrollbar settings, `Ctrl+Home`/`Ctrl+End` boundary jumps, distinct floating scalar menus, and a searchable persisted-default model configurator are reviewed owned interactions. |
|
|
33
|
+
| `pi-components/models-dialog.ts` — the bare-A1 `/models` dialog | `models/index.ts` and `core/editor/components.ts` — `EditorModal` | Keeps the `all`/`scoped` filters, substring search over id/name/provider, provider-then-id ordering, Enter to switch the current session, Space to scope, Ctrl+S to save scope, the `(unsaved)` marker, and the `●`/`○` scope markers. A1 renders the marker before the id and the active `✓` after `[provider]`, lists the scoped filter in cycling order (saved-but-removed members last), keeps the pinned bulk/provider/reorder bindings, and hands every switch, scope write, save, and catalog refresh to the engine adapter. Persisted default-model changes are deliberately separate in owned Settings. |
|
|
34
34
|
|
|
35
35
|
## Ported from the pinned engine
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
|
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `src/cli/package-messages.ts`, `src/cli/packages.ts`, and `src/integrations/pi/engine/package-integration.ts` — package/model CLI messages | Pi 0.84.2 `packages/coding-agent/src/package-manager-cli.ts` and public `DefaultPackageManager` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Preserves terminal-aware Chalk success/error/progress styling, literal model-refresh messages, operational details, and user-settings warnings. Focused syntax/help substitutes the `a1 pi` namespace and supported grammar; A1 syntax exit status remains two. Profile isolation and independent-Pi-update rejection remain intentional. Independent CLI evidence: `test/cli/package-message-parity.test.ts` runs the untouched pinned CLI presenter in an isolated process with model-runtime test substitution, not A1-generated expected strings. This does not certify the still-pending interactive message outcomes. |
|
|
40
40
|
| `pi-engine-adapter/adapter.ts`, `engine/workflows.ts`, and `session-shell/session-shell.ts` — interactive command outcomes | Pi 0.84.2 `modes/interactive/interactive-mode.ts`, `core/model-resolver.ts`, and `config.ts` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Preserves route-specific status/warning/error semantics, authentication partial-success sequences, stored-credential logout wording, fork/clone empty states, import context/recovery, share URL and failure/cancellation behavior, and post-login warning lifetime. A1 deliberately returns recoverable failed workflow results instead of adopting Pi's process-owning shutdown/exit for fatal `/new`, `/resume`, and `/import` outcomes. Focused outcome evidence is in `test/integrations/pi/engine/workflows.test.ts` and `test/app/session-shell/session-shell.test.ts`; terminal-cell geometry remains a separate milestone. |
|
|
41
|
-
| `pi-engine-adapter/settings-bridge.ts` — presentation metadata | pinned Pi settings selector | Labels, descriptions, order, dialog parts, and bounds are extracted from the selector source at build time by `scripts/pi/build-pi-settings-metadata.mjs` into `dist
|
|
41
|
+
| `pi-engine-adapter/settings-bridge.ts` — presentation metadata | pinned Pi settings selector and model selector | Labels, descriptions, order, dialog parts, and bounds for generated settings are extracted from the settings selector source at build time by `scripts/pi/build-pi-settings-metadata.mjs` into `dist/`. The reviewed supplemental `defaultModel` setting comes from the pinned model selector and Pi's existing default-provider/model storage APIs; its separate inventory keeps generated metadata drift checks exact. |
|
|
42
42
|
| `pi-engine/session-integration.ts` and `pi-components/shell-footer-status.ts` — steering queue | pinned Pi interactive mode `onSubmit` and `updatePendingMessagesDisplay` | Steering/follow-up uses `prompt(..., { streamingBehavior })`, allowing Pi to emit the accepted user row, while remaining steering rows preserve Pi's opening spacer, dim `Steering:` labels, dequeue hint, and order before `Working`. |
|
|
43
43
|
| `ui-components/list-view.ts`, `dialog-panel.ts`, `value-menu.ts`, and `features/owned-ui/settings-app.ts` — setting presentation | pinned Pi `SettingsSelectorComponent`, Pi TUI `SettingsList`, `SelectList`, and `Input` at `0.84.2` | Cursor and selected label accents, unselected muted values, the 30-column label cap, dialog styling, notices, and narrow-width geometry retain pinned semantics. Scalar-menu placement and input remain shared, while the muted, hover-brightened selected value, A1/Agent grouping, pointer steppers, explicit `/` search, ruled shared input, shortcut-derived status hints, suppressed selected descriptions, `scrollbarSpeed`-driven wheel movement, a list rail that follows `scrollbarAppearance` and `scrollbarStyle` with transcript-style hover and drag, `Ctrl+Home`/`Ctrl+End` boundary jumps, and the dark floating menu with lighter active row and effective-value check mark are declared product-owned differences. Independent row evidence: `test/features/owned-ui/pinned-settings-presentation-parity.test.ts`; owned interaction evidence: `test/features/owned-ui/settings-app.test.ts`, `test/ui/components/value-menu.test.ts`, and `test/composition/settings-route-host.test.ts`. |
|
|
44
44
|
| `features/owned-ui/project-trust-prompt.ts` — pre-resource selector | pinned Pi `cli/startup-ui.ts`, `cli/project-trust.ts`, and `core/project-trust.ts` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Uses a fixed, dependency-bounded A1 startup selector rather than importing private CLI modules. It preserves selected-option accent, navigation/accept/reject/cancel semantics, fail-closed behavior, raw-mode restoration, clearing, cursor restoration, and parent-screen restoration before diagnostics. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timurproko/a1",
|
|
3
|
-
"version": "0.2.7-dev.
|
|
3
|
+
"version": "0.2.7-dev.1238",
|
|
4
4
|
"description": "Standalone terminal workspace for supervised native and managed agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"privateLaunchContract": "neutral-launch-v1",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"@mariozechner/clipboard": "^0.3.9"
|
|
124
124
|
},
|
|
125
125
|
"publication": {
|
|
126
|
-
"source": "
|
|
127
|
-
"pullRequest":
|
|
126
|
+
"source": "32afd4b3acaa0de3fddb429058084448ef5874fb",
|
|
127
|
+
"pullRequest": 863
|
|
128
128
|
}
|
|
129
129
|
}
|