@epoch-agent/core 2.7.0 → 3.0.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.
Files changed (4) hide show
  1. package/README.md +114 -5
  2. package/dist/index.d.ts +962 -168
  3. package/dist/index.js +1686 -674
  4. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { REASONING_EFFORTS, MODEL_ENTRY_PROTOCOLS, PROVIDER_TYPES, isModelEntryId, parseModelRef, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, BRAND_WHEN_SIDEBAR_COLLAPSED, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure, MIN_OPTIONS, MAX_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, entrySecretName, isEntrySecretName, entryRef, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, normalizeAppearanceTranslucent, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, SCHEDULE_DRAFT_SAVED_MARK, imagePartOmittedSummary, ENTRY_REF_PREFIX, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
2
- export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval } from '@epoch-agent/protocol';
3
- import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync, renameSync, realpathSync, symlinkSync, cpSync, lstatSync, unlinkSync, rmdirSync, mkdtempSync, copyFileSync, openSync, writeSync, closeSync, readSync } from 'fs';
4
- import { homedir, platform, tmpdir, hostname } from 'os';
5
- import { join, dirname, resolve, relative, isAbsolute, extname, sep, basename } from 'path';
6
- import { execFile, execSync, execFileSync, spawn } from 'child_process';
7
- import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, expandPath, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, isInWorkspace, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, listStoredSecretNames, maskApiKey, unsetSectionField, setSectionField, countCodePoints, headCodePoints, attachmentsDir, workspacesPath, samePath, lastSessionsPath, appearancePath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, getSecretValues, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
1
+ import { REASONING_EFFORTS, MODEL_ENTRY_PROTOCOLS, PROVIDER_TYPES, isModelEntryId, parseModelRef, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, BRAND_WHEN_SIDEBAR_COLLAPSED, DEFAULT_PERMISSION_LEVEL, resolvePermissionLevel, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure, MIN_OPTIONS, MAX_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, SCHEDULE_DEFAULT_PERMISSION, approvalPolicyForLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, OFFLOADED_IMAGE_NOTE, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, entrySecretName, isEntrySecretName, entryRef, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, normalizeAppearanceTranslucent, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, SCHEDULE_DRAFT_SAVED_MARK, imagePartOmittedSummary, ENTRY_REF_PREFIX, placeholdersOf, SCHEDULE_CONFIRM_PLACEHOLDER, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
2
+ export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, LEGACY_LEVEL_ALIASES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, approvalPolicyForLevel, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval, resolvePermissionLevel } from '@epoch-agent/protocol';
3
+ import { existsSync, mkdirSync, rmSync, writeFileSync, statSync, readdirSync, readFileSync, renameSync, realpathSync, symlinkSync, cpSync, lstatSync, unlinkSync, rmdirSync, mkdtempSync, copyFileSync, openSync, writeSync, closeSync, readSync } from 'fs';
4
+ import { platform, tmpdir, homedir, hostname } from 'os';
5
+ import { join, resolve, dirname, relative, isAbsolute, extname, sep, basename, delimiter } from 'path';
6
+ import { execFile, spawn, execSync, execFileSync } from 'child_process';
7
+ import { createLogger, t, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, registerJob, JOB_OUTPUT_LIMIT, artifactsDir, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, expandPath, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, isInWorkspace, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, listStoredSecretNames, maskApiKey, unsetSectionField, setSectionField, countCodePoints, headCodePoints, attachmentsDir, workspacesPath, samePath, lastSessionsPath, appearancePath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, parseShellCommand, writeArtifact, sandboxModeForLevel, planEscalation, getSecretValues, envPath, checkDangerousCommand, checkObfuscation, shellSpawnArgs, confine, classifyFailure, keepHeadAndTail, SANDBOX_PERMISSIONS_PARAM, JUSTIFICATION_PARAM, widerTargets, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
8
8
  export { IS_WINDOWS, WINDOWS_HIDE_FLAGS, buildBwrapArgs, buildProfile, checkDangerousCommand, checkObfuscation, createLogger, detectBackend, getDefaultShell, getPythonCommand, isInWorkspace, isReadOnlyCommand, isolate, maskApiKey, probeBubblewrap, probeSeatbelt, complianceDir as resolveComplianceDir, policiesDir as resolvePoliciesDir, setLogLevel } from '@epoch-agent/infra';
9
9
  import { createRequire } from 'module';
10
10
  import { randomUUID, createHash, randomBytes } from 'crypto';
@@ -932,7 +932,7 @@ var ToolRegistry = class {
932
932
  this.addTool(tool, source);
933
933
  }
934
934
  registerTools(tools, source = "builtin") {
935
- for (const t81 of tools) this.addTool(t81, source);
935
+ for (const t83 of tools) this.addTool(t83, source);
936
936
  }
937
937
  addTool(tool, source) {
938
938
  const existing = this.tools.get(tool.name);
@@ -942,6 +942,30 @@ var ToolRegistry = class {
942
942
  }
943
943
  this.tools.set(tool.name, { tool, source });
944
944
  }
945
+ replaceBySource(tools, source = "builtin") {
946
+ const incoming = /* @__PURE__ */ new Map();
947
+ for (const tool of tools) {
948
+ if (!incoming.has(tool.name)) incoming.set(tool.name, tool);
949
+ }
950
+ let removed = 0;
951
+ for (const [name, entry] of this.tools) {
952
+ if (entry.source !== source || incoming.has(name)) continue;
953
+ this.tools.delete(name);
954
+ removed++;
955
+ }
956
+ this.conflicts = this.conflicts.filter(
957
+ (c) => c.keptFrom !== source && c.droppedFrom !== source
958
+ );
959
+ for (const tool of incoming.values()) {
960
+ const existing = this.tools.get(tool.name);
961
+ if (existing && existing.source === source) {
962
+ this.tools.set(tool.name, { tool, source });
963
+ continue;
964
+ }
965
+ this.addTool(tool, source);
966
+ }
967
+ return removed;
968
+ }
945
969
  unregisterBySource(source) {
946
970
  let removed = 0;
947
971
  for (const [name, entry] of this.tools) {
@@ -981,14 +1005,14 @@ var ToolRegistry = class {
981
1005
  return isCodeModeDeferred(tool) ? ToolExposure.Deferred : declared;
982
1006
  }
983
1007
  exposed() {
984
- return this.list().filter((t81) => this.exposureOf(t81) === ToolExposure.Direct);
1008
+ return this.list().filter((t83) => this.exposureOf(t83) === ToolExposure.Direct);
985
1009
  }
986
1010
  byExposure(exposure) {
987
- return this.list().filter((t81) => this.exposureOf(t81) === exposure);
1011
+ return this.list().filter((t83) => this.exposureOf(t83) === exposure);
988
1012
  }
989
1013
  codeModeDeclared() {
990
1014
  if (!this.codeMode) return [];
991
- return this.list().filter((t81) => isCodeModeDeferred(t81));
1015
+ return this.list().filter((t83) => isCodeModeDeferred(t83));
992
1016
  }
993
1017
  sourceOf(name) {
994
1018
  return this.tools.get(name)?.source;
@@ -1037,7 +1061,7 @@ var ToolRegistry = class {
1037
1061
  function staticToolProvider(tools) {
1038
1062
  return {
1039
1063
  list: () => tools,
1040
- get: (name) => tools.find((t81) => t81.name === name)
1064
+ get: (name) => tools.find((t83) => t83.name === name)
1041
1065
  };
1042
1066
  }
1043
1067
  var log = createLogger("tokenizer");
@@ -1054,8 +1078,8 @@ function fallbackCount(text) {
1054
1078
  return Math.ceil(text.length / 4);
1055
1079
  }
1056
1080
  function encoderFor(encoding) {
1057
- const cached = encoders.get(encoding);
1058
- if (cached) return cached;
1081
+ const cached2 = encoders.get(encoding);
1082
+ if (cached2) return cached2;
1059
1083
  const entry = { counter: null, memo: /* @__PURE__ */ new Map(), memoChars: 0 };
1060
1084
  try {
1061
1085
  const mod = loadEncoding(encoding);
@@ -1339,6 +1363,285 @@ function createAskQuestionTool(deps) {
1339
1363
  function isAbortSignal(signal) {
1340
1364
  return !!signal && typeof signal.addEventListener === "function" && typeof signal.removeEventListener === "function";
1341
1365
  }
1366
+ function describeIsolation() {
1367
+ const backend = detectBackend();
1368
+ const os = platform();
1369
+ if (backend === "seatbelt") {
1370
+ return {
1371
+ level: "os",
1372
+ backend,
1373
+ detail: t("sandbox_isolation.seatbelt"),
1374
+ reason: "os-isolated",
1375
+ platform: os
1376
+ };
1377
+ }
1378
+ if (backend === "bubblewrap") {
1379
+ return {
1380
+ level: "os",
1381
+ backend,
1382
+ detail: t("sandbox_isolation.bubblewrap"),
1383
+ reason: "os-isolated",
1384
+ platform: os
1385
+ };
1386
+ }
1387
+ const [why, reason9] = os === "darwin" ? [t("sandbox_isolation.no_seatbelt"), "backend-unavailable"] : os === "linux" ? [t("sandbox_isolation.no_bubblewrap"), "backend-missing"] : (
1388
+ [t("sandbox_isolation.by_design", { platform: os }), "platform-unsupported"]
1389
+ );
1390
+ return {
1391
+ level: "process",
1392
+ backend,
1393
+ detail: t("sandbox_isolation.process_only", { why }),
1394
+ reason: reason9,
1395
+ platform: os
1396
+ };
1397
+ }
1398
+ var SANDBOX_COVERS = ["code_exec", "terminal"];
1399
+ var SANDBOX_EXCLUDES = [];
1400
+ // src/sandbox/node-permission.ts
1401
+ var NODE_PERMISSION_MIN = [22, 5];
1402
+ function nodePermissionSupported(platformName, nodeVersion) {
1403
+ if (platformName !== "win32") return false;
1404
+ const [major = Number.NaN, minor = Number.NaN] = nodeVersion.split(".").map((part) => Number.parseInt(part, 10));
1405
+ if (!Number.isFinite(major) || !Number.isFinite(minor)) return false;
1406
+ if (major > NODE_PERMISSION_MIN[0]) return true;
1407
+ if (major < NODE_PERMISSION_MIN[0]) return false;
1408
+ return minor >= NODE_PERMISSION_MIN[1];
1409
+ }
1410
+ function nodePermissionArgs(input) {
1411
+ if (!nodePermissionSupported(input.platformName, input.nodeVersion)) return [];
1412
+ const readable = [.../* @__PURE__ */ new Set([input.sandboxDir, ...input.readableDirs])];
1413
+ return [
1414
+ "--permission",
1415
+ `--allow-fs-read=${readable.join(",")}`,
1416
+ `--allow-fs-write=${input.sandboxDir}`
1417
+ ];
1418
+ }
1419
+ // src/sandbox/sandbox.ts
1420
+ var IS_WINDOWS = platform() === "win32";
1421
+ var SANDBOX_ROOT = join(tmpdir(), "epoch-sandbox");
1422
+ var DEFAULT_TIMEOUT = 3e4;
1423
+ var MAX_OUTPUT = 1e5;
1424
+ var MAX_MEMORY = 512;
1425
+ var TRUNCATE_HEAD = 2e3;
1426
+ var TRUNCATE_TAIL = 2e3;
1427
+ function resolveExecutable(name) {
1428
+ if (IS_WINDOWS || name.includes("/") || name.includes("\\")) return name;
1429
+ for (const dir of (process.env["PATH"] ?? "").split(delimiter)) {
1430
+ if (!dir) continue;
1431
+ const candidate = join(dir, name);
1432
+ if (existsSync(candidate)) return candidate;
1433
+ }
1434
+ return name;
1435
+ }
1436
+ var CodeSandbox = class {
1437
+ sandboxDir;
1438
+ readableDirs;
1439
+ constructor(sessionId, workDir) {
1440
+ const safeId = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
1441
+ this.sandboxDir = join(SANDBOX_ROOT, safeId);
1442
+ mkdirSync(this.sandboxDir, { recursive: true });
1443
+ this.readableDirs = [];
1444
+ if (workDir) {
1445
+ const abs = resolve(workDir);
1446
+ if (abs !== resolve(homedir()) && abs !== "/") this.readableDirs.push(abs);
1447
+ }
1448
+ }
1449
+ getIsolation() {
1450
+ return describeIsolation();
1451
+ }
1452
+ async execute(req) {
1453
+ const start = Date.now();
1454
+ const timeout = req.timeout ?? DEFAULT_TIMEOUT;
1455
+ const isolation = describeIsolation().level;
1456
+ try {
1457
+ const spec = req.language === "javascript" ? this.jsSpec(req.code) : this.pySpec(req.code);
1458
+ const result = await this.run(spec, {
1459
+ timeout,
1460
+ allowNetwork: req.allowNetwork === true,
1461
+ ...req.signal ? { signal: req.signal } : {}
1462
+ });
1463
+ return { ...result, isolation };
1464
+ } catch (err) {
1465
+ const msg = err instanceof Error ? err.message : String(err);
1466
+ if (msg.includes("timed out") || msg.includes("killed")) {
1467
+ return {
1468
+ success: false,
1469
+ output: "",
1470
+ error: `[\u8D85\u65F6] \u8D85\u8FC7 ${timeout / 1e3} \u79D2`,
1471
+ errorType: "timeout",
1472
+ duration: Date.now() - start,
1473
+ isolation
1474
+ };
1475
+ }
1476
+ return {
1477
+ success: false,
1478
+ output: "",
1479
+ error: `[\u9519\u8BEF] ${msg}`,
1480
+ errorType: "runtime",
1481
+ duration: Date.now() - start,
1482
+ isolation
1483
+ };
1484
+ }
1485
+ }
1486
+ destroy() {
1487
+ try {
1488
+ rmSync(this.sandboxDir, { recursive: true });
1489
+ } catch {
1490
+ }
1491
+ }
1492
+ getDir() {
1493
+ return this.sandboxDir;
1494
+ }
1495
+ jsSpec(code) {
1496
+ const tmpFile = join(this.sandboxDir, "code.js");
1497
+ writeFileSync(tmpFile, code, "utf-8");
1498
+ return {
1499
+ command: process.execPath,
1500
+ args: [
1501
+ `--max-old-space-size=${MAX_MEMORY}`,
1502
+ ...nodePermissionArgs({
1503
+ platformName: platform(),
1504
+ nodeVersion: process.versions.node,
1505
+ sandboxDir: this.sandboxDir,
1506
+ readableDirs: this.readableDirs
1507
+ }),
1508
+ tmpFile
1509
+ ],
1510
+ env: EXEC_PATH_AS_NODE_ENV
1511
+ };
1512
+ }
1513
+ pySpec(code) {
1514
+ const tmpFile = join(this.sandboxDir, "code.py");
1515
+ writeFileSync(tmpFile, code, "utf-8");
1516
+ return {
1517
+ command: resolveExecutable(IS_WINDOWS ? "python" : "python3"),
1518
+ args: ["-I", tmpFile]
1519
+ };
1520
+ }
1521
+ run(spec, opts) {
1522
+ const start = Date.now();
1523
+ const wrapped = isolate(spec.command, spec.args, {
1524
+ writableDirs: [this.sandboxDir, tmpdir()],
1525
+ readableDirs: this.readableDirs,
1526
+ allowNetwork: opts.allowNetwork,
1527
+ cwd: this.sandboxDir
1528
+ });
1529
+ const { command, args } = wrapped ?? spec;
1530
+ const spawnOpts = {
1531
+ cwd: this.sandboxDir,
1532
+ timeout: opts.timeout,
1533
+ env: {
1534
+ PATH: process.env["PATH"] ?? "",
1535
+ HOME: process.env["HOME"] ?? "",
1536
+ TMPDIR: this.sandboxDir,
1537
+ LANG: process.env["LANG"] ?? "en_US.UTF-8",
1538
+ ...spec.env
1539
+ },
1540
+ stdio: ["ignore", "pipe", "pipe"],
1541
+ ...IS_WINDOWS ? { windowsHide: true } : {}
1542
+ };
1543
+ return new Promise((resolve17) => {
1544
+ let stdout = "";
1545
+ let stderr = "";
1546
+ let killedForOutput = false;
1547
+ let child = null;
1548
+ const onAbort = () => {
1549
+ child?.kill("SIGKILL");
1550
+ };
1551
+ const failToStart = (err) => {
1552
+ opts.signal?.removeEventListener("abort", onAbort);
1553
+ if (err.code === "ENOENT") {
1554
+ resolve17({
1555
+ success: false,
1556
+ output: "",
1557
+ error: `[\u9519\u8BEF] \u627E\u4E0D\u5230\u89E3\u91CA\u5668: ${command}`,
1558
+ errorType: "compile",
1559
+ duration: Date.now() - start
1560
+ });
1561
+ return;
1562
+ }
1563
+ resolve17({
1564
+ success: false,
1565
+ output: "",
1566
+ error: `[\u9519\u8BEF] ${err.message}\uFF08command=${command}\uFF0Ccwd=${spawnOpts.cwd ?? ""}\uFF09`,
1567
+ errorType: "runtime",
1568
+ duration: Date.now() - start
1569
+ });
1570
+ };
1571
+ child = guardSpawn(() => spawn(command, args, spawnOpts), failToStart);
1572
+ if (!child) return;
1573
+ const proc = child;
1574
+ let outDecoder = null;
1575
+ let errDecoder = null;
1576
+ proc.stdout?.on("data", (c) => {
1577
+ if (killedForOutput) return;
1578
+ outDecoder ??= createStreamDecoder();
1579
+ stdout += outDecoder.write(c);
1580
+ if (stdout.length >= MAX_OUTPUT) {
1581
+ killedForOutput = true;
1582
+ stdout = stdout.slice(0, MAX_OUTPUT);
1583
+ proc.kill("SIGKILL");
1584
+ }
1585
+ });
1586
+ proc.stderr?.on("data", (c) => {
1587
+ if (stderr.length >= MAX_OUTPUT) return;
1588
+ errDecoder ??= createStreamDecoder();
1589
+ stderr += errDecoder.write(c);
1590
+ });
1591
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
1592
+ proc.on("error", failToStart);
1593
+ proc.on("close", (code, sig) => {
1594
+ opts.signal?.removeEventListener("abort", onAbort);
1595
+ if (!killedForOutput) stdout += outDecoder?.end() ?? "";
1596
+ if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
1597
+ const duration = Date.now() - start;
1598
+ if (killedForOutput) {
1599
+ resolve17({
1600
+ success: false,
1601
+ output: trunc(stdout, stderr),
1602
+ error: `[\u8F93\u51FA\u8D85\u9650] \u8D85\u8FC7 ${MAX_OUTPUT} \u5B57\u8282\uFF0C\u5DF2\u7EC8\u6B62`,
1603
+ errorType: "runtime",
1604
+ duration
1605
+ });
1606
+ return;
1607
+ }
1608
+ if (sig) {
1609
+ resolve17({
1610
+ success: false,
1611
+ output: trunc(stdout, stderr),
1612
+ error: opts.signal?.aborted ? "[\u5DF2\u53D6\u6D88]" : "[\u8D85\u65F6] \u6267\u884C\u8D85\u65F6",
1613
+ errorType: "timeout",
1614
+ duration
1615
+ });
1616
+ return;
1617
+ }
1618
+ const output = trunc(stdout, stderr);
1619
+ if (code !== 0 && code !== null) {
1620
+ const oom = /heap out of memory|allocation failed/i.test(stderr);
1621
+ resolve17({
1622
+ success: false,
1623
+ output,
1624
+ error: oom ? `[\u5185\u5B58\u8D85\u9650] \u8D85\u8FC7 ${MAX_MEMORY}MB` : `[\u8FD0\u884C\u65F6\u9519\u8BEF] exit ${code}: ${stderr.slice(0, 200)}`,
1625
+ errorType: oom ? "oom" : "runtime",
1626
+ duration
1627
+ });
1628
+ return;
1629
+ }
1630
+ resolve17({ success: true, output, duration });
1631
+ });
1632
+ });
1633
+ }
1634
+ };
1635
+ function trunc(stdout, stderr) {
1636
+ const nl = "\n";
1637
+ let out = stdout;
1638
+ if (stderr) out += (out ? nl : "") + stderr;
1639
+ if (out.length <= MAX_OUTPUT) return out || "(\u65E0\u8F93\u51FA)";
1640
+ const head = out.slice(0, TRUNCATE_HEAD);
1641
+ const tail = out.slice(-TRUNCATE_TAIL);
1642
+ const skipped = out.length - TRUNCATE_HEAD - TRUNCATE_TAIL;
1643
+ return `${head}${nl}...[\u5DF2\u622A\u65AD ${(skipped / 1024).toFixed(1)} KB]...${nl}${tail}`;
1644
+ }
1342
1645
  // src/tools/builtin/code-exec.ts
1343
1646
  var MAX_CODE_LENGTH = 1e5;
1344
1647
  function createCodeExecTool(sandbox) {
@@ -1349,7 +1652,9 @@ function createCodeExecTool(sandbox) {
1349
1652
  "\u5728\u9694\u79BB\u73AF\u5883\u91CC\u6267\u884C\u4E00\u6BB5 JavaScript \u6216 Python\uFF0C\u8FD4\u56DE stdout/stderr\u3002",
1350
1653
  "\u9002\u5408\uFF1A\u7B97\u6570\u503C\u3001\u9A8C\u8BC1\u4E00\u6BB5\u903B\u8F91\u3001\u5904\u7406\u6570\u636E\u3001\u5FEB\u901F\u8BD5\u8BED\u6CD5\u3002",
1351
1654
  `\u5F53\u524D\u9694\u79BB\u7EA7\u522B\uFF1A${iso.level === "os" ? "OS \u7EA7" : "\u4EC5\u8FDB\u7A0B\u7EA7"}\u2014\u2014${iso.detail}`,
1352
- iso.level === "os" ? "\u9694\u79BB\u5F71\u54CD\uFF1A\u9ED8\u8BA4\u65E0\u7F51\u7EDC\uFF1B\u786E\u5B9E\u9700\u8981\u8054\u7F51\u65F6\u4F20 allow_network=true\uFF08\u4F1A\u653E\u5F00\u7F51\u7EDC\u9650\u5236\uFF09\u3002" : "\u9694\u79BB\u5F71\u54CD\uFF1A\u672C\u5E73\u53F0\u6CA1\u6709 OS \u7EA7\u9694\u79BB\uFF0C\u4EE3\u7801\u80FD\u8BFB\u5199\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u5E76\u8054\u7F51\uFF0C\u522B\u6267\u884C\u4E0D\u53EF\u4FE1\u4EE3\u7801\u3002",
1655
+ iso.level === "os" ? "\u9694\u79BB\u5F71\u54CD\uFF1A\u9ED8\u8BA4\u65E0\u7F51\u7EDC\uFF1B\u786E\u5B9E\u9700\u8981\u8054\u7F51\u65F6\u4F20 allow_network=true\uFF08\u4F1A\u653E\u5F00\u7F51\u7EDC\u9650\u5236\uFF09\u3002" : (
1656
+ nodePermissionSupported(iso.platform, process.versions.node) ? "\u9694\u79BB\u5F71\u54CD\uFF1AJavaScript \u90A3\u6863\u7531 Node \u6743\u9650\u6A21\u578B\u6536\u7A84 \u2014\u2014 \u5199\u4E0D\u51FA\u6C99\u7BB1\u76EE\u5F55\u3001\u8D77\u4E0D\u4E86\u5B50\u8FDB\u7A0B\uFF0C\u8BFB\u53D6\u9650\u4E8E\u6C99\u7BB1\u76EE\u5F55\u548C\u5DE5\u4F5C\u533A\uFF1B\u7F51\u7EDC\u4ECD\u7136\u901A\uFF0CPython \u90A3\u6863\u6CA1\u6709\u8FD9\u5C42\u9650\u5236\u3002" : "\u9694\u79BB\u5F71\u54CD\uFF1A\u672C\u5E73\u53F0\u6CA1\u6709 OS \u7EA7\u9694\u79BB\uFF0C\u4EE3\u7801\u80FD\u8BFB\u5199\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u5E76\u8054\u7F51\uFF0C\u522B\u6267\u884C\u4E0D\u53EF\u4FE1\u4EE3\u7801\u3002"
1657
+ ),
1353
1658
  "\u5DE5\u4F5C\u76EE\u5F55\u662F\u4E00\u4E2A\u4E34\u65F6\u6C99\u7BB1\u76EE\u5F55\uFF0C\u8FDB\u7A0B\u7ED3\u675F\u540E\u76EE\u5F55\u4F1A\u88AB\u6E05\u7406\u2014\u2014\u9700\u8981\u7559\u5B58\u7684\u7ED3\u679C\u8BF7\u6253\u5370\u51FA\u6765\u3002"
1354
1659
  ].join("\n"),
1355
1660
  parameters: {
@@ -1660,6 +1965,15 @@ function filterWhole(guard, text) {
1660
1965
  }
1661
1966
  // src/delegate/manager.ts
1662
1967
  var ABORTED = "\u5DF2\u4E2D\u6B62\uFF1A\u7528\u6237\u7EC8\u6B62\u4E86\u8FD9\u4E00\u8F6E";
1968
+ var LABEL_LIMIT = 80;
1969
+ function backgroundNotice(id, count) {
1970
+ return [
1971
+ `\u5DF2\u6D3E\u5230\u540E\u53F0\uFF1A${id}\uFF08${count} \u4E2A\u5B50\u4EFB\u52A1\uFF09\u3002\u8FD9\u4E00\u8F6E\u4F60\u53EF\u4EE5\u63A5\u7740\u5E72\u522B\u7684\u3002`,
1972
+ `\u53D6\u7ED3\u8BBA\uFF1Atask_wait ${id}\uFF08\u963B\u585E\u7B49\u5B83\u7ED3\u675F\uFF09\u6216 task_output ${id}\uFF08\u770B\u8FDB\u5EA6\uFF09\u3002`,
1973
+ `\u5B83\u4EEC\u8DD1\u5B8C\u4F1A\u5728\u4E0B\u4E00\u8F6E\u63D0\u793A\u4F60\u3002`,
1974
+ `\u26A0\uFE0F \u7528\u6237\u6309\u505C\u6B62\u53EA\u505C\u4F60\u81EA\u5DF1\u8FD9\u4E00\u8F6E\uFF0C**\u4E0D\u4F1A**\u505C\u6389\u5B83\u4EEC\uFF1B\u8981\u6536\u8D70\u7528 task_stop ${id}\u3002`
1975
+ ].join("\n");
1976
+ }
1663
1977
  var BLOCKED_TOOLS = Object.freeze([
1664
1978
  "delegate_task",
1665
1979
  "clarify",
@@ -1685,7 +1999,7 @@ var DelegateManager = class {
1685
1999
  this.config = {
1686
2000
  max_concurrent_children: 3,
1687
2001
  max_spawn_depth: 1,
1688
- task_timeout: 3e5,
2002
+ task_timeout: 9e5,
1689
2003
  ...config
1690
2004
  };
1691
2005
  }
@@ -1700,27 +2014,93 @@ var DelegateManager = class {
1700
2014
  duration: 0
1701
2015
  };
1702
2016
  }
1703
- const results = await this.runWithConcurrency(tasks, maxConcurrent, parentSessionId, signal);
1704
- const summary = this.buildSummary(results, Date.now() - start);
1705
- return {
1706
- success: results.every((r) => r.success),
1707
- output: summary,
1708
- duration: Date.now() - start
1709
- };
2017
+ if (tasks.length === 0) {
2018
+ return { success: true, output: this.buildSummary([], Date.now() - start), duration: 0 };
2019
+ }
2020
+ const background = args.background === true;
2021
+ if (signal?.aborted) {
2022
+ return { success: false, output: ABORTED, duration: 0 };
2023
+ }
2024
+ const controller = new AbortController();
2025
+ const job = background ? this.registerJob(parentSessionId, tasks, controller) : void 0;
2026
+ const unlink = background ? () => {
2027
+ } : linkAbort(isAbortSignal(signal) ? signal : void 0, controller);
2028
+ const run = this.runWithConcurrency(
2029
+ tasks,
2030
+ maxConcurrent,
2031
+ parentSessionId,
2032
+ controller.signal,
2033
+ job ? (r) => job.append(`${r.success ? "\u2705" : "\u274C"} ${this.describeResult(r)}
2034
+ `) : void 0,
2035
+ job ? (line) => job.append(`${line}
2036
+ `) : void 0
2037
+ ).then((results) => {
2038
+ const total = Date.now() - start;
2039
+ const summary = this.buildSummary(results, total);
2040
+ const ok2 = results.every((r) => r.success);
2041
+ if (job) {
2042
+ job.append(`
2043
+ ${summary}`);
2044
+ job.finish(ok2 ? "exited" : "failed");
2045
+ }
2046
+ return { results, summary, total, ok: ok2 };
2047
+ });
2048
+ if (background && job) {
2049
+ void run.catch(() => void 0);
2050
+ return {
2051
+ success: true,
2052
+ output: backgroundNotice(job.id, tasks.length),
2053
+ duration: Date.now() - start,
2054
+ jobId: job.id,
2055
+ background: true
2056
+ };
2057
+ }
2058
+ try {
2059
+ const r = await run;
2060
+ return {
2061
+ success: r.ok,
2062
+ output: r.summary,
2063
+ duration: r.total,
2064
+ ...job ? { jobId: job.id } : {}
2065
+ };
2066
+ } finally {
2067
+ unlink();
2068
+ }
2069
+ }
2070
+ registerJob(parentSessionId, tasks, controller) {
2071
+ const label = tasks.map((t83) => t83.goal).join(" | ");
2072
+ return registerJob({
2073
+ sessionId: parentSessionId,
2074
+ kind: "agent",
2075
+ label: label.length > LABEL_LIMIT ? `${label.slice(0, LABEL_LIMIT)}\u2026` : label,
2076
+ maxOutput: JOB_OUTPUT_LIMIT,
2077
+ artifactsDir: artifactsDir(parentSessionId),
2078
+ artifactLabel: "agent",
2079
+ stop: async () => {
2080
+ controller.abort();
2081
+ },
2082
+ terminates: true,
2083
+ detail: { abort: controller }
2084
+ });
1710
2085
  }
1711
2086
  normalizeTasks(args) {
1712
2087
  if (args.tasks && args.tasks.length > 0) {
1713
- return args.role ? args.tasks.map((t81) => ({ ...t81, role: t81.role ?? args.role })) : args.tasks;
2088
+ return args.role ? args.tasks.map((t83) => ({ ...t83, role: t83.role ?? args.role })) : args.tasks;
1714
2089
  }
1715
2090
  if (args.goal) return [{ goal: args.goal, ...args.role ? { role: args.role } : {} }];
1716
2091
  return [];
1717
2092
  }
1718
- async runWithConcurrency(tasks, maxConcurrent, parentSessionId, signal) {
2093
+ async runWithConcurrency(tasks, maxConcurrent, parentSessionId, signal, onDone, onProgress) {
1719
2094
  const results = [];
1720
2095
  for (let i = 0; i < tasks.length; i += maxConcurrent) {
1721
2096
  const batch = tasks.slice(i, i + maxConcurrent);
1722
2097
  const batchResults = await Promise.allSettled(
1723
- batch.map((t81) => this.runSingleTask(t81, parentSessionId, signal))
2098
+ batch.map(
2099
+ (t83) => this.runSingleTask(t83, parentSessionId, signal, onProgress).then((r) => {
2100
+ onDone?.(r);
2101
+ return r;
2102
+ })
2103
+ )
1724
2104
  );
1725
2105
  for (const r of batchResults) {
1726
2106
  if (r.status === "fulfilled") {
@@ -1738,7 +2118,7 @@ var DelegateManager = class {
1738
2118
  }
1739
2119
  return results;
1740
2120
  }
1741
- async runSingleTask(task, parentSessionId, signal) {
2121
+ async runSingleTask(task, parentSessionId, signal, onProgress) {
1742
2122
  const start = Date.now();
1743
2123
  const sessionId = `delegate-${randomUUID().slice(0, 8)}`;
1744
2124
  const failed = (error, summary = "") => ({
@@ -1755,22 +2135,24 @@ var DelegateManager = class {
1755
2135
  const ms = this.config.task_timeout;
1756
2136
  let timer;
1757
2137
  try {
1758
- const result = await Promise.race([
1759
- this.runner(
1760
- task.goal,
1761
- task.context ?? "",
1762
- sessionId,
1763
- task.role,
1764
- parentSessionId,
1765
- controller.signal
1766
- ),
2138
+ const work = this.runner(
2139
+ task.goal,
2140
+ task.context ?? "",
2141
+ sessionId,
2142
+ task.role,
2143
+ parentSessionId,
2144
+ controller.signal,
2145
+ onProgress
2146
+ );
2147
+ const result = await (ms > 0 ? Promise.race([
2148
+ work,
1767
2149
  new Promise((_, reject2) => {
1768
2150
  timer = setTimeout(() => {
1769
2151
  reject2(new Error(`\u8D85\u65F6 (${ms / 1e3}s)`));
1770
2152
  controller.abort();
1771
2153
  }, ms);
1772
2154
  })
1773
- ]);
2155
+ ]) : work);
1774
2156
  if (controller.signal.aborted) return failed(ABORTED, result.text);
1775
2157
  return {
1776
2158
  goal: task.goal,
@@ -1789,6 +2171,10 @@ var DelegateManager = class {
1789
2171
  unlink();
1790
2172
  }
1791
2173
  }
2174
+ describeResult(r) {
2175
+ const who = r.role ? `[${r.role}] ` : "";
2176
+ return `${who}${r.goal} (${r.duration}ms)`;
2177
+ }
1792
2178
  buildSummary(results, totalDuration) {
1793
2179
  const lines = [];
1794
2180
  const succeeded = results.filter((r) => r.success).length;
@@ -1835,8 +2221,15 @@ function createDelegateTool(runner, config, roles) {
1835
2221
  "\u628A\u4E00\u6279\u76F8\u4E92\u72EC\u7ACB\u7684\u5B50\u4EFB\u52A1\u6D3E\u7ED9\u5B50 agent \u5E76\u884C\u6267\u884C\uFF0C\u62FF\u56DE\u5404\u81EA\u7684\u7ED3\u8BBA\u3002",
1836
2222
  "\u9002\u5408\uFF1A\u9700\u8981\u5206\u522B\u8C03\u7814\u591A\u4E2A\u6A21\u5757\u3001\u540C\u65F6\u9A8C\u8BC1\u591A\u4E2A\u5047\u8BBE\u3001\u6279\u91CF\u5904\u7406\u4E92\u4E0D\u4F9D\u8D56\u7684\u6539\u52A8\u3002",
1837
2223
  "\u4E0D\u9002\u5408\uFF1A\u6709\u5148\u540E\u4F9D\u8D56\u7684\u6B65\u9AA4\uFF08\u81EA\u5DF1\u6309\u987A\u5E8F\u505A\u5C31\u884C\uFF09\u3001\u9700\u8981\u6765\u56DE\u8FFD\u95EE\u7684\u4EFB\u52A1\u3002",
2224
+ "\u9ED8\u8BA4**\u7B49\u5B83\u4EEC\u5168\u90E8\u8DD1\u5B8C**\u518D\u8FD4\u56DE\uFF08\u5341\u51E0\u6B21\u5DE5\u5177\u8C03\u7528\u5C31\u56DE\u6765\u4E86\uFF0C\u5E38\u89C1\u60C5\u5F62\u7528\u8FD9\u4E2A\uFF09\u3002",
2225
+ "\u4F60\u81EA\u5DF1\u8FD8\u6709\u522B\u7684\u6D3B\u8981\u5E72\u3001\u4E0D\u5FC5\u7B49\u7ED3\u8BBA\u65F6\u4F20 background: true\uFF1A\u7ACB\u523B\u8FD4\u56DE\u4E00\u4E2A\u4F5C\u4E1A id\uFF08\u6BD4\u5982 a1\uFF09\uFF0C",
2226
+ "\u8FD9\u4E00\u8F6E\u63A5\u7740\u5E72\u522B\u7684\u3002\u4E4B\u540E\u7528 task_wait a1 \u7B49\u7ED3\u679C\u3001task_output a1 \u770B\u8FDB\u5EA6\u3001task_stop a1 \u6536\u6389\u5B83\uFF1B",
2227
+ "\u5B83\u4EEC\u8DD1\u5B8C\u4E5F\u4F1A\u5728\u4E0B\u4E00\u8F6E\u63D0\u793A\u4F60\u3002",
2228
+ "\u26A0\uFE0F \u540E\u53F0\u5B50\u4EFB\u52A1\u4E0D\u8DDF\u7740\u7236\u56DE\u5408\u8D70 \u2014\u2014 \u7528\u6237\u6309\u505C\u6B62\u53EA\u505C\u7236\u56DE\u5408\uFF0C\u5B83\u4EEC\u4F1A\u7EE7\u7EED\u8DD1\uFF08\u8981\u6536\u5F97 task_stop\uFF09\u3002",
1838
2229
  `\u5B50 agent \u62FF\u4E0D\u5230\u8FD9\u4E9B\u5DE5\u5177\uFF1A${BLOCKED_TOOLS.join(", ")}\uFF0C\u4E5F\u4E0D\u80FD\u518D\u5F80\u4E0B\u6D3E\u6D3B\u3002`,
1839
2230
  "\u5B50 agent \u770B\u4E0D\u5230\u5F53\u524D\u5BF9\u8BDD\uFF0C\u6240\u6709\u5FC5\u8981\u4FE1\u606F\u90FD\u8981\u5199\u8FDB goal \u548C context\u3002",
2231
+ "\u8D85\u65F6\uFF1A\u5355\u4E2A\u5B50\u4EFB\u52A1\u9ED8\u8BA4 15 \u5206\u949F\uFF08\u914D\u7F6E delegate.taskTimeoutSec \u53EF\u8C03\uFF0C0 = \u4E0D\u9650\u65F6\uFF09\uFF1B\u649E\u4E0A\u5C31\u6390\u6389\uFF0C\u90A3\u534A\u622A\u7ED3\u8BBA\u4E0D\u4FDD\u7559\u3002",
2232
+ "\u5B83\u8DD1\u5B8C\u4E4B\u540E\u8FD9\u4E00\u8F6E\u4F1A\u843D\u8FDB\u4F1A\u8BDD\u5E93\uFF08\u5E26 parent_id\uFF09\uFF1A\u60F3\u77E5\u9053\u5B83\u4E2D\u95F4\u8BFB\u4E86\u4EC0\u4E48\u3001\u8BD5\u8FC7\u54EA\u4E9B\u8DEF\uFF0C\u7528 session_trace \u627E\u5230\u5B83\u7684\u4F1A\u8BDD\u518D session_messages_read \u8FDB\u53BB\u770B\u3002",
1840
2233
  ...renderRoles(listRoles())
1841
2234
  ].join("\n");
1842
2235
  },
@@ -1863,7 +2256,11 @@ function createDelegateTool(runner, config, roles) {
1863
2256
  },
1864
2257
  goal: { type: "string", description: "\u5355\u4E2A\u4EFB\u52A1\u7684\u76EE\u6807\uFF08tasks \u7684\u7B80\u5199\u5F62\u5F0F\uFF09" },
1865
2258
  ...role ? { role } : {},
1866
- max_concurrent: { type: "number", description: "\u6700\u5927\u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 3\uFF09" }
2259
+ max_concurrent: { type: "number", description: "\u6700\u5927\u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 3\uFF09" },
2260
+ background: {
2261
+ type: "boolean",
2262
+ description: "true = \u6D3E\u5230\u540E\u53F0\u7ACB\u523B\u8FD4\u56DE\u4F5C\u4E1A id\uFF08a1\uFF09\uFF1B\u4E0D\u7ED9 = \u7B49\u5B83\u4EEC\u5168\u90E8\u8DD1\u5B8C\u518D\u8FD4\u56DE\u3002"
2263
+ }
1867
2264
  }
1868
2265
  };
1869
2266
  },
@@ -1878,21 +2275,21 @@ function createDelegateTool(runner, config, roles) {
1878
2275
  operation: "command",
1879
2276
  describeTarget: (args) => {
1880
2277
  const tasks = Array.isArray(args["tasks"]) ? args["tasks"] : [];
1881
- const goals = tasks.map((t81) => String(t81["goal"] ?? ""));
2278
+ const goals = tasks.map((t83) => String(t83["goal"] ?? ""));
1882
2279
  const single = typeof args["goal"] === "string" ? args["goal"] : "";
1883
2280
  return [...goals, single].filter(Boolean).join(" | ");
1884
2281
  },
1885
2282
  async execute(args, ctx) {
1886
2283
  const start = Date.now();
1887
2284
  const rawTasks = Array.isArray(args["tasks"]) ? args["tasks"] : void 0;
1888
- const tasks = rawTasks?.map((t81) => {
1889
- const o = t81;
2285
+ const tasks = rawTasks?.map((t83) => {
2286
+ const o = t83;
1890
2287
  return {
1891
2288
  goal: String(o["goal"] ?? "").trim(),
1892
2289
  context: String(o["context"] ?? ""),
1893
2290
  ...typeof o["role"] === "string" && o["role"].trim() ? { role: o["role"].trim() } : {}
1894
2291
  };
1895
- }).filter((t81) => t81.goal.length > 0);
2292
+ }).filter((t83) => t83.goal.length > 0);
1896
2293
  const goal = typeof args["goal"] === "string" ? args["goal"].trim() : "";
1897
2294
  const topRole = typeof args["role"] === "string" ? args["role"].trim() : "";
1898
2295
  if ((!tasks || tasks.length === 0) && !goal) {
@@ -1908,7 +2305,7 @@ function createDelegateTool(runner, config, roles) {
1908
2305
  };
1909
2306
  }
1910
2307
  const known = listRoles();
1911
- const requested = [...(tasks ?? []).map((t81) => t81.role), topRole].filter(
2308
+ const requested = [...(tasks ?? []).map((t83) => t83.role), topRole].filter(
1912
2309
  (r) => Boolean(r)
1913
2310
  );
1914
2311
  const unknown = [...new Set(requested)].filter((r) => !known.some((k) => k.name === r));
@@ -1930,7 +2327,8 @@ function createDelegateTool(runner, config, roles) {
1930
2327
  ...tasks && tasks.length > 0 ? { tasks } : {},
1931
2328
  ...goal ? { goal } : {},
1932
2329
  ...topRole ? { role: topRole } : {},
1933
- ...maxConcurrent ? { max_concurrent: maxConcurrent } : {}
2330
+ ...maxConcurrent ? { max_concurrent: maxConcurrent } : {},
2331
+ ...args["background"] === true ? { background: true } : {}
1934
2332
  },
1935
2333
  ctx.sessionId,
1936
2334
  ctx.signal
@@ -2082,8 +2480,8 @@ function createGoalTool(deps) {
2082
2480
  }
2083
2481
  var VALID_TARGETS = ["memory", "user"];
2084
2482
  var VALID_ACTIONS = ["add", "replace", "remove", "search", "read", "budget"];
2085
- function isValidTarget(t81) {
2086
- return typeof t81 === "string" && VALID_TARGETS.includes(t81);
2483
+ function isValidTarget(t83) {
2484
+ return typeof t83 === "string" && VALID_TARGETS.includes(t83);
2087
2485
  }
2088
2486
  function isValidAction(a) {
2089
2487
  return VALID_ACTIONS.includes(a);
@@ -2278,6 +2676,9 @@ function unknownToolNotice(name) {
2278
2676
  function codeModeDeclaredNotice(name) {
2279
2677
  return `\u5DE5\u5177 ${name} \u4E0D\u5728\u4F60\u7684\u5DE5\u5177\u8868\u91CC\uFF0C\u4F46\u5B83**\u5B58\u5728** \u2014\u2014 \u5F53\u524D\u662F code \u6863\uFF0C\u5B83\u548C\u5176\u4F59\u8BFB\u7C7B\u5DE5\u5177\u4E00\u8D77\u6539\u4EE5 <code_mode_sdk> \u90A3\u6BB5\u58F0\u660E\u5B58\u5728\u3002\u8BF7\u7528 run_code \u5199\u4E00\u6BB5\u7A0B\u5E8F\u3001\u5728\u91CC\u9762 \`await tools.${name}({ ... })\` \u8C03\u5B83\u3002`;
2280
2678
  }
2679
+ function policyNotice() {
2680
+ return "run_code \u7684\u8FD0\u884C\u65F6\u6743\u9650\u7B56\u7565\u6CA1\u80FD\u542F\u52A8\u5B83\u81EA\u5DF1\u7684\u5165\u53E3\uFF08\u5185\u90E8\u914D\u7F6E\u95EE\u9898\uFF09\u3002\u8FD9\u4E0D\u662F\u4F60\u7684\u7A0B\u5E8F\u7684\u95EE\u9898\uFF0C\u8BF7\u6539\u7528\u76F4\u63A5\u8C03\u7528\u5DE5\u5177\u3002";
2681
+ }
2281
2682
  function noChannelNotice() {
2282
2683
  return "run_code \u5728\u5F53\u524D\u5BBF\u4E3B\u91CC\u4E0D\u53EF\u7528\uFF1A\u5B50\u8C03\u7528\u901A\u9053\u6CA1\u6709\u63A5\u4E0A\u3002\u8BF7\u6539\u7528\u76F4\u63A5\u8C03\u7528\u5DE5\u5177\u3002";
2283
2684
  }
@@ -2297,6 +2698,47 @@ ${outcome.value}`);
2297
2698
  if (parts.length === 1 && outcome.ok) parts.push("(\u7A0B\u5E8F\u6CA1\u6709\u6253\u5370\u4E5F\u6CA1\u6709\u8FD4\u56DE\u4EFB\u4F55\u4E1C\u897F)");
2298
2699
  return parts.join("\n");
2299
2700
  }
2701
+ function workerExecArgv(input) {
2702
+ const flag = permissionFlag();
2703
+ if (!flag) return void 0;
2704
+ const entryDirs = bothForms(parentOf(realOrSame(input.entry)));
2705
+ const cwds = bothForms(input.cwd);
2706
+ const tmps = bothForms(tmpdir());
2707
+ const args = [flag];
2708
+ for (const dir of unique([...entryDirs, ...cwds, ...tmps])) args.push(`--allow-fs-read=${dir}`);
2709
+ for (const dir of unique([...cwds, ...tmps])) args.push(`--allow-fs-write=${dir}`);
2710
+ return args;
2711
+ }
2712
+ function bothForms(path) {
2713
+ const real = realOrSame(path);
2714
+ return real === path ? [path] : [path, real];
2715
+ }
2716
+ function workerEnv() {
2717
+ const kept = {};
2718
+ for (const key of TEMP_ENV_KEYS) {
2719
+ const value = process.env[key];
2720
+ if (value) kept[key] = value;
2721
+ }
2722
+ return kept;
2723
+ }
2724
+ var TEMP_ENV_KEYS = ["TMPDIR", "TMP", "TEMP"];
2725
+ function permissionFlag() {
2726
+ return process.allowedNodeEnvironmentFlags.has("--permission") ? "--permission" : void 0;
2727
+ }
2728
+ function realOrSame(path) {
2729
+ try {
2730
+ return realpathSync(path);
2731
+ } catch {
2732
+ return path;
2733
+ }
2734
+ }
2735
+ function parentOf(path) {
2736
+ const cut = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
2737
+ return cut > 0 ? path.slice(0, cut) : path;
2738
+ }
2739
+ function unique(paths) {
2740
+ return [...new Set(paths.filter((p) => p.length > 0))];
2741
+ }
2300
2742
  var WORKER_BASENAME = "worker";
2301
2743
  var WORKER_DIR = "code-mode";
2302
2744
  function resolveCodeModeWorker(baseDir, exists2 = existsSync) {
@@ -2330,11 +2772,14 @@ async function runCode(input) {
2330
2772
  maxValueChars: limits.maxValueChars,
2331
2773
  cwd: input.cwd
2332
2774
  };
2775
+ const execArgv = workerExecArgv({ entry, cwd: input.cwd });
2333
2776
  const worker = new Worker(entry, {
2334
2777
  workerData,
2335
2778
  stdout: true,
2336
2779
  stderr: true,
2337
- resourceLimits: { maxOldGenerationSizeMb: limits.maxMemoryMb }
2780
+ resourceLimits: { maxOldGenerationSizeMb: limits.maxMemoryMb },
2781
+ env: workerEnv(),
2782
+ ...execArgv ? { execArgv } : {}
2338
2783
  });
2339
2784
  let printed = "";
2340
2785
  const take = (text) => {
@@ -2394,7 +2839,7 @@ async function runCode(input) {
2394
2839
  });
2395
2840
  worker.on("error", (err) => {
2396
2841
  if (settled) return;
2397
- ending = err.code === "ERR_WORKER_OUT_OF_MEMORY" ? { kind: "oom", message: oomNotice(limits.maxMemoryMb) } : { kind: "failed", message: err.message };
2842
+ ending = err.code === "ERR_WORKER_OUT_OF_MEMORY" ? { kind: "oom", message: oomNotice(limits.maxMemoryMb) } : err.code === "ERR_ACCESS_DENIED" ? { kind: "policy", message: policyNotice() } : { kind: "failed", message: err.message };
2398
2843
  finish();
2399
2844
  });
2400
2845
  worker.on("exit", () => {
@@ -2482,6 +2927,7 @@ function createRunCodeTool(deps = {}) {
2482
2927
  "**\u53EA\u6709\u4F60 print \u51FA\u6765\u7684\u548C return \u7684\u4F1A\u56DE\u5230\u5BF9\u8BDD\u91CC** \u2014\u2014 \u81EA\u5DF1\u7B5B\u3002",
2483
2928
  "",
2484
2929
  `\u9650\u5236\uFF1A\`tools.*\` \u53EA\u653E\u884C**\u53EA\u8BFB**\u5DE5\u5177\uFF08\u5199\u6587\u4EF6\u3001\u8DD1\u547D\u4EE4\u8BF7\u5728\u7A0B\u5E8F\u5916\u76F4\u63A5\u8C03\u90A3\u4E9B\u5DE5\u5177\uFF09\uFF1B\u6700\u591A ${limits.maxToolCalls} \u6B21\u5B50\u8C03\u7528\uFF1B\u9ED8\u8BA4 ${Math.round(limits.timeoutMs / 1e3)} \u79D2\u8D85\u65F6\uFF08timeout \u53C2\u6570\u53EF\u8C03\uFF09\u3002`,
2930
+ "\u7A0B\u5E8F\u672C\u8EAB\u4E5F\u53D7\u9650\u5236\uFF1A\u53EA\u80FD\u8BFB\u5199\u8FD9\u6BB5\u4F1A\u8BDD\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u548C\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\uFF09\uFF0C\u8D77\u4E0D\u4E86\u5B50\u8FDB\u7A0B\uFF0C\u770B\u4E0D\u5230\u73AF\u5883\u53D8\u91CF\uFF1B\u8054\u7F51\u4ECD\u7136\u53EF\u4EE5\u3002",
2485
2931
  "\u6BCF\u4E00\u6B21 `tools.*` \u5B50\u8C03\u7528\u90FD\u7167\u5E38\u8D70\u6743\u9650\u5224\u5B9A\u548C\u5BA1\u6279\u3002",
2486
2932
  "**\u6574\u6BB5\u7A0B\u5E8F\u81EA\u5DF1\u4E5F\u8981\u5148\u8FC7\u4E00\u6B21\u5BA1\u6279** \u2014\u2014 \u7528\u6237\u770B\u5230\u7684\u662F\u7A0B\u5E8F\u5168\u6587\uFF0C\u6279\u7684\u662F\u300C\u8FD9\u6BB5\u7A0B\u5E8F\u53EF\u4EE5\u8DD1\u300D\u3002",
2487
2933
  "\u7A0B\u5E8F\u91CC\u7684\u5B50\u8C03\u7528**\u770B\u5F97\u5230\u5B8C\u6574\u8F93\u51FA**\uFF08\u6BD4\u4F60\u76F4\u63A5\u8C03\u90A3\u4E2A\u5DE5\u5177\u770B\u5230\u7684\u591A\u5F97\u591A\uFF09\u2014\u2014 \u6240\u4EE5\u7B5B\u5728\u7A0B\u5E8F\u91CC\u505A\u3002"
@@ -2569,7 +3015,7 @@ function narrow(outcome) {
2569
3015
  function createEnterTool(deps) {
2570
3016
  return {
2571
3017
  name: "enter_plan_mode",
2572
- description: "\u8FDB\u5165 plan \u6A21\u5F0F\uFF1A\u63A5\u4E0B\u6765\u53EA\u8BFB\u5730\u8C03\u7814\uFF08\u4E0D\u5199\u6587\u4EF6\u3001\u4E0D\u6267\u884C\u547D\u4EE4\u3001\u4E0D\u8054\u7F51\uFF09\uFF0C\u8C03\u7814\u5B8C\u7528 exit_plan_mode \u4EA4\u4E00\u4EFD\u8BA1\u5212\u7ED9\u7528\u6237\u6279\u3002\u4EFB\u52A1\u8981\u52A8\u4E09\u4E2A\u4EE5\u4E0A\u6587\u4EF6\u3001\u6709\u591A\u79CD\u5408\u7406\u5B9E\u73B0\u8DEF\u5F84\u3001\u8981\u52A0/\u6539\u4F9D\u8D56\u3001\u8981\u52A8\u8DE8\u5305\u5951\u7EA6\uFF0C\u6216\u8005\u7528\u6237\u7684\u63CF\u8FF0\u6709\u6B67\u4E49\u4E14\u6B67\u4E49\u4F1A\u6539\u53D8\u5B9E\u73B0\u65F6\uFF0C\u5148\u8C03\u5B83\u3002\u5355\u6587\u4EF6\u5C0F\u6539\u52A8\u3001\u53EA\u6709\u4E00\u79CD\u660E\u663E\u505A\u6CD5\u3001\u6539\u6587\u6848\u4FEE typo \u4E0D\u8981\u7528\u3002",
3018
+ description: "\u8FDB\u5165 plan \u6A21\u5F0F\uFF1A\u63A5\u4E0B\u6765\u53EA\u8BFB\uFF08\u4E0D\u5199\u6587\u4EF6\u3001\u4E0D\u6267\u884C\u547D\u4EE4\u3001\u4E0D\u8054\u7F51\uFF09\uFF0C\u8C03\u7814\u5B8C\u7528 exit_plan_mode \u4EA4\u4E00\u4EFD\u8BA1\u5212\u7ED9\u7528\u6237\u6279\u3002**\u5148\u628A\u8303\u56F4\u6478\u5230\u80FD\u5199\u51FA\u8BA1\u5212\u4E86\u518D\u8C03\u5B83** \u2014\u2014 plan \u7EA7\u522B\u4E0B\u5DE5\u4F5C\u533A\u5916\u7684\u8BFB\u53D6\u8981\u7528\u6237\u9010\u4E2A\u6279\u51C6\uFF0C\u5927\u8303\u56F4\u8C03\u7814\u653E\u8FDB\u53BB\u4F1A\u53D8\u6210\u4E00\u4E32\u786E\u8BA4\u6846\uFF1B\u8FDB plan \u4E4B\u540E\u53EA\u505A\u5B9A\u70B9\u6838\u5B9E\u3002\u4EFB\u52A1\u8981\u52A8\u4E09\u4E2A\u4EE5\u4E0A\u6587\u4EF6\u3001\u6709\u591A\u79CD\u5408\u7406\u5B9E\u73B0\u8DEF\u5F84\u3001\u8981\u52A0/\u6539\u4F9D\u8D56\u3001\u8981\u52A8\u8DE8\u5305\u5951\u7EA6\uFF0C\u6216\u8005\u7528\u6237\u7684\u63CF\u8FF0\u6709\u6B67\u4E49\u4E14\u6B67\u4E49\u4F1A\u6539\u53D8\u5B9E\u73B0\u65F6\uFF0C\u624D\u503C\u5F97\u51FA\u8BA1\u5212\u3002\u5355\u6587\u4EF6\u5C0F\u6539\u52A8\u3001\u53EA\u6709\u4E00\u79CD\u660E\u663E\u505A\u6CD5\u3001\u6539\u6587\u6848\u4FEE typo \u4E0D\u8981\u7528\u3002",
2573
3019
  parameters: {
2574
3020
  type: "object",
2575
3021
  properties: {
@@ -2707,48 +3153,13 @@ function createExitTool(deps) {
2707
3153
  function createPlanModeTools(deps) {
2708
3154
  return [createEnterTool(deps), createExitTool(deps)];
2709
3155
  }
2710
- var SCHEDULE_ISSUE_CODES = [
2711
- "name-empty",
2712
- "prompt-empty",
2713
- "permission-unknown",
2714
- "work-dir-missing",
2715
- "budget-missing",
2716
- "max-turns-invalid",
2717
- "timeout-invalid",
2718
- "trigger-time-invalid",
2719
- "trigger-times-too-many",
2720
- "trigger-weekdays-empty",
2721
- "trigger-days-invalid",
2722
- "trigger-interval-invalid",
2723
- "trigger-once-date-invalid",
2724
- "trigger-once-past",
2725
- "date-range-invalid",
2726
- "date-range-on-once",
2727
- "rule-invalid",
2728
- "bypass-needs-workdir",
2729
- "bypass-workdir-too-broad",
2730
- "bypass-needs-limits",
2731
- "bypass-not-confirmed"
2732
- ];
2733
- var SCHEDULE_DEFAULTS = {
2734
- maxTurns: DEFAULT_MAX_TURNS,
2735
- timeoutMs: 60 * 60 * 1e3
2736
- };
2737
- var SCHEDULE_FORM_BUDGET_USD = 200;
2738
- var SCHEDULE_DEFAULT_ALLOWLIST = {
2739
- allowTools: [],
2740
- allowOperations: ["file_read"],
2741
- allowRules: []
2742
- };
2743
- // src/schedule/draft.ts
2744
3156
  var bad = (message) => ({ ok: false, message });
2745
3157
  var good = (value) => ({ ok: true, value });
2746
3158
  var PERMISSION_ORDER = [
2747
3159
  "plan",
2748
3160
  "default",
2749
3161
  "acceptEdits",
2750
- "auto",
2751
- "bypass"
3162
+ "auto"
2752
3163
  ];
2753
3164
  var OPERATION_TYPES = [
2754
3165
  "file_read",
@@ -2790,17 +3201,11 @@ function operationList(v) {
2790
3201
  function permission(v) {
2791
3202
  const s = str(v);
2792
3203
  if (s === void 0) return bad("permission \u662F\u7A7A\u7684");
2793
- if (!PERMISSION_ORDER.includes(s)) {
3204
+ const normalized = resolvePermissionLevel(s) ?? s;
3205
+ if (!PERMISSION_ORDER.includes(normalized)) {
2794
3206
  return bad(`permission \u53EA\u80FD\u662F ${PERMISSION_ORDER.join(" / ")} \u4E4B\u4E00\uFF0C\u6536\u5230\u7684\u662F ${s}`);
2795
3207
  }
2796
- return good(s);
2797
- }
2798
- function budget(v) {
2799
- if (v === void 0) return good(void 0);
2800
- if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
2801
- return bad("maxBudgetUsd \u8981\u662F\u5927\u4E8E 0 \u7684\u6570\uFF08\u5355\u4F4D\u7F8E\u5143\uFF09\u3002\u4E0D\u7ED9\u5C31\u6309\u65B0\u5EFA\u8868\u5355\u90A3\u4E2A\u7F3A\u7701\uFF08200\uFF09");
2802
- }
2803
- return good(v);
3208
+ return good(normalized);
2804
3209
  }
2805
3210
  function clock(v) {
2806
3211
  const s = str(v);
@@ -2913,13 +3318,11 @@ function parseCreate(raw, note) {
2913
3318
  }
2914
3319
  const trigger = parseTrigger(raw["trigger"]);
2915
3320
  if (!trigger.ok) return trigger;
2916
- const level = permission(raw["permission"]);
3321
+ const level = raw["permission"] === void 0 ? good(SCHEDULE_DEFAULT_PERMISSION) : permission(raw["permission"]);
2917
3322
  if (!level.ok) return level;
2918
3323
  const allow = parseAllowlist(raw);
2919
3324
  if (!allow.ok) return allow;
2920
3325
  const workDir = str(raw["workDir"]);
2921
- const usd = budget(raw["maxBudgetUsd"]);
2922
- if (!usd.ok) return usd;
2923
3326
  return good({
2924
3327
  action: "create",
2925
3328
  name,
@@ -2928,7 +3331,6 @@ function parseCreate(raw, note) {
2928
3331
  permission: level.value,
2929
3332
  note,
2930
3333
  ...workDir === void 0 ? {} : { workDir },
2931
- ...usd.value === void 0 ? {} : { maxBudgetUsd: usd.value },
2932
3334
  ...allow.value
2933
3335
  });
2934
3336
  }
@@ -2963,9 +3365,6 @@ function parseUpdate(raw, note) {
2963
3365
  const timeout = posInt(raw["timeoutMs"], "timeoutMs");
2964
3366
  if (!timeout.ok) return timeout;
2965
3367
  if (timeout.value !== void 0) patch.timeoutMs = timeout.value;
2966
- const usd = budget(raw["maxBudgetUsd"]);
2967
- if (!usd.ok) return usd;
2968
- if (usd.value !== void 0) patch.maxBudgetUsd = usd.value;
2969
3368
  if (Object.keys(patch).length === 0) {
2970
3369
  return bad(
2971
3370
  "update \u4E00\u683C\u90FD\u6CA1\u6539\u3002\u4E00\u4EFD\u4EC0\u4E48\u90FD\u4E0D\u6539\u7684\u63D0\u6848\u4F1A\u767D\u5360\u7528\u6237\u4E00\u6B21\u70B9\u51FB \u2014\u2014 \u8981\u4E48\u7ED9\u51FA\u5177\u4F53\u6539\u54EA\u51E0\u683C\uFF0C\u8981\u4E48\u522B\u63D0"
@@ -2991,7 +3390,7 @@ function parseScheduleDraft(raw) {
2991
3390
  }
2992
3391
  function draftWidening(draft, current) {
2993
3392
  const out = [];
2994
- const base2 = draft.action === "update" && current ? current.permission : "default";
3393
+ const base2 = draft.action === "update" && current ? current.permission : SCHEDULE_DEFAULT_PERMISSION;
2995
3394
  const level = draft.permission;
2996
3395
  if (level !== void 0 && PERMISSION_ORDER.indexOf(level) > PERMISSION_ORDER.indexOf(base2)) {
2997
3396
  out.push("permission");
@@ -3045,9 +3444,39 @@ function draftCommandLine(draft) {
3045
3444
  for (const tool of draft.allowTools ?? []) argv.push("--allow-tool", shellArg(tool));
3046
3445
  for (const op2 of draft.allowOperations ?? []) argv.push("--allow-operation", op2);
3047
3446
  for (const rule of draft.allowRules ?? []) argv.push("--allow-rule", shellArg(rule));
3048
- argv.push("--budget", String(draft.maxBudgetUsd ?? SCHEDULE_FORM_BUDGET_USD));
3049
3447
  return argv.join(" ");
3050
3448
  }
3449
+ var SCHEDULE_ISSUE_CODES = [
3450
+ "name-empty",
3451
+ "prompt-empty",
3452
+ "permission-unknown",
3453
+ "work-dir-missing",
3454
+ "max-turns-invalid",
3455
+ "timeout-invalid",
3456
+ "trigger-time-invalid",
3457
+ "trigger-times-too-many",
3458
+ "trigger-weekdays-empty",
3459
+ "trigger-days-invalid",
3460
+ "trigger-interval-invalid",
3461
+ "trigger-once-date-invalid",
3462
+ "trigger-once-past",
3463
+ "date-range-invalid",
3464
+ "date-range-on-once",
3465
+ "rule-invalid",
3466
+ "bypass-needs-workdir",
3467
+ "bypass-workdir-too-broad",
3468
+ "bypass-needs-limits",
3469
+ "bypass-not-confirmed"
3470
+ ];
3471
+ var SCHEDULE_DEFAULTS = {
3472
+ maxTurns: DEFAULT_MAX_TURNS,
3473
+ timeoutMs: 60 * 60 * 1e3
3474
+ };
3475
+ var SCHEDULE_DEFAULT_ALLOWLIST = {
3476
+ allowTools: [],
3477
+ allowOperations: ["file_read"],
3478
+ allowRules: []
3479
+ };
3051
3480
  // src/schedule/unattended-lint.ts
3052
3481
  var ANCHORS = [
3053
3482
  { kind: "asks-human", zh: /问(?:一下|下)?我/, en: /\bask\s+(?:me|us)\b/i },
@@ -3477,15 +3906,12 @@ function validateSchedule(input) {
3477
3906
  const now = input.now ?? Date.now();
3478
3907
  if (!input.name.trim()) issues.push({ code: "name-empty" });
3479
3908
  if (!input.prompt.trim()) issues.push({ code: "prompt-empty" });
3480
- if (!isPermissionLevel(input.permission)) {
3909
+ if (!resolvePermissionLevel(input.permission)) {
3481
3910
  issues.push({ code: "permission-unknown", detail: input.permission });
3482
3911
  }
3483
3912
  if (input.workDir && !isExistingDir(input.workDir)) {
3484
3913
  issues.push({ code: "work-dir-missing", detail: input.workDir });
3485
3914
  }
3486
- if (!Number.isFinite(input.maxBudgetUsd) || input.maxBudgetUsd <= 0) {
3487
- issues.push({ code: "budget-missing", detail: String(input.maxBudgetUsd) });
3488
- }
3489
3915
  if (!Number.isInteger(input.maxTurns) || input.maxTurns < 0) {
3490
3916
  issues.push({ code: "max-turns-invalid", detail: String(input.maxTurns) });
3491
3917
  }
@@ -3498,7 +3924,8 @@ function validateSchedule(input) {
3498
3924
  for (const issue of rules.issues) {
3499
3925
  issues.push({ code: "rule-invalid", detail: issue.message });
3500
3926
  }
3501
- if (input.permission === "bypass") {
3927
+ const level = resolvePermissionLevel(input.permission);
3928
+ if (level && approvalPolicyForLevel(level) === "never") {
3502
3929
  issues.push(...bypassIssues(input));
3503
3930
  }
3504
3931
  return {
@@ -3511,7 +3938,7 @@ function bypassIssues(input) {
3511
3938
  if (input.workDir.trim() && isTooBroad(input.workDir, input.homeDir)) {
3512
3939
  issues.push({ code: "bypass-workdir-too-broad", detail: input.workDir });
3513
3940
  }
3514
- const missingRuler = !(input.maxBudgetUsd > 0) || !(input.timeoutMs > 0);
3941
+ const missingRuler = !(input.timeoutMs > 0);
3515
3942
  if (missingRuler) issues.push({ code: "bypass-needs-limits" });
3516
3943
  if (input.bypassAcknowledged !== true) issues.push({ code: "bypass-not-confirmed" });
3517
3944
  return issues;
@@ -3529,7 +3956,7 @@ function isExistingDir(path) {
3529
3956
  }
3530
3957
  }
3531
3958
  function needsAllowlist(permission2) {
3532
- return permission2 !== "plan" && permission2 !== "bypass";
3959
+ return permission2 !== "plan" && approvalPolicyForLevel(permission2) !== "never";
3533
3960
  }
3534
3961
  // src/tools/builtin/schedule-draft.ts
3535
3962
  function say4(text, ok2, startedAt) {
@@ -3545,22 +3972,18 @@ var WIDENING_TEXT = {
3545
3972
  allowlist: "\u6E05\u5355\u591A\u4E86\u6761\u76EE\uFF08= \u591A\u4E86\u51E0\u4EF6\u4E0D\u7ECF\u786E\u8BA4\u5C31\u4F1A\u505A\u7684\u4E8B\uFF09",
3546
3973
  enabled: "\u628A\u4E00\u6761\u505C\u7528\u7684\u4EFB\u52A1\u6539\u6210\u542F\u7528"
3547
3974
  };
3548
- function budgetOf(draft, current) {
3549
- return draft.maxBudgetUsd ?? current?.maxBudgetUsd ?? SCHEDULE_FORM_BUDGET_USD;
3550
- }
3551
3975
  function validateDraft(draft, homeDir, current) {
3552
- const permission2 = draft.permission ?? current?.permission ?? "default";
3976
+ const permission2 = draft.permission ?? current?.permission ?? SCHEDULE_DEFAULT_PERMISSION;
3553
3977
  const merged = draft.action === "create" ? {
3554
3978
  name: draft.name,
3555
3979
  prompt: draft.prompt,
3556
3980
  permission: permission2,
3557
3981
  workDir: draft.workDir ?? "",
3558
3982
  maxTurns: SCHEDULE_DEFAULTS.maxTurns,
3559
- maxBudgetUsd: budgetOf(draft),
3560
3983
  timeoutMs: SCHEDULE_DEFAULTS.timeoutMs,
3561
3984
  trigger: draft.trigger,
3562
3985
  allowRules: draft.allowRules ?? [],
3563
- bypassAcknowledged: permission2 === "bypass",
3986
+ bypassAcknowledged: approvalPolicyForLevel(permission2) === "never",
3564
3987
  homeDir
3565
3988
  } : {
3566
3989
  name: draft.name ?? current?.name ?? "",
@@ -3568,11 +3991,10 @@ function validateDraft(draft, homeDir, current) {
3568
3991
  permission: permission2,
3569
3992
  workDir: draft.workDir ?? current?.workDir ?? "",
3570
3993
  maxTurns: draft.maxTurns ?? current?.maxTurns ?? SCHEDULE_DEFAULTS.maxTurns,
3571
- maxBudgetUsd: budgetOf(draft, current),
3572
3994
  timeoutMs: draft.timeoutMs ?? current?.timeoutMs ?? SCHEDULE_DEFAULTS.timeoutMs,
3573
3995
  trigger: draft.trigger ?? current?.trigger ?? { kind: "cron", cycle: "daily", at: "09:00" },
3574
3996
  allowRules: draft.allowRules ?? current?.allowRules ?? [],
3575
- bypassAcknowledged: permission2 === "bypass",
3997
+ bypassAcknowledged: approvalPolicyForLevel(permission2) === "never",
3576
3998
  homeDir
3577
3999
  };
3578
4000
  return validateSchedule(merged).issues.map(
@@ -3666,15 +4088,14 @@ function summarizeSaved(draft, def, registration, current) {
3666
4088
  return lines.join("\n");
3667
4089
  }
3668
4090
  async function persist(save, draft, current) {
3669
- const permission2 = draft.permission ?? current?.permission ?? "default";
3670
- const acknowledged = { bypassAcknowledged: permission2 === "bypass" };
4091
+ const permission2 = draft.permission ?? current?.permission ?? SCHEDULE_DEFAULT_PERMISSION;
4092
+ const acknowledged = { bypassAcknowledged: approvalPolicyForLevel(permission2) === "never" };
3671
4093
  if (draft.action === "create") {
3672
4094
  return save.create(
3673
4095
  {
3674
4096
  name: draft.name,
3675
4097
  prompt: draft.prompt,
3676
- permission: draft.permission,
3677
- maxBudgetUsd: budgetOf(draft),
4098
+ permission: permission2,
3678
4099
  trigger: draft.trigger,
3679
4100
  ...draft.workDir === void 0 ? {} : { workDir: draft.workDir },
3680
4101
  ...draft.allowTools === void 0 ? {} : { allowTools: draft.allowTools },
@@ -3695,7 +4116,6 @@ async function persist(save, draft, current) {
3695
4116
  ...draft.enabled === void 0 ? {} : { enabled: draft.enabled },
3696
4117
  ...draft.maxTurns === void 0 ? {} : { maxTurns: draft.maxTurns },
3697
4118
  ...draft.timeoutMs === void 0 ? {} : { timeoutMs: draft.timeoutMs },
3698
- ...draft.maxBudgetUsd === void 0 ? {} : { maxBudgetUsd: draft.maxBudgetUsd },
3699
4119
  ...draft.allowTools === void 0 ? {} : { allowTools: draft.allowTools },
3700
4120
  ...draft.allowOperations === void 0 ? {} : { allowOperations: draft.allowOperations },
3701
4121
  ...draft.allowRules === void 0 ? {} : { allowRules: draft.allowRules }
@@ -3706,7 +4126,7 @@ async function persist(save, draft, current) {
3706
4126
  function createScheduleDraftTool(deps) {
3707
4127
  return {
3708
4128
  name: SCHEDULE_DRAFT_TOOL,
3709
- description: "\u5EFA\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u6216\u8005\u6539\u4E00\u6761\u5DF2\u6709\u7684\u3002\u5B9A\u65F6\u4EFB\u52A1\u662F\u300C\u5230\u70B9\u4E86\u81EA\u5DF1\u8DD1\u4E00\u6B21\u300D\u2014\u2014\u7528\u6237\u8BF4\u300C\u4EE5\u540E\u6BCF\u5929/\u6BCF\u5468\u5E2E\u6211\u505A\u67D0\u4EF6\u4E8B\u300D\u300C\u5B9A\u671F\u68C0\u67E5\u67D0\u67D0\u300D\uFF0C\u6216\u8005\u4F60\u53D1\u73B0\u67D0\u4EF6\u4E8B\u4ED6\u6BCF\u6B21\u90FD\u8981\u624B\u52A8\u505A\u4E00\u904D\u65F6\uFF0C\u7528\u5B83\u3002action=list \u770B\u73B0\u6709\u4EFB\u52A1\uFF08\u8981\u6539\u67D0\u6761\u5FC5\u987B\u5148 list \u62FF\u5230 id\uFF09\uFF1Baction=create \u76F4\u63A5\u5EFA\u4E00\u6761\uFF1Baction=update \u76F4\u63A5\u6539\u4E00\u6761\u3002\u26A0\uFE0F **\u5B83\u4EA4\u4E0A\u53BB\u5C31\u751F\u6548**\uFF08plan \u53EA\u8BFB\u6863\u4E0B\u9664\u5916\uFF0C\u90A3\u4E00\u6863\u53EA\u4EA4\u63D0\u6848\uFF09\u2014\u2014 \u5EFA\u5B8C\u5728\u4F60\u7684\u56DE\u590D\u91CC\u4EA4\u4EE3\u4E00\u53E5\u5C31\u884C\uFF0C\u522B\u95EE\u300C\u8981\u4E0D\u8981\u6211\u5EFA\u300D\u3002\u9884\u7B97\u4E0D\u7ED9\u5C31\u662F 200 \u7F8E\u5143\uFF1B\u8FD9\u6D3B\u660E\u663E\u66F4\u8F7B\u6216\u66F4\u91CD\u65F6\u81EA\u5DF1\u7ED9 maxBudgetUsd\u3002\u6743\u9650\u6863 plan/default/acceptEdits/auto/bypass \u90FD\u80FD\u63D0 \u2014\u2014 \u4F46**\u9ED8\u8BA4\u7528 plan**\uFF1A\u53EA\u8BFB\u6863\u5929\u7136\u96F6\u6388\u6743\u9762\uFF0C\u9700\u8981\u5199\u4E1C\u897F\u518D\u7528 default \u52A0\u4E00\u4EFD\u7CBE\u786E\u7684 allowRules\uFF08\u6BD4\u5982 terminal(git status)\uFF09\uFF0C\u522B\u4E00\u4E0A\u6765\u5C31 bypass\u3002",
4129
+ description: "\u5EFA\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u6216\u8005\u6539\u4E00\u6761\u5DF2\u6709\u7684\u3002\u5B9A\u65F6\u4EFB\u52A1\u662F\u300C\u5230\u70B9\u4E86\u81EA\u5DF1\u8DD1\u4E00\u6B21\u300D\u2014\u2014\u7528\u6237\u8BF4\u300C\u4EE5\u540E\u6BCF\u5929/\u6BCF\u5468\u5E2E\u6211\u505A\u67D0\u4EF6\u4E8B\u300D\u300C\u5B9A\u671F\u68C0\u67E5\u67D0\u67D0\u300D\uFF0C\u6216\u8005\u4F60\u53D1\u73B0\u67D0\u4EF6\u4E8B\u4ED6\u6BCF\u6B21\u90FD\u8981\u624B\u52A8\u505A\u4E00\u904D\u65F6\uFF0C\u7528\u5B83\u3002action=list \u770B\u73B0\u6709\u4EFB\u52A1\uFF08\u8981\u6539\u67D0\u6761\u5FC5\u987B\u5148 list \u62FF\u5230 id\uFF09\uFF1Baction=create \u76F4\u63A5\u5EFA\u4E00\u6761\uFF1Baction=update \u76F4\u63A5\u6539\u4E00\u6761\u3002\u26A0\uFE0F **\u5B83\u4EA4\u4E0A\u53BB\u5C31\u751F\u6548**\uFF08plan \u53EA\u8BFB\u6863\u4E0B\u9664\u5916\uFF0C\u90A3\u4E00\u6863\u53EA\u4EA4\u63D0\u6848\uFF09\u2014\u2014 \u5EFA\u5B8C\u5728\u4F60\u7684\u56DE\u590D\u91CC\u4EA4\u4EE3\u4E00\u53E5\u5C31\u884C\uFF0C\u522B\u95EE\u300C\u8981\u4E0D\u8981\u6211\u5EFA\u300D\u3002\u6743\u9650\u6863\u4E0D\u7ED9\u5C31\u662F auto\uFF08\u65B0\u5EFA\u4EFB\u52A1\u7684\u7F3A\u7701\uFF09\u2014\u2014 \u5B9A\u65F6\u4EFB\u52A1\u6CA1\u4EBA\u53EF\u95EE\uFF0C\u4F4E\u6863\u5728\u8FD9\u91CC\u7B49\u4E8E\u300C\u5230\u70B9\u5FC5\u7136\u5931\u8D25\u300D\u3002\u53EA\u6709\u7528\u6237\u660E\u786E\u8981\u6C42\u300C\u53EA\u8BFB / \u522B\u52A8\u4E1C\u897F\u300D\u65F6\u624D\u7528 plan\uFF0C\u90A3\u7C7B\u4EFB\u52A1\u5929\u7136\u96F6\u6388\u6743\u9762\u3002",
3710
4130
  parameters: {
3711
4131
  type: "object",
3712
4132
  properties: {
@@ -3750,12 +4170,8 @@ function createScheduleDraftTool(deps) {
3750
4170
  },
3751
4171
  permission: {
3752
4172
  type: "string",
3753
- description: "\u6743\u9650\u6863\u3002plan=\u53EA\u8BFB\uFF08\u8BFB\u4E00\u904D\u5199\u4EFD\u62A5\u544A\u7684\u4EFB\u52A1\u5C31\u8BE5\u7528\u5B83\uFF0C\u5929\u7136\u96F6\u6388\u6743\u9762\uFF09\uFF1Bdefault=\u6309\u9700\u786E\u8BA4\uFF1BacceptEdits=\u81EA\u52A8\u63A5\u53D7\u6539\u52A8\uFF1Bauto=\u4E0D\u518D\u786E\u8BA4\uFF1Bbypass=\u8DF3\u8FC7\u5168\u90E8\u68C0\u67E5\uFF08**\u6700\u540E\u624B\u6BB5**\uFF1A\u5230\u70B9\u4E86\u5B83\u60F3\u8DD1\u4EC0\u4E48\u5C31\u8DD1\u4EC0\u4E48\uFF09",
3754
- enum: ["plan", "default", "acceptEdits", "auto", "bypass"]
3755
- },
3756
- maxBudgetUsd: {
3757
- type: "number",
3758
- description: "\u6BCF\u6B21\u6700\u591A\u82B1\u591A\u5C11\u94B1\uFF08\u7F8E\u5143\uFF0C\u8981\u5927\u4E8E 0\uFF09\u3002\u4E0D\u7ED9\u5C31\u662F 200\u3002\u8FD9\u6D3B\u660E\u663E\u66F4\u8F7B\uFF08\u8BFB\u4E00\u4E2A\u6587\u4EF6\u3001\u53D1\u4E00\u6761\u6D88\u606F\uFF09\u5C31\u7ED9\u5C0F\u4E00\u70B9\uFF0C\u660E\u663E\u66F4\u91CD\uFF08\u626B\u5927\u4ED3\u3001\u591A\u8F6E\u8C03\u7814\uFF09\u5C31\u653E\u5927 \u2014\u2014 \u82B1\u8D85\u4E86\u90A3\u4E00\u8F6E\u4F1A\u505C\u4F4F"
4173
+ description: "\u6743\u9650\u6863\u3002\u4E0D\u7ED9\u5C31\u662F auto\uFF08**\u65B0\u5EFA\u4EFB\u52A1\u7684\u7F3A\u7701**\uFF0C2026-10-27 \u8D77\uFF1A\u5B9A\u65F6\u4EFB\u52A1\u6CA1\u4EBA\u53EF\u95EE\uFF0C\u4F4E\u6863\u5728\u8FD9\u91CC\u7B49\u4E8E\u300C\u5230\u70B9\u5FC5\u7136\u5931\u8D25\u300D\uFF09\u3002auto=\u5B8C\u5168\u8BBF\u95EE\uFF08\u4E0D\u518D\u786E\u8BA4\uFF0C\u53EA\u5269\u707E\u96BE\u7EA7\u4F1A\u88AB\u62E6\u4E0B\uFF09\uFF1BacceptEdits=\u8BF7\u6C42\u6279\u51C6\uFF1Bdefault=\u9010\u6761\u786E\u8BA4\uFF1Bplan=\u53EA\u8BFB\uFF08\u8BFB\u4E00\u904D\u5199\u4EFD\u62A5\u544A\u7684\u4EFB\u52A1\u7528\u5B83\uFF0C\u5929\u7136\u96F6\u6388\u6743\u9762\uFF09\u3002**\u53EA\u6709\u5728\u7528\u6237\u660E\u786E\u8BF4\u4E86\u8981\u7A84\u4E00\u6863\u65F6\u624D\u964D**",
4174
+ enum: ["plan", "default", "acceptEdits", "auto"]
3759
4175
  },
3760
4176
  allowTools: {
3761
4177
  type: "array",
@@ -3914,6 +4330,21 @@ function buildFtsMatchQuery(safeQuery) {
3914
4330
  function buildTrigramMatchQuery(safeQuery) {
3915
4331
  return safeQuery.split(/\s+/).filter(Boolean).map((word) => `"${word}"`).join(" ");
3916
4332
  }
4333
+ function mergeRankedRows(primary, secondary, limit) {
4334
+ const merged = [];
4335
+ const seen = /* @__PURE__ */ new Set();
4336
+ for (const row of primary) {
4337
+ seen.add(row.id);
4338
+ merged.push(row);
4339
+ if (merged.length >= limit) return merged;
4340
+ }
4341
+ for (const row of secondary) {
4342
+ if (seen.has(row.id)) continue;
4343
+ merged.push(row);
4344
+ if (merged.length >= limit) return merged;
4345
+ }
4346
+ return merged;
4347
+ }
3917
4348
  var TRIGRAM_MIN_CHARS = 3;
3918
4349
  function belowTrigramFloor(safeQuery) {
3919
4350
  const words = safeQuery.split(/\s+/).filter(Boolean);
@@ -3995,12 +4426,15 @@ function traceSession(db, input) {
3995
4426
  truncated
3996
4427
  };
3997
4428
  }
4429
+ // src/session/types.ts
4430
+ var DELEGATE_SESSION_SOURCE = "delegate";
3998
4431
  // src/session/query.ts
3999
4432
  var SESSION_SEARCH_TOOL = "session_search";
4000
4433
  var SESSION_MESSAGES_SEARCH_TOOL = "session_messages_search";
4001
4434
  var SESSION_MESSAGES_READ_TOOL = "session_messages_read";
4002
4435
  var SESSION_TRACE_TOOL = "session_trace";
4003
4436
  var SESSION_LIST_TOOL = "session_list";
4437
+ var DELEGATE_CLAUSE = "AND COALESCE(s.source, '') != @delegateSource";
4004
4438
  var SESSION_QUERY_TOOLS = Object.freeze([
4005
4439
  SESSION_SEARCH_TOOL,
4006
4440
  SESSION_MESSAGES_SEARCH_TOOL,
@@ -4103,8 +4537,9 @@ var SessionQueries = class {
4103
4537
  listSessions(input) {
4104
4538
  const vis = resolveSessionVisibility({ ...input, db: this.db });
4105
4539
  const archivedClause = input.archived === "all" ? "" : input.archived === "only" ? "AND s.archived = 1" : "AND s.archived = 0";
4106
- const where = `${vis.clause} ${archivedClause}`;
4107
- const total = this.db.prepare(`SELECT count(*) AS n FROM sessions s WHERE ${where}`).get(vis.params).n;
4540
+ const where = `${vis.clause} ${archivedClause} ${DELEGATE_CLAUSE}`;
4541
+ const params = { ...vis.params, delegateSource: DELEGATE_SESSION_SOURCE };
4542
+ const total = this.db.prepare(`SELECT count(*) AS n FROM sessions s WHERE ${where}`).get(params).n;
4108
4543
  const rows = this.db.prepare(
4109
4544
  `SELECT s.id,
4110
4545
  COALESCE(NULLIF(s.display_name, ''), s.title) AS title,
@@ -4116,7 +4551,7 @@ var SessionQueries = class {
4116
4551
  WHERE ${where}
4117
4552
  ORDER BY COALESCE(s.updated_at, s.started_at) DESC
4118
4553
  LIMIT @limit`
4119
- ).all({ ...vis.params, limit: input.limit });
4554
+ ).all({ ...params, limit: input.limit });
4120
4555
  return {
4121
4556
  total,
4122
4557
  sessions: rows.map((row) => ({
@@ -4141,9 +4576,19 @@ var SessionQueries = class {
4141
4576
  ORDER BY rank
4142
4577
  LIMIT @limit`;
4143
4578
  const limitRows = limit * CANDIDATE_FACTOR;
4144
- const fts = this.db.prepare(sql("messages_fts")).all({ ...visParams, query: buildFtsMatchQuery(safe), limit: limitRows });
4145
- if (fts.length > 0) return fts;
4146
- return this.db.prepare(sql("messages_fts_trigram")).all({ ...visParams, query: buildTrigramMatchQuery(safe), limit: limitRows });
4579
+ return this.rankedRows(sql, safe, { ...visParams, limit: limitRows }, limitRows);
4580
+ }
4581
+ rankedRows(sql, safe, params, limit) {
4582
+ const porter = this.db.prepare(sql("messages_fts")).all({
4583
+ ...params,
4584
+ query: buildFtsMatchQuery(safe)
4585
+ });
4586
+ if (belowTrigramFloor(safe)) return porter;
4587
+ const trigram = this.db.prepare(sql("messages_fts_trigram")).all({
4588
+ ...params,
4589
+ query: buildTrigramMatchQuery(safe)
4590
+ });
4591
+ return mergeRankedRows(porter, trigram, limit);
4147
4592
  }
4148
4593
  titleRows(safe, limit, visClause, visParams) {
4149
4594
  return this.db.prepare(
@@ -4190,9 +4635,7 @@ var SessionQueries = class {
4190
4635
  WHERE ${table} MATCH @query AND m.session_id = @sid
4191
4636
  ORDER BY rank
4192
4637
  LIMIT @limit`;
4193
- const fts = this.db.prepare(sql("messages_fts")).all({ query: buildFtsMatchQuery(safe), sid: sessionId, limit });
4194
- if (fts.length > 0) return fts;
4195
- return this.db.prepare(sql("messages_fts_trigram")).all({ query: buildTrigramMatchQuery(safe), sid: sessionId, limit });
4638
+ return this.rankedRows(sql, safe, { sid: sessionId, limit }, limit);
4196
4639
  }
4197
4640
  };
4198
4641
  // src/tools/builtin/session-list.ts
@@ -4336,41 +4779,41 @@ function clampWindow(raw, fallback2, max) {
4336
4779
  function stamp2(ms) {
4337
4780
  return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
4338
4781
  }
4339
- function budgetOf2(total) {
4782
+ function budgetOf(total) {
4340
4783
  const shares = ANCHOR_SHARE + Math.max(0, total - 1);
4341
4784
  const unit = Math.max(MIN_BODY, Math.floor(BODY_BUDGET / shares));
4342
4785
  return { anchor: unit * ANCHOR_SHARE, other: unit };
4343
4786
  }
4344
- function body(text, budget2) {
4787
+ function body(text, budget) {
4345
4788
  const flat = text.replace(/\r\n/g, "\n").trimEnd();
4346
- if (flat.length <= budget2) return flat;
4347
- const rest = flat.length - budget2;
4348
- return `${flat.slice(0, budget2)}
4789
+ if (flat.length <= budget) return flat;
4790
+ const rest = flat.length - budget;
4791
+ return `${flat.slice(0, budget)}
4349
4792
  \u2026[cut ${rest} more chars \u2014 lower before/after to spend the budget on fewer messages]`;
4350
4793
  }
4351
4794
  function surfaceOf(record, isCurrent) {
4352
4795
  if (!isCurrent) return record.surface === "active" ? "active there" : "compacted there";
4353
4796
  return record.surface === "active" ? "still in your context" : "compacted out of your context";
4354
4797
  }
4355
- function renderRecord(record, mark, budget2, isCurrent) {
4798
+ function renderRecord(record, mark, budget, isCurrent) {
4356
4799
  const tool = record.toolName ? ` \xB7 ${record.toolName}` : "";
4357
4800
  return [
4358
4801
  `${mark} #${record.messageId} [${record.role}${tool} \xB7 ${stamp2(record.timestamp)} \xB7 ${surfaceOf(record, isCurrent)}]`,
4359
- ` ${body(record.content, budget2).split("\n").join("\n ")}`
4802
+ ` ${body(record.content, budget).split("\n").join("\n ")}`
4360
4803
  ].join("\n");
4361
4804
  }
4362
4805
  function renderWindow(out, sessionId, isCurrent) {
4363
4806
  const anchor = out.anchor;
4364
4807
  if (!anchor) return "";
4365
4808
  const count = out.before.length + out.after.length + 1;
4366
- const budget2 = budgetOf2(count);
4809
+ const budget = budgetOf(count);
4367
4810
  return [
4368
4811
  `Session ${sessionId}${isCurrent ? " (this session)" : ""} \u2014 ${out.title || "(untitled)"}`,
4369
4812
  `${count} of ${out.total} message(s), around #${anchor.messageId}:`,
4370
4813
  "",
4371
- ...out.before.map((r) => renderRecord(r, " ", budget2.other, isCurrent)),
4372
- renderRecord(anchor, "\xBB", budget2.anchor, isCurrent),
4373
- ...out.after.map((r) => renderRecord(r, " ", budget2.other, isCurrent))
4814
+ ...out.before.map((r) => renderRecord(r, " ", budget.other, isCurrent)),
4815
+ renderRecord(anchor, "\xBB", budget.anchor, isCurrent),
4816
+ ...out.after.map((r) => renderRecord(r, " ", budget.other, isCurrent))
4374
4817
  ].join("\n");
4375
4818
  }
4376
4819
  function createSessionMessagesReadTool(deps) {
@@ -5225,7 +5668,7 @@ function available(tools) {
5225
5668
  if (tools.length === 0) {
5226
5669
  return "No deferred tools in this session \u2014 every tool is already in your tool list.";
5227
5670
  }
5228
- const names = tools.map((t81) => t81.name).sort();
5671
+ const names = tools.map((t83) => t83.name).sort();
5229
5672
  const shown = names.slice(0, MAX_LISTED);
5230
5673
  const rest = names.length - shown.length;
5231
5674
  return `Searchable tools: ${shown.join(", ")}${rest > 0 ? ` (\u2026and ${rest} more)` : ""}.`;
@@ -5345,19 +5788,22 @@ var TrackerDB = class {
5345
5788
  );
5346
5789
  this.db.transaction(() => {
5347
5790
  this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
5348
- for (const t81 of todos) {
5791
+ for (const t83 of todos) {
5349
5792
  insertStmt.run({
5350
- id: t81.id,
5793
+ id: t83.id,
5351
5794
  session_id: sessionId,
5352
- content: t81.content,
5353
- status: t81.status,
5354
- reason: t81.reason || null,
5355
- created_at: t81.created_at,
5356
- updated_at: t81.updated_at
5795
+ content: t83.content,
5796
+ status: t83.status,
5797
+ reason: t83.reason || null,
5798
+ created_at: t83.created_at,
5799
+ updated_at: t83.updated_at
5357
5800
  });
5358
5801
  }
5359
5802
  })();
5360
5803
  }
5804
+ deleteBySession(sessionId) {
5805
+ return this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId).changes;
5806
+ }
5361
5807
  close() {
5362
5808
  releaseDatabase(this.path);
5363
5809
  }
@@ -5375,9 +5821,9 @@ function formatTodoList(todos, pendingCount) {
5375
5821
  cancelled: "\u5DF2\u53D6\u6D88"
5376
5822
  };
5377
5823
  lines.push(`[todo] \u72B6\u6001\u6C47\u603B: ${pendingCount} \u4E2A\u5F85\u5B8C\u6210`);
5378
- for (const t81 of todos) {
5379
- const label = statusLabel[t81.status] || t81.status;
5380
- lines.push(`- [${label}] ${t81.content}${t81.reason ? ` \u2014\u2014 ${t81.reason}` : ""}`);
5824
+ for (const t83 of todos) {
5825
+ const label = statusLabel[t83.status] || t83.status;
5826
+ lines.push(`- [${label}] ${t83.content}${t83.reason ? ` \u2014\u2014 ${t83.reason}` : ""}`);
5381
5827
  }
5382
5828
  return lines.join("\n");
5383
5829
  }
@@ -5451,14 +5897,14 @@ function createTodoTool(db) {
5451
5897
  };
5452
5898
  }
5453
5899
  const now = (/* @__PURE__ */ new Date()).toISOString();
5454
- const existing = new Map(db.listBySession(ctx.sessionId).map((t81) => [t81.id, t81]));
5455
- const todos = rawTodos.map((t81) => {
5456
- const prev = t81.id ? existing.get(t81.id) : void 0;
5457
- const status = isValidStatus(t81.status) ? t81.status : "pending";
5458
- const reason9 = normalizeReason(status, t81.reason);
5900
+ const existing = new Map(db.listBySession(ctx.sessionId).map((t83) => [t83.id, t83]));
5901
+ const todos = rawTodos.map((t83) => {
5902
+ const prev = t83.id ? existing.get(t83.id) : void 0;
5903
+ const status = isValidStatus(t83.status) ? t83.status : "pending";
5904
+ const reason9 = normalizeReason(status, t83.reason);
5459
5905
  return {
5460
- id: t81.id || generateShortId(),
5461
- content: String(t81.content || "").slice(0, 500),
5906
+ id: t83.id || generateShortId(),
5907
+ content: String(t83.content || "").slice(0, 500),
5462
5908
  status,
5463
5909
  ...reason9 ? { reason: reason9 } : {},
5464
5910
  created_at: prev?.created_at || now,
@@ -5466,7 +5912,7 @@ function createTodoTool(db) {
5466
5912
  };
5467
5913
  });
5468
5914
  db.replaceAll(ctx.sessionId, todos);
5469
- const pendingCount = todos.filter((t81) => isOpenTodo(t81.status)).length;
5915
+ const pendingCount = todos.filter((t83) => isOpenTodo(t83.status)).length;
5470
5916
  return {
5471
5917
  success: true,
5472
5918
  output: formatTodoList(todos, pendingCount),
@@ -5813,7 +6259,7 @@ function intersectNames(base2, incoming) {
5813
6259
  if (!base2) return incoming ? [...incoming] : void 0;
5814
6260
  if (!incoming) return [...base2];
5815
6261
  const baseSet = new Set(base2);
5816
- return incoming.filter((t81) => baseSet.has(t81));
6262
+ return incoming.filter((t83) => baseSet.has(t83));
5817
6263
  }
5818
6264
  function findRole(roles, name) {
5819
6265
  return roles.find((r) => r.name === name);
@@ -5965,7 +6411,7 @@ function roleScopedProvider(base2, role, blocked) {
5965
6411
  return allow ? allow.has(name) : true;
5966
6412
  };
5967
6413
  return {
5968
- list: () => base2.list().filter((t81) => permitted(t81.name)),
6414
+ list: () => base2.list().filter((t83) => permitted(t83.name)),
5969
6415
  get: (name) => permitted(name) ? base2.get(name) : void 0
5970
6416
  };
5971
6417
  }
@@ -5992,7 +6438,7 @@ function createRoleScope(base2, sessionRole, blocked) {
5992
6438
  }
5993
6439
  function unknownRoleTools(base2, role, blocked) {
5994
6440
  if (!role?.tools) return [];
5995
- const available2 = new Set(base2.list().map((t81) => t81.name));
6441
+ const available2 = new Set(base2.list().map((t83) => t83.name));
5996
6442
  const blockedSet = new Set(blocked);
5997
6443
  return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
5998
6444
  }
@@ -6355,7 +6801,7 @@ function createToolScope(base2) {
6355
6801
  const permitted = (name) => allow ? allow.has(name) : true;
6356
6802
  return {
6357
6803
  provider: {
6358
- list: () => base2.list().filter((t81) => permitted(t81.name)),
6804
+ list: () => base2.list().filter((t83) => permitted(t83.name)),
6359
6805
  get: (name) => permitted(name) ? base2.get(name) : void 0
6360
6806
  },
6361
6807
  enter: (next) => {
@@ -6376,6 +6822,8 @@ function createToolScope(base2) {
6376
6822
  var HOOK_TYPE_FLAGS = {
6377
6823
  "session:start": true,
6378
6824
  "session:end": true,
6825
+ "subagent:start": true,
6826
+ "subagent:stop": true,
6379
6827
  "turn:before": true,
6380
6828
  "turn:after": true,
6381
6829
  "tool:before": true,
@@ -6525,7 +6973,7 @@ function scanHooks(dir, issues) {
6525
6973
  return [...parsed.entries].map(([type, list]) => ({
6526
6974
  type,
6527
6975
  count: list.reduce((sum, entry) => sum + entry.hooks.length, 0)
6528
- })).filter((t81) => t81.count > 0).sort((a, b) => a.type.localeCompare(b.type));
6976
+ })).filter((t83) => t83.count > 0).sort((a, b) => a.type.localeCompare(b.type));
6529
6977
  }
6530
6978
  function scanMcp(dir, plugin, issues) {
6531
6979
  const path = join(dir, PLUGIN_LAYOUT.mcp);
@@ -8464,7 +8912,8 @@ async function setPluginEnabled(name, enabled, opts) {
8464
8912
  const verb = enabled ? t("plugin_lifecycle.verb_enable") : t("plugin_lifecycle.verb_disable");
8465
8913
  return {
8466
8914
  ok: true,
8467
- message: changed ? t("plugin_lifecycle.toggled", { verb, name }) : t("plugin_lifecycle.already", { name, verb })
8915
+ message: changed ? t("plugin_lifecycle.toggled", { verb, name }) : t("plugin_lifecycle.already", { name, verb }),
8916
+ changed
8468
8917
  };
8469
8918
  }
8470
8919
  async function updatePlugin(name, opts) {
@@ -8713,14 +9162,14 @@ var CheckpointStore = class {
8713
9162
  }
8714
9163
  async enforceTotalSize(protectSession, protectTurnIndex) {
8715
9164
  const turns = await this.collectTurns();
8716
- let total = turns.reduce((sum, t81) => sum + t81.bytes, 0);
9165
+ let total = turns.reduce((sum, t83) => sum + t83.bytes, 0);
8717
9166
  if (total <= this.maxTotalBytes) return;
8718
9167
  turns.sort((a, b) => a.createdAt - b.createdAt);
8719
- for (const t81 of turns) {
9168
+ for (const t83 of turns) {
8720
9169
  if (total <= this.maxTotalBytes) return;
8721
- if (t81.sessionId === protectSession && t81.turnIndex === protectTurnIndex) continue;
8722
- await this.removeTurn(t81.sessionId, t81.turnIndex);
8723
- total -= t81.bytes;
9170
+ if (t83.sessionId === protectSession && t83.turnIndex === protectTurnIndex) continue;
9171
+ await this.removeTurn(t83.sessionId, t83.turnIndex);
9172
+ total -= t83.bytes;
8724
9173
  }
8725
9174
  }
8726
9175
  async collectTurns() {
@@ -9040,7 +9489,7 @@ async function snapshotFiles(paths) {
9040
9489
  }
9041
9490
  async function collectDiffs(before, workDir) {
9042
9491
  const out = [];
9043
- let budget2 = MAX_CALL_BYTES;
9492
+ let budget = MAX_CALL_BYTES;
9044
9493
  for (const [path, old] of before) {
9045
9494
  if (old.kind === "opaque") continue;
9046
9495
  const now = await readSnapshot(path);
@@ -9048,8 +9497,8 @@ async function collectDiffs(before, workDir) {
9048
9497
  if (old.kind === "absent" && now.kind === "absent") continue;
9049
9498
  if (old.kind === "text" && now.kind === "text" && old.text === now.text) continue;
9050
9499
  const bytes = (old.kind === "text" ? old.bytes : 0) + (now.kind === "text" ? now.bytes : 0);
9051
- if (bytes > budget2) continue;
9052
- budget2 -= bytes;
9500
+ if (bytes > budget) continue;
9501
+ budget -= bytes;
9053
9502
  out.push({
9054
9503
  filePath: workspaceDisplayPath(path, workDir),
9055
9504
  ...old.kind === "text" ? { oldContent: old.text } : {},
@@ -9094,10 +9543,11 @@ var TaskNoticeTracker = class {
9094
9543
  }
9095
9544
  const lines = [];
9096
9545
  for (const task of tasks) {
9097
- if (task.kind !== "command") continue;
9546
+ if (task.kind === "shell") continue;
9098
9547
  const before = this.seen.get(task.id);
9099
9548
  if (before === task.status) continue;
9100
9549
  this.seen.set(task.id, task.status);
9550
+ if (task.kind === "agent" && task.status === "running") continue;
9101
9551
  lines.push(describeBackgroundTask(task));
9102
9552
  }
9103
9553
  if (lines.length === 0) return null;
@@ -9107,15 +9557,16 @@ var TaskNoticeTracker = class {
9107
9557
  function describeBackgroundTask(task) {
9108
9558
  const text = task.label;
9109
9559
  const cmd = text.length > 60 ? `${text.slice(0, 60)}\u2026` : text;
9560
+ const agent = task.kind === "agent";
9110
9561
  switch (task.status) {
9111
9562
  case "running":
9112
9563
  return `- ${task.id} \u8FD0\u884C\u4E2D\uFF1A${cmd}`;
9113
9564
  case "exited":
9114
- return `- ${task.id} \u5DF2\u7ED3\u675F\uFF0C\u9000\u51FA\u7801 ${task.exitCode}\uFF1A${cmd}`;
9565
+ return agent ? `- ${task.id} \u5DF2\u7ED3\u675F\uFF1A${cmd}` : `- ${task.id} \u5DF2\u7ED3\u675F\uFF0C\u9000\u51FA\u7801 ${task.exitCode}\uFF1A${cmd}`;
9115
9566
  case "killed":
9116
9567
  return `- ${task.id} \u5DF2\u88AB\u505C\u6B62\uFF1A${cmd}`;
9117
9568
  case "failed":
9118
- return `- ${task.id} \u542F\u52A8\u5931\u8D25\uFF1A${cmd}`;
9569
+ return agent ? `- ${task.id} \u5931\u8D25\uFF1A${cmd}` : `- ${task.id} \u542F\u52A8\u5931\u8D25\uFF1A${cmd}`;
9119
9570
  case "closed":
9120
9571
  return `- ${task.id} \u5DF2\u5173\u95ED\uFF1A${cmd}`;
9121
9572
  case "reaped":
@@ -9208,6 +9659,19 @@ function skillNotes(input, skillTokens) {
9208
9659
  }
9209
9660
  return notes;
9210
9661
  }
9662
+ function cacheStats(usage) {
9663
+ if (!usage) return void 0;
9664
+ const hit = usage.cacheHitTokens;
9665
+ const write = usage.cacheWriteTokens;
9666
+ if (hit === void 0 && write === void 0) return void 0;
9667
+ const prompt = promptTokens(usage);
9668
+ return {
9669
+ hit: hit ?? 0,
9670
+ write: write ?? 0,
9671
+ prompt,
9672
+ hitPercent: prompt > 0 ? Math.round((hit ?? 0) / prompt * 100) : 0
9673
+ };
9674
+ }
9211
9675
  function buildContextBreakdown(input) {
9212
9676
  const toolTokens = input.tools.reduce((sum, { tool }) => sum + toolDefinitionTokens(tool), 0);
9213
9677
  const { detail, counts, notes } = toolDetail(input, toolTokens);
@@ -9229,7 +9693,16 @@ ${input.system.rules}`),
9229
9693
  notes.push({ code: "near_limit", params: { percent: Math.round(used / input.limit * 100) } });
9230
9694
  }
9231
9695
  const hints = notes.map((note) => t(NOTE_KEY[note.code], note.params));
9232
- return { limit: input.limit, used, remaining, segments, hints, notes };
9696
+ const cache = cacheStats(input.lastUsage);
9697
+ return {
9698
+ limit: input.limit,
9699
+ used,
9700
+ remaining,
9701
+ segments,
9702
+ hints,
9703
+ notes,
9704
+ ...cache ? { cache } : {}
9705
+ };
9233
9706
  }
9234
9707
  // src/cost/display.ts
9235
9708
  var currency;
@@ -9557,6 +10030,7 @@ function systemPromptSegments(params) {
9557
10030
  "- \u505A\u6CD5\u6709\u4E24\u79CD\u5408\u7406\u8D70\u6CD5\u3001\u4E14\u9009\u9519\u4E86\u8981\u767D\u5E72\u65F6\uFF0C\u624D\u505C\u4E0B\u6765\u95EE\u4E00\u53E5\uFF1B\u6D3B\u591A\u3001\u8981\u82B1\u65F6\u95F4\u3001\u6B65\u9AA4\u91CD\u590D\u90FD\u4E0D\u662F\u8981\u95EE\u7684\u7406\u7531",
9558
10031
  "- \u6539\u5B8C\u4EE3\u7801\u522B\u76F4\u63A5\u8BF4\u300C\u5B8C\u6210\u4E86\u300D\uFF1A\u5148\u8DD1\u4E00\u904D\u8FD9\u4E2A\u9879\u76EE\u81EA\u5DF1\u7684\u68C0\u67E5\uFF08\u6D4B\u8BD5 / \u6784\u5EFA / \u7C7B\u578B\u68C0\u67E5\uFF0C\u547D\u4EE4\u4E00\u822C\u5728 README \u6216 package.json \u91CC\uFF09\u3002\u8DD1\u4E0D\u4E86\u6216\u8005\u6CA1\u8DD1\u5C31\u7167\u5B9E\u8BF4\u4F60\u6CA1\u9A8C\u8BC1\u8FC7\uFF0C\u522B\u5199\u300C\u5E94\u8BE5\u6CA1\u95EE\u9898\u300D",
9559
10032
  "- \u88AB\u6743\u9650\u62E6\u4E0B\u4E4B\u540E**\u522B\u6362\u6761\u8DEF\u518D\u6765**\uFF1A\u4E0D\u8BB8\u6539\u7528 terminal / code_exec \u53BB\u5199\u540C\u4E00\u6837\u4E1C\u897F\uFF0C\u4E5F\u4E0D\u8BB8\u62FF\u300C\u4EA4\u4E00\u4EFD\u8BA1\u5212\u300D\u53BB\u6362\u6743\u9650\u3002\u5C31\u5730\u6536\u624B\uFF0C\u8BF4\u6E05\u4F60\u8981\u505A\u4EC0\u4E48\u3001\u5361\u5728\u54EA\uFF0C\u8BF7\u7528\u6237\u653E\u6743\u3002\u7ED5\u8FC7\u5BA1\u6279\u6BD4\u8FD9\u4EF6\u4E8B\u505A\u4E0D\u6210\u4E25\u91CD\u5F97\u591A",
10033
+ "- \u547D\u4EE4\u88AB**\u6C99\u7BB1**\u6321\u4E0B\uFF08\u9519\u8BEF\u7801 2008 / 2009\uFF0C\u4E0D\u662F\u5BA1\u6279\u62D2\u7EDD\uFF09\u65F6\u6709\u4E00\u6B21\u6B63\u89C4\u7684\u51FA\u8DEF\uFF1A\u786E\u8BA4\u8FD9\u4E00\u6B21\u5FC5\u987B\u8D8A\u754C\uFF0C\u5C31\u5E26\u4E0A `sandbox_permissions` \u548C `justification`\uFF08\u4E00\u53E5\u8BDD\u8BF4\u660E\u4E3A\u4EC0\u4E48\uFF09\u518D\u8C03\u4E00\u6B21\uFF0C\u7528\u6237\u4F1A\u5355\u72EC\u6279 \u2014\u2014 \u53EA\u653E\u5F00\u8FD9\u4E00\u6B21\u3002\u522B\u62FF\u5B83\u5F53\u4E07\u7528\u5F00\u5173\uFF1A\u80FD\u632A\u8FDB\u5DE5\u4F5C\u533A\u3001\u80FD\u6362\u4E2A\u4E0D\u9700\u8981\u8D8A\u754C\u7684\u5199\u6CD5\uFF0C\u5C31\u5148\u90A3\u4E48\u529E\uFF0C\u88AB\u7528\u6237\u62D2\u4E86\u5C31\u6536\u624B",
9560
10034
  "- \u7528\u6237\u7ED9\u4E86\u8303\u56F4\u5C31\u628A\u90A3\u4E2A\u8303\u56F4\u505A\u5B8C\uFF1A\u8BF4 100 \u6761\u5C31\u662F 100 \u6761\u3002\u505A 3 \u6761\u518D\u95EE\u300C\u8981\u4E0D\u8981\u7EE7\u7EED\u300D\u662F\u6CA1\u505A\u5B8C\uFF0C\u4E0D\u662F\u8C28\u614E",
9561
10035
  "- \u8BE5\u7701\u7684\u662F\u91CD\u590D\u8C03\u540C\u4E00\u6B21\u5DE5\u5177\uFF08\u540C\u6837\u7684\u53C2\u6570\u95EE\u7B2C\u4E8C\u904D\uFF09\uFF0C\u4E0D\u662F\u5C11\u505A\u51E0\u4EF6\u4E8B \u2014\u2014 \u5DE5\u5177\u8C03\u7528\u6B21\u6570\u4ECE\u6765\u4E0D\u662F\u8981\u538B\u7684\u6307\u6807",
9562
10036
  "- \u8BF4\u4E86\u4E0B\u4E00\u6B65\u5C31\u5F53\u573A\u505A\uFF1A\u300C\u6211\u5148\u53BB\u67E5\u4E00\u4E0B\u300D\u300Clet me \u68C0\u67E5\u4E00\u4E0B\u300D\u8FD9\u79CD\u8BDD\u540E\u9762\u5FC5\u987B\u7D27\u8DDF\u4E00\u6B21\u5DE5\u5177\u8C03\u7528\uFF0C\u522B\u62FF\u5B83\u5F53\u4E00\u8F6E\u56DE\u590D\u7684\u7ED3\u5C3E",
@@ -9637,10 +10111,10 @@ function renderSettings(s) {
9637
10111
  bits.push(`\u4E0A\u4E0B\u6587\u7A97\u53E3 ${s.contextLength}`);
9638
10112
  bits.push(`\u81EA\u52A8\u538B\u7F29 ${s.compression.enabled ? `\u5F00\uFF08${s.compression.threshold}\uFF09` : "\u5173"}`);
9639
10113
  bits.push(`\u7EC8\u7AEF\u6C99\u7BB1 ${s.sandboxTerminal ? "\u5F00" : "\u5173"}`);
9640
- const budget2 = [];
9641
- if (s.budget?.maxCostUsd !== void 0) budget2.push(`$${s.budget.maxCostUsd}`);
9642
- if (s.budget?.maxTokens !== void 0) budget2.push(`${s.budget.maxTokens} tokens`);
9643
- bits.push(`\u9884\u7B97\u4E0A\u9650 ${budget2.length > 0 ? budget2.join(" / ") : "\u672A\u8BBE"}`);
10114
+ const budget = [];
10115
+ if (s.budget?.maxCostUsd !== void 0) budget.push(`$${s.budget.maxCostUsd}`);
10116
+ if (s.budget?.maxTokens !== void 0) budget.push(`${s.budget.maxTokens} tokens`);
10117
+ bits.push(`\u9884\u7B97\u4E0A\u9650 ${budget.length > 0 ? budget.join(" / ") : "\u672A\u8BBE"}`);
9644
10118
  return [
9645
10119
  "<settings>",
9646
10120
  bits.join(" \xB7 "),
@@ -10538,6 +11012,111 @@ function getGitStatus(rootDir) {
10538
11012
  function findProjectInstructions(rootDir, workDir = rootDir) {
10539
11013
  return findInstructionFiles(rootDir, workDir)[0];
10540
11014
  }
11015
+ var DATA_URL_PREFIX2 = /^data:[^;,]*;base64,/i;
11016
+ var encoder = new TextEncoder();
11017
+ function imageBytes(image) {
11018
+ if (/^https?:/i.test(image)) return 0;
11019
+ return encoder.encode(image.replace(DATA_URL_PREFIX2, "")).length;
11020
+ }
11021
+ function requestImageBytes(messages) {
11022
+ let total = 0;
11023
+ for (const message of messages) {
11024
+ for (const part of message.parts ?? []) {
11025
+ if (part.type !== "image") continue;
11026
+ total += imageBytes(part.image);
11027
+ }
11028
+ for (const result of message.toolResults ?? []) {
11029
+ for (const artifact of result.artifacts ?? []) {
11030
+ if (!artifact.data) continue;
11031
+ total += encoder.encode(artifact.data).length;
11032
+ }
11033
+ }
11034
+ }
11035
+ return total;
11036
+ }
11037
+ function imageSites(messages) {
11038
+ const sites = [];
11039
+ for (const [messageIndex, message] of messages.entries()) {
11040
+ const parts = message.parts ?? [];
11041
+ for (const [partIndex, part] of parts.entries()) {
11042
+ if (part.type !== "image") continue;
11043
+ sites.push({ messageIndex, partIndex, bytes: imageBytes(part.image) });
11044
+ }
11045
+ const results = message.toolResults ?? [];
11046
+ for (const [resultIndex, result] of results.entries()) {
11047
+ const artifacts = result.artifacts ?? [];
11048
+ for (const [artifactIndex, artifact] of artifacts.entries()) {
11049
+ if (!artifact.data) continue;
11050
+ sites.push({
11051
+ messageIndex,
11052
+ resultIndex,
11053
+ artifactIndex,
11054
+ bytes: encoder.encode(artifact.data).length
11055
+ });
11056
+ }
11057
+ }
11058
+ }
11059
+ return sites;
11060
+ }
11061
+ function offloadOldestImages(messages, opts = { budgetBytes: 0 }) {
11062
+ const keepFresh = Math.max(1, opts.keepFresh ?? 1);
11063
+ const sites = imageSites(messages);
11064
+ const total = sites.reduce((sum, s) => sum + s.bytes, 0);
11065
+ if (total <= opts.budgetBytes) {
11066
+ return { messages: [...messages], offloaded: 0, remainingBytes: total };
11067
+ }
11068
+ const freshFrom = Math.max(0, messages.length - keepFresh);
11069
+ const picked = { parts: /* @__PURE__ */ new Set(), artifacts: /* @__PURE__ */ new Set() };
11070
+ let running = total;
11071
+ for (const site of sites) {
11072
+ if (running <= opts.budgetBytes) break;
11073
+ if (site.messageIndex >= freshFrom) continue;
11074
+ if (site.partIndex !== void 0) {
11075
+ picked.parts.add(`${site.messageIndex}:${site.partIndex}`);
11076
+ } else {
11077
+ picked.artifacts.add(`${site.messageIndex}:${site.resultIndex}:${site.artifactIndex}`);
11078
+ }
11079
+ running -= site.bytes;
11080
+ }
11081
+ const count = picked.parts.size + picked.artifacts.size;
11082
+ if (count === 0) return { messages: [...messages], offloaded: 0, remainingBytes: total };
11083
+ const out = messages.map((message, messageIndex) => {
11084
+ const parts = message.parts ?? [];
11085
+ const results = message.toolResults ?? [];
11086
+ const hitParts = parts.some((_, i) => picked.parts.has(`${messageIndex}:${i}`));
11087
+ const hitArtifacts = results.some(
11088
+ (r, ri) => (r.artifacts ?? []).some((_, ai) => picked.artifacts.has(`${messageIndex}:${ri}:${ai}`))
11089
+ );
11090
+ if (!hitParts && !hitArtifacts) return message;
11091
+ const nextResults = hitArtifacts ? results.map((result, ri) => {
11092
+ const artifacts = result.artifacts ?? [];
11093
+ if (!artifacts.some((_, ai) => picked.artifacts.has(`${messageIndex}:${ri}:${ai}`))) {
11094
+ return result;
11095
+ }
11096
+ return {
11097
+ ...result,
11098
+ artifacts: artifacts.map(
11099
+ (artifact, ai) => picked.artifacts.has(`${messageIndex}:${ri}:${ai}`) ? withoutData(artifact) : artifact
11100
+ )
11101
+ };
11102
+ }) : message.toolResults;
11103
+ const nextParts = hitParts ? parts.map(
11104
+ (part, i) => picked.parts.has(`${messageIndex}:${i}`) ? (
11105
+ { type: "text", text: OFFLOADED_IMAGE_NOTE }
11106
+ ) : part
11107
+ ) : message.parts;
11108
+ return {
11109
+ ...message,
11110
+ ...nextParts ? { parts: nextParts } : {},
11111
+ ...nextResults ? { toolResults: nextResults } : {}
11112
+ };
11113
+ });
11114
+ return { messages: out, offloaded: count, remainingBytes: requestImageBytes(out) };
11115
+ }
11116
+ function withoutData(artifact) {
11117
+ const { data: _data, ...rest } = artifact;
11118
+ return rest;
11119
+ }
10541
11120
  function persistedUsage(entry) {
10542
11121
  return {
10543
11122
  ...entry.usage,
@@ -10857,6 +11436,50 @@ ${this.describeWarning(status)}
10857
11436
  });
10858
11437
  }
10859
11438
  };
11439
+ var DEFAULT_MAX_REQUEST_IMAGE_BYTES = 8 * 1024 * 1024;
11440
+ var LEARN_SHRINK = 0.8;
11441
+ var TTL_MS = 30 * 24 * 60 * 60 * 1e3;
11442
+ var BODY_LIMIT_VERSION = 1;
11443
+ function cachePath(homeDir) {
11444
+ return join(homeDir, "cache", "body_limit.json");
11445
+ }
11446
+ function limitKey(provider, model) {
11447
+ return `${provider}:${model}`;
11448
+ }
11449
+ function loadFile(homeDir) {
11450
+ const empty = { version: BODY_LIMIT_VERSION, entries: {} };
11451
+ try {
11452
+ const path = cachePath(homeDir);
11453
+ if (!existsSync(path)) return empty;
11454
+ const data = JSON.parse(readFileSync(path, "utf-8"));
11455
+ if (data.version !== BODY_LIMIT_VERSION) return empty;
11456
+ return { version: BODY_LIMIT_VERSION, entries: data.entries ?? {} };
11457
+ } catch {
11458
+ return empty;
11459
+ }
11460
+ }
11461
+ function readLearnedImageBudget(homeDir, provider, model) {
11462
+ const entry = loadFile(homeDir).entries[limitKey(provider, model)];
11463
+ if (!entry) return void 0;
11464
+ if (Date.now() - entry.at > TTL_MS) return void 0;
11465
+ return entry.bytes;
11466
+ }
11467
+ function learnImageBudget(homeDir, provider, model, usedBytes) {
11468
+ if (!(usedBytes > 0)) return;
11469
+ try {
11470
+ const file = loadFile(homeDir);
11471
+ const learned = Math.max(1, Math.floor(usedBytes * LEARN_SHRINK));
11472
+ const prev = file.entries[limitKey(provider, model)];
11473
+ if (prev && prev.bytes <= learned && Date.now() - prev.at <= TTL_MS) return;
11474
+ file.entries[limitKey(provider, model)] = { bytes: learned, at: Date.now() };
11475
+ mkdirSync(join(homeDir, "cache"), { recursive: true });
11476
+ writeFileSync(cachePath(homeDir), JSON.stringify(file, null, 2), "utf-8");
11477
+ } catch {
11478
+ }
11479
+ }
11480
+ function effectiveImageBudget(configured, learned) {
11481
+ return learned !== void 0 && learned < configured ? learned : configured;
11482
+ }
10860
11483
  var ProviderError = class extends Error {
10861
11484
  constructor(message, code, recoverable, recovery = "abort") {
10862
11485
  super(message);
@@ -10877,6 +11500,10 @@ function isContextOverflow(err) {
10877
11500
  if (err instanceof ProviderError) return err.code === PROVIDER_CONTEXT_OVERFLOW;
10878
11501
  return classifyProviderError(err).code === PROVIDER_CONTEXT_OVERFLOW;
10879
11502
  }
11503
+ function isBodyTooLarge(err) {
11504
+ if (err instanceof ProviderError) return err.code === PROVIDER_BODY_TOO_LARGE;
11505
+ return classifyProviderError(err).code === PROVIDER_BODY_TOO_LARGE;
11506
+ }
10880
11507
  function isAbortError(err) {
10881
11508
  for (let cur = err, depth = 0; cur && depth < 5; depth++) {
10882
11509
  if (typeof cur !== "object") return false;
@@ -10976,16 +11603,17 @@ function classifyProviderError(err) {
10976
11603
  }
10977
11604
  return { code: 9999, recovery: "retry" };
10978
11605
  }
11606
+ var EMPTY_REASONING = { providerOptions: { openaiCompatible: { reasoning_content: "" } } };
10979
11607
  function toModelMessages(messages, opts = {}) {
10980
- if (opts.toolMediaInline !== false) return messages.map(toModelMessage);
10981
- return hoistToolMedia(messages);
11608
+ if (opts.toolMediaInline === false) return hoistToolMedia(messages, opts);
11609
+ return messages.map((m) => toModelMessage(m, opts));
10982
11610
  }
10983
- function hoistToolMedia(messages) {
11611
+ function hoistToolMedia(messages, opts = {}) {
10984
11612
  const out = [];
10985
11613
  let pending = [];
10986
11614
  for (const [i, m] of messages.entries()) {
10987
11615
  if (m.role !== "tool") {
10988
- out.push(toModelMessage(m));
11616
+ out.push(toModelMessage(m, opts));
10989
11617
  continue;
10990
11618
  }
10991
11619
  const results = (m.toolResults ?? []).map((r) => {
@@ -11002,7 +11630,7 @@ ${note}` : note,
11002
11630
  artifacts: (r.artifacts ?? []).map(({ data: _data, ...rest }) => rest)
11003
11631
  };
11004
11632
  });
11005
- out.push(toModelMessage({ ...m, toolResults: results }));
11633
+ out.push(toModelMessage({ ...m, toolResults: results }, opts));
11006
11634
  if (pending.length > 0 && messages[i + 1]?.role !== "tool") {
11007
11635
  out.push({
11008
11636
  role: "user",
@@ -11085,7 +11713,7 @@ function toSdkPart(p) {
11085
11713
  }
11086
11714
  return assertNeverPart(p);
11087
11715
  }
11088
- function toModelMessage(m) {
11716
+ function toModelMessage(m, opts = {}) {
11089
11717
  if (m.role === "tool") {
11090
11718
  return {
11091
11719
  role: "tool",
@@ -11100,10 +11728,13 @@ function toModelMessage(m) {
11100
11728
  if (m.role === "user" && m.parts && m.parts.length > 0) {
11101
11729
  return { role: "user", content: m.parts.map(toSdkPart) };
11102
11730
  }
11731
+ const reasoningPart = opts.reasoning && m.reasoning ? [{ type: "reasoning", text: m.reasoning }] : [];
11732
+ const emptyReasoning = opts.reasoning && !m.reasoning ? EMPTY_REASONING : {};
11103
11733
  if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
11104
11734
  return {
11105
11735
  role: "assistant",
11106
11736
  content: [
11737
+ ...reasoningPart,
11107
11738
  ...m.content ? [{ type: "text", text: m.content }] : [],
11108
11739
  ...m.toolCalls.map((tc) => ({
11109
11740
  type: "tool-call",
@@ -11111,19 +11742,35 @@ function toModelMessage(m) {
11111
11742
  toolName: tc.toolName,
11112
11743
  input: tc.input
11113
11744
  }))
11114
- ]
11745
+ ],
11746
+ ...emptyReasoning
11747
+ };
11748
+ }
11749
+ if (m.role === "assistant" && (reasoningPart.length > 0 || opts.reasoning)) {
11750
+ return {
11751
+ role: "assistant",
11752
+ content: [
11753
+ ...reasoningPart,
11754
+ ...m.content ? [{ type: "text", text: m.content }] : []
11755
+ ],
11756
+ ...emptyReasoning
11115
11757
  };
11116
11758
  }
11117
11759
  return { role: m.role, content: m.content };
11118
11760
  }
11119
- function toSdkTools(tools) {
11761
+ var ANTHROPIC_CACHE_BREAKPOINT = {
11762
+ anthropic: { cacheControl: { type: "ephemeral" } }
11763
+ };
11764
+ function toSdkTools(tools, opts = {}) {
11120
11765
  const result = {};
11121
- for (const et of tools) {
11766
+ const breakpointAt = opts.cacheBreakpoint === true ? tools.length - 1 : -1;
11767
+ tools.forEach((et, i) => {
11122
11768
  result[et.name] = tool({
11123
11769
  description: et.description,
11124
- inputSchema: jsonSchema(et.parameters)
11770
+ inputSchema: jsonSchema(et.parameters),
11771
+ ...i === breakpointAt ? { providerOptions: ANTHROPIC_CACHE_BREAKPOINT } : {}
11125
11772
  });
11126
- }
11773
+ });
11127
11774
  return result;
11128
11775
  }
11129
11776
  function toOutputSpec(rf) {
@@ -11323,12 +11970,17 @@ function flattenMessage(m) {
11323
11970
  return {
11324
11971
  role: m.role,
11325
11972
  content: m.content,
11326
- ...toolCallIds.length > 0 ? { toolCallIds } : {}
11973
+ ...toolCallIds.length > 0 ? { toolCallIds } : {},
11974
+ ...m.reasoning ? { reasoning: m.reasoning } : {}
11327
11975
  };
11328
11976
  }
11329
11977
  function unflattenMessage(m) {
11330
11978
  if (m.role === "assistant") {
11331
- return { role: "assistant", content: m.content || "(\u65E0\u8F93\u51FA)" };
11979
+ return {
11980
+ role: "assistant",
11981
+ content: m.content || "(\u65E0\u8F93\u51FA)",
11982
+ ...m.reasoning ? { reasoning: m.reasoning } : {}
11983
+ };
11332
11984
  }
11333
11985
  if (m.role === "system") {
11334
11986
  return { role: "system", content: m.content };
@@ -11473,13 +12125,18 @@ var COMMAND_APPROVAL_POLICY = [
11473
12125
  ].join("\n");
11474
12126
  var PLAN_MODE_POLICY = [
11475
12127
  "<plan_mode>",
11476
- "\u9047\u5230\u5927\u4EFB\u52A1\u65F6\u5148\u8C03 `enter_plan_mode` \u53EA\u8BFB\u5730\u8C03\u7814\uFF0C\u518D\u7528 `exit_plan_mode` \u628A\u8BA1\u5212\u4EA4\u7ED9\u7528\u6237\u6279\u3002",
12128
+ "\u9047\u5230\u5927\u4EFB\u52A1\u65F6\uFF0C\u5148\u628A\u8303\u56F4\u6478\u6E05\u695A \u2014\u2014 \u8BFB\u6587\u4EF6\u3001\u641C\u7D22\u3001\u5217\u76EE\u5F55\uFF0C\u8FD9\u4E9B\u5728\u4F60\u73B0\u5728\u7684\u6743\u9650\u7EA7\u522B\u4E0B\u5C31\u80FD\u505A\u3002",
12129
+ "\u6478\u5230\u300C\u8981\u6539\u54EA\u4E9B\u6587\u4EF6\u3001\u52A8\u54EA\u51E0\u5904\u300D\u5FC3\u91CC\u6709\u6570\u4E86\uFF0C\u518D\u8C03 `enter_plan_mode` \u51FA\u8BA1\u5212\uFF0C",
12130
+ "\u7528 `exit_plan_mode` \u628A\u5B83\u4EA4\u7ED9\u7528\u6237\u6279\u3002",
11477
12131
  "\u7F16\u7801\u4EFB\u52A1\u6700\u8D35\u7684\u5931\u8D25\u4E0D\u662F\u5199\u9519\u4E00\u884C\uFF0C\u662F\u65B9\u5411\u7406\u89E3\u9519\u4E86\u7136\u540E\u5E72\u4E86\u4E8C\u5341\u5206\u949F\u3002",
11478
12132
  "**\u8BE5\u5148\u51FA\u8BA1\u5212**\uFF1A\u9884\u8BA1\u8981\u6539 3 \u4E2A\u4EE5\u4E0A\u6587\u4EF6 \xB7 \u6709\u591A\u79CD\u5408\u7406\u5B9E\u73B0\u8DEF\u5F84 \xB7 \u8981\u65B0\u589E\u6216\u6539\u52A8\u4F9D\u8D56 \xB7",
11479
12133
  "\u8981\u52A8\u8DE8\u5305\u5951\u7EA6 \xB7 \u7528\u6237\u7684\u63CF\u8FF0\u6709\u6B67\u4E49\u4E14\u6B67\u4E49\u4F1A\u6539\u53D8\u5B9E\u73B0\u3002",
11480
12134
  "**\u4E0D\u8BE5\u51FA\u8BA1\u5212**\uFF1A\u5355\u6587\u4EF6\u6539\u52A8 \xB7 \u53EA\u6709\u4E00\u79CD\u660E\u663E\u505A\u6CD5 \xB7 \u6539\u6587\u6848\u4FEE typo \xB7 \u52A0\u4E00\u6761\u6D4B\u8BD5\u3002",
12135
+ "\u26A0\uFE0F **\u522B\u4E00\u4E0A\u6765\u5C31\u8FDB plan \u6A21\u5F0F\u518D\u5F00\u59CB\u8C03\u7814**\uFF1Aplan \u7EA7\u522B\u4E0B\u5DE5\u4F5C\u533A\u5916\u7684\u8BFB\u53D6\u8981\u7528\u6237\u9010\u4E2A\u6279\u51C6\uFF0C",
12136
+ "\u628A\u5927\u8303\u56F4\u8C03\u7814\u653E\u8FDB\u53BB\uFF0C\u7B49\u4E8E\u8BA9\u4ED6\u4E3A\u6BCF\u4E00\u6B21\u7FFB\u76EE\u5F55\u70B9\u4E00\u6B21\u786E\u8BA4\u6846\u3002",
12137
+ "\u8FDB plan \u4E4B\u540E\u53EA\u505A**\u5B9A\u70B9\u6838\u5B9E**\uFF08\u67D0\u4E2A\u884C\u53F7\u3001\u67D0\u4E2A\u5951\u7EA6\u3001\u67D0\u6761\u7528\u4F8B\uFF09\uFF0C\u4E0D\u8981\u518D\u4ECE\u5934\u626B\u4E00\u904D\u3002",
11481
12138
  "\u5728 plan \u6A21\u5F0F\u91CC\u5199\u6587\u4EF6\u3001\u6267\u884C\u547D\u4EE4\u3001\u8054\u7F51\u3001\u8DD1\u4EE3\u7801\u90FD\u4F1A\u88AB\u62D2 \u2014\u2014 \u90A3\u4E0D\u662F\u6545\u969C\uFF0C\u662F\u8FD9\u4E2A\u6A21\u5F0F\u7684\u5B9A\u4E49\u3002",
11482
- "\u7167\u5E38\u80FD\u7528\u7684\u662F**\u53EA\u8BFB**\u90A3\u4E00\u6863\uFF1A\u8BFB\u6587\u4EF6\u3001\u641C\u7D22\u3001\u5217\u76EE\u5F55\u3001\u8BFB\u6280\u80FD\u6B63\u6587\u3001\u7FFB\u4F1A\u8BDD\u5386\u53F2\u3002\u7528\u5B83\u4EEC\u628A\u65B9\u6848\u6478\u624E\u5B9E\u3002",
12139
+ "\u7167\u5E38\u80FD\u7528\u7684\u662F**\u53EA\u8BFB**\u90A3\u4E00\u6863\uFF1A\u8BFB\u6587\u4EF6\u3001\u641C\u7D22\u3001\u5217\u76EE\u5F55\u3001\u8BFB\u6280\u80FD\u6B63\u6587\u3001\u7FFB\u4F1A\u8BDD\u5386\u53F2\u3002\u7528\u5B83\u505A\u4E0A\u9762\u8BF4\u7684\u5B9A\u70B9\u6838\u5B9E\u3002",
11483
12140
  "\u26A0\uFE0F \u4EA4\u4E00\u4EFD\u8BA1\u5212**\u6362\u4E0D\u6765**\u5199\u6743\u9650\uFF1A\u7528\u6237\u672C\u6765\u5C31\u5728 plan \u6863\u65F6\uFF0C\u300C\u6279\u51C6\u5E76\u6267\u884C\u300D\u90A3\u4E2A\u51FA\u53E3\u538B\u6839\u4E0D\u7ED9\uFF08\u6743\u9650\u53EA\u56E0\u7528\u6237\u81EA\u5DF1\u7684\u52A8\u4F5C\u653E\u5BBD\uFF09\u3002",
11484
12141
  "\u7528\u6237\u5728\u8FD9\u4E2A\u6A21\u5F0F\u91CC\u50AC\u4F60\u52A8\u624B\u3001\u6216\u8005\u7528\u7948\u4F7F\u53E5\u8981\u6C42\u6267\u884C\uFF0C**\u90FD\u4E0D\u7B97\u6388\u6743** \u2014\u2014 \u90A3\u662F\u8BA9\u4F60\u628A\u300C\u600E\u4E48\u6267\u884C\u300D\u5199\u6E05\u695A\uFF0C\u4E0D\u662F\u8BA9\u4F60\u53BB\u6267\u884C\u3002\u6743\u9650\u6863\u53EA\u4F1A\u56E0\u4E3A\u4ED6\u81EA\u5DF1\u7684\u52A8\u4F5C\u6539\u53D8\u3002",
11485
12142
  "</plan_mode>"
@@ -11595,11 +12252,11 @@ var PromptBuilder = class {
11595
12252
  memoryInitialized = false;
11596
12253
  projectBlock(tools) {
11597
12254
  const permLevel = this.deps.permissionManager?.getLevel() || "default";
11598
- const hasPlanTool = tools.some((t81) => t81.name === "enter_plan_mode");
11599
- const hasLspTool = tools.some((t81) => t81.name === "lsp_diagnostics");
11600
- const hasAskTool = tools.some((t81) => t81.name === "ask_user_question");
11601
- const hasTodoTool = tools.some((t81) => t81.name === "todo");
11602
- const hasTerminal = tools.some((t81) => t81.name === "terminal");
12255
+ const hasPlanTool = tools.some((t83) => t83.name === "enter_plan_mode");
12256
+ const hasLspTool = tools.some((t83) => t83.name === "lsp_diagnostics");
12257
+ const hasAskTool = tools.some((t83) => t83.name === "ask_user_question");
12258
+ const hasTodoTool = tools.some((t83) => t83.name === "todo");
12259
+ const hasTerminal = tools.some((t83) => t83.name === "terminal");
11603
12260
  const needsCommandApproval = hasTerminal && permLevel === "default";
11604
12261
  const plan = this.deps.approvedPlan?.()?.trim();
11605
12262
  const codeSdk = renderSdkDeclaration(this.deps.codeModeTools?.() ?? []);
@@ -11882,9 +12539,50 @@ function toAbsolute2(path, workDir) {
11882
12539
  const expanded = expandPath(path);
11883
12540
  return isAbsolute(expanded) ? resolve(expanded) : resolve(workDir, expanded);
11884
12541
  }
12542
+ function declaresEscalation(et) {
12543
+ return Boolean(et.parameters.properties?.[SANDBOX_PERMISSIONS_PARAM]);
12544
+ }
12545
+ function stripEscalationArgs(args) {
12546
+ if (!(SANDBOX_PERMISSIONS_PARAM in args) && !(JUSTIFICATION_PARAM in args)) return args;
12547
+ const rest = { ...args };
12548
+ delete rest[SANDBOX_PERMISSIONS_PARAM];
12549
+ delete rest[JUSTIFICATION_PARAM];
12550
+ return rest;
12551
+ }
12552
+ var PARAM_HINT = `${SANDBOX_PERMISSIONS_PARAM} \u548C ${JUSTIFICATION_PARAM}`;
12553
+ function escalationRefusal(rejection, current) {
12554
+ const reason9 = rejection.reason;
12555
+ const target = rejection.target ?? "";
12556
+ switch (reason9) {
12557
+ case "unpaired":
12558
+ return {
12559
+ message: `\u5347\u6743\u8BF7\u6C42\u65E0\u6548\uFF1A${PARAM_HINT} \u5FC5\u987B\u6210\u5BF9\u51FA\u73B0\uFF0C\u8FD9\u6B21\u53EA\u7ED9\u4E86\u5176\u4E2D\u4E00\u4E2A\u3002\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002`,
12560
+ suggestion: "\u8981\u4E48\u4E24\u683C\u90FD\u7ED9\uFF08sandbox_permissions \u5199\u76EE\u6807\u6863\u3001justification \u5199\u4E3A\u4EC0\u4E48\u5FC5\u987B\u8D8A\u754C\uFF09\uFF0C\u8981\u4E48\u4E24\u683C\u90FD\u4E0D\u7ED9\u3002\u53EA\u7ED9 justification \u4E5F\u4E00\u6837\u4F1A\u88AB\u62D2 \u2014\u2014 \u5F15\u64CE\u4E0D\u4F1A\u66FF\u4F60\u731C\u4E00\u4E2A\u76EE\u6807\u3002"
12561
+ };
12562
+ case "unknown-target":
12563
+ return {
12564
+ message: `\u5347\u6743\u8BF7\u6C42\u65E0\u6548\uFF1A\u4E0D\u8BA4\u8BC6\u7684\u6C99\u7BB1\u76EE\u6807\u300C${target}\u300D\u3002\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002`,
12565
+ suggestion: `\u5F53\u524D\u6863\uFF08${current}\uFF09\u80FD\u5347\u5230\u7684\u76EE\u6807\u53EA\u6709\uFF1A${widerTargets(current).join(" / ") || "(\u6CA1\u6709 \u2014\u2014 \u8FD9\u4E00\u6863\u5DF2\u7ECF\u662F\u6700\u5BBD\u7684)"}\u3002`
12566
+ };
12567
+ case "not-wider":
12568
+ return {
12569
+ message: `\u5347\u6743\u8BF7\u6C42\u65E0\u6548\uFF1A\u76EE\u6807\u300C${target}\u300D\u4E0D\u6BD4\u5F53\u524D\u6863\uFF08${current}\uFF09\u66F4\u5BBD\uFF0C\u800C\u5347\u6743\u53EA\u80FD\u5F80\u66F4\u5BBD\u7684\u65B9\u5411\u8D70\u3002\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002`,
12570
+ suggestion: "\u7B49\u4E8E\u5F53\u524D\u6863\u7684\u8BF7\u6C42\u4E0D\u4F1A\u751F\u6548\uFF08\u5B83\u53EA\u4F1A\u8BA9\u7528\u6237\u591A\u70B9\u4E00\u6B21\u5F39\u7A97\uFF09\u3002\u5F53\u524D\u6863\u5DF2\u7ECF\u591F\u7528\u65F6\uFF0C\u76F4\u63A5\u53BB\u6389\u8FD9\u4E24\u683C\u91CD\u8DD1\u8FD9\u6761\u547D\u4EE4\u3002"
12571
+ };
12572
+ case "no-wider":
12573
+ default:
12574
+ return {
12575
+ message: `\u5347\u6743\u8BF7\u6C42\u65E0\u6548\uFF1A\u5F53\u524D\u6863\uFF08${current}\uFF09\u672C\u6765\u5C31\u4E0D\u4E0A\u6C99\u7BB1\uFF0C\u6CA1\u6709\u66F4\u5BBD\u7684\u8FB9\u754C\u53EF\u5347\u3002\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002`,
12576
+ suggestion: "\u8FD9\u4E00\u6863\u4E0B\u6C99\u7BB1\u4E0D\u662F\u62E6\u4F4F\u4F60\u7684\u539F\u56E0 \u2014\u2014 \u56DE\u53BB\u770B\u4F60\u90A3\u6761\u547D\u4EE4\u672C\u8EAB\uFF08\u8DEF\u5F84\u5B58\u4E0D\u5B58\u5728\u3001\u6743\u9650\u591F\u4E0D\u591F\uFF09\uFF0C\u6216\u8005\u8BFB\u4E00\u4E0B\u4E0A\u4E00\u6B21\u7684\u9519\u8BEF\u7801\u3002"
12577
+ };
12578
+ }
12579
+ }
12580
+ function escalationApprovalReason(from, to, justification) {
12581
+ return t("permission_reason.sandbox_escalation", { from, to, justification });
12582
+ }
11885
12583
  var MAX_TOOL_OUTPUT = 3e4;
11886
12584
  var MAX_SUB_CALL_OUTPUT = CODE_MODE_LIMITS.maxValueChars;
11887
- function budgetOf3(tool, depth, path = "success") {
12585
+ function budgetOf2(tool, depth, path = "success") {
11888
12586
  if (tool.governsOwnOutput && path === "success") return Number.POSITIVE_INFINITY;
11889
12587
  return depth > 0 ? MAX_SUB_CALL_OUTPUT : MAX_TOOL_OUTPUT;
11890
12588
  }
@@ -12120,22 +12818,28 @@ var ToolExecutor = class {
12120
12818
  if (!gate.allowed) {
12121
12819
  return finish({ ...base2, success: false, output: gate.output });
12122
12820
  }
12821
+ const escalation = await this.resolveEscalation(et, tc.input, signal);
12822
+ if (escalation.kind === "refused") {
12823
+ return finish({ ...base2, success: false, output: escalation.output });
12824
+ }
12825
+ const toolArgs = stripEscalationArgs(tc.input);
12123
12826
  const targets = this.writeTargets(et, tc.input);
12124
12827
  const snapshotted = await this.snapshot(sessionId, targets);
12125
12828
  const before = targets.length > 0 ? await snapshotFiles(targets) : void 0;
12126
12829
  const extraRoots = this.deps.permissionManager?.getExtraRoots?.();
12127
12830
  const homeDir = (this.deps.multimodal?.() ?? resolveMultimodalPolicy()).homeDir;
12128
- const r = await et.execute(tc.input, {
12831
+ const r = await et.execute(toolArgs, {
12129
12832
  sessionId,
12130
12833
  workDir: this.deps.workDir(),
12131
12834
  ...extraRoots?.length ? { extraRoots } : {},
12132
12835
  ...gate.grantedPaths?.length ? { grantedPaths: gate.grantedPaths } : {},
12133
12836
  ...homeDir ? { homeDir } : {},
12134
12837
  ...this.deps.sandboxTerminal !== void 0 ? { sandboxTerminal: this.deps.sandboxTerminal } : {},
12838
+ ...escalation.kind === "approved" ? { sandboxEscalation: escalation.target } : {},
12135
12839
  permissionLevel: this.deps.permissionManager?.getLevel() || "auto",
12136
12840
  ...signal ? { signal } : {},
12137
12841
  ...onOutput ? { onOutput } : {},
12138
- outputBudget: budgetOf3(et, depth),
12842
+ outputBudget: budgetOf2(et, depth),
12139
12843
  ...depth < MAX_SUB_CALL_DEPTH ? { callTool: this.subCallChannel(sessionId, signal, span, depth) } : {}
12140
12844
  });
12141
12845
  if (snapshotted.length > 0) {
@@ -12167,7 +12871,7 @@ var ToolExecutor = class {
12167
12871
  success: true,
12168
12872
  output: this.renderOutput(
12169
12873
  et,
12170
- appendArtifactSummaries(text, artifacts, budgetOf3(et, depth), shapeOf(r.output))
12874
+ appendArtifactSummaries(text, artifacts, budgetOf2(et, depth), shapeOf(r.output))
12171
12875
  ),
12172
12876
  ...artifacts.length > 0 ? { artifacts } : {},
12173
12877
  ...diffs.length > 0 ? { diffs } : {}
@@ -12178,7 +12882,7 @@ var ToolExecutor = class {
12178
12882
  success: false,
12179
12883
  output: this.renderOutput(
12180
12884
  et,
12181
- truncateForModel(failureText(r), budgetOf3(et, depth, "failure"), "text")
12885
+ truncateForModel(failureText(r), budgetOf2(et, depth, "failure"), "text")
12182
12886
  )
12183
12887
  });
12184
12888
  } catch (err) {
@@ -12260,6 +12964,56 @@ var ToolExecutor = class {
12260
12964
  );
12261
12965
  return guessed || argsKey(args);
12262
12966
  }
12967
+ async resolveEscalation(et, args, signal) {
12968
+ const level = this.deps.permissionManager?.getLevel() ?? "auto";
12969
+ const current = sandboxModeForLevel(level);
12970
+ const plan = planEscalation(args, current);
12971
+ if (plan.kind === "none") return { kind: "none" };
12972
+ if (plan.kind === "rejected") {
12973
+ const { message, suggestion } = escalationRefusal(plan, current);
12974
+ return { kind: "refused", output: `\u5347\u6743\u88AB\u62D2 [\u6C99\u7BB1]: ${message}
12975
+ ${suggestion}` };
12976
+ }
12977
+ if (!declaresEscalation(et)) {
12978
+ return {
12979
+ kind: "refused",
12980
+ output: `\u5347\u6743\u88AB\u62D2 [\u6C99\u7BB1]: \u5DE5\u5177 ${et.name} \u4E0D\u63A5\u53D7 sandbox_permissions \u2014\u2014 \u5B83\u7684\u6C99\u7BB1\u8FB9\u754C\u4E0D\u7531\u8FD9\u4E00\u683C\u63A7\u5236\u3002
12981
+ \u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002\u53BB\u6389\u8FD9\u4E24\u683C\u91CD\u8DD1\uFF1B\u5982\u679C\u5B83\u786E\u5B9E\u9700\u8981\u8D8A\u754C\uFF0C\u90A3\u8BF4\u660E\u8BE5\u6362\u4E00\u4E2A\u80FD\u8868\u8FBE\u8FD9\u4EF6\u4E8B\u7684\u5DE5\u5177\u3002`
12982
+ };
12983
+ }
12984
+ if (!this.deps.onApprovalRequest || this.deps.permissionManager?.isNonInteractive?.()) {
12985
+ return {
12986
+ kind: "refused",
12987
+ output: "\u5347\u6743\u88AB\u62D2 [\u6C99\u7BB1]: \u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u4EA4\u4E92\u786E\u8BA4\uFF0C\u800C\u5347\u6743\u6309\u5B9A\u4E49\u9700\u8981\u4EBA\u70B9\u4E00\u6B21\u3002\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002\n\u65E0\u4EBA\u503C\u5B88\u4E0B\u6CA1\u6709\u522B\u7684\u8DEF\uFF1A\u628A\u8981\u505A\u7684\u6539\u52A8\u632A\u8FDB\u5DE5\u4F5C\u533A\uFF0C\u6216\u8005\u8BA9\u7528\u6237\u5728\u4EA4\u4E92\u4F1A\u8BDD\u91CC\u8DD1\u3002"
12988
+ };
12989
+ }
12990
+ const opType = mapOperation(et);
12991
+ const opTarget = this.resolveTarget(et, args);
12992
+ const { outcome } = normalizeApproval(
12993
+ await this.deps.onApprovalRequest({
12994
+ toolName: et.name,
12995
+ type: opType,
12996
+ target: opTarget,
12997
+ detail: typeof args["command"] === "string" ? String(args["command"]) : opTarget,
12998
+ reason: escalationApprovalReason(current, plan.target, plan.justification),
12999
+ rememberable: false
13000
+ })
13001
+ );
13002
+ if (signal?.aborted) {
13003
+ return { kind: "refused", output: "\u64CD\u4F5C\u5DF2\u4E2D\u6B62\uFF1A\u7528\u6237\u7EC8\u6B62\u4E86\u8FD9\u4E00\u8F6E\u3002" };
13004
+ }
13005
+ this.deps.permissionManager?.auditUserDecision?.(
13006
+ { type: opType, target: opTarget, toolName: et.name },
13007
+ outcome
13008
+ );
13009
+ if (outcome !== "allow-once" && outcome !== "allow-session" && outcome !== "allow-always") {
13010
+ return {
13011
+ kind: "refused",
13012
+ output: "\u5347\u6743\u88AB\u62D2 [\u6C99\u7BB1]: \u7528\u6237\u6CA1\u6709\u6279\u51C6\u8FD9\u4E00\u6B21\u653E\u5F00\u8FB9\u754C\uFF0C\u547D\u4EE4\u6CA1\u6709\u6267\u884C\u3002\n\u522B\u6362\u4E2A\u5199\u6CD5\u518D\u8BD5\uFF0C\u4E5F\u522B\u5207\u6863\uFF08\u90A3\u662F\u6574\u4E2A\u4F1A\u8BDD\u7684\u4E8B\uFF09\u3002\u628A\u8FD9\u4EF6\u4E8B\u4F5C\u4E3A\u300C\u9700\u8981\u4ED6\u653E\u6743\u624D\u80FD\u505A\u300D\u62A5\u7ED9\u4ED6\uFF0C\u6216\u8005\u6362\u4E00\u6761\u4E0D\u9700\u8981\u8D8A\u754C\u7684\u505A\u6CD5\u3002"
13013
+ };
13014
+ }
13015
+ return { kind: "approved", target: plan.target };
13016
+ }
12263
13017
  async checkPermission(et, args, signal) {
12264
13018
  const pm = this.deps.permissionManager;
12265
13019
  if (!pm) return { allowed: true };
@@ -12328,15 +13082,16 @@ var ToolExecutor = class {
12328
13082
  var DEFAULT_CONTEXT_LENGTH = 128e3;
12329
13083
  function shrank(before, after) {
12330
13084
  const chars = (list) => list.reduce((sum, m) => sum + m.content.length, 0);
12331
- return chars(after) < chars(before);
13085
+ if (chars(after) < chars(before)) return true;
13086
+ return requestImageBytes(after) < requestImageBytes(before);
12332
13087
  }
12333
13088
  var NO_OUTPUT_TEXT = "(\u65E0\u8F93\u51FA)";
12334
13089
  function imageParts(content) {
12335
13090
  return typeof content === "string" ? [] : content.filter((p) => p.type === "image");
12336
13091
  }
12337
13092
  function toolSignatureOf(config, tools) {
12338
- const direct = tools.map((t81) => t81.name).join("\0");
12339
- const declared = (config.codeModeTools?.() ?? []).map((t81) => t81.name).join("\0");
13093
+ const direct = tools.map((t83) => t83.name).join("\0");
13094
+ const declared = (config.codeModeTools?.() ?? []).map((t83) => t83.name).join("\0");
12340
13095
  return declared ? `${direct}|${declared}` : direct;
12341
13096
  }
12342
13097
  function modelSignatureOf(info) {
@@ -12355,13 +13110,13 @@ function stripHistoryImages(history) {
12355
13110
  });
12356
13111
  return { messages, stripped };
12357
13112
  }
12358
- function budgetStop(budget2, messages) {
12359
- const breach = budget2.breachReason();
12360
- if (breach) return { message: budget2.describeBreach(breach), breach };
13113
+ function budgetStop(budget, messages) {
13114
+ const breach = budget.breachReason();
13115
+ if (breach) return { message: budget.describeBreach(breach), breach };
12361
13116
  const imageTokens = pendingImageTokens(messages);
12362
- const projected = budget2.projectedBreach(imageTokens);
13117
+ const projected = budget.projectedBreach(imageTokens);
12363
13118
  if (!projected) return void 0;
12364
- return { message: budget2.describeProjectedBreach(projected, imageTokens), breach: projected };
13119
+ return { message: budget.describeProjectedBreach(projected, imageTokens), breach: projected };
12365
13120
  }
12366
13121
  var AgentLoop = class {
12367
13122
  config;
@@ -12427,12 +13182,34 @@ var AgentLoop = class {
12427
13182
  abort() {
12428
13183
  this.aborted = true;
12429
13184
  }
13185
+ tryOffloadForBodyLimit(messages) {
13186
+ const sent = requestImageBytes(messages);
13187
+ this.learnBodyLimit(sent);
13188
+ const saved = offloadOldestImages(messages, {
13189
+ budgetBytes: Math.max(1, Math.floor(sent * 0.8))
13190
+ });
13191
+ return saved.offloaded > 0 ? { messages: saved.messages, offloaded: saved.offloaded } : null;
13192
+ }
12430
13193
  multimodal() {
12431
13194
  return resolveMultimodalPolicy({
12432
13195
  ...this.config.multimodal,
12433
13196
  supportsImages: this.config.provider.capabilities?.supportsImages ?? false
12434
13197
  });
12435
13198
  }
13199
+ imageBudget() {
13200
+ const configured = this.config.maxRequestImageBytes ?? DEFAULT_MAX_REQUEST_IMAGE_BYTES;
13201
+ const key = this.imageLimitKey();
13202
+ return key ? effectiveImageBudget(configured, readLearnedImageBudget(...key)) : configured;
13203
+ }
13204
+ learnBodyLimit(sentBytes) {
13205
+ const key = this.imageLimitKey();
13206
+ if (key) learnImageBudget(...key, sentBytes);
13207
+ }
13208
+ imageLimitKey() {
13209
+ const homeDir = this.config.homeDir;
13210
+ if (!homeDir) return void 0;
13211
+ return [homeDir, "", this.config.provider.modelId ?? "unknown"];
13212
+ }
12436
13213
  modelInfo() {
12437
13214
  const id = this.config.provider.modelId;
12438
13215
  if (!id) return void 0;
@@ -12472,6 +13249,7 @@ var AgentLoop = class {
12472
13249
  projectBlock: seg2.projectBlock,
12473
13250
  memoryBlock: seg2.memoryBlock,
12474
13251
  historyText: opts.historyText,
13252
+ ...opts.lastUsage ? { lastUsage: opts.lastUsage } : {},
12475
13253
  tools: this.toolProvider.list().map((tool) => ({ tool, source: opts.toolSource?.(tool.name) ?? "builtin" })),
12476
13254
  skillIndex: this.config.skillSystem?.indexStats?.({
12477
13255
  only: this.config.roleSkills?.()
@@ -12489,6 +13267,7 @@ var AgentLoop = class {
12489
13267
  let errored;
12490
13268
  const started = Date.now();
12491
13269
  for await (const ev of this.runTurns(input, turns)) {
13270
+ input.onEvent?.(ev);
12492
13271
  if (ev.type === "finish") {
12493
13272
  usage = ev.usage;
12494
13273
  if (ev.reason === "stalled") stalled = true;
@@ -12591,13 +13370,15 @@ var AgentLoop = class {
12591
13370
  let lastText = "";
12592
13371
  let imagesDroppedReported = false;
12593
13372
  let overflowRescued = false;
13373
+ let imagesRescue = { offloaded: 0 };
13374
+ let imagesOffloaded = false;
12594
13375
  let runUsage;
12595
13376
  let lastModel;
12596
13377
  let lastRequestedModel;
12597
13378
  const addRun = (u) => {
12598
13379
  runUsage = runUsage ? addUsage(runUsage, u) : u;
12599
13380
  };
12600
- const budget2 = new BudgetGuard({
13381
+ const budget = new BudgetGuard({
12601
13382
  ...this.config.budget ? { budget: this.config.budget } : {},
12602
13383
  ...this.config.modelPricing ? { pricing: this.config.modelPricing } : {},
12603
13384
  ...input.maxCallCostUsd !== void 0 ? { maxCallCostUsd: input.maxCallCostUsd } : {},
@@ -12609,7 +13390,7 @@ var AgentLoop = class {
12609
13390
  const finish = (reason9, breach) => ({
12610
13391
  type: "finish",
12611
13392
  reason: reason9,
12612
- usage: budget2.snapshot(),
13393
+ usage: budget.snapshot(),
12613
13394
  ...runUsage ? { runUsage } : {},
12614
13395
  ...lastModel ? { model: lastModel } : {},
12615
13396
  ...lastRequestedModel ? { requestedModel: lastRequestedModel } : {},
@@ -12661,8 +13442,8 @@ var AgentLoop = class {
12661
13442
  await this.prompt.syncMemory(userText, lastText);
12662
13443
  return;
12663
13444
  }
12664
- budget2.syncFromStore();
12665
- const stop = budgetStop(budget2, messages);
13445
+ budget.syncFromStore();
13446
+ const stop = budgetStop(budget, messages);
12666
13447
  if (stop) {
12667
13448
  yield { type: "text-delta", text: stop.message };
12668
13449
  await this.prompt.syncMemory(userText, lastText || stop.message);
@@ -12714,8 +13495,8 @@ var AgentLoop = class {
12714
13495
  }
12715
13496
  if (rescued.usage) {
12716
13497
  addRun(rescued.usage);
12717
- yield* budget2.toEvents(
12718
- budget2.add(rescued.usage, this.rateOf(rescued.model)),
13498
+ yield* budget.toEvents(
13499
+ budget.add(rescued.usage, this.rateOf(rescued.model)),
12719
13500
  runUsage
12720
13501
  );
12721
13502
  }
@@ -12747,6 +13528,7 @@ var AgentLoop = class {
12747
13528
  }
12748
13529
  }
12749
13530
  const turnStart = Date.now();
13531
+ imagesRescue = { offloaded: 0 };
12750
13532
  const llmSpan = this.telemetry.startSpan(
12751
13533
  "llm_call",
12752
13534
  {
@@ -12759,6 +13541,24 @@ var AgentLoop = class {
12759
13541
  );
12760
13542
  let guard = this.config.compliance?.outputGuard();
12761
13543
  let reasoningGuard = this.config.compliance?.outputGuard();
13544
+ const imageBudget = this.imageBudget();
13545
+ const beforeBytes = requestImageBytes(messages);
13546
+ if (beforeBytes > imageBudget) {
13547
+ const offloadedResult = offloadOldestImages(messages, { budgetBytes: imageBudget });
13548
+ if (offloadedResult.offloaded > 0) {
13549
+ messages = offloadedResult.messages;
13550
+ if (!imagesOffloaded) {
13551
+ imagesOffloaded = true;
13552
+ yield {
13553
+ type: "notice",
13554
+ code: "images-offloaded",
13555
+ text: t("agent_run.images_offloaded", {
13556
+ count: offloadedResult.offloaded
13557
+ })
13558
+ };
13559
+ }
13560
+ }
13561
+ }
12762
13562
  const requestFor = (msgs) => ({
12763
13563
  provider: this.config.provider,
12764
13564
  input: {
@@ -12775,34 +13575,59 @@ var AgentLoop = class {
12775
13575
  });
12776
13576
  let result;
12777
13577
  try {
12778
- result = yield* runTurn(requestFor(messages));
12779
- if (result.usage) {
12780
- llmSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, result.usage.inputTokens);
12781
- llmSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, result.usage.outputTokens);
12782
- }
12783
- llmSpan.setAttribute("epoch.finish_reason", result.finishReason);
12784
- } catch (err) {
12785
- llmSpan.recordError(err);
12786
- const engine = this.config.contextEngine;
12787
- if (overflowRescued || !engine || !isContextOverflow(err)) throw err;
12788
- overflowRescued = true;
12789
- const rescued = await this.compressNow(messages, {
12790
- totalTokens: totalInput + totalOutput
12791
- });
12792
- if (rescued.usage) {
12793
- addRun(rescued.usage);
12794
- yield* budget2.toEvents(budget2.add(rescued.usage, this.rateOf(rescued.model)), runUsage);
13578
+ for (; ; ) {
13579
+ try {
13580
+ result = yield* runTurn(requestFor(messages));
13581
+ if (result.usage) {
13582
+ llmSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, result.usage.inputTokens);
13583
+ llmSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, result.usage.outputTokens);
13584
+ }
13585
+ llmSpan.setAttribute("epoch.finish_reason", result.finishReason);
13586
+ break;
13587
+ } catch (err) {
13588
+ llmSpan.recordError(err);
13589
+ if (isBodyTooLarge(err) && imagesRescue.offloaded === 0) {
13590
+ const saved = this.tryOffloadForBodyLimit(messages);
13591
+ if (saved) {
13592
+ messages = saved.messages;
13593
+ imagesRescue = { offloaded: saved.offloaded };
13594
+ yield {
13595
+ type: "notice",
13596
+ code: "images-offloaded",
13597
+ text: t("agent_run.images_offloaded", { count: saved.offloaded })
13598
+ };
13599
+ guard = this.config.compliance?.outputGuard();
13600
+ reasoningGuard = this.config.compliance?.outputGuard();
13601
+ continue;
13602
+ }
13603
+ }
13604
+ const engine = this.config.contextEngine;
13605
+ const retriable = isContextOverflow(err) || isBodyTooLarge(err);
13606
+ if (overflowRescued || !engine || !retriable) throw err;
13607
+ overflowRescued = true;
13608
+ const rescued = await this.compressNow(messages, {
13609
+ totalTokens: totalInput + totalOutput,
13610
+ force: true
13611
+ });
13612
+ if (rescued.usage) {
13613
+ addRun(rescued.usage);
13614
+ yield* budget.toEvents(
13615
+ budget.add(rescued.usage, this.rateOf(rescued.model)),
13616
+ runUsage
13617
+ );
13618
+ }
13619
+ if (!shrank(messages, rescued.messages)) throw err;
13620
+ messages = rescued.messages;
13621
+ yield {
13622
+ type: "notice",
13623
+ code: "context-compacted",
13624
+ text: t("agent_run.context_compacted")
13625
+ };
13626
+ guard = this.config.compliance?.outputGuard();
13627
+ reasoningGuard = this.config.compliance?.outputGuard();
13628
+ continue;
13629
+ }
12795
13630
  }
12796
- if (!shrank(messages, rescued.messages)) throw err;
12797
- messages = rescued.messages;
12798
- yield {
12799
- type: "notice",
12800
- code: "context-compacted",
12801
- text: t("agent_run.context_compacted")
12802
- };
12803
- guard = this.config.compliance?.outputGuard();
12804
- reasoningGuard = this.config.compliance?.outputGuard();
12805
- result = yield* runTurn(requestFor(messages));
12806
13631
  } finally {
12807
13632
  llmSpan.end();
12808
13633
  this.telemetry.histogram(METRIC.LLM_CALL_LATENCY, Date.now() - turnStart);
@@ -12848,14 +13673,14 @@ var AgentLoop = class {
12848
13673
  "epoch.token_type": "cache_write"
12849
13674
  });
12850
13675
  }
12851
- const status = budget2.add(usage, this.rateOf(result.model));
13676
+ const status = budget.add(usage, this.rateOf(result.model));
12852
13677
  input.onTurnUsage?.(
12853
13678
  status.turnCostUsd === void 0 ? usage : { ...usage, costUsd: status.turnCostUsd }
12854
13679
  );
12855
13680
  addRun(usage);
12856
13681
  lastModel = result.model;
12857
13682
  lastRequestedModel = result.requestedModel;
12858
- yield* budget2.toEvents(status, runUsage);
13683
+ yield* budget.toEvents(status, runUsage);
12859
13684
  }
12860
13685
  const verdict = mergeVerdicts(guard?.verdict(), reasoningGuard?.verdict());
12861
13686
  if (verdict) this.reportCompliance(verdict, "output");
@@ -12920,7 +13745,11 @@ ${finalText}` };
12920
13745
  turn
12921
13746
  });
12922
13747
  if (turn < maxTurns && input.steer?.pending() === true) {
12923
- messages.push({ role: "assistant", content: finalText || NO_OUTPUT_TEXT });
13748
+ messages.push({
13749
+ role: "assistant",
13750
+ content: finalText || NO_OUTPUT_TEXT,
13751
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13752
+ });
12924
13753
  yield* this.drainSteer(input, messages);
12925
13754
  continue;
12926
13755
  }
@@ -12931,7 +13760,11 @@ ${finalText}` };
12931
13760
  );
12932
13761
  if (wantContinue && turn < maxTurns) {
12933
13762
  messages.push(
12934
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13763
+ {
13764
+ role: "assistant",
13765
+ content: finalText || NO_OUTPUT_TEXT,
13766
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13767
+ },
12935
13768
  {
12936
13769
  role: "user",
12937
13770
  content: systemNote(
@@ -12951,7 +13784,11 @@ ${finalText}` };
12951
13784
  text: t("agent_run.blank_response")
12952
13785
  };
12953
13786
  messages.push(
12954
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13787
+ {
13788
+ role: "assistant",
13789
+ content: finalText || NO_OUTPUT_TEXT,
13790
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13791
+ },
12955
13792
  { role: "user", content: systemNote(blankResponseNote(finalText.trim().length)) }
12956
13793
  );
12957
13794
  continue;
@@ -12963,7 +13800,11 @@ ${finalText}` };
12963
13800
  if (open !== void 0 && open > 0) {
12964
13801
  todoNudged = true;
12965
13802
  messages.push(
12966
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13803
+ {
13804
+ role: "assistant",
13805
+ content: finalText || NO_OUTPUT_TEXT,
13806
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13807
+ },
12967
13808
  {
12968
13809
  role: "user",
12969
13810
  content: systemNote(unfinishedTodoNote(open))
@@ -12975,7 +13816,11 @@ ${finalText}` };
12975
13816
  if (!idleNudged && turn < maxTurns && !truncated && announcedNextStep(finalText)) {
12976
13817
  idleNudged = true;
12977
13818
  messages.push(
12978
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13819
+ {
13820
+ role: "assistant",
13821
+ content: finalText || NO_OUTPUT_TEXT,
13822
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13823
+ },
12979
13824
  { role: "user", content: systemNote(announcedButIdleNote()) }
12980
13825
  );
12981
13826
  continue;
@@ -12993,6 +13838,7 @@ ${finalText}` };
12993
13838
  messages.push({
12994
13839
  role: "assistant",
12995
13840
  content: result.text,
13841
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {},
12996
13842
  toolCalls: toolCalls.map((tc) => ({
12997
13843
  toolCallId: tc.toolCallId,
12998
13844
  toolName: tc.toolName,
@@ -13092,8 +13938,8 @@ ${finalText}` };
13092
13938
  messages = compacted.messages;
13093
13939
  if (compacted.usage) {
13094
13940
  addRun(compacted.usage);
13095
- yield* budget2.toEvents(
13096
- budget2.add(compacted.usage, this.rateOf(compacted.model)),
13941
+ yield* budget.toEvents(
13942
+ budget.add(compacted.usage, this.rateOf(compacted.model)),
13097
13943
  runUsage
13098
13944
  );
13099
13945
  }
@@ -13170,7 +14016,7 @@ ${finalText}` };
13170
14016
  return { ...result, ran: true };
13171
14017
  }
13172
14018
  async compressNow(messages, opts = {}) {
13173
- const { instruction, totalTokens: totalTokens2, promptTokens: promptTokens2 } = opts;
14019
+ const { instruction, totalTokens: totalTokens2, promptTokens: promptTokens3, force } = opts;
13174
14020
  const engine = this.config.contextEngine;
13175
14021
  if (!engine) return { messages };
13176
14022
  try {
@@ -13180,7 +14026,8 @@ ${finalText}` };
13180
14026
  contextLength: this.config.contextLength,
13181
14027
  ...instruction ? { instruction } : {},
13182
14028
  ...plan ? { plan } : {},
13183
- ...promptTokens2 === void 0 ? {} : { promptTokens: promptTokens2 }
14029
+ ...force ? { force } : {},
14030
+ ...promptTokens3 === void 0 ? {} : { promptTokens: promptTokens3 }
13184
14031
  });
13185
14032
  const bill = {
13186
14033
  ...compressed.usage ? { usage: compressed.usage } : {},
@@ -13397,6 +14244,9 @@ var CompressionSchema = z.object({
13397
14244
  enabled: z.boolean().default(true),
13398
14245
  threshold: z.number().gt(0).max(1).default(DEFAULT_COMPRESSION_THRESHOLD)
13399
14246
  });
14247
+ var LimitsSchema = z.object({
14248
+ maxRequestImageBytes: z.int().positive().optional()
14249
+ });
13400
14250
  var DEFAULT_MEMORY_CHAR_LIMIT = 2e4;
13401
14251
  var DEFAULT_USER_CHAR_LIMIT = 6e3;
13402
14252
  var DEFAULT_MEMORY_INDEX_CHAR_LIMIT = 4e3;
@@ -13408,6 +14258,9 @@ var MemorySchema = z.object({
13408
14258
  var SandboxSchema = z.object({
13409
14259
  terminal: z.boolean().default(true)
13410
14260
  });
14261
+ var DelegateSchema = z.object({
14262
+ taskTimeoutSec: z.int().nonnegative().default(900)
14263
+ });
13411
14264
  var ToolsSchema = z.object({
13412
14265
  deferMcp: z.boolean().default(false),
13413
14266
  mode: z.enum(["tools", "both", "code"]).default("tools")
@@ -13484,7 +14337,8 @@ var BlankGreetingSchema = z.object({
13484
14337
  });
13485
14338
  var HostUserSchema = z.object({
13486
14339
  label: z.string().min(1),
13487
- icon: z.string().min(1).optional()
14340
+ icon: z.string().min(1).optional(),
14341
+ avatar: z.string().min(1).optional()
13488
14342
  });
13489
14343
  var ArtifactsSchema = z.object({
13490
14344
  maxAgeDays: z.int().positive().max(3650).default(ARTIFACT_RETENTION_DEFAULTS.maxAgeDays),
@@ -13515,7 +14369,17 @@ var EpochConfigSchema = z.object({
13515
14369
  maxOutputTokens: z.int().positive().optional(),
13516
14370
  homeDir: z.string().min(1).default(() => resolveHomeDir()),
13517
14371
  dbPath: z.string().min(1).default(() => dbPath(resolveHomeDir())),
13518
- permission: z.enum(PERMISSION_LEVELS).default("default"),
14372
+ permission: z.string().default(DEFAULT_PERMISSION_LEVEL).superRefine((raw, ctx) => {
14373
+ if (resolvePermissionLevel(raw) === void 0) {
14374
+ ctx.addIssue({
14375
+ code: "custom",
14376
+ message: t("config_load.bad_permission_level", {
14377
+ raw,
14378
+ levels: PERMISSION_LEVELS.join(" / ")
14379
+ })
14380
+ });
14381
+ }
14382
+ }).transform((raw) => resolvePermissionLevel(raw) ?? DEFAULT_PERMISSION_LEVEL),
13519
14383
  permissions: z.object({
13520
14384
  allow: z.array(z.string().min(1)).optional(),
13521
14385
  ask: z.array(z.string().min(1)).optional(),
@@ -13528,9 +14392,11 @@ var EpochConfigSchema = z.object({
13528
14392
  hooks: z.record(z.string(), z.unknown()).optional(),
13529
14393
  trust: TrustSchema.optional(),
13530
14394
  sandbox: SandboxSchema.prefault({}),
14395
+ delegate: DelegateSchema.prefault({}),
13531
14396
  tools: ToolsSchema.prefault({}),
13532
14397
  skills: SkillsSchema.optional(),
13533
14398
  compression: CompressionSchema.prefault({}),
14399
+ limits: LimitsSchema.prefault({}),
13534
14400
  memory: MemorySchema.prefault({}),
13535
14401
  artifacts: ArtifactsSchema.prefault({}),
13536
14402
  sessions: SessionsSchema.prefault({}),
@@ -13562,6 +14428,7 @@ var EpochConfigSchema = z.object({
13562
14428
  message: "blankCards: id must be unique"
13563
14429
  }).optional(),
13564
14430
  blankGreeting: BlankGreetingSchema.optional(),
14431
+ scheduleRemoveConfirm: HostTextSchema.optional(),
13565
14432
  user: HostUserSchema.optional(),
13566
14433
  settingsHidden: z.array(z.string().min(1)).min(1).optional(),
13567
14434
  settingsPanes: z.array(
@@ -13604,6 +14471,22 @@ function settingsPaneBeforeIssues(config) {
13604
14471
  message: `unknown settings pane id: ${String(pane.before)} (known: ${SETTINGS_PANE_IDS.join(", ")})`
13605
14472
  }));
13606
14473
  }
14474
+ function scheduleRemoveConfirmIssues(config) {
14475
+ const texts = config.scheduleRemoveConfirm;
14476
+ if (texts === void 0) return [];
14477
+ const issues = [];
14478
+ for (const [lang, text] of Object.entries(texts)) {
14479
+ if (text === void 0) continue;
14480
+ for (const name of placeholdersOf(text)) {
14481
+ if (name === SCHEDULE_CONFIRM_PLACEHOLDER) continue;
14482
+ issues.push({
14483
+ path: `scheduleRemoveConfirm.${lang}`,
14484
+ message: `unknown placeholder: {${name}} (only {${SCHEDULE_CONFIRM_PLACEHOLDER}} is substituted; write {{${SCHEDULE_CONFIRM_PLACEHOLDER}}} if you meant it literally)`
14485
+ });
14486
+ }
14487
+ }
14488
+ return issues;
14489
+ }
13607
14490
  function defaultConfig() {
13608
14491
  return EpochConfigSchema.parse({});
13609
14492
  }
@@ -13737,7 +14620,7 @@ var ManagedPolicyError = class extends Error {
13737
14620
  }
13738
14621
  };
13739
14622
  function assertLevelAllowed(level, policy) {
13740
- if (level !== "bypass" || !policy.disableBypassPermissionsMode) return;
14623
+ if (approvalPolicyForLevel(level) !== "never" || !policy.disableBypassPermissionsMode) return;
13741
14624
  throw new ManagedPolicyError(
13742
14625
  t("managed_settings.bypass_blocked"),
13743
14626
  t("managed_settings.bypass_blocked_hint", { path: policy.path })
@@ -14042,18 +14925,18 @@ function _loadImpl(overrideHomeDir) {
14042
14925
  if (read.kind === "corrupt" && _lastKnownGood?.homeDir === homeDir) {
14043
14926
  _lastIssues = issues;
14044
14927
  _lastOrigins = _lastKnownGood.origins;
14045
- const cached = _lastKnownGood;
14928
+ const cached2 = _lastKnownGood;
14046
14929
  _cache = {
14047
14930
  homeDir,
14048
14931
  mtime,
14049
14932
  size,
14050
14933
  entriesStamp,
14051
14934
  envSnapshot,
14052
- config: cached.config,
14935
+ config: cached2.config,
14053
14936
  issues,
14054
- origins: cached.origins
14937
+ origins: cached2.origins
14055
14938
  };
14056
- return cached.config;
14939
+ return cached2.config;
14057
14940
  }
14058
14941
  const config = parseRawConfig(read.raw, issues, track);
14059
14942
  reportDroppedEnv(issues);
@@ -14109,6 +14992,7 @@ function parseRawConfig(raw, issues, track) {
14109
14992
  issues.push(...outcome.issues);
14110
14993
  issues.push(...settingsHiddenIssues(outcome.value));
14111
14994
  issues.push(...settingsPaneBeforeIssues(outcome.value));
14995
+ issues.push(...scheduleRemoveConfirmIssues(outcome.value));
14112
14996
  if (outcome.issues.length === 0) collectKeyPaths(cleaned, track.file);
14113
14997
  return outcome.value;
14114
14998
  }
@@ -14239,8 +15123,9 @@ function applyEnvOverrides(config, issues, track) {
14239
15123
  } else issues.push(envIssue("EPOCH_LOG_LEVEL", env.EPOCH_LOG_LEVEL, levels.join(" / ")));
14240
15124
  }
14241
15125
  if (env.EPOCH_PERMISSION) {
14242
- if (isPermissionLevel(env.EPOCH_PERMISSION)) {
14243
- config.permission = env.EPOCH_PERMISSION;
15126
+ const level = resolvePermissionLevel(env.EPOCH_PERMISSION);
15127
+ if (level) {
15128
+ config.permission = level;
14244
15129
  track.env.add("permission");
14245
15130
  } else {
14246
15131
  issues.push(
@@ -14750,11 +15635,11 @@ function buildPricing(prompt, completion, cacheRead) {
14750
15635
  const output = Number.parseFloat(completion) * 1e6;
14751
15636
  if (!Number.isFinite(input) || !Number.isFinite(output)) return void 0;
14752
15637
  if (input <= 0 && output <= 0) return void 0;
14753
- const cached = cacheRead ? Number.parseFloat(cacheRead) * 1e6 : NaN;
15638
+ const cached2 = cacheRead ? Number.parseFloat(cacheRead) * 1e6 : NaN;
14754
15639
  return {
14755
15640
  inputPerMTok: input,
14756
15641
  outputPerMTok: output,
14757
- ...Number.isFinite(cached) && cached > 0 ? { cachedInputPerMTok: cached } : {}
15642
+ ...Number.isFinite(cached2) && cached2 > 0 ? { cachedInputPerMTok: cached2 } : {}
14758
15643
  };
14759
15644
  }
14760
15645
  function matchesWindow(o, at) {
@@ -14971,6 +15856,30 @@ var TOOL_RESULT_MEDIA = {
14971
15856
  function acceptsToolResultMedia(type) {
14972
15857
  return TOOL_RESULT_MEDIA[type];
14973
15858
  }
15859
+ var CARRIES_REASONING_BACK = {
15860
+ "openai-compatible": true,
15861
+ deepseek: true,
15862
+ openai: false,
15863
+ anthropic: false,
15864
+ google: false,
15865
+ groq: false,
15866
+ mistral: false,
15867
+ moonshotai: false,
15868
+ zhipu: true,
15869
+ dashscope: false,
15870
+ bytedance: true,
15871
+ qianfan: true,
15872
+ cohere: false,
15873
+ xai: false,
15874
+ perplexity: false,
15875
+ togetherai: false,
15876
+ azure: false,
15877
+ "amazon-bedrock": false,
15878
+ ollama: false
15879
+ };
15880
+ function carriesReasoningBack(type) {
15881
+ return CARRIES_REASONING_BACK[type];
15882
+ }
14974
15883
  function isModuleNotFound(err) {
14975
15884
  if (!err || typeof err !== "object") return false;
14976
15885
  const code = err.code;
@@ -14979,9 +15888,10 @@ function isModuleNotFound(err) {
14979
15888
  return /Cannot find (module|package)|Failed to resolve/i.test(msg);
14980
15889
  }
14981
15890
  // src/provider/entry-route.ts
14982
- function buildEntryTable(entries2) {
15891
+ function buildEntryTable(entries2, homeDir) {
14983
15892
  return {
14984
- entries: new Map((entries2 ?? []).map((e) => [e.id, e]))
15893
+ entries: new Map((entries2 ?? []).map((e) => [e.id, e])),
15894
+ ...homeDir ? { homeDir } : {}
14985
15895
  };
14986
15896
  }
14987
15897
  function entryProvider(_table, entry) {
@@ -15016,6 +15926,14 @@ function entryAcceptsToolMedia(table, provider, entry) {
15016
15926
  if (e?.provider === "openai-compatible" && e.protocol === "anthropic-messages") return true;
15017
15927
  return acceptsToolResultMedia(provider);
15018
15928
  }
15929
+ function entryCarriesReasoningBack(table, provider, entry) {
15930
+ const e = table.entries.get(entry);
15931
+ if (e?.provider === "openai-compatible" && e.protocol === "anthropic-messages") return false;
15932
+ if (!carriesReasoningBack(provider)) return false;
15933
+ if (!e) return false;
15934
+ if (!table.homeDir) return false;
15935
+ return applyEntryCapability(getCapability(e.model, table.homeDir, e.provider), e).supportsReasoning;
15936
+ }
15019
15937
  function capabilityOfRef(config, ref = config.model) {
15020
15938
  const parsed = ref ? parseModelRef(ref) : void 0;
15021
15939
  const entry = parsed ? config.modelEntries?.find((e) => e.id === parsed.entry) : void 0;
@@ -15183,7 +16101,7 @@ var ProviderRouter = class {
15183
16101
  }
15184
16102
  constructor(config) {
15185
16103
  this.config = config;
15186
- this.entryTable = buildEntryTable(config.modelEntries);
16104
+ this.entryTable = buildEntryTable(config.modelEntries, config.homeDir);
15187
16105
  this.configSelection = this.resolveConfigured(config);
15188
16106
  this.initFactories();
15189
16107
  this.selection = { ...this.configSelection };
@@ -15234,7 +16152,7 @@ var ProviderRouter = class {
15234
16152
  return result;
15235
16153
  }
15236
16154
  reloadModelEntries(entries2) {
15237
- this.entryTable = buildEntryTable(entries2);
16155
+ this.entryTable = buildEntryTable(entries2, this.config.homeDir);
15238
16156
  for (const [route, opts] of [...this.available]) {
15239
16157
  if (!opts.entry) continue;
15240
16158
  this.available.delete(route);
@@ -15383,8 +16301,8 @@ var ProviderRouter = class {
15383
16301
  return { selection, configured: true };
15384
16302
  }
15385
16303
  async getModel(route, modelId) {
15386
- const cached = this.factories.get(route);
15387
- if (cached) return cached(modelId ?? this.selection.model);
16304
+ const cached2 = this.factories.get(route);
16305
+ if (cached2) return cached2(modelId ?? this.selection.model);
15388
16306
  const opts = this.available.get(route);
15389
16307
  if (!opts) {
15390
16308
  throw new ProviderError(
@@ -15494,7 +16412,7 @@ var ProviderRouter = class {
15494
16412
  const state = this.startPoint(input);
15495
16413
  const requestedModel = state.cm;
15496
16414
  const messagesFor = messageTranslator(input.messages, this.entryTable);
15497
- const tools = input.tools ? toSdkTools(input.tools) : void 0;
16415
+ const tools = input.tools ? toSdkTools(input.tools, { cacheBreakpoint: true }) : void 0;
15498
16416
  const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
15499
16417
  for (; ; ) {
15500
16418
  if (state.attempt > this.config.retry.maxRetries) {
@@ -15568,7 +16486,7 @@ var ProviderRouter = class {
15568
16486
  const requestedModel = state.cm;
15569
16487
  let emitted = false;
15570
16488
  const messagesFor = messageTranslator(input.messages, this.entryTable);
15571
- const tools = input.tools ? toSdkTools(input.tools) : void 0;
16489
+ const tools = input.tools ? toSdkTools(input.tools, { cacheBreakpoint: true }) : void 0;
15572
16490
  const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
15573
16491
  let streamPartError;
15574
16492
  for (; ; ) {
@@ -15705,8 +16623,8 @@ var ProviderRouter = class {
15705
16623
  async probeImageSupport() {
15706
16624
  const { provider, model, entry } = this.selection;
15707
16625
  const home = this.config.homeDir;
15708
- const cached = readImageProbe(home, provider, model);
15709
- if (cached !== void 0) return cached;
16626
+ const cached2 = readImageProbe(home, provider, model);
16627
+ if (cached2 !== void 0) return cached2;
15710
16628
  let lm;
15711
16629
  try {
15712
16630
  lm = await this.getModel(this.routeOf(provider, entry), model);
@@ -15734,10 +16652,12 @@ function messageTranslator(messages, table) {
15734
16652
  const cache = /* @__PURE__ */ new Map();
15735
16653
  return (type, entry) => {
15736
16654
  const inline = entryAcceptsToolMedia(table, type, entry);
15737
- let hit = cache.get(inline);
16655
+ const reasoning = entryCarriesReasoningBack(table, type, entry);
16656
+ const key = `${inline}|${reasoning}`;
16657
+ let hit = cache.get(key);
15738
16658
  if (!hit) {
15739
- hit = toModelMessages(messages, { toolMediaInline: inline });
15740
- cache.set(inline, hit);
16659
+ hit = toModelMessages(messages, { toolMediaInline: inline, reasoning });
16660
+ cache.set(key, hit);
15741
16661
  }
15742
16662
  return hit;
15743
16663
  };
@@ -15808,12 +16728,12 @@ var PROVIDER_DISCOVERY = {
15808
16728
  function discoverySpec(provider) {
15809
16729
  return PROVIDER_DISCOVERY[provider];
15810
16730
  }
15811
- function cachePath(homeDir, provider) {
16731
+ function cachePath2(homeDir, provider) {
15812
16732
  return join(homeDir, "cache", `models_${provider}.json`);
15813
16733
  }
15814
16734
  function loadCache2(homeDir, provider) {
15815
16735
  try {
15816
- const path = cachePath(homeDir, provider);
16736
+ const path = cachePath2(homeDir, provider);
15817
16737
  if (!existsSync(path)) return null;
15818
16738
  const data = JSON.parse(readFileSync(path, "utf-8"));
15819
16739
  const age = Date.now() - (data._cachedAt ?? 0);
@@ -15828,7 +16748,7 @@ function saveCache2(homeDir, provider, models) {
15828
16748
  const dir = join(homeDir, "cache");
15829
16749
  mkdirSync(dir, { recursive: true });
15830
16750
  writeFileSync(
15831
- cachePath(homeDir, provider),
16751
+ cachePath2(homeDir, provider),
15832
16752
  JSON.stringify({ models, _cachedAt: Date.now() }),
15833
16753
  "utf-8"
15834
16754
  );
@@ -15907,13 +16827,13 @@ async function discoverModels(homeDir, provider, apiKey, baseUrl) {
15907
16827
  }
15908
16828
  }
15909
16829
  }
15910
- const cached = loadCache2(homeDir, provider);
15911
- if (cached) return { models: cached, source: "cache" };
16830
+ const cached2 = loadCache2(homeDir, provider);
16831
+ if (cached2) return { models: cached2, source: "cache" };
15912
16832
  return { models: STATIC_MODEL_CATALOG[provider] ?? [], source: "static" };
15913
16833
  }
15914
16834
  function clearModelCache(homeDir, provider) {
15915
16835
  try {
15916
- const path = cachePath(homeDir, provider);
16836
+ const path = cachePath2(homeDir, provider);
15917
16837
  if (existsSync(path)) writeFileSync(path, "{}", "utf-8");
15918
16838
  } catch {
15919
16839
  }
@@ -16110,7 +17030,10 @@ var VALUE_SCHEMA = {
16110
17030
  "automation.keepRuns": EpochConfigSchema.shape.automation.unwrap().shape.keepRuns.unwrap(),
16111
17031
  "automation.maxRecordingMb": EpochConfigSchema.shape.automation.unwrap().shape.maxRecordingMb.unwrap(),
16112
17032
  "sessionSearch.allWorkspaces": EpochConfigSchema.shape.sessionSearch.unwrap().shape.allWorkspaces.unwrap(),
16113
- permission: EpochConfigSchema.shape.permission.unwrap()
17033
+ permission: EpochConfigSchema.shape.permission
17034
+ };
17035
+ var ENUM_CHOICES = {
17036
+ permission: PERMISSION_LEVELS
16114
17037
  };
16115
17038
  function settingValueKind(key) {
16116
17039
  if (!isWritableSettingKey(key)) return null;
@@ -16121,6 +17044,8 @@ function settingValueKind(key) {
16121
17044
  }
16122
17045
  function settingValueChoices(key) {
16123
17046
  if (!isWritableSettingKey(key)) return null;
17047
+ const explicit = ENUM_CHOICES[key];
17048
+ if (explicit) return explicit;
16124
17049
  const schema = VALUE_SCHEMA[key];
16125
17050
  if (schema.def.type !== "enum") return null;
16126
17051
  return Object.values(schema.def.entries).map(String);
@@ -16263,6 +17188,7 @@ function commit(path, body2, createMode, value) {
16263
17188
  }
16264
17189
  return { ok: true, value };
16265
17190
  }
17191
+ var MACHINE_DEPENDENT_DEFAULTS = ["homeDir", "dbPath"];
16266
17192
  var DESCRIPTIONS = {
16267
17193
  $schema: "\u8FD9\u4EFD\u6587\u4EF6\u7684 JSON Schema \u5730\u5740\u3002\u53EA\u5F71\u54CD\u7F16\u8F91\u5668\u7684\u8865\u5168\u4E0E\u6821\u9A8C\uFF0Cepoch \u672C\u8EAB\u5FFD\u7565\u5B83\uFF08\u4E0D\u7B97\u672A\u77E5\u914D\u7F6E\u9879\uFF09\u3002",
16268
17194
  permissions: "\u6743\u9650\u89C4\u5219\u7684\u4E09\u5F20\u5217\u8868\u3002\u4F18\u5148\u7EA7 deny > ask > allow\uFF0C\u4E14\u4E24\u4E2A deny \u90FD\u538B\u8FC7\u5BA1\u6279\u5F39\u7A97\u91CC\u70B9\u8FC7\u7684\u300C\u603B\u662F\u5141\u8BB8\u300D\u3002",
@@ -16273,7 +17199,112 @@ var DESCRIPTIONS = {
16273
17199
  maxTurns: "\u5355\u6B21\u4F1A\u8BDD\u6700\u591A\u51E0\u8F6E\uFF08\u4E00\u8F6E = \u4E00\u6B21\u6A21\u578B\u8C03\u7528 + \u5B83\u89E6\u53D1\u7684\u5DE5\u5177\u6267\u884C\uFF09\u30020 \u6216\u6B63\u6574\u6570\uFF0C**0 = \u4E0D\u8BBE\u4E0A\u9650**\uFF08\u7F3A\u7701\uFF09\u3002",
16274
17200
  allowManagedPermissionRulesOnly: "\u6253\u5F00\u540E\uFF0C\u5176\u4F59\u5404\u5C42\uFF08\u7528\u6237\u7EA7 / \u9879\u76EE\u7EA7 / \u9879\u76EE\u672C\u5730 / --settings\uFF09\u7684 allow / ask / deny **\u6574\u4EFD\u5FFD\u7565**\uFF0C\u53EA\u6709\u8FD9\u4EFD\u6258\u7BA1\u6587\u4EF6\u91CC\u7684\u89C4\u5219\u7B97\u6570\u3002\u8FD9\u662F\u5168\u6D41\u7A0B\u552F\u4E00\u4E00\u5904\u66FF\u6362\u8BED\u4E49\uFF0C\u4E0D\u662F\u5E76\u96C6\u3002\u9ED8\u8BA4 false\u3002",
16275
17201
  allowManagedHooksOnly: "\u6253\u5F00\u540E\uFF0C\u7528\u6237\u7EA7 hook \u4E00\u6761\u90FD\u4E0D\u52A0\u8F7D\uFF08hook \u80FD spawn \u4EFB\u610F\u5B50\u8FDB\u7A0B\uFF09\u3002\u9ED8\u8BA4 false\u3002",
16276
- disableBypassPermissionsMode: "\u6253\u5F00\u540E\uFF0C`permission: bypass` **\u62D2\u7EDD\u542F\u52A8**\uFF08\u9000\u51FA\u7801 4\uFF09\uFF0C\u8FD0\u884C\u671F\u5207\u5230 bypass \u4E5F\u4F1A\u88AB\u6321\u4E0B\u3002\u9ED8\u8BA4 false\u3002"
17202
+ disableBypassPermissionsMode: "\u6253\u5F00\u540E\uFF0C`permission: bypass` **\u62D2\u7EDD\u542F\u52A8**\uFF08\u9000\u51FA\u7801 4\uFF09\uFF0C\u8FD0\u884C\u671F\u5207\u5230 bypass \u4E5F\u4F1A\u88AB\u6321\u4E0B\u3002\u9ED8\u8BA4 false\u3002",
17203
+ version: "\u914D\u7F6E\u6587\u4EF6\u7684\u683C\u5F0F\u7248\u672C\uFF0C\u7531\u8FC1\u79FB\u6D41\u7A0B\u8BFB\u8D70\uFF08\u4E0D\u8FDB EpochConfig\uFF09\u3002\u4E00\u822C\u4E0D\u7528\u624B\u5199 \u2014\u2014 \u8001\u6587\u4EF6\u6CA1\u6709\u8FD9\u4E00\u884C\u65F6\u6309\u5F53\u524D\u7248\u672C\u5904\u7406\u3002",
17204
+ models: "\u5185\u90E8\u4EFB\u52A1\u4E13\u7528\u7684\u6A21\u578B\u3002\u7F3A\u7701 = \u5168\u90E8\u8D70\u4E3B\u6A21\u578B\u3002",
17205
+ "models.utility": "\u5C0F\u5DE5\u5177\u4EFB\u52A1\uFF08\u751F\u6210\u6807\u9898\u3001\u6458\u8981\u8FD9\u7C7B\uFF09\u7528\u7684\u6A21\u578B\uFF0C\u5199 `@<id>`\uFF08\u300C\u6211\u7684\u6A21\u578B\u300D\u91CC\u7684\u4E00\u6761\uFF09\u3002\u4E0D\u914D = \u7528\u4E3B\u6A21\u578B\u3002",
17206
+ modelEntries: "\u300C\u6211\u7684\u6A21\u578B\u300D\u7684\u6761\u76EE\u6E05\u5355\u3002**\u5199\u5728 config.yaml \u91CC\u4E0D\u751F\u6548** \u2014\u2014 \u771F\u6E90\u662F `models.json`\uFF0C\u8FD9\u4E00\u683C\u8BFB\u5230\u53EA\u4F1A\u62A5\u4E00\u6761 `legacy_ignored`\u3002",
17207
+ shell: "`terminal` \u7528\u54EA\u79CD shell\uFF1A`cmd` / `powershell` / `pwsh`\u3002\u4E0D\u5199 = \u81EA\u52A8\u63A2\u6D4B\uFF08Windows \u4E0A\u4F18\u5148 pwsh\uFF09\u3002",
17208
+ contextLength: "\u6A21\u578B\u7684\u4E0A\u4E0B\u6587\u7A97\u53E3\u5927\u5C0F\uFF08token\uFF09\u3002\u7F3A\u7701 200000\u3002",
17209
+ maxOutputTokens: "\u5355\u6B21\u6A21\u578B\u8BF7\u6C42\u6700\u591A\u751F\u6210\u591A\u5C11 token\u3002\u4E0D\u914D = \u4E0D\u53D1 `max_tokens`\uFF0C\u7528\u5404\u5BB6\u670D\u52A1\u7AEF\u81EA\u5DF1\u7684\u9ED8\u8BA4\u503C\u3002",
17210
+ homeDir: "\u6570\u636E\u76EE\u5F55\u3002\u26A0\uFE0F **\u8BF7\u6539\u7528\u73AF\u5883\u53D8\u91CF `EPOCH_HOME`** \u2014\u2014 \u8FD9\u4E00\u683C\u6709\u4E24\u5957\u89E3\u6790\u771F\u6E90\uFF08`paths.ts` \u53EA\u770B\u73AF\u5883\u53D8\u91CF\uFF0Cruntime \u8BFB `config.homeDir`\uFF09\uFF0C\u5199\u5B83\u53EA\u4F1A\u8BA9\u6570\u636E\u8DD1\u53BB\u65B0\u76EE\u5F55\u800C\u914D\u7F6E\u6587\u4EF6\u7559\u5728\u65E7\u5BB6\u3002",
17211
+ dbPath: "\u4F1A\u8BDD\u5E93\u8DEF\u5F84\uFF08\u7F3A\u7701 `<homeDir>/sessions.db`\uFF09\u3002\u26A0\uFE0F \u540C `homeDir` \u4E00\u65CF\uFF0C\u800C\u4E14\u5207\u5F97\u66F4\u788E\uFF1Aruntime \u7684\u4F1A\u8BDD\u5E93\u8BFB\u5B83\uFF0C\u5B9A\u65F6\u4EFB\u52A1\u90A3\u8FB9\u6309 `homeDir` \u63A8\u3002",
17212
+ permission: "\u65B0\u4F1A\u8BDD\u7684\u9ED8\u8BA4\u6743\u9650\u6863\uFF1A`plan` / `default` / `acceptEdits` / `auto`\u3002\u4E0D\u5199\u65F6\u662F `acceptEdits`\uFF08\u8BF7\u6C42\u6279\u51C6\uFF09\u3002\u8001\u540D\u5B57 `bypass` \u4ECD\u80FD\u8BFB\uFF0C\u4F1A\u9759\u9ED8\u5F53\u4F5C `auto`\u3002\u5DE5\u4F5C\u533A\u4FE1\u4EFB\u90A3\u4E00\u5C42\u4E0D\u6539\u53D8\u8FD9\u4E00\u683C\u3002",
17213
+ logLevel: "\u65E5\u5FD7\u7EA7\u522B\uFF1A`debug` / `info` / `warn` / `error`\u3002",
17214
+ profile: "\u5F53\u524D\u914D\u7F6E\u6863\u540D\u3002\u26A0\uFE0F **\u8FD9\u4E00\u683C\u53EA\u7528\u4E8E\u5C55\u793A** \u2014\u2014 \u771F\u6B63\u51B3\u5B9A\u76EE\u5F55\u7684\u662F\u73AF\u5883\u53D8\u91CF `EPOCH_PROFILE`\uFF0C\u5199\u5728\u8FD9\u91CC\u4E0D\u4F1A\u5207\u6362\u3002",
17215
+ retry: "\u6A21\u578B\u8BF7\u6C42\u7684\u91CD\u8BD5\u7B56\u7565\u3002",
17216
+ "retry.maxRetries": "\u6700\u591A\u91CD\u8BD5\u51E0\u6B21\uFF08\u7F3A\u7701 3\uFF09\u3002",
17217
+ "retry.baseBackoffMs": "\u7B2C\u4E00\u6B21\u91CD\u8BD5\u7B49\u591A\u4E45\uFF08\u6BEB\u79D2\uFF0C\u7F3A\u7701 1000\uFF09\uFF0C\u4E4B\u540E\u6307\u6570\u9000\u907F\u3002",
17218
+ "retry.maxBackoffMs": "\u9000\u907F\u7684\u4E0A\u9650\uFF08\u6BEB\u79D2\uFF0C\u7F3A\u7701 30000\uFF09\u3002",
17219
+ fallback: "\u4E3B\u6A21\u578B\u5931\u8D25\u65F6\u7684\u964D\u7EA7\u76EE\u6807\u3002",
17220
+ "fallback.model": "\u964D\u7EA7\u7528\u7684\u6A21\u578B\uFF0C\u5199 `@<id>`\uFF08\u300C\u6211\u7684\u6A21\u578B\u300D\u91CC\u7684\u4E00\u6761\uFF09\u3002",
17221
+ hooks: "\u65E7\u683C\u5F0F\u7684 hook \u914D\u7F6E\uFF0C\u5DF2\u642C\u53BB `~/.epoch/hooks.json`\u3002\u8FD9\u91CC\u4ECD\u7136\u7167\u5E38\u52A0\u8F7D\uFF08\u5220\u6389\u8FD9\u4E00\u952E\u7B49\u4E8E\u8BA9\u6240\u6709\u4EBA\u7684 hook \u9759\u9ED8\u5931\u6548\uFF09\uFF0C\u53EA\u662F\u542F\u52A8\u65F6\u591A\u4E00\u6761\u8FC1\u79FB\u63D0\u793A\u3002",
17222
+ trust: "\u5DE5\u4F5C\u533A\u4FE1\u4EFB\u95F8\u95E8\u3002\u26A0\uFE0F \u7F3A\u7701\u662F**\u5F00\u7740**\u7684 \u2014\u2014 `enabled: false` \u624D\u662F\u5173\u6389\u3002",
17223
+ "trust.enabled": "`false` = \u5173\u6389\u5DE5\u4F5C\u533A\u4FE1\u4EFB\u6821\u9A8C\u3002**\u4E0D\u5199\u8FD9\u4E2A\u952E = \u4FE1\u4EFB\u95F8\u95E8\u5F00\u7740**\uFF0C\u522B\u7528\u771F\u5047\u503C\u5224\u65AD\u8FD9\u4E00\u683C\u3002",
17224
+ sandbox: "OS \u7EA7\u6C99\u7BB1\u3002",
17225
+ "sandbox.terminal": "`terminal` \u5BB6\u65CF\u7684\u5199\u5165\u6C99\u7BB1\u5F00\u5173\uFF08\u7F3A\u7701 true\uFF09\u3002\u5173\u6389\u4E4B\u540E\u547D\u4EE4\u80FD\u5199\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF \u2014\u2014 \u6B63\u5F53\u7406\u7531\u53EA\u6709\u300C\u7F13\u5B58\u76EE\u5F55\u8868\u6CA1\u8986\u76D6\u5230\u3001\u628A\u80FD\u8DD1\u7684\u547D\u4EE4\u6321\u4F4F\u4E86\u300D\u3002",
17226
+ delegate: "`delegate_task` \u6D3E\u51FA\u53BB\u7684\u5B50\u4EFB\u52A1\u3002",
17227
+ "delegate.taskTimeoutSec": "\u4E00\u4E2A\u5B50\u4EFB\u52A1\u6700\u591A\u8DD1\u591A\u4E45\uFF08\u79D2\uFF0C\u7F3A\u7701 900\uFF09\u3002`0` = \u4E0D\u9650\u65F6\u3002\u649E\u4E0A\u53BB\u4E4B\u540E\u5B50 agent \u5F53\u573A\u88AB\u6390\uFF0C\u534A\u622A\u7ED3\u8BBA\u4E0D\u4FDD\u7559\u3002",
17228
+ tools: "\u6309\u9700\u53D1\u73B0\u5DE5\u5177\u3002",
17229
+ "tools.deferMcp": "MCP \u5DE5\u5177\u4E0D\u8FDB\u521D\u59CB\u5DE5\u5177\u5217\u8868\uFF0C\u6A21\u578B\u8981\u7528 `tool_search` \u624D\u627E\u5F97\u56DE\u6765\uFF08\u7F3A\u7701 false\uFF09\u3002\u6253\u5F00\u662F\u4E00\u6B21\u9759\u9ED8\u7684\u80FD\u529B\u7F29\u51CF\u3002",
17230
+ "tools.mode": "code mode \u4E09\u6863\uFF1A`tools`\uFF08\u6CA1\u6709 `run_code`\uFF09/ `both`\uFF08\u5168\u90E8\u5DE5\u5177 + `run_code`\uFF09/ `code`\uFF08\u80FD\u7A0B\u5E8F\u8C03\u7684\u5207\u8D70\uFF09\u3002\u7F3A\u7701 `tools`\u3002",
17231
+ skills: "\u6280\u80FD\u7D22\u5F15\u3002",
17232
+ "skills.maxIndexed": "\u7D22\u5F15\u91CC\u6700\u591A\u5217\u51E0\u6761\u6280\u80FD\u3002\u4E0D\u914D = \u4E0D\u5C01\u9876\uFF08\u88AB\u622A\u6389\u7684\u6280\u80FD\u6A21\u578B\u6C38\u8FDC\u4E0D\u77E5\u9053\u5B83\u5B58\u5728\uFF0C\u6240\u4EE5\u9ED8\u8BA4\u4E0D\u5C01\uFF09\u3002",
17233
+ compression: "\u4E0A\u4E0B\u6587\u5FEB\u6EE1\u65F6\u81EA\u52A8\u538B\u7F29\u3002",
17234
+ "compression.enabled": "\u5F00\u5173\uFF08\u7F3A\u7701 true\uFF09\u3002",
17235
+ "compression.threshold": "\u63D0\u793A\u8BCD\u5360\u5230\u4E0A\u4E0B\u6587\u7A97\u53E3\u7684\u51E0\u6210\u65F6\u5F00\u59CB\u538B\uFF0C\u53D6\u503C (0, 1]\uFF0C\u7F3A\u7701 0.8\u30020 \u7B49\u4E8E\u6BCF\u8F6E\u90FD\u538B\uFF08\u4E00\u53F0\u70E7\u94B1\u673A\u5668\uFF09\uFF0C\u6240\u4EE5\u4E0D\u542B 0\u3002",
17236
+ limits: "\u5355\u6B21\u8BF7\u6C42\u7684\u4F53\u79EF\u4E0A\u9650\uFF08**\u5B57\u8282**\u8FD9\u6761\u7EBF\uFF0C\u548C\u4E0A\u9762 compression \u7684 token \u90A3\u6761\u662F\u4E24\u4E2A\u91CF\u7EB2\uFF09\u3002",
17237
+ "limits.maxRequestImageBytes": "\u5355\u6B21\u8BF7\u6C42\u91CC\u6240\u6709\u56FE\u7247 base64 \u7684\u5408\u8BA1\u4E0A\u9650\uFF08\u5B57\u8282\uFF09\u3002\u8D85\u4E86\u5C31\u81EA\u52A8\u628A\u6700\u8001\u7684\u51E0\u5F20\u56FE\u4E0B\u653E\u6210\u5360\u4F4D\u3001\u4F1A\u8BDD\u7EE7\u7EED\u3002\u4E0D\u914D = 8 MiB\uFF08\u8FD9\u4E2A\u6570\u6CA1\u6709\u6743\u5A01\u51FA\u5904\uFF0C\u649E\u4E00\u6B21\u4E4B\u540E\u4F1A\u6309\u5B9E\u6D4B\u6536\u7D27\uFF09\u3002",
17238
+ memory: "\u957F\u671F\u8BB0\u5FC6\u7684\u914D\u989D\u3002",
17239
+ "memory.memoryCharLimit": "\u8BB0\u5FC6\u6B63\u6587\u7684\u5B58\u50A8\u914D\u989D\uFF08\u5B57\u7B26\uFF0C\u7F3A\u7701 20000\uFF09\u3002**\u8FD9\u662F\u5B58\u50A8\uFF0C\u4E0D\u662F\u6BCF\u8F6E\u5F00\u9500\u3002**",
17240
+ "memory.userCharLimit": "\u7528\u6237\u753B\u50CF\u7684\u5B58\u50A8\u914D\u989D\uFF08\u5B57\u7B26\uFF0C\u7F3A\u7701 6000\uFF09\u3002\u540C\u6837\u662F\u5B58\u50A8\u3002",
17241
+ "memory.indexCharLimit": "\u8BB0\u5FC6\u7D22\u5F15\u5757\u7684\u914D\u989D\uFF08\u5B57\u7B26\uFF0C\u7F3A\u7701 4000\uFF09\u3002\u4E09\u4E2A\u91CC\u552F\u4E00\u6309\u8F6E\u8BA1\u8D39\u7684 \u2014\u2014 \u8FD9\u4E00\u5757\u6BCF\u8F6E\u90FD\u5728 system prompt \u91CC\u3002",
17242
+ artifacts: "artifact \u7684\u4FDD\u7559\u7B56\u7565\u3002",
17243
+ "artifacts.maxAgeDays": "\u591A\u5C11\u5929\u6CA1\u52A8\u8FC7\u7684 artifact \u5220\u6389\uFF08\u7F3A\u7701 7\uFF0C\u4E0A\u9650 3650\uFF09\u3002",
17244
+ "artifacts.maxTotalBytes": "artifact \u603B\u91CF\u4E0A\u9650\uFF08\u5B57\u8282\uFF0C\u7F3A\u7701 512MB\uFF09\u3002\u8D85\u4E86\u6309\u5E74\u9F84\u4ECE\u65E7\u5230\u65B0\u5220\uFF0C\u5F53\u524D\u4F1A\u8BDD\u7684\u76EE\u5F55\u6C38\u4E0D\u5220\u3002",
17245
+ sessions: "\u4F1A\u8BDD\u5E93\u7684\u4FDD\u7559\u671F\u6E05\u7406\u3002",
17246
+ "sessions.autoPrune": "\u6BCF\u6B21\u542F\u52A8\u65F6\u81EA\u52A8\u6E05\u7406\u4E00\u6B21\uFF0C\u81F3\u591A\u4E00\u5929\u4E00\u6B21\uFF08\u7F3A\u7701 false\uFF09\u3002\u5173\u7740\u65F6\u624B\u52A8\u7684 `epoch sessions prune` \u7167\u5E38\u80FD\u7528\u3002",
17247
+ "sessions.retentionDays": "\u591A\u5C11\u5929\u6CA1\u52A8\u8FC7\u7B97\u65E7\uFF08\u7F3A\u7701 90\uFF0C\u4E0A\u9650 3650\uFF09\u3002",
17248
+ "sessions.vacuumAfterPrune": "\u6E05\u5B8C\u4E4B\u540E `VACUUM` \u56DE\u6536\u78C1\u76D8\uFF08\u7F3A\u7701 true\uFF09\u3002\u8FD8\u8981\u8FC7\u300C\u771F\u5220\u6389\u4E86\u4E1C\u897F\u300D\u548C\u300C\u7A7A\u9875\u8D85\u8FC7 25%\u300D\u4E24\u9053\u624D\u771F\u8DD1\u3002",
17249
+ automation: "\u5B9A\u65F6\u4EFB\u52A1\u3002",
17250
+ "automation.keepRuns": "\u4E00\u4E2A\u4EFB\u52A1\u7559\u6700\u8FD1\u591A\u5C11\u6B21\u8FD0\u884C\u7684\u5F55\u50CF\uFF08\u7F3A\u7701 200\uFF0C\u4E0A\u9650 10000\uFF09\u3002",
17251
+ "automation.maxRecordingMb": "\u5355\u4E2A\u4EFB\u52A1\u7684\u5F55\u50CF\u603B\u91CF\u4E0A\u9650\uFF08MB\uFF0C\u7F3A\u7701 64\uFF09\u3002\u548C `keepRuns` \u5148\u649E\u54EA\u6761\u7B97\u54EA\u6761\u3002",
17252
+ budget: "\u82B1\u8D39\u4E0E token \u7684\u95F8\u95E8\u3002**\u7F3A\u7701\u65E0\u4E0A\u9650**\uFF08\u7B49\u4E8E\u4E0D\u542F\u7528\uFF09\u3002",
17253
+ "budget.maxCostUsd": "\u4E00\u6B21\u4F1A\u8BDD\u6700\u591A\u82B1\u591A\u5C11\u7F8E\u5143\u3002\u4E0D\u914D = \u4E0D\u8BBE\u4E0A\u9650\u3002",
17254
+ "budget.maxTokens": "\u4E00\u6B21\u4F1A\u8BDD\u6700\u591A\u7528\u591A\u5C11 token\u3002\u4E0D\u914D = \u4E0D\u8BBE\u4E0A\u9650\u3002",
17255
+ "budget.warnAtPercent": "\u82B1\u5230\u4E0A\u9650\u7684\u767E\u5206\u4E4B\u51E0\u65F6\u5F00\u59CB\u63D0\u9192\uFF080~100\uFF09\u3002",
17256
+ "budget.onUnknownPricing": "\u9047\u5230\u4EF7\u683C\u672A\u77E5\u7684\u6A21\u578B\u600E\u4E48\u529E\uFF1A`block`\uFF08\u62D2\u7EDD\u8C03\u7528\uFF09/ `warn`\uFF08\u7167\u8DD1\uFF0C\u53EA\u8BB0\u4E00\u6761\uFF09\u3002",
17257
+ compliance: "\u5185\u5BB9\u5408\u89C4\u68C0\u67E5\u3002",
17258
+ "compliance.enabled": "\u603B\u5F00\u5173\u3002\u7F3A\u7701 = \u5F00\uFF08\u8FD9\u662F\u4EA7\u54C1\u8981\u6C42\uFF0C\u4E0D\u662F\u53EF\u9009\u589E\u5F3A\uFF09\u3002",
17259
+ "compliance.scope": "\u68C0\u67E5\u54EA\u51E0\u6761\u8DEF\u3002\u4E09\u683C\u5404\u81EA\u7684\u7F3A\u7701\u4E0D\u540C\uFF0C\u89C1\u4E0B\u9762\u4E09\u6761\u3002",
17260
+ "compliance.scope.output": "\u6A21\u578B\u8F93\u51FA\u3002\u7F3A\u7701\u5173 \u2014\u2014 \u8BEF\u4F24\u548C\u6D41\u5F0F\u5EF6\u8FDF\u7684\u5927\u5934\u3002",
17261
+ "compliance.scope.input": "\u7528\u6237\u8F93\u5165\u3002\u7F3A\u7701\u5F00\uFF0C\u8FD9\u4E00\u4FA7\u662F\u5408\u89C4\u7684\u4E3B\u6218\u573A\u3002",
17262
+ "compliance.scope.toolArgs": "\u6A21\u578B\u751F\u6210\u7684\u5DE5\u5177\u5165\u53C2\u3002\u7F3A\u7701\u5173 \u2014\u2014 \u62E6\u4E00\u6B21 `file_write` \u7684\u8BEF\u4F24\u4EE3\u4EF7\u592A\u5927\u3002",
17263
+ "compliance.skipCodeBlocks": "\u56F4\u680F\u4EE3\u7801\u5757\u548C\u884C\u5185 code \u4E0D\u5224\uFF08\u7F3A\u7701\u5F00\uFF09\u3002",
17264
+ "compliance.maxHoldChars": "\u6D41\u5F0F\u68C0\u67E5\u7684\u7F13\u51B2\u533A\u4E0A\u9650\uFF08\u5B57\u7B26\uFF0C\u4E0A\u9650 10000\uFF09\u3002\u8D85\u8FC7\u5C31\u5F3A\u5236\u5224\u4E00\u6B21\uFF0C\u522B\u4E3A\u4E86\u7B49\u4E00\u4E2A\u53E5\u53F7\u628A\u6574\u6BB5\u618B\u4F4F\u3002",
17265
+ "compliance.actions": "\u7C7B\u522B \u2192 \u52A8\u4F5C\u3002\u6CA1\u5217\u5230\u7684\u7C7B\u522B\u8D70\u9ED8\u8BA4\u6863 \u2014\u2014 \u53EA\u5199\u4E86\u4E00\u884C\u4E0D\u4F1A\u628A\u5176\u4F59\u7C7B\u522B\u4E00\u8D77\u53D8\u6210\u7A7A\u3002",
17266
+ headless: "`epoch --print` \u8FD9\u7C7B\u975E\u4EA4\u4E92\u6A21\u5F0F\u7684\u5DE5\u5177\u767D\u540D\u5355\u3002\u7F3A\u7701 = \u8D70\u5404\u81EA\u7684\u5185\u7F6E\u9ED8\u8BA4\u6E05\u5355\u3002",
17267
+ "headless.allowTools": "\u5141\u8BB8\u5728\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u4F7F\u7528\u7684\u5DE5\u5177\u540D\u6E05\u5355\u3002",
17268
+ "headless.allowOperations": "\u5141\u8BB8\u5728\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u4F7F\u7528\u7684\u64CD\u4F5C\u7C7B\u522B\u6E05\u5355\u3002",
17269
+ telemetry: "\u8FD0\u884C\u9065\u6D4B\u3002\u7F3A\u7701 = \u4E0D\u542F\u7528\u3002",
17270
+ "telemetry.enabled": "\u5F00\u5173\u3002",
17271
+ "telemetry.target": "\u8F93\u51FA\u5230\u54EA\u513F\uFF1A`file` / `console` / `otlp`\u3002",
17272
+ "telemetry.outfile": "`target: file` \u65F6\u5199\u5230\u54EA\u4E2A\u6587\u4EF6\u3002",
17273
+ "telemetry.endpoint": "`target: otlp` \u65F6\u7684\u4E0A\u62A5\u5730\u5740\u3002",
17274
+ search: "web \u641C\u7D22\u3002\u7F3A\u7701 = \u4E0D\u542F\u7528\uFF08`web_search` \u5DE5\u5177\u6839\u672C\u4E0D\u4F1A\u6CE8\u518C\uFF09\u3002",
17275
+ "search.provider": "\u7528\u54EA\u5BB6\uFF1A`native` / `tavily` / `brave` / `searxng` / `keyless`\u3002\u4E0D\u914D = \u6309\u624B\u91CC\u6709\u54EA\u4E9B\u51ED\u636E\u81EA\u52A8\u6311\u3002",
17276
+ "search.baseUrl": "`searxng` \u8FD9\u4E00\u7C7B\u81EA\u5EFA\u670D\u52A1\u7684\u57FA\u7840\u5730\u5740\u3002",
17277
+ "search.maxResults": "\u4E00\u6B21\u6700\u591A\u8FD4\u56DE\u51E0\u6761\uFF08\u4E0A\u9650 20\uFF09\u3002",
17278
+ sessionSearch: "\u8DE8\u4F1A\u8BDD\u68C0\u7D22\uFF08`session_search`\uFF09\u3002",
17279
+ "sessionSearch.allWorkspaces": "\u5141\u8BB8\u68C0\u7D22\u5176\u4ED6\u5DE5\u4F5C\u533A\u7684\u4F1A\u8BDD\uFF08\u7F3A\u7701 false = \u53EA\u641C\u5F53\u524D\u5DE5\u4F5C\u533A\uFF09\u3002\u26A0\uFE0F \u653E\u5F00\u5B83\u7B49\u4E8E\u8BA9\u540C\u4E00\u4E2A agent \u8BFB\u5230\u4F60\u522B\u7684\u9879\u76EE\u7684\u5BF9\u8BDD\u3002",
17280
+ statusLine: "\u81EA\u5B9A\u4E49\u72B6\u6001\u680F\u3002\u7F3A\u7701 = \u4E0D\u663E\u793A\u3002",
17281
+ "statusLine.command": "\u53D6\u72B6\u6001\u7684\u90A3\u6761\u547D\u4EE4\u3002\u5B83\u6BCF\u6B21\u5237\u65B0\u90FD\u4F1A spawn \u4E00\u4E2A shell\uFF0C\u522B\u5199\u6210\u5E38\u9A7B\u8FDB\u7A0B\u3002",
17282
+ "statusLine.intervalMs": "\u5237\u65B0\u95F4\u9694\uFF08\u6BEB\u79D2\uFF0C500~300000\uFF09\u3002\u4F4E\u4E8E 500 \u53EA\u662F\u5728\u70E7\u5B50\u8FDB\u7A0B\u3002",
17283
+ display: "\u754C\u9762\u4E0A\u7684\u663E\u793A\u504F\u597D\u3002",
17284
+ "display.language": "\u754C\u9762\u8BED\u8A00\uFF1A`zh` / `en`\u3002\u4E0D\u914D = \u6309\u7CFB\u7EDF locale \u63A2\u6D4B\uFF08\u522B\u9ED8\u8BA4\u5199\u6210 zh\uFF0C\u90A3\u4F1A\u628A\u975E\u4E2D\u6587\u7528\u6237\u63A8\u8FDB\u4E2D\u6587\u754C\u9762\uFF09\u3002",
17285
+ "display.currency": "\u5C4F\u5E55\u4E0A\u90A3\u4E9B\u91D1\u989D\u5370\u6210\u4EC0\u4E48\u5E01\u79CD\u3002**\u53EA\u7BA1\u5370\uFF0C\u4E0D\u6539\u53D8\u4EFB\u4F55\u8BA1\u7B97** \u2014\u2014 \u5355\u4EF7\u8868\u3001\u95F8\u95E8\u6BD4\u8F83\u3001\u843D\u76D8\u7684\u6BCF\u4E00\u5217\u3001\u4F60\u8F93\u8FDB\u6765\u7684\u6BCF\u4E00\u683C\u5168\u662F\u7F8E\u5143\u3002",
17286
+ "display.currency.code": "\u5E01\u79CD\u4EE3\u7801\uFF1A`USD` / `CNY`\u3002",
17287
+ "display.currency.rate": "1 \u7F8E\u5143\u7B49\u4E8E\u591A\u5C11\u8FD9\u4E2A\u5E01\u79CD\u3002**\u6CA1\u6709\u9ED8\u8BA4\u503C\uFF0C\u4E5F\u4E0D\u8054\u7F51\u6293** \u2014\u2014 \u6309 1 \u7B97\u7B49\u4E8E\u628A\u7F8E\u5143\u6570\u5B57\u6362\u4E0A\u4EBA\u6C11\u5E01\u7B26\u53F7\uFF0C\u6BD4\u4E0D\u6362\u66F4\u7CDF\u3002",
17288
+ brand: "\u5BBF\u4E3B\u54C1\u724C\u8BCD\u6807\uFF08\u4FA7\u680F\u90A3\u4E00\u683C\uFF09\u3002\u7F3A\u7701 = \u4EC0\u4E48\u90FD\u4E0D\u663E\u793A\uFF0C\u4E0D\u662F\u56DE\u843D\u5230 `epoch`\u3002",
17289
+ brandWhenSidebarCollapsed: "\u4FA7\u680F\u6536\u8D77\u65F6\u54C1\u724C\u90A3\u4E00\u683C\u7684\u53BB\u7559\uFF1A`hide` / `show`\uFF08\u7F3A\u7701 hide\uFF09\u3002",
17290
+ brandMenuId: "\u70B9\u54C1\u724C\u90A3\u4E00\u683C\u56DE\u7ED9\u5BBF\u4E3B\u7684 `id`\u3002\u6CA1\u914D `brand` \u65F6\u5199\u4E86\u4E5F\u4E0D\u7B97\u9519\uFF0C\u53EA\u662F\u4E0D\u8D77\u4F5C\u7528\u3002",
17291
+ sidebarMenu: "\u5BBF\u4E3B\u81EA\u5B9A\u4E49\u7684\u4FA7\u680F\u5E95\u90E8\u5E38\u9A7B\u6309\u94AE\u3002\u7F3A\u7701 = \u4E00\u884C\u90FD\u4E0D\u591A\u3002`id` \u4E0D\u8BB8\u91CD\u590D\uFF0C`primary: true` \u6700\u591A\u4E00\u9879\u3002",
17292
+ userMenuHost: "\u300C\u7528\u6237\u300D\u90A3\u4E00\u683C\u6574\u5F20\u83DC\u5355\u5F52\u5BBF\u4E3B\u81EA\u5DF1\u753B\u3002\u7F3A\u7701 false = \u7167\u65E7\u6211\u4EEC\u753B\u3002",
17293
+ sidebarHeadMenu: "\u5BBF\u4E3B\u81EA\u5B9A\u4E49\u7684\u4FA7\u680F\u5934\u90A3\u51E0\u679A\u56FE\u6807\u3002\u7F3A\u7701 = \u4E00\u679A\u90FD\u4E0D\u591A\uFF1B`id` \u4E0D\u8BB8\u91CD\u590D\uFF08\u90A3\u4E00\u6392\u4E0D\u5206\u91CC\u5916\uFF0C\u5199 `primary` \u4F1A\u88AB\u5265\u6389\uFF09\u3002",
17294
+ topbarMenu: "\u5BBF\u4E3B\u81EA\u5B9A\u4E49\u7684\u9876\u680F\u53F3\u4E0A\u56FE\u6807\u3002\u7F3A\u7701 = \u4E00\u679A\u90FD\u4E0D\u591A\uFF1B\u754C\u9762\u4E0A\u53EA\u628A\u7B2C\u4E00\u9879\u9732\u5728\u5916\u9762\uFF0C\u5176\u4F59\u6536\u8FDB\u300C\u22EF\u300D\u3002",
17295
+ blankCards: "\u5BBF\u4E3B\u81EA\u5B9A\u4E49\u7684\u5F00\u573A\u5361\u7247\u3002\u7F3A\u7701 = \u65B0\u4F1A\u8BDD\u7A7A\u5C4F\u4E0A\u4E00\u5F20\u90FD\u6CA1\u6709\u3002",
17296
+ blankGreeting: "\u5BBF\u4E3B\u81EA\u5B9A\u4E49\u7684\u90A3\u53E5\u5F00\u573A\u62DB\u547C\u3002\u7F3A\u7701 = \u7528\u6211\u4EEC\u90A3\u53E5\uFF08\u548C\u4E0A\u9762\u51E0\u4E2A\u5BBF\u4E3B\u952E\u7684\u9ED8\u8BA4\u6863\u76F8\u53CD\uFF09\u3002",
17297
+ "blankGreeting.eyebrow": "\u62DB\u547C\u4E0A\u65B9\u90A3\u884C\u5C0F\u5B57\uFF0C\u6309\u8BED\u8A00\u7ED9\uFF08`zh` / `en`\uFF09\uFF0C\u81F3\u5C11\u7ED9\u4E00\u79CD\u3002",
17298
+ "blankGreeting.title": "\u62DB\u547C\u7684\u6807\u9898\uFF0C\u6309\u8BED\u8A00\u7ED9\uFF08`zh` / `en`\uFF09\u3002",
17299
+ "blankGreeting.subtitle": "\u62DB\u547C\u7684\u526F\u6807\u9898\uFF0C\u6309\u8BED\u8A00\u7ED9\uFF08`zh` / `en`\uFF09\u3002",
17300
+ "blankGreeting.hidden": "`true` = \u4E0D\u663E\u793A\u90A3\u53E5\u62DB\u547C\uFF08\u6587\u6848\u53EF\u4EE5\u7559\u7740\uFF0C\u4E0D\u4F1A\u56E0\u4E3A\u85CF\u8D77\u6765\u5C31\u903C\u4EBA\u5220\u6389\uFF09\u3002",
17301
+ scheduleRemoveConfirm: "\u5220\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\u65F6\u90A3\u53E5\u786E\u8BA4\uFF0C\u6574\u53E5\u5F52\u4F60\uFF08\u6309\u8BED\u8A00\u7ED9 `zh` / `en`\uFF0C`{name}` \u4F1A\u6362\u6210\u4EFB\u52A1\u540D\uFF09\u3002\u7F3A\u7701 = \u6211\u4EEC\u90A3\u53E5\u3002\u26A0\uFE0F \u8FDE\u540C\u300C\u8BB0\u5F55\u548C\u5F55\u50CF\u627E\u4E0D\u56DE\u6765\u300D\u90A3\u534A\u53E5\u4E5F\u4E00\u8D77\u5F52\u4F60\u3002",
17302
+ user: "\u4FA7\u680F\u6700\u4E0B\u9762\u90A3\u4E00\u884C\u300C\u7528\u6237\u300D\u7684\u540D\u5B57\u548C\u5934\u50CF\u3002\u7F3A\u7701 = \u300C\u7528\u6237\u300D+ \u4E00\u679A `user-round`\uFF0C\u90A3\u4E00\u884C\u6C38\u8FDC\u5728\u3002",
17303
+ "user.label": "\u90A3\u4E00\u884C\u663E\u793A\u7684\u540D\u5B57\u3002\u5FC5\u586B \u2014\u2014 \u53EA\u7ED9\u56FE\u6807\u4E0D\u5199\u5B83\u6CA1\u6CD5\u56DE\u7B54\u300C\u8C01\u5728\u7528\u8FD9\u4E2A agent\u300D\u3002",
17304
+ "user.icon": "\u56FE\u6807\u540D\u3002\u53EA\u6821\u9A8C\u975E\u7A7A\u5B57\u7B26\u4E32\uFF0C\u8BA4\u4E0D\u51FA\u6765\u7684\u843D\u4E00\u679A\u515C\u5E95\u7684\u3002",
17305
+ "user.avatar": "\u5934\u50CF\uFF1A`data:image/\u2026` \u6216 `https://\u2026`\u3002\u8D85\u5927\u7684\u56FE\u5F53\u6CA1\u7ED9\u3001\u753B\u56FE\u6807\uFF0C\u4E0D\u4F1A\u5224\u6210\u914D\u7F6E\u9519\u8BEF\u3002",
17306
+ settingsHidden: "\u8BBE\u7F6E\u9875\u91CC\u4E0D\u753B\u54EA\u51E0\u4E2A\u5206\u533A\u3002\u7F3A\u7701 = \u5341\u4E2A\u5168\u753B\u3002\u62FC\u9519\u7684 id \u53EA\u4EA7\u4E00\u6761\u8B66\u544A\uFF0C\u4E0D\u4F1A\u8BA9\u6574\u4EFD\u914D\u7F6E\u5931\u6548\u3002",
17307
+ settingsPanes: "\u5BBF\u4E3B\u81EA\u5DF1\u7684\u8BBE\u7F6E\u5206\u533A\uFF08\u4F1A\u51FA\u73B0\u5728\u8BBE\u7F6E\u9875\u91CC\uFF09\u3002`id` \u4E0D\u8BB8\u548C\u5185\u7F6E\u5341\u4E2A\u649E\u540D\uFF1B`url` \u53EA\u6536 http(s) \u2014\u2014 \u5B83\u662F\u4E00\u6761\u6267\u884C\u5165\u53E3\u3002"
16277
17308
  };
16278
17309
  function buildSchemaExports() {
16279
17310
  const used = /* @__PURE__ */ new Set();
@@ -16299,6 +17330,24 @@ function buildSchemaExports() {
16299
17330
  },
16300
17331
  used
16301
17332
  )
17333
+ },
17334
+ {
17335
+ file: "config.schema.json",
17336
+ document: document(
17337
+ EpochConfigSchema,
17338
+ {
17339
+ title: "epoch \u7528\u6237\u7EA7\u914D\u7F6E\uFF08config.yaml\uFF09",
17340
+ description: "\u7528\u6237\u7EA7\u4E3B\u914D\u7F6E `~/.epoch/config.yaml`\uFF08\u591A profile \u65F6\u5728 `~/.epoch/profiles/<\u540D\u5B57>/`\uFF09\u3002\u4F18\u5148\u7EA7\u89C1 docs/SETTINGS.md \u90A3\u5F20\u5C42\u8868\uFF1A\u73AF\u5883\u53D8\u91CF\u538B\u8FC7\u8FD9\u4E00\u4EFD\uFF0C\u8FD9\u4E00\u4EFD\u538B\u8FC7\u5185\u7F6E\u9ED8\u8BA4\u503C\uFF1B\u9879\u76EE\u7EA7 `settings.json` \u53EA\u8BA4\u5F97\u5176\u4E2D\u4E09\u4E2A\u952E\u3002",
17341
+ io: "input",
17342
+ extra: {
17343
+ version: {
17344
+ type: "integer",
17345
+ description: `\u914D\u7F6E\u6587\u4EF6\u7684\u683C\u5F0F\u7248\u672C\uFF0C\u7531\u8FC1\u79FB\u6D41\u7A0B\u8BFB\u8D70\uFF08\u5F53\u524D ${CURRENT_CONFIG_VERSION}\uFF09\u3002\u4E00\u822C\u4E0D\u7528\u624B\u5199 \u2014\u2014 \u8001\u6587\u4EF6\u6CA1\u6709\u8FD9\u4E00\u884C\u65F6\u6309\u5F53\u524D\u7248\u672C\u5904\u7406\u3002`
17346
+ }
17347
+ }
17348
+ },
17349
+ used
17350
+ )
16302
17351
  }
16303
17352
  ];
16304
17353
  const stale = Object.keys(DESCRIPTIONS).filter((key) => !used.has(key));
@@ -16309,13 +17358,22 @@ function buildSchemaExports() {
16309
17358
  }
16310
17359
  return exports;
16311
17360
  }
17361
+ var EXPLICIT_ENUMS = {
17362
+ permission: PERMISSION_LEVELS
17363
+ };
16312
17364
  function document(schema, meta, used) {
16313
- const generated = z.toJSONSchema(schema);
17365
+ const generated = meta.io ? z.toJSONSchema(schema, { io: meta.io }) : z.toJSONSchema(schema);
16314
17366
  const dialect = generated.$schema;
16315
17367
  const properties = {
16316
17368
  $schema: { type: "string" },
17369
+ ...meta.extra,
16317
17370
  ...generated.properties
16318
17371
  };
17372
+ for (const [key, values] of Object.entries(EXPLICIT_ENUMS)) {
17373
+ const node = properties[key];
17374
+ if (node === void 0 || node.type !== "string") continue;
17375
+ properties[key] = { ...node, enum: [...values] };
17376
+ }
16319
17377
  const doc = {
16320
17378
  $schema: dialect,
16321
17379
  title: meta.title,
@@ -16339,10 +17397,138 @@ function annotate(node, path, used) {
16339
17397
  );
16340
17398
  }
16341
17399
  child.description = text;
17400
+ if (MACHINE_DEPENDENT_DEFAULTS.includes(full)) delete child.default;
16342
17401
  used.add(full);
16343
17402
  annotate(child, full, used);
16344
17403
  }
16345
17404
  }
17405
+ var BLOCKED = /* @__PURE__ */ new Set(["homeDir", "dbPath", "profile"]);
17406
+ var cached;
17407
+ function configKeyTable() {
17408
+ cached ??= buildTable();
17409
+ return cached;
17410
+ }
17411
+ function configKeyByPath(path) {
17412
+ return configKeyTable().find((entry) => entry.path === path);
17413
+ }
17414
+ function writableConfigKeys() {
17415
+ return configKeyTable().filter((entry) => entry.write === "scalar");
17416
+ }
17417
+ function buildTable() {
17418
+ const tree = z.toJSONSchema(EpochConfigSchema, { io: "input" });
17419
+ const top = tree.properties;
17420
+ const permission2 = top?.["permission"];
17421
+ if (permission2 !== void 0) permission2.enum = [...PERMISSION_LEVELS];
17422
+ const out = [];
17423
+ collect3(tree, "", 0, out);
17424
+ return out;
17425
+ }
17426
+ function collect3(node, path, depth, out) {
17427
+ const properties = node.properties;
17428
+ if (properties) {
17429
+ for (const [key, child] of Object.entries(properties)) {
17430
+ collect3(child, path ? `${path}.${key}` : key, depth + 1, out);
17431
+ }
17432
+ return;
17433
+ }
17434
+ const kind = scalarKindOf(node.type);
17435
+ const entry = baseEntry(node, path, depth, kind);
17436
+ out.push(entry);
17437
+ }
17438
+ function scalarKindOf(type) {
17439
+ const kinds = Array.isArray(type) ? type : [type];
17440
+ for (const kind of kinds) {
17441
+ if (kind === "string" || kind === "integer" || kind === "number" || kind === "boolean") {
17442
+ return kind;
17443
+ }
17444
+ }
17445
+ return void 0;
17446
+ }
17447
+ function baseEntry(node, path, depth, kind) {
17448
+ const dot = depth === 2 ? path.indexOf(".") : -1;
17449
+ const entry = {
17450
+ path,
17451
+ ...dot < 0 ? {} : { section: path.slice(0, dot), field: path.slice(dot + 1) },
17452
+ kind: kind ?? "string",
17453
+ write: "manual",
17454
+ ...numericBounds(node),
17455
+ ...node.minLength === void 0 ? {} : { minLength: node.minLength },
17456
+ ...Array.isArray(node.enum) ? {
17457
+ choices: node.enum.filter((v) => typeof v === "string")
17458
+ } : {},
17459
+ ...isSettingValue(node.default) ? { builtin: node.default } : {}
17460
+ };
17461
+ if (BLOCKED.has(path)) return { ...entry, write: "blocked", reason: "position" };
17462
+ if (kind === void 0) return { ...entry, reason: "structured" };
17463
+ if (depth > 2) return { ...entry, reason: "nested" };
17464
+ return { ...entry, write: "scalar" };
17465
+ }
17466
+ function numericBounds(node) {
17467
+ return {
17468
+ ...typeof node.minimum === "number" ? { minimum: node.minimum } : {},
17469
+ ...typeof node.exclusiveMinimum === "number" ? { exclusiveMinimum: node.exclusiveMinimum } : {},
17470
+ ...typeof node.maximum === "number" ? { maximum: node.maximum } : {},
17471
+ ...typeof node.exclusiveMaximum === "number" ? { exclusiveMaximum: node.exclusiveMaximum } : {}
17472
+ };
17473
+ }
17474
+ function isSettingValue(value) {
17475
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
17476
+ }
17477
+ function checkConfigValue(entry, raw) {
17478
+ const text = raw.trim();
17479
+ switch (entry.kind) {
17480
+ case "boolean": {
17481
+ if (text !== "true" && text !== "false") {
17482
+ return { ok: false, problem: { code: "not-a-kind", kind: "boolean" } };
17483
+ }
17484
+ return { ok: true, literal: text };
17485
+ }
17486
+ case "integer":
17487
+ case "number": {
17488
+ const value = Number(text);
17489
+ if (text === "" || !Number.isFinite(value)) {
17490
+ return { ok: false, problem: { code: "not-a-kind", kind: entry.kind } };
17491
+ }
17492
+ if (entry.kind === "integer" && !Number.isInteger(value)) {
17493
+ return { ok: false, problem: { code: "not-a-kind", kind: "integer" } };
17494
+ }
17495
+ const bounds = checkBounds(entry, value);
17496
+ if (bounds) return { ok: false, problem: bounds };
17497
+ return { ok: true, literal: String(value) };
17498
+ }
17499
+ }
17500
+ const choices = entry.choices;
17501
+ if (choices !== void 0) {
17502
+ if (!choices.includes(text)) {
17503
+ return { ok: false, problem: { code: "not-in-choices", choices } };
17504
+ }
17505
+ return { ok: true, literal: text };
17506
+ }
17507
+ const minLength = entry.minLength ?? 1;
17508
+ if (text.length < minLength) {
17509
+ return { ok: false, problem: { code: "too-short", minLength } };
17510
+ }
17511
+ return { ok: true, literal: quoteIfNeeded(text) };
17512
+ }
17513
+ function checkBounds(entry, value) {
17514
+ if (entry.exclusiveMinimum !== void 0 && value <= entry.exclusiveMinimum) {
17515
+ return { code: "below-minimum", bound: entry.exclusiveMinimum, exclusive: true };
17516
+ }
17517
+ if (entry.minimum !== void 0 && value < entry.minimum) {
17518
+ return { code: "below-minimum", bound: entry.minimum, exclusive: false };
17519
+ }
17520
+ if (entry.exclusiveMaximum !== void 0 && value >= entry.exclusiveMaximum) {
17521
+ return { code: "above-maximum", bound: entry.exclusiveMaximum, exclusive: true };
17522
+ }
17523
+ if (entry.maximum !== void 0 && value > entry.maximum) {
17524
+ return { code: "above-maximum", bound: entry.maximum, exclusive: false };
17525
+ }
17526
+ return void 0;
17527
+ }
17528
+ function quoteIfNeeded(value) {
17529
+ if (/^[A-Za-z0-9_][A-Za-z0-9_./+=:-]*$/.test(value)) return value;
17530
+ return `'${value.replaceAll("'", "''")}'`;
17531
+ }
16346
17532
  var MODULE2 = "Keybindings";
16347
17533
  var VIM_MODE_KEY = "vimMode";
16348
17534
  var FileSchema = z.record(z.string(), z.array(z.string()));
@@ -16537,11 +17723,11 @@ function buildSearchQuery(query) {
16537
17723
  }
16538
17724
  const latinPart = cleaned.replace(CJK_RUN, " ");
16539
17725
  for (const w of latinPart.split(/\s+/)) {
16540
- const t81 = w.trim();
16541
- if (t81.length >= 2) terms.add(t81);
17726
+ const t83 = w.trim();
17727
+ if (t83.length >= 2) terms.add(t83);
16542
17728
  }
16543
17729
  if (terms.size === 0) return null;
16544
- return [...terms].map((t81) => `"${t81.replace(/"/g, "")}"`).join(" OR ");
17730
+ return [...terms].map((t83) => `"${t83.replace(/"/g, "")}"`).join(" OR ");
16545
17731
  }
16546
17732
  // src/memory/db.ts
16547
17733
  var NS2 = "memory";
@@ -17032,8 +18218,8 @@ var MemoryManager = class {
17032
18218
  async add(target, content) {
17033
18219
  const label = injectionLabelOf(content);
17034
18220
  if (label !== void 0) return this.refuseInjection(label);
17035
- const budget2 = this.getBudget()[target];
17036
- if (budget2.current + content.length > budget2.limit) {
18221
+ const budget = this.getBudget()[target];
18222
+ if (budget.current + content.length > budget.limit) {
17037
18223
  this.consolidationFailures++;
17038
18224
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
17039
18225
  return {
@@ -17080,9 +18266,9 @@ var MemoryManager = class {
17080
18266
  }
17081
18267
  const label = injectionLabelOf(content);
17082
18268
  if (label !== void 0) return this.refuseInjection(label);
17083
- const budget2 = this.getBudget()[target];
18269
+ const budget = this.getBudget()[target];
17084
18270
  const delta = content.length - existing.charLength;
17085
- if (budget2.current + delta > budget2.limit) {
18271
+ if (budget.current + delta > budget.limit) {
17086
18272
  this.consolidationFailures++;
17087
18273
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
17088
18274
  return { success: false, done: true, error: "Consolidation limit reached" };
@@ -17287,11 +18473,11 @@ var MemoryManager = class {
17287
18473
  for (const target of ["memory", "user"]) {
17288
18474
  const rows = kept.filter((e) => e.target === target);
17289
18475
  if (rows.length === 0) continue;
17290
- const budget2 = this.getBudget()[target];
18476
+ const budget = this.getBudget()[target];
17291
18477
  const label = target === "user" ? "USER PROFILE (who the user is)" : "MEMORY (your personal notes)";
17292
18478
  sections.push(
17293
18479
  [
17294
- `${label} [${rows.length} entries \xB7 ${budget2.current.toLocaleString()}/${budget2.limit.toLocaleString()} chars]`,
18480
+ `${label} [${rows.length} entries \xB7 ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars]`,
17295
18481
  ...rows.map((e) => this.indexLine(e))
17296
18482
  ].join("\n")
17297
18483
  );
@@ -17322,7 +18508,7 @@ var MemoryManager = class {
17322
18508
  return `${e.id} ${e.summary} (${e.charLength} chars)`;
17323
18509
  }
17324
18510
  buildConsolidateHint(target) {
17325
- const budget2 = this.getBudget()[target];
18511
+ const budget = this.getBudget()[target];
17326
18512
  const heat = /* @__PURE__ */ new Map();
17327
18513
  for (const row of this.db.getAll(target)) {
17328
18514
  const id = this.idByContent.get(contentKey(row.target, row.content));
@@ -17330,7 +18516,7 @@ var MemoryManager = class {
17330
18516
  }
17331
18517
  const candidates2 = this.indexEntries(target).sort((a, b) => (heat.get(a.id) ?? 0) - (heat.get(b.id) ?? 0) || b.charLength - a.charLength).slice(0, 5);
17332
18518
  return [
17333
- `Memory at ${budget2.current.toLocaleString()}/${budget2.limit.toLocaleString()} chars (${budget2.percent}%) for ${target}. Cannot add new entry.`,
18519
+ `Memory at ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars (${budget.percent}%) for ${target}. Cannot add new entry.`,
17334
18520
  "",
17335
18521
  "To make room, work by id \u2014 NOT by old_text. The MEMORY INDEX only shows a truncated",
17336
18522
  "first line, so text copied from it will never match an entry exactly.",
@@ -18172,6 +19358,10 @@ var SessionDB = class {
18172
19358
  where += " AND cwd = @cwd";
18173
19359
  params["cwd"] = input.cwd;
18174
19360
  }
19361
+ if (!input.includeDelegate) {
19362
+ where += " AND COALESCE(source, '') != @delegateSource";
19363
+ params["delegateSource"] = DELEGATE_SESSION_SOURCE;
19364
+ }
18175
19365
  if (input.updatedSince !== void 0) {
18176
19366
  where += " AND COALESCE(updated_at, started_at) > @updatedSince";
18177
19367
  params["updatedSince"] = input.updatedSince;
@@ -18232,8 +19422,8 @@ var SessionDB = class {
18232
19422
  const todo = this.db.prepare(
18233
19423
  "INSERT INTO todos (id, session_id, content, status, reason, created_at, updated_at) VALUES (@id, @sessionId, @content, @status, @reason, @createdAt, @updatedAt)"
18234
19424
  );
18235
- for (const t81 of extras.todos ?? []) {
18236
- todo.run({ ...t81, sessionId: m.id, reason: t81.reason || null });
19425
+ for (const t83 of extras.todos ?? []) {
19426
+ todo.run({ ...t83, sessionId: m.id, reason: t83.reason || null });
18237
19427
  }
18238
19428
  }
18239
19429
  const g = extras.goal;
@@ -18387,57 +19577,25 @@ var SessionDB = class {
18387
19577
  searchContent(query, limit) {
18388
19578
  const safeQuery = sanitizeFtsQuery(query);
18389
19579
  if (!safeQuery) return [];
18390
- const ftsQuery = buildFtsMatchQuery(safeQuery);
18391
- const ftsRows = this.db.prepare(
18392
- `
19580
+ const sql = (table) => `
18393
19581
  SELECT m.*, s.title as session_title
18394
- FROM messages_fts f
18395
- JOIN messages m ON f.rowid = m.id
18396
- JOIN sessions s ON m.session_id = s.id
18397
- WHERE messages_fts MATCH @query AND s.archived = 0
18398
- ORDER BY rank
18399
- LIMIT @limit
18400
- `
18401
- ).all({
18402
- query: ftsQuery,
18403
- limit: limit * 3
18404
- });
18405
- if (ftsRows.length === 0) {
18406
- const trigramRows = this.db.prepare(
18407
- `
18408
- SELECT m.*, s.title as session_title
18409
- FROM messages_fts_trigram f
19582
+ FROM ${table} f
18410
19583
  JOIN messages m ON f.rowid = m.id
18411
19584
  JOIN sessions s ON m.session_id = s.id
18412
- WHERE messages_fts_trigram MATCH @query AND s.archived = 0
18413
- ORDER BY rank
18414
- LIMIT @limit
18415
- `
18416
- ).all({ query: buildTrigramMatchQuery(safeQuery), limit: limit * 3 });
18417
- const seen2 = /* @__PURE__ */ new Set();
18418
- const results2 = [];
18419
- for (const r of trigramRows) {
18420
- const sid = r["session_id"];
18421
- if (seen2.has(sid)) continue;
18422
- seen2.add(sid);
18423
- if (results2.length >= limit) break;
18424
- results2.push({
18425
- session: this.getSessionMeta(sid),
18426
- matches: [
18427
- {
18428
- messageIndex: r["id"],
18429
- snippet: String(r["content"] ?? "").slice(0, 200),
18430
- score: 1
18431
- }
18432
- ],
18433
- score: 1
18434
- });
18435
- }
18436
- return results2;
18437
- }
19585
+ WHERE ${table} MATCH @query AND s.archived = 0
19586
+ ORDER BY rank
19587
+ LIMIT @limit`;
19588
+ const candidates2 = limit * 3;
19589
+ const rowsFor = (table, match) => this.db.prepare(sql(table)).all({ query: match, limit: candidates2 });
19590
+ const porter = rowsFor("messages_fts", buildFtsMatchQuery(safeQuery));
19591
+ const rows = belowTrigramFloor(safeQuery) ? porter : mergeRankedRows(
19592
+ porter,
19593
+ rowsFor("messages_fts_trigram", buildTrigramMatchQuery(safeQuery)),
19594
+ candidates2
19595
+ );
18438
19596
  const seen = /* @__PURE__ */ new Set();
18439
19597
  const results = [];
18440
- for (const r of ftsRows) {
19598
+ for (const r of rows) {
18441
19599
  const sid = r["session_id"];
18442
19600
  if (seen.has(sid)) continue;
18443
19601
  seen.add(sid);
@@ -18446,7 +19604,7 @@ var SessionDB = class {
18446
19604
  session: this.getSessionMeta(sid),
18447
19605
  matches: [
18448
19606
  {
18449
- messageIndex: r["id"],
19607
+ messageIndex: r.id,
18450
19608
  snippet: String(r["content"] ?? "").slice(0, 200),
18451
19609
  score: 1
18452
19610
  }
@@ -18523,6 +19681,26 @@ var SessionDB = class {
18523
19681
  duration: 0
18524
19682
  };
18525
19683
  }
19684
+ offloadImageParts(sessionId) {
19685
+ const rows = this.db.prepare(
19686
+ `SELECT id, parts FROM messages WHERE session_id = ? AND active = 1 AND parts IS NOT NULL AND parts LIKE '%"image"%'`
19687
+ ).all(sessionId);
19688
+ const update = this.db.prepare("UPDATE messages SET parts = @parts WHERE id = @id");
19689
+ let changed = 0;
19690
+ const run = this.db.transaction(() => {
19691
+ for (const row of rows) {
19692
+ const parts = parseParts(row.parts);
19693
+ if (!parts || !parts.some((p) => p.type === "image")) continue;
19694
+ const next = parts.map(
19695
+ (p) => p.type === "image" ? { type: "text", text: OFFLOADED_IMAGE_NOTE } : p
19696
+ );
19697
+ update.run({ parts: JSON.stringify(next), id: row.id });
19698
+ changed += 1;
19699
+ }
19700
+ });
19701
+ run();
19702
+ return changed;
19703
+ }
18526
19704
  archiveAndCompact(sessionId, input) {
18527
19705
  const run = this.db.transaction(() => {
18528
19706
  const rows = this.db.prepare(
@@ -18745,7 +19923,7 @@ function checkFtsHealth(db) {
18745
19923
  messages,
18746
19924
  tables,
18747
19925
  missingTriggers,
18748
- healthy: missingTriggers.length === 0 && tables.every((t81) => t81.error === null && t81.drift === null && t81.rows === messages),
19926
+ healthy: missingTriggers.length === 0 && tables.every((t83) => t83.error === null && t83.drift === null && t83.rows === messages),
18749
19927
  indexBytes: indexBytesOf(db),
18750
19928
  space: spaceOf(db)
18751
19929
  };
@@ -18895,10 +20073,10 @@ var AUTO_VACUUM_MIN_FREELIST_RATIO = 0.25;
18895
20073
  var IMPORT_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
18896
20074
  var TODO_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "cancelled"]);
18897
20075
  function validateImportExtras(input) {
18898
- for (const [i, t81] of (input.todos ?? []).entries()) {
18899
- if (typeof t81.content !== "string" || t81.content === "") return `todos[${i}].content`;
18900
- if (!TODO_STATUSES.has(t81.status)) return `todos[${i}].status`;
18901
- if (typeof t81.createdAt !== "string" || typeof t81.updatedAt !== "string") return `todos[${i}].at`;
20076
+ for (const [i, t83] of (input.todos ?? []).entries()) {
20077
+ if (typeof t83.content !== "string" || t83.content === "") return `todos[${i}].content`;
20078
+ if (!TODO_STATUSES.has(t83.status)) return `todos[${i}].status`;
20079
+ if (typeof t83.createdAt !== "string" || typeof t83.updatedAt !== "string") return `todos[${i}].at`;
18902
20080
  }
18903
20081
  const g = input.goal;
18904
20082
  if (g === void 0) return void 0;
@@ -19087,7 +20265,7 @@ var SessionManager = class {
19087
20265
  converted.push({ ...rest, parts: result.parts });
19088
20266
  }
19089
20267
  const droppedParentId = meta.parentId !== void 0 && !this.db.hasSession(meta.parentId) ? meta.parentId : void 0;
19090
- const todos = (input.todos ?? []).map((t81) => ({ ...t81, id: this.db.freshTodoId() }));
20268
+ const todos = (input.todos ?? []).map((t83) => ({ ...t83, id: this.db.freshTodoId() }));
19091
20269
  const goal = input.goal === void 0 ? void 0 : { ...input.goal, id: `goal_${randomUUID().replace(/-/g, "")}` };
19092
20270
  try {
19093
20271
  const newIds = this.db.importSession(
@@ -19105,7 +20283,7 @@ var SessionManager = class {
19105
20283
  messageCount: newIds.length,
19106
20284
  idMap,
19107
20285
  ...droppedParentId === void 0 ? {} : { droppedParentId },
19108
- ...input.todos === void 0 ? {} : { todoIds: todos.map((t81) => t81.id) },
20286
+ ...input.todos === void 0 ? {} : { todoIds: todos.map((t83) => t83.id) },
19109
20287
  ...goal === void 0 ? {} : { goalId: goal.id }
19110
20288
  };
19111
20289
  } catch (err) {
@@ -19252,6 +20430,9 @@ var SessionManager = class {
19252
20430
  const summary = toSummarize.length === 0 ? "" : await this.buildSummary(toSummarize, summarize2) ?? outlineOf(toSummarize);
19253
20431
  return this.db.compact(sessionId, summary, beforeCount, IMPORTANT_ROUNDS * 2);
19254
20432
  }
20433
+ offloadImageParts(sessionId) {
20434
+ return this.db.offloadImageParts(sessionId);
20435
+ }
19255
20436
  archiveAndCompact(sessionId, input) {
19256
20437
  return this.db.archiveAndCompact(sessionId, input);
19257
20438
  }
@@ -19447,8 +20628,8 @@ var GoalService = class {
19447
20628
  const text = objective.trim();
19448
20629
  if (!text) return { ok: false, reason: "empty-objective" };
19449
20630
  if (text.length > MAX_OBJECTIVE_CHARS) return { ok: false, reason: "objective-too-long" };
19450
- const budget2 = maxRounds ?? DEFAULT_GOAL_MAX_ROUNDS;
19451
- if (!Number.isInteger(budget2) || budget2 < 1 || budget2 > MAX_GOAL_MAX_ROUNDS) {
20631
+ const budget = maxRounds ?? DEFAULT_GOAL_MAX_ROUNDS;
20632
+ if (!Number.isInteger(budget) || budget < 1 || budget > MAX_GOAL_MAX_ROUNDS) {
19452
20633
  return { ok: false, reason: "bad-budget" };
19453
20634
  }
19454
20635
  const existing = this.current(sessionId);
@@ -19460,7 +20641,7 @@ var GoalService = class {
19460
20641
  objective: text,
19461
20642
  phase: "active",
19462
20643
  block: void 0,
19463
- maxRounds: budget2,
20644
+ maxRounds: budget,
19464
20645
  roundsUsed: 0,
19465
20646
  completeEvidence: void 0,
19466
20647
  createdAt: at,
@@ -19605,7 +20786,7 @@ function withGoalCompression(engine, view) {
19605
20786
  if (!now) return engine.compress(messages, config);
19606
20787
  return engine.compress(messages, { ...config, goal: now.objective });
19607
20788
  },
19608
- shouldCompress: (promptTokens2, contextLength) => engine.shouldCompress(promptTokens2, contextLength),
20789
+ shouldCompress: (promptTokens3, contextLength) => engine.shouldCompress(promptTokens3, contextLength),
19609
20790
  trackUsage: (usage) => engine.trackUsage(usage),
19610
20791
  getTotalTokens: () => engine.getTotalTokens(),
19611
20792
  reset: () => engine.reset()
@@ -20000,7 +21181,7 @@ var AppearanceStore = class {
20000
21181
  }
20001
21182
  };
20002
21183
  function ruleAskActive(level) {
20003
- return level !== "bypass";
21184
+ return approvalPolicyForLevel(level) === "ask";
20004
21185
  }
20005
21186
  function checkCodeExec(isolation) {
20006
21187
  if (isolation === "os") {
@@ -20034,7 +21215,7 @@ function checkPlan(operation, extraRoots) {
20034
21215
  code: "level-default"
20035
21216
  };
20036
21217
  }
20037
- if (operation.type === "file_write" || operation.type === "command") {
21218
+ if (operation.type === "file_write") {
20038
21219
  return { allowed: false, reason: t("permission_reason.plan_no_write"), code: "level-default" };
20039
21220
  }
20040
21221
  if (operation.type === "network") {
@@ -20044,6 +21225,10 @@ function checkPlan(operation, extraRoots) {
20044
21225
  code: "level-default"
20045
21226
  };
20046
21227
  }
21228
+ if (operation.type === "command") {
21229
+ if (isReadOnlyCommand(operation.detail)) return { allowed: true, code: "level-default" };
21230
+ return { allowed: false, reason: t("permission_reason.plan_no_write"), code: "level-default" };
21231
+ }
20047
21232
  return outsideWorkspace(operation, extraRoots) ?? { allowed: true, code: "level-default" };
20048
21233
  }
20049
21234
  function checkAcceptEdits(operation, isolation, extraRoots) {
@@ -20069,8 +21254,6 @@ function checkDefault(operation, extraRoots) {
20069
21254
  }
20070
21255
  function decideByLevel(level, operation, isolation, extraRoots = []) {
20071
21256
  switch (level) {
20072
- case "bypass":
20073
- return { allowed: true, code: "level-default" };
20074
21257
  case "auto":
20075
21258
  return checkAuto();
20076
21259
  case "plan":
@@ -20250,11 +21433,11 @@ var ApprovalCache = class {
20250
21433
  return !rel.startsWith("..") && !isAbsolute(rel);
20251
21434
  }
20252
21435
  normalizeTarget(target) {
20253
- const t81 = target.trim();
20254
- if (!t81) return "";
20255
- const unix = t81.replace(/\\/g, "/");
21436
+ const t83 = target.trim();
21437
+ if (!t83) return "";
21438
+ const unix = t83.replace(/\\/g, "/");
20256
21439
  if (unix.startsWith("/") || /^[A-Za-z]:\//.test(unix)) return resolve(unix);
20257
- return t81;
21440
+ return t83;
20258
21441
  }
20259
21442
  load() {
20260
21443
  try {
@@ -20357,9 +21540,9 @@ function createSharedPermissionState(cache) {
20357
21540
  };
20358
21541
  }
20359
21542
  function cacheKeyFor(raw) {
20360
- const t81 = raw.trim();
20361
- if (!t81) return null;
20362
- return t81.slice(0, 500);
21543
+ const t83 = raw.trim();
21544
+ if (!t83) return null;
21545
+ return t83.slice(0, 500);
20363
21546
  }
20364
21547
  // src/permission/manager.ts
20365
21548
  var log3 = createLogger("permission");
@@ -20472,15 +21655,23 @@ var PermissionManager = class _PermissionManager {
20472
21655
  code: hard.code
20473
21656
  };
20474
21657
  }
20475
- if (this.level === "bypass" && hard.code === "obfuscated") {
21658
+ const neverAsk = approvalPolicyForLevel(this.level) === "never";
21659
+ if (neverAsk && hard.code === "obfuscated") {
20476
21660
  return {
20477
21661
  allowed: true,
20478
21662
  reason: t("permission_reason.bypass_unaskable", { reason: hard.reason ?? "" }),
20479
21663
  code: hard.code
20480
21664
  };
20481
21665
  }
20482
- const cached = this.cachedDecision(operation);
20483
- if (cached) return cached;
21666
+ const cached2 = this.cachedDecision(operation);
21667
+ if (cached2) return cached2;
21668
+ if (neverAsk) {
21669
+ return {
21670
+ allowed: false,
21671
+ reason: t("permission_reason.forbidden_unaskable", { reason: hard.reason ?? "" }),
21672
+ code: hard.code
21673
+ };
21674
+ }
20484
21675
  if (this.shared.nonInteractive) {
20485
21676
  return {
20486
21677
  allowed: false,
@@ -20491,7 +21682,7 @@ var PermissionManager = class _PermissionManager {
20491
21682
  return null;
20492
21683
  }
20493
21684
  approvalCacheActive() {
20494
- return this.level !== "bypass";
21685
+ return approvalPolicyForLevel(this.level) === "ask";
20495
21686
  }
20496
21687
  rememberFlag() {
20497
21688
  return this.approvalCacheActive() ? {} : { rememberable: false };
@@ -20499,15 +21690,15 @@ var PermissionManager = class _PermissionManager {
20499
21690
  cachedDecision(operation) {
20500
21691
  const cacheTool = operation.toolName ?? operation.type;
20501
21692
  const cacheKey = cacheKeyFor(operation.target || operation.detail);
20502
- const cached = cacheKey ? this.shared.cache.get(cacheTool, cacheKey) : null;
20503
- if (cached === "allow-always" || cached === "allow-session") {
21693
+ const cached2 = cacheKey ? this.shared.cache.get(cacheTool, cacheKey) : null;
21694
+ if (cached2 === "allow-always" || cached2 === "allow-session") {
20504
21695
  return {
20505
21696
  allowed: true,
20506
21697
  reason: t("permission_reason.cached_allow"),
20507
21698
  code: "approval-cache"
20508
21699
  };
20509
21700
  }
20510
- if (cached === "deny") {
21701
+ if (cached2 === "deny") {
20511
21702
  return {
20512
21703
  allowed: false,
20513
21704
  reason: t("permission_reason.cached_deny"),
@@ -20530,8 +21721,8 @@ var PermissionManager = class _PermissionManager {
20530
21721
  if (ruleDecision && !ruleDecision.allowed && !ruleDecision.requireApproval) {
20531
21722
  return ruleDecision;
20532
21723
  }
20533
- const cached = this.approvalCacheActive() ? this.cachedDecision(operation) : null;
20534
- if (cached) return cached;
21724
+ const cached2 = this.approvalCacheActive() ? this.cachedDecision(operation) : null;
21725
+ if (cached2) return cached2;
20535
21726
  const asked = listMatch?.verdict === "ask" ? {
20536
21727
  allowed: false,
20537
21728
  requireApproval: true,
@@ -20814,6 +22005,14 @@ var MIGRATIONS3 = [
20814
22005
  up: (db) => {
20815
22006
  db.exec(`ALTER TABLE schedules ADD COLUMN show_in_session_list INTEGER NOT NULL DEFAULT 0;`);
20816
22007
  }
22008
+ },
22009
+ {
22010
+ id: "schedule-drop-budget",
22011
+ version: 3,
22012
+ description: "\u64A4\u6389\u300C\u8FD9\u4E00\u6B21\u6700\u591A\u82B1\u591A\u5C11\u300D\u90A3\u4E00\u5217\uFF08\u9884\u7B97\u4E0D\u518D\u662F\u4E00\u628A\u5C3A\u5B50\uFF09",
22013
+ up: (db) => {
22014
+ db.exec(`ALTER TABLE schedules DROP COLUMN max_budget_usd;`);
22015
+ }
20817
22016
  }
20818
22017
  ];
20819
22018
  var ScheduleStore = class {
@@ -20844,7 +22043,6 @@ var ScheduleStore = class {
20844
22043
  allowOperations: input.allowOperations ?? [],
20845
22044
  allowRules: input.allowRules ?? [],
20846
22045
  maxTurns: input.maxTurns ?? SCHEDULE_DEFAULTS.maxTurns,
20847
- maxBudgetUsd: input.maxBudgetUsd,
20848
22046
  timeoutMs: input.timeoutMs ?? SCHEDULE_DEFAULTS.timeoutMs,
20849
22047
  ...input.showInSessionList === true ? { showInSessionList: true } : {},
20850
22048
  trigger: normalizeTrigger(input.trigger),
@@ -20870,7 +22068,6 @@ var ScheduleStore = class {
20870
22068
  allowOperations: [...input.allowOperations],
20871
22069
  allowRules: [...input.allowRules],
20872
22070
  maxTurns: input.maxTurns,
20873
- maxBudgetUsd: input.maxBudgetUsd,
20874
22071
  timeoutMs: input.timeoutMs,
20875
22072
  ...input.showInSessionList === true ? { showInSessionList: true } : {},
20876
22073
  trigger: normalizeTrigger(input.trigger),
@@ -20912,14 +22109,14 @@ var ScheduleStore = class {
20912
22109
  `INSERT INTO schedules (
20913
22110
  id, name, enabled, created_at, updated_at, prompt, work_dir, model, role,
20914
22111
  skills, mcp_servers, permission, allow_tools, allow_operations, allow_rules,
20915
- max_turns, max_budget_usd, timeout_ms, show_in_session_list,
22112
+ max_turns, timeout_ms, show_in_session_list,
20916
22113
  trigger_kind, trigger_spec,
20917
22114
  start_date, end_date, os_backend, os_task_id, os_registered_at, os_command,
20918
22115
  last_run_at, last_status
20919
22116
  ) VALUES (
20920
22117
  @id, @name, @enabled, @created_at, @updated_at, @prompt, @work_dir, @model, @role,
20921
22118
  @skills, @mcp_servers, @permission, @allow_tools, @allow_operations, @allow_rules,
20922
- @max_turns, @max_budget_usd, @timeout_ms, @show_in_session_list,
22119
+ @max_turns, @timeout_ms, @show_in_session_list,
20923
22120
  @trigger_kind, @trigger_spec,
20924
22121
  @start_date, @end_date, @os_backend, @os_task_id, @os_registered_at, @os_command,
20925
22122
  @last_run_at, @last_status
@@ -21035,7 +22232,7 @@ var ScheduleStore = class {
21035
22232
  work_dir = @work_dir, model = @model, role = @role, skills = @skills,
21036
22233
  mcp_servers = @mcp_servers, permission = @permission, allow_tools = @allow_tools,
21037
22234
  allow_operations = @allow_operations, allow_rules = @allow_rules,
21038
- max_turns = @max_turns, max_budget_usd = @max_budget_usd, timeout_ms = @timeout_ms,
22235
+ max_turns = @max_turns, timeout_ms = @timeout_ms,
21039
22236
  show_in_session_list = @show_in_session_list,
21040
22237
  trigger_kind = @trigger_kind, trigger_spec = @trigger_spec,
21041
22238
  start_date = @start_date, end_date = @end_date, os_backend = @os_backend,
@@ -21057,7 +22254,6 @@ function mergeSchedulePatch(current, patch) {
21057
22254
  "allowOperations",
21058
22255
  "allowRules",
21059
22256
  "maxTurns",
21060
- "maxBudgetUsd",
21061
22257
  "timeoutMs",
21062
22258
  "showInSessionList",
21063
22259
  "trigger",
@@ -21088,12 +22284,11 @@ function toRow(def) {
21088
22284
  role: def.role ?? null,
21089
22285
  skills: def.skills ? JSON.stringify(def.skills) : null,
21090
22286
  mcp_servers: def.mcpServers ? JSON.stringify(def.mcpServers) : null,
21091
- permission: def.permission,
22287
+ permission: permissionOfRow(def.permission),
21092
22288
  allow_tools: JSON.stringify(def.allowTools),
21093
22289
  allow_operations: JSON.stringify(def.allowOperations),
21094
22290
  allow_rules: JSON.stringify(def.allowRules),
21095
22291
  max_turns: def.maxTurns,
21096
- max_budget_usd: def.maxBudgetUsd,
21097
22292
  timeout_ms: def.timeoutMs,
21098
22293
  show_in_session_list: def.showInSessionList === true ? 1 : 0,
21099
22294
  trigger_kind: def.trigger.kind,
@@ -21108,6 +22303,9 @@ function toRow(def) {
21108
22303
  last_status: def.lastStatus ?? null
21109
22304
  };
21110
22305
  }
22306
+ function permissionOfRow(raw) {
22307
+ return resolvePermissionLevel(raw) ?? SCHEDULE_DEFAULT_PERMISSION;
22308
+ }
21111
22309
  function fromRow(row) {
21112
22310
  return {
21113
22311
  id: row.id,
@@ -21121,12 +22319,11 @@ function fromRow(row) {
21121
22319
  ...row.role !== null ? { role: row.role } : {},
21122
22320
  ...row.skills !== null ? { skills: parseArray2(row.skills) } : {},
21123
22321
  ...row.mcp_servers !== null ? { mcpServers: parseArray2(row.mcp_servers) } : {},
21124
- permission: row.permission,
22322
+ permission: permissionOfRow(row.permission),
21125
22323
  allowTools: parseArray2(row.allow_tools),
21126
22324
  allowOperations: parseArray2(row.allow_operations),
21127
22325
  allowRules: parseArray2(row.allow_rules),
21128
22326
  maxTurns: row.max_turns,
21129
- maxBudgetUsd: row.max_budget_usd,
21130
22327
  timeoutMs: row.timeout_ms,
21131
22328
  ...row.show_in_session_list !== 0 ? { showInSessionList: true } : {},
21132
22329
  trigger: JSON.parse(row.trigger_spec),
@@ -22572,7 +23769,7 @@ function loadLexiconDir(dir, source) {
22572
23769
  });
22573
23770
  continue;
22574
23771
  }
22575
- collect3(content, file, spec, source, out);
23772
+ collect4(content, file, spec, source, out);
22576
23773
  }
22577
23774
  return out;
22578
23775
  }
@@ -22585,7 +23782,7 @@ function classify2(file) {
22585
23782
  }
22586
23783
  return CATEGORY_SET.has(base2) ? { kind: "terms", category: base2 } : null;
22587
23784
  }
22588
- function collect3(content, file, spec, source, out) {
23785
+ function collect4(content, file, spec, source, out) {
22589
23786
  const lines = content.split("\n");
22590
23787
  for (let i = 0; i < lines.length; i++) {
22591
23788
  const line = (lines[i] ?? "").trim();
@@ -22637,6 +23834,18 @@ function resolveComplianceDirs(opts) {
22637
23834
  dirs.push({ dir: projectDir, source: "project" });
22638
23835
  return { dirs, projectSkipped: false };
22639
23836
  }
23837
+ function hookSkipReason(reason9) {
23838
+ if (reason9 === "escalated") return "mode-disabled";
23839
+ return reason9;
23840
+ }
23841
+ function hookSandboxPolicy(workDir) {
23842
+ return {
23843
+ mode: "workspace-write",
23844
+ workspaceRoot: workDir,
23845
+ allowNetwork: true,
23846
+ cwd: workDir
23847
+ };
23848
+ }
22640
23849
  function sanitizeEnvironment(env) {
22641
23850
  const sensitivePrefixes = [
22642
23851
  "EPOCH_API_KEY",
@@ -22665,6 +23874,15 @@ function runCommandHook(config, event, workDir) {
22665
23874
  const baseEnv = sanitizeEnvironment(process.env);
22666
23875
  const injectedEnv = injectEpochEnv(event, config.pluginRoot);
22667
23876
  const hookEnv = { ...baseEnv, ...injectedEnv, ...config.env };
23877
+ const policy = hookSandboxPolicy(workDir);
23878
+ const raw = shellSpawnArgs(config.command);
23879
+ const confinement = confine(raw.file, raw.args, policy);
23880
+ const sandbox = confinement.confined ? { confined: true, mode: confinement.mode, backend: confinement.backend } : {
23881
+ confined: false,
23882
+ mode: confinement.mode,
23883
+ backend: confinement.backend,
23884
+ skipped: hookSkipReason(confinement.reason)
23885
+ };
22668
23886
  return new Promise((resolve17) => {
22669
23887
  let stdout = "";
22670
23888
  let stderr = "";
@@ -22677,11 +23895,16 @@ function runCommandHook(config, event, workDir) {
22677
23895
  stdout,
22678
23896
  stderr,
22679
23897
  blocked: false,
22680
- rejectReason: t("hook_load.spawn_failed", { message: err.message })
23898
+ rejectReason: t("hook_load.spawn_failed", { message: err.message }),
23899
+ sandbox
22681
23900
  });
22682
23901
  };
22683
23902
  const child = guardSpawn(
22684
- () => spawn(config.command, {
23903
+ () => confinement.confined ? spawn(confinement.command, confinement.args, {
23904
+ cwd: workDir,
23905
+ env: hookEnv,
23906
+ stdio: ["pipe", "pipe", "pipe"]
23907
+ }) : spawn(config.command, {
22685
23908
  shell: true,
22686
23909
  cwd: workDir,
22687
23910
  env: hookEnv,
@@ -22699,7 +23922,8 @@ function runCommandHook(config, event, workDir) {
22699
23922
  stdout,
22700
23923
  stderr,
22701
23924
  blocked: false,
22702
- rejectReason: t("hook_load.timeout")
23925
+ rejectReason: t("hook_load.timeout"),
23926
+ sandbox
22703
23927
  });
22704
23928
  }
22705
23929
  }, timeout);
@@ -22730,13 +23954,21 @@ function runCommandHook(config, event, workDir) {
22730
23954
  }
22731
23955
  } catch {
22732
23956
  }
23957
+ const failure = confinement.confined ? classifyFailure({
23958
+ exitCode,
23959
+ stderr,
23960
+ denialSignatures: confinement.denialSignatures,
23961
+ runnerFailureRules: confinement.runnerFailureRules
23962
+ }) : void 0;
22733
23963
  resolve17({
22734
23964
  exitCode,
22735
23965
  stdout: stdout.trim(),
22736
23966
  stderr: stderr.trim(),
22737
23967
  blocked,
22738
23968
  rejectReason,
22739
- output
23969
+ output,
23970
+ sandbox,
23971
+ ...failure ? { failure } : {}
22740
23972
  });
22741
23973
  });
22742
23974
  child.on("error", (err) => {
@@ -22843,7 +24075,7 @@ var HookManager = class {
22843
24075
  for (const config of entry.configs) {
22844
24076
  try {
22845
24077
  const result = await runCommandHook(config, event, workDir);
22846
- reportIfNeverRan(this.log, type, config, result);
24078
+ reportIfNotRun(this.log, type, config, result);
22847
24079
  if (result.blocked) {
22848
24080
  return { blocked: true, reason: result.rejectReason || t("hook_load.rejected") };
22849
24081
  }
@@ -22877,7 +24109,7 @@ var HookManager = class {
22877
24109
  for (const entry of cmdEntries) {
22878
24110
  if (!matchesHook(entry.matcher, event)) continue;
22879
24111
  for (const config of entry.configs) {
22880
- runCommandHook(config, event, ctx.workDir).then((result) => reportIfNeverRan(this.log, type, config, result)).catch((err) => {
24112
+ runCommandHook(config, event, ctx.workDir).then((result) => reportIfNotRun(this.log, type, config, result)).catch((err) => {
22881
24113
  this.log("warn", `[HookManager] ${type} \u7684 command hook \u5F02\u6B65\u5F02\u5E38: ${describe12(err)}`, {
22882
24114
  hookType: type,
22883
24115
  command: config.command
@@ -22933,7 +24165,7 @@ var HookManager = class {
22933
24165
  for (const config of entry.configs) {
22934
24166
  try {
22935
24167
  const result = await runCommandHook(config, event, ctx.workDir);
22936
- reportIfNeverRan(this.log, type, config, result);
24168
+ reportIfNotRun(this.log, type, config, result);
22937
24169
  const verdict = verdictOf2(result);
22938
24170
  if (verdict) results.push(verdict);
22939
24171
  } catch (err) {
@@ -22967,12 +24199,25 @@ function verdictOf2(result) {
22967
24199
  }
22968
24200
  return void 0;
22969
24201
  }
22970
- function reportIfNeverRan(log5, type, config, result) {
22971
- if (result.exitCode !== -1) return;
24202
+ function reportIfNotRun(log5, type, config, result) {
24203
+ if (result.exitCode === -1) {
24204
+ log5(
24205
+ "warn",
24206
+ `[HookManager] ${type} \u7684 command hook \u6CA1\u80FD\u6267\u884C\uFF0C\u672C\u6B21\u6309\u653E\u884C\u5904\u7406: ${result.rejectReason ?? t("hook_load.unknown_reason")}`,
24207
+ { hookType: type, command: config.command }
24208
+ );
24209
+ return;
24210
+ }
24211
+ const kind = result.failure?.kind;
24212
+ if (kind !== "sandbox-denied" && kind !== "runner-failed") return;
22972
24213
  log5(
22973
24214
  "warn",
22974
- `[HookManager] ${type} \u7684 command hook \u6CA1\u80FD\u6267\u884C\uFF0C\u672C\u6B21\u6309\u653E\u884C\u5904\u7406: ${result.rejectReason ?? "\u672A\u77E5\u539F\u56E0"}`,
22975
- { hookType: type, command: config.command }
24215
+ t(kind === "sandbox-denied" ? "hook_load.sandbox_denied" : "hook_load.sandbox_runner_failed", {
24216
+ type,
24217
+ command: config.command,
24218
+ detail: result.failure?.evidence ?? result.stderr.split("\n")[0] ?? ""
24219
+ }),
24220
+ { hookType: type, command: config.command, failure: kind }
22976
24221
  );
22977
24222
  }
22978
24223
  var MAPPINGS = {
@@ -23139,6 +24384,8 @@ var EVENT_MAP = {
23139
24384
  SessionStart: "session:start",
23140
24385
  SessionEnd: "session:end",
23141
24386
  Stop: "stop",
24387
+ SubagentStart: "subagent:start",
24388
+ SubagentStop: "subagent:stop",
23142
24389
  PostCompact: "compress:after"
23143
24390
  };
23144
24391
  var UNRUNNABLE_EVENTS = {
@@ -23588,7 +24835,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23588
24835
  const dir = join(staging, category, name);
23589
24836
  mkdirSync(dir, { recursive: true });
23590
24837
  copyFileSync(join(source, SKILL_FILE), join(dir, SKILL_FILE));
23591
- let budget2 = MAX_FILES_PER_SKILL - 1;
24838
+ let budget = MAX_FILES_PER_SKILL - 1;
23592
24839
  for (const entry of readEntries2(source, label, issues, lang)) {
23593
24840
  if (entry.name === SKILL_FILE) continue;
23594
24841
  if (!entry.isDir || !SKILL_ASSET_DIRS.includes(entry.name)) {
@@ -23598,7 +24845,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23598
24845
  });
23599
24846
  continue;
23600
24847
  }
23601
- budget2 = copyTree(join(source, entry.name), join(dir, entry.name), label, budget2, issues, lang);
24848
+ budget = copyTree(join(source, entry.name), join(dir, entry.name), label, budget, issues, lang);
23602
24849
  }
23603
24850
  skills.push({ name, category, dir });
23604
24851
  }
@@ -23657,8 +24904,8 @@ function placeOf(root, parts, lang) {
23657
24904
  const category = parts.at(-2) ?? "general";
23658
24905
  return { name, category: skillSegmentIssue(category, lang) === void 0 ? category : "general" };
23659
24906
  }
23660
- function copyTree(from, to, label, budget2, issues, lang) {
23661
- let left = budget2;
24907
+ function copyTree(from, to, label, budget, issues, lang) {
24908
+ let left = budget;
23662
24909
  mkdirSync(to, { recursive: true });
23663
24910
  for (const entry of readEntries2(from, label, issues, lang)) {
23664
24911
  if (left <= 0) {
@@ -24204,9 +25451,9 @@ var SkillSystem = class {
24204
25451
  );
24205
25452
  }
24206
25453
  };
24207
- function sameDirs(cached, current) {
24208
- if (cached.length !== current.length) return false;
24209
- return cached.every((dir, i) => dir === current[i]?.dir);
25454
+ function sameDirs(cached2, current) {
25455
+ if (cached2.length !== current.length) return false;
25456
+ return cached2.every((dir, i) => dir === current[i]?.dir);
24210
25457
  }
24211
25458
  var MODULE6 = "Skill";
24212
25459
  function resolveSkillDirs(opts) {
@@ -24722,11 +25969,11 @@ function readOne(path, opts, usedBytes) {
24722
25969
  }
24723
25970
  const remaining = MAX_ATTACH_TOTAL_BYTES - usedBytes;
24724
25971
  if (remaining <= 0) return { part: { type: "file", path, omitted: "over-limit", bytes: size } };
24725
- const budget2 = Math.min(MAX_ATTACH_BYTES, remaining);
24726
- if (raw.length <= budget2) {
25972
+ const budget = Math.min(MAX_ATTACH_BYTES, remaining);
25973
+ if (raw.length <= budget) {
24727
25974
  return { part: { type: "file", path, text: raw.toString("utf8"), bytes: size } };
24728
25975
  }
24729
- const { text, truncatedTo } = truncateToBudget(raw, budget2);
25976
+ const { text, truncatedTo } = truncateToBudget(raw, budget);
24730
25977
  return {
24731
25978
  part: { type: "file", path, text, truncatedTo, bytes: size },
24732
25979
  notice: t("mentions.truncated", {
@@ -24940,6 +26187,9 @@ function goalSection(goal) {
24940
26187
  ].join("\n");
24941
26188
  }
24942
26189
  var DEFAULT_THRESHOLD = DEFAULT_COMPRESSION_THRESHOLD;
26190
+ function messageTokens(m) {
26191
+ return estimateTokens(m.content) + (m.reasoning ? estimateTokens(m.reasoning) : 0);
26192
+ }
24943
26193
  var COOLDOWN_TURNS = 5;
24944
26194
  var MAX_FAILURES = 3;
24945
26195
  var DEFAULT_CONTEXT_LENGTH3 = 128e3;
@@ -24970,11 +26220,11 @@ var ContextCompressor = class {
24970
26220
  enabled: opts.enabled !== false
24971
26221
  };
24972
26222
  }
24973
- shouldCompress(promptTokens2, contextLength) {
26223
+ shouldCompress(promptTokens3, contextLength) {
24974
26224
  if (!this.opts.enabled) return false;
24975
26225
  if (this.turnsSinceCompress < COOLDOWN_TURNS) return false;
24976
26226
  if (this.failureCount >= MAX_FAILURES) return false;
24977
- return isContextNearlyFull(promptTokens2, contextLength, this.opts.threshold);
26227
+ return isContextNearlyFull(promptTokens3, contextLength, this.opts.threshold);
24978
26228
  }
24979
26229
  async compress(messages, partial = {}) {
24980
26230
  const config = {
@@ -25016,7 +26266,11 @@ var ContextCompressor = class {
25016
26266
  };
25017
26267
  }
25018
26268
  const nl = "\n";
25019
- const middleText = middle.map((m) => `${m.role}: ${m.content}`).join(nl);
26269
+ const middleText = middle.map((m) => {
26270
+ const body2 = `${m.role}: ${m.content}`;
26271
+ return m.reasoning ? `${body2}
26272
+ [thinking] ${m.reasoning}` : body2;
26273
+ }).join(nl);
25020
26274
  const summarized = await this.generateSummary(
25021
26275
  middleText,
25022
26276
  config.instruction,
@@ -25073,13 +26327,13 @@ var ContextCompressor = class {
25073
26327
  determineBoundaries(messages, config) {
25074
26328
  const head = messages.slice(0, config.protectFirstN);
25075
26329
  const thresholdTokens = config.threshold * config.contextLength;
25076
- const tailBudget = thresholdTokens * config.targetRatio;
26330
+ const tailBudget = config.force ? 0 : thresholdTokens * config.targetRatio;
25077
26331
  let tailTokens = 0;
25078
26332
  let tailStart = messages.length;
25079
26333
  let userCount = 0;
25080
26334
  for (let i = messages.length - 1; i >= config.protectFirstN; i--) {
25081
26335
  const msg = messages[i];
25082
- tailTokens += estimateTokens(msg.content);
26336
+ tailTokens += messageTokens(msg);
25083
26337
  if (msg.role === "user") userCount++;
25084
26338
  tailStart = i;
25085
26339
  const enoughTokens = tailTokens >= tailBudget;
@@ -25179,7 +26433,7 @@ ${extra}` : withGoal;
25179
26433
  return result;
25180
26434
  }
25181
26435
  estimateTokens(messages) {
25182
- return messages.reduce((sum, m) => sum + estimateTokens(m.content), 0);
26436
+ return messages.reduce((sum, m) => sum + messageTokens(m), 0);
25183
26437
  }
25184
26438
  };
25185
26439
  var FILE_VERSION5 = 1;
@@ -25419,248 +26673,6 @@ function hasExecTierContent(inv) {
25419
26673
  function hasDedicatedExecContent(inv) {
25420
26674
  return inv.hookFiles.includes(EPOCH_HOOKS_RELPATH) || inv.policies > 0;
25421
26675
  }
25422
- function describeIsolation() {
25423
- const backend = detectBackend();
25424
- const os = platform();
25425
- if (backend === "seatbelt") {
25426
- return {
25427
- level: "os",
25428
- backend,
25429
- detail: t("sandbox_isolation.seatbelt"),
25430
- reason: "os-isolated",
25431
- platform: os
25432
- };
25433
- }
25434
- if (backend === "bubblewrap") {
25435
- return {
25436
- level: "os",
25437
- backend,
25438
- detail: t("sandbox_isolation.bubblewrap"),
25439
- reason: "os-isolated",
25440
- platform: os
25441
- };
25442
- }
25443
- const [why, reason9] = os === "darwin" ? [t("sandbox_isolation.no_seatbelt"), "backend-unavailable"] : os === "linux" ? [t("sandbox_isolation.no_bubblewrap"), "backend-missing"] : (
25444
- [t("sandbox_isolation.by_design", { platform: os }), "platform-unsupported"]
25445
- );
25446
- return {
25447
- level: "process",
25448
- backend,
25449
- detail: t("sandbox_isolation.process_only", { why }),
25450
- reason: reason9,
25451
- platform: os
25452
- };
25453
- }
25454
- var SANDBOX_COVERS = ["code_exec", "terminal"];
25455
- var SANDBOX_EXCLUDES = [];
25456
- // src/sandbox/sandbox.ts
25457
- var IS_WINDOWS = platform() === "win32";
25458
- var SANDBOX_ROOT = join(tmpdir(), "epoch-sandbox");
25459
- var DEFAULT_TIMEOUT = 3e4;
25460
- var MAX_OUTPUT = 1e5;
25461
- var MAX_MEMORY = 512;
25462
- var TRUNCATE_HEAD = 2e3;
25463
- var TRUNCATE_TAIL = 2e3;
25464
- var CodeSandbox = class {
25465
- sandboxDir;
25466
- readableDirs;
25467
- constructor(sessionId, workDir) {
25468
- const safeId = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
25469
- this.sandboxDir = join(SANDBOX_ROOT, safeId);
25470
- mkdirSync(this.sandboxDir, { recursive: true });
25471
- this.readableDirs = [];
25472
- if (workDir) {
25473
- const abs = resolve(workDir);
25474
- if (abs !== resolve(homedir()) && abs !== "/") this.readableDirs.push(abs);
25475
- }
25476
- }
25477
- getIsolation() {
25478
- return describeIsolation();
25479
- }
25480
- async execute(req) {
25481
- const start = Date.now();
25482
- const timeout = req.timeout ?? DEFAULT_TIMEOUT;
25483
- const isolation = describeIsolation().level;
25484
- try {
25485
- const spec = req.language === "javascript" ? this.jsSpec(req.code) : this.pySpec(req.code);
25486
- const result = await this.run(spec, {
25487
- timeout,
25488
- allowNetwork: req.allowNetwork === true,
25489
- ...req.signal ? { signal: req.signal } : {}
25490
- });
25491
- return { ...result, isolation };
25492
- } catch (err) {
25493
- const msg = err instanceof Error ? err.message : String(err);
25494
- if (msg.includes("timed out") || msg.includes("killed")) {
25495
- return {
25496
- success: false,
25497
- output: "",
25498
- error: `[\u8D85\u65F6] \u8D85\u8FC7 ${timeout / 1e3} \u79D2`,
25499
- errorType: "timeout",
25500
- duration: Date.now() - start,
25501
- isolation
25502
- };
25503
- }
25504
- return {
25505
- success: false,
25506
- output: "",
25507
- error: `[\u9519\u8BEF] ${msg}`,
25508
- errorType: "runtime",
25509
- duration: Date.now() - start,
25510
- isolation
25511
- };
25512
- }
25513
- }
25514
- destroy() {
25515
- try {
25516
- rmSync(this.sandboxDir, { recursive: true });
25517
- } catch {
25518
- }
25519
- }
25520
- getDir() {
25521
- return this.sandboxDir;
25522
- }
25523
- jsSpec(code) {
25524
- const tmpFile = join(this.sandboxDir, "code.js");
25525
- writeFileSync(tmpFile, code, "utf-8");
25526
- return {
25527
- command: process.execPath,
25528
- args: [`--max-old-space-size=${MAX_MEMORY}`, tmpFile],
25529
- env: EXEC_PATH_AS_NODE_ENV
25530
- };
25531
- }
25532
- pySpec(code) {
25533
- const tmpFile = join(this.sandboxDir, "code.py");
25534
- writeFileSync(tmpFile, code, "utf-8");
25535
- return {
25536
- command: IS_WINDOWS ? "python" : "python3",
25537
- args: ["-I", tmpFile]
25538
- };
25539
- }
25540
- run(spec, opts) {
25541
- const start = Date.now();
25542
- const wrapped = isolate(spec.command, spec.args, {
25543
- writableDirs: [this.sandboxDir, tmpdir()],
25544
- readableDirs: this.readableDirs,
25545
- allowNetwork: opts.allowNetwork,
25546
- cwd: this.sandboxDir
25547
- });
25548
- const { command, args } = wrapped ?? spec;
25549
- const spawnOpts = {
25550
- cwd: this.sandboxDir,
25551
- timeout: opts.timeout,
25552
- env: {
25553
- PATH: process.env["PATH"] ?? "",
25554
- HOME: process.env["HOME"] ?? "",
25555
- TMPDIR: this.sandboxDir,
25556
- LANG: process.env["LANG"] ?? "en_US.UTF-8",
25557
- ...spec.env
25558
- },
25559
- stdio: ["ignore", "pipe", "pipe"],
25560
- ...IS_WINDOWS ? { windowsHide: true } : {}
25561
- };
25562
- return new Promise((resolve17) => {
25563
- let stdout = "";
25564
- let stderr = "";
25565
- let killedForOutput = false;
25566
- let child = null;
25567
- const onAbort = () => {
25568
- child?.kill("SIGKILL");
25569
- };
25570
- const failToStart = (err) => {
25571
- opts.signal?.removeEventListener("abort", onAbort);
25572
- if (err.code === "ENOENT") {
25573
- resolve17({
25574
- success: false,
25575
- output: "",
25576
- error: `[\u9519\u8BEF] \u627E\u4E0D\u5230\u89E3\u91CA\u5668: ${command}`,
25577
- errorType: "compile",
25578
- duration: Date.now() - start
25579
- });
25580
- return;
25581
- }
25582
- resolve17({
25583
- success: false,
25584
- output: "",
25585
- error: `[\u9519\u8BEF] ${err.message}\uFF08command=${command}\uFF0Ccwd=${spawnOpts.cwd ?? ""}\uFF09`,
25586
- errorType: "runtime",
25587
- duration: Date.now() - start
25588
- });
25589
- };
25590
- child = guardSpawn(() => spawn(command, args, spawnOpts), failToStart);
25591
- if (!child) return;
25592
- const proc = child;
25593
- let outDecoder = null;
25594
- let errDecoder = null;
25595
- proc.stdout?.on("data", (c) => {
25596
- if (killedForOutput) return;
25597
- outDecoder ??= createStreamDecoder();
25598
- stdout += outDecoder.write(c);
25599
- if (stdout.length >= MAX_OUTPUT) {
25600
- killedForOutput = true;
25601
- stdout = stdout.slice(0, MAX_OUTPUT);
25602
- proc.kill("SIGKILL");
25603
- }
25604
- });
25605
- proc.stderr?.on("data", (c) => {
25606
- if (stderr.length >= MAX_OUTPUT) return;
25607
- errDecoder ??= createStreamDecoder();
25608
- stderr += errDecoder.write(c);
25609
- });
25610
- opts.signal?.addEventListener("abort", onAbort, { once: true });
25611
- proc.on("error", failToStart);
25612
- proc.on("close", (code, sig) => {
25613
- opts.signal?.removeEventListener("abort", onAbort);
25614
- if (!killedForOutput) stdout += outDecoder?.end() ?? "";
25615
- if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
25616
- const duration = Date.now() - start;
25617
- if (killedForOutput) {
25618
- resolve17({
25619
- success: false,
25620
- output: trunc(stdout, stderr),
25621
- error: `[\u8F93\u51FA\u8D85\u9650] \u8D85\u8FC7 ${MAX_OUTPUT} \u5B57\u8282\uFF0C\u5DF2\u7EC8\u6B62`,
25622
- errorType: "runtime",
25623
- duration
25624
- });
25625
- return;
25626
- }
25627
- if (sig) {
25628
- resolve17({
25629
- success: false,
25630
- output: trunc(stdout, stderr),
25631
- error: opts.signal?.aborted ? "[\u5DF2\u53D6\u6D88]" : "[\u8D85\u65F6] \u6267\u884C\u8D85\u65F6",
25632
- errorType: "timeout",
25633
- duration
25634
- });
25635
- return;
25636
- }
25637
- const output = trunc(stdout, stderr);
25638
- if (code !== 0 && code !== null) {
25639
- const oom = /heap out of memory|allocation failed/i.test(stderr);
25640
- resolve17({
25641
- success: false,
25642
- output,
25643
- error: oom ? `[\u5185\u5B58\u8D85\u9650] \u8D85\u8FC7 ${MAX_MEMORY}MB` : `[\u8FD0\u884C\u65F6\u9519\u8BEF] exit ${code}: ${stderr.slice(0, 200)}`,
25644
- errorType: oom ? "oom" : "runtime",
25645
- duration
25646
- });
25647
- return;
25648
- }
25649
- resolve17({ success: true, output, duration });
25650
- });
25651
- });
25652
- }
25653
- };
25654
- function trunc(stdout, stderr) {
25655
- const nl = "\n";
25656
- let out = stdout;
25657
- if (stderr) out += (out ? nl : "") + stderr;
25658
- if (out.length <= MAX_OUTPUT) return out || "(\u65E0\u8F93\u51FA)";
25659
- const head = out.slice(0, TRUNCATE_HEAD);
25660
- const tail = out.slice(-TRUNCATE_TAIL);
25661
- const skipped = out.length - TRUNCATE_HEAD - TRUNCATE_TAIL;
25662
- return `${head}${nl}...[\u5DF2\u622A\u65AD ${(skipped / 1024).toFixed(1)} KB]...${nl}${tail}`;
25663
- }
25664
26676
  /**
25665
26677
  * @license
25666
26678
  * Copyright 2025 Google LLC
@@ -25706,4 +26718,4 @@ function trunc(stdout, stderr) {
25706
26718
  * `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
25707
26719
  */
25708
26720
 
25709
- export { ABOUT_DESC_MAX_CHARS, ABOUT_README_MAX_CHARS, AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_ENTRIES_FILE, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PERMISSION_AUDIT_CODES, PLUGIN_ICON_MAX_BYTES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_ASSET_DIRS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_FILE, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, UNSETTABLE_USER_KEYS, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, approvalEntryFor, approvalRootFor, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, capabilityOfRef, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createPluginScope, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, discoverySpec, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, grantedPathsFor, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, hasUsableModel, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidInterval, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listEntryModels, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadModelEntries, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelEntriesPath, modelListCandidates, needsAllowlist, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, normalizeModelEntry, normalizeTrigger, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelEntry, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readEntryKey, readImageSize, readMarketplaces, readPluginAbout, readPluginIcon, readPluginManifest, readPluginRecords, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, saveModelEntries, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, testModelEntry, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, unsetUserSetting, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
26721
+ export { ABOUT_DESC_MAX_CHARS, ABOUT_README_MAX_CHARS, AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DELEGATE_SESSION_SOURCE, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_ENTRIES_FILE, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PERMISSION_AUDIT_CODES, PLUGIN_ICON_MAX_BYTES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_ASSET_DIRS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_FILE, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, UNSETTABLE_USER_KEYS, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, approvalEntryFor, approvalRootFor, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, capabilityOfRef, checkConfigValue, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, configKeyByPath, configKeyTable, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createPluginScope, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, discoverySpec, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, grantedPathsFor, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, hasUsableModel, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidInterval, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listEntryModels, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadModelEntries, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelEntriesPath, modelListCandidates, needsAllowlist, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, normalizeModelEntry, normalizeTrigger, offloadOldestImages, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelEntry, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readEntryKey, readImageSize, readMarketplaces, readPluginAbout, readPluginIcon, readPluginManifest, readPluginRecords, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, requestImageBytes, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, saveModelEntries, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, testModelEntry, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, unsetUserSetting, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writableConfigKeys, writeSettingValue };