@timurproko/a1 0.1.8-dev.271 → 0.1.8-dev.277
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/dist/composition/owned-ui.js +6 -0
- package/dist/contracts/owned-ui/index.d.ts +1 -0
- package/dist/contracts/owned-ui/index.js +1 -0
- package/dist/contracts/owned-ui/model.d.ts +52 -0
- package/dist/contracts/owned-ui/prompt-suggestions.d.ts +3 -0
- package/dist/contracts/owned-ui/prompt-suggestions.js +41 -0
- package/dist/contracts/owned-ui/validation.d.ts +5 -1
- package/dist/contracts/owned-ui/validation.js +67 -3
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +5 -3
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +15 -0
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +8 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +15 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +102 -3
- package/dist/integrations/pi/engine/adapter.d.ts +3 -2
- package/dist/integrations/pi/engine/adapter.js +106 -2
- package/dist/integrations/pi/engine/conformance.d.ts +1 -1
- package/dist/integrations/pi/engine/conformance.js +2 -2
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +1 -1
- package/dist/integrations/pi/session-ui/index.d.ts +1 -0
- package/dist/integrations/pi/session-ui/index.js +1 -0
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.d.ts +27 -0
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.js +132 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +12 -1
- package/dist/integrations/pi/session-ui/session-shell-root.js +27 -1
- package/dist/integrations/pi/session-ui/session-shell.js +86 -2
- 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/ui/settings/declarations.d.ts +1 -1
- package/dist/ui/settings/declarations.js +9 -1
- package/dist/ui/settings/migrations.js +7 -0
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import { promisify } from "node:util";
|
|
|
8
8
|
import { PRODUCT_IDENTITY } from "../../../product-identity.js";
|
|
9
9
|
import { configureOwnedHttpDispatcher } from "./http-dispatcher.js";
|
|
10
10
|
import { copyToClipboard, CredentialSynchronizationError, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, VERSION, } from "@earendil-works/pi-coding-agent";
|
|
11
|
-
import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiSnapshot, } from "../../../contracts/owned-ui/index.js";
|
|
11
|
+
import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiSnapshot, normalizePromptSuggestionCandidate, } from "../../../contracts/owned-ui/index.js";
|
|
12
12
|
import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
|
|
13
13
|
import { createPiRuntimeIntegration } from "./runtime-integration.js";
|
|
14
14
|
import { PiSessionCommandIntegration } from "./session-integration.js";
|
|
@@ -129,6 +129,8 @@ export class PiEngineAdapter {
|
|
|
129
129
|
#eventQueueProcessing;
|
|
130
130
|
#droppedEventCount = 0;
|
|
131
131
|
#agentRunActive = false;
|
|
132
|
+
#agentRunSequence = 0;
|
|
133
|
+
#assistantResponseSequence = 0;
|
|
132
134
|
#statusKind = null;
|
|
133
135
|
#sessionCommands;
|
|
134
136
|
#gitBranch = null;
|
|
@@ -200,6 +202,69 @@ export class PiEngineAdapter {
|
|
|
200
202
|
get disposed() {
|
|
201
203
|
return this.#disposed;
|
|
202
204
|
}
|
|
205
|
+
async generate(request) {
|
|
206
|
+
assertOwnedUiPromptSuggestionRequest(request);
|
|
207
|
+
const identity = request.identity;
|
|
208
|
+
const session = this.#session;
|
|
209
|
+
const runtime = this.#runtime;
|
|
210
|
+
const activeModel = this.#activeModel;
|
|
211
|
+
if (this.#disposed || session === undefined || runtime === undefined || request.signal.aborted
|
|
212
|
+
|| identity.sessionId !== this.#sessionId
|
|
213
|
+
|| identity.sessionGeneration !== this.#sessionGeneration
|
|
214
|
+
|| identity.runSequence !== this.#agentRunSequence
|
|
215
|
+
|| identity.responseSequence !== this.#assistantResponseSequence
|
|
216
|
+
|| activeModel === null
|
|
217
|
+
|| identity.model.providerId !== activeModel.providerId
|
|
218
|
+
|| identity.model.modelId !== activeModel.modelId) {
|
|
219
|
+
return { identity, text: null };
|
|
220
|
+
}
|
|
221
|
+
const model = session.model;
|
|
222
|
+
const agentState = session.agent.state;
|
|
223
|
+
if (model === undefined || typeof runtime.services.modelRuntime.completeSimple !== "function") {
|
|
224
|
+
return { identity, text: null };
|
|
225
|
+
}
|
|
226
|
+
const messages = agentState.messages.filter(message => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
|
|
227
|
+
const reasoning = session.thinkingLevel === "minimal"
|
|
228
|
+
|| session.thinkingLevel === "low"
|
|
229
|
+
|| session.thinkingLevel === "medium"
|
|
230
|
+
|| session.thinkingLevel === "high"
|
|
231
|
+
|| session.thinkingLevel === "xhigh"
|
|
232
|
+
|| session.thinkingLevel === "max"
|
|
233
|
+
? session.thinkingLevel
|
|
234
|
+
: undefined;
|
|
235
|
+
const response = await runtime.services.modelRuntime.completeSimple(model, {
|
|
236
|
+
systemPrompt: agentState.systemPrompt,
|
|
237
|
+
messages: [
|
|
238
|
+
...messages,
|
|
239
|
+
{ role: "user", content: CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, timestamp: Date.now() },
|
|
240
|
+
],
|
|
241
|
+
tools: agentState.tools,
|
|
242
|
+
}, {
|
|
243
|
+
signal: request.signal,
|
|
244
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
245
|
+
});
|
|
246
|
+
if (isRecord(response)
|
|
247
|
+
&& (stringValue(response.errorMessage) !== undefined
|
|
248
|
+
|| stringValue(response.stopReason) === "error"
|
|
249
|
+
|| stringValue(response.stopReason) === "aborted")) {
|
|
250
|
+
const result = { identity, text: null };
|
|
251
|
+
assertOwnedUiPromptSuggestionResult(result);
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
const content = Array.isArray(response.content) ? response.content : [];
|
|
255
|
+
if (content.some(block => isRecord(block) && block.type === "toolCall")) {
|
|
256
|
+
const result = { identity, text: null };
|
|
257
|
+
assertOwnedUiPromptSuggestionResult(result);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
const textBlock = content.find(block => isRecord(block) && block.type === "text" && typeof block.text === "string");
|
|
261
|
+
const result = {
|
|
262
|
+
identity,
|
|
263
|
+
text: normalizePromptSuggestionCandidate(isRecord(textBlock) && typeof textBlock.text === "string" ? textBlock.text : null),
|
|
264
|
+
};
|
|
265
|
+
assertOwnedUiPromptSuggestionResult(result);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
203
268
|
async start() {
|
|
204
269
|
if (this.#runtime)
|
|
205
270
|
return this.view();
|
|
@@ -1773,6 +1838,8 @@ export class PiEngineAdapter {
|
|
|
1773
1838
|
this.#status = { ...this.#status, workingMessage: null, badges: [] };
|
|
1774
1839
|
this.#statusKind = null;
|
|
1775
1840
|
this.#agentRunActive = false;
|
|
1841
|
+
this.#agentRunSequence = 0;
|
|
1842
|
+
this.#assistantResponseSequence = 0;
|
|
1776
1843
|
this.#activeModel = readModel(session.model);
|
|
1777
1844
|
this.#reconcileActiveModelAvailability();
|
|
1778
1845
|
this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
|
|
@@ -1880,6 +1947,7 @@ export class PiEngineAdapter {
|
|
|
1880
1947
|
switch (event.type) {
|
|
1881
1948
|
case "agent_start":
|
|
1882
1949
|
this.#agentRunActive = true;
|
|
1950
|
+
this.#agentRunSequence += 1;
|
|
1883
1951
|
this.#emitEvent({ type: "agent-run-started" });
|
|
1884
1952
|
this.#enterWorkState("working", "Working");
|
|
1885
1953
|
return;
|
|
@@ -1906,7 +1974,26 @@ export class PiEngineAdapter {
|
|
|
1906
1974
|
// finalization is intentionally not a substitute: rebuilds, retries,
|
|
1907
1975
|
// thinking parts, and tool rows can all finalize independently.
|
|
1908
1976
|
if (isRecord(event.message) && event.message.role === "assistant") {
|
|
1909
|
-
this.#
|
|
1977
|
+
this.#assistantResponseSequence += 1;
|
|
1978
|
+
const content = Array.isArray(event.message.content) ? event.message.content : [];
|
|
1979
|
+
const stopReason = stringValue(event.message.stopReason) ?? null;
|
|
1980
|
+
const toolContinuation = stopReason === "toolUse"
|
|
1981
|
+
|| content.some(item => isRecord(item) && item.type === "toolCall");
|
|
1982
|
+
const successful = stringValue(event.message.errorMessage) === undefined
|
|
1983
|
+
&& stopReason !== "error"
|
|
1984
|
+
&& stopReason !== "aborted"
|
|
1985
|
+
&& textFromContent(content).trim().length > 0;
|
|
1986
|
+
this.#emitEvent({
|
|
1987
|
+
type: "assistant-message-completed",
|
|
1988
|
+
sessionGeneration: this.#sessionGeneration,
|
|
1989
|
+
runSequence: this.#agentRunSequence,
|
|
1990
|
+
responseSequence: this.#assistantResponseSequence,
|
|
1991
|
+
model: this.#activeModel,
|
|
1992
|
+
assistantMessageCount: this.#transcript.filter(block => block.kind === "assistant").length,
|
|
1993
|
+
successful,
|
|
1994
|
+
stopReason,
|
|
1995
|
+
toolContinuation,
|
|
1996
|
+
});
|
|
1910
1997
|
}
|
|
1911
1998
|
return;
|
|
1912
1999
|
case "turn_end":
|
|
@@ -1951,6 +2038,23 @@ export class PiEngineAdapter {
|
|
|
1951
2038
|
this.#leaveWorkStates();
|
|
1952
2039
|
}
|
|
1953
2040
|
this.#emitView();
|
|
2041
|
+
if (event.type === "agent_settled") {
|
|
2042
|
+
const assistants = finalMessages.filter(message => isRecord(message) && message.role === "assistant");
|
|
2043
|
+
const lastAssistant = assistants.at(-1);
|
|
2044
|
+
const successful = lastAssistant !== undefined
|
|
2045
|
+
&& stringValue(lastAssistant.errorMessage) === undefined
|
|
2046
|
+
&& stringValue(lastAssistant.stopReason) !== "error"
|
|
2047
|
+
&& stringValue(lastAssistant.stopReason) !== "aborted";
|
|
2048
|
+
this.#emitEvent({
|
|
2049
|
+
type: "agent-run-settled",
|
|
2050
|
+
sessionGeneration: this.#sessionGeneration,
|
|
2051
|
+
runSequence: this.#agentRunSequence,
|
|
2052
|
+
responseSequence: this.#assistantResponseSequence,
|
|
2053
|
+
model: this.#activeModel,
|
|
2054
|
+
assistantMessageCount: assistants.length,
|
|
2055
|
+
successful,
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
1954
2058
|
return;
|
|
1955
2059
|
}
|
|
1956
2060
|
case "queue_update": {
|
|
@@ -7,7 +7,7 @@ export declare const REQUIRED_PI_CAPABILITY_OPERATIONS: Readonly<{
|
|
|
7
7
|
readonly "public-exports": readonly ["services.create", "session.create", "runtime.create"];
|
|
8
8
|
readonly "session-lifecycle": readonly ["session.new", "session.resume", "session.rebind", "session.dispose"];
|
|
9
9
|
readonly "commands-events": readonly ["prompt", "steer", "followUp", "abort", "compact", "setModel", "setThinkingLevel", "subscribe", "dispose"];
|
|
10
|
-
readonly "models-authentication": readonly ["models.list", "models.refresh", "auth.status", "auth.login", "auth.logout", "auth.cancel"];
|
|
10
|
+
readonly "models-authentication": readonly ["models.list", "models.refresh", "models.completeSimple", "auth.status", "auth.login", "auth.logout", "auth.cancel"];
|
|
11
11
|
readonly settings: readonly ["settings.read", "settings.write", "settings.flush"];
|
|
12
12
|
readonly "resources-extensions": readonly ["resources.discover", "extensions.inline", "extensions.bind", "extensions.reload", "renderers.invoke"];
|
|
13
13
|
readonly workflows: readonly ["workflow.route", "workflow.validate", "workflow.diagnostics"];
|
|
@@ -9,7 +9,7 @@ export const REQUIRED_PI_CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
9
9
|
"public-exports": ["services.create", "session.create", "runtime.create"],
|
|
10
10
|
"session-lifecycle": ["session.new", "session.resume", "session.rebind", "session.dispose"],
|
|
11
11
|
"commands-events": ["prompt", "steer", "followUp", "abort", "compact", "setModel", "setThinkingLevel", "subscribe", "dispose"],
|
|
12
|
-
"models-authentication": ["models.list", "models.refresh", "auth.status", "auth.login", "auth.logout", "auth.cancel"],
|
|
12
|
+
"models-authentication": ["models.list", "models.refresh", "models.completeSimple", "auth.status", "auth.login", "auth.logout", "auth.cancel"],
|
|
13
13
|
settings: ["settings.read", "settings.write", "settings.flush"],
|
|
14
14
|
"resources-extensions": ["resources.discover", "extensions.inline", "extensions.bind", "extensions.reload", "renderers.invoke"],
|
|
15
15
|
workflows: ["workflow.route", "workflow.validate", "workflow.diagnostics"],
|
|
@@ -86,7 +86,7 @@ export async function runPiUpgradeConformance() {
|
|
|
86
86
|
const session = created.session;
|
|
87
87
|
sessionId = session.sessionId;
|
|
88
88
|
requireMethods(session, "session commands", ["prompt", "steer", "followUp", "abort", "compact", "setModel", "setThinkingLevel", "subscribe", "dispose"]);
|
|
89
|
-
requireMethods(services.modelRuntime, "models/authentication", ["getModels", "getModel", "checkAuth", "login", "logout", "refresh"]);
|
|
89
|
+
requireMethods(services.modelRuntime, "models/authentication", ["getModels", "getModel", "completeSimple", "checkAuth", "login", "logout", "refresh"]);
|
|
90
90
|
requireMethods(services.settingsManager, "settings", ["getGlobalSettings", "getProjectSettings", "flush"]);
|
|
91
91
|
requireMethods(services.resourceLoader, "resources/extensions", ["getExtensions", "getSkills", "getPrompts", "getThemes", "reload"]);
|
|
92
92
|
const unsubscribe = session.subscribe(() => undefined);
|
|
@@ -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" | "session" | "scoped-models" | "export" | "import" | "copy" | "changelog" | "hotkeys" | "fork" | "clone" | "tree" | "trust" | "
|
|
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" | "tree" | "trust" | "debug" | "arminsayshi" | "dementedelves")[];
|
|
21
21
|
export declare const PI_WORKFLOW_CAPABILITIES: readonly WorkflowCapability[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type OwnedUiPromptSuggestionGeneratorPort, type OwnedUiPromptSuggestionIdentity, type OwnedUiPromptSuggestionState } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
export interface ContextualPromptSuggestionSurface {
|
|
3
|
+
canPresent(identity: OwnedUiPromptSuggestionIdentity): boolean;
|
|
4
|
+
present(text: string): boolean;
|
|
5
|
+
clear(): void;
|
|
6
|
+
requestRender(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface ContextualPromptSuggestionControllerOptions {
|
|
9
|
+
readonly generator: OwnedUiPromptSuggestionGeneratorPort;
|
|
10
|
+
readonly surface: ContextualPromptSuggestionSurface;
|
|
11
|
+
readonly enabled: boolean;
|
|
12
|
+
readonly timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Prefetches one candidate, holds it invisibly, and publishes only after matching settlement. */
|
|
15
|
+
export declare class ContextualPromptSuggestionController {
|
|
16
|
+
#private;
|
|
17
|
+
readonly options: ContextualPromptSuggestionControllerOptions;
|
|
18
|
+
constructor(options: ContextualPromptSuggestionControllerOptions);
|
|
19
|
+
get state(): OwnedUiPromptSuggestionState;
|
|
20
|
+
setEnabled(enabled: boolean): void;
|
|
21
|
+
consider(identity: OwnedUiPromptSuggestionIdentity, eligible: boolean): void;
|
|
22
|
+
settle(identity: OwnedUiPromptSuggestionIdentity): void;
|
|
23
|
+
accept(): void;
|
|
24
|
+
invalidate(): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function samePromptSuggestionIdentity(left: OwnedUiPromptSuggestionIdentity, right: OwnedUiPromptSuggestionIdentity): boolean;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { assertOwnedUiPromptSuggestionResult, normalizePromptSuggestionCandidate, } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
/** Prefetches one candidate, holds it invisibly, and publishes only after matching settlement. */
|
|
3
|
+
export class ContextualPromptSuggestionController {
|
|
4
|
+
options;
|
|
5
|
+
#state = { status: "idle" };
|
|
6
|
+
#epoch = 0;
|
|
7
|
+
#abort = null;
|
|
8
|
+
#lastConsidered = "";
|
|
9
|
+
#enabled;
|
|
10
|
+
#disposed = false;
|
|
11
|
+
#timeoutMs;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.#enabled = options.enabled;
|
|
15
|
+
this.#timeoutMs = options.timeoutMs ?? 15_000;
|
|
16
|
+
}
|
|
17
|
+
get state() { return this.#state; }
|
|
18
|
+
setEnabled(enabled) {
|
|
19
|
+
if (this.#enabled === enabled)
|
|
20
|
+
return;
|
|
21
|
+
this.#enabled = enabled;
|
|
22
|
+
this.invalidate();
|
|
23
|
+
}
|
|
24
|
+
consider(identity, eligible) {
|
|
25
|
+
const key = identityKey(identity);
|
|
26
|
+
if (key === this.#lastConsidered)
|
|
27
|
+
return;
|
|
28
|
+
this.#lastConsidered = key;
|
|
29
|
+
this.invalidate();
|
|
30
|
+
if (!this.#enabled || this.#disposed || !eligible)
|
|
31
|
+
return;
|
|
32
|
+
const epoch = this.#epoch;
|
|
33
|
+
const abort = new AbortController();
|
|
34
|
+
this.#abort = abort;
|
|
35
|
+
this.#state = { status: "generating", identity, settled: false };
|
|
36
|
+
const timeout = setTimeout(() => {
|
|
37
|
+
abort.abort();
|
|
38
|
+
if (this.#epoch === epoch && this.#state.status === "generating") {
|
|
39
|
+
this.#abort = null;
|
|
40
|
+
this.#state = { status: "idle" };
|
|
41
|
+
}
|
|
42
|
+
}, this.#timeoutMs);
|
|
43
|
+
timeout.unref?.();
|
|
44
|
+
void this.options.generator.generate({ identity, signal: abort.signal })
|
|
45
|
+
.then(result => this.#receive(epoch, identity, result))
|
|
46
|
+
.catch(() => this.#fail(epoch))
|
|
47
|
+
.finally(() => {
|
|
48
|
+
clearTimeout(timeout);
|
|
49
|
+
if (this.#epoch === epoch)
|
|
50
|
+
this.#abort = null;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
settle(identity) {
|
|
54
|
+
if (this.#disposed || !this.#enabled || this.#state.status === "idle")
|
|
55
|
+
return;
|
|
56
|
+
if (!samePromptSuggestionIdentity(this.#state.identity, identity)) {
|
|
57
|
+
this.invalidate();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (this.#state.status === "generating") {
|
|
61
|
+
this.#state = { ...this.#state, settled: true };
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (this.#state.status === "prepared")
|
|
65
|
+
this.#show(identity, this.#state.text);
|
|
66
|
+
}
|
|
67
|
+
accept() {
|
|
68
|
+
this.#invalidate(false);
|
|
69
|
+
}
|
|
70
|
+
invalidate() {
|
|
71
|
+
this.#invalidate(true);
|
|
72
|
+
}
|
|
73
|
+
dispose() {
|
|
74
|
+
this.#disposed = true;
|
|
75
|
+
this.invalidate();
|
|
76
|
+
}
|
|
77
|
+
#invalidate(clearSurface) {
|
|
78
|
+
const hadVisibleSuggestion = this.#state.status === "available";
|
|
79
|
+
this.#epoch += 1;
|
|
80
|
+
this.#abort?.abort();
|
|
81
|
+
this.#abort = null;
|
|
82
|
+
this.#state = { status: "idle" };
|
|
83
|
+
if (clearSurface && hadVisibleSuggestion) {
|
|
84
|
+
this.options.surface.clear();
|
|
85
|
+
this.options.surface.requestRender();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
#fail(epoch) {
|
|
89
|
+
if (this.#epoch !== epoch)
|
|
90
|
+
return;
|
|
91
|
+
this.#abort = null;
|
|
92
|
+
this.#state = { status: "idle" };
|
|
93
|
+
}
|
|
94
|
+
#receive(epoch, identity, result) {
|
|
95
|
+
assertOwnedUiPromptSuggestionResult(result);
|
|
96
|
+
if (this.#disposed || !this.#enabled || this.#epoch !== epoch || this.#state.status !== "generating")
|
|
97
|
+
return;
|
|
98
|
+
if (!samePromptSuggestionIdentity(identity, result.identity)) {
|
|
99
|
+
this.invalidate();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const text = normalizePromptSuggestionCandidate(result.text);
|
|
103
|
+
if (text === null) {
|
|
104
|
+
this.#fail(epoch);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.#abort = null;
|
|
108
|
+
if (this.#state.settled)
|
|
109
|
+
this.#show(identity, text);
|
|
110
|
+
else
|
|
111
|
+
this.#state = { status: "prepared", identity, text };
|
|
112
|
+
}
|
|
113
|
+
#show(identity, text) {
|
|
114
|
+
if (!this.options.surface.canPresent(identity) || !this.options.surface.present(text)) {
|
|
115
|
+
this.invalidate();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.#state = { status: "available", identity, text };
|
|
119
|
+
this.options.surface.requestRender();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export function samePromptSuggestionIdentity(left, right) {
|
|
123
|
+
return left.sessionId === right.sessionId
|
|
124
|
+
&& left.sessionGeneration === right.sessionGeneration
|
|
125
|
+
&& left.runSequence === right.runSequence
|
|
126
|
+
&& left.responseSequence === right.responseSequence
|
|
127
|
+
&& left.model.providerId === right.model.providerId
|
|
128
|
+
&& left.model.modelId === right.model.modelId;
|
|
129
|
+
}
|
|
130
|
+
function identityKey(identity) {
|
|
131
|
+
return `${identity.sessionId}\0${identity.sessionGeneration}\0${identity.runSequence}\0${identity.responseSequence}\0${identity.model.providerId}\0${identity.model.modelId}`;
|
|
132
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OwnedUiSessionViewModel, OwnedUiViewportSettings, OwnedUiViewportSettingsPort } from "../../../contracts/owned-ui/index.js";
|
|
1
|
+
import type { OwnedUiPromptSuggestionGeneratorPort, OwnedUiSessionViewModel, OwnedUiViewportSettings, OwnedUiViewportSettingsPort } from "../../../contracts/owned-ui/index.js";
|
|
2
2
|
import type { UiRouteHost } from "../../../ui/apps/index.js";
|
|
3
3
|
import { type TranscriptViewportFrame, type TranscriptViewportFrameDescriptor } from "../../../ui/components/index.js";
|
|
4
4
|
import { type PiEngineAdapter, type PiWorkflowMessage, type PiWorkflowResult } from "../engine/index.js";
|
|
@@ -39,6 +39,11 @@ export interface OwnedUiSessionShellOptions {
|
|
|
39
39
|
readonly scheduler?: StreamPresentationScheduler;
|
|
40
40
|
};
|
|
41
41
|
/** Optional deterministic seam for keyboard scheduling and phase evidence. */
|
|
42
|
+
readonly promptSuggestions?: {
|
|
43
|
+
readonly generator: OwnedUiPromptSuggestionGeneratorPort;
|
|
44
|
+
readonly enabled: () => boolean;
|
|
45
|
+
readonly onChange: (listener: (enabled: boolean) => void) => () => void;
|
|
46
|
+
};
|
|
42
47
|
readonly inputPresentation?: {
|
|
43
48
|
readonly scheduler?: PiTuiInputCoordinationScheduler;
|
|
44
49
|
readonly onEvent?: (event: PiTuiInputDiagnosticsEvent) => void;
|
|
@@ -73,6 +78,9 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
73
78
|
readonly onMessageCopy?: () => void;
|
|
74
79
|
readonly onFollowUp?: () => void;
|
|
75
80
|
readonly onDequeue?: () => void;
|
|
81
|
+
readonly onEditorChange?: (text: string) => void;
|
|
82
|
+
readonly onPromptSuggestionAccepted?: (text: string) => void;
|
|
83
|
+
readonly onInputSurfaceChanged?: () => void;
|
|
76
84
|
readonly onCopyText?: (text: string) => void;
|
|
77
85
|
readonly readClipboardContent?: () => Promise<PiShellClipboardContent | null>;
|
|
78
86
|
}, startup?: PiShellHeaderOptions, agentDir?: string, extensionRenderers?: PiShellExtensionRendererResolver, sessionLayout?: "pinned" | "custom-viewport", imageAssets?: PiShellImageAssetResolver);
|
|
@@ -83,6 +91,9 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
83
91
|
setMermaidRenderingMode(mode: "off" | "final" | "streaming"): void;
|
|
84
92
|
setImagePresentation(showImages: boolean, imageWidthCells: number): void;
|
|
85
93
|
preparePromptSubmission(text: string): PreparedPrompt;
|
|
94
|
+
canPreparePromptSuggestion(): boolean;
|
|
95
|
+
canPresentPromptSuggestion(): boolean;
|
|
96
|
+
setPromptSuggestion(text: string | null): void;
|
|
86
97
|
update(view: OwnedUiSessionViewModel): void;
|
|
87
98
|
/**
|
|
88
99
|
* Applies one block: its component is created or updated in place and the order grows
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { backgroundSgrSpan, composeSubmittedPromptRows, displayWidth, formatSubmittedPromptTime, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, progressStatusText, submittedPromptLayout, stripAnsi, } from "../../../ui/components/index.js";
|
|
1
|
+
import { PROMPT_GLYPH, backgroundSgrSpan, caretCell, composeSubmittedPromptRows, displayWidth, faint, formatSubmittedPromptTime, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, progressStatusText, submittedPromptLayout, stripAnsi, } from "../../../ui/components/index.js";
|
|
2
2
|
import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "../engine/index.js";
|
|
3
3
|
import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, piShellVisibleWidth, piTheme, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/index.js";
|
|
4
4
|
import { PiTuiRuntimeAdapter, classifyPiTuiInput, } from "../tui-runtime/index.js";
|
|
@@ -29,6 +29,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
29
29
|
#viewportController;
|
|
30
30
|
#viewportTheme;
|
|
31
31
|
#onViewportFrame;
|
|
32
|
+
#onInputSurfaceChanged;
|
|
32
33
|
#componentRuntime;
|
|
33
34
|
#toolsExpanded = false;
|
|
34
35
|
#thinkingVisible = true;
|
|
@@ -73,6 +74,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
73
74
|
this.#imageAssets = imageAssets;
|
|
74
75
|
this.#componentRuntime = handlers;
|
|
75
76
|
this.#onViewportFrame = handlers.onViewportFrame;
|
|
77
|
+
this.#onInputSurfaceChanged = handlers.onInputSurfaceChanged;
|
|
76
78
|
this.#dockInputReuseEnabled = handlers.enableDockInputReuse ?? true;
|
|
77
79
|
this.header = createPiShellHeader(startup);
|
|
78
80
|
this.resources = createPiShellLoadedResources(startup.resources ?? [], startup.expanded ?? false);
|
|
@@ -107,6 +109,15 @@ export class OwnedUiSessionShellRoot {
|
|
|
107
109
|
cwd,
|
|
108
110
|
...(agentDir === undefined ? {} : { agentDir }),
|
|
109
111
|
onToolsExpand: () => this.#setToolsExpanded(!this.#toolsExpanded),
|
|
112
|
+
...(this.#customViewport ? {
|
|
113
|
+
promptPresentation: {
|
|
114
|
+
prefix: PROMPT_GLYPH,
|
|
115
|
+
styleSuggestion: faint,
|
|
116
|
+
styleSuggestionCaret: caretCell,
|
|
117
|
+
},
|
|
118
|
+
} : {}),
|
|
119
|
+
...(handlers.onEditorChange === undefined ? {} : { onChange: handlers.onEditorChange }),
|
|
120
|
+
...(handlers.onPromptSuggestionAccepted === undefined ? {} : { onPromptSuggestionAccepted: handlers.onPromptSuggestionAccepted }),
|
|
110
121
|
});
|
|
111
122
|
this.#viewportController = new SessionViewportController({
|
|
112
123
|
enabled: this.#customViewport,
|
|
@@ -183,6 +194,20 @@ export class OwnedUiSessionShellRoot {
|
|
|
183
194
|
preparePromptSubmission(text) {
|
|
184
195
|
return this.#promptChips.prepareSubmission(text);
|
|
185
196
|
}
|
|
197
|
+
canPreparePromptSuggestion() {
|
|
198
|
+
return this.usesDefaultInputSurface()
|
|
199
|
+
&& this.#view.dialog === null
|
|
200
|
+
&& this.#view.overlay === null
|
|
201
|
+
&& this.editor.getText().length === 0;
|
|
202
|
+
}
|
|
203
|
+
canPresentPromptSuggestion() {
|
|
204
|
+
return this.canPreparePromptSuggestion()
|
|
205
|
+
&& this.#view.lifecycle === "ready"
|
|
206
|
+
&& this.editor.canPresentPromptSuggestion();
|
|
207
|
+
}
|
|
208
|
+
setPromptSuggestion(text) {
|
|
209
|
+
this.editor.setPromptSuggestion(text);
|
|
210
|
+
}
|
|
186
211
|
update(view) {
|
|
187
212
|
if (view.diagnostics.length !== this.#view.diagnostics.length
|
|
188
213
|
|| view.diagnostics.some((diagnostic, index) => diagnostic.sequence !== this.#view.diagnostics[index]?.sequence)) {
|
|
@@ -769,6 +794,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
769
794
|
this.#inputSurface.dispose?.();
|
|
770
795
|
this.#inputSurface = next;
|
|
771
796
|
this.#inputSurfaceCoordination = nextCoordination;
|
|
797
|
+
this.#onInputSurfaceChanged?.();
|
|
772
798
|
this.#inputSurface.setFocused?.(true);
|
|
773
799
|
this.invalidate();
|
|
774
800
|
}
|