@timurproko/a1 0.1.8-dev.226 → 0.1.8-dev.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/guardian.js +4 -2
- package/bin/supervisor.js +1 -1
- package/bin/ui.js +44 -36
- package/bin/warmup.js +18 -9
- package/dist/cli/dispatch.d.ts +9 -1
- package/dist/cli/dispatch.js +73 -14
- package/dist/cli/package-messages.d.ts +26 -0
- package/dist/cli/package-messages.js +71 -0
- package/dist/cli/packages.d.ts +2 -1
- package/dist/cli/packages.js +12 -19
- package/dist/composition/owned-ui.d.ts +5 -2
- package/dist/composition/owned-ui.js +3 -1
- package/dist/contracts/agent-engine/package-ports.d.ts +6 -0
- package/dist/features/launch/intent.d.ts +5 -3
- package/dist/features/launch/intent.js +7 -2
- package/dist/features/owned-ui/index.d.ts +1 -0
- package/dist/features/owned-ui/index.js +1 -0
- package/dist/features/owned-ui/session-fork-prompt.d.ts +8 -0
- package/dist/features/owned-ui/session-fork-prompt.js +34 -0
- package/dist/foundation/launch-guardian/main.d.ts +2 -0
- package/dist/foundation/launch-guardian/main.js +8 -3
- package/dist/foundation/lifecycle/index.d.ts +2 -0
- package/dist/foundation/lifecycle/index.js +2 -0
- package/dist/foundation/lifecycle/paths.d.ts +2 -0
- package/dist/foundation/lifecycle/paths.js +9 -2
- package/dist/foundation/lifecycle/session-selection.d.ts +9 -0
- package/dist/foundation/lifecycle/session-selection.js +39 -0
- package/dist/foundation/lifecycle/supervisor-startup.d.ts +30 -0
- package/dist/foundation/lifecycle/supervisor-startup.js +106 -0
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +5 -0
- package/dist/foundation/process-containment/darwin-process-inspector.js +7 -0
- package/dist/foundation/process-containment/index.d.ts +1 -0
- package/dist/foundation/process-containment/index.js +1 -0
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +2 -1
- package/dist/foundation/process-containment/windows-process-inspector.js +4 -2
- package/dist/foundation/release/bootstrap.d.ts +10 -3
- package/dist/foundation/release/bootstrap.js +58 -25
- package/dist/foundation/release/dependency-layer.js +11 -10
- package/dist/foundation/release/immutable-platform.d.ts +6 -0
- package/dist/foundation/release/immutable-platform.js +13 -0
- package/dist/foundation/release/index.d.ts +1 -0
- package/dist/foundation/release/index.js +1 -0
- package/dist/foundation/release/release-store.d.ts +1 -1
- package/dist/foundation/release/release-store.js +4 -2
- package/dist/foundation/release/restart-certification.d.ts +67 -0
- package/dist/foundation/release/restart-certification.js +211 -0
- package/dist/foundation/release/update.js +4 -4
- package/dist/foundation/release/warmup.js +1 -1
- package/dist/foundation/startup/startup-runtime.d.ts +2 -2
- package/dist/foundation/startup/startup-runtime.js +1 -1
- package/dist/foundation/supervision/main.d.ts +1 -1
- package/dist/foundation/supervision/main.js +60 -30
- package/dist/foundation/supervision/server.js +14 -1
- package/dist/integrations/pi/components/shell-footer-status.js +28 -12
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +2 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +5 -0
- package/dist/integrations/pi/engine/adapter.d.ts +6 -0
- package/dist/integrations/pi/engine/adapter.js +354 -53
- package/dist/integrations/pi/engine/index.d.ts +1 -0
- package/dist/integrations/pi/engine/index.js +1 -0
- package/dist/integrations/pi/engine/package-integration.js +31 -25
- package/dist/integrations/pi/engine/runtime-integration.d.ts +3 -0
- package/dist/integrations/pi/engine/runtime-integration.js +11 -4
- package/dist/integrations/pi/engine/session-selection.d.ts +28 -0
- package/dist/integrations/pi/engine/session-selection.js +90 -0
- package/dist/integrations/pi/engine/workflows.d.ts +18 -1
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +5 -1
- package/dist/integrations/pi/session-ui/session-shell-root.js +76 -24
- package/dist/integrations/pi/session-ui/session-shell.d.ts +1 -1
- package/dist/integrations/pi/session-ui/session-shell.js +46 -14
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +2 -1
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +47 -18
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +12 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +166 -26
- package/dist/native/darwin-arm64/manifest.json +4 -4
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/text-selection.js +8 -35
- package/dist/ui/components/transcript-viewport.d.ts +8 -2
- package/dist/ui/components/transcript-viewport.js +8 -6
- package/dist/ui/components/visible-hyperlinks.d.ts +19 -0
- package/dist/ui/components/visible-hyperlinks.js +64 -0
- package/docs/architecture/process-guardian-provenance.md +3 -1
- package/docs/architecture/ui-reference-provenance.md +2 -0
- package/docs/ci-release-runbook.md +1 -1
- package/docs/features/launch-profiles.md +17 -1
- package/docs/manual-ghost-link-baseline.md +89 -0
- package/package.json +1 -1
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
"crateVersion": "0.1.0-dev",
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
|
-
"capability": "
|
|
8
|
-
"builtAt": "2026-09-
|
|
7
|
+
"capability": "supported",
|
|
8
|
+
"builtAt": "2026-09-06T11:53:56.572Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
11
|
+
"sha256": "dc03605e5780e2aeebb4ecafd62868dc673e22ddd38b024a369aff704ff136dc",
|
|
12
|
+
"size": 393152
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
15
15
|
"language": "Rust",
|
|
Binary file
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-06T11:53:48.191Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "ee8a00eaaf79c707459bbbfb52518e9739314967049fbe5fa625f36ce33db9ee",
|
|
File without changes
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-06T11:54:20.386Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "4fc782bdb0b01e3aa12377e9d79d5e8d6222d58b6f0672d60c0dafbef9987586",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -11,6 +11,7 @@ export * from "./revision.js";
|
|
|
11
11
|
export * from "./scrollbar.js";
|
|
12
12
|
export * from "./shortcuts.js";
|
|
13
13
|
export * from "./spans.js";
|
|
14
|
+
export * from "./visible-hyperlinks.js";
|
|
14
15
|
export * from "./status-line.js";
|
|
15
16
|
export * from "./submitted-prompt.js";
|
|
16
17
|
export * from "./stepper.js";
|
|
@@ -11,6 +11,7 @@ export * from "./revision.js";
|
|
|
11
11
|
export * from "./scrollbar.js";
|
|
12
12
|
export * from "./shortcuts.js";
|
|
13
13
|
export * from "./spans.js";
|
|
14
|
+
export * from "./visible-hyperlinks.js";
|
|
14
15
|
export * from "./status-line.js";
|
|
15
16
|
export * from "./submitted-prompt.js";
|
|
16
17
|
export * from "./stepper.js";
|
|
@@ -15,40 +15,16 @@ export function orderedTextSelection(selection) {
|
|
|
15
15
|
...(selection.throughFinalColumn ? { throughFinalColumn: true } : {}),
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
if (selection.cell)
|
|
19
|
-
const anchorFirst = comparePoints(anchor, head) <= 0;
|
|
20
|
-
const first = anchorFirst ? anchor : head;
|
|
21
|
-
const last = anchorFirst ? head : anchor;
|
|
22
|
-
return {
|
|
23
|
-
start: { line: first.line, column: pointBefore(first) },
|
|
24
|
-
end: { line: last.line, column: pointAfter(last) },
|
|
25
|
-
...(selection.throughFinalColumn ? { throughFinalColumn: true } : {}),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
if (selection.dragged !== true)
|
|
29
|
-
return undefined;
|
|
30
|
-
const direction = comparePoints(anchor, head);
|
|
31
|
-
if (direction === 0)
|
|
32
|
-
return undefined;
|
|
33
|
-
if (sameGrapheme(anchor, head)) {
|
|
34
|
-
return {
|
|
35
|
-
start: { line: anchor.line, column: pointBefore(anchor) },
|
|
36
|
-
end: { line: anchor.line, column: pointAfter(anchor) },
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
const ordered = direction < 0
|
|
40
|
-
? {
|
|
41
|
-
start: { line: anchor.line, column: pointBefore(anchor) },
|
|
42
|
-
end: { line: head.line, column: pointBefore(head) },
|
|
43
|
-
}
|
|
44
|
-
: {
|
|
45
|
-
start: { line: head.line, column: pointAfter(head) },
|
|
46
|
-
end: { line: anchor.line, column: pointAfter(anchor) },
|
|
47
|
-
};
|
|
48
|
-
if (ordered.start.line === ordered.end.line && ordered.start.column >= ordered.end.column)
|
|
18
|
+
if (!selection.cell && selection.dragged !== true)
|
|
49
19
|
return undefined;
|
|
20
|
+
// Invariant: an accepted drag includes both endpoint graphemes, even after returning
|
|
21
|
+
// to its anchor. Only an unextended point press is empty; paint and copy stay half-open.
|
|
22
|
+
const anchorFirst = comparePoints(anchor, head) <= 0;
|
|
23
|
+
const first = anchorFirst ? anchor : head;
|
|
24
|
+
const last = anchorFirst ? head : anchor;
|
|
50
25
|
return {
|
|
51
|
-
|
|
26
|
+
start: { line: first.line, column: pointBefore(first) },
|
|
27
|
+
end: { line: last.line, column: pointAfter(last) },
|
|
52
28
|
...(selection.throughFinalColumn ? { throughFinalColumn: true } : {}),
|
|
53
29
|
};
|
|
54
30
|
}
|
|
@@ -224,9 +200,6 @@ function pointBefore(point) {
|
|
|
224
200
|
function pointAfter(point) {
|
|
225
201
|
return point.to ?? Math.max(0, point.column);
|
|
226
202
|
}
|
|
227
|
-
function sameGrapheme(left, right) {
|
|
228
|
-
return left.line === right.line && pointBefore(left) === pointBefore(right) && pointAfter(left) === pointAfter(right);
|
|
229
|
-
}
|
|
230
203
|
function comparePoints(left, right) {
|
|
231
204
|
return left.line === right.line ? left.column - right.column : left.line - right.line;
|
|
232
205
|
}
|
|
@@ -34,6 +34,11 @@ export interface TranscriptViewportFrameInput {
|
|
|
34
34
|
* notification rows cannot make the control jump vertically.
|
|
35
35
|
*/
|
|
36
36
|
readonly bottomControlRow?: number;
|
|
37
|
+
/** Latest reported one-based terminal coordinates; bottom hover uses this frame's hit region. */
|
|
38
|
+
readonly pointerPosition?: {
|
|
39
|
+
readonly column: number;
|
|
40
|
+
readonly row: number;
|
|
41
|
+
};
|
|
37
42
|
readonly now?: number;
|
|
38
43
|
readonly theme?: TranscriptViewportTheme;
|
|
39
44
|
}
|
|
@@ -56,6 +61,8 @@ export interface TranscriptViewportHitRegions {
|
|
|
56
61
|
readonly columnStart: number;
|
|
57
62
|
readonly columnEnd: number;
|
|
58
63
|
} | null;
|
|
64
|
+
/** Transient non-selectable document-tail rows currently visible in the viewport. */
|
|
65
|
+
readonly transientTail: readonly number[];
|
|
59
66
|
}
|
|
60
67
|
export interface TranscriptViewportFrameDescriptor {
|
|
61
68
|
readonly frameId: number;
|
|
@@ -122,7 +129,6 @@ export declare class TranscriptViewport {
|
|
|
122
129
|
setRailHovered(hovered: boolean): void;
|
|
123
130
|
setRailDragging(dragging: boolean): void;
|
|
124
131
|
setStickyHovered(hovered: boolean): void;
|
|
125
|
-
setBottomHovered(hovered: boolean): void;
|
|
126
132
|
get selectionActive(): boolean;
|
|
127
133
|
get hasSelection(): boolean;
|
|
128
134
|
pressSelection(column: number, viewportRow: number, now?: number): boolean;
|
|
@@ -135,7 +141,7 @@ export declare class TranscriptViewport {
|
|
|
135
141
|
scrollToEnd(now?: number): boolean;
|
|
136
142
|
/** Jumps to the pinned prompt governing this position, then earlier prompts. */
|
|
137
143
|
scrollToPreviousPrompt(now?: number): boolean;
|
|
138
|
-
/** Jumps to the next submitted prompt
|
|
144
|
+
/** Jumps to the next submitted prompt, or resumes following at the bottom. */
|
|
139
145
|
scrollToNextPrompt(now?: number): boolean;
|
|
140
146
|
reset(): void;
|
|
141
147
|
clearTransient(): void;
|
|
@@ -30,7 +30,6 @@ export class TranscriptViewport {
|
|
|
30
30
|
#railHovered = false;
|
|
31
31
|
#railDragging = false;
|
|
32
32
|
#stickyHovered = false;
|
|
33
|
-
#bottomHovered = false;
|
|
34
33
|
#selection;
|
|
35
34
|
#selectionClick;
|
|
36
35
|
#selectionRevision = 0;
|
|
@@ -73,7 +72,6 @@ export class TranscriptViewport {
|
|
|
73
72
|
setRailHovered(hovered) { this.#railHovered = hovered; }
|
|
74
73
|
setRailDragging(dragging) { this.#railDragging = dragging; }
|
|
75
74
|
setStickyHovered(hovered) { this.#stickyHovered = hovered; }
|
|
76
|
-
setBottomHovered(hovered) { this.#bottomHovered = hovered; }
|
|
77
75
|
get selectionActive() { return this.#selection?.selecting === true; }
|
|
78
76
|
get hasSelection() { return orderedTextSelection(this.#selection) !== undefined; }
|
|
79
77
|
pressSelection(column, viewportRow, now = Date.now()) {
|
|
@@ -193,7 +191,7 @@ export class TranscriptViewport {
|
|
|
193
191
|
const destination = target === earliest ? 0 : target;
|
|
194
192
|
return target >= 0 && this.scrollTo(destination, now);
|
|
195
193
|
}
|
|
196
|
-
/** Jumps to the next submitted prompt
|
|
194
|
+
/** Jumps to the next submitted prompt, or resumes following at the bottom. */
|
|
197
195
|
scrollToNextPrompt(now = Date.now()) {
|
|
198
196
|
if (this.#promptAnchors.length === 0)
|
|
199
197
|
return false;
|
|
@@ -206,7 +204,7 @@ export class TranscriptViewport {
|
|
|
206
204
|
if (anchor.firstRow > after && anchor.firstRow < target)
|
|
207
205
|
target = anchor.firstRow;
|
|
208
206
|
}
|
|
209
|
-
return Number.isFinite(target)
|
|
207
|
+
return Number.isFinite(target) ? this.scrollTo(target, now) : this.scrollToEnd(now);
|
|
210
208
|
}
|
|
211
209
|
reset() {
|
|
212
210
|
this.#scrollTop = 0;
|
|
@@ -234,7 +232,6 @@ export class TranscriptViewport {
|
|
|
234
232
|
this.#railHovered = false;
|
|
235
233
|
this.#railDragging = false;
|
|
236
234
|
this.#stickyHovered = false;
|
|
237
|
-
this.#bottomHovered = false;
|
|
238
235
|
}
|
|
239
236
|
compose(input) {
|
|
240
237
|
const width = Math.max(1, input.width);
|
|
@@ -359,8 +356,12 @@ export class TranscriptViewport {
|
|
|
359
356
|
const fallbackRow = Math.max(0, viewportHeight - 1);
|
|
360
357
|
const row = clamp(input.bottomControlRow ?? fallbackRow, 0, frameRows.length - 1);
|
|
361
358
|
const left = Math.floor((contentWidth - labelWidth) / 2);
|
|
362
|
-
frameRows[row] = overlaySpan(padRowPreservingBackground(frameRows[row] ?? "", width), left, left + labelWidth, `${CONTROL_STYLE_RESET}${theme.bottomControl(label, this.#bottomHovered)}`);
|
|
363
359
|
bottomHit = { row: row + 1, columnStart: left + 1, columnEnd: left + labelWidth };
|
|
360
|
+
// Invariant: appearance and hit testing share current geometry, even when no mouse moves.
|
|
361
|
+
const pointer = input.pointerPosition;
|
|
362
|
+
const bottomHovered = pointer !== undefined && pointer.row === bottomHit.row
|
|
363
|
+
&& pointer.column >= bottomHit.columnStart && pointer.column <= bottomHit.columnEnd;
|
|
364
|
+
frameRows[row] = overlaySpan(padRowPreservingBackground(frameRows[row] ?? "", width), left, left + labelWidth, `${CONTROL_STYLE_RESET}${theme.bottomControl(label, bottomHovered)}`);
|
|
364
365
|
}
|
|
365
366
|
}
|
|
366
367
|
const nextDocumentRange = {
|
|
@@ -415,6 +416,7 @@ export class TranscriptViewport {
|
|
|
415
416
|
},
|
|
416
417
|
sticky: stickyActive ? { row: 1, target: governing.firstRow, width: contentWidth } : null,
|
|
417
418
|
bottom: bottomHit,
|
|
419
|
+
transientTail: Array.from({ length: Math.max(0, Math.min(input.documentRows.length, this.#scrollTop + viewportHeight) - Math.max(this.#scrollTop, this.#selectableDocumentRowCount)) }, (_row, index) => Math.max(this.#scrollTop, this.#selectableDocumentRowCount) - this.#scrollTop + index + 1),
|
|
418
420
|
};
|
|
419
421
|
const frame = {
|
|
420
422
|
rows: frameRows,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface VisibleHyperlinkRange {
|
|
2
|
+
readonly from: number;
|
|
3
|
+
readonly to: number;
|
|
4
|
+
readonly target: string;
|
|
5
|
+
readonly kind: "explicit" | "candidate";
|
|
6
|
+
}
|
|
7
|
+
export interface VisibleHyperlinkRow {
|
|
8
|
+
readonly ranges: readonly VisibleHyperlinkRange[];
|
|
9
|
+
/** False for unclosed links, malformed escapes, images, or row-moving controls. */
|
|
10
|
+
readonly replaySafe: boolean;
|
|
11
|
+
readonly width: number;
|
|
12
|
+
/** Paint identity includes labels, not just targets; it never changes source bytes. */
|
|
13
|
+
readonly signature: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads final visible row geometry without decorating text. Candidate ranges
|
|
17
|
+
* only invalidate host-hover paint; they never invent an explicit link target.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readVisibleHyperlinks(line: string, widthOf?: (text: string) => number): VisibleHyperlinkRow;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { displayWidth } from "./text.js";
|
|
2
|
+
const TOKENS = /\u001b\[[0-?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|[^\u001b]+|\u001b/gu;
|
|
3
|
+
const LINK = /^\u001b\]8;[^;\u0007\u001b]*;([^\u0007\u001b]*)(?:\u0007|\u001b\\)$/u;
|
|
4
|
+
const SGR = /^\u001b\[[\d;:]*m$/u;
|
|
5
|
+
const SEMANTIC_ZONE = /^\u001b\]133;[ABC](?:\u0007|\u001b\\)$/u;
|
|
6
|
+
// Platform: this is a conservative cleanup detector, NOT a link-activation
|
|
7
|
+
// parser. Hosts recognize URLs, domains and file-like labels without OSC 8.
|
|
8
|
+
const CANDIDATE = /(?:[a-z][a-z\d+.-]*:\/\/[^\s<>"']+|www\.[^\s<>"']+|(?:[a-z]:[\\/]|\.{1,2}[\\/])[^\s<>"']+|[\p{L}\p{N}_-]+(?:\.\p{L}[\p{L}\p{N}_-]+)+(?:[\\/][^\s<>"']+)?)/giu;
|
|
9
|
+
/**
|
|
10
|
+
* Reads final visible row geometry without decorating text. Candidate ranges
|
|
11
|
+
* only invalidate host-hover paint; they never invent an explicit link target.
|
|
12
|
+
*/
|
|
13
|
+
export function readVisibleHyperlinks(line, widthOf = displayWidth) {
|
|
14
|
+
const ranges = [];
|
|
15
|
+
let plain = "";
|
|
16
|
+
let column = 0;
|
|
17
|
+
let active;
|
|
18
|
+
let replaySafe = true;
|
|
19
|
+
const finish = () => {
|
|
20
|
+
if (active !== undefined && column > active.from) {
|
|
21
|
+
ranges.push({ from: active.from, to: column, target: active.target, kind: "explicit" });
|
|
22
|
+
}
|
|
23
|
+
active = undefined;
|
|
24
|
+
};
|
|
25
|
+
for (const match of line.matchAll(TOKENS)) {
|
|
26
|
+
const token = match[0];
|
|
27
|
+
if (token.startsWith("\u001b")) {
|
|
28
|
+
const link = LINK.exec(token);
|
|
29
|
+
if (link !== null) {
|
|
30
|
+
finish();
|
|
31
|
+
if (link[1])
|
|
32
|
+
active = { from: column, target: link[1] };
|
|
33
|
+
}
|
|
34
|
+
else if (!SGR.test(token) && !SEMANTIC_ZONE.test(token))
|
|
35
|
+
replaySafe = false;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if (/[\u0000-\u001f\u007f]/u.test(token))
|
|
39
|
+
replaySafe = false;
|
|
40
|
+
plain += token;
|
|
41
|
+
column += widthOf(token);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (active !== undefined) {
|
|
45
|
+
replaySafe = false;
|
|
46
|
+
finish();
|
|
47
|
+
}
|
|
48
|
+
for (const match of plain.matchAll(CANDIDATE)) {
|
|
49
|
+
const from = widthOf(plain.slice(0, match.index));
|
|
50
|
+
const to = from + widthOf(match[0]);
|
|
51
|
+
if (!ranges.some(range => from < range.to && to > range.from)) {
|
|
52
|
+
ranges.push({ from, to, target: match[0], kind: "candidate" });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
ranges.sort((left, right) => left.from - right.from);
|
|
56
|
+
return {
|
|
57
|
+
ranges,
|
|
58
|
+
replaySafe,
|
|
59
|
+
width: column,
|
|
60
|
+
// Rationale: a path with spaces or a wrapped host match may extend beyond
|
|
61
|
+
// the conservative candidate. Changes anywhere in that row invalidate it.
|
|
62
|
+
signature: ranges.length === 0 ? "" : JSON.stringify([ranges, plain]),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -8,6 +8,8 @@ The executable owns platform process containment and launches one immutable `a1-
|
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
+
On Linux and macOS it creates a dedicated POSIX process group before exec, transfers foreground-terminal ownership only when a terminal is present, monitors the verified parent, applies bounded TERM/KILL cleanup to that group, and restores the prior foreground group. Linux derives process start identity from procfs. macOS derives it from the native `proc_pidinfo(PROC_PIDTBSDINFO)` start timestamp; PID liveness alone is never treated as identity.
|
|
12
|
+
|
|
11
13
|
## Artifact contract
|
|
12
14
|
|
|
13
15
|
Release builds place the exact executable at:
|
|
@@ -19,7 +21,7 @@ dist/native/darwin-x64/process-guardian
|
|
|
19
21
|
dist/native/darwin-arm64/process-guardian
|
|
20
22
|
```
|
|
21
23
|
|
|
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
|
|
24
|
+
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. The build marks a platform supported only after its native containment integration runs on that platform. A missing, altered, incompatible, unsupported, or wrong-platform artifact is a concise containment launch error; A1 never silently falls back to PID-only cleanup.
|
|
23
25
|
|
|
24
26
|
## Licensing
|
|
25
27
|
|
|
@@ -35,6 +35,8 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
|
|
|
35
35
|
|
|
36
36
|
| A1 module | Pinned Pi source | Adaptation |
|
|
37
37
|
| --- | --- | --- |
|
|
38
|
+
| `src/cli/package-messages.ts`, `src/cli/packages.ts`, and `src/integrations/pi/engine/package-integration.ts` — package/model CLI messages | Pi 0.84.2 `packages/coding-agent/src/package-manager-cli.ts` and public `DefaultPackageManager` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Preserves terminal-aware Chalk success/error/progress styling, literal model-refresh messages, operational details, and user-settings warnings. Focused syntax/help substitutes the `a1 pi` namespace and supported grammar; A1 syntax exit status remains two. Profile isolation and independent-Pi-update rejection remain intentional. Independent CLI evidence: `test/cli/package-message-parity.test.ts` runs the untouched pinned CLI presenter in an isolated process with model-runtime test substitution, not A1-generated expected strings. This does not certify the still-pending interactive message outcomes. |
|
|
39
|
+
| `pi-engine-adapter/adapter.ts`, `engine/workflows.ts`, and `pi-session-ui/session-shell.ts` — interactive command outcomes | Pi 0.84.2 `modes/interactive/interactive-mode.ts`, `core/model-resolver.ts`, and `config.ts` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Preserves route-specific status/warning/error semantics, authentication partial-success sequences, stored-credential logout wording, fork/clone empty states, import context/recovery, share URL and failure/cancellation behavior, and post-login warning lifetime. A1 deliberately returns recoverable failed workflow results instead of adopting Pi's process-owning shutdown/exit for fatal `/new`, `/resume`, and `/import` outcomes. Focused outcome evidence is in `test/integrations/pi/engine/workflows.test.ts` and `test/integrations/pi/session-ui/session-shell.test.ts`; terminal-cell geometry remains a separate milestone. |
|
|
38
40
|
| `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. |
|
|
39
41
|
| `pi-engine/session-integration.ts` and `pi-components/shell-footer-status.ts` — steering queue | pinned Pi interactive mode `onSubmit` and `updatePendingMessagesDisplay` | Steering/follow-up uses `prompt(..., { streamingBehavior })`, allowing Pi to emit the accepted user row, while remaining steering rows preserve Pi's opening spacer, dim `Steering:` labels, dequeue hint, and order before `Working`. |
|
|
40
42
|
| `ui-components/list-view.ts`, `dialog-panel.ts`, `value-menu.ts`, and `features/owned-ui/settings-app.ts` — setting presentation | pinned Pi `SettingsSelectorComponent`, Pi TUI `SettingsList`, `SelectList`, and `Input` at `0.84.2` | Cursor, selected label/value accents, unselected muted values, the 30-column label cap, dialog styling, notices, and narrow-width geometry retain pinned semantics. Scalar-menu placement and input remain shared, while A1/Agent grouping, pointer steppers, explicit `/` search, ruled shared input, shortcut-derived status hints, suppressed selected descriptions, `scrollbarSpeed`-driven wheel movement, and the dark floating menu with lighter active row and effective-value check mark are declared product-owned differences. Independent row evidence: `test/features/owned-ui/pinned-settings-presentation-parity.test.ts`; owned interaction evidence: `test/features/owned-ui/settings-app.test.ts`, `test/ui/components/value-menu.test.ts`, and `test/composition/settings-route-host.test.ts`. |
|
|
@@ -98,7 +98,7 @@ node scripts/release/report-resource-sensitive-validation.mjs --repeats 3 --outp
|
|
|
98
98
|
|
|
99
99
|
## Pull request integration
|
|
100
100
|
|
|
101
|
-
`Development validation required` remains the merge gate for every pull request.
|
|
101
|
+
`Development validation required` remains the merge gate for every pull request. For code changes, it also requires a dedicated Defender-enabled exact-package startup matrix on Windows Node 22 and 24. Each matrix lane runs the package-install startup scenarios once: a failed budget remains failed and is never retried to obtain a warmed result. Both post-update profiles must reach input-ready state within five seconds, and both warm profiles must remain within three seconds.
|
|
102
102
|
Documentation auto-merge reads the complete GitHub changed-file response and arms
|
|
103
103
|
squash auto-merge for an eligible pull request while required validation is pending;
|
|
104
104
|
branch protection prevents integration until `Development validation required`
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
| Command | Purpose | Pi user profile |
|
|
6
6
|
|---|---|---|
|
|
7
|
-
| `a1` | A1-owned Pi-compatible UI | `~/.a1/agent` |
|
|
7
|
+
| `a1` | Fresh A1-owned Pi-compatible UI session | `~/.a1/agent` |
|
|
8
|
+
| `a1 --session <path\|id>` | Resume a persisted A1 session | `~/.a1/agent` (or explicitly selected storage) |
|
|
8
9
|
| `a1 pi` | Pi-compatible comparison surface using the ordinary Pi profile | ordinary `~/.pi/agent` |
|
|
9
10
|
|
|
10
11
|
`a1 pi` is a development instrument for comparing A1 against pinned Pi. Prerelease builds — what `a1 update --develop` or `a1 update --develop <number>` installs — expose it. A release build does not recognize the launch form. Repository development can run the comparison directly with `npm start:pi`.
|
|
@@ -30,6 +31,21 @@ npm/
|
|
|
30
31
|
|
|
31
32
|
Run Pi’s normal `/login` independently in each profile that needs stored authentication. Provider credentials supplied through supported environment variables remain available to Pi, but profile authentication files never fall back to another root automatically.
|
|
32
33
|
|
|
34
|
+
## Resume a session
|
|
35
|
+
|
|
36
|
+
After a conversation has been persisted, exit A1 and run the command it prints from the original project directory:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
a1 --session <id>
|
|
40
|
+
a1 --session-dir '/path/to/session store' --session <id>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`--session` also accepts a `.jsonl` file path; `--session-dir` may come before or after it. Explicit storage overrides `PI_CODING_AGENT_SESSION_DIR`, which overrides default storage. IDs are searched in the current project before other projects within the A1 store. A cross-project ID match asks before creating a fork with a new identity; an explicit file resumes directly using its stored working directory. Missing or invalid targets fail rather than create an empty replacement.
|
|
44
|
+
|
|
45
|
+
Resume uses the shipped Pi's public restoration behavior, including the compaction format it writes. It sends no model prompt by itself and does not recover, convert, or modify an unrelated conversation. Bare `a1` remains a fresh launch. Pi picker/continue CLI aliases and `a1 pi --session` are not supported by this repair; in-UI `/resume` is unchanged.
|
|
46
|
+
|
|
47
|
+
Regression evidence creates disposable sessions through Pi 0.84.2, then executes the emitted command through the packed public entry, bootstrap, guardian, and owned UI. Default/custom storage, Windows Git Bash quoting, supervisor restart, failure cleanup, and simultaneous distinct selections are covered in `test/foundation/release/session-resume.integration.test.ts`. CI runs it in the resource-sensitive `package-smoke` scope, not the fast remainder. Unsupported synthetic retained-tail certification is separate deferred work.
|
|
48
|
+
|
|
33
49
|
## Pi comparison profile
|
|
34
50
|
|
|
35
51
|
`a1 pi` uses the same A1-owned rendering and input pipeline as bare `a1`, but withholds A1-specific screens and removes A1’s Pi configuration-root override. The pinned Pi engine therefore uses its normal `~/.pi/agent` settings, authentication, sessions, resources, packages, and trust decisions. Running the standalone `pi` command remains the independent upstream reference.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Ghost hyperlink underline baseline
|
|
2
|
+
|
|
3
|
+
## Status and scope
|
|
4
|
+
|
|
5
|
+
This is the diagnostic stage of `fix-ghost-link-underlines`, accepted in specification PR #237. It does **not** repair production rendering or claim the screenshot's root cause is proven.
|
|
6
|
+
|
|
7
|
+
Two executable regressions reproduce the code gaps: the damage adapter removes a requested full clear when the last visible link disappears; the viewport controller does not request cleanup when a hovered link changes bounds on the same row with the same target. Both were run as ordinary tests and failed at those exact assertions, with the other 22 controller/adapter tests passing. During baseline review they use Vitest's `it.fails`, not a skip: the known failure must still occur, and an unexpected pass fails the suite. The repair must remove those markers in tasks 2.2 and 3.2.
|
|
8
|
+
|
|
9
|
+
The standalone protocol fixture drives the **real A1 damage adapter** with the pinned complete-row write grammar. It does not launch A1, Pi, or an agent and does not simulate their complete rendering/input pipeline. In particular it cannot establish shell overlay, selection, or streaming acceptance. It separates emitted-byte evidence from the host's native-hover decoration so those later tests do not mistake a passing controller assertion for a fixed visual bug.
|
|
10
|
+
|
|
11
|
+
## Confirmed physical baseline
|
|
12
|
+
|
|
13
|
+
The user reports that in **mode 2 (auto-detected)**, wheel scrolling moves the hovered link upward while its underline remains under the stationary pointer briefly, then disappears. That transient trail is a failure, not successful cleanup.
|
|
14
|
+
|
|
15
|
+
The clean `f3613bb` run at 123 columns by 29 rows (`run-smbQ70/trace.jsonl`) records `ghost: true` in auto-detected mode after a downward wheel scroll at scroll position 9. The immediately preceding frame, 214, contains no explicit OSC 8 target opens, and the adapter reports `suppressed-redundant-clear`; its forwarded write has no full-screen clear. This confirms an auto-detection reproduction but does not prove that restoring the clear will fix it. The recorded one-shot `f` comparisons were in explicit mode, with no observation establishing their effect on the auto-detected case. Terminal version and the precise host detection setting remain unrecorded.
|
|
16
|
+
|
|
17
|
+
The persistent-clear comparison below avoids asking the user to press `f` before the short-lived artifact disappears. It changes the diagnostic write path only; production behavior and terminal settings remain unchanged. The user reports that **CLEAR:ON helps** for the confirmed mode-2 case. This supports preserving the complete clear in the production repair, but does not by itself prove the artifact is completely absent in the built A1 candidate.
|
|
18
|
+
|
|
19
|
+
The test host is Windows Terminal `1.24.2607.10001`, determined from the running executable after the comparison. Its settings file contains no explicit global, profile-default, or per-profile `experimental.detectURLs` override; therefore the effective host default applies. The fixture geometry was 123 by 29. Explicit-mode reproduction remains unconfirmed and is recorded as unknown rather than assumed clean.
|
|
20
|
+
|
|
21
|
+
## Run in Windows Terminal / Git Bash
|
|
22
|
+
|
|
23
|
+
From the implementation checkout, with dependencies installed:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
./scripts/pi/reproduce-ghost-link-underlines
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This shell entry preserves VT color. It uses `tsx` directly, so no application build is needed for this standalone probe. It is not a substitute for the eventual built-candidate `./scripts/dev` acceptance run.
|
|
30
|
+
|
|
31
|
+
Optional host facts can be recorded explicitly; use the actual version from Windows Terminal's About page and the actual automatic URL-detection setting, without changing either:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
./scripts/pi/reproduce-ghost-link-underlines --terminal-version YOUR_VERSION --url-detection enabled
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Missing facts are recorded as `unknown`, not inferred. Use a terminal wide enough to display the controls (192 columns by 54 rows is a useful comparison geometry).
|
|
38
|
+
|
|
39
|
+
## Persistent clear comparison for the confirmed auto-detection case
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
./scripts/pi/reproduce-ghost-link-underlines --auto --preserve-clears
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This starts directly in mode 2 with **CLEAR:ON**. Every frame, including every wheel-scroll frame, sends the requested complete synchronized clear-and-repaint directly to the terminal. It is a labelled control experiment, not an implemented A1 fix.
|
|
46
|
+
|
|
47
|
+
Hover a link and scroll with the wheel while keeping the pointer stationary. Record **y** if any underline trail remains, even if it disappears shortly afterward; record **n** only if this case stays clean. Press **p** to switch to **CLEAR:OFF**, restore the same content position with **r**, and compare the same gesture through the existing adapter. **p** persists across subsequent scrolls, mode changes, and resets. The status row always shows its state. Press **q** to exit.
|
|
48
|
+
|
|
49
|
+
Trace format 2 records `preserveClears` and actual `bypass` state separately on frames and observations. If the trail survives CLEAR:ON, merely preserving the full clear is insufficient for this reproduced host behavior; further diagnosis is required before choosing the production cleanup strategy.
|
|
50
|
+
|
|
51
|
+
## Observe and record
|
|
52
|
+
|
|
53
|
+
1. Press **1** for explicit OSC 8 links. Hover the long label, a file label, or a wrapped URL. Keep the pointer stationary and press **x** to replace the content with blank green rows. Also inspect those former cells by hovering them again.
|
|
54
|
+
2. Press **y** if a ghost underline is visible or **n** if the current case is clean. These keys record a **human** observation of the preceding action; capture a screenshot before pressing them because feedback itself repaints.
|
|
55
|
+
3. If a ghost is visible, press **f**. This sends the complete clear directly to the terminal, bypassing the optimizer for this one control experiment. Record **y** or **n** again. The trace labels bypass writes so they cannot be mistaken for production output.
|
|
56
|
+
4. Press **r** to restore the current mode. Hover the long label at its left side and press **b** to shorten/reposition it. Record the result. This visual fixture does not exercise the controller's hover-key logic; the controller regression covers that independently.
|
|
57
|
+
5. Restore with **r** and use the wheel or **j/k** to scroll while the pointer remains stationary. Scroll until all links are above the screen, then hover plain text and blank rows. Record the result, including cases that do **not** reproduce.
|
|
58
|
+
6. Press **2** for the same labels without explicit OSC 8 targets; repeat blanking, scrolling, and the forced-clear comparison. Whether these labels become links is controlled by Windows Terminal's own detection. The file-like tool rows are deliberately not wrapped in OSC 8 in either mode.
|
|
59
|
+
7. Press **q** or Ctrl+C to restore the terminal. Report the terminal version, automatic URL-detection setting, observations, and screenshot if the symptom occurs.
|
|
60
|
+
|
|
61
|
+
The fixture does not implement link activation. The host may still handle its normal hyperlink gestures; the explicit file target is this checkout's `package.json`, and web targets use the reserved `example.invalid` domain.
|
|
62
|
+
|
|
63
|
+
## Built A1 candidate
|
|
64
|
+
|
|
65
|
+
The implementation branch `fix/ghost-link-underlines` now carries the production repair. Build it, launch the color-preserving repository entry, produce output containing web URLs, file paths, and linked file labels, and test all of the following while leaving the pointer stationary over a link:
|
|
66
|
+
|
|
67
|
+
- wheel-scroll the link upward and downward;
|
|
68
|
+
- use Home/End, Shift+Up/Down, the scrollbar, and followed streaming output;
|
|
69
|
+
- resize or reflow the terminal so the link changes row or width;
|
|
70
|
+
- select linked content, edge-auto-scroll, release, and copy;
|
|
71
|
+
- let linked content leave the viewport and hover the resulting plain and blank cells.
|
|
72
|
+
|
|
73
|
+
Expected: the underline moves with current link cells and never remains under the pointer, even briefly; current links remain clickable and retain their colors; copied text is unchanged; no blank intermediate screen is visible. The implementation limits complete cleanup frames to transitions involving visible explicit or conservative host-detected link candidates. Stable dock typing and motion within one unchanged link retain bounded differential paint.
|
|
74
|
+
|
|
75
|
+
Known gaps before acceptance: the exact built candidate still needs physical Windows Terminal review; explicit-mode baseline behavior was not confirmed; required code CI has not yet been recorded; and user acceptance remains open.
|
|
76
|
+
|
|
77
|
+
## Evidence and limitations
|
|
78
|
+
|
|
79
|
+
Each run creates its own ignored `.artifacts/ghost-link-baseline/run-*/trace.jsonl`. It records checkout commit and dirty status, supplied host facts, geometry, requested writes, actual forwarded writes, adapter decisions, probe input, and explicit human observations. It captures only the synthetic fixture, not an agent conversation. Review local file targets before sharing the trace.
|
|
80
|
+
|
|
81
|
+
For noninteractive protocol evidence:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
./scripts/pi/reproduce-ghost-link-underlines --capture
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Capture mode always records `physical-result: not-observed`. Final text, closed OSC 8 sequences, and a preserved clear cannot prove that Windows Terminal discarded its hover overlay. A clean probe also does not disprove the original A1 screenshot: the complete UI pipeline still needs review.
|
|
88
|
+
|
|
89
|
+
Baseline task 1.2 remains incomplete: the mode-2 failure, host facts, and a helpful persistent-clear comparison are recorded, but explicit-mode and fully-clean control outcomes have not been confirmed. The remaining production repair and exact-candidate acceptance tasks remain open. If the preserved-clear control still ghosts, that is evidence to investigate before assuming clear preservation alone is sufficient.
|