@timurproko/a1 0.2.6-dev.717 → 0.2.6-dev.727
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/prompt-suggestion-controller.d.ts +5 -4
- package/dist/app/session-shell/prompt-suggestion-controller.js +148 -58
- package/dist/app/session-shell/session-shell-root.d.ts +2 -1
- package/dist/app/session-shell/session-shell-root.js +13 -6
- package/dist/app/session-shell/session-shell.js +45 -3
- 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 +3 -0
- package/dist/composition/owned-ui.js +66 -6
- package/dist/composition/settings-route-host.js +2 -1
- package/dist/contracts/owned-ui/index.d.ts +4 -2
- package/dist/contracts/owned-ui/index.js +1 -1
- package/dist/contracts/owned-ui/model.d.ts +4 -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 +21 -39
- 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/progress-palette.generated.d.ts +2 -2
- package/dist/foundation/release/progress-palette.generated.js +2 -2
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/integrations/pi/components/components.js +2 -2
- package/dist/integrations/pi/components/index.d.ts +2 -2
- package/dist/integrations/pi/components/index.js +1 -1
- package/dist/integrations/pi/components/modal-frame.js +3 -4
- package/dist/integrations/pi/components/models-dialog.js +5 -5
- 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 +8 -7
- package/dist/integrations/pi/components/shell-extension-ui.d.ts +2 -0
- package/dist/integrations/pi/components/shell-extension-ui.js +20 -10
- 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 +10 -10
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -6
- package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
- 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/system-theme.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/system-theme.js +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +18 -1
- package/dist/integrations/pi/components/upstream/theme/theme.js +207 -10
- package/dist/integrations/pi/engine/pi-settings-metadata.json +1 -1
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/session-events.js +10 -2
- package/dist/integrations/pi/engine/transcript-projection.js +2 -1
- 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 -0
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +3 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.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 +26 -14
- package/dist/ui/settings/declarations.js +24 -11
- package/dist/ui/settings/migrations.js +16 -0
- package/docs/architecture/project-structure.md +4 -3
- 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/bin/cli.js
CHANGED
|
@@ -55,8 +55,19 @@ process.exitCode = await dispatchCli(process.argv.slice(2), {
|
|
|
55
55
|
return await runPackageCommand(request, { createPort: createPiPackagesPort });
|
|
56
56
|
},
|
|
57
57
|
sessionContext: async request => {
|
|
58
|
-
const { runSessionContextCommand } = await
|
|
59
|
-
|
|
58
|
+
const [{ runSessionContextCommand }, { createNativeProcessInspector }, { readLaunchContext }] = await Promise.all([
|
|
59
|
+
import("../dist/cli/session-context.js"),
|
|
60
|
+
import("../dist/foundation/process-containment/native-process-inspector.js"),
|
|
61
|
+
import("../dist/foundation/launch-context/index.js"),
|
|
62
|
+
]);
|
|
63
|
+
const launch = readLaunchContext(process.env);
|
|
64
|
+
let inspector;
|
|
65
|
+
return await runSessionContextCommand(request, {
|
|
66
|
+
inspectProcess: async pid => {
|
|
67
|
+
inspector ??= createNativeProcessInspector(process.env, process.platform, launch.releaseRoot);
|
|
68
|
+
return (await inspector).observe(pid);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
60
71
|
},
|
|
61
72
|
}, {
|
|
62
73
|
stdout: message => process.stdout.write(message),
|
package/bin/ui.js
CHANGED
|
@@ -9,6 +9,13 @@ const { readLaunchContext } = await import("../dist/foundation/launch-context/in
|
|
|
9
9
|
const launchContext = readLaunchContext(process.env, "profile");
|
|
10
10
|
const profile = launchContext.launchProfile;
|
|
11
11
|
if (profile === undefined) throw new Error("A1 launch profile is missing");
|
|
12
|
+
if (profile === "a1") {
|
|
13
|
+
const [{ randomUUID }, { PRODUCT_IDENTITY }] = await Promise.all([
|
|
14
|
+
import("node:crypto"),
|
|
15
|
+
import("../dist/product-identity.js"),
|
|
16
|
+
]);
|
|
17
|
+
process.env[PRODUCT_IDENTITY.environment.sessionRuntimeId] = randomUUID();
|
|
18
|
+
}
|
|
12
19
|
const { installFatalExit } = await import("../dist/foundation/terminal-cleanup/fatal-exit.js");
|
|
13
20
|
const { resolveProductPaths } = await import("../dist/foundation/lifecycle/paths.js");
|
|
14
21
|
const { join } = await import("node:path");
|
|
@@ -49,10 +56,16 @@ Promise.resolve().then(() => {
|
|
|
49
56
|
if (sessionSelection && profile !== "a1") throw new Error("session selection requires the normal A1 profile");
|
|
50
57
|
return runSelectedInteractiveRuntime(profile, {
|
|
51
58
|
ownedUi: async (profileId, ownedSurfaces) => {
|
|
59
|
+
const processInspector = ownedSurfaces
|
|
60
|
+
? await import("../dist/foundation/process-containment/native-process-inspector.js")
|
|
61
|
+
.then(module => module.createNativeProcessInspector(process.env, process.platform, launchContext.releaseRoot))
|
|
62
|
+
.catch(() => null)
|
|
63
|
+
: null;
|
|
52
64
|
const { application, settings } = await composeOwnedUi({
|
|
53
65
|
cwd: process.cwd(),
|
|
54
66
|
profileId,
|
|
55
67
|
ownedSurfaces,
|
|
68
|
+
...(processInspector === null ? {} : { inspectProcess: pid => processInspector.observe(pid) }),
|
|
56
69
|
projectTrustPrompt: createConsoleProjectTrustPrompt({ presentation: profile === "a1" ? "bare" : "comparison" }),
|
|
57
70
|
sessionForkPrompt: createConsoleSessionForkPrompt(),
|
|
58
71
|
...(sessionSelection === undefined ? {} : { sessionSelection }),
|
|
@@ -17,7 +17,7 @@ export interface ContextualPromptSuggestionControllerOptions {
|
|
|
17
17
|
type DecisionIdentity = Omit<OwnedUiPromptSuggestionIdentity, "model"> & {
|
|
18
18
|
readonly model: OwnedUiPromptSuggestionIdentity["model"] | null;
|
|
19
19
|
};
|
|
20
|
-
/** Prefetches one inert candidate
|
|
20
|
+
/** Prefetches one inert candidate and owns one bounded settlement/retry recovery lifecycle. */
|
|
21
21
|
export declare class ContextualPromptSuggestionController {
|
|
22
22
|
#private;
|
|
23
23
|
readonly options: ContextualPromptSuggestionControllerOptions;
|
|
@@ -26,12 +26,13 @@ export declare class ContextualPromptSuggestionController {
|
|
|
26
26
|
setEnabled(enabled: boolean): void;
|
|
27
27
|
skip(identity: DecisionIdentity, reason: SuggestionDecisionReason): void;
|
|
28
28
|
consider(identity: OwnedUiPromptSuggestionIdentity, decision: boolean | SuggestionDecision): void;
|
|
29
|
-
|
|
29
|
+
/** Settles an existing prefetch or starts the first request when that eligible prefetch was missed. */
|
|
30
|
+
settle(identity: OwnedUiPromptSuggestionIdentity, decision?: boolean | SuggestionDecision): void;
|
|
30
31
|
accept(): void;
|
|
31
32
|
invalidate(): void;
|
|
32
|
-
/** Retires
|
|
33
|
+
/** Retires generating or scheduled recovery; a prepared or available suggestion survives a draft edit. */
|
|
33
34
|
abortPending(): void;
|
|
34
|
-
/** Reasserts retained ghost text
|
|
35
|
+
/** Reasserts retained ghost text or retries a deferred presentation when the editor is eligible. */
|
|
35
36
|
restoreAvailable(): void;
|
|
36
37
|
dispose(): void;
|
|
37
38
|
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { assertOwnedUiPromptSuggestionResult, normalizePromptSuggestionCandidate, } from "../../contracts/owned-ui/index.js";
|
|
2
|
-
|
|
2
|
+
const TRANSIENT_PRESENTATION_REASONS = new Set([
|
|
3
|
+
"not-ready", "not-focused", "autocomplete", "prompt-mode",
|
|
4
|
+
]);
|
|
5
|
+
const RETRYABLE_OUTCOMES = new Set([
|
|
6
|
+
"empty", "rejected", "provider-failure", "unavailable", "timeout",
|
|
7
|
+
]);
|
|
8
|
+
/** Prefetches one inert candidate and owns one bounded settlement/retry recovery lifecycle. */
|
|
3
9
|
export class ContextualPromptSuggestionController {
|
|
4
10
|
#state = { status: "idle" };
|
|
11
|
+
#lifecycle = null;
|
|
5
12
|
#request = null;
|
|
6
13
|
#lastConsidered = "";
|
|
14
|
+
#retiredIdentity = "";
|
|
7
15
|
#enabled;
|
|
8
16
|
#disposed = false;
|
|
9
17
|
#sessionKey = "";
|
|
@@ -27,6 +35,7 @@ export class ContextualPromptSuggestionController {
|
|
|
27
35
|
}
|
|
28
36
|
skip(identity, reason) {
|
|
29
37
|
this.invalidate();
|
|
38
|
+
this.#lastConsidered = decisionIdentityKey(identity);
|
|
30
39
|
this.#emit(this.#metadata(identity), "skipped", reason);
|
|
31
40
|
}
|
|
32
41
|
consider(identity, decision) {
|
|
@@ -34,51 +43,43 @@ export class ContextualPromptSuggestionController {
|
|
|
34
43
|
if (key === this.#lastConsidered)
|
|
35
44
|
return;
|
|
36
45
|
this.#lastConsidered = key;
|
|
37
|
-
this
|
|
38
|
-
const reason = this.#
|
|
39
|
-
: decision === false ? "ineligible" : decision === true ? null : decision;
|
|
46
|
+
this.#reset(true, true);
|
|
47
|
+
const reason = this.#decisionReason(decision);
|
|
40
48
|
if (reason !== null) {
|
|
41
49
|
this.#emit(this.#metadata(identity), "skipped", reason);
|
|
42
50
|
return;
|
|
43
51
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
catch { /* Security: isolate diagnostic metadata failure. */ }
|
|
49
|
-
const request = {
|
|
50
|
-
identity,
|
|
51
|
-
metadata: { ...this.#metadata(identity), request: ++this.#requestSequence, reasoning },
|
|
52
|
-
abort: new AbortController(), startedAt: this.#now(), terminal: false,
|
|
53
|
-
};
|
|
54
|
-
this.#request = request;
|
|
55
|
-
this.#state = { status: "generating", identity, settled: false };
|
|
56
|
-
this.#emit(request.metadata, "started");
|
|
57
|
-
request.timer = setTimeout(() => {
|
|
58
|
-
if (this.#request !== request || this.#state.status !== "generating")
|
|
59
|
-
return;
|
|
60
|
-
this.#finish(request, "timeout");
|
|
61
|
-
this.#state = { status: "idle" };
|
|
62
|
-
this.#request = null;
|
|
63
|
-
request.abort.abort();
|
|
64
|
-
}, this.#timeoutMs);
|
|
65
|
-
request.timer.unref?.();
|
|
66
|
-
try {
|
|
67
|
-
void this.options.generator.generate({ identity, signal: request.abort.signal })
|
|
68
|
-
.then(result => this.#receive(request, result), () => this.#failed(request))
|
|
69
|
-
.finally(() => clearTimeout(request.timer));
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
this.#failed(request);
|
|
73
|
-
}
|
|
52
|
+
this.#retiredIdentity = "";
|
|
53
|
+
this.#lifecycle = { identity, settled: false, attempts: 0, retryPending: false, presentationDeferred: false };
|
|
54
|
+
this.#startAttempt("prefetch");
|
|
74
55
|
}
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
/** Settles an existing prefetch or starts the first request when that eligible prefetch was missed. */
|
|
57
|
+
settle(identity, decision = null) {
|
|
58
|
+
if (this.#disposed || !this.#enabled || identityKey(identity) === this.#retiredIdentity)
|
|
77
59
|
return;
|
|
78
|
-
|
|
60
|
+
const reason = this.#decisionReason(decision);
|
|
61
|
+
const lifecycle = this.#lifecycle;
|
|
62
|
+
if (lifecycle !== null && !samePromptSuggestionIdentity(lifecycle.identity, identity)) {
|
|
79
63
|
if (this.#request)
|
|
80
64
|
this.#finish(this.#request, "stale-result", "stale-identity");
|
|
81
|
-
this
|
|
65
|
+
this.#reset(true, true);
|
|
66
|
+
}
|
|
67
|
+
if (reason !== null) {
|
|
68
|
+
if (this.#lifecycle !== null)
|
|
69
|
+
this.#reset(true, true);
|
|
70
|
+
else
|
|
71
|
+
this.#emit(this.#metadata(identity), "skipped", reason);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (this.#lifecycle === null) {
|
|
75
|
+
this.#retiredIdentity = "";
|
|
76
|
+
this.#lifecycle = { identity, settled: true, attempts: 0, retryPending: false, presentationDeferred: false };
|
|
77
|
+
this.#startAttempt("settlement");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.#lifecycle.settled = true;
|
|
81
|
+
if (this.#lifecycle.retryPending) {
|
|
82
|
+
this.#startAttempt("retry");
|
|
82
83
|
return;
|
|
83
84
|
}
|
|
84
85
|
if (this.#state.status === "generating")
|
|
@@ -86,23 +87,23 @@ export class ContextualPromptSuggestionController {
|
|
|
86
87
|
else if (this.#state.status === "prepared" && this.#request)
|
|
87
88
|
this.#show(this.#request, this.#state.text);
|
|
88
89
|
}
|
|
89
|
-
accept() { this.#
|
|
90
|
-
invalidate() { this.#
|
|
91
|
-
/** Retires
|
|
90
|
+
accept() { this.#reset(false, true); }
|
|
91
|
+
invalidate() { this.#reset(true, true); }
|
|
92
|
+
/** Retires generating or scheduled recovery; a prepared or available suggestion survives a draft edit. */
|
|
92
93
|
abortPending() {
|
|
93
|
-
if (this.#state.status !== "generating")
|
|
94
|
-
return;
|
|
95
|
-
const request = this.#request;
|
|
96
|
-
this.#request = null;
|
|
97
|
-
this.#state = { status: "idle" };
|
|
98
|
-
if (request === null)
|
|
94
|
+
if (this.#state.status !== "generating" && !(this.#state.status === "idle" && this.#lifecycle?.retryPending))
|
|
99
95
|
return;
|
|
100
|
-
this.#
|
|
101
|
-
request.abort.abort();
|
|
96
|
+
this.#reset(false, true);
|
|
102
97
|
}
|
|
103
|
-
/** Reasserts retained ghost text
|
|
98
|
+
/** Reasserts retained ghost text or retries a deferred presentation when the editor is eligible. */
|
|
104
99
|
restoreAvailable() {
|
|
105
|
-
if (this.#disposed || !this.#enabled
|
|
100
|
+
if (this.#disposed || !this.#enabled)
|
|
101
|
+
return;
|
|
102
|
+
if (this.#state.status === "prepared" && this.#lifecycle?.settled && this.#request) {
|
|
103
|
+
this.#show(this.#request, this.#state.text);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (this.#state.status !== "available")
|
|
106
107
|
return;
|
|
107
108
|
const reason = this.options.surface.presentationBlockReason?.(this.#state.identity)
|
|
108
109
|
?? (this.options.surface.canPresent(this.#state.identity) ? null : "presentation-unavailable");
|
|
@@ -114,16 +115,64 @@ export class ContextualPromptSuggestionController {
|
|
|
114
115
|
this.#disposed = true;
|
|
115
116
|
this.invalidate();
|
|
116
117
|
this.#lastConsidered = "";
|
|
118
|
+
this.#retiredIdentity = "";
|
|
117
119
|
this.#sessionKey = "";
|
|
118
120
|
}
|
|
119
|
-
#
|
|
121
|
+
#decisionReason(decision) {
|
|
122
|
+
return this.#disposed ? "disposed" : !this.#enabled ? "disabled"
|
|
123
|
+
: decision === false ? "ineligible" : decision === true ? null : decision;
|
|
124
|
+
}
|
|
125
|
+
#startAttempt(trigger) {
|
|
126
|
+
const lifecycle = this.#lifecycle;
|
|
127
|
+
if (lifecycle === null || this.#request !== null || lifecycle.attempts >= 2)
|
|
128
|
+
return;
|
|
129
|
+
lifecycle.retryPending = false;
|
|
130
|
+
lifecycle.presentationDeferred = false;
|
|
131
|
+
const attempt = ++lifecycle.attempts;
|
|
132
|
+
let reasoning = "unavailable";
|
|
133
|
+
try {
|
|
134
|
+
reasoning = this.options.generator.suggestionReasoningPolicy?.() ?? "unavailable";
|
|
135
|
+
}
|
|
136
|
+
catch { /* Security: isolate diagnostic metadata failure. */ }
|
|
137
|
+
const request = {
|
|
138
|
+
identity: lifecycle.identity,
|
|
139
|
+
metadata: { ...this.#metadata(lifecycle.identity), request: ++this.#requestSequence, reasoning, attempt, trigger },
|
|
140
|
+
abort: new AbortController(), startedAt: this.#now(), terminal: false,
|
|
141
|
+
};
|
|
142
|
+
this.#request = request;
|
|
143
|
+
this.#state = { status: "generating", identity: lifecycle.identity, settled: lifecycle.settled };
|
|
144
|
+
this.#emit(request.metadata, "started");
|
|
145
|
+
request.timer = setTimeout(() => {
|
|
146
|
+
if (this.#request !== request || this.#state.status !== "generating")
|
|
147
|
+
return;
|
|
148
|
+
this.#finish(request, "timeout");
|
|
149
|
+
this.#request = null;
|
|
150
|
+
this.#state = { status: "idle" };
|
|
151
|
+
request.abort.abort();
|
|
152
|
+
this.#completeNoCandidate(request, "timeout");
|
|
153
|
+
}, this.#timeoutMs);
|
|
154
|
+
request.timer.unref?.();
|
|
155
|
+
try {
|
|
156
|
+
void this.options.generator.generate({ identity: lifecycle.identity, signal: request.abort.signal })
|
|
157
|
+
.then(result => this.#receive(request, result), () => this.#failed(request))
|
|
158
|
+
.finally(() => clearTimeout(request.timer));
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
this.#failed(request);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
#reset(clearSurface, retire) {
|
|
120
165
|
const hadVisibleSuggestion = this.#state.status === "available";
|
|
166
|
+
const identity = this.#lifecycle?.identity ?? (this.#state.status === "idle" ? null : this.#state.identity);
|
|
121
167
|
const request = this.#request;
|
|
122
168
|
if (request)
|
|
123
169
|
this.#finish(request, "cancelled");
|
|
124
170
|
this.#request = null;
|
|
171
|
+
this.#lifecycle = null;
|
|
125
172
|
this.#state = { status: "idle" };
|
|
126
173
|
request?.abort.abort();
|
|
174
|
+
if (retire && identity !== null)
|
|
175
|
+
this.#retiredIdentity = identityKey(identity);
|
|
127
176
|
if (clearSurface && hadVisibleSuggestion) {
|
|
128
177
|
this.options.surface.clear();
|
|
129
178
|
this.options.surface.requestRender();
|
|
@@ -137,6 +186,7 @@ export class ContextualPromptSuggestionController {
|
|
|
137
186
|
this.#finish(request, "provider-failure");
|
|
138
187
|
this.#request = null;
|
|
139
188
|
this.#state = { status: "idle" };
|
|
189
|
+
this.#completeNoCandidate(request, "provider-failure");
|
|
140
190
|
}
|
|
141
191
|
#receive(request, result) {
|
|
142
192
|
if (this.#disposed || this.#request !== request || request.terminal || this.#state.status !== "generating") {
|
|
@@ -148,41 +198,76 @@ export class ContextualPromptSuggestionController {
|
|
|
148
198
|
}
|
|
149
199
|
catch {
|
|
150
200
|
this.#finish(request, "rejected");
|
|
151
|
-
this
|
|
201
|
+
this.#request = null;
|
|
202
|
+
this.#state = { status: "idle" };
|
|
203
|
+
this.#completeNoCandidate(request, "rejected");
|
|
152
204
|
return;
|
|
153
205
|
}
|
|
154
206
|
if (!samePromptSuggestionIdentity(request.identity, result.identity)) {
|
|
155
207
|
this.#finish(request, "stale-result", "stale-identity");
|
|
156
|
-
this
|
|
208
|
+
this.#reset(true, true);
|
|
157
209
|
return;
|
|
158
210
|
}
|
|
159
211
|
if (result.outcome !== "candidate") {
|
|
160
212
|
this.#finish(request, result.outcome);
|
|
161
|
-
this
|
|
213
|
+
this.#request = null;
|
|
214
|
+
this.#state = { status: "idle" };
|
|
215
|
+
if (RETRYABLE_OUTCOMES.has(result.outcome))
|
|
216
|
+
this.#completeNoCandidate(request, result.outcome);
|
|
217
|
+
else {
|
|
218
|
+
this.#retiredIdentity = identityKey(request.identity);
|
|
219
|
+
this.#lifecycle = null;
|
|
220
|
+
}
|
|
162
221
|
return;
|
|
163
222
|
}
|
|
164
223
|
const text = normalizePromptSuggestionCandidate(result.text);
|
|
165
224
|
if (text === null) {
|
|
166
225
|
this.#finish(request, "rejected");
|
|
167
|
-
this
|
|
226
|
+
this.#request = null;
|
|
227
|
+
this.#state = { status: "idle" };
|
|
228
|
+
this.#completeNoCandidate(request, "rejected");
|
|
168
229
|
return;
|
|
169
230
|
}
|
|
170
231
|
clearTimeout(request.timer);
|
|
171
|
-
if (this.#
|
|
232
|
+
if (this.#lifecycle?.settled)
|
|
172
233
|
this.#show(request, text);
|
|
173
234
|
else
|
|
174
235
|
this.#state = { status: "prepared", identity: request.identity, text };
|
|
175
236
|
}
|
|
237
|
+
#completeNoCandidate(request, outcome) {
|
|
238
|
+
const lifecycle = this.#lifecycle;
|
|
239
|
+
if (lifecycle === null || !samePromptSuggestionIdentity(lifecycle.identity, request.identity))
|
|
240
|
+
return;
|
|
241
|
+
if (lifecycle.attempts >= 2 || !RETRYABLE_OUTCOMES.has(outcome)) {
|
|
242
|
+
this.#emit({ ...request.metadata, elapsedMs: Math.max(0, this.#now() - request.startedAt) }, "retry-exhausted");
|
|
243
|
+
this.#retiredIdentity = identityKey(request.identity);
|
|
244
|
+
this.#lifecycle = null;
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
lifecycle.retryPending = true;
|
|
248
|
+
if (lifecycle.settled)
|
|
249
|
+
this.#startAttempt("retry");
|
|
250
|
+
}
|
|
176
251
|
#show(request, text) {
|
|
177
252
|
const reason = this.options.surface.presentationBlockReason?.(request.identity)
|
|
178
253
|
?? (this.options.surface.canPresent(request.identity) ? null : "presentation-unavailable");
|
|
254
|
+
if (reason !== null && TRANSIENT_PRESENTATION_REASONS.has(reason)) {
|
|
255
|
+
this.#state = { status: "prepared", identity: request.identity, text };
|
|
256
|
+
if (this.#lifecycle !== null && !this.#lifecycle.presentationDeferred) {
|
|
257
|
+
this.#lifecycle.presentationDeferred = true;
|
|
258
|
+
this.#emit({ ...request.metadata, elapsedMs: Math.max(0, this.#now() - request.startedAt) }, "presentation-deferred", reason);
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
179
262
|
if (reason !== null || !this.options.surface.present(text)) {
|
|
180
263
|
this.#finish(request, "presentation-blocked", reason ?? "presentation-unavailable");
|
|
181
|
-
this
|
|
264
|
+
this.#reset(true, true);
|
|
182
265
|
return;
|
|
183
266
|
}
|
|
184
267
|
this.#state = { status: "available", identity: request.identity, text };
|
|
185
268
|
this.#finish(request, "displayed");
|
|
269
|
+
if (this.#lifecycle !== null)
|
|
270
|
+
this.#lifecycle.presentationDeferred = false;
|
|
186
271
|
this.options.surface.requestRender();
|
|
187
272
|
}
|
|
188
273
|
#finish(request, event, reason) {
|
|
@@ -227,3 +312,8 @@ export function samePromptSuggestionIdentity(left, right) {
|
|
|
227
312
|
function identityKey(identity) {
|
|
228
313
|
return `${identity.sessionId}\0${identity.sessionGeneration}\0${identity.runSequence}\0${identity.responseSequence}\0${identity.model.providerId}\0${identity.model.modelId}`;
|
|
229
314
|
}
|
|
315
|
+
function decisionIdentityKey(identity) {
|
|
316
|
+
return identity.model === null
|
|
317
|
+
? `${identity.sessionId}\0${identity.sessionGeneration}\0${identity.runSequence}\0${identity.responseSequence}\0\0`
|
|
318
|
+
: identityKey(identity);
|
|
319
|
+
}
|
|
@@ -172,6 +172,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
172
172
|
readonly onEditorChange?: (text: string) => void;
|
|
173
173
|
readonly onPromptSuggestionAccepted?: (text: string) => void;
|
|
174
174
|
readonly onInputSurfaceChanged?: () => void;
|
|
175
|
+
readonly onPromptSuggestionEligibilityChanged?: () => void;
|
|
175
176
|
readonly onCopyText?: (text: string) => void;
|
|
176
177
|
readonly readClipboardContent?: (signal?: AbortSignal) => Promise<PiShellClipboardContent | null>;
|
|
177
178
|
readonly captureClipboardPaste?: () => PasteSource;
|
|
@@ -245,7 +246,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
245
246
|
maxScroll: number;
|
|
246
247
|
cause: "initial" | "steady" | "dock-input" | "follow-shift" | "detached" | "geometry-change" | null;
|
|
247
248
|
};
|
|
248
|
-
/** Visible non-
|
|
249
|
+
/** Visible non-persistent Steering, alignment, and Working rows, for rendering evidence. */
|
|
249
250
|
viewportTransientTailRowCount(): number;
|
|
250
251
|
hasActiveSelection(): boolean;
|
|
251
252
|
setViewportConfig(config: OwnedUiViewportSettings): void;
|
|
@@ -48,6 +48,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
48
48
|
#viewportTheme;
|
|
49
49
|
#onViewportFrame;
|
|
50
50
|
#onInputSurfaceChanged;
|
|
51
|
+
#onPromptSuggestionEligibilityChanged;
|
|
51
52
|
#componentRuntime;
|
|
52
53
|
#toolsExpanded = false;
|
|
53
54
|
#thinkingVisible = true;
|
|
@@ -107,6 +108,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
107
108
|
this.#componentRuntime = handlers;
|
|
108
109
|
this.#onViewportFrame = handlers.onViewportFrame;
|
|
109
110
|
this.#onInputSurfaceChanged = handlers.onInputSurfaceChanged;
|
|
111
|
+
this.#onPromptSuggestionEligibilityChanged = handlers.onPromptSuggestionEligibilityChanged;
|
|
110
112
|
this.#dockInputReuseEnabled = handlers.enableDockInputReuse ?? true;
|
|
111
113
|
this.header = createPiShellHeader({
|
|
112
114
|
...startup,
|
|
@@ -223,8 +225,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
223
225
|
// Invariant: colours come from the active theme, so rendered rows outlive their revision only
|
|
224
226
|
// until the theme under them changes.
|
|
225
227
|
this.#themeUnsubscribe = onPiThemeChange(() => {
|
|
226
|
-
this
|
|
227
|
-
this.#
|
|
228
|
+
this.invalidate();
|
|
229
|
+
this.#componentRuntime.requestRender(true);
|
|
228
230
|
});
|
|
229
231
|
}
|
|
230
232
|
setEditorPaddingX(padding) {
|
|
@@ -327,6 +329,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
327
329
|
this.editor.setPromptSuggestion(text);
|
|
328
330
|
}
|
|
329
331
|
update(view) {
|
|
332
|
+
const becameReady = this.#view.lifecycle !== "ready" && view.lifecycle === "ready";
|
|
330
333
|
if (view.diagnostics.length !== this.#view.diagnostics.length
|
|
331
334
|
|| view.diagnostics.some((diagnostic, index) => diagnostic.sequence !== this.#view.diagnostics[index]?.sequence)) {
|
|
332
335
|
this.#documentLayouts.clear();
|
|
@@ -349,6 +352,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
349
352
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
350
353
|
// Performance: semantic updates already invalidate affected transcript blocks. Chrome is a separate authority.
|
|
351
354
|
this.#invalidateChrome();
|
|
355
|
+
if (becameReady)
|
|
356
|
+
this.#onPromptSuggestionEligibilityChanged?.();
|
|
352
357
|
}
|
|
353
358
|
/**
|
|
354
359
|
* Applies one block: its component is created or updated in place and the order grows
|
|
@@ -458,13 +463,13 @@ export class OwnedUiSessionShellRoot {
|
|
|
458
463
|
if (frame === null) {
|
|
459
464
|
frame = this.#viewportController.compose({
|
|
460
465
|
// Performance: do not copy the complete document on unchanged dock-only input.
|
|
461
|
-
// Steering and Working retain their one non-
|
|
466
|
+
// Steering and Working retain their one non-persistent viewport-tail ownership.
|
|
462
467
|
documentRows: [...document.rows, ...steeringRows, ...statusRows],
|
|
463
468
|
...(this.#viewportController.transcriptPointerSelecting
|
|
464
469
|
? { paintDocumentRow: heldNativeHyperlinkStyle }
|
|
465
470
|
: {}),
|
|
466
|
-
// Invariant:
|
|
467
|
-
//
|
|
471
|
+
// Invariant: this boundary tracks persistent transcript rows for streaming and
|
|
472
|
+
// geometry; transient Steering and Working rows remain complete-frame selectable.
|
|
468
473
|
selectableDocumentRowCount,
|
|
469
474
|
...(document.liveTailStartRow === undefined ? {} : { liveTailStartRow: document.liveTailStartRow }),
|
|
470
475
|
bottomAlignedTailRowCount: steeringRows.length + statusRows.length,
|
|
@@ -527,7 +532,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
527
532
|
cause: frame?.descriptor.cause ?? null,
|
|
528
533
|
};
|
|
529
534
|
}
|
|
530
|
-
/** Visible non-
|
|
535
|
+
/** Visible non-persistent Steering, alignment, and Working rows, for rendering evidence. */
|
|
531
536
|
viewportTransientTailRowCount() {
|
|
532
537
|
return this.#viewportController.frame?.hits.transientTail.length ?? 0;
|
|
533
538
|
}
|
|
@@ -1179,6 +1184,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
1179
1184
|
}
|
|
1180
1185
|
setFocused(focused) {
|
|
1181
1186
|
this.#inputSurface.setFocused?.(focused);
|
|
1187
|
+
if (focused)
|
|
1188
|
+
this.#onPromptSuggestionEligibilityChanged?.();
|
|
1182
1189
|
}
|
|
1183
1190
|
dispose() {
|
|
1184
1191
|
this.#clearCopyAcknowledgement(false);
|
|
@@ -11,6 +11,7 @@ import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../../ui
|
|
|
11
11
|
import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
|
|
12
12
|
import { PINNED_PI_HIDDEN_COMMAND_NAMES, workflowCommandNames } from "../../integrations/pi/engine/workflows.js";
|
|
13
13
|
import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
|
|
14
|
+
import { ProgramStatusReporter } from "../../integrations/pi/components/upstream/program-status-reporter.js";
|
|
14
15
|
import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
|
|
15
16
|
import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission, skillPrompt, skillsFromCommands, } from "../../integrations/pi/components/skills-command.js";
|
|
16
17
|
import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
|
|
@@ -79,6 +80,7 @@ export class OwnedUiSessionShell {
|
|
|
79
80
|
#unbindTerminalSettings;
|
|
80
81
|
#unbindShutdownSettings;
|
|
81
82
|
#terminalProgressEnabled = false;
|
|
83
|
+
#programStatus;
|
|
82
84
|
#showImages = true;
|
|
83
85
|
#imageWidthCells = 80;
|
|
84
86
|
#fullscreenExitOutput = "transcript";
|
|
@@ -199,6 +201,9 @@ export class OwnedUiSessionShell {
|
|
|
199
201
|
this.root.reconcilePromptImageLimitNotice(text);
|
|
200
202
|
},
|
|
201
203
|
onPromptSuggestionAccepted: () => promptSuggestionController?.accept(),
|
|
204
|
+
onPromptSuggestionEligibilityChanged: () => {
|
|
205
|
+
queueMicrotask(() => promptSuggestionController?.restoreAvailable());
|
|
206
|
+
},
|
|
202
207
|
onInputSurfaceChanged: () => {
|
|
203
208
|
promptSuggestionController?.invalidate();
|
|
204
209
|
this.#promptHistory?.synchronize();
|
|
@@ -292,6 +297,8 @@ export class OwnedUiSessionShell {
|
|
|
292
297
|
};
|
|
293
298
|
runtime = new PiTuiRuntimeAdapter(runtimeOptions);
|
|
294
299
|
this.runtime = runtime;
|
|
300
|
+
this.#programStatus = new ProgramStatusReporter(status => { if (this.runtime.active)
|
|
301
|
+
this.runtime.setProgramStatus(status); }, () => this.view().status.footer?.sessionName ?? undefined);
|
|
295
302
|
this.#damageTerminal = damageTerminal ?? null;
|
|
296
303
|
this.#quitOutro = quitOutro;
|
|
297
304
|
this.#reloadPresentation = reloadPresentation;
|
|
@@ -503,6 +510,7 @@ export class OwnedUiSessionShell {
|
|
|
503
510
|
},
|
|
504
511
|
agentDir: this.backend.agentDir,
|
|
505
512
|
setInputSurface: component => this.root.setInputSurface(component, true, "opaque"),
|
|
513
|
+
setProgramStatusBlocked: status => this.#programStatus.setBlocked("extension-dialog", status),
|
|
506
514
|
showOverlay: (component, overlayOptions) => this.runtime.showOverlay(component, overlayOptions),
|
|
507
515
|
listenInput: handler => this.runtime.addInputListener(handler),
|
|
508
516
|
replaceWidget: (key, component, placement) => this.root.setExtensionWidget(key, component, placement),
|
|
@@ -540,6 +548,12 @@ export class OwnedUiSessionShell {
|
|
|
540
548
|
this.#promptSuggestions?.invalidate();
|
|
541
549
|
this.root.resumeViewportFollowing();
|
|
542
550
|
}
|
|
551
|
+
if (event.type === "assistant-message-completed" && !event.successful) {
|
|
552
|
+
this.#programStatus.setErrorMessage(this.view().status.diagnostics.at(-1));
|
|
553
|
+
}
|
|
554
|
+
if (event.type === "agent-run-started" || event.type === "assistant-message-completed" || event.type === "agent-run-settled") {
|
|
555
|
+
this.#programStatus.handleEvent(event);
|
|
556
|
+
}
|
|
543
557
|
if (event.type === "assistant-message-completed") {
|
|
544
558
|
this.root.noteCompletedAssistantMessage();
|
|
545
559
|
const identity = {
|
|
@@ -571,14 +585,22 @@ export class OwnedUiSessionShell {
|
|
|
571
585
|
if (event.type === "status" && /^(Retrying|Compacting)/.test(event.status.workingMessage ?? "")) {
|
|
572
586
|
this.#promptSuggestions?.invalidate();
|
|
573
587
|
}
|
|
574
|
-
if (event.type === "agent-run-settled"
|
|
575
|
-
|
|
588
|
+
if (event.type === "agent-run-settled") {
|
|
589
|
+
const identity = {
|
|
576
590
|
sessionId: event.sessionId,
|
|
577
591
|
sessionGeneration: event.sessionGeneration,
|
|
578
592
|
runSequence: event.runSequence,
|
|
579
593
|
responseSequence: event.responseSequence,
|
|
580
594
|
model: event.model,
|
|
581
|
-
}
|
|
595
|
+
};
|
|
596
|
+
if (event.model === null)
|
|
597
|
+
this.#promptSuggestions?.skip(identity, "no-model");
|
|
598
|
+
else
|
|
599
|
+
this.#promptSuggestions?.settle({ ...identity, model: event.model }, !event.successful ? "failed-response"
|
|
600
|
+
: event.toolContinuation || event.stopReason === "toolUse" ? "tool-continuation"
|
|
601
|
+
: event.stopReason !== "stop" ? "incomplete-response"
|
|
602
|
+
: event.assistantMessageCount < 2 ? "early-conversation"
|
|
603
|
+
: this.root.promptSuggestionPrepareBlockReason());
|
|
582
604
|
}
|
|
583
605
|
if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
|
|
584
606
|
this.#settleStoppedLifecycle();
|
|
@@ -1685,6 +1707,7 @@ export class OwnedUiSessionShell {
|
|
|
1685
1707
|
attempt(() => this.#unsubscribe());
|
|
1686
1708
|
attempt(() => this.#dialogHandle?.hide());
|
|
1687
1709
|
attempt(() => this.#extensionBridge.dispose());
|
|
1710
|
+
attempt(() => this.#programStatus.clear());
|
|
1688
1711
|
// Invariant: from here to the leave nothing but the outro paints. A throttled frame the
|
|
1689
1712
|
// renderer still has queued would otherwise land during the stop-time input drain and
|
|
1690
1713
|
// flash the prompt and footer, whether or not an effect plays.
|
|
@@ -1778,6 +1801,17 @@ export class OwnedUiSessionShell {
|
|
|
1778
1801
|
if (!this.runtime.active)
|
|
1779
1802
|
return;
|
|
1780
1803
|
this.runtime.setTerminalProgress(this.#terminalProgressEnabled && view.lifecycle === "busy");
|
|
1804
|
+
if (view.lifecycle === "stopped" || view.lifecycle === "stopping") {
|
|
1805
|
+
this.#programStatus.clear();
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
this.#programStatus.setBlocked("dialog", view.dialog === null ? undefined : {
|
|
1809
|
+
kind: "question",
|
|
1810
|
+
message: view.dialog.title,
|
|
1811
|
+
});
|
|
1812
|
+
if (view.lifecycle === "failed")
|
|
1813
|
+
this.#programStatus.setErrorMessage(view.status.diagnostics.at(-1));
|
|
1814
|
+
this.#programStatus.report();
|
|
1781
1815
|
}
|
|
1782
1816
|
#syncView() {
|
|
1783
1817
|
this.#streamPresentation.noteImmediatePresentation();
|
|
@@ -1789,6 +1823,7 @@ export class OwnedUiSessionShell {
|
|
|
1789
1823
|
this.root.resetPendingPastes();
|
|
1790
1824
|
this.#promptSuggestions?.invalidate();
|
|
1791
1825
|
this.#sessionGeneration = this.backend.sessionGeneration;
|
|
1826
|
+
this.#programStatus.reset();
|
|
1792
1827
|
// Invariant: delivery recovery invalidates callbacks, not same-session local recall or its draft.
|
|
1793
1828
|
if (this.#sessionBindingGeneration !== this.backend.sessionBindingGeneration) {
|
|
1794
1829
|
this.#sessionBindingGeneration = this.backend.sessionBindingGeneration;
|
|
@@ -2124,6 +2159,11 @@ export class OwnedUiSessionShell {
|
|
|
2124
2159
|
}, request.providerName);
|
|
2125
2160
|
this.#activeLoginDialog = dialog;
|
|
2126
2161
|
this.root.setInputSurface(dialog);
|
|
2162
|
+
this.#programStatus.setBlocked("authentication", {
|
|
2163
|
+
kind: "auth",
|
|
2164
|
+
message: `Log in to ${request.providerName}`,
|
|
2165
|
+
});
|
|
2166
|
+
this.#syncTerminalProgress(this.view());
|
|
2127
2167
|
this.runtime.requestRender();
|
|
2128
2168
|
}
|
|
2129
2169
|
#requestWorkflowInput(request) {
|
|
@@ -2180,6 +2220,8 @@ export class OwnedUiSessionShell {
|
|
|
2180
2220
|
return;
|
|
2181
2221
|
this.#activeLoginDialog = undefined;
|
|
2182
2222
|
this.root.setInputSurface(null);
|
|
2223
|
+
this.#programStatus.setBlocked("authentication", undefined);
|
|
2224
|
+
this.#syncTerminalProgress(this.view());
|
|
2183
2225
|
this.runtime.requestRender();
|
|
2184
2226
|
}
|
|
2185
2227
|
async #execute(command, draft) {
|