@epoch-agent/core 2.8.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 +108 -5
  2. package/dist/index.d.ts +909 -163
  3. package/dist/index.js +1630 -625
  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, delimiter } 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"
@@ -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",
@@ -4010,12 +4426,15 @@ function traceSession(db, input) {
4010
4426
  truncated
4011
4427
  };
4012
4428
  }
4429
+ // src/session/types.ts
4430
+ var DELEGATE_SESSION_SOURCE = "delegate";
4013
4431
  // src/session/query.ts
4014
4432
  var SESSION_SEARCH_TOOL = "session_search";
4015
4433
  var SESSION_MESSAGES_SEARCH_TOOL = "session_messages_search";
4016
4434
  var SESSION_MESSAGES_READ_TOOL = "session_messages_read";
4017
4435
  var SESSION_TRACE_TOOL = "session_trace";
4018
4436
  var SESSION_LIST_TOOL = "session_list";
4437
+ var DELEGATE_CLAUSE = "AND COALESCE(s.source, '') != @delegateSource";
4019
4438
  var SESSION_QUERY_TOOLS = Object.freeze([
4020
4439
  SESSION_SEARCH_TOOL,
4021
4440
  SESSION_MESSAGES_SEARCH_TOOL,
@@ -4118,8 +4537,9 @@ var SessionQueries = class {
4118
4537
  listSessions(input) {
4119
4538
  const vis = resolveSessionVisibility({ ...input, db: this.db });
4120
4539
  const archivedClause = input.archived === "all" ? "" : input.archived === "only" ? "AND s.archived = 1" : "AND s.archived = 0";
4121
- const where = `${vis.clause} ${archivedClause}`;
4122
- 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;
4123
4543
  const rows = this.db.prepare(
4124
4544
  `SELECT s.id,
4125
4545
  COALESCE(NULLIF(s.display_name, ''), s.title) AS title,
@@ -4131,7 +4551,7 @@ var SessionQueries = class {
4131
4551
  WHERE ${where}
4132
4552
  ORDER BY COALESCE(s.updated_at, s.started_at) DESC
4133
4553
  LIMIT @limit`
4134
- ).all({ ...vis.params, limit: input.limit });
4554
+ ).all({ ...params, limit: input.limit });
4135
4555
  return {
4136
4556
  total,
4137
4557
  sessions: rows.map((row) => ({
@@ -4359,41 +4779,41 @@ function clampWindow(raw, fallback2, max) {
4359
4779
  function stamp2(ms) {
4360
4780
  return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
4361
4781
  }
4362
- function budgetOf2(total) {
4782
+ function budgetOf(total) {
4363
4783
  const shares = ANCHOR_SHARE + Math.max(0, total - 1);
4364
4784
  const unit = Math.max(MIN_BODY, Math.floor(BODY_BUDGET / shares));
4365
4785
  return { anchor: unit * ANCHOR_SHARE, other: unit };
4366
4786
  }
4367
- function body(text, budget2) {
4787
+ function body(text, budget) {
4368
4788
  const flat = text.replace(/\r\n/g, "\n").trimEnd();
4369
- if (flat.length <= budget2) return flat;
4370
- const rest = flat.length - budget2;
4371
- return `${flat.slice(0, budget2)}
4789
+ if (flat.length <= budget) return flat;
4790
+ const rest = flat.length - budget;
4791
+ return `${flat.slice(0, budget)}
4372
4792
  \u2026[cut ${rest} more chars \u2014 lower before/after to spend the budget on fewer messages]`;
4373
4793
  }
4374
4794
  function surfaceOf(record, isCurrent) {
4375
4795
  if (!isCurrent) return record.surface === "active" ? "active there" : "compacted there";
4376
4796
  return record.surface === "active" ? "still in your context" : "compacted out of your context";
4377
4797
  }
4378
- function renderRecord(record, mark, budget2, isCurrent) {
4798
+ function renderRecord(record, mark, budget, isCurrent) {
4379
4799
  const tool = record.toolName ? ` \xB7 ${record.toolName}` : "";
4380
4800
  return [
4381
4801
  `${mark} #${record.messageId} [${record.role}${tool} \xB7 ${stamp2(record.timestamp)} \xB7 ${surfaceOf(record, isCurrent)}]`,
4382
- ` ${body(record.content, budget2).split("\n").join("\n ")}`
4802
+ ` ${body(record.content, budget).split("\n").join("\n ")}`
4383
4803
  ].join("\n");
4384
4804
  }
4385
4805
  function renderWindow(out, sessionId, isCurrent) {
4386
4806
  const anchor = out.anchor;
4387
4807
  if (!anchor) return "";
4388
4808
  const count = out.before.length + out.after.length + 1;
4389
- const budget2 = budgetOf2(count);
4809
+ const budget = budgetOf(count);
4390
4810
  return [
4391
4811
  `Session ${sessionId}${isCurrent ? " (this session)" : ""} \u2014 ${out.title || "(untitled)"}`,
4392
4812
  `${count} of ${out.total} message(s), around #${anchor.messageId}:`,
4393
4813
  "",
4394
- ...out.before.map((r) => renderRecord(r, " ", budget2.other, isCurrent)),
4395
- renderRecord(anchor, "\xBB", budget2.anchor, isCurrent),
4396
- ...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))
4397
4817
  ].join("\n");
4398
4818
  }
4399
4819
  function createSessionMessagesReadTool(deps) {
@@ -5248,7 +5668,7 @@ function available(tools) {
5248
5668
  if (tools.length === 0) {
5249
5669
  return "No deferred tools in this session \u2014 every tool is already in your tool list.";
5250
5670
  }
5251
- const names = tools.map((t81) => t81.name).sort();
5671
+ const names = tools.map((t83) => t83.name).sort();
5252
5672
  const shown = names.slice(0, MAX_LISTED);
5253
5673
  const rest = names.length - shown.length;
5254
5674
  return `Searchable tools: ${shown.join(", ")}${rest > 0 ? ` (\u2026and ${rest} more)` : ""}.`;
@@ -5368,19 +5788,22 @@ var TrackerDB = class {
5368
5788
  );
5369
5789
  this.db.transaction(() => {
5370
5790
  this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
5371
- for (const t81 of todos) {
5791
+ for (const t83 of todos) {
5372
5792
  insertStmt.run({
5373
- id: t81.id,
5793
+ id: t83.id,
5374
5794
  session_id: sessionId,
5375
- content: t81.content,
5376
- status: t81.status,
5377
- reason: t81.reason || null,
5378
- created_at: t81.created_at,
5379
- 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
5380
5800
  });
5381
5801
  }
5382
5802
  })();
5383
5803
  }
5804
+ deleteBySession(sessionId) {
5805
+ return this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId).changes;
5806
+ }
5384
5807
  close() {
5385
5808
  releaseDatabase(this.path);
5386
5809
  }
@@ -5398,9 +5821,9 @@ function formatTodoList(todos, pendingCount) {
5398
5821
  cancelled: "\u5DF2\u53D6\u6D88"
5399
5822
  };
5400
5823
  lines.push(`[todo] \u72B6\u6001\u6C47\u603B: ${pendingCount} \u4E2A\u5F85\u5B8C\u6210`);
5401
- for (const t81 of todos) {
5402
- const label = statusLabel[t81.status] || t81.status;
5403
- 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}` : ""}`);
5404
5827
  }
5405
5828
  return lines.join("\n");
5406
5829
  }
@@ -5474,14 +5897,14 @@ function createTodoTool(db) {
5474
5897
  };
5475
5898
  }
5476
5899
  const now = (/* @__PURE__ */ new Date()).toISOString();
5477
- const existing = new Map(db.listBySession(ctx.sessionId).map((t81) => [t81.id, t81]));
5478
- const todos = rawTodos.map((t81) => {
5479
- const prev = t81.id ? existing.get(t81.id) : void 0;
5480
- const status = isValidStatus(t81.status) ? t81.status : "pending";
5481
- 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);
5482
5905
  return {
5483
- id: t81.id || generateShortId(),
5484
- content: String(t81.content || "").slice(0, 500),
5906
+ id: t83.id || generateShortId(),
5907
+ content: String(t83.content || "").slice(0, 500),
5485
5908
  status,
5486
5909
  ...reason9 ? { reason: reason9 } : {},
5487
5910
  created_at: prev?.created_at || now,
@@ -5489,7 +5912,7 @@ function createTodoTool(db) {
5489
5912
  };
5490
5913
  });
5491
5914
  db.replaceAll(ctx.sessionId, todos);
5492
- const pendingCount = todos.filter((t81) => isOpenTodo(t81.status)).length;
5915
+ const pendingCount = todos.filter((t83) => isOpenTodo(t83.status)).length;
5493
5916
  return {
5494
5917
  success: true,
5495
5918
  output: formatTodoList(todos, pendingCount),
@@ -5836,7 +6259,7 @@ function intersectNames(base2, incoming) {
5836
6259
  if (!base2) return incoming ? [...incoming] : void 0;
5837
6260
  if (!incoming) return [...base2];
5838
6261
  const baseSet = new Set(base2);
5839
- return incoming.filter((t81) => baseSet.has(t81));
6262
+ return incoming.filter((t83) => baseSet.has(t83));
5840
6263
  }
5841
6264
  function findRole(roles, name) {
5842
6265
  return roles.find((r) => r.name === name);
@@ -5988,7 +6411,7 @@ function roleScopedProvider(base2, role, blocked) {
5988
6411
  return allow ? allow.has(name) : true;
5989
6412
  };
5990
6413
  return {
5991
- list: () => base2.list().filter((t81) => permitted(t81.name)),
6414
+ list: () => base2.list().filter((t83) => permitted(t83.name)),
5992
6415
  get: (name) => permitted(name) ? base2.get(name) : void 0
5993
6416
  };
5994
6417
  }
@@ -6015,7 +6438,7 @@ function createRoleScope(base2, sessionRole, blocked) {
6015
6438
  }
6016
6439
  function unknownRoleTools(base2, role, blocked) {
6017
6440
  if (!role?.tools) return [];
6018
- const available2 = new Set(base2.list().map((t81) => t81.name));
6441
+ const available2 = new Set(base2.list().map((t83) => t83.name));
6019
6442
  const blockedSet = new Set(blocked);
6020
6443
  return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
6021
6444
  }
@@ -6378,7 +6801,7 @@ function createToolScope(base2) {
6378
6801
  const permitted = (name) => allow ? allow.has(name) : true;
6379
6802
  return {
6380
6803
  provider: {
6381
- list: () => base2.list().filter((t81) => permitted(t81.name)),
6804
+ list: () => base2.list().filter((t83) => permitted(t83.name)),
6382
6805
  get: (name) => permitted(name) ? base2.get(name) : void 0
6383
6806
  },
6384
6807
  enter: (next) => {
@@ -6399,6 +6822,8 @@ function createToolScope(base2) {
6399
6822
  var HOOK_TYPE_FLAGS = {
6400
6823
  "session:start": true,
6401
6824
  "session:end": true,
6825
+ "subagent:start": true,
6826
+ "subagent:stop": true,
6402
6827
  "turn:before": true,
6403
6828
  "turn:after": true,
6404
6829
  "tool:before": true,
@@ -6548,7 +6973,7 @@ function scanHooks(dir, issues) {
6548
6973
  return [...parsed.entries].map(([type, list]) => ({
6549
6974
  type,
6550
6975
  count: list.reduce((sum, entry) => sum + entry.hooks.length, 0)
6551
- })).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));
6552
6977
  }
6553
6978
  function scanMcp(dir, plugin, issues) {
6554
6979
  const path = join(dir, PLUGIN_LAYOUT.mcp);
@@ -8737,14 +9162,14 @@ var CheckpointStore = class {
8737
9162
  }
8738
9163
  async enforceTotalSize(protectSession, protectTurnIndex) {
8739
9164
  const turns = await this.collectTurns();
8740
- let total = turns.reduce((sum, t81) => sum + t81.bytes, 0);
9165
+ let total = turns.reduce((sum, t83) => sum + t83.bytes, 0);
8741
9166
  if (total <= this.maxTotalBytes) return;
8742
9167
  turns.sort((a, b) => a.createdAt - b.createdAt);
8743
- for (const t81 of turns) {
9168
+ for (const t83 of turns) {
8744
9169
  if (total <= this.maxTotalBytes) return;
8745
- if (t81.sessionId === protectSession && t81.turnIndex === protectTurnIndex) continue;
8746
- await this.removeTurn(t81.sessionId, t81.turnIndex);
8747
- total -= t81.bytes;
9170
+ if (t83.sessionId === protectSession && t83.turnIndex === protectTurnIndex) continue;
9171
+ await this.removeTurn(t83.sessionId, t83.turnIndex);
9172
+ total -= t83.bytes;
8748
9173
  }
8749
9174
  }
8750
9175
  async collectTurns() {
@@ -9064,7 +9489,7 @@ async function snapshotFiles(paths) {
9064
9489
  }
9065
9490
  async function collectDiffs(before, workDir) {
9066
9491
  const out = [];
9067
- let budget2 = MAX_CALL_BYTES;
9492
+ let budget = MAX_CALL_BYTES;
9068
9493
  for (const [path, old] of before) {
9069
9494
  if (old.kind === "opaque") continue;
9070
9495
  const now = await readSnapshot(path);
@@ -9072,8 +9497,8 @@ async function collectDiffs(before, workDir) {
9072
9497
  if (old.kind === "absent" && now.kind === "absent") continue;
9073
9498
  if (old.kind === "text" && now.kind === "text" && old.text === now.text) continue;
9074
9499
  const bytes = (old.kind === "text" ? old.bytes : 0) + (now.kind === "text" ? now.bytes : 0);
9075
- if (bytes > budget2) continue;
9076
- budget2 -= bytes;
9500
+ if (bytes > budget) continue;
9501
+ budget -= bytes;
9077
9502
  out.push({
9078
9503
  filePath: workspaceDisplayPath(path, workDir),
9079
9504
  ...old.kind === "text" ? { oldContent: old.text } : {},
@@ -9118,10 +9543,11 @@ var TaskNoticeTracker = class {
9118
9543
  }
9119
9544
  const lines = [];
9120
9545
  for (const task of tasks) {
9121
- if (task.kind !== "command") continue;
9546
+ if (task.kind === "shell") continue;
9122
9547
  const before = this.seen.get(task.id);
9123
9548
  if (before === task.status) continue;
9124
9549
  this.seen.set(task.id, task.status);
9550
+ if (task.kind === "agent" && task.status === "running") continue;
9125
9551
  lines.push(describeBackgroundTask(task));
9126
9552
  }
9127
9553
  if (lines.length === 0) return null;
@@ -9131,15 +9557,16 @@ var TaskNoticeTracker = class {
9131
9557
  function describeBackgroundTask(task) {
9132
9558
  const text = task.label;
9133
9559
  const cmd = text.length > 60 ? `${text.slice(0, 60)}\u2026` : text;
9560
+ const agent = task.kind === "agent";
9134
9561
  switch (task.status) {
9135
9562
  case "running":
9136
9563
  return `- ${task.id} \u8FD0\u884C\u4E2D\uFF1A${cmd}`;
9137
9564
  case "exited":
9138
- 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}`;
9139
9566
  case "killed":
9140
9567
  return `- ${task.id} \u5DF2\u88AB\u505C\u6B62\uFF1A${cmd}`;
9141
9568
  case "failed":
9142
- 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}`;
9143
9570
  case "closed":
9144
9571
  return `- ${task.id} \u5DF2\u5173\u95ED\uFF1A${cmd}`;
9145
9572
  case "reaped":
@@ -9232,6 +9659,19 @@ function skillNotes(input, skillTokens) {
9232
9659
  }
9233
9660
  return notes;
9234
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
+ }
9235
9675
  function buildContextBreakdown(input) {
9236
9676
  const toolTokens = input.tools.reduce((sum, { tool }) => sum + toolDefinitionTokens(tool), 0);
9237
9677
  const { detail, counts, notes } = toolDetail(input, toolTokens);
@@ -9253,7 +9693,16 @@ ${input.system.rules}`),
9253
9693
  notes.push({ code: "near_limit", params: { percent: Math.round(used / input.limit * 100) } });
9254
9694
  }
9255
9695
  const hints = notes.map((note) => t(NOTE_KEY[note.code], note.params));
9256
- 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
+ };
9257
9706
  }
9258
9707
  // src/cost/display.ts
9259
9708
  var currency;
@@ -9581,6 +10030,7 @@ function systemPromptSegments(params) {
9581
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",
9582
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",
9583
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",
9584
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",
9585
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",
9586
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",
@@ -9661,10 +10111,10 @@ function renderSettings(s) {
9661
10111
  bits.push(`\u4E0A\u4E0B\u6587\u7A97\u53E3 ${s.contextLength}`);
9662
10112
  bits.push(`\u81EA\u52A8\u538B\u7F29 ${s.compression.enabled ? `\u5F00\uFF08${s.compression.threshold}\uFF09` : "\u5173"}`);
9663
10113
  bits.push(`\u7EC8\u7AEF\u6C99\u7BB1 ${s.sandboxTerminal ? "\u5F00" : "\u5173"}`);
9664
- const budget2 = [];
9665
- if (s.budget?.maxCostUsd !== void 0) budget2.push(`$${s.budget.maxCostUsd}`);
9666
- if (s.budget?.maxTokens !== void 0) budget2.push(`${s.budget.maxTokens} tokens`);
9667
- 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"}`);
9668
10118
  return [
9669
10119
  "<settings>",
9670
10120
  bits.join(" \xB7 "),
@@ -10562,6 +11012,111 @@ function getGitStatus(rootDir) {
10562
11012
  function findProjectInstructions(rootDir, workDir = rootDir) {
10563
11013
  return findInstructionFiles(rootDir, workDir)[0];
10564
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
+ }
10565
11120
  function persistedUsage(entry) {
10566
11121
  return {
10567
11122
  ...entry.usage,
@@ -10881,6 +11436,50 @@ ${this.describeWarning(status)}
10881
11436
  });
10882
11437
  }
10883
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
+ }
10884
11483
  var ProviderError = class extends Error {
10885
11484
  constructor(message, code, recoverable, recovery = "abort") {
10886
11485
  super(message);
@@ -10901,6 +11500,10 @@ function isContextOverflow(err) {
10901
11500
  if (err instanceof ProviderError) return err.code === PROVIDER_CONTEXT_OVERFLOW;
10902
11501
  return classifyProviderError(err).code === PROVIDER_CONTEXT_OVERFLOW;
10903
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
+ }
10904
11507
  function isAbortError(err) {
10905
11508
  for (let cur = err, depth = 0; cur && depth < 5; depth++) {
10906
11509
  if (typeof cur !== "object") return false;
@@ -11000,16 +11603,17 @@ function classifyProviderError(err) {
11000
11603
  }
11001
11604
  return { code: 9999, recovery: "retry" };
11002
11605
  }
11606
+ var EMPTY_REASONING = { providerOptions: { openaiCompatible: { reasoning_content: "" } } };
11003
11607
  function toModelMessages(messages, opts = {}) {
11004
- if (opts.toolMediaInline !== false) return messages.map(toModelMessage);
11005
- return hoistToolMedia(messages);
11608
+ if (opts.toolMediaInline === false) return hoistToolMedia(messages, opts);
11609
+ return messages.map((m) => toModelMessage(m, opts));
11006
11610
  }
11007
- function hoistToolMedia(messages) {
11611
+ function hoistToolMedia(messages, opts = {}) {
11008
11612
  const out = [];
11009
11613
  let pending = [];
11010
11614
  for (const [i, m] of messages.entries()) {
11011
11615
  if (m.role !== "tool") {
11012
- out.push(toModelMessage(m));
11616
+ out.push(toModelMessage(m, opts));
11013
11617
  continue;
11014
11618
  }
11015
11619
  const results = (m.toolResults ?? []).map((r) => {
@@ -11026,7 +11630,7 @@ ${note}` : note,
11026
11630
  artifacts: (r.artifacts ?? []).map(({ data: _data, ...rest }) => rest)
11027
11631
  };
11028
11632
  });
11029
- out.push(toModelMessage({ ...m, toolResults: results }));
11633
+ out.push(toModelMessage({ ...m, toolResults: results }, opts));
11030
11634
  if (pending.length > 0 && messages[i + 1]?.role !== "tool") {
11031
11635
  out.push({
11032
11636
  role: "user",
@@ -11109,7 +11713,7 @@ function toSdkPart(p) {
11109
11713
  }
11110
11714
  return assertNeverPart(p);
11111
11715
  }
11112
- function toModelMessage(m) {
11716
+ function toModelMessage(m, opts = {}) {
11113
11717
  if (m.role === "tool") {
11114
11718
  return {
11115
11719
  role: "tool",
@@ -11124,10 +11728,13 @@ function toModelMessage(m) {
11124
11728
  if (m.role === "user" && m.parts && m.parts.length > 0) {
11125
11729
  return { role: "user", content: m.parts.map(toSdkPart) };
11126
11730
  }
11731
+ const reasoningPart = opts.reasoning && m.reasoning ? [{ type: "reasoning", text: m.reasoning }] : [];
11732
+ const emptyReasoning = opts.reasoning && !m.reasoning ? EMPTY_REASONING : {};
11127
11733
  if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
11128
11734
  return {
11129
11735
  role: "assistant",
11130
11736
  content: [
11737
+ ...reasoningPart,
11131
11738
  ...m.content ? [{ type: "text", text: m.content }] : [],
11132
11739
  ...m.toolCalls.map((tc) => ({
11133
11740
  type: "tool-call",
@@ -11135,19 +11742,35 @@ function toModelMessage(m) {
11135
11742
  toolName: tc.toolName,
11136
11743
  input: tc.input
11137
11744
  }))
11138
- ]
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
11139
11757
  };
11140
11758
  }
11141
11759
  return { role: m.role, content: m.content };
11142
11760
  }
11143
- function toSdkTools(tools) {
11761
+ var ANTHROPIC_CACHE_BREAKPOINT = {
11762
+ anthropic: { cacheControl: { type: "ephemeral" } }
11763
+ };
11764
+ function toSdkTools(tools, opts = {}) {
11144
11765
  const result = {};
11145
- for (const et of tools) {
11766
+ const breakpointAt = opts.cacheBreakpoint === true ? tools.length - 1 : -1;
11767
+ tools.forEach((et, i) => {
11146
11768
  result[et.name] = tool({
11147
11769
  description: et.description,
11148
- inputSchema: jsonSchema(et.parameters)
11770
+ inputSchema: jsonSchema(et.parameters),
11771
+ ...i === breakpointAt ? { providerOptions: ANTHROPIC_CACHE_BREAKPOINT } : {}
11149
11772
  });
11150
- }
11773
+ });
11151
11774
  return result;
11152
11775
  }
11153
11776
  function toOutputSpec(rf) {
@@ -11347,12 +11970,17 @@ function flattenMessage(m) {
11347
11970
  return {
11348
11971
  role: m.role,
11349
11972
  content: m.content,
11350
- ...toolCallIds.length > 0 ? { toolCallIds } : {}
11973
+ ...toolCallIds.length > 0 ? { toolCallIds } : {},
11974
+ ...m.reasoning ? { reasoning: m.reasoning } : {}
11351
11975
  };
11352
11976
  }
11353
11977
  function unflattenMessage(m) {
11354
11978
  if (m.role === "assistant") {
11355
- 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
+ };
11356
11984
  }
11357
11985
  if (m.role === "system") {
11358
11986
  return { role: "system", content: m.content };
@@ -11624,11 +12252,11 @@ var PromptBuilder = class {
11624
12252
  memoryInitialized = false;
11625
12253
  projectBlock(tools) {
11626
12254
  const permLevel = this.deps.permissionManager?.getLevel() || "default";
11627
- const hasPlanTool = tools.some((t81) => t81.name === "enter_plan_mode");
11628
- const hasLspTool = tools.some((t81) => t81.name === "lsp_diagnostics");
11629
- const hasAskTool = tools.some((t81) => t81.name === "ask_user_question");
11630
- const hasTodoTool = tools.some((t81) => t81.name === "todo");
11631
- 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");
11632
12260
  const needsCommandApproval = hasTerminal && permLevel === "default";
11633
12261
  const plan = this.deps.approvedPlan?.()?.trim();
11634
12262
  const codeSdk = renderSdkDeclaration(this.deps.codeModeTools?.() ?? []);
@@ -11911,9 +12539,50 @@ function toAbsolute2(path, workDir) {
11911
12539
  const expanded = expandPath(path);
11912
12540
  return isAbsolute(expanded) ? resolve(expanded) : resolve(workDir, expanded);
11913
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
+ }
11914
12583
  var MAX_TOOL_OUTPUT = 3e4;
11915
12584
  var MAX_SUB_CALL_OUTPUT = CODE_MODE_LIMITS.maxValueChars;
11916
- function budgetOf3(tool, depth, path = "success") {
12585
+ function budgetOf2(tool, depth, path = "success") {
11917
12586
  if (tool.governsOwnOutput && path === "success") return Number.POSITIVE_INFINITY;
11918
12587
  return depth > 0 ? MAX_SUB_CALL_OUTPUT : MAX_TOOL_OUTPUT;
11919
12588
  }
@@ -12149,22 +12818,28 @@ var ToolExecutor = class {
12149
12818
  if (!gate.allowed) {
12150
12819
  return finish({ ...base2, success: false, output: gate.output });
12151
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);
12152
12826
  const targets = this.writeTargets(et, tc.input);
12153
12827
  const snapshotted = await this.snapshot(sessionId, targets);
12154
12828
  const before = targets.length > 0 ? await snapshotFiles(targets) : void 0;
12155
12829
  const extraRoots = this.deps.permissionManager?.getExtraRoots?.();
12156
12830
  const homeDir = (this.deps.multimodal?.() ?? resolveMultimodalPolicy()).homeDir;
12157
- const r = await et.execute(tc.input, {
12831
+ const r = await et.execute(toolArgs, {
12158
12832
  sessionId,
12159
12833
  workDir: this.deps.workDir(),
12160
12834
  ...extraRoots?.length ? { extraRoots } : {},
12161
12835
  ...gate.grantedPaths?.length ? { grantedPaths: gate.grantedPaths } : {},
12162
12836
  ...homeDir ? { homeDir } : {},
12163
12837
  ...this.deps.sandboxTerminal !== void 0 ? { sandboxTerminal: this.deps.sandboxTerminal } : {},
12838
+ ...escalation.kind === "approved" ? { sandboxEscalation: escalation.target } : {},
12164
12839
  permissionLevel: this.deps.permissionManager?.getLevel() || "auto",
12165
12840
  ...signal ? { signal } : {},
12166
12841
  ...onOutput ? { onOutput } : {},
12167
- outputBudget: budgetOf3(et, depth),
12842
+ outputBudget: budgetOf2(et, depth),
12168
12843
  ...depth < MAX_SUB_CALL_DEPTH ? { callTool: this.subCallChannel(sessionId, signal, span, depth) } : {}
12169
12844
  });
12170
12845
  if (snapshotted.length > 0) {
@@ -12196,7 +12871,7 @@ var ToolExecutor = class {
12196
12871
  success: true,
12197
12872
  output: this.renderOutput(
12198
12873
  et,
12199
- appendArtifactSummaries(text, artifacts, budgetOf3(et, depth), shapeOf(r.output))
12874
+ appendArtifactSummaries(text, artifacts, budgetOf2(et, depth), shapeOf(r.output))
12200
12875
  ),
12201
12876
  ...artifacts.length > 0 ? { artifacts } : {},
12202
12877
  ...diffs.length > 0 ? { diffs } : {}
@@ -12207,7 +12882,7 @@ var ToolExecutor = class {
12207
12882
  success: false,
12208
12883
  output: this.renderOutput(
12209
12884
  et,
12210
- truncateForModel(failureText(r), budgetOf3(et, depth, "failure"), "text")
12885
+ truncateForModel(failureText(r), budgetOf2(et, depth, "failure"), "text")
12211
12886
  )
12212
12887
  });
12213
12888
  } catch (err) {
@@ -12289,6 +12964,56 @@ var ToolExecutor = class {
12289
12964
  );
12290
12965
  return guessed || argsKey(args);
12291
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
+ }
12292
13017
  async checkPermission(et, args, signal) {
12293
13018
  const pm = this.deps.permissionManager;
12294
13019
  if (!pm) return { allowed: true };
@@ -12357,15 +13082,16 @@ var ToolExecutor = class {
12357
13082
  var DEFAULT_CONTEXT_LENGTH = 128e3;
12358
13083
  function shrank(before, after) {
12359
13084
  const chars = (list) => list.reduce((sum, m) => sum + m.content.length, 0);
12360
- return chars(after) < chars(before);
13085
+ if (chars(after) < chars(before)) return true;
13086
+ return requestImageBytes(after) < requestImageBytes(before);
12361
13087
  }
12362
13088
  var NO_OUTPUT_TEXT = "(\u65E0\u8F93\u51FA)";
12363
13089
  function imageParts(content) {
12364
13090
  return typeof content === "string" ? [] : content.filter((p) => p.type === "image");
12365
13091
  }
12366
13092
  function toolSignatureOf(config, tools) {
12367
- const direct = tools.map((t81) => t81.name).join("\0");
12368
- 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");
12369
13095
  return declared ? `${direct}|${declared}` : direct;
12370
13096
  }
12371
13097
  function modelSignatureOf(info) {
@@ -12384,13 +13110,13 @@ function stripHistoryImages(history) {
12384
13110
  });
12385
13111
  return { messages, stripped };
12386
13112
  }
12387
- function budgetStop(budget2, messages) {
12388
- const breach = budget2.breachReason();
12389
- 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 };
12390
13116
  const imageTokens = pendingImageTokens(messages);
12391
- const projected = budget2.projectedBreach(imageTokens);
13117
+ const projected = budget.projectedBreach(imageTokens);
12392
13118
  if (!projected) return void 0;
12393
- return { message: budget2.describeProjectedBreach(projected, imageTokens), breach: projected };
13119
+ return { message: budget.describeProjectedBreach(projected, imageTokens), breach: projected };
12394
13120
  }
12395
13121
  var AgentLoop = class {
12396
13122
  config;
@@ -12456,12 +13182,34 @@ var AgentLoop = class {
12456
13182
  abort() {
12457
13183
  this.aborted = true;
12458
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
+ }
12459
13193
  multimodal() {
12460
13194
  return resolveMultimodalPolicy({
12461
13195
  ...this.config.multimodal,
12462
13196
  supportsImages: this.config.provider.capabilities?.supportsImages ?? false
12463
13197
  });
12464
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
+ }
12465
13213
  modelInfo() {
12466
13214
  const id = this.config.provider.modelId;
12467
13215
  if (!id) return void 0;
@@ -12501,6 +13249,7 @@ var AgentLoop = class {
12501
13249
  projectBlock: seg2.projectBlock,
12502
13250
  memoryBlock: seg2.memoryBlock,
12503
13251
  historyText: opts.historyText,
13252
+ ...opts.lastUsage ? { lastUsage: opts.lastUsage } : {},
12504
13253
  tools: this.toolProvider.list().map((tool) => ({ tool, source: opts.toolSource?.(tool.name) ?? "builtin" })),
12505
13254
  skillIndex: this.config.skillSystem?.indexStats?.({
12506
13255
  only: this.config.roleSkills?.()
@@ -12518,6 +13267,7 @@ var AgentLoop = class {
12518
13267
  let errored;
12519
13268
  const started = Date.now();
12520
13269
  for await (const ev of this.runTurns(input, turns)) {
13270
+ input.onEvent?.(ev);
12521
13271
  if (ev.type === "finish") {
12522
13272
  usage = ev.usage;
12523
13273
  if (ev.reason === "stalled") stalled = true;
@@ -12620,13 +13370,15 @@ var AgentLoop = class {
12620
13370
  let lastText = "";
12621
13371
  let imagesDroppedReported = false;
12622
13372
  let overflowRescued = false;
13373
+ let imagesRescue = { offloaded: 0 };
13374
+ let imagesOffloaded = false;
12623
13375
  let runUsage;
12624
13376
  let lastModel;
12625
13377
  let lastRequestedModel;
12626
13378
  const addRun = (u) => {
12627
13379
  runUsage = runUsage ? addUsage(runUsage, u) : u;
12628
13380
  };
12629
- const budget2 = new BudgetGuard({
13381
+ const budget = new BudgetGuard({
12630
13382
  ...this.config.budget ? { budget: this.config.budget } : {},
12631
13383
  ...this.config.modelPricing ? { pricing: this.config.modelPricing } : {},
12632
13384
  ...input.maxCallCostUsd !== void 0 ? { maxCallCostUsd: input.maxCallCostUsd } : {},
@@ -12638,7 +13390,7 @@ var AgentLoop = class {
12638
13390
  const finish = (reason9, breach) => ({
12639
13391
  type: "finish",
12640
13392
  reason: reason9,
12641
- usage: budget2.snapshot(),
13393
+ usage: budget.snapshot(),
12642
13394
  ...runUsage ? { runUsage } : {},
12643
13395
  ...lastModel ? { model: lastModel } : {},
12644
13396
  ...lastRequestedModel ? { requestedModel: lastRequestedModel } : {},
@@ -12690,8 +13442,8 @@ var AgentLoop = class {
12690
13442
  await this.prompt.syncMemory(userText, lastText);
12691
13443
  return;
12692
13444
  }
12693
- budget2.syncFromStore();
12694
- const stop = budgetStop(budget2, messages);
13445
+ budget.syncFromStore();
13446
+ const stop = budgetStop(budget, messages);
12695
13447
  if (stop) {
12696
13448
  yield { type: "text-delta", text: stop.message };
12697
13449
  await this.prompt.syncMemory(userText, lastText || stop.message);
@@ -12743,8 +13495,8 @@ var AgentLoop = class {
12743
13495
  }
12744
13496
  if (rescued.usage) {
12745
13497
  addRun(rescued.usage);
12746
- yield* budget2.toEvents(
12747
- budget2.add(rescued.usage, this.rateOf(rescued.model)),
13498
+ yield* budget.toEvents(
13499
+ budget.add(rescued.usage, this.rateOf(rescued.model)),
12748
13500
  runUsage
12749
13501
  );
12750
13502
  }
@@ -12776,6 +13528,7 @@ var AgentLoop = class {
12776
13528
  }
12777
13529
  }
12778
13530
  const turnStart = Date.now();
13531
+ imagesRescue = { offloaded: 0 };
12779
13532
  const llmSpan = this.telemetry.startSpan(
12780
13533
  "llm_call",
12781
13534
  {
@@ -12788,6 +13541,24 @@ var AgentLoop = class {
12788
13541
  );
12789
13542
  let guard = this.config.compliance?.outputGuard();
12790
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
+ }
12791
13562
  const requestFor = (msgs) => ({
12792
13563
  provider: this.config.provider,
12793
13564
  input: {
@@ -12804,34 +13575,59 @@ var AgentLoop = class {
12804
13575
  });
12805
13576
  let result;
12806
13577
  try {
12807
- result = yield* runTurn(requestFor(messages));
12808
- if (result.usage) {
12809
- llmSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, result.usage.inputTokens);
12810
- llmSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, result.usage.outputTokens);
12811
- }
12812
- llmSpan.setAttribute("epoch.finish_reason", result.finishReason);
12813
- } catch (err) {
12814
- llmSpan.recordError(err);
12815
- const engine = this.config.contextEngine;
12816
- if (overflowRescued || !engine || !isContextOverflow(err)) throw err;
12817
- overflowRescued = true;
12818
- const rescued = await this.compressNow(messages, {
12819
- totalTokens: totalInput + totalOutput
12820
- });
12821
- if (rescued.usage) {
12822
- addRun(rescued.usage);
12823
- 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
+ }
12824
13630
  }
12825
- if (!shrank(messages, rescued.messages)) throw err;
12826
- messages = rescued.messages;
12827
- yield {
12828
- type: "notice",
12829
- code: "context-compacted",
12830
- text: t("agent_run.context_compacted")
12831
- };
12832
- guard = this.config.compliance?.outputGuard();
12833
- reasoningGuard = this.config.compliance?.outputGuard();
12834
- result = yield* runTurn(requestFor(messages));
12835
13631
  } finally {
12836
13632
  llmSpan.end();
12837
13633
  this.telemetry.histogram(METRIC.LLM_CALL_LATENCY, Date.now() - turnStart);
@@ -12877,14 +13673,14 @@ var AgentLoop = class {
12877
13673
  "epoch.token_type": "cache_write"
12878
13674
  });
12879
13675
  }
12880
- const status = budget2.add(usage, this.rateOf(result.model));
13676
+ const status = budget.add(usage, this.rateOf(result.model));
12881
13677
  input.onTurnUsage?.(
12882
13678
  status.turnCostUsd === void 0 ? usage : { ...usage, costUsd: status.turnCostUsd }
12883
13679
  );
12884
13680
  addRun(usage);
12885
13681
  lastModel = result.model;
12886
13682
  lastRequestedModel = result.requestedModel;
12887
- yield* budget2.toEvents(status, runUsage);
13683
+ yield* budget.toEvents(status, runUsage);
12888
13684
  }
12889
13685
  const verdict = mergeVerdicts(guard?.verdict(), reasoningGuard?.verdict());
12890
13686
  if (verdict) this.reportCompliance(verdict, "output");
@@ -12949,7 +13745,11 @@ ${finalText}` };
12949
13745
  turn
12950
13746
  });
12951
13747
  if (turn < maxTurns && input.steer?.pending() === true) {
12952
- 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
+ });
12953
13753
  yield* this.drainSteer(input, messages);
12954
13754
  continue;
12955
13755
  }
@@ -12960,7 +13760,11 @@ ${finalText}` };
12960
13760
  );
12961
13761
  if (wantContinue && turn < maxTurns) {
12962
13762
  messages.push(
12963
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13763
+ {
13764
+ role: "assistant",
13765
+ content: finalText || NO_OUTPUT_TEXT,
13766
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13767
+ },
12964
13768
  {
12965
13769
  role: "user",
12966
13770
  content: systemNote(
@@ -12980,7 +13784,11 @@ ${finalText}` };
12980
13784
  text: t("agent_run.blank_response")
12981
13785
  };
12982
13786
  messages.push(
12983
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13787
+ {
13788
+ role: "assistant",
13789
+ content: finalText || NO_OUTPUT_TEXT,
13790
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13791
+ },
12984
13792
  { role: "user", content: systemNote(blankResponseNote(finalText.trim().length)) }
12985
13793
  );
12986
13794
  continue;
@@ -12992,7 +13800,11 @@ ${finalText}` };
12992
13800
  if (open !== void 0 && open > 0) {
12993
13801
  todoNudged = true;
12994
13802
  messages.push(
12995
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13803
+ {
13804
+ role: "assistant",
13805
+ content: finalText || NO_OUTPUT_TEXT,
13806
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13807
+ },
12996
13808
  {
12997
13809
  role: "user",
12998
13810
  content: systemNote(unfinishedTodoNote(open))
@@ -13004,7 +13816,11 @@ ${finalText}` };
13004
13816
  if (!idleNudged && turn < maxTurns && !truncated && announcedNextStep(finalText)) {
13005
13817
  idleNudged = true;
13006
13818
  messages.push(
13007
- { role: "assistant", content: finalText || NO_OUTPUT_TEXT },
13819
+ {
13820
+ role: "assistant",
13821
+ content: finalText || NO_OUTPUT_TEXT,
13822
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {}
13823
+ },
13008
13824
  { role: "user", content: systemNote(announcedButIdleNote()) }
13009
13825
  );
13010
13826
  continue;
@@ -13022,6 +13838,7 @@ ${finalText}` };
13022
13838
  messages.push({
13023
13839
  role: "assistant",
13024
13840
  content: result.text,
13841
+ ...result.reasoningText ? { reasoning: result.reasoningText } : {},
13025
13842
  toolCalls: toolCalls.map((tc) => ({
13026
13843
  toolCallId: tc.toolCallId,
13027
13844
  toolName: tc.toolName,
@@ -13121,8 +13938,8 @@ ${finalText}` };
13121
13938
  messages = compacted.messages;
13122
13939
  if (compacted.usage) {
13123
13940
  addRun(compacted.usage);
13124
- yield* budget2.toEvents(
13125
- budget2.add(compacted.usage, this.rateOf(compacted.model)),
13941
+ yield* budget.toEvents(
13942
+ budget.add(compacted.usage, this.rateOf(compacted.model)),
13126
13943
  runUsage
13127
13944
  );
13128
13945
  }
@@ -13199,7 +14016,7 @@ ${finalText}` };
13199
14016
  return { ...result, ran: true };
13200
14017
  }
13201
14018
  async compressNow(messages, opts = {}) {
13202
- const { instruction, totalTokens: totalTokens2, promptTokens: promptTokens2 } = opts;
14019
+ const { instruction, totalTokens: totalTokens2, promptTokens: promptTokens3, force } = opts;
13203
14020
  const engine = this.config.contextEngine;
13204
14021
  if (!engine) return { messages };
13205
14022
  try {
@@ -13209,7 +14026,8 @@ ${finalText}` };
13209
14026
  contextLength: this.config.contextLength,
13210
14027
  ...instruction ? { instruction } : {},
13211
14028
  ...plan ? { plan } : {},
13212
- ...promptTokens2 === void 0 ? {} : { promptTokens: promptTokens2 }
14029
+ ...force ? { force } : {},
14030
+ ...promptTokens3 === void 0 ? {} : { promptTokens: promptTokens3 }
13213
14031
  });
13214
14032
  const bill = {
13215
14033
  ...compressed.usage ? { usage: compressed.usage } : {},
@@ -13426,6 +14244,9 @@ var CompressionSchema = z.object({
13426
14244
  enabled: z.boolean().default(true),
13427
14245
  threshold: z.number().gt(0).max(1).default(DEFAULT_COMPRESSION_THRESHOLD)
13428
14246
  });
14247
+ var LimitsSchema = z.object({
14248
+ maxRequestImageBytes: z.int().positive().optional()
14249
+ });
13429
14250
  var DEFAULT_MEMORY_CHAR_LIMIT = 2e4;
13430
14251
  var DEFAULT_USER_CHAR_LIMIT = 6e3;
13431
14252
  var DEFAULT_MEMORY_INDEX_CHAR_LIMIT = 4e3;
@@ -13437,6 +14258,9 @@ var MemorySchema = z.object({
13437
14258
  var SandboxSchema = z.object({
13438
14259
  terminal: z.boolean().default(true)
13439
14260
  });
14261
+ var DelegateSchema = z.object({
14262
+ taskTimeoutSec: z.int().nonnegative().default(900)
14263
+ });
13440
14264
  var ToolsSchema = z.object({
13441
14265
  deferMcp: z.boolean().default(false),
13442
14266
  mode: z.enum(["tools", "both", "code"]).default("tools")
@@ -13545,7 +14369,17 @@ var EpochConfigSchema = z.object({
13545
14369
  maxOutputTokens: z.int().positive().optional(),
13546
14370
  homeDir: z.string().min(1).default(() => resolveHomeDir()),
13547
14371
  dbPath: z.string().min(1).default(() => dbPath(resolveHomeDir())),
13548
- 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),
13549
14383
  permissions: z.object({
13550
14384
  allow: z.array(z.string().min(1)).optional(),
13551
14385
  ask: z.array(z.string().min(1)).optional(),
@@ -13558,9 +14392,11 @@ var EpochConfigSchema = z.object({
13558
14392
  hooks: z.record(z.string(), z.unknown()).optional(),
13559
14393
  trust: TrustSchema.optional(),
13560
14394
  sandbox: SandboxSchema.prefault({}),
14395
+ delegate: DelegateSchema.prefault({}),
13561
14396
  tools: ToolsSchema.prefault({}),
13562
14397
  skills: SkillsSchema.optional(),
13563
14398
  compression: CompressionSchema.prefault({}),
14399
+ limits: LimitsSchema.prefault({}),
13564
14400
  memory: MemorySchema.prefault({}),
13565
14401
  artifacts: ArtifactsSchema.prefault({}),
13566
14402
  sessions: SessionsSchema.prefault({}),
@@ -13592,6 +14428,7 @@ var EpochConfigSchema = z.object({
13592
14428
  message: "blankCards: id must be unique"
13593
14429
  }).optional(),
13594
14430
  blankGreeting: BlankGreetingSchema.optional(),
14431
+ scheduleRemoveConfirm: HostTextSchema.optional(),
13595
14432
  user: HostUserSchema.optional(),
13596
14433
  settingsHidden: z.array(z.string().min(1)).min(1).optional(),
13597
14434
  settingsPanes: z.array(
@@ -13634,6 +14471,22 @@ function settingsPaneBeforeIssues(config) {
13634
14471
  message: `unknown settings pane id: ${String(pane.before)} (known: ${SETTINGS_PANE_IDS.join(", ")})`
13635
14472
  }));
13636
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
+ }
13637
14490
  function defaultConfig() {
13638
14491
  return EpochConfigSchema.parse({});
13639
14492
  }
@@ -13767,7 +14620,7 @@ var ManagedPolicyError = class extends Error {
13767
14620
  }
13768
14621
  };
13769
14622
  function assertLevelAllowed(level, policy) {
13770
- if (level !== "bypass" || !policy.disableBypassPermissionsMode) return;
14623
+ if (approvalPolicyForLevel(level) !== "never" || !policy.disableBypassPermissionsMode) return;
13771
14624
  throw new ManagedPolicyError(
13772
14625
  t("managed_settings.bypass_blocked"),
13773
14626
  t("managed_settings.bypass_blocked_hint", { path: policy.path })
@@ -14072,18 +14925,18 @@ function _loadImpl(overrideHomeDir) {
14072
14925
  if (read.kind === "corrupt" && _lastKnownGood?.homeDir === homeDir) {
14073
14926
  _lastIssues = issues;
14074
14927
  _lastOrigins = _lastKnownGood.origins;
14075
- const cached = _lastKnownGood;
14928
+ const cached2 = _lastKnownGood;
14076
14929
  _cache = {
14077
14930
  homeDir,
14078
14931
  mtime,
14079
14932
  size,
14080
14933
  entriesStamp,
14081
14934
  envSnapshot,
14082
- config: cached.config,
14935
+ config: cached2.config,
14083
14936
  issues,
14084
- origins: cached.origins
14937
+ origins: cached2.origins
14085
14938
  };
14086
- return cached.config;
14939
+ return cached2.config;
14087
14940
  }
14088
14941
  const config = parseRawConfig(read.raw, issues, track);
14089
14942
  reportDroppedEnv(issues);
@@ -14139,6 +14992,7 @@ function parseRawConfig(raw, issues, track) {
14139
14992
  issues.push(...outcome.issues);
14140
14993
  issues.push(...settingsHiddenIssues(outcome.value));
14141
14994
  issues.push(...settingsPaneBeforeIssues(outcome.value));
14995
+ issues.push(...scheduleRemoveConfirmIssues(outcome.value));
14142
14996
  if (outcome.issues.length === 0) collectKeyPaths(cleaned, track.file);
14143
14997
  return outcome.value;
14144
14998
  }
@@ -14269,8 +15123,9 @@ function applyEnvOverrides(config, issues, track) {
14269
15123
  } else issues.push(envIssue("EPOCH_LOG_LEVEL", env.EPOCH_LOG_LEVEL, levels.join(" / ")));
14270
15124
  }
14271
15125
  if (env.EPOCH_PERMISSION) {
14272
- if (isPermissionLevel(env.EPOCH_PERMISSION)) {
14273
- config.permission = env.EPOCH_PERMISSION;
15126
+ const level = resolvePermissionLevel(env.EPOCH_PERMISSION);
15127
+ if (level) {
15128
+ config.permission = level;
14274
15129
  track.env.add("permission");
14275
15130
  } else {
14276
15131
  issues.push(
@@ -14780,11 +15635,11 @@ function buildPricing(prompt, completion, cacheRead) {
14780
15635
  const output = Number.parseFloat(completion) * 1e6;
14781
15636
  if (!Number.isFinite(input) || !Number.isFinite(output)) return void 0;
14782
15637
  if (input <= 0 && output <= 0) return void 0;
14783
- const cached = cacheRead ? Number.parseFloat(cacheRead) * 1e6 : NaN;
15638
+ const cached2 = cacheRead ? Number.parseFloat(cacheRead) * 1e6 : NaN;
14784
15639
  return {
14785
15640
  inputPerMTok: input,
14786
15641
  outputPerMTok: output,
14787
- ...Number.isFinite(cached) && cached > 0 ? { cachedInputPerMTok: cached } : {}
15642
+ ...Number.isFinite(cached2) && cached2 > 0 ? { cachedInputPerMTok: cached2 } : {}
14788
15643
  };
14789
15644
  }
14790
15645
  function matchesWindow(o, at) {
@@ -15001,6 +15856,30 @@ var TOOL_RESULT_MEDIA = {
15001
15856
  function acceptsToolResultMedia(type) {
15002
15857
  return TOOL_RESULT_MEDIA[type];
15003
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
+ }
15004
15883
  function isModuleNotFound(err) {
15005
15884
  if (!err || typeof err !== "object") return false;
15006
15885
  const code = err.code;
@@ -15009,9 +15888,10 @@ function isModuleNotFound(err) {
15009
15888
  return /Cannot find (module|package)|Failed to resolve/i.test(msg);
15010
15889
  }
15011
15890
  // src/provider/entry-route.ts
15012
- function buildEntryTable(entries2) {
15891
+ function buildEntryTable(entries2, homeDir) {
15013
15892
  return {
15014
- entries: new Map((entries2 ?? []).map((e) => [e.id, e]))
15893
+ entries: new Map((entries2 ?? []).map((e) => [e.id, e])),
15894
+ ...homeDir ? { homeDir } : {}
15015
15895
  };
15016
15896
  }
15017
15897
  function entryProvider(_table, entry) {
@@ -15046,6 +15926,14 @@ function entryAcceptsToolMedia(table, provider, entry) {
15046
15926
  if (e?.provider === "openai-compatible" && e.protocol === "anthropic-messages") return true;
15047
15927
  return acceptsToolResultMedia(provider);
15048
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
+ }
15049
15937
  function capabilityOfRef(config, ref = config.model) {
15050
15938
  const parsed = ref ? parseModelRef(ref) : void 0;
15051
15939
  const entry = parsed ? config.modelEntries?.find((e) => e.id === parsed.entry) : void 0;
@@ -15213,7 +16101,7 @@ var ProviderRouter = class {
15213
16101
  }
15214
16102
  constructor(config) {
15215
16103
  this.config = config;
15216
- this.entryTable = buildEntryTable(config.modelEntries);
16104
+ this.entryTable = buildEntryTable(config.modelEntries, config.homeDir);
15217
16105
  this.configSelection = this.resolveConfigured(config);
15218
16106
  this.initFactories();
15219
16107
  this.selection = { ...this.configSelection };
@@ -15264,7 +16152,7 @@ var ProviderRouter = class {
15264
16152
  return result;
15265
16153
  }
15266
16154
  reloadModelEntries(entries2) {
15267
- this.entryTable = buildEntryTable(entries2);
16155
+ this.entryTable = buildEntryTable(entries2, this.config.homeDir);
15268
16156
  for (const [route, opts] of [...this.available]) {
15269
16157
  if (!opts.entry) continue;
15270
16158
  this.available.delete(route);
@@ -15413,8 +16301,8 @@ var ProviderRouter = class {
15413
16301
  return { selection, configured: true };
15414
16302
  }
15415
16303
  async getModel(route, modelId) {
15416
- const cached = this.factories.get(route);
15417
- if (cached) return cached(modelId ?? this.selection.model);
16304
+ const cached2 = this.factories.get(route);
16305
+ if (cached2) return cached2(modelId ?? this.selection.model);
15418
16306
  const opts = this.available.get(route);
15419
16307
  if (!opts) {
15420
16308
  throw new ProviderError(
@@ -15524,7 +16412,7 @@ var ProviderRouter = class {
15524
16412
  const state = this.startPoint(input);
15525
16413
  const requestedModel = state.cm;
15526
16414
  const messagesFor = messageTranslator(input.messages, this.entryTable);
15527
- const tools = input.tools ? toSdkTools(input.tools) : void 0;
16415
+ const tools = input.tools ? toSdkTools(input.tools, { cacheBreakpoint: true }) : void 0;
15528
16416
  const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
15529
16417
  for (; ; ) {
15530
16418
  if (state.attempt > this.config.retry.maxRetries) {
@@ -15598,7 +16486,7 @@ var ProviderRouter = class {
15598
16486
  const requestedModel = state.cm;
15599
16487
  let emitted = false;
15600
16488
  const messagesFor = messageTranslator(input.messages, this.entryTable);
15601
- const tools = input.tools ? toSdkTools(input.tools) : void 0;
16489
+ const tools = input.tools ? toSdkTools(input.tools, { cacheBreakpoint: true }) : void 0;
15602
16490
  const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
15603
16491
  let streamPartError;
15604
16492
  for (; ; ) {
@@ -15735,8 +16623,8 @@ var ProviderRouter = class {
15735
16623
  async probeImageSupport() {
15736
16624
  const { provider, model, entry } = this.selection;
15737
16625
  const home = this.config.homeDir;
15738
- const cached = readImageProbe(home, provider, model);
15739
- if (cached !== void 0) return cached;
16626
+ const cached2 = readImageProbe(home, provider, model);
16627
+ if (cached2 !== void 0) return cached2;
15740
16628
  let lm;
15741
16629
  try {
15742
16630
  lm = await this.getModel(this.routeOf(provider, entry), model);
@@ -15764,10 +16652,12 @@ function messageTranslator(messages, table) {
15764
16652
  const cache = /* @__PURE__ */ new Map();
15765
16653
  return (type, entry) => {
15766
16654
  const inline = entryAcceptsToolMedia(table, type, entry);
15767
- let hit = cache.get(inline);
16655
+ const reasoning = entryCarriesReasoningBack(table, type, entry);
16656
+ const key = `${inline}|${reasoning}`;
16657
+ let hit = cache.get(key);
15768
16658
  if (!hit) {
15769
- hit = toModelMessages(messages, { toolMediaInline: inline });
15770
- cache.set(inline, hit);
16659
+ hit = toModelMessages(messages, { toolMediaInline: inline, reasoning });
16660
+ cache.set(key, hit);
15771
16661
  }
15772
16662
  return hit;
15773
16663
  };
@@ -15838,12 +16728,12 @@ var PROVIDER_DISCOVERY = {
15838
16728
  function discoverySpec(provider) {
15839
16729
  return PROVIDER_DISCOVERY[provider];
15840
16730
  }
15841
- function cachePath(homeDir, provider) {
16731
+ function cachePath2(homeDir, provider) {
15842
16732
  return join(homeDir, "cache", `models_${provider}.json`);
15843
16733
  }
15844
16734
  function loadCache2(homeDir, provider) {
15845
16735
  try {
15846
- const path = cachePath(homeDir, provider);
16736
+ const path = cachePath2(homeDir, provider);
15847
16737
  if (!existsSync(path)) return null;
15848
16738
  const data = JSON.parse(readFileSync(path, "utf-8"));
15849
16739
  const age = Date.now() - (data._cachedAt ?? 0);
@@ -15858,7 +16748,7 @@ function saveCache2(homeDir, provider, models) {
15858
16748
  const dir = join(homeDir, "cache");
15859
16749
  mkdirSync(dir, { recursive: true });
15860
16750
  writeFileSync(
15861
- cachePath(homeDir, provider),
16751
+ cachePath2(homeDir, provider),
15862
16752
  JSON.stringify({ models, _cachedAt: Date.now() }),
15863
16753
  "utf-8"
15864
16754
  );
@@ -15937,13 +16827,13 @@ async function discoverModels(homeDir, provider, apiKey, baseUrl) {
15937
16827
  }
15938
16828
  }
15939
16829
  }
15940
- const cached = loadCache2(homeDir, provider);
15941
- if (cached) return { models: cached, source: "cache" };
16830
+ const cached2 = loadCache2(homeDir, provider);
16831
+ if (cached2) return { models: cached2, source: "cache" };
15942
16832
  return { models: STATIC_MODEL_CATALOG[provider] ?? [], source: "static" };
15943
16833
  }
15944
16834
  function clearModelCache(homeDir, provider) {
15945
16835
  try {
15946
- const path = cachePath(homeDir, provider);
16836
+ const path = cachePath2(homeDir, provider);
15947
16837
  if (existsSync(path)) writeFileSync(path, "{}", "utf-8");
15948
16838
  } catch {
15949
16839
  }
@@ -16140,7 +17030,10 @@ var VALUE_SCHEMA = {
16140
17030
  "automation.keepRuns": EpochConfigSchema.shape.automation.unwrap().shape.keepRuns.unwrap(),
16141
17031
  "automation.maxRecordingMb": EpochConfigSchema.shape.automation.unwrap().shape.maxRecordingMb.unwrap(),
16142
17032
  "sessionSearch.allWorkspaces": EpochConfigSchema.shape.sessionSearch.unwrap().shape.allWorkspaces.unwrap(),
16143
- permission: EpochConfigSchema.shape.permission.unwrap()
17033
+ permission: EpochConfigSchema.shape.permission
17034
+ };
17035
+ var ENUM_CHOICES = {
17036
+ permission: PERMISSION_LEVELS
16144
17037
  };
16145
17038
  function settingValueKind(key) {
16146
17039
  if (!isWritableSettingKey(key)) return null;
@@ -16151,6 +17044,8 @@ function settingValueKind(key) {
16151
17044
  }
16152
17045
  function settingValueChoices(key) {
16153
17046
  if (!isWritableSettingKey(key)) return null;
17047
+ const explicit = ENUM_CHOICES[key];
17048
+ if (explicit) return explicit;
16154
17049
  const schema = VALUE_SCHEMA[key];
16155
17050
  if (schema.def.type !== "enum") return null;
16156
17051
  return Object.values(schema.def.entries).map(String);
@@ -16293,6 +17188,7 @@ function commit(path, body2, createMode, value) {
16293
17188
  }
16294
17189
  return { ok: true, value };
16295
17190
  }
17191
+ var MACHINE_DEPENDENT_DEFAULTS = ["homeDir", "dbPath"];
16296
17192
  var DESCRIPTIONS = {
16297
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",
16298
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",
@@ -16303,7 +17199,112 @@ var DESCRIPTIONS = {
16303
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",
16304
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",
16305
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",
16306
- 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"
16307
17308
  };
16308
17309
  function buildSchemaExports() {
16309
17310
  const used = /* @__PURE__ */ new Set();
@@ -16329,6 +17330,24 @@ function buildSchemaExports() {
16329
17330
  },
16330
17331
  used
16331
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
+ )
16332
17351
  }
16333
17352
  ];
16334
17353
  const stale = Object.keys(DESCRIPTIONS).filter((key) => !used.has(key));
@@ -16339,13 +17358,22 @@ function buildSchemaExports() {
16339
17358
  }
16340
17359
  return exports;
16341
17360
  }
17361
+ var EXPLICIT_ENUMS = {
17362
+ permission: PERMISSION_LEVELS
17363
+ };
16342
17364
  function document(schema, meta, used) {
16343
- const generated = z.toJSONSchema(schema);
17365
+ const generated = meta.io ? z.toJSONSchema(schema, { io: meta.io }) : z.toJSONSchema(schema);
16344
17366
  const dialect = generated.$schema;
16345
17367
  const properties = {
16346
17368
  $schema: { type: "string" },
17369
+ ...meta.extra,
16347
17370
  ...generated.properties
16348
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
+ }
16349
17377
  const doc = {
16350
17378
  $schema: dialect,
16351
17379
  title: meta.title,
@@ -16369,10 +17397,138 @@ function annotate(node, path, used) {
16369
17397
  );
16370
17398
  }
16371
17399
  child.description = text;
17400
+ if (MACHINE_DEPENDENT_DEFAULTS.includes(full)) delete child.default;
16372
17401
  used.add(full);
16373
17402
  annotate(child, full, used);
16374
17403
  }
16375
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
+ }
16376
17532
  var MODULE2 = "Keybindings";
16377
17533
  var VIM_MODE_KEY = "vimMode";
16378
17534
  var FileSchema = z.record(z.string(), z.array(z.string()));
@@ -16567,11 +17723,11 @@ function buildSearchQuery(query) {
16567
17723
  }
16568
17724
  const latinPart = cleaned.replace(CJK_RUN, " ");
16569
17725
  for (const w of latinPart.split(/\s+/)) {
16570
- const t81 = w.trim();
16571
- if (t81.length >= 2) terms.add(t81);
17726
+ const t83 = w.trim();
17727
+ if (t83.length >= 2) terms.add(t83);
16572
17728
  }
16573
17729
  if (terms.size === 0) return null;
16574
- return [...terms].map((t81) => `"${t81.replace(/"/g, "")}"`).join(" OR ");
17730
+ return [...terms].map((t83) => `"${t83.replace(/"/g, "")}"`).join(" OR ");
16575
17731
  }
16576
17732
  // src/memory/db.ts
16577
17733
  var NS2 = "memory";
@@ -17062,8 +18218,8 @@ var MemoryManager = class {
17062
18218
  async add(target, content) {
17063
18219
  const label = injectionLabelOf(content);
17064
18220
  if (label !== void 0) return this.refuseInjection(label);
17065
- const budget2 = this.getBudget()[target];
17066
- if (budget2.current + content.length > budget2.limit) {
18221
+ const budget = this.getBudget()[target];
18222
+ if (budget.current + content.length > budget.limit) {
17067
18223
  this.consolidationFailures++;
17068
18224
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
17069
18225
  return {
@@ -17110,9 +18266,9 @@ var MemoryManager = class {
17110
18266
  }
17111
18267
  const label = injectionLabelOf(content);
17112
18268
  if (label !== void 0) return this.refuseInjection(label);
17113
- const budget2 = this.getBudget()[target];
18269
+ const budget = this.getBudget()[target];
17114
18270
  const delta = content.length - existing.charLength;
17115
- if (budget2.current + delta > budget2.limit) {
18271
+ if (budget.current + delta > budget.limit) {
17116
18272
  this.consolidationFailures++;
17117
18273
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
17118
18274
  return { success: false, done: true, error: "Consolidation limit reached" };
@@ -17317,11 +18473,11 @@ var MemoryManager = class {
17317
18473
  for (const target of ["memory", "user"]) {
17318
18474
  const rows = kept.filter((e) => e.target === target);
17319
18475
  if (rows.length === 0) continue;
17320
- const budget2 = this.getBudget()[target];
18476
+ const budget = this.getBudget()[target];
17321
18477
  const label = target === "user" ? "USER PROFILE (who the user is)" : "MEMORY (your personal notes)";
17322
18478
  sections.push(
17323
18479
  [
17324
- `${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]`,
17325
18481
  ...rows.map((e) => this.indexLine(e))
17326
18482
  ].join("\n")
17327
18483
  );
@@ -17352,7 +18508,7 @@ var MemoryManager = class {
17352
18508
  return `${e.id} ${e.summary} (${e.charLength} chars)`;
17353
18509
  }
17354
18510
  buildConsolidateHint(target) {
17355
- const budget2 = this.getBudget()[target];
18511
+ const budget = this.getBudget()[target];
17356
18512
  const heat = /* @__PURE__ */ new Map();
17357
18513
  for (const row of this.db.getAll(target)) {
17358
18514
  const id = this.idByContent.get(contentKey(row.target, row.content));
@@ -17360,7 +18516,7 @@ var MemoryManager = class {
17360
18516
  }
17361
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);
17362
18518
  return [
17363
- `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.`,
17364
18520
  "",
17365
18521
  "To make room, work by id \u2014 NOT by old_text. The MEMORY INDEX only shows a truncated",
17366
18522
  "first line, so text copied from it will never match an entry exactly.",
@@ -18202,6 +19358,10 @@ var SessionDB = class {
18202
19358
  where += " AND cwd = @cwd";
18203
19359
  params["cwd"] = input.cwd;
18204
19360
  }
19361
+ if (!input.includeDelegate) {
19362
+ where += " AND COALESCE(source, '') != @delegateSource";
19363
+ params["delegateSource"] = DELEGATE_SESSION_SOURCE;
19364
+ }
18205
19365
  if (input.updatedSince !== void 0) {
18206
19366
  where += " AND COALESCE(updated_at, started_at) > @updatedSince";
18207
19367
  params["updatedSince"] = input.updatedSince;
@@ -18262,8 +19422,8 @@ var SessionDB = class {
18262
19422
  const todo = this.db.prepare(
18263
19423
  "INSERT INTO todos (id, session_id, content, status, reason, created_at, updated_at) VALUES (@id, @sessionId, @content, @status, @reason, @createdAt, @updatedAt)"
18264
19424
  );
18265
- for (const t81 of extras.todos ?? []) {
18266
- 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 });
18267
19427
  }
18268
19428
  }
18269
19429
  const g = extras.goal;
@@ -18521,6 +19681,26 @@ var SessionDB = class {
18521
19681
  duration: 0
18522
19682
  };
18523
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
+ }
18524
19704
  archiveAndCompact(sessionId, input) {
18525
19705
  const run = this.db.transaction(() => {
18526
19706
  const rows = this.db.prepare(
@@ -18743,7 +19923,7 @@ function checkFtsHealth(db) {
18743
19923
  messages,
18744
19924
  tables,
18745
19925
  missingTriggers,
18746
- 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),
18747
19927
  indexBytes: indexBytesOf(db),
18748
19928
  space: spaceOf(db)
18749
19929
  };
@@ -18893,10 +20073,10 @@ var AUTO_VACUUM_MIN_FREELIST_RATIO = 0.25;
18893
20073
  var IMPORT_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
18894
20074
  var TODO_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "cancelled"]);
18895
20075
  function validateImportExtras(input) {
18896
- for (const [i, t81] of (input.todos ?? []).entries()) {
18897
- if (typeof t81.content !== "string" || t81.content === "") return `todos[${i}].content`;
18898
- if (!TODO_STATUSES.has(t81.status)) return `todos[${i}].status`;
18899
- 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`;
18900
20080
  }
18901
20081
  const g = input.goal;
18902
20082
  if (g === void 0) return void 0;
@@ -19085,7 +20265,7 @@ var SessionManager = class {
19085
20265
  converted.push({ ...rest, parts: result.parts });
19086
20266
  }
19087
20267
  const droppedParentId = meta.parentId !== void 0 && !this.db.hasSession(meta.parentId) ? meta.parentId : void 0;
19088
- const todos = (input.todos ?? []).map((t81) => ({ ...t81, id: this.db.freshTodoId() }));
20268
+ const todos = (input.todos ?? []).map((t83) => ({ ...t83, id: this.db.freshTodoId() }));
19089
20269
  const goal = input.goal === void 0 ? void 0 : { ...input.goal, id: `goal_${randomUUID().replace(/-/g, "")}` };
19090
20270
  try {
19091
20271
  const newIds = this.db.importSession(
@@ -19103,7 +20283,7 @@ var SessionManager = class {
19103
20283
  messageCount: newIds.length,
19104
20284
  idMap,
19105
20285
  ...droppedParentId === void 0 ? {} : { droppedParentId },
19106
- ...input.todos === void 0 ? {} : { todoIds: todos.map((t81) => t81.id) },
20286
+ ...input.todos === void 0 ? {} : { todoIds: todos.map((t83) => t83.id) },
19107
20287
  ...goal === void 0 ? {} : { goalId: goal.id }
19108
20288
  };
19109
20289
  } catch (err) {
@@ -19250,6 +20430,9 @@ var SessionManager = class {
19250
20430
  const summary = toSummarize.length === 0 ? "" : await this.buildSummary(toSummarize, summarize2) ?? outlineOf(toSummarize);
19251
20431
  return this.db.compact(sessionId, summary, beforeCount, IMPORTANT_ROUNDS * 2);
19252
20432
  }
20433
+ offloadImageParts(sessionId) {
20434
+ return this.db.offloadImageParts(sessionId);
20435
+ }
19253
20436
  archiveAndCompact(sessionId, input) {
19254
20437
  return this.db.archiveAndCompact(sessionId, input);
19255
20438
  }
@@ -19445,8 +20628,8 @@ var GoalService = class {
19445
20628
  const text = objective.trim();
19446
20629
  if (!text) return { ok: false, reason: "empty-objective" };
19447
20630
  if (text.length > MAX_OBJECTIVE_CHARS) return { ok: false, reason: "objective-too-long" };
19448
- const budget2 = maxRounds ?? DEFAULT_GOAL_MAX_ROUNDS;
19449
- 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) {
19450
20633
  return { ok: false, reason: "bad-budget" };
19451
20634
  }
19452
20635
  const existing = this.current(sessionId);
@@ -19458,7 +20641,7 @@ var GoalService = class {
19458
20641
  objective: text,
19459
20642
  phase: "active",
19460
20643
  block: void 0,
19461
- maxRounds: budget2,
20644
+ maxRounds: budget,
19462
20645
  roundsUsed: 0,
19463
20646
  completeEvidence: void 0,
19464
20647
  createdAt: at,
@@ -19603,7 +20786,7 @@ function withGoalCompression(engine, view) {
19603
20786
  if (!now) return engine.compress(messages, config);
19604
20787
  return engine.compress(messages, { ...config, goal: now.objective });
19605
20788
  },
19606
- shouldCompress: (promptTokens2, contextLength) => engine.shouldCompress(promptTokens2, contextLength),
20789
+ shouldCompress: (promptTokens3, contextLength) => engine.shouldCompress(promptTokens3, contextLength),
19607
20790
  trackUsage: (usage) => engine.trackUsage(usage),
19608
20791
  getTotalTokens: () => engine.getTotalTokens(),
19609
20792
  reset: () => engine.reset()
@@ -19998,7 +21181,7 @@ var AppearanceStore = class {
19998
21181
  }
19999
21182
  };
20000
21183
  function ruleAskActive(level) {
20001
- return level !== "bypass";
21184
+ return approvalPolicyForLevel(level) === "ask";
20002
21185
  }
20003
21186
  function checkCodeExec(isolation) {
20004
21187
  if (isolation === "os") {
@@ -20032,7 +21215,7 @@ function checkPlan(operation, extraRoots) {
20032
21215
  code: "level-default"
20033
21216
  };
20034
21217
  }
20035
- if (operation.type === "file_write" || operation.type === "command") {
21218
+ if (operation.type === "file_write") {
20036
21219
  return { allowed: false, reason: t("permission_reason.plan_no_write"), code: "level-default" };
20037
21220
  }
20038
21221
  if (operation.type === "network") {
@@ -20042,6 +21225,10 @@ function checkPlan(operation, extraRoots) {
20042
21225
  code: "level-default"
20043
21226
  };
20044
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
+ }
20045
21232
  return outsideWorkspace(operation, extraRoots) ?? { allowed: true, code: "level-default" };
20046
21233
  }
20047
21234
  function checkAcceptEdits(operation, isolation, extraRoots) {
@@ -20067,8 +21254,6 @@ function checkDefault(operation, extraRoots) {
20067
21254
  }
20068
21255
  function decideByLevel(level, operation, isolation, extraRoots = []) {
20069
21256
  switch (level) {
20070
- case "bypass":
20071
- return { allowed: true, code: "level-default" };
20072
21257
  case "auto":
20073
21258
  return checkAuto();
20074
21259
  case "plan":
@@ -20248,11 +21433,11 @@ var ApprovalCache = class {
20248
21433
  return !rel.startsWith("..") && !isAbsolute(rel);
20249
21434
  }
20250
21435
  normalizeTarget(target) {
20251
- const t81 = target.trim();
20252
- if (!t81) return "";
20253
- const unix = t81.replace(/\\/g, "/");
21436
+ const t83 = target.trim();
21437
+ if (!t83) return "";
21438
+ const unix = t83.replace(/\\/g, "/");
20254
21439
  if (unix.startsWith("/") || /^[A-Za-z]:\//.test(unix)) return resolve(unix);
20255
- return t81;
21440
+ return t83;
20256
21441
  }
20257
21442
  load() {
20258
21443
  try {
@@ -20355,9 +21540,9 @@ function createSharedPermissionState(cache) {
20355
21540
  };
20356
21541
  }
20357
21542
  function cacheKeyFor(raw) {
20358
- const t81 = raw.trim();
20359
- if (!t81) return null;
20360
- return t81.slice(0, 500);
21543
+ const t83 = raw.trim();
21544
+ if (!t83) return null;
21545
+ return t83.slice(0, 500);
20361
21546
  }
20362
21547
  // src/permission/manager.ts
20363
21548
  var log3 = createLogger("permission");
@@ -20470,15 +21655,23 @@ var PermissionManager = class _PermissionManager {
20470
21655
  code: hard.code
20471
21656
  };
20472
21657
  }
20473
- if (this.level === "bypass" && hard.code === "obfuscated") {
21658
+ const neverAsk = approvalPolicyForLevel(this.level) === "never";
21659
+ if (neverAsk && hard.code === "obfuscated") {
20474
21660
  return {
20475
21661
  allowed: true,
20476
21662
  reason: t("permission_reason.bypass_unaskable", { reason: hard.reason ?? "" }),
20477
21663
  code: hard.code
20478
21664
  };
20479
21665
  }
20480
- const cached = this.cachedDecision(operation);
20481
- 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
+ }
20482
21675
  if (this.shared.nonInteractive) {
20483
21676
  return {
20484
21677
  allowed: false,
@@ -20489,7 +21682,7 @@ var PermissionManager = class _PermissionManager {
20489
21682
  return null;
20490
21683
  }
20491
21684
  approvalCacheActive() {
20492
- return this.level !== "bypass";
21685
+ return approvalPolicyForLevel(this.level) === "ask";
20493
21686
  }
20494
21687
  rememberFlag() {
20495
21688
  return this.approvalCacheActive() ? {} : { rememberable: false };
@@ -20497,15 +21690,15 @@ var PermissionManager = class _PermissionManager {
20497
21690
  cachedDecision(operation) {
20498
21691
  const cacheTool = operation.toolName ?? operation.type;
20499
21692
  const cacheKey = cacheKeyFor(operation.target || operation.detail);
20500
- const cached = cacheKey ? this.shared.cache.get(cacheTool, cacheKey) : null;
20501
- 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") {
20502
21695
  return {
20503
21696
  allowed: true,
20504
21697
  reason: t("permission_reason.cached_allow"),
20505
21698
  code: "approval-cache"
20506
21699
  };
20507
21700
  }
20508
- if (cached === "deny") {
21701
+ if (cached2 === "deny") {
20509
21702
  return {
20510
21703
  allowed: false,
20511
21704
  reason: t("permission_reason.cached_deny"),
@@ -20528,8 +21721,8 @@ var PermissionManager = class _PermissionManager {
20528
21721
  if (ruleDecision && !ruleDecision.allowed && !ruleDecision.requireApproval) {
20529
21722
  return ruleDecision;
20530
21723
  }
20531
- const cached = this.approvalCacheActive() ? this.cachedDecision(operation) : null;
20532
- if (cached) return cached;
21724
+ const cached2 = this.approvalCacheActive() ? this.cachedDecision(operation) : null;
21725
+ if (cached2) return cached2;
20533
21726
  const asked = listMatch?.verdict === "ask" ? {
20534
21727
  allowed: false,
20535
21728
  requireApproval: true,
@@ -20812,6 +22005,14 @@ var MIGRATIONS3 = [
20812
22005
  up: (db) => {
20813
22006
  db.exec(`ALTER TABLE schedules ADD COLUMN show_in_session_list INTEGER NOT NULL DEFAULT 0;`);
20814
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
+ }
20815
22016
  }
20816
22017
  ];
20817
22018
  var ScheduleStore = class {
@@ -20842,7 +22043,6 @@ var ScheduleStore = class {
20842
22043
  allowOperations: input.allowOperations ?? [],
20843
22044
  allowRules: input.allowRules ?? [],
20844
22045
  maxTurns: input.maxTurns ?? SCHEDULE_DEFAULTS.maxTurns,
20845
- maxBudgetUsd: input.maxBudgetUsd,
20846
22046
  timeoutMs: input.timeoutMs ?? SCHEDULE_DEFAULTS.timeoutMs,
20847
22047
  ...input.showInSessionList === true ? { showInSessionList: true } : {},
20848
22048
  trigger: normalizeTrigger(input.trigger),
@@ -20868,7 +22068,6 @@ var ScheduleStore = class {
20868
22068
  allowOperations: [...input.allowOperations],
20869
22069
  allowRules: [...input.allowRules],
20870
22070
  maxTurns: input.maxTurns,
20871
- maxBudgetUsd: input.maxBudgetUsd,
20872
22071
  timeoutMs: input.timeoutMs,
20873
22072
  ...input.showInSessionList === true ? { showInSessionList: true } : {},
20874
22073
  trigger: normalizeTrigger(input.trigger),
@@ -20910,14 +22109,14 @@ var ScheduleStore = class {
20910
22109
  `INSERT INTO schedules (
20911
22110
  id, name, enabled, created_at, updated_at, prompt, work_dir, model, role,
20912
22111
  skills, mcp_servers, permission, allow_tools, allow_operations, allow_rules,
20913
- max_turns, max_budget_usd, timeout_ms, show_in_session_list,
22112
+ max_turns, timeout_ms, show_in_session_list,
20914
22113
  trigger_kind, trigger_spec,
20915
22114
  start_date, end_date, os_backend, os_task_id, os_registered_at, os_command,
20916
22115
  last_run_at, last_status
20917
22116
  ) VALUES (
20918
22117
  @id, @name, @enabled, @created_at, @updated_at, @prompt, @work_dir, @model, @role,
20919
22118
  @skills, @mcp_servers, @permission, @allow_tools, @allow_operations, @allow_rules,
20920
- @max_turns, @max_budget_usd, @timeout_ms, @show_in_session_list,
22119
+ @max_turns, @timeout_ms, @show_in_session_list,
20921
22120
  @trigger_kind, @trigger_spec,
20922
22121
  @start_date, @end_date, @os_backend, @os_task_id, @os_registered_at, @os_command,
20923
22122
  @last_run_at, @last_status
@@ -21033,7 +22232,7 @@ var ScheduleStore = class {
21033
22232
  work_dir = @work_dir, model = @model, role = @role, skills = @skills,
21034
22233
  mcp_servers = @mcp_servers, permission = @permission, allow_tools = @allow_tools,
21035
22234
  allow_operations = @allow_operations, allow_rules = @allow_rules,
21036
- max_turns = @max_turns, max_budget_usd = @max_budget_usd, timeout_ms = @timeout_ms,
22235
+ max_turns = @max_turns, timeout_ms = @timeout_ms,
21037
22236
  show_in_session_list = @show_in_session_list,
21038
22237
  trigger_kind = @trigger_kind, trigger_spec = @trigger_spec,
21039
22238
  start_date = @start_date, end_date = @end_date, os_backend = @os_backend,
@@ -21055,7 +22254,6 @@ function mergeSchedulePatch(current, patch) {
21055
22254
  "allowOperations",
21056
22255
  "allowRules",
21057
22256
  "maxTurns",
21058
- "maxBudgetUsd",
21059
22257
  "timeoutMs",
21060
22258
  "showInSessionList",
21061
22259
  "trigger",
@@ -21086,12 +22284,11 @@ function toRow(def) {
21086
22284
  role: def.role ?? null,
21087
22285
  skills: def.skills ? JSON.stringify(def.skills) : null,
21088
22286
  mcp_servers: def.mcpServers ? JSON.stringify(def.mcpServers) : null,
21089
- permission: def.permission,
22287
+ permission: permissionOfRow(def.permission),
21090
22288
  allow_tools: JSON.stringify(def.allowTools),
21091
22289
  allow_operations: JSON.stringify(def.allowOperations),
21092
22290
  allow_rules: JSON.stringify(def.allowRules),
21093
22291
  max_turns: def.maxTurns,
21094
- max_budget_usd: def.maxBudgetUsd,
21095
22292
  timeout_ms: def.timeoutMs,
21096
22293
  show_in_session_list: def.showInSessionList === true ? 1 : 0,
21097
22294
  trigger_kind: def.trigger.kind,
@@ -21106,6 +22303,9 @@ function toRow(def) {
21106
22303
  last_status: def.lastStatus ?? null
21107
22304
  };
21108
22305
  }
22306
+ function permissionOfRow(raw) {
22307
+ return resolvePermissionLevel(raw) ?? SCHEDULE_DEFAULT_PERMISSION;
22308
+ }
21109
22309
  function fromRow(row) {
21110
22310
  return {
21111
22311
  id: row.id,
@@ -21119,12 +22319,11 @@ function fromRow(row) {
21119
22319
  ...row.role !== null ? { role: row.role } : {},
21120
22320
  ...row.skills !== null ? { skills: parseArray2(row.skills) } : {},
21121
22321
  ...row.mcp_servers !== null ? { mcpServers: parseArray2(row.mcp_servers) } : {},
21122
- permission: row.permission,
22322
+ permission: permissionOfRow(row.permission),
21123
22323
  allowTools: parseArray2(row.allow_tools),
21124
22324
  allowOperations: parseArray2(row.allow_operations),
21125
22325
  allowRules: parseArray2(row.allow_rules),
21126
22326
  maxTurns: row.max_turns,
21127
- maxBudgetUsd: row.max_budget_usd,
21128
22327
  timeoutMs: row.timeout_ms,
21129
22328
  ...row.show_in_session_list !== 0 ? { showInSessionList: true } : {},
21130
22329
  trigger: JSON.parse(row.trigger_spec),
@@ -22570,7 +23769,7 @@ function loadLexiconDir(dir, source) {
22570
23769
  });
22571
23770
  continue;
22572
23771
  }
22573
- collect3(content, file, spec, source, out);
23772
+ collect4(content, file, spec, source, out);
22574
23773
  }
22575
23774
  return out;
22576
23775
  }
@@ -22583,7 +23782,7 @@ function classify2(file) {
22583
23782
  }
22584
23783
  return CATEGORY_SET.has(base2) ? { kind: "terms", category: base2 } : null;
22585
23784
  }
22586
- function collect3(content, file, spec, source, out) {
23785
+ function collect4(content, file, spec, source, out) {
22587
23786
  const lines = content.split("\n");
22588
23787
  for (let i = 0; i < lines.length; i++) {
22589
23788
  const line = (lines[i] ?? "").trim();
@@ -22635,6 +23834,18 @@ function resolveComplianceDirs(opts) {
22635
23834
  dirs.push({ dir: projectDir, source: "project" });
22636
23835
  return { dirs, projectSkipped: false };
22637
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
+ }
22638
23849
  function sanitizeEnvironment(env) {
22639
23850
  const sensitivePrefixes = [
22640
23851
  "EPOCH_API_KEY",
@@ -22663,6 +23874,15 @@ function runCommandHook(config, event, workDir) {
22663
23874
  const baseEnv = sanitizeEnvironment(process.env);
22664
23875
  const injectedEnv = injectEpochEnv(event, config.pluginRoot);
22665
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
+ };
22666
23886
  return new Promise((resolve17) => {
22667
23887
  let stdout = "";
22668
23888
  let stderr = "";
@@ -22675,11 +23895,16 @@ function runCommandHook(config, event, workDir) {
22675
23895
  stdout,
22676
23896
  stderr,
22677
23897
  blocked: false,
22678
- rejectReason: t("hook_load.spawn_failed", { message: err.message })
23898
+ rejectReason: t("hook_load.spawn_failed", { message: err.message }),
23899
+ sandbox
22679
23900
  });
22680
23901
  };
22681
23902
  const child = guardSpawn(
22682
- () => 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, {
22683
23908
  shell: true,
22684
23909
  cwd: workDir,
22685
23910
  env: hookEnv,
@@ -22697,7 +23922,8 @@ function runCommandHook(config, event, workDir) {
22697
23922
  stdout,
22698
23923
  stderr,
22699
23924
  blocked: false,
22700
- rejectReason: t("hook_load.timeout")
23925
+ rejectReason: t("hook_load.timeout"),
23926
+ sandbox
22701
23927
  });
22702
23928
  }
22703
23929
  }, timeout);
@@ -22728,13 +23954,21 @@ function runCommandHook(config, event, workDir) {
22728
23954
  }
22729
23955
  } catch {
22730
23956
  }
23957
+ const failure = confinement.confined ? classifyFailure({
23958
+ exitCode,
23959
+ stderr,
23960
+ denialSignatures: confinement.denialSignatures,
23961
+ runnerFailureRules: confinement.runnerFailureRules
23962
+ }) : void 0;
22731
23963
  resolve17({
22732
23964
  exitCode,
22733
23965
  stdout: stdout.trim(),
22734
23966
  stderr: stderr.trim(),
22735
23967
  blocked,
22736
23968
  rejectReason,
22737
- output
23969
+ output,
23970
+ sandbox,
23971
+ ...failure ? { failure } : {}
22738
23972
  });
22739
23973
  });
22740
23974
  child.on("error", (err) => {
@@ -22841,7 +24075,7 @@ var HookManager = class {
22841
24075
  for (const config of entry.configs) {
22842
24076
  try {
22843
24077
  const result = await runCommandHook(config, event, workDir);
22844
- reportIfNeverRan(this.log, type, config, result);
24078
+ reportIfNotRun(this.log, type, config, result);
22845
24079
  if (result.blocked) {
22846
24080
  return { blocked: true, reason: result.rejectReason || t("hook_load.rejected") };
22847
24081
  }
@@ -22875,7 +24109,7 @@ var HookManager = class {
22875
24109
  for (const entry of cmdEntries) {
22876
24110
  if (!matchesHook(entry.matcher, event)) continue;
22877
24111
  for (const config of entry.configs) {
22878
- 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) => {
22879
24113
  this.log("warn", `[HookManager] ${type} \u7684 command hook \u5F02\u6B65\u5F02\u5E38: ${describe12(err)}`, {
22880
24114
  hookType: type,
22881
24115
  command: config.command
@@ -22931,7 +24165,7 @@ var HookManager = class {
22931
24165
  for (const config of entry.configs) {
22932
24166
  try {
22933
24167
  const result = await runCommandHook(config, event, ctx.workDir);
22934
- reportIfNeverRan(this.log, type, config, result);
24168
+ reportIfNotRun(this.log, type, config, result);
22935
24169
  const verdict = verdictOf2(result);
22936
24170
  if (verdict) results.push(verdict);
22937
24171
  } catch (err) {
@@ -22965,12 +24199,25 @@ function verdictOf2(result) {
22965
24199
  }
22966
24200
  return void 0;
22967
24201
  }
22968
- function reportIfNeverRan(log5, type, config, result) {
22969
- 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;
22970
24213
  log5(
22971
24214
  "warn",
22972
- `[HookManager] ${type} \u7684 command hook \u6CA1\u80FD\u6267\u884C\uFF0C\u672C\u6B21\u6309\u653E\u884C\u5904\u7406: ${result.rejectReason ?? "\u672A\u77E5\u539F\u56E0"}`,
22973
- { 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 }
22974
24221
  );
22975
24222
  }
22976
24223
  var MAPPINGS = {
@@ -23137,6 +24384,8 @@ var EVENT_MAP = {
23137
24384
  SessionStart: "session:start",
23138
24385
  SessionEnd: "session:end",
23139
24386
  Stop: "stop",
24387
+ SubagentStart: "subagent:start",
24388
+ SubagentStop: "subagent:stop",
23140
24389
  PostCompact: "compress:after"
23141
24390
  };
23142
24391
  var UNRUNNABLE_EVENTS = {
@@ -23586,7 +24835,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23586
24835
  const dir = join(staging, category, name);
23587
24836
  mkdirSync(dir, { recursive: true });
23588
24837
  copyFileSync(join(source, SKILL_FILE), join(dir, SKILL_FILE));
23589
- let budget2 = MAX_FILES_PER_SKILL - 1;
24838
+ let budget = MAX_FILES_PER_SKILL - 1;
23590
24839
  for (const entry of readEntries2(source, label, issues, lang)) {
23591
24840
  if (entry.name === SKILL_FILE) continue;
23592
24841
  if (!entry.isDir || !SKILL_ASSET_DIRS.includes(entry.name)) {
@@ -23596,7 +24845,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23596
24845
  });
23597
24846
  continue;
23598
24847
  }
23599
- 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);
23600
24849
  }
23601
24850
  skills.push({ name, category, dir });
23602
24851
  }
@@ -23655,8 +24904,8 @@ function placeOf(root, parts, lang) {
23655
24904
  const category = parts.at(-2) ?? "general";
23656
24905
  return { name, category: skillSegmentIssue(category, lang) === void 0 ? category : "general" };
23657
24906
  }
23658
- function copyTree(from, to, label, budget2, issues, lang) {
23659
- let left = budget2;
24907
+ function copyTree(from, to, label, budget, issues, lang) {
24908
+ let left = budget;
23660
24909
  mkdirSync(to, { recursive: true });
23661
24910
  for (const entry of readEntries2(from, label, issues, lang)) {
23662
24911
  if (left <= 0) {
@@ -24202,9 +25451,9 @@ var SkillSystem = class {
24202
25451
  );
24203
25452
  }
24204
25453
  };
24205
- function sameDirs(cached, current) {
24206
- if (cached.length !== current.length) return false;
24207
- 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);
24208
25457
  }
24209
25458
  var MODULE6 = "Skill";
24210
25459
  function resolveSkillDirs(opts) {
@@ -24720,11 +25969,11 @@ function readOne(path, opts, usedBytes) {
24720
25969
  }
24721
25970
  const remaining = MAX_ATTACH_TOTAL_BYTES - usedBytes;
24722
25971
  if (remaining <= 0) return { part: { type: "file", path, omitted: "over-limit", bytes: size } };
24723
- const budget2 = Math.min(MAX_ATTACH_BYTES, remaining);
24724
- if (raw.length <= budget2) {
25972
+ const budget = Math.min(MAX_ATTACH_BYTES, remaining);
25973
+ if (raw.length <= budget) {
24725
25974
  return { part: { type: "file", path, text: raw.toString("utf8"), bytes: size } };
24726
25975
  }
24727
- const { text, truncatedTo } = truncateToBudget(raw, budget2);
25976
+ const { text, truncatedTo } = truncateToBudget(raw, budget);
24728
25977
  return {
24729
25978
  part: { type: "file", path, text, truncatedTo, bytes: size },
24730
25979
  notice: t("mentions.truncated", {
@@ -24938,6 +26187,9 @@ function goalSection(goal) {
24938
26187
  ].join("\n");
24939
26188
  }
24940
26189
  var DEFAULT_THRESHOLD = DEFAULT_COMPRESSION_THRESHOLD;
26190
+ function messageTokens(m) {
26191
+ return estimateTokens(m.content) + (m.reasoning ? estimateTokens(m.reasoning) : 0);
26192
+ }
24941
26193
  var COOLDOWN_TURNS = 5;
24942
26194
  var MAX_FAILURES = 3;
24943
26195
  var DEFAULT_CONTEXT_LENGTH3 = 128e3;
@@ -24968,11 +26220,11 @@ var ContextCompressor = class {
24968
26220
  enabled: opts.enabled !== false
24969
26221
  };
24970
26222
  }
24971
- shouldCompress(promptTokens2, contextLength) {
26223
+ shouldCompress(promptTokens3, contextLength) {
24972
26224
  if (!this.opts.enabled) return false;
24973
26225
  if (this.turnsSinceCompress < COOLDOWN_TURNS) return false;
24974
26226
  if (this.failureCount >= MAX_FAILURES) return false;
24975
- return isContextNearlyFull(promptTokens2, contextLength, this.opts.threshold);
26227
+ return isContextNearlyFull(promptTokens3, contextLength, this.opts.threshold);
24976
26228
  }
24977
26229
  async compress(messages, partial = {}) {
24978
26230
  const config = {
@@ -25014,7 +26266,11 @@ var ContextCompressor = class {
25014
26266
  };
25015
26267
  }
25016
26268
  const nl = "\n";
25017
- 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);
25018
26274
  const summarized = await this.generateSummary(
25019
26275
  middleText,
25020
26276
  config.instruction,
@@ -25071,13 +26327,13 @@ var ContextCompressor = class {
25071
26327
  determineBoundaries(messages, config) {
25072
26328
  const head = messages.slice(0, config.protectFirstN);
25073
26329
  const thresholdTokens = config.threshold * config.contextLength;
25074
- const tailBudget = thresholdTokens * config.targetRatio;
26330
+ const tailBudget = config.force ? 0 : thresholdTokens * config.targetRatio;
25075
26331
  let tailTokens = 0;
25076
26332
  let tailStart = messages.length;
25077
26333
  let userCount = 0;
25078
26334
  for (let i = messages.length - 1; i >= config.protectFirstN; i--) {
25079
26335
  const msg = messages[i];
25080
- tailTokens += estimateTokens(msg.content);
26336
+ tailTokens += messageTokens(msg);
25081
26337
  if (msg.role === "user") userCount++;
25082
26338
  tailStart = i;
25083
26339
  const enoughTokens = tailTokens >= tailBudget;
@@ -25177,7 +26433,7 @@ ${extra}` : withGoal;
25177
26433
  return result;
25178
26434
  }
25179
26435
  estimateTokens(messages) {
25180
- return messages.reduce((sum, m) => sum + estimateTokens(m.content), 0);
26436
+ return messages.reduce((sum, m) => sum + messageTokens(m), 0);
25181
26437
  }
25182
26438
  };
25183
26439
  var FILE_VERSION5 = 1;
@@ -25417,257 +26673,6 @@ function hasExecTierContent(inv) {
25417
26673
  function hasDedicatedExecContent(inv) {
25418
26674
  return inv.hookFiles.includes(EPOCH_HOOKS_RELPATH) || inv.policies > 0;
25419
26675
  }
25420
- function describeIsolation() {
25421
- const backend = detectBackend();
25422
- const os = platform();
25423
- if (backend === "seatbelt") {
25424
- return {
25425
- level: "os",
25426
- backend,
25427
- detail: t("sandbox_isolation.seatbelt"),
25428
- reason: "os-isolated",
25429
- platform: os
25430
- };
25431
- }
25432
- if (backend === "bubblewrap") {
25433
- return {
25434
- level: "os",
25435
- backend,
25436
- detail: t("sandbox_isolation.bubblewrap"),
25437
- reason: "os-isolated",
25438
- platform: os
25439
- };
25440
- }
25441
- const [why, reason9] = os === "darwin" ? [t("sandbox_isolation.no_seatbelt"), "backend-unavailable"] : os === "linux" ? [t("sandbox_isolation.no_bubblewrap"), "backend-missing"] : (
25442
- [t("sandbox_isolation.by_design", { platform: os }), "platform-unsupported"]
25443
- );
25444
- return {
25445
- level: "process",
25446
- backend,
25447
- detail: t("sandbox_isolation.process_only", { why }),
25448
- reason: reason9,
25449
- platform: os
25450
- };
25451
- }
25452
- var SANDBOX_COVERS = ["code_exec", "terminal"];
25453
- var SANDBOX_EXCLUDES = [];
25454
- // src/sandbox/sandbox.ts
25455
- var IS_WINDOWS = platform() === "win32";
25456
- var SANDBOX_ROOT = join(tmpdir(), "epoch-sandbox");
25457
- var DEFAULT_TIMEOUT = 3e4;
25458
- var MAX_OUTPUT = 1e5;
25459
- var MAX_MEMORY = 512;
25460
- var TRUNCATE_HEAD = 2e3;
25461
- var TRUNCATE_TAIL = 2e3;
25462
- function resolveExecutable(name) {
25463
- if (IS_WINDOWS || name.includes("/") || name.includes("\\")) return name;
25464
- for (const dir of (process.env["PATH"] ?? "").split(delimiter)) {
25465
- if (!dir) continue;
25466
- const candidate = join(dir, name);
25467
- if (existsSync(candidate)) return candidate;
25468
- }
25469
- return name;
25470
- }
25471
- var CodeSandbox = class {
25472
- sandboxDir;
25473
- readableDirs;
25474
- constructor(sessionId, workDir) {
25475
- const safeId = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
25476
- this.sandboxDir = join(SANDBOX_ROOT, safeId);
25477
- mkdirSync(this.sandboxDir, { recursive: true });
25478
- this.readableDirs = [];
25479
- if (workDir) {
25480
- const abs = resolve(workDir);
25481
- if (abs !== resolve(homedir()) && abs !== "/") this.readableDirs.push(abs);
25482
- }
25483
- }
25484
- getIsolation() {
25485
- return describeIsolation();
25486
- }
25487
- async execute(req) {
25488
- const start = Date.now();
25489
- const timeout = req.timeout ?? DEFAULT_TIMEOUT;
25490
- const isolation = describeIsolation().level;
25491
- try {
25492
- const spec = req.language === "javascript" ? this.jsSpec(req.code) : this.pySpec(req.code);
25493
- const result = await this.run(spec, {
25494
- timeout,
25495
- allowNetwork: req.allowNetwork === true,
25496
- ...req.signal ? { signal: req.signal } : {}
25497
- });
25498
- return { ...result, isolation };
25499
- } catch (err) {
25500
- const msg = err instanceof Error ? err.message : String(err);
25501
- if (msg.includes("timed out") || msg.includes("killed")) {
25502
- return {
25503
- success: false,
25504
- output: "",
25505
- error: `[\u8D85\u65F6] \u8D85\u8FC7 ${timeout / 1e3} \u79D2`,
25506
- errorType: "timeout",
25507
- duration: Date.now() - start,
25508
- isolation
25509
- };
25510
- }
25511
- return {
25512
- success: false,
25513
- output: "",
25514
- error: `[\u9519\u8BEF] ${msg}`,
25515
- errorType: "runtime",
25516
- duration: Date.now() - start,
25517
- isolation
25518
- };
25519
- }
25520
- }
25521
- destroy() {
25522
- try {
25523
- rmSync(this.sandboxDir, { recursive: true });
25524
- } catch {
25525
- }
25526
- }
25527
- getDir() {
25528
- return this.sandboxDir;
25529
- }
25530
- jsSpec(code) {
25531
- const tmpFile = join(this.sandboxDir, "code.js");
25532
- writeFileSync(tmpFile, code, "utf-8");
25533
- return {
25534
- command: process.execPath,
25535
- args: [`--max-old-space-size=${MAX_MEMORY}`, tmpFile],
25536
- env: EXEC_PATH_AS_NODE_ENV
25537
- };
25538
- }
25539
- pySpec(code) {
25540
- const tmpFile = join(this.sandboxDir, "code.py");
25541
- writeFileSync(tmpFile, code, "utf-8");
25542
- return {
25543
- command: resolveExecutable(IS_WINDOWS ? "python" : "python3"),
25544
- args: ["-I", tmpFile]
25545
- };
25546
- }
25547
- run(spec, opts) {
25548
- const start = Date.now();
25549
- const wrapped = isolate(spec.command, spec.args, {
25550
- writableDirs: [this.sandboxDir, tmpdir()],
25551
- readableDirs: this.readableDirs,
25552
- allowNetwork: opts.allowNetwork,
25553
- cwd: this.sandboxDir
25554
- });
25555
- const { command, args } = wrapped ?? spec;
25556
- const spawnOpts = {
25557
- cwd: this.sandboxDir,
25558
- timeout: opts.timeout,
25559
- env: {
25560
- PATH: process.env["PATH"] ?? "",
25561
- HOME: process.env["HOME"] ?? "",
25562
- TMPDIR: this.sandboxDir,
25563
- LANG: process.env["LANG"] ?? "en_US.UTF-8",
25564
- ...spec.env
25565
- },
25566
- stdio: ["ignore", "pipe", "pipe"],
25567
- ...IS_WINDOWS ? { windowsHide: true } : {}
25568
- };
25569
- return new Promise((resolve17) => {
25570
- let stdout = "";
25571
- let stderr = "";
25572
- let killedForOutput = false;
25573
- let child = null;
25574
- const onAbort = () => {
25575
- child?.kill("SIGKILL");
25576
- };
25577
- const failToStart = (err) => {
25578
- opts.signal?.removeEventListener("abort", onAbort);
25579
- if (err.code === "ENOENT") {
25580
- resolve17({
25581
- success: false,
25582
- output: "",
25583
- error: `[\u9519\u8BEF] \u627E\u4E0D\u5230\u89E3\u91CA\u5668: ${command}`,
25584
- errorType: "compile",
25585
- duration: Date.now() - start
25586
- });
25587
- return;
25588
- }
25589
- resolve17({
25590
- success: false,
25591
- output: "",
25592
- error: `[\u9519\u8BEF] ${err.message}\uFF08command=${command}\uFF0Ccwd=${spawnOpts.cwd ?? ""}\uFF09`,
25593
- errorType: "runtime",
25594
- duration: Date.now() - start
25595
- });
25596
- };
25597
- child = guardSpawn(() => spawn(command, args, spawnOpts), failToStart);
25598
- if (!child) return;
25599
- const proc = child;
25600
- let outDecoder = null;
25601
- let errDecoder = null;
25602
- proc.stdout?.on("data", (c) => {
25603
- if (killedForOutput) return;
25604
- outDecoder ??= createStreamDecoder();
25605
- stdout += outDecoder.write(c);
25606
- if (stdout.length >= MAX_OUTPUT) {
25607
- killedForOutput = true;
25608
- stdout = stdout.slice(0, MAX_OUTPUT);
25609
- proc.kill("SIGKILL");
25610
- }
25611
- });
25612
- proc.stderr?.on("data", (c) => {
25613
- if (stderr.length >= MAX_OUTPUT) return;
25614
- errDecoder ??= createStreamDecoder();
25615
- stderr += errDecoder.write(c);
25616
- });
25617
- opts.signal?.addEventListener("abort", onAbort, { once: true });
25618
- proc.on("error", failToStart);
25619
- proc.on("close", (code, sig) => {
25620
- opts.signal?.removeEventListener("abort", onAbort);
25621
- if (!killedForOutput) stdout += outDecoder?.end() ?? "";
25622
- if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
25623
- const duration = Date.now() - start;
25624
- if (killedForOutput) {
25625
- resolve17({
25626
- success: false,
25627
- output: trunc(stdout, stderr),
25628
- error: `[\u8F93\u51FA\u8D85\u9650] \u8D85\u8FC7 ${MAX_OUTPUT} \u5B57\u8282\uFF0C\u5DF2\u7EC8\u6B62`,
25629
- errorType: "runtime",
25630
- duration
25631
- });
25632
- return;
25633
- }
25634
- if (sig) {
25635
- resolve17({
25636
- success: false,
25637
- output: trunc(stdout, stderr),
25638
- error: opts.signal?.aborted ? "[\u5DF2\u53D6\u6D88]" : "[\u8D85\u65F6] \u6267\u884C\u8D85\u65F6",
25639
- errorType: "timeout",
25640
- duration
25641
- });
25642
- return;
25643
- }
25644
- const output = trunc(stdout, stderr);
25645
- if (code !== 0 && code !== null) {
25646
- const oom = /heap out of memory|allocation failed/i.test(stderr);
25647
- resolve17({
25648
- success: false,
25649
- output,
25650
- error: oom ? `[\u5185\u5B58\u8D85\u9650] \u8D85\u8FC7 ${MAX_MEMORY}MB` : `[\u8FD0\u884C\u65F6\u9519\u8BEF] exit ${code}: ${stderr.slice(0, 200)}`,
25651
- errorType: oom ? "oom" : "runtime",
25652
- duration
25653
- });
25654
- return;
25655
- }
25656
- resolve17({ success: true, output, duration });
25657
- });
25658
- });
25659
- }
25660
- };
25661
- function trunc(stdout, stderr) {
25662
- const nl = "\n";
25663
- let out = stdout;
25664
- if (stderr) out += (out ? nl : "") + stderr;
25665
- if (out.length <= MAX_OUTPUT) return out || "(\u65E0\u8F93\u51FA)";
25666
- const head = out.slice(0, TRUNCATE_HEAD);
25667
- const tail = out.slice(-TRUNCATE_TAIL);
25668
- const skipped = out.length - TRUNCATE_HEAD - TRUNCATE_TAIL;
25669
- return `${head}${nl}...[\u5DF2\u622A\u65AD ${(skipped / 1024).toFixed(1)} KB]...${nl}${tail}`;
25670
- }
25671
26676
  /**
25672
26677
  * @license
25673
26678
  * Copyright 2025 Google LLC
@@ -25713,4 +26718,4 @@ function trunc(stdout, stderr) {
25713
26718
  * `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
25714
26719
  */
25715
26720
 
25716
- 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 };