@timurproko/a1 0.2.6-dev.717 → 0.2.6-dev.734
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/cli.js +13 -2
- package/bin/ui.js +13 -0
- package/dist/app/session-shell/clipboard-services.d.ts +19 -0
- package/dist/app/session-shell/clipboard-services.js +7 -0
- package/dist/app/session-shell/exit-notice.d.ts +9 -5
- package/dist/app/session-shell/exit-notice.js +5 -5
- package/dist/app/session-shell/index.d.ts +3 -1
- package/dist/app/session-shell/index.js +1 -0
- package/dist/app/session-shell/paste-preparation-client.d.ts +3 -0
- package/dist/app/session-shell/paste-preparation-client.js +5 -2
- package/dist/app/session-shell/path-chip-presentation.d.ts +10 -3
- package/dist/app/session-shell/path-chip-presentation.js +48 -6
- package/dist/app/session-shell/prompt-chips.js +30 -4
- package/dist/app/session-shell/prompt-suggestion-controller.d.ts +5 -4
- package/dist/app/session-shell/prompt-suggestion-controller.js +148 -58
- package/dist/app/session-shell/quit-outro.d.ts +3 -1
- package/dist/app/session-shell/quit-outro.js +23 -8
- package/dist/app/session-shell/response-copy-transport.d.ts +6 -1
- package/dist/app/session-shell/response-copy-transport.js +8 -2
- package/dist/app/session-shell/session-shell-root.d.ts +22 -11
- package/dist/app/session-shell/session-shell-root.js +19 -12
- package/dist/app/session-shell/session-shell.d.ts +19 -21
- package/dist/app/session-shell/session-shell.js +678 -840
- package/dist/app/session-shell/session-viewport-controller.js +33 -21
- package/dist/cli/dispatch.js +5 -3
- package/dist/cli/session-context.d.ts +5 -1
- package/dist/cli/session-context.js +25 -8
- package/dist/composition/owned-ui.d.ts +23 -2
- package/dist/composition/owned-ui.js +124 -33
- package/dist/composition/session-info-reference.d.ts +2 -2
- package/dist/composition/session-info-reference.js +2 -2
- package/dist/composition/settings-route-host.js +2 -1
- package/dist/contracts/agent-engine/capability-ports.d.ts +1 -85
- package/dist/contracts/agent-engine/capability-ports.js +1 -35
- package/dist/contracts/agent-engine/index.d.ts +1 -7
- package/dist/contracts/agent-engine/index.js +0 -4
- package/dist/contracts/owned-ui/index.d.ts +9 -3
- package/dist/contracts/owned-ui/index.js +2 -1
- package/dist/contracts/owned-ui/model.d.ts +10 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +119 -0
- package/dist/contracts/owned-ui/session-presenters.d.ts +55 -0
- package/dist/contracts/owned-ui/session-presenters.js +1 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +342 -0
- package/dist/contracts/owned-ui/session-workflows.js +30 -0
- package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +5 -1
- package/dist/contracts/owned-ui/suggestion-diagnostics.js +3 -1
- package/dist/contracts/owned-ui/validation.js +7 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/session-context.d.ts +17 -2
- package/dist/features/launch/session-context.js +9 -5
- package/dist/features/owned-ui/settings-app.js +56 -54
- package/dist/features/prompt-suggestions/diagnostics.js +4 -1
- package/dist/foundation/lifecycle/index.d.ts +2 -2
- package/dist/foundation/lifecycle/index.js +1 -1
- package/dist/foundation/lifecycle/session-repository-context.d.ts +19 -2
- package/dist/foundation/lifecycle/session-repository-context.js +434 -66
- package/dist/foundation/process-containment/index.d.ts +1 -0
- package/dist/foundation/process-containment/index.js +1 -0
- package/dist/foundation/process-containment/native-process-inspector.d.ts +3 -0
- package/dist/foundation/process-containment/native-process-inspector.js +21 -0
- package/dist/foundation/release/bootstrap.js +1 -2
- package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
- package/dist/foundation/release/progress-palette.generated.js +2 -2
- package/dist/foundation/release/release-gc.js +1 -1
- package/dist/foundation/release/update.js +0 -4
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/foundation/storage/control-store.js +0 -12
- package/dist/integrations/pi/components/components.js +2 -18
- package/dist/integrations/pi/components/index.d.ts +4 -4
- package/dist/integrations/pi/components/index.js +2 -2
- package/dist/integrations/pi/components/modal-frame.js +3 -4
- package/dist/integrations/pi/components/models-dialog.js +6 -35
- package/dist/integrations/pi/components/prompt-input-port.d.ts +1 -0
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
- package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
- package/dist/integrations/pi/components/share-operation-dialog.js +3 -3
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +19 -16
- package/dist/integrations/pi/components/shell-extension-ui.d.ts +5 -3
- package/dist/integrations/pi/components/shell-extension-ui.js +26 -12
- package/dist/integrations/pi/components/shell-hotkey-sections.js +2 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +10 -10
- package/dist/integrations/pi/components/shell-presenters-transcript.js +13 -13
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -6
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +21 -1
- package/dist/integrations/pi/components/shell-shared-facade.js +19 -3
- package/dist/integrations/pi/components/skills-dialog.js +3 -3
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -3
- package/dist/integrations/pi/components/theme.d.ts +9 -0
- package/dist/integrations/pi/components/theme.js +30 -0
- package/dist/integrations/pi/components/tool-result-adapter.js +6 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +6 -6
- package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -4
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +4 -4
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +5 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +8 -5
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -3
- package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +9 -9
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +3 -2
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +6 -3
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +4 -0
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +11 -1
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +27 -18
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +4 -4
- package/dist/integrations/pi/components/upstream/history/editor-core.js +11 -7
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +31 -0
- package/dist/integrations/pi/components/upstream/program-status-reporter.js +89 -0
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +22 -1
- package/dist/integrations/pi/components/upstream/theme/theme.js +225 -10
- package/dist/integrations/pi/engine/adapter.d.ts +44 -24
- package/dist/integrations/pi/engine/adapter.js +130 -22
- package/dist/integrations/pi/engine/command-dispatch.d.ts +2 -5
- package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -11
- package/dist/integrations/pi/engine/host-ports.d.ts +20 -0
- package/dist/integrations/pi/engine/host-ports.js +13 -0
- package/dist/integrations/pi/engine/host.d.ts +70 -0
- package/dist/integrations/pi/engine/host.js +147 -0
- package/dist/integrations/pi/engine/index.d.ts +8 -4
- package/dist/integrations/pi/engine/index.js +3 -1
- package/dist/integrations/pi/engine/pi-settings-metadata.json +1 -1
- package/dist/integrations/pi/engine/resource-catalog.d.ts +4 -23
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/runtime-integration.d.ts +8 -2
- package/dist/integrations/pi/engine/runtime-integration.js +12 -7
- package/dist/integrations/pi/engine/session-events.js +10 -2
- package/dist/integrations/pi/engine/session-integration.d.ts +3 -8
- package/dist/integrations/pi/engine/session-integration.js +0 -49
- package/dist/integrations/pi/engine/session-runtime.d.ts +18 -3
- package/dist/integrations/pi/engine/session-runtime.js +18 -7
- package/dist/integrations/pi/engine/settings-bridge.d.ts +2 -2
- package/dist/integrations/pi/engine/settings-bridge.js +0 -2
- package/dist/integrations/pi/engine/settings-port.d.ts +2 -0
- package/dist/integrations/pi/engine/settings-port.js +3 -5
- package/dist/integrations/pi/engine/transcript-projection.js +2 -1
- package/dist/integrations/pi/engine/workflows.d.ts +33 -260
- package/dist/integrations/pi/engine/workflows.js +6 -28
- package/dist/integrations/pi/session-presenters/index.d.ts +2 -0
- package/dist/integrations/pi/session-presenters/index.js +1 -0
- package/dist/integrations/pi/session-presenters/presenters.d.ts +16 -0
- package/dist/integrations/pi/session-presenters/presenters.js +341 -0
- package/dist/integrations/pi/startup-public.js +306 -290
- package/dist/integrations/pi/startup-public.manifest.json +289 -265
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -1
- package/dist/integrations/pi/tui-runtime/adapter.js +7 -2
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +3 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +8 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +67 -2
- package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/index.js +1 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +2 -2
- package/dist/product-identity.js +2 -2
- package/dist/product-identity.json +3 -1
- package/dist/runtime-payload-inventory.json +3092 -2878
- package/dist/ui/components/prompt-input.d.ts +2 -0
- package/dist/ui/components/prompt-input.js +5 -1
- package/dist/ui/components/text-selection.d.ts +1 -1
- package/dist/ui/components/text-selection.js +3 -1
- package/dist/ui/components/theme.d.ts +2 -0
- package/dist/ui/components/transcript-viewport.d.ts +6 -4
- package/dist/ui/components/transcript-viewport.js +94 -16
- package/dist/ui/components/value-menu.d.ts +2 -0
- package/dist/ui/components/value-menu.js +13 -8
- package/dist/ui/settings/declarations.d.ts +38 -14
- package/dist/ui/settings/declarations.js +35 -11
- package/dist/ui/settings/migrations.js +23 -0
- package/docs/architecture/boundaries.md +9 -1
- package/docs/architecture/project-structure.md +7 -5
- package/docs/architecture/prompt-suggestions.md +14 -10
- package/docs/local-worktree-cleanup.md +11 -5
- package/docs/openspec-archive-automation.md +6 -5
- package/package.json +3 -3
- package/dist/contracts/agent-engine/model.d.ts +0 -78
- package/dist/contracts/agent-engine/model.js +0 -1
- package/dist/contracts/agent-engine/ports.d.ts +0 -18
- package/dist/contracts/agent-engine/serialization.d.ts +0 -7
- package/dist/contracts/agent-engine/serialization.js +0 -58
- package/dist/contracts/agent-engine/validation.d.ts +0 -5
- package/dist/contracts/agent-engine/validation.js +0 -128
- package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +0 -61
- package/dist/integrations/pi/components/upstream/theme/system-theme.js +0 -709
- /package/dist/contracts/{agent-engine/ports.js → owned-ui/session-backend.js} +0 -0
|
@@ -10,6 +10,8 @@ export interface PromptInputBody {
|
|
|
10
10
|
readonly topRule?: string | undefined;
|
|
11
11
|
readonly bottomRule?: string | undefined;
|
|
12
12
|
readonly after?: readonly string[];
|
|
13
|
+
/** Outer indentation for after-body rows; defaults to the prompt prefix width. */
|
|
14
|
+
readonly afterIndent?: number;
|
|
13
15
|
}
|
|
14
16
|
export interface PromptInputMetrics {
|
|
15
17
|
readonly measure: (text: string) => number;
|
|
@@ -31,6 +31,10 @@ export class PromptInput {
|
|
|
31
31
|
return [];
|
|
32
32
|
const { prefixWidth, innerWidth } = this.geometry(width, padding, promptGlyph);
|
|
33
33
|
const body = renderBody(innerWidth);
|
|
34
|
+
const requestedAfterIndent = body.afterIndent ?? prefixWidth;
|
|
35
|
+
const afterIndent = Number.isFinite(requestedAfterIndent)
|
|
36
|
+
? Math.min(width, Math.max(0, Math.floor(requestedAfterIndent)))
|
|
37
|
+
: prefixWidth;
|
|
34
38
|
const fit = (row) => {
|
|
35
39
|
const clipped = this.metrics.truncate(row, width);
|
|
36
40
|
return clipped + " ".repeat(Math.max(0, width - this.metrics.measure(clipped)));
|
|
@@ -45,7 +49,7 @@ export class PromptInput {
|
|
|
45
49
|
...(ruled ? [rule(body.topRule)] : []),
|
|
46
50
|
...rows,
|
|
47
51
|
...(ruled ? [rule(body.bottomRule)] : []),
|
|
48
|
-
...(body.after ?? []).map(row => fit(`${" ".repeat(
|
|
52
|
+
...(body.after ?? []).map(row => fit(`${" ".repeat(afterIndent)}${row}`)),
|
|
49
53
|
];
|
|
50
54
|
}
|
|
51
55
|
}
|
|
@@ -56,7 +56,7 @@ export declare function releaseTextSelection(selection: TextSelection | undefine
|
|
|
56
56
|
export declare function textSelectionLineExtendColumn(selection: TextSelection, line: number, contentWidth: number): number;
|
|
57
57
|
/** Resolves a reported one-based pointer cell to the complete grapheme it intersects. */
|
|
58
58
|
export declare function textSelectionPointAt(line: number, column: number, lineText: string): TextSelectionPoint;
|
|
59
|
-
export declare function textSelectionText(selection: OrderedTextSelection, rows: readonly string[], lineContent?: (line: number) => TextSelectionLineContent | undefined): string;
|
|
59
|
+
export declare function textSelectionText(selection: OrderedTextSelection, rows: readonly string[], lineContent?: (line: number) => TextSelectionLineContent | undefined, includeLine?: (line: number) => boolean): string;
|
|
60
60
|
/** Returns the plain cells covered by [from,to), expanding at grapheme edges. */
|
|
61
61
|
export declare function plainTextBetweenColumns(line: string, from: number, to: number): string;
|
|
62
62
|
export declare function usefulTextLineContent(line: string, from?: number): TextSelectionLineContent;
|
|
@@ -111,9 +111,11 @@ export function textSelectionPointAt(line, column, lineText) {
|
|
|
111
111
|
}
|
|
112
112
|
return { line, column, from: target, to: target + 1 };
|
|
113
113
|
}
|
|
114
|
-
export function textSelectionText(selection, rows, lineContent) {
|
|
114
|
+
export function textSelectionText(selection, rows, lineContent, includeLine = () => true) {
|
|
115
115
|
const parts = [];
|
|
116
116
|
for (let line = selection.start.line; line <= selection.end.line && line < rows.length; line += 1) {
|
|
117
|
+
if (!includeLine(line))
|
|
118
|
+
continue;
|
|
117
119
|
const plain = stripAnsi(rows[line] ?? "");
|
|
118
120
|
const content = lineContent?.(line);
|
|
119
121
|
if (content?.selectable === false) {
|
|
@@ -8,6 +8,8 @@ export interface UiTheme {
|
|
|
8
8
|
* into a dark terminal, which is how the engine leaves its lists too.
|
|
9
9
|
*/
|
|
10
10
|
plain(text: string): string;
|
|
11
|
+
/** Optional palette preview used by controls that choose the active accent. */
|
|
12
|
+
accentPreview?(color: string, text: string): string | null;
|
|
11
13
|
/** Selection background for an active list item or floating-surface row. */
|
|
12
14
|
highlight(text: string): string;
|
|
13
15
|
/** A control that is present but cannot act: quieter than quiet text. */
|
|
@@ -25,7 +25,7 @@ export interface TranscriptViewportFrameInput {
|
|
|
25
25
|
readonly documentRows: readonly string[];
|
|
26
26
|
/** Optional paint-only transform; semantic selection and copying keep documentRows exact. */
|
|
27
27
|
readonly paintDocumentRow?: (row: string) => string;
|
|
28
|
-
/** Leading document rows
|
|
28
|
+
/** Leading persistent document rows; later transient rows remain selectable visible-frame content. */
|
|
29
29
|
readonly selectableDocumentRowCount?: number;
|
|
30
30
|
/**
|
|
31
31
|
* First document row owned by the block that is currently streaming, if any. Rows from
|
|
@@ -84,7 +84,7 @@ export interface TranscriptViewportHitRegions {
|
|
|
84
84
|
readonly columnStart: number;
|
|
85
85
|
readonly columnEnd: number;
|
|
86
86
|
} | null;
|
|
87
|
-
/** Transient non-
|
|
87
|
+
/** Transient non-persistent document-tail rows currently visible in the viewport. */
|
|
88
88
|
readonly transientTail: readonly number[];
|
|
89
89
|
}
|
|
90
90
|
export interface TranscriptViewportFrameDescriptor {
|
|
@@ -109,7 +109,7 @@ export interface TranscriptViewportFrameDescriptor {
|
|
|
109
109
|
};
|
|
110
110
|
readonly previousFollowingEnd: boolean | null;
|
|
111
111
|
readonly followingEnd: boolean;
|
|
112
|
-
/** Complete non-
|
|
112
|
+
/** Complete non-persistent suffix, including pending steering, alignment, and status rows. */
|
|
113
113
|
readonly transientRowCount: number;
|
|
114
114
|
/** Flexible rows inserted before the bottom-aligned live status while content fits. */
|
|
115
115
|
readonly transientAlignmentGapRows: number;
|
|
@@ -164,7 +164,9 @@ export declare class TranscriptViewport {
|
|
|
164
164
|
get hasSelection(): boolean;
|
|
165
165
|
/** True when the active gesture began on transcript rows rather than the dock. */
|
|
166
166
|
get selectionFromContent(): boolean;
|
|
167
|
-
pressSelection(column: number, frameRow: number, now?: number
|
|
167
|
+
pressSelection(column: number, frameRow: number, now?: number, options?: {
|
|
168
|
+
readonly continueMultiClick?: boolean;
|
|
169
|
+
}): boolean;
|
|
168
170
|
extendSelection(column: number, frameRow: number, now?: number, autoScroll?: boolean): boolean;
|
|
169
171
|
releaseSelection(): boolean;
|
|
170
172
|
clearSelection(): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MAX_COPY_SOURCE_UNITS } from "./selection-copy.js";
|
|
1
|
+
import { MAX_COPY_SOURCE_UNITS, selectionCopyLineContent } from "./selection-copy.js";
|
|
2
2
|
import { isThumbRow, scrollbarGeometry, scrollbarPresentation, } from "./scrollbar.js";
|
|
3
3
|
import { backgroundSgrSpan, overlaySpan } from "./spans.js";
|
|
4
4
|
import { faint, truncateToWidth, displayWidth, stripAnsi } from "./text.js";
|
|
@@ -8,8 +8,13 @@ const CONTROL_STYLE_RESET = "\u001b]8;;\u001b\\\u001b[0m";
|
|
|
8
8
|
// the row surface painted beneath its dedicated, non-semantic gutter.
|
|
9
9
|
const GUTTER_DECORATION_RESET = "\u001b]8;;\u001b\\\u001b[22;23;24;25;27;28;29;39;54;55m";
|
|
10
10
|
const IDENTITY_ROW = (row) => row;
|
|
11
|
-
function documentSelectionAnchor(row, source) {
|
|
12
|
-
return { kind: "document", row, source, identity: stripAnsi(source).trimEnd() };
|
|
11
|
+
function documentSelectionAnchor(row, source, liveFromBottom) {
|
|
12
|
+
return { kind: "document", row, source, identity: stripAnsi(source).trimEnd(), liveFromBottom };
|
|
13
|
+
}
|
|
14
|
+
function viewportSelectionAnchor(row, source, selectableDocumentRowCount, documentRowCount) {
|
|
15
|
+
return row < selectableDocumentRowCount
|
|
16
|
+
? documentSelectionAnchor(row, source)
|
|
17
|
+
: { kind: "transient", fromBottom: documentRowCount - row - 1 };
|
|
13
18
|
}
|
|
14
19
|
const DEFAULT_CONFIG = {
|
|
15
20
|
scrollbarAppearance: "auto",
|
|
@@ -53,6 +58,7 @@ export class TranscriptViewport {
|
|
|
53
58
|
#previousVisibleStates = [];
|
|
54
59
|
#documentRows = [];
|
|
55
60
|
#selectableDocumentRowCount = 0;
|
|
61
|
+
#liveTailStartRow;
|
|
56
62
|
#promptAnchors = [];
|
|
57
63
|
#contentWidth = 0;
|
|
58
64
|
#selectionWidth = 0;
|
|
@@ -91,7 +97,7 @@ export class TranscriptViewport {
|
|
|
91
97
|
get hasSelection() { return orderedTextSelection(this.#selection) !== undefined; }
|
|
92
98
|
/** True when the active gesture began on transcript rows rather than the dock. */
|
|
93
99
|
get selectionFromContent() { return this.#selectionAnchors !== undefined && this.#selectionAnchors.anchor.kind !== "dock"; }
|
|
94
|
-
pressSelection(column, frameRow, now = Date.now()) {
|
|
100
|
+
pressSelection(column, frameRow, now = Date.now(), options = {}) {
|
|
95
101
|
const frameHeight = this.#frame?.rows.length ?? 0;
|
|
96
102
|
const selectionWidth = frameRow <= (this.#frame?.hits.viewportHeight ?? 0)
|
|
97
103
|
? this.#contentWidth
|
|
@@ -105,7 +111,7 @@ export class TranscriptViewport {
|
|
|
105
111
|
contentWidth: selectionWidth,
|
|
106
112
|
lineText: this.#selectionRows[line] ?? "",
|
|
107
113
|
lineContent: usefulTextLineContent(this.#selectionRows[line] ?? ""),
|
|
108
|
-
...(this.#selectionClick
|
|
114
|
+
...(options.continueMultiClick !== false && this.#selectionClick !== undefined ? { previousClick: this.#selectionClick } : {}),
|
|
109
115
|
now,
|
|
110
116
|
});
|
|
111
117
|
this.#selection = pressed.selection;
|
|
@@ -176,7 +182,29 @@ export class TranscriptViewport {
|
|
|
176
182
|
const selection = this.#visibleSelection();
|
|
177
183
|
if (selection === undefined)
|
|
178
184
|
return null;
|
|
179
|
-
|
|
185
|
+
// Invariant: the blue range spans the complete frame, but automatic and Ctrl+C frame copy
|
|
186
|
+
// admit only persistent transcript rows. Transient status, suggestions, input, and footer
|
|
187
|
+
// chrome remain visually selectable without becoming clipboard payload.
|
|
188
|
+
const copiedLines = Array.from({ length: Math.max(0, selection.end.line - selection.start.line + 1) }, (_value, index) => selection.start.line + index).filter(line => {
|
|
189
|
+
const anchor = this.#selectionRowAnchors[line];
|
|
190
|
+
return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount;
|
|
191
|
+
});
|
|
192
|
+
if (copiedLines.length === 0)
|
|
193
|
+
return null;
|
|
194
|
+
// Rationale: a prompt selected by itself uses semantic prompt text. Once surrounding transcript text
|
|
195
|
+
// participates, preserve the prompt's visible prefix, alignment, and timestamp in the bulk range.
|
|
196
|
+
const prompt = this.#promptAnchors.find(candidate => copiedLines.every(line => {
|
|
197
|
+
const anchor = this.#selectionRowAnchors[line];
|
|
198
|
+
return anchor?.kind === "document" && anchor.row >= candidate.firstRow && anchor.row <= candidate.lastRow;
|
|
199
|
+
}));
|
|
200
|
+
const included = new Set(copiedLines);
|
|
201
|
+
const text = textSelectionText(selection, this.#selectionRows, line => {
|
|
202
|
+
const row = this.#selectionRows[line] ?? "";
|
|
203
|
+
const anchor = this.#selectionRowAnchors[line];
|
|
204
|
+
return prompt !== undefined && anchor?.kind === "document"
|
|
205
|
+
? selectionCopyLineContent(row, anchor.row === prompt.firstRow ? "first" : "continuation")
|
|
206
|
+
: usefulTextLineContent(row);
|
|
207
|
+
}, line => included.has(line));
|
|
180
208
|
return text.length === 0 ? null : text;
|
|
181
209
|
}
|
|
182
210
|
/** Captures visible selected text, not off-screen history. */
|
|
@@ -188,6 +216,8 @@ export class TranscriptViewport {
|
|
|
188
216
|
if (text === null)
|
|
189
217
|
return null;
|
|
190
218
|
text = text.trim();
|
|
219
|
+
if (text.length === 0)
|
|
220
|
+
return null;
|
|
191
221
|
const normalized = {
|
|
192
222
|
start: { line: 0, column: 0 },
|
|
193
223
|
end: { line: 0, column: Number.MAX_SAFE_INTEGER },
|
|
@@ -280,6 +310,7 @@ export class TranscriptViewport {
|
|
|
280
310
|
this.#previousVisibleStates = [];
|
|
281
311
|
this.#documentRows = [];
|
|
282
312
|
this.#selectableDocumentRowCount = 0;
|
|
313
|
+
this.#liveTailStartRow = undefined;
|
|
283
314
|
this.#promptAnchors = [];
|
|
284
315
|
this.#contentWidth = 0;
|
|
285
316
|
this.#selectionWidth = 0;
|
|
@@ -340,6 +371,9 @@ export class TranscriptViewport {
|
|
|
340
371
|
const contentWidth = presentation.reservesSpace ? Math.max(1, width - 1) : width;
|
|
341
372
|
this.#documentRows = documentRows;
|
|
342
373
|
this.#selectableDocumentRowCount = clamp(input.selectableDocumentRowCount ?? input.documentRows.length, 0, input.documentRows.length);
|
|
374
|
+
this.#liveTailStartRow = input.liveTailStartRow === undefined || !Number.isSafeInteger(input.liveTailStartRow)
|
|
375
|
+
? undefined
|
|
376
|
+
: clamp(input.liveTailStartRow, 0, this.#selectableDocumentRowCount);
|
|
343
377
|
this.#promptAnchors = input.promptAnchors;
|
|
344
378
|
this.#contentWidth = contentWidth;
|
|
345
379
|
// Invariant: dock rows remain selectable at full width; scrollable source rows
|
|
@@ -389,7 +423,7 @@ export class TranscriptViewport {
|
|
|
389
423
|
const visibleAnchors = Array.from({ length: viewportHeight }, (_value, row) => {
|
|
390
424
|
const documentRow = this.#scrollTop + row;
|
|
391
425
|
return documentRow < documentRows.length
|
|
392
|
-
?
|
|
426
|
+
? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, documentRows.length)
|
|
393
427
|
: { kind: "screen", row, source: this.#selectionRows[row] ?? "" };
|
|
394
428
|
});
|
|
395
429
|
// Invariant: a pinned prompt is chrome over its hidden source row, which clips like off-screen rows.
|
|
@@ -486,7 +520,7 @@ export class TranscriptViewport {
|
|
|
486
520
|
// Invariant: floating control styling is applied only after the row and
|
|
487
521
|
// gutter surfaces are resolved, so its inline background cannot leak.
|
|
488
522
|
const withControl = rowControl === null ? withRail : overlaySpan(withRail, rowControl.left, rowControl.right, rowControl.text);
|
|
489
|
-
return overlayWidth === 0 ? withControl : overlaySpan(padRowPreservingBackground(withControl, width), overlayLimit - overlayWidth, overlayLimit, `${
|
|
523
|
+
return overlayWidth === 0 ? withControl : overlaySpan(padRowPreservingBackground(withControl, width), overlayLimit - overlayWidth, overlayLimit, `${GUTTER_DECORATION_RESET}${overlayText}`, { inheritStartStyle: true });
|
|
490
524
|
});
|
|
491
525
|
rowRecomputed ||= !final.reused;
|
|
492
526
|
frameRows[row] = final.value;
|
|
@@ -643,17 +677,30 @@ export class TranscriptViewport {
|
|
|
643
677
|
return undefined;
|
|
644
678
|
if (line < this.#viewportHeight) {
|
|
645
679
|
const existing = this.#selectionRowAnchors[line];
|
|
646
|
-
if (this.#frame?.scrollTop === this.#scrollTop)
|
|
647
|
-
return existing
|
|
680
|
+
if (this.#frame?.scrollTop === this.#scrollTop) {
|
|
681
|
+
return existing?.kind === "document"
|
|
682
|
+
? this.#interactionDocumentAnchor(existing.row, existing.source)
|
|
683
|
+
: existing;
|
|
684
|
+
}
|
|
648
685
|
// Concurrency: selection edge auto-scroll updates scrollTop before the next frame is
|
|
649
686
|
// composed. Resolve ordinary viewport rows from that latest position instead of stale paint.
|
|
650
687
|
const documentRow = this.#scrollTop + line;
|
|
651
688
|
if (documentRow >= 0 && documentRow < this.#documentRows.length) {
|
|
652
|
-
return
|
|
689
|
+
return documentRow < this.#selectableDocumentRowCount
|
|
690
|
+
? this.#interactionDocumentAnchor(documentRow, this.#documentRows[documentRow] ?? "")
|
|
691
|
+
: viewportSelectionAnchor(documentRow, this.#documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, this.#documentRows.length);
|
|
653
692
|
}
|
|
654
693
|
}
|
|
655
694
|
return this.#selectionRowAnchors[line];
|
|
656
695
|
}
|
|
696
|
+
#interactionDocumentAnchor(row, source) {
|
|
697
|
+
const liveFromBottom = this.#liveTailStartRow !== undefined
|
|
698
|
+
&& row >= this.#liveTailStartRow
|
|
699
|
+
&& row < this.#selectableDocumentRowCount
|
|
700
|
+
? this.#selectableDocumentRowCount - row - 1
|
|
701
|
+
: undefined;
|
|
702
|
+
return documentSelectionAnchor(row, source, liveFromBottom);
|
|
703
|
+
}
|
|
657
704
|
#projectSelection(documentRows, dockRows, height) {
|
|
658
705
|
const selection = this.#selection;
|
|
659
706
|
const anchors = this.#selectionAnchors;
|
|
@@ -672,9 +719,16 @@ export class TranscriptViewport {
|
|
|
672
719
|
? undefined
|
|
673
720
|
: { line: anchor.row, anchor };
|
|
674
721
|
}
|
|
722
|
+
if (anchor.kind === "transient") {
|
|
723
|
+
const documentRow = documentRows.length - anchor.fromBottom - 1;
|
|
724
|
+
if (documentRow < this.#selectableDocumentRowCount || documentRow >= documentRows.length)
|
|
725
|
+
return undefined;
|
|
726
|
+
const visible = this.#selectionRowAnchors.findIndex(candidate => candidate?.kind === "transient" && candidate.fromBottom === anchor.fromBottom);
|
|
727
|
+
return visible < 0 ? undefined : { line: visible, anchor };
|
|
728
|
+
}
|
|
675
729
|
if (anchor.row >= 0 && anchor.row < documentRows.length
|
|
676
730
|
&& stripAnsi(documentRows[anchor.row] ?? "").trimEnd() === anchor.identity) {
|
|
677
|
-
const current = documentSelectionAnchor(anchor.row, documentRows[anchor.row] ?? "");
|
|
731
|
+
const current = documentSelectionAnchor(anchor.row, documentRows[anchor.row] ?? "", anchor.liveFromBottom);
|
|
678
732
|
const visible = this.#selectionRowAnchors.findIndex(candidate => candidate?.kind === "document"
|
|
679
733
|
&& candidate.row === current.row && candidate.identity === current.identity);
|
|
680
734
|
return { line: visible >= 0 ? visible : current.row - this.#scrollTop, anchor: current };
|
|
@@ -684,9 +738,29 @@ export class TranscriptViewport {
|
|
|
684
738
|
const visibleMatches = this.#selectionRowAnchors
|
|
685
739
|
.map((candidate, line) => ({ candidate, line }))
|
|
686
740
|
.filter((entry) => entry.candidate?.kind === "document" && entry.candidate.identity === anchor.identity);
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
741
|
+
if (visibleMatches.length === 1) {
|
|
742
|
+
const match = visibleMatches[0];
|
|
743
|
+
return {
|
|
744
|
+
line: match.line,
|
|
745
|
+
anchor: documentSelectionAnchor(match.candidate.row, match.candidate.source, anchor.liveFromBottom),
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
// Concurrency: a streaming chunk may replace or reflow the pointer's current live-tail
|
|
749
|
+
// row before the next motion report. During the held gesture only, retain its distance
|
|
750
|
+
// from the persistent tail; release restores identity-only projection.
|
|
751
|
+
if (selection.selecting && anchor.liveFromBottom !== undefined && this.#liveTailStartRow !== undefined) {
|
|
752
|
+
const documentRow = this.#selectableDocumentRowCount - anchor.liveFromBottom - 1;
|
|
753
|
+
if (documentRow >= this.#liveTailStartRow && documentRow < this.#selectableDocumentRowCount) {
|
|
754
|
+
const visible = this.#selectionRowAnchors.findIndex(candidate => candidate?.kind === "document" && candidate.row === documentRow);
|
|
755
|
+
if (visible >= 0) {
|
|
756
|
+
return {
|
|
757
|
+
line: visible,
|
|
758
|
+
anchor: documentSelectionAnchor(documentRow, documentRows[documentRow] ?? "", anchor.liveFromBottom),
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return undefined;
|
|
690
764
|
};
|
|
691
765
|
const projectedAnchor = project(anchors.anchor);
|
|
692
766
|
const projectedHead = project(anchors.head);
|
|
@@ -704,7 +778,11 @@ export class TranscriptViewport {
|
|
|
704
778
|
};
|
|
705
779
|
}
|
|
706
780
|
#visibleSelection() {
|
|
707
|
-
const
|
|
781
|
+
const anchors = this.#selectionAnchors;
|
|
782
|
+
const crossesDock = anchors?.anchor.kind === "dock" || anchors?.head.kind === "dock";
|
|
783
|
+
// Invariant: mixed viewport/dock ranges project across the same complete frame regardless
|
|
784
|
+
// of origin. Document-only ranges remain clipped to the scrollable viewport when off-screen.
|
|
785
|
+
const rows = crossesDock ? this.#selectionRows.length : this.#viewportHeight;
|
|
708
786
|
return visibleTextSelection(orderedTextSelection(this.#selection), rows, this.#firstSelectableRow);
|
|
709
787
|
}
|
|
710
788
|
#updateCopyableSelection() {
|
|
@@ -7,6 +7,8 @@ import type { UiTheme } from "./theme.js";
|
|
|
7
7
|
export interface ValueMenuState {
|
|
8
8
|
/** Values offered, as they should read. */
|
|
9
9
|
readonly choices: readonly string[];
|
|
10
|
+
/** Optional already-painted square previews, aligned before each choice label. */
|
|
11
|
+
readonly previews?: readonly string[];
|
|
10
12
|
/** The value in effect, marked rather than highlighted. */
|
|
11
13
|
readonly current: string | null;
|
|
12
14
|
/** The entry picked by a key or the pointer, or -1 while none is. */
|
|
@@ -8,10 +8,12 @@ export function valueMenuFrame(state, anchor, layout) {
|
|
|
8
8
|
const top = below + state.choices.length <= bodyBottom
|
|
9
9
|
? below
|
|
10
10
|
: Math.max(bodyTop, anchor.screenRow - state.choices.length);
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const previewWidth = Math.max(0, ...(state.previews ?? []).map(displayWidth));
|
|
12
|
+
const previewColumns = previewWidth === 0 ? 0 : previewWidth + 1;
|
|
13
|
+
const width = Math.max(...state.choices.map(choice => displayWidth(choice) + previewColumns + 4), 6);
|
|
14
|
+
// Invariant: rows reserve two cells for the effective-value mark and, when present,
|
|
15
|
+
// a preview plus one gap. Choice text remains aligned with the source value column.
|
|
16
|
+
const column = Math.min(Math.max(0, anchor.valueColumn - 2 - previewColumns), Math.max(0, layout.surfaceWidth - width - layout.reservedRight));
|
|
15
17
|
return { top, column, width, rows: state.choices.length };
|
|
16
18
|
}
|
|
17
19
|
/** Draws the menu over the rows behind it, leaving them otherwise untouched. */
|
|
@@ -24,11 +26,14 @@ export function renderValueMenu(lines, state, frame, theme) {
|
|
|
24
26
|
const active = index === state.index;
|
|
25
27
|
const paint = active ? theme.highlight : theme.panel;
|
|
26
28
|
const current = choice === state.current;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const previews = state.previews ?? [];
|
|
30
|
+
const previewWidth = Math.max(0, ...previews.map(displayWidth));
|
|
31
|
+
const preview = previewWidth === 0 ? "" : `${padToWidth(previews[index] ?? "", previewWidth)} `;
|
|
32
|
+
// Invariant: the current-value check stays on ordinary text while the optional
|
|
33
|
+
// square carries the palette color; the row treatment supplies only its surface.
|
|
34
|
+
const tail = padToWidth(`${current ? " " : " "}${preview}${choice} `, frame.width - (current ? 1 : 0));
|
|
30
35
|
const painted = current
|
|
31
|
-
? `${paint(theme.fg("
|
|
36
|
+
? `${paint(theme.fg("text", "✓"))}${paint(tail)}`
|
|
32
37
|
: paint(tail);
|
|
33
38
|
output[target] = overlaySpan(output[target] ?? "", frame.column, frame.column + frame.width, painted);
|
|
34
39
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 13;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
@@ -20,18 +20,6 @@ export interface OwnedUiSettingDeclaration {
|
|
|
20
20
|
* values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
|
|
21
21
|
*/
|
|
22
22
|
export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
23
|
-
readonly quitAnimation: Readonly<{
|
|
24
|
-
id: "quitAnimation";
|
|
25
|
-
label: "Quit animation";
|
|
26
|
-
section: Readonly<{
|
|
27
|
-
id: "generic";
|
|
28
|
-
title: "Generic";
|
|
29
|
-
}>;
|
|
30
|
-
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.";
|
|
31
|
-
application: "live";
|
|
32
|
-
defaultValue: true;
|
|
33
|
-
allowedValues: readonly [true, false];
|
|
34
|
-
}>;
|
|
35
23
|
readonly updateCheck: Readonly<{
|
|
36
24
|
id: "updateCheck";
|
|
37
25
|
label: "Update check";
|
|
@@ -44,6 +32,42 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
44
32
|
defaultValue: true;
|
|
45
33
|
allowedValues: readonly [true, false];
|
|
46
34
|
}>;
|
|
35
|
+
readonly accentColor: Readonly<{
|
|
36
|
+
id: "accentColor";
|
|
37
|
+
label: "Accent color";
|
|
38
|
+
section: Readonly<{
|
|
39
|
+
id: "appearance";
|
|
40
|
+
title: "Appearance";
|
|
41
|
+
}>;
|
|
42
|
+
description: "Color used by highlighted interface controls, list markers, and tonal interface surfaces.";
|
|
43
|
+
application: "live";
|
|
44
|
+
defaultValue: "purple";
|
|
45
|
+
allowedValues: readonly ["purple", "blue", "cyan", "green", "orange", "pink"];
|
|
46
|
+
}>;
|
|
47
|
+
readonly backgroundStyle: Readonly<{
|
|
48
|
+
id: "backgroundStyle";
|
|
49
|
+
label: "Background";
|
|
50
|
+
section: Readonly<{
|
|
51
|
+
id: "appearance";
|
|
52
|
+
title: "Appearance";
|
|
53
|
+
}>;
|
|
54
|
+
description: "Fullscreen canvas background: terminal transparency, a dark accent tint, or fixed neutral dark.";
|
|
55
|
+
application: "live";
|
|
56
|
+
defaultValue: "transparent";
|
|
57
|
+
allowedValues: readonly ["transparent", "accent", "dark"];
|
|
58
|
+
}>;
|
|
59
|
+
readonly quitAnimation: Readonly<{
|
|
60
|
+
id: "quitAnimation";
|
|
61
|
+
label: "Quit animation";
|
|
62
|
+
section: Readonly<{
|
|
63
|
+
id: "appearance";
|
|
64
|
+
title: "Appearance";
|
|
65
|
+
}>;
|
|
66
|
+
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.";
|
|
67
|
+
application: "live";
|
|
68
|
+
defaultValue: true;
|
|
69
|
+
allowedValues: readonly [true, false];
|
|
70
|
+
}>;
|
|
47
71
|
readonly scrollbarAppearance: Readonly<{
|
|
48
72
|
id: "scrollbarAppearance";
|
|
49
73
|
label: "Scrollbar mode";
|
|
@@ -111,7 +135,7 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
111
135
|
id: "agent";
|
|
112
136
|
title: "Agent";
|
|
113
137
|
}>;
|
|
114
|
-
description: "Predict likely next prompts with one
|
|
138
|
+
description: "Predict likely next prompts with one background request, plus one bounded recovery request when needed, using the selected model.";
|
|
115
139
|
application: "live";
|
|
116
140
|
defaultValue: true;
|
|
117
141
|
allowedValues: readonly [true, false];
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 13;
|
|
2
|
+
const UI_ACCENT_COLOR_CHOICES = Object.freeze([
|
|
3
|
+
"purple", "blue", "cyan", "green", "orange", "pink",
|
|
4
|
+
]);
|
|
2
5
|
const MAX_ID_LENGTH = 64;
|
|
3
6
|
const ID_PATTERN = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
4
7
|
/** Declared first so the settings screen opens on it; sections follow first-declaration order. */
|
|
5
8
|
const GENERIC_SECTION = Object.freeze({ id: "generic", title: "Generic" });
|
|
9
|
+
const APPEARANCE_SECTION = Object.freeze({ id: "appearance", title: "Appearance" });
|
|
6
10
|
const SCROLL_SECTION = Object.freeze({ id: "scroll", title: "Scroll" });
|
|
7
11
|
const AGENT_SECTION = Object.freeze({ id: "agent", title: "Agent" });
|
|
8
12
|
/**
|
|
@@ -10,15 +14,6 @@ const AGENT_SECTION = Object.freeze({ id: "agent", title: "Agent" });
|
|
|
10
14
|
* values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
|
|
11
15
|
*/
|
|
12
16
|
export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
13
|
-
quitAnimation: Object.freeze({
|
|
14
|
-
id: "quitAnimation",
|
|
15
|
-
label: "Quit animation",
|
|
16
|
-
section: GENERIC_SECTION,
|
|
17
|
-
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.",
|
|
18
|
-
application: "live",
|
|
19
|
-
defaultValue: true,
|
|
20
|
-
allowedValues: Object.freeze([true, false]),
|
|
21
|
-
}),
|
|
22
17
|
updateCheck: Object.freeze({
|
|
23
18
|
id: "updateCheck",
|
|
24
19
|
label: "Update check",
|
|
@@ -28,6 +23,35 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
28
23
|
defaultValue: true,
|
|
29
24
|
allowedValues: Object.freeze([true, false]),
|
|
30
25
|
}),
|
|
26
|
+
accentColor: Object.freeze({
|
|
27
|
+
id: "accentColor",
|
|
28
|
+
label: "Accent color",
|
|
29
|
+
section: APPEARANCE_SECTION,
|
|
30
|
+
description: "Color used by highlighted interface controls, list markers, and tonal interface surfaces.",
|
|
31
|
+
application: "live",
|
|
32
|
+
defaultValue: "purple",
|
|
33
|
+
allowedValues: UI_ACCENT_COLOR_CHOICES,
|
|
34
|
+
}),
|
|
35
|
+
backgroundStyle: Object.freeze({
|
|
36
|
+
id: "backgroundStyle",
|
|
37
|
+
label: "Background",
|
|
38
|
+
section: APPEARANCE_SECTION,
|
|
39
|
+
description: "Fullscreen canvas background: terminal transparency, a dark accent tint, or fixed neutral dark.",
|
|
40
|
+
application: "live",
|
|
41
|
+
defaultValue: "transparent",
|
|
42
|
+
allowedValues: Object.freeze([
|
|
43
|
+
"transparent", "accent", "dark",
|
|
44
|
+
]),
|
|
45
|
+
}),
|
|
46
|
+
quitAnimation: Object.freeze({
|
|
47
|
+
id: "quitAnimation",
|
|
48
|
+
label: "Quit animation",
|
|
49
|
+
section: APPEARANCE_SECTION,
|
|
50
|
+
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.",
|
|
51
|
+
application: "live",
|
|
52
|
+
defaultValue: true,
|
|
53
|
+
allowedValues: Object.freeze([true, false]),
|
|
54
|
+
}),
|
|
31
55
|
scrollbarAppearance: Object.freeze({
|
|
32
56
|
id: "scrollbarAppearance",
|
|
33
57
|
label: "Scrollbar mode",
|
|
@@ -77,7 +101,7 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
77
101
|
id: "promptSuggestions",
|
|
78
102
|
label: "Prompt suggestions",
|
|
79
103
|
section: AGENT_SECTION,
|
|
80
|
-
description: "Predict likely next prompts with one
|
|
104
|
+
description: "Predict likely next prompts with one background request, plus one bounded recovery request when needed, using the selected model.",
|
|
81
105
|
application: "live",
|
|
82
106
|
defaultValue: true,
|
|
83
107
|
allowedValues: Object.freeze([true, false]),
|
|
@@ -81,6 +81,29 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
81
81
|
return { ...values };
|
|
82
82
|
},
|
|
83
83
|
}),
|
|
84
|
+
Object.freeze({
|
|
85
|
+
to: 11,
|
|
86
|
+
description: "Introduce semantic accent selection with the active-theme default.",
|
|
87
|
+
migrate(values) {
|
|
88
|
+
return { ...values };
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
Object.freeze({
|
|
92
|
+
to: 12,
|
|
93
|
+
description: "Rename the inherited default accent choice to explicit purple.",
|
|
94
|
+
migrate(values) {
|
|
95
|
+
return values.accentColor === "default"
|
|
96
|
+
? { ...values, accentColor: "purple" }
|
|
97
|
+
: { ...values };
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
Object.freeze({
|
|
101
|
+
to: 13,
|
|
102
|
+
description: "Introduce the transparent fullscreen background default.",
|
|
103
|
+
migrate(values) {
|
|
104
|
+
return { ...values };
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
84
107
|
]);
|
|
85
108
|
export function assertOwnedUiSettingsMigrations(migrations, currentVersion = OWNED_UI_SETTINGS_VERSION) {
|
|
86
109
|
const firstProduced = currentVersion - migrations.length + 1;
|
|
@@ -30,7 +30,7 @@ The owned Pi-backed surface is not an arbitrary-CLI terminal multiplexer. A feat
|
|
|
30
30
|
- `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).
|
|
31
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
32
|
- `supervisor`: endpoint identity, plural cohort ownership, per-instance reconciliation, and aggregate release shutdown coordination. It owns no terminal surface.
|
|
33
|
-
- `pi-engine-adapter`, `pi-component-adapter`, and `pi-tui-runtime-adapter`: isolate pinned Pi engine and presentation knowledge behind neutral contracts; product features do not import them directly.
|
|
33
|
+
- `pi-engine-adapter`, `pi-component-adapter`, and `pi-tui-runtime-adapter`: isolate pinned Pi engine and presentation knowledge behind neutral contracts; product features do not import them directly. Inside the engine adapter, one process-level engine host owns what is process-wide (the HTTP dispatcher, the theme singletons, the one-time changelog and package-update announcements, the startup trace) and is the only place sessions are created, so every session id is unique per process.
|
|
34
34
|
- `session-shell` (`src/app/session-shell`): the application layer that composes the feature owners, the UI foundations, and the three Pi adapters into the interactive session; only composition imports it. Its 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 session shell.
|
|
35
35
|
- release/update/bootstrap: package-derived immutable release identity, process-guardian integrity, cohort selection, durable update transactions, rollback, and dependency-light command entry. After an unsuccessful bare-A1 child exit, the bootstrap restores owned terminal modes; neither guardian emits terminal controls.
|
|
36
36
|
- `terminal-cleanup`: bounded, idempotent emergency restoration and fatal-exit diagnostics for an explicitly owned terminal. It does not inspect commands, relay terminal input/output, or emulate terminal state. The owned UI installs its fatal boundary before terminal activation; packaged bootstrap and checkout launch provide the surviving-owner fallback. Fatal records retain at most ten 16 KiB records with trusted classifications and code locations, never prompts, attachments, raw input, credentials, or arbitrary exception messages.
|
|
@@ -43,6 +43,14 @@ The JavaScript product path has no PTY or terminal-emulator dependency. Reintrod
|
|
|
43
43
|
|
|
44
44
|
`scripts/governance/check-architecture.mjs` enforces the structural parts of these boundaries. Cross-cutting rationale belongs here; implementation history belongs in Git and archived OpenSpec changes.
|
|
45
45
|
|
|
46
|
+
## Multi-session limits of the pinned engine
|
|
47
|
+
|
|
48
|
+
The engine host removes A1's own obstacles to several Pi sessions in one process, but three remain in the pinned Pi SDK and must be planned around, not discovered:
|
|
49
|
+
|
|
50
|
+
- The extension cache is keyed by one working directory and cleared on reload (`core/extensions/loader.js`), so sessions in different directories reload each other's extensions.
|
|
51
|
+
- `SettingsManager` writes under a lockfile but never re-reads the file, so a second in-memory copy goes stale after another session writes a setting (`core/settings-manager.js`). The host keeps the one process-wide effect, the HTTP dispatcher, consistent itself.
|
|
52
|
+
- `AgentSessionRuntime.dispose()` releases the session but not the model runtime or the resource loader it was created with (`core/agent-session-runtime.js`), so a session's services outlive it until the process ends.
|
|
53
|
+
|
|
46
54
|
## Planned multi-agent boundaries
|
|
47
55
|
|
|
48
56
|
The multi-agent workspace feature, its dependency-free contracts, the structured-agent runtime, and the Node side of the terminal-host protocol were removed from `develop` while the multi-agent plan is on hold; their last state is preserved on the `archive/multi-agent-workspace` branch. A future implementation starts from the current codebase rather than from that copy. The boundary rules below remain the design intent for that work.
|
|
@@ -18,13 +18,14 @@ src/
|
|
|
18
18
|
prompt-history/ profile-isolated prompt retention and bounded SQLite worker lifecycle
|
|
19
19
|
prompt-suggestions/ opt-in bounded metadata capture and local diagnostic snapshots
|
|
20
20
|
contracts/
|
|
21
|
-
agent-engine/ dependency-free agent
|
|
21
|
+
agent-engine/ dependency-free agent settings and package contracts
|
|
22
22
|
owned-ui/ dependency-free owned-session and extension UI contracts
|
|
23
23
|
presentation/ dependency-free component, terminal, and runtime ports
|
|
24
24
|
integrations/
|
|
25
25
|
pi/
|
|
26
26
|
components/ pinned Pi component and theme adaptation
|
|
27
27
|
engine/ pinned Pi engine, settings, resource, package, and workflow integration
|
|
28
|
+
session-presenters/ pinned Pi selectors and transcript renderers built from engine objects
|
|
28
29
|
tui-runtime/ neutral presentation runtime over pinned Pi TUI
|
|
29
30
|
foundation/
|
|
30
31
|
launch-guardian/ authenticated launch-instance coordination
|
|
@@ -42,7 +43,7 @@ src/
|
|
|
42
43
|
|
|
43
44
|
Each directory owner exposes `index.ts`, which lists its named exports; `export *` is rejected there so a public contract is readable in one place. Imports within one owner may use private files. Imports crossing owners must use the provider's public entry and follow the dependency DAG declared by `PROJECT_OWNERS`. Two exceptions exist: `src/composition` is the dependency-injection root and may reach past any public entry, and a module on the eager startup path may import a leaf of a provider whose public entry is a prohibited startup entry (`PROHIBITED_STARTUP_ENTRIES` in `startup-graph-policy.mjs`), because loading that barrel would pull the provider's whole graph into startup. `product-identity` is the sole exception to the directory-entry convention because its public entry is `src/product-identity.ts`.
|
|
44
45
|
|
|
45
|
-
Three layer boundaries hold regardless of the DAG: `src/contracts/*` import nothing outside their own contract; `src/ui/components` imports only contracts; only `src/integrations/pi/*` and the shipped `bin/` entries import the pinned Pi packages. The session shell under `src/app` is the application layer: it may import the contracts, the UI foundations, the three Pi adapters, and the feature owners, and only composition imports it.
|
|
46
|
+
Three layer boundaries hold regardless of the DAG: `src/contracts/*` import nothing outside their own contract; `src/ui/components` imports only contracts; only `src/integrations/pi/*` and the shipped `bin/` entries import the pinned Pi packages. The session shell under `src/app` is the application layer: it may import the contracts, the UI foundations, the three Pi adapters, and the feature owners, and only composition imports it. The shell never holds a Pi engine object: selectors and transcript renderers built from one live in `src/integrations/pi/session-presenters`, the only owner besides composition that may import both the engine and the component adapters, and the shell opens them through the `OwnedUiSessionPresenters` contract.
|
|
46
47
|
|
|
47
48
|
`src/cli` contains command policy but delegates runtime work. `src/composition` is the concrete dependency-injection boundary: it may know both neutral contracts and Pi implementations, while product features receive vendor-neutral ports. Foundation modules never import product features. Pi package knowledge remains inside the Pi adapter owners.
|
|
48
49
|
|
|
@@ -92,18 +93,19 @@ Build output mirrors the production namespaces directly under ignored `dist/`, w
|
|
|
92
93
|
|
|
93
94
|
Every task worktree must be created at `{working-dir}/.worktrees/<task-id>`, where `{working-dir}` is the agent session's initial working directory. The `.worktrees` directory is inside that working directory, not beside it. For working directory `D:/Git/a1`, `D:/Git/a1/.worktrees/<task-id>` is correct and `D:/Git/a1-<task-id>` is forbidden.
|
|
94
95
|
|
|
95
|
-
From the initial working directory,
|
|
96
|
+
From the initial working directory, run `a1 session worktrees` to inspect cooperative live ownership before selecting an existing checkout. A `busy` or `unverifiable` worktree is unavailable. An `available` checkout may be reused only when its exact branch, OpenSpec change, and pull request match the requested stream and atomic `a1 session link-worktree <absolute-worktree>` succeeds; names, similar files, recency, cleanliness, and ancestry are not ownership evidence. Otherwise create a fresh task branch/worktree, link it from the owning A1 session, and continue to address it explicitly:
|
|
96
97
|
|
|
97
98
|
```sh
|
|
99
|
+
a1 session worktrees
|
|
98
100
|
git fetch origin develop
|
|
99
101
|
git worktree add -b <type>/<short-description> .worktrees/<task-id> origin/develop
|
|
100
102
|
a1 session link-worktree <absolute-worktree>
|
|
101
103
|
git -C .worktrees/<task-id> status
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
A successful link response confirming the exact canonical worktree is required before planning, implementation, test, or delivery-documentation edits. If linking fails or confirms another path, stop task edits and report the blocker
|
|
106
|
+
Inventory is read-only and advisory because another runtime may win a later race. A successful link response confirming the exact canonical worktree is required before planning, implementation, test, or delivery-documentation edits. If inventory or linking fails, reports a live/unverifiable owner, loses a race, or confirms another path, stop task edits and report the blocker; use a separate fresh worktree rather than overriding, unlinking, or recovering that owner. A session resuming an existing delivery or switching streams atomically claims and links the exact worktree before editing it.
|
|
105
107
|
|
|
106
|
-
The
|
|
108
|
+
The link acquires a cooperative live-session editing claim and switches bare A1's footer repository context and pull-request discovery. It does not change process or tool cwd, create a Git worktree lock, or register, release, or authorize cleanup. Clean unlink/disposal releases live editing authority; a crashed owner becomes reusable only after exact process death is verified. Repository commands therefore keep an explicit worktree path. The primary worktree stays on `develop` for integration and must not be used for task edits. Do not edit, adopt, move, or remove another session's worktree.
|
|
107
109
|
|
|
108
110
|
An agent that directly chooses a transient file path for a PR/comment body, command payload, captured output, temporary patch/diff, or ad hoc log must place it beneath the linked worktree's exact `.artifacts/` root, never the primary checkout, another worktree, OS temp, home/desktop, or a sibling path. Such scratch remains ignored, unstaged, uncommitted, disposable, non-authoritative, and secret-free. This repository path rule does not relocate storage internally selected by tools, product runtime code, or hermetic tests.
|
|
109
111
|
|