@epoch-agent/core 0.10.0 → 0.11.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 (3) hide show
  1. package/dist/index.d.ts +359 -3
  2. package/dist/index.js +433 -136
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSy
2
2
  import { homedir, platform, tmpdir, hostname } from 'os';
3
3
  import { join, dirname, resolve, relative, isAbsolute, sep, extname, basename } from 'path';
4
4
  import { execFile, execSync, execFileSync, spawn } from 'child_process';
5
- import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, t, openDatabase, migrate, releaseDatabase, unknownKeyIssues, parseLenient, issueDetails, splitWords, checkpointsDir, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, isInWorkspace, workspacesPath, automationLogsDir, projectComplianceDir, projectSkillsDir, attachmentsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, countCodePoints, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, headCodePoints, tailCodePoints } from '@epoch-agent/infra';
5
+ import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, isInWorkspace, workspacesPath, samePath, lastSessionsPath, automationLogsDir, projectComplianceDir, projectSkillsDir, attachmentsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, countCodePoints, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, headCodePoints, tailCodePoints } from '@epoch-agent/infra';
6
6
  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';
7
7
  import { PROVIDER_TYPES, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, PERMISSION_LEVELS, SHELL_KINDS, ToolExposure, MAX_OPTIONS, MIN_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, SCHEDULE_INTERVAL_MINUTES, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, parseModelRef, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, apiKeyEnvVar, PROVIDER_INFOS, isProviderType, API_KEY_ENV_VARS, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
8
8
  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';
@@ -264,7 +264,7 @@ function createLaunchdBackend(opts = {}) {
264
264
  mkdirSync(input.logDir, { recursive: true });
265
265
  writeFileSync(path, renderPlist(label, input), "utf-8");
266
266
  } catch (err) {
267
- return { ok: false, detail: `plist-write-failed: ${describe6(err)}` };
267
+ return { ok: false, detail: `plist-write-failed: ${describe7(err)}` };
268
268
  }
269
269
  await run("launchctl", ["bootout", `${guiDomain()}/${label}`]);
270
270
  const toggle = await backend.setEnabled(label, input.enabled);
@@ -280,7 +280,7 @@ function createLaunchdBackend(opts = {}) {
280
280
  try {
281
281
  rmSync(plistPath(label), { force: true });
282
282
  } catch (err) {
283
- return { ok: false, detail: `plist-remove-failed: ${describe6(err)}` };
283
+ return { ok: false, detail: `plist-remove-failed: ${describe7(err)}` };
284
284
  }
285
285
  return { ok: true };
286
286
  },
@@ -435,7 +435,7 @@ function detailOf2(what, res) {
435
435
  const msg = (res.stderr || res.stdout).trim();
436
436
  return `${what} exit ${res.code}${msg ? `: ${msg}` : ""}`;
437
437
  }
438
- function describe6(err) {
438
+ function describe7(err) {
439
439
  return err instanceof Error ? err.message : String(err);
440
440
  }
441
441
  var LAUNCHD_LABEL_PREFIX;
@@ -475,10 +475,10 @@ function intervalSlots(everyMinutes) {
475
475
  var runCommand2;
476
476
  var init_backend = __esm({
477
477
  "src/schedule/os/backend.ts"() {
478
- runCommand2 = (file, args) => new Promise((resolve15) => {
478
+ runCommand2 = (file, args) => new Promise((resolve16) => {
479
479
  execFile(file, [...args], { encoding: "buffer", windowsHide: true }, (err, stdout, stderr) => {
480
480
  const code = err && typeof err.code === "number" ? err.code ?? 1 : err ? 1 : 0;
481
- resolve15({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
481
+ resolve16({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
482
482
  });
483
483
  });
484
484
  }
@@ -629,7 +629,7 @@ var ToolRegistry = class {
629
629
  this.addTool(tool, source);
630
630
  }
631
631
  registerTools(tools, source = "builtin") {
632
- for (const t78 of tools) this.addTool(t78, source);
632
+ for (const t79 of tools) this.addTool(t79, source);
633
633
  }
634
634
  addTool(tool, source) {
635
635
  const existing = this.tools.get(tool.name);
@@ -678,14 +678,14 @@ var ToolRegistry = class {
678
678
  return isCodeModeDeferred(tool) ? ToolExposure.Deferred : declared;
679
679
  }
680
680
  exposed() {
681
- return this.list().filter((t78) => this.exposureOf(t78) === ToolExposure.Direct);
681
+ return this.list().filter((t79) => this.exposureOf(t79) === ToolExposure.Direct);
682
682
  }
683
683
  byExposure(exposure) {
684
- return this.list().filter((t78) => this.exposureOf(t78) === exposure);
684
+ return this.list().filter((t79) => this.exposureOf(t79) === exposure);
685
685
  }
686
686
  codeModeDeclared() {
687
687
  if (!this.codeMode) return [];
688
- return this.list().filter((t78) => isCodeModeDeferred(t78));
688
+ return this.list().filter((t79) => isCodeModeDeferred(t79));
689
689
  }
690
690
  sourceOf(name) {
691
691
  return this.tools.get(name)?.source;
@@ -734,7 +734,7 @@ var ToolRegistry = class {
734
734
  function staticToolProvider(tools) {
735
735
  return {
736
736
  list: () => tools,
737
- get: (name) => tools.find((t78) => t78.name === name)
737
+ get: (name) => tools.find((t79) => t79.name === name)
738
738
  };
739
739
  }
740
740
  var log = createLogger("tokenizer");
@@ -1146,7 +1146,7 @@ var DelegateManager = class {
1146
1146
  }
1147
1147
  normalizeTasks(args) {
1148
1148
  if (args.tasks && args.tasks.length > 0) {
1149
- return args.role ? args.tasks.map((t78) => ({ ...t78, role: t78.role ?? args.role })) : args.tasks;
1149
+ return args.role ? args.tasks.map((t79) => ({ ...t79, role: t79.role ?? args.role })) : args.tasks;
1150
1150
  }
1151
1151
  if (args.goal) return [{ goal: args.goal, ...args.role ? { role: args.role } : {} }];
1152
1152
  return [];
@@ -1156,7 +1156,7 @@ var DelegateManager = class {
1156
1156
  for (let i = 0; i < tasks.length; i += maxConcurrent) {
1157
1157
  const batch = tasks.slice(i, i + maxConcurrent);
1158
1158
  const batchResults = await Promise.allSettled(
1159
- batch.map((t78) => this.runSingleTask(t78, parentSessionId))
1159
+ batch.map((t79) => this.runSingleTask(t79, parentSessionId))
1160
1160
  );
1161
1161
  for (const r of batchResults) {
1162
1162
  if (r.status === "fulfilled") {
@@ -1297,21 +1297,21 @@ function createDelegateTool(runner, config, roles) {
1297
1297
  operation: "command",
1298
1298
  describeTarget: (args) => {
1299
1299
  const tasks = Array.isArray(args["tasks"]) ? args["tasks"] : [];
1300
- const goals = tasks.map((t78) => String(t78["goal"] ?? ""));
1300
+ const goals = tasks.map((t79) => String(t79["goal"] ?? ""));
1301
1301
  const single = typeof args["goal"] === "string" ? args["goal"] : "";
1302
1302
  return [...goals, single].filter(Boolean).join(" | ");
1303
1303
  },
1304
1304
  async execute(args, ctx) {
1305
1305
  const start = Date.now();
1306
1306
  const rawTasks = Array.isArray(args["tasks"]) ? args["tasks"] : void 0;
1307
- const tasks = rawTasks?.map((t78) => {
1308
- const o = t78;
1307
+ const tasks = rawTasks?.map((t79) => {
1308
+ const o = t79;
1309
1309
  return {
1310
1310
  goal: String(o["goal"] ?? "").trim(),
1311
1311
  context: String(o["context"] ?? ""),
1312
1312
  ...typeof o["role"] === "string" && o["role"].trim() ? { role: o["role"].trim() } : {}
1313
1313
  };
1314
- }).filter((t78) => t78.goal.length > 0);
1314
+ }).filter((t79) => t79.goal.length > 0);
1315
1315
  const goal = typeof args["goal"] === "string" ? args["goal"].trim() : "";
1316
1316
  const topRole = typeof args["role"] === "string" ? args["role"].trim() : "";
1317
1317
  if ((!tasks || tasks.length === 0) && !goal) {
@@ -1327,7 +1327,7 @@ function createDelegateTool(runner, config, roles) {
1327
1327
  };
1328
1328
  }
1329
1329
  const known = listRoles();
1330
- const requested = [...(tasks ?? []).map((t78) => t78.role), topRole].filter(
1330
+ const requested = [...(tasks ?? []).map((t79) => t79.role), topRole].filter(
1331
1331
  (r) => Boolean(r)
1332
1332
  );
1333
1333
  const unknown = [...new Set(requested)].filter((r) => !known.some((k) => k.name === r));
@@ -1500,8 +1500,8 @@ function createGoalTool(deps) {
1500
1500
  }
1501
1501
  var VALID_TARGETS = ["memory", "user"];
1502
1502
  var VALID_ACTIONS = ["add", "replace", "remove", "search", "read", "budget"];
1503
- function isValidTarget(t78) {
1504
- return typeof t78 === "string" && VALID_TARGETS.includes(t78);
1503
+ function isValidTarget(t79) {
1504
+ return typeof t79 === "string" && VALID_TARGETS.includes(t79);
1505
1505
  }
1506
1506
  function isValidAction(a) {
1507
1507
  return VALID_ACTIONS.includes(a);
@@ -1755,17 +1755,25 @@ async function runCode(input) {
1755
1755
  resourceLimits: { maxOldGenerationSizeMb: limits.maxMemoryMb }
1756
1756
  });
1757
1757
  let printed = "";
1758
- const collect3 = (chunk) => {
1759
- if (printed.length >= limits.maxPrintChars) return;
1760
- printed = (printed + String(chunk)).slice(0, limits.maxPrintChars);
1758
+ const take = (text) => {
1759
+ if (!text || printed.length >= limits.maxPrintChars) return;
1760
+ printed = (printed + text).slice(0, limits.maxPrintChars);
1761
+ };
1762
+ const attach = (stream) => {
1763
+ let decoder = null;
1764
+ stream.on("data", (chunk) => {
1765
+ decoder ??= createStreamDecoder();
1766
+ take(decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
1767
+ });
1768
+ stream.on("end", () => take(decoder?.end() ?? ""));
1761
1769
  };
1762
- worker.stdout.on("data", collect3);
1763
- worker.stderr.on("data", collect3);
1770
+ attach(worker.stdout);
1771
+ attach(worker.stderr);
1764
1772
  const drained = Promise.all([streamEnd(worker.stdout), streamEnd(worker.stderr)]);
1765
1773
  let toolCalls = 0;
1766
1774
  let capped2 = false;
1767
1775
  let ending = null;
1768
- return await new Promise((resolve15) => {
1776
+ return await new Promise((resolve16) => {
1769
1777
  let settled = false;
1770
1778
  const finish = () => {
1771
1779
  if (settled) return;
@@ -1774,7 +1782,7 @@ async function runCode(input) {
1774
1782
  clearInterval(abortWatch);
1775
1783
  void worker.terminate().catch(() => {
1776
1784
  }).then(() => Promise.race([drained, delay(STREAM_DRAIN_MS)])).then(() => {
1777
- resolve15(toOutcome(ending, { printed, toolCalls, capped: capped2, limits}));
1785
+ resolve16(toOutcome(ending, { printed, toolCalls, capped: capped2, limits}));
1778
1786
  });
1779
1787
  };
1780
1788
  const timer = setTimeout(() => {
@@ -1845,14 +1853,14 @@ async function runCode(input) {
1845
1853
  var ABORT_POLL_MS = 100;
1846
1854
  var STREAM_DRAIN_MS = 500;
1847
1855
  function streamEnd(stream) {
1848
- return new Promise((resolve15) => {
1849
- stream.once("end", () => resolve15());
1850
- stream.once("close", () => resolve15());
1856
+ return new Promise((resolve16) => {
1857
+ stream.once("end", () => resolve16());
1858
+ stream.once("close", () => resolve16());
1851
1859
  });
1852
1860
  }
1853
1861
  function delay(ms) {
1854
- return new Promise((resolve15) => {
1855
- setTimeout(resolve15, ms).unref();
1862
+ return new Promise((resolve16) => {
1863
+ setTimeout(resolve16, ms).unref();
1856
1864
  });
1857
1865
  }
1858
1866
  function timeoutMessage(timeoutMs, toolCalls) {
@@ -4197,6 +4205,7 @@ var SkillFrontmatterSchema = z.object({
4197
4205
  tags: looseStringList.optional().catch(void 0)
4198
4206
  });
4199
4207
  var FRONTMATTER_KEYS = Object.keys(SkillFrontmatterSchema.shape);
4208
+ var METADATA_KEYS = ["tags"];
4200
4209
  function parseSkillFrontmatter(raw, label) {
4201
4210
  const parsed = parseFrontmatter(raw);
4202
4211
  if ("error" in parsed) {
@@ -4224,7 +4233,13 @@ function parseSkillFrontmatter(raw, label) {
4224
4233
  const raw_ = isRecord3(data) ? data : {};
4225
4234
  for (const key of Object.keys(raw_)) {
4226
4235
  if (!FRONTMATTER_KEYS.includes(key)) {
4227
- issues.push({ path: `${label}.${key}`, message: t("skill_load.unknown_key", { key }) });
4236
+ const suggest = suggestKey(key, FRONTMATTER_KEYS);
4237
+ if (suggest !== void 0) {
4238
+ issues.push({
4239
+ path: `${label}.${key}`,
4240
+ message: t("infra_misc.schema_misspelled_key", { suggest })
4241
+ });
4242
+ }
4228
4243
  continue;
4229
4244
  }
4230
4245
  if (raw_[key] !== void 0 && result.data[key] === void 0) {
@@ -4240,6 +4255,7 @@ function parseSkillFrontmatter(raw, label) {
4240
4255
  });
4241
4256
  }
4242
4257
  }
4258
+ issues.push(...misspelledKeyIssues(METADATA_KEYS, raw_.metadata, `${label}.metadata`));
4243
4259
  const fm = {};
4244
4260
  if (result.data.name !== void 0) fm.name = result.data.name;
4245
4261
  if (result.data.description !== void 0) fm.description = result.data.description;
@@ -4491,7 +4507,7 @@ function available(tools) {
4491
4507
  if (tools.length === 0) {
4492
4508
  return "No deferred tools in this session \u2014 every tool is already in your tool list.";
4493
4509
  }
4494
- const names = tools.map((t78) => t78.name).sort();
4510
+ const names = tools.map((t79) => t79.name).sort();
4495
4511
  const shown = names.slice(0, MAX_LISTED);
4496
4512
  const rest = names.length - shown.length;
4497
4513
  return `Searchable tools: ${shown.join(", ")}${rest > 0 ? ` (\u2026and ${rest} more)` : ""}.`;
@@ -4611,15 +4627,15 @@ var TrackerDB = class {
4611
4627
  );
4612
4628
  this.db.transaction(() => {
4613
4629
  this.db.prepare("DELETE FROM todos WHERE session_id = ?").run(sessionId);
4614
- for (const t78 of todos) {
4630
+ for (const t79 of todos) {
4615
4631
  insertStmt.run({
4616
- id: t78.id,
4632
+ id: t79.id,
4617
4633
  session_id: sessionId,
4618
- content: t78.content,
4619
- status: t78.status,
4620
- reason: t78.reason || null,
4621
- created_at: t78.created_at,
4622
- updated_at: t78.updated_at
4634
+ content: t79.content,
4635
+ status: t79.status,
4636
+ reason: t79.reason || null,
4637
+ created_at: t79.created_at,
4638
+ updated_at: t79.updated_at
4623
4639
  });
4624
4640
  }
4625
4641
  })();
@@ -4641,9 +4657,9 @@ function formatTodoList(todos, pendingCount) {
4641
4657
  cancelled: "\u5DF2\u53D6\u6D88"
4642
4658
  };
4643
4659
  lines.push(`[todo] \u72B6\u6001\u6C47\u603B: ${pendingCount} \u4E2A\u5F85\u5B8C\u6210`);
4644
- for (const t78 of todos) {
4645
- const label = statusLabel[t78.status] || t78.status;
4646
- lines.push(`- [${label}] ${t78.content}${t78.reason ? ` \u2014\u2014 ${t78.reason}` : ""}`);
4660
+ for (const t79 of todos) {
4661
+ const label = statusLabel[t79.status] || t79.status;
4662
+ lines.push(`- [${label}] ${t79.content}${t79.reason ? ` \u2014\u2014 ${t79.reason}` : ""}`);
4647
4663
  }
4648
4664
  return lines.join("\n");
4649
4665
  }
@@ -4717,14 +4733,14 @@ function createTodoTool(db) {
4717
4733
  };
4718
4734
  }
4719
4735
  const now = (/* @__PURE__ */ new Date()).toISOString();
4720
- const existing = new Map(db.listBySession(ctx.sessionId).map((t78) => [t78.id, t78]));
4721
- const todos = rawTodos.map((t78) => {
4722
- const prev = t78.id ? existing.get(t78.id) : void 0;
4723
- const status = isValidStatus(t78.status) ? t78.status : "pending";
4724
- const reason9 = normalizeReason(status, t78.reason);
4736
+ const existing = new Map(db.listBySession(ctx.sessionId).map((t79) => [t79.id, t79]));
4737
+ const todos = rawTodos.map((t79) => {
4738
+ const prev = t79.id ? existing.get(t79.id) : void 0;
4739
+ const status = isValidStatus(t79.status) ? t79.status : "pending";
4740
+ const reason9 = normalizeReason(status, t79.reason);
4725
4741
  return {
4726
- id: t78.id || generateShortId(),
4727
- content: String(t78.content || "").slice(0, 500),
4742
+ id: t79.id || generateShortId(),
4743
+ content: String(t79.content || "").slice(0, 500),
4728
4744
  status,
4729
4745
  ...reason9 ? { reason: reason9 } : {},
4730
4746
  created_at: prev?.created_at || now,
@@ -4732,7 +4748,7 @@ function createTodoTool(db) {
4732
4748
  };
4733
4749
  });
4734
4750
  db.replaceAll(ctx.sessionId, todos);
4735
- const pendingCount = todos.filter((t78) => isOpenTodo(t78.status)).length;
4751
+ const pendingCount = todos.filter((t79) => isOpenTodo(t79.status)).length;
4736
4752
  return {
4737
4753
  success: true,
4738
4754
  output: formatTodoList(todos, pendingCount),
@@ -4881,7 +4897,7 @@ function parseRoleDefinition(raw, file, source) {
4881
4897
  };
4882
4898
  }
4883
4899
  const parsed = split.data;
4884
- const issues = unknownKeyIssues(FRONTMATTER_KEYS2, parsed, file);
4900
+ const issues = misspelledKeyIssues(FRONTMATTER_KEYS2, parsed, file);
4885
4901
  const outcome = parseLenient(FrontmatterSchema.optional(), parsed, void 0);
4886
4902
  if (!outcome.value) {
4887
4903
  issues.push(
@@ -5067,7 +5083,7 @@ function intersectNames(base, incoming) {
5067
5083
  if (!base) return incoming ? [...incoming] : void 0;
5068
5084
  if (!incoming) return [...base];
5069
5085
  const baseSet = new Set(base);
5070
- return incoming.filter((t78) => baseSet.has(t78));
5086
+ return incoming.filter((t79) => baseSet.has(t79));
5071
5087
  }
5072
5088
  function findRole(roles, name) {
5073
5089
  return roles.find((r) => r.name === name);
@@ -5153,7 +5169,7 @@ function roleScopedProvider(base, role, blocked) {
5153
5169
  return allow ? allow.has(name) : true;
5154
5170
  };
5155
5171
  return {
5156
- list: () => base.list().filter((t78) => permitted(t78.name)),
5172
+ list: () => base.list().filter((t79) => permitted(t79.name)),
5157
5173
  get: (name) => permitted(name) ? base.get(name) : void 0
5158
5174
  };
5159
5175
  }
@@ -5180,7 +5196,7 @@ function createRoleScope(base, sessionRole, blocked) {
5180
5196
  }
5181
5197
  function unknownRoleTools(base, role, blocked) {
5182
5198
  if (!role?.tools) return [];
5183
- const available2 = new Set(base.list().map((t78) => t78.name));
5199
+ const available2 = new Set(base.list().map((t79) => t79.name));
5184
5200
  const blockedSet = new Set(blocked);
5185
5201
  return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
5186
5202
  }
@@ -5300,7 +5316,7 @@ function loadCommandFile(file, source, prefix) {
5300
5316
  ]
5301
5317
  };
5302
5318
  }
5303
- const issues = unknownKeyIssues(FRONTMATTER_KEYS3, parsed.data, at);
5319
+ const issues = misspelledKeyIssues(FRONTMATTER_KEYS3, parsed.data, at);
5304
5320
  const checked = parseLenient(CommandFrontmatterSchema.optional(), parsed.data, void 0);
5305
5321
  if (!checked.value) {
5306
5322
  issues.push(
@@ -5516,7 +5532,7 @@ function createToolScope(base) {
5516
5532
  const permitted = (name) => allow ? allow.has(name) : true;
5517
5533
  return {
5518
5534
  provider: {
5519
- list: () => base.list().filter((t78) => permitted(t78.name)),
5535
+ list: () => base.list().filter((t79) => permitted(t79.name)),
5520
5536
  get: (name) => permitted(name) ? base.get(name) : void 0
5521
5537
  },
5522
5538
  enter: (next) => {
@@ -5686,7 +5702,7 @@ function scanHooks(dir, issues) {
5686
5702
  return [...parsed.entries].map(([type, list]) => ({
5687
5703
  type,
5688
5704
  count: list.reduce((sum, entry) => sum + entry.hooks.length, 0)
5689
- })).filter((t78) => t78.count > 0).sort((a, b) => a.type.localeCompare(b.type));
5705
+ })).filter((t79) => t79.count > 0).sort((a, b) => a.type.localeCompare(b.type));
5690
5706
  }
5691
5707
  function scanMcp(dir, plugin, issues) {
5692
5708
  const path = join(dir, PLUGIN_LAYOUT.mcp);
@@ -5773,6 +5789,7 @@ var PluginAuthorSchema = z.object({
5773
5789
  email: z.string().min(1).optional(),
5774
5790
  url: z.string().min(1).optional()
5775
5791
  });
5792
+ var PLUGIN_VISIBILITY = ["private", "public"];
5776
5793
  var ManifestSchema = z.object({
5777
5794
  name: z.string().min(1),
5778
5795
  version: z.string().min(1),
@@ -5780,9 +5797,14 @@ var ManifestSchema = z.object({
5780
5797
  author: PluginAuthorSchema.optional(),
5781
5798
  epochVersion: z.string().min(1).optional(),
5782
5799
  homepage: z.string().min(1).optional(),
5783
- keywords: z.array(z.string().min(1)).optional()
5800
+ keywords: z.array(z.string().min(1)).optional(),
5801
+ displayName: z.string().min(1).optional(),
5802
+ category: z.string().min(1).optional(),
5803
+ icon: z.string().min(1).optional(),
5804
+ changelog: z.string().min(1).optional(),
5805
+ visibility: z.enum(PLUGIN_VISIBILITY).optional().catch(void 0)
5784
5806
  });
5785
- var MANIFEST_KEYS = Object.keys(ManifestSchema.shape);
5807
+ var PLUGIN_MANIFEST_KEYS = Object.keys(ManifestSchema.shape);
5786
5808
  function readPluginManifest(dir) {
5787
5809
  const path = join(dir, PLUGIN_MANIFEST_FILE);
5788
5810
  let raw;
@@ -5807,7 +5829,17 @@ function readPluginManifest(dir) {
5807
5829
  issues: [{ path, message: t("plugin_manifest.bad_json", { reason: firstLine2(err) }) }]
5808
5830
  };
5809
5831
  }
5810
- const issues = unknownKeyIssues(MANIFEST_KEYS, parsed, PLUGIN_MANIFEST_FILE);
5832
+ const issues = unknownKeyIssues(PLUGIN_MANIFEST_KEYS, parsed, PLUGIN_MANIFEST_FILE);
5833
+ const rawVisibility = parsed?.visibility;
5834
+ if (rawVisibility !== void 0 && !PLUGIN_VISIBILITY.includes(rawVisibility)) {
5835
+ issues.push({
5836
+ path: `${path}.visibility`,
5837
+ message: t("plugin_manifest.bad_visibility", {
5838
+ raw: String(rawVisibility),
5839
+ allowed: PLUGIN_VISIBILITY.join(" / ")
5840
+ })
5841
+ });
5842
+ }
5811
5843
  const checked = parseLenient(ManifestSchema.optional(), parsed, void 0);
5812
5844
  if (!checked.value) {
5813
5845
  issues.push(
@@ -6087,7 +6119,7 @@ var PluginStore = class {
6087
6119
  }
6088
6120
  };
6089
6121
  function sleep(ms) {
6090
- return new Promise((resolve15) => setTimeout(resolve15, ms));
6122
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
6091
6123
  }
6092
6124
  // src/plugin/loader.ts
6093
6125
  var MODULE = "Plugin";
@@ -6892,7 +6924,7 @@ var EntrySchema = z.object({
6892
6924
  changelog: z.string().min(1).optional(),
6893
6925
  size: z.number().int().positive().optional(),
6894
6926
  homepage: z.string().min(1).optional(),
6895
- visibility: z.enum(["private", "public"]).optional().catch(void 0),
6927
+ visibility: z.enum(PLUGIN_VISIBILITY).optional().catch(void 0),
6896
6928
  status: z.enum(["listed", "reviewing", "rejected", "offline"]).optional().catch(void 0)
6897
6929
  });
6898
6930
  var MARKETPLACE_DISPLAY_FIELDS = [
@@ -7395,14 +7427,14 @@ var CheckpointStore = class {
7395
7427
  }
7396
7428
  async enforceTotalSize(protectSession, protectTurnIndex) {
7397
7429
  const turns = await this.collectTurns();
7398
- let total = turns.reduce((sum, t78) => sum + t78.bytes, 0);
7430
+ let total = turns.reduce((sum, t79) => sum + t79.bytes, 0);
7399
7431
  if (total <= this.maxTotalBytes) return;
7400
7432
  turns.sort((a, b) => a.createdAt - b.createdAt);
7401
- for (const t78 of turns) {
7433
+ for (const t79 of turns) {
7402
7434
  if (total <= this.maxTotalBytes) return;
7403
- if (t78.sessionId === protectSession && t78.turnIndex === protectTurnIndex) continue;
7404
- await this.removeTurn(t78.sessionId, t78.turnIndex);
7405
- total -= t78.bytes;
7435
+ if (t79.sessionId === protectSession && t79.turnIndex === protectTurnIndex) continue;
7436
+ await this.removeTurn(t79.sessionId, t79.turnIndex);
7437
+ total -= t79.bytes;
7406
7438
  }
7407
7439
  }
7408
7440
  async collectTurns() {
@@ -8108,8 +8140,8 @@ function sanitizeToolOutput(text) {
8108
8140
  if (!stripped.includes("\r")) return stripped;
8109
8141
  return stripped.split("\n").map((line) => line.includes("\r") ? line.slice(line.lastIndexOf("\r") + 1) : line).join("\n");
8110
8142
  }
8111
- var sleep2 = (ms) => new Promise((resolve15) => {
8112
- setTimeout(resolve15, ms);
8143
+ var sleep2 = (ms) => new Promise((resolve16) => {
8144
+ setTimeout(resolve16, ms);
8113
8145
  });
8114
8146
  async function* pumpToolOutput(target, start, options = {}) {
8115
8147
  const flushMs = options.flushMs ?? FLUSH_MS;
@@ -8177,8 +8209,8 @@ async function* pumpToolOutput(target, start, options = {}) {
8177
8209
  continue;
8178
8210
  }
8179
8211
  if (!running) break;
8180
- await new Promise((resolve15) => {
8181
- wake = resolve15;
8212
+ await new Promise((resolve16) => {
8213
+ wake = resolve16;
8182
8214
  });
8183
8215
  if (running && flushMs > 0) await sleep2(flushMs);
8184
8216
  }
@@ -10115,11 +10147,11 @@ var PromptBuilder = class {
10115
10147
  memoryInitialized = false;
10116
10148
  projectBlock(tools) {
10117
10149
  const permLevel = this.deps.permissionManager?.getLevel() || "default";
10118
- const hasPlanTool = tools.some((t78) => t78.name === "enter_plan_mode");
10119
- const hasLspTool = tools.some((t78) => t78.name === "lsp_diagnostics");
10120
- const hasAskTool = tools.some((t78) => t78.name === "ask_user_question");
10121
- const hasTodoTool = tools.some((t78) => t78.name === "todo");
10122
- const hasTerminal = tools.some((t78) => t78.name === "terminal");
10150
+ const hasPlanTool = tools.some((t79) => t79.name === "enter_plan_mode");
10151
+ const hasLspTool = tools.some((t79) => t79.name === "lsp_diagnostics");
10152
+ const hasAskTool = tools.some((t79) => t79.name === "ask_user_question");
10153
+ const hasTodoTool = tools.some((t79) => t79.name === "todo");
10154
+ const hasTerminal = tools.some((t79) => t79.name === "terminal");
10123
10155
  const needsCommandApproval = hasTerminal && permLevel === "default";
10124
10156
  const plan = this.deps.approvedPlan?.()?.trim();
10125
10157
  const codeSdk = renderSdkDeclaration(this.deps.codeModeTools?.() ?? []);
@@ -10998,8 +11030,8 @@ function imageParts(content) {
10998
11030
  return typeof content === "string" ? [] : content.filter((p) => p.type === "image");
10999
11031
  }
11000
11032
  function toolSignatureOf(config, tools) {
11001
- const direct = tools.map((t78) => t78.name).join("\0");
11002
- const declared = (config.codeModeTools?.() ?? []).map((t78) => t78.name).join("\0");
11033
+ const direct = tools.map((t79) => t79.name).join("\0");
11034
+ const declared = (config.codeModeTools?.() ?? []).map((t79) => t79.name).join("\0");
11003
11035
  return declared ? `${direct}|${declared}` : direct;
11004
11036
  }
11005
11037
  function modelSignatureOf(info) {
@@ -11793,7 +11825,12 @@ ${finalText}` };
11793
11825
  "epoch.summary_skipped": compressed.summarySkipped === true,
11794
11826
  "epoch.pruned_tokens": compressed.prunedTokens
11795
11827
  });
11796
- return { messages: compressed.messages.map((m) => unflattenMessage(m)), ...bill };
11828
+ return {
11829
+ messages: compressed.messages.map((m) => unflattenMessage(m)),
11830
+ ...bill,
11831
+ ...compressed.carriedHead === void 0 ? {} : { carriedHead: compressed.carriedHead },
11832
+ ...compressed.carriedTail === void 0 ? {} : { carriedTail: compressed.carriedTail }
11833
+ };
11797
11834
  } catch {
11798
11835
  return { messages };
11799
11836
  }
@@ -11947,6 +11984,11 @@ var ArtifactsSchema = z.object({
11947
11984
  maxAgeDays: z.int().positive().max(3650).default(ARTIFACT_RETENTION_DEFAULTS.maxAgeDays),
11948
11985
  maxTotalBytes: z.int().positive().default(ARTIFACT_RETENTION_DEFAULTS.maxTotalBytes)
11949
11986
  });
11987
+ var SessionsSchema = z.object({
11988
+ autoPrune: z.boolean().default(false),
11989
+ retentionDays: z.int().positive().max(3650).default(90),
11990
+ vacuumAfterPrune: z.boolean().default(true)
11991
+ });
11950
11992
  var TelemetrySchema = z.object({
11951
11993
  enabled: z.boolean().optional(),
11952
11994
  target: z.enum(["file", "console", "otlp"]).optional(),
@@ -11983,6 +12025,7 @@ var EpochConfigSchema = z.object({
11983
12025
  compression: CompressionSchema.prefault({}),
11984
12026
  memory: MemorySchema.prefault({}),
11985
12027
  artifacts: ArtifactsSchema.prefault({}),
12028
+ sessions: SessionsSchema.prefault({}),
11986
12029
  budget: BudgetSchema.optional(),
11987
12030
  compliance: ComplianceSchema.optional(),
11988
12031
  headless: HeadlessSchema.optional(),
@@ -12468,7 +12511,11 @@ function parseRawConfig(raw, issues, track) {
12468
12511
  const migrated = migrateRawConfig(expanded);
12469
12512
  issues.push(...migrated.issues);
12470
12513
  issues.push(...unknownKeyIssues(CONFIG_KEYS, migrated.raw));
12471
- const outcome = parseLenient(EpochConfigSchema, dropEmptySections(migrated.raw), defaultConfig());
12514
+ const outcome = parseLenient(
12515
+ EpochConfigSchema,
12516
+ dropEmptySections(migrated.raw),
12517
+ EpochConfigSchema.parse({})
12518
+ );
12472
12519
  issues.push(...outcome.issues);
12473
12520
  if (outcome.issues.length === 0) collectKeyPaths(migrated.raw, track.file);
12474
12521
  return outcome.value;
@@ -13078,6 +13125,7 @@ function getCapability(modelId, homeDir, provider) {
13078
13125
  }
13079
13126
  return {
13080
13127
  contextLength: DEFAULT_CONTEXT_LENGTH2,
13128
+ contextLengthUnknown: true,
13081
13129
  maxOutputTokens: 4096,
13082
13130
  supportsTools: true,
13083
13131
  ...images,
@@ -14034,6 +14082,9 @@ var SETTING_WRITE_KEYS = [
14034
14082
  "budget.maxTokens",
14035
14083
  "budget.warnAtPercent",
14036
14084
  "budget.onUnknownPricing",
14085
+ "sessions.autoPrune",
14086
+ "sessions.retentionDays",
14087
+ "sessions.vacuumAfterPrune",
14037
14088
  "permission"
14038
14089
  ];
14039
14090
  function isWritableSettingKey(value) {
@@ -14097,6 +14148,9 @@ var VALUE_SCHEMA = {
14097
14148
  "budget.maxTokens": EpochConfigSchema.shape.budget.unwrap().shape.maxTokens.unwrap(),
14098
14149
  "budget.warnAtPercent": EpochConfigSchema.shape.budget.unwrap().shape.warnAtPercent.unwrap(),
14099
14150
  "budget.onUnknownPricing": EpochConfigSchema.shape.budget.unwrap().shape.onUnknownPricing.unwrap(),
14151
+ "sessions.autoPrune": EpochConfigSchema.shape.sessions.unwrap().shape.autoPrune.unwrap(),
14152
+ "sessions.retentionDays": EpochConfigSchema.shape.sessions.unwrap().shape.retentionDays.unwrap(),
14153
+ "sessions.vacuumAfterPrune": EpochConfigSchema.shape.sessions.unwrap().shape.vacuumAfterPrune.unwrap(),
14100
14154
  permission: EpochConfigSchema.shape.permission.unwrap()
14101
14155
  };
14102
14156
  function settingValueKind(key) {
@@ -14505,11 +14559,11 @@ function buildSearchQuery(query) {
14505
14559
  }
14506
14560
  const latinPart = cleaned.replace(CJK_RUN, " ");
14507
14561
  for (const w of latinPart.split(/\s+/)) {
14508
- const t78 = w.trim();
14509
- if (t78.length >= 2) terms.add(t78);
14562
+ const t79 = w.trim();
14563
+ if (t79.length >= 2) terms.add(t79);
14510
14564
  }
14511
14565
  if (terms.size === 0) return null;
14512
- return [...terms].map((t78) => `"${t78.replace(/"/g, "")}"`).join(" OR ");
14566
+ return [...terms].map((t79) => `"${t79.replace(/"/g, "")}"`).join(" OR ");
14513
14567
  }
14514
14568
  // src/memory/db.ts
14515
14569
  var NS2 = "memory";
@@ -15838,6 +15892,19 @@ var SESSION_MIGRATIONS = [
15838
15892
  up: (db) => {
15839
15893
  db.exec(`ALTER TABLE sessions ADD COLUMN priced_call_count INTEGER NOT NULL DEFAULT 0;`);
15840
15894
  }
15895
+ },
15896
+ {
15897
+ id: "session-meta-kv",
15898
+ version: 11,
15899
+ description: "\u7EF4\u62A4\u7528\u7684 key/value \u8868\uFF08\u81EA\u52A8\u6E05\u7406\u7684\u8282\u6D41\u65F6\u523B\uFF0C2026-09-15\uFF09",
15900
+ up: (db) => {
15901
+ db.exec(`
15902
+ CREATE TABLE IF NOT EXISTS session_meta (
15903
+ key TEXT PRIMARY KEY,
15904
+ value TEXT NOT NULL
15905
+ );
15906
+ `);
15907
+ }
15841
15908
  }
15842
15909
  ];
15843
15910
  var FTS_EXTERNAL_SCHEMA = `
@@ -15888,7 +15955,7 @@ var FTS_EXTERNAL_SCHEMA = `
15888
15955
  var FTS_EXTERNAL_MIGRATIONS = [
15889
15956
  {
15890
15957
  id: "fts-external-content",
15891
- version: 10,
15958
+ version: 12,
15892
15959
  description: "\u4E24\u5F20 FTS \u8868\u6539\u6210\u5916\u90E8\u5185\u5BB9\u8868\uFF08\u6B63\u6587\u4E0D\u518D\u5B58\u4E09\u4EFD\uFF0C2026-08-21\uFF09",
15893
15960
  up: (db) => {
15894
15961
  db.exec(FTS_EXTERNAL_SCHEMA);
@@ -16191,6 +16258,46 @@ var SessionDB = class {
16191
16258
  }
16192
16259
  return { results };
16193
16260
  }
16261
+ pruneCandidates(input) {
16262
+ const clauses = ["ended_at IS NOT NULL", "updated_at < @cutoff"];
16263
+ const params = { cutoff: input.olderThanMs };
16264
+ if (typeof input.archived === "boolean") {
16265
+ clauses.push("archived = @archived");
16266
+ params["archived"] = input.archived ? 1 : 0;
16267
+ }
16268
+ if (!input.includePinned) clauses.push("COALESCE(pinned, 0) = 0");
16269
+ const rows = this.db.prepare(`SELECT * FROM sessions WHERE ${clauses.join(" AND ")} ORDER BY updated_at ASC`).all(params);
16270
+ return rows.map((r) => this.rowToMeta(r));
16271
+ }
16272
+ openPruneMatches(input) {
16273
+ const clauses = ["ended_at IS NULL", "updated_at < @cutoff"];
16274
+ if (!input.includePinned) clauses.push("COALESCE(pinned, 0) = 0");
16275
+ const row = this.db.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${clauses.join(" AND ")}`).get({ cutoff: input.olderThanMs });
16276
+ return row.n;
16277
+ }
16278
+ freelistRatio() {
16279
+ const pages = Number(this.db.pragma("page_count", { simple: true }));
16280
+ if (pages <= 0) return 0;
16281
+ return Number(this.db.pragma("freelist_count", { simple: true })) / pages;
16282
+ }
16283
+ getMeta(key) {
16284
+ const row = this.db.prepare("SELECT value FROM session_meta WHERE key = ?").get(key);
16285
+ return row?.value ?? null;
16286
+ }
16287
+ setMeta(key, value) {
16288
+ this.db.prepare(
16289
+ "INSERT INTO session_meta (key, value) VALUES (@key, @value) ON CONFLICT(key) DO UPDATE SET value = @value"
16290
+ ).run({ key, value });
16291
+ }
16292
+ claimMaintenanceSlot(key, minIntervalMs, now) {
16293
+ const run = this.db.transaction(() => {
16294
+ const last = this.getMeta(key);
16295
+ if (last !== null && now - Number(last) < minIntervalMs) return false;
16296
+ this.setMeta(key, String(now));
16297
+ return true;
16298
+ });
16299
+ return run();
16300
+ }
16194
16301
  compact(sessionId, summary, beforeCount, keep) {
16195
16302
  const messages = this.db.prepare(
16196
16303
  "SELECT id FROM messages WHERE session_id = ? AND active = 1 ORDER BY timestamp DESC"
@@ -16209,6 +16316,29 @@ var SessionDB = class {
16209
16316
  duration: 0
16210
16317
  };
16211
16318
  }
16319
+ archiveAndCompact(sessionId, input) {
16320
+ const run = this.db.transaction(() => {
16321
+ const rows = this.db.prepare(
16322
+ "SELECT id, timestamp FROM messages WHERE session_id = ? AND active = 1 AND id <= ? ORDER BY timestamp ASC, id ASC"
16323
+ ).all(sessionId, input.watermark);
16324
+ const end = rows.length - input.tailCount;
16325
+ const middle = end > input.headCount ? rows.slice(input.headCount, end) : [];
16326
+ if (middle.length === 0) return null;
16327
+ const mark = this.db.prepare("UPDATE messages SET active = 0, compacted = 1 WHERE id = ?");
16328
+ for (const row of middle) mark.run(row.id);
16329
+ const summaryId = this.nextMessageId();
16330
+ this.insertMessage({
16331
+ sessionId,
16332
+ id: summaryId,
16333
+ role: "user",
16334
+ content: input.summary,
16335
+ timestamp: middle[0].timestamp
16336
+ });
16337
+ this.db.prepare("UPDATE sessions SET summary = @s WHERE id = @id").run({ s: input.summary, id: sessionId });
16338
+ return { archived: middle.length, summaryId };
16339
+ });
16340
+ return run();
16341
+ }
16212
16342
  getSummary(sessionId) {
16213
16343
  const row = this.db.prepare("SELECT summary FROM sessions WHERE id = ?").get(sessionId);
16214
16344
  return row?.summary ?? null;
@@ -16407,7 +16537,7 @@ function checkFtsHealth(db) {
16407
16537
  messages,
16408
16538
  tables,
16409
16539
  missingTriggers,
16410
- healthy: missingTriggers.length === 0 && tables.every((t78) => t78.error === null && t78.drift === null && t78.rows === messages),
16540
+ healthy: missingTriggers.length === 0 && tables.every((t79) => t79.error === null && t79.drift === null && t79.rows === messages),
16411
16541
  indexBytes: indexBytesOf(db),
16412
16542
  space: spaceOf(db)
16413
16543
  };
@@ -16553,6 +16683,7 @@ var SessionReferences = class {
16553
16683
  }
16554
16684
  };
16555
16685
  // src/session/manager.ts
16686
+ var AUTO_VACUUM_MIN_FREELIST_RATIO = 0.25;
16556
16687
  var SessionManager = class {
16557
16688
  db;
16558
16689
  queries;
@@ -16647,6 +16778,44 @@ var SessionManager = class {
16647
16778
  unarchive(id) {
16648
16779
  this.db.unarchiveSession(id);
16649
16780
  }
16781
+ pruneCandidates(input) {
16782
+ return this.db.pruneCandidates(input);
16783
+ }
16784
+ openPruneMatches(input) {
16785
+ return this.db.openPruneMatches(input);
16786
+ }
16787
+ freelistRatio() {
16788
+ return this.db.freelistRatio();
16789
+ }
16790
+ maybeAutoPrune(opts) {
16791
+ const result = { skipped: false, pruned: 0, vacuumed: false };
16792
+ if (!opts.enabled) return { ...result, skipped: true };
16793
+ const now = opts.now?.() ?? Date.now();
16794
+ const minInterval = opts.minIntervalMs ?? 24 * 60 * 60 * 1e3;
16795
+ const minVacuumInterval = opts.minVacuumIntervalMs ?? 30 * 24 * 60 * 60 * 1e3;
16796
+ try {
16797
+ if (!this.db.claimMaintenanceSlot("last_auto_prune", minInterval, now)) {
16798
+ return { ...result, skipped: true };
16799
+ }
16800
+ const cutoff = now - opts.retentionDays * 24 * 60 * 60 * 1e3;
16801
+ const victims = this.db.pruneCandidates({ olderThanMs: cutoff });
16802
+ let pruned = 0;
16803
+ for (const s of victims) if (this.db.deleteSession(s.id)) pruned++;
16804
+ let vacuumed = false;
16805
+ if (opts.vacuum && pruned > 0) {
16806
+ const lastVacuum = this.db.getMeta("last_vacuum");
16807
+ const vacuumDue = lastVacuum === null || now - Number(lastVacuum) >= minVacuumInterval;
16808
+ if (vacuumDue && this.db.freelistRatio() > AUTO_VACUUM_MIN_FREELIST_RATIO) {
16809
+ const report3 = reclaimFtsSpace(this.db.db, true);
16810
+ vacuumed = report3.vacuumed;
16811
+ if (vacuumed) this.db.setMeta("last_vacuum", String(now));
16812
+ }
16813
+ }
16814
+ return { skipped: false, pruned, vacuumed };
16815
+ } catch (cause) {
16816
+ return { ...result, error: cause instanceof Error ? cause.message : String(cause) };
16817
+ }
16818
+ }
16650
16819
  delete(id) {
16651
16820
  return this.db.deleteSession(id);
16652
16821
  }
@@ -16697,6 +16866,9 @@ var SessionManager = class {
16697
16866
  const summary = toSummarize.length === 0 ? "" : await this.buildSummary(toSummarize, summarize3) ?? outlineOf(toSummarize);
16698
16867
  return this.db.compact(sessionId, summary, beforeCount, IMPORTANT_ROUNDS * 2);
16699
16868
  }
16869
+ archiveAndCompact(sessionId, input) {
16870
+ return this.db.archiveAndCompact(sessionId, input);
16871
+ }
16700
16872
  async buildSummary(msgs, summarize3) {
16701
16873
  if (!summarize3) return null;
16702
16874
  const transcript = msgs.map((m) => `[${m.role}] ${m.content}`).join("\n\n");
@@ -17123,8 +17295,8 @@ var RecentWorkspaces = class {
17123
17295
  }
17124
17296
  remember(dir, now, name) {
17125
17297
  const root = resolve(dir);
17126
- const kept = this.entries.find((e) => e.root === root)?.name;
17127
- const rest = this.entries.filter((e) => e.root !== root);
17298
+ const kept = this.entries.find((e) => samePath(e.root, root))?.name;
17299
+ const rest = this.entries.filter((e) => !samePath(e.root, root));
17128
17300
  this.entries = [
17129
17301
  { root, name: name || kept || basename(root) || root, usedAt: now },
17130
17302
  ...rest
@@ -17133,7 +17305,7 @@ var RecentWorkspaces = class {
17133
17305
  }
17134
17306
  forget(dir) {
17135
17307
  const root = resolve(dir);
17136
- const next = this.entries.filter((e) => e.root !== root);
17308
+ const next = this.entries.filter((e) => !samePath(e.root, root));
17137
17309
  if (next.length === this.entries.length) return;
17138
17310
  this.entries = next;
17139
17311
  this.save();
@@ -17209,6 +17381,84 @@ function isRecentWorkspace(value) {
17209
17381
  const v = value;
17210
17382
  return typeof v["root"] === "string" && v["root"].length > 0 && typeof v["name"] === "string" && typeof v["usedAt"] === "number";
17211
17383
  }
17384
+ var MAX_LAST_OPEN_SESSIONS = 20;
17385
+ var FILE_VERSION3 = 1;
17386
+ function describe6(err) {
17387
+ return err instanceof Error ? err.message : String(err);
17388
+ }
17389
+ var LastOpenSessions = class {
17390
+ constructor(filePath = lastSessionsPath()) {
17391
+ this.filePath = filePath;
17392
+ this.load();
17393
+ }
17394
+ filePath;
17395
+ entries = [];
17396
+ loadErrors = [];
17397
+ writeErrors = [];
17398
+ get(root) {
17399
+ const key = resolve(root);
17400
+ return this.entries.find((e) => samePath(e.root, key))?.sessionId ?? null;
17401
+ }
17402
+ remember(root, sessionId, now) {
17403
+ const key = resolve(root);
17404
+ const head = this.entries[0];
17405
+ if (head && samePath(head.root, key) && head.sessionId === sessionId) return;
17406
+ const rest = this.entries.filter((e) => !samePath(e.root, key));
17407
+ this.entries = [{ root: key, sessionId, at: now }, ...rest].slice(0, MAX_LAST_OPEN_SESSIONS);
17408
+ this.save();
17409
+ }
17410
+ forget(root) {
17411
+ const key = resolve(root);
17412
+ const next = this.entries.filter((e) => !samePath(e.root, key));
17413
+ if (next.length === this.entries.length) return;
17414
+ this.entries = next;
17415
+ this.save();
17416
+ }
17417
+ load() {
17418
+ this.entries = [];
17419
+ if (!existsSync(this.filePath)) return;
17420
+ let parsed;
17421
+ try {
17422
+ parsed = JSON.parse(readFileSync(this.filePath, "utf-8"));
17423
+ } catch (err) {
17424
+ this.loadErrors.push(
17425
+ (lang) => t("last_session.unreadable", { path: this.filePath, reason: describe6(err) }, lang)
17426
+ );
17427
+ return;
17428
+ }
17429
+ this.adopt(parsed);
17430
+ }
17431
+ adopt(parsed) {
17432
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
17433
+ const file = parsed;
17434
+ if (file.version !== FILE_VERSION3) return;
17435
+ if (!Array.isArray(file.sessions)) return;
17436
+ this.entries = file.sessions.filter(isLastOpenSession).sort((a, b) => b.at - a.at).slice(0, MAX_LAST_OPEN_SESSIONS);
17437
+ }
17438
+ save() {
17439
+ const body2 = JSON.stringify(
17440
+ { version: FILE_VERSION3, sessions: this.entries },
17441
+ null,
17442
+ 2
17443
+ );
17444
+ const tmp = `${this.filePath}.${process.pid}.tmp`;
17445
+ try {
17446
+ mkdirSync(dirname(this.filePath), { recursive: true });
17447
+ writeFileSync(tmp, body2, { encoding: "utf-8", mode: 384 });
17448
+ renameSync(tmp, this.filePath);
17449
+ } catch (err) {
17450
+ rmSync(tmp, { force: true });
17451
+ this.writeErrors.push(
17452
+ (lang) => t("last_session.write_failed", { path: this.filePath, reason: describe6(err) }, lang)
17453
+ );
17454
+ }
17455
+ }
17456
+ };
17457
+ function isLastOpenSession(value) {
17458
+ if (typeof value !== "object" || value === null) return false;
17459
+ const v = value;
17460
+ return typeof v["root"] === "string" && v["root"].length > 0 && typeof v["sessionId"] === "string" && v["sessionId"].length > 0 && typeof v["at"] === "number";
17461
+ }
17212
17462
  function checkCodeExec(isolation) {
17213
17463
  if (isolation === "os") {
17214
17464
  return { allowed: true, reason: t("permission_reason.sandbox_os"), code: "sandbox-isolation" };
@@ -17457,11 +17707,11 @@ var ApprovalCache = class {
17457
17707
  return !rel.startsWith("..") && !isAbsolute(rel);
17458
17708
  }
17459
17709
  normalizeTarget(target) {
17460
- const t78 = target.trim();
17461
- if (!t78) return "";
17462
- const unix = t78.replace(/\\/g, "/");
17710
+ const t79 = target.trim();
17711
+ if (!t79) return "";
17712
+ const unix = t79.replace(/\\/g, "/");
17463
17713
  if (unix.startsWith("/") || /^[A-Za-z]:\//.test(unix)) return resolve(unix);
17464
- return t78;
17714
+ return t79;
17465
17715
  }
17466
17716
  load() {
17467
17717
  try {
@@ -17564,9 +17814,9 @@ function createSharedPermissionState(cache) {
17564
17814
  };
17565
17815
  }
17566
17816
  function cacheKeyFor(raw) {
17567
- const t78 = raw.trim();
17568
- if (!t78) return null;
17569
- return t78.slice(0, 500);
17817
+ const t79 = raw.trim();
17818
+ if (!t79) return null;
17819
+ return t79.slice(0, 500);
17570
17820
  }
17571
17821
  // src/permission/manager.ts
17572
17822
  var log3 = createLogger("permission");
@@ -18372,6 +18622,24 @@ function acquireScheduleLock(db, scheduleId, clock2 = Date.now) {
18372
18622
  }
18373
18623
  };
18374
18624
  }
18625
+ function isLive(holder, now) {
18626
+ if (holder.heartbeatAt < now - LOCK_STALE_MS) return false;
18627
+ return holder.host !== hostname() || pidAlive(holder.pid);
18628
+ }
18629
+ function listRunningSchedules(db, clock2 = Date.now) {
18630
+ const now = clock2();
18631
+ const rows = db.prepare(
18632
+ `SELECT schedule_id AS scheduleId, pid, host,
18633
+ acquired_at AS acquiredAt, heartbeat_at AS heartbeatAt
18634
+ FROM schedule_locks`
18635
+ ).all();
18636
+ return rows.filter((row) => isLive(row, now)).map((row) => ({
18637
+ scheduleId: row.scheduleId,
18638
+ startedAt: row.acquiredAt,
18639
+ pid: row.pid,
18640
+ host: row.host
18641
+ }));
18642
+ }
18375
18643
  function readHolder(db, scheduleId) {
18376
18644
  const row = db.prepare(
18377
18645
  "SELECT pid, host, acquired_at AS acquiredAt, heartbeat_at AS heartbeatAt FROM schedule_locks WHERE schedule_id = ?"
@@ -18612,9 +18880,15 @@ var ScheduleRegistrar = class {
18612
18880
  this.opts.store.setEnabled(def.id, enabled);
18613
18881
  return { ok: false, reason: "unsupported-platform" };
18614
18882
  }
18615
- if (enabled) this.opts.store.setEnabled(def.id, true);
18616
- const res = def.osTaskId ? await backend.setEnabled(def.osTaskId, enabled) : { ok: true };
18617
- if (!enabled) this.opts.store.setEnabled(def.id, false);
18883
+ if (enabled) {
18884
+ this.opts.store.setEnabled(def.id, true);
18885
+ const on = { ...def, enabled: true };
18886
+ if (!def.osTaskId) return this.register(on);
18887
+ const res2 = await backend.setEnabled(def.osTaskId, true);
18888
+ return res2.ok ? { ok: true, warnings: [] } : this.register(on);
18889
+ }
18890
+ const res = def.osTaskId ? await backend.setEnabled(def.osTaskId, false) : { ok: true };
18891
+ this.opts.store.setEnabled(def.id, false);
18618
18892
  return res.ok ? { ok: true, warnings: [] } : { ok: false, reason: "backend-error", detail: res.detail };
18619
18893
  }
18620
18894
  async retire(def) {
@@ -19725,7 +19999,7 @@ function runCommandHook(config, event, workDir) {
19725
19999
  const baseEnv = sanitizeEnvironment(process.env);
19726
20000
  const injectedEnv = injectEpochEnv(event, config.pluginRoot);
19727
20001
  const hookEnv = { ...baseEnv, ...injectedEnv, ...config.env };
19728
- return new Promise((resolve15) => {
20002
+ return new Promise((resolve16) => {
19729
20003
  const child = spawn(config.command, {
19730
20004
  shell: true,
19731
20005
  cwd: workDir,
@@ -19739,7 +20013,7 @@ function runCommandHook(config, event, workDir) {
19739
20013
  if (!settled) {
19740
20014
  settled = true;
19741
20015
  child.kill("SIGKILL");
19742
- resolve15({
20016
+ resolve16({
19743
20017
  exitCode: -1,
19744
20018
  stdout,
19745
20019
  stderr,
@@ -19748,16 +20022,22 @@ function runCommandHook(config, event, workDir) {
19748
20022
  });
19749
20023
  }
19750
20024
  }, timeout);
20025
+ let outDecoder = null;
20026
+ let errDecoder = null;
19751
20027
  child.stdout?.on("data", (data) => {
19752
- stdout += data.toString("utf-8");
20028
+ outDecoder ??= createStreamDecoder();
20029
+ stdout += outDecoder.write(data);
19753
20030
  });
19754
20031
  child.stderr?.on("data", (data) => {
19755
- stderr += data.toString("utf-8");
20032
+ errDecoder ??= createStreamDecoder();
20033
+ stderr += errDecoder.write(data);
19756
20034
  });
19757
20035
  child.on("close", (code) => {
19758
20036
  clearTimeout(timer);
19759
20037
  if (settled) return;
19760
20038
  settled = true;
20039
+ stdout += outDecoder?.end() ?? "";
20040
+ stderr += errDecoder?.end() ?? "";
19761
20041
  const exitCode = code ?? -1;
19762
20042
  const blocked = exitCode === 2;
19763
20043
  const rejectReason = blocked ? stderr.trim() || t("hook_load.rejected_this") : void 0;
@@ -19769,7 +20049,7 @@ function runCommandHook(config, event, workDir) {
19769
20049
  }
19770
20050
  } catch {
19771
20051
  }
19772
- resolve15({
20052
+ resolve16({
19773
20053
  exitCode,
19774
20054
  stdout: stdout.trim(),
19775
20055
  stderr: stderr.trim(),
@@ -19782,7 +20062,7 @@ function runCommandHook(config, event, workDir) {
19782
20062
  clearTimeout(timer);
19783
20063
  if (settled) return;
19784
20064
  settled = true;
19785
- resolve15({
20065
+ resolve16({
19786
20066
  exitCode: -1,
19787
20067
  stdout,
19788
20068
  stderr,
@@ -19895,7 +20175,7 @@ var HookManager = class {
19895
20175
  return { blocked: true, reason: result.rejectReason || t("hook_load.rejected") };
19896
20176
  }
19897
20177
  } catch (err) {
19898
- this.log("warn", `[HookManager] ${type} \u7684 command hook \u6267\u884C\u5F02\u5E38: ${describe7(err)}`, {
20178
+ this.log("warn", `[HookManager] ${type} \u7684 command hook \u6267\u884C\u5F02\u5E38: ${describe8(err)}`, {
19899
20179
  hookType: type,
19900
20180
  command: config.command
19901
20181
  });
@@ -19913,7 +20193,7 @@ var HookManager = class {
19913
20193
  (_, reject2) => setTimeout(() => reject2(new Error("Hook timeout")), HOOK_TIMEOUT_MS)
19914
20194
  )
19915
20195
  ]).catch((err) => {
19916
- this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe7(err)}`, {
20196
+ this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe8(err)}`, {
19917
20197
  hookType: type,
19918
20198
  plugin: reg.plugin
19919
20199
  });
@@ -19925,7 +20205,7 @@ var HookManager = class {
19925
20205
  if (!matchesHook(entry.matcher, event)) continue;
19926
20206
  for (const config of entry.configs) {
19927
20207
  runCommandHook(config, event, ctx.workDir).then((result) => reportIfNeverRan(this.log, type, config, result)).catch((err) => {
19928
- this.log("warn", `[HookManager] ${type} \u7684 command hook \u5F02\u6B65\u5F02\u5E38: ${describe7(err)}`, {
20208
+ this.log("warn", `[HookManager] ${type} \u7684 command hook \u5F02\u6B65\u5F02\u5E38: ${describe8(err)}`, {
19929
20209
  hookType: type,
19930
20210
  command: config.command
19931
20211
  });
@@ -19946,7 +20226,7 @@ var HookManager = class {
19946
20226
  ]);
19947
20227
  if (result !== null && result !== void 0) return result;
19948
20228
  } catch (err) {
19949
- this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe7(err)}`, {
20229
+ this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe8(err)}`, {
19950
20230
  hookType: type,
19951
20231
  plugin: reg.plugin
19952
20232
  });
@@ -19967,7 +20247,7 @@ var HookManager = class {
19967
20247
  ]);
19968
20248
  if (result) results.push(result);
19969
20249
  } catch (err) {
19970
- this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe7(err)}`, {
20250
+ this.log("warn", `[HookManager] ${type} (${reg.plugin}) \u5931\u8D25: ${describe8(err)}`, {
19971
20251
  hookType: type,
19972
20252
  plugin: reg.plugin
19973
20253
  });
@@ -19984,7 +20264,7 @@ var HookManager = class {
19984
20264
  const verdict = verdictOf2(result);
19985
20265
  if (verdict) results.push(verdict);
19986
20266
  } catch (err) {
19987
- this.log("warn", `[HookManager] ${type} \u7684 command hook \u6267\u884C\u5F02\u5E38: ${describe7(err)}`, {
20267
+ this.log("warn", `[HookManager] ${type} \u7684 command hook \u6267\u884C\u5F02\u5E38: ${describe8(err)}`, {
19988
20268
  hookType: type,
19989
20269
  command: config.command
19990
20270
  });
@@ -20000,7 +20280,7 @@ var HookManager = class {
20000
20280
  return [...list].sort((a, b) => a.priority - b.priority);
20001
20281
  }
20002
20282
  };
20003
- function describe7(err) {
20283
+ function describe8(err) {
20004
20284
  return err instanceof Error ? err.message : String(err);
20005
20285
  }
20006
20286
  function verdictOf2(result) {
@@ -21297,7 +21577,7 @@ async function withStaged(source, opts, fn) {
21297
21577
  shape = existsSync(join(unpacked, SKILL_FILE)) ? "skill-dir" : "tree";
21298
21578
  }
21299
21579
  const staged = stageImport(root, staging, shape, opts.lang);
21300
- const preview3 = describe8(source, form, staged.skills, staged.issues, opts.existing, opts.lang);
21580
+ const preview3 = describe9(source, form, staged.skills, staged.issues, opts.existing, opts.lang);
21301
21581
  if (preview3.skills.length === 0) {
21302
21582
  const detail = t("skill.import_empty", { path: source }, opts.lang);
21303
21583
  return { ok: false, reason: "empty", detail, issues: preview3.issues };
@@ -21308,7 +21588,7 @@ async function withStaged(source, opts, fn) {
21308
21588
  rmSync(unpacked, { recursive: true, force: true });
21309
21589
  }
21310
21590
  }
21311
- function describe8(source, form, staged, issues, existing, lang) {
21591
+ function describe9(source, form, staged, issues, existing, lang) {
21312
21592
  const byName = new Map(existing.map((s) => [s.name, s.scope]));
21313
21593
  const entries2 = [];
21314
21594
  const taken = /* @__PURE__ */ new Set();
@@ -21978,6 +22258,19 @@ var ContextCompressor = class {
21978
22258
  };
21979
22259
  }
21980
22260
  const { head, middle, tail } = this.determineBoundaries(pruned.messages, config);
22261
+ if (middle.length === 0) {
22262
+ const kept = this.cleanupOrphans(pruned.messages);
22263
+ this.turnsSinceCompress = 0;
22264
+ return {
22265
+ messages: kept,
22266
+ summary: "",
22267
+ ...prunedCounters,
22268
+ summarizedMessages: 0,
22269
+ newTokenCount: this.estimateTokens(kept),
22270
+ droppedOrphans: pruned.messages.length - kept.length,
22271
+ summarySkipped: true
22272
+ };
22273
+ }
21981
22274
  const nl = "\n";
21982
22275
  const middleText = middle.map((m) => `${m.role}: ${m.content}`).join(nl);
21983
22276
  const summarized = await this.generateSummary(
@@ -22013,6 +22306,7 @@ var ContextCompressor = class {
22013
22306
  summarizedMessages: middle.length,
22014
22307
  newTokenCount: this.estimateTokens(result),
22015
22308
  droppedOrphans,
22309
+ ...summary && droppedOrphans === 0 && middle.length > 0 ? { carriedHead: head.length, carriedTail: tail.length } : {},
22016
22310
  ...summarized?.usage ? { usage: summarized.usage } : {},
22017
22311
  ...summarized?.model ? { model: summarized.model } : {}
22018
22312
  };
@@ -22144,10 +22438,10 @@ ${extra}` : withGoal;
22144
22438
  return messages.reduce((sum, m) => sum + estimateTokens(m.content), 0);
22145
22439
  }
22146
22440
  };
22147
- var FILE_VERSION3 = 1;
22441
+ var FILE_VERSION4 = 1;
22148
22442
  var LEVELS = ["trusted", "untrusted"];
22149
22443
  var SCOPES = ["directory", "directory-tree"];
22150
- function describe9(err) {
22444
+ function describe10(err) {
22151
22445
  return err instanceof Error ? err.message : String(err);
22152
22446
  }
22153
22447
  function covers(base, target, scope) {
@@ -22156,9 +22450,6 @@ function covers(base, target, scope) {
22156
22450
  const rel = relative(base, target);
22157
22451
  return !rel.startsWith("..") && !isAbsolute(rel);
22158
22452
  }
22159
- function samePath(a, b) {
22160
- return relative(a, b) === "";
22161
- }
22162
22453
  var TrustManager = class {
22163
22454
  constructor(filePath = trustPath()) {
22164
22455
  this.filePath = filePath;
@@ -22224,7 +22515,7 @@ var TrustManager = class {
22224
22515
  raw = readFileSync(this.filePath, "utf-8");
22225
22516
  } catch (err) {
22226
22517
  this.loadErrors.push(
22227
- t("trust_gate.record_read_failed", { path: this.filePath, reason: describe9(err) })
22518
+ t("trust_gate.record_read_failed", { path: this.filePath, reason: describe10(err) })
22228
22519
  );
22229
22520
  return;
22230
22521
  }
@@ -22233,7 +22524,7 @@ var TrustManager = class {
22233
22524
  parsed = JSON.parse(raw);
22234
22525
  } catch (err) {
22235
22526
  this.loadErrors.push(
22236
- t("trust_gate.record_bad_json", { path: this.filePath, reason: describe9(err) })
22527
+ t("trust_gate.record_bad_json", { path: this.filePath, reason: describe10(err) })
22237
22528
  );
22238
22529
  return;
22239
22530
  }
@@ -22245,12 +22536,12 @@ var TrustManager = class {
22245
22536
  return;
22246
22537
  }
22247
22538
  const file = parsed;
22248
- if (file.version !== FILE_VERSION3) {
22539
+ if (file.version !== FILE_VERSION4) {
22249
22540
  this.loadErrors.push(
22250
22541
  t("trust_gate.record_bad_version", {
22251
22542
  path: this.filePath,
22252
22543
  found: String(file.version),
22253
- expected: FILE_VERSION3
22544
+ expected: FILE_VERSION4
22254
22545
  })
22255
22546
  );
22256
22547
  return;
@@ -22271,7 +22562,7 @@ var TrustManager = class {
22271
22562
  save() {
22272
22563
  mkdirSync(dirname(this.filePath), { recursive: true });
22273
22564
  const body2 = JSON.stringify(
22274
- { version: FILE_VERSION3, records: this.records },
22565
+ { version: FILE_VERSION4, records: this.records },
22275
22566
  null,
22276
22567
  2
22277
22568
  );
@@ -22282,7 +22573,7 @@ var TrustManager = class {
22282
22573
  } catch (err) {
22283
22574
  rmSync(tmp, { force: true });
22284
22575
  throw new Error(
22285
- t("trust_gate.record_write_failed", { path: this.filePath, reason: describe9(err) })
22576
+ t("trust_gate.record_write_failed", { path: this.filePath, reason: describe10(err) })
22286
22577
  );
22287
22578
  }
22288
22579
  }
@@ -22525,13 +22816,16 @@ var CodeSandbox = class {
22525
22816
  ...IS_WINDOWS ? { windowsHide: true } : {}
22526
22817
  };
22527
22818
  const child = spawn(command, args, spawnOpts);
22528
- return new Promise((resolve15) => {
22819
+ return new Promise((resolve16) => {
22529
22820
  let stdout = "";
22530
22821
  let stderr = "";
22531
22822
  let killedForOutput = false;
22823
+ let outDecoder = null;
22824
+ let errDecoder = null;
22532
22825
  child.stdout?.on("data", (c) => {
22533
22826
  if (killedForOutput) return;
22534
- stdout += c.toString();
22827
+ outDecoder ??= createStreamDecoder();
22828
+ stdout += outDecoder.write(c);
22535
22829
  if (stdout.length >= MAX_OUTPUT) {
22536
22830
  killedForOutput = true;
22537
22831
  stdout = stdout.slice(0, MAX_OUTPUT);
@@ -22540,14 +22834,15 @@ var CodeSandbox = class {
22540
22834
  });
22541
22835
  child.stderr?.on("data", (c) => {
22542
22836
  if (stderr.length >= MAX_OUTPUT) return;
22543
- stderr += c.toString();
22837
+ errDecoder ??= createStreamDecoder();
22838
+ stderr += errDecoder.write(c);
22544
22839
  });
22545
22840
  const onAbort = () => child.kill("SIGKILL");
22546
22841
  opts.signal?.addEventListener("abort", onAbort, { once: true });
22547
22842
  child.on("error", (err) => {
22548
22843
  opts.signal?.removeEventListener("abort", onAbort);
22549
22844
  if (err.code === "ENOENT") {
22550
- resolve15({
22845
+ resolve16({
22551
22846
  success: false,
22552
22847
  output: "",
22553
22848
  error: `[\u9519\u8BEF] \u627E\u4E0D\u5230\u89E3\u91CA\u5668: ${command}`,
@@ -22556,7 +22851,7 @@ var CodeSandbox = class {
22556
22851
  });
22557
22852
  return;
22558
22853
  }
22559
- resolve15({
22854
+ resolve16({
22560
22855
  success: false,
22561
22856
  output: "",
22562
22857
  error: `[\u9519\u8BEF] ${err.message}`,
@@ -22566,9 +22861,11 @@ var CodeSandbox = class {
22566
22861
  });
22567
22862
  child.on("close", (code, sig) => {
22568
22863
  opts.signal?.removeEventListener("abort", onAbort);
22864
+ if (!killedForOutput) stdout += outDecoder?.end() ?? "";
22865
+ if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
22569
22866
  const duration = Date.now() - start;
22570
22867
  if (killedForOutput) {
22571
- resolve15({
22868
+ resolve16({
22572
22869
  success: false,
22573
22870
  output: trunc(stdout, stderr),
22574
22871
  error: `[\u8F93\u51FA\u8D85\u9650] \u8D85\u8FC7 ${MAX_OUTPUT} \u5B57\u8282\uFF0C\u5DF2\u7EC8\u6B62`,
@@ -22578,7 +22875,7 @@ var CodeSandbox = class {
22578
22875
  return;
22579
22876
  }
22580
22877
  if (sig) {
22581
- resolve15({
22878
+ resolve16({
22582
22879
  success: false,
22583
22880
  output: trunc(stdout, stderr),
22584
22881
  error: opts.signal?.aborted ? "[\u5DF2\u53D6\u6D88]" : "[\u8D85\u65F6] \u6267\u884C\u8D85\u65F6",
@@ -22590,7 +22887,7 @@ var CodeSandbox = class {
22590
22887
  const output = trunc(stdout, stderr);
22591
22888
  if (code !== 0 && code !== null) {
22592
22889
  const oom = /heap out of memory|allocation failed/i.test(stderr);
22593
- resolve15({
22890
+ resolve16({
22594
22891
  success: false,
22595
22892
  output,
22596
22893
  error: oom ? `[\u5185\u5B58\u8D85\u9650] \u8D85\u8FC7 ${MAX_MEMORY}MB` : `[\u8FD0\u884C\u65F6\u9519\u8BEF] exit ${code}: ${stderr.slice(0, 200)}`,
@@ -22599,7 +22896,7 @@ var CodeSandbox = class {
22599
22896
  });
22600
22897
  return;
22601
22898
  }
22602
- resolve15({ success: true, output, duration });
22899
+ resolve16({ success: true, output, duration });
22603
22900
  });
22604
22901
  });
22605
22902
  }
@@ -22659,4 +22956,4 @@ function trunc(stdout, stderr) {
22659
22956
  * `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
22660
22957
  */
22661
22958
 
22662
- export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, 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, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, MemoryReviewer, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, 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_CREATE_TOOL, SKILL_EDIT_TOOL, 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, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, 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, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
22959
+ export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, 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_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, MemoryReviewer, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, 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_CREATE_TOOL, SKILL_EDIT_TOOL, 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, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, 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, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };