@oh-my-pi/pi-coding-agent 14.5.12 → 14.5.13
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 +32 -0
- package/package.json +18 -10
- package/src/cli/jupyter-cli.ts +1 -1
- package/src/config/model-equivalence.ts +49 -16
- package/src/config/model-registry.ts +100 -25
- package/src/config/model-resolver.ts +29 -15
- package/src/config/settings-schema.ts +20 -6
- package/src/config/settings.ts +9 -8
- package/src/config.ts +9 -0
- package/src/eval/backend.ts +43 -0
- package/src/eval/eval.lark +43 -0
- package/src/eval/index.ts +5 -0
- package/src/eval/js/context-manager.ts +717 -0
- package/src/eval/js/executor.ts +131 -0
- package/src/eval/js/index.ts +46 -0
- package/src/eval/js/prelude.ts +2 -0
- package/src/eval/js/prelude.txt +84 -0
- package/src/eval/js/tool-bridge.ts +124 -0
- package/src/eval/parse.ts +337 -0
- package/src/{ipy → eval/py}/executor.ts +2 -180
- package/src/{ipy → eval/py}/gateway-coordinator.ts +2 -2
- package/src/eval/py/index.ts +58 -0
- package/src/{ipy → eval/py}/kernel.ts +5 -41
- package/src/{ipy → eval/py}/prelude.py +39 -227
- package/src/eval/types.ts +48 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +8 -10
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/lsp/client.ts +9 -0
- package/src/lsp/index.ts +395 -0
- package/src/lsp/types.ts +15 -4
- package/src/main.ts +25 -14
- package/src/mcp/oauth-flow.ts +1 -1
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/components/{python-execution.ts → eval-execution.ts} +11 -4
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +2 -1
- package/src/modes/components/tool-execution.ts +3 -4
- package/src/modes/controllers/command-controller.ts +28 -8
- package/src/modes/controllers/input-controller.ts +4 -4
- package/src/modes/controllers/selector-controller.ts +2 -1
- package/src/modes/interactive-mode.ts +4 -5
- package/src/modes/types.ts +3 -3
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/prompts/system/system-prompt.md +3 -3
- package/src/prompts/tools/eval.md +92 -0
- package/src/prompts/tools/lsp.md +7 -3
- package/src/sdk.ts +45 -31
- package/src/session/agent-session.ts +42 -42
- package/src/session/messages.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/system-prompt.ts +34 -66
- package/src/task/executor.ts +5 -9
- package/src/tools/browser/launch.ts +22 -0
- package/src/tools/browser/registry.ts +25 -244
- package/src/tools/browser/render.ts +1 -1
- package/src/tools/browser/tab-protocol.ts +101 -0
- package/src/tools/browser/tab-supervisor.ts +429 -0
- package/src/tools/browser/tab-worker-entry.ts +21 -0
- package/src/tools/browser/tab-worker.ts +1006 -0
- package/src/tools/browser.ts +12 -29
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/{python.ts → eval.ts} +324 -315
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/index.ts +62 -100
- package/src/tools/read.ts +0 -6
- package/src/tools/recipe/runners/pkg.ts +34 -32
- package/src/tools/renderers.ts +2 -2
- package/src/tools/resolve.ts +7 -2
- package/src/tools/todo-write.ts +0 -1
- package/src/tools/tool-timeouts.ts +2 -2
- package/src/utils/markit.ts +15 -7
- package/src/utils/tools-manager.ts +5 -5
- package/src/web/search/index.ts +5 -5
- package/src/web/search/provider.ts +121 -39
- package/src/web/search/providers/gemini.ts +2 -2
- package/src/web/search/render.ts +2 -2
- package/src/ipy/modules.ts +0 -144
- package/src/prompts/tools/python.md +0 -57
- package/src/tools/browser/vm.ts +0 -792
- /package/src/{ipy → eval/py}/cancellation.ts +0 -0
- /package/src/{ipy → eval/py}/prelude.ts +0 -0
- /package/src/{ipy → eval/py}/runtime.ts +0 -0
|
@@ -68,6 +68,11 @@ import {
|
|
|
68
68
|
import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
|
|
69
69
|
import type { Settings, SkillsSettings } from "../config/settings";
|
|
70
70
|
import { normalizeDiff, normalizeToLF, ParseError, previewPatch, stripBom } from "../edit";
|
|
71
|
+
import {
|
|
72
|
+
disposeKernelSessionsByOwner,
|
|
73
|
+
executePython as executePythonCommand,
|
|
74
|
+
type PythonResult,
|
|
75
|
+
} from "../eval/py/executor";
|
|
71
76
|
import { type BashResult, executeBash as executeBashCommand } from "../exec/bash-executor";
|
|
72
77
|
import { exportSessionToHtml } from "../export/html";
|
|
73
78
|
import type { TtsrManager, TtsrMatchContext } from "../export/ttsr";
|
|
@@ -98,11 +103,6 @@ import type { HookCommandContext } from "../extensibility/hooks/types";
|
|
|
98
103
|
import type { Skill, SkillWarning } from "../extensibility/skills";
|
|
99
104
|
import { expandSlashCommand, type FileSlashCommand } from "../extensibility/slash-commands";
|
|
100
105
|
import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
101
|
-
import {
|
|
102
|
-
disposeKernelSessionsByOwner,
|
|
103
|
-
executePython as executePythonCommand,
|
|
104
|
-
type PythonResult,
|
|
105
|
-
} from "../ipy/executor";
|
|
106
106
|
import {
|
|
107
107
|
buildDiscoverableMCPSearchIndex,
|
|
108
108
|
collectDiscoverableMCPTools,
|
|
@@ -259,7 +259,7 @@ export interface AgentSessionConfig {
|
|
|
259
259
|
/** Secret obfuscator for deobfuscating streaming edit content */
|
|
260
260
|
obfuscator?: SecretObfuscator;
|
|
261
261
|
/** Logical owner for retained Python kernels created by this session. */
|
|
262
|
-
|
|
262
|
+
evalKernelOwnerId?: string;
|
|
263
263
|
/** Agent identity (registry id like "0-Main" or "3-Alice") used for IRC routing. */
|
|
264
264
|
agentId?: string;
|
|
265
265
|
/** Shared agent registry (for forwarding IRC observations to the main session UI). */
|
|
@@ -474,11 +474,11 @@ export class AgentSession {
|
|
|
474
474
|
#pendingBashMessages: BashExecutionMessage[] = [];
|
|
475
475
|
|
|
476
476
|
// Python execution state
|
|
477
|
-
#
|
|
478
|
-
#
|
|
477
|
+
#evalAbortControllers = new Set<AbortController>();
|
|
478
|
+
#evalKernelOwnerId: string;
|
|
479
479
|
#pendingPythonMessages: PythonExecutionMessage[] = [];
|
|
480
|
-
#
|
|
481
|
-
#
|
|
480
|
+
#activeEvalExecutions = new Set<Promise<unknown>>();
|
|
481
|
+
#evalExecutionDisposing = false;
|
|
482
482
|
|
|
483
483
|
// Background-channel IRC exchanges queued while the recipient was streaming.
|
|
484
484
|
// Drained into history (via emitExternalEvent) once the recipient becomes idle.
|
|
@@ -577,7 +577,7 @@ export class AgentSession {
|
|
|
577
577
|
this.settings = config.settings;
|
|
578
578
|
this.#startPowerAssertion();
|
|
579
579
|
this.#asyncJobManager = config.asyncJobManager;
|
|
580
|
-
this.#
|
|
580
|
+
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
581
581
|
this.#scopedModels = config.scopedModels ?? [];
|
|
582
582
|
this.#thinkingLevel = config.thinkingLevel;
|
|
583
583
|
this.#promptTemplates = config.promptTemplates ?? [];
|
|
@@ -1938,7 +1938,7 @@ export class AgentSession {
|
|
|
1938
1938
|
* Call this when completely done with the session.
|
|
1939
1939
|
*/
|
|
1940
1940
|
async dispose(): Promise<void> {
|
|
1941
|
-
this.#
|
|
1941
|
+
this.#evalExecutionDisposing = true;
|
|
1942
1942
|
try {
|
|
1943
1943
|
if (this.#extensionRunner?.hasHandlers("session_shutdown")) {
|
|
1944
1944
|
await this.#extensionRunner.emit({ type: "session_shutdown" });
|
|
@@ -1953,13 +1953,13 @@ export class AgentSession {
|
|
|
1953
1953
|
if (drained === false && deliveryState) {
|
|
1954
1954
|
logger.warn("Async job completion deliveries still pending during dispose", { ...deliveryState });
|
|
1955
1955
|
}
|
|
1956
|
-
const pythonExecutionsSettled = await this.#
|
|
1956
|
+
const pythonExecutionsSettled = await this.#prepareEvalExecutionsForDispose();
|
|
1957
1957
|
if (!pythonExecutionsSettled) {
|
|
1958
1958
|
logger.warn(
|
|
1959
1959
|
"Detaching retained Python kernel ownership during dispose while Python execution is still active",
|
|
1960
1960
|
);
|
|
1961
1961
|
}
|
|
1962
|
-
await disposeKernelSessionsByOwner(this.#
|
|
1962
|
+
await disposeKernelSessionsByOwner(this.#evalKernelOwnerId);
|
|
1963
1963
|
this.#stopPowerAssertion();
|
|
1964
1964
|
await this.sessionManager.close();
|
|
1965
1965
|
this.#closeAllProviderSessions("dispose");
|
|
@@ -3423,7 +3423,7 @@ export class AgentSession {
|
|
|
3423
3423
|
this.abortCompaction();
|
|
3424
3424
|
this.abortHandoff();
|
|
3425
3425
|
this.abortBash();
|
|
3426
|
-
this.
|
|
3426
|
+
this.abortEval();
|
|
3427
3427
|
const postPromptDrain = this.#cancelPostPromptTasks();
|
|
3428
3428
|
this.agent.abort();
|
|
3429
3429
|
await postPromptDrain;
|
|
@@ -5895,7 +5895,7 @@ export class AgentSession {
|
|
|
5895
5895
|
|
|
5896
5896
|
/**
|
|
5897
5897
|
* Execute Python code in the shared kernel.
|
|
5898
|
-
* Uses the same kernel session as
|
|
5898
|
+
* Uses the same kernel session as eval's Python backend, allowing collaborative editing.
|
|
5899
5899
|
* @param code The Python code to execute
|
|
5900
5900
|
* @param onChunk Optional streaming callback for output
|
|
5901
5901
|
* @param options.excludeFromContext If true, execution won't be sent to LLM ($$ prefix)
|
|
@@ -5907,7 +5907,7 @@ export class AgentSession {
|
|
|
5907
5907
|
): Promise<PythonResult> {
|
|
5908
5908
|
const excludeFromContext = options?.excludeFromContext === true;
|
|
5909
5909
|
const cwd = this.sessionManager.getCwd();
|
|
5910
|
-
this.
|
|
5910
|
+
this.assertEvalExecutionAllowed();
|
|
5911
5911
|
|
|
5912
5912
|
const abortController = new AbortController();
|
|
5913
5913
|
const execution = (async (): Promise<PythonResult> => {
|
|
@@ -5918,20 +5918,20 @@ export class AgentSession {
|
|
|
5918
5918
|
excludeFromContext,
|
|
5919
5919
|
cwd,
|
|
5920
5920
|
});
|
|
5921
|
-
this.
|
|
5921
|
+
this.assertEvalExecutionAllowed();
|
|
5922
5922
|
if (hookResult?.result) {
|
|
5923
5923
|
this.recordPythonResult(code, hookResult.result, options);
|
|
5924
5924
|
return hookResult.result;
|
|
5925
5925
|
}
|
|
5926
5926
|
}
|
|
5927
5927
|
|
|
5928
|
-
// Use the same session ID as
|
|
5928
|
+
// Use the same session ID as eval's Python backend for kernel sharing
|
|
5929
5929
|
const sessionFile = this.sessionManager.getSessionFile();
|
|
5930
5930
|
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${cwd}` : `cwd:${cwd}`;
|
|
5931
5931
|
const result = await executePythonCommand(code, {
|
|
5932
5932
|
cwd,
|
|
5933
5933
|
sessionId,
|
|
5934
|
-
kernelOwnerId: this.#
|
|
5934
|
+
kernelOwnerId: this.#evalKernelOwnerId,
|
|
5935
5935
|
kernelMode: this.settings.get("python.kernelMode"),
|
|
5936
5936
|
useSharedGateway: this.settings.get("python.sharedGateway"),
|
|
5937
5937
|
onChunk,
|
|
@@ -5940,11 +5940,11 @@ export class AgentSession {
|
|
|
5940
5940
|
this.recordPythonResult(code, result, options);
|
|
5941
5941
|
return result;
|
|
5942
5942
|
})();
|
|
5943
|
-
return await this.
|
|
5943
|
+
return await this.trackEvalExecution(execution, abortController);
|
|
5944
5944
|
}
|
|
5945
5945
|
|
|
5946
|
-
|
|
5947
|
-
if (this.#
|
|
5946
|
+
assertEvalExecutionAllowed(): void {
|
|
5947
|
+
if (this.#evalExecutionDisposing) {
|
|
5948
5948
|
throw new Error("Python execution is unavailable while session disposal is in progress");
|
|
5949
5949
|
}
|
|
5950
5950
|
}
|
|
@@ -5952,17 +5952,17 @@ export class AgentSession {
|
|
|
5952
5952
|
/**
|
|
5953
5953
|
* Track Python work started outside AgentSession.executePython so dispose can await and abort it too.
|
|
5954
5954
|
*/
|
|
5955
|
-
|
|
5956
|
-
this.#
|
|
5957
|
-
this.#
|
|
5955
|
+
trackEvalExecution<T>(execution: Promise<T>, abortController: AbortController): Promise<T> {
|
|
5956
|
+
this.#evalAbortControllers.add(abortController);
|
|
5957
|
+
this.#activeEvalExecutions.add(execution);
|
|
5958
5958
|
void execution.then(
|
|
5959
5959
|
() => {
|
|
5960
|
-
this.#
|
|
5961
|
-
this.#
|
|
5960
|
+
this.#evalAbortControllers.delete(abortController);
|
|
5961
|
+
this.#activeEvalExecutions.delete(execution);
|
|
5962
5962
|
},
|
|
5963
5963
|
() => {
|
|
5964
|
-
this.#
|
|
5965
|
-
this.#
|
|
5964
|
+
this.#evalAbortControllers.delete(abortController);
|
|
5965
|
+
this.#activeEvalExecutions.delete(execution);
|
|
5966
5966
|
},
|
|
5967
5967
|
);
|
|
5968
5968
|
return execution;
|
|
@@ -5997,35 +5997,35 @@ export class AgentSession {
|
|
|
5997
5997
|
/**
|
|
5998
5998
|
* Cancel running Python execution.
|
|
5999
5999
|
*/
|
|
6000
|
-
|
|
6001
|
-
for (const abortController of this.#
|
|
6000
|
+
abortEval(): void {
|
|
6001
|
+
for (const abortController of this.#evalAbortControllers) {
|
|
6002
6002
|
abortController.abort();
|
|
6003
6003
|
}
|
|
6004
6004
|
}
|
|
6005
6005
|
|
|
6006
|
-
async #
|
|
6006
|
+
async #waitForEvalExecutionsToSettle(timeoutMs: number): Promise<boolean> {
|
|
6007
6007
|
const deadline = Date.now() + timeoutMs;
|
|
6008
|
-
while (this.#
|
|
6008
|
+
while (this.#activeEvalExecutions.size > 0) {
|
|
6009
6009
|
const remainingMs = deadline - Date.now();
|
|
6010
6010
|
if (remainingMs <= 0) {
|
|
6011
6011
|
return false;
|
|
6012
6012
|
}
|
|
6013
6013
|
const settled = await Promise.race([
|
|
6014
|
-
Promise.allSettled(Array.from(this.#
|
|
6014
|
+
Promise.allSettled(Array.from(this.#activeEvalExecutions)).then(() => true),
|
|
6015
6015
|
Bun.sleep(remainingMs).then(() => false),
|
|
6016
6016
|
]);
|
|
6017
|
-
if (!settled && this.#
|
|
6017
|
+
if (!settled && this.#activeEvalExecutions.size > 0) {
|
|
6018
6018
|
return false;
|
|
6019
6019
|
}
|
|
6020
6020
|
}
|
|
6021
6021
|
return true;
|
|
6022
6022
|
}
|
|
6023
6023
|
|
|
6024
|
-
async #
|
|
6025
|
-
if (!(await this.#
|
|
6024
|
+
async #prepareEvalExecutionsForDispose(): Promise<boolean> {
|
|
6025
|
+
if (!(await this.#waitForEvalExecutionsToSettle(3_000))) {
|
|
6026
6026
|
logger.warn("Aborting active Python execution during dispose before retained kernel cleanup");
|
|
6027
|
-
this.
|
|
6028
|
-
if (!(await this.#
|
|
6027
|
+
this.abortEval();
|
|
6028
|
+
if (!(await this.#waitForEvalExecutionsToSettle(1_000))) {
|
|
6029
6029
|
logger.warn(
|
|
6030
6030
|
"Python execution is still active after dispose aborted all active runs; retained kernel ownership will still be detached",
|
|
6031
6031
|
);
|
|
@@ -6036,8 +6036,8 @@ export class AgentSession {
|
|
|
6036
6036
|
}
|
|
6037
6037
|
|
|
6038
6038
|
/** Whether a Python execution is currently running */
|
|
6039
|
-
get
|
|
6040
|
-
return this.#
|
|
6039
|
+
get isEvalRunning(): boolean {
|
|
6040
|
+
return this.#evalAbortControllers.size > 0;
|
|
6041
6041
|
}
|
|
6042
6042
|
|
|
6043
6043
|
/** Whether there are pending Python messages waiting to be flushed */
|
package/src/session/messages.ts
CHANGED
|
@@ -59,7 +59,7 @@ export interface BashExecutionMessage {
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Message type for user-initiated Python executions via the $ command.
|
|
62
|
-
* Shares the same kernel session as
|
|
62
|
+
* Shares the same kernel session as eval's Python backend.
|
|
63
63
|
*/
|
|
64
64
|
export interface PythonExecutionMessage {
|
|
65
65
|
role: "pythonExecution";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as os from "node:os";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
|
-
import { getOAuthProviders } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/utils/oauth";
|
|
5
5
|
import { getConfigDirName } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { invalidate as invalidateFsCache } from "../capability/fs";
|
|
7
7
|
import type { SettingPath, SettingValue } from "../config/settings";
|
package/src/system-prompt.ts
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
* System prompt construction and project context loading
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import * as fs from "node:fs";
|
|
6
5
|
import * as os from "node:os";
|
|
7
|
-
import * as path from "node:path";
|
|
8
6
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
7
|
+
import { FileType, glob } from "@oh-my-pi/pi-natives";
|
|
9
8
|
import { $env, getGpuCachePath, getProjectDir, hasFsCode, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
10
9
|
import { $ } from "bun";
|
|
11
10
|
import { contextFileCapability } from "./capability/context-file";
|
|
@@ -89,81 +88,44 @@ const AGENTS_MD_LIMIT = 200;
|
|
|
89
88
|
const SYSTEM_PROMPT_PREP_TIMEOUT_MS = 5000;
|
|
90
89
|
const AGENTS_MD_EXCLUDED_DIRS = new Set(["node_modules", ".git"]);
|
|
91
90
|
|
|
92
|
-
interface AgentsMdSearch {
|
|
91
|
+
export interface AgentsMdSearch {
|
|
93
92
|
scopePath: string;
|
|
94
93
|
limit: number;
|
|
95
94
|
pattern: string;
|
|
96
95
|
files: string[];
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
function normalizePath(value: string): string {
|
|
100
|
-
return value.replace(/\\/g, "/");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function shouldSkipAgentsDir(name: string): boolean {
|
|
104
|
-
if (AGENTS_MD_EXCLUDED_DIRS.has(name)) return true;
|
|
105
|
-
return name.startsWith(".");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function collectAgentsMdFiles(
|
|
109
|
-
root: string,
|
|
110
|
-
dir: string,
|
|
111
|
-
depth: number,
|
|
112
|
-
limit: number,
|
|
113
|
-
discovered: Set<string>,
|
|
114
|
-
): Promise<void> {
|
|
115
|
-
if (depth > AGENTS_MD_MAX_DEPTH || discovered.size >= limit) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let entries: fs.Dirent[];
|
|
120
|
-
try {
|
|
121
|
-
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
122
|
-
} catch {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (depth >= AGENTS_MD_MIN_DEPTH) {
|
|
127
|
-
const hasAgentsMd = entries.some(entry => entry.isFile() && entry.name === "AGENTS.md");
|
|
128
|
-
if (hasAgentsMd) {
|
|
129
|
-
const relPath = normalizePath(path.relative(root, path.join(dir, "AGENTS.md")));
|
|
130
|
-
if (relPath.length > 0) {
|
|
131
|
-
discovered.add(relPath);
|
|
132
|
-
}
|
|
133
|
-
if (discovered.size >= limit) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (depth === AGENTS_MD_MAX_DEPTH) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const childDirs = entries
|
|
144
|
-
.filter(entry => entry.isDirectory() && !shouldSkipAgentsDir(entry.name))
|
|
145
|
-
.map(entry => entry.name)
|
|
146
|
-
.sort();
|
|
147
|
-
|
|
148
|
-
await Promise.all(
|
|
149
|
-
childDirs.map(async child => {
|
|
150
|
-
if (discovered.size >= limit) return;
|
|
151
|
-
await collectAgentsMdFiles(root, path.join(dir, child), depth + 1, limit, discovered);
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
98
|
async function listAgentsMdFiles(root: string, limit: number): Promise<string[]> {
|
|
157
99
|
try {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
100
|
+
const result = await glob({
|
|
101
|
+
pattern: "**/AGENTS.md",
|
|
102
|
+
path: root,
|
|
103
|
+
fileType: FileType.File,
|
|
104
|
+
recursive: true,
|
|
105
|
+
hidden: false,
|
|
106
|
+
gitignore: true,
|
|
107
|
+
maxResults: limit * 4,
|
|
108
|
+
cache: true,
|
|
109
|
+
});
|
|
110
|
+
const files: string[] = [];
|
|
111
|
+
for (const m of result.matches) {
|
|
112
|
+
const rel = m.path.replace(/\\/g, "/");
|
|
113
|
+
if (!rel?.endsWith("AGENTS.md")) continue;
|
|
114
|
+
const segments = rel.split("/");
|
|
115
|
+
const depth = segments.length - 1;
|
|
116
|
+
if (depth < AGENTS_MD_MIN_DEPTH || depth > AGENTS_MD_MAX_DEPTH) continue;
|
|
117
|
+
const dirSegments = segments.slice(0, -1);
|
|
118
|
+
if (dirSegments.some(seg => AGENTS_MD_EXCLUDED_DIRS.has(seg) || seg.startsWith("."))) continue;
|
|
119
|
+
files.push(rel);
|
|
120
|
+
if (files.length >= limit) break;
|
|
121
|
+
}
|
|
122
|
+
return Array.from(new Set(files)).sort().slice(0, limit);
|
|
161
123
|
} catch {
|
|
162
124
|
return [];
|
|
163
125
|
}
|
|
164
126
|
}
|
|
165
127
|
|
|
166
|
-
async function buildAgentsMdSearch(cwd: string): Promise<AgentsMdSearch> {
|
|
128
|
+
export async function buildAgentsMdSearch(cwd: string): Promise<AgentsMdSearch> {
|
|
167
129
|
const files = await listAgentsMdFiles(cwd, AGENTS_MD_LIMIT);
|
|
168
130
|
return {
|
|
169
131
|
scopePath: ".",
|
|
@@ -445,6 +407,8 @@ export interface BuildSystemPromptOptions {
|
|
|
445
407
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
446
408
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
447
409
|
secretsEnabled?: boolean;
|
|
410
|
+
/** Pre-loaded AGENTS.md search (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
411
|
+
agentsMdSearch?: AgentsMdSearch | Promise<AgentsMdSearch>;
|
|
448
412
|
}
|
|
449
413
|
|
|
450
414
|
/** Build the system prompt with tools, guidelines, and context */
|
|
@@ -470,6 +434,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
470
434
|
mcpDiscoveryServerSummaries = [],
|
|
471
435
|
eagerTasks = false,
|
|
472
436
|
secretsEnabled = false,
|
|
437
|
+
agentsMdSearch: providedAgentsMdSearch,
|
|
473
438
|
} = options;
|
|
474
439
|
const resolvedCwd = cwd ?? getProjectDir();
|
|
475
440
|
|
|
@@ -480,7 +445,10 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
480
445
|
const contextFilesPromise = providedContextFiles
|
|
481
446
|
? Promise.resolve(providedContextFiles)
|
|
482
447
|
: logger.time("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
|
|
483
|
-
const agentsMdSearchPromise =
|
|
448
|
+
const agentsMdSearchPromise =
|
|
449
|
+
providedAgentsMdSearch !== undefined
|
|
450
|
+
? Promise.resolve(providedAgentsMdSearch)
|
|
451
|
+
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
|
|
484
452
|
const skillsPromise: Promise<Skill[]> =
|
|
485
453
|
providedSkills !== undefined
|
|
486
454
|
? Promise.resolve(providedSkills)
|
|
@@ -572,7 +540,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
572
540
|
toolNames = Array.from(tools.keys());
|
|
573
541
|
} else {
|
|
574
542
|
// Use defaults
|
|
575
|
-
toolNames = ["read", "bash", "
|
|
543
|
+
toolNames = ["read", "bash", "eval", "edit", "write"]; // TODO: Why?
|
|
576
544
|
}
|
|
577
545
|
}
|
|
578
546
|
|
package/src/task/executor.ts
CHANGED
|
@@ -532,16 +532,12 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
532
532
|
if (atMaxDepth && toolNames?.includes("task")) {
|
|
533
533
|
toolNames = toolNames.filter(name => name !== "task");
|
|
534
534
|
}
|
|
535
|
-
const pythonToolMode = settings.get("python.toolMode") ?? "both";
|
|
536
535
|
if (toolNames?.includes("exec")) {
|
|
536
|
+
const allowEvalPy = settings.get("eval.py") ?? true;
|
|
537
|
+
const allowEvalJs = settings.get("eval.js") ?? true;
|
|
537
538
|
const expanded = toolNames.filter(name => name !== "exec");
|
|
538
|
-
if (
|
|
539
|
-
|
|
540
|
-
} else if (pythonToolMode === "ipy-only") {
|
|
541
|
-
expanded.push("python");
|
|
542
|
-
} else {
|
|
543
|
-
expanded.push("python", "bash");
|
|
544
|
-
}
|
|
539
|
+
if (allowEvalPy || allowEvalJs) expanded.push("eval");
|
|
540
|
+
expanded.push("bash");
|
|
545
541
|
toolNames = Array.from(new Set(expanded));
|
|
546
542
|
}
|
|
547
543
|
|
|
@@ -557,7 +553,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
557
553
|
|
|
558
554
|
const lspEnabled = enableLsp ?? true;
|
|
559
555
|
const ircEnabled = subagentSettings.get("irc.enabled") === true;
|
|
560
|
-
const skipPythonPreflight = Array.isArray(toolNames) && !toolNames.includes("
|
|
556
|
+
const skipPythonPreflight = Array.isArray(toolNames) && !toolNames.includes("eval");
|
|
561
557
|
|
|
562
558
|
const outputChunks: string[] = [];
|
|
563
559
|
const finalOutputChunks: string[] = [];
|
|
@@ -22,6 +22,14 @@ import stealthWorkerScript from "../puppeteer/13_stealth_worker.txt" with { type
|
|
|
22
22
|
import { ToolError } from "../tool-errors";
|
|
23
23
|
|
|
24
24
|
export const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1.25 };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Per-CDP-message timeout applied to every puppeteer launch/connect. Set above
|
|
28
|
+
* `TOOL_TIMEOUTS.browser.max` (30s) so the agent-side wall-clock is the canonical
|
|
29
|
+
* limit; this constant only catches genuinely stuck CDP sockets (renderer wedged,
|
|
30
|
+
* connection dropped, etc.).
|
|
31
|
+
*/
|
|
32
|
+
export const BROWSER_PROTOCOL_TIMEOUT_MS = 60_000;
|
|
25
33
|
export const STEALTH_IGNORE_DEFAULT_ARGS = [
|
|
26
34
|
"--disable-extensions",
|
|
27
35
|
"--disable-default-apps",
|
|
@@ -55,6 +63,19 @@ export async function loadPuppeteer(): Promise<typeof Puppeteer> {
|
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
66
|
+
let puppeteerModuleWorker: typeof Puppeteer | undefined;
|
|
67
|
+
export async function loadPuppeteerInWorker(safeDir: string): Promise<typeof Puppeteer> {
|
|
68
|
+
if (puppeteerModuleWorker) return puppeteerModuleWorker;
|
|
69
|
+
const orig = process.cwd;
|
|
70
|
+
Object.defineProperty(process, "cwd", { value: () => safeDir, configurable: true });
|
|
71
|
+
try {
|
|
72
|
+
puppeteerModuleWorker = (await import("puppeteer-core")).default;
|
|
73
|
+
return puppeteerModuleWorker;
|
|
74
|
+
} finally {
|
|
75
|
+
Object.defineProperty(process, "cwd", { value: orig, configurable: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
58
79
|
/**
|
|
59
80
|
* Lazily download Chromium on first browser launch via @puppeteer/browsers.
|
|
60
81
|
* Skipped when a system Chromium (NixOS) or PUPPETEER_EXECUTABLE_PATH is set.
|
|
@@ -243,6 +264,7 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
|
|
|
243
264
|
executablePath: await ensureChromiumExecutable(),
|
|
244
265
|
args: launchArgs,
|
|
245
266
|
ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS],
|
|
267
|
+
protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
|
|
246
268
|
});
|
|
247
269
|
}
|
|
248
270
|
|