@timurproko/a1 0.1.8-dev.151 → 0.1.8-dev.182
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 +53 -9
- package/bin/cli.js +1 -0
- package/bin/ui.js +17 -2
- package/dist/cli/dispatch.d.ts +6 -0
- package/dist/cli/dispatch.js +107 -81
- package/dist/cli/version-stats.js +7 -1
- package/dist/composition/owned-ui.d.ts +4 -1
- package/dist/composition/owned-ui.js +8 -5
- package/dist/contracts/agent-engine/capability-ports.d.ts +2 -2
- package/dist/contracts/agent-engine/domain-validation.js +22 -4
- package/dist/contracts/agent-engine/domain.d.ts +17 -0
- package/dist/contracts/owned-ui/model.d.ts +8 -0
- package/dist/contracts/owned-ui/validation.js +11 -0
- package/dist/features/owned-ui/index.d.ts +1 -0
- package/dist/features/owned-ui/index.js +1 -0
- package/dist/features/owned-ui/project-trust-prompt.d.ts +20 -0
- package/dist/features/owned-ui/project-trust-prompt.js +32 -0
- package/dist/features/owned-ui/settings-app.js +37 -9
- package/dist/integrations/pi/components/shell-components.d.ts +1 -0
- package/dist/integrations/pi/components/shell-components.js +1 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -0
- package/dist/integrations/pi/components/shell-footer-status.js +14 -9
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +3 -36
- package/dist/integrations/pi/components/shell-presenters-transcript.js +88 -128
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +11 -1
- package/dist/integrations/pi/engine/adapter.d.ts +15 -4
- package/dist/integrations/pi/engine/adapter.js +185 -37
- package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
- package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
- package/dist/integrations/pi/engine/index.d.ts +3 -0
- package/dist/integrations/pi/engine/index.js +3 -0
- package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
- package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
- package/dist/integrations/pi/engine/runtime-integration.d.ts +16 -1
- package/dist/integrations/pi/engine/runtime-integration.js +34 -4
- package/dist/integrations/pi/engine/settings-effects.d.ts +55 -0
- package/dist/integrations/pi/engine/settings-effects.js +229 -0
- package/dist/integrations/pi/engine/settings-integration.d.ts +14 -26
- package/dist/integrations/pi/engine/settings-integration.js +102 -108
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +1 -1
- package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
- package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +5 -1
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +9 -2
- package/dist/integrations/pi/session-ui/session-shell-root.js +83 -9
- package/dist/integrations/pi/session-ui/session-shell.d.ts +2 -0
- package/dist/integrations/pi/session-ui/session-shell.js +151 -15
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +34 -2
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +9 -0
- package/dist/integrations/pi/session-ui/system-clipboard.js +34 -5
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +4 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +28 -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/ui/components/spans.d.ts +2 -0
- package/dist/ui/components/spans.js +25 -0
- package/dist/ui/components/text.js +24 -2
- package/dist/ui/settings/sections.d.ts +8 -19
- package/dist/ui/settings/sections.js +12 -4
- package/dist/ui/settings/session.d.ts +8 -18
- package/dist/ui/settings/session.js +59 -52
- package/docs/architecture/project-structure.md +14 -0
- package/docs/ci-release-runbook.md +75 -9
- package/docs/features/launch-profiles.md +3 -3
- package/docs/repository-governance-live-acceptance.md +77 -0
- package/package.json +5 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
export interface OwnedProjectTrustPromptRequest {
|
|
3
|
+
readonly cwd: string;
|
|
4
|
+
readonly defaultDecision: "ask" | "always" | "never";
|
|
5
|
+
}
|
|
6
|
+
export type OwnedProjectTrustPrompt = (request: OwnedProjectTrustPromptRequest) => Promise<boolean | null>;
|
|
7
|
+
export interface ConsoleProjectTrustPromptOptions {
|
|
8
|
+
readonly input?: Readable & {
|
|
9
|
+
readonly isTTY?: boolean;
|
|
10
|
+
};
|
|
11
|
+
readonly output?: Writable & {
|
|
12
|
+
readonly isTTY?: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Minimal pre-session surface. It depends only on the parent terminal and fixed
|
|
17
|
+
* A1 wording, so no project setting, theme, extension, prompt, or skill can
|
|
18
|
+
* execute before the decision.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createConsoleProjectTrustPrompt(options?: ConsoleProjectTrustPromptOptions): OwnedProjectTrustPrompt;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal pre-session surface. It depends only on the parent terminal and fixed
|
|
4
|
+
* A1 wording, so no project setting, theme, extension, prompt, or skill can
|
|
5
|
+
* execute before the decision.
|
|
6
|
+
*/
|
|
7
|
+
export function createConsoleProjectTrustPrompt(options = {}) {
|
|
8
|
+
const input = options.input ?? process.stdin;
|
|
9
|
+
const output = options.output ?? process.stdout;
|
|
10
|
+
return async ({ cwd }) => {
|
|
11
|
+
if (input.isTTY !== true || output.isTTY !== true) {
|
|
12
|
+
throw new Error("an interactive terminal is unavailable");
|
|
13
|
+
}
|
|
14
|
+
const reader = createInterface({ input, output, terminal: true });
|
|
15
|
+
try {
|
|
16
|
+
output.write(`\nA1 found project-local settings or executable resources in:\n${cwd}\n`);
|
|
17
|
+
output.write("Trusting permits project settings, skills, prompts, packages, themes, and extensions to load.\n");
|
|
18
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
19
|
+
const answer = (await reader.question("Trust this project for this and future launches? [y/N] ")).trim().toLowerCase();
|
|
20
|
+
if (answer === "y" || answer === "yes")
|
|
21
|
+
return true;
|
|
22
|
+
if (answer === "" || answer === "n" || answer === "no")
|
|
23
|
+
return false;
|
|
24
|
+
output.write("Enter y or n.\n");
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
reader.close();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -323,7 +323,7 @@ export class SettingsApp {
|
|
|
323
323
|
if (typeof shown === "number")
|
|
324
324
|
return;
|
|
325
325
|
if (!entry.editable || entry.choices === null || entry.choices.length === 0) {
|
|
326
|
-
this.#notice = `${labelOf(entry)} cannot be changed here`;
|
|
326
|
+
this.#notice = entry.limitationReason ?? `${labelOf(entry)} cannot be changed here`;
|
|
327
327
|
return;
|
|
328
328
|
}
|
|
329
329
|
const current = shown === null ? 0 : Math.max(0, entry.choices.indexOf(shown));
|
|
@@ -456,7 +456,7 @@ export class SettingsApp {
|
|
|
456
456
|
return;
|
|
457
457
|
const entry = row.value;
|
|
458
458
|
if (!entry.editable) {
|
|
459
|
-
this.#notice = `${labelOf(entry)} cannot be changed here`;
|
|
459
|
+
this.#notice = entry.limitationReason ?? `${labelOf(entry)} cannot be changed here`;
|
|
460
460
|
return;
|
|
461
461
|
}
|
|
462
462
|
const shown = this.#shownValue(entry);
|
|
@@ -470,7 +470,7 @@ export class SettingsApp {
|
|
|
470
470
|
}
|
|
471
471
|
const choices = entry.choices;
|
|
472
472
|
if (choices === null || choices.length === 0) {
|
|
473
|
-
this.#notice = `${labelOf(entry)} cannot be changed here`;
|
|
473
|
+
this.#notice = entry.limitationReason ?? `${labelOf(entry)} cannot be changed here`;
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
476
476
|
const current = shown === null ? -1 : choices.indexOf(shown);
|
|
@@ -486,15 +486,22 @@ export class SettingsApp {
|
|
|
486
486
|
// steps from here rather than from a value the source has not caught up to.
|
|
487
487
|
this.#pending.set(key, value);
|
|
488
488
|
void this.#session.change(entry.backend, entry.id, value).then(outcome => {
|
|
489
|
-
if (outcome.failure !== null) {
|
|
489
|
+
if (outcome.failure !== null || outcome.status === "failed") {
|
|
490
490
|
this.#pending.delete(key);
|
|
491
|
-
this.#notice = `Could not save ${labelOf(entry)}: ${outcome.failure}`;
|
|
491
|
+
this.#notice = `Could not save ${labelOf(entry)}: ${outcome.failure ?? "the effect failed"}`;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (outcome.status === "unavailable" || outcome.limitationReason !== null) {
|
|
495
|
+
this.#pending.delete(key);
|
|
496
|
+
this.#notice = outcome.limitationReason ?? `${labelOf(entry)} is unavailable`;
|
|
492
497
|
return;
|
|
493
498
|
}
|
|
494
499
|
// A later press may have moved on; only the last request clears itself.
|
|
495
500
|
if (this.#pending.get(key) === value)
|
|
496
501
|
this.#pending.delete(key);
|
|
497
|
-
this.#notice = outcome.
|
|
502
|
+
this.#notice = outcome.status === "deferred" && outcome.application !== null
|
|
503
|
+
? `${labelOf(entry)} is stored and applies ${applicationLabel(outcome.application)}`
|
|
504
|
+
: null;
|
|
498
505
|
});
|
|
499
506
|
}
|
|
500
507
|
/** What the row shows: the value asked for if one is outstanding, else the source's. */
|
|
@@ -571,9 +578,13 @@ export class SettingsApp {
|
|
|
571
578
|
/** What the list view needs to draw a setting: its words, and where it can go. */
|
|
572
579
|
#viewRow(entry) {
|
|
573
580
|
const shown = this.#shownValue(entry);
|
|
574
|
-
const value = entry.
|
|
575
|
-
?
|
|
576
|
-
:
|
|
581
|
+
const value = !entry.available
|
|
582
|
+
? `unavailable — ${entry.limitationReason ?? "effect is unavailable"}`
|
|
583
|
+
: entry.structured
|
|
584
|
+
? CONFIGURE
|
|
585
|
+
: shown === null
|
|
586
|
+
? describeRaw(entry.rawValue)
|
|
587
|
+
: effectiveDisplay(entry, shown);
|
|
577
588
|
const range = rangeOf(entry);
|
|
578
589
|
return {
|
|
579
590
|
key: `${entry.backend}:${entry.id}`,
|
|
@@ -648,6 +659,23 @@ function displayValue(value) {
|
|
|
648
659
|
return value ? "yes" : "no";
|
|
649
660
|
return String(value);
|
|
650
661
|
}
|
|
662
|
+
function effectiveDisplay(entry, stored) {
|
|
663
|
+
const effective = entry.effectiveValue;
|
|
664
|
+
if (effective === stored)
|
|
665
|
+
return displayValue(stored);
|
|
666
|
+
const shownEffective = typeof effective === "string" || typeof effective === "number" || typeof effective === "boolean"
|
|
667
|
+
? displayValue(effective)
|
|
668
|
+
: describeRaw(effective);
|
|
669
|
+
return `${displayValue(stored)} (effective ${shownEffective}; ${applicationLabel(entry.application)})`;
|
|
670
|
+
}
|
|
671
|
+
function applicationLabel(application) {
|
|
672
|
+
switch (application) {
|
|
673
|
+
case "live": return "live";
|
|
674
|
+
case "next-session": return "in the next session";
|
|
675
|
+
case "next-start": return "on the next start";
|
|
676
|
+
case "current-exit": return "when the current session exits";
|
|
677
|
+
}
|
|
678
|
+
}
|
|
651
679
|
function describeRaw(value) {
|
|
652
680
|
if (value === null || value === undefined)
|
|
653
681
|
return "unset";
|
|
@@ -2,5 +2,6 @@ export * from "./shell-shared-facade.js";
|
|
|
2
2
|
export * from "./shell-editor-autocomplete.js";
|
|
3
3
|
export * from "./shell-selectors-dialogs.js";
|
|
4
4
|
export * from "./shell-presenters-transcript.js";
|
|
5
|
+
export * from "./shell-presenters-info.js";
|
|
5
6
|
export * from "./shell-footer-status.js";
|
|
6
7
|
export * from "./shell-extension-ui.js";
|
|
@@ -2,5 +2,6 @@ export * from "./shell-shared-facade.js";
|
|
|
2
2
|
export * from "./shell-editor-autocomplete.js";
|
|
3
3
|
export * from "./shell-selectors-dialogs.js";
|
|
4
4
|
export * from "./shell-presenters-transcript.js";
|
|
5
|
+
export * from "./shell-presenters-info.js";
|
|
5
6
|
export * from "./shell-footer-status.js";
|
|
6
7
|
export * from "./shell-extension-ui.js";
|
|
@@ -158,6 +158,16 @@ export function createPiShellEditor(options) {
|
|
|
158
158
|
setSubmitHandler: handler => { submitHandler = handler; },
|
|
159
159
|
setInterruptHandler: handler => { interruptHandler = handler; },
|
|
160
160
|
setAutocompleteCommands,
|
|
161
|
+
setPaddingX(padding) {
|
|
162
|
+
editor.setPaddingX(padding);
|
|
163
|
+
editor.invalidate();
|
|
164
|
+
tui.requestRender();
|
|
165
|
+
},
|
|
166
|
+
setAutocompleteMaxVisible(maxVisible) {
|
|
167
|
+
editor.setAutocompleteMaxVisible(maxVisible);
|
|
168
|
+
editor.invalidate();
|
|
169
|
+
tui.requestRender();
|
|
170
|
+
},
|
|
161
171
|
addAutocompleteProvider(factory) {
|
|
162
172
|
if (typeof factory !== "function")
|
|
163
173
|
throw new TypeError("extension autocomplete factory must be a function");
|
|
@@ -71,16 +71,17 @@ export function createPiShellStatus(view, runtime) {
|
|
|
71
71
|
ensureTheme();
|
|
72
72
|
const statusUi = createTuiFacade(runtime ?? { getColumns: () => 80, getRows: () => 24, requestRender() { } });
|
|
73
73
|
let workingOverride;
|
|
74
|
-
let
|
|
75
|
-
let
|
|
74
|
+
let outputPad = PINNED_PI_LAYOUT.outputPad;
|
|
75
|
+
let component = statusComponent(view, statusUi, workingOverride, outputPad);
|
|
76
|
+
let signature = statusSignature(view, workingOverride, outputPad);
|
|
76
77
|
const rebuild = () => {
|
|
77
|
-
const nextSignature = statusSignature(view, workingOverride);
|
|
78
|
+
const nextSignature = statusSignature(view, workingOverride, outputPad);
|
|
78
79
|
if (nextSignature === signature)
|
|
79
80
|
return;
|
|
80
81
|
if (component !== undefined && "dispose" in component && typeof component.dispose === "function")
|
|
81
82
|
component.dispose();
|
|
82
83
|
signature = nextSignature;
|
|
83
|
-
component = statusComponent(view, statusUi, workingOverride);
|
|
84
|
+
component = statusComponent(view, statusUi, workingOverride, outputPad);
|
|
84
85
|
};
|
|
85
86
|
return {
|
|
86
87
|
render: width => component?.render(width) ?? [],
|
|
@@ -93,6 +94,10 @@ export function createPiShellStatus(view, runtime) {
|
|
|
93
94
|
workingOverride = message;
|
|
94
95
|
rebuild();
|
|
95
96
|
},
|
|
97
|
+
setOutputPad(padding) {
|
|
98
|
+
outputPad = padding;
|
|
99
|
+
rebuild();
|
|
100
|
+
},
|
|
96
101
|
dispose() {
|
|
97
102
|
if (component !== undefined && "dispose" in component && typeof component.dispose === "function")
|
|
98
103
|
component.dispose();
|
|
@@ -121,19 +126,19 @@ export function createPiQueuedInputStatus(submissions, presentation = "pinned")
|
|
|
121
126
|
},
|
|
122
127
|
};
|
|
123
128
|
}
|
|
124
|
-
function statusComponent(view, ui, workingOverride) {
|
|
129
|
+
function statusComponent(view, ui, workingOverride, outputPad) {
|
|
125
130
|
if (view.lifecycle === "busy")
|
|
126
131
|
return new WorkingStatusIndicator(ui, workingOverride ?? view.status.workingMessage ?? "Working...");
|
|
127
132
|
if (view.lifecycle === "failed") {
|
|
128
|
-
return new Text(piTheme().fg("error", view.status.diagnostics.at(-1) ?? "Session failed"),
|
|
133
|
+
return new Text(piTheme().fg("error", view.status.diagnostics.at(-1) ?? "Session failed"), outputPad, 0);
|
|
129
134
|
}
|
|
130
135
|
if (view.status.workingMessage !== null) {
|
|
131
|
-
return new Text(piTheme().fg("muted", view.status.workingMessage),
|
|
136
|
+
return new Text(piTheme().fg("muted", view.status.workingMessage), outputPad, 0);
|
|
132
137
|
}
|
|
133
138
|
return undefined;
|
|
134
139
|
}
|
|
135
|
-
function statusSignature(view, workingOverride) {
|
|
136
|
-
return `${view.lifecycle}\u0000${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
|
|
140
|
+
function statusSignature(view, workingOverride, outputPad) {
|
|
141
|
+
return `${outputPad}\u0000${view.lifecycle}\u0000${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
|
|
137
142
|
}
|
|
138
143
|
function queuedInputText(submissions, presentation) {
|
|
139
144
|
if (submissions.length === 0)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type PiShellComponentPort } from "./shell-shared-facade.js";
|
|
2
|
+
export interface PiShellSessionInfoPresentation {
|
|
3
|
+
readonly sessionName?: string;
|
|
4
|
+
readonly stats: {
|
|
5
|
+
readonly sessionFile?: string;
|
|
6
|
+
readonly sessionId: string;
|
|
7
|
+
readonly userMessages: number;
|
|
8
|
+
readonly assistantMessages: number;
|
|
9
|
+
readonly toolCalls: number;
|
|
10
|
+
readonly toolResults: number;
|
|
11
|
+
readonly totalMessages: number;
|
|
12
|
+
readonly tokens: {
|
|
13
|
+
readonly input: number;
|
|
14
|
+
readonly output: number;
|
|
15
|
+
readonly cacheRead: number;
|
|
16
|
+
readonly cacheWrite: number;
|
|
17
|
+
readonly total: number;
|
|
18
|
+
};
|
|
19
|
+
readonly cost: number;
|
|
20
|
+
};
|
|
21
|
+
readonly cacheWaste: {
|
|
22
|
+
readonly missedTokens: number;
|
|
23
|
+
readonly missedCost: number;
|
|
24
|
+
readonly missCount: number;
|
|
25
|
+
};
|
|
26
|
+
readonly usageBreakdown: readonly {
|
|
27
|
+
readonly key: string;
|
|
28
|
+
readonly cost: number;
|
|
29
|
+
readonly tokens: number;
|
|
30
|
+
}[];
|
|
31
|
+
}
|
|
32
|
+
export declare function renderPiShellStatusText(message: string, width: number, outputPad?: 0 | 1): readonly string[];
|
|
33
|
+
export declare function createPiShellSessionInfo(presentation: PiShellSessionInfoPresentation): PiShellComponentPort;
|
|
34
|
+
export declare function createPiShellCollapsedChangelog(): PiShellComponentPort;
|
|
35
|
+
export declare function createPiShellChangelog(markdown: string): PiShellComponentPort;
|
|
36
|
+
export declare function createPiShellHotkeys(): PiShellComponentPort;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DynamicBorder, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Container, Markdown, Spacer, Text } from "#pi-tui";
|
|
3
|
+
import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
|
|
4
|
+
import { PINNED_PI_LAYOUT, piTheme } from "./theme.js";
|
|
5
|
+
import { componentPort, ensureTheme, formatSessionTokens } from "./shell-shared-facade.js";
|
|
6
|
+
export function renderPiShellStatusText(message, width, outputPad = PINNED_PI_LAYOUT.outputPad) {
|
|
7
|
+
ensureTheme();
|
|
8
|
+
return new Text(piTheme().fg("dim", message), outputPad, 0).render(width);
|
|
9
|
+
}
|
|
10
|
+
export function createPiShellSessionInfo(presentation) {
|
|
11
|
+
ensureTheme();
|
|
12
|
+
const { stats, sessionName, cacheWaste, usageBreakdown } = presentation;
|
|
13
|
+
let info = `${piTheme().bold("Session Info")}\n\n`;
|
|
14
|
+
if (sessionName)
|
|
15
|
+
info += `${piTheme().fg("dim", "Name:")} ${sessionName}\n`;
|
|
16
|
+
info += `${piTheme().fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n${piTheme().fg("dim", "ID:")} ${stats.sessionId}\n\n`;
|
|
17
|
+
info += `${piTheme().bold("Messages")}\n${piTheme().fg("dim", "Total:")} ${stats.totalMessages}\n${piTheme().fg("dim", "User:")} ${stats.userMessages}\n`;
|
|
18
|
+
info += `${piTheme().fg("dim", "Assistant:")} ${stats.assistantMessages}\n${piTheme().fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results\n\n`;
|
|
19
|
+
info += `${piTheme().bold("Tokens")}\n`;
|
|
20
|
+
const { input, cacheRead, cacheWrite } = stats.tokens;
|
|
21
|
+
const promptTokens = input + cacheRead + cacheWrite;
|
|
22
|
+
info += `${piTheme().fg("dim", "Input:")} ${promptTokens.toLocaleString()}\n`;
|
|
23
|
+
if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
|
|
24
|
+
info += ` ${piTheme().fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}\n`;
|
|
25
|
+
const written = cacheWrite > 0 ? ` ${piTheme().fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
|
|
26
|
+
info += ` ${piTheme().fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}\n`;
|
|
27
|
+
}
|
|
28
|
+
info += `${piTheme().fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n${piTheme().fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
29
|
+
if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
|
|
30
|
+
info += `\n${piTheme().bold("Cost")}\n${piTheme().fg("dim", "Total:")} $${stats.cost.toFixed(3)}`;
|
|
31
|
+
if (usageBreakdown.length > 1)
|
|
32
|
+
for (const entry of usageBreakdown)
|
|
33
|
+
info += `\n ${piTheme().fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}`;
|
|
34
|
+
if (cacheWaste.missedTokens > 0) {
|
|
35
|
+
const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`}`;
|
|
36
|
+
info += cacheWaste.missedCost >= 0.0001 ? `\n${piTheme().fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}` : `\n${piTheme().fg("dim", "Cache Re-billed:")} ${detail}`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const container = new Container();
|
|
40
|
+
container.addChild(new Spacer(1));
|
|
41
|
+
container.addChild(new Text(info, 1, 0));
|
|
42
|
+
return componentPort(container);
|
|
43
|
+
}
|
|
44
|
+
export function createPiShellCollapsedChangelog() {
|
|
45
|
+
ensureTheme();
|
|
46
|
+
const container = new Container();
|
|
47
|
+
container.addChild(new Spacer(1));
|
|
48
|
+
container.addChild(new DynamicBorder());
|
|
49
|
+
container.addChild(new Text(`${piTheme().bold(piTheme().fg("accent", "What's New"))}\n${piTheme().fg("muted", "Run /changelog to view the full release notes.")}`, 1, 0));
|
|
50
|
+
container.addChild(new DynamicBorder());
|
|
51
|
+
return componentPort(container);
|
|
52
|
+
}
|
|
53
|
+
export function createPiShellChangelog(markdown) {
|
|
54
|
+
ensureTheme();
|
|
55
|
+
const container = new Container();
|
|
56
|
+
container.addChild(new Spacer(1));
|
|
57
|
+
container.addChild(new DynamicBorder());
|
|
58
|
+
container.addChild(new Text(piTheme().bold(piTheme().fg("accent", "What's New")), 1, 0));
|
|
59
|
+
container.addChild(new Spacer(1));
|
|
60
|
+
container.addChild(new Markdown(markdown.trim() || "No changelog entries found.", 1, 1, getMarkdownTheme()));
|
|
61
|
+
container.addChild(new DynamicBorder());
|
|
62
|
+
return componentPort(container);
|
|
63
|
+
}
|
|
64
|
+
export function createPiShellHotkeys() {
|
|
65
|
+
ensureTheme();
|
|
66
|
+
const keys = new KeybindingsManager();
|
|
67
|
+
const display = (action) => keys.getKeys(action).map(key => key.split("+").map(part => part.charAt(0).toUpperCase() + part.slice(1)).join("+")).join("/");
|
|
68
|
+
const row = (actions, description) => `| ${actions.map(action => `\`${display(action)}\``).join(" / ")} | ${description} |`;
|
|
69
|
+
const markdown = ["**Navigation**", "| Key | Action |", "|-----|--------|", row(["tui.editor.cursorUp", "tui.editor.cursorDown", "tui.editor.cursorLeft", "tui.editor.cursorRight"], "Move cursor / browse history"), row(["tui.editor.cursorWordLeft", "tui.editor.cursorWordRight"], "Move by word"), row(["tui.editor.cursorLineStart"], "Start of line"), row(["tui.editor.cursorLineEnd"], "End of line"), row(["tui.editor.jumpForward"], "Jump forward to character"), row(["tui.editor.jumpBackward"], "Jump backward to character"), row(["tui.editor.pageUp", "tui.editor.pageDown"], "Scroll by page"), "", "**Editing**", "| Key | Action |", "|-----|--------|", row(["tui.input.submit"], "Send message"), row(["tui.input.newLine"], `New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""}`), row(["tui.editor.deleteWordBackward"], "Delete word backwards"), row(["tui.editor.deleteWordForward"], "Delete word forwards"), row(["tui.editor.deleteToLineStart"], "Delete to start of line"), row(["tui.editor.deleteToLineEnd"], "Delete to end of line"), row(["tui.editor.yank"], "Paste the most-recently-deleted text"), row(["tui.editor.yankPop"], "Cycle through the deleted text after pasting"), row(["tui.editor.undo"], "Undo"), "", "**Other**", "| Key | Action |", "|-----|--------|", row(["tui.input.tab"], "Path completion / accept autocomplete"), row(["app.interrupt"], "Cancel autocomplete / abort streaming"), row(["app.clear"], "Clear editor (first) / exit (second)"), row(["app.exit"], "Exit (when editor is empty)"), row(["app.suspend"], "Suspend to background"), row(["app.thinking.cycle"], "Cycle thinking level"), row(["app.model.cycleForward", "app.model.cycleBackward"], "Cycle models"), row(["app.model.select"], "Open model selector"), row(["app.tools.expand"], "Toggle tool output expansion"), row(["app.thinking.toggle"], "Toggle thinking block visibility"), row(["app.editor.external"], "Edit message in external editor"), row(["app.message.copy"], "Copy last assistant message"), row(["app.message.followUp"], "Queue follow-up message"), row(["app.message.dequeue"], "Restore queued messages"), row(["app.clipboard.pasteImage"], "Paste image or text from clipboard"), "| `/` | Slash commands |", "| `!` | Run bash command |", "| `!!` | Run bash command (excluded from context) |"].join("\n");
|
|
70
|
+
const container = new Container();
|
|
71
|
+
container.addChild(new Spacer(1));
|
|
72
|
+
container.addChild(new DynamicBorder());
|
|
73
|
+
container.addChild(new Text(piTheme().bold(piTheme().fg("accent", "Keyboard Shortcuts")), 1, 0));
|
|
74
|
+
container.addChild(new Spacer(1));
|
|
75
|
+
container.addChild(new Markdown(markdown, 1, 1, getMarkdownTheme()));
|
|
76
|
+
container.addChild(new DynamicBorder());
|
|
77
|
+
return componentPort(container);
|
|
78
|
+
}
|
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
import { AssistantMessageComponent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type MermaidRenderingMode } from "./upstream/components/mermaid.js";
|
|
2
3
|
import type { OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
|
|
3
4
|
import { type PiShellSubmittedPromptComposer } from "./submitted-prompt-adapter.js";
|
|
4
5
|
export type { PiShellSubmittedPromptComposer } from "./submitted-prompt-adapter.js";
|
|
5
|
-
import { type
|
|
6
|
-
export
|
|
7
|
-
readonly sessionName?: string;
|
|
8
|
-
readonly stats: {
|
|
9
|
-
readonly sessionFile?: string;
|
|
10
|
-
readonly sessionId: string;
|
|
11
|
-
readonly userMessages: number;
|
|
12
|
-
readonly assistantMessages: number;
|
|
13
|
-
readonly toolCalls: number;
|
|
14
|
-
readonly toolResults: number;
|
|
15
|
-
readonly totalMessages: number;
|
|
16
|
-
readonly tokens: {
|
|
17
|
-
readonly input: number;
|
|
18
|
-
readonly output: number;
|
|
19
|
-
readonly cacheRead: number;
|
|
20
|
-
readonly cacheWrite: number;
|
|
21
|
-
readonly total: number;
|
|
22
|
-
};
|
|
23
|
-
readonly cost: number;
|
|
24
|
-
};
|
|
25
|
-
readonly cacheWaste: {
|
|
26
|
-
readonly missedTokens: number;
|
|
27
|
-
readonly missedCost: number;
|
|
28
|
-
readonly missCount: number;
|
|
29
|
-
};
|
|
30
|
-
readonly usageBreakdown: readonly {
|
|
31
|
-
readonly key: string;
|
|
32
|
-
readonly cost: number;
|
|
33
|
-
readonly tokens: number;
|
|
34
|
-
}[];
|
|
35
|
-
}
|
|
36
|
-
export declare function renderPiShellStatusText(message: string, width: number): readonly string[];
|
|
37
|
-
export declare function createPiShellSessionInfo(presentation: PiShellSessionInfoPresentation): PiShellComponentPort;
|
|
38
|
-
export declare function createPiShellChangelog(markdown: string): PiShellComponentPort;
|
|
39
|
-
export declare function createPiShellHotkeys(): PiShellComponentPort;
|
|
40
|
-
export declare function createPiShellTranscriptComponent(initial: OwnedUiTranscriptBlock, cwd: string, extensions?: PiShellExtensionRendererResolver, submittedPrompt?: PiShellSubmittedPromptComposer): PiShellTranscriptComponentPort;
|
|
6
|
+
import { type PiShellExtensionRendererResolver, type PiShellImageAssetResolver, type PiShellTranscriptComponentPort } from "./shell-shared-facade.js";
|
|
7
|
+
export declare function createPiShellTranscriptComponent(initial: OwnedUiTranscriptBlock, cwd: string, extensions?: PiShellExtensionRendererResolver, submittedPrompt?: PiShellSubmittedPromptComposer, initialOutputPad?: 0 | 1, initialHideThinkingBlock?: boolean, initialMermaidRenderingMode?: MermaidRenderingMode, initialShowImages?: boolean, initialImageWidthCells?: number, imageAssets?: PiShellImageAssetResolver): PiShellTranscriptComponentPort;
|
|
41
8
|
export declare function renderPiShellTranscriptBlock(block: OwnedUiTranscriptBlock, width: number, cwd: string): readonly string[];
|
|
42
9
|
/**
|
|
43
10
|
* Pinned Pi's CLI prints startup diagnostics with `reportDiagnostics` before
|