@timurproko/a1 0.2.2-dev.611 → 0.2.2
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 +16 -8
- package/bin/pinned-pi-public.js +11 -4
- package/dist/app/session-shell/index.d.ts +1 -1
- package/dist/app/session-shell/paste-executor.js +6 -1
- package/dist/app/session-shell/prompt-chips.d.ts +8 -0
- package/dist/app/session-shell/prompt-chips.js +58 -13
- package/dist/app/session-shell/session-shell-root.d.ts +21 -3
- package/dist/app/session-shell/session-shell-root.js +110 -17
- package/dist/app/session-shell/session-shell.d.ts +1 -1
- package/dist/app/session-shell/session-shell.js +76 -31
- package/dist/app/session-shell/session-viewport-controller.d.ts +13 -0
- package/dist/app/session-shell/session-viewport-controller.js +28 -1
- package/dist/cli/version-stats.js +4 -7
- package/dist/composition/owned-ui.d.ts +7 -0
- package/dist/composition/owned-ui.js +71 -3
- package/dist/contracts/owned-ui/image-attachments.d.ts +7 -4
- package/dist/contracts/owned-ui/image-attachments.js +20 -7
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/index.js +1 -1
- package/dist/contracts/owned-ui/prompt-chips.d.ts +2 -2
- package/dist/contracts/owned-ui/prompt-chips.js +9 -6
- package/dist/features/owned-ui/project-trust-prompt.js +39 -3
- package/dist/features/owned-ui/release-note-state.d.ts +13 -0
- package/dist/features/owned-ui/release-note-state.js +109 -0
- package/dist/features/owned-ui/release-notes.d.ts +10 -0
- package/dist/features/owned-ui/release-notes.js +73 -0
- package/dist/features/owned-ui/resources/release-notes.json +9 -0
- package/dist/foundation/release/latest-release.d.ts +55 -0
- package/dist/foundation/release/latest-release.js +166 -0
- package/dist/foundation/release/update.js +9 -0
- package/dist/integrations/pi/components/index.d.ts +1 -1
- package/dist/integrations/pi/components/index.js +1 -1
- package/dist/integrations/pi/components/prompt-chip-presentation.d.ts +3 -0
- package/dist/integrations/pi/components/prompt-chip-presentation.js +14 -0
- package/dist/integrations/pi/components/shell-footer-status.js +30 -9
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +77 -1
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +0 -6
- package/dist/integrations/pi/components/shell-presenters-transcript.js +0 -19
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +2 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +1 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +13 -4
- package/dist/integrations/pi/engine/adapter.d.ts +12 -0
- package/dist/integrations/pi/engine/adapter.js +13 -0
- package/dist/integrations/pi/engine/session-runtime.d.ts +1 -0
- package/dist/integrations/pi/engine/session-runtime.js +2 -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 +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/settings/declarations.d.ts +25 -1
- package/dist/ui/settings/declarations.js +19 -1
- package/dist/ui/settings/migrations.js +14 -0
- package/docs/architecture/internal-naming.md +2 -2
- package/docs/architecture/resource-and-data-policy.md +11 -0
- package/docs/architecture/toolchain.md +1 -1
- package/docs/architecture/ui-reference-provenance.md +1 -0
- package/docs/ci-release-runbook.md +154 -70
- package/docs/local-worktree-cleanup.md +21 -4
- package/docs/manual-owned-ui-checkpoint.md +1 -0
- package/docs/openspec-archive-automation.md +1 -1
- package/docs/releases/0.2.2.md +47 -0
- package/docs/validation.md +13 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
## Install
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
npm x -y -- @timurproko/a1-install # release
|
|
7
|
+
npm x -y -- @timurproko/a1-install --develop # develop
|
|
8
|
+
npm x -y -- @timurproko/a1-install --develop 107 # preview number
|
|
9
|
+
npm x -y -- @timurproko/a1-install --develop 0.1.8-dev.107 # exact preview
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
## Use
|
|
@@ -14,7 +14,7 @@ npx -y @timurproko/a1-install --develop 0.1.8-dev.107 # exact preview
|
|
|
14
14
|
```sh
|
|
15
15
|
a1 # launch A1 (profile: ~/.a1/agent)
|
|
16
16
|
a1 help # show all commands
|
|
17
|
-
a1 version # show the
|
|
17
|
+
a1 version # show the current and latest versions
|
|
18
18
|
a1 pi # vanilla Pi oracle: ~/.pi/agent
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -22,12 +22,14 @@ Update:
|
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
a1 update # install the release
|
|
25
|
-
a1 update --models # refresh A1's model catalogs
|
|
26
25
|
a1 update --develop # install develop
|
|
27
26
|
a1 update --develop 107 # install preview 107
|
|
28
27
|
a1 update --develop 0.1.8-dev.107 # install that exact preview
|
|
28
|
+
a1 update --models # refresh A1's model catalogs
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
On interactive startup A1 checks, at most once a day, whether a newer release exists on its own channel and shows an `Update Available` notice with the command to run. It never installs anything. Turn it off with the `Update check` setting (Generic section), or set `A1_SKIP_VERSION_CHECK=1`; it is also skipped when `PI_OFFLINE` or `CI` is set, or when output is not a terminal. The check reads the public npm registry, or `npm_config_registry` when set.
|
|
32
|
+
|
|
31
33
|
## Extensions
|
|
32
34
|
|
|
33
35
|
```sh
|
|
@@ -46,13 +48,13 @@ a1 update npm:pi-mcp-adapter # update one
|
|
|
46
48
|
## Develop
|
|
47
49
|
|
|
48
50
|
```sh
|
|
49
|
-
npm run doctor # report Node, npm, git, Rust, and dependency readiness
|
|
50
51
|
npm ci # install exact locked dependencies
|
|
51
52
|
npm run build # compile TypeScript and the process guardian into dist
|
|
52
53
|
npm start # build and launch a source `a1`
|
|
53
54
|
npm run start:pi # build and launch a source `a1 pi`
|
|
54
55
|
npm run test:fast # typecheck + fast suite (alias: npm test)
|
|
55
56
|
npm run test:full # complete non-physical suite
|
|
57
|
+
npm run doctor # report Node, npm, git, Rust, and dependency readiness
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
## Publish
|
|
@@ -69,5 +71,11 @@ npm run develop # request the preview publish
|
|
|
69
71
|
npm run release -- patch # 0.1.8-dev -> 0.1.8
|
|
70
72
|
npm run release -- minor # 0.1.8-dev -> 0.2.0
|
|
71
73
|
npm run release -- major # 0.1.8-dev -> 1.0.0
|
|
72
|
-
npm run release -- 0.4.0 # an exact stable version
|
|
74
|
+
npm run release -- 0.4.0 # prepare an exact stable version
|
|
73
75
|
```
|
|
76
|
+
|
|
77
|
+
The command creates the draft, starts validation of its source, prints the progress
|
|
78
|
+
link, and waits. When validation passes it prints the draft link: edit the Pi-style
|
|
79
|
+
`## [version] - YYYY-MM-DD` changelog, then choose
|
|
80
|
+
GitHub's native **Publish release** button: that publishes both packages to npm. If
|
|
81
|
+
publication fails before npm, the Release returns to draft so you can publish it again.
|
package/bin/pinned-pi-public.js
CHANGED
|
@@ -2,16 +2,23 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, posix, resolve, sep } from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
|
|
5
|
+
/** @type {string | undefined} */
|
|
6
|
+
let configuredRoot;
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Bind the generated facade to the package reached through Pi's public entry.
|
|
7
10
|
* @param {string | URL} entryUrl
|
|
8
11
|
*/
|
|
9
12
|
export function configurePinnedPiPublicPackageEntry(entryUrl) {
|
|
10
|
-
const root = dirname(dirname(fileURLToPath(entryUrl)));
|
|
13
|
+
const root = resolve(dirname(dirname(fileURLToPath(entryUrl))));
|
|
11
14
|
const manifest = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
|
|
12
15
|
if (manifest.name !== "@earendil-works/pi-coding-agent" || typeof manifest.version !== "string") {
|
|
13
16
|
throw new Error("pinned Pi public package identity is invalid");
|
|
14
17
|
}
|
|
18
|
+
if (configuredRoot !== undefined && configuredRoot !== root) {
|
|
19
|
+
throw new Error("pinned Pi public package identity cannot change after configuration");
|
|
20
|
+
}
|
|
21
|
+
configuredRoot = root;
|
|
15
22
|
process.env.PI_PACKAGE_DIR = root;
|
|
16
23
|
return { root, version: manifest.version };
|
|
17
24
|
}
|
|
@@ -55,9 +62,9 @@ export function resolvePinnedPiImport(specifier) {
|
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
function pinnedRoot() {
|
|
58
|
-
|
|
59
|
-
if (!
|
|
60
|
-
return
|
|
65
|
+
if (configuredRoot === undefined) throw new Error("pinned Pi public package directory is not configured");
|
|
66
|
+
if (!existsSync(resolve(configuredRoot, "package.json"))) throw new Error("pinned Pi public package directory is unavailable");
|
|
67
|
+
return configuredRoot;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
/**
|
|
@@ -2,6 +2,6 @@ export { ClipboardDiagnosticCapture } from "./clipboard-diagnostics.js";
|
|
|
2
2
|
export { ContextualPromptSuggestionController, samePromptSuggestionIdentity } from "./prompt-suggestion-controller.js";
|
|
3
3
|
export type { ContextualPromptSuggestionControllerOptions, ContextualPromptSuggestionSurface } from "./prompt-suggestion-controller.js";
|
|
4
4
|
export { OwnedUiSessionShell, OwnedUiSessionShellRoot, formatSessionResumeCommand, quoteCommandArgument } from "./session-shell.js";
|
|
5
|
-
export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
|
|
5
|
+
export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellPromptImagesOptions, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
|
|
6
6
|
export { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer } from "./stream-presentation-coalescer.js";
|
|
7
7
|
export type { StreamPresentationScheduler } from "./stream-presentation-coalescer.js";
|
|
@@ -181,7 +181,12 @@ export function startPasteExecutor(content, signal, phase = () => { }, helper, p
|
|
|
181
181
|
fail();
|
|
182
182
|
}
|
|
183
183
|
catch (error) {
|
|
184
|
-
|
|
184
|
+
if (error instanceof ImageAttachmentError) {
|
|
185
|
+
failure = error;
|
|
186
|
+
cancel();
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
fail("paste-unavailable");
|
|
185
190
|
}
|
|
186
191
|
});
|
|
187
192
|
child.once("error", () => fail());
|
|
@@ -17,6 +17,7 @@ export declare class PromptChipStore {
|
|
|
17
17
|
readonly isolated?: boolean;
|
|
18
18
|
readonly onEvent?: (event: PasteEvent) => void;
|
|
19
19
|
readonly preparation?: Omit<PastePreparationClientOptions, "onEvent">;
|
|
20
|
+
readonly imageLimit?: () => number;
|
|
20
21
|
});
|
|
21
22
|
/** Forks the spare paste helper so the session's first paste does not pay for helper startup. */
|
|
22
23
|
warm(): void;
|
|
@@ -31,10 +32,17 @@ export declare class PromptChipStore {
|
|
|
31
32
|
reconcileDraft(text: string): void;
|
|
32
33
|
resetPastes(text: string): string;
|
|
33
34
|
dispose(): Promise<void>;
|
|
35
|
+
imageLimitState(text: string): {
|
|
36
|
+
readonly count: number;
|
|
37
|
+
readonly limit: number;
|
|
38
|
+
readonly corrected: boolean;
|
|
39
|
+
};
|
|
34
40
|
transformPastedContent(content: PiShellClipboardContent, currentText?: string): string;
|
|
35
41
|
/** Hide provisional clipboard identities until the read identifies an actual image. */
|
|
36
42
|
hiddenRanges(line: string): readonly PiShellEditorTextRange[];
|
|
37
43
|
atomicRanges(line: string): readonly PiShellEditorTextRange[];
|
|
44
|
+
unsentRanges(line: string): readonly PiShellEditorTextRange[];
|
|
45
|
+
omitUnsentImages(text: string): string;
|
|
38
46
|
hyperlinkRanges(text: string): readonly {
|
|
39
47
|
start: number;
|
|
40
48
|
end: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { canonicalizeClipboardImage } from "./clipboard-image.js";
|
|
4
|
-
import { assertImageEncodedSize, assertPromptImages, canonicalPromptChipMatches, ImageAttachmentError, replaceCanonicalPromptChips, } from "../../contracts/owned-ui/index.js";
|
|
4
|
+
import { assertImageEncodedSize, assertPromptImages, canonicalPromptChipMatches, DEFAULT_PROMPT_IMAGE_LIMIT, ImageAttachmentError, replaceCanonicalPromptChips, } from "../../contracts/owned-ui/index.js";
|
|
5
5
|
import { ImagePreparationClient } from "./image-preparation-client.js";
|
|
6
6
|
import { preparePasteText } from "./paste-text-preparation.js";
|
|
7
7
|
import { pathChipTag } from "./path-chip-presentation.js";
|
|
@@ -13,18 +13,29 @@ function imageChipIdentifier(tag) {
|
|
|
13
13
|
const match = IMAGE_CHIP_IDENTIFIER_PATTERN.exec(tag);
|
|
14
14
|
return match === null ? null : match[1] ?? null;
|
|
15
15
|
}
|
|
16
|
+
function rejectedImageMarker(marker, error) {
|
|
17
|
+
return error.code === "image-count" ? marker : marker.replace("screenshot-", "failed-");
|
|
18
|
+
}
|
|
19
|
+
function referencesPasteMarker(text, entry) {
|
|
20
|
+
return text.includes(entry.marker)
|
|
21
|
+
|| (entry.error !== undefined && entry.replacement !== undefined && text.includes(entry.replacement));
|
|
22
|
+
}
|
|
16
23
|
/** Owns semantic chips and bounded pending paste references; cancels background work on reset or disposal. */
|
|
17
24
|
export class PromptChipStore {
|
|
18
25
|
#chips = new Map();
|
|
19
26
|
// Invariant: clearing the editor never recycles a recoverable text chip's identity.
|
|
20
27
|
#textCounter = 0;
|
|
21
28
|
#pending = new Map();
|
|
29
|
+
// Invariant: count-rejected images keep the standard chip label, so retain their identities after draft cleanup.
|
|
30
|
+
#countRejectedImages = new Set();
|
|
22
31
|
#preparation = new ImagePreparationClient();
|
|
23
32
|
#stopping = new Set();
|
|
24
33
|
#isolated;
|
|
25
34
|
#provisionalOwners = new Map();
|
|
26
35
|
#ownedChipTags = new Map();
|
|
36
|
+
#imageLimit;
|
|
27
37
|
constructor(options = {}) {
|
|
38
|
+
this.#imageLimit = options.imageLimit ?? (() => DEFAULT_PROMPT_IMAGE_LIMIT);
|
|
28
39
|
this.#isolated = options.isolated
|
|
29
40
|
? new PastePreparationClient({ ...options.preparation, ...(options.onEvent === undefined ? {} : { onEvent: options.onEvent }) })
|
|
30
41
|
: undefined;
|
|
@@ -44,8 +55,9 @@ export class PromptChipStore {
|
|
|
44
55
|
entry.kind = content?.kind === "image" ? "image" : "text";
|
|
45
56
|
if (content?.kind === "image") {
|
|
46
57
|
onImage();
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
const imageLimit = this.#imageLimit();
|
|
59
|
+
if (this.#imageCount(currentText) >= imageLimit)
|
|
60
|
+
throw new ImageAttachmentError("image-count", imageLimit);
|
|
49
61
|
}
|
|
50
62
|
return content;
|
|
51
63
|
});
|
|
@@ -60,9 +72,11 @@ export class PromptChipStore {
|
|
|
60
72
|
entry.error = error instanceof ImageAttachmentError ? error : new ImageAttachmentError("image-codec");
|
|
61
73
|
if (entry.error.code !== "image-canceled" && entry.references === 0)
|
|
62
74
|
onError(entry.error);
|
|
63
|
-
return marker
|
|
75
|
+
return rejectedImageMarker(marker, entry.error);
|
|
64
76
|
}).then(replacement => {
|
|
65
77
|
entry.replacement = replacement;
|
|
78
|
+
if (entry.error?.code === "image-count")
|
|
79
|
+
this.#countRejectedImages.add(replacement);
|
|
66
80
|
return replacement;
|
|
67
81
|
}) };
|
|
68
82
|
this.#pending.set(marker, entry);
|
|
@@ -86,8 +100,9 @@ export class PromptChipStore {
|
|
|
86
100
|
}, () => {
|
|
87
101
|
entry.kind = "image";
|
|
88
102
|
onImage();
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
const imageLimit = this.#imageLimit();
|
|
104
|
+
if (this.#imageCount(currentText) >= imageLimit)
|
|
105
|
+
throw new ImageAttachmentError("image-count", imageLimit);
|
|
91
106
|
}, () => { entry.kind = "text"; });
|
|
92
107
|
entry = { marker, job, references: 0, kind: "unknown", completion: job.result.catch(error => {
|
|
93
108
|
this.#finishChipOwnership(owner, false);
|
|
@@ -97,8 +112,13 @@ export class PromptChipStore {
|
|
|
97
112
|
entry.kind = "image";
|
|
98
113
|
if (entry.error.code !== "image-canceled" && entry.references === 0)
|
|
99
114
|
onError(entry.error);
|
|
100
|
-
return entry.kind === "image" ? marker
|
|
101
|
-
}).then(replacement => {
|
|
115
|
+
return entry.kind === "image" ? rejectedImageMarker(marker, entry.error) : "";
|
|
116
|
+
}).then(replacement => {
|
|
117
|
+
entry.replacement = replacement;
|
|
118
|
+
if (entry.error?.code === "image-count")
|
|
119
|
+
this.#countRejectedImages.add(replacement);
|
|
120
|
+
return replacement;
|
|
121
|
+
}) };
|
|
102
122
|
entry.onComplete = () => {
|
|
103
123
|
this.#finishChipOwnership(owner, job.isCurrent() && entry.error === undefined);
|
|
104
124
|
job.complete();
|
|
@@ -152,7 +172,7 @@ export class PromptChipStore {
|
|
|
152
172
|
}
|
|
153
173
|
reconcileDraft(text) {
|
|
154
174
|
for (const entry of this.#pending.values()) {
|
|
155
|
-
if (entry.references === 0 && !
|
|
175
|
+
if (entry.references === 0 && !referencesPasteMarker(text, entry)) {
|
|
156
176
|
entry.job.cancel();
|
|
157
177
|
if (entry.replacement !== undefined) {
|
|
158
178
|
entry.onComplete?.();
|
|
@@ -180,13 +200,21 @@ export class PromptChipStore {
|
|
|
180
200
|
await Promise.all([this.#preparation.dispose(), this.#isolated?.dispose(), ...this.#stopping]);
|
|
181
201
|
this.#chips.clear();
|
|
182
202
|
this.#pending.clear();
|
|
203
|
+
this.#countRejectedImages.clear();
|
|
183
204
|
this.#provisionalOwners.clear();
|
|
184
205
|
this.#ownedChipTags.clear();
|
|
185
206
|
}
|
|
207
|
+
imageLimitState(text) {
|
|
208
|
+
const limit = this.#imageLimit();
|
|
209
|
+
const rejectedCountMarker = canonicalPromptChipMatches(text).some(match => this.#countRejectedImages.has(match.text))
|
|
210
|
+
|| [...this.#pending.values()].some(entry => entry.error?.code === "image-count" && referencesPasteMarker(text, entry));
|
|
211
|
+
const count = this.#imageCount(text);
|
|
212
|
+
return { count, limit, corrected: count <= limit && !rejectedCountMarker };
|
|
213
|
+
}
|
|
186
214
|
#imageCount(text) {
|
|
187
215
|
const tags = new Set([...this.#chips.values()].filter(chip => chip.kind === "image" && text.includes(chip.tag)).map(chip => chip.tag));
|
|
188
216
|
for (const entry of this.#pending.values()) {
|
|
189
|
-
if (entry.kind !== "text" &&
|
|
217
|
+
if (entry.kind !== "text" && entry.error?.code !== "image-count" && referencesPasteMarker(text, entry)) {
|
|
190
218
|
// Invariant: a ready screenshot can retain its pending label but still occupies only one slot.
|
|
191
219
|
tags.add(entry.replacement ?? entry.marker);
|
|
192
220
|
}
|
|
@@ -207,7 +235,7 @@ export class PromptChipStore {
|
|
|
207
235
|
const image = canonicalizeClipboardImage(content);
|
|
208
236
|
if (image === null)
|
|
209
237
|
return "";
|
|
210
|
-
assertPromptImages([...this.prepareSubmission(currentText).images, { type: "image", ...image }]);
|
|
238
|
+
assertPromptImages([...this.prepareSubmission(currentText).images, { type: "image", ...image }], this.#imageLimit());
|
|
211
239
|
const id = randomBytes(5).toString("hex");
|
|
212
240
|
const tag = `[📷 screenshot-${id}]`;
|
|
213
241
|
this.#chips.set(tag, {
|
|
@@ -262,6 +290,15 @@ export class PromptChipStore {
|
|
|
262
290
|
.filter(match => !match.text.startsWith("[paste #") || this.#chips.has(match.text))
|
|
263
291
|
.map(match => ({ start: match.start, end: match.end }));
|
|
264
292
|
}
|
|
293
|
+
unsentRanges(line) {
|
|
294
|
+
return canonicalPromptChipMatches(line)
|
|
295
|
+
.filter(match => this.#countRejectedImages.has(match.text))
|
|
296
|
+
.map(match => ({ start: match.start, end: match.end }));
|
|
297
|
+
}
|
|
298
|
+
omitUnsentImages(text) {
|
|
299
|
+
return replaceCanonicalPromptChips(text, match => this.#countRejectedImages.has(match.text)
|
|
300
|
+
|| this.#pendingEntry(match.text)?.error?.code === "image-count" ? "" : match.text);
|
|
301
|
+
}
|
|
265
302
|
hyperlinkRanges(text) {
|
|
266
303
|
const ranges = [];
|
|
267
304
|
for (const chip of this.#chips.values()) {
|
|
@@ -292,7 +329,7 @@ export class PromptChipStore {
|
|
|
292
329
|
* chips carrying the original attachment bytes.
|
|
293
330
|
*/
|
|
294
331
|
prepareHistoryText(text) {
|
|
295
|
-
return this.#replaceResolvable(text, false, false).text.trim();
|
|
332
|
+
return this.#replaceResolvable(this.omitUnsentImages(text), false, false).text.trim();
|
|
296
333
|
}
|
|
297
334
|
/**
|
|
298
335
|
* Extract the image chip attachments referenced by an editor draft in occurrence order,
|
|
@@ -396,6 +433,7 @@ export class PromptChipStore {
|
|
|
396
433
|
}
|
|
397
434
|
prepareSubmission(text) {
|
|
398
435
|
const expanded = this.#replaceResolvable(text, true);
|
|
436
|
+
assertPromptImages(expanded.images, this.#imageLimit());
|
|
399
437
|
return { text: expanded.text, images: expanded.images };
|
|
400
438
|
}
|
|
401
439
|
#replaceResolvable(text, includeImages, omitImages = false) {
|
|
@@ -419,9 +457,13 @@ export class PromptChipStore {
|
|
|
419
457
|
// Invariant: scan only draft tokens (and resolved reservation tokens), never emitted payloads.
|
|
420
458
|
const expanded = replaceCanonicalPromptChips(text, match => {
|
|
421
459
|
const tag = match.text;
|
|
422
|
-
|
|
460
|
+
if (this.#countRejectedImages.has(tag))
|
|
461
|
+
return "";
|
|
462
|
+
const entry = this.#pendingEntry(tag);
|
|
423
463
|
if (entry === undefined)
|
|
424
464
|
return resolveChip(tag);
|
|
465
|
+
if (entry.error?.code === "image-count")
|
|
466
|
+
return "";
|
|
425
467
|
if (includeImages && entry.error !== undefined)
|
|
426
468
|
throw entry.error;
|
|
427
469
|
if (includeImages && entry.replacement === undefined)
|
|
@@ -430,6 +472,9 @@ export class PromptChipStore {
|
|
|
430
472
|
});
|
|
431
473
|
return { text: expanded, images };
|
|
432
474
|
}
|
|
475
|
+
#pendingEntry(tag) {
|
|
476
|
+
return this.#pending.get(tag) ?? [...this.#pending.values()].find(entry => entry.error !== undefined && entry.replacement === tag);
|
|
477
|
+
}
|
|
433
478
|
#claimChip(tag, owner, created) {
|
|
434
479
|
if (owner === undefined) {
|
|
435
480
|
this.#provisionalOwners.delete(tag);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { ImageAttachmentError } from "../../contracts/owned-ui/index.js";
|
|
1
2
|
import type { OwnedUiPromptSuggestionGeneratorPort, OwnedUiSessionViewModel, OwnedUiViewportSettings, OwnedUiViewportSettingsPort, OwnedUiQuitOutroSettingsPort, SuggestionDecision, SuggestionDiagnosticObserver } from "../../contracts/owned-ui/index.js";
|
|
2
3
|
import type { PiTuiPointerSurface } from "../../integrations/pi/tui-runtime/contracts.js";
|
|
3
|
-
import type { UiRouteHost } from "../../ui/apps/contracts.js";
|
|
4
|
+
import type { UiRouteHost, UiRouteInput } from "../../ui/apps/contracts.js";
|
|
4
5
|
import type { TranscriptViewportFrame, TranscriptViewportFrameDescriptor } from "../../ui/components/transcript-viewport.js";
|
|
5
6
|
import type { PiEngineAdapter } from "../../integrations/pi/engine/adapter.js";
|
|
6
7
|
import type { PiWorkflowMessage, PiWorkflowResult } from "../../integrations/pi/engine/workflows.js";
|
|
@@ -35,6 +36,12 @@ export interface OwnedUiShellEngineOptions {
|
|
|
35
36
|
* every other route continues to the pinned workflow table unchanged.
|
|
36
37
|
*/
|
|
37
38
|
readonly routeHost?: UiRouteHost;
|
|
39
|
+
/** One product-owned route presented after the first usable frame and any startup modal. */
|
|
40
|
+
readonly startupRoute?: {
|
|
41
|
+
readonly route: string;
|
|
42
|
+
readonly input?: UiRouteInput;
|
|
43
|
+
readonly onClosed?: () => void | Promise<void>;
|
|
44
|
+
};
|
|
38
45
|
/** Bare A1 selects the owned bounded viewport; comparison profiles stay pinned. */
|
|
39
46
|
readonly sessionLayout?: "pinned" | "custom-viewport";
|
|
40
47
|
}
|
|
@@ -110,6 +117,11 @@ export interface OwnedUiShellDiagnosticOptions {
|
|
|
110
117
|
/** Test seams for isolated paste preparation: an in-process starter, or the forked helper's entry. */
|
|
111
118
|
readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent" | "spareIdleMs">;
|
|
112
119
|
}
|
|
120
|
+
/** Live prompt-image admission policy supplied only by the bare-A1 profile. */
|
|
121
|
+
export interface OwnedUiShellPromptImagesOptions {
|
|
122
|
+
readonly limit: () => number;
|
|
123
|
+
readonly onChange: (listener: () => void) => () => void;
|
|
124
|
+
}
|
|
113
125
|
/** The skills presentation choice: collapse the per-skill commands into one skills command or expand them. */
|
|
114
126
|
export interface OwnedUiShellSkillsOptions {
|
|
115
127
|
readonly presentation: () => "collapse" | "expand";
|
|
@@ -121,6 +133,8 @@ export interface OwnedUiSessionShellOptions {
|
|
|
121
133
|
readonly presentation?: OwnedUiShellPresentationOptions;
|
|
122
134
|
readonly history?: OwnedUiShellHistoryOptions;
|
|
123
135
|
readonly suggestions?: OwnedUiShellSuggestionOptions;
|
|
136
|
+
/** Supplied only to bare A1; absent retains the fixed eight-image comparison policy. */
|
|
137
|
+
readonly promptImages?: OwnedUiShellPromptImagesOptions;
|
|
124
138
|
/** Supplied only to the bare-A1 composition; absent keeps the pinned per-skill command list. */
|
|
125
139
|
readonly skills?: OwnedUiShellSkillsOptions;
|
|
126
140
|
readonly diagnostics?: OwnedUiShellDiagnosticOptions;
|
|
@@ -160,6 +174,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
160
174
|
readonly captureClipboardPaste?: () => PasteSource;
|
|
161
175
|
readonly pasteDiagnostics?: (event: PasteEvent) => void;
|
|
162
176
|
readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent">;
|
|
177
|
+
readonly promptImageLimit?: () => number;
|
|
163
178
|
readonly skillsPresentation?: () => "collapse" | "expand";
|
|
164
179
|
}, startup?: PiShellHeaderOptions, agentDir?: string, extensionRenderers?: PiShellExtensionRendererResolver, sessionLayout?: "pinned" | "custom-viewport", imageAssets?: PiShellImageAssetResolver);
|
|
165
180
|
setEditorPaddingX(padding: number): void;
|
|
@@ -169,6 +184,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
169
184
|
setMermaidRenderingMode(mode: "off" | "final" | "streaming"): void;
|
|
170
185
|
setImagePresentation(showImages: boolean, imageWidthCells: number): void;
|
|
171
186
|
prepareHistoryText(text: string): string;
|
|
187
|
+
omitUnsentPromptImages(text: string): string;
|
|
172
188
|
rehydrateHistoryText(text: string, resolveImage: (id: string) => import("../../contracts/owned-ui/index.js").PromptHistoryImageSidecarAttachment | null): string;
|
|
173
189
|
imageChipAttachmentsFromEditor(text: string): {
|
|
174
190
|
readonly id: string;
|
|
@@ -180,6 +196,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
180
196
|
};
|
|
181
197
|
}[];
|
|
182
198
|
preparePromptSubmission(text: string): PreparedPrompt;
|
|
199
|
+
reconcilePromptImageLimitNotice(text?: string): boolean;
|
|
183
200
|
hasPendingPastes(text: string): boolean;
|
|
184
201
|
waitForPromptPastes(text: string, signal: AbortSignal): Promise<PreparedPrompt>;
|
|
185
202
|
resetPendingPastes(): void;
|
|
@@ -244,8 +261,8 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
244
261
|
transcriptComponent(id: string): PiShellTranscriptComponentPort | undefined;
|
|
245
262
|
exitTranscript(width: number): string;
|
|
246
263
|
appendWorkflowStatus(message: string): void;
|
|
247
|
-
appendWorkflowMessage(message: PiWorkflowMessage): void;
|
|
248
|
-
appendWorkflowResult(result: PiWorkflowResult): void;
|
|
264
|
+
appendWorkflowMessage(message: PiWorkflowMessage, errorCode?: ImageAttachmentError["code"]): void;
|
|
265
|
+
appendWorkflowResult(result: PiWorkflowResult, errorCode?: ImageAttachmentError["code"]): void;
|
|
249
266
|
appendDaxnuts(): void;
|
|
250
267
|
toggleThinkingVisibility(): void;
|
|
251
268
|
get toolsExpanded(): boolean;
|
|
@@ -255,6 +272,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
255
272
|
setExtensionFooter(component: PiShellComponentPort | null): void;
|
|
256
273
|
setExtensionStatus(key: string, text: string | undefined): void;
|
|
257
274
|
setExtensionWorking(message: string | undefined, visible?: boolean): void;
|
|
275
|
+
beginImageSubmissionStatus(): () => void;
|
|
258
276
|
hotkeysPresentation(): PiShellHotkeysPresentation;
|
|
259
277
|
resetWorkflowPresentation(): void;
|
|
260
278
|
resetExtensionUi(): void;
|