@timurproko/a1 0.1.8-dev.332 → 0.1.8-dev.368
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 +40 -11
- package/dist/composition/owned-ui.d.ts +2 -0
- package/dist/composition/owned-ui.js +20 -3
- package/dist/contracts/owned-ui/index.d.ts +2 -0
- package/dist/contracts/owned-ui/index.js +2 -0
- package/dist/contracts/owned-ui/model.d.ts +38 -3
- package/dist/contracts/owned-ui/prompt-history.d.ts +18 -0
- package/dist/contracts/owned-ui/prompt-suggestions.d.ts +1 -1
- package/dist/contracts/owned-ui/prompt-suggestions.js +4 -1
- package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +22 -0
- package/dist/contracts/owned-ui/suggestion-diagnostics.js +9 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.d.ts +5 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.js +33 -0
- package/dist/contracts/owned-ui/validation.js +45 -2
- package/dist/contracts/presentation/index.d.ts +8 -0
- package/dist/features/owned-ui/settings-app.js +1 -1
- package/dist/features/prompt-history/image-sidecar.d.ts +32 -0
- package/dist/features/prompt-history/image-sidecar.js +130 -0
- package/dist/features/prompt-history/index.d.ts +2 -0
- package/dist/features/prompt-history/index.js +1 -0
- package/dist/features/prompt-history/paths.d.ts +1 -0
- package/dist/features/prompt-history/paths.js +2 -1
- package/dist/features/prompt-history/store.d.ts +1 -1
- package/dist/features/prompt-history/store.js +46 -2
- package/dist/features/prompt-history/worker.js +1 -1
- package/dist/features/prompt-suggestions/diagnostics.d.ts +18 -0
- package/dist/features/prompt-suggestions/diagnostics.js +82 -0
- package/dist/features/prompt-suggestions/index.d.ts +1 -0
- package/dist/features/prompt-suggestions/index.js +1 -0
- package/dist/foundation/release/bootstrap.d.ts +2 -0
- package/dist/foundation/release/bootstrap.js +54 -18
- package/dist/foundation/release/index.d.ts +1 -0
- package/dist/foundation/release/index.js +1 -0
- package/dist/foundation/release/update-launch.d.ts +6 -0
- package/dist/foundation/release/update-launch.js +17 -0
- package/dist/foundation/release/update.d.ts +2 -0
- package/dist/foundation/release/update.js +20 -21
- package/dist/foundation/supervision/main.js +5 -2
- package/dist/foundation/supervision/server.js +40 -13
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +9 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +11 -8
- package/dist/integrations/pi/components/prompt-input-port.d.ts +18 -0
- package/dist/integrations/pi/components/prompt-input-port.js +1 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +66 -11
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +9 -5
- package/dist/integrations/pi/components/shell-presenters-info.js +7 -2
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +6 -2
- package/dist/integrations/pi/components/shell-presenters-transcript.js +81 -49
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +17 -4
- package/dist/integrations/pi/components/shell-shared-facade.js +2 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +3 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +20 -4
- package/dist/integrations/pi/components/tool-result-adapter.d.ts +6 -0
- package/dist/integrations/pi/components/tool-result-adapter.js +27 -0
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +5 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +41 -33
- package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +12 -7
- package/dist/integrations/pi/components/upstream/history/editor-core.js +1 -1
- package/dist/integrations/pi/engine/adapter.d.ts +2 -1
- package/dist/integrations/pi/engine/adapter.js +181 -84
- package/dist/integrations/pi/engine/tool-rendering.d.ts +13 -0
- package/dist/integrations/pi/engine/tool-rendering.js +122 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +30 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +128 -1
- package/dist/integrations/pi/session-ui/prompt-history-controller.d.ts +8 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.js +30 -2
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.d.ts +11 -3
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.js +126 -51
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +24 -2
- package/dist/integrations/pi/session-ui/session-shell-root.js +108 -38
- package/dist/integrations/pi/session-ui/session-shell.js +51 -31
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +7 -1
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +106 -16
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +35 -11
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +4 -1
- package/dist/integrations/pi/tui-runtime/index.d.ts +1 -0
- package/dist/integrations/pi/tui-runtime/index.js +1 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.d.ts +13 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.js +59 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +32 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.js +159 -0
- 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 +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/line-input.d.ts +2 -3
- package/dist/ui/components/line-input.js +4 -20
- package/dist/ui/components/mouse.d.ts +1 -1
- package/dist/ui/components/mouse.js +4 -3
- package/dist/ui/components/prompt-input.d.ts +32 -0
- package/dist/ui/components/prompt-input.js +54 -0
- package/docs/architecture/boundaries.md +1 -0
- package/docs/architecture/history-editor-provenance.md +5 -1
- package/docs/architecture/project-structure.md +1 -0
- package/docs/architecture/prompt-suggestions.md +68 -0
- package/docs/architecture/toolchain.md +1 -1
- package/docs/architecture/ui-reference-provenance.md +23 -0
- package/docs/ci-release-runbook.md +45 -7
- package/docs/features/modal-content-interaction.md +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { TuiAltScreen, isFocusable } from "#pi-tui";
|
|
2
|
+
/** Observes public component renders in compositor order, without accessing renderer state. */
|
|
3
|
+
export class OverlayGeometryTracker {
|
|
4
|
+
publish;
|
|
5
|
+
#frame = [];
|
|
6
|
+
#pending;
|
|
7
|
+
#columns = 0;
|
|
8
|
+
#rows = 0;
|
|
9
|
+
ports = new WeakMap();
|
|
10
|
+
constructor(publish) {
|
|
11
|
+
this.publish = publish;
|
|
12
|
+
}
|
|
13
|
+
invalidate() {
|
|
14
|
+
if (this.#frame === null)
|
|
15
|
+
return;
|
|
16
|
+
this.#frame = null;
|
|
17
|
+
this.publish(null);
|
|
18
|
+
}
|
|
19
|
+
begin(columns, rows) {
|
|
20
|
+
this.#columns = columns;
|
|
21
|
+
this.#rows = rows;
|
|
22
|
+
this.#pending = [];
|
|
23
|
+
}
|
|
24
|
+
record(component, options, width, height) {
|
|
25
|
+
if (this.#pending === undefined)
|
|
26
|
+
return;
|
|
27
|
+
const region = overlayPointerSurface(component, options, width, height, this.#columns, this.#rows);
|
|
28
|
+
if (region !== undefined)
|
|
29
|
+
this.#pending.push(region);
|
|
30
|
+
}
|
|
31
|
+
end() {
|
|
32
|
+
const next = this.#pending ?? [];
|
|
33
|
+
this.#pending = undefined;
|
|
34
|
+
if (this.#frame !== null && samePointerSurfaces(this.#frame, next))
|
|
35
|
+
return;
|
|
36
|
+
this.#frame = next;
|
|
37
|
+
this.publish(next);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The protected composition hook brackets the actual visible, focus-ordered overlay renders. */
|
|
41
|
+
export class GeometryObservedAltScreen extends TuiAltScreen {
|
|
42
|
+
geometry;
|
|
43
|
+
#overlayComponents = new WeakMap();
|
|
44
|
+
get overlayFocused() {
|
|
45
|
+
return this.isOverlayFocused();
|
|
46
|
+
}
|
|
47
|
+
setFocus(component) {
|
|
48
|
+
super.setFocus(component === null ? null : this.#overlayComponents.get(component) ?? component);
|
|
49
|
+
}
|
|
50
|
+
showOverlay(component, options) {
|
|
51
|
+
// Compatibility: all overlay entries cross the public method, including the runtime's search
|
|
52
|
+
// dialog. Observe a wrapper, never replace a method on an installed component.
|
|
53
|
+
const geometry = this.geometry;
|
|
54
|
+
const port = geometry?.ports.get(component) ?? component;
|
|
55
|
+
const wrapper = {
|
|
56
|
+
get focused() { return isFocusable(component) && component.focused; },
|
|
57
|
+
set focused(value) { if (isFocusable(component))
|
|
58
|
+
component.focused = value; },
|
|
59
|
+
get wantsKeyRelease() { return component.wantsKeyRelease ?? false; },
|
|
60
|
+
render(width) {
|
|
61
|
+
const lines = component.render(width);
|
|
62
|
+
geometry?.record(port, options, width, lines.length);
|
|
63
|
+
return lines;
|
|
64
|
+
},
|
|
65
|
+
invalidate: () => component.invalidate(),
|
|
66
|
+
handleInput: data => component.handleInput?.(data),
|
|
67
|
+
};
|
|
68
|
+
this.#overlayComponents.set(component, wrapper);
|
|
69
|
+
geometry?.invalidate();
|
|
70
|
+
const handle = super.showOverlay(wrapper, options);
|
|
71
|
+
return {
|
|
72
|
+
hide: () => { geometry?.invalidate(); handle.hide(); this.#overlayComponents.delete(component); },
|
|
73
|
+
setHidden: hidden => {
|
|
74
|
+
if (hidden !== handle.isHidden())
|
|
75
|
+
geometry?.invalidate();
|
|
76
|
+
handle.setHidden(hidden);
|
|
77
|
+
},
|
|
78
|
+
isHidden: () => handle.isHidden(),
|
|
79
|
+
focus: () => { geometry?.invalidate(); handle.focus(); },
|
|
80
|
+
unfocus: target => {
|
|
81
|
+
geometry?.invalidate();
|
|
82
|
+
if (target === undefined)
|
|
83
|
+
handle.unfocus();
|
|
84
|
+
else
|
|
85
|
+
handle.unfocus({ target: target.target === null ? null : this.#overlayComponents.get(target.target) ?? target.target });
|
|
86
|
+
},
|
|
87
|
+
isFocused: () => handle.isFocused(),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
hideOverlay() {
|
|
91
|
+
this.geometry?.invalidate();
|
|
92
|
+
super.hideOverlay();
|
|
93
|
+
}
|
|
94
|
+
compositeOverlays(lines, width, height) {
|
|
95
|
+
this.geometry?.begin(width, height);
|
|
96
|
+
const result = super.compositeOverlays(lines, width, height);
|
|
97
|
+
this.geometry?.end();
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Compare only geometry and ownership, never animation content or transcript position. */
|
|
102
|
+
export function samePointerSurfaces(a, b) {
|
|
103
|
+
return a.length === b.length && a.every((surface, index) => {
|
|
104
|
+
const other = b[index];
|
|
105
|
+
return surface.component === other.component && surface.columnStart === other.columnStart
|
|
106
|
+
&& surface.columnEnd === other.columnEnd && surface.rowStart === other.rowStart && surface.rowEnd === other.rowEnd;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the pinned public overlay options against the actual render width/height.
|
|
111
|
+
* The runtime still owns layout and painting. Independent terminal-cell tests certify
|
|
112
|
+
* this option-to-hit-region projection against its compositor, including clipping.
|
|
113
|
+
*/
|
|
114
|
+
export function overlayPointerSurface(component, options, width, renderedHeight, columns, rows) {
|
|
115
|
+
const opt = options ?? {};
|
|
116
|
+
const margin = typeof opt.margin === "number"
|
|
117
|
+
? { top: opt.margin, right: opt.margin, bottom: opt.margin, left: opt.margin }
|
|
118
|
+
: opt.margin ?? {};
|
|
119
|
+
const top = Math.max(0, margin.top ?? 0);
|
|
120
|
+
const right = Math.max(0, margin.right ?? 0);
|
|
121
|
+
const bottom = Math.max(0, margin.bottom ?? 0);
|
|
122
|
+
const left = Math.max(0, margin.left ?? 0);
|
|
123
|
+
const availableHeight = Math.max(1, rows - top - bottom);
|
|
124
|
+
const availableWidth = Math.max(1, columns - left - right);
|
|
125
|
+
const limit = size(opt.maxHeight, rows);
|
|
126
|
+
const height = limit === undefined ? renderedHeight : Math.min(renderedHeight, Math.floor(Math.max(1, Math.min(limit, availableHeight))));
|
|
127
|
+
const anchor = opt.anchor ?? "center";
|
|
128
|
+
const anchorRow = anchor.startsWith("top-") ? top
|
|
129
|
+
: anchor.startsWith("bottom-") ? top + availableHeight - height
|
|
130
|
+
: top + Math.floor((availableHeight - height) / 2);
|
|
131
|
+
const anchorColumn = anchor.endsWith("-left") || anchor === "left-center" ? left
|
|
132
|
+
: anchor.endsWith("-right") || anchor === "right-center" ? left + availableWidth - width
|
|
133
|
+
: left + Math.floor((availableWidth - width) / 2);
|
|
134
|
+
const row = Math.max(top, Math.min(position(opt.row, top, availableHeight, height, anchorRow) + (opt.offsetY ?? 0), rows - bottom - height));
|
|
135
|
+
const column = Math.max(left, Math.min(position(opt.col, left, availableWidth, width, anchorColumn) + (opt.offsetX ?? 0), columns - right - width));
|
|
136
|
+
if (height <= 0 || row >= rows || column >= columns)
|
|
137
|
+
return undefined;
|
|
138
|
+
return {
|
|
139
|
+
component,
|
|
140
|
+
rowStart: row + 1,
|
|
141
|
+
rowEnd: Math.min(rows, row + height),
|
|
142
|
+
columnStart: column + 1,
|
|
143
|
+
columnEnd: Math.min(columns, column + width),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function size(value, extent) {
|
|
147
|
+
if (typeof value !== "string")
|
|
148
|
+
return value;
|
|
149
|
+
const match = /^(\d+(?:\.\d+)?)%$/.exec(value);
|
|
150
|
+
return match === null ? undefined : Math.floor(extent * Number(match[1]) / 100);
|
|
151
|
+
}
|
|
152
|
+
function position(value, margin, available, extent, anchor) {
|
|
153
|
+
if (value === undefined)
|
|
154
|
+
return anchor;
|
|
155
|
+
if (typeof value === "number")
|
|
156
|
+
return value;
|
|
157
|
+
const percent = size(value, Math.max(0, available - extent));
|
|
158
|
+
return percent === undefined ? margin + Math.floor((available - extent) / 2) : margin + percent;
|
|
159
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-13T13:11:55.967Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-13T13:12:46.201Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-13T13:12:19.326Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "e6a7be98f08249e90a59466be1c132959a55a80830b000a660724da4d572887a",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const ENVIRONMENT_KEYS: readonly ["configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "terminalSessionId"];
|
|
1
|
+
declare const ENVIRONMENT_KEYS: readonly ["configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
3
|
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
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
|
"configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint",
|
|
4
|
-
"paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "terminalSessionId",
|
|
4
|
+
"paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId",
|
|
5
5
|
];
|
|
6
6
|
const FILESYSTEM_KEYS = ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
7
7
|
const STATE_KEYS = ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UiTheme } from "./theme.js";
|
|
1
2
|
export type LineInputOutcome = {
|
|
2
3
|
readonly kind: "editing";
|
|
3
4
|
} | {
|
|
@@ -47,9 +48,6 @@ export declare class LineInput {
|
|
|
47
48
|
}
|
|
48
49
|
/** Applies one key to the input and reports whether the caller should commit. */
|
|
49
50
|
export declare function handleLineInputKey(input: LineInput, data: string): LineInputOutcome;
|
|
50
|
-
/** Draws the reference prompt rule foreground-only so underlying cells survive. */
|
|
51
|
-
export declare function promptRule(width: number): string;
|
|
52
|
-
export declare const PROMPT_GLYPH = "\u001B[38;2;154;160;166m\u276F\u001B[39m ";
|
|
53
51
|
/**
|
|
54
52
|
* The caret the reference draws: the cell under it is reversed rather than given
|
|
55
53
|
* a colour of its own, so it reads as a block in whatever theme is in use.
|
|
@@ -60,6 +58,7 @@ export interface InputRowOptions {
|
|
|
60
58
|
readonly placeholder?: string;
|
|
61
59
|
/** Rules above and below, in the prompt's own grey. Default true. */
|
|
62
60
|
readonly ruled?: boolean;
|
|
61
|
+
readonly theme?: Pick<UiTheme, "fg">;
|
|
63
62
|
}
|
|
64
63
|
export interface InputRow {
|
|
65
64
|
/** The rows to draw, already padded to the width. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { displayWidth, faint, truncateToWidth } from "./text.js";
|
|
2
|
+
import { PromptInput } from "./prompt-input.js";
|
|
2
3
|
const MAX_VALUE_LENGTH = 4_096;
|
|
3
4
|
const GRAPHEMES = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
4
5
|
/** Character class for a word boundary: 0 space, 1 word, 2 anything else. */
|
|
@@ -198,11 +199,6 @@ export function handleLineInputKey(input, data) {
|
|
|
198
199
|
input.insert(data);
|
|
199
200
|
return { kind: "editing" };
|
|
200
201
|
}
|
|
201
|
-
/** Draws the reference prompt rule foreground-only so underlying cells survive. */
|
|
202
|
-
export function promptRule(width) {
|
|
203
|
-
return `\u001b[38;2;154;160;166m${"─".repeat(Math.max(0, width))}\u001b[39m`;
|
|
204
|
-
}
|
|
205
|
-
export const PROMPT_GLYPH = `\u001b[38;2;154;160;166m❯\u001b[39m `;
|
|
206
202
|
/**
|
|
207
203
|
* The caret the reference draws: the cell under it is reversed rather than given
|
|
208
204
|
* a colour of its own, so it reads as a block in whatever theme is in use.
|
|
@@ -212,8 +208,8 @@ export function caretCell(text) {
|
|
|
212
208
|
}
|
|
213
209
|
/** The input row as the reference draws one, padded to exactly the width. */
|
|
214
210
|
export function renderInputRow(input, width, options = {}) {
|
|
215
|
-
const
|
|
216
|
-
const view = input.view(
|
|
211
|
+
const presentation = new PromptInput(options.theme);
|
|
212
|
+
const view = input.view(Math.max(0, width - presentation.geometry(width).prefixWidth));
|
|
217
213
|
const placeholder = options.placeholder ?? "";
|
|
218
214
|
const empty = view.text.length === 0 && placeholder.length > 0;
|
|
219
215
|
const before = view.text.slice(0, view.caretColumn);
|
|
@@ -224,17 +220,5 @@ export function renderInputRow(input, width, options = {}) {
|
|
|
224
220
|
const body = empty
|
|
225
221
|
? `${caretCell(placeholder.slice(0, 1))}${faint(placeholder.slice(1))}`
|
|
226
222
|
: `${before}${caretCell(under)}${after}`;
|
|
227
|
-
|
|
228
|
-
? placeholder
|
|
229
|
-
: `${view.text}${view.caretColumn >= view.text.length ? " " : ""}`;
|
|
230
|
-
const row = padVisible(truncateToWidth(`${PROMPT_GLYPH}${body}`, width), width, `❯ ${plain}`);
|
|
231
|
-
if (options.ruled === false)
|
|
232
|
-
return { lines: [row] };
|
|
233
|
-
const rule = promptRule(width);
|
|
234
|
-
return { lines: [rule, row, rule] };
|
|
235
|
-
}
|
|
236
|
-
/** Pads by visible width, so styling escapes do not shift the layout. */
|
|
237
|
-
function padVisible(line, width, raw) {
|
|
238
|
-
const visible = displayWidth(raw);
|
|
239
|
-
return visible >= width ? line : line + " ".repeat(width - visible);
|
|
223
|
+
return { lines: presentation.render(width, () => ({ rows: [body] }), options.ruled) };
|
|
240
224
|
}
|
|
@@ -22,4 +22,4 @@ export interface RoutedMouseInput {
|
|
|
22
22
|
* Routes every SGR report independently, preserving keyboard bytes and mouse
|
|
23
23
|
* reports the caller does not claim even when they share one physical chunk.
|
|
24
24
|
*/
|
|
25
|
-
export declare function routeMouseInput(data: string, claim: (event: PaneMouseEvent) => boolean): RoutedMouseInput;
|
|
25
|
+
export declare function routeMouseInput(data: string, claim: (event: PaneMouseEvent, report: string) => boolean): RoutedMouseInput;
|
|
@@ -40,12 +40,13 @@ export function routeMouseInput(data, claim) {
|
|
|
40
40
|
let output = "";
|
|
41
41
|
let index = 0;
|
|
42
42
|
let consumed = false;
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Concurrency: reentrant routing may deliver a report to another component using this parser.
|
|
44
|
+
const pattern = new RegExp(SGR_PATTERN.source, "g");
|
|
45
|
+
for (let match = pattern.exec(data); match !== null; match = pattern.exec(data)) {
|
|
45
46
|
output += data.slice(index, match.index);
|
|
46
47
|
index = match.index + match[0].length;
|
|
47
48
|
const event = toEvent(Number.parseInt(match[1] ?? "", 10), Number.parseInt(match[2] ?? "", 10), Number.parseInt(match[3] ?? "", 10), match[4] === "m");
|
|
48
|
-
if (event !== null && claim(event))
|
|
49
|
+
if (event !== null && claim(event, match[0]))
|
|
49
50
|
consumed = true;
|
|
50
51
|
else
|
|
51
52
|
output += match[0];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type UiTheme } from "./theme.js";
|
|
2
|
+
export declare const PROMPT_GLYPH = "\u276F ";
|
|
3
|
+
export declare const PROMPT_PREFIX_WIDTH = 2;
|
|
4
|
+
export declare function promptRuleText(text: string): string;
|
|
5
|
+
export declare function promptRule(width: number): string;
|
|
6
|
+
export declare function promptArrow(text: string, theme: Pick<UiTheme, "fg">): string;
|
|
7
|
+
export interface PromptInputBody {
|
|
8
|
+
readonly rows: readonly string[];
|
|
9
|
+
/** Semantic border fragments retain editor history and scroll annotations. */
|
|
10
|
+
readonly topRule?: string | undefined;
|
|
11
|
+
readonly bottomRule?: string | undefined;
|
|
12
|
+
readonly after?: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
export interface PromptInputMetrics {
|
|
15
|
+
readonly measure: (text: string) => number;
|
|
16
|
+
readonly truncate: (text: string, width: number) => string;
|
|
17
|
+
}
|
|
18
|
+
/** Composes shared input chrome around controller-owned body rows without owning editing state. */
|
|
19
|
+
export declare class PromptInput {
|
|
20
|
+
readonly theme: Pick<UiTheme, "fg">;
|
|
21
|
+
readonly metrics: PromptInputMetrics;
|
|
22
|
+
constructor(theme?: Pick<UiTheme, "fg">, metrics?: PromptInputMetrics);
|
|
23
|
+
styleRule(text: string): string;
|
|
24
|
+
geometry(width: number, padding?: number): {
|
|
25
|
+
prefixWidth: number;
|
|
26
|
+
innerWidth: number;
|
|
27
|
+
paddingX: number;
|
|
28
|
+
contentWidth: number;
|
|
29
|
+
layoutWidth: number;
|
|
30
|
+
};
|
|
31
|
+
render(width: number, renderBody: (innerWidth: number) => PromptInputBody, ruled?: boolean, padding?: number): string[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { displayWidth, truncateToWidth } from "./text.js";
|
|
2
|
+
import { PLAIN_THEME } from "./theme.js";
|
|
3
|
+
export const PROMPT_GLYPH = "❯ ";
|
|
4
|
+
export const PROMPT_PREFIX_WIDTH = 2;
|
|
5
|
+
export function promptRuleText(text) {
|
|
6
|
+
return `\u001b[38;2;154;160;166m${text}\u001b[39m`;
|
|
7
|
+
}
|
|
8
|
+
export function promptRule(width) {
|
|
9
|
+
return promptRuleText("─".repeat(Math.max(0, width)));
|
|
10
|
+
}
|
|
11
|
+
export function promptArrow(text, theme) {
|
|
12
|
+
return theme.fg("muted", text);
|
|
13
|
+
}
|
|
14
|
+
/** Composes shared input chrome around controller-owned body rows without owning editing state. */
|
|
15
|
+
export class PromptInput {
|
|
16
|
+
theme;
|
|
17
|
+
metrics;
|
|
18
|
+
constructor(theme = PLAIN_THEME, metrics = { measure: displayWidth, truncate: truncateToWidth }) {
|
|
19
|
+
this.theme = theme;
|
|
20
|
+
this.metrics = metrics;
|
|
21
|
+
}
|
|
22
|
+
styleRule(text) { return promptRuleText(text); }
|
|
23
|
+
geometry(width, padding = 0) {
|
|
24
|
+
const prefixWidth = Math.min(PROMPT_PREFIX_WIDTH, Math.max(0, width));
|
|
25
|
+
// Compatibility: editor wrapping needs room for a two-cell grapheme plus its cursor cell.
|
|
26
|
+
// Lay out against a safe virtual width, then clip only at the shared frame boundary.
|
|
27
|
+
const innerWidth = Math.max(3 + padding * 2, width - prefixWidth);
|
|
28
|
+
const paddingX = Math.min(padding, Math.max(0, Math.floor((innerWidth - 1) / 2)));
|
|
29
|
+
const contentWidth = Math.max(1, innerWidth - paddingX * 2);
|
|
30
|
+
return { prefixWidth, innerWidth, paddingX, contentWidth, layoutWidth: Math.max(1, contentWidth - (paddingX ? 0 : 1)) };
|
|
31
|
+
}
|
|
32
|
+
render(width, renderBody, ruled = true, padding = 0) {
|
|
33
|
+
if (width <= 0)
|
|
34
|
+
return [];
|
|
35
|
+
const { prefixWidth, innerWidth } = this.geometry(width, padding);
|
|
36
|
+
const body = renderBody(innerWidth);
|
|
37
|
+
const fit = (row) => {
|
|
38
|
+
const clipped = this.metrics.truncate(row, width);
|
|
39
|
+
return clipped + " ".repeat(Math.max(0, width - this.metrics.measure(clipped)));
|
|
40
|
+
};
|
|
41
|
+
const rows = body.rows.map((row, index) => fit(`${index === 0
|
|
42
|
+
? promptArrow(PROMPT_GLYPH, this.theme)
|
|
43
|
+
: " ".repeat(prefixWidth)}${row}`));
|
|
44
|
+
const rule = (fragment) => fragment === undefined
|
|
45
|
+
? promptRule(width)
|
|
46
|
+
: fit(fragment + promptRule(Math.max(0, width - this.metrics.measure(fragment))));
|
|
47
|
+
return [
|
|
48
|
+
...(ruled ? [rule(body.topRule)] : []),
|
|
49
|
+
...rows,
|
|
50
|
+
...(ruled ? [rule(body.bottomRule)] : []),
|
|
51
|
+
...(body.after ?? []).map(row => fit(`${" ".repeat(prefixWidth)}${row}`)),
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -30,6 +30,7 @@ The owned Pi-backed surface is not an arbitrary-CLI terminal multiplexer. A feat
|
|
|
30
30
|
- `protocol`: additive control handshake, bounded line framing, authenticated launch-instance commands, typed stop intent, snapshots, and command results.
|
|
31
31
|
- `launch`: resolves the effective profile home and the history-specific data root. Composition uses its public path resolver to select `~/.a1/data` for history unless `A1_DATA_DIR` is explicit; global control/release/runtime/cache paths stay unchanged.
|
|
32
32
|
- `prompt-history`: narrowly typed user-input retention in separate profile-owned databases under `~/.a1/data/history` (or `<A1_DATA_DIR>/history`), with bounded worker queues and no supervisor/control-store payloads. It receives a resolved root rather than importing launch-feature internals; it never probes, imports, or deletes the former platform-default history. The editor's owned history state machine remains inside the Pi component boundary; the session UI only coordinates snapshots and classified submissions.
|
|
33
|
+
- `prompt-suggestions`: opt-in bounded diagnostic snapshots of neutral suggestion lifecycle metadata. Composition supplies an explicit local destination; this feature never imports Pi, generates suggestions, stores conversation text, or owns the editor. See [prompt suggestion diagnostics](prompt-suggestions.md).
|
|
33
34
|
- `storage`: SQLite migrations, prior-boot reconciliation, and plural launch-instance persistence. Legacy foreground rows are historical migration input and never authorize current ownership.
|
|
34
35
|
- `supervisor`: endpoint identity, plural cohort ownership, per-instance reconciliation, and aggregate release shutdown coordination. It owns no terminal surface.
|
|
35
36
|
- `pi-engine-adapter`, `pi-component-adapter`, `pi-tui-runtime-adapter`, and `pi-session-ui-integration`: isolate pinned Pi engine and presentation knowledge behind neutral contracts; product features do not import them directly. The session UI render root assembles semantic document and dock rows, while its focused viewport controller owns follow state, pointer routing, selection, and interaction timers. Owned-app route lifecycle belongs to the neutral `ui-apps` owner and is only hosted by the Pi session UI.
|
|
@@ -39,7 +39,11 @@ snapshot installation and observation methods preserve input and undo state,
|
|
|
39
39
|
freeze an active browse cycle, and change caret placement only in that mode.
|
|
40
40
|
The editor renders position/overflow in its existing border. The position label
|
|
41
41
|
uses an injected neutral status-text style (`dim`), while the surrounding rules
|
|
42
|
-
retain the active input-border color, as clarified during manual review.
|
|
42
|
+
retain the active input-border color, as clarified during manual review. The approved
|
|
43
|
+
`show-autocomplete-above-prompt` review refinement omits the literal `History` title:
|
|
44
|
+
`─── 1/100 ─…` keeps the same four-cell inset, count calculation, dim color, optional
|
|
45
|
+
scroll-overflow suffix, and clipping. Core and shell regressions verify numbering,
|
|
46
|
+
100-entry recall, draft restoration, and the absence of the title. Recall temporarily
|
|
43
47
|
separates the draft's live paste backing from recalled literal text, restoring it
|
|
44
48
|
on return; ordinary pinned mode remains source-equivalent.
|
|
45
49
|
|
|
@@ -14,6 +14,7 @@ src/
|
|
|
14
14
|
launch/ launch profiles, profile paths, and runtime selection
|
|
15
15
|
owned-ui/ owned screens, settings application, diagnostics, and runtime lifecycle
|
|
16
16
|
prompt-history/ profile-isolated prompt retention and bounded SQLite worker lifecycle
|
|
17
|
+
prompt-suggestions/ opt-in bounded metadata capture and local diagnostic snapshots
|
|
17
18
|
workspace/ multi-agent presentation, reducer state, routing, and persistence orchestration
|
|
18
19
|
contracts/
|
|
19
20
|
agent-engine/ dependency-free agent engine, session, package, and capability ports
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Prompt suggestion reliability
|
|
2
|
+
|
|
3
|
+
Bare A1 predicts a short next user input with one isolated request using the selected model. A clear offered action consistent with recent user intent, such as `archive it` after accepted merged work, is preferred over abstaining merely because optional testing was also offered. Required validation, conflicting intent, and unresolved choices still allow no suggestion. There is no quoted-text extraction fallback.
|
|
4
|
+
|
|
5
|
+
## Request and editor boundaries
|
|
6
|
+
|
|
7
|
+
The adapter resolves the lowest supported effort from the selected session model's available thinking levels, without changing the main thinking setting. Non-reasoning models omit reasoning controls; supported `off` also omits the optional reasoning argument, matching the model runtime's ordinary completion contract. Other supported efforts are supplied explicitly. Missing capabilities produce `unavailable`, not a guessed provider option. Provider mapping/clamping remains owned by the pinned model runtime; the diagnostic policy describes the applied request option, not inferred server-side computation.
|
|
8
|
+
|
|
9
|
+
The controller retains the 15-second deadline, does not retry or switch models, and retires a timeout before aborting. A provider that ignores abort cannot revive retired text. A timely candidate remains private until matching settlement. Ghost text is not editor content, approval, or a submission: Tab accepts it into the editor, and Enter must be pressed separately to submit it. Typing, session/model replacement, continuation, or disabling suggestions invalidates unaccepted work. `a1 pi` does not install this feature.
|
|
10
|
+
|
|
11
|
+
## Opt-in local diagnostics
|
|
12
|
+
|
|
13
|
+
Set `A1_SUGGESTION_DIAGNOSTICS` to a **dedicated writable file** when launching bare A1. It is an explicit snapshot destination, not a log directory. The parent directory must already exist. The selected file is overwritten with the latest bounded snapshot; never select a session, source, credential, or other valuable file. Do not share one destination between running processes.
|
|
14
|
+
|
|
15
|
+
For a repository checkout in Git Bash:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm run build && A1_SUGGESTION_DIAGNOSTICS="$PWD/.artifacts/suggestion-diagnostics.json" ./scripts/dev
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Create `.artifacts` first if it does not exist. For an installed release, use the same environment assignment before `a1`. The variable follows the ordinary launch environment; it adds no interactive flag, setting, footer row, or transcript message. SDK composition can supply `suggestionDiagnosticsPath` explicitly. Comparison or settings-free compositions ignore both paths.
|
|
22
|
+
|
|
23
|
+
While that session is running, inspect the selected JSON file using a local editor or:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
node -e 'const fs=require("node:fs"); console.table(JSON.parse(fs.readFileSync(process.argv[1],"utf8")).records)' .artifacts/suggestion-diagnostics.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The `prompt-suggestion-diagnostics-v1` snapshot contains at most 128 metadata records and 64 KiB. Capture is off by default. Records include process-local session/run/response/request numbers, bounded provider/model identifiers, applied reasoning policy, elapsed milliseconds, event, and optional reason. They exclude conversation/candidate text, tool data, raw errors, credentials, and session/worktree paths. There is no upload. Older records are evicted, so a retained terminal event can outlive its evicted start event. `request: 0` means no request was started for that decision.
|
|
30
|
+
|
|
31
|
+
| Event | Meaning |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `skipped` | No request started; inspect `reason`. |
|
|
34
|
+
| `started` | One generator request was initiated; reasoning may be `unavailable` for an injected generator without policy metadata. |
|
|
35
|
+
| `displayed` | A current candidate was shown after settlement. |
|
|
36
|
+
| `empty` | The provider returned no text; this does not reveal the model's private reason for abstaining. |
|
|
37
|
+
| `rejected` | Returned content violated the candidate contract, contained tool calls, was truncated, or had invalid formatting/voice. |
|
|
38
|
+
| `provider-failure` | Provider error/rejection; raw error details are intentionally omitted. |
|
|
39
|
+
| `unavailable` | Model/runtime/capabilities or request identity were unavailable at the generator boundary. |
|
|
40
|
+
| `timeout` | The controller's 15-second deadline expired. |
|
|
41
|
+
| `cancelled` | Work was invalidated or aborted, including typing or continuation. |
|
|
42
|
+
| `stale-result` | A result or settlement identity did not match its request. |
|
|
43
|
+
| `presentation-blocked` | A valid candidate could not be shown; inspect `reason`. |
|
|
44
|
+
| `late-result-discarded` | A retired request eventually resolved/rejected; this is not a second terminal outcome or request. |
|
|
45
|
+
|
|
46
|
+
Eligibility/presentation reasons include `disabled`, `disposed`, `early-conversation`, `no-model`, `failed-response`, `incomplete-response`, `tool-continuation`, `replacement-input`, `modal`, `draft`, `not-ready`, `not-focused`, `autocomplete`, `prompt-mode`, and `stale-identity`. `ineligible` and `presentation-unavailable` cover injected ports without finer metadata. A missing suggestion alone is never evidence of timeout or intentional model abstention.
|
|
47
|
+
|
|
48
|
+
Writes are asynchronous and coalesced: at most one write and one pending latest snapshot. Sink failures do not affect generation, input, or rendering and do not appear in the UI. An unwritable destination may therefore produce no file; check the destination locally rather than diagnosing the model from that absence. Disposal clears in-memory capture and pending writes; a write already in progress can finish. Exported local files remain until you remove them.
|
|
49
|
+
|
|
50
|
+
To disable capture, omit/unset `A1_SUGGESTION_DIAGNOSTICS` before the next launch and remove the dedicated snapshot if no longer needed. This does not disable suggestions; `/settings` -> Agent -> Prompt suggestions controls generation independently.
|
|
51
|
+
|
|
52
|
+
## Verification and evidence limits
|
|
53
|
+
|
|
54
|
+
The focused tests cover the synthetic archive offer and required-testing/unresolved-choice counterexamples, unchanged primary thinking, typed outcomes, timeout/cancellation races, editor acceptance, and local privacy/bounds:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npx vitest run test/integrations/pi/session-ui/prompt-suggestion-controller.test.ts test/integrations/pi/engine/adapter.test.ts test/features/prompt-suggestions/diagnostics.test.ts test/contracts/owned-ui/contracts.test.ts test/integrations/pi/session-ui/session-shell.test.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A separate opt-in probe uses the profile's saved model/authentication with a scratch cwd, in-memory sessions/settings, disabled extensions/skills/prompts, and no tools. It performs five original-policy predictions, five revised-policy predictions, and one required-testing counterexample. This makes **11 provider requests** and can consume quota. Run only with explicit authorization:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
RUN_PROMPT_SUGGESTION_PROVIDER_TEST=1 PROMPT_SUGGESTION_AGENT_DIR="C:/path/to/agent-profile" npx vitest run test/integrations/pi/engine/prompt-suggestion-provider.integration.test.ts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The probe reports outcome/latency summaries and archival-candidate counts, not raw text. It requires at least one revised archival continuation and rejects an archival continuation in the required-testing case. Five samples are a smoke comparison, not a statistical reliability guarantee. Missing credentials, model availability, or a successful real archival prediction leave provider acceptance pending; fake tests do not prove model quality.
|
|
67
|
+
|
|
68
|
+
For manual review, use a harmless synthetic conversation with two assistant responses: establish that an imaginary change was tested, accepted, and merged, then ask for a closeout offer containing `Say archive it` with optional extra testing. Do not ask the agent to perform real archival to test ghost text. Keep main thinking high and the editor untouched while waiting, inspect the outcome snapshot, check Tab/Enter separately, and repeat with typing cancellation and suggestions disabled. Verify required testing is not treated as optional. Real latency, model abstention, and existing context-reconstruction limitations can still leave the editor empty.
|
|
@@ -71,7 +71,7 @@ Presentation acceptance is the reader comparing `a1 pi` with pinned Pi. `node sc
|
|
|
71
71
|
|
|
72
72
|
## Publication
|
|
73
73
|
|
|
74
|
-
One workflow publishes both channels: `.github/workflows/release.yml`. Pushes do not publish. Nightly development verification runs at `03:17 UTC`; `npm run develop` explicitly requests a numbered preview and `npm run release` explicitly requests stable publication after its version pull request
|
|
74
|
+
One workflow publishes both channels: `.github/workflows/release.yml`. Pushes do not publish. Nightly development verification runs at `03:17 UTC`; `npm run develop` explicitly requests a numbered preview and `npm run release -- patch` explicitly requests stable publication after its version pull request is manually merged; it promotes a development version to its stable core. The following development-version pull request also requires manual merge. A preview is stamped as `-dev.<merged pull-request number>` and uses npm `next` only as an internal dist-tag. New candidates pack once and are validated on Windows, Linux, and macOS; a repeated nightly verifies the exact immutable registry tarball. All publication uses provenance from the `npm-publish` environment, a preview never changes `latest`, and the stable tag, GitHub Release, and `master` are written only after npm has the package. One global non-cancelling concurrency group serializes the final registry check.
|
|
75
75
|
|
|
76
76
|
`docs/ci-release-runbook.md` is the operational reference.
|
|
77
77
|
|
|
@@ -47,6 +47,29 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
|
|
|
47
47
|
|
|
48
48
|
## Deliberate differences
|
|
49
49
|
|
|
50
|
+
- **Above-prompt autocomplete.** The accepted `show-autocomplete-above-prompt` change
|
|
51
|
+
moves the existing default-editor completion block above the prompt in bare A1.
|
|
52
|
+
`src/integrations/pi/components/upstream/components/owned-editor.ts` exposes its
|
|
53
|
+
border-inclusive body height using the existing atomic-aware layout boundary;
|
|
54
|
+
`shell-editor-autocomplete.ts` moves rows only after prefix/selection decoration
|
|
55
|
+
and adds one top line using the editor's current border-color function.
|
|
56
|
+
The accepted refinement (#343) restores this line instead of panel shading.
|
|
57
|
+
The user-approved review refinement in #344 moves the existing trailing counter
|
|
58
|
+
into that line without parentheses, at the history-label inset and in the same dim
|
|
59
|
+
color. The select-list `scrollInfo` callback identifies its final counter row;
|
|
60
|
+
private selection state and rendered-frame parsing are not used. Counter meaning
|
|
61
|
+
and visibility remain unchanged, and candidate ANSI styling/padding stay intact. The line is
|
|
62
|
+
included in the body offset, disappears with the menu, and is non-text chrome.
|
|
63
|
+
Theme/mode/resize tests verify matching line color and width; terminal background
|
|
64
|
+
replay verifies the menu has no added shading.
|
|
65
|
+
The session shell uses the resulting body offset for pointer routing. Neither
|
|
66
|
+
editor implementation, history mode, menu sizing, completion state, nor installed
|
|
67
|
+
Pi package changes. `a1 pi` remains below-prompt and byte-identical to the independently
|
|
68
|
+
run pinned editor in `test/integrations/pi/components/pinned-editor-input-parity.test.ts`.
|
|
69
|
+
Placement and whole-menu ANSI evidence is in `editor-autocomplete-placement.test.ts`;
|
|
70
|
+
shell checkpoint replay covers final cells, cursor rows, resizing, detached/streaming
|
|
71
|
+
transcripts, widgets, pointer selection, and extension-editor restoration. Physical
|
|
72
|
+
acceptance of the exact build remains required before merging the implementation.
|
|
50
73
|
- **Owned in-session routes are overlays.** The A1 UI reference owns its surface and can switch screens; A1 renders in-session owned routes through the pinned Pi TUI as full-viewport overlays. The pre-resource trust selector is separate and uses a bounded alternate startup surface solely so every completion path can restore the untouched parent terminal before engine activation or a fail-closed diagnostic.
|
|
51
74
|
- **Colour is a port, not an import.** The reference takes a Pi `Theme` directly. A1 defines
|
|
52
75
|
`UiTheme` so the component layer never imports a Pi adapter and can be rendered plainly in
|