@timurproko/a1 0.2.6-dev.736 → 0.2.7-dev.746
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/bin/guardian.js +5 -0
- package/bin/tab-launch-context.d.ts +18 -0
- package/bin/tab-launch-context.js +36 -0
- package/bin/ui.js +14 -3
- package/dist/app/session-shell/helper-pool.d.ts +3 -5
- package/dist/app/session-shell/helper-pool.js +3 -5
- package/dist/app/session-shell/session-shell-root.d.ts +6 -1
- package/dist/app/session-shell/session-shell-root.js +3 -2
- package/dist/app/session-shell/session-shell.js +41 -24
- package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
- package/dist/app/session-shell/session-viewport-controller.js +3 -3
- package/dist/composition/owned-ui.d.ts +17 -0
- package/dist/composition/owned-ui.js +28 -1
- package/dist/features/owned-ui/resources/release-notes.json +4 -0
- package/dist/features/owned-ui/settings-app.js +3 -3
- package/dist/foundation/launch-context/index.d.ts +6 -0
- package/dist/foundation/launch-context/index.js +15 -0
- package/dist/foundation/launch-guardian/main.d.ts +5 -0
- package/dist/foundation/launch-guardian/main.js +2 -1
- package/dist/foundation/release/bootstrap.d.ts +4 -0
- package/dist/foundation/release/bootstrap.js +33 -6
- package/dist/foundation/release/cohort-state.js +5 -2
- package/dist/foundation/resident-tabs/bridge-client.d.ts +36 -0
- package/dist/foundation/resident-tabs/bridge-client.js +157 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +28 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +115 -0
- package/dist/foundation/resident-tabs/index.d.ts +5 -0
- package/dist/foundation/resident-tabs/index.js +3 -0
- package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
- package/dist/foundation/resident-tabs/protocol.js +195 -0
- package/dist/foundation/resident-tabs/routing.d.ts +10 -0
- package/dist/foundation/resident-tabs/routing.js +8 -0
- package/dist/foundation/resident-tabs/settings.d.ts +2 -0
- package/dist/foundation/resident-tabs/settings.js +17 -0
- package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
- package/dist/foundation/terminal-cleanup/index.js +1 -1
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +5 -0
- package/dist/ui/components/transcript-viewport.d.ts +6 -3
- package/dist/ui/components/transcript-viewport.js +13 -15
- package/dist/ui/components/value-menu.d.ts +6 -3
- package/dist/ui/components/value-menu.js +6 -5
- package/dist/ui/settings/declarations.d.ts +13 -1
- package/dist/ui/settings/declarations.js +10 -1
- package/dist/ui/settings/index.d.ts +1 -1
- package/dist/ui/settings/index.js +1 -1
- package/dist/ui/settings/migrations.js +7 -0
- package/dist/ui/settings/resolution.d.ts +2 -0
- package/dist/ui/settings/resolution.js +13 -1
- package/docs/architecture/boundaries.md +1 -1
- package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +70 -0
- package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +32 -0
- package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +115 -0
- package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +48 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +125 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +56 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +174 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +18 -0
- package/docs/architecture/internal-naming.md +1 -1
- package/docs/architecture/resident-tabs-requirements.md +813 -0
- package/docs/architecture/resident-tabs.md +502 -0
- package/docs/architecture/terminal-host-proof-gate.md +3 -1
- package/docs/architecture/terminal-host-spike-evidence.md +1 -1
- package/docs/openspec-archive-automation.md +12 -11
- package/docs/releases/0.2.6.md +44 -0
- package/docs/validation.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseOwnedUiSettingsDocument, resolveResidentTabsSetting } from "../../ui/settings/index.js";
|
|
4
|
+
const MAX_SETTINGS_BYTES = 256 * 1024;
|
|
5
|
+
/** Read only the routing flag before any UI or extension module is loaded. */
|
|
6
|
+
export async function readResidentTabsSetting(configDir, profileId = "a1") {
|
|
7
|
+
const file = resolve(configDir, "settings", `${profileId}.json`);
|
|
8
|
+
try {
|
|
9
|
+
const raw = await readFile(file, "utf8");
|
|
10
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_SETTINGS_BYTES)
|
|
11
|
+
return false;
|
|
12
|
+
return resolveResidentTabsSetting(parseOwnedUiSettingsDocument(raw));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
|
|
1
|
+
export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
|
|
2
2
|
export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
|
|
3
3
|
export type { FatalExitOptions } from "./fatal-exit.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
|
|
1
|
+
export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
|
|
2
2
|
export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const CLEAR_EXIT_INTERVAL_MS = 500;
|
|
1
2
|
export declare const EMERGENCY_TERMINAL_RESET: string;
|
|
2
3
|
export declare function restoreProcessTerminal(): void;
|
|
3
4
|
export declare function restoreAfterOwnedExit(ownsTerminal: boolean, code: number | null, signal: string | null, restore?: () => void): number;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeSync } from "node:fs";
|
|
2
|
+
export const CLEAR_EXIT_INTERVAL_MS = 500;
|
|
2
3
|
// Protocol: reset modes on both screens; setting keyboard flags to zero is idempotent,
|
|
3
4
|
// unlike repeatedly popping a keyboard-protocol stack owned by a parent application.
|
|
4
5
|
// Resetting parent-screen margins homes the cursor, so preserve its restored position.
|
|
@@ -44,7 +44,7 @@ export declare class OwnedEditorUxInterception {
|
|
|
44
44
|
pasteClipboard(): boolean;
|
|
45
45
|
}
|
|
46
46
|
export interface PromptSelectionUxOptions {
|
|
47
|
-
readonly copyText: (text: string) => void;
|
|
47
|
+
readonly copyText: (text: string, cut: boolean) => void;
|
|
48
48
|
readonly readClipboardContent: () => Promise<PiShellClipboardContent | null>;
|
|
49
49
|
readonly beginClipboardPaste?: () => PiShellPasteReservation;
|
|
50
50
|
readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
|
|
@@ -88,12 +88,12 @@ class PromptSelectionInterceptor {
|
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
if (this.keybindings.matches(data, "tui.input.copy") && this.hasSelection()) {
|
|
91
|
-
this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
|
|
91
|
+
this.options.copyText(this.options.expandCopiedText(this.#selectedText()), false);
|
|
92
92
|
this.#collapseCopiedSelection();
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
if (this.keybindings.matches(data, "owned.editor.cut") && this.hasSelection()) {
|
|
96
|
-
this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
|
|
96
|
+
this.options.copyText(this.options.expandCopiedText(this.#selectedText()), true);
|
|
97
97
|
if (!this.#deleteSelection())
|
|
98
98
|
this.#deleteAtomicFocus();
|
|
99
99
|
return;
|
|
@@ -185,7 +185,7 @@ export interface PiShellEditorOptions {
|
|
|
185
185
|
readonly onFollowUp?: (() => void) | undefined;
|
|
186
186
|
readonly onDequeue?: (() => void) | undefined;
|
|
187
187
|
readonly onPromptSuggestionAccepted?: (text: string) => void;
|
|
188
|
-
readonly onCopyText?: (text: string) => void;
|
|
188
|
+
readonly onCopyText?: (text: string, cut: boolean) => void;
|
|
189
189
|
readonly readClipboardContent?: (signal?: AbortSignal) => Promise<PiShellClipboardContent | null>;
|
|
190
190
|
readonly beginClipboardPaste?: () => PiShellPasteReservation;
|
|
191
191
|
readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-12T06:07:21.173Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "51b0b2dc1c9ae577f2586c4217066929278726c2c15308d9144eb6f538e118ff",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-12T06:06:54.072Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "ef62679e83bc1fad3d38f9f21675d9256f08d20e67fd4791571032115ca1b0e9",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-12T06:07:34.911Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "3f4e2d9c37a775b8a12f15539af5421e64f11149293ab2adc1bf3b06ce85b66d",
|
|
12
12
|
"size": 288256
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath", "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
|
|
1
|
+
declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath", "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "tabEndpoint", "tabId", "tabToken", "tabsEndpoint", "terminalHostPath", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
3
|
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory", "sessionWorktreeClaimDirectory"];
|
|
4
4
|
declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
package/dist/product-identity.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import rawIdentity from "./product-identity.json" with { type: "json" };
|
|
2
2
|
const ENVIRONMENT_KEYS = [
|
|
3
3
|
"clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath",
|
|
4
|
-
"paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "terminalSessionId",
|
|
4
|
+
"paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "tabEndpoint", "tabId", "tabToken", "tabsEndpoint", "terminalHostPath", "terminalSessionId",
|
|
5
5
|
];
|
|
6
6
|
const FILESYSTEM_KEYS = ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
7
7
|
const STATE_KEYS = ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory", "sessionWorktreeClaimDirectory"];
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"skipVersionCheck": "A1_SKIP_VERSION_CHECK",
|
|
29
29
|
"startupTrace": "A1_STARTUP_TRACE",
|
|
30
30
|
"suggestionDiagnostics": "A1_SUGGESTION_DIAGNOSTICS",
|
|
31
|
+
"tabEndpoint": "A1_TAB_ENDPOINT",
|
|
32
|
+
"tabId": "A1_TAB_ID",
|
|
33
|
+
"tabToken": "A1_TAB_TOKEN",
|
|
34
|
+
"tabsEndpoint": "A1_TABS_ENDPOINT",
|
|
35
|
+
"terminalHostPath": "A1_TERMINAL_HOST_PATH",
|
|
31
36
|
"terminalSessionId": "A1_TERMINAL_SESSION_ID"
|
|
32
37
|
},
|
|
33
38
|
"state": {
|
|
@@ -36,6 +36,7 @@ export interface TranscriptViewportFrameInput {
|
|
|
36
36
|
/** Final transient rows to bottom-align with unused viewport space while content fits. */
|
|
37
37
|
readonly bottomAlignedTailRowCount?: number;
|
|
38
38
|
readonly dockRows: readonly string[];
|
|
39
|
+
readonly explicitCopyDockRows?: number;
|
|
39
40
|
readonly promptAnchors: readonly TranscriptPromptAnchor[];
|
|
40
41
|
readonly width: number;
|
|
41
42
|
readonly height: number;
|
|
@@ -141,6 +142,7 @@ export interface TranscriptViewportFrame {
|
|
|
141
142
|
readonly cacheEntries: number;
|
|
142
143
|
};
|
|
143
144
|
}
|
|
145
|
+
type SelectionCopyIntent = "automatic" | "explicit";
|
|
144
146
|
/**
|
|
145
147
|
* A1-owned viewport state. It knows only rows and semantic prompt anchors; Pi
|
|
146
148
|
* components and transcript payloads remain outside this neutral boundary.
|
|
@@ -170,9 +172,9 @@ export declare class TranscriptViewport {
|
|
|
170
172
|
extendSelection(column: number, frameRow: number, now?: number, autoScroll?: boolean): boolean;
|
|
171
173
|
releaseSelection(): boolean;
|
|
172
174
|
clearSelection(): boolean;
|
|
173
|
-
selectedText(): string | null;
|
|
175
|
+
selectedText(intent?: SelectionCopyIntent): string | null;
|
|
174
176
|
/** Captures visible selected text, not off-screen history. */
|
|
175
|
-
captureSelectedText(): SelectionCopySnapshot | null;
|
|
177
|
+
captureSelectedText(intent?: SelectionCopyIntent): SelectionCopySnapshot | null;
|
|
176
178
|
scrollBy(lines: number, now?: number): boolean;
|
|
177
179
|
scrollTo(position: number, now?: number): boolean;
|
|
178
180
|
scrollToEnd(now?: number): boolean;
|
|
@@ -184,6 +186,7 @@ export declare class TranscriptViewport {
|
|
|
184
186
|
clearTransient(): void;
|
|
185
187
|
compose(input: TranscriptViewportFrameInput): TranscriptViewportFrame;
|
|
186
188
|
/** Reuses an established transcript frame when only same-height dock rows changed. */
|
|
187
|
-
composeDockOnly(dockRows: readonly string[], width: number, height: number): TranscriptViewportFrame | null;
|
|
189
|
+
composeDockOnly(dockRows: readonly string[], width: number, height: number, explicitCopyDockRows?: number): TranscriptViewportFrame | null;
|
|
188
190
|
}
|
|
189
191
|
export declare function assertTranscriptViewportFrameDescriptor(descriptor: TranscriptViewportFrameDescriptor): void;
|
|
192
|
+
export {};
|
|
@@ -63,6 +63,7 @@ export class TranscriptViewport {
|
|
|
63
63
|
#contentWidth = 0;
|
|
64
64
|
#selectionWidth = 0;
|
|
65
65
|
#viewportHeight = 0;
|
|
66
|
+
#explicitCopyDockRows = 0;
|
|
66
67
|
#firstSelectableRow = 0;
|
|
67
68
|
#selectionRowAnchors = [];
|
|
68
69
|
#frameId = 0;
|
|
@@ -178,22 +179,19 @@ export class TranscriptViewport {
|
|
|
178
179
|
this.#selectionRevision += 1;
|
|
179
180
|
return true;
|
|
180
181
|
}
|
|
181
|
-
selectedText() {
|
|
182
|
+
selectedText(intent = "automatic") {
|
|
182
183
|
const selection = this.#visibleSelection();
|
|
183
184
|
if (selection === undefined)
|
|
184
185
|
return null;
|
|
185
|
-
// Invariant: the blue range spans the complete frame, but automatic and Ctrl+C frame copy
|
|
186
|
-
// admit only persistent transcript rows. Transient status, suggestions, input, and footer
|
|
187
|
-
// chrome remain visually selectable without becoming clipboard payload.
|
|
188
186
|
const copiedLines = Array.from({ length: Math.max(0, selection.end.line - selection.start.line + 1) }, (_value, index) => selection.start.line + index).filter(line => {
|
|
189
187
|
const anchor = this.#selectionRowAnchors[line];
|
|
190
|
-
return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount
|
|
188
|
+
return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount
|
|
189
|
+
|| intent === "explicit" && anchor?.kind === "dock" && anchor.fromBottom < this.#explicitCopyDockRows;
|
|
191
190
|
});
|
|
192
191
|
if (copiedLines.length === 0)
|
|
193
192
|
return null;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const prompt = this.#promptAnchors.find(candidate => copiedLines.every(line => {
|
|
193
|
+
const transcriptLines = copiedLines.filter(line => this.#selectionRowAnchors[line]?.kind === "document");
|
|
194
|
+
const prompt = transcriptLines.length === 0 ? undefined : this.#promptAnchors.find(candidate => transcriptLines.every(line => {
|
|
197
195
|
const anchor = this.#selectionRowAnchors[line];
|
|
198
196
|
return anchor?.kind === "document" && anchor.row >= candidate.firstRow && anchor.row <= candidate.lastRow;
|
|
199
197
|
}));
|
|
@@ -208,11 +206,11 @@ export class TranscriptViewport {
|
|
|
208
206
|
return text.length === 0 ? null : text;
|
|
209
207
|
}
|
|
210
208
|
/** Captures visible selected text, not off-screen history. */
|
|
211
|
-
captureSelectedText() {
|
|
212
|
-
if (!this.#copyableSelection)
|
|
209
|
+
captureSelectedText(intent = "automatic") {
|
|
210
|
+
if (intent === "automatic" && !this.#copyableSelection)
|
|
213
211
|
return null;
|
|
214
212
|
const started = performance.now();
|
|
215
|
-
let text = this.selectedText();
|
|
213
|
+
let text = this.selectedText(intent);
|
|
216
214
|
if (text === null)
|
|
217
215
|
return null;
|
|
218
216
|
text = text.trim();
|
|
@@ -315,6 +313,7 @@ export class TranscriptViewport {
|
|
|
315
313
|
this.#contentWidth = 0;
|
|
316
314
|
this.#selectionWidth = 0;
|
|
317
315
|
this.#viewportHeight = 0;
|
|
316
|
+
this.#explicitCopyDockRows = 0;
|
|
318
317
|
this.clearTransient();
|
|
319
318
|
this.#frame = null;
|
|
320
319
|
}
|
|
@@ -330,6 +329,7 @@ export class TranscriptViewport {
|
|
|
330
329
|
const theme = input.theme ?? PLAIN_THEME;
|
|
331
330
|
const previousDescriptor = this.#frame?.descriptor ?? null;
|
|
332
331
|
const dock = input.dockRows.length > height ? input.dockRows.slice(-height) : [...input.dockRows];
|
|
332
|
+
this.#explicitCopyDockRows = clamp(input.explicitCopyDockRows ?? 0, 0, dock.length);
|
|
333
333
|
const viewportHeight = Math.max(0, height - dock.length);
|
|
334
334
|
const bottomAlignedTailRowCount = clamp(input.bottomAlignedTailRowCount ?? 0, 0, input.documentRows.length);
|
|
335
335
|
const transientAlignmentGapRows = bottomAlignedTailRowCount > 0
|
|
@@ -414,8 +414,6 @@ export class TranscriptViewport {
|
|
|
414
414
|
visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
|
|
415
415
|
visibleSource[0] = sourceRow;
|
|
416
416
|
}
|
|
417
|
-
// Invariant: selection uses exact visible source rows within the rendered
|
|
418
|
-
// content boundary; paint-only guards and the rail gutter stay presentation-only.
|
|
419
417
|
this.#selectionRows = [
|
|
420
418
|
...visibleSource.map(row => truncateToWidth(row, contentWidth)),
|
|
421
419
|
...dock,
|
|
@@ -426,7 +424,6 @@ export class TranscriptViewport {
|
|
|
426
424
|
? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, documentRows.length)
|
|
427
425
|
: { kind: "screen", row, source: this.#selectionRows[row] ?? "" };
|
|
428
426
|
});
|
|
429
|
-
// Invariant: a pinned prompt is chrome over its hidden source row, which clips like off-screen rows.
|
|
430
427
|
this.#firstSelectableRow = stickyActive && viewportHeight > 0 ? 1 : 0;
|
|
431
428
|
this.#selectionRowAnchors = [
|
|
432
429
|
...visibleAnchors,
|
|
@@ -609,7 +606,7 @@ export class TranscriptViewport {
|
|
|
609
606
|
return frame;
|
|
610
607
|
}
|
|
611
608
|
/** Reuses an established transcript frame when only same-height dock rows changed. */
|
|
612
|
-
composeDockOnly(dockRows, width, height) {
|
|
609
|
+
composeDockOnly(dockRows, width, height, explicitCopyDockRows = 0) {
|
|
613
610
|
const previous = this.#frame;
|
|
614
611
|
const boundedWidth = Math.max(1, width);
|
|
615
612
|
const boundedHeight = Math.max(0, height);
|
|
@@ -645,6 +642,7 @@ export class TranscriptViewport {
|
|
|
645
642
|
};
|
|
646
643
|
assertTranscriptViewportFrameDescriptor(descriptor);
|
|
647
644
|
this.#selectionRows = [...this.#selectionRows.slice(0, viewportHeight), ...dock].slice(0, boundedHeight);
|
|
645
|
+
this.#explicitCopyDockRows = clamp(explicitCopyDockRows, 0, dock.length);
|
|
648
646
|
const frame = {
|
|
649
647
|
rows: [...previous.rows.slice(0, viewportHeight), ...dock].slice(0, boundedHeight),
|
|
650
648
|
contentWidth: previous.contentWidth,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { UiTheme } from "./theme.js";
|
|
2
2
|
/**
|
|
3
3
|
* The menu a value opens: anchored to the row it was opened from, marking what
|
|
4
|
-
* is in effect, highlighting nothing until something is picked, and
|
|
5
|
-
*
|
|
4
|
+
* is in effect, highlighting nothing until something is picked, and laid over
|
|
5
|
+
* its anchor so the value in effect stays where it was read.
|
|
6
6
|
*/
|
|
7
7
|
export interface ValueMenuState {
|
|
8
8
|
/** Values offered, as they should read. */
|
|
@@ -37,7 +37,10 @@ export interface ValueMenuLayout {
|
|
|
37
37
|
/** Columns reserved at the right edge, such as a scrollbar rail. */
|
|
38
38
|
readonly reservedRight: number;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Where the menu is placed: over its anchor, so the value in effect sits on the very
|
|
42
|
+
* row it was read from, shifted only as far as the body edges require.
|
|
43
|
+
*/
|
|
41
44
|
export declare function valueMenuFrame(state: ValueMenuState, anchor: ValueMenuAnchor, layout: ValueMenuLayout): ValueMenuFrame;
|
|
42
45
|
/** Draws the menu over the rows behind it, leaving them otherwise untouched. */
|
|
43
46
|
export declare function renderValueMenu(lines: readonly string[], state: ValueMenuState, frame: ValueMenuFrame, theme: UiTheme): readonly string[];
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { overlaySpan } from "./spans.js";
|
|
2
2
|
import { displayWidth, padToWidth } from "./text.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Where the menu is placed: over its anchor, so the value in effect sits on the very
|
|
5
|
+
* row it was read from, shifted only as far as the body edges require.
|
|
6
|
+
*/
|
|
4
7
|
export function valueMenuFrame(state, anchor, layout) {
|
|
5
8
|
const bodyTop = layout.bodyTop ?? 0;
|
|
6
9
|
const bodyBottom = bodyTop + layout.bodyHeight;
|
|
7
|
-
const
|
|
8
|
-
const top =
|
|
9
|
-
? below
|
|
10
|
-
: Math.max(bodyTop, anchor.screenRow - state.choices.length);
|
|
10
|
+
const currentIndex = Math.max(0, state.current === null ? 0 : state.choices.indexOf(state.current));
|
|
11
|
+
const top = Math.max(bodyTop, Math.min(anchor.screenRow - currentIndex, bodyBottom - state.choices.length));
|
|
11
12
|
const previewWidth = Math.max(0, ...(state.previews ?? []).map(displayWidth));
|
|
12
13
|
const previewColumns = previewWidth === 0 ? 0 : previewWidth + 1;
|
|
13
14
|
const width = Math.max(...state.choices.map(choice => displayWidth(choice) + previewColumns + 4), 6);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 14;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
@@ -164,6 +164,18 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
164
164
|
defaultValue: 8;
|
|
165
165
|
allowedValues: readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
|
166
166
|
}>;
|
|
167
|
+
readonly residentTabs: Readonly<{
|
|
168
|
+
id: "residentTabs";
|
|
169
|
+
label: "Resident tabs";
|
|
170
|
+
section: Readonly<{
|
|
171
|
+
id: "generic";
|
|
172
|
+
title: "Generic";
|
|
173
|
+
}>;
|
|
174
|
+
description: "Keep bare A1 running when its terminal closes and reattach on the next launch. Resident tabs have no stop command yet; turning this off prevents new resident launches but does not stop an existing tab.";
|
|
175
|
+
application: "restart";
|
|
176
|
+
defaultValue: false;
|
|
177
|
+
allowedValues: readonly [true, false];
|
|
178
|
+
}>;
|
|
167
179
|
}>;
|
|
168
180
|
export type OwnedSettingId = keyof typeof OWNED_SETTING_DECLARATIONS;
|
|
169
181
|
/** The value type a setting's declaration allows: a literal union for a choice, `number` for a stepped range. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 14;
|
|
2
2
|
const UI_ACCENT_COLOR_CHOICES = Object.freeze([
|
|
3
3
|
"purple", "blue", "cyan", "green", "orange", "pink",
|
|
4
4
|
]);
|
|
@@ -124,6 +124,15 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
124
124
|
defaultValue: 8,
|
|
125
125
|
allowedValues: Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
|
|
126
126
|
}),
|
|
127
|
+
residentTabs: Object.freeze({
|
|
128
|
+
id: "residentTabs",
|
|
129
|
+
label: "Resident tabs",
|
|
130
|
+
section: GENERIC_SECTION,
|
|
131
|
+
description: "Keep bare A1 running when its terminal closes and reattach on the next launch. Resident tabs have no stop command yet; turning this off prevents new resident launches but does not stop an existing tab.",
|
|
132
|
+
application: "restart",
|
|
133
|
+
defaultValue: false,
|
|
134
|
+
allowedValues: Object.freeze([true, false]),
|
|
135
|
+
}),
|
|
127
136
|
});
|
|
128
137
|
/** The declarations in declaration order, for resolution, persistence, and the settings screen. */
|
|
129
138
|
export const OWNED_UI_SETTING_DECLARATIONS = Object.freeze(Object.values(OWNED_SETTING_DECLARATIONS));
|
|
@@ -2,7 +2,7 @@ export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING
|
|
|
2
2
|
export type { OwnedSettingId, OwnedSettingValueOf, OwnedUiSettingApplication, OwnedUiSettingDeclaration, OwnedUiSettingValue } from "./declarations.js";
|
|
3
3
|
export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
|
|
4
4
|
export type { OwnedUiSettingsMigration } from "./migrations.js";
|
|
5
|
-
export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, settingValue } from "./resolution.js";
|
|
5
|
+
export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, resolveResidentTabsSetting, settingValue } from "./resolution.js";
|
|
6
6
|
export type { OwnedUiResolvedSetting, OwnedUiSettingSource, OwnedUiSettingsDocument, OwnedUiSettingsNotice, OwnedUiSettingsNoticeCode, OwnedUiSettingsResolution, ResolveOwnedUiSettingsInput, } from "./resolution.js";
|
|
7
7
|
export { AGENT_SECTION_ID, buildOwnedUiSettingsSections, findOwnedUiSettingsEntry } from "./sections.js";
|
|
8
8
|
export type { AgentSettingsSnapshot, BuildOwnedUiSettingsSectionsInput, OwnedUiSettingsBackend, OwnedUiSettingsEntry, OwnedUiSettingsSection, } from "./sections.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS, assertOwnedUiSettingDeclarations, findOwnedUiSettingDeclaration, isOwnedSettingId, } from "./declarations.js";
|
|
2
2
|
export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
|
|
3
|
-
export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, settingValue } from "./resolution.js";
|
|
3
|
+
export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, resolveResidentTabsSetting, settingValue } from "./resolution.js";
|
|
4
4
|
export { AGENT_SECTION_ID, buildOwnedUiSettingsSections, findOwnedUiSettingsEntry } from "./sections.js";
|
|
5
5
|
export { OwnedSettingsManager } from "./manager.js";
|
|
@@ -104,6 +104,13 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
104
104
|
return { ...values };
|
|
105
105
|
},
|
|
106
106
|
}),
|
|
107
|
+
Object.freeze({
|
|
108
|
+
to: 14,
|
|
109
|
+
description: "Introduce the opt-in resident tabs preview with a disabled default.",
|
|
110
|
+
migrate(values) {
|
|
111
|
+
return { ...values };
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
107
114
|
]);
|
|
108
115
|
export function assertOwnedUiSettingsMigrations(migrations, currentVersion = OWNED_UI_SETTINGS_VERSION) {
|
|
109
116
|
const firstProduced = currentVersion - migrations.length + 1;
|
|
@@ -42,4 +42,6 @@ export interface ResolveOwnedUiSettingsInput {
|
|
|
42
42
|
*/
|
|
43
43
|
export declare function resolveOwnedUiSettings(input: ResolveOwnedUiSettingsInput): OwnedUiSettingsResolution;
|
|
44
44
|
export declare function settingValue(resolution: OwnedUiSettingsResolution, id: string): OwnedUiSettingValue | null;
|
|
45
|
+
/** Resolve the launch-routing flag without constructing the UI settings manager. */
|
|
46
|
+
export declare function resolveResidentTabsSetting(document: OwnedUiSettingsDocument | null): boolean;
|
|
45
47
|
export declare function documentFrom(resolution: OwnedUiSettingsResolution): OwnedUiSettingsDocument;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OWNED_UI_SETTINGS_VERSION, findOwnedUiSettingDeclaration, } from "./declarations.js";
|
|
1
|
+
import { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, findOwnedUiSettingDeclaration, } from "./declarations.js";
|
|
2
2
|
import { migrationsFrom } from "./migrations.js";
|
|
3
3
|
export function parseOwnedUiSettingsDocument(raw) {
|
|
4
4
|
let parsed;
|
|
@@ -122,6 +122,18 @@ export function resolveOwnedUiSettings(input) {
|
|
|
122
122
|
export function settingValue(resolution, id) {
|
|
123
123
|
return resolution.settings.find(setting => setting.declaration.id === id)?.value ?? null;
|
|
124
124
|
}
|
|
125
|
+
/** Resolve the launch-routing flag without constructing the UI settings manager. */
|
|
126
|
+
export function resolveResidentTabsSetting(document) {
|
|
127
|
+
if (document === null || document.version !== OWNED_UI_SETTINGS_VERSION)
|
|
128
|
+
return false;
|
|
129
|
+
const resolution = resolveOwnedUiSettings({
|
|
130
|
+
declarations: [OWNED_SETTING_DECLARATIONS.residentTabs],
|
|
131
|
+
migrations: [],
|
|
132
|
+
document,
|
|
133
|
+
currentVersion: OWNED_UI_SETTINGS_VERSION,
|
|
134
|
+
});
|
|
135
|
+
return settingValue(resolution, "residentTabs") === true;
|
|
136
|
+
}
|
|
125
137
|
export function documentFrom(resolution) {
|
|
126
138
|
const values = { ...resolution.preserved };
|
|
127
139
|
for (const setting of resolution.settings) {
|
|
@@ -53,7 +53,7 @@ The engine host removes A1's own obstacles to several Pi sessions in one process
|
|
|
53
53
|
|
|
54
54
|
## Planned multi-agent boundaries
|
|
55
55
|
|
|
56
|
-
The multi-agent workspace feature, its dependency-free contracts, the structured-agent runtime, and the Node side of the terminal-host protocol were removed from `develop
|
|
56
|
+
The multi-agent workspace feature, its dependency-free contracts, the structured-agent runtime, and the Node side of the terminal-host protocol were removed from `develop`; their last state is commit `243eb7a7` in `develop` history. That structured-workspace plan was retired on 2026-10-10 in favor of `add-persistent-multi-agent-tabs`, where every tab runs the complete A1 UI under the native terminal host. A future implementation starts from the current codebase rather than from that copy. The boundary rules below remain the design intent for that work.
|
|
57
57
|
|
|
58
58
|
The workspace feature may depend on workspace contracts and the structured/terminal-host foundations. Those foundations may depend only on dependency-free workspace contracts, never on product features, launch profiles, or each other unless an approved capability adds the dependency. Launch profiles do not import, initialize, launch, or connect to composed terminal-host infrastructure.
|
|
59
59
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Manual one-pane terminal-host result
|
|
2
|
+
|
|
3
|
+
## First manual artifact
|
|
4
|
+
|
|
5
|
+
- Artifact: `native/terminal-host/target/debug/addone-terminal-host.exe`
|
|
6
|
+
- SHA-256: `8d81db199687a9b8f195b4e40cb4b235de3ea00ffeb9cf55f9c64b1b3582cbba`
|
|
7
|
+
- Size: 2,214,912 bytes
|
|
8
|
+
- User-controlled run: one fullscreen `cmd.exe` pane inside Windows Terminal
|
|
9
|
+
|
|
10
|
+
The user reported that typing commands, cursor movement, colors, Windows Terminal resize, and fast output worked correctly.
|
|
11
|
+
|
|
12
|
+
The first artifact had one finding: `Ctrl+Shift+Q` did not exit.
|
|
13
|
+
|
|
14
|
+
## Corrective artifact
|
|
15
|
+
|
|
16
|
+
- Corrective artifact SHA-256: `1025a0823114477b7ab311ef55098647fc337b98a9194cf389fc124a047360bf`
|
|
17
|
+
- Size: 2,227,712 bytes
|
|
18
|
+
- Correction: accept `Ctrl+Q` or `Ctrl+Shift+Q`, including uppercase key delivery.
|
|
19
|
+
- Correction: resolve Windows command shims through generic PATH/PATHEXT lookup; `pi` resolves to `C:\Users\tprokopiev\AppData\Roaming\npm\pi.cmd`.
|
|
20
|
+
- Automated non-interactive result after correction: build, version/provenance, terminal model, PTY start, process exit, and cleanup passed.
|
|
21
|
+
|
|
22
|
+
## Follow-up artifact
|
|
23
|
+
|
|
24
|
+
- Artifact SHA-256: `b083af60eb8c4ca90438e4022c4e6d10b1410dda5554f554a8b814ba2cdee291`
|
|
25
|
+
- Size: 2,238,464 bytes
|
|
26
|
+
- Removed the host-owned quit shortcut so `Ctrl+Q` no longer intercepts input.
|
|
27
|
+
- Added generic calling-shell ancestry detection; this Git Bash environment resolves to `C:\Program Files\Git\usr\bin\bash.exe --login -i`.
|
|
28
|
+
- Disabled mouse capture by default so the outer terminal can provide native selection.
|
|
29
|
+
- Added `Shift+PageUp`/`Shift+PageDown` and `Shift+Up`/`Shift+Down` host scrollback.
|
|
30
|
+
- Non-interactive terminal model and PTY cleanup probes pass.
|
|
31
|
+
|
|
32
|
+
## Theme-preservation artifact
|
|
33
|
+
|
|
34
|
+
- Artifact SHA-256: `9989373d1ee28ac42bf7a6b1be02d68b92c23b71f14336db493a93d76df48198`
|
|
35
|
+
- Size: 2,238,464 bytes
|
|
36
|
+
- The frame composer now emits the outer terminal's default foreground/background for unstyled cells instead of forcing black.
|
|
37
|
+
- The automated probe asserts the default-background sequence and passes.
|
|
38
|
+
|
|
39
|
+
## Palette-preservation artifact
|
|
40
|
+
|
|
41
|
+
- Artifact SHA-256: `203219f0ee79f3086467dccb5479b5749b09f8bac79623f84682545d374ba2f4`
|
|
42
|
+
- Size: 2,238,976 bytes
|
|
43
|
+
- Palette-indexed child colors are emitted as palette indexes (`38;5;n` / `48;5;n`) instead of being converted through Ghostty's default RGB palette.
|
|
44
|
+
- The automated probe asserts default-background and palette-passthrough sequences and passes.
|
|
45
|
+
|
|
46
|
+
## Scrollback artifact
|
|
47
|
+
|
|
48
|
+
- Artifact SHA-256: `6280786d713abe37eb979e309d27b7ed1eb1f1e1e76e485b0bc5f9c357391ca6`
|
|
49
|
+
- Size: 2,246,656 bytes
|
|
50
|
+
- Mouse capture is enabled so wheel events reach the host; wheel and Shift+navigation scroll the retained terminal viewport.
|
|
51
|
+
- Viewport changes mark the frame dirty before composition.
|
|
52
|
+
- The non-interactive probe verifies retained scrollback, viewport movement, and repainted earlier content.
|
|
53
|
+
- Use the outer terminal's modifier-based selection, usually Shift+drag in Windows Terminal, while pointer capture is active.
|
|
54
|
+
|
|
55
|
+
## Selection and exit artifact
|
|
56
|
+
|
|
57
|
+
- Artifact SHA-256: `fc6d9d1ecbaae4718b850a42b5e6868758d1f4c8c8e1a7645237115d1f1d883c`
|
|
58
|
+
- Size: 2,266,624 bytes
|
|
59
|
+
- Plain left-drag selects terminal content with one uniform white background and black foreground; release emits an OSC 52 clipboard write.
|
|
60
|
+
- Double-click selects a word; triple-click selects a line.
|
|
61
|
+
- Ctrl+C clears an active host selection before reaching the child; without selection, interrupt keys remain child-owned.
|
|
62
|
+
- Mouse wheel scrolls the retained viewport. Home, End, PageUp, PageDown, and arrow keys remain child-owned; no host keyboard scroll binding is active.
|
|
63
|
+
- PTY output-channel closure exits the host when the child session ends.
|
|
64
|
+
- Non-interactive probes pass for terminal rendering, scrollback, selection, and PTY cleanup.
|
|
65
|
+
|
|
66
|
+
Manual retest should verify plain drag selection, double/triple click, mouse wheel, and Ctrl+C selection clearing. No terminal was launched or driven by automation on the active workstation.
|
|
67
|
+
|
|
68
|
+
## User acceptance
|
|
69
|
+
|
|
70
|
+
The user manually confirmed that the vanilla-Pi terminal-host proof launches and works after the interaction corrections. The accepted one-pane pipeline includes theme/palette preservation, retained scrollback, mouse wheel scrolling, plain drag selection, word/line click selection, selection clearing with Ctrl+C, and child-owned interrupt behavior. Task 5.1 is accepted; the next gate is the 2×2 multi-pane proof.
|
package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Manual 2×2 terminal-host basic result
|
|
2
|
+
|
|
3
|
+
## Exact artifact
|
|
4
|
+
|
|
5
|
+
- Recorded at: `2026-08-14T16:13:20.217Z`
|
|
6
|
+
- Source commit: `aeb7421ff5cc82bc5da04cfb0d356e8f8041b6cb`
|
|
7
|
+
- Artifact: `native/terminal-host/target/debug/addone-terminal-host.exe`
|
|
8
|
+
- SHA-256: `896e1b5d2e2730dfd8e591fe3bd565a2aa283fb3ec6f64e8345745c6afcf024d`
|
|
9
|
+
- Size: 2,367,488 bytes
|
|
10
|
+
- Invocation: user-controlled `npm run proof:terminal-host`
|
|
11
|
+
- Surface: one existing outer terminal, one fixed tab, four independently PTY-backed Pi sessions
|
|
12
|
+
|
|
13
|
+
## User-reported result
|
|
14
|
+
|
|
15
|
+
The user manually confirmed all of the following against the exact artifact:
|
|
16
|
+
|
|
17
|
+
- all four panes launched Pi;
|
|
18
|
+
- the four-pane presentation worked without observed flicker;
|
|
19
|
+
- pane focus and input routing worked;
|
|
20
|
+
- changing or clicking the focused pane preserved content in every pane after the repaint correction.
|
|
21
|
+
|
|
22
|
+
The user accepted this basic multi-pane proof result.
|
|
23
|
+
|
|
24
|
+
## Explicit product disposition
|
|
25
|
+
|
|
26
|
+
The fixed 2×2 grid and dashed pane outlines are disposable proof scaffolding, not an accepted production layout or UI design. This result proves the native multi-pane pipeline only. It does not approve carrying the fixed geometry, border treatment, or proof interaction model into the product.
|
|
27
|
+
|
|
28
|
+
Production tabs and panes are expected later to use authoritative arbitrary layouts selected through the AddOne UI. The proof implementation may be removed or replaced after the exact-artifact 2×2 gate is finished; it must not become the default production workspace by inertia.
|
|
29
|
+
|
|
30
|
+
## Gate scope
|
|
31
|
+
|
|
32
|
+
This record does not complete the formal tasks 5.3–5.6 acceptance gate. High-rate output, broader terminal modes and interactions, resize/terminal-size measurements, resource observations, abnormal exits, isolated-worker requirements, and the final stop/go decision remain separately required. No automated physical terminal control was performed on the active workstation.
|