@timurproko/a1 0.2.7-dev.810 → 0.2.7-dev.831
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/README.md +7 -1
- package/bin/automatic-update.js +7 -0
- package/bin/ui.js +1 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
- package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
- package/dist/app/session-shell/session-shell-root.d.ts +9 -3
- package/dist/app/session-shell/session-shell.js +70 -20
- package/dist/app/session-shell/session-viewport-controller.js +6 -4
- package/dist/app/session-shell/transcript-document.js +1 -1
- package/dist/composition/owned-ui.d.ts +3 -11
- package/dist/composition/owned-ui.js +93 -43
- package/dist/composition/resident-tab-bridge.d.ts +10 -1
- package/dist/composition/resident-tab-bridge.js +21 -0
- package/dist/composition/settings-route-host.d.ts +12 -2
- package/dist/composition/settings-route-host.js +27 -9
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/settings-app.d.ts +10 -3
- package/dist/features/owned-ui/settings-app.js +98 -45
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +2 -0
- package/dist/foundation/launch-context/index.d.ts +2 -0
- package/dist/foundation/launch-context/index.js +1 -0
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +1 -9
- package/dist/foundation/release/bootstrap.js +7 -3
- package/dist/foundation/release/index.d.ts +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -2
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/update-execution-lease.d.ts +36 -0
- package/dist/foundation/release/update-execution-lease.js +145 -0
- package/dist/foundation/release/update.d.ts +8 -6
- package/dist/foundation/release/update.js +232 -167
- package/dist/foundation/resident-tabs/bridge-client.d.ts +9 -3
- package/dist/foundation/resident-tabs/bridge-client.js +14 -7
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/settings.d.ts +0 -1
- package/dist/foundation/resident-tabs/settings.js +0 -2
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
- package/dist/integrations/pi/components/shell-footer-status.js +19 -3
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
- package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
- package/dist/integrations/pi/engine/adapter.js +7 -2
- package/dist/integrations/pi/engine/ports.d.ts +1 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/workflow-runner.js +9 -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/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.js +35 -30
- package/dist/ui/components/line-input.d.ts +3 -0
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +5 -4
- package/dist/ui/components/prompt-input.js +8 -6
- package/dist/ui/components/submitted-prompt.d.ts +2 -2
- package/dist/ui/components/submitted-prompt.js +3 -3
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/transcript-viewport.d.ts +1 -1
- package/dist/ui/components/transcript-viewport.js +141 -24
- package/dist/ui/settings/declarations.d.ts +65 -15
- package/dist/ui/settings/declarations.js +45 -13
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +22 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/resident-tabs.md +39 -11
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/package.json +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 19;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
5
5
|
readonly id: string;
|
|
6
|
-
/** Optional presentation label; the id remains the persistence key. */
|
|
7
6
|
readonly label?: string;
|
|
8
|
-
/** Optional settings-screen group for related owned controls. */
|
|
9
7
|
readonly section?: {
|
|
10
8
|
readonly id: string;
|
|
11
9
|
readonly title: string;
|
|
@@ -14,23 +12,28 @@ export interface OwnedUiSettingDeclaration {
|
|
|
14
12
|
readonly application: OwnedUiSettingApplication;
|
|
15
13
|
readonly defaultValue: OwnedUiSettingValue;
|
|
16
14
|
readonly allowedValues: readonly OwnedUiSettingValue[];
|
|
15
|
+
/**
|
|
16
|
+
* Parts of a structured setting, edited through their own dialog: the value is the comma-separated
|
|
17
|
+
* keys of the enabled parts in this order.
|
|
18
|
+
*/
|
|
19
|
+
readonly flags?: readonly {
|
|
20
|
+
readonly key: string;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly description: string;
|
|
23
|
+
}[];
|
|
17
24
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Every A1 setting, keyed by its id. The table is the one declaration: ids, defaults, and allowed
|
|
20
|
-
* values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
|
|
21
|
-
*/
|
|
22
25
|
export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
23
26
|
readonly updateCheck: Readonly<{
|
|
24
27
|
id: "updateCheck";
|
|
25
|
-
label: "Update
|
|
28
|
+
label: "Update";
|
|
26
29
|
section: Readonly<{
|
|
27
30
|
id: "generic";
|
|
28
31
|
title: "Generic";
|
|
29
32
|
}>;
|
|
30
|
-
description: "
|
|
33
|
+
description: "Auto installs verified channel updates silently, Notify shows the current update notice, and No leaves updates manual. Checks use the public npm registry; applies on next start.";
|
|
31
34
|
application: "restart";
|
|
32
|
-
defaultValue:
|
|
33
|
-
allowedValues: readonly [
|
|
35
|
+
defaultValue: "auto";
|
|
36
|
+
allowedValues: readonly ["auto", "notify", "no"];
|
|
34
37
|
}>;
|
|
35
38
|
readonly timeFormat: Readonly<{
|
|
36
39
|
id: "timeFormat";
|
|
@@ -123,11 +126,23 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
123
126
|
id: "history";
|
|
124
127
|
title: "History";
|
|
125
128
|
}>;
|
|
126
|
-
description: "Retain reusable prompts
|
|
129
|
+
description: "Retain reusable prompts after restart. Applies on next start; disabling does not erase saved history or stop existing instances.";
|
|
127
130
|
application: "restart";
|
|
128
131
|
defaultValue: true;
|
|
129
132
|
allowedValues: readonly [true, false];
|
|
130
133
|
}>;
|
|
134
|
+
readonly promptHistoryScope: Readonly<{
|
|
135
|
+
id: "promptHistoryScope";
|
|
136
|
+
label: "History scope";
|
|
137
|
+
section: Readonly<{
|
|
138
|
+
id: "history";
|
|
139
|
+
title: "History";
|
|
140
|
+
}>;
|
|
141
|
+
description: "Unique shows each agent session's history; Shared shows the profile-wide history. Switches running agents without moving or erasing saved prompts.";
|
|
142
|
+
application: "live";
|
|
143
|
+
defaultValue: "unique";
|
|
144
|
+
allowedValues: readonly ["unique", "shared"];
|
|
145
|
+
}>;
|
|
131
146
|
readonly promptHistoryMaxItems: Readonly<{
|
|
132
147
|
id: "promptHistoryMaxItems";
|
|
133
148
|
label: "History limit";
|
|
@@ -176,12 +191,49 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
176
191
|
defaultValue: 8;
|
|
177
192
|
allowedValues: readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
|
178
193
|
}>;
|
|
194
|
+
readonly tabsPosition: Readonly<{
|
|
195
|
+
id: "tabsPosition";
|
|
196
|
+
label: "Position";
|
|
197
|
+
section: Readonly<{
|
|
198
|
+
id: "tabs";
|
|
199
|
+
title: "Tab Bar";
|
|
200
|
+
}>;
|
|
201
|
+
description: "Place the resident tab bar at the top or bottom of the terminal.";
|
|
202
|
+
application: "live";
|
|
203
|
+
defaultValue: "top";
|
|
204
|
+
allowedValues: readonly ["top", "bottom"];
|
|
205
|
+
}>;
|
|
206
|
+
readonly tabBarPlusMenu: Readonly<{
|
|
207
|
+
id: "tabBarPlusMenu";
|
|
208
|
+
label: "Plus menu";
|
|
209
|
+
section: Readonly<{
|
|
210
|
+
id: "tabs";
|
|
211
|
+
title: "Tab Bar";
|
|
212
|
+
}>;
|
|
213
|
+
description: "The tabs + offers. With only Agent, + adds an Agent at once; with more, it opens a menu; with none, + is hidden.";
|
|
214
|
+
application: "live";
|
|
215
|
+
defaultValue: "agent";
|
|
216
|
+
allowedValues: readonly ["", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog"];
|
|
217
|
+
flags: readonly (Readonly<{
|
|
218
|
+
key: "agent";
|
|
219
|
+
label: "Agent";
|
|
220
|
+
description: "Add a new Agent tab every time.";
|
|
221
|
+
}> | Readonly<{
|
|
222
|
+
key: "settings";
|
|
223
|
+
label: "Settings";
|
|
224
|
+
description: "Show the one Settings tab.";
|
|
225
|
+
}> | Readonly<{
|
|
226
|
+
key: "changelog";
|
|
227
|
+
label: "Changelog";
|
|
228
|
+
description: "Show the one Changelog tab.";
|
|
229
|
+
}>)[];
|
|
230
|
+
}>;
|
|
179
231
|
readonly tabsAutoName: Readonly<{
|
|
180
232
|
id: "tabsAutoName";
|
|
181
233
|
label: "Auto name";
|
|
182
234
|
section: Readonly<{
|
|
183
235
|
id: "tabs";
|
|
184
|
-
title: "
|
|
236
|
+
title: "Tab Bar";
|
|
185
237
|
}>;
|
|
186
238
|
description: "Automatically name a new tab from its first completed turn with the tab's current model.";
|
|
187
239
|
application: "live";
|
|
@@ -190,9 +242,7 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
190
242
|
}>;
|
|
191
243
|
}>;
|
|
192
244
|
export type OwnedSettingId = keyof typeof OWNED_SETTING_DECLARATIONS;
|
|
193
|
-
/** The value type a setting's declaration allows: a literal union for a choice, `number` for a stepped range. */
|
|
194
245
|
export type OwnedSettingValueOf<Id extends OwnedSettingId> = (typeof OWNED_SETTING_DECLARATIONS)[Id]["allowedValues"][number];
|
|
195
|
-
/** The declarations in declaration order, for resolution, persistence, and the settings screen. */
|
|
196
246
|
export declare const OWNED_UI_SETTING_DECLARATIONS: readonly OwnedUiSettingDeclaration[];
|
|
197
247
|
export declare function isOwnedSettingId(id: string): id is OwnedSettingId;
|
|
198
248
|
export declare function assertOwnedUiSettingDeclarations(declarations: readonly OwnedUiSettingDeclaration[]): void;
|
|
@@ -1,28 +1,33 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 19;
|
|
2
2
|
const UI_ACCENT_COLOR_CHOICES = Object.freeze([
|
|
3
3
|
"purple", "blue", "cyan", "green", "orange", "pink",
|
|
4
4
|
]);
|
|
5
5
|
const MAX_ID_LENGTH = 64;
|
|
6
6
|
const ID_PATTERN = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
7
|
-
/** Declared first so the settings screen opens on it; sections follow first-declaration order. */
|
|
8
7
|
const GENERIC_SECTION = Object.freeze({ id: "generic", title: "Generic" });
|
|
9
8
|
const APPEARANCE_SECTION = Object.freeze({ id: "appearance", title: "Appearance" });
|
|
10
9
|
const SCROLL_SECTION = Object.freeze({ id: "scroll", title: "Scroll" });
|
|
11
10
|
const AGENT_SECTION = Object.freeze({ id: "agent", title: "Agent" });
|
|
12
|
-
const TABS_SECTION = Object.freeze({ id: "tabs", title: "
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const TABS_SECTION = Object.freeze({ id: "tabs", title: "Tab Bar" });
|
|
12
|
+
/** The tab kinds the strip's `+` can offer, in the order it shows them. */
|
|
13
|
+
const PLUS_MENU_KINDS = Object.freeze([
|
|
14
|
+
Object.freeze({ key: "agent", label: "Agent", description: "Add a new Agent tab every time." }),
|
|
15
|
+
Object.freeze({ key: "settings", label: "Settings", description: "Show the one Settings tab." }),
|
|
16
|
+
Object.freeze({ key: "changelog", label: "Changelog", description: "Show the one Changelog tab." }),
|
|
17
|
+
]);
|
|
18
|
+
/** Every enabled subset of the plus-menu kinds, each in kind order. */
|
|
19
|
+
const PLUS_MENU_VALUES = Object.freeze([
|
|
20
|
+
"", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog",
|
|
21
|
+
]);
|
|
17
22
|
export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
18
23
|
updateCheck: Object.freeze({
|
|
19
24
|
id: "updateCheck",
|
|
20
|
-
label: "Update
|
|
25
|
+
label: "Update",
|
|
21
26
|
section: GENERIC_SECTION,
|
|
22
|
-
description: "
|
|
27
|
+
description: "Auto installs verified channel updates silently, Notify shows the current update notice, and No leaves updates manual. Checks use the public npm registry; applies on next start.",
|
|
23
28
|
application: "restart",
|
|
24
|
-
defaultValue:
|
|
25
|
-
allowedValues: Object.freeze([
|
|
29
|
+
defaultValue: "auto",
|
|
30
|
+
allowedValues: Object.freeze(["auto", "notify", "no"]),
|
|
26
31
|
}),
|
|
27
32
|
timeFormat: Object.freeze({
|
|
28
33
|
id: "timeFormat",
|
|
@@ -95,11 +100,20 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
95
100
|
id: "promptHistoryEnabled",
|
|
96
101
|
label: "Persistent history",
|
|
97
102
|
section: Object.freeze({ id: "history", title: "History" }),
|
|
98
|
-
description: "Retain reusable prompts
|
|
103
|
+
description: "Retain reusable prompts after restart. Applies on next start; disabling does not erase saved history or stop existing instances.",
|
|
99
104
|
application: "restart",
|
|
100
105
|
defaultValue: true,
|
|
101
106
|
allowedValues: Object.freeze([true, false]),
|
|
102
107
|
}),
|
|
108
|
+
promptHistoryScope: Object.freeze({
|
|
109
|
+
id: "promptHistoryScope",
|
|
110
|
+
label: "History scope",
|
|
111
|
+
section: Object.freeze({ id: "history", title: "History" }),
|
|
112
|
+
description: "Unique shows each agent session's history; Shared shows the profile-wide history. Switches running agents without moving or erasing saved prompts.",
|
|
113
|
+
application: "live",
|
|
114
|
+
defaultValue: "unique",
|
|
115
|
+
allowedValues: Object.freeze(["unique", "shared"]),
|
|
116
|
+
}),
|
|
103
117
|
promptHistoryMaxItems: Object.freeze({
|
|
104
118
|
id: "promptHistoryMaxItems",
|
|
105
119
|
label: "History limit",
|
|
@@ -136,6 +150,25 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
136
150
|
defaultValue: 8,
|
|
137
151
|
allowedValues: Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
|
|
138
152
|
}),
|
|
153
|
+
tabsPosition: Object.freeze({
|
|
154
|
+
id: "tabsPosition",
|
|
155
|
+
label: "Position",
|
|
156
|
+
section: TABS_SECTION,
|
|
157
|
+
description: "Place the resident tab bar at the top or bottom of the terminal.",
|
|
158
|
+
application: "live",
|
|
159
|
+
defaultValue: "top",
|
|
160
|
+
allowedValues: Object.freeze(["top", "bottom"]),
|
|
161
|
+
}),
|
|
162
|
+
tabBarPlusMenu: Object.freeze({
|
|
163
|
+
id: "tabBarPlusMenu",
|
|
164
|
+
label: "Plus menu",
|
|
165
|
+
section: TABS_SECTION,
|
|
166
|
+
description: "The tabs + offers. With only Agent, + adds an Agent at once; with more, it opens a menu; with none, + is hidden.",
|
|
167
|
+
application: "live",
|
|
168
|
+
defaultValue: "agent",
|
|
169
|
+
allowedValues: PLUS_MENU_VALUES,
|
|
170
|
+
flags: PLUS_MENU_KINDS,
|
|
171
|
+
}),
|
|
139
172
|
tabsAutoName: Object.freeze({
|
|
140
173
|
id: "tabsAutoName",
|
|
141
174
|
label: "Auto name",
|
|
@@ -146,7 +179,6 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
146
179
|
allowedValues: Object.freeze([true, false]),
|
|
147
180
|
}),
|
|
148
181
|
});
|
|
149
|
-
/** The declarations in declaration order, for resolution, persistence, and the settings screen. */
|
|
150
182
|
export const OWNED_UI_SETTING_DECLARATIONS = Object.freeze(Object.values(OWNED_SETTING_DECLARATIONS));
|
|
151
183
|
export function isOwnedSettingId(id) {
|
|
152
184
|
return Object.hasOwn(OWNED_SETTING_DECLARATIONS, id);
|
|
@@ -188,9 +188,13 @@ export class OwnedSettingsManager {
|
|
|
188
188
|
return () => this.#listeners.delete(listener);
|
|
189
189
|
}
|
|
190
190
|
async changeStructured(backend, id, value) {
|
|
191
|
-
if (backend
|
|
192
|
-
return
|
|
193
|
-
|
|
191
|
+
if (backend === "agent")
|
|
192
|
+
return await this.#changeAgentValue(id, value);
|
|
193
|
+
// Invariant: a flagged A1 setting stores its enabled parts as one value, in declaration order.
|
|
194
|
+
const flags = findOwnedUiSettingsEntry(this.sections(), id, "a1")?.flags ?? [];
|
|
195
|
+
if (flags.length === 0)
|
|
196
|
+
return failed(`${id} holds no structured value`);
|
|
197
|
+
return await this.change("a1", id, flags.filter(flag => value[flag.key] === true).map(flag => flag.key).join(","));
|
|
194
198
|
}
|
|
195
199
|
async change(backend, id, value) {
|
|
196
200
|
const entry = findOwnedUiSettingsEntry(this.sections(), id, backend);
|
|
@@ -137,6 +137,24 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
137
137
|
return { ...values };
|
|
138
138
|
},
|
|
139
139
|
}),
|
|
140
|
+
Object.freeze({
|
|
141
|
+
to: 18,
|
|
142
|
+
description: "Introduce the top resident tab-bar position default.",
|
|
143
|
+
migrate(values) {
|
|
144
|
+
return { ...values };
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
Object.freeze({
|
|
148
|
+
to: 19,
|
|
149
|
+
description: "Expand the update check into auto, notify, and no modes.",
|
|
150
|
+
migrate(values) {
|
|
151
|
+
if (values.updateCheck === true)
|
|
152
|
+
return { ...values, updateCheck: "notify" };
|
|
153
|
+
if (values.updateCheck === false)
|
|
154
|
+
return { ...values, updateCheck: "no" };
|
|
155
|
+
return { ...values };
|
|
156
|
+
},
|
|
157
|
+
}),
|
|
140
158
|
]);
|
|
141
159
|
/** Shape-selected compatibility for the metadata-free product object. */
|
|
142
160
|
export function normalizeOwnedUiSettingsValues(source) {
|
|
@@ -146,6 +164,10 @@ export function normalizeOwnedUiSettingsValues(source) {
|
|
|
146
164
|
values.scrollbarAppearance = "auto";
|
|
147
165
|
if (values.accentColor === "default")
|
|
148
166
|
values.accentColor = "purple";
|
|
167
|
+
if (values.updateCheck === true)
|
|
168
|
+
values.updateCheck = "notify";
|
|
169
|
+
else if (values.updateCheck === false)
|
|
170
|
+
values.updateCheck = "no";
|
|
149
171
|
if (values.quitEffect === "off") {
|
|
150
172
|
if (Object.hasOwn(values, "quitAnimation") && values.quitAnimation !== false) {
|
|
151
173
|
return { values: null, failure: "legacy quitEffect conflicts with quitAnimation" };
|
|
@@ -49,4 +49,8 @@ export interface BuildOwnedUiSettingsSectionsInput {
|
|
|
49
49
|
}
|
|
50
50
|
export declare const AGENT_SECTION_ID = "agent";
|
|
51
51
|
export declare function buildOwnedUiSettingsSections(input: BuildOwnedUiSettingsSectionsInput): readonly OwnedUiSettingsSection[];
|
|
52
|
+
/** The enabled parts a flagged A1 setting's comma-separated value names, as the record its dialog edits. */
|
|
53
|
+
export declare function flagRecord(flags: readonly {
|
|
54
|
+
readonly key: string;
|
|
55
|
+
}[], value: unknown): Record<string, boolean>;
|
|
52
56
|
export declare function findOwnedUiSettingsEntry(sections: readonly OwnedUiSettingsSection[], id: string, backend: OwnedUiSettingsBackend): OwnedUiSettingsEntry | null;
|
|
@@ -5,20 +5,21 @@ export function buildOwnedUiSettingsSections(input) {
|
|
|
5
5
|
for (const setting of input.resolution.settings) {
|
|
6
6
|
const section = setting.declaration.section ?? { id: "a1", title: "A1" };
|
|
7
7
|
const group = grouped.get(section.id) ?? { title: section.title, entries: [] };
|
|
8
|
+
const flagged = setting.declaration.flags;
|
|
8
9
|
group.entries.push({
|
|
9
10
|
id: setting.declaration.id,
|
|
10
11
|
label: setting.declaration.label ?? null,
|
|
11
12
|
backend: "a1",
|
|
12
13
|
description: setting.declaration.description,
|
|
13
14
|
value: setting.value,
|
|
14
|
-
rawValue: setting.value,
|
|
15
|
+
rawValue: flagged === undefined ? setting.value : flagRecord(flagged, setting.value),
|
|
15
16
|
storedValue: setting.value,
|
|
16
17
|
effectiveValue: setting.value,
|
|
17
18
|
editable: true,
|
|
18
|
-
structured:
|
|
19
|
+
structured: flagged !== undefined,
|
|
19
20
|
minimum: null,
|
|
20
21
|
maximum: null,
|
|
21
|
-
flags: [],
|
|
22
|
+
flags: flagged?.map(flag => ({ ...flag, fallback: false })) ?? [],
|
|
22
23
|
choices: setting.declaration.allowedValues,
|
|
23
24
|
origin: setting.source,
|
|
24
25
|
application: setting.declaration.application === "restart" ? "next-start" : "live",
|
|
@@ -44,6 +45,11 @@ export function buildOwnedUiSettingsSections(input) {
|
|
|
44
45
|
};
|
|
45
46
|
return Object.freeze([...ownedSections, combinedAgent]);
|
|
46
47
|
}
|
|
48
|
+
/** The enabled parts a flagged A1 setting's comma-separated value names, as the record its dialog edits. */
|
|
49
|
+
export function flagRecord(flags, value) {
|
|
50
|
+
const enabled = new Set(typeof value === "string" ? value.split(",") : []);
|
|
51
|
+
return Object.fromEntries(flags.map(flag => [flag.key, enabled.has(flag.key)]));
|
|
52
|
+
}
|
|
47
53
|
export function findOwnedUiSettingsEntry(sections, id, backend) {
|
|
48
54
|
for (const section of sections) {
|
|
49
55
|
const entry = section.entries.find(candidate => candidate.id === id && candidate.backend === backend);
|
|
@@ -167,6 +167,16 @@ Every attach, server, and holder role is `process.execPath` running the release'
|
|
|
167
167
|
Because ConPTY belongs to its creating process, each tab has its own creator. A server crash therefore does not destroy a tab's ConPTY; holder failure affects only that tab. Actual child/tree exit and writer-lease release still require verification, not inference from the pseudoconsole closing. The 2026-10-17 Windows x64 migration measurement put an idle Node-hosted holder at approximately 30–40 MiB of resident memory, versus a few MiB for the former native-executable holder; allocator and shared-page accounting make this a range rather than a per-tab budget. The separate holder process is retained for certified crash isolation, and latency budgets were not raised to absorb Node startup.
|
|
168
168
|
- **Why the server relays surfaces instead of clients connecting to holders directly.** It gives one authenticated endpoint per client, one ordering domain for focus and topology, and simpler multi-client arbitration. Measure the extra local pipe hop in the native hot-path evidence rather than assuming its latency. The server forwards encoded holder surfaces and does not re-render them.
|
|
169
169
|
|
|
170
|
+
### Windows completion-driven transport and wake diagnosis
|
|
171
|
+
|
|
172
|
+
Windows server and client endpoints use overlapped named-pipe I/O. Each operation owns a stable `OVERLAPPED` and non-inherited completion event; read and write workers share handle custody but never serialize a pending read against a write. A worker waits for completion, connection shutdown, or an absolute admission deadline, not `PeekNamedPipe` plus a short sleep. The four-second first-envelope deadline covers fragmented headers and bodies. Cancellation targets one operation and drains its kernel completion before releasing its buffer/event; split-worker guards wake the other direction when the connection owner exits. Owner ACLs, first-instance protection, peer checks, frame limits, control ordering, and latest-surface bounds are unchanged. Unix socket behavior is unchanged.
|
|
173
|
+
|
|
174
|
+
Transport correctness does not rely on `timeBeginPeriod`. The existing per-role timer request remains for non-I/O timers, including ConPTY render-pass settling and animation/maintenance deadlines; removing it requires separate scheduling evidence. A detected attach gap still requests one complete cached repaint, never a process restart or prompt replay.
|
|
175
|
+
|
|
176
|
+
The existing opt-in progress route (`A1_RESIDENT_PROGRESS_OBSERVATION_DIR` plus `A1_RESIDENT_PROGRESS_RUN_ID`) adds `pipe-frame-read`, `pipe-write-complete`, `outer-write-complete`, `wake-gap`, and `wake-repaint-requested` stages. Connection-level records may omit tab identity. `durationUs` is local monotonic completion/assembly time; `idleWaitUs` separately describes waiting for the next frame and is not delivery latency. Native records include `monotonicUs`, `clockEpoch`, and optional `clockDiscontinuity`. Consumers exclude negative, epoch-crossing, and clock-discontinuity-crossing derived samples, while retaining valid local completion durations. The queue remains bounded and asynchronous, with loss accounting and no terminal content or credentials. Outer-write completion is not proof of physical pixels.
|
|
177
|
+
|
|
178
|
+
For recurring post-wake stalls, compare an existing resident client, a new client on the same resident host, and direct `a1 solo` in the same outer terminal. Use matched holder/attach revisions and local I/O durations to distinguish child progress, transport delay, and outer writes. If solo and other terminal applications also stall, do not assign the failure to resident pipes without further evidence. Simulated-gap fixtures do not certify physical sleep/wake recovery; do not automate workstation suspend. Updates retain old resident cohorts, so new endpoint code takes effect only as those roles reach their ordinary lifecycle or the maintainer explicitly chooses to stop them. Installing the update never recycles running tabs.
|
|
179
|
+
|
|
170
180
|
### 3. Host scope, endpoints, writer authority, and credentials
|
|
171
181
|
|
|
172
182
|
- There is **one server per OS user × canonical A1 profile root**, independent of the release cohort. Tabs must outlive release changes, and the supervisor is forbidden from retaining runtime processes.
|
|
@@ -250,7 +260,7 @@ Direct `a1 solo` is the behavioural oracle for every viewed resident child. Only
|
|
|
250
260
|
- **Modes and cursor.** Each surface carries the child's cursor cell and modes. Attach mirrors a cursor shape the child chose and resets it on detach or failure; a default block cursor is left to the outer terminal, as direct A1 leaves it.
|
|
251
261
|
- **Shortcut projection.** The owned child projects the effective A1 keybindings plus `keybindings.json` into one assignment per tab action: a key an A1 action holds never becomes a tab shortcut, and the earlier declaration keeps a duplicated key. It publishes the projection as `bridge.shortcuts`; the server relays it as `tabs.shortcuts`, and attach dispatches and labels exactly that projection. `/hotkeys` renders the same projection. A shared case file is replayed by both resolvers.
|
|
252
262
|
- **Committed presentation.** A requested view commits only with a topology that lists it and a complete target surface. Retained rows, palette/default colors, modes, and cursor are captured atomically; equal captures do not invent a revision. Surfaces of the committed view apply in revision order, and revisions rise across holder incarnations. A retained re-offer is marked stale from the request start and parks for at most 120 ms while the fresh frame arrives. One compositor writes strip, body, overlay, modes, and the viewport-clipped final cursor inside a single synchronized update. It erases suffix cells when a complete replacement row becomes shorter and restores the newest underlying surface when an overlay leaves. Clock, hover, and title-animation damage repaints only the strip and restores the child's cursor.
|
|
253
|
-
- **Evidence.** One interaction matrix drives three layers: an owned-shell oracle that records content-free observations, role fixtures that drive real server, holder, and attach processes with a certified parity child, and exact-artifact harnesses. `verify-interaction-parity.mjs` compares the bytes the same child observes directly and through the built host. `verify-resident-parallel-progress.mjs` configures the real engine against a bounded loopback streaming provider and crosses all six first-response orders with all three initial views while every turn remains unfinished behind a completion barrier. At matching 100×29 child geometry, the same real-A1 prompt also runs through isolated direct-solo and resident profiles; the native Ghostty oracle compares a content-free digest of the intermediate fixture cells and their resolved styles together with normalized cursor and mode state. It advances only at parser-reported synchronized-output boundaries, rejects unbalanced transactions or disappearing intermediate cells, and excludes only the resident strip row rather than normalizing terminal semantics. Opt-in per-process JSONL observations identify engine delivery, completed child writes, PTY ingestion, distinct holder commits, attach receipt, and completed outer paint by run/tab/incarnation/process-instance/sequence without recording terminal or conversation content; queue loss, clock faults, duplicates, stale order, and missing stages fail the report. Named pressure variants keep all turns unfinished while exercising hidden progress, repeated switching, navigation input, rename overlays, tab-local resize, a bounded 35 ms attach-receipt delay, and a 900 ms paused outer sink; every tab must retain partial animation/progress before settlement and then present its final output. A detached/reconnect variant releases all three unfinished turns after terminating the authenticated attach and exact self-reported server identity; child writes, PTY ingestion, and holder commits must continue before a replacement server and attach present every retained final output. Separate isolated exact-artifact control markers stop one target tab at engine delivery, physical child rendering, holder commit, or outer paint; each control requires its upstream stages and the other tabs to advance, then removes the stop and requires final output from all three tabs. A fifth control fragments a real owned-shell synchronized write into at most 64 physical writes without changing its bytes, records the DEC private mode 2026 transaction classification, and requires ordinary complete holder commits without deadline recovery before final output. This characterizes the built PTY/ConPTY coherence path; a passing fragmented run does not by itself assign the historical stall to that path without a failing live reproduction. The marker path is absent in ordinary sessions and is scrubbed before extensions or tools inherit the child environment, while observation callbacks remain passive and cannot select a control. CI runs the vendored Ghostty VT tests and both exact-artifact harnesses on Windows x64, Linux x64, and macOS arm64. Each platform's verdict stands alone.
|
|
263
|
+
- **Evidence.** One interaction matrix drives three layers: an owned-shell oracle that records content-free observations, role fixtures that drive real server, holder, and attach processes with a certified parity child, and exact-artifact harnesses. `verify-interaction-parity.mjs` compares the bytes the same child observes directly and through the built host. `verify-resident-parallel-progress.mjs` configures the real engine against a bounded loopback streaming provider and crosses all six first-response orders with all three initial views while every turn remains unfinished behind a completion barrier. At matching 100×29 child geometry, the same real-A1 prompt also runs through isolated direct-solo and resident profiles; the native Ghostty oracle compares a content-free digest of the intermediate fixture cells and their resolved styles together with normalized cursor and mode state. It advances only at parser-reported synchronized-output boundaries, rejects unbalanced transactions or disappearing intermediate cells, and excludes only the resident strip row rather than normalizing terminal semantics. Opt-in per-process JSONL observations identify engine delivery, completed child writes, PTY ingestion, distinct holder commits, attach receipt, and completed outer paint by run/tab/incarnation/process-instance/sequence without recording terminal or conversation content; queue loss, clock faults, duplicates, stale order, and missing stages fail the report. Named pressure variants keep all turns unfinished while exercising hidden progress, repeated switching, navigation input, rename overlays, tab-local resize, a bounded 35 ms attach-receipt delay, and a 900 ms paused outer sink; every tab must retain partial animation/progress before settlement and then present its final output. A detached/reconnect variant releases all three unfinished turns after terminating the authenticated attach and exact self-reported server identity; child writes, PTY ingestion, and holder commits must continue before a replacement server and attach present every retained final output. Separate isolated exact-artifact control markers stop one target tab at engine delivery, physical child rendering, holder commit, or outer paint; each control requires its upstream stages and the other tabs to advance, then removes the stop and requires final output from all three tabs. The outer-paint control waits for a payload-free `fixture-stop-ack` from the sole writer before starting its scenario; that acknowledgement follows all earlier writes, and same-writer sequence ordering excludes only pre-acknowledgement paints from the zero-progress check. Any later paint still fails. A fifth control fragments a real owned-shell synchronized write into at most 64 physical writes without changing its bytes, records the DEC private mode 2026 transaction classification, and requires ordinary complete holder commits without deadline recovery before final output. This characterizes the built PTY/ConPTY coherence path; a passing fragmented run does not by itself assign the historical stall to that path without a failing live reproduction. The marker path is absent in ordinary sessions and is scrubbed before extensions or tools inherit the child environment, while observation callbacks remain passive and cannot select a control. CI runs the vendored Ghostty VT tests and both exact-artifact harnesses on Windows x64, Linux x64, and macOS arm64. Each platform's verdict stands alone.
|
|
254
264
|
|
|
255
265
|
### 7. Status and attention come from the tab bridge
|
|
256
266
|
|
|
@@ -303,7 +313,7 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
|
|
|
303
313
|
| Tab child exits or crashes | holder sees the child exit | If the exit was not requested: `crashed`. The holder respawns `ui.js --tab --session <file>` with backoff 1 s, 5 s, 30 s. After 3 restarts in 10 min: `failed`, with a banner `✗ <reason> — [r] retry · [f] start fresh · [alt+w] close`. An interrupted prompt (reported via the bridge, or detected as a trailing user entry) is offered back into the editor and never resent. Child stderr, if separately available, is a private content-bearing recovery artifact, never an ordinary diagnostic log. |
|
|
304
314
|
| Holder crash | pipe EOF plus verified process identity/exit | Pseudoterminal loss is not proof that every descendant exited. Verify the old writer/tree is gone and acquire its writer lease before resuming in a new holder; otherwise keep the tab blocked without duplicate writers. |
|
|
305
315
|
| Holder hang | holder control heartbeat every 5 s, 3 missed over a healthy supervision path, then one heartbeat of grace for the holder-local watchdog record | Terminate only the verified holder/tree within bounded deadlines, then follow the lease-gated recovery path. |
|
|
306
|
-
| A1 child hang | missing child event-loop heartbeat over a healthy bridge path |
|
|
316
|
+
| A1 child hang | missing child event-loop heartbeat over a healthy bridge path | Record `unresponsive` at 30 s without changing the chip or painting a notice; restart at `tabsUnresponsiveRestartSeconds` (default 120), preserving journal/screen. A server/bridge transport outage alone is not proof of a child hang. |
|
|
307
317
|
| Server crash | clients and holders see the pipe EOF | Holders keep running. A client or holder starts a replacement, which acquires the released writer lease, increments the epoch, derives expected credentials, and re-admits identity-verified holders. A live but unresponsive recorded owner is terminated only after native identity verification; an unverifiable owner blocks replacement. After 3 starts in 60 s, clients show `✗ tab host stopped — [r] restart · [q] quit`. |
|
|
308
318
|
| Attach client crash or terminal close | the server sees the pipe EOF | Nothing happens to tabs. Pending requests stay `needs-input`. |
|
|
309
319
|
| Reboot or logout | the platform boot identity differs, or no verified holders exist for a new OS session | Tabs are not restored in this version. The server moves the previous tab set to registry history, and bare `a1` starts with one new tab. Every previous session stays resumable through the normal session picker or `a1 --session`. A pending journaled prompt is offered when its session is next opened in a tab, for up to seven days. Restoring tabs after reboot is a follow-up change. |
|
|
@@ -325,7 +335,7 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
|
|
|
325
335
|
|
|
326
336
|
- **Strip.** Row 0, drawn by the attach client, reproduces the approved v2 default appearance:
|
|
327
337
|
- normal chips use RGB background `38,38,38` with foreground `170,170,170`, hover uses `48,48,48`, and the viewed chip uses `55,55,55` with white text, plus v2's palette fallback;
|
|
328
|
-
- the empty state
|
|
338
|
+
- the empty state keeps the hoverable `+` control and the clock beside the landing, with no cursor;
|
|
329
339
|
- chips are at most 20 columns, clipped with `…` by grapheme width, with no separators or close glyph;
|
|
330
340
|
- a `…` overflow menu holds tabs that do not fit, and `+` is always reserved;
|
|
331
341
|
- these presentation choices are fixed, not settings, while each working spinner uses that tab's reported A1 accent.
|
|
@@ -337,6 +347,8 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
|
|
|
337
347
|
| `Alt+W` | close |
|
|
338
348
|
| `F2` | rename |
|
|
339
349
|
| `Alt+,` | open the global Settings tab, or close it while it is viewed |
|
|
350
|
+
| `Alt+/` | open the command palette, or close it while it is open |
|
|
351
|
+
| unbound (`tabs.changelog`) | open the Changelog tab |
|
|
340
352
|
| `Alt+1`…`Alt+9`, `Alt+0` | jump to a tab |
|
|
341
353
|
| unbound (`tabs.next` / `tabs.previous`) | next / previous, wrapping |
|
|
342
354
|
| unbound (`tabs.moveRight` / `tabs.moveLeft`) | move the tab right / left |
|
|
@@ -347,24 +359,32 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
|
|
|
347
359
|
- **`Ctrl+C` twice leaves, in every tab (user decision).** The client forwards the first `Ctrl+C` to the tab unchanged, so a single press still clears, copies, or interrupts. It consumes the second press within the existing clear/exit interval as the detach request and does not forward it. The tab therefore never receives a second `Ctrl+C` from the chord, which removes v2's bug where `Ctrl+C+C` killed the agent. The trade-off: a future CLI tab cannot receive two quick `Ctrl+C`s. Pressing them more slowly than the interval still reaches the program.
|
|
348
360
|
- **Mouse on row 0:**
|
|
349
361
|
- click activates a tab;
|
|
350
|
-
- right-click opens `Rename`/`Close` (`Close` only for Settings);
|
|
362
|
+
- right-click opens `Rename`/`Close` (`Close` only for Settings and Changelog);
|
|
363
|
+
- right-click on `+` opens a one-row `Configure` menu that opens the `Plus Menu` dialog over the dimmed active tab, without switching tabs;
|
|
351
364
|
- dragging reorders, with a `│` drop marker, after 250 ms or once the pointer moves;
|
|
352
|
-
- `+` opens a persistent dark v2 menu
|
|
365
|
+
- `+` follows the plus menu: with only Agent enabled it adds an Agent at once; with other kinds it opens a persistent dark v2 menu of the enabled kinds, `+ Agent` first and a separator before the `Settings` and `Changelog` toggles, each `✓` while its tab is open; with none the strip hides `+`. Hover and click remain active through the opening release.
|
|
353
366
|
- **Settings.** The strip owns one profile-global `Settings` tab. The grouped launcher creates or activates it; `Alt+,` does the same from another tab and closes it while it is viewed. Esc does not close the Settings tab; closing the tab removes it. Resident Agent autocomplete omits `/settings`, and manually entering it does not open a per-Agent screen. `a1 solo` and `a1 pi` retain their existing Settings routes.
|
|
354
|
-
- **Commands inside A1 tabs:**
|
|
367
|
+
- **Commands inside A1 tabs:** a tab's name and its session's name stay in sync. a strip rename or an automatic name sets the session name through the bridge, and a session name set otherwise publishes the session view at once, so the tab takes it as a user name; and a resumed session's name becomes the tab name. An automatic name never replaces a user name. Resident Agents also omit and refuse `/changelog`, which opens as its own tab, and `/name`, since renaming the tab (`F2`) renames the session; direct A1 keeps both. Milestone 5 adds no tab-management slash commands: `/new-tab`, `/close`, `/tabs`, `/quit-all`, and `/restart-tab` are not exposed.
|
|
355
368
|
- **Naming:**
|
|
356
369
|
- Default names are `agent` or `agent N`.
|
|
357
370
|
- When `tabsAutoName` is on (the default, by user decision), the tab's model proposes a hyphenated lowercase name of at most 16 characters after the first settled turn. It uses a small budget and one retry, with a fallback derived from the first prompt, and it runs inside the child.
|
|
358
371
|
- A user rename sets `nameSource=user`, and auto-naming never overrides it.
|
|
359
372
|
- An automatic rename uses v2's title transition: 600 ms moving rainbow, typing at 45 ms per character, then a 300 ms settle. Manual renames do not animate.
|
|
360
373
|
- Rename is inline in the chip: `Enter` commits, `Esc` or a click elsewhere cancels, and `Ctrl+Left` / `Ctrl+Right` move by word. The name is trimmed, must not be empty, is at most 64 characters, and has control characters stripped.
|
|
361
|
-
- **
|
|
374
|
+
- **Plus menu.** The A1 setting `tabBarPlusMenu`, shown as `Plus menu` in the `Tab Bar` Settings section, holds the kinds `+` offers, Agent and every utility kind, as comma-joined keys in that order and defaults to `agent`. The `Plus Menu` dialog lists every kind with `●` when enabled and `○` when not under `Choose what the + button in the tab bar can add.`, with `Space` to toggle, Up and Down to move, typing to filter, and `Esc` to close. `+`'s `Configure` opens it in the attach client as a modal over the dimmed active tab or landing, built on the command palette's list and renderer, with a reversed white search caret like owned inputs. The Settings `configure` entry opens the same modal: the Settings child sends a `configure-plus` bridge request as the tab's controlling client, which the server forwards to that client as `plus.configure`; only without a bridge does Settings open its own equivalent dialog. Each toggle is written at once: the attach client takes the `settings.json.lock` directory lock the TypeScript writer takes through `proper-lockfile`, rereads the document, replaces only `a1.tabBarPlusMenu` (removed at the default), and renames a temporary file over it. A refused write restores the stored menu. The attach client reads the setting live, and with no kind enabled the strip hides `+`. Because a modal's overlay saves and restores the cursor while faint is active, the strip starts every frame from reset attributes, so no chip stays faint after a modal closes.
|
|
375
|
+
- **Command palette.** `Alt+/` (`tabs.commands`) opens a centered box over the whole frame in the owned list style: an accent `Commands` title, a `> ` search row, `Agent`, `Settings`, and `Changelog` with their effective shortcuts right-aligned, and a key hint. Typing or pasting filters it, Up and Down move, `Enter` runs the selected entry through its shortcut action, and `Esc` or `Alt+/` closes it; another tab shortcut closes it and runs. While it is open no key, paste, or pointer event reaches the strip or a child, the cursor is hidden, and the composer redraws the strip and body with faint re-asserted after every SGR beneath the bright overlay.
|
|
376
|
+
- **Changelog.** The changelog opens as a singleton `changelog-` utility tab titled `Changelog`, from the palette or the unbound `tabs.changelog` action. The tab registry accepts the `agent`, `settings`, and `changelog` kinds. Like Settings it is never renamed, offers only `Close`, is placed last, does not count as an Agent, and is prewarmed and hidden: the attach client starts it with `tab.prewarm-changelog` once Settings is ready, and again after a Changelog tab closes, so opening it shows the running tab at once. Like a hidden Settings tab, a hidden Changelog never takes the view when the viewed tab closes, so closing the last Agent shows the landing. Its screen is the tab: it shows no `Esc close` hint, and neither `Esc` nor one `Ctrl+C` closes it.
|
|
377
|
+
- **Utility tab rules.** Settings and Changelog are kinds in one table, `UtilityKind` in the attach core and `UTILITY_KINDS` on the server, and every rule follows the kind rather than a per-kind path: a kind's toggle shows a tab already in the strip in its slot, reveals a hidden or absent one through `tab.open-<kind>`, which the server answers by starting it or placing the running one last, and hides the viewed one while its process keeps running; the client prewarms each kind with `tab.prewarm-<kind>` once the kind before it runs; and a hidden one never takes the view when another tab closes. In the child, the route matching a resident tab's id is that tab's pinned screen. Adding a kind is a table entry, its prewarm state, and its route.
|
|
378
|
+
- **Reveal.** Opening a tab draws it at once: a created Agent and a revealed Settings or Changelog tab both keep the current content, another tab or the landing, until their first drawn frame commits, a retained blank surface included, and a tab that never draws shows after the bounded first-draw wait. Only a resize fades (`roles/reveal.rs`): once the curtain lifts, the whole frame draws faint for one fade interval with no cursor, then at full intensity. A starting chip shows no spinner. One tab opens at a time: new-Agent input while a tab the client asked for is starting or not yet shown starts nothing.
|
|
379
|
+
- **Rename selection.** The inline rename draws a selection on the prompt editor's selection background (RGB `38,79,120`, palette `24`) and keeps the chip's text color, restoring the chip's background after it; only the caret is a reversed cell.
|
|
380
|
+
- **Close.** `Alt+W` and the right-click menu close a tab directly, including while it is working. Closing removes the chip without a confirmation, stopping/read-only frame, or close glyph, then gracefully stops the child under the existing bounded termination policy. Its session stays resumable. Closing the last tab leaves `+` and the clock beside the landing.
|
|
381
|
+
- **Landing.** While the attach client has no tab to show, the attach role renders the a1 landing as the frame's body. The launch cwd sits where an Agent tab's footer shows it, flush left on the second-to-last body row with the same `~` formatting and `dim` color, so the folder does not move when a tab opens or closes; `v<version>`, read once from the release root's `package.json`, sits below it and the three-row half-block `a1` wordmark in the profile's accent above it (omitted in Apple Terminal). The accent colors are a native table pinned by `landing_accents.json`, which a TypeScript test checks against `derivePiAccentProjection`. The body is painted with the canvas `backgroundStyle` gives Agent tabs. The landing holds no input: tab shortcuts, the launcher, strip mouse, and double-`Ctrl+C` behave as with tabs, and no cursor shows. A hidden Settings tab is prewarmed and kept running beside the landing, as beside Agents, so Settings opens without a start; it closes when the client detaches, and an attach treats a registry holding only Settings as empty, so it starts and views its first Agent. Adding a tab from the landing keeps it until the tab's first drawn frame replaces it at once; the landing has no animation of its own.
|
|
362
382
|
- **Detach.**
|
|
363
383
|
- `Ctrl+C` twice detaches from any tab and is handled by the attach client, so it works even when the bridge is down. Inside an A1 tab, `/quit` and empty-editor `Ctrl+D` send a detach request carrying the immutable command-origin controller identity and revision; stale, ambiguous or unavailable attribution keeps the tab running and directs the user to `Ctrl+C` twice.
|
|
364
384
|
- The client restores the terminal. When agents are still running, it prints `1 agent still running run a1 to return.` or `N agents still running run a1 to return.`; otherwise it prints the resume hint.
|
|
365
385
|
- **Launch and reattach:**
|
|
366
386
|
- Bare `a1` ensures the server and attaches.
|
|
367
|
-
- With zero tabs, it creates one A1 tab in the launch cwd.
|
|
387
|
+
- With zero tabs, it creates one A1 tab in the launch cwd; a registry holding only the hidden Settings tab counts as zero.
|
|
368
388
|
- Otherwise it views the last active tab from its retained surface without byte-history replay. Every holder keeps draining its PTY and updating its retained terminal independently while hidden; selecting a working tab requests its newest complete partial-response surface immediately and then continues live delivery. Measure retained first paint and first input separately from bootstrap; status for the remaining tabs arrives without subscribing to their surfaces.
|
|
369
389
|
- `a1 --session X` opens X in a new tab, or focuses the tab holding it.
|
|
370
390
|
- A new tab's cwd is the client's launch cwd.
|
|
@@ -377,7 +397,7 @@ Tab status is an A1-owned state machine fed by `bridge.status`:
|
|
|
377
397
|
Internal resources remain bounded without imposing a user-visible tab-count quota.
|
|
378
398
|
|
|
379
399
|
- The only user-facing Tabs setting during resident launches is `tabsAutoName` (default on). Legacy `tabsMax` data is preserved but ignored. Tab enablement is not a setting. Direct `a1 solo` and `a1 pi` launches omit the Tabs section entirely.
|
|
380
|
-
- Unresponsive restart
|
|
400
|
+
- Unresponsive restart timing is internal fixed behavior at 120 seconds; there is no progress-stall notice; concurrent starts use internal default 2/hard cap 8 and prewarm uses internal count/hard cap 1.
|
|
381
401
|
- Every open tab keeps its holder and A1 process until explicit close. There is no idle-suspension setting, timer, lifecycle, or glyph.
|
|
382
402
|
- Scrollback is capped at 10 MiB per holder, and the replay after a server restart is the retained model itself.
|
|
383
403
|
- The server exits after 10 minutes with no attached client and no live user tab; the prewarmed standby does not keep it alive and is stopped first.
|
|
@@ -479,8 +499,8 @@ The v2 daemon was unreliable for structural reasons, and a forensic pass over it
|
|
|
479
499
|
**Agents that stop making progress.** Liveness and progress are separate signals:
|
|
480
500
|
|
|
481
501
|
- Holder heartbeat: a Rust main-loop tick every 5 s seen by the server; three missed ticks over an otherwise healthy supervision path plus one heartbeat of grace trigger verified holder recovery. The grace keeps external termination behind the holder's 15-second process-local watchdog so its own crash evidence wins despite scheduler jitter.
|
|
482
|
-
- A1 heartbeat: the child's Node event loop, seen through the bridge. After 30 s
|
|
483
|
-
- Agent progress: model or tool events
|
|
502
|
+
- A1 heartbeat: the child's Node event loop, seen through the bridge. After 30 s the server records `unresponsive` in the registry and counters, without changing the presented status, chip glyph, or colour and without a notice row or restart key; after `tabsUnresponsiveRestartSeconds` (default 120) the tab restarts from its session with its journal and last screen preserved. A missing server or bridge transport degrades status, not proof of a child event-loop stall; reacquire liveness evidence before destructive recovery.
|
|
503
|
+
- Agent progress: not supervised. A working tab without model or tool events keeps its working status indefinitely, gains no transcript notice, and is never auto-killed, because a legitimate tool can run for a long time.
|
|
484
504
|
|
|
485
505
|
**Data-loss envelope.** Distinguish process termination, power loss, and loss of both holder and child:
|
|
486
506
|
|
|
@@ -589,6 +609,14 @@ The user made these decisions on 2026-09-24:
|
|
|
589
609
|
- `Ctrl+C` twice leaves `a1` from any tab while tabs keep running. There is no separate detach key.
|
|
590
610
|
- Reliability is a first-class, release-gating requirement (Decision 16).
|
|
591
611
|
|
|
612
|
+
On 2026-10-24 the user refined the tab bar:
|
|
613
|
+
|
|
614
|
+
- `+` adds an Agent by default; what it offers, Agent, Settings, and Changelog, is configurable from a right-click `Configure` modal over the active tab and from `Tab Bar` › `Plus menu` in Settings, where the `Tabs` section is renamed `Tab Bar`. With nothing enabled, `+` is hidden.
|
|
615
|
+
- `Alt+/` opens a keyboard-only command palette over a dimmed frame: `Agent`, `Settings`, and `Changelog`.
|
|
616
|
+
- The changelog opens at once as its own prewarmed singleton tab that `Esc` does not close, and leaves resident Agents' commands.
|
|
617
|
+
- Opening a tab, a new Agent or a revealed Settings or Changelog tab, draws it at once when its first frame is drawn, with no fade and no starting spinner; the current content, landing included, stays until then. Only a resize fades. The landing's wipe animation is removed.
|
|
618
|
+
- The tab rename selection matches the editor's selection style, and a tab's name and its session's name stay in sync.
|
|
619
|
+
|
|
592
620
|
On 2026-10-16 the `ci-critical-path` change replaced the Intel runner for the release builds:
|
|
593
621
|
|
|
594
622
|
- The macOS x64 process guardian and terminal host are cross-compiled on the `macos-15` arm64 runner (`npm run build:terminal-host -- --target x86_64-apple-darwin`, same for the guardian) and uploaded beside the arm64 artifacts; the Intel runner took 6 to 11 minutes for builds the arm64 runner finishes in about 4, and it gated packing in every publication. The x64 executable's `--version` identity is read by executing it through Rosetta 2 when the runner can; otherwise the manifest records a Mach-O architecture check as `provenance.identityCheck: "mach-o-header"` instead of a report nobody observed. Intel macOS stays certified separately and its physical acceptance record is unchanged; no publication lane ran the x64 bytes beyond that identity check before either, because the published-pair smoke matrix has no Intel lane.
|
|
@@ -16,7 +16,7 @@ The source unit is from MIT-licensed repository <https://github.com/earendil-wor
|
|
|
16
16
|
|
|
17
17
|
One conversion is active per mounted tool. The waiting set is the newest current source set, not an unbounded queue of superseded snapshots. Hidden images and non-Kitty protocols do not start conversions. Work removed before codec entry is skipped. A public codec call already in progress cannot be cancelled; its eventual result is discarded if the source or mount is obsolete. This limitation is explicit, not claimed as cancellable worker execution.
|
|
18
18
|
|
|
19
|
-
Pending Kitty conversion has a visible `[Image converting: ...]` marker. Failure, rejection, or oversized converted bytes produces `[Image unavailable: ...]`, without exposing codec diagnostics or changing the operation outcome. Converted pixels never replace the original payload passed to extension renderers. PNG and iTerm presentation use the actual public `Image` component.
|
|
19
|
+
Pending Kitty conversion has a visible `[Image converting: ...]` marker. Failure, rejection, or oversized converted bytes produces `[Image unavailable: ...]`, without exposing codec diagnostics or changing the operation outcome. Converted pixels never replace the original payload passed to extension renderers. PNG and iTerm presentation use the actual public `Image` component. Explicitly hidden images retain pinned textual indicators. When image display is enabled but the terminal advertises no image protocol, bare A1 omits the non-actionable attachment fallback from user and tool transcript content; attachment bytes, metadata, model delivery, the explicit `a1 pi` route, and extension-owned image components remain unchanged.
|
|
20
20
|
|
|
21
21
|
Rebuild/disposal releases image ownership and invalidates the previous mount. Reentrant renderer invalidation is suppressed during synchronous reconstruction; valid asynchronous callbacks still request the existing scheduler and update presentation revision, not semantic revision.
|
|
22
22
|
|
|
@@ -31,6 +31,7 @@ This correction does not introduce an ANSI parser, change link detection, relax
|
|
|
31
31
|
- `transcript-image-conversion.test.ts`: independent pinned stale-cache baseline, JPEG replacement, PNG replacement, hidden/non-Kitty parity, failed-conversion fallback and real scheduled Kitty publication at unchanged semantic revision.
|
|
32
32
|
- `tool-image-presentation.test.ts`: current-source sharing, one active conversion, superseded bursts, removed/disposed callbacks, hidden/iTerm work avoidance, rejection and size fallback without retry loops.
|
|
33
33
|
- `tool-shell-parity.test.ts`: independent actual pinned read/bash/edit/write/grep/find/ls and generic rendering at widths 40/80/192, partial/final/error states, expansion, binary text, and extension default/self shells with original inputs and stable renderer state.
|
|
34
|
+
- `shell-components.test.ts`: unsupported user and tool transcript images produce no fallback row or spacer, while explicit hiding and capable-terminal unavailable diagnostics remain visible.
|
|
34
35
|
- Existing attachment-ownership, payload-limit, content-retention and shell tests remain non-regression gates. The wrapped-URL counterexample remains explicitly unresolved under issue #353.
|
|
35
36
|
|
|
36
37
|
These are automated content and ownership checks, not Windows Terminal visual acceptance. The broader change stays open until scheduled-producer evidence, required CI, and exact-candidate user review satisfy its remaining tasks.
|