@timurproko/a1 0.1.8-dev.290 → 0.1.8-dev.322
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/bin/guardian.js +4 -3
- package/bin/ui.js +4 -2
- package/bin/update-recovery.js +3 -1
- package/dist/composition/owned-ui.js +13 -1
- package/dist/contracts/owned-ui/index.d.ts +1 -0
- package/dist/contracts/owned-ui/index.js +1 -0
- package/dist/contracts/owned-ui/prompt-history.d.ts +37 -0
- package/dist/contracts/owned-ui/prompt-history.js +23 -0
- package/dist/features/launch/profile-paths.d.ts +2 -0
- package/dist/features/launch/profile-paths.js +11 -0
- package/dist/features/prompt-history/index.d.ts +2 -0
- package/dist/features/prompt-history/index.js +2 -0
- package/dist/features/prompt-history/paths.d.ts +4 -0
- package/dist/features/prompt-history/paths.js +16 -0
- package/dist/features/prompt-history/service.d.ts +24 -0
- package/dist/features/prompt-history/service.js +260 -0
- package/dist/features/prompt-history/store.d.ts +15 -0
- package/dist/features/prompt-history/store.js +166 -0
- package/dist/features/prompt-history/worker.d.ts +9 -0
- package/dist/features/prompt-history/worker.js +42 -0
- package/dist/foundation/launch-context/index.d.ts +29 -0
- package/dist/foundation/launch-context/index.js +83 -0
- package/dist/foundation/launch-guardian/main.js +4 -3
- package/dist/foundation/release/bootstrap.d.ts +1 -1
- package/dist/foundation/release/bootstrap.js +18 -12
- package/dist/foundation/release/cohort-state.d.ts +1 -0
- package/dist/foundation/release/cohort-state.js +4 -0
- package/dist/foundation/release/release-gc.js +2 -2
- package/dist/foundation/release/release-store.js +10 -3
- package/dist/foundation/release/release.d.ts +3 -1
- package/dist/foundation/release/release.js +7 -3
- package/dist/foundation/release/restart-certification.d.ts +1 -0
- package/dist/foundation/release/restart-certification.js +5 -0
- package/dist/foundation/release/update-recovery.d.ts +2 -0
- package/dist/foundation/release/update-recovery.js +4 -0
- package/dist/foundation/release/update.js +5 -0
- package/dist/foundation/release/warmup.d.ts +1 -1
- package/dist/foundation/release/warmup.js +5 -4
- package/dist/foundation/startup/startup-runtime.js +13 -11
- package/dist/foundation/supervision/main.js +8 -7
- package/dist/integrations/pi/components/editor-interaction.d.ts +38 -0
- package/dist/integrations/pi/components/editor-interaction.js +1 -0
- package/dist/integrations/pi/components/history-editor-loader.d.ts +5 -0
- package/dist/integrations/pi/components/history-editor-loader.js +5 -0
- package/dist/integrations/pi/components/index.d.ts +1 -0
- package/dist/integrations/pi/components/index.js +1 -0
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +95 -22
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +12 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +9 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +35 -4
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +3 -2
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +8 -5
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +15 -2
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +12 -0
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +25 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +13 -9
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +137 -133
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +280 -0
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2062 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +32 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +48 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.js +40 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.d.ts +14 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +28 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +22 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +36 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.d.ts +24 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +100 -0
- package/dist/integrations/pi/engine/adapter.d.ts +13 -1
- package/dist/integrations/pi/engine/adapter.js +86 -25
- package/dist/integrations/pi/engine/changelog.d.ts +2 -0
- package/dist/integrations/pi/engine/changelog.js +63 -0
- package/dist/integrations/pi/engine/resources/changelog.json +12 -0
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +1 -1
- package/dist/integrations/pi/engine/workflows.d.ts +1 -1
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +4 -1
- package/dist/integrations/pi/session-ui/prompt-chips.js +40 -13
- package/dist/integrations/pi/session-ui/prompt-history-controller.d.ts +30 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.js +147 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +33 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +68 -74
- package/dist/integrations/pi/session-ui/session-shell.d.ts +1 -1
- package/dist/integrations/pi/session-ui/session-shell.js +98 -12
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +21 -15
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +7 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +7 -6
- 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 +3 -36
- package/dist/product-identity.json +1 -22
- package/dist/ui/components/transcript-viewport.d.ts +8 -0
- package/dist/ui/components/transcript-viewport.js +23 -2
- package/dist/ui/components/visible-hyperlinks.d.ts +7 -0
- package/dist/ui/components/visible-hyperlinks.js +1 -0
- package/dist/ui/settings/declarations.js +19 -0
- package/dist/ui/settings/sections.js +11 -1
- package/dist/ui/settings/session.js +13 -4
- package/docs/architecture/boundaries.md +2 -0
- package/docs/architecture/history-editor-provenance.md +96 -0
- package/docs/architecture/internal-naming.md +89 -0
- package/docs/architecture/project-structure.md +1 -0
- package/docs/architecture/resource-and-data-policy.md +27 -1
- package/docs/architecture/toolchain.md +15 -1
- package/docs/features/prompt-history.md +149 -0
- package/package.json +8 -3
|
@@ -17,5 +17,5 @@ export declare class PiWorkflowControllerPort implements AgentWorkflowPort {
|
|
|
17
17
|
listWorkflows(): Promise<readonly AgentWorkflowDescriptor[]>;
|
|
18
18
|
executeWorkflow(workflowId: string, input: AgentJsonValue, signal?: AbortSignal): Promise<AgentJsonValue>;
|
|
19
19
|
}
|
|
20
|
-
export declare const PI_BUILTIN_WORKFLOW_ROUTES: readonly ("name" | "share" | "model" | "login" | "logout" | "settings" | "reload" | "compact" | "new" | "resume" | "quit" | "session" | "scoped-models" | "export" | "import" | "copy" | "changelog" | "hotkeys" | "fork" | "clone" | "
|
|
20
|
+
export declare const PI_BUILTIN_WORKFLOW_ROUTES: readonly ("name" | "share" | "model" | "login" | "logout" | "settings" | "reload" | "compact" | "new" | "resume" | "quit" | "session" | "tree" | "scoped-models" | "export" | "import" | "copy" | "changelog" | "hotkeys" | "fork" | "clone" | "trust" | "debug" | "arminsayshi" | "dementedelves")[];
|
|
21
21
|
export declare const PI_WORKFLOW_CAPABILITIES: readonly WorkflowCapability[];
|
|
@@ -178,7 +178,7 @@ export interface PiWorkflowHost {
|
|
|
178
178
|
readonly stdout: string;
|
|
179
179
|
readonly stderr: string;
|
|
180
180
|
}>;
|
|
181
|
-
readChangelog(): Promise<string>;
|
|
181
|
+
readChangelog(sinceVersion?: string): Promise<string>;
|
|
182
182
|
}
|
|
183
183
|
export interface PiBashWorkflowResult {
|
|
184
184
|
readonly command: string;
|
|
@@ -11,7 +11,7 @@ export interface PreparedPrompt {
|
|
|
11
11
|
/** Owns semantic chips and bounded pending paste references; cancels background work on reset or disposal. */
|
|
12
12
|
export declare class PromptChipStore {
|
|
13
13
|
#private;
|
|
14
|
-
beginPaste(currentText: string, read: (signal: AbortSignal) => Promise<PiShellClipboardContent | null>, onError: (error: unknown) => void): {
|
|
14
|
+
beginPaste(currentText: string, read: (signal: AbortSignal) => Promise<PiShellClipboardContent | null>, onError: (error: unknown) => void, onImage?: () => void): {
|
|
15
15
|
marker: string;
|
|
16
16
|
result: Promise<string>;
|
|
17
17
|
};
|
|
@@ -21,6 +21,8 @@ export declare class PromptChipStore {
|
|
|
21
21
|
resetPastes(text: string): string;
|
|
22
22
|
dispose(): Promise<void>;
|
|
23
23
|
transformPastedContent(content: PiShellClipboardContent, currentText?: string): string;
|
|
24
|
+
/** Hide provisional clipboard identities until the read identifies an actual image. */
|
|
25
|
+
hiddenRanges(line: string): readonly PiShellEditorTextRange[];
|
|
24
26
|
atomicRanges(line: string): readonly PiShellEditorTextRange[];
|
|
25
27
|
hyperlinkRanges(text: string): readonly {
|
|
26
28
|
start: number;
|
|
@@ -28,5 +30,6 @@ export declare class PromptChipStore {
|
|
|
28
30
|
target: string;
|
|
29
31
|
}[];
|
|
30
32
|
expandCopiedText(text: string): string;
|
|
33
|
+
prepareHistoryText(text: string): string;
|
|
31
34
|
prepareSubmission(text: string): PreparedPrompt;
|
|
32
35
|
}
|
|
@@ -15,27 +15,32 @@ export class PromptChipStore {
|
|
|
15
15
|
#pending = new Map();
|
|
16
16
|
#preparation = new ImagePreparationClient();
|
|
17
17
|
#stopping = new Set();
|
|
18
|
-
beginPaste(currentText, read, onError) {
|
|
19
|
-
const
|
|
18
|
+
beginPaste(currentText, read, onError, onImage = () => { }) {
|
|
19
|
+
const id = randomBytes(5).toString("hex");
|
|
20
|
+
// Invariant: reserve paste identity immediately, but only display a screenshot after identifying an image.
|
|
21
|
+
const marker = `[📷 screenshot-${id}]`;
|
|
20
22
|
const job = this.#preparation.start(async (signal) => {
|
|
21
23
|
const content = await read(signal);
|
|
22
24
|
entry.kind = content?.kind === "image" ? "image" : "text";
|
|
23
|
-
if (content?.kind === "image"
|
|
24
|
-
|
|
25
|
+
if (content?.kind === "image") {
|
|
26
|
+
onImage();
|
|
27
|
+
if (this.#imageCount(currentText) >= 8)
|
|
28
|
+
throw new ImageAttachmentError("image-count");
|
|
29
|
+
}
|
|
25
30
|
return content;
|
|
26
31
|
});
|
|
27
32
|
const entry = { marker, job, references: 0, kind: "unknown", completion: job.result.then(content => {
|
|
28
33
|
if (content === null)
|
|
29
34
|
return "";
|
|
30
35
|
if (content.kind === "image") {
|
|
31
|
-
return this.#addPreparedImage(content);
|
|
36
|
+
return this.#addPreparedImage(content, id);
|
|
32
37
|
}
|
|
33
38
|
return this.transformPastedContent(content);
|
|
34
39
|
}).catch(error => {
|
|
35
40
|
entry.error = error instanceof ImageAttachmentError ? error : new ImageAttachmentError("image-codec");
|
|
36
41
|
if (entry.error.code !== "image-canceled" && entry.references === 0)
|
|
37
42
|
onError(entry.error);
|
|
38
|
-
return marker.replace("
|
|
43
|
+
return marker.replace("screenshot-", "failed-");
|
|
39
44
|
}).then(replacement => {
|
|
40
45
|
entry.replacement = replacement;
|
|
41
46
|
return replacement;
|
|
@@ -95,12 +100,17 @@ export class PromptChipStore {
|
|
|
95
100
|
}
|
|
96
101
|
async dispose() { await Promise.all([this.#preparation.dispose(), ...this.#stopping]); }
|
|
97
102
|
#imageCount(text) {
|
|
98
|
-
const
|
|
99
|
-
|
|
103
|
+
const tags = new Set([...this.#chips.values()].filter(chip => chip.kind === "image" && text.includes(chip.tag)).map(chip => chip.tag));
|
|
104
|
+
for (const entry of this.#pending.values()) {
|
|
105
|
+
if (entry.kind !== "text" && (text.includes(entry.marker) || text.includes(entry.marker.replace("screenshot-", "failed-")))) {
|
|
106
|
+
// Invariant: a ready screenshot can retain its pending label but still occupies only one slot.
|
|
107
|
+
tags.add(entry.replacement ?? entry.marker);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return tags.size;
|
|
100
111
|
}
|
|
101
|
-
#addPreparedImage(image) {
|
|
102
|
-
const
|
|
103
|
-
const tag = `[📷 screenshot-${randomBytes(5).toString("hex")}${image.transformed ? "-resized" : ""}.${suffix}]`;
|
|
112
|
+
#addPreparedImage(image, id) {
|
|
113
|
+
const tag = `[📷 screenshot-${id}${image.transformed ? "-resized" : ""}]`;
|
|
104
114
|
const attachment = Object.freeze({ type: "image", data: image.data, mimeType: image.mimeType });
|
|
105
115
|
assertPromptImages([attachment]);
|
|
106
116
|
this.#chips.set(tag, { kind: "image", tag, image: attachment });
|
|
@@ -114,7 +124,7 @@ export class PromptChipStore {
|
|
|
114
124
|
return "";
|
|
115
125
|
assertPromptImages([...this.prepareSubmission(currentText).images, { type: "image", ...image }]);
|
|
116
126
|
const id = randomBytes(5).toString("hex");
|
|
117
|
-
const tag = `[📷 screenshot-${id}
|
|
127
|
+
const tag = `[📷 screenshot-${id}]`;
|
|
118
128
|
this.#chips.set(tag, {
|
|
119
129
|
kind: "image",
|
|
120
130
|
tag,
|
|
@@ -142,6 +152,20 @@ export class PromptChipStore {
|
|
|
142
152
|
return this.#recordUnique({ kind: "file", tag: `[${icon} ${label}]`, path: item.fullPath });
|
|
143
153
|
}).join("");
|
|
144
154
|
}
|
|
155
|
+
/** Hide provisional clipboard identities until the read identifies an actual image. */
|
|
156
|
+
hiddenRanges(line) {
|
|
157
|
+
const ranges = [];
|
|
158
|
+
for (const entry of this.#pending.values()) {
|
|
159
|
+
if (entry.kind === "image")
|
|
160
|
+
continue;
|
|
161
|
+
let from = 0;
|
|
162
|
+
while ((from = line.indexOf(entry.marker, from)) >= 0) {
|
|
163
|
+
ranges.push({ start: from, end: from + entry.marker.length });
|
|
164
|
+
from += entry.marker.length;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return ranges.sort((left, right) => left.start - right.start);
|
|
168
|
+
}
|
|
145
169
|
atomicRanges(line) {
|
|
146
170
|
return [...line.matchAll(CHIP_PATTERN)].map(match => ({
|
|
147
171
|
start: match.index,
|
|
@@ -171,6 +195,9 @@ export class PromptChipStore {
|
|
|
171
195
|
expandCopiedText(text) {
|
|
172
196
|
return this.#replaceResolvable(text, false).text;
|
|
173
197
|
}
|
|
198
|
+
prepareHistoryText(text) {
|
|
199
|
+
return this.#replaceResolvable(text, false).text.replace(CHIP_PATTERN, tag => this.#chips.get(tag)?.kind === "image" ? "" : tag).trim();
|
|
200
|
+
}
|
|
174
201
|
prepareSubmission(text) {
|
|
175
202
|
const expanded = this.#replaceResolvable(text, true);
|
|
176
203
|
return { text: expanded.text, images: expanded.images };
|
|
@@ -178,7 +205,7 @@ export class PromptChipStore {
|
|
|
178
205
|
#replaceResolvable(text, includeImages) {
|
|
179
206
|
let expanded = text;
|
|
180
207
|
for (const entry of this.#pending.values()) {
|
|
181
|
-
if (!expanded.includes(entry.marker) && !expanded.includes(entry.marker.replace("
|
|
208
|
+
if (!expanded.includes(entry.marker) && !expanded.includes(entry.marker.replace("screenshot-", "failed-")))
|
|
182
209
|
continue;
|
|
183
210
|
if (includeImages && entry.error !== undefined)
|
|
184
211
|
throw entry.error;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type PromptHistoryKind, type PromptHistoryPort } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
import type { PiShellEditorPort } from "../components/index.js";
|
|
3
|
+
/** Merges bounded recall snapshots; the editor alone owns browsing, draft, cursor, and undo state. */
|
|
4
|
+
export declare class PromptHistoryController {
|
|
5
|
+
#private;
|
|
6
|
+
readonly options: {
|
|
7
|
+
editor: PiShellEditorPort;
|
|
8
|
+
store: PromptHistoryPort;
|
|
9
|
+
limit: number;
|
|
10
|
+
fallback: readonly string[];
|
|
11
|
+
active(): boolean;
|
|
12
|
+
render(): void;
|
|
13
|
+
failure(message: string): void;
|
|
14
|
+
};
|
|
15
|
+
constructor(options: {
|
|
16
|
+
editor: PiShellEditorPort;
|
|
17
|
+
store: PromptHistoryPort;
|
|
18
|
+
limit: number;
|
|
19
|
+
fallback: readonly string[];
|
|
20
|
+
active(): boolean;
|
|
21
|
+
render(): void;
|
|
22
|
+
failure(message: string): void;
|
|
23
|
+
});
|
|
24
|
+
start(): void;
|
|
25
|
+
capture(text: string, kind: PromptHistoryKind, cwd: string, sessionId: string): void;
|
|
26
|
+
rememberRecovery(text: string): void;
|
|
27
|
+
synchronize(): void;
|
|
28
|
+
reset(fallback: readonly string[]): void;
|
|
29
|
+
close(): Promise<boolean>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { PROMPT_HISTORY_MAX_TEXT_BYTES } from "../../../contracts/owned-ui/index.js";
|
|
3
|
+
/** Merges bounded recall snapshots; the editor alone owns browsing, draft, cursor, and undo state. */
|
|
4
|
+
export class PromptHistoryController {
|
|
5
|
+
options;
|
|
6
|
+
#local = new Map();
|
|
7
|
+
#unsubscribe;
|
|
8
|
+
#snapshot;
|
|
9
|
+
#fallback;
|
|
10
|
+
#disposed = false;
|
|
11
|
+
#generation = 0;
|
|
12
|
+
#unsubscribeSnapshot;
|
|
13
|
+
#unsubscribeFailure;
|
|
14
|
+
#closeFailed = false;
|
|
15
|
+
#closePromise;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
if (options.editor.recall === undefined)
|
|
19
|
+
throw new Error("The selected editor does not expose typed history");
|
|
20
|
+
this.#snapshot = { revision: 0, limit: options.limit, entries: [] };
|
|
21
|
+
this.#fallback = boundedTexts([...options.fallback].reverse(), options.limit);
|
|
22
|
+
this.#unsubscribeSnapshot = this.#subscribeSnapshot();
|
|
23
|
+
this.#unsubscribeFailure = options.store.onFailure(code => {
|
|
24
|
+
if (this.#disposed)
|
|
25
|
+
this.#closeFailed = true;
|
|
26
|
+
else
|
|
27
|
+
options.failure(`Prompt history ${code}; current-session recall remains available.`);
|
|
28
|
+
});
|
|
29
|
+
this.#unsubscribe = [
|
|
30
|
+
options.editor.recall.observe(() => {
|
|
31
|
+
if (this.#disposed)
|
|
32
|
+
return;
|
|
33
|
+
if (options.editor.recall.position().index === -1)
|
|
34
|
+
this.synchronize();
|
|
35
|
+
options.store.refresh();
|
|
36
|
+
}),
|
|
37
|
+
];
|
|
38
|
+
this.synchronize();
|
|
39
|
+
}
|
|
40
|
+
start() { this.options.store.start(); }
|
|
41
|
+
capture(text, kind, cwd, sessionId) {
|
|
42
|
+
const canonical = text.trim();
|
|
43
|
+
if (!canonical || this.#disposed)
|
|
44
|
+
return;
|
|
45
|
+
const id = randomUUID();
|
|
46
|
+
const generation = this.#generation;
|
|
47
|
+
this.#local.set(id, { text: canonical, committed: false });
|
|
48
|
+
this.#boundLocal();
|
|
49
|
+
this.synchronize();
|
|
50
|
+
void this.options.store.record({ id, text: canonical, timestamp: Date.now(), kind, cwd, sessionId }).then(outcome => {
|
|
51
|
+
if (this.#disposed || generation !== this.#generation)
|
|
52
|
+
return;
|
|
53
|
+
const local = this.#local.get(id);
|
|
54
|
+
if (local === undefined || outcome !== "committed")
|
|
55
|
+
return;
|
|
56
|
+
local.committed = true;
|
|
57
|
+
if (this.#snapshot.entries.some(entry => entry.submissionId === id))
|
|
58
|
+
this.#local.delete(id);
|
|
59
|
+
this.synchronize();
|
|
60
|
+
}).catch(() => {
|
|
61
|
+
if (!this.#disposed)
|
|
62
|
+
this.options.failure("Prompt history unavailable; current-session recall remains available.");
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
rememberRecovery(text) {
|
|
66
|
+
if (this.#disposed || !text.trim())
|
|
67
|
+
return;
|
|
68
|
+
const canonical = text.trim();
|
|
69
|
+
if ([...this.#local.values()].some(item => item.text === canonical)
|
|
70
|
+
|| this.#snapshot.entries.some(item => item.text === canonical))
|
|
71
|
+
return;
|
|
72
|
+
this.#local.set(randomUUID(), { text: canonical, committed: false });
|
|
73
|
+
this.#boundLocal();
|
|
74
|
+
this.synchronize();
|
|
75
|
+
}
|
|
76
|
+
synchronize() {
|
|
77
|
+
if (this.#disposed || !this.options.active())
|
|
78
|
+
return;
|
|
79
|
+
const values = [...this.#local.values()].reverse().map(item => item.text)
|
|
80
|
+
.concat(this.#snapshot.entries.map(item => item.text), this.#fallback);
|
|
81
|
+
this.options.editor.recall.replace(boundedTexts(values, this.#snapshot.limit));
|
|
82
|
+
this.options.render();
|
|
83
|
+
}
|
|
84
|
+
reset(fallback) {
|
|
85
|
+
this.#generation++;
|
|
86
|
+
this.#unsubscribeSnapshot();
|
|
87
|
+
this.#unsubscribeSnapshot = this.#subscribeSnapshot();
|
|
88
|
+
this.#fallback = boundedTexts([...fallback].reverse(), this.options.limit);
|
|
89
|
+
this.#local.clear();
|
|
90
|
+
this.options.editor.recall.reset();
|
|
91
|
+
this.synchronize();
|
|
92
|
+
this.options.store.refresh();
|
|
93
|
+
}
|
|
94
|
+
close() {
|
|
95
|
+
if (this.#closePromise !== undefined)
|
|
96
|
+
return this.#closePromise;
|
|
97
|
+
this.#disposed = true;
|
|
98
|
+
this.#unsubscribeSnapshot();
|
|
99
|
+
for (const unsubscribe of this.#unsubscribe)
|
|
100
|
+
unsubscribe();
|
|
101
|
+
this.#local.clear();
|
|
102
|
+
this.#fallback = [];
|
|
103
|
+
this.#closePromise = this.options.store.close().then(() => !this.#closeFailed, () => false)
|
|
104
|
+
.finally(() => this.#unsubscribeFailure());
|
|
105
|
+
return this.#closePromise;
|
|
106
|
+
}
|
|
107
|
+
#subscribeSnapshot() {
|
|
108
|
+
const generation = this.#generation;
|
|
109
|
+
return this.options.store.onSnapshot(snapshot => {
|
|
110
|
+
if (this.#disposed || generation !== this.#generation || snapshot.revision < this.#snapshot.revision)
|
|
111
|
+
return;
|
|
112
|
+
this.#snapshot = snapshot;
|
|
113
|
+
for (const [id, entry] of this.#local) {
|
|
114
|
+
if (entry.committed)
|
|
115
|
+
this.#local.delete(id);
|
|
116
|
+
}
|
|
117
|
+
this.synchronize();
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
#boundLocal() {
|
|
121
|
+
let bytes = [...this.#local.values()].reduce((sum, item) => sum + Buffer.byteLength(item.text), 0);
|
|
122
|
+
while (this.#local.size > this.options.limit || bytes > PROMPT_HISTORY_MAX_TEXT_BYTES) {
|
|
123
|
+
const id = this.#local.keys().next().value;
|
|
124
|
+
bytes -= Buffer.byteLength(this.#local.get(id).text);
|
|
125
|
+
this.#local.delete(id);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function boundedTexts(values, limit) {
|
|
130
|
+
const result = [];
|
|
131
|
+
const seen = new Set();
|
|
132
|
+
let bytes = 0;
|
|
133
|
+
for (const value of values) {
|
|
134
|
+
const text = value.trim();
|
|
135
|
+
if (!text || seen.has(text))
|
|
136
|
+
continue;
|
|
137
|
+
const size = Buffer.byteLength(text);
|
|
138
|
+
if (bytes + size > PROMPT_HISTORY_MAX_TEXT_BYTES)
|
|
139
|
+
continue;
|
|
140
|
+
result.push(text);
|
|
141
|
+
seen.add(text);
|
|
142
|
+
bytes += size;
|
|
143
|
+
if (result.length >= limit)
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
@@ -18,6 +18,11 @@ export interface OwnedUiClipboardPort {
|
|
|
18
18
|
writeText?(text: string): Promise<void>;
|
|
19
19
|
}
|
|
20
20
|
export interface OwnedUiSessionShellOptions {
|
|
21
|
+
readonly promptHistory?: {
|
|
22
|
+
readonly store: import("../../../contracts/owned-ui/index.js").PromptHistoryPort;
|
|
23
|
+
readonly limit: number;
|
|
24
|
+
readonly editor: import("../components/index.js").HistoryEditorConstructor;
|
|
25
|
+
};
|
|
21
26
|
readonly backend: OwnedUiBackendPort;
|
|
22
27
|
readonly cwd: string;
|
|
23
28
|
readonly terminal?: OwnedUiTerminalPort;
|
|
@@ -67,6 +72,8 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
67
72
|
readonly onViewportFrame?: (frame: TranscriptViewportFrame) => void;
|
|
68
73
|
readonly requestHyperlinkCleanup?: () => void;
|
|
69
74
|
readonly enableDockInputReuse?: boolean;
|
|
75
|
+
readonly persistentHistory?: boolean;
|
|
76
|
+
readonly historyEditor?: import("../components/index.js").HistoryEditorConstructor;
|
|
70
77
|
readonly onSubmit: (text: string) => void;
|
|
71
78
|
readonly onPasteRejected?: (error: unknown) => void;
|
|
72
79
|
readonly onInterrupt: () => void;
|
|
@@ -91,6 +98,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
91
98
|
setHideThinkingBlock(hidden: boolean): void;
|
|
92
99
|
setMermaidRenderingMode(mode: "off" | "final" | "streaming"): void;
|
|
93
100
|
setImagePresentation(showImages: boolean, imageWidthCells: number): void;
|
|
101
|
+
prepareHistoryText(text: string): string;
|
|
94
102
|
preparePromptSubmission(text: string): PreparedPrompt;
|
|
95
103
|
hasPendingPastes(text: string): boolean;
|
|
96
104
|
waitForPromptPastes(text: string, signal: AbortSignal): Promise<PreparedPrompt>;
|
|
@@ -108,6 +116,31 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
108
116
|
applyTranscriptBlock(block: OwnedUiSessionViewModel["transcript"][number]): void;
|
|
109
117
|
render(width: number): readonly string[];
|
|
110
118
|
viewportFrameDescriptor(): TranscriptViewportFrameDescriptor | null;
|
|
119
|
+
/** Read-only, payload-free evidence; observing a pending frame must never render or repair it. */
|
|
120
|
+
viewportPresentationEvidence(): {
|
|
121
|
+
candidate: boolean;
|
|
122
|
+
candidateRevision: number | null;
|
|
123
|
+
currentRevision: number;
|
|
124
|
+
composedRevision: number | null;
|
|
125
|
+
currentPointer: {
|
|
126
|
+
readonly column: number;
|
|
127
|
+
readonly row: number;
|
|
128
|
+
} | null;
|
|
129
|
+
composedPointer: {
|
|
130
|
+
readonly column: number;
|
|
131
|
+
readonly row: number;
|
|
132
|
+
} | null;
|
|
133
|
+
frameId: number | null;
|
|
134
|
+
bottom: {
|
|
135
|
+
readonly row: number;
|
|
136
|
+
readonly columnStart: number;
|
|
137
|
+
readonly columnEnd: number;
|
|
138
|
+
} | null;
|
|
139
|
+
followingEnd: boolean;
|
|
140
|
+
scrollTop: number;
|
|
141
|
+
maxScroll: number;
|
|
142
|
+
cause: "detached" | "initial" | "steady" | "dock-input" | "follow-shift" | "geometry-change" | null;
|
|
143
|
+
};
|
|
111
144
|
/** Visible non-selectable Steering, alignment, and Working rows, for rendering evidence. */
|
|
112
145
|
viewportTransientTailRowCount(): number;
|
|
113
146
|
hasActiveSelection(): boolean;
|