@epoch-agent/core 0.13.0 → 0.15.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.
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, 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';
5
+ import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, countCodePoints, headCodePoints, attachmentsDir, isInWorkspace, workspacesPath, samePath, lastSessionsPath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, 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';
@@ -476,10 +476,18 @@ var runCommand2;
476
476
  var init_backend = __esm({
477
477
  "src/schedule/os/backend.ts"() {
478
478
  runCommand2 = (file, args) => new Promise((resolve16) => {
479
- execFile(file, [...args], { encoding: "buffer", windowsHide: true }, (err, stdout, stderr) => {
480
- const code = err && typeof err.code === "number" ? err.code ?? 1 : err ? 1 : 0;
481
- resolve16({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
482
- });
479
+ guardSpawn(
480
+ () => execFile(
481
+ file,
482
+ [...args],
483
+ { encoding: "buffer", windowsHide: true },
484
+ (err, stdout, stderr) => {
485
+ const code = err && typeof err.code === "number" ? err.code ?? 1 : err ? 1 : 0;
486
+ resolve16({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
487
+ }
488
+ ),
489
+ (err) => resolve16({ code: 1, stdout: "", stderr: `${file}: ${err.message}` })
490
+ );
483
491
  });
484
492
  }
485
493
  });
@@ -534,6 +542,7 @@ var INTERNAL_TOOLS = /* @__PURE__ */ new Set([
534
542
  "session_messages_search",
535
543
  "session_messages_read",
536
544
  "session_trace",
545
+ "session_list",
537
546
  "goal",
538
547
  "skill_view",
539
548
  "tool_search",
@@ -1109,6 +1118,7 @@ var BLOCKED_TOOLS = Object.freeze([
1109
1118
  "session_messages_search",
1110
1119
  "session_messages_read",
1111
1120
  "session_trace",
1121
+ "session_list",
1112
1122
  "goal"
1113
1123
  ]);
1114
1124
  var DelegateManager = class {
@@ -2430,8 +2440,9 @@ var SCHEDULE_ISSUE_CODES = [
2430
2440
  ];
2431
2441
  var SCHEDULE_DEFAULTS = {
2432
2442
  maxTurns: DEFAULT_MAX_TURNS,
2433
- timeoutMs: 15 * 60 * 1e3
2443
+ timeoutMs: 60 * 60 * 1e3
2434
2444
  };
2445
+ var SCHEDULE_FORM_BUDGET_USD = 200;
2435
2446
  var SCHEDULE_DEFAULT_ALLOWLIST = {
2436
2447
  allowTools: [],
2437
2448
  allowOperations: ["file_read"],
@@ -3039,9 +3050,7 @@ function validateSchedule(input) {
3039
3050
  }
3040
3051
  function bypassIssues(input) {
3041
3052
  const issues = [];
3042
- if (!input.workDir.trim()) {
3043
- issues.push({ code: "bypass-needs-workdir" });
3044
- } else if (isTooBroad(input.workDir, input.homeDir)) {
3053
+ if (input.workDir.trim() && isTooBroad(input.workDir, input.homeDir)) {
3045
3054
  issues.push({ code: "bypass-workdir-too-broad", detail: input.workDir });
3046
3055
  }
3047
3056
  const missingRuler = !(input.maxBudgetUsd > 0) || !(input.timeoutMs > 0);
@@ -3431,11 +3440,13 @@ var SESSION_SEARCH_TOOL = "session_search";
3431
3440
  var SESSION_MESSAGES_SEARCH_TOOL = "session_messages_search";
3432
3441
  var SESSION_MESSAGES_READ_TOOL = "session_messages_read";
3433
3442
  var SESSION_TRACE_TOOL = "session_trace";
3443
+ var SESSION_LIST_TOOL = "session_list";
3434
3444
  var SESSION_QUERY_TOOLS = Object.freeze([
3435
3445
  SESSION_SEARCH_TOOL,
3436
3446
  SESSION_MESSAGES_SEARCH_TOOL,
3437
3447
  SESSION_MESSAGES_READ_TOOL,
3438
- SESSION_TRACE_TOOL
3448
+ SESSION_TRACE_TOOL,
3449
+ SESSION_LIST_TOOL
3439
3450
  ]);
3440
3451
  var MAX_SNIPPET = 160;
3441
3452
  var CANDIDATE_FACTOR = 4;
@@ -3529,6 +3540,36 @@ var SessionQueries = class {
3529
3540
  trace(input) {
3530
3541
  return traceSession(this.db, input);
3531
3542
  }
3543
+ listSessions(input) {
3544
+ const vis = resolveSessionVisibility({ ...input, db: this.db });
3545
+ const archivedClause = input.archived === "all" ? "" : input.archived === "only" ? "AND s.archived = 1" : "AND s.archived = 0";
3546
+ const where = `${vis.clause} ${archivedClause}`;
3547
+ const total = this.db.prepare(`SELECT count(*) AS n FROM sessions s WHERE ${where}`).get(vis.params).n;
3548
+ const rows = this.db.prepare(
3549
+ `SELECT s.id,
3550
+ COALESCE(NULLIF(s.display_name, ''), s.title) AS title,
3551
+ s.cwd,
3552
+ COALESCE(s.updated_at, s.started_at) AS updated_at,
3553
+ s.message_count,
3554
+ s.archived
3555
+ FROM sessions s
3556
+ WHERE ${where}
3557
+ ORDER BY COALESCE(s.updated_at, s.started_at) DESC
3558
+ LIMIT @limit`
3559
+ ).all({ ...vis.params, limit: input.limit });
3560
+ return {
3561
+ total,
3562
+ sessions: rows.map((row) => ({
3563
+ sessionId: row.id,
3564
+ title: row.title,
3565
+ cwd: row.cwd ?? void 0,
3566
+ updatedAt: row.updated_at,
3567
+ messageCount: row.message_count,
3568
+ archived: row.archived === 1,
3569
+ isCurrent: row.id === input.currentSessionId
3570
+ }))
3571
+ };
3572
+ }
3532
3573
  contentRows(safe, limit, visClause, visParams) {
3533
3574
  const sql = (table) => `
3534
3575
  SELECT m.id, m.session_id, m.role, m.content, m.timestamp, m.active,
@@ -3594,7 +3635,123 @@ var SessionQueries = class {
3594
3635
  return this.db.prepare(sql("messages_fts_trigram")).all({ query: buildTrigramMatchQuery(safe), sid: sessionId, limit });
3595
3636
  }
3596
3637
  };
3597
- // src/tools/builtin/session-read.ts
3638
+ // src/tools/builtin/session-list.ts
3639
+ var DEFAULT_LIMIT = 10;
3640
+ var MAX_LIMIT = 30;
3641
+ var MAX_TITLE = 80;
3642
+ function fail3(message, start, code, suggestion) {
3643
+ return {
3644
+ success: false,
3645
+ output: "",
3646
+ error: { code, message, ...{} },
3647
+ duration: Date.now() - start
3648
+ };
3649
+ }
3650
+ function clampLimit(raw) {
3651
+ const n = Number(raw);
3652
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
3653
+ return Math.min(Math.floor(n), MAX_LIMIT);
3654
+ }
3655
+ function stamp(ms) {
3656
+ return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
3657
+ }
3658
+ function archivedScopeOf(raw) {
3659
+ return raw === "only" || raw === "all" ? raw : "exclude";
3660
+ }
3661
+ function scopeNote(allWorkspaces, archived) {
3662
+ const where = allWorkspaces ? "all workspaces" : "current workspace + this session lineage";
3663
+ const which = archived === "only" ? "archived only" : archived === "all" ? "archived included" : "archived excluded";
3664
+ return `${where}, ${which}`;
3665
+ }
3666
+ function renderEntry(entry, index) {
3667
+ const self = entry.isCurrent ? " (this session)" : "";
3668
+ const title = entry.title.length > MAX_TITLE ? `${entry.title.slice(0, MAX_TITLE)}\u2026` : entry.title;
3669
+ const where = entry.cwd ? ` \xB7 ${entry.cwd}` : "";
3670
+ const archived = entry.archived ? " \xB7 archived" : "";
3671
+ return [
3672
+ `${index + 1}. ${entry.sessionId}${self} \u2014 ${title || "(untitled)"}`,
3673
+ ` ${stamp(entry.updatedAt)} \xB7 ${entry.messageCount} messages${where}${archived}`
3674
+ ].join("\n");
3675
+ }
3676
+ function render(out, note) {
3677
+ const shown = out.sessions.length;
3678
+ const head = shown < out.total ? `${out.total} session(s) (${note}), showing the ${shown} most recently active:` : `${out.total} session(s) (${note}):`;
3679
+ return [
3680
+ head,
3681
+ "",
3682
+ ...out.sessions.map(renderEntry),
3683
+ "",
3684
+ "Next: session_messages_search({ sessionId, query }) to look inside one, or session_messages_read({ sessionId, messageId }) once you have a hit."
3685
+ ].join("\n");
3686
+ }
3687
+ function createSessionListTool(deps) {
3688
+ return {
3689
+ name: SESSION_LIST_TOOL,
3690
+ description: "\u5217\u51FA\u4F60\u80FD\u8BFB\u5230\u7684\u5386\u53F2\u4F1A\u8BDD \u2014\u2014 \u68C0\u7D22\u90A3\u51E0\u4E2A\u90FD\u8981\u5148\u60F3\u51FA\u4E00\u4E2A\u8BCD\uFF0C\u8FD9\u4E2A\u4E0D\u8981\u3002\n\u7528\u5B83\u56DE\u7B54\u300C\u6211\u90FD\u6709\u54EA\u4E9B\u4F1A\u8BDD\u300D\u300C\u4E0A\u6B21\u90A3\u6BB5\u804A\u7684\u662F\u4EC0\u4E48\u300D\uFF0C\u4EE5\u53CA\u7528\u6237\u63D0\u8D77\u67D0\u6BB5\u8FC7\u53BB\u7684\u4F1A\u8BDD\u5374\u6CA1\u7ED9 id \u7684\u65F6\u5019\u3002\n\u6309\u6700\u540E\u6D3B\u52A8\u5012\u5E8F\uFF0C\u6BCF\u6BB5\u7ED9 sessionId + \u6807\u9898 + \u65F6\u95F4 + \u6D88\u606F\u6570\uFF1B\u62FF\u5230 id \u518D\u7528 session_messages_search \u8FDB\u53BB\u770B\u3002\n\u9ED8\u8BA4\u4E0D\u5217\u5F52\u6863\u7684\u4F1A\u8BDD\uFF08\u5F52\u6863\u662F\u300C\u7528\u6237\u628A\u5B83\u6536\u8D77\u6765\u4E86\u300D\uFF09\u2014\u2014 \u8981\u627E\u5F52\u6863\u7684\u5C31\u4F20 archived\u3002\n\u9ED8\u8BA4\u53EA\u5217\u5F53\u524D\u5DE5\u4F5C\u533A\u7684\u4F1A\u8BDD\uFF0C\u5916\u52A0\u672C\u4F1A\u8BDD fork \u51FA\u6765\u7684\u90A3\u6761\u8840\u7F18\u94FE\u3002",
3691
+ get parameters() {
3692
+ return {
3693
+ type: "object",
3694
+ properties: {
3695
+ limit: {
3696
+ type: "number",
3697
+ description: `\u56DE\u51E0\u6BB5\uFF0C\u9ED8\u8BA4 ${DEFAULT_LIMIT}\uFF0C\u4E0A\u9650 ${MAX_LIMIT}\uFF08\u4F20\u5927\u4E86\u81EA\u52A8\u5939\u7D27\uFF0C\u4E0D\u62A5\u9519\uFF09`
3698
+ },
3699
+ archived: {
3700
+ type: "string",
3701
+ enum: ["exclude", "only", "all"],
3702
+ description: "exclude \u4E0D\u5217\u5F52\u6863\u7684\uFF08\u9ED8\u8BA4\uFF09\uFF1Bonly \u53EA\u5217\u5F52\u6863\u7684\uFF1Ball \u4E24\u8FB9\u90FD\u5217\u3002\u7528\u6237\u63D0\u8D77\u4E00\u6BB5\u300C\u4EE5\u524D\u5F52\u6863\u6389\u7684\u4F1A\u8BDD\u300D\u65F6\u7528 only"
3703
+ },
3704
+ ...deps.allowAllWorkspaces ? {
3705
+ allWorkspaces: {
3706
+ type: "boolean",
3707
+ description: "\u5217\u5168\u90E8\u5DE5\u4F5C\u533A\u7684\u4F1A\u8BDD\uFF08\u9ED8\u8BA4 false\uFF0C\u53EA\u5217\u5F53\u524D\u5DE5\u4F5C\u533A\uFF09"
3708
+ }
3709
+ } : {}
3710
+ },
3711
+ required: []
3712
+ };
3713
+ },
3714
+ exposure: ToolExposure.Direct,
3715
+ supportsParallel: true,
3716
+ annotations: {
3717
+ readOnlyHint: true,
3718
+ destructiveHint: false,
3719
+ idempotentHint: true,
3720
+ openWorldHint: true
3721
+ },
3722
+ operation: "file_read",
3723
+ describeTarget: (args) => `${SESSION_LIST_TOOL}:${archivedScopeOf(args["archived"])}`,
3724
+ execute(args, ctx) {
3725
+ const start = Date.now();
3726
+ const allWorkspaces = deps.allowAllWorkspaces === true && args["allWorkspaces"] === true;
3727
+ const archived = archivedScopeOf(args["archived"]);
3728
+ try {
3729
+ const out = deps.queries.listSessions({
3730
+ limit: clampLimit(args["limit"]),
3731
+ archived,
3732
+ currentSessionId: ctx.sessionId,
3733
+ workspaceCwd: deps.workspaceCwd,
3734
+ allWorkspaces
3735
+ });
3736
+ if (out.sessions.length === 0) {
3737
+ const hint = archived === "exclude" ? ' If the user is asking about a session they archived, retry with archived: "only".' : "";
3738
+ return Promise.resolve({
3739
+ success: true,
3740
+ output: `No session in readable scope (${scopeNote(allWorkspaces, archived)}).${hint}`,
3741
+ duration: Date.now() - start
3742
+ });
3743
+ }
3744
+ return Promise.resolve({
3745
+ success: true,
3746
+ output: render(out, scopeNote(allWorkspaces, archived)),
3747
+ duration: Date.now() - start
3748
+ });
3749
+ } catch (err) {
3750
+ return Promise.resolve(fail3(err instanceof Error ? err.message : String(err), start, 8014));
3751
+ }
3752
+ }
3753
+ };
3754
+ }
3598
3755
  var DEFAULT_WINDOW = 2;
3599
3756
  var MAX_WINDOW = 10;
3600
3757
  var DEFAULT_TRACE_LIMIT = 20;
@@ -3602,7 +3759,7 @@ var MAX_TRACE_LIMIT = 50;
3602
3759
  var BODY_BUDGET = 1500;
3603
3760
  var MIN_BODY = 60;
3604
3761
  var ANCHOR_SHARE = 4;
3605
- function fail3(message, start, code, suggestion) {
3762
+ function fail4(message, start, code, suggestion) {
3606
3763
  return {
3607
3764
  success: false,
3608
3765
  output: "",
@@ -3616,7 +3773,7 @@ function clampWindow(raw, fallback2, max) {
3616
3773
  if (!Number.isFinite(n) || n < 0) return fallback2;
3617
3774
  return Math.min(Math.floor(n), max);
3618
3775
  }
3619
- function stamp(ms) {
3776
+ function stamp2(ms) {
3620
3777
  return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
3621
3778
  }
3622
3779
  function budgetOf(total) {
@@ -3638,7 +3795,7 @@ function surfaceOf(record, isCurrent) {
3638
3795
  function renderRecord(record, mark, budget, isCurrent) {
3639
3796
  const tool = record.toolName ? ` \xB7 ${record.toolName}` : "";
3640
3797
  return [
3641
- `${mark} #${record.messageId} [${record.role}${tool} \xB7 ${stamp(record.timestamp)} \xB7 ${surfaceOf(record, isCurrent)}]`,
3798
+ `${mark} #${record.messageId} [${record.role}${tool} \xB7 ${stamp2(record.timestamp)} \xB7 ${surfaceOf(record, isCurrent)}]`,
3642
3799
  ` ${body(record.content, budget).split("\n").join("\n ")}`
3643
3800
  ].join("\n");
3644
3801
  }
@@ -3704,11 +3861,11 @@ before / after \u5404\u9ED8\u8BA4 ${DEFAULT_WINDOW}\u3001\u4E0A\u9650 ${MAX_WIND
3704
3861
  execute(args, ctx) {
3705
3862
  const start = Date.now();
3706
3863
  const sessionId = String(args["sessionId"] ?? "").trim();
3707
- if (!sessionId) return Promise.resolve(fail3("sessionId must not be empty", start, 8007));
3864
+ if (!sessionId) return Promise.resolve(fail4("sessionId must not be empty", start, 8007));
3708
3865
  const messageId = Number(args["messageId"]);
3709
3866
  if (!Number.isInteger(messageId) || messageId <= 0) {
3710
3867
  return Promise.resolve(
3711
- fail3(
3868
+ fail4(
3712
3869
  "messageId must be a positive integer",
3713
3870
  start,
3714
3871
  8008,
@@ -3729,7 +3886,7 @@ before / after \u5404\u9ED8\u8BA4 ${DEFAULT_WINDOW}\u3001\u4E0A\u9650 ${MAX_WIND
3729
3886
  });
3730
3887
  if (!out.authorized) {
3731
3888
  return Promise.resolve(
3732
- fail3(
3889
+ fail4(
3733
3890
  `session ${sessionId} is out of readable scope`,
3734
3891
  start,
3735
3892
  8009,
@@ -3739,7 +3896,7 @@ before / after \u5404\u9ED8\u8BA4 ${DEFAULT_WINDOW}\u3001\u4E0A\u9650 ${MAX_WIND
3739
3896
  }
3740
3897
  if (!out.anchor) {
3741
3898
  return Promise.resolve(
3742
- fail3(
3899
+ fail4(
3743
3900
  `session ${sessionId} has no message #${messageId} (it has ${out.total})`,
3744
3901
  start,
3745
3902
  8010,
@@ -3753,7 +3910,7 @@ before / after \u5404\u9ED8\u8BA4 ${DEFAULT_WINDOW}\u3001\u4E0A\u9650 ${MAX_WIND
3753
3910
  duration: Date.now() - start
3754
3911
  });
3755
3912
  } catch (err) {
3756
- return Promise.resolve(fail3(err instanceof Error ? err.message : String(err), start, 8011));
3913
+ return Promise.resolve(fail4(err instanceof Error ? err.message : String(err), start, 8011));
3757
3914
  }
3758
3915
  }
3759
3916
  };
@@ -3767,7 +3924,7 @@ function renderNode(node, isCurrent) {
3767
3924
  const where = node.cwd ? ` \xB7 ${node.cwd}` : "";
3768
3925
  const archived = node.archived ? " \xB7 archived" : "";
3769
3926
  return `${indent}${node.sessionId}${self} \u2014 ${node.title || "(untitled)"}
3770
- ${indent} ${stamp(node.startedAt ?? 0)} \xB7 ${node.messageCount ?? 0} messages${where}${archived}`;
3927
+ ${indent} ${stamp2(node.startedAt ?? 0)} \xB7 ${node.messageCount ?? 0} messages${where}${archived}`;
3771
3928
  }
3772
3929
  function renderTrace(trace, currentSessionId) {
3773
3930
  const anchor = trace.anchor;
@@ -3846,7 +4003,7 @@ function createSessionTraceTool(deps) {
3846
4003
  });
3847
4004
  if (!trace.authorized) {
3848
4005
  return Promise.resolve(
3849
- fail3(
4006
+ fail4(
3850
4007
  `session ${sessionId} is out of readable scope`,
3851
4008
  start,
3852
4009
  8012,
@@ -3860,14 +4017,14 @@ function createSessionTraceTool(deps) {
3860
4017
  duration: Date.now() - start
3861
4018
  });
3862
4019
  } catch (err) {
3863
- return Promise.resolve(fail3(err instanceof Error ? err.message : String(err), start, 8013));
4020
+ return Promise.resolve(fail4(err instanceof Error ? err.message : String(err), start, 8013));
3864
4021
  }
3865
4022
  }
3866
4023
  };
3867
4024
  }
3868
- var DEFAULT_LIMIT = 5;
3869
- var MAX_LIMIT = 20;
3870
- function fail4(message, start, code, suggestion) {
4025
+ var DEFAULT_LIMIT2 = 5;
4026
+ var MAX_LIMIT2 = 20;
4027
+ function fail5(message, start, code, suggestion) {
3871
4028
  return {
3872
4029
  success: false,
3873
4030
  output: "",
@@ -3878,12 +4035,12 @@ function fail4(message, start, code, suggestion) {
3878
4035
  function emptyHint(query) {
3879
4036
  return belowTrigramFloor(sanitizeFtsQuery(query)) ? ` Note: every word here is shorter than ${TRIGRAM_MIN_CHARS} characters, which the substring index cannot match at all \u2014 this is "cannot search", not "not found". Retry with a longer phrase.` : " Try fewer or different words.";
3880
4037
  }
3881
- function clampLimit(raw) {
4038
+ function clampLimit2(raw) {
3882
4039
  const n = Number(raw);
3883
- if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
3884
- return Math.min(Math.floor(n), MAX_LIMIT);
4040
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT2;
4041
+ return Math.min(Math.floor(n), MAX_LIMIT2);
3885
4042
  }
3886
- function stamp2(ms) {
4043
+ function stamp3(ms) {
3887
4044
  return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
3888
4045
  }
3889
4046
  function surfaceNote(hit, isCurrent) {
@@ -3897,14 +4054,14 @@ function renderSessionHit(hit, index) {
3897
4054
  const self = hit.isCurrent ? " (this session)" : "";
3898
4055
  return [
3899
4056
  `${index + 1}. ${hit.sessionId}${self} \u2014 ${hit.title || "(untitled)"}`,
3900
- ` ${stamp2(hit.startedAt)} \xB7 ${hit.messageCount} messages${where}`,
4057
+ ` ${stamp3(hit.startedAt)} \xB7 ${hit.messageCount} messages${where}`,
3901
4058
  ` best match [${hit.best.role} #${hit.best.messageId}, ${surfaceNote(hit.best, hit.isCurrent)}]:`,
3902
4059
  ` ${hit.best.snippet}`
3903
4060
  ].join("\n");
3904
4061
  }
3905
4062
  function renderMessageHit(hit, isCurrent) {
3906
4063
  return [
3907
- `#${hit.messageId} [${hit.role} \xB7 ${stamp2(hit.timestamp)} \xB7 ${surfaceNote(hit, isCurrent)}]`,
4064
+ `#${hit.messageId} [${hit.role} \xB7 ${stamp3(hit.timestamp)} \xB7 ${surfaceNote(hit, isCurrent)}]`,
3908
4065
  ` ${hit.snippet}`
3909
4066
  ].join("\n");
3910
4067
  }
@@ -3922,7 +4079,7 @@ function createSessionSearchTool(deps) {
3922
4079
  },
3923
4080
  limit: {
3924
4081
  type: "number",
3925
- description: `\u8FD4\u56DE\u51E0\u6BB5\u4F1A\u8BDD\uFF0C\u9ED8\u8BA4 ${DEFAULT_LIMIT}\uFF0C\u4E0A\u9650 ${MAX_LIMIT}`
4082
+ description: `\u8FD4\u56DE\u51E0\u6BB5\u4F1A\u8BDD\uFF0C\u9ED8\u8BA4 ${DEFAULT_LIMIT2}\uFF0C\u4E0A\u9650 ${MAX_LIMIT2}`
3926
4083
  },
3927
4084
  scope: {
3928
4085
  type: "string",
@@ -3952,13 +4109,13 @@ function createSessionSearchTool(deps) {
3952
4109
  execute(args, ctx) {
3953
4110
  const start = Date.now();
3954
4111
  const query = String(args["query"] ?? "").trim();
3955
- if (!query) return Promise.resolve(fail4("query must not be empty", start, 8001));
4112
+ if (!query) return Promise.resolve(fail5("query must not be empty", start, 8001));
3956
4113
  const allWorkspaces = deps.allowAllWorkspaces === true && args["allWorkspaces"] === true;
3957
4114
  const scope = args["scope"] === "title" ? "title" : "all";
3958
4115
  try {
3959
4116
  const hits = deps.queries.searchSessions({
3960
4117
  query,
3961
- limit: clampLimit(args["limit"]),
4118
+ limit: clampLimit2(args["limit"]),
3962
4119
  scope,
3963
4120
  currentSessionId: ctx.sessionId,
3964
4121
  workspaceCwd: deps.workspaceCwd,
@@ -3985,7 +4142,7 @@ function createSessionSearchTool(deps) {
3985
4142
  });
3986
4143
  } catch (err) {
3987
4144
  return Promise.resolve(
3988
- fail4(
4145
+ fail5(
3989
4146
  err instanceof Error ? err.message : String(err),
3990
4147
  start,
3991
4148
  8002,
@@ -4014,7 +4171,7 @@ function createSessionMessagesSearchTool(deps) {
4014
4171
  },
4015
4172
  limit: {
4016
4173
  type: "number",
4017
- description: `\u8FD4\u56DE\u51E0\u6761\uFF0C\u9ED8\u8BA4 ${DEFAULT_LIMIT}\uFF0C\u4E0A\u9650 ${MAX_LIMIT}`
4174
+ description: `\u8FD4\u56DE\u51E0\u6761\uFF0C\u9ED8\u8BA4 ${DEFAULT_LIMIT2}\uFF0C\u4E0A\u9650 ${MAX_LIMIT2}`
4018
4175
  },
4019
4176
  ...deps.allowAllWorkspaces ? {
4020
4177
  allWorkspaces: {
@@ -4040,21 +4197,21 @@ function createSessionMessagesSearchTool(deps) {
4040
4197
  const start = Date.now();
4041
4198
  const sessionId = String(args["sessionId"] ?? "").trim();
4042
4199
  const query = String(args["query"] ?? "").trim();
4043
- if (!sessionId) return Promise.resolve(fail4("sessionId must not be empty", start, 8003));
4044
- if (!query) return Promise.resolve(fail4("query must not be empty", start, 8004));
4200
+ if (!sessionId) return Promise.resolve(fail5("sessionId must not be empty", start, 8003));
4201
+ if (!query) return Promise.resolve(fail5("query must not be empty", start, 8004));
4045
4202
  const allWorkspaces = deps.allowAllWorkspaces === true && args["allWorkspaces"] === true;
4046
4203
  try {
4047
4204
  const out = deps.queries.searchMessages({
4048
4205
  sessionId,
4049
4206
  query,
4050
- limit: clampLimit(args["limit"]),
4207
+ limit: clampLimit2(args["limit"]),
4051
4208
  currentSessionId: ctx.sessionId,
4052
4209
  workspaceCwd: deps.workspaceCwd,
4053
4210
  allWorkspaces
4054
4211
  });
4055
4212
  if (!out.authorized) {
4056
4213
  return Promise.resolve(
4057
- fail4(
4214
+ fail5(
4058
4215
  `session ${sessionId} is out of searchable scope`,
4059
4216
  start,
4060
4217
  8005,
@@ -4081,7 +4238,7 @@ function createSessionMessagesSearchTool(deps) {
4081
4238
  });
4082
4239
  } catch (err) {
4083
4240
  return Promise.resolve(
4084
- fail4(
4241
+ fail5(
4085
4242
  err instanceof Error ? err.message : String(err),
4086
4243
  start,
4087
4244
  8006,
@@ -4097,12 +4254,13 @@ function createSessionSearchTools(deps) {
4097
4254
  createSessionSearchTool(deps),
4098
4255
  createSessionMessagesSearchTool(deps),
4099
4256
  createSessionMessagesReadTool(deps),
4100
- createSessionTraceTool(deps)
4257
+ createSessionTraceTool(deps),
4258
+ createSessionListTool(deps)
4101
4259
  ];
4102
4260
  }
4103
4261
  var SKILL_VIEW_TOOL = "skill_view";
4104
4262
  var MAX_SUGGESTED = 20;
4105
- function fail5(message, start, code, suggestion) {
4263
+ function fail6(message, start, code, suggestion) {
4106
4264
  return {
4107
4265
  success: false,
4108
4266
  output: "",
@@ -4147,18 +4305,18 @@ function createSkillViewTool(deps) {
4147
4305
  const name = String(args["name"] ?? "").trim();
4148
4306
  if (!name) {
4149
4307
  return Promise.resolve(
4150
- fail5("name must not be empty", start, 3001, describeAvailableSkills(deps.skills))
4308
+ fail6("name must not be empty", start, 3001, describeAvailableSkills(deps.skills))
4151
4309
  );
4152
4310
  }
4153
4311
  const body2 = deps.skills.view(name);
4154
4312
  if (body2 === null) {
4155
4313
  return Promise.resolve(
4156
- fail5(`skill "${name}" not found`, start, 3002, describeAvailableSkills(deps.skills))
4314
+ fail6(`skill "${name}" not found`, start, 3002, describeAvailableSkills(deps.skills))
4157
4315
  );
4158
4316
  }
4159
4317
  if (body2.trim() === "") {
4160
4318
  return Promise.resolve(
4161
- fail5(
4319
+ fail6(
4162
4320
  `skill "${name}" has an empty body \u2014 its SKILL.md is missing or empty`,
4163
4321
  start,
4164
4322
  3003,
@@ -4285,7 +4443,7 @@ var SKILL_EDIT_TOOL = "skill_edit";
4285
4443
  var SKILL_PATCH_TOOL = "skill_patch";
4286
4444
  var SKILL_WRITE_FILE_TOOL = "skill_write_file";
4287
4445
  var WRITABLE_SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
4288
- function fail6(message, start, code, suggestion) {
4446
+ function fail7(message, start, code, suggestion) {
4289
4447
  return {
4290
4448
  success: false,
4291
4449
  output: "",
@@ -4297,7 +4455,7 @@ function done(output, start) {
4297
4455
  return { success: true, output, duration: Date.now() - start };
4298
4456
  }
4299
4457
  function fromThrow(err, start, suggestion) {
4300
- return fail6(err instanceof Error ? err.message : String(err), start, 3102, suggestion);
4458
+ return fail7(err instanceof Error ? err.message : String(err), start, 3102, suggestion);
4301
4459
  }
4302
4460
  function str2(args, key) {
4303
4461
  const raw = args[key];
@@ -4353,13 +4511,13 @@ function createTool(deps) {
4353
4511
  const content = str2(args, "content");
4354
4512
  const category = str2(args, "category").trim() || "general";
4355
4513
  if (!WRITABLE_SKILL_NAME.test(name)) {
4356
- return fail6(`invalid skill name: ${JSON.stringify(name)}`, start, 3101, WRITABLE_HINT);
4514
+ return fail7(`invalid skill name: ${JSON.stringify(name)}`, start, 3101, WRITABLE_HINT);
4357
4515
  }
4358
4516
  if (!WRITABLE_SKILL_NAME.test(category)) {
4359
- return fail6(`invalid category: ${JSON.stringify(category)}`, start, 3101, WRITABLE_HINT);
4517
+ return fail7(`invalid category: ${JSON.stringify(category)}`, start, 3101, WRITABLE_HINT);
4360
4518
  }
4361
4519
  const bad2 = checkFrontmatter(name, content);
4362
- if (bad2 !== null) return fail6(bad2, start, 3103);
4520
+ if (bad2 !== null) return fail7(bad2, start, 3103);
4363
4521
  try {
4364
4522
  const path = await deps.skills.create(name, content, category);
4365
4523
  return done(`created skill "${name}" at ${path}`, start);
@@ -4391,10 +4549,10 @@ function editTool(deps) {
4391
4549
  const name = str2(args, "name").trim();
4392
4550
  const content = str2(args, "content");
4393
4551
  if (!name) {
4394
- return fail6("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4552
+ return fail7("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4395
4553
  }
4396
4554
  const bad2 = checkFrontmatter(name, content);
4397
- if (bad2 !== null) return fail6(bad2, start, 3103);
4555
+ if (bad2 !== null) return fail7(bad2, start, 3103);
4398
4556
  try {
4399
4557
  await deps.skills.edit(name, content);
4400
4558
  return done(`rewrote SKILL.md of skill "${name}"`, start);
@@ -4432,10 +4590,10 @@ function patchTool(deps) {
4432
4590
  const oldString = str2(args, "old_string");
4433
4591
  const filePath = str2(args, "file_path").trim();
4434
4592
  if (!name) {
4435
- return fail6("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4593
+ return fail7("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4436
4594
  }
4437
4595
  if (oldString === "") {
4438
- return fail6("old_string must not be empty", start, 3101);
4596
+ return fail7("old_string must not be empty", start, 3101);
4439
4597
  }
4440
4598
  try {
4441
4599
  await deps.skills.patch(
@@ -4477,9 +4635,9 @@ function writeFileTool(deps) {
4477
4635
  const name = str2(args, "name").trim();
4478
4636
  const filePath = str2(args, "file_path").trim();
4479
4637
  if (!name) {
4480
- return fail6("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4638
+ return fail7("name must not be empty", start, 3101, describeAvailableSkills(deps.skills));
4481
4639
  }
4482
- if (!filePath) return fail6("file_path must not be empty", start, 3101);
4640
+ if (!filePath) return fail7("file_path must not be empty", start, 3101);
4483
4641
  try {
4484
4642
  await deps.skills.writeFile(name, filePath, str2(args, "content"));
4485
4643
  return done(`wrote ${filePath} into skill "${name}"`, start);
@@ -4495,7 +4653,7 @@ function createSkillWriteTools(deps) {
4495
4653
  var TOOL_SEARCH_TOOL = "tool_search";
4496
4654
  var MAX_LISTED = 40;
4497
4655
  var MAX_REVEAL_PER_CALL = 10;
4498
- function fail7(message, start, code, suggestion) {
4656
+ function fail8(message, start, code, suggestion) {
4499
4657
  return {
4500
4658
  success: false,
4501
4659
  output: "",
@@ -4545,12 +4703,12 @@ function createToolSearchTool(deps) {
4545
4703
  const pool = deps.deferred();
4546
4704
  const raw = String(args["query"] ?? "").trim();
4547
4705
  if (!raw) {
4548
- return Promise.resolve(fail7("query must not be empty", start, 3101, available(pool)));
4706
+ return Promise.resolve(fail8("query must not be empty", start, 3101, available(pool)));
4549
4707
  }
4550
4708
  const terms = raw.toLowerCase().split(/\s+/).filter(Boolean);
4551
4709
  const hits = pool.filter((tool) => matches(tool, terms));
4552
4710
  if (hits.length === 0) {
4553
- return Promise.resolve(fail7(`No tool matches "${raw}"`, start, 3102, available(pool)));
4711
+ return Promise.resolve(fail8(`No tool matches "${raw}"`, start, 3102, available(pool)));
4554
4712
  }
4555
4713
  const picked = hits.slice(0, MAX_REVEAL_PER_CALL);
4556
4714
  for (const tool of picked) deps.reveal.reveal(tool.name);
@@ -5106,7 +5264,7 @@ function createRoleFile(input, opts) {
5106
5264
  const detail = checked.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
5107
5265
  return { ok: false, reason: "invalid-field", detail };
5108
5266
  }
5109
- const text = render(fm, input.prompt ?? "");
5267
+ const text = render2(fm, input.prompt ?? "");
5110
5268
  const file = `${name}.md`;
5111
5269
  const back = parseRoleDefinition(text, file, opts.source ?? "user");
5112
5270
  if (!back.role) {
@@ -5138,7 +5296,7 @@ function frontmatterOf(name, input) {
5138
5296
  ...input.maxTurns === void 0 ? {} : { maxTurns: input.maxTurns }
5139
5297
  };
5140
5298
  }
5141
- function render(fm, prompt) {
5299
+ function render2(fm, prompt) {
5142
5300
  const head = dump(fm, { lineWidth: -1, noRefs: true });
5143
5301
  const body2 = prompt.trim();
5144
5302
  return `---
@@ -6363,30 +6521,41 @@ function runCommand(file, args, opts = {}) {
6363
6521
  const spawnFile = viaCmd ? process.env["ComSpec"] ?? "cmd.exe" : file;
6364
6522
  const spawnArgs = viaCmd ? ["/d", "/s", "/c", cmdLine(file, args)] : [...args];
6365
6523
  return new Promise((resolvePromise) => {
6366
- execFile(
6367
- spawnFile,
6368
- spawnArgs,
6369
- {
6370
- timeout: opts.timeoutMs ?? COMMAND_TIMEOUT_MS,
6371
- maxBuffer: MAX_OUTPUT_BYTES,
6372
- windowsHide: true,
6373
- windowsVerbatimArguments: viaCmd,
6374
- ...opts.cwd ? { cwd: opts.cwd } : {}
6375
- },
6376
- (err, stdout, stderr) => {
6377
- const out = String(stdout ?? "");
6378
- const errOut = String(stderr ?? "");
6379
- if (!err) {
6380
- resolvePromise({ ok: true, stdout: out, stderr: errOut });
6381
- return;
6524
+ guardSpawn(
6525
+ () => execFile(
6526
+ spawnFile,
6527
+ spawnArgs,
6528
+ {
6529
+ timeout: opts.timeoutMs ?? COMMAND_TIMEOUT_MS,
6530
+ maxBuffer: MAX_OUTPUT_BYTES,
6531
+ windowsHide: true,
6532
+ windowsVerbatimArguments: viaCmd,
6533
+ ...opts.cwd ? { cwd: opts.cwd } : {}
6534
+ },
6535
+ (err, stdout, stderr) => {
6536
+ const out = String(stdout ?? "");
6537
+ const errOut = String(stderr ?? "");
6538
+ if (!err) {
6539
+ resolvePromise({ ok: true, stdout: out, stderr: errOut });
6540
+ return;
6541
+ }
6542
+ const detail = describe3(file, err, errOut);
6543
+ resolvePromise({
6544
+ ok: false,
6545
+ detail,
6546
+ stdout: out,
6547
+ stderr: errOut,
6548
+ ...err.code === "ENOENT" ? { notFound: true } : {}
6549
+ });
6382
6550
  }
6383
- const detail = describe3(file, err, errOut);
6551
+ ),
6552
+ (err) => {
6384
6553
  resolvePromise({
6385
6554
  ok: false,
6386
- detail,
6387
- stdout: out,
6388
- stderr: errOut,
6389
- ...err.code === "ENOENT" ? { notFound: true } : {}
6555
+ detail: describe3(file, err, ""),
6556
+ stdout: "",
6557
+ stderr: "",
6558
+ ...err.code === "ENOENT" || err.code === "ENOTDIR" ? { notFound: true } : {}
6390
6559
  });
6391
6560
  }
6392
6561
  );
@@ -6999,11 +7168,29 @@ var MARKETPLACE_DISPLAY_FIELDS = [
6999
7168
  var DisplaySchema = z.object({
7000
7169
  fields: z.array(z.string().min(1)).optional()
7001
7170
  });
7171
+ var CategoryNodeSchema = z.lazy(
7172
+ () => z.object({
7173
+ id: z.string().min(1),
7174
+ label: z.string().min(1).optional(),
7175
+ children: z.array(CategoryNodeSchema).optional()
7176
+ })
7177
+ );
7178
+ var CategoriesSchema = z.array(CategoryNodeSchema).refine(hasUniqueCategoryIds, { message: "category ids must be unique across the whole tree" }).optional().catch(void 0);
7179
+ function hasUniqueCategoryIds(nodes) {
7180
+ const seen = /* @__PURE__ */ new Set();
7181
+ const walk3 = (list) => list.every((node) => {
7182
+ if (seen.has(node.id)) return false;
7183
+ seen.add(node.id);
7184
+ return node.children === void 0 || walk3(node.children);
7185
+ });
7186
+ return walk3(nodes);
7187
+ }
7002
7188
  var CatalogSchema = z.object({
7003
7189
  name: z.string().regex(NAME_PATTERN),
7004
7190
  description: z.string().min(1).optional(),
7005
7191
  owner: z.object({ name: z.string().min(1), url: z.string().min(1).optional() }).optional(),
7006
7192
  display: DisplaySchema.optional(),
7193
+ categories: CategoriesSchema,
7007
7194
  plugins: z.array(EntrySchema)
7008
7195
  });
7009
7196
  var RecordSchema2 = z.object({
@@ -7019,13 +7206,17 @@ var StateSchema2 = z.object({
7019
7206
  function projectEntryForDisplay(entry, display) {
7020
7207
  const allowed = display?.fields;
7021
7208
  if (allowed === void 0) return entry;
7022
- const keep = new Set(allowed);
7023
7209
  const out = { ...entry };
7024
7210
  for (const field of MARKETPLACE_DISPLAY_FIELDS) {
7025
- if (!keep.has(field)) delete out[field];
7211
+ if (!displayAllows(display, field)) delete out[field];
7026
7212
  }
7027
7213
  return out;
7028
7214
  }
7215
+ function displayAllows(display, field) {
7216
+ const allowed = display?.fields;
7217
+ if (allowed === void 0) return true;
7218
+ return allowed.includes(field);
7219
+ }
7029
7220
  function readMarketplaces(statePath) {
7030
7221
  if (!existsSync(statePath)) return { records: [], issues: [] };
7031
7222
  let raw;
@@ -9246,7 +9437,18 @@ function gitProbeEnv() {
9246
9437
  delete env.GIT_WORK_TREE;
9247
9438
  return env;
9248
9439
  }
9440
+ function insideGitRepo(rootDir) {
9441
+ let dir = rootDir;
9442
+ for (; ; ) {
9443
+ if (existsSync(join(dir, ".git"))) return true;
9444
+ const parent = dirname(dir);
9445
+ if (parent === dir) return false;
9446
+ dir = parent;
9447
+ }
9448
+ }
9249
9449
  function getGitStatus(rootDir) {
9450
+ if (!insideGitRepo(rootDir)) return void 0;
9451
+ if (!canProbeGit()) return void 0;
9250
9452
  const quiet = ["ignore", "pipe", "ignore"];
9251
9453
  const env = gitProbeEnv();
9252
9454
  try {
@@ -11471,6 +11673,9 @@ var AgentLoop = class {
11471
11673
  let blankAfterNudge = false;
11472
11674
  for (let turn = 1; turn <= maxTurns; turn++) {
11473
11675
  if (this.isAborted(input.signal)) {
11676
+ if (turns.length > 0 || lastText) {
11677
+ yield { type: "notice", code: "aborted", text: t("agent_run.aborted") };
11678
+ }
11474
11679
  yield finish("aborted");
11475
11680
  await this.prompt.syncMemory(userText, lastText);
11476
11681
  return;
@@ -11902,6 +12107,12 @@ ${finalText}` };
11902
12107
  });
11903
12108
  yield finish("max-turns");
11904
12109
  } catch (err) {
12110
+ if (this.isAborted(input.signal)) {
12111
+ await this.prompt.syncMemory(userText, lastText);
12112
+ yield { type: "notice", code: "aborted", text: t("agent_run.aborted") };
12113
+ yield finish("aborted");
12114
+ return;
12115
+ }
11905
12116
  const msg = err instanceof Error ? err.message : String(err);
11906
12117
  runSpan.recordError(err);
11907
12118
  await this.prompt.syncMemory(userText, "");
@@ -12148,6 +12359,10 @@ var SessionsSchema = z.object({
12148
12359
  retentionDays: z.int().positive().max(3650).default(90),
12149
12360
  vacuumAfterPrune: z.boolean().default(true)
12150
12361
  });
12362
+ var AutomationSchema = z.object({
12363
+ keepRuns: z.int().positive().max(1e4).default(200),
12364
+ maxRecordingMb: z.int().positive().max(1e5).default(64)
12365
+ });
12151
12366
  var TelemetrySchema = z.object({
12152
12367
  enabled: z.boolean().optional(),
12153
12368
  target: z.enum(["file", "console", "otlp"]).optional(),
@@ -12186,6 +12401,7 @@ var EpochConfigSchema = z.object({
12186
12401
  memory: MemorySchema.prefault({}),
12187
12402
  artifacts: ArtifactsSchema.prefault({}),
12188
12403
  sessions: SessionsSchema.prefault({}),
12404
+ automation: AutomationSchema.prefault({}),
12189
12405
  budget: BudgetSchema.optional(),
12190
12406
  compliance: ComplianceSchema.optional(),
12191
12407
  headless: HeadlessSchema.optional(),
@@ -14246,6 +14462,9 @@ var SETTING_WRITE_KEYS = [
14246
14462
  "sessions.autoPrune",
14247
14463
  "sessions.retentionDays",
14248
14464
  "sessions.vacuumAfterPrune",
14465
+ "automation.keepRuns",
14466
+ "automation.maxRecordingMb",
14467
+ "sessionSearch.allWorkspaces",
14249
14468
  "permission"
14250
14469
  ];
14251
14470
  function isWritableSettingKey(value) {
@@ -14313,6 +14532,9 @@ var VALUE_SCHEMA = {
14313
14532
  "sessions.autoPrune": EpochConfigSchema.shape.sessions.unwrap().shape.autoPrune.unwrap(),
14314
14533
  "sessions.retentionDays": EpochConfigSchema.shape.sessions.unwrap().shape.retentionDays.unwrap(),
14315
14534
  "sessions.vacuumAfterPrune": EpochConfigSchema.shape.sessions.unwrap().shape.vacuumAfterPrune.unwrap(),
14535
+ "automation.keepRuns": EpochConfigSchema.shape.automation.unwrap().shape.keepRuns.unwrap(),
14536
+ "automation.maxRecordingMb": EpochConfigSchema.shape.automation.unwrap().shape.maxRecordingMb.unwrap(),
14537
+ "sessionSearch.allWorkspaces": EpochConfigSchema.shape.sessionSearch.unwrap().shape.allWorkspaces.unwrap(),
14316
14538
  permission: EpochConfigSchema.shape.permission.unwrap()
14317
14539
  };
14318
14540
  function settingValueKind(key) {
@@ -15245,8 +15467,8 @@ var MemoryManager = class {
15245
15467
  return "";
15246
15468
  }
15247
15469
  }
15248
- if (content.length > MAX_ENTRY_CHARS) {
15249
- return content.slice(0, MAX_ENTRY_CHARS) + " \u2026[\u6761\u76EE\u8FC7\u957F\u5DF2\u622A\u65AD]";
15470
+ if (countCodePoints(content) > MAX_ENTRY_CHARS) {
15471
+ return headCodePoints(content, MAX_ENTRY_CHARS) + " \u2026[\u6761\u76EE\u8FC7\u957F\u5DF2\u622A\u65AD]";
15250
15472
  }
15251
15473
  return content;
15252
15474
  }
@@ -15256,7 +15478,7 @@ var MemoryManager = class {
15256
15478
  const id = this.idByContent.get(contentKey(target, content));
15257
15479
  if (!id) continue;
15258
15480
  const firstLine4 = (content.split("\n")[0] ?? "").trim();
15259
- const summary = firstLine4.length > INDEX_SUMMARY_CHARS ? `${firstLine4.slice(0, INDEX_SUMMARY_CHARS)}\u2026` : firstLine4;
15481
+ const summary = countCodePoints(firstLine4) > INDEX_SUMMARY_CHARS ? `${headCodePoints(firstLine4, INDEX_SUMMARY_CHARS)}\u2026` : firstLine4;
15260
15482
  out.push({ id, target, summary, charLength: content.length });
15261
15483
  }
15262
15484
  return out;
@@ -15491,6 +15713,17 @@ function textSimilarity(a, b) {
15491
15713
  const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
15492
15714
  return intersection / union.size;
15493
15715
  }
15716
+ var ATTACHMENT_RETENTION_DEFAULTS = {
15717
+ maxAgeDays: 7,
15718
+ maxTotalBytes: ATTACHMENT_MAX_TOTAL_BYTES
15719
+ };
15720
+ function pruneAttachments(root, keepSession, retention = ATTACHMENT_RETENTION_DEFAULTS, now = Date.now()) {
15721
+ const shared = retention;
15722
+ return pruneArtifacts(root, keepSession, shared, now);
15723
+ }
15724
+ async function removeSessionAttachments(sessionId, homeDir) {
15725
+ await rm(attachmentsDir(sessionId, homeDir), { recursive: true, force: true });
15726
+ }
15494
15727
  // src/goal/store.ts
15495
15728
  function toGoal(row) {
15496
15729
  const phase = isGoalPhase(row.phase) ? row.phase : "blocked";
@@ -15644,6 +15877,52 @@ function restoreParts(parts, opts) {
15644
15877
  }
15645
15878
  });
15646
15879
  }
15880
+ function importParts(parts) {
15881
+ const out = [];
15882
+ for (const part of parts) {
15883
+ if (part.type === "text") {
15884
+ out.push({ type: "text", text: part.text });
15885
+ continue;
15886
+ }
15887
+ if (part.type === "file") {
15888
+ out.push({
15889
+ type: "file",
15890
+ path: part.path,
15891
+ ...part.omitted ? { omitted: part.omitted } : {},
15892
+ ...part.bytes === void 0 ? {} : { bytes: part.bytes }
15893
+ });
15894
+ continue;
15895
+ }
15896
+ if (part.type === "session") {
15897
+ out.push({
15898
+ type: "session",
15899
+ ref: part.ref,
15900
+ ...part.sessionId ? { sessionId: part.sessionId } : {},
15901
+ ...part.title ? { title: part.title } : {},
15902
+ ...part.omitted ? { omitted: part.omitted } : {},
15903
+ ...part.messageCount === void 0 ? {} : { messageCount: part.messageCount },
15904
+ ...part.totalMessages === void 0 ? {} : { totalMessages: part.totalMessages }
15905
+ });
15906
+ continue;
15907
+ }
15908
+ if (part.type !== "image") {
15909
+ assertNeverPart(part);
15910
+ continue;
15911
+ }
15912
+ const { image, mediaType } = part;
15913
+ const media = mediaType ? { mediaType } : {};
15914
+ if (/^https?:/i.test(image)) {
15915
+ out.push({ type: "image", url: image, ...media });
15916
+ continue;
15917
+ }
15918
+ if (isAbsolute(image)) {
15919
+ out.push({ type: "image", path: image, ...media });
15920
+ continue;
15921
+ }
15922
+ return { ok: false, detail: DATA_URL.test(image) ? "image:data-url" : "image:base64" };
15923
+ }
15924
+ return { ok: true, parts: out };
15925
+ }
15647
15926
  function stripArtifactData(artifacts) {
15648
15927
  return artifacts.map(({ data: _data, ...rest }) => rest);
15649
15928
  }
@@ -16142,13 +16421,11 @@ var FTS_EXTERNAL_MIGRATIONS = [
16142
16421
  var SessionDB = class {
16143
16422
  db;
16144
16423
  path;
16145
- msgId = 0;
16146
16424
  constructor(dbPath$1) {
16147
16425
  this.path = dbPath$1 ?? dbPath();
16148
16426
  this.db = openDatabase(this.path);
16149
16427
  migrate(this.db, NS3, SESSION_MIGRATIONS);
16150
16428
  this.migrateFtsIndex();
16151
- this.refreshMsgId();
16152
16429
  }
16153
16430
  migrateFtsIndex() {
16154
16431
  try {
@@ -16160,9 +16437,8 @@ var SessionDB = class {
16160
16437
  );
16161
16438
  }
16162
16439
  }
16163
- refreshMsgId() {
16164
- const row = this.db.prepare("SELECT MAX(id) as maxId FROM messages").get();
16165
- this.msgId = row?.maxId ?? 0;
16440
+ transaction(fn) {
16441
+ return this.db.transaction(fn)();
16166
16442
  }
16167
16443
  insertSession(meta) {
16168
16444
  this.db.prepare(
@@ -16204,6 +16480,9 @@ var SessionDB = class {
16204
16480
  if (!row) return null;
16205
16481
  return this.rowToMeta(row);
16206
16482
  }
16483
+ hasSession(id) {
16484
+ return this.db.prepare("SELECT 1 FROM sessions WHERE id = ? LIMIT 1").get(id) !== void 0;
16485
+ }
16207
16486
  listSessions(input) {
16208
16487
  const limit = input.limit ?? 20;
16209
16488
  const key = input.sortKey ?? "startedAt";
@@ -16213,7 +16492,7 @@ var SessionDB = class {
16213
16492
  const dir = input.direction ?? "desc";
16214
16493
  let where = "1=1";
16215
16494
  const params = {};
16216
- if (input.archived !== void 0) {
16495
+ if (input.archived === "any") ; else if (input.archived !== void 0) {
16217
16496
  where += " AND archived = @archived";
16218
16497
  params["archived"] = input.archived ? 1 : 0;
16219
16498
  } else {
@@ -16226,6 +16505,10 @@ var SessionDB = class {
16226
16505
  where += " AND cwd = @cwd";
16227
16506
  params["cwd"] = input.cwd;
16228
16507
  }
16508
+ if (input.updatedSince !== void 0) {
16509
+ where += " AND COALESCE(updated_at, started_at) > @updatedSince";
16510
+ params["updatedSince"] = input.updatedSince;
16511
+ }
16229
16512
  if (input.cursor) {
16230
16513
  where += ` AND ${cursorCol} < @cursor`;
16231
16514
  params["cursor"] = Number(input.cursor);
@@ -16269,11 +16552,20 @@ var SessionDB = class {
16269
16552
  unarchiveSession(id) {
16270
16553
  this.db.prepare("UPDATE sessions SET archived = 0 WHERE id = ?").run(id);
16271
16554
  }
16555
+ importSession(meta, messages) {
16556
+ const run = this.db.transaction(
16557
+ (m, list) => {
16558
+ this.insertSession(m);
16559
+ return list.map((msg) => this.insertMessage({ ...msg, sessionId: m.id }));
16560
+ }
16561
+ );
16562
+ return run(meta, messages);
16563
+ }
16272
16564
  insertMessage(msg) {
16273
- this.db.prepare(
16565
+ const info = this.db.prepare(
16274
16566
  `
16275
- INSERT INTO messages (id, session_id, role, content, tool_call_id, tool_calls, tool_results, parts, tool_name, timestamp, token_count, finish_reason, reasoning, turn_facts, active, compacted, observed)
16276
- VALUES (@id, @sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, 1, 0, 0)
16567
+ INSERT INTO messages (session_id, role, content, tool_call_id, tool_calls, tool_results, parts, tool_name, timestamp, token_count, finish_reason, reasoning, turn_facts, active, compacted, observed)
16568
+ VALUES (@sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, 1, 0, 0)
16277
16569
  `
16278
16570
  ).run({
16279
16571
  ...msg,
@@ -16291,7 +16583,7 @@ var SessionDB = class {
16291
16583
  this.db.prepare(
16292
16584
  `UPDATE sessions SET message_count = message_count + 1, tool_call_count = tool_call_count + @toolCalls, updated_at = MAX(COALESCE(updated_at, 0), @timestamp) WHERE id = @id`
16293
16585
  ).run({ id: msg.sessionId, toolCalls, timestamp: msg.timestamp });
16294
- if (msg.id > this.msgId) this.msgId = msg.id;
16586
+ return Number(info.lastInsertRowid);
16295
16587
  }
16296
16588
  recordUsage(sessionId, usage) {
16297
16589
  const priced = usage.costUsd !== void 0;
@@ -16353,9 +16645,6 @@ var SessionDB = class {
16353
16645
  }
16354
16646
  return info.changes;
16355
16647
  }
16356
- nextMessageId() {
16357
- return ++this.msgId;
16358
- }
16359
16648
  search(input) {
16360
16649
  const limit = input.limit ?? 5;
16361
16650
  const scope = input.scope ?? "all";
@@ -16444,7 +16733,7 @@ var SessionDB = class {
16444
16733
  return { results };
16445
16734
  }
16446
16735
  pruneCandidates(input) {
16447
- const clauses = ["ended_at IS NOT NULL", "updated_at < @cutoff"];
16736
+ const clauses = ["ended_at IS NOT NULL", "updated_at < @cutoff", "ended_at < @cutoff"];
16448
16737
  const params = { cutoff: input.olderThanMs };
16449
16738
  if (typeof input.archived === "boolean") {
16450
16739
  clauses.push("archived = @archived");
@@ -16454,6 +16743,15 @@ var SessionDB = class {
16454
16743
  const rows = this.db.prepare(`SELECT * FROM sessions WHERE ${clauses.join(" AND ")} ORDER BY updated_at ASC`).all(params);
16455
16744
  return rows.map((r) => this.rowToMeta(r));
16456
16745
  }
16746
+ sweepOrphanedSessions(input) {
16747
+ const info = this.db.prepare(
16748
+ "UPDATE sessions SET ended_at = @now, end_reason = 'orphan_sweep' WHERE ended_at IS NULL AND updated_at < @cutoff"
16749
+ ).run({ now: input.now, cutoff: input.olderThanMs });
16750
+ return info.changes;
16751
+ }
16752
+ reopenSession(id) {
16753
+ this.db.prepare("UPDATE sessions SET ended_at = NULL, end_reason = NULL WHERE id = ?").run(id);
16754
+ }
16457
16755
  openPruneMatches(input) {
16458
16756
  const clauses = ["ended_at IS NULL", "updated_at < @cutoff"];
16459
16757
  if (!input.includePinned) clauses.push("COALESCE(pinned, 0) = 0");
@@ -16511,10 +16809,8 @@ var SessionDB = class {
16511
16809
  if (middle.length === 0) return null;
16512
16810
  const mark = this.db.prepare("UPDATE messages SET active = 0, compacted = 1 WHERE id = ?");
16513
16811
  for (const row of middle) mark.run(row.id);
16514
- const summaryId = this.nextMessageId();
16515
- this.insertMessage({
16812
+ const summaryId = this.insertMessage({
16516
16813
  sessionId,
16517
- id: summaryId,
16518
16814
  role: "user",
16519
16815
  content: input.summary,
16520
16816
  timestamp: middle[0].timestamp
@@ -16870,8 +17166,64 @@ var SessionReferences = class {
16870
17166
  };
16871
17167
  // src/session/manager.ts
16872
17168
  var AUTO_VACUUM_MIN_FREELIST_RATIO = 0.25;
17169
+ var IMPORT_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
17170
+ function validateImport(input) {
17171
+ const { meta, messages } = input;
17172
+ if (typeof meta.id !== "string" || meta.id.length === 0) return "meta.id";
17173
+ if (!Number.isFinite(meta.startedAt)) return "meta.startedAt";
17174
+ if (!Number.isFinite(meta.updatedAt)) return "meta.updatedAt";
17175
+ if (meta.endedAt !== void 0 && !Number.isFinite(meta.endedAt)) return "meta.endedAt";
17176
+ const seen = /* @__PURE__ */ new Set();
17177
+ for (const [i, msg] of messages.entries()) {
17178
+ if (!IMPORT_ROLES.has(msg.role)) return `messages[${i}].role`;
17179
+ if (typeof msg.content !== "string") return `messages[${i}].content`;
17180
+ if (!Number.isFinite(msg.timestamp)) return `messages[${i}].timestamp`;
17181
+ if (!Number.isFinite(msg.id) || seen.has(msg.id)) return `messages[${i}].id`;
17182
+ seen.add(msg.id);
17183
+ }
17184
+ return void 0;
17185
+ }
17186
+ function buildImportedMeta(meta, keepParent) {
17187
+ return {
17188
+ id: meta.id,
17189
+ title: meta.title,
17190
+ displayName: meta.displayName,
17191
+ source: meta.source ?? "import",
17192
+ cwd: meta.cwd,
17193
+ workspaceState: meta.workspaceState,
17194
+ workspaceRoot: meta.workspaceRoot,
17195
+ gitBranch: meta.gitBranch,
17196
+ gitRepoRoot: meta.gitRepoRoot,
17197
+ model: meta.model,
17198
+ tags: [],
17199
+ preview: "",
17200
+ pinned: meta.pinned ?? false,
17201
+ archived: meta.archived ?? false,
17202
+ rewindCount: 0,
17203
+ parentId: keepParent ? meta.parentId : void 0,
17204
+ messageCount: 0,
17205
+ toolCallCount: 0,
17206
+ apiCallCount: 0,
17207
+ inputTokens: 0,
17208
+ outputTokens: 0,
17209
+ cacheReadTokens: 0,
17210
+ cacheWriteTokens: 0,
17211
+ reasoningTokens: 0,
17212
+ estimatedCostUsd: void 0,
17213
+ actualCostUsd: void 0,
17214
+ compressionIneffectiveCount: 0,
17215
+ expiryFinalized: 0,
17216
+ summary: null,
17217
+ plan: null,
17218
+ startedAt: new Date(meta.startedAt),
17219
+ updatedAt: new Date(meta.updatedAt),
17220
+ endedAt: meta.endedAt === void 0 ? void 0 : new Date(meta.endedAt),
17221
+ endReason: meta.endReason
17222
+ };
17223
+ }
16873
17224
  var SessionManager = class {
16874
17225
  db;
17226
+ touched = /* @__PURE__ */ new Set();
16875
17227
  queries;
16876
17228
  goals;
16877
17229
  references;
@@ -16930,6 +17282,7 @@ var SessionManager = class {
16930
17282
  endReason: void 0
16931
17283
  };
16932
17284
  this.db.insertSession(meta);
17285
+ this.touched.add(id);
16933
17286
  if (input?.systemPrompt) {
16934
17287
  this.appendMessage(id, { role: "system", content: input.systemPrompt });
16935
17288
  }
@@ -16942,6 +17295,9 @@ var SessionManager = class {
16942
17295
  const messages = this.db.getMessages(id);
16943
17296
  return this.buildSession(id, messages);
16944
17297
  }
17298
+ has(id) {
17299
+ return this.db.hasSession(id);
17300
+ }
16945
17301
  getLatest() {
16946
17302
  const result = this.db.listSessions({ limit: 1, sortKey: "updatedAt", direction: "desc" });
16947
17303
  if (result.sessions.length === 0) return void 0;
@@ -16955,6 +17311,56 @@ var SessionManager = class {
16955
17311
  list(input) {
16956
17312
  return this.db.listSessions(input ?? {});
16957
17313
  }
17314
+ importSession(input) {
17315
+ const { meta, messages } = input;
17316
+ const id = meta.id;
17317
+ const bad2 = validateImport(input);
17318
+ if (bad2) return { ok: false, id, reason: "invalid", detail: bad2 };
17319
+ if (this.db.hasSession(id)) return { ok: false, id, reason: "exists" };
17320
+ const converted = [];
17321
+ for (const [i, msg] of messages.entries()) {
17322
+ const { id: _sourceId, parts, ...rest } = msg;
17323
+ if (!parts) {
17324
+ converted.push(rest);
17325
+ continue;
17326
+ }
17327
+ const result = importParts(parts);
17328
+ if (!result.ok) {
17329
+ return {
17330
+ ok: false,
17331
+ id,
17332
+ reason: "invalid",
17333
+ detail: `messages[${i}].parts.${result.detail}`
17334
+ };
17335
+ }
17336
+ converted.push({ ...rest, parts: result.parts });
17337
+ }
17338
+ const droppedParentId = meta.parentId !== void 0 && !this.db.hasSession(meta.parentId) ? meta.parentId : void 0;
17339
+ try {
17340
+ const newIds = this.db.importSession(
17341
+ buildImportedMeta(meta, droppedParentId === void 0),
17342
+ converted
17343
+ );
17344
+ const idMap = {};
17345
+ messages.forEach((msg, i) => {
17346
+ idMap[msg.id] = newIds[i];
17347
+ });
17348
+ return {
17349
+ ok: true,
17350
+ id,
17351
+ messageCount: newIds.length,
17352
+ idMap,
17353
+ ...droppedParentId === void 0 ? {} : { droppedParentId }
17354
+ };
17355
+ } catch (err) {
17356
+ return {
17357
+ ok: false,
17358
+ id,
17359
+ reason: "failed",
17360
+ ...err instanceof Error ? { detail: err.name } : {}
17361
+ };
17362
+ }
17363
+ }
16958
17364
  updateMeta(id, patch) {
16959
17365
  this.db.updateSessionMeta(id, patch);
16960
17366
  }
@@ -16970,11 +17376,17 @@ var SessionManager = class {
16970
17376
  openPruneMatches(input) {
16971
17377
  return this.db.openPruneMatches(input);
16972
17378
  }
17379
+ sweepOrphans(input) {
17380
+ return this.db.sweepOrphanedSessions(input);
17381
+ }
17382
+ reopen(id) {
17383
+ this.db.reopenSession(id);
17384
+ }
16973
17385
  freelistRatio() {
16974
17386
  return this.db.freelistRatio();
16975
17387
  }
16976
17388
  maybeAutoPrune(opts) {
16977
- const result = { skipped: false, pruned: 0, vacuumed: false };
17389
+ const result = { skipped: false, swept: 0, pruned: 0, vacuumed: false };
16978
17390
  if (!opts.enabled) return { ...result, skipped: true };
16979
17391
  const now = opts.now?.() ?? Date.now();
16980
17392
  const minInterval = opts.minIntervalMs ?? 24 * 60 * 60 * 1e3;
@@ -16984,6 +17396,7 @@ var SessionManager = class {
16984
17396
  return { ...result, skipped: true };
16985
17397
  }
16986
17398
  const cutoff = now - opts.retentionDays * 24 * 60 * 60 * 1e3;
17399
+ const swept = this.db.sweepOrphanedSessions({ olderThanMs: cutoff, now });
16987
17400
  const victims = this.db.pruneCandidates({ olderThanMs: cutoff });
16988
17401
  let pruned = 0;
16989
17402
  for (const s of victims) if (this.db.deleteSession(s.id)) pruned++;
@@ -16997,27 +17410,50 @@ var SessionManager = class {
16997
17410
  if (vacuumed) this.db.setMeta("last_vacuum", String(now));
16998
17411
  }
16999
17412
  }
17000
- return { skipped: false, pruned, vacuumed };
17413
+ return { skipped: false, swept, pruned, vacuumed };
17001
17414
  } catch (cause) {
17002
17415
  return { ...result, error: cause instanceof Error ? cause.message : String(cause) };
17003
17416
  }
17004
17417
  }
17418
+ maybePruneAttachments(root, keepSession, opts = {}) {
17419
+ const now = opts.now?.() ?? Date.now();
17420
+ const minInterval = opts.minIntervalMs ?? 24 * 60 * 60 * 1e3;
17421
+ try {
17422
+ if (!this.db.claimMaintenanceSlot("last_attachment_prune", minInterval, now)) return 0;
17423
+ return pruneAttachments(root, keepSession, ATTACHMENT_RETENTION_DEFAULTS, now);
17424
+ } catch {
17425
+ return 0;
17426
+ }
17427
+ }
17005
17428
  delete(id) {
17006
17429
  return this.db.deleteSession(id);
17007
17430
  }
17008
17431
  endSession(id, reason9 = "user_stop") {
17009
17432
  this.db.updateSessionMeta(id, { endedAt: /* @__PURE__ */ new Date(), endReason: reason9 });
17010
17433
  }
17011
- appendMessage(sessionId, msg) {
17012
- const id = this.db.nextMessageId();
17013
- this.db.insertMessage({ ...msg, sessionId, id, timestamp: Date.now() });
17014
- const meta = this.db.getSessionMeta(sessionId);
17015
- if (!meta) return id;
17016
- if (!meta.title && msg.role === "user") {
17017
- const title = msg.content.slice(0, 50).replace(/\n/g, " ");
17018
- this.db.updateSessionMeta(sessionId, { title });
17434
+ endTouchedSessions(reason9 = "process_exit") {
17435
+ let ended = 0;
17436
+ for (const id of this.touched) {
17437
+ try {
17438
+ this.endSession(id, reason9);
17439
+ ended++;
17440
+ } catch {
17441
+ }
17019
17442
  }
17020
- return id;
17443
+ return ended;
17444
+ }
17445
+ appendMessage(sessionId, msg) {
17446
+ this.touched.add(sessionId);
17447
+ return this.db.transaction(() => {
17448
+ const id = this.db.insertMessage({ ...msg, sessionId, timestamp: Date.now() });
17449
+ const meta = this.db.getSessionMeta(sessionId);
17450
+ if (!meta) return id;
17451
+ if (!meta.title && msg.role === "user") {
17452
+ const title = msg.content.slice(0, 50).replace(/\n/g, " ");
17453
+ this.db.updateSessionMeta(sessionId, { title });
17454
+ }
17455
+ return id;
17456
+ });
17021
17457
  }
17022
17458
  attachTurnFacts(sessionId, messageId, input) {
17023
17459
  this.db.attachTurnFacts(sessionId, messageId, input);
@@ -17026,6 +17462,7 @@ var SessionManager = class {
17026
17462
  this.db.setOpenTurn(sessionId, from);
17027
17463
  }
17028
17464
  recordUsage(sessionId, usage) {
17465
+ this.touched.add(sessionId);
17029
17466
  this.db.recordUsage(sessionId, usage);
17030
17467
  }
17031
17468
  markObserved(sessionId, messageId) {
@@ -17195,6 +17632,30 @@ function firstSentence(text, maxLen) {
17195
17632
  const lastSpace = cut.lastIndexOf(" ");
17196
17633
  return (lastSpace > maxLen * 0.6 ? cut.slice(0, lastSpace) : cut) + "\u2026";
17197
17634
  }
17635
+ // src/session/persist-failure.ts
17636
+ function codeOf(cause) {
17637
+ if (typeof cause !== "object" || cause === null) return "";
17638
+ const code = cause.code;
17639
+ return typeof code === "string" ? code : "";
17640
+ }
17641
+ function classifyPersistFailure(cause) {
17642
+ const code = codeOf(cause);
17643
+ if (!code) return "unknown";
17644
+ if (code.startsWith("SQLITE_FULL") || code === "ENOSPC") return "disk-full";
17645
+ if (code.startsWith("SQLITE_BUSY") || code.startsWith("SQLITE_LOCKED")) return "locked";
17646
+ if (code.startsWith("SQLITE_READONLY") || code.startsWith("SQLITE_PERM") || code.startsWith("SQLITE_CANTOPEN") || code === "EACCES" || code === "EPERM" || code === "EROFS") {
17647
+ return "read-only";
17648
+ }
17649
+ if (code.startsWith("SQLITE_CORRUPT") || code === "SQLITE_NOTADB") return "corrupt";
17650
+ if (code.startsWith("SQLITE_IOERR") || code === "EIO") return "io-error";
17651
+ return "unknown";
17652
+ }
17653
+ function isTransientPersistFailure(kind) {
17654
+ return kind === "locked";
17655
+ }
17656
+ function persistFailureCode(cause) {
17657
+ return codeOf(cause);
17658
+ }
17198
17659
  function isSettled(phase) {
17199
17660
  return phase === "complete";
17200
17661
  }
@@ -18456,6 +18917,14 @@ var MIGRATIONS3 = [
18456
18917
  );
18457
18918
  `);
18458
18919
  }
18920
+ },
18921
+ {
18922
+ id: "schedule-show-in-session-list",
18923
+ version: 2,
18924
+ description: "\u4EFB\u52A1\u591A\u4E00\u683C\uFF1A\u8DD1\u5B8C\u7684\u4F1A\u8BDD\u76F4\u63A5\u8FDB\u4F1A\u8BDD\u5217\u8868\u5417\uFF08\u7F3A\u7701\u4E0D\u8FDB\uFF09",
18925
+ up: (db) => {
18926
+ db.exec(`ALTER TABLE schedules ADD COLUMN show_in_session_list INTEGER NOT NULL DEFAULT 0;`);
18927
+ }
18459
18928
  }
18460
18929
  ];
18461
18930
  var ScheduleStore = class {
@@ -18480,6 +18949,7 @@ var ScheduleStore = class {
18480
18949
  prompt: input.prompt,
18481
18950
  workDir: input.workDir,
18482
18951
  ...input.model !== void 0 ? { model: input.model } : {},
18952
+ ...input.role !== void 0 ? { role: input.role } : {},
18483
18953
  permission: input.permission,
18484
18954
  allowTools: input.allowTools ?? [],
18485
18955
  allowOperations: input.allowOperations ?? [],
@@ -18487,6 +18957,7 @@ var ScheduleStore = class {
18487
18957
  maxTurns: input.maxTurns ?? SCHEDULE_DEFAULTS.maxTurns,
18488
18958
  maxBudgetUsd: input.maxBudgetUsd,
18489
18959
  timeoutMs: input.timeoutMs ?? SCHEDULE_DEFAULTS.timeoutMs,
18960
+ ...input.showInSessionList === true ? { showInSessionList: true } : {},
18490
18961
  trigger: input.trigger,
18491
18962
  ...input.startDate !== void 0 ? { startDate: input.startDate } : {},
18492
18963
  ...input.endDate !== void 0 ? { endDate: input.endDate } : {},
@@ -18498,13 +18969,15 @@ var ScheduleStore = class {
18498
18969
  `INSERT INTO schedules (
18499
18970
  id, name, enabled, created_at, updated_at, prompt, work_dir, model, role,
18500
18971
  skills, mcp_servers, permission, allow_tools, allow_operations, allow_rules,
18501
- max_turns, max_budget_usd, timeout_ms, trigger_kind, trigger_spec,
18972
+ max_turns, max_budget_usd, timeout_ms, show_in_session_list,
18973
+ trigger_kind, trigger_spec,
18502
18974
  start_date, end_date, os_backend, os_task_id, os_registered_at, os_command,
18503
18975
  last_run_at, last_status
18504
18976
  ) VALUES (
18505
18977
  @id, @name, @enabled, @created_at, @updated_at, @prompt, @work_dir, @model, @role,
18506
18978
  @skills, @mcp_servers, @permission, @allow_tools, @allow_operations, @allow_rules,
18507
- @max_turns, @max_budget_usd, @timeout_ms, @trigger_kind, @trigger_spec,
18979
+ @max_turns, @max_budget_usd, @timeout_ms, @show_in_session_list,
18980
+ @trigger_kind, @trigger_spec,
18508
18981
  @start_date, @end_date, @os_backend, @os_task_id, @os_registered_at, @os_command,
18509
18982
  @last_run_at, @last_status
18510
18983
  )`
@@ -18590,6 +19063,16 @@ var ScheduleStore = class {
18590
19063
  const rows = this.db.prepare("SELECT * FROM schedule_runs WHERE schedule_id = ? ORDER BY started_at DESC LIMIT ?").all(scheduleId, limit);
18591
19064
  return rows.map(runFromRow);
18592
19065
  }
19066
+ recordedSessions(scheduleId, limit = 2) {
19067
+ if (limit <= 0) return [];
19068
+ const rows = this.db.prepare(
19069
+ `SELECT session_id FROM schedule_runs
19070
+ WHERE schedule_id = ? AND session_id IS NOT NULL
19071
+ ORDER BY started_at DESC
19072
+ LIMIT ?`
19073
+ ).all(scheduleId, limit);
19074
+ return rows.map((r) => r.session_id);
19075
+ }
18593
19076
  listRecentRuns(limit = 50) {
18594
19077
  const rows = this.db.prepare("SELECT * FROM schedule_runs ORDER BY started_at DESC LIMIT ?").all(limit);
18595
19078
  return rows.map(runFromRow);
@@ -18611,6 +19094,7 @@ var ScheduleStore = class {
18611
19094
  mcp_servers = @mcp_servers, permission = @permission, allow_tools = @allow_tools,
18612
19095
  allow_operations = @allow_operations, allow_rules = @allow_rules,
18613
19096
  max_turns = @max_turns, max_budget_usd = @max_budget_usd, timeout_ms = @timeout_ms,
19097
+ show_in_session_list = @show_in_session_list,
18614
19098
  trigger_kind = @trigger_kind, trigger_spec = @trigger_spec,
18615
19099
  start_date = @start_date, end_date = @end_date, os_backend = @os_backend,
18616
19100
  os_task_id = @os_task_id, os_registered_at = @os_registered_at,
@@ -18633,6 +19117,7 @@ function mergeSchedulePatch(current, patch) {
18633
19117
  "maxTurns",
18634
19118
  "maxBudgetUsd",
18635
19119
  "timeoutMs",
19120
+ "showInSessionList",
18636
19121
  "trigger",
18637
19122
  "enabled"
18638
19123
  ])
@@ -18643,9 +19128,10 @@ function mergeSchedulePatch(current, patch) {
18643
19128
  if (value === null) delete next[key];
18644
19129
  else next[key] = value;
18645
19130
  }
19131
+ if (next.showInSessionList !== true) delete next.showInSessionList;
18646
19132
  return next;
18647
19133
  }
18648
- var CLEARABLE = ["model", "startDate", "endDate"];
19134
+ var CLEARABLE = ["model", "role", "startDate", "endDate"];
18649
19135
  function toRow(def) {
18650
19136
  return {
18651
19137
  id: def.id,
@@ -18666,6 +19152,7 @@ function toRow(def) {
18666
19152
  max_turns: def.maxTurns,
18667
19153
  max_budget_usd: def.maxBudgetUsd,
18668
19154
  timeout_ms: def.timeoutMs,
19155
+ show_in_session_list: def.showInSessionList === true ? 1 : 0,
18669
19156
  trigger_kind: def.trigger.kind,
18670
19157
  trigger_spec: JSON.stringify(def.trigger),
18671
19158
  start_date: def.startDate ?? null,
@@ -18698,6 +19185,7 @@ function fromRow(row) {
18698
19185
  maxTurns: row.max_turns,
18699
19186
  maxBudgetUsd: row.max_budget_usd,
18700
19187
  timeoutMs: row.timeout_ms,
19188
+ ...row.show_in_session_list !== 0 ? { showInSessionList: true } : {},
18701
19189
  trigger: JSON.parse(row.trigger_spec),
18702
19190
  ...row.start_date !== null ? { startDate: row.start_date } : {},
18703
19191
  ...row.end_date !== null ? { endDate: row.end_date } : {},
@@ -19983,9 +20471,9 @@ function buildAutomaton(patterns) {
19983
20471
  while (queue.length > 0) {
19984
20472
  const cur = queue.shift();
19985
20473
  for (const [ch, child] of nodes[cur].next) {
19986
- let fail8 = nodes[cur].fail;
19987
- while (fail8 !== 0 && !nodes[fail8].next.has(ch)) fail8 = nodes[fail8].fail;
19988
- const target = nodes[fail8].next.get(ch);
20474
+ let fail9 = nodes[cur].fail;
20475
+ while (fail9 !== 0 && !nodes[fail9].next.has(ch)) fail9 = nodes[fail9].fail;
20476
+ const target = nodes[fail9].next.get(ch);
19989
20477
  nodes[child].fail = target !== void 0 && target !== child ? target : 0;
19990
20478
  nodes[child].out.push(...nodes[nodes[child].fail].out);
19991
20479
  queue.push(child);
@@ -20210,15 +20698,30 @@ function runCommandHook(config, event, workDir) {
20210
20698
  const injectedEnv = injectEpochEnv(event, config.pluginRoot);
20211
20699
  const hookEnv = { ...baseEnv, ...injectedEnv, ...config.env };
20212
20700
  return new Promise((resolve16) => {
20213
- const child = spawn(config.command, {
20214
- shell: true,
20215
- cwd: workDir,
20216
- env: hookEnv,
20217
- stdio: ["pipe", "pipe", "pipe"]
20218
- });
20219
20701
  let stdout = "";
20220
20702
  let stderr = "";
20221
20703
  let settled = false;
20704
+ const failToStart = (err) => {
20705
+ if (settled) return;
20706
+ settled = true;
20707
+ resolve16({
20708
+ exitCode: -1,
20709
+ stdout,
20710
+ stderr,
20711
+ blocked: false,
20712
+ rejectReason: t("hook_load.spawn_failed", { message: err.message })
20713
+ });
20714
+ };
20715
+ const child = guardSpawn(
20716
+ () => spawn(config.command, {
20717
+ shell: true,
20718
+ cwd: workDir,
20719
+ env: hookEnv,
20720
+ stdio: ["pipe", "pipe", "pipe"]
20721
+ }),
20722
+ failToStart
20723
+ );
20724
+ if (!child) return;
20222
20725
  const timer = setTimeout(() => {
20223
20726
  if (!settled) {
20224
20727
  settled = true;
@@ -20270,15 +20773,7 @@ function runCommandHook(config, event, workDir) {
20270
20773
  });
20271
20774
  child.on("error", (err) => {
20272
20775
  clearTimeout(timer);
20273
- if (settled) return;
20274
- settled = true;
20275
- resolve16({
20276
- exitCode: -1,
20277
- stdout,
20278
- stderr,
20279
- blocked: false,
20280
- rejectReason: t("hook_load.spawn_failed", { message: err.message })
20281
- });
20776
+ failToStart(err);
20282
20777
  });
20283
20778
  const input = JSON.stringify({
20284
20779
  session_id: event.sessionId,
@@ -22043,6 +22538,7 @@ function listWorkspaceFiles(root) {
22043
22538
  }
22044
22539
  function listFromGit(root) {
22045
22540
  if (!existsSync(join(root, ".git"))) return null;
22541
+ if (!canProbeGit()) return null;
22046
22542
  try {
22047
22543
  const out = execFileSync("git", ["ls-files", "-co", "--exclude-standard", "-z"], {
22048
22544
  cwd: root,
@@ -22251,17 +22747,6 @@ function readSession(ref, opts, usedBytes) {
22251
22747
  })
22252
22748
  };
22253
22749
  }
22254
- var ATTACHMENT_RETENTION_DEFAULTS = {
22255
- maxAgeDays: 7,
22256
- maxTotalBytes: ATTACHMENT_MAX_TOTAL_BYTES
22257
- };
22258
- function pruneAttachments(root, keepSession, retention = ATTACHMENT_RETENTION_DEFAULTS, now = Date.now()) {
22259
- const shared = retention;
22260
- return pruneArtifacts(root, keepSession, shared, now);
22261
- }
22262
- async function removeSessionAttachments(sessionId, homeDir) {
22263
- await rm(attachmentsDir(sessionId, homeDir), { recursive: true, force: true });
22264
- }
22265
22750
  var OPENABLE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
22266
22751
  function artifactOpenRefusal(path, homeDir) {
22267
22752
  const root = homeDir === void 0 ? artifactsDir() : artifactsDir(void 0, homeDir);
@@ -22296,12 +22781,12 @@ async function openArtifact(path, homeDir) {
22296
22781
  if (refusal2) throw new Error(describeArtifactRefusal(refusal2));
22297
22782
  const launch = artifactLaunchArgv(path, process.platform, homeDir);
22298
22783
  if (!launch) throw new Error(describeArtifactRefusal("launch-failed"));
22299
- await new Promise((done2, fail8) => {
22784
+ await new Promise((done2, fail9) => {
22300
22785
  try {
22301
22786
  const child = spawn(launch.command, launch.args, { stdio: "ignore", detached: true });
22302
22787
  child.on(
22303
22788
  "error",
22304
- (err) => fail8(
22789
+ (err) => fail9(
22305
22790
  new Error(
22306
22791
  t("artifact.open_launch_failed_detail", {
22307
22792
  reason: describeArtifactRefusal("launch-failed"),
@@ -22315,7 +22800,7 @@ async function openArtifact(path, homeDir) {
22315
22800
  done2();
22316
22801
  });
22317
22802
  } catch (err) {
22318
- fail8(
22803
+ fail9(
22319
22804
  new Error(
22320
22805
  t("artifact.open_launch_failed_detail", {
22321
22806
  reason: describeArtifactRefusal("launch-failed"),
@@ -23025,31 +23510,15 @@ var CodeSandbox = class {
23025
23510
  stdio: ["ignore", "pipe", "pipe"],
23026
23511
  ...IS_WINDOWS ? { windowsHide: true } : {}
23027
23512
  };
23028
- const child = spawn(command, args, spawnOpts);
23029
23513
  return new Promise((resolve16) => {
23030
23514
  let stdout = "";
23031
23515
  let stderr = "";
23032
23516
  let killedForOutput = false;
23033
- let outDecoder = null;
23034
- let errDecoder = null;
23035
- child.stdout?.on("data", (c) => {
23036
- if (killedForOutput) return;
23037
- outDecoder ??= createStreamDecoder();
23038
- stdout += outDecoder.write(c);
23039
- if (stdout.length >= MAX_OUTPUT) {
23040
- killedForOutput = true;
23041
- stdout = stdout.slice(0, MAX_OUTPUT);
23042
- child.kill("SIGKILL");
23043
- }
23044
- });
23045
- child.stderr?.on("data", (c) => {
23046
- if (stderr.length >= MAX_OUTPUT) return;
23047
- errDecoder ??= createStreamDecoder();
23048
- stderr += errDecoder.write(c);
23049
- });
23050
- const onAbort = () => child.kill("SIGKILL");
23051
- opts.signal?.addEventListener("abort", onAbort, { once: true });
23052
- child.on("error", (err) => {
23517
+ let child = null;
23518
+ const onAbort = () => {
23519
+ child?.kill("SIGKILL");
23520
+ };
23521
+ const failToStart = (err) => {
23053
23522
  opts.signal?.removeEventListener("abort", onAbort);
23054
23523
  if (err.code === "ENOENT") {
23055
23524
  resolve16({
@@ -23064,12 +23533,34 @@ var CodeSandbox = class {
23064
23533
  resolve16({
23065
23534
  success: false,
23066
23535
  output: "",
23067
- error: `[\u9519\u8BEF] ${err.message}`,
23536
+ error: `[\u9519\u8BEF] ${err.message}\uFF08command=${command}\uFF0Ccwd=${spawnOpts.cwd ?? ""}\uFF09`,
23068
23537
  errorType: "runtime",
23069
23538
  duration: Date.now() - start
23070
23539
  });
23540
+ };
23541
+ child = guardSpawn(() => spawn(command, args, spawnOpts), failToStart);
23542
+ if (!child) return;
23543
+ const proc = child;
23544
+ let outDecoder = null;
23545
+ let errDecoder = null;
23546
+ proc.stdout?.on("data", (c) => {
23547
+ if (killedForOutput) return;
23548
+ outDecoder ??= createStreamDecoder();
23549
+ stdout += outDecoder.write(c);
23550
+ if (stdout.length >= MAX_OUTPUT) {
23551
+ killedForOutput = true;
23552
+ stdout = stdout.slice(0, MAX_OUTPUT);
23553
+ proc.kill("SIGKILL");
23554
+ }
23071
23555
  });
23072
- child.on("close", (code, sig) => {
23556
+ proc.stderr?.on("data", (c) => {
23557
+ if (stderr.length >= MAX_OUTPUT) return;
23558
+ errDecoder ??= createStreamDecoder();
23559
+ stderr += errDecoder.write(c);
23560
+ });
23561
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
23562
+ proc.on("error", failToStart);
23563
+ proc.on("close", (code, sig) => {
23073
23564
  opts.signal?.removeEventListener("abort", onAbort);
23074
23565
  if (!killedForOutput) stdout += outDecoder?.end() ?? "";
23075
23566
  if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
@@ -23166,4 +23657,4 @@ function trunc(stdout, stderr) {
23166
23657
  * `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
23167
23658
  */
23168
23659
 
23169
- 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, 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, 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 };
23660
+ 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_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_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, classifyPersistFailure, 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, 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, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, 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, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, 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, 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 };