@timurproko/a1 0.1.1-dev.5 → 0.1.1-dev.7
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 +32 -9
- package/bin/a1-guardian.js +22 -0
- package/bin/a1-ui.js +7 -4
- package/dist/native/darwin-arm64/a1-process-guardian +0 -0
- package/dist/native/darwin-arm64/manifest.json +20 -0
- package/dist/native/linux-x64/a1-process-guardian +0 -0
- package/dist/native/linux-x64/manifest.json +20 -0
- package/dist/native/win32-x64/a1-process-guardian.exe +0 -0
- package/dist/native/win32-x64/manifest.json +20 -0
- package/dist/src/composition/index.d.ts +26 -0
- package/dist/src/composition/index.js +88 -2
- package/dist/src/features/launch/runtime-selection.d.ts +2 -1
- package/dist/src/features/launch/runtime-selection.js +2 -2
- package/dist/src/features/owned-ui/index.d.ts +1 -0
- package/dist/src/features/owned-ui/index.js +1 -0
- package/dist/src/features/owned-ui/run.d.ts +6 -0
- package/dist/src/features/owned-ui/run.js +3 -1
- package/dist/src/features/owned-ui/settings-app.d.ts +22 -0
- package/dist/src/features/owned-ui/settings-app.js +631 -0
- package/dist/src/foundation/agent-engine-contracts/domain.d.ts +26 -0
- package/dist/src/foundation/launch-guardian/index.d.ts +1 -0
- package/dist/src/foundation/launch-guardian/index.js +1 -0
- package/dist/src/foundation/launch-guardian/main.d.ts +31 -0
- package/dist/src/foundation/launch-guardian/main.js +165 -0
- package/dist/src/foundation/lifecycle/commands.d.ts +40 -0
- package/dist/src/foundation/lifecycle/commands.js +1 -0
- package/dist/src/foundation/lifecycle/index.d.ts +2 -0
- package/dist/src/foundation/lifecycle/index.js +2 -0
- package/dist/src/foundation/lifecycle/launch-instance.d.ts +46 -0
- package/dist/src/foundation/lifecycle/launch-instance.js +134 -0
- package/dist/src/foundation/lifecycle/model.d.ts +6 -42
- package/dist/src/foundation/owned-ui-settings/declarations.d.ts +13 -0
- package/dist/src/foundation/owned-ui-settings/declarations.js +39 -0
- package/dist/src/foundation/owned-ui-settings/index.d.ts +6 -0
- package/dist/src/foundation/owned-ui-settings/index.js +6 -0
- package/dist/src/foundation/owned-ui-settings/migrations.d.ts +13 -0
- package/dist/src/foundation/owned-ui-settings/migrations.js +28 -0
- package/dist/src/foundation/owned-ui-settings/resolution.d.ts +45 -0
- package/dist/src/foundation/owned-ui-settings/resolution.js +147 -0
- package/dist/src/foundation/owned-ui-settings/sections.d.ts +70 -0
- package/dist/src/foundation/owned-ui-settings/sections.js +93 -0
- package/dist/src/foundation/owned-ui-settings/session.d.ts +44 -0
- package/dist/src/foundation/owned-ui-settings/session.js +145 -0
- package/dist/src/foundation/owned-ui-settings/store.d.ts +28 -0
- package/dist/src/foundation/owned-ui-settings/store.js +90 -0
- package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -0
- package/dist/src/foundation/pi-component-adapter/index.js +1 -0
- package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.d.ts +9 -1
- package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.js +10 -8
- package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +12 -3
- package/dist/src/foundation/pi-engine-adapter/adapter.js +73 -7
- package/dist/src/foundation/pi-engine-adapter/pi-settings-metadata.json +374 -0
- package/dist/src/foundation/pi-engine-adapter/public-main-entry.js +10 -1
- package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +42 -2
- package/dist/src/foundation/pi-engine-adapter/settings-integration.js +185 -24
- package/dist/src/foundation/pi-engine-adapter/workflows.d.ts +9 -0
- package/dist/src/foundation/pi-owned-ui-integration/index.d.ts +1 -0
- package/dist/src/foundation/pi-owned-ui-integration/index.js +1 -0
- package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +29 -0
- package/dist/src/foundation/pi-owned-ui-integration/route-host.js +1 -0
- package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +6 -0
- package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +71 -1
- package/dist/src/foundation/pi-tui-runtime-adapter/adapter.d.ts +6 -0
- package/dist/src/foundation/pi-tui-runtime-adapter/adapter.js +9 -0
- package/dist/src/foundation/process-containment/artifact.d.ts +1 -0
- package/dist/src/foundation/process-containment/artifact.js +34 -0
- package/dist/src/foundation/process-containment/contracts.d.ts +31 -0
- package/dist/src/foundation/process-containment/contracts.js +35 -0
- package/dist/src/foundation/process-containment/index.d.ts +5 -0
- package/dist/src/foundation/process-containment/index.js +5 -0
- package/dist/src/foundation/process-containment/linux-process-inspector.d.ts +6 -0
- package/dist/src/foundation/process-containment/linux-process-inspector.js +26 -0
- package/dist/src/foundation/process-containment/native-guardian-containment.d.ts +16 -0
- package/dist/src/foundation/process-containment/native-guardian-containment.js +139 -0
- package/dist/src/foundation/process-containment/windows-process-inspector.d.ts +17 -0
- package/dist/src/foundation/process-containment/windows-process-inspector.js +53 -0
- package/dist/src/foundation/protocol/client.d.ts +2 -1
- package/dist/src/foundation/protocol/client.js +3 -0
- package/dist/src/foundation/protocol/messages.d.ts +5 -5
- package/dist/src/foundation/protocol/messages.js +1 -1
- package/dist/src/foundation/release/bootstrap.js +29 -10
- package/dist/src/foundation/release/cohort-selection.js +3 -3
- package/dist/src/foundation/release/cohort-state.d.ts +3 -2
- package/dist/src/foundation/release/process-cleanup.d.ts +1 -1
- package/dist/src/foundation/release/process-cleanup.js +2 -2
- package/dist/src/foundation/release/update.js +2 -2
- package/dist/src/foundation/storage/control-store.d.ts +8 -7
- package/dist/src/foundation/storage/control-store.js +105 -55
- package/dist/src/foundation/supervision/server.d.ts +4 -2
- package/dist/src/foundation/supervision/server.js +254 -87
- package/dist/src/foundation/transparent-terminal/command-resolution.js +6 -3
- package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +5 -13
- package/dist/src/foundation/transparent-terminal/foreground-broker.js +11 -68
- package/dist/src/foundation/transparent-terminal/main.js +1 -8
- package/dist/src/foundation/ui-apps/contracts.d.ts +42 -0
- package/dist/src/foundation/ui-apps/contracts.js +12 -0
- package/dist/src/foundation/ui-apps/host.d.ts +42 -0
- package/dist/src/foundation/ui-apps/host.js +152 -0
- package/dist/src/foundation/ui-apps/index.d.ts +3 -0
- package/dist/src/foundation/ui-apps/index.js +3 -0
- package/dist/src/foundation/ui-apps/registry.d.ts +11 -0
- package/dist/src/foundation/ui-apps/registry.js +32 -0
- package/dist/src/foundation/ui-components/dialog-panel.d.ts +36 -0
- package/dist/src/foundation/ui-components/dialog-panel.js +47 -0
- package/dist/src/foundation/ui-components/frame.d.ts +21 -0
- package/dist/src/foundation/ui-components/frame.js +54 -0
- package/dist/src/foundation/ui-components/index.d.ts +18 -0
- package/dist/src/foundation/ui-components/index.js +18 -0
- package/dist/src/foundation/ui-components/label.d.ts +8 -0
- package/dist/src/foundation/ui-components/label.js +36 -0
- package/dist/src/foundation/ui-components/line-input.d.ts +74 -0
- package/dist/src/foundation/ui-components/line-input.js +245 -0
- package/dist/src/foundation/ui-components/list-block.d.ts +66 -0
- package/dist/src/foundation/ui-components/list-block.js +176 -0
- package/dist/src/foundation/ui-components/list-view.d.ts +66 -0
- package/dist/src/foundation/ui-components/list-view.js +76 -0
- package/dist/src/foundation/ui-components/mouse.d.ts +15 -0
- package/dist/src/foundation/ui-components/mouse.js +46 -0
- package/dist/src/foundation/ui-components/pane.d.ts +33 -0
- package/dist/src/foundation/ui-components/pane.js +7 -0
- package/dist/src/foundation/ui-components/revision.d.ts +35 -0
- package/dist/src/foundation/ui-components/revision.js +79 -0
- package/dist/src/foundation/ui-components/scrollbar.d.ts +62 -0
- package/dist/src/foundation/ui-components/scrollbar.js +102 -0
- package/dist/src/foundation/ui-components/shortcuts.d.ts +57 -0
- package/dist/src/foundation/ui-components/shortcuts.js +81 -0
- package/dist/src/foundation/ui-components/spans.d.ts +7 -0
- package/dist/src/foundation/ui-components/spans.js +59 -0
- package/dist/src/foundation/ui-components/status-line.d.ts +16 -0
- package/dist/src/foundation/ui-components/status-line.js +14 -0
- package/dist/src/foundation/ui-components/stepper.d.ts +24 -0
- package/dist/src/foundation/ui-components/stepper.js +33 -0
- package/dist/src/foundation/ui-components/surface.d.ts +20 -0
- package/dist/src/foundation/ui-components/surface.js +38 -0
- package/dist/src/foundation/ui-components/text.d.ts +24 -0
- package/dist/src/foundation/ui-components/text.js +123 -0
- package/dist/src/foundation/ui-components/theme.d.ts +17 -0
- package/dist/src/foundation/ui-components/theme.js +12 -0
- package/dist/src/foundation/ui-components/value-menu.d.ts +41 -0
- package/dist/src/foundation/ui-components/value-menu.js +33 -0
- package/dist/src/product-identity.d.ts +2 -2
- package/dist/src/product-identity.js +2 -1
- package/dist/src/product-identity.json +3 -0
- package/docs/architecture/boundaries.md +31 -13
- package/docs/architecture/process-guardian-provenance.md +26 -0
- package/docs/architecture/toolchain.md +6 -0
- package/docs/architecture/ui-reference-provenance.md +50 -0
- package/docs/ci-release-runbook.md +38 -42
- package/docs/features/launch-profiles.md +7 -1
- package/docs/manual-launch-instance-acceptance.md +66 -0
- package/package.json +11 -12
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ScrollbarGeometry } from "./scrollbar.js";
|
|
2
|
+
import type { UiTheme } from "./theme.js";
|
|
3
|
+
/**
|
|
4
|
+
* The chrome around a list: the rail down its right edge, and what it shows when
|
|
5
|
+
* there is nothing to list. Both are the same on any screen, so neither is a
|
|
6
|
+
* screen's to draw.
|
|
7
|
+
*/
|
|
8
|
+
/** Columns the rail occupies: its own, plus the gap before it. */
|
|
9
|
+
export declare const RAIL_COLUMNS = 2;
|
|
10
|
+
export interface RailOptions {
|
|
11
|
+
/** Rows at the top the rail does not run beside, such as a sticky header. */
|
|
12
|
+
readonly topInset?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Draws the rail beside each row, padding the rows to a common width first. */
|
|
15
|
+
export declare function withScrollbarRail(lines: readonly string[], geometry: ScrollbarGeometry | null, contentWidth: number, theme: UiTheme, options?: RailOptions): readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* What a list shows instead of rows: a mark and a line, both quiet, sitting in
|
|
18
|
+
* the middle of the space the rows would have had.
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderEmptyState(message: string, mark: string, height: number, width: number, theme: UiTheme): readonly string[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isThumbRow } from "./scrollbar.js";
|
|
2
|
+
import { displayWidth } from "./text.js";
|
|
3
|
+
/**
|
|
4
|
+
* The chrome around a list: the rail down its right edge, and what it shows when
|
|
5
|
+
* there is nothing to list. Both are the same on any screen, so neither is a
|
|
6
|
+
* screen's to draw.
|
|
7
|
+
*/
|
|
8
|
+
/** Columns the rail occupies: its own, plus the gap before it. */
|
|
9
|
+
export const RAIL_COLUMNS = 2;
|
|
10
|
+
/** Draws the rail beside each row, padding the rows to a common width first. */
|
|
11
|
+
export function withScrollbarRail(lines, geometry, contentWidth, theme, options = {}) {
|
|
12
|
+
const inset = options.topInset ?? 0;
|
|
13
|
+
return lines.map((line, offset) => {
|
|
14
|
+
const cell = offset < inset || geometry === null
|
|
15
|
+
? " "
|
|
16
|
+
: isThumbRow(geometry, offset - inset) ? theme.fg("accent", "│") : theme.fg("dim", "│");
|
|
17
|
+
return `${pad(line, contentWidth)} ${cell}`;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What a list shows instead of rows: a mark and a line, both quiet, sitting in
|
|
22
|
+
* the middle of the space the rows would have had.
|
|
23
|
+
*/
|
|
24
|
+
export function renderEmptyState(message, mark, height, width, theme) {
|
|
25
|
+
const middle = Math.floor(height / 2);
|
|
26
|
+
return Array.from({ length: Math.max(0, height) }, (_line, index) => {
|
|
27
|
+
if (index === middle - 1)
|
|
28
|
+
return centre(theme.fg("muted", mark), mark, width);
|
|
29
|
+
return index === middle ? centre(theme.fg("muted", message), message, width) : "";
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function centre(painted, raw, width) {
|
|
33
|
+
return `${" ".repeat(Math.max(0, Math.floor((width - displayWidth(raw)) / 2)))}${painted}`;
|
|
34
|
+
}
|
|
35
|
+
function pad(line, width) {
|
|
36
|
+
const visible = displayWidth(line);
|
|
37
|
+
return visible >= width ? line : line + " ".repeat(width - visible);
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-width text handling. Terminals lay out by column, not by code unit,
|
|
3
|
+
* so measuring with `String.length` mis-sizes CJK, emoji, and combining marks,
|
|
4
|
+
* and counting styling escapes as visible width breaks every layout that uses
|
|
5
|
+
* colour.
|
|
6
|
+
*/
|
|
7
|
+
/** Removes styling sequences so only visible characters remain. */
|
|
8
|
+
export declare function stripAnsi(text: string): string;
|
|
9
|
+
/** Columns this text occupies once styling is removed. */
|
|
10
|
+
export declare function displayWidth(text: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* Truncates to a column budget without splitting a grapheme. Styling sequences
|
|
13
|
+
* are carried through and a reset is appended when any style was opened, so a
|
|
14
|
+
* truncated row cannot leak colour into the rest of the line.
|
|
15
|
+
*/
|
|
16
|
+
export declare function truncateToWidth(text: string, maxWidth: number): string;
|
|
17
|
+
/** Pads to an exact column count, truncating when the text is already wider. */
|
|
18
|
+
export declare function padToWidth(text: string, width: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* A control that is present but cannot be used: the terminal own faint
|
|
21
|
+
* attribute over whatever colour it already carries, so it reads as half of what
|
|
22
|
+
* it would otherwise be in any theme.
|
|
23
|
+
*/
|
|
24
|
+
export declare function faint(text: string): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-width text handling. Terminals lay out by column, not by code unit,
|
|
3
|
+
* so measuring with `String.length` mis-sizes CJK, emoji, and combining marks,
|
|
4
|
+
* and counting styling escapes as visible width breaks every layout that uses
|
|
5
|
+
* colour.
|
|
6
|
+
*/
|
|
7
|
+
const ANSI_PATTERN = /\[[0-9;:?]*[ -/]*[@-~]|\][^]*(?:|\\)|[@-Z\\-_]/g;
|
|
8
|
+
const SEGMENTER = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
9
|
+
/** Removes styling sequences so only visible characters remain. */
|
|
10
|
+
export function stripAnsi(text) {
|
|
11
|
+
return text.replace(ANSI_PATTERN, "");
|
|
12
|
+
}
|
|
13
|
+
function codePointWidth(codePoint) {
|
|
14
|
+
// Zero-width: combining marks, joiners, variation selectors, and format characters.
|
|
15
|
+
if (codePoint === 0x200d || codePoint === 0xfeff)
|
|
16
|
+
return 0;
|
|
17
|
+
if (codePoint >= 0x0300 && codePoint <= 0x036f)
|
|
18
|
+
return 0;
|
|
19
|
+
if (codePoint >= 0x200b && codePoint <= 0x200f)
|
|
20
|
+
return 0;
|
|
21
|
+
if (codePoint >= 0xfe00 && codePoint <= 0xfe0f)
|
|
22
|
+
return 0;
|
|
23
|
+
if (codePoint >= 0x20d0 && codePoint <= 0x20f0)
|
|
24
|
+
return 0;
|
|
25
|
+
if (codePoint >= 0x1ab0 && codePoint <= 0x1aff)
|
|
26
|
+
return 0;
|
|
27
|
+
if (codePoint >= 0x1dc0 && codePoint <= 0x1dff)
|
|
28
|
+
return 0;
|
|
29
|
+
if (codePoint >= 0xe0100 && codePoint <= 0xe01ef)
|
|
30
|
+
return 0;
|
|
31
|
+
// Wide: CJK, Hangul, fullwidth forms, and the emoji planes terminals render double-width.
|
|
32
|
+
if (codePoint >= 0x1100 && codePoint <= 0x115f)
|
|
33
|
+
return 2;
|
|
34
|
+
if (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f)
|
|
35
|
+
return 2;
|
|
36
|
+
if (codePoint >= 0xac00 && codePoint <= 0xd7a3)
|
|
37
|
+
return 2;
|
|
38
|
+
if (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
39
|
+
return 2;
|
|
40
|
+
if (codePoint >= 0xfe30 && codePoint <= 0xfe6f)
|
|
41
|
+
return 2;
|
|
42
|
+
if (codePoint >= 0xff00 && codePoint <= 0xff60)
|
|
43
|
+
return 2;
|
|
44
|
+
if (codePoint >= 0xffe0 && codePoint <= 0xffe6)
|
|
45
|
+
return 2;
|
|
46
|
+
if (codePoint >= 0x1f300 && codePoint <= 0x1f64f)
|
|
47
|
+
return 2;
|
|
48
|
+
if (codePoint >= 0x1f900 && codePoint <= 0x1f9ff)
|
|
49
|
+
return 2;
|
|
50
|
+
if (codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
51
|
+
return 2;
|
|
52
|
+
// Control characters occupy nothing.
|
|
53
|
+
if (codePoint < 0x20 || (codePoint >= 0x7f && codePoint < 0xa0))
|
|
54
|
+
return 0;
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
/** Width of one grapheme cluster: the widest code point it contains, marks excluded. */
|
|
58
|
+
function graphemeWidth(grapheme) {
|
|
59
|
+
let width = 0;
|
|
60
|
+
for (const character of grapheme) {
|
|
61
|
+
width = Math.max(width, codePointWidth(character.codePointAt(0) ?? 0));
|
|
62
|
+
}
|
|
63
|
+
return width;
|
|
64
|
+
}
|
|
65
|
+
/** Columns this text occupies once styling is removed. */
|
|
66
|
+
export function displayWidth(text) {
|
|
67
|
+
let width = 0;
|
|
68
|
+
for (const { segment } of SEGMENTER.segment(stripAnsi(text)))
|
|
69
|
+
width += graphemeWidth(segment);
|
|
70
|
+
return width;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Truncates to a column budget without splitting a grapheme. Styling sequences
|
|
74
|
+
* are carried through and a reset is appended when any style was opened, so a
|
|
75
|
+
* truncated row cannot leak colour into the rest of the line.
|
|
76
|
+
*/
|
|
77
|
+
export function truncateToWidth(text, maxWidth) {
|
|
78
|
+
if (maxWidth <= 0)
|
|
79
|
+
return "";
|
|
80
|
+
if (displayWidth(text) <= maxWidth)
|
|
81
|
+
return text;
|
|
82
|
+
let out = "";
|
|
83
|
+
let width = 0;
|
|
84
|
+
let styled = false;
|
|
85
|
+
let index = 0;
|
|
86
|
+
while (index < text.length) {
|
|
87
|
+
ANSI_PATTERN.lastIndex = index;
|
|
88
|
+
const match = ANSI_PATTERN.exec(text);
|
|
89
|
+
if (match && match.index === index) {
|
|
90
|
+
out += match[0];
|
|
91
|
+
styled = true;
|
|
92
|
+
index += match[0].length;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const nextEscape = match ? match.index : text.length;
|
|
96
|
+
for (const { segment } of SEGMENTER.segment(text.slice(index, nextEscape))) {
|
|
97
|
+
const segmentWidth = graphemeWidth(segment);
|
|
98
|
+
if (width + segmentWidth > maxWidth)
|
|
99
|
+
return styled ? `${out}[0m` : out;
|
|
100
|
+
out += segment;
|
|
101
|
+
width += segmentWidth;
|
|
102
|
+
}
|
|
103
|
+
index = nextEscape;
|
|
104
|
+
}
|
|
105
|
+
return styled ? `${out}[0m` : out;
|
|
106
|
+
}
|
|
107
|
+
/** Pads to an exact column count, truncating when the text is already wider. */
|
|
108
|
+
export function padToWidth(text, width) {
|
|
109
|
+
if (width <= 0)
|
|
110
|
+
return "";
|
|
111
|
+
const current = displayWidth(text);
|
|
112
|
+
if (current > width)
|
|
113
|
+
return truncateToWidth(text, width);
|
|
114
|
+
return text + " ".repeat(width - current);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A control that is present but cannot be used: the terminal own faint
|
|
118
|
+
* attribute over whatever colour it already carries, so it reads as half of what
|
|
119
|
+
* it would otherwise be in any theme.
|
|
120
|
+
*/
|
|
121
|
+
export function faint(text) {
|
|
122
|
+
return `[2m${text}[22m`;
|
|
123
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour as a port, so a screen can be rendered and asserted without a terminal
|
|
3
|
+
* or a theme, and so the component layer never imports a Pi adapter.
|
|
4
|
+
*/
|
|
5
|
+
export type UiThemeToken = "accent" | "text" | "muted" | "dim" | "border" | "error";
|
|
6
|
+
export interface UiTheme {
|
|
7
|
+
fg(token: UiThemeToken, text: string): string;
|
|
8
|
+
bold(text: string): string;
|
|
9
|
+
/** Inverted styling for the active row of a floating surface. */
|
|
10
|
+
highlight(text: string): string;
|
|
11
|
+
/** A control that is present but cannot act: quieter than quiet text. */
|
|
12
|
+
disabled(text: string): string;
|
|
13
|
+
/** Background for a floating surface, so it reads as above the content. */
|
|
14
|
+
panel(text: string): string;
|
|
15
|
+
}
|
|
16
|
+
/** Renders every token as plain text. What tests assert against. */
|
|
17
|
+
export declare const PLAIN_THEME: UiTheme;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour as a port, so a screen can be rendered and asserted without a terminal
|
|
3
|
+
* or a theme, and so the component layer never imports a Pi adapter.
|
|
4
|
+
*/
|
|
5
|
+
/** Renders every token as plain text. What tests assert against. */
|
|
6
|
+
export const PLAIN_THEME = Object.freeze({
|
|
7
|
+
fg: (_token, text) => text,
|
|
8
|
+
bold: (text) => text,
|
|
9
|
+
highlight: (text) => text,
|
|
10
|
+
disabled: (text) => text,
|
|
11
|
+
panel: (text) => text,
|
|
12
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { UiTheme } from "./theme.js";
|
|
2
|
+
/**
|
|
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 flipping
|
|
5
|
+
* above its anchor only when it would otherwise run off the bottom.
|
|
6
|
+
*/
|
|
7
|
+
export interface ValueMenuState {
|
|
8
|
+
/** Values offered, as they should read. */
|
|
9
|
+
readonly choices: readonly string[];
|
|
10
|
+
/** The value in effect, marked rather than highlighted. */
|
|
11
|
+
readonly current: string | null;
|
|
12
|
+
/** The entry picked by a key or the pointer, or -1 while none is. */
|
|
13
|
+
readonly index: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ValueMenuAnchor {
|
|
16
|
+
/** Row the menu was opened from, which it keeps even as the selection moves. */
|
|
17
|
+
readonly screenRow: number;
|
|
18
|
+
/** Column the values start at. */
|
|
19
|
+
readonly valueColumn: number;
|
|
20
|
+
}
|
|
21
|
+
/** Where a menu sits on screen, for reading a pointer against it. */
|
|
22
|
+
export interface ValueMenuFrame {
|
|
23
|
+
readonly top: number;
|
|
24
|
+
readonly column: number;
|
|
25
|
+
readonly width: number;
|
|
26
|
+
readonly rows: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ValueMenuLayout {
|
|
29
|
+
/** Rows available above the footer. */
|
|
30
|
+
readonly bodyHeight: number;
|
|
31
|
+
/** Full surface width, so the menu stays inside it. */
|
|
32
|
+
readonly surfaceWidth: number;
|
|
33
|
+
/** Columns reserved at the right edge, such as a scrollbar rail. */
|
|
34
|
+
readonly reservedRight: number;
|
|
35
|
+
}
|
|
36
|
+
/** Where the menu is placed: below its anchor where there is room, else above. */
|
|
37
|
+
export declare function valueMenuFrame(state: ValueMenuState, anchor: ValueMenuAnchor, layout: ValueMenuLayout): ValueMenuFrame;
|
|
38
|
+
/** Draws the menu over the rows behind it, leaving them otherwise untouched. */
|
|
39
|
+
export declare function renderValueMenu(lines: readonly string[], state: ValueMenuState, frame: ValueMenuFrame, theme: UiTheme): readonly string[];
|
|
40
|
+
/** Whether a pointer report lands inside the menu rather than behind it. */
|
|
41
|
+
export declare function menuRowAt(frame: ValueMenuFrame, row: number, column: number): number | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { overlaySpan } from "./spans.js";
|
|
2
|
+
import { displayWidth, padToWidth } from "./text.js";
|
|
3
|
+
/** Where the menu is placed: below its anchor where there is room, else above. */
|
|
4
|
+
export function valueMenuFrame(state, anchor, layout) {
|
|
5
|
+
const below = anchor.screenRow + 1;
|
|
6
|
+
const top = below + state.choices.length <= layout.bodyHeight
|
|
7
|
+
? below
|
|
8
|
+
: Math.max(0, anchor.screenRow - state.choices.length);
|
|
9
|
+
const width = Math.max(...state.choices.map(choice => displayWidth(choice) + 4), 6);
|
|
10
|
+
const column = Math.min(anchor.valueColumn, Math.max(0, layout.surfaceWidth - width - layout.reservedRight));
|
|
11
|
+
return { top, column, width, rows: state.choices.length };
|
|
12
|
+
}
|
|
13
|
+
/** Draws the menu over the rows behind it, leaving them otherwise untouched. */
|
|
14
|
+
export function renderValueMenu(lines, state, frame, theme) {
|
|
15
|
+
const output = [...lines];
|
|
16
|
+
state.choices.forEach((choice, index) => {
|
|
17
|
+
const target = frame.top + index;
|
|
18
|
+
if (target < 0 || target >= output.length)
|
|
19
|
+
return;
|
|
20
|
+
const mark = choice === state.current ? "✓ " : " ";
|
|
21
|
+
const text = padToWidth(`${mark}${choice} `, frame.width);
|
|
22
|
+
const painted = index === state.index ? theme.highlight(text) : theme.panel(text);
|
|
23
|
+
output[target] = overlaySpan(output[target] ?? "", frame.column, frame.column + frame.width, painted);
|
|
24
|
+
});
|
|
25
|
+
return output;
|
|
26
|
+
}
|
|
27
|
+
/** Whether a pointer report lands inside the menu rather than behind it. */
|
|
28
|
+
export function menuRowAt(frame, row, column) {
|
|
29
|
+
const at = row - frame.top;
|
|
30
|
+
if (at < 0 || at >= frame.rows)
|
|
31
|
+
return null;
|
|
32
|
+
return column > frame.column && column <= frame.column + frame.width ? at : null;
|
|
33
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
declare const ENVIRONMENT_KEYS: readonly ["certificationTarball", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "fixture", "fixtureInput", "fixtureToken", "inputAcknowledgement", "internalPackaging", "launchArgumentsJson", "launchProfile", "nativePi", "paneId", "piParityIntentionalMutation", "piPortRoot", "piSourceLedgerPath", "piSourceScanRoot", "probeTrace", "profileHome", "protocolVersion", "releaseDigest", "releaseId", "releaseRoot", "releaseRunnerLabel", "runtimeDir", "structuredFlowLimits", "terminalArgumentsJson", "terminalExecutable", "terminalSessionId"];
|
|
1
|
+
declare const ENVIRONMENT_KEYS: readonly ["certificationTarball", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "fixture", "fixtureInput", "fixtureToken", "inputAcknowledgement", "internalPackaging", "launchArgumentsJson", "launchProfile", "nativePi", "paneId", "piParityIntentionalMutation", "piPortRoot", "piSourceLedgerPath", "piSourceScanRoot", "probeTrace", "processGuardianPath", "profileHome", "protocolVersion", "releaseDigest", "releaseId", "releaseRoot", "releaseRunnerLabel", "runtimeDir", "structuredFlowLimits", "terminalArgumentsJson", "terminalExecutable", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
3
|
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "piSandboxProfile"];
|
|
4
4
|
declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
|
5
5
|
declare const MANIFEST_KEYS: readonly ["releaseFilename", "packageFilename"];
|
|
6
6
|
declare const PROTOCOL_KEYS: readonly ["namespace", "controlEnvelope", "supervisorSchema", "nativeHostSchema", "structuredAgentSchema", "controlStoreSchema", "releaseCohortSchema", "updateJournalSchema"];
|
|
7
7
|
declare const EVIDENCE_KEYS: readonly ["nativeSpikeSchema", "terminalProvenanceSchema", "terminalProofSchema", "stableReleaseSchema", "previewReleaseSchema", "releaseCertificationSchema", "previewPlatformVerdictSchema", "piSourceLedgerSchema", "piComponentParitySchema", "piEventFrameParitySchema"];
|
|
8
|
-
declare const ARTIFACT_KEYS: readonly ["cliEntry", "supervisorEntry", "uiEntry", "nativeExecutable", "nativeCrate", "releaseTarballStem", "diagnosticStem"];
|
|
8
|
+
declare const ARTIFACT_KEYS: readonly ["cliEntry", "supervisorEntry", "guardianEntry", "uiEntry", "nativeExecutable", "nativeCrate", "processGuardianExecutable", "releaseTarballStem", "diagnosticStem"];
|
|
9
9
|
type StringRecord<Keys extends readonly string[]> = {
|
|
10
10
|
readonly [Key in Keys[number]]: string;
|
|
11
11
|
};
|
|
@@ -21,6 +21,7 @@ const ENVIRONMENT_KEYS = [
|
|
|
21
21
|
"piSourceLedgerPath",
|
|
22
22
|
"piSourceScanRoot",
|
|
23
23
|
"probeTrace",
|
|
24
|
+
"processGuardianPath",
|
|
24
25
|
"profileHome",
|
|
25
26
|
"protocolVersion",
|
|
26
27
|
"releaseDigest",
|
|
@@ -39,7 +40,7 @@ const ENDPOINT_KEYS = ["windowsPipeStem", "unixSocketFilename", "metadataFilenam
|
|
|
39
40
|
const MANIFEST_KEYS = ["releaseFilename", "packageFilename"];
|
|
40
41
|
const PROTOCOL_KEYS = ["namespace", "controlEnvelope", "supervisorSchema", "nativeHostSchema", "structuredAgentSchema", "controlStoreSchema", "releaseCohortSchema", "updateJournalSchema"];
|
|
41
42
|
const EVIDENCE_KEYS = ["nativeSpikeSchema", "terminalProvenanceSchema", "terminalProofSchema", "stableReleaseSchema", "previewReleaseSchema", "releaseCertificationSchema", "previewPlatformVerdictSchema", "piSourceLedgerSchema", "piComponentParitySchema", "piEventFrameParitySchema"];
|
|
42
|
-
const ARTIFACT_KEYS = ["cliEntry", "supervisorEntry", "uiEntry", "nativeExecutable", "nativeCrate", "releaseTarballStem", "diagnosticStem"];
|
|
43
|
+
const ARTIFACT_KEYS = ["cliEntry", "supervisorEntry", "guardianEntry", "uiEntry", "nativeExecutable", "nativeCrate", "processGuardianExecutable", "releaseTarballStem", "diagnosticStem"];
|
|
43
44
|
const ROOT_KEYS = ["schema", "displayName", "commandName", "packageName", "filesystem", "environment", "state", "endpoint", "manifest", "protocol", "evidence", "artifacts"];
|
|
44
45
|
export function validateProductIdentity(value) {
|
|
45
46
|
const root = exactObject(value, ROOT_KEYS, "product identity");
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"piSourceLedgerPath": "A1_PI_SOURCE_LEDGER_PATH",
|
|
32
32
|
"piSourceScanRoot": "A1_PI_SOURCE_SCAN_ROOT",
|
|
33
33
|
"probeTrace": "A1_PROBE_TRACE",
|
|
34
|
+
"processGuardianPath": "A1_PROCESS_GUARDIAN_PATH",
|
|
34
35
|
"profileHome": "A1_PROFILE_HOME",
|
|
35
36
|
"protocolVersion": "A1_PROTOCOL_VERSION",
|
|
36
37
|
"releaseDigest": "A1_RELEASE_DIGEST",
|
|
@@ -87,9 +88,11 @@
|
|
|
87
88
|
"artifacts": {
|
|
88
89
|
"cliEntry": "bin/a1.js",
|
|
89
90
|
"supervisorEntry": "bin/a1-supervisor.js",
|
|
91
|
+
"guardianEntry": "bin/a1-guardian.js",
|
|
90
92
|
"uiEntry": "bin/a1-ui.js",
|
|
91
93
|
"nativeExecutable": "a1-terminal-host",
|
|
92
94
|
"nativeCrate": "a1-terminal-host",
|
|
95
|
+
"processGuardianExecutable": "a1-process-guardian",
|
|
93
96
|
"releaseTarballStem": "timurproko-a1",
|
|
94
97
|
"diagnosticStem": "a1"
|
|
95
98
|
}
|
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
# Architecture boundaries
|
|
2
2
|
|
|
3
|
-
## Terminal ownership
|
|
3
|
+
## Terminal and launch-instance ownership
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every interactive command uses this profile-neutral lifecycle path:
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
8
|
mutable a1 entry
|
|
9
9
|
-> verified immutable release
|
|
10
|
-
->
|
|
11
|
-
->
|
|
10
|
+
-> Node launch guardian and authenticated launch-instance registration
|
|
11
|
+
-> native process guardian and per-instance containment
|
|
12
|
+
-> a1-ui runtime selection
|
|
13
|
+
-> owned UI or transparent Pi with inherited physical terminal handles
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Launch-instance ownership is plural. The supervisor tracks any number of independently authenticated `a1`, `a1 pi`, and `a1 sandbox` instances and never uses one product-wide foreground mutex. Closing one instance closes only its verified process tree. Persisted rows alone do not prove liveness, and uncertainty never authorizes terminating an unrelated process.
|
|
17
|
+
|
|
18
|
+
Neither guardian reads ordinary terminal input, parses or relays runtime output, renders cells, infers frames, synthesizes terminal responses, or emits display control. Bare `a1` retains its owned UI authority. In explicit Pi profiles, Pi and the physical terminal retain direct terminal behavior. The private guardian boundary carries only bounded identity, readiness, stop, and outcome data.
|
|
15
19
|
|
|
16
20
|
Transparent mode has no A1-authoritative terminal surface, internal pane, virtual scrollback, inactive resident tab, or visual reconnection. A future feature that requires those properties must introduce and certify a separate composed-terminal authority; it must not weaken or silently intercept transparent mode.
|
|
17
21
|
|
|
18
22
|
## Current module responsibilities
|
|
19
23
|
|
|
20
|
-
- `
|
|
24
|
+
- `lifecycle`: dependency-free launch-instance, process identity, state transition, outcome, and control-command contracts.
|
|
25
|
+
- `process-containment`: artifact verification, OS-verifiable process inspection, containment adapters, and bounded graceful/forced cleanup; it owns no terminal content.
|
|
26
|
+
- `launch-guardian`: one profile-neutral instance coordinator above runtime selection.
|
|
21
27
|
- `workspace-contracts`: dependency-free multi-agent identity, capability, command/event/snapshot, terminal window/tab/pane/session topology, terminal-host, and recovery contracts.
|
|
22
28
|
- `structured-agent-runtime`: planned typed event/command/snapshot runtime. It must not infer semantics from terminal text, own pseudoterminals, or reconstruct screens.
|
|
23
29
|
- `native-host-protocol`: bounded typed local boundary for terminal-host identity, topology revisions, lifecycle, and recovery. Terminal bytes, per-event child input, and rendered cells are forbidden across it.
|
|
24
|
-
- `protocol`: additive control handshake, bounded line framing,
|
|
25
|
-
- `storage`: SQLite migrations,
|
|
26
|
-
- `supervisor`: endpoint identity, cohort ownership,
|
|
27
|
-
- `transparent`: generic command resolution
|
|
28
|
-
- release/update/bootstrap: package-derived immutable release identity, cohort selection, durable update transactions, rollback, and dependency-light command entry.
|
|
30
|
+
- `protocol`: additive control handshake, bounded line framing, authenticated launch-instance commands, typed stop intent, snapshots, and command results.
|
|
31
|
+
- `storage`: SQLite migrations, prior-boot reconciliation, and plural launch-instance persistence. Legacy foreground rows are historical migration input and never authorize current ownership.
|
|
32
|
+
- `supervisor`: endpoint identity, plural cohort ownership, per-instance reconciliation, and aggregate release shutdown coordination. It owns no terminal surface.
|
|
33
|
+
- `transparent-terminal`: generic command resolution and direct inherited-handle child waiting below the outer launch guardian; it owns no supervisor lease.
|
|
34
|
+
- release/update/bootstrap: package-derived immutable release identity, process-guardian integrity, cohort selection, durable update transactions, rollback, and dependency-light command entry.
|
|
29
35
|
|
|
30
36
|
## Dependency direction
|
|
31
37
|
|
|
32
38
|
Terminal boundaries are application-agnostic. They may not select behavior from executable names, arguments, CLI-specific environment variables, or visible text. Production code may not import private Pi distribution APIs or use a Pi-specific terminal workaround.
|
|
33
39
|
|
|
34
|
-
The
|
|
40
|
+
The transparent path has no PTY or terminal-emulator dependency. Reintroducing `node-pty`, xterm state, custom input encoders, mode/query trackers, cadence-derived frame inference, or renderer/projection code requires an approved capability change and cannot enter transparent mode. The Rust process guardian is a lifecycle-only native boundary and is independent from the held composed terminal host.
|
|
35
41
|
|
|
36
42
|
`scripts/check-architecture.mjs` enforces the structural parts of these boundaries. Cross-cutting rationale belongs here; implementation history belongs in Git and archived OpenSpec changes.
|
|
37
43
|
|
|
@@ -39,8 +45,20 @@ The repository has no production PTY or terminal-emulator dependency. Reintroduc
|
|
|
39
45
|
|
|
40
46
|
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. Explicit launch profiles and transparent fallback may not import, initialize, launch, or connect to composed terminal-host infrastructure.
|
|
41
47
|
|
|
48
|
+
Default future agents and composed hosts are children of the originating bare-`a1` launch instance and terminate when it closes. A process that survives command closure requires a separately approved explicit resident capability; durable identity or state recovery never implies an implicitly surviving process.
|
|
49
|
+
|
|
42
50
|
The structured-agent runtime uses a versioned adapter hello and feature negotiation before readiness. Runtime state must use typed events, commands, snapshots, cancellation, and recovery evidence. It must not infer structured semantics from terminal text, terminal timing, or visual content and must not own pseudoterminals or renderer state.
|
|
43
51
|
|
|
44
|
-
The terminal-host protocol boundary may carry only bounded typed control and semantic lifecycle messages. Pseudoterminal bytes, per-event child input, rendered cells, cell grids, framebuffers, and screen buffers are forbidden across it. Console host executables and packaging live outside the current JavaScript production owners and remain unavailable until the isolated in-terminal
|
|
52
|
+
The terminal-host protocol boundary may carry only bounded typed control and semantic lifecycle messages. Pseudoterminal bytes, per-event child input, rendered cells, cell grids, framebuffers, and screen buffers are forbidden across it. Console host executables and packaging live outside the current JavaScript production owners and remain unavailable until the isolated in-terminal proof passes; replacing them with a lightweight custom parser/renderer is forbidden. A desktop-native window and GPU application shell are postponed and are not required for the terminal-hosted product.
|
|
45
53
|
|
|
46
54
|
Resource queues, retained state, evidence, logs, and diagnostics follow `docs/architecture/resource-and-data-policy.md`. Terminal content and authentication material are never control-store records. Unknown data is potentially sensitive by default and must be rejected or redacted before persistence.
|
|
55
|
+
|
|
56
|
+
## The rules every surface follows
|
|
57
|
+
|
|
58
|
+
How A1 builds what a reader sees — deriving a vendor engine's data rather than
|
|
59
|
+
transcribing it, failing the build by name when a vendor change has not been
|
|
60
|
+
absorbed, keeping vendor knowledge behind the vendor boundary, composing screens
|
|
61
|
+
from shared components, showing state rather than narrating it, taking colour
|
|
62
|
+
from declared theme roles, and answering both dispatch and description from one
|
|
63
|
+
declaration — is stated once in the `owned-ux-architecture` capability rather
|
|
64
|
+
than restated per feature.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Process guardian provenance
|
|
2
|
+
|
|
3
|
+
A1 launch-instance containment uses a standalone native executable named `a1-process-guardian`. The approved implementation is a separate Rust crate at `native/process-guardian`; it is not a Node-API addon and does not reuse or resume the held composed terminal-host implementation.
|
|
4
|
+
|
|
5
|
+
## Boundary
|
|
6
|
+
|
|
7
|
+
The executable owns platform process containment and launches one immutable `a1-ui` runtime with inherited terminal handles. It does not create a PTY, read ordinary terminal input, capture or parse runtime output, render, or relay terminal bytes. Its private parent/child coordination is restricted to bounded lifecycle and identity data.
|
|
8
|
+
|
|
9
|
+
On Windows it uses `windows-sys 0.61.2` to create a Job Object, enables `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, assigns the runtime before resuming it, denies silent breakaway by never enabling breakaway limits, monitors the verified Node parent process, and terminates remaining job members after root or parent exit.
|
|
10
|
+
|
|
11
|
+
## Artifact contract
|
|
12
|
+
|
|
13
|
+
Release builds place the exact executable at:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
dist/native/win32-x64/a1-process-guardian.exe
|
|
17
|
+
dist/native/linux-x64/a1-process-guardian
|
|
18
|
+
dist/native/darwin-x64/a1-process-guardian
|
|
19
|
+
dist/native/darwin-arm64/a1-process-guardian
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each supported artifact must be named in the release integrity manifest with target triple, crate version, guardian protocol version, SHA-256, byte size, build provenance, and signature/attestation status. A missing, altered, incompatible, or wrong-platform artifact is a concise unsupported-containment launch error; A1 never silently falls back to PID-only cleanup.
|
|
23
|
+
|
|
24
|
+
## Licensing
|
|
25
|
+
|
|
26
|
+
The guardian is A1-owned MIT-licensed source. Its Windows API binding is `windows-sys`, dual-licensed MIT or Apache-2.0 by its upstream project. Exact transitive notices are generated from the locked release dependency graph.
|
|
@@ -32,6 +32,12 @@ The published package contains:
|
|
|
32
32
|
|
|
33
33
|
It contains no PTY, terminal emulator, browser/desktop GUI, custom renderer, input translator, physical automation driver, or generated runtime data.
|
|
34
34
|
|
|
35
|
+
## Native launch containment
|
|
36
|
+
|
|
37
|
+
Source builds require Rust/Cargo 1.85 or newer. `npm run build` compiles the standalone `native/process-guardian` crate for the host platform, places it under `dist/native/<platform>-<architecture>/`, and writes an integrity manifest. Preview and stable candidate workflows build platform artifacts on isolated Windows, Linux, and macOS runners and assemble them before packing. macOS remains explicitly unsupported until its exact containment adapter is certified; the package must fail before runtime startup rather than use PID-only cleanup.
|
|
38
|
+
|
|
39
|
+
The process guardian inherits terminal handles but transports no terminal bytes. See [`process-guardian-provenance.md`](process-guardian-provenance.md).
|
|
40
|
+
|
|
35
41
|
## Gates
|
|
36
42
|
|
|
37
43
|
```sh
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# A1 UI reference provenance
|
|
2
|
+
|
|
3
|
+
The A1 UI component layer is ported from an existing A1-authored reference implementation
|
|
4
|
+
rather than reinvented. Rebuilding a screen from screenshots reproduces its shape but not its
|
|
5
|
+
decisions, and the decisions are where the work is: column arithmetic, sticky-header
|
|
6
|
+
reservation, block-jump targets, span overlay under styling, and pointer hit regions.
|
|
7
|
+
|
|
8
|
+
This file records what came from where, so a divergence can be judged against its source
|
|
9
|
+
instead of argued from memory. It is the same discipline as
|
|
10
|
+
[`terminal-host-provenance.md`](terminal-host-provenance.md), applied to UI source.
|
|
11
|
+
|
|
12
|
+
## Source
|
|
13
|
+
|
|
14
|
+
A1 UI reference implementation, local working copy. The reference is a Pi extension built on
|
|
15
|
+
its own `core` facade layer; A1 is a product, so the port adapts imports and keeps behavior.
|
|
16
|
+
|
|
17
|
+
## Ported units
|
|
18
|
+
|
|
19
|
+
| A1 module | Reference unit | Adaptation |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| `ui-components/line-input.ts` — word motion and word delete | `core/panes/line-input.ts` | `wordLeft`/`wordRight` and their key bindings ported verbatim, including deciding a word delete before a plain one because a raw backspace byte is ctrl+backspace on Windows Terminal. |
|
|
22
|
+
| `ui-components/spans.ts` — `overlaySpan` | `core/presentation/spans.ts` | Uses A1 `displayWidth`; hyperlink and style replay kept verbatim. |
|
|
23
|
+
| `ui-components/scrollbar.ts` — geometry | `core/presentation/scrollbar.ts` — `scrollbarGeom` | Same formula; A1 names the fields and returns null rather than undefined. |
|
|
24
|
+
| `ui-components/scrollbar.ts` — rails | `core/presentation/scrollbar.ts` — zone and hover state | Rail identity kept; A1 holds state in an instance rather than a module global. |
|
|
25
|
+
| `ui-components/list-block.ts` — block navigation | `settings/impl.ts` — `blockJumpTarget`, `blockRowSpan`, `bottomBlockTarget` | Row type generalized from settings rows to a grouped list; targets and edge behavior identical. |
|
|
26
|
+
| `ui-components/list-block.ts` — sticky scroll | `settings/impl.ts` — `stickyHeaderGroup`, `topPaddingRows`, `visibleRowCountAt`, `clampScrollForView` | Same reservation arithmetic and two-pass reveal. |
|
|
27
|
+
| `ui-components/mouse.ts` | `core/panes/sgr-mouse.ts` | Same SGR decoding and per-call regex reset; A1 emits its own event shape. |
|
|
28
|
+
| `ui-components/mouse.ts` — tracking sequences | `core/host/pi/providers/host-bridge-surface.ts` | Mouse modes only. A1 does not take the alternate screen, because the Pi TUI owns the screen A1 renders through. |
|
|
29
|
+
| `features/owned-ui/settings-app.ts` — layout | `settings/impl.ts` — `settingsValueColumn`, `renderFieldLine`, footer and search rendering | Setting discovery is A1's own section model; presentation follows the reference. |
|
|
30
|
+
|
|
31
|
+
## Ported from the pinned engine
|
|
32
|
+
|
|
33
|
+
| A1 module | Pinned Pi source | Adaptation |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `pi-engine-adapter/settings-integration.ts` — `SETTING_LABELS` | pinned Pi settings selector | Labels and descriptions transcribed so an owned screen reads as the vanilla route words it. Ids are mapped from the selector kebab-case to the exposed camelCase keys. |
|
|
36
|
+
|
|
37
|
+
## Deliberate differences
|
|
38
|
+
|
|
39
|
+
- **No alternate screen.** The reference owns its surface and can switch screens; A1 renders
|
|
40
|
+
through the pinned Pi TUI, so an owned screen is a full-viewport overlay instead.
|
|
41
|
+
- **Colour is a port, not an import.** The reference takes a Pi `Theme` directly. A1 defines
|
|
42
|
+
`UiTheme` so the component layer never imports a Pi adapter and can be rendered plainly in
|
|
43
|
+
tests.
|
|
44
|
+
- **Settings content.** The reference aggregates per-extension settings files; A1 has one
|
|
45
|
+
section for its own settings and one for the agent's, read through the engine settings port.
|
|
46
|
+
|
|
47
|
+
## Keeping this honest
|
|
48
|
+
|
|
49
|
+
When a ported unit is changed, either change it to match the reference or record here why it
|
|
50
|
+
diverges. An undocumented divergence is the thing this file exists to prevent.
|