@timurproko/a1 0.1.8-dev.508 → 0.1.8-dev.510
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 +44 -1
- package/bin/activate.js +8 -0
- package/bin/sync-pi-tui-proxy.js +7 -0
- package/dist/app/session-shell/index.d.ts +1 -1
- package/dist/app/session-shell/prompt-suggestion-controller.d.ts +2 -0
- package/dist/app/session-shell/prompt-suggestion-controller.js +12 -0
- package/dist/app/session-shell/session-shell-root.d.ts +10 -0
- package/dist/app/session-shell/session-shell-root.js +12 -2
- package/dist/app/session-shell/session-shell.d.ts +8 -1
- package/dist/app/session-shell/session-shell.js +217 -18
- package/dist/composition/index.d.ts +1 -1
- package/dist/composition/owned-ui.js +21 -3
- package/dist/composition/settings-route-host.d.ts +17 -4
- package/dist/composition/settings-route-host.js +47 -16
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/model.d.ts +1 -5
- package/dist/features/owned-ui/index.d.ts +3 -0
- package/dist/features/owned-ui/index.js +2 -0
- package/dist/features/owned-ui/reference-routes.d.ts +6 -0
- package/dist/features/owned-ui/reference-routes.js +6 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +37 -0
- package/dist/features/owned-ui/reference-screen-app.js +232 -0
- package/dist/features/owned-ui/settings-app.js +2 -7
- package/dist/foundation/release/index.d.ts +2 -0
- package/dist/foundation/release/index.js +1 -0
- package/dist/foundation/release/update-activation.d.ts +72 -0
- package/dist/foundation/release/update-activation.js +204 -0
- package/dist/foundation/release/update.d.ts +5 -10
- package/dist/foundation/release/update.js +39 -38
- package/dist/integrations/pi/components/index.d.ts +4 -2
- package/dist/integrations/pi/components/index.js +2 -1
- package/dist/integrations/pi/components/models-dialog.d.ts +55 -0
- package/dist/integrations/pi/components/models-dialog.js +310 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +10 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +29 -5
- package/dist/integrations/pi/components/shell-footer-status.js +2 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +10 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +31 -11
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +11 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +13 -0
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +8 -0
- package/dist/integrations/pi/components/skills-command.d.ts +47 -0
- package/dist/integrations/pi/components/skills-command.js +118 -0
- package/dist/integrations/pi/components/skills-dialog.d.ts +9 -0
- package/dist/integrations/pi/components/skills-dialog.js +112 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +42 -7
- package/dist/integrations/pi/engine/adapter.d.ts +5 -1
- package/dist/integrations/pi/engine/adapter.js +13 -1
- package/dist/integrations/pi/engine/index.d.ts +2 -2
- package/dist/integrations/pi/engine/index.js +1 -1
- package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
- package/dist/integrations/pi/engine/resource-catalog.js +19 -9
- package/dist/integrations/pi/engine/workflow-contexts.d.ts +8 -1
- package/dist/integrations/pi/engine/workflow-contexts.js +52 -8
- package/dist/integrations/pi/engine/workflow-runner.js +98 -74
- package/dist/integrations/pi/engine/workflows.d.ts +26 -2
- package/dist/integrations/pi/engine/workflows.js +8 -0
- 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/runtime-payload-inventory.json +1 -0
- package/dist/ui/apps/contracts.d.ts +9 -1
- package/dist/ui/apps/index.d.ts +1 -1
- package/dist/ui/settings/declarations.d.ts +15 -29
- package/dist/ui/settings/declarations.js +14 -25
- package/dist/ui/settings/index.d.ts +1 -1
- package/dist/ui/settings/index.js +1 -1
- package/dist/ui/settings/migrations.js +18 -0
- package/docs/architecture/internal-naming.md +2 -0
- package/docs/architecture/prompt-suggestions.md +1 -1
- package/docs/architecture/ui-reference-provenance.md +1 -0
- package/docs/features/modal-content-interaction.md +3 -2
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -53,10 +53,40 @@ shown on the level name in the status bar, not on the input bars.
|
|
|
53
53
|
|
|
54
54
|
In bare A1, **Ctrl+L cycles the thinking level**. **Shift+Tab is unassigned** and
|
|
55
55
|
performs no agent-input action, including while a suggestion is visible. Model
|
|
56
|
-
selection has **no default shortcut**; use `/
|
|
56
|
+
selection has **no default shortcut**; use `/models`. Explicit keybinding overrides
|
|
57
57
|
remain configurable, and `/hotkeys` shows the resolved bindings. The `a1 pi`
|
|
58
58
|
comparison profile keeps its original presentation and shortcuts.
|
|
59
59
|
|
|
60
|
+
## Models
|
|
61
|
+
|
|
62
|
+
Bare A1 has one model command, `/models`, which replaces the pinned `/model` and
|
|
63
|
+
`/scoped-models` pair (those remain in the `a1 pi` comparison profile). The dialog
|
|
64
|
+
lists every authenticated model with `all` and `scoped` filters (Tab), a search box,
|
|
65
|
+
a scope marker before each model id, and the active checkmark after `[provider]`.
|
|
66
|
+
Enter switches to the highlighted model and persists it as the default; Space adds or
|
|
67
|
+
removes it from the Ctrl+P cycling scope for this session only; Ctrl+S saves that scope
|
|
68
|
+
to settings. While the scope or its order differs from the last save, the title reads
|
|
69
|
+
`Models (unsaved)`; Escape closes without saving and keeps the session-only scope.
|
|
70
|
+
`/models <text>` seeds the search instead of switching. The bulk, provider, and reorder
|
|
71
|
+
scope actions keep their existing bindings and appear under `/hotkeys`.
|
|
72
|
+
|
|
73
|
+
## Reference screens
|
|
74
|
+
|
|
75
|
+
In bare A1, `/changelog` and `/hotkeys` open a full-screen read-only reference
|
|
76
|
+
screen over the session instead of appending a long document to the
|
|
77
|
+
conversation: `What's New` shows the complete pinned changelog and
|
|
78
|
+
`Keyboard Shortcuts` shows the bare-A1 keybinding tables, including extension
|
|
79
|
+
shortcuts as bound when the screen opens. Scroll with ↑/↓, PageUp/PageDown,
|
|
80
|
+
Home/End, the mouse wheel, or the scrollbar rail, which follows the
|
|
81
|
+
`/settings` → Scroll appearance, style, and speed; press `Esc` to close. The
|
|
82
|
+
screen is framed between two border-coloured rules with the hint below, as the
|
|
83
|
+
v2 extension drew it. After
|
|
84
|
+
an upgrade, the release notes newer than the last acknowledged version open the
|
|
85
|
+
same `What's New` screen once at startup while the feed keeps only the
|
|
86
|
+
`Run /changelog to view the full release notes.` hint. If another dialog is up
|
|
87
|
+
at that moment, the screen is not opened for that launch. The `a1 pi`
|
|
88
|
+
comparison profile keeps Pi's in-feed changelog and hotkeys documents.
|
|
89
|
+
|
|
60
90
|
## Prompt suggestions
|
|
61
91
|
|
|
62
92
|
Bare A1's `/settings` → Agent → Prompt suggestions controls faint next-prompt
|
|
@@ -71,6 +101,19 @@ preferred when consistent with your intent; genuinely unclear cases can remain
|
|
|
71
101
|
empty. [Private diagnostic capture](docs/architecture/prompt-suggestions.md)
|
|
72
102
|
explains how to distinguish skipped, empty, rejected, and timed-out suggestions.
|
|
73
103
|
|
|
104
|
+
## Skills
|
|
105
|
+
|
|
106
|
+
Bare A1's `/settings` → Agent → Skills chooses how discovered skills appear in the
|
|
107
|
+
slash-command menu. `collapse` (the default) replaces the per-skill `/skill:<name>`
|
|
108
|
+
entries with one `/skills` command: bare `/skills` opens a searchable Skills dialog,
|
|
109
|
+
`/skills <name> [args]` applies a skill directly (an unknown name reports
|
|
110
|
+
`Unknown skill: <name>`), and typing `/skills:` lists every skill inline so
|
|
111
|
+
`/skills:<name> args` runs as `/skill:<name> args`. With the `skills` row selected in
|
|
112
|
+
the menu, typing `:` completes to `/skills:`. `expand` lists every `/skill:<name>`
|
|
113
|
+
command as before. Both take effect immediately; a typed `/skill:<name>` always
|
|
114
|
+
works, and the engine's own Skill commands toggle still governs whether skills are
|
|
115
|
+
offered at all. The `a1 pi` comparison profile is unchanged.
|
|
116
|
+
|
|
74
117
|
## Quitting
|
|
75
118
|
|
|
76
119
|
`/quit`, `Ctrl+C` twice, and `Ctrl+D` on an empty prompt end a bare A1 session.
|
package/bin/activate.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Protocol: the updater that installed this tree starts this entry with --data-dir and
|
|
4
|
+
// --target-version and reads one JSON progress event per line. The tree activates itself
|
|
5
|
+
// with its own release code, so the updater needs to know nothing else about its layout.
|
|
6
|
+
const { runActivationEntry } = await import("../dist/foundation/release/update-activation.js");
|
|
7
|
+
|
|
8
|
+
process.exitCode = await runActivationEntry(process.argv.slice(2), import.meta.url);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Compatibility: updaters older than 0.1.8-dev.479 run this entry from the tree they just
|
|
4
|
+
// installed to point a terminal-package proxy that no longer exists at that tree. Those
|
|
5
|
+
// updaters treat a missing file as a failure worth a stack trace on the user's terminal,
|
|
6
|
+
// so the entry stays, does nothing, and exits successfully. Newer updaters never run it.
|
|
7
|
+
process.exitCode = 0;
|
|
@@ -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, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
|
|
5
|
+
export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, 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";
|
|
@@ -29,6 +29,8 @@ export declare class ContextualPromptSuggestionController {
|
|
|
29
29
|
settle(identity: OwnedUiPromptSuggestionIdentity): void;
|
|
30
30
|
accept(): void;
|
|
31
31
|
invalidate(): void;
|
|
32
|
+
/** Retires only a generating request; a prepared or available suggestion survives the draft edit. */
|
|
33
|
+
abortPending(): void;
|
|
32
34
|
dispose(): void;
|
|
33
35
|
}
|
|
34
36
|
export declare function samePromptSuggestionIdentity(left: OwnedUiPromptSuggestionIdentity, right: OwnedUiPromptSuggestionIdentity): boolean;
|
|
@@ -88,6 +88,18 @@ export class ContextualPromptSuggestionController {
|
|
|
88
88
|
}
|
|
89
89
|
accept() { this.#invalidate(false); }
|
|
90
90
|
invalidate() { this.#invalidate(true); }
|
|
91
|
+
/** Retires only a generating request; a prepared or available suggestion survives the draft edit. */
|
|
92
|
+
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)
|
|
99
|
+
return;
|
|
100
|
+
this.#finish(request, "cancelled");
|
|
101
|
+
request.abort.abort();
|
|
102
|
+
}
|
|
91
103
|
dispose() {
|
|
92
104
|
this.#disposed = true;
|
|
93
105
|
this.invalidate();
|
|
@@ -4,6 +4,7 @@ import type { UiRouteHost } from "../../ui/apps/contracts.js";
|
|
|
4
4
|
import type { TranscriptViewportFrame, TranscriptViewportFrameDescriptor } from "../../ui/components/transcript-viewport.js";
|
|
5
5
|
import type { PiEngineAdapter } from "../../integrations/pi/engine/adapter.js";
|
|
6
6
|
import type { PiWorkflowMessage, PiWorkflowResult } from "../../integrations/pi/engine/workflows.js";
|
|
7
|
+
import { type PiShellHotkeysPresentation } from "../../integrations/pi/components/shell-presenters-info.js";
|
|
7
8
|
import { type PiShellClipboardContent, type PiShellComponentPort, type PiShellEditorPort, type PiShellExtensionRendererResolver, type PiShellHeaderOptions, type PiShellHeaderPort, type PiShellImageAssetResolver, type PiShellLoadedResourcesPort, type PiShellResourceEntry, type PiShellTranscriptComponentPort } from "../../integrations/pi/components/shell-shared-facade.js";
|
|
8
9
|
import { type PiTuiInputCoordinationScheduler, type PiTuiInputSurfaceKind } from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
|
|
9
10
|
import type { PiTuiComponentPort, PiTuiInputDiagnosticsEvent, PiTuiLayoutNode, PiTuiTerminalPort } from "../../integrations/pi/tui-runtime/contracts.js";
|
|
@@ -109,12 +110,19 @@ export interface OwnedUiShellDiagnosticOptions {
|
|
|
109
110
|
/** Test seams for isolated paste preparation: an in-process starter, or the forked helper's entry. */
|
|
110
111
|
readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent" | "spareIdleMs">;
|
|
111
112
|
}
|
|
113
|
+
/** The skills presentation choice: collapse the per-skill commands into one skills command or expand them. */
|
|
114
|
+
export interface OwnedUiShellSkillsOptions {
|
|
115
|
+
readonly presentation: () => "collapse" | "expand";
|
|
116
|
+
readonly onChange: (listener: () => void) => () => void;
|
|
117
|
+
}
|
|
112
118
|
/** What composes an owned session shell, grouped by the collaborator that provides each part. */
|
|
113
119
|
export interface OwnedUiSessionShellOptions {
|
|
114
120
|
readonly engine: OwnedUiShellEngineOptions;
|
|
115
121
|
readonly presentation?: OwnedUiShellPresentationOptions;
|
|
116
122
|
readonly history?: OwnedUiShellHistoryOptions;
|
|
117
123
|
readonly suggestions?: OwnedUiShellSuggestionOptions;
|
|
124
|
+
/** Supplied only to the bare-A1 composition; absent keeps the pinned per-skill command list. */
|
|
125
|
+
readonly skills?: OwnedUiShellSkillsOptions;
|
|
118
126
|
readonly diagnostics?: OwnedUiShellDiagnosticOptions;
|
|
119
127
|
}
|
|
120
128
|
/** Composes backend session state into the owned transcript, viewport, editor, and dock presentation. */
|
|
@@ -152,6 +160,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
152
160
|
readonly captureClipboardPaste?: () => PasteSource;
|
|
153
161
|
readonly pasteDiagnostics?: (event: PasteEvent) => void;
|
|
154
162
|
readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent">;
|
|
163
|
+
readonly skillsPresentation?: () => "collapse" | "expand";
|
|
155
164
|
}, startup?: PiShellHeaderOptions, agentDir?: string, extensionRenderers?: PiShellExtensionRendererResolver, sessionLayout?: "pinned" | "custom-viewport", imageAssets?: PiShellImageAssetResolver);
|
|
156
165
|
setEditorPaddingX(padding: number): void;
|
|
157
166
|
setAutocompleteMaxVisible(maxVisible: number): void;
|
|
@@ -244,6 +253,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
|
|
|
244
253
|
setExtensionFooter(component: PiShellComponentPort | null): void;
|
|
245
254
|
setExtensionStatus(key: string, text: string | undefined): void;
|
|
246
255
|
setExtensionWorking(message: string | undefined, visible?: boolean): void;
|
|
256
|
+
hotkeysPresentation(): PiShellHotkeysPresentation;
|
|
247
257
|
resetWorkflowPresentation(): void;
|
|
248
258
|
resetExtensionUi(): void;
|
|
249
259
|
addExtensionNotification(message: string, type: "info" | "warning" | "error"): void;
|
|
@@ -682,7 +682,9 @@ export class OwnedUiSessionShellRoot {
|
|
|
682
682
|
.flatMap(diagnostic => renderPiShellPackageUpdateNotice(diagnostic.message.split("\n").filter(line => line.startsWith("- ")).map(line => line.slice(2)), width)));
|
|
683
683
|
rows.push(...diagnostics
|
|
684
684
|
.filter(diagnostic => diagnostic.code === "changelog-collapsed" || diagnostic.code === "changelog-expanded")
|
|
685
|
-
|
|
685
|
+
// Rationale: the custom viewport shows the new entries on the What's New screen, so its feed
|
|
686
|
+
// carries only the hint for either diagnostic; the pinned layout keeps the full document.
|
|
687
|
+
.flatMap(diagnostic => diagnostic.code === "changelog-collapsed" || this.#customViewport
|
|
686
688
|
? createPiShellCollapsedChangelog().render(width)
|
|
687
689
|
: createPiShellChangelog(diagnostic.message).render(width)));
|
|
688
690
|
rows.push(...diagnostics
|
|
@@ -809,7 +811,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
809
811
|
}
|
|
810
812
|
if (result.command === "hotkeys" && result.outcome === "completed") {
|
|
811
813
|
this.#lastWorkflowStatusId = undefined;
|
|
812
|
-
const
|
|
814
|
+
const presentation = this.hotkeysPresentation();
|
|
815
|
+
const hotkeys = createPiShellHotkeys(presentation.bindings, presentation.getShortcuts, presentation.profile);
|
|
813
816
|
this.#appendAnchoredWorkflowComponent(width => hotkeys.render(width), () => hotkeys.dispose?.());
|
|
814
817
|
return;
|
|
815
818
|
}
|
|
@@ -903,6 +906,13 @@ export class OwnedUiSessionShellRoot {
|
|
|
903
906
|
this.#status.setWorkingOverride(visible ? message : undefined);
|
|
904
907
|
this.invalidate();
|
|
905
908
|
}
|
|
909
|
+
hotkeysPresentation() {
|
|
910
|
+
return {
|
|
911
|
+
bindings: this.editor.keybindingConfig(),
|
|
912
|
+
getShortcuts: bindings => this.#extensionRenderers.getShortcuts?.(bindings) ?? [],
|
|
913
|
+
profile: this.#customViewport ? "a1" : "pi",
|
|
914
|
+
};
|
|
915
|
+
}
|
|
906
916
|
resetWorkflowPresentation() {
|
|
907
917
|
this.#dockNotice = undefined;
|
|
908
918
|
for (const id of this.#workflowStatusAnchors.keys()) {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { OwnedUiSessionViewModel, OwnedUiThinkingLevel } from "../../contracts/owned-ui/index.js";
|
|
2
2
|
import type { AdapterCommandResult } from "../../integrations/pi/engine/adapter.js";
|
|
3
3
|
import type { PiWorkflowRequest } from "../../integrations/pi/engine/workflows.js";
|
|
4
|
+
import { type PiShellSkillSummary } from "../../integrations/pi/components/skills-command.js";
|
|
5
|
+
import { type PiShellHotkeysPresentation } from "../../integrations/pi/components/shell-presenters-info.js";
|
|
4
6
|
import type { PiShellSelectorOption } from "../../integrations/pi/components/shell-shared-facade.js";
|
|
5
7
|
import { type PiTuiDamageDecision } from "../../integrations/pi/tui-runtime/damage-aware-terminal.js";
|
|
6
8
|
import { PiTuiRuntimeAdapter } from "../../integrations/pi/tui-runtime/adapter.js";
|
|
7
9
|
import type { PiTuiOverlayHandle } from "../../integrations/pi/tui-runtime/contracts.js";
|
|
8
10
|
import { OwnedUiSessionShellRoot, type OwnedUiBackendPort, type OwnedUiSessionShellOptions } from "./session-shell-root.js";
|
|
9
|
-
export { OwnedUiSessionShellRoot, type OwnedUiSessionShellOptions, type OwnedUiShellDiagnosticOptions, type OwnedUiShellEngineOptions, type OwnedUiShellHistoryOptions, type OwnedUiShellPresentationOptions, type OwnedUiShellSuggestionOptions, } from "./session-shell-root.js";
|
|
11
|
+
export { OwnedUiSessionShellRoot, type OwnedUiSessionShellOptions, type OwnedUiShellDiagnosticOptions, type OwnedUiShellEngineOptions, type OwnedUiShellHistoryOptions, type OwnedUiShellPresentationOptions, type OwnedUiShellSkillsOptions, type OwnedUiShellSuggestionOptions, } from "./session-shell-root.js";
|
|
10
12
|
/** Coordinates backend, owned presentation, and Pi TUI lifecycles for one interactive session. */
|
|
11
13
|
export declare class OwnedUiSessionShell {
|
|
12
14
|
#private;
|
|
@@ -35,6 +37,8 @@ export declare class OwnedUiSessionShell {
|
|
|
35
37
|
restoreQueuedInput(): void;
|
|
36
38
|
showSelector(title: string, options: readonly PiShellSelectorOption[], onSelect: (id: string) => void, onCancel?: () => void): PiTuiOverlayHandle;
|
|
37
39
|
showThinkingSelector(): void;
|
|
40
|
+
/** The Skills dialog: a replacement input like the model selector, applying the chosen skill through the prompt path. */
|
|
41
|
+
showSkillsSelector(skills?: readonly PiShellSkillSummary[]): void;
|
|
38
42
|
showModelSelector(initialSearchInput?: string): void;
|
|
39
43
|
showForkSelector(): void;
|
|
40
44
|
showLogoutSelector(): Promise<void>;
|
|
@@ -48,7 +52,10 @@ export declare class OwnedUiSessionShell {
|
|
|
48
52
|
runWorkflow(request: PiWorkflowRequest): Promise<AdapterCommandResult>;
|
|
49
53
|
showTrustSelector(): void;
|
|
50
54
|
showScopedModelsSelector(): void;
|
|
55
|
+
/** The bare-A1 unified Models dialog: switch on Enter, scope on Space, persist on Ctrl+S, all through the engine. */
|
|
56
|
+
showModelsDialog(initialQuery?: string): void;
|
|
51
57
|
dispose(): Promise<void>;
|
|
58
|
+
hotkeysPresentation(): PiShellHotkeysPresentation;
|
|
52
59
|
}
|
|
53
60
|
export interface SessionResumeCommandMetadata {
|
|
54
61
|
readonly sessionId: string;
|
|
@@ -9,12 +9,14 @@ import { assertPromptImages, ImageAttachmentError } from "../../contracts/owned-
|
|
|
9
9
|
import { ContextualPromptSuggestionController } from "./prompt-suggestion-controller.js";
|
|
10
10
|
import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../../ui/components/mouse.js";
|
|
11
11
|
import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
|
|
12
|
-
import { PINNED_PI_HIDDEN_COMMAND_NAMES,
|
|
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
14
|
import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
|
|
15
|
+
import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission, skillPrompt, skillsFromCommands, } from "../../integrations/pi/components/skills-command.js";
|
|
15
16
|
import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
|
|
16
|
-
import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellThinkingSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
|
|
17
|
+
import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellSkillsSelector, createPiShellThinkingSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
|
|
17
18
|
import { createPiShellChangelog, createPiShellHotkeys, createPiShellSessionInfo, renderPiShellStatusText, } from "../../integrations/pi/components/shell-presenters-info.js";
|
|
19
|
+
import { CHANGELOG_ROUTE } from "../../features/owned-ui/reference-routes.js";
|
|
18
20
|
import { createPiShellTranscriptComponent, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellTranscriptBlock, } from "../../integrations/pi/components/shell-presenters-transcript.js";
|
|
19
21
|
import { onPiThemeChange, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
|
|
20
22
|
import { DamageAwareTerminalAdapter } from "../../integrations/pi/tui-runtime/damage-aware-terminal.js";
|
|
@@ -35,6 +37,9 @@ export class OwnedUiSessionShell {
|
|
|
35
37
|
#unsubscribe;
|
|
36
38
|
#unsubscribePromptSuggestions;
|
|
37
39
|
#promptSuggestions;
|
|
40
|
+
#skills;
|
|
41
|
+
#unsubscribeSkills;
|
|
42
|
+
#installedCommandSignature = "";
|
|
38
43
|
#promptHistory = null;
|
|
39
44
|
#promptHistoryImageSidecar;
|
|
40
45
|
#extensionBridge;
|
|
@@ -51,6 +56,8 @@ export class OwnedUiSessionShell {
|
|
|
51
56
|
#shutdownPromise;
|
|
52
57
|
#disposePromise;
|
|
53
58
|
#pointerReporting = false;
|
|
59
|
+
// Invariant: the startup release notes open at most once per shell, whatever the view does later.
|
|
60
|
+
#startupChangelogHandled = false;
|
|
54
61
|
#customViewport;
|
|
55
62
|
#responseCopy;
|
|
56
63
|
// Rationale: only an executor this shell created owns a spare copy helper worth warming and disposing.
|
|
@@ -96,6 +103,8 @@ export class OwnedUiSessionShell {
|
|
|
96
103
|
let streamPresentation;
|
|
97
104
|
let pendingClipboardWrite = Promise.resolve();
|
|
98
105
|
let promptSuggestionController = null;
|
|
106
|
+
// Invariant: the collapsed skills presentation is a bare-A1 replacement; comparison profiles keep the pinned list.
|
|
107
|
+
this.#skills = this.#customViewport ? options.skills ?? null : null;
|
|
99
108
|
const terminalCopy = terminal !== undefined || hasAsyncClipboardOutput();
|
|
100
109
|
this.#responseCopy = this.#customViewport ? new ResponseCopyCoordinator({
|
|
101
110
|
execute: responseCopy?.execute ?? (this.#copyExecutor = createResponseCopyExecutor({
|
|
@@ -160,7 +169,7 @@ export class OwnedUiSessionShell {
|
|
|
160
169
|
onInterrupt: () => { void this.interrupt(); },
|
|
161
170
|
onClear: () => { void this.clearOrExit(); },
|
|
162
171
|
onExit: () => { void this.shutdown(); },
|
|
163
|
-
onModelSelect: () => this.showModelSelector(),
|
|
172
|
+
onModelSelect: () => this.#customViewport ? this.showModelsDialog() : this.showModelSelector(),
|
|
164
173
|
onModelCycle: direction => { void this.cycleModel(direction); },
|
|
165
174
|
onThinkingCycle: () => { void this.cycleThinkingLevel(); },
|
|
166
175
|
onThinkingToggle: () => {
|
|
@@ -170,7 +179,7 @@ export class OwnedUiSessionShell {
|
|
|
170
179
|
onMessageCopy: () => { void this.runWorkflow({ command: "copy", argument: "" }); },
|
|
171
180
|
onFollowUp: () => { void this.queueFollowUp().catch(() => this.#reportSubmissionError()); },
|
|
172
181
|
onDequeue: () => this.restoreQueuedInput(),
|
|
173
|
-
onEditorChange: () => { this.#editorRevision++; promptSuggestionController?.
|
|
182
|
+
onEditorChange: () => { this.#editorRevision++; promptSuggestionController?.abortPending(); },
|
|
174
183
|
onPromptSuggestionAccepted: () => promptSuggestionController?.accept(),
|
|
175
184
|
onInputSurfaceChanged: () => {
|
|
176
185
|
promptSuggestionController?.invalidate();
|
|
@@ -207,6 +216,7 @@ export class OwnedUiSessionShell {
|
|
|
207
216
|
},
|
|
208
217
|
...(pasteDiagnostics === undefined ? {} : { pasteDiagnostics: pasteDiagnostics }),
|
|
209
218
|
...(pastePreparation === undefined ? {} : { pastePreparation }),
|
|
219
|
+
skillsPresentation: () => this.#skills?.presentation() ?? "expand",
|
|
210
220
|
}, {
|
|
211
221
|
...startup,
|
|
212
222
|
resources: startup?.resources ?? shellResourceEntries(this.backend),
|
|
@@ -287,6 +297,15 @@ export class OwnedUiSessionShell {
|
|
|
287
297
|
this.#unsubscribePromptSuggestions = promptSuggestionOptions === undefined
|
|
288
298
|
? () => { }
|
|
289
299
|
: promptSuggestionOptions.onChange(enabled => this.#promptSuggestions?.setEnabled(enabled));
|
|
300
|
+
// Rationale: the same listener refreshes the menu for the A1 presentation choice and for the engine's
|
|
301
|
+
// skill-command registration, both of which the owned settings manager reports through one change.
|
|
302
|
+
this.#unsubscribeSkills = this.#skills === null ? () => { } : this.#skills.onChange(() => {
|
|
303
|
+
// Invariant: reinstalling drops extension provider wrappers, so an unrelated setting change leaves the list alone.
|
|
304
|
+
if (this.#disposed || this.#commandListSignature() === this.#installedCommandSignature)
|
|
305
|
+
return;
|
|
306
|
+
this.#installAutocompleteCommands();
|
|
307
|
+
this.runtime.requestRender();
|
|
308
|
+
});
|
|
290
309
|
const initialPiSettings = this.backend.pinnedSettingsSnapshot();
|
|
291
310
|
this.runtime.setHardwareCursor(initialPiSettings.showHardwareCursor);
|
|
292
311
|
this.runtime.setClearOnShrink(initialPiSettings.clearOnShrink);
|
|
@@ -453,7 +472,7 @@ export class OwnedUiSessionShell {
|
|
|
453
472
|
},
|
|
454
473
|
finishLogin: () => this.#finishWorkflowLogin(),
|
|
455
474
|
});
|
|
456
|
-
this
|
|
475
|
+
this.#installAutocompleteCommands();
|
|
457
476
|
this.#unsubscribe = this.backend.onEvent(event => {
|
|
458
477
|
// Performance: a streamed chunk names one block, and touching only that block is what keeps the
|
|
459
478
|
// cost of a chunk the same in a long session as in a new one. Everything else
|
|
@@ -669,7 +688,7 @@ export class OwnedUiSessionShell {
|
|
|
669
688
|
}, displayInput);
|
|
670
689
|
}
|
|
671
690
|
async clearOrExit(now = Date.now()) {
|
|
672
|
-
this.#promptSuggestions?.
|
|
691
|
+
this.#promptSuggestions?.abortPending();
|
|
673
692
|
if (now - this.#lastClearTime < 500)
|
|
674
693
|
return this.shutdown();
|
|
675
694
|
this.root.editor.setText("");
|
|
@@ -834,6 +853,23 @@ export class OwnedUiSessionShell {
|
|
|
834
853
|
this.root.setInputSurface(component);
|
|
835
854
|
this.runtime.requestRender();
|
|
836
855
|
}
|
|
856
|
+
/** The Skills dialog: a replacement input like the model selector, applying the chosen skill through the prompt path. */
|
|
857
|
+
showSkillsSelector(skills = this.#skillSummaries()) {
|
|
858
|
+
const close = () => {
|
|
859
|
+
this.root.setInputSurface(null);
|
|
860
|
+
this.runtime.requestRender();
|
|
861
|
+
};
|
|
862
|
+
const component = createPiShellSkillsSelector({
|
|
863
|
+
skills,
|
|
864
|
+
onSelect: name => {
|
|
865
|
+
close();
|
|
866
|
+
void this.#submitSkillPrompt(skillPrompt(name), skillPrompt(name)).catch(() => this.#reportSubmissionError());
|
|
867
|
+
},
|
|
868
|
+
onCancel: close,
|
|
869
|
+
});
|
|
870
|
+
this.root.setInputSurface(component);
|
|
871
|
+
this.runtime.requestRender();
|
|
872
|
+
}
|
|
837
873
|
showModelSelector(initialSearchInput) {
|
|
838
874
|
const context = this.backend.pinnedModelSelectorContext();
|
|
839
875
|
const close = () => {
|
|
@@ -1139,6 +1175,10 @@ export class OwnedUiSessionShell {
|
|
|
1139
1175
|
this.showScopedModelsSelector();
|
|
1140
1176
|
return { outcome: "completed", diagnostic: null };
|
|
1141
1177
|
}
|
|
1178
|
+
if (request.command === "models" && request.selection === undefined) {
|
|
1179
|
+
this.showModelsDialog(request.argument.trim() || undefined);
|
|
1180
|
+
return { outcome: "completed", diagnostic: null };
|
|
1181
|
+
}
|
|
1142
1182
|
if (request.command === "model" && request.selection === undefined && request.confirmed === undefined && request.argument.trim().length === 0) {
|
|
1143
1183
|
this.showModelSelector();
|
|
1144
1184
|
return { outcome: "completed", diagnostic: null };
|
|
@@ -1247,10 +1287,10 @@ export class OwnedUiSessionShell {
|
|
|
1247
1287
|
if (request.command === "reload" && result.outcome === "completed") {
|
|
1248
1288
|
this.root.resetWorkflowPresentation();
|
|
1249
1289
|
this.root.editor.reloadKeybindings();
|
|
1250
|
-
this
|
|
1290
|
+
this.#installAutocompleteCommands();
|
|
1251
1291
|
}
|
|
1252
1292
|
this.root.appendWorkflowResult(result);
|
|
1253
|
-
if (request.command === "model" && result.outcome === "completed")
|
|
1293
|
+
if ((request.command === "model" || request.command === "models") && result.outcome === "completed")
|
|
1254
1294
|
this.#showDaxnutsForActiveModel();
|
|
1255
1295
|
this.runtime.requestRender();
|
|
1256
1296
|
return workflowAdapterResult(result);
|
|
@@ -1357,6 +1397,90 @@ export class OwnedUiSessionShell {
|
|
|
1357
1397
|
this.runtime.requestRender();
|
|
1358
1398
|
}).finally(() => clearTimeout(timeout));
|
|
1359
1399
|
}
|
|
1400
|
+
// Invariant: bare A1 routes `models`; the comparison profile keeps the pinned `model`/`scoped-models` pair. Hidden routes are shared.
|
|
1401
|
+
#isWorkflowRoute(value) {
|
|
1402
|
+
return workflowCommandNames(this.#customViewport ? "bare" : "comparison").includes(value)
|
|
1403
|
+
|| PINNED_PI_HIDDEN_COMMAND_NAMES.includes(value);
|
|
1404
|
+
}
|
|
1405
|
+
/** The bare-A1 unified Models dialog: switch on Enter, scope on Space, persist on Ctrl+S, all through the engine. */
|
|
1406
|
+
showModelsDialog(initialQuery) {
|
|
1407
|
+
const context = this.backend.modelsContext();
|
|
1408
|
+
const available = new Set(context.models.map(model => `${model.provider}/${model.id}`));
|
|
1409
|
+
const savedScopeIds = context.persistedScopeIds.filter(id => available.has(id));
|
|
1410
|
+
// Invariant: an explicit session scope wins; otherwise the dialog starts from what is persisted, never from "all rows scoped".
|
|
1411
|
+
const scopeIds = context.sessionScopeIds.length > 0 ? context.sessionScopeIds : savedScopeIds;
|
|
1412
|
+
let disposed = false;
|
|
1413
|
+
let timedOut = false;
|
|
1414
|
+
const controller = new AbortController();
|
|
1415
|
+
const timeout = setTimeout(() => {
|
|
1416
|
+
timedOut = true;
|
|
1417
|
+
controller.abort();
|
|
1418
|
+
}, 15_000);
|
|
1419
|
+
const close = () => {
|
|
1420
|
+
disposed = true;
|
|
1421
|
+
clearTimeout(timeout);
|
|
1422
|
+
controller.abort();
|
|
1423
|
+
this.root.setInputSurface(null);
|
|
1424
|
+
this.runtime.requestRender();
|
|
1425
|
+
};
|
|
1426
|
+
const dialog = createPiShellModelsDialog({
|
|
1427
|
+
models: context.models,
|
|
1428
|
+
activeModelId: context.activeModelId,
|
|
1429
|
+
scopeIds,
|
|
1430
|
+
savedScopeIds,
|
|
1431
|
+
...(initialQuery === undefined ? {} : { initialQuery }),
|
|
1432
|
+
refreshStatus: "Refreshing model catalogs…",
|
|
1433
|
+
requestRender: () => this.runtime.requestRender(),
|
|
1434
|
+
onSelect: modelId => {
|
|
1435
|
+
void this.runWorkflow({ command: "models", argument: "", selection: modelId }).then(result => {
|
|
1436
|
+
if (!disposed && result.outcome === "completed")
|
|
1437
|
+
close();
|
|
1438
|
+
});
|
|
1439
|
+
},
|
|
1440
|
+
onScopeChange: ids => {
|
|
1441
|
+
this.backend.setSessionModelScope(ids);
|
|
1442
|
+
this.runtime.requestRender();
|
|
1443
|
+
},
|
|
1444
|
+
onSave: ids => {
|
|
1445
|
+
try {
|
|
1446
|
+
this.backend.persistModelScope(ids);
|
|
1447
|
+
}
|
|
1448
|
+
catch (error) {
|
|
1449
|
+
this.root.appendWorkflowResult({ command: "models", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
1450
|
+
this.runtime.requestRender();
|
|
1451
|
+
throw error;
|
|
1452
|
+
}
|
|
1453
|
+
this.root.appendWorkflowStatus("Model selection saved to settings");
|
|
1454
|
+
this.runtime.requestRender();
|
|
1455
|
+
},
|
|
1456
|
+
onCancel: close,
|
|
1457
|
+
});
|
|
1458
|
+
const component = {
|
|
1459
|
+
...dialog,
|
|
1460
|
+
dispose: () => {
|
|
1461
|
+
disposed = true;
|
|
1462
|
+
clearTimeout(timeout);
|
|
1463
|
+
controller.abort();
|
|
1464
|
+
dialog.dispose?.();
|
|
1465
|
+
},
|
|
1466
|
+
};
|
|
1467
|
+
this.root.setInputSurface(component);
|
|
1468
|
+
this.runtime.requestRender();
|
|
1469
|
+
void this.backend.refreshModels(controller.signal).then(refreshed => {
|
|
1470
|
+
if (disposed)
|
|
1471
|
+
return;
|
|
1472
|
+
component.updateModels(refreshed.models);
|
|
1473
|
+
component.setRefreshStatus(timedOut ? "Model refresh timed out; showing cached models." : refreshed.status, timedOut ? "warning" : refreshed.statusKind);
|
|
1474
|
+
this.runtime.requestRender();
|
|
1475
|
+
}).catch(error => {
|
|
1476
|
+
if (disposed)
|
|
1477
|
+
return;
|
|
1478
|
+
component.setRefreshStatus(timedOut
|
|
1479
|
+
? "Model refresh timed out; showing cached models."
|
|
1480
|
+
: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
1481
|
+
this.runtime.requestRender();
|
|
1482
|
+
}).finally(() => clearTimeout(timeout));
|
|
1483
|
+
}
|
|
1360
1484
|
#showDaxnutsForActiveModel() {
|
|
1361
1485
|
const model = this.view().activeModel;
|
|
1362
1486
|
if (model?.providerId === "opencode" && model.modelId.toLowerCase().includes("kimi-k2.5")) {
|
|
@@ -1409,6 +1533,7 @@ export class OwnedUiSessionShell {
|
|
|
1409
1533
|
attempt(() => this.#streamPresentation.dispose());
|
|
1410
1534
|
attempt(() => this.#promptSuggestions?.dispose());
|
|
1411
1535
|
attempt(() => this.#unsubscribePromptSuggestions());
|
|
1536
|
+
attempt(() => this.#unsubscribeSkills());
|
|
1412
1537
|
attempt(() => this.#unsubscribeSettings());
|
|
1413
1538
|
let fullscreenExitText = "";
|
|
1414
1539
|
attempt(() => {
|
|
@@ -1453,11 +1578,13 @@ export class OwnedUiSessionShell {
|
|
|
1453
1578
|
if (!this.runtime.active || this.runtime.mode !== "fullscreen")
|
|
1454
1579
|
return null;
|
|
1455
1580
|
try {
|
|
1456
|
-
|
|
1457
|
-
if (!enabled)
|
|
1581
|
+
if (!outro.snapshot().enabled)
|
|
1458
1582
|
return null;
|
|
1459
1583
|
const viewport = this.runtime.viewport();
|
|
1460
|
-
return {
|
|
1584
|
+
return {
|
|
1585
|
+
rows: this.#damageTerminal.presentedRows(), columns: viewport.columns, height: viewport.rows,
|
|
1586
|
+
settings: { effect: QUIT_OUTRO_EFFECT, durationMs: QUIT_OUTRO_DURATION_MS },
|
|
1587
|
+
};
|
|
1461
1588
|
}
|
|
1462
1589
|
catch {
|
|
1463
1590
|
return null;
|
|
@@ -1550,13 +1677,31 @@ export class OwnedUiSessionShell {
|
|
|
1550
1677
|
}
|
|
1551
1678
|
this.root.update(view);
|
|
1552
1679
|
this.#syncDialog(view.dialog);
|
|
1680
|
+
this.#presentStartupChangelog(view);
|
|
1553
1681
|
this.runtime.requestRender();
|
|
1554
1682
|
for (const listener of this.#listeners)
|
|
1555
1683
|
listener(view);
|
|
1556
1684
|
return view;
|
|
1557
1685
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1686
|
+
// Rationale: the engine has already stored the current version when the diagnostic arrives, so the
|
|
1687
|
+
// entries newer than the last acknowledged one exist only in its message; the screen shows exactly those.
|
|
1688
|
+
#presentStartupChangelog(view) {
|
|
1689
|
+
if (this.#startupChangelogHandled || !this.#customViewport || this.#routeHost === null)
|
|
1690
|
+
return;
|
|
1691
|
+
const diagnostic = view.diagnostics.find(candidate => candidate.code === "changelog-expanded");
|
|
1692
|
+
// Invariant: a view synchronized before the runtime starts is not the moment of arrival; start() replays it.
|
|
1693
|
+
if (diagnostic === undefined || !this.runtime.active)
|
|
1694
|
+
return;
|
|
1695
|
+
this.#startupChangelogHandled = true;
|
|
1696
|
+
// Invariant: a dialog, selector, or owned screen already up keeps the screen; the hint stays in the feed.
|
|
1697
|
+
if (this.#dialogHandle !== undefined || !this.root.usesDefaultInputSurface())
|
|
1698
|
+
return;
|
|
1699
|
+
if (!this.#routeHost.claims(CHANGELOG_ROUTE))
|
|
1700
|
+
return;
|
|
1701
|
+
this.#openOwnedRoute(CHANGELOG_ROUTE, { document: diagnostic.message });
|
|
1702
|
+
}
|
|
1703
|
+
#openOwnedRoute(route, input) {
|
|
1704
|
+
const surface = this.#routeHost?.open(route, input) ?? null;
|
|
1560
1705
|
if (surface === null)
|
|
1561
1706
|
return { outcome: "failed", diagnostic: `route is unavailable: ${route}` };
|
|
1562
1707
|
if (!this.runtime.active)
|
|
@@ -1683,6 +1828,9 @@ export class OwnedUiSessionShell {
|
|
|
1683
1828
|
});
|
|
1684
1829
|
this.#dialogId = dialog.id;
|
|
1685
1830
|
}
|
|
1831
|
+
hotkeysPresentation() {
|
|
1832
|
+
return this.root.hotkeysPresentation();
|
|
1833
|
+
}
|
|
1686
1834
|
async #slashCommand(text) {
|
|
1687
1835
|
const body = text.slice(1).trim();
|
|
1688
1836
|
const separator = body.search(/\s/);
|
|
@@ -1690,8 +1838,15 @@ export class OwnedUiSessionShell {
|
|
|
1690
1838
|
const argument = separator < 0 ? "" : body.slice(separator + 1).trimStart();
|
|
1691
1839
|
if (this.#routeHost?.claims(name))
|
|
1692
1840
|
return this.#openOwnedRoute(name);
|
|
1693
|
-
if (isWorkflowRoute(name))
|
|
1841
|
+
if (this.#isWorkflowRoute(name))
|
|
1694
1842
|
return this.runWorkflow({ command: name, argument });
|
|
1843
|
+
if (this.#skillsCollapsed()) {
|
|
1844
|
+
if (name === SKILLS_COMMAND_NAME)
|
|
1845
|
+
return this.#runSkillsCommand(text, argument);
|
|
1846
|
+
const rewritten = rewriteSkillsTunnelSubmission(text);
|
|
1847
|
+
if (rewritten !== text)
|
|
1848
|
+
return this.#submitSkillPrompt(rewritten, text);
|
|
1849
|
+
}
|
|
1695
1850
|
// Compatibility: unknown slash input, prompt templates, skills, and extension commands remain Pi prompt input.
|
|
1696
1851
|
this.#rememberInput(text, "slash");
|
|
1697
1852
|
this.root.resumeViewportFollowing();
|
|
@@ -1702,6 +1857,51 @@ export class OwnedUiSessionShell {
|
|
|
1702
1857
|
text,
|
|
1703
1858
|
});
|
|
1704
1859
|
}
|
|
1860
|
+
#installAutocompleteCommands() {
|
|
1861
|
+
this.root.editor.setAutocompleteCommands(this.backend.workflowAutocompleteCommands());
|
|
1862
|
+
this.#installedCommandSignature = this.#commandListSignature();
|
|
1863
|
+
}
|
|
1864
|
+
#commandListSignature() {
|
|
1865
|
+
return JSON.stringify([this.#skills?.presentation() ?? "expand", this.backend.disposed ? [] : this.backend.workflowAutocompleteCommands().map(command => command.name)]);
|
|
1866
|
+
}
|
|
1867
|
+
// Invariant: collapse applies only while the engine registers skill commands; otherwise there is nothing to collapse.
|
|
1868
|
+
#skillsCollapsed() {
|
|
1869
|
+
return this.#skills !== null && this.#skills.presentation() === "collapse"
|
|
1870
|
+
&& !this.backend.disposed && this.backend.pinnedSettingsSnapshot().enableSkillCommands;
|
|
1871
|
+
}
|
|
1872
|
+
#skillSummaries() {
|
|
1873
|
+
return skillsFromCommands(this.backend.workflowAutocompleteCommands());
|
|
1874
|
+
}
|
|
1875
|
+
// Protocol: bare "/skills" opens the dialog, "/skills <name> [args]" applies directly, and an unknown name is a command outcome.
|
|
1876
|
+
async #runSkillsCommand(text, argument) {
|
|
1877
|
+
const skills = this.#skillSummaries();
|
|
1878
|
+
const trimmed = argument.trim();
|
|
1879
|
+
if (trimmed.length === 0) {
|
|
1880
|
+
this.showSkillsSelector(skills);
|
|
1881
|
+
return { outcome: "completed", diagnostic: null };
|
|
1882
|
+
}
|
|
1883
|
+
const separator = trimmed.search(/\s/u);
|
|
1884
|
+
const token = separator < 0 ? trimmed : trimmed.slice(0, separator);
|
|
1885
|
+
const skill = findSkillByArgument(skills, token);
|
|
1886
|
+
if (skill === undefined) {
|
|
1887
|
+
const message = "Unknown skill: " + token;
|
|
1888
|
+
this.root.appendWorkflowMessage({ kind: "error", message });
|
|
1889
|
+
this.runtime.requestRender();
|
|
1890
|
+
return { outcome: "failed", diagnostic: message };
|
|
1891
|
+
}
|
|
1892
|
+
return this.#submitSkillPrompt(skillPrompt(skill.name, separator < 0 ? "" : trimmed.slice(separator + 1)), text);
|
|
1893
|
+
}
|
|
1894
|
+
// Rationale: the engine expands "/skill:<name> args" itself; history keeps the form the user typed so recall restores the invocation.
|
|
1895
|
+
#submitSkillPrompt(prompt, typed) {
|
|
1896
|
+
this.#rememberInput(typed, "slash");
|
|
1897
|
+
this.root.resumeViewportFollowing();
|
|
1898
|
+
return this.#execute({
|
|
1899
|
+
type: this.view().lifecycle === "busy" ? "steer" : "prompt",
|
|
1900
|
+
correlationId: this.#correlation("prompt-command"),
|
|
1901
|
+
sessionId: this.backend.sessionId,
|
|
1902
|
+
text: prompt,
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1705
1905
|
async #completeTreeSelection(entryId, skipSummaryPrompt) {
|
|
1706
1906
|
let summarize = false;
|
|
1707
1907
|
let customInstructions;
|
|
@@ -1911,7 +2111,6 @@ function workflowAdapterResult(result) {
|
|
|
1911
2111
|
const INTERRUPT = "\u0003";
|
|
1912
2112
|
const INTERRUPT_CHORD_MS = 1_500;
|
|
1913
2113
|
const RELOAD_SURFACE_MIN_VISIBLE_MS = 400;
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
}
|
|
2114
|
+
// Rationale: the outro is not configurable; the switch only decides whether this plan plays.
|
|
2115
|
+
const QUIT_OUTRO_EFFECT = "fall";
|
|
2116
|
+
const QUIT_OUTRO_DURATION_MS = 800;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { composeOwnedUi, composeOwnedUiApplication, type OwnedUiComposition, type OwnedUiCompositionOptions, } from "./owned-ui.js";
|
|
2
|
-
export { createOwnedRouteHost } from "./settings-route-host.js";
|
|
2
|
+
export { createOwnedRouteHost, type OwnedReferenceProviders, type OwnedReferenceRows } from "./settings-route-host.js";
|