@oh-my-pi/pi-coding-agent 17.2.8 → 17.2.10
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 +94 -1
- package/dist/{CHANGELOG-0b0w17se.md → CHANGELOG-rhwzpexa.md} +94 -1
- package/dist/cli.js +13216 -15614
- package/dist/types/async/job-manager.d.ts +12 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/discovery/agents.d.ts +11 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/loader.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +10 -0
- package/dist/types/hindsight/state.d.ts +0 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/lsp/utils.d.ts +8 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +12 -5
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/session-observer-registry.d.ts +2 -0
- package/dist/types/registry/agent-lifecycle.d.ts +1 -1
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +8 -4
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +10 -1
- package/dist/types/session/turn-recovery.d.ts +6 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +2 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +3 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/read.d.ts +3 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +21 -3
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/changelog.d.ts +3 -2
- package/dist/types/utils/late-cleanup.d.ts +2 -0
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +4 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/omp +11 -2
- package/src/advisor/runtime.ts +54 -0
- package/src/async/job-manager.ts +26 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +4 -4
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +3 -57
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/commands/say.ts +1 -1
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/cli.ts +1 -1
- package/src/config/config-file.ts +2 -2
- package/src/config/keybindings.ts +3 -3
- package/src/config/model-discovery.ts +36 -2
- package/src/config/model-registry.ts +4 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/discovery/agents.ts +73 -3
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude.ts +8 -5
- package/src/discovery/cursor.ts +8 -5
- package/src/discovery/gemini.ts +11 -8
- package/src/discovery/vscode.ts +1 -0
- package/src/discovery/windsurf.ts +4 -2
- package/src/edit/hashline/filesystem.ts +7 -7
- package/src/eval/agent-bridge.ts +1 -3
- package/src/eval/jl/prelude.jl +4 -4
- package/src/eval/js/shared/prelude.txt +2 -2
- package/src/eval/py/prelude.py +0 -3
- package/src/eval/py/runner.py +38 -1
- package/src/eval/rb/prelude.rb +1 -2
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +7 -6
- package/src/extensibility/extensions/types.ts +39 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +23 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/hindsight/state.ts +2 -22
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +33 -0
- package/src/launch/terminal-output.ts +1 -1
- package/src/lsp/client.ts +2 -2
- package/src/lsp/utils.ts +29 -0
- package/src/main.ts +85 -21
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +670 -192
- package/src/modes/components/agent-transcript-viewer.ts +1 -3
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/custom-editor.ts +2 -2
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/session-selector.ts +2 -2
- package/src/modes/components/status-line/segments.ts +4 -3
- package/src/modes/controllers/event-controller.ts +188 -7
- package/src/modes/controllers/extension-ui-controller.ts +4 -3
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +40 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/print-mode.ts +29 -12
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/session-observer-registry.ts +5 -0
- package/src/modes/theme/theme.ts +2 -2
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +35 -10
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +11 -5
- package/src/session/agent-session-types.ts +8 -0
- package/src/session/agent-session.ts +177 -188
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +105 -110
- package/src/session/session-tools.ts +28 -1
- package/src/session/settings-stream-fn.ts +7 -3
- package/src/session/turn-recovery.ts +236 -18
- package/src/session/unexpected-stop-classifier.ts +12 -3
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/helpers/stats-dashboard.ts +2 -1
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +173 -47
- package/src/task/index.ts +9 -28
- package/src/task/isolation-runner.ts +43 -12
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +31 -6
- package/src/task/types.ts +4 -0
- package/src/tiny/worker.ts +1 -1
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-interceptor.ts +8 -4
- package/src/tools/bash.ts +6 -6
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +76 -19
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/fetch.ts +1 -1
- package/src/tools/glob.ts +4 -2
- package/src/tools/path-utils.ts +22 -5
- package/src/tools/read.ts +22 -5
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +38 -9
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/changelog.ts +5 -4
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +202 -58
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/hackage.ts +1 -12
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/ecosia.ts +2 -2
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/firecrawl.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +2 -2
- package/src/web/search/providers/perplexity.ts +1 -1
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/startpage.ts +2 -2
- package/src/markit/converters/mammoth.d.ts +0 -24
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import * as path from "node:path";
|
|
22
22
|
import type * as natives from "@oh-my-pi/pi-natives";
|
|
23
|
+
import { AgentRegistry } from "../registry/agent-registry";
|
|
23
24
|
import type { ToolSession } from "../tools";
|
|
24
25
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
25
26
|
import * as git from "../utils/git";
|
|
27
|
+
import { trackLateCleanup } from "../utils/late-cleanup";
|
|
26
28
|
import type { ExecutorOptions } from "./executor";
|
|
27
29
|
import { runSubprocess } from "./executor";
|
|
28
30
|
import type { SingleResult } from "./types";
|
|
@@ -43,6 +45,15 @@ import {
|
|
|
43
45
|
|
|
44
46
|
type IsoBackendKind = natives.IsoBackendKind;
|
|
45
47
|
|
|
48
|
+
function rememberAgentArtifacts(result: SingleResult): SingleResult {
|
|
49
|
+
AgentRegistry.global().setHistory(result.id, {
|
|
50
|
+
outputPath: result.outputPath,
|
|
51
|
+
patchPath: result.patchPath,
|
|
52
|
+
branchName: result.branchName,
|
|
53
|
+
});
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
46
57
|
/** Resolved repo + baseline used by every isolated spawn in a single call. */
|
|
47
58
|
export interface IsolationContext {
|
|
48
59
|
repoRoot: string;
|
|
@@ -146,6 +157,7 @@ async function writeIsolationPatch(
|
|
|
146
157
|
*/
|
|
147
158
|
export async function runIsolatedSubprocess(opts: IsolatedRunOptions): Promise<SingleResult> {
|
|
148
159
|
let handle: IsolationHandle | undefined;
|
|
160
|
+
let deferredCleanup: Promise<void> | undefined;
|
|
149
161
|
try {
|
|
150
162
|
const taskBaseline = structuredClone(opts.context.baseline);
|
|
151
163
|
handle = await ensureIsolation(opts.context.repoRoot, opts.agentId, opts.preferredBackend);
|
|
@@ -155,7 +167,12 @@ export async function runIsolatedSubprocess(opts: IsolatedRunOptions): Promise<S
|
|
|
155
167
|
worktree: isolationDir,
|
|
156
168
|
preloadedExtensionPaths: undefined,
|
|
157
169
|
preloadedCustomToolPaths: undefined,
|
|
170
|
+
onCleanupDeferred: completion => {
|
|
171
|
+
deferredCleanup = completion;
|
|
172
|
+
opts.baseOptions.onCleanupDeferred?.(completion);
|
|
173
|
+
},
|
|
158
174
|
});
|
|
175
|
+
if (deferredCleanup) return result;
|
|
159
176
|
if (opts.mergeMode === "branch" && result.exitCode === 0) {
|
|
160
177
|
try {
|
|
161
178
|
const commitResult = await commitToBranch(
|
|
@@ -165,14 +182,14 @@ export async function runIsolatedSubprocess(opts: IsolatedRunOptions): Promise<S
|
|
|
165
182
|
opts.description,
|
|
166
183
|
opts.buildCommitMessage?.(),
|
|
167
184
|
);
|
|
168
|
-
return {
|
|
185
|
+
return rememberAgentArtifacts({
|
|
169
186
|
...result,
|
|
170
187
|
branchName: commitResult?.branchName,
|
|
171
188
|
branchBaseSha: commitResult?.baseSha,
|
|
172
189
|
nestedPatches: commitResult?.nestedPatches,
|
|
173
|
-
};
|
|
190
|
+
});
|
|
174
191
|
} catch (mergeErr) {
|
|
175
|
-
// Agent succeeded but branch commit failed — clean up stale branch
|
|
192
|
+
// Agent succeeded but branch commit failed — clean up stale branch.
|
|
176
193
|
const branchName = `omp/task/${opts.agentId}`;
|
|
177
194
|
await git.branch.tryDelete(opts.context.repoRoot, branchName);
|
|
178
195
|
const msg = mergeErr instanceof Error ? mergeErr.message : String(mergeErr);
|
|
@@ -183,37 +200,51 @@ export async function runIsolatedSubprocess(opts: IsolatedRunOptions): Promise<S
|
|
|
183
200
|
opts.artifactsDir,
|
|
184
201
|
opts.agentId,
|
|
185
202
|
);
|
|
186
|
-
return {
|
|
203
|
+
return rememberAgentArtifacts({
|
|
187
204
|
...result,
|
|
188
205
|
patchPath: patchResult.patchPath,
|
|
189
206
|
nestedPatches: patchResult.nestedPatches,
|
|
190
207
|
error: `Merge failed: ${msg}`,
|
|
191
|
-
};
|
|
208
|
+
});
|
|
192
209
|
} catch (patchErr) {
|
|
193
210
|
const patchMsg = patchErr instanceof Error ? patchErr.message : String(patchErr);
|
|
194
|
-
return {
|
|
211
|
+
return rememberAgentArtifacts({
|
|
212
|
+
...result,
|
|
213
|
+
error: `Merge failed: ${msg}; patch capture failed: ${patchMsg}`,
|
|
214
|
+
});
|
|
195
215
|
}
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
218
|
if (result.exitCode === 0) {
|
|
199
219
|
try {
|
|
200
220
|
const patchResult = await writeIsolationPatch(isolationDir, taskBaseline, opts.artifactsDir, opts.agentId);
|
|
201
|
-
return {
|
|
221
|
+
return rememberAgentArtifacts({
|
|
202
222
|
...result,
|
|
203
223
|
patchPath: patchResult.patchPath,
|
|
204
224
|
nestedPatches: patchResult.nestedPatches,
|
|
205
|
-
};
|
|
225
|
+
});
|
|
206
226
|
} catch (patchErr) {
|
|
207
227
|
const msg = patchErr instanceof Error ? patchErr.message : String(patchErr);
|
|
208
|
-
return { ...result, error: `Patch capture failed: ${msg}` };
|
|
228
|
+
return rememberAgentArtifacts({ ...result, error: `Patch capture failed: ${msg}` });
|
|
209
229
|
}
|
|
210
230
|
}
|
|
211
|
-
return result;
|
|
231
|
+
return rememberAgentArtifacts(result);
|
|
212
232
|
} catch (err) {
|
|
213
|
-
return opts.buildFailureResult(err);
|
|
233
|
+
return rememberAgentArtifacts(opts.buildFailureResult(err));
|
|
214
234
|
} finally {
|
|
215
235
|
if (handle) {
|
|
216
|
-
|
|
236
|
+
const isolationHandle = handle;
|
|
237
|
+
if (deferredCleanup) {
|
|
238
|
+
trackLateCleanup(
|
|
239
|
+
deferredCleanup.then(() => cleanupIsolation(isolationHandle)),
|
|
240
|
+
{
|
|
241
|
+
agentId: opts.agentId,
|
|
242
|
+
resource: "isolation",
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
} else {
|
|
246
|
+
await cleanupIsolation(isolationHandle);
|
|
247
|
+
}
|
|
217
248
|
}
|
|
218
249
|
}
|
|
219
250
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
-
|
|
3
2
|
import type { ModelRegistry } from "../config/model-registry";
|
|
3
|
+
import { formatModelRoleAlias } from "../config/model-roles";
|
|
4
4
|
import type { Settings } from "../config/settings";
|
|
5
5
|
import { MCPManager } from "../mcp/manager";
|
|
6
6
|
import type { PersistedSubagentReviverFactory } from "../registry/agent-lifecycle";
|
|
@@ -79,6 +79,14 @@ export function createPersistedSubagentReviverFactory(
|
|
|
79
79
|
taskDepth++;
|
|
80
80
|
parentId = registry.get(parentId)?.parentId;
|
|
81
81
|
}
|
|
82
|
+
const subagentSettings = createSubagentSettings(
|
|
83
|
+
ctx.settings,
|
|
84
|
+
init.readSummarize === false ? { "read.summarize.enabled": false } : undefined,
|
|
85
|
+
);
|
|
86
|
+
const persistedModelPattern =
|
|
87
|
+
init.modelRole && init.modelRole !== "default"
|
|
88
|
+
? [formatModelRoleAlias(init.modelRole), ...(init.resolvedModel ? [init.resolvedModel] : [])]
|
|
89
|
+
: init.resolvedModel;
|
|
82
90
|
return async expectedRef => {
|
|
83
91
|
// Re-open fresh on every revive: park closes the writer, so this takes
|
|
84
92
|
// the single-writer lock cleanly and restores the full message history.
|
|
@@ -96,10 +104,9 @@ export function createPersistedSubagentReviverFactory(
|
|
|
96
104
|
cwd: ctx.session.sessionManager.getCwd(),
|
|
97
105
|
authStorage: ctx.authStorage,
|
|
98
106
|
modelRegistry: ctx.modelRegistry,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
),
|
|
107
|
+
...(persistedModelPattern ? { modelPattern: persistedModelPattern } : {}),
|
|
108
|
+
modelPatternAuthFallback: init.resolvedModel,
|
|
109
|
+
settings: subagentSettings,
|
|
103
110
|
sessionManager: reopened,
|
|
104
111
|
agentId: ref.id,
|
|
105
112
|
agentDisplayName: ref.displayName,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AgentDefinition } from "./types";
|
|
2
|
+
|
|
3
|
+
// Built-in tools whose approval tier is "read" (see tool classes' `approval`).
|
|
4
|
+
// An agent is read-only iff its declared tools are a non-empty subset of this set.
|
|
5
|
+
// Fail-safe: any unknown tool makes the agent not read-only.
|
|
6
|
+
export const READ_ONLY_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
7
|
+
"read",
|
|
8
|
+
"grep",
|
|
9
|
+
"glob",
|
|
10
|
+
"web_search",
|
|
11
|
+
"ast_grep",
|
|
12
|
+
"yield",
|
|
13
|
+
"hub",
|
|
14
|
+
"ask",
|
|
15
|
+
"todo",
|
|
16
|
+
"recall",
|
|
17
|
+
"reflect",
|
|
18
|
+
"retain",
|
|
19
|
+
"memory_edit",
|
|
20
|
+
"inspect_image",
|
|
21
|
+
"checkpoint",
|
|
22
|
+
"rewind",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export function isReadOnlyAgent(agent: AgentDefinition): boolean {
|
|
26
|
+
return !!agent.tools?.length && agent.tools.every(tool => READ_ONLY_TOOL_NAMES.has(tool));
|
|
27
|
+
}
|
|
@@ -8,7 +8,7 @@ import * as fs from "node:fs/promises";
|
|
|
8
8
|
import * as os from "node:os";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { $env, prompt, Snowflake } from "@oh-my-pi/pi-utils";
|
|
11
|
-
import { resolveAgentModelPatterns } from "../config/model-resolver";
|
|
11
|
+
import { resolveAgentModelPatterns, resolveAgentModelSource, resolveExplicitModelRole } from "../config/model-resolver";
|
|
12
12
|
import type { LocalProtocolOptions } from "../internal-urls";
|
|
13
13
|
import { registerArtifactsDir } from "../internal-urls/registry-helpers";
|
|
14
14
|
import { MCPManager } from "../mcp/manager";
|
|
@@ -20,6 +20,7 @@ import type { TaskEffort } from "../thinking";
|
|
|
20
20
|
import type { ToolSession } from "../tools";
|
|
21
21
|
import { isIrcEnabled } from "../tools/hub";
|
|
22
22
|
import { buildOutputValidator } from "../tools/output-schema-validator";
|
|
23
|
+
import { trackLateCleanup } from "../utils/late-cleanup";
|
|
23
24
|
import { type DiscoveryResult, discoverAgents, getAgent } from "./discovery";
|
|
24
25
|
import { type ExecutorOptions, runSubprocess } from "./executor";
|
|
25
26
|
import {
|
|
@@ -122,6 +123,8 @@ export interface EffectiveSubagentPolicy {
|
|
|
122
123
|
agent: AgentDefinition;
|
|
123
124
|
effectiveAgent: AgentDefinition;
|
|
124
125
|
modelOverride?: string | string[];
|
|
126
|
+
/** Explicit pre-expansion model role alias selected for this run. */
|
|
127
|
+
modelRole?: string;
|
|
125
128
|
parentActiveModelPattern?: string;
|
|
126
129
|
schema: StructuredSubagentSchemaResolution;
|
|
127
130
|
planMode: boolean;
|
|
@@ -277,13 +280,18 @@ export async function resolveEffectiveSubagentPolicy(
|
|
|
277
280
|
}
|
|
278
281
|
const agentModelOverrides = request.session.settings.get("task.agentModelOverrides");
|
|
279
282
|
const parentActiveModelPattern = request.session.getActiveModelString?.();
|
|
280
|
-
const
|
|
281
|
-
|
|
283
|
+
const modelResolution = {
|
|
284
|
+
requestModel: request.model,
|
|
285
|
+
settingsOverride: agentModelOverrides[agentName],
|
|
282
286
|
agentModel: effectiveAgent.model,
|
|
283
287
|
settings: request.session.settings,
|
|
284
288
|
activeModelPattern: parentActiveModelPattern,
|
|
285
289
|
fallbackModelPattern: request.session.getModelString?.(),
|
|
286
|
-
}
|
|
290
|
+
};
|
|
291
|
+
// Keep role identity from the same effective non-empty source that supplies
|
|
292
|
+
// model selection: caller request, settings override, then agent definition.
|
|
293
|
+
const modelRole = resolveExplicitModelRole(resolveAgentModelSource(modelResolution), request.session.settings);
|
|
294
|
+
const modelOverride = resolveAgentModelPatterns(modelResolution);
|
|
287
295
|
const isolationMode = request.session.settings.get("task.isolation.mode");
|
|
288
296
|
const isIsolated = request.isolation?.requested === true;
|
|
289
297
|
if (isIsolated && isolationMode === "none") {
|
|
@@ -298,6 +306,7 @@ export async function resolveEffectiveSubagentPolicy(
|
|
|
298
306
|
agent,
|
|
299
307
|
effectiveAgent,
|
|
300
308
|
modelOverride,
|
|
309
|
+
modelRole,
|
|
301
310
|
parentActiveModelPattern,
|
|
302
311
|
schema,
|
|
303
312
|
planMode,
|
|
@@ -393,6 +402,7 @@ function buildExecutorOptions(
|
|
|
393
402
|
invokedAt: request.invokedAt,
|
|
394
403
|
acquiredAt: request.acquiredAt,
|
|
395
404
|
modelOverride: policy.modelOverride,
|
|
405
|
+
modelRole: policy.modelRole,
|
|
396
406
|
parentActiveModelPattern: policy.parentActiveModelPattern,
|
|
397
407
|
thinkingLevel: policy.effectiveAgent.thinkingLevel,
|
|
398
408
|
effort: request.effort,
|
|
@@ -475,6 +485,7 @@ function buildFailureResult(
|
|
|
475
485
|
tokens: 0,
|
|
476
486
|
requests: 0,
|
|
477
487
|
modelOverride: policy.modelOverride,
|
|
488
|
+
modelRole: policy.modelRole,
|
|
478
489
|
error: message,
|
|
479
490
|
};
|
|
480
491
|
};
|
|
@@ -540,12 +551,16 @@ export async function runStructuredSubagent(request: StructuredSubagentRequest):
|
|
|
540
551
|
let mergeSummary = "";
|
|
541
552
|
let requiresRecoveryArtifacts = false;
|
|
542
553
|
let completedSuccessfully = false;
|
|
554
|
+
let deferredCleanup: Promise<void> | undefined;
|
|
543
555
|
try {
|
|
544
556
|
const id = await reserveStructuredSubagentId(request.session, {
|
|
545
557
|
...request.identity,
|
|
546
558
|
label: request.identity?.label ?? (request.invocationKind === "eval" ? "EvalAgent" : undefined),
|
|
547
559
|
});
|
|
548
560
|
const baseOptions = buildExecutorOptions(request, policy, lease, id);
|
|
561
|
+
baseOptions.onCleanupDeferred = completion => {
|
|
562
|
+
deferredCleanup = completion;
|
|
563
|
+
};
|
|
549
564
|
baseOptions.planReference = await loadPlanReference(request, policy);
|
|
550
565
|
let isolationContext: IsolationContext | null = null;
|
|
551
566
|
if (policy.isIsolated) {
|
|
@@ -639,8 +654,18 @@ export async function runStructuredSubagent(request: StructuredSubagentRequest):
|
|
|
639
654
|
(policy.isIsolated && (!policy.applyChanges || changesApplied === false || requiresRecoveryArtifacts));
|
|
640
655
|
const shouldCleanup = lease.temporary && !shouldRetainArtifacts;
|
|
641
656
|
if (shouldCleanup) {
|
|
642
|
-
|
|
643
|
-
|
|
657
|
+
const cleanupArtifacts = async (): Promise<void> => {
|
|
658
|
+
await fs.rm(lease.artifactsDir, { recursive: true, force: true });
|
|
659
|
+
lease.unregister?.();
|
|
660
|
+
};
|
|
661
|
+
if (deferredCleanup) {
|
|
662
|
+
trackLateCleanup(deferredCleanup.then(cleanupArtifacts), {
|
|
663
|
+
resource: "artifacts",
|
|
664
|
+
artifactsDir: lease.artifactsDir,
|
|
665
|
+
});
|
|
666
|
+
} else {
|
|
667
|
+
await cleanupArtifacts();
|
|
668
|
+
}
|
|
644
669
|
}
|
|
645
670
|
}
|
|
646
671
|
}
|
package/src/task/types.ts
CHANGED
|
@@ -426,6 +426,8 @@ export interface AgentProgress {
|
|
|
426
426
|
cost: number;
|
|
427
427
|
durationMs: number;
|
|
428
428
|
modelOverride?: string | string[];
|
|
429
|
+
/** Explicit pre-expansion model role alias selected for this run. */
|
|
430
|
+
modelRole?: string;
|
|
429
431
|
/** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Undefined when the model could not be resolved. */
|
|
430
432
|
resolvedModel?: string;
|
|
431
433
|
/** True when {@link resolvedModel} is the target of an active retry fallback (not the originally configured model). Lets observer-only UIs (collab guests, Agent Hub rows with no live session) flag the fallback and keep the provider. */
|
|
@@ -495,6 +497,8 @@ export interface SingleResult {
|
|
|
495
497
|
/** Model's context window in tokens, when known. */
|
|
496
498
|
contextWindow?: number;
|
|
497
499
|
modelOverride?: string | string[];
|
|
500
|
+
/** Explicit pre-expansion model role alias selected for this run. */
|
|
501
|
+
modelRole?: string;
|
|
498
502
|
/** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Omitted from tool-result JSON when undefined to keep wire payloads small. */
|
|
499
503
|
resolvedModel?: string;
|
|
500
504
|
/** True when {@link resolvedModel} is the target of an active retry fallback. Mirrors {@link AgentProgress.resolvedModelIsFallback} onto the settled result. */
|
package/src/tiny/worker.ts
CHANGED
|
@@ -94,7 +94,7 @@ function createStopOnTextCriteria(
|
|
|
94
94
|
this.#text = text;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
_call(inputIds: number[][]): boolean[] {
|
|
97
|
+
override _call(inputIds: number[][]): boolean[] {
|
|
98
98
|
return inputIds.map(ids => {
|
|
99
99
|
const tail = ids.slice(-STOP_DECODE_WINDOW_TOKENS);
|
|
100
100
|
const decoded = this.#tokenizer.decode(tail, {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { isEnoent, peekFile } from "@oh-my-pi/pi-utils";
|
|
9
|
-
import { LRUCache } from "
|
|
9
|
+
import { LRUCache } from "@oh-my-pi/pi-utils/lru";
|
|
10
10
|
import { settings as globalSettings, isSettingsInitialized, type Settings } from "../config/settings";
|
|
11
11
|
import { getDefault } from "../config/settings-schema";
|
|
12
12
|
import { ToolError } from "./tool-errors";
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
visibleWidth,
|
|
12
12
|
} from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
14
|
-
import type * as XtermModule from "@
|
|
15
|
-
import type { Terminal as XtermTerminalType } from "@
|
|
14
|
+
import type * as XtermModule from "@oh-my-pi/pi-utils/vterm";
|
|
15
|
+
import type { Terminal as XtermTerminalType } from "@oh-my-pi/pi-utils/vterm";
|
|
16
16
|
import { Settings } from "../config/settings";
|
|
17
17
|
import type { Theme } from "../modes/theme/theme";
|
|
18
18
|
import { OutputSink, type OutputSummary } from "../session/streaming-output";
|
|
@@ -36,13 +36,13 @@ function normalizeCaptureChunk(chunk: string): string {
|
|
|
36
36
|
// source of truth for the final captured output.
|
|
37
37
|
const MAX_LIVE_WRITE_QUEUE_CHUNKS = 512;
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// The virtual terminal is only needed once an interactive PTY session actually starts,
|
|
40
40
|
// so it is loaded lazily (and memoized) instead of weighing down CLI startup.
|
|
41
41
|
let xtermTerminalCtor: typeof XtermModule.Terminal | undefined;
|
|
42
42
|
|
|
43
43
|
async function loadXtermTerminal(): Promise<typeof XtermModule.Terminal> {
|
|
44
44
|
if (!xtermTerminalCtor) {
|
|
45
|
-
const mod = (await import("@
|
|
45
|
+
const mod = (await import("@oh-my-pi/pi-utils/vterm")) as typeof XtermModule & { default?: typeof XtermModule };
|
|
46
46
|
xtermTerminalCtor = (mod.default ?? mod).Terminal;
|
|
47
47
|
}
|
|
48
48
|
return xtermTerminalCtor;
|
|
@@ -96,10 +96,14 @@ function withoutLeadingEnvironmentAssignments(command: string): string | null {
|
|
|
96
96
|
|
|
97
97
|
function interceptionCandidates(command: string): string[] {
|
|
98
98
|
const candidates = [command.trim()];
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
for (const segment of extractFlatShellCommandSegments(command)) {
|
|
100
|
+
// A segment that consumes the previous stage's stdout via `|` reads piped
|
|
101
|
+
// stdin, which no path-based dedicated tool (read/grep/glob) — nor any
|
|
102
|
+
// other dedicated tool — can replace, so it is not an interception
|
|
103
|
+
// candidate. Standalone and first-stage commands still match.
|
|
104
|
+
if (segment.pipedStdin) continue;
|
|
105
|
+
candidates.push(segment.text);
|
|
106
|
+
const withoutAssignments = withoutLeadingEnvironmentAssignments(segment.text);
|
|
103
107
|
if (withoutAssignments) candidates.push(withoutAssignments);
|
|
104
108
|
}
|
|
105
109
|
return candidates;
|
package/src/tools/bash.ts
CHANGED
|
@@ -384,8 +384,8 @@ function normalizeBashEnv(env: Record<string, string> | undefined): Record<strin
|
|
|
384
384
|
return normalized;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
function escapeBashEnvValueForDisplay(value:
|
|
388
|
-
return value
|
|
387
|
+
function escapeBashEnvValueForDisplay(value: unknown): string {
|
|
388
|
+
return String(value)
|
|
389
389
|
.replaceAll("\\", "\\\\")
|
|
390
390
|
.replaceAll("\n", "\\n")
|
|
391
391
|
.replaceAll("\r", "\\r")
|
|
@@ -395,7 +395,7 @@ function escapeBashEnvValueForDisplay(value: string): string {
|
|
|
395
395
|
.replaceAll("`", "\\`");
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
function formatBashEnvAssignments(env: Record<string,
|
|
398
|
+
function formatBashEnvAssignments(env: Record<string, unknown> | undefined): string {
|
|
399
399
|
if (!env || Object.keys(env).length === 0) return "";
|
|
400
400
|
return Object.entries(env)
|
|
401
401
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
@@ -1498,7 +1498,7 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
1498
1498
|
// =============================================================================
|
|
1499
1499
|
export interface BashRenderArgs {
|
|
1500
1500
|
command?: string;
|
|
1501
|
-
env?: Record<string,
|
|
1501
|
+
env?: Record<string, unknown>;
|
|
1502
1502
|
timeout?: number;
|
|
1503
1503
|
cwd?: string;
|
|
1504
1504
|
__partialJson?: string;
|
|
@@ -1522,7 +1522,7 @@ export interface ShellRendererConfig<TArgs> {
|
|
|
1522
1522
|
resolveTitle: (args: TArgs | undefined, options: RenderResultOptions) => string;
|
|
1523
1523
|
resolveCommand?: (args: TArgs | undefined) => string | undefined;
|
|
1524
1524
|
resolveCwd?: (args: TArgs | undefined) => string | undefined;
|
|
1525
|
-
resolveEnv?: (args: TArgs | undefined) => Record<string,
|
|
1525
|
+
resolveEnv?: (args: TArgs | undefined) => Record<string, unknown> | undefined;
|
|
1526
1526
|
showHeader?: boolean;
|
|
1527
1527
|
}
|
|
1528
1528
|
|
|
@@ -1532,7 +1532,7 @@ function getPartialJson<TArgs>(args: TArgs | undefined): string | undefined {
|
|
|
1532
1532
|
return typeof value === "string" ? value : undefined;
|
|
1533
1533
|
}
|
|
1534
1534
|
|
|
1535
|
-
export function getBashEnvForDisplay(args: BashRenderArgs): Record<string,
|
|
1535
|
+
export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, unknown> | undefined {
|
|
1536
1536
|
// The parsed args don't always mirror the exact current stream prefix, so recover
|
|
1537
1537
|
// env from the raw JSON buffer to surface `NAME="..." cmd` in the preview as it
|
|
1538
1538
|
// streams rather than only once the args object finishes.
|
|
@@ -8,7 +8,16 @@ import { resizeImage } from "../../../utils/image-resize";
|
|
|
8
8
|
import type { ToolSession } from "../../index";
|
|
9
9
|
import { resolveToCwd } from "../../path-utils";
|
|
10
10
|
import { formatScreenshot } from "../../render-utils";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
bindRunFacade,
|
|
13
|
+
isBrowserRunOwnedRejection,
|
|
14
|
+
markBrowserRunRejection,
|
|
15
|
+
observeBrowserRunPromise,
|
|
16
|
+
resolvePredicateTimeout,
|
|
17
|
+
type WaitPredicateOptions,
|
|
18
|
+
waitForRun,
|
|
19
|
+
withBrowserPromiseCombinatorTracking,
|
|
20
|
+
} from "../../run-scope";
|
|
12
21
|
import { ToolAbortError, ToolError, throwIfAborted } from "../../tool-errors";
|
|
13
22
|
import { type AriaSnapshotOptions, assertSelectorString, buildAriaSnapshotScript } from "../aria/aria-snapshot";
|
|
14
23
|
import { DEFAULT_VIEWPORT } from "../launch";
|
|
@@ -1368,6 +1377,7 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1368
1377
|
const signal = AbortSignal.any(
|
|
1369
1378
|
opts.signal ? [timeoutSignal, opts.signal, runAc.signal] : [timeoutSignal, runAc.signal],
|
|
1370
1379
|
);
|
|
1380
|
+
const runEndedError = postmortem.markExpectedCleanupError(new ToolAbortError("Browser run ended"));
|
|
1371
1381
|
const output = new RunOutput();
|
|
1372
1382
|
const screenshots: ScreenshotResult[] = [];
|
|
1373
1383
|
const runId = crypto.randomUUID();
|
|
@@ -1382,6 +1392,28 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1382
1392
|
// handler to this promise; keep its armed rejection from surfacing as an
|
|
1383
1393
|
// unhandled rejection — the postmortem-fatal path this run guards against.
|
|
1384
1394
|
cancelRejection.catch(() => {});
|
|
1395
|
+
const rejectionOwner = {};
|
|
1396
|
+
const { promise: floatingFailure, reject: rejectFloatingFailure } = Promise.withResolvers<never>();
|
|
1397
|
+
floatingFailure.catch(() => {});
|
|
1398
|
+
let runActive = true;
|
|
1399
|
+
let hasFloatingFailure = false;
|
|
1400
|
+
const recordFloatingFailure = (reason: unknown): void => {
|
|
1401
|
+
if (hasFloatingFailure || postmortem.isExpectedCleanupError(reason)) return;
|
|
1402
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
1403
|
+
if (!runActive) {
|
|
1404
|
+
logger.warn("Unhandled rejection after browser run ended", { runId, error: message });
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
hasFloatingFailure = true;
|
|
1408
|
+
const error = new Error(`Unhandled rejection (missing await?): ${message}`, { cause: reason });
|
|
1409
|
+
if (reason instanceof Error) error.name = reason.name;
|
|
1410
|
+
rejectFloatingFailure(error);
|
|
1411
|
+
};
|
|
1412
|
+
const uninstallRejectionInterceptor = postmortem.interceptUnhandledRejections(reason => {
|
|
1413
|
+
if (!isBrowserRunOwnedRejection(reason, rejectionOwner, `cmux-run-${runId}.js`)) return false;
|
|
1414
|
+
recordFloatingFailure(reason);
|
|
1415
|
+
return true;
|
|
1416
|
+
});
|
|
1385
1417
|
const onAbort = (): void => {
|
|
1386
1418
|
if (timeoutSignal.aborted) {
|
|
1387
1419
|
reject(new ToolError(`Browser code execution timed out after ${opts.timeoutMs}ms`));
|
|
@@ -1402,24 +1434,30 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1402
1434
|
// Keep both inside try so a concurrent in-process eval/browser run surfaces as
|
|
1403
1435
|
// a rejected promise the supervisor can report, never an unhandled rejection.
|
|
1404
1436
|
runtime.setCwd(opts.snapshot.cwd);
|
|
1405
|
-
const runTab = bindRunFacade(tab, signal);
|
|
1437
|
+
const runTab = bindRunFacade(tab, signal, rejectionOwner, recordFloatingFailure);
|
|
1406
1438
|
runtime.setRunScope({
|
|
1407
|
-
page: bindRunFacade(tab.page, signal),
|
|
1408
|
-
browser: bindRunFacade(tab.browser, signal),
|
|
1439
|
+
page: bindRunFacade(tab.page, signal, rejectionOwner, recordFloatingFailure),
|
|
1440
|
+
browser: bindRunFacade(tab.browser, signal, rejectionOwner, recordFloatingFailure),
|
|
1409
1441
|
tab: runTab,
|
|
1410
1442
|
assert: (cond: unknown, text?: string): void => {
|
|
1411
1443
|
if (!cond) throw new ToolError(text ?? "Assertion failed");
|
|
1412
1444
|
},
|
|
1413
1445
|
wait: (msOrPredicate: number | (() => unknown), waitOpts?: WaitPredicateOptions): Promise<unknown> =>
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1446
|
+
observeBrowserRunPromise(
|
|
1447
|
+
waitForRun(
|
|
1448
|
+
msOrPredicate,
|
|
1449
|
+
signal,
|
|
1450
|
+
typeof msOrPredicate === "number"
|
|
1451
|
+
? waitOpts
|
|
1452
|
+
: {
|
|
1453
|
+
timeout: resolvePredicateTimeout(opts.timeoutMs, waitOpts?.timeout),
|
|
1454
|
+
interval: waitOpts?.interval,
|
|
1455
|
+
},
|
|
1456
|
+
).catch(error => {
|
|
1457
|
+
throw markBrowserRunRejection(error, rejectionOwner);
|
|
1458
|
+
}),
|
|
1459
|
+
rejectionOwner,
|
|
1460
|
+
recordFloatingFailure,
|
|
1423
1461
|
),
|
|
1424
1462
|
});
|
|
1425
1463
|
|
|
@@ -1444,16 +1482,24 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1444
1482
|
let runError: unknown;
|
|
1445
1483
|
let runFailed = false;
|
|
1446
1484
|
try {
|
|
1447
|
-
returnValue = await
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1485
|
+
returnValue = await withBrowserPromiseCombinatorTracking(
|
|
1486
|
+
rejectionOwner,
|
|
1487
|
+
recordFloatingFailure,
|
|
1488
|
+
async () =>
|
|
1489
|
+
await Promise.race([
|
|
1490
|
+
runtime.run(opts.code, filename, hooks, { runId, cwd: opts.snapshot.cwd }),
|
|
1491
|
+
cancelRejection,
|
|
1492
|
+
floatingFailure,
|
|
1493
|
+
]),
|
|
1494
|
+
);
|
|
1451
1495
|
} catch (error) {
|
|
1452
1496
|
runFailed = true;
|
|
1453
1497
|
runError = error;
|
|
1454
1498
|
}
|
|
1499
|
+
runAc.abort(runEndedError);
|
|
1455
1500
|
// Let rejection callbacks run while this run can still own guest-created promises.
|
|
1456
1501
|
await Bun.sleep(0);
|
|
1502
|
+
if (hasFloatingFailure && !runFailed) await floatingFailure;
|
|
1457
1503
|
if (runFailed) {
|
|
1458
1504
|
for (const reason of activeRun.floatingRejections) {
|
|
1459
1505
|
logger.warn("Unhandled rejection accompanied a failed cmux browser run", { filename, error: reason });
|
|
@@ -1470,8 +1516,10 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1470
1516
|
}
|
|
1471
1517
|
return { displays: output.finish(), returnValue: cloneSafe(returnValue), screenshots };
|
|
1472
1518
|
} finally {
|
|
1519
|
+
runActive = false;
|
|
1520
|
+
uninstallRejectionInterceptor();
|
|
1473
1521
|
signal.removeEventListener("abort", onAbort);
|
|
1474
|
-
runAc.abort(
|
|
1522
|
+
runAc.abort(runEndedError);
|
|
1475
1523
|
activeCmuxRuns.delete(filename);
|
|
1476
1524
|
rememberCmuxRunFile(filename);
|
|
1477
1525
|
tab.clearRunContext();
|