@oh-my-pi/pi-coding-agent 15.11.7 → 15.12.0
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/CHANGELOG.md +63 -1
- package/dist/cli.js +8106 -7708
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/collab/crypto.d.ts +7 -0
- package/dist/types/collab/guest.d.ts +23 -0
- package/dist/types/collab/host.d.ts +29 -0
- package/dist/types/collab/protocol.d.ts +113 -0
- package/dist/types/collab/relay-client.d.ts +22 -0
- package/dist/types/commands/join.d.ts +12 -0
- package/dist/types/config/settings-schema.d.ts +60 -5
- package/dist/types/export/custom-share.d.ts +1 -2
- package/dist/types/export/html/index.d.ts +39 -1
- package/dist/types/export/share.d.ts +43 -0
- package/dist/types/extensibility/slash-commands.d.ts +1 -11
- package/dist/types/main.d.ts +2 -0
- package/dist/types/modes/components/agent-hub.d.ts +32 -1
- package/dist/types/modes/components/collab-prompt-message.d.ts +10 -0
- package/dist/types/modes/components/hook-selector.d.ts +4 -6
- package/dist/types/modes/components/segment-track.d.ts +11 -6
- package/dist/types/modes/components/status-line/component.d.ts +10 -2
- package/dist/types/modes/components/status-line/types.d.ts +11 -0
- package/dist/types/modes/controllers/event-controller.d.ts +7 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/session-focus-controller.d.ts +31 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -0
- package/dist/types/modes/session-observer-registry.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +20 -0
- package/dist/types/session/agent-session.d.ts +13 -0
- package/dist/types/session/codex-auto-reset.d.ts +8 -4
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/snapcompact-inline.d.ts +6 -3
- package/dist/types/slash-commands/builtin-registry.d.ts +9 -0
- package/dist/types/task/executor.d.ts +7 -0
- package/dist/types/task/types.d.ts +9 -0
- package/package.json +14 -13
- package/scripts/bench-guard.ts +71 -0
- package/scripts/build-binary.ts +4 -0
- package/scripts/bundle-dist.ts +4 -0
- package/scripts/generate-share-viewer.ts +34 -0
- package/src/cli/args.ts +2 -0
- package/src/cli-commands.ts +1 -0
- package/src/collab/crypto.ts +63 -0
- package/src/collab/guest.ts +450 -0
- package/src/collab/host.ts +556 -0
- package/src/collab/protocol.ts +232 -0
- package/src/collab/relay-client.ts +216 -0
- package/src/commands/join.ts +39 -0
- package/src/config/model-registry.ts +22 -14
- package/src/config/settings-schema.ts +67 -5
- package/src/config/settings.ts +12 -0
- package/src/export/custom-share.ts +1 -1
- package/src/export/html/index.ts +122 -17
- package/src/export/html/share-loader.js +102 -0
- package/src/export/html/template.css +745 -459
- package/src/export/html/template.html +6 -3
- package/src/export/html/template.js +240 -915
- package/src/export/html/tool-views.generated.js +38 -0
- package/src/export/share.ts +268 -0
- package/src/extensibility/slash-commands.ts +1 -97
- package/src/internal-urls/docs-index.generated.ts +74 -73
- package/src/main.ts +33 -11
- package/src/modes/components/agent-hub.ts +659 -431
- package/src/modes/components/assistant-message.ts +126 -6
- package/src/modes/components/collab-prompt-message.ts +30 -0
- package/src/modes/components/hook-selector.ts +4 -5
- package/src/modes/components/segment-track.ts +44 -7
- package/src/modes/components/status-line/component.ts +59 -6
- package/src/modes/components/status-line/presets.ts +1 -1
- package/src/modes/components/status-line/segments.ts +18 -1
- package/src/modes/components/status-line/types.ts +12 -0
- package/src/modes/components/tips.txt +4 -1
- package/src/modes/controllers/command-controller.ts +55 -96
- package/src/modes/controllers/event-controller.ts +45 -16
- package/src/modes/controllers/input-controller.ts +175 -9
- package/src/modes/controllers/selector-controller.ts +13 -15
- package/src/modes/controllers/session-focus-controller.ts +112 -0
- package/src/modes/controllers/streaming-reveal.ts +7 -0
- package/src/modes/interactive-mode.ts +56 -6
- package/src/modes/session-observer-registry.ts +11 -0
- package/src/modes/theme/theme.ts +6 -0
- package/src/modes/types.ts +20 -0
- package/src/modes/utils/ui-helpers.ts +23 -13
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk.ts +239 -36
- package/src/session/agent-session.ts +82 -7
- package/src/session/codex-auto-reset.ts +23 -11
- package/src/session/session-manager.ts +44 -0
- package/src/session/snapcompact-inline.ts +9 -3
- package/src/slash-commands/builtin-registry.ts +261 -24
- package/src/task/executor.ts +14 -0
- package/src/task/index.ts +5 -1
- package/src/task/render.ts +76 -5
- package/src/task/types.ts +9 -0
- package/src/tiny/worker.ts +17 -95
- package/src/tools/job.ts +6 -9
- package/src/tools/read.ts +38 -5
- package/src/tools/write.ts +13 -42
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/export/html/template.generated.d.ts +0 -1
- package/dist/types/export/html/template.macro.d.ts +0 -5
- package/dist/types/tiny/compiled-runtime.d.ts +0 -35
- package/scripts/generate-template.ts +0 -33
- package/src/bun-imports.d.ts +0 -28
- package/src/export/html/template.generated.ts +0 -2
- package/src/export/html/template.macro.ts +0 -25
- package/src/tiny/compiled-runtime.ts +0 -179
|
@@ -23,6 +23,13 @@ export declare class EventController {
|
|
|
23
23
|
*/
|
|
24
24
|
notifyInterrupting(): void;
|
|
25
25
|
subscribeToAgent(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Clear every transcript-anchored/turn-scoped piece of state. Used by the
|
|
28
|
+
* session focus proxy when re-pointing the transcript at another session:
|
|
29
|
+
* components, timers, and stream-reveal state all reference the previous
|
|
30
|
+
* session's transcript and must not bleed into the new one.
|
|
31
|
+
*/
|
|
32
|
+
resetTranscriptAnchors(): void;
|
|
26
33
|
handleEvent(event: AgentSessionEvent): Promise<void>;
|
|
27
34
|
sendCompletionNotification(): void;
|
|
28
35
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AutocompleteProvider, type SlashCommand } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
3
3
|
import { readImageFromClipboard, readTextFromClipboard } from "../../utils/clipboard";
|
|
4
4
|
export declare class InputController {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionFocusController - Weak retargeting primitive between the rendering/
|
|
3
|
+
* input layer and the AgentSession it displays.
|
|
4
|
+
*
|
|
5
|
+
* Focusing re-points the transcript, streaming event subscription, status
|
|
6
|
+
* line, and editor prompt/interrupt at a subagent's live AgentSession (from
|
|
7
|
+
* AgentRegistry) without touching the main session underneath; unfocusing
|
|
8
|
+
* re-attaches the main session and rebuilds the transcript from its
|
|
9
|
+
* authoritative state.
|
|
10
|
+
*/
|
|
11
|
+
import { AgentLifecycleManager } from "../../registry/agent-lifecycle";
|
|
12
|
+
import { AgentRegistry } from "../../registry/agent-registry";
|
|
13
|
+
import type { AgentSession } from "../../session/agent-session";
|
|
14
|
+
import type { InteractiveModeContext } from "../types";
|
|
15
|
+
export declare class SessionFocusController {
|
|
16
|
+
#private;
|
|
17
|
+
private ctx;
|
|
18
|
+
private registry;
|
|
19
|
+
private lifecycle;
|
|
20
|
+
constructor(ctx: InteractiveModeContext, registry?: AgentRegistry, lifecycle?: () => AgentLifecycleManager);
|
|
21
|
+
get focusedAgentId(): string | undefined;
|
|
22
|
+
/** Focused live session, undefined when unfocused. */
|
|
23
|
+
get target(): AgentSession | undefined;
|
|
24
|
+
/** Focus the main view on an agent's live session. Throws an Error with a user-displayable message. */
|
|
25
|
+
focusAgent(id: string): Promise<void>;
|
|
26
|
+
/** Focus the focused agent's parent agent, falling back to the main session. No-op when unfocused. */
|
|
27
|
+
focusParent(): Promise<void>;
|
|
28
|
+
/** Return to the main session. No-op when unfocused. */
|
|
29
|
+
unfocus(): Promise<void>;
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
@@ -3,6 +3,8 @@ import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
|
3
3
|
import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Component, EditorTheme } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { Container, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import type { CollabGuestLink } from "../collab/guest";
|
|
7
|
+
import type { CollabHost } from "../collab/host";
|
|
6
8
|
import { KeybindingsManager } from "../config/keybindings";
|
|
7
9
|
import { Settings } from "../config/settings";
|
|
8
10
|
import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
|
|
@@ -25,6 +27,7 @@ import type { HookSelectorComponent, HookSelectorSlider } from "./components/hoo
|
|
|
25
27
|
import { StatusLineComponent } from "./components/status-line";
|
|
26
28
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
27
29
|
import { TranscriptContainer } from "./components/transcript-container";
|
|
30
|
+
import { EventController } from "./controllers/event-controller";
|
|
28
31
|
import { type LoopLimitRuntime } from "./loop-limit";
|
|
29
32
|
import { OAuthManualInputManager } from "./oauth-manual-input";
|
|
30
33
|
import type { ObservableSession } from "./session-observer-registry";
|
|
@@ -47,6 +50,9 @@ export interface InteractiveModeOptions {
|
|
|
47
50
|
* Build the anchored subagent HUD block: a bold accent "Subagents" header plus
|
|
48
51
|
* one hooked row per running agent in the same `Id: description` shape the
|
|
49
52
|
* inline task rows use (muted task preview when no description was given).
|
|
53
|
+
* Only detached background spawns are listed: a sync task call blocks the
|
|
54
|
+
* parent turn and its inline tool block already renders progress live, and
|
|
55
|
+
* eval `agent()` spawns are rendered by their own eval cell tree.
|
|
50
56
|
* Returns an empty array when nothing is running so the container can clear.
|
|
51
57
|
*/
|
|
52
58
|
export declare function renderSubagentHudLines(sessions: ObservableSession[], columns: number): string[];
|
|
@@ -119,8 +125,18 @@ export declare class InteractiveMode implements InteractiveModeContext {
|
|
|
119
125
|
fileSlashCommands: Set<string>;
|
|
120
126
|
skillCommands: Map<string, string>;
|
|
121
127
|
oauthManualInput: OAuthManualInputManager;
|
|
128
|
+
collabHost?: CollabHost;
|
|
129
|
+
collabGuest?: CollabGuestLink;
|
|
122
130
|
readonly lspServers: LspStartupServerInfo[] | undefined;
|
|
123
131
|
mcpManager?: MCPManager;
|
|
132
|
+
get eventController(): EventController;
|
|
133
|
+
get eventBus(): EventBus | undefined;
|
|
134
|
+
get viewSession(): AgentSession;
|
|
135
|
+
get focusedAgentId(): string | undefined;
|
|
136
|
+
focusAgentSession(id: string): Promise<void>;
|
|
137
|
+
focusParentSession(): Promise<void>;
|
|
138
|
+
unfocusSession(): Promise<void>;
|
|
139
|
+
clearTransientSessionUi(): void;
|
|
124
140
|
constructor(session: AgentSession, version: string, changelogMarkdown?: string | undefined, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: MCPManager, eventBus?: EventBus, titleSystemPrompt?: string);
|
|
125
141
|
playWelcomeIntro(): void;
|
|
126
142
|
init(options?: InteractiveModeInitOptions): Promise<void>;
|
|
@@ -9,6 +9,13 @@ export interface ObservableSession {
|
|
|
9
9
|
status: "active" | "completed" | "failed" | "aborted";
|
|
10
10
|
sessionFile?: string;
|
|
11
11
|
parentToolCallId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Spawn runs as a detached background job (parent turn not blocked on it).
|
|
14
|
+
* The anchored subagent HUD only lists detached spawns: sync task spawns
|
|
15
|
+
* and eval `agent()` spawns are already rendered live by their own inline
|
|
16
|
+
* tool block / eval cell.
|
|
17
|
+
*/
|
|
18
|
+
detached?: boolean;
|
|
12
19
|
index?: number;
|
|
13
20
|
lastUpdate: number;
|
|
14
21
|
/** Latest progress snapshot from the subagent executor */
|
|
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
|
|
|
6
6
|
/**
|
|
7
7
|
* All available symbol keys organized by category.
|
|
8
8
|
*/
|
|
9
|
-
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.job" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc";
|
|
9
|
+
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc";
|
|
10
10
|
export type SpinnerType = "status" | "activity";
|
|
11
11
|
export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
|
|
12
12
|
/** Check if a string is a valid ThemeColor value */
|
|
@@ -169,6 +169,7 @@ export declare class Theme {
|
|
|
169
169
|
cost: string;
|
|
170
170
|
time: string;
|
|
171
171
|
pi: string;
|
|
172
|
+
ghost: string;
|
|
172
173
|
agents: string;
|
|
173
174
|
job: string;
|
|
174
175
|
cache: string;
|
|
@@ -2,6 +2,8 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
3
|
import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Component, Container, EditorTheme, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
|
|
5
|
+
import type { CollabGuestLink } from "../collab/guest";
|
|
6
|
+
import type { CollabHost } from "../collab/host";
|
|
5
7
|
import type { KeybindingsManager } from "../config/keybindings";
|
|
6
8
|
import type { Settings } from "../config/settings";
|
|
7
9
|
import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
|
|
@@ -13,6 +15,7 @@ import type { HistoryStorage } from "../session/history-storage";
|
|
|
13
15
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
14
16
|
import type { ShakeMode } from "../session/shake-types";
|
|
15
17
|
import type { LspStartupServerInfo } from "../tools";
|
|
18
|
+
import type { EventBus } from "../utils/event-bus";
|
|
16
19
|
import type { AssistantMessageComponent } from "./components/assistant-message";
|
|
17
20
|
import type { BashExecutionComponent } from "./components/bash-execution";
|
|
18
21
|
import type { CustomEditor } from "./components/custom-editor";
|
|
@@ -23,6 +26,7 @@ import type { HookSelectorComponent, HookSelectorOptions } from "./components/ho
|
|
|
23
26
|
import type { StatusLineComponent } from "./components/status-line";
|
|
24
27
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
25
28
|
import type { TranscriptContainer } from "./components/transcript-container";
|
|
29
|
+
import type { EventController } from "./controllers/event-controller";
|
|
26
30
|
import type { LoopLimitRuntime } from "./loop-limit";
|
|
27
31
|
import type { OAuthManualInputManager } from "./oauth-manual-input";
|
|
28
32
|
import type { Theme } from "./theme/theme";
|
|
@@ -77,6 +81,18 @@ export interface InteractiveModeContext {
|
|
|
77
81
|
statusLine: StatusLineComponent;
|
|
78
82
|
session: AgentSession;
|
|
79
83
|
sessionManager: SessionManager;
|
|
84
|
+
/** Session the transcript/editor/status are attached to: the focused agent's, else `session`. */
|
|
85
|
+
readonly viewSession: AgentSession;
|
|
86
|
+
/** Id of the focused agent, undefined when the main session is attached. */
|
|
87
|
+
readonly focusedAgentId: string | undefined;
|
|
88
|
+
/** Focus the main view on an agent's live session (delegates to SessionFocusController.focusAgent). */
|
|
89
|
+
focusAgentSession(id: string): Promise<void>;
|
|
90
|
+
/** Focus the focused agent's parent session, falling back to main (delegates to focusParent). */
|
|
91
|
+
focusParentSession(): Promise<void>;
|
|
92
|
+
/** Return the view to the main session (delegates to SessionFocusController.unfocus). */
|
|
93
|
+
unfocusSession(): Promise<void>;
|
|
94
|
+
/** Clear loader, status/pending containers, streaming state, and pending tools. */
|
|
95
|
+
clearTransientSessionUi(): void;
|
|
80
96
|
settings: Settings;
|
|
81
97
|
keybindings: KeybindingsManager;
|
|
82
98
|
agent: AgentSession["agent"];
|
|
@@ -84,6 +100,10 @@ export interface InteractiveModeContext {
|
|
|
84
100
|
mcpManager?: MCPManager;
|
|
85
101
|
lspServers?: LspStartupServerInfo[];
|
|
86
102
|
titleSystemPrompt?: string;
|
|
103
|
+
collabHost?: CollabHost;
|
|
104
|
+
collabGuest?: CollabGuestLink;
|
|
105
|
+
eventController: EventController;
|
|
106
|
+
eventBus?: EventBus;
|
|
87
107
|
isInitialized: boolean;
|
|
88
108
|
isBashMode: boolean;
|
|
89
109
|
toolOutputExpanded: boolean;
|
|
@@ -341,6 +341,8 @@ export declare class AgentSession {
|
|
|
341
341
|
getMnemopiSessionState(): MnemopiSessionState | undefined;
|
|
342
342
|
/** TTSR manager for time-traveling stream rules */
|
|
343
343
|
get ttsrManager(): TtsrManager | undefined;
|
|
344
|
+
/** Secret obfuscator, when secrets are configured; /share redaction reuses it. */
|
|
345
|
+
get obfuscator(): SecretObfuscator | undefined;
|
|
344
346
|
/** Whether a TTSR abort is pending (stream was aborted to inject rules) */
|
|
345
347
|
get isTtsrAbortPending(): boolean;
|
|
346
348
|
/** Whether the plan-mode → compaction transition's expected internal abort is
|
|
@@ -385,6 +387,9 @@ export declare class AgentSession {
|
|
|
385
387
|
*/
|
|
386
388
|
subscribe(listener: AgentSessionEventListener): () => void;
|
|
387
389
|
subscribeCommandMetadataChanged(listener: CommandMetadataChangedListener): () => void;
|
|
390
|
+
/** True once dispose() has begun; deferred background work (e.g. the deferred
|
|
391
|
+
* MCP discovery task in sdk.ts) must not touch the session past this point. */
|
|
392
|
+
get isDisposed(): boolean;
|
|
388
393
|
/**
|
|
389
394
|
* Synchronously mark the session as disposing so new work is rejected
|
|
390
395
|
* immediately: Python/eval starts throw, queued asides are dropped, and the
|
|
@@ -443,6 +448,14 @@ export declare class AgentSession {
|
|
|
443
448
|
*/
|
|
444
449
|
getAllToolNames(): string[];
|
|
445
450
|
isMCPDiscoveryEnabled(): boolean;
|
|
451
|
+
/**
|
|
452
|
+
* Flip MCP discovery on after deferred discovery learns the real tool count.
|
|
453
|
+
* UI sessions resolve `tools.discoveryMode: "auto"` before MCP servers
|
|
454
|
+
* connect, so a large MCP toolset discovered later must be able to upgrade
|
|
455
|
+
* the session from the force-activate path to the discovery path. One-way:
|
|
456
|
+
* discovery is never downgraded mid-session.
|
|
457
|
+
*/
|
|
458
|
+
enableMCPDiscovery(): void;
|
|
446
459
|
getSelectedMCPToolNames(): string[];
|
|
447
460
|
activateDiscoveredMCPTools(toolNames: string[]): Promise<string[]>;
|
|
448
461
|
isToolDiscoveryEnabled(): boolean;
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
* eligibility off exact limit ids (`openai-codex:primary` /
|
|
25
25
|
* `openai-codex:secondary`) and `usedFraction`, never off `status`.
|
|
26
26
|
*
|
|
27
|
-
* ANTI-WASTE GATES (in evaluation order): the policy must be
|
|
28
|
-
*
|
|
27
|
+
* ANTI-WASTE GATES (in evaluation order): the policy must not be set to "no";
|
|
28
|
+
* the active model must be Codex (not Spark — a Spark block lives on a
|
|
29
29
|
* separate meter and it is unknown whether a credit even resets it); a fresh
|
|
30
30
|
* usage report for the active account must confirm `limitReached`; the WEEKLY
|
|
31
31
|
* (secondary) window must be genuinely exhausted — a 5h-only block self-heals
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
* read-only views are passed in so the predicate itself stays deterministic.
|
|
39
39
|
*/
|
|
40
40
|
import type { OAuthAccountIdentity, ResetCreditTarget, UsageReport } from "@oh-my-pi/pi-ai";
|
|
41
|
+
import type { CodexAutoRedeemMode } from "../config/settings-schema";
|
|
41
42
|
/** Weekly window counts as exhausted at `usedFraction >= 0.999` (used_percent >= 99.9). */
|
|
42
43
|
export declare const WEEKLY_EXHAUSTED_MIN_FRACTION = 0.999;
|
|
43
44
|
/** A weekly reset can never be more than one window length (7d) away; +1h slack for skew. */
|
|
@@ -48,6 +49,8 @@ export declare const REPORT_FRESHNESS_MS: number;
|
|
|
48
49
|
export declare const ATTEMPT_COOLDOWN_MS = 60000;
|
|
49
50
|
/** Minute bucket for blockKey, absorbing `reset_after_seconds`-derived jitter. */
|
|
50
51
|
export declare const DEBOUNCE_BUCKET_MS = 60000;
|
|
52
|
+
export declare function shouldEvaluateCodexAutoRedeem(mode: CodexAutoRedeemMode): boolean;
|
|
53
|
+
export declare function shouldPromptCodexAutoRedeem(mode: CodexAutoRedeemMode): boolean;
|
|
51
54
|
export type CodexAutoRedeemSkipReason = "disabled" | "wrong-provider" | "spark-model" | "no-identity" | "no-report" | "stale-report" | "not-limit-reached" | "weekly-not-exhausted" | "no-reset-time" | "reset-too-soon" | "reset-implausible" | "credits-unknown" | "reserve" | "already-attempted" | "cooldown";
|
|
52
55
|
export interface CodexAutoRedeemInput {
|
|
53
56
|
nowMs: number;
|
|
@@ -67,7 +70,7 @@ export interface CodexAutoRedeemInput {
|
|
|
67
70
|
attemptedBlockKeys: ReadonlySet<string>;
|
|
68
71
|
lastAttemptAtByAccount: ReadonlyMap<string, number>;
|
|
69
72
|
}
|
|
70
|
-
export
|
|
73
|
+
export interface CodexAutoRedeemRedeemDecision {
|
|
71
74
|
redeem: true;
|
|
72
75
|
target: ResetCreditTarget;
|
|
73
76
|
accountKey: string;
|
|
@@ -75,7 +78,8 @@ export type CodexAutoRedeemDecision = {
|
|
|
75
78
|
weeklyResetAtMs: number;
|
|
76
79
|
remainingMs: number;
|
|
77
80
|
availableCount: number;
|
|
78
|
-
}
|
|
81
|
+
}
|
|
82
|
+
export type CodexAutoRedeemDecision = CodexAutoRedeemRedeemDecision | {
|
|
79
83
|
redeem: false;
|
|
80
84
|
reason: CodexAutoRedeemSkipReason;
|
|
81
85
|
};
|
|
@@ -309,6 +309,12 @@ export declare class SessionManager {
|
|
|
309
309
|
private sessionDir;
|
|
310
310
|
private readonly persist;
|
|
311
311
|
private readonly storage;
|
|
312
|
+
/**
|
|
313
|
+
* Collab replication tap: invoked for every appended entry with the
|
|
314
|
+
* in-memory (pre-blob-externalization) entry, so inline images survive.
|
|
315
|
+
* Failures are swallowed — a broadcast error must never break persistence.
|
|
316
|
+
*/
|
|
317
|
+
onEntryAppended?: (entry: SessionEntry) => void;
|
|
312
318
|
private constructor();
|
|
313
319
|
/** Puts a binary blob into the blob store and returns the blob reference */
|
|
314
320
|
putBlob(data: Buffer, options?: BlobPutOptions): Promise<BlobPutResult>;
|
|
@@ -434,6 +440,21 @@ export declare class SessionManager {
|
|
|
434
440
|
*/
|
|
435
441
|
setSessionName(name: string, source?: "auto" | "user"): Promise<boolean>;
|
|
436
442
|
_persist(entry: SessionEntry): void;
|
|
443
|
+
/**
|
|
444
|
+
* Append a foreign (host-authored) entry verbatim, preserving its
|
|
445
|
+
* `id`/`parentId` — no id minting. Used by collab guests to mirror the
|
|
446
|
+
* host session into the local replica file.
|
|
447
|
+
*/
|
|
448
|
+
ingestReplicatedEntry(entry: SessionEntry): void;
|
|
449
|
+
/**
|
|
450
|
+
* Snapshot the session for collab replication: the live header plus a deep
|
|
451
|
+
* copy of every entry (the host mutates entries in place on
|
|
452
|
+
* truncation/rewrite paths, so guests must not share references).
|
|
453
|
+
*/
|
|
454
|
+
snapshotForReplication(): {
|
|
455
|
+
header: SessionHeader;
|
|
456
|
+
entries: SessionEntry[];
|
|
457
|
+
};
|
|
437
458
|
/** Append a message as child of current leaf, then advance leaf. Returns entry id.
|
|
438
459
|
* Does not allow writing CompactionSummaryMessage and BranchSummaryMessage directly.
|
|
439
460
|
* Reason: we want these to be top-level entries in the session, not message session entries,
|
|
@@ -24,14 +24,16 @@ export interface SnapcompactInlineOptions {
|
|
|
24
24
|
}
|
|
25
25
|
/** Tool-result swap candidate, in context order. */
|
|
26
26
|
export interface InlineToolResultCandidate {
|
|
27
|
-
/**
|
|
27
|
+
/** Stable identifier for rendering cache key and applying the swap. */
|
|
28
28
|
id: string;
|
|
29
|
-
/** Token count of the joined text blocks (0
|
|
29
|
+
/** Token count of the joined text blocks (0 for empty or image-carrying). */
|
|
30
30
|
textTokens: number;
|
|
31
|
-
/** Frames needed to render the text (0 = empty
|
|
31
|
+
/** Frames needed to render the text (0 = empty, below floor, image-carrying, or error). */
|
|
32
32
|
frames: number;
|
|
33
33
|
/** Already carries an image (screenshot etc.) — never re-imaged. */
|
|
34
34
|
hasImage: boolean;
|
|
35
|
+
/** Error tool results must stay text-only for provider API validation. */
|
|
36
|
+
isError?: boolean;
|
|
35
37
|
}
|
|
36
38
|
export interface InlineSystemPromptCandidate {
|
|
37
39
|
textTokens: number;
|
|
@@ -73,6 +75,7 @@ export interface InlineMessageView {
|
|
|
73
75
|
role: string;
|
|
74
76
|
toolCallId?: string;
|
|
75
77
|
content?: unknown;
|
|
78
|
+
isError?: boolean;
|
|
76
79
|
}
|
|
77
80
|
export interface SnapcompactSavingsEstimate {
|
|
78
81
|
/** Frames only ship on models that accept image input. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AutocompleteItem } from "@oh-my-pi/pi-tui";
|
|
1
2
|
import type { BuiltinSlashCommand, ParsedSlashCommand, SlashCommandResult, SlashCommandRuntime, SlashCommandSpec, TuiSlashCommandRuntime } from "./types";
|
|
2
3
|
export type { BuiltinSlashCommand, SubcommandDef } from "./types";
|
|
3
4
|
/** TUI-specific runtime accepted by `executeBuiltinSlashCommand`. */
|
|
@@ -5,6 +6,14 @@ export type BuiltinSlashCommandRuntime = TuiSlashCommandRuntime;
|
|
|
5
6
|
export declare const BUILTIN_SLASH_COMMAND_RESERVED_NAMES: ReadonlySet<string>;
|
|
6
7
|
/** Builtin command metadata used for slash-command autocomplete and help text. */
|
|
7
8
|
export declare const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand>;
|
|
9
|
+
/**
|
|
10
|
+
* Materialized builtin slash commands with completion functions derived from
|
|
11
|
+
* declarative subcommand/hint definitions.
|
|
12
|
+
*/
|
|
13
|
+
export declare const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand & {
|
|
14
|
+
getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null;
|
|
15
|
+
getInlineHint?: (argumentText: string) => string | null;
|
|
16
|
+
}>;
|
|
8
17
|
/**
|
|
9
18
|
* Unified registry exposed for cross-mode tooling. Each spec carries at least
|
|
10
19
|
* one of `handle` / `handleTui`. The TUI dispatcher prefers `handleTui`; the
|
|
@@ -54,6 +54,13 @@ export interface ExecutorOptions {
|
|
|
54
54
|
index: number;
|
|
55
55
|
id: string;
|
|
56
56
|
parentToolCallId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Spawn runs as a detached background job (parent turn not blocked on it).
|
|
59
|
+
* Rides the subagent lifecycle/progress payloads so HUD-style surfaces can
|
|
60
|
+
* skip spawns the transcript already renders inline. See
|
|
61
|
+
* {@link SubagentLifecyclePayload.detached}.
|
|
62
|
+
*/
|
|
63
|
+
detached?: boolean;
|
|
57
64
|
modelOverride?: string | string[];
|
|
58
65
|
/**
|
|
59
66
|
* Active model selector of the parent session, used as an auth-aware fallback
|
|
@@ -25,6 +25,8 @@ export interface SubagentProgressPayload {
|
|
|
25
25
|
assignment?: string;
|
|
26
26
|
progress: AgentProgress;
|
|
27
27
|
sessionFile?: string;
|
|
28
|
+
/** See {@link SubagentLifecyclePayload.detached}. */
|
|
29
|
+
detached?: boolean;
|
|
28
30
|
}
|
|
29
31
|
/** Payload emitted on TASK_SUBAGENT_EVENT_CHANNEL */
|
|
30
32
|
export interface SubagentEventPayload {
|
|
@@ -41,6 +43,13 @@ export interface SubagentLifecyclePayload {
|
|
|
41
43
|
sessionFile?: string;
|
|
42
44
|
parentToolCallId?: string;
|
|
43
45
|
index: number;
|
|
46
|
+
/**
|
|
47
|
+
* Spawn runs as a detached background job: the parent turn keeps working
|
|
48
|
+
* while this agent runs. Sync task spawns (parent blocked on the call) and
|
|
49
|
+
* eval `agent()` bridge spawns (rendered inside their eval cell) leave this
|
|
50
|
+
* unset — surfaces like the subagent HUD only list detached spawns.
|
|
51
|
+
*/
|
|
52
|
+
detached?: boolean;
|
|
44
53
|
}
|
|
45
54
|
export declare const taskItemSchema: z.ZodObject<{
|
|
46
55
|
id: z.ZodOptional<z.ZodString>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.12.0",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -40,23 +40,24 @@
|
|
|
40
40
|
"fmt": "biome format --write . && bun run format-prompts",
|
|
41
41
|
"format-prompts": "bun scripts/format-prompts.ts",
|
|
42
42
|
"generate-docs-index": "bun scripts/generate-docs-index.ts",
|
|
43
|
-
"prepack": "bun scripts/generate-docs-index.ts && bun scripts/bundle-dist.ts",
|
|
44
|
-
"
|
|
43
|
+
"prepack": "bun scripts/generate-docs-index.ts && bun --cwd=../collab-web run build:tool-views && bun scripts/bundle-dist.ts",
|
|
44
|
+
"bench:guard": "bun scripts/bench-guard.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.22.1",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.
|
|
54
|
-
"@oh-my-pi/pi-catalog": "15.
|
|
55
|
-
"@oh-my-pi/pi-mnemopi": "15.
|
|
56
|
-
"@oh-my-pi/pi-natives": "15.
|
|
57
|
-
"@oh-my-pi/pi-tui": "15.
|
|
58
|
-
"@oh-my-pi/pi-utils": "15.
|
|
59
|
-
"@oh-my-pi/
|
|
50
|
+
"@oh-my-pi/hashline": "15.12.0",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.12.0",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.12.0",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.12.0",
|
|
54
|
+
"@oh-my-pi/pi-catalog": "15.12.0",
|
|
55
|
+
"@oh-my-pi/pi-mnemopi": "15.12.0",
|
|
56
|
+
"@oh-my-pi/pi-natives": "15.12.0",
|
|
57
|
+
"@oh-my-pi/pi-tui": "15.12.0",
|
|
58
|
+
"@oh-my-pi/pi-utils": "15.12.0",
|
|
59
|
+
"@oh-my-pi/pi-wire": "15.12.0",
|
|
60
|
+
"@oh-my-pi/snapcompact": "15.12.0",
|
|
60
61
|
"@opentelemetry/api": "^1.9.1",
|
|
61
62
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
62
63
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Boot-time regression guard (Phase A1 of the boot/TUI perf work).
|
|
4
|
+
*
|
|
5
|
+
* Re-runs the `PI_TIMING=x` cold-boot benchmark under hyperfine and fails when
|
|
6
|
+
* the median regresses past `baseline * THRESHOLD`. `PI_TIMING=x` runs the full
|
|
7
|
+
* pre-paint chain in `runRootCommand` and then `process.exit(0)`, so the
|
|
8
|
+
* never-exiting interactive launch becomes a terminating, benchmarkable boot.
|
|
9
|
+
*
|
|
10
|
+
* Boot wall-clock is MACHINE-RELATIVE: a baseline captured on one machine is
|
|
11
|
+
* meaningless on another (and on CI). This is a LOCAL guard — regenerate the
|
|
12
|
+
* baseline on the machine you measure on, then compare on that same machine.
|
|
13
|
+
* It is intentionally NOT wired into CI for that reason.
|
|
14
|
+
*
|
|
15
|
+
* bun scripts/bench-guard.ts --update # capture/refresh the baseline
|
|
16
|
+
* bun scripts/bench-guard.ts # measure + compare; exit 1 on regression
|
|
17
|
+
*
|
|
18
|
+
* Requires `hyperfine` on PATH.
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from "node:fs";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
|
|
23
|
+
const THRESHOLD = 1.05; // 5% regression budget
|
|
24
|
+
const BASELINE_PATH = path.join(import.meta.dir, "..", "bench", "boot-baseline.json");
|
|
25
|
+
const BENCH_COMMAND = "PI_TIMING=x bun src/cli.ts";
|
|
26
|
+
const cwd = path.join(import.meta.dir, "..");
|
|
27
|
+
|
|
28
|
+
function medianOf(hyperfineJson: string): number {
|
|
29
|
+
const parsed = JSON.parse(hyperfineJson) as { results: Array<{ mean: number; median?: number }> };
|
|
30
|
+
const result = parsed.results[0];
|
|
31
|
+
if (!result) throw new Error("hyperfine produced no result");
|
|
32
|
+
return result.median ?? result.mean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function measure(): Promise<{ seconds: number; raw: string }> {
|
|
36
|
+
const tmp = path.join(import.meta.dir, "..", "bench", `.boot-run-${Date.now()}.json`);
|
|
37
|
+
const proc = Bun.spawn(["hyperfine", "--warmup", "3", "--min-runs", "10", "--export-json", tmp, BENCH_COMMAND], {
|
|
38
|
+
cwd,
|
|
39
|
+
stdout: "inherit",
|
|
40
|
+
stderr: "inherit",
|
|
41
|
+
});
|
|
42
|
+
const code = await proc.exited;
|
|
43
|
+
if (code !== 0) throw new Error(`hyperfine exited ${code}`);
|
|
44
|
+
const raw = await Bun.file(tmp).text();
|
|
45
|
+
fs.rmSync(tmp, { force: true });
|
|
46
|
+
return { seconds: medianOf(raw), raw };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const update = process.argv.includes("--update");
|
|
50
|
+
const { seconds, raw } = await measure();
|
|
51
|
+
|
|
52
|
+
if (update) {
|
|
53
|
+
fs.mkdirSync(path.dirname(BASELINE_PATH), { recursive: true });
|
|
54
|
+
await Bun.write(BASELINE_PATH, raw);
|
|
55
|
+
console.log(`Baseline updated: ${(seconds * 1000).toFixed(0)}ms median -> ${BASELINE_PATH}`);
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!fs.existsSync(BASELINE_PATH)) {
|
|
60
|
+
console.error("No baseline found. Run `bun scripts/bench-guard.ts --update` on this machine first.");
|
|
61
|
+
process.exit(2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const baseline = medianOf(await Bun.file(BASELINE_PATH).text());
|
|
65
|
+
const ratio = seconds / baseline;
|
|
66
|
+
const verdict = ratio > THRESHOLD ? "REGRESSION" : "ok";
|
|
67
|
+
console.log(
|
|
68
|
+
`boot median: ${(seconds * 1000).toFixed(0)}ms vs baseline ${(baseline * 1000).toFixed(0)}ms ` +
|
|
69
|
+
`(${((ratio - 1) * 100).toFixed(1)}%, budget ${((THRESHOLD - 1) * 100).toFixed(0)}%) -> ${verdict}`,
|
|
70
|
+
);
|
|
71
|
+
process.exit(ratio > THRESHOLD ? 1 : 0);
|
package/scripts/build-binary.ts
CHANGED
|
@@ -59,6 +59,10 @@ async function main(): Promise<void> {
|
|
|
59
59
|
`process.env.PI_TINY_TRANSFORMERS_VERSION=${JSON.stringify(transformersVersion)}`,
|
|
60
60
|
"--external",
|
|
61
61
|
"mupdf",
|
|
62
|
+
"--external",
|
|
63
|
+
"fastembed",
|
|
64
|
+
"--external",
|
|
65
|
+
"onnxruntime-node",
|
|
62
66
|
"--root",
|
|
63
67
|
".",
|
|
64
68
|
"./packages/coding-agent/src/cli.ts",
|
package/scripts/bundle-dist.ts
CHANGED
|
@@ -72,6 +72,10 @@ async function main(): Promise<void> {
|
|
|
72
72
|
"@oh-my-pi/pi-natives",
|
|
73
73
|
"--external",
|
|
74
74
|
"@huggingface/transformers",
|
|
75
|
+
"--external",
|
|
76
|
+
"fastembed",
|
|
77
|
+
"--external",
|
|
78
|
+
"onnxruntime-node",
|
|
75
79
|
"--define",
|
|
76
80
|
'process.env.PI_BUNDLED="true"',
|
|
77
81
|
"./src/cli.ts",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Build the standalone share-viewer page the omp relay serves at `GET /s/<id>`.
|
|
4
|
+
*
|
|
5
|
+
* Same template as HTML exports, but with no embedded session: share-loader.js
|
|
6
|
+
* (injected right after the empty #session-data tag) fetches the sealed blob
|
|
7
|
+
* (gist or relay store), decrypts it with the `#<key>` fragment in-browser, and
|
|
8
|
+
* hands the JSON to template.js via `window.__OMP_SESSION_DATA__`.
|
|
9
|
+
*
|
|
10
|
+
* The relay repo's build script runs this and embeds the output via go:embed.
|
|
11
|
+
*/
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import { generateThemeVars, getTemplate } from "../src/export/html";
|
|
14
|
+
|
|
15
|
+
const outPath = process.argv[2];
|
|
16
|
+
if (!outPath) {
|
|
17
|
+
console.error("usage: bun scripts/generate-share-viewer.ts <output.html>");
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const loaderJs = await Bun.file(new URL("../src/export/html/share-loader.js", import.meta.url).pathname).text();
|
|
22
|
+
// Pin a built-in theme: the viewer is a public artifact, not a per-user export.
|
|
23
|
+
const themeVars = await generateThemeVars("dark");
|
|
24
|
+
|
|
25
|
+
const html = getTemplate()
|
|
26
|
+
.replace("<theme-vars/>", () => `<style>:root { ${themeVars} }</style>`)
|
|
27
|
+
.replace("<title>Session Export</title>", () => "<title>omp session</title>")
|
|
28
|
+
.replace("{{SESSION_DATA}}</script>", () => `</script>\n <script>${loaderJs}</script>`);
|
|
29
|
+
|
|
30
|
+
if (html.includes("{{SESSION_DATA}}")) throw new Error("session-data placeholder survived substitution");
|
|
31
|
+
if (!html.includes("__OMP_SESSION_DATA__")) throw new Error("share loader not injected");
|
|
32
|
+
|
|
33
|
+
await Bun.write(outPath, html);
|
|
34
|
+
console.log(`Generated ${path.resolve(outPath)} (${(html.length / 1024).toFixed(0)} KB)`);
|
package/src/cli/args.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface Args {
|
|
|
32
32
|
sessionDir?: string;
|
|
33
33
|
providerSessionId?: string;
|
|
34
34
|
fork?: string;
|
|
35
|
+
/** Collab link to join at startup (set by the `join` subcommand; no CLI flag). */
|
|
36
|
+
join?: string;
|
|
35
37
|
models?: string[];
|
|
36
38
|
tools?: string[];
|
|
37
39
|
noTools?: boolean;
|
package/src/cli-commands.ts
CHANGED
|
@@ -26,6 +26,7 @@ export const commands: CommandEntry[] = [
|
|
|
26
26
|
{ name: "gallery", load: () => import("./commands/gallery").then(m => m.default) },
|
|
27
27
|
{ name: "grievances", load: () => import("./commands/grievances").then(m => m.default) },
|
|
28
28
|
{ name: "install", load: () => import("./commands/install").then(m => m.default) },
|
|
29
|
+
{ name: "join", load: () => import("./commands/join").then(m => m.default) },
|
|
29
30
|
{ name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
|
|
30
31
|
{ name: "setup", load: () => import("./commands/setup").then(m => m.default) },
|
|
31
32
|
{ name: "shell", load: () => import("./commands/shell").then(m => m.default) },
|