@oh-my-pi/pi-coding-agent 17.1.7 → 17.2.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 +127 -0
- package/dist/{CHANGELOG-5k4dq4g6.md → CHANGELOG-be1f2t8h.md} +127 -0
- package/dist/cli.js +9554 -12379
- package/dist/template-8vdv6xb4.js +1671 -0
- package/dist/template-f8wx9vfn.css +1355 -0
- package/dist/template-qat058wr.html +55 -0
- package/dist/tool-views.generated-jdfmzwmn.js +35 -0
- package/dist/types/advisor/advise-tool.d.ts +0 -7
- package/dist/types/advisor/runtime.d.ts +8 -11
- package/dist/types/advisor/transcript-recorder.d.ts +15 -0
- package/dist/types/async/job-manager.d.ts +11 -0
- package/dist/types/auto-thinking/classifier.d.ts +9 -2
- package/dist/types/cleanse/agent.d.ts +19 -0
- package/dist/types/cleanse/balance.d.ts +7 -0
- package/dist/types/cleanse/checkers.d.ts +13 -0
- package/dist/types/cleanse/index.d.ts +16 -0
- package/dist/types/cleanse/loop.d.ts +16 -0
- package/dist/types/cleanse/parsers.d.ts +13 -0
- package/dist/types/cleanse/progress.d.ts +14 -0
- package/dist/types/cleanse/types.d.ts +64 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/session-picker.d.ts +14 -7
- package/dist/types/collab/guest.d.ts +21 -2
- package/dist/types/commands/cleanse.d.ts +23 -0
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +63 -8
- package/dist/types/cursor-bridge-tools.d.ts +54 -0
- package/dist/types/cursor.d.ts +193 -8
- package/dist/types/edit/edit-clipboard.d.ts +19 -0
- package/dist/types/edit/hashline/diff.d.ts +9 -1
- package/dist/types/edit/index.d.ts +8 -1
- package/dist/types/edit/streaming.d.ts +8 -1
- package/dist/types/export/html/index.d.ts +2 -0
- package/dist/types/extensibility/extensions/runner.d.ts +19 -2
- package/dist/types/extensibility/extensions/types.d.ts +25 -1
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +27 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +2 -0
- package/dist/types/internal-urls/local-protocol.d.ts +3 -2
- package/dist/types/launch/terminal-output-worker-client.d.ts +5 -0
- package/dist/types/launch/terminal-output-worker-protocol.d.ts +14 -0
- package/dist/types/launch/terminal-output-worker.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +33 -4
- package/dist/types/modes/components/codex-reset-fireworks.d.ts +51 -0
- package/dist/types/modes/components/read-tool-group.d.ts +12 -0
- package/dist/types/modes/components/session-selector.d.ts +6 -0
- package/dist/types/modes/components/status-line/component.d.ts +23 -0
- package/dist/types/modes/components/tool-execution.d.ts +16 -7
- package/dist/types/modes/components/usage-row.d.ts +2 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +26 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +8 -5
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +16 -0
- package/dist/types/modes/theme/theme.d.ts +12 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/sdk.d.ts +1 -0
- package/dist/types/session/agent-session-events.d.ts +2 -0
- package/dist/types/session/agent-session-types.d.ts +36 -1
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/async-job-delivery.d.ts +8 -0
- package/dist/types/session/claude-session-store.d.ts +13 -0
- package/dist/types/session/codex-session-store.d.ts +14 -0
- package/dist/types/session/credential-pin.d.ts +58 -0
- package/dist/types/session/foreign-session-import.d.ts +15 -0
- package/dist/types/session/foreign-session-jsonl.d.ts +9 -0
- package/dist/types/session/foreign-session-store.d.ts +23 -0
- package/dist/types/session/model-controls.d.ts +3 -3
- package/dist/types/session/session-advisors.d.ts +47 -2
- package/dist/types/session/session-entries.d.ts +19 -1
- package/dist/types/session/session-handoff.d.ts +4 -1
- package/dist/types/session/session-loader.d.ts +2 -0
- package/dist/types/session/session-maintenance.d.ts +1 -1
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-tools.d.ts +25 -1
- package/dist/types/session/turn-recovery.d.ts +10 -4
- package/dist/types/system-prompt.d.ts +2 -1
- package/dist/types/task/executor.d.ts +11 -2
- package/dist/types/task/isolation-ownership.d.ts +34 -0
- package/dist/types/task/render.d.ts +7 -0
- package/dist/types/thinking.d.ts +18 -6
- package/dist/types/tools/acp-bridge.d.ts +28 -5
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +1 -2
- package/dist/types/tools/browser/tab-worker.d.ts +1 -4
- package/dist/types/tools/grep.d.ts +17 -1
- package/dist/types/tools/hub/launch.d.ts +5 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/path-utils.d.ts +19 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +2 -1
- package/dist/types/tui/output-block.d.ts +5 -5
- package/dist/types/utils/changelog.d.ts +19 -0
- package/dist/types/utils/git.d.ts +10 -0
- package/dist/types/utils/jj.d.ts +11 -4
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +16 -12
- package/scripts/bundle-dist.ts +3 -1
- package/src/advisor/advise-tool.ts +0 -11
- package/src/advisor/runtime.ts +138 -24
- package/src/advisor/transcript-recorder.ts +56 -0
- package/src/async/job-manager.ts +30 -7
- package/src/auto-thinking/classifier.ts +48 -10
- package/src/cleanse/agent.ts +226 -0
- package/src/cleanse/balance.ts +79 -0
- package/src/cleanse/checkers.ts +996 -0
- package/src/cleanse/index.ts +190 -0
- package/src/cleanse/loop.ts +51 -0
- package/src/cleanse/parsers.ts +726 -0
- package/src/cleanse/progress.ts +50 -0
- package/src/cleanse/prompts/assignment.md +47 -0
- package/src/cleanse/types.ts +72 -0
- package/src/cli/args.ts +6 -0
- package/src/cli/flag-tables.ts +2 -0
- package/src/cli/gallery-fixtures/fs.ts +25 -0
- package/src/cli/session-picker.ts +34 -17
- package/src/cli/ttsr-cli.ts +19 -1
- package/src/cli/update-cli.ts +3 -0
- package/src/cli/usage-cli.ts +29 -4
- package/src/cli/worktree-cli.ts +28 -11
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +11 -1
- package/src/collab/guest.ts +37 -4
- package/src/collab/host.ts +1 -0
- package/src/commands/cleanse.ts +45 -0
- package/src/commands/launch.ts +6 -0
- package/src/config/model-registry.ts +103 -25
- package/src/config/settings-schema.ts +65 -6
- package/src/config/settings.ts +25 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +463 -12
- package/src/discovery/claude-plugins.ts +144 -34
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/hashline/diff.ts +49 -10
- package/src/edit/hashline/execute.ts +38 -6
- package/src/edit/hashline/filesystem.ts +27 -3
- package/src/edit/index.ts +12 -2
- package/src/edit/renderer.ts +4 -4
- package/src/edit/streaming.ts +17 -2
- package/src/export/html/index.ts +17 -10
- package/src/export/html/template.js +1 -1
- package/src/extensibility/extensions/runner.ts +70 -0
- package/src/extensibility/extensions/types.ts +30 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +46 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +12 -13
- package/src/internal-urls/local-protocol.ts +3 -2
- package/src/internal-urls/mcp-protocol.ts +4 -1
- package/src/launch/broker.ts +14 -4
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/lsp/client.ts +18 -3
- package/src/lsp/index.ts +4 -0
- package/src/lsp/render.ts +1 -1
- package/src/main.ts +127 -56
- package/src/mcp/manager.ts +120 -18
- package/src/modes/acp/acp-agent.ts +27 -18
- package/src/modes/acp/acp-event-mapper.ts +38 -4
- package/src/modes/components/chat-transcript-builder.ts +30 -11
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/read-tool-group.ts +159 -17
- package/src/modes/components/session-selector.ts +19 -3
- package/src/modes/components/settings-selector.ts +9 -1
- package/src/modes/components/status-line/component.ts +381 -54
- package/src/modes/components/tool-execution.ts +78 -31
- package/src/modes/components/usage-row.ts +10 -5
- package/src/modes/controllers/event-controller.ts +26 -4
- package/src/modes/controllers/mcp-command-controller.ts +68 -3
- package/src/modes/controllers/selector-controller.ts +94 -36
- package/src/modes/controllers/tan-command-controller.ts +14 -0
- package/src/modes/interactive-mode.ts +67 -64
- package/src/modes/prompt-action-autocomplete.ts +5 -3
- package/src/modes/rpc/rpc-client.ts +18 -0
- package/src/modes/rpc/rpc-mode.ts +16 -1
- package/src/modes/rpc/rpc-types.ts +11 -0
- package/src/modes/theme/theme.ts +151 -6
- package/src/modes/types.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +38 -21
- package/src/prompts/goals/guided-goal-interview.md +41 -6
- package/src/prompts/system/auto-thinking-difficulty.md +4 -2
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/system/vibe-mode-active.md +4 -1
- package/src/prompts/system/xdev-mount-notice.md +1 -1
- package/src/prompts/tools/bash.md +15 -17
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/glob.md +7 -6
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/grep.md +7 -6
- package/src/sdk.ts +178 -15
- package/src/session/agent-session-events.ts +1 -0
- package/src/session/agent-session-types.ts +40 -1
- package/src/session/agent-session.ts +330 -179
- package/src/session/async-job-delivery.ts +8 -0
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/model-controls.ts +15 -7
- package/src/session/session-advisors.ts +142 -23
- package/src/session/session-entries.ts +21 -1
- package/src/session/session-handoff.ts +20 -3
- package/src/session/session-history-format.ts +31 -6
- package/src/session/session-loader.ts +25 -9
- package/src/session/session-maintenance.ts +59 -11
- package/src/session/session-manager.ts +61 -0
- package/src/session/session-tools.ts +107 -5
- package/src/session/settings-stream-fn.ts +1 -0
- package/src/session/turn-recovery.ts +97 -51
- package/src/slash-commands/builtin-registry.ts +136 -5
- package/src/system-prompt.ts +3 -2
- package/src/task/executor.ts +17 -4
- package/src/task/isolation-ownership.ts +106 -0
- package/src/task/render.ts +20 -4
- package/src/task/worktree.ts +8 -0
- package/src/thinking.ts +29 -10
- package/src/tools/acp-bridge.ts +52 -8
- package/src/tools/ask.ts +3 -3
- package/src/tools/ast-edit.ts +9 -2
- package/src/tools/browser/cmux/cmux-tab.ts +9 -14
- package/src/tools/browser/tab-worker.ts +12 -35
- package/src/tools/browser.ts +7 -2
- package/src/tools/checkpoint.ts +0 -13
- package/src/tools/gh-renderer.ts +3 -3
- package/src/tools/grep.ts +64 -8
- package/src/tools/hub/launch.ts +10 -15
- package/src/tools/index.ts +38 -5
- package/src/tools/path-utils.ts +88 -0
- package/src/tools/read.ts +3 -0
- package/src/tools/write.ts +44 -3
- package/src/tools/xdev.ts +39 -14
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/output-block.ts +25 -8
- package/src/utils/changelog.ts +144 -4
- package/src/utils/git.ts +61 -14
- package/src/utils/jj.ts +19 -13
- package/src/utils/shell-snapshot-fn-env.sh +5 -2
- package/src/web/search/providers/codex.ts +34 -0
- package/src/web/search/render.ts +2 -2
- package/src/web/search/types.ts +1 -1
- package/dist/types/goals/guided-setup.d.ts +0 -30
- package/src/goals/guided-setup.ts +0 -171
- package/src/prompts/goals/guided-goal-system.md +0 -33
|
@@ -24,6 +24,8 @@ export interface ReadToolDetails {
|
|
|
24
24
|
method?: string;
|
|
25
25
|
notes?: string[];
|
|
26
26
|
meta?: OutputMeta;
|
|
27
|
+
/** Full on-disk byte size recorded before applying a file range. */
|
|
28
|
+
fileSize?: number;
|
|
27
29
|
/** Raw text + start line for user-visible TUI rendering, set when content is text-like.
|
|
28
30
|
* Mirrors the same lines the model receives but without hashline/line-number prefixes,
|
|
29
31
|
* so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
|
|
@@ -103,10 +103,11 @@ export declare function resolveMountedXdevTool(state: XdevState, name: string):
|
|
|
103
103
|
export declare function resolveMountedXdevExecutable(state: XdevState, name: string): Tool | undefined;
|
|
104
104
|
/** Mounted tools in presentation order, resolved from the canonical map. */
|
|
105
105
|
export declare function listXdevTools(state: XdevState): Tool[];
|
|
106
|
-
/** `{name, summary}`
|
|
106
|
+
/** `{name, summary, dynamic}` triples for prompt templates and `/tools` display. */
|
|
107
107
|
export declare function xdevEntries(state: XdevState): Array<{
|
|
108
108
|
name: string;
|
|
109
109
|
summary: string;
|
|
110
|
+
dynamic: boolean;
|
|
110
111
|
}>;
|
|
111
112
|
/** `read xd://` listing with one device per line. */
|
|
112
113
|
export declare function xdevListing(state: XdevState): string;
|
|
@@ -16,6 +16,7 @@ export interface OutputBlockOptions {
|
|
|
16
16
|
width: number;
|
|
17
17
|
applyBg?: boolean;
|
|
18
18
|
contentPaddingLeft?: number;
|
|
19
|
+
contentPaddingRight?: number;
|
|
19
20
|
/** Override the state-derived border color. Used for muted "legacy" tool
|
|
20
21
|
* frames that should not visually compete with framed-output tools. */
|
|
21
22
|
borderColor?: ThemeColor;
|
|
@@ -28,12 +29,11 @@ export declare function markFramedBlockComponent<T extends Component>(component:
|
|
|
28
29
|
export declare function isFramedBlockComponent(component: Component): boolean;
|
|
29
30
|
/**
|
|
30
31
|
* Inner content width that {@link renderOutputBlock} wraps its body to, for a
|
|
31
|
-
* given outer `width`: both vertical borders
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* into more rows than they counted and the box overflows its intended height.
|
|
32
|
+
* given outer `width`: both vertical borders plus symmetric content padding.
|
|
33
|
+
* An explicit left padding of zero keeps legacy flush blocks flush on both
|
|
34
|
+
* sides unless a right padding is provided separately.
|
|
35
35
|
*/
|
|
36
|
-
export declare function outputBlockContentWidth(width: number, contentPaddingLeft?: number): number;
|
|
36
|
+
export declare function outputBlockContentWidth(width: number, contentPaddingLeft?: number, contentPaddingRight?: number): number;
|
|
37
37
|
export declare function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[];
|
|
38
38
|
/**
|
|
39
39
|
* Cached wrapper around `renderOutputBlock`.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SettingValue } from "../config/settings.js";
|
|
1
2
|
export interface ChangelogEntry {
|
|
2
3
|
major: number;
|
|
3
4
|
minor: number;
|
|
@@ -21,7 +22,13 @@ export interface StartupChangelogSelection {
|
|
|
21
22
|
persistCurrentVersion: boolean;
|
|
22
23
|
truncated: boolean;
|
|
23
24
|
selectedEntries: number;
|
|
25
|
+
totalUnseenEntries: number;
|
|
26
|
+
latestVersion: string | undefined;
|
|
27
|
+
changeCount: number;
|
|
28
|
+
categoryCounts: Record<string, number>;
|
|
24
29
|
}
|
|
30
|
+
/** Format the compact, deterministic startup update notice. */
|
|
31
|
+
export declare function formatStartupChangelogSummary(selection: StartupChangelogSelection): string;
|
|
25
32
|
/**
|
|
26
33
|
* Parse changelog entries from omp's package asset when available, falling back
|
|
27
34
|
* to the copy embedded in compiled binaries.
|
|
@@ -55,6 +62,18 @@ export declare function renderChangelogEntries(entries: ChangelogEntry[], option
|
|
|
55
62
|
* Select bounded release notes for interactive startup.
|
|
56
63
|
*/
|
|
57
64
|
export declare function selectStartupChangelog(entries: ChangelogEntry[], lastVersion: string | undefined, currentVersion: string): StartupChangelogSelection;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve and persist the automatic startup changelog decision.
|
|
67
|
+
*
|
|
68
|
+
* Hidden mode advances the marker only for an upgrade, so downgrades do not
|
|
69
|
+
* erase knowledge of a newer version the user has already seen.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveStartupChangelogForDisplay(options: {
|
|
72
|
+
mode: SettingValue<"startup.changelogMode">;
|
|
73
|
+
currentVersion: string;
|
|
74
|
+
changelogPath?: string;
|
|
75
|
+
agentDir?: string;
|
|
76
|
+
}): Promise<StartupChangelogSelection | undefined>;
|
|
58
77
|
export { getChangelogPath } from "../config.js";
|
|
59
78
|
/**
|
|
60
79
|
* Last omp version whose changelog the user has seen. Stored as a plain-text
|
|
@@ -147,6 +147,14 @@ export declare const GIT_COMMAND_TIMEOUT_MS: number;
|
|
|
147
147
|
export declare const GIT_NETWORK_TIMEOUT_MS: number;
|
|
148
148
|
/** Maximum captured stdout or stderr bytes retained from git and gh subprocesses. */
|
|
149
149
|
export declare const GIT_COMMAND_OUTPUT_LIMIT_BYTES: number;
|
|
150
|
+
/**
|
|
151
|
+
* Deadline for synchronous git plumbing commands launched via
|
|
152
|
+
* {@link gitSpawnSyncText}. These run on the render path (e.g. reftable HEAD
|
|
153
|
+
* resolution), so the deadline is short: a command that has not exited by then
|
|
154
|
+
* is killed and reported as {@link GIT_COMMAND_TIMEOUT_EXIT_CODE} so the caller
|
|
155
|
+
* degrades instead of freezing the UI indefinitely.
|
|
156
|
+
*/
|
|
157
|
+
export declare const GIT_SPAWN_SYNC_TIMEOUT_MS = 5000;
|
|
150
158
|
interface CommandOptions {
|
|
151
159
|
readonly env?: Record<string, string | undefined>;
|
|
152
160
|
readonly maxOutputBytes?: number;
|
|
@@ -271,6 +279,8 @@ export declare const log: {
|
|
|
271
279
|
export declare const revList: {
|
|
272
280
|
/** Commits in `base..head`, oldest first. */
|
|
273
281
|
range(cwd: string, base: string, head: string, signal?: AbortSignal): Promise<string[]>;
|
|
282
|
+
/** Commits reachable from `ref` that touched `file`, newest first, capped at `limit`. */
|
|
283
|
+
touching(cwd: string, ref: string, file: string, limit: number, signal?: AbortSignal): Promise<string[]>;
|
|
274
284
|
};
|
|
275
285
|
export declare const branch: {
|
|
276
286
|
/** Current branch name, or null if detached/unavailable. */
|
package/dist/types/utils/jj.d.ts
CHANGED
|
@@ -16,14 +16,21 @@ export interface JjRepository {
|
|
|
16
16
|
storeDir: string;
|
|
17
17
|
}
|
|
18
18
|
/** Options for `jj diff` invocations. */
|
|
19
|
-
export interface DiffOptions {
|
|
19
|
+
export interface DiffOptions extends JjCommandOptions {
|
|
20
20
|
/** Optional file paths to restrict the diff with `-- <files>`. */
|
|
21
21
|
readonly files?: readonly string[];
|
|
22
22
|
/** Return only changed file names instead of Git-format diff text. */
|
|
23
23
|
readonly nameOnly?: boolean;
|
|
24
|
-
|
|
24
|
+
}
|
|
25
|
+
/** Options for a bounded `jj` subprocess query. */
|
|
26
|
+
export interface JjCommandOptions {
|
|
27
|
+
/** Optional cancellation signal for the subprocess. */
|
|
25
28
|
readonly signal?: AbortSignal;
|
|
29
|
+
/** Deadline in milliseconds. Defaults to {@link JJ_COMMAND_TIMEOUT_MS}. */
|
|
30
|
+
readonly timeoutMs?: number;
|
|
26
31
|
}
|
|
32
|
+
/** Default finite deadline for local jj subprocesses. */
|
|
33
|
+
export declare const JJ_COMMAND_TIMEOUT_MS = 5000;
|
|
27
34
|
/** Error thrown when a checked `jj` command exits non-zero. */
|
|
28
35
|
export declare class JjCommandError extends Error {
|
|
29
36
|
/** Arguments passed after the common `jj --no-pager --color=never` prefix. */
|
|
@@ -46,7 +53,7 @@ export declare const workingCopy: {
|
|
|
46
53
|
* Label `@` with its nearest bookmark, falling back to its short change ID.
|
|
47
54
|
* Returns `null` when `jj` is unavailable or the query fails.
|
|
48
55
|
*/
|
|
49
|
-
label(cwd: string,
|
|
56
|
+
label(cwd: string, options?: JjCommandOptions): Promise<string | null>;
|
|
50
57
|
/** Parse working-copy label query output. */
|
|
51
58
|
parseLabel: typeof parseWorkingCopyLabel;
|
|
52
59
|
};
|
|
@@ -56,7 +63,7 @@ export declare const status: {
|
|
|
56
63
|
* Count changes in `@` relative to its parent using the Git status shape.
|
|
57
64
|
* Jujutsu has no index, so `staged` is always zero.
|
|
58
65
|
*/
|
|
59
|
-
summary(cwd: string,
|
|
66
|
+
summary(cwd: string, options?: JjCommandOptions): Promise<git.GitStatusSummary | null>;
|
|
60
67
|
/** Parse `jj diff --summary` output into status counts. */
|
|
61
68
|
parse: typeof parseStatusSummary;
|
|
62
69
|
};
|
|
@@ -34,7 +34,7 @@ export declare const SEARCH_PROVIDER_OPTIONS: readonly [{
|
|
|
34
34
|
}, {
|
|
35
35
|
readonly value: "exa";
|
|
36
36
|
readonly label: "Exa";
|
|
37
|
-
readonly description: "
|
|
37
|
+
readonly description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP";
|
|
38
38
|
}, {
|
|
39
39
|
readonly value: "tinyfish";
|
|
40
40
|
readonly label: "TinyFish";
|
|
@@ -136,7 +136,7 @@ export declare const SEARCH_PROVIDER_CHOICES: ({
|
|
|
136
136
|
} | {
|
|
137
137
|
readonly value: "exa";
|
|
138
138
|
readonly label: "Exa";
|
|
139
|
-
readonly description: "
|
|
139
|
+
readonly description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP";
|
|
140
140
|
} | {
|
|
141
141
|
readonly value: "tinyfish";
|
|
142
142
|
readonly label: "TinyFish";
|
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": "17.
|
|
4
|
+
"version": "17.2.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",
|
|
@@ -52,17 +52,17 @@
|
|
|
52
52
|
"@agentclientprotocol/sdk": "1.2.1",
|
|
53
53
|
"@babel/parser": "^7.29.7",
|
|
54
54
|
"@mozilla/readability": "^0.6.0",
|
|
55
|
-
"@oh-my-pi/hashline": "17.
|
|
56
|
-
"@oh-my-pi/omp-stats": "17.
|
|
57
|
-
"@oh-my-pi/pi-agent-core": "17.
|
|
58
|
-
"@oh-my-pi/pi-ai": "17.
|
|
59
|
-
"@oh-my-pi/pi-catalog": "17.
|
|
60
|
-
"@oh-my-pi/pi-mnemopi": "17.
|
|
61
|
-
"@oh-my-pi/pi-natives": "17.
|
|
62
|
-
"@oh-my-pi/pi-tui": "17.
|
|
63
|
-
"@oh-my-pi/pi-utils": "17.
|
|
64
|
-
"@oh-my-pi/pi-wire": "17.
|
|
65
|
-
"@oh-my-pi/snapcompact": "17.
|
|
55
|
+
"@oh-my-pi/hashline": "17.2.0",
|
|
56
|
+
"@oh-my-pi/omp-stats": "17.2.0",
|
|
57
|
+
"@oh-my-pi/pi-agent-core": "17.2.0",
|
|
58
|
+
"@oh-my-pi/pi-ai": "17.2.0",
|
|
59
|
+
"@oh-my-pi/pi-catalog": "17.2.0",
|
|
60
|
+
"@oh-my-pi/pi-mnemopi": "17.2.0",
|
|
61
|
+
"@oh-my-pi/pi-natives": "17.2.0",
|
|
62
|
+
"@oh-my-pi/pi-tui": "17.2.0",
|
|
63
|
+
"@oh-my-pi/pi-utils": "17.2.0",
|
|
64
|
+
"@oh-my-pi/pi-wire": "17.2.0",
|
|
65
|
+
"@oh-my-pi/snapcompact": "17.2.0",
|
|
66
66
|
"@opentelemetry/api": "^1.9.1",
|
|
67
67
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
68
68
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
@@ -107,6 +107,10 @@
|
|
|
107
107
|
"dist/cli.js",
|
|
108
108
|
"dist/CHANGELOG-*.md",
|
|
109
109
|
"dist/*.node",
|
|
110
|
+
"dist/template-*.css",
|
|
111
|
+
"dist/template-*.html",
|
|
112
|
+
"dist/template-*.js",
|
|
113
|
+
"dist/tool-views.generated-*.js",
|
|
110
114
|
"scripts",
|
|
111
115
|
"examples",
|
|
112
116
|
"README.md",
|
package/scripts/bundle-dist.ts
CHANGED
|
@@ -9,6 +9,7 @@ const packageDir = path.join(import.meta.dir, "..");
|
|
|
9
9
|
const outDir = path.join(packageDir, "dist");
|
|
10
10
|
const cliPath = path.join(outDir, "cli.js");
|
|
11
11
|
const shebang = "#!/usr/bin/env bun\n";
|
|
12
|
+
const legacyHtmlExportAssetPattern = /^(?:template-[^.]+\.(?:css|html|js)|tool-views\.generated-[^.]+\.js)$/;
|
|
12
13
|
|
|
13
14
|
// Native / optional / platform-specific deps are loaded from installed files.
|
|
14
15
|
// `omp-legacy-pi-modules` exists only in compiled binaries via the build plugin;
|
|
@@ -80,7 +81,8 @@ async function cleanBundleOutputs(): Promise<void> {
|
|
|
80
81
|
entry === "cli.js" ||
|
|
81
82
|
entry.endsWith(".node") ||
|
|
82
83
|
entry.endsWith(".js.map") ||
|
|
83
|
-
(entry.startsWith("CHANGELOG-") && entry.endsWith(".md"))
|
|
84
|
+
(entry.startsWith("CHANGELOG-") && entry.endsWith(".md")) ||
|
|
85
|
+
legacyHtmlExportAssetPattern.test(entry),
|
|
84
86
|
)
|
|
85
87
|
.map(entry => fs.rm(path.join(outDir, entry), { force: true })),
|
|
86
88
|
);
|
|
@@ -75,17 +75,6 @@ export function isInterruptingSeverity(severity: AdvisorSeverity | undefined): b
|
|
|
75
75
|
return severity === "concern" || severity === "blocker";
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/**
|
|
79
|
-
* Append a staleness caveat to an advisor note when newer primary turns arrived
|
|
80
|
-
* after the reviewed transcript window (i.e. `hasFreshBacklog` is true on the
|
|
81
|
-
* advisor runtime at delivery time). Pure function — no session coupling — so it
|
|
82
|
-
* can be unit-tested in isolation and called from `AgentSession#routeAdvice`.
|
|
83
|
-
*/
|
|
84
|
-
export function annotateForStaleness(note: string, hasFreshBacklog: boolean): string {
|
|
85
|
-
if (!hasFreshBacklog) return note;
|
|
86
|
-
return `${note}\n\n_(Note: newer primary turns arrived after this reviewed window — verify this still applies.)_`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
78
|
/** How an advisor note is routed to the primary. */
|
|
90
79
|
export type AdvisorDeliveryChannel = "aside" | "steer" | "preserve";
|
|
91
80
|
/** Half-open turn-count fence for the post-interrupt cooldown. */
|
package/src/advisor/runtime.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import { estimateTokens } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
3
|
import type { AssistantMessage, ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import * as AIError from "@oh-my-pi/pi-ai/error";
|
|
5
|
+
import { raceWithSignal } from "@oh-my-pi/pi-ai/utils/abort";
|
|
5
6
|
import { type CursorExecResolvedCarrier, kCursorExecResolved } from "@oh-my-pi/pi-ai/utils/block-symbols";
|
|
6
7
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
8
|
import { obfuscateToolArguments, type SecretObfuscator } from "../secrets/obfuscator";
|
|
@@ -47,7 +48,7 @@ export interface AdvisorRuntimeHost {
|
|
|
47
48
|
* recovery path must never replay the full primary transcript.
|
|
48
49
|
* Optional: hosts that omit it get no proactive maintenance.
|
|
49
50
|
*/
|
|
50
|
-
maintainContext?(incomingTokens: number): Promise<boolean>;
|
|
51
|
+
maintainContext?(incomingTokens: number, signal: AbortSignal): Promise<boolean>;
|
|
51
52
|
/**
|
|
52
53
|
* Called immediately before each `agent.prompt(batch)` cycle. Lets the host
|
|
53
54
|
* clear per-update advisor state — currently the one-advise-per-update gate
|
|
@@ -67,6 +68,7 @@ export interface AdvisorRuntimeHost {
|
|
|
67
68
|
onTurnError?(
|
|
68
69
|
error: unknown,
|
|
69
70
|
failedMessages: readonly AgentMessage[],
|
|
71
|
+
signal: AbortSignal,
|
|
70
72
|
): Promise<boolean | undefined> | boolean | undefined;
|
|
71
73
|
/** Called after a successful advisor turn so the host can finish fallback lifecycle reporting. */
|
|
72
74
|
onTurnSuccess?(): Promise<void> | void;
|
|
@@ -76,6 +78,8 @@ export interface AdvisorRuntimeHost {
|
|
|
76
78
|
* recovery (credential switch, fallback chain) declined. Cleared only by
|
|
77
79
|
* an explicit reset (`/new`, config rebuild, session restart). */
|
|
78
80
|
notifyQuotaExhausted?(): void;
|
|
81
|
+
/** Stable identity for the live advisor model. Used to restore full transcript rendering after a model switch. */
|
|
82
|
+
getModelIdentity?(): string;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/**
|
|
@@ -228,7 +232,6 @@ const MAX_COALESCE_ROUNDS = 3;
|
|
|
228
232
|
const MAX_QUARANTINE_RETRIES = 2;
|
|
229
233
|
|
|
230
234
|
const ADVISOR_RENDER_OPTIONS = {
|
|
231
|
-
includeThinking: true,
|
|
232
235
|
includeToolIntent: true,
|
|
233
236
|
watchedRoles: true,
|
|
234
237
|
expandPrimaryContext: true,
|
|
@@ -285,11 +288,17 @@ export class AdvisorRuntime {
|
|
|
285
288
|
#advisorRegexSecretValues = new Set<string>();
|
|
286
289
|
#pending: PendingDelta[] = [];
|
|
287
290
|
#busy = false;
|
|
291
|
+
#sessionTransitionPaused = false;
|
|
292
|
+
#promptInFlight: Promise<void> | undefined;
|
|
293
|
+
#iterationAbort: AbortController | undefined;
|
|
288
294
|
#backlog = 0;
|
|
289
295
|
#consecutiveFailures = 0;
|
|
290
296
|
#failureNotified = false;
|
|
291
297
|
/** Consecutive quarantined turns since the last success/reset (issue #6661). */
|
|
292
298
|
#consecutiveQuarantines = 0;
|
|
299
|
+
/** Whether primary reasoning is included in advisor deltas for the current model. */
|
|
300
|
+
#includeThinking = true;
|
|
301
|
+
#modelIdentity: string | undefined;
|
|
293
302
|
/** Completed 3-failure backlog-drop cycles since the last success/reset. */
|
|
294
303
|
#droppedBacklogs = 0;
|
|
295
304
|
/**
|
|
@@ -340,18 +349,6 @@ export class AdvisorRuntime {
|
|
|
340
349
|
return this.#halted;
|
|
341
350
|
}
|
|
342
351
|
|
|
343
|
-
/**
|
|
344
|
-
* True when `#pending` is non-empty while the drain loop is busy — i.e., newer
|
|
345
|
-
* primary turns arrived after the current batch's transcript window was fixed
|
|
346
|
-
* but before the advisor model finished processing it. The delivery path uses
|
|
347
|
-
* this to annotate advice that was generated without seeing those newer turns.
|
|
348
|
-
* Can be true during `agent.prompt()`, a `maintainContext` await, or a retry
|
|
349
|
-
* sleep — any time `#drain` is busy and a concurrent `onTurnEnd` pushed.
|
|
350
|
-
*/
|
|
351
|
-
get hasFreshBacklog(): boolean {
|
|
352
|
-
return this.#pending.length > 0;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
352
|
/**
|
|
356
353
|
* Called after each primary turn ends. Renders the incremental delta and
|
|
357
354
|
* queues it for the advisor model.
|
|
@@ -440,6 +437,7 @@ export class AdvisorRuntime {
|
|
|
440
437
|
}
|
|
441
438
|
|
|
442
439
|
dispose(): void {
|
|
440
|
+
this.#iterationAbort?.abort("advisor disposed");
|
|
443
441
|
this.disposed = true;
|
|
444
442
|
this.#epoch++;
|
|
445
443
|
this.#pending = [];
|
|
@@ -502,6 +500,31 @@ export class AdvisorRuntime {
|
|
|
502
500
|
});
|
|
503
501
|
}
|
|
504
502
|
|
|
503
|
+
/** Stop new advisor work and wait only for the active prompt's recorder-visible events. */
|
|
504
|
+
pauseForSessionTransition(): Promise<void> {
|
|
505
|
+
if (!this.#sessionTransitionPaused) {
|
|
506
|
+
this.#sessionTransitionPaused = true;
|
|
507
|
+
this.#wakeAllWaiters();
|
|
508
|
+
this.#iterationAbort?.abort("advisor session transition");
|
|
509
|
+
try {
|
|
510
|
+
this.agent.abort("advisor session transition");
|
|
511
|
+
} catch {}
|
|
512
|
+
}
|
|
513
|
+
return (
|
|
514
|
+
this.#promptInFlight?.then(
|
|
515
|
+
() => {},
|
|
516
|
+
() => {},
|
|
517
|
+
) ?? Promise.resolve()
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** Continue queued work after a session transition rolls back or preserves the conversation. */
|
|
522
|
+
resumeAfterSessionTransition(): void {
|
|
523
|
+
if (!this.#sessionTransitionPaused) return;
|
|
524
|
+
this.#sessionTransitionPaused = false;
|
|
525
|
+
if (!this.#quotaExhausted && !this.#halted) void this.#drain();
|
|
526
|
+
}
|
|
527
|
+
|
|
505
528
|
/**
|
|
506
529
|
* Re-prime the advisor after a history rewrite (compaction, session
|
|
507
530
|
* switch/resume, branch). Clears the advisor's own (non-persisted) context
|
|
@@ -510,7 +533,9 @@ export class AdvisorRuntime {
|
|
|
510
533
|
* leaving it blind to everything before the rewrite.
|
|
511
534
|
*/
|
|
512
535
|
reset(): void {
|
|
536
|
+
this.#iterationAbort?.abort("advisor reset");
|
|
513
537
|
this.#epoch++;
|
|
538
|
+
this.#sessionTransitionPaused = false;
|
|
514
539
|
this.#quotaExhausted = false;
|
|
515
540
|
this.#halted = false;
|
|
516
541
|
this.#failing = false;
|
|
@@ -542,13 +567,23 @@ export class AdvisorRuntime {
|
|
|
542
567
|
this.#wakeAllWaiters();
|
|
543
568
|
}
|
|
544
569
|
|
|
570
|
+
#syncModelIdentity(): void {
|
|
571
|
+
const identity = this.host.getModelIdentity?.();
|
|
572
|
+
if (identity === undefined || identity === this.#modelIdentity) return;
|
|
573
|
+
this.#modelIdentity = identity;
|
|
574
|
+
this.#includeThinking = true;
|
|
575
|
+
}
|
|
576
|
+
|
|
545
577
|
#formatRawDelta(rawMessages: AgentMessage[], wip = false): string | null {
|
|
546
578
|
const delta = rawMessages
|
|
547
579
|
.filter(message => !(message.role === "custom" && message.customType === "advisor"))
|
|
548
580
|
.map(message => this.#dedupContextMessage(message));
|
|
549
581
|
if (delta.length === 0) return null;
|
|
550
582
|
const obfuscator = this.host.obfuscator;
|
|
551
|
-
let md = formatSessionHistoryMarkdown(delta,
|
|
583
|
+
let md = formatSessionHistoryMarkdown(delta, {
|
|
584
|
+
...ADVISOR_RENDER_OPTIONS,
|
|
585
|
+
includeThinking: this.#includeThinking,
|
|
586
|
+
});
|
|
552
587
|
if (!md.trim()) return null;
|
|
553
588
|
if (obfuscator?.hasSecrets()) {
|
|
554
589
|
let discoveredNewRegexSecretValue = false;
|
|
@@ -582,7 +617,7 @@ export class AdvisorRuntime {
|
|
|
582
617
|
? obfuscateAdvisorMessage(obfuscator, message, this.#advisorRegexSecretValues)
|
|
583
618
|
: message,
|
|
584
619
|
),
|
|
585
|
-
ADVISOR_RENDER_OPTIONS,
|
|
620
|
+
{ ...ADVISOR_RENDER_OPTIONS, includeThinking: this.#includeThinking },
|
|
586
621
|
);
|
|
587
622
|
md = obfuscator.obfuscate(md, this.#advisorRegexSecretValues);
|
|
588
623
|
}
|
|
@@ -708,6 +743,7 @@ export class AdvisorRuntime {
|
|
|
708
743
|
epoch: number,
|
|
709
744
|
initial: PendingDelta[],
|
|
710
745
|
recoveringOverflow: boolean,
|
|
746
|
+
signal: AbortSignal,
|
|
711
747
|
): Promise<{
|
|
712
748
|
batch: string | null;
|
|
713
749
|
rawMessages: AgentMessage[];
|
|
@@ -724,11 +760,12 @@ export class AdvisorRuntime {
|
|
|
724
760
|
let wip = initial.at(-1)?.wip ?? false;
|
|
725
761
|
|
|
726
762
|
for (let round = 0; round < MAX_COALESCE_ROUNDS; round++) {
|
|
763
|
+
if (this.#sessionTransitionPaused) break;
|
|
727
764
|
if (this.host.maintainContext) {
|
|
728
765
|
const incomingTokens = estimateTokens({ role: "user", content: batchText, timestamp: Date.now() });
|
|
729
766
|
let shouldResetContext = false;
|
|
730
767
|
try {
|
|
731
|
-
shouldResetContext = await this.host.maintainContext(incomingTokens);
|
|
768
|
+
shouldResetContext = await this.host.maintainContext(incomingTokens, signal);
|
|
732
769
|
} catch (err) {
|
|
733
770
|
logger.debug("advisor context maintenance failed", { err: String(err) });
|
|
734
771
|
}
|
|
@@ -745,6 +782,7 @@ export class AdvisorRuntime {
|
|
|
745
782
|
// remain queued and ship as their own subsequent batch.
|
|
746
783
|
if (round > 0) {
|
|
747
784
|
const lateItems = this.#pending.splice(0);
|
|
785
|
+
initial.push(...lateItems);
|
|
748
786
|
turns += lateItems.reduce((sum, b) => sum + b.turns, 0);
|
|
749
787
|
if (lateItems.length > 0) {
|
|
750
788
|
wip = lateItems.at(-1)!.wip;
|
|
@@ -781,6 +819,7 @@ export class AdvisorRuntime {
|
|
|
781
819
|
// update WIP state, and re-check the maintenance budget.
|
|
782
820
|
const late = this.#pending.splice(0);
|
|
783
821
|
if (late.length === 0) break;
|
|
822
|
+
initial.push(...late);
|
|
784
823
|
batchText = [batchText, ...late.map(b => b.text)].join("\n\n");
|
|
785
824
|
rawMessages = rawMessages.concat(late.flatMap(b => b.rawMessages));
|
|
786
825
|
turns += late.reduce((sum, b) => sum + b.turns, 0);
|
|
@@ -814,10 +853,12 @@ export class AdvisorRuntime {
|
|
|
814
853
|
}
|
|
815
854
|
|
|
816
855
|
async #drain(): Promise<void> {
|
|
817
|
-
if (this.#busy) return;
|
|
856
|
+
if (this.#busy || this.#sessionTransitionPaused) return;
|
|
818
857
|
this.#busy = true;
|
|
819
858
|
try {
|
|
820
|
-
|
|
859
|
+
this.#syncModelIdentity();
|
|
860
|
+
while (!this.disposed && !this.#sessionTransitionPaused && this.#pending.length) {
|
|
861
|
+
this.#syncModelIdentity();
|
|
821
862
|
let popped: PendingDelta[];
|
|
822
863
|
if (this.#pending[0]?.overflowRecovery) {
|
|
823
864
|
const recovery = this.#pending.shift();
|
|
@@ -826,6 +867,8 @@ export class AdvisorRuntime {
|
|
|
826
867
|
} else {
|
|
827
868
|
popped = this.#pending.splice(0);
|
|
828
869
|
}
|
|
870
|
+
const iterationAbort = new AbortController();
|
|
871
|
+
this.#iterationAbort = iterationAbort;
|
|
829
872
|
const epoch = this.#epoch;
|
|
830
873
|
for (const delta of popped) {
|
|
831
874
|
if (delta.renderRevision === this.#renderRevision) continue;
|
|
@@ -834,10 +877,19 @@ export class AdvisorRuntime {
|
|
|
834
877
|
delta.renderRevision = this.#renderRevision;
|
|
835
878
|
}
|
|
836
879
|
const recoveringOverflow = popped.some(delta => delta.overflowRecovery === true);
|
|
837
|
-
const result = await this.#collectAndMaintainBatch(
|
|
880
|
+
const result = await this.#collectAndMaintainBatch(
|
|
881
|
+
epoch,
|
|
882
|
+
popped,
|
|
883
|
+
recoveringOverflow,
|
|
884
|
+
iterationAbort.signal,
|
|
885
|
+
);
|
|
838
886
|
|
|
839
887
|
// Epoch was invalidated during batch collection; restart the loop.
|
|
840
888
|
if (result === null) continue;
|
|
889
|
+
if (this.#sessionTransitionPaused) {
|
|
890
|
+
this.#pending.unshift(...popped);
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
841
893
|
|
|
842
894
|
const { batch, rawMessages, finalTurns, wip, resetContext } = result;
|
|
843
895
|
|
|
@@ -859,7 +911,13 @@ export class AdvisorRuntime {
|
|
|
859
911
|
// Reset the host's per-update advisor state (one-advise-per-update
|
|
860
912
|
// gate) before each model cycle so the new batch starts fresh.
|
|
861
913
|
this.host.beginAdvisorUpdate?.();
|
|
862
|
-
|
|
914
|
+
const prompt = this.agent.prompt(batch);
|
|
915
|
+
this.#promptInFlight = prompt;
|
|
916
|
+
try {
|
|
917
|
+
await prompt;
|
|
918
|
+
} finally {
|
|
919
|
+
if (this.#promptInFlight === prompt) this.#promptInFlight = undefined;
|
|
920
|
+
}
|
|
863
921
|
// Agent.#runLoop catches provider/stream failures internally and
|
|
864
922
|
// resolves prompt() cleanly with stopReason: "error". Treat that
|
|
865
923
|
// as a failed turn so endpoint rejections trip the retry path.
|
|
@@ -880,12 +938,17 @@ export class AdvisorRuntime {
|
|
|
880
938
|
this.#consecutiveQuarantines = 0;
|
|
881
939
|
if (this.host.onTurnSuccess) {
|
|
882
940
|
try {
|
|
883
|
-
await this.host.onTurnSuccess();
|
|
941
|
+
await raceWithSignal(Promise.resolve(this.host.onTurnSuccess()), iterationAbort.signal);
|
|
884
942
|
} catch (hookErr) {
|
|
885
943
|
logger.debug("advisor onTurnSuccess hook failed", { err: String(hookErr) });
|
|
886
944
|
}
|
|
887
945
|
}
|
|
888
946
|
} catch (err) {
|
|
947
|
+
if (this.#sessionTransitionPaused) {
|
|
948
|
+
this.#rollbackFailedTurn(messageSnapshot);
|
|
949
|
+
this.#pending.unshift(...popped);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
889
952
|
// reset()/dispose() aborts the in-flight prompt; treat it as a
|
|
890
953
|
// reset, not a transient failure — drop the stale batch.
|
|
891
954
|
if (this.#epoch !== epoch) continue;
|
|
@@ -897,6 +960,9 @@ export class AdvisorRuntime {
|
|
|
897
960
|
this.#wakeAllWaiters();
|
|
898
961
|
const failedMessages = this.agent.state.messages.slice(messageSnapshot);
|
|
899
962
|
const terminalFailure = this.#terminalAssistantFailure(messageSnapshot);
|
|
963
|
+
const classifierRefusal =
|
|
964
|
+
(terminalFailure !== undefined && isClassifierRefusal(terminalFailure)) ||
|
|
965
|
+
AIError.is(AIError.classify(err), AIError.Flag.ContentBlocked);
|
|
900
966
|
const terminalFailureId =
|
|
901
967
|
terminalFailure === undefined ? undefined : AIError.classifyMessage(terminalFailure);
|
|
902
968
|
const contextOverflow =
|
|
@@ -912,12 +978,43 @@ export class AdvisorRuntime {
|
|
|
912
978
|
AIError.is(terminalFailureId, AIError.Flag.ContextOverflow);
|
|
913
979
|
this.#rollbackFailedTurn(messageSnapshot);
|
|
914
980
|
logger.debug("advisor turn failed", { err: String(err) });
|
|
981
|
+
if (classifierRefusal) {
|
|
982
|
+
if (this.#includeThinking) {
|
|
983
|
+
this.#includeThinking = false;
|
|
984
|
+
const strippedBatch = this.#formatRawDelta(rawMessages, wip);
|
|
985
|
+
if (strippedBatch) {
|
|
986
|
+
this.#pending.unshift({
|
|
987
|
+
text: strippedBatch,
|
|
988
|
+
rawMessages,
|
|
989
|
+
renderRevision: this.#renderRevision,
|
|
990
|
+
turns: finalTurns,
|
|
991
|
+
wip,
|
|
992
|
+
overflowRecovery: recoveringOverflow || undefined,
|
|
993
|
+
});
|
|
994
|
+
logger.debug("advisor refusal recovered by stripping primary reasoning");
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
this.#notifyFailureOnce(err);
|
|
999
|
+
this.#clearSeenContext();
|
|
1000
|
+
this.#backlog = Math.max(0, this.#backlog - finalTurns);
|
|
1001
|
+
this.#notifyWaiters();
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
915
1004
|
let recovered = false;
|
|
916
1005
|
try {
|
|
917
|
-
recovered =
|
|
1006
|
+
recovered =
|
|
1007
|
+
(await raceWithSignal(
|
|
1008
|
+
Promise.resolve(this.host.onTurnError?.(err, failedMessages, iterationAbort.signal)),
|
|
1009
|
+
iterationAbort.signal,
|
|
1010
|
+
)) === true;
|
|
918
1011
|
} catch (hookErr) {
|
|
919
1012
|
logger.debug("advisor onTurnError hook failed", { err: String(hookErr) });
|
|
920
1013
|
}
|
|
1014
|
+
if (this.#sessionTransitionPaused) {
|
|
1015
|
+
this.#pending.unshift(...popped);
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
921
1018
|
if (err instanceof AdvisorOutputQuarantinedError) {
|
|
922
1019
|
// A quarantine discards the advisor's whole turn before dispatch, so
|
|
923
1020
|
// its advice never reaches the primary. One re-prime is allowed to
|
|
@@ -1032,7 +1129,15 @@ export class AdvisorRuntime {
|
|
|
1032
1129
|
wip,
|
|
1033
1130
|
overflowRecovery: recoveringOverflow || undefined,
|
|
1034
1131
|
});
|
|
1035
|
-
|
|
1132
|
+
if (this.retryDelayMs <= 0) {
|
|
1133
|
+
await Bun.sleep(0);
|
|
1134
|
+
} else {
|
|
1135
|
+
try {
|
|
1136
|
+
await raceWithSignal(Bun.sleep(this.retryDelayMs), iterationAbort.signal);
|
|
1137
|
+
} catch (sleepError) {
|
|
1138
|
+
if (!iterationAbort.signal.aborted) throw sleepError;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1036
1141
|
}
|
|
1037
1142
|
}
|
|
1038
1143
|
}
|
|
@@ -1043,11 +1148,20 @@ export class AdvisorRuntime {
|
|
|
1043
1148
|
}
|
|
1044
1149
|
}
|
|
1045
1150
|
} finally {
|
|
1151
|
+
this.#iterationAbort = undefined;
|
|
1046
1152
|
this.#busy = false;
|
|
1047
1153
|
}
|
|
1048
1154
|
}
|
|
1049
1155
|
}
|
|
1050
1156
|
|
|
1157
|
+
/** Mirrors turn recovery's refusal classification and retains AIError's provider-neutral content-block fallback. */
|
|
1158
|
+
function isClassifierRefusal(message: AssistantMessage): boolean {
|
|
1159
|
+
if (message.stopReason !== "error") return false;
|
|
1160
|
+
const stopType = message.stopDetails?.type;
|
|
1161
|
+
if (stopType === "refusal" || stopType === "sensitive") return true;
|
|
1162
|
+
return AIError.is(AIError.classifyMessage(message), AIError.Flag.ContentBlocked);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1051
1165
|
/**
|
|
1052
1166
|
* The only malformed advisor turn shape: the prompt resolved but produced no
|
|
1053
1167
|
* assistant response at all. Everything an assistant message carries — advice,
|