@epoch-agent/core 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +12411 -12373
  3. package/dist/index.js +301 -152
  4. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { REASONING_EFFORTS, MODEL_ENTRY_PROTOCOLS, PROVIDER_TYPES, isModelEntryId, parseModelRef, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, BRAND_WHEN_SIDEBAR_COLLAPSED, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure, MIN_OPTIONS, MAX_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, entrySecretName, isEntrySecretName, entryRef, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, normalizeAppearanceTranslucent, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, ENTRY_REF_PREFIX, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
1
+ import { REASONING_EFFORTS, MODEL_ENTRY_PROTOCOLS, PROVIDER_TYPES, isModelEntryId, parseModelRef, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, BRAND_WHEN_SIDEBAR_COLLAPSED, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure, MIN_OPTIONS, MAX_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, entrySecretName, isEntrySecretName, entryRef, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, normalizeAppearanceTranslucent, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, SCHEDULE_DRAFT_SAVED_MARK, imagePartOmittedSummary, ENTRY_REF_PREFIX, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
2
2
  export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval } from '@epoch-agent/protocol';
3
3
  import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync, renameSync, realpathSync, symlinkSync, cpSync, lstatSync, unlinkSync, rmdirSync, mkdtempSync, copyFileSync, openSync, writeSync, closeSync, readSync } from 'fs';
4
4
  import { homedir, platform, tmpdir, hostname } from 'os';
@@ -2707,13 +2707,48 @@ function createExitTool(deps) {
2707
2707
  function createPlanModeTools(deps) {
2708
2708
  return [createEnterTool(deps), createExitTool(deps)];
2709
2709
  }
2710
+ var SCHEDULE_ISSUE_CODES = [
2711
+ "name-empty",
2712
+ "prompt-empty",
2713
+ "permission-unknown",
2714
+ "work-dir-missing",
2715
+ "budget-missing",
2716
+ "max-turns-invalid",
2717
+ "timeout-invalid",
2718
+ "trigger-time-invalid",
2719
+ "trigger-times-too-many",
2720
+ "trigger-weekdays-empty",
2721
+ "trigger-days-invalid",
2722
+ "trigger-interval-invalid",
2723
+ "trigger-once-date-invalid",
2724
+ "trigger-once-past",
2725
+ "date-range-invalid",
2726
+ "date-range-on-once",
2727
+ "rule-invalid",
2728
+ "bypass-needs-workdir",
2729
+ "bypass-workdir-too-broad",
2730
+ "bypass-needs-limits",
2731
+ "bypass-not-confirmed"
2732
+ ];
2733
+ var SCHEDULE_DEFAULTS = {
2734
+ maxTurns: DEFAULT_MAX_TURNS,
2735
+ timeoutMs: 60 * 60 * 1e3
2736
+ };
2737
+ var SCHEDULE_FORM_BUDGET_USD = 200;
2738
+ var SCHEDULE_DEFAULT_ALLOWLIST = {
2739
+ allowTools: [],
2740
+ allowOperations: ["file_read"],
2741
+ allowRules: []
2742
+ };
2743
+ // src/schedule/draft.ts
2710
2744
  var bad = (message) => ({ ok: false, message });
2711
2745
  var good = (value) => ({ ok: true, value });
2712
2746
  var PERMISSION_ORDER = [
2713
2747
  "plan",
2714
2748
  "default",
2715
2749
  "acceptEdits",
2716
- "auto"
2750
+ "auto",
2751
+ "bypass"
2717
2752
  ];
2718
2753
  var OPERATION_TYPES = [
2719
2754
  "file_read",
@@ -2755,16 +2790,18 @@ function operationList(v) {
2755
2790
  function permission(v) {
2756
2791
  const s = str(v);
2757
2792
  if (s === void 0) return bad("permission \u662F\u7A7A\u7684");
2758
- if (s === "bypass") {
2759
- return bad(
2760
- "\u8349\u7A3F\u4E0D\u8BB8\u7528 bypass\uFF08\u8DF3\u8FC7\u5168\u90E8\u68C0\u67E5\uFF09\u3002\u65E0\u4EBA\u503C\u5B88\u4E0B\u90A3\u4E00\u6863\u8981\u4EBA\u4EB2\u624B\u52FE\u4E00\u6B21\u786E\u8BA4\uFF0C\u8BA9\u8349\u7A3F\u63D0\u8BAE\u5B83\u7B49\u4E8E\u628A\u90A3\u6B21\u786E\u8BA4\u53D8\u6210\u4F8B\u884C\u624B\u7EED\u3002\u8981\u653E\u5F00\u67D0\u51E0\u4EF6\u4E8B\uFF0C\u7528 permission: default \u52A0\u4E00\u4EFD\u7CBE\u786E\u7684 allowRules"
2761
- );
2762
- }
2763
2793
  if (!PERMISSION_ORDER.includes(s)) {
2764
2794
  return bad(`permission \u53EA\u80FD\u662F ${PERMISSION_ORDER.join(" / ")} \u4E4B\u4E00\uFF0C\u6536\u5230\u7684\u662F ${s}`);
2765
2795
  }
2766
2796
  return good(s);
2767
2797
  }
2798
+ function budget(v) {
2799
+ if (v === void 0) return good(void 0);
2800
+ if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
2801
+ return bad("maxBudgetUsd \u8981\u662F\u5927\u4E8E 0 \u7684\u6570\uFF08\u5355\u4F4D\u7F8E\u5143\uFF09\u3002\u4E0D\u7ED9\u5C31\u6309\u65B0\u5EFA\u8868\u5355\u90A3\u4E2A\u7F3A\u7701\uFF08200\uFF09");
2802
+ }
2803
+ return good(v);
2804
+ }
2768
2805
  function clock(v) {
2769
2806
  const s = str(v);
2770
2807
  if (s === void 0 || !/^\d{1,2}:\d{2}$/.test(s))
@@ -2881,6 +2918,8 @@ function parseCreate(raw, note) {
2881
2918
  const allow = parseAllowlist(raw);
2882
2919
  if (!allow.ok) return allow;
2883
2920
  const workDir = str(raw["workDir"]);
2921
+ const usd = budget(raw["maxBudgetUsd"]);
2922
+ if (!usd.ok) return usd;
2884
2923
  return good({
2885
2924
  action: "create",
2886
2925
  name,
@@ -2889,6 +2928,7 @@ function parseCreate(raw, note) {
2889
2928
  permission: level.value,
2890
2929
  note,
2891
2930
  ...workDir === void 0 ? {} : { workDir },
2931
+ ...usd.value === void 0 ? {} : { maxBudgetUsd: usd.value },
2892
2932
  ...allow.value
2893
2933
  });
2894
2934
  }
@@ -2923,6 +2963,9 @@ function parseUpdate(raw, note) {
2923
2963
  const timeout = posInt(raw["timeoutMs"], "timeoutMs");
2924
2964
  if (!timeout.ok) return timeout;
2925
2965
  if (timeout.value !== void 0) patch.timeoutMs = timeout.value;
2966
+ const usd = budget(raw["maxBudgetUsd"]);
2967
+ if (!usd.ok) return usd;
2968
+ if (usd.value !== void 0) patch.maxBudgetUsd = usd.value;
2926
2969
  if (Object.keys(patch).length === 0) {
2927
2970
  return bad(
2928
2971
  "update \u4E00\u683C\u90FD\u6CA1\u6539\u3002\u4E00\u4EFD\u4EC0\u4E48\u90FD\u4E0D\u6539\u7684\u63D0\u6848\u4F1A\u767D\u5360\u7528\u6237\u4E00\u6B21\u70B9\u51FB \u2014\u2014 \u8981\u4E48\u7ED9\u51FA\u5177\u4F53\u6539\u54EA\u51E0\u683C\uFF0C\u8981\u4E48\u522B\u63D0"
@@ -2948,7 +2991,7 @@ function parseScheduleDraft(raw) {
2948
2991
  }
2949
2992
  function draftWidening(draft, current) {
2950
2993
  const out = [];
2951
- const base2 = draft.action === "update" && current ? current.permission === "bypass" ? "auto" : current.permission : "default";
2994
+ const base2 = draft.action === "update" && current ? current.permission : "default";
2952
2995
  const level = draft.permission;
2953
2996
  if (level !== void 0 && PERMISSION_ORDER.indexOf(level) > PERMISSION_ORDER.indexOf(base2)) {
2954
2997
  out.push("permission");
@@ -3002,42 +3045,9 @@ function draftCommandLine(draft) {
3002
3045
  for (const tool of draft.allowTools ?? []) argv.push("--allow-tool", shellArg(tool));
3003
3046
  for (const op2 of draft.allowOperations ?? []) argv.push("--allow-operation", op2);
3004
3047
  for (const rule of draft.allowRules ?? []) argv.push("--allow-rule", shellArg(rule));
3005
- argv.push("--budget", shellArg("<\u4F60\u5B9A\u4E00\u4E2A\u4E0A\u9650\uFF0C\u5355\u4F4D\u7F8E\u5143>"));
3048
+ argv.push("--budget", String(draft.maxBudgetUsd ?? SCHEDULE_FORM_BUDGET_USD));
3006
3049
  return argv.join(" ");
3007
3050
  }
3008
- var SCHEDULE_ISSUE_CODES = [
3009
- "name-empty",
3010
- "prompt-empty",
3011
- "permission-unknown",
3012
- "work-dir-missing",
3013
- "budget-missing",
3014
- "max-turns-invalid",
3015
- "timeout-invalid",
3016
- "trigger-time-invalid",
3017
- "trigger-times-too-many",
3018
- "trigger-weekdays-empty",
3019
- "trigger-days-invalid",
3020
- "trigger-interval-invalid",
3021
- "trigger-once-date-invalid",
3022
- "trigger-once-past",
3023
- "date-range-invalid",
3024
- "date-range-on-once",
3025
- "rule-invalid",
3026
- "bypass-needs-workdir",
3027
- "bypass-workdir-too-broad",
3028
- "bypass-needs-limits",
3029
- "bypass-not-confirmed"
3030
- ];
3031
- var SCHEDULE_DEFAULTS = {
3032
- maxTurns: DEFAULT_MAX_TURNS,
3033
- timeoutMs: 60 * 60 * 1e3
3034
- };
3035
- var SCHEDULE_FORM_BUDGET_USD = 200;
3036
- var SCHEDULE_DEFAULT_ALLOWLIST = {
3037
- allowTools: [],
3038
- allowOperations: ["file_read"],
3039
- allowRules: []
3040
- };
3041
3051
  // src/schedule/unattended-lint.ts
3042
3052
  var ANCHORS = [
3043
3053
  { kind: "asks-human", zh: /问(?:一下|下)?我/, en: /\bask\s+(?:me|us)\b/i },
@@ -3473,7 +3483,7 @@ function validateSchedule(input) {
3473
3483
  if (input.workDir && !isExistingDir(input.workDir)) {
3474
3484
  issues.push({ code: "work-dir-missing", detail: input.workDir });
3475
3485
  }
3476
- if (!input.budgetPending && (!Number.isFinite(input.maxBudgetUsd) || input.maxBudgetUsd <= 0)) {
3486
+ if (!Number.isFinite(input.maxBudgetUsd) || input.maxBudgetUsd <= 0) {
3477
3487
  issues.push({ code: "budget-missing", detail: String(input.maxBudgetUsd) });
3478
3488
  }
3479
3489
  if (!Number.isInteger(input.maxTurns) || input.maxTurns < 0) {
@@ -3535,29 +3545,34 @@ var WIDENING_TEXT = {
3535
3545
  allowlist: "\u6E05\u5355\u591A\u4E86\u6761\u76EE\uFF08= \u591A\u4E86\u51E0\u4EF6\u4E0D\u7ECF\u786E\u8BA4\u5C31\u4F1A\u505A\u7684\u4E8B\uFF09",
3536
3546
  enabled: "\u628A\u4E00\u6761\u505C\u7528\u7684\u4EFB\u52A1\u6539\u6210\u542F\u7528"
3537
3547
  };
3548
+ function budgetOf(draft, current) {
3549
+ return draft.maxBudgetUsd ?? current?.maxBudgetUsd ?? SCHEDULE_FORM_BUDGET_USD;
3550
+ }
3538
3551
  function validateDraft(draft, homeDir, current) {
3552
+ const permission2 = draft.permission ?? current?.permission ?? "default";
3539
3553
  const merged = draft.action === "create" ? {
3540
3554
  name: draft.name,
3541
3555
  prompt: draft.prompt,
3542
- permission: draft.permission,
3556
+ permission: permission2,
3543
3557
  workDir: draft.workDir ?? "",
3544
3558
  maxTurns: SCHEDULE_DEFAULTS.maxTurns,
3545
- maxBudgetUsd: 0,
3559
+ maxBudgetUsd: budgetOf(draft),
3546
3560
  timeoutMs: SCHEDULE_DEFAULTS.timeoutMs,
3547
3561
  trigger: draft.trigger,
3548
3562
  allowRules: draft.allowRules ?? [],
3549
- budgetPending: true,
3563
+ bypassAcknowledged: permission2 === "bypass",
3550
3564
  homeDir
3551
3565
  } : {
3552
3566
  name: draft.name ?? current?.name ?? "",
3553
3567
  prompt: draft.prompt ?? current?.prompt ?? "",
3554
- permission: draft.permission ?? current?.permission ?? "default",
3568
+ permission: permission2,
3555
3569
  workDir: draft.workDir ?? current?.workDir ?? "",
3556
3570
  maxTurns: draft.maxTurns ?? current?.maxTurns ?? SCHEDULE_DEFAULTS.maxTurns,
3557
- maxBudgetUsd: current?.maxBudgetUsd ?? 0,
3571
+ maxBudgetUsd: budgetOf(draft, current),
3558
3572
  timeoutMs: draft.timeoutMs ?? current?.timeoutMs ?? SCHEDULE_DEFAULTS.timeoutMs,
3559
3573
  trigger: draft.trigger ?? current?.trigger ?? { kind: "cron", cycle: "daily", at: "09:00" },
3560
3574
  allowRules: draft.allowRules ?? current?.allowRules ?? [],
3575
+ bypassAcknowledged: permission2 === "bypass",
3561
3576
  homeDir
3562
3577
  };
3563
3578
  return validateSchedule(merged).issues.map(
@@ -3590,13 +3605,27 @@ function structuralNotes(draft, current) {
3590
3605
  }
3591
3606
  return out;
3592
3607
  }
3593
- function summarize(draft, current) {
3608
+ function registrationLine(outcome) {
3609
+ if (outcome === void 0) return void 0;
3610
+ if (outcome.ok) {
3611
+ return "\u5DF2\u7ECF\u6CE8\u518C\u8FDB\u64CD\u4F5C\u7CFB\u7EDF\uFF0C\u5230\u70B9\u4F1A\u81EA\u5DF1\u8DD1\uFF08\u5173\u6389\u8FD9\u4E2A\u5E94\u7528\u4E5F\u8DD1\uFF09\u3002";
3612
+ }
3613
+ switch (outcome.reason) {
3614
+ case "unsupported-platform":
3615
+ return "\u26A0\uFE0F \u8FD9\u53F0\u673A\u5668\uFF08Linux\uFF09\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u89E6\u53D1 \u2014\u2014 \u4EFB\u52A1\u5B58\u4E0B\u6765\u4E86\uFF0C\u4F46\u5230\u70B9\u4E0D\u4F1A\u81EA\u5DF1\u8DD1\u3002";
3616
+ case "refused":
3617
+ return outcome.refusal === "npx" ? "\u26A0\uFE0F \u6CA1\u6CE8\u518C\u8FDB\u64CD\u4F5C\u7CFB\u7EDF\uFF1A\u8FD9\u4E00\u4EFD\u662F npx \u4E34\u65F6\u8DD1\u8D77\u6765\u7684\uFF0C\u7F13\u5B58\u76EE\u5F55\u4E00\u6E05\u90A3\u6761\u4EFB\u52A1\u5C31\u5E9F\u3002\u88C5\u6210\u5168\u5C40\uFF08`npm i -g`\uFF09\u4E4B\u540E\u91CD\u5EFA\u4E00\u6B21\u624D\u6CE8\u518C\u5F97\u4E0A\u3002\u4EFB\u52A1\u672C\u8EAB\u5B58\u4E0B\u6765\u4E86\u3002" : "\u26A0\uFE0F \u6CA1\u6CE8\u518C\u8FDB\u64CD\u4F5C\u7CFB\u7EDF\uFF1A\u8FD9\u4E2A\u5BBF\u4E3B\u6CA1\u7ED9\u300C\u5230\u70B9\u4E86\u600E\u4E48\u8D77\u4E00\u6B21\u300D\u7684\u5165\u53E3\u3002\u4EFB\u52A1\u5B58\u4E0B\u6765\u4E86\uFF0C\u4F46\u5E94\u7528\u5173\u7740\u7684\u65F6\u5019\u5B83\u4E0D\u4F1A\u8DD1\u3002";
3618
+ case "backend-error":
3619
+ return `\u26A0\uFE0F \u6CE8\u518C\u8FDB\u64CD\u4F5C\u7CFB\u7EDF\u5931\u8D25\u4E86\uFF1A${outcome.detail}\u3002\u4EFB\u52A1\u5B58\u4E0B\u6765\u4E86\uFF0C\u4F46\u5230\u70B9\u4E0D\u4F1A\u81EA\u5DF1\u8DD1\u3002`;
3620
+ }
3621
+ }
3622
+ function summarizeProposal(draft, current, readOnly) {
3594
3623
  const lines = [];
3595
3624
  lines.push(
3596
3625
  draft.action === "create" ? `\u8349\u7A3F\u5DF2\u7ECF\u6446\u5230\u7528\u6237\u773C\u524D\u4E86\uFF08\u4E00\u5F20\u5361\u7247\uFF0C\u6807\u9898\u300C${draft.name}\u300D\uFF09\u3002` : `\u6539\u52A8\u63D0\u6848\u5DF2\u7ECF\u6446\u5230\u7528\u6237\u773C\u524D\u4E86\uFF08\u9488\u5BF9\u300C${current?.name ?? draft.id}\u300D\uFF09\u3002`
3597
3626
  );
3598
3627
  lines.push(
3599
- "\u5B83**\u8FD8\u6CA1\u6709\u751F\u6548\uFF0C\u4E5F\u6CA1\u6709\u843D\u5E93** \u2014\u2014 \u8981\u7528\u6237\u5728\u5361\u7247\u4E0A\u70B9\u300C\u5BA1\u9605\u5E76\u4FDD\u5B58\u300D\u3001\u5728\u8868\u5355\u91CC\u81EA\u5DF1\u586B\u4E00\u4E2A\u9884\u7B97\u4E0A\u9650\uFF0C\u90A3\u4E00\u4E0B\u624D\u662F\u771F\u7684\u5EFA\u7ACB\u3002"
3628
+ readOnly ? "\u5B83**\u8FD8\u6CA1\u6709\u751F\u6548\uFF0C\u4E5F\u6CA1\u6709\u843D\u5E93** \u2014\u2014 \u4F60\u5728\u53EA\u8BFB\u8C03\u7814\u6A21\u5F0F\uFF08plan\uFF09\u4E0B\uFF0C\u8FD9\u4E00\u6863\u4E0D\u5199\u76D8\u3002\u8981\u7528\u6237\u81EA\u5DF1\u5BA1\u4E00\u904D\u3001\u70B9\u300C\u5BA1\u9605\u5E76\u4FDD\u5B58\u300D\u624D\u662F\u771F\u7684\u5EFA\u7ACB\u3002" : "\u5B83**\u8FD8\u6CA1\u6709\u843D\u5E93** \u2014\u2014 \u8FD9\u4E2A\u5BBF\u4E3B\u6CA1\u6709\u63A5\u5199\u5165\u8DEF\uFF0C\u8981\u7528\u6237\u81EA\u5DF1\u5BA1\u4E00\u904D\u3001\u70B9\u300C\u5BA1\u9605\u5E76\u4FDD\u5B58\u300D\u624D\u662F\u771F\u7684\u5EFA\u7ACB\u3002"
3600
3629
  );
3601
3630
  const widening = draftWidening(draft, current);
3602
3631
  if (widening.length > 0) {
@@ -3607,16 +3636,77 @@ function summarize(draft, current) {
3607
3636
  for (const note of structuralNotes(draft, current)) lines.push(`\u26A0\uFE0F ${note}`);
3608
3637
  const cli = draftCommandLine(draft);
3609
3638
  lines.push(
3610
- cli === void 0 ? "\uFF08\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF1A\u6539\u4E00\u6761\u5DF2\u6709\u4EFB\u52A1\u4ECA\u5929\u53EA\u6709\u754C\u9762\u90A3\u6761\u8DEF \u2014\u2014 `epoch schedule` \u5E95\u4E0B\u6CA1\u6709\u6539\u5B57\u6BB5\u7684\u5B50\u547D\u4EE4\uFF0C\u53EA\u6709 enable / disable / rm\u3002\u522B\u7F16\u4E00\u6761 update \u51FA\u6765\u3002\uFF09" : `\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF0C\u7B49\u4EF7\u7684\u547D\u4EE4\u884C\u662F\uFF08\u9884\u7B97\u90A3\u4E00\u683C\u8981\u7528\u6237\u81EA\u5DF1\u586B\uFF09\uFF1A
3639
+ cli === void 0 ? "\uFF08\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF1A\u6539\u4E00\u6761\u5DF2\u6709\u4EFB\u52A1\u4ECA\u5929\u53EA\u6709\u754C\u9762\u90A3\u6761\u8DEF \u2014\u2014 `epoch schedule` \u5E95\u4E0B\u6CA1\u6709\u6539\u5B57\u6BB5\u7684\u5B50\u547D\u4EE4\uFF0C\u53EA\u6709 enable / disable / rm\u3002\u522B\u7F16\u4E00\u6761 update \u51FA\u6765\u3002\uFF09" : `\u6CA1\u6709\u754C\u9762\u7684\u5BBF\u4E3B\u4E0A\uFF0C\u7B49\u4EF7\u7684\u547D\u4EE4\u884C\u662F\uFF1A
3611
3640
  ${cli}`
3612
3641
  );
3613
- lines.push("\u522B\u91CD\u590D\u63D0\u4EA4\u540C\u4E00\u4EFD\u8349\u7A3F\u7B49\u5B83\u300C\u751F\u6548\u300D\u2014\u2014 \u7528\u6237\u4EC0\u4E48\u65F6\u5019\u5BA1\u662F\u4ED6\u7684\u4E8B\uFF0C\u4F60\u8FD9\u4E00\u8F6E\u5230\u6B64\u4E3A\u6B62\u3002");
3614
3642
  return lines.join("\n");
3615
3643
  }
3644
+ function summarizeSaved(draft, def, registration, current) {
3645
+ const lines = [];
3646
+ lines.push(
3647
+ `${SCHEDULE_DRAFT_SAVED_MARK}${def.id}`,
3648
+ draft.action === "create" ? `\u5DF2\u7ECF\u5EFA\u7ACB\u5E76\u751F\u6548\uFF1A\u300C${def.name}\u300D\u3002` : `\u5DF2\u7ECF\u6539\u597D\u4E86\uFF1A\u300C${current?.name ?? def.name}\u300D\u3002`
3649
+ );
3650
+ const when = draft.action === "create" ? draft.trigger : draft.trigger ?? current?.trigger;
3651
+ if (when !== void 0) lines.push(`\u5230\u70B9\uFF1A${JSON.stringify(when)}`);
3652
+ const permission2 = draft.permission ?? current?.permission;
3653
+ if (permission2 !== void 0) lines.push(`\u6743\u9650\u6863\uFF1A${permission2}`);
3654
+ const registered = registrationLine(registration);
3655
+ if (registered !== void 0) lines.push(registered);
3656
+ const widening = draftWidening(draft, current);
3657
+ if (widening.length > 0) {
3658
+ lines.push(
3659
+ `\u26A0\uFE0F \u8FD9\u6B21\u6539\u52A8\u653E\u5BBD\u4E86\u6388\u6743\u9762\uFF1A${widening.map((k) => WIDENING_TEXT[k]).join("\uFF1B")}\u3002\u5728\u4F60\u7684\u56DE\u590D\u91CC**\u4E3B\u52A8**\u628A\u8FD9\u4E00\u70B9\u8BF4\u7ED9\u7528\u6237\u542C \u2014\u2014 \u5B83\u5DF2\u7ECF\u751F\u6548\u4E86\uFF0C\u90A3\u4E2A\u4EBA\u53EA\u6709\u9760\u4F60\u8FD9\u53E5\u8BDD\u624D\u77E5\u9053\u521A\u624D\u591A\u653E\u884C\u4E86\u4EC0\u4E48\u3002`
3660
+ );
3661
+ }
3662
+ for (const note of structuralNotes(draft, current)) lines.push(`\u26A0\uFE0F ${note}`);
3663
+ lines.push(
3664
+ "\u5728\u4F60\u7684\u56DE\u590D\u91CC\u7528\u4E00\u53E5\u8BDD\u4EA4\u4EE3\u300C\u5EFA\u4E86\u4EC0\u4E48\u3001\u4EC0\u4E48\u65F6\u5019\u8DD1\u300D\u5C31\u591F\u4E86\u3002**\u522B\u518D\u8BF4\u300C\u8981\u4E0D\u8981\u6211\u5EFA\u4E00\u6761\u300D** \u2014\u2014 \u5B83\u5DF2\u7ECF\u5728\u4E86\uFF1B\u7528\u6237\u60F3\u64A4\u6389\uFF0C\u5361\u7247\u4E0A\u6709\u64A4\u9500\u952E\uFF0C\u4E5F\u53EF\u4EE5\u53BB\u81EA\u52A8\u5316\u9762\u677F\u5220\u3002"
3665
+ );
3666
+ return lines.join("\n");
3667
+ }
3668
+ async function persist(save, draft, current) {
3669
+ const permission2 = draft.permission ?? current?.permission ?? "default";
3670
+ const acknowledged = { bypassAcknowledged: permission2 === "bypass" };
3671
+ if (draft.action === "create") {
3672
+ return save.create(
3673
+ {
3674
+ name: draft.name,
3675
+ prompt: draft.prompt,
3676
+ permission: draft.permission,
3677
+ maxBudgetUsd: budgetOf(draft),
3678
+ trigger: draft.trigger,
3679
+ ...draft.workDir === void 0 ? {} : { workDir: draft.workDir },
3680
+ ...draft.allowTools === void 0 ? {} : { allowTools: draft.allowTools },
3681
+ ...draft.allowOperations === void 0 ? {} : { allowOperations: draft.allowOperations },
3682
+ ...draft.allowRules === void 0 ? {} : { allowRules: draft.allowRules }
3683
+ },
3684
+ acknowledged
3685
+ );
3686
+ }
3687
+ return save.update(
3688
+ draft.id,
3689
+ {
3690
+ ...draft.name === void 0 ? {} : { name: draft.name },
3691
+ ...draft.prompt === void 0 ? {} : { prompt: draft.prompt },
3692
+ ...draft.permission === void 0 ? {} : { permission: draft.permission },
3693
+ ...draft.trigger === void 0 ? {} : { trigger: draft.trigger },
3694
+ ...draft.workDir === void 0 ? {} : { workDir: draft.workDir },
3695
+ ...draft.enabled === void 0 ? {} : { enabled: draft.enabled },
3696
+ ...draft.maxTurns === void 0 ? {} : { maxTurns: draft.maxTurns },
3697
+ ...draft.timeoutMs === void 0 ? {} : { timeoutMs: draft.timeoutMs },
3698
+ ...draft.maxBudgetUsd === void 0 ? {} : { maxBudgetUsd: draft.maxBudgetUsd },
3699
+ ...draft.allowTools === void 0 ? {} : { allowTools: draft.allowTools },
3700
+ ...draft.allowOperations === void 0 ? {} : { allowOperations: draft.allowOperations },
3701
+ ...draft.allowRules === void 0 ? {} : { allowRules: draft.allowRules }
3702
+ },
3703
+ acknowledged
3704
+ );
3705
+ }
3616
3706
  function createScheduleDraftTool(deps) {
3617
3707
  return {
3618
3708
  name: SCHEDULE_DRAFT_TOOL,
3619
- description: "\u8D77\u8349\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u4EA4\u7ED9\u7528\u6237\u5BA1\u3002\u5B9A\u65F6\u4EFB\u52A1\u662F\u300C\u5230\u70B9\u4E86\u81EA\u5DF1\u8DD1\u4E00\u6B21\u300D\u2014\u2014\u7528\u6237\u8BF4\u300C\u4EE5\u540E\u6BCF\u5929/\u6BCF\u5468\u5E2E\u6211\u505A\u67D0\u4EF6\u4E8B\u300D\u300C\u5B9A\u671F\u68C0\u67E5\u67D0\u67D0\u300D\uFF0C\u6216\u8005\u4F60\u53D1\u73B0\u67D0\u4EF6\u4E8B\u4ED6\u6BCF\u6B21\u90FD\u8981\u624B\u52A8\u505A\u4E00\u904D\u65F6\uFF0C\u7528\u5B83\u3002action=list \u770B\u73B0\u6709\u4EFB\u52A1\uFF08\u8981\u6539\u67D0\u6761\u5FC5\u987B\u5148 list \u62FF\u5230 id\uFF09\uFF1Baction=create \u8D77\u8349\u65B0\u7684\uFF1Baction=update \u63D0\u8BAE\u6539\u4E00\u6761\u5DF2\u6709\u7684\u3002\u26A0\uFE0F \u4F60\u4EA4\u7684\u662F\u8349\u7A3F\uFF0C\u4E0D\u662F\u4EFB\u52A1\uFF1A\u5B83\u4E0D\u4F1A\u751F\u6548\uFF0C\u8981\u7528\u6237\u81EA\u5DF1\u5BA1\u4E00\u904D\u3001\u586B\u4E00\u4E2A\u9884\u7B97\u4E0A\u9650\u624D\u5EFA\u7ACB\u3002\u4EA4\u5B8C\u8FD9\u4E00\u8F6E\u5C31\u7ED3\u675F\uFF0C\u522B\u53CD\u590D\u63D0\u4EA4\u7B49\u5B83\u751F\u6548\u3002\u6743\u9650\u6863\u53EA\u6709 plan/default/acceptEdits/auto \u2014\u2014 bypass \u4E0D\u8BB8\u63D0\u8BAE\u3002\u8981\u653E\u5F00\u67D0\u51E0\u4EF6\u4E8B\u5C31\u7528 default \u52A0\u7CBE\u786E\u7684 allowRules\uFF08\u6BD4\u5982 terminal(git status)\uFF09\uFF0C\u522B\u6574\u4F53\u8C03\u677E\u3002",
3709
+ description: "\u5EFA\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u6216\u8005\u6539\u4E00\u6761\u5DF2\u6709\u7684\u3002\u5B9A\u65F6\u4EFB\u52A1\u662F\u300C\u5230\u70B9\u4E86\u81EA\u5DF1\u8DD1\u4E00\u6B21\u300D\u2014\u2014\u7528\u6237\u8BF4\u300C\u4EE5\u540E\u6BCF\u5929/\u6BCF\u5468\u5E2E\u6211\u505A\u67D0\u4EF6\u4E8B\u300D\u300C\u5B9A\u671F\u68C0\u67E5\u67D0\u67D0\u300D\uFF0C\u6216\u8005\u4F60\u53D1\u73B0\u67D0\u4EF6\u4E8B\u4ED6\u6BCF\u6B21\u90FD\u8981\u624B\u52A8\u505A\u4E00\u904D\u65F6\uFF0C\u7528\u5B83\u3002action=list \u770B\u73B0\u6709\u4EFB\u52A1\uFF08\u8981\u6539\u67D0\u6761\u5FC5\u987B\u5148 list \u62FF\u5230 id\uFF09\uFF1Baction=create \u76F4\u63A5\u5EFA\u4E00\u6761\uFF1Baction=update \u76F4\u63A5\u6539\u4E00\u6761\u3002\u26A0\uFE0F **\u5B83\u4EA4\u4E0A\u53BB\u5C31\u751F\u6548**\uFF08plan \u53EA\u8BFB\u6863\u4E0B\u9664\u5916\uFF0C\u90A3\u4E00\u6863\u53EA\u4EA4\u63D0\u6848\uFF09\u2014\u2014 \u5EFA\u5B8C\u5728\u4F60\u7684\u56DE\u590D\u91CC\u4EA4\u4EE3\u4E00\u53E5\u5C31\u884C\uFF0C\u522B\u95EE\u300C\u8981\u4E0D\u8981\u6211\u5EFA\u300D\u3002\u9884\u7B97\u4E0D\u7ED9\u5C31\u662F 200 \u7F8E\u5143\uFF1B\u8FD9\u6D3B\u660E\u663E\u66F4\u8F7B\u6216\u66F4\u91CD\u65F6\u81EA\u5DF1\u7ED9 maxBudgetUsd\u3002\u6743\u9650\u6863 plan/default/acceptEdits/auto/bypass \u90FD\u80FD\u63D0 \u2014\u2014 \u4F46**\u9ED8\u8BA4\u7528 plan**\uFF1A\u53EA\u8BFB\u6863\u5929\u7136\u96F6\u6388\u6743\u9762\uFF0C\u9700\u8981\u5199\u4E1C\u897F\u518D\u7528 default \u52A0\u4E00\u4EFD\u7CBE\u786E\u7684 allowRules\uFF08\u6BD4\u5982 terminal(git status)\uFF09\uFF0C\u522B\u4E00\u4E0A\u6765\u5C31 bypass\u3002",
3620
3710
  parameters: {
3621
3711
  type: "object",
3622
3712
  properties: {
@@ -3627,7 +3717,7 @@ function createScheduleDraftTool(deps) {
3627
3717
  },
3628
3718
  note: {
3629
3719
  type: "string",
3630
- description: "\u4E3A\u4EC0\u4E48\u5EFA\u8BAE\u8FD9\u6761\uFF0C\u4E00\u53E5\u8BDD\u3002create / update \u5FC5\u586B\uFF0C\u4F1A\u5370\u5728\u7528\u6237\u770B\u5230\u7684\u5361\u7247\u4E0A\uFF1B\u8BF4\u4E0D\u51FA\u771F\u5B9E\u7406\u7531\u5C31\u522B\u63D0\u8FD9\u4E2A\u5EFA\u8BAE"
3720
+ description: "\u4E3A\u4EC0\u4E48\u5EFA\u8FD9\u6761\uFF0C\u4E00\u53E5\u8BDD\u3002create / update \u5FC5\u586B\uFF0C\u4F1A\u5370\u5728\u7528\u6237\u770B\u5230\u7684\u5361\u7247\u4E0A\uFF1B\u8BF4\u4E0D\u51FA\u771F\u5B9E\u7406\u7531\u5C31\u522B\u63D0\u8FD9\u4E2A\u5EFA\u8BAE"
3631
3721
  },
3632
3722
  id: { type: "string", description: "\u6539\u54EA\u4E00\u6761\u3002update \u5FC5\u586B\uFF0C\u4ECE action=list \u91CC\u62FF" },
3633
3723
  name: { type: "string", description: "\u4EFB\u52A1\u540D\uFF0C\u7528\u6237\u5728\u5217\u8868\u91CC\u770B\u5230\u7684\u5C31\u662F\u5B83" },
@@ -3660,8 +3750,12 @@ function createScheduleDraftTool(deps) {
3660
3750
  },
3661
3751
  permission: {
3662
3752
  type: "string",
3663
- description: "\u6743\u9650\u6863\u3002plan=\u53EA\u8BFB\uFF08\u8BFB\u4E00\u904D\u5199\u4EFD\u62A5\u544A\u7684\u4EFB\u52A1\u5C31\u8BE5\u7528\u5B83\uFF0C\u5929\u7136\u96F6\u6388\u6743\u9762\uFF09\uFF1Bdefault=\u6309\u9700\u786E\u8BA4\uFF1BacceptEdits=\u81EA\u52A8\u63A5\u53D7\u6539\u52A8\uFF1Bauto=\u4E0D\u518D\u786E\u8BA4",
3664
- enum: ["plan", "default", "acceptEdits", "auto"]
3753
+ description: "\u6743\u9650\u6863\u3002plan=\u53EA\u8BFB\uFF08\u8BFB\u4E00\u904D\u5199\u4EFD\u62A5\u544A\u7684\u4EFB\u52A1\u5C31\u8BE5\u7528\u5B83\uFF0C\u5929\u7136\u96F6\u6388\u6743\u9762\uFF09\uFF1Bdefault=\u6309\u9700\u786E\u8BA4\uFF1BacceptEdits=\u81EA\u52A8\u63A5\u53D7\u6539\u52A8\uFF1Bauto=\u4E0D\u518D\u786E\u8BA4\uFF1Bbypass=\u8DF3\u8FC7\u5168\u90E8\u68C0\u67E5\uFF08**\u6700\u540E\u624B\u6BB5**\uFF1A\u5230\u70B9\u4E86\u5B83\u60F3\u8DD1\u4EC0\u4E48\u5C31\u8DD1\u4EC0\u4E48\uFF09",
3754
+ enum: ["plan", "default", "acceptEdits", "auto", "bypass"]
3755
+ },
3756
+ maxBudgetUsd: {
3757
+ type: "number",
3758
+ description: "\u6BCF\u6B21\u6700\u591A\u82B1\u591A\u5C11\u94B1\uFF08\u7F8E\u5143\uFF0C\u8981\u5927\u4E8E 0\uFF09\u3002\u4E0D\u7ED9\u5C31\u662F 200\u3002\u8FD9\u6D3B\u660E\u663E\u66F4\u8F7B\uFF08\u8BFB\u4E00\u4E2A\u6587\u4EF6\u3001\u53D1\u4E00\u6761\u6D88\u606F\uFF09\u5C31\u7ED9\u5C0F\u4E00\u70B9\uFF0C\u660E\u663E\u66F4\u91CD\uFF08\u626B\u5927\u4ED3\u3001\u591A\u8F6E\u8C03\u7814\uFF09\u5C31\u653E\u5927 \u2014\u2014 \u82B1\u8D85\u4E86\u90A3\u4E00\u8F6E\u4F1A\u505C\u4F4F"
3665
3759
  },
3666
3760
  allowTools: {
3667
3761
  type: "array",
@@ -3687,58 +3781,71 @@ function createScheduleDraftTool(deps) {
3687
3781
  exposure: ToolExposure.Direct,
3688
3782
  supportsParallel: false,
3689
3783
  annotations: {
3690
- readOnlyHint: true,
3691
- destructiveHint: false,
3692
- idempotentHint: true,
3784
+ readOnlyHint: false,
3785
+ destructiveHint: true,
3786
+ idempotentHint: false,
3693
3787
  openWorldHint: false
3694
3788
  },
3695
3789
  operation: "file_read",
3696
3790
  describeTarget: (args) => `schedule_draft:${String(args["action"] ?? "list")}`,
3697
- execute(rawArgs, _ctx) {
3791
+ async execute(rawArgs, ctx) {
3698
3792
  const startedAt = Date.now();
3699
3793
  const action = rawArgs["action"];
3700
3794
  if (action === "list") {
3701
3795
  const rows = deps.list();
3702
- return Promise.resolve(
3703
- say4(
3704
- rows.length === 0 ? "\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\u90FD\u8FD8\u6CA1\u6709\u3002\u8981\u65B0\u5EFA\u5C31\u7528 action=create\u3002" : `\u73B0\u6709 ${rows.length} \u6761\uFF1A
3796
+ return say4(
3797
+ rows.length === 0 ? "\u4E00\u6761\u5B9A\u65F6\u4EFB\u52A1\u90FD\u8FD8\u6CA1\u6709\u3002\u8981\u65B0\u5EFA\u5C31\u7528 action=create\u3002" : `\u73B0\u6709 ${rows.length} \u6761\uFF1A
3705
3798
  ${rows.map(describeRow).join("\n")}`,
3706
- true,
3707
- startedAt
3708
- )
3799
+ true,
3800
+ startedAt
3709
3801
  );
3710
3802
  }
3711
3803
  const parsed = parseScheduleDraft(rawArgs);
3712
- if (!parsed.ok) return Promise.resolve(say4(parsed.message, false, startedAt));
3804
+ if (!parsed.ok) return say4(parsed.message, false, startedAt);
3713
3805
  const draft = parsed.value;
3714
3806
  const current = draft.action === "update" ? deps.get(draft.id) : void 0;
3715
3807
  if (draft.action === "update" && current === void 0) {
3716
- return Promise.resolve(
3717
- say4(`\u6CA1\u6709 id \u4E3A ${draft.id} \u7684\u4EFB\u52A1\u3002\u5148\u7528 action=list \u770B\u4E00\u773C\u73B0\u6709\u7684\u3002`, false, startedAt)
3808
+ return say4(
3809
+ `\u6CA1\u6709 id \u4E3A ${draft.id} \u7684\u4EFB\u52A1\u3002\u5148\u7528 action=list \u770B\u4E00\u773C\u73B0\u6709\u7684\u3002`,
3810
+ false,
3811
+ startedAt
3718
3812
  );
3719
3813
  }
3720
3814
  const issues = validateDraft(draft, deps.homeDir, current);
3721
3815
  if (issues.length > 0) {
3722
- return Promise.resolve(
3723
- say4(
3724
- `\u8FD9\u4EFD\u8349\u7A3F\u4FDD\u5B58\u4E0D\u4E86\uFF0C\u5148\u6539\u8FD9\u51E0\u5904\u518D\u4EA4\uFF1A${issues.join("\u3001")}\u3002\uFF08\u8FD9\u4E9B\u662F\u6821\u9A8C\u7801\uFF0C\u542B\u4E49\u770B\u5B57\u9762\uFF1Bdetail \u62EC\u5728\u540E\u9762\uFF09`,
3725
- false,
3726
- startedAt
3727
- )
3816
+ return say4(
3817
+ `\u8FD9\u4EFD\u63D0\u6848\u5B58\u4E0D\u4E0B\u6765\uFF0C\u5148\u6539\u8FD9\u51E0\u5904\u518D\u8BF4\uFF1A${issues.join("\u3001")}\u3002\uFF08\u8FD9\u4E9B\u662F\u6821\u9A8C\u7801\uFF0C\u542B\u4E49\u770B\u5B57\u9762\uFF1Bdetail \u62EC\u5728\u540E\u9762\uFF09`,
3818
+ false,
3819
+ startedAt
3728
3820
  );
3729
3821
  }
3730
3822
  const unattended = unattendedIssues(draft);
3731
3823
  if (unattended.length > 0) {
3732
- return Promise.resolve(
3733
- say4(
3734
- `\u8FD9\u4EFD\u8349\u7A3F\u4FDD\u5B58\u5F97\u4E86\uFF0C\u4F46\u5B83\u5230\u70B9\u4E86**\u8DD1\u4E0D\u51FA\u7ED3\u679C** \u2014\u2014 \u5B9A\u65F6\u4EFB\u52A1\u662F\u65E0\u4EBA\u503C\u5B88\u7684\uFF0C\u6CA1\u4EBA\u5728\u65C1\u8FB9\u56DE\u8BDD\u3002\u6539\u5B8C\u518D\u4EA4\uFF1A
3824
+ return say4(
3825
+ `\u8FD9\u4EFD\u63D0\u6848\u5B58\u5F97\u4E0B\u6765\uFF0C\u4F46\u5B83\u5230\u70B9\u4E86**\u8DD1\u4E0D\u51FA\u7ED3\u679C** \u2014\u2014 \u5B9A\u65F6\u4EFB\u52A1\u662F\u65E0\u4EBA\u503C\u5B88\u7684\uFF0C\u6CA1\u4EBA\u5728\u65C1\u8FB9\u56DE\u8BDD\u3002\u6539\u5B8C\u518D\u4EA4\uFF1A
3735
3826
  ${unattended.map((x) => `\xB7 ${x}`).join("\n")}`,
3736
- false,
3737
- startedAt
3738
- )
3827
+ false,
3828
+ startedAt
3829
+ );
3830
+ }
3831
+ const readOnly = ctx.permissionLevel === "plan";
3832
+ if (readOnly || deps.save === void 0) {
3833
+ return say4(summarizeProposal(draft, current, readOnly), true, startedAt);
3834
+ }
3835
+ const saved = await persist(deps.save, draft, current);
3836
+ if (saved.issues.length > 0) {
3837
+ const codes = saved.issues.map((x) => x.detail ? `${x.code}\uFF08${x.detail}\uFF09` : x.code);
3838
+ return say4(`\u5F15\u64CE\u62D2\u7EDD\u4E86\u8FD9\u4EFD\u63D0\u6848\uFF0C\u4E00\u4E2A\u5B57\u90FD\u6CA1\u5199\uFF1A${codes.join("\u3001")}\u3002`, false, startedAt);
3839
+ }
3840
+ const def = saved.schedule;
3841
+ if (def === void 0) {
3842
+ return say4(
3843
+ "\u5F15\u64CE\u8BF4\u5B58\u4E0B\u6765\u4E86\uFF0C\u4F46\u6CA1\u56DE\u90A3\u6761\u4EFB\u52A1 \u2014\u2014 \u53BB\u81EA\u52A8\u5316\u9762\u677F\u770B\u4E00\u773C\u5B83\u5230\u5E95\u5728\u4E0D\u5728\u3002",
3844
+ false,
3845
+ startedAt
3739
3846
  );
3740
3847
  }
3741
- return Promise.resolve(say4(summarize(draft, current), true, startedAt));
3848
+ return say4(summarizeSaved(draft, def, saved.registration, current), true, startedAt);
3742
3849
  }
3743
3850
  };
3744
3851
  }
@@ -4229,41 +4336,41 @@ function clampWindow(raw, fallback2, max) {
4229
4336
  function stamp2(ms) {
4230
4337
  return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
4231
4338
  }
4232
- function budgetOf(total) {
4339
+ function budgetOf2(total) {
4233
4340
  const shares = ANCHOR_SHARE + Math.max(0, total - 1);
4234
4341
  const unit = Math.max(MIN_BODY, Math.floor(BODY_BUDGET / shares));
4235
4342
  return { anchor: unit * ANCHOR_SHARE, other: unit };
4236
4343
  }
4237
- function body(text, budget) {
4344
+ function body(text, budget2) {
4238
4345
  const flat = text.replace(/\r\n/g, "\n").trimEnd();
4239
- if (flat.length <= budget) return flat;
4240
- const rest = flat.length - budget;
4241
- return `${flat.slice(0, budget)}
4346
+ if (flat.length <= budget2) return flat;
4347
+ const rest = flat.length - budget2;
4348
+ return `${flat.slice(0, budget2)}
4242
4349
  \u2026[cut ${rest} more chars \u2014 lower before/after to spend the budget on fewer messages]`;
4243
4350
  }
4244
4351
  function surfaceOf(record, isCurrent) {
4245
4352
  if (!isCurrent) return record.surface === "active" ? "active there" : "compacted there";
4246
4353
  return record.surface === "active" ? "still in your context" : "compacted out of your context";
4247
4354
  }
4248
- function renderRecord(record, mark, budget, isCurrent) {
4355
+ function renderRecord(record, mark, budget2, isCurrent) {
4249
4356
  const tool = record.toolName ? ` \xB7 ${record.toolName}` : "";
4250
4357
  return [
4251
4358
  `${mark} #${record.messageId} [${record.role}${tool} \xB7 ${stamp2(record.timestamp)} \xB7 ${surfaceOf(record, isCurrent)}]`,
4252
- ` ${body(record.content, budget).split("\n").join("\n ")}`
4359
+ ` ${body(record.content, budget2).split("\n").join("\n ")}`
4253
4360
  ].join("\n");
4254
4361
  }
4255
4362
  function renderWindow(out, sessionId, isCurrent) {
4256
4363
  const anchor = out.anchor;
4257
4364
  if (!anchor) return "";
4258
4365
  const count = out.before.length + out.after.length + 1;
4259
- const budget = budgetOf(count);
4366
+ const budget2 = budgetOf2(count);
4260
4367
  return [
4261
4368
  `Session ${sessionId}${isCurrent ? " (this session)" : ""} \u2014 ${out.title || "(untitled)"}`,
4262
4369
  `${count} of ${out.total} message(s), around #${anchor.messageId}:`,
4263
4370
  "",
4264
- ...out.before.map((r) => renderRecord(r, " ", budget.other, isCurrent)),
4265
- renderRecord(anchor, "\xBB", budget.anchor, isCurrent),
4266
- ...out.after.map((r) => renderRecord(r, " ", budget.other, isCurrent))
4371
+ ...out.before.map((r) => renderRecord(r, " ", budget2.other, isCurrent)),
4372
+ renderRecord(anchor, "\xBB", budget2.anchor, isCurrent),
4373
+ ...out.after.map((r) => renderRecord(r, " ", budget2.other, isCurrent))
4267
4374
  ].join("\n");
4268
4375
  }
4269
4376
  function createSessionMessagesReadTool(deps) {
@@ -5976,6 +6083,7 @@ var CommandFrontmatterSchema = z.object({
5976
6083
  });
5977
6084
  var FRONTMATTER_KEYS3 = Object.keys(CommandFrontmatterSchema.shape);
5978
6085
  var MAX_POSITIONAL = 9;
6086
+ var ARG_PREVIEW_MAX = 120;
5979
6087
  var UTILITY_KEYWORD = "utility";
5980
6088
  function loadCommands(files, source, prefix) {
5981
6089
  const commands = [];
@@ -6081,6 +6189,14 @@ function expandCommand(def, args, opts = {}) {
6081
6189
  })
6082
6190
  );
6083
6191
  }
6192
+ if (trimmed !== "" && used.size === 0 && !def.prompt.includes("$ARGUMENTS")) {
6193
+ warnings.push(
6194
+ t("custom_command.unused_args", {
6195
+ name: def.name,
6196
+ args: trimmed.length > ARG_PREVIEW_MAX ? `${trimmed.slice(0, ARG_PREVIEW_MAX)}\u2026` : trimmed
6197
+ })
6198
+ );
6199
+ }
6084
6200
  const model = resolveCommandModel(def, opts, warnings);
6085
6201
  return {
6086
6202
  prompt: out,
@@ -6156,7 +6272,7 @@ function loadCommandDefinitions(opts) {
6156
6272
  }
6157
6273
  diags?.ok(
6158
6274
  SLASH_COMMANDS_DIAG_MODULE,
6159
- summarize2(commands.length, plugin.commands.length, user, project)
6275
+ summarize(commands.length, plugin.commands.length, user, project)
6160
6276
  );
6161
6277
  return {
6162
6278
  commands,
@@ -6167,7 +6283,7 @@ function loadCommandDefinitions(opts) {
6167
6283
  issues: [...plugin.issues, ...user.issues, ...project.issues]
6168
6284
  };
6169
6285
  }
6170
- function summarize2(total, pluginCount, user, project) {
6286
+ function summarize(total, pluginCount, user, project) {
6171
6287
  if (total === 0) return t("custom_command.none");
6172
6288
  const parts = [
6173
6289
  t("custom_command.count_user", { count: user.commands.length }),
@@ -8924,7 +9040,7 @@ async function snapshotFiles(paths) {
8924
9040
  }
8925
9041
  async function collectDiffs(before, workDir) {
8926
9042
  const out = [];
8927
- let budget = MAX_CALL_BYTES;
9043
+ let budget2 = MAX_CALL_BYTES;
8928
9044
  for (const [path, old] of before) {
8929
9045
  if (old.kind === "opaque") continue;
8930
9046
  const now = await readSnapshot(path);
@@ -8932,8 +9048,8 @@ async function collectDiffs(before, workDir) {
8932
9048
  if (old.kind === "absent" && now.kind === "absent") continue;
8933
9049
  if (old.kind === "text" && now.kind === "text" && old.text === now.text) continue;
8934
9050
  const bytes = (old.kind === "text" ? old.bytes : 0) + (now.kind === "text" ? now.bytes : 0);
8935
- if (bytes > budget) continue;
8936
- budget -= bytes;
9051
+ if (bytes > budget2) continue;
9052
+ budget2 -= bytes;
8937
9053
  out.push({
8938
9054
  filePath: workspaceDisplayPath(path, workDir),
8939
9055
  ...old.kind === "text" ? { oldContent: old.text } : {},
@@ -9521,10 +9637,10 @@ function renderSettings(s) {
9521
9637
  bits.push(`\u4E0A\u4E0B\u6587\u7A97\u53E3 ${s.contextLength}`);
9522
9638
  bits.push(`\u81EA\u52A8\u538B\u7F29 ${s.compression.enabled ? `\u5F00\uFF08${s.compression.threshold}\uFF09` : "\u5173"}`);
9523
9639
  bits.push(`\u7EC8\u7AEF\u6C99\u7BB1 ${s.sandboxTerminal ? "\u5F00" : "\u5173"}`);
9524
- const budget = [];
9525
- if (s.budget?.maxCostUsd !== void 0) budget.push(`$${s.budget.maxCostUsd}`);
9526
- if (s.budget?.maxTokens !== void 0) budget.push(`${s.budget.maxTokens} tokens`);
9527
- bits.push(`\u9884\u7B97\u4E0A\u9650 ${budget.length > 0 ? budget.join(" / ") : "\u672A\u8BBE"}`);
9640
+ const budget2 = [];
9641
+ if (s.budget?.maxCostUsd !== void 0) budget2.push(`$${s.budget.maxCostUsd}`);
9642
+ if (s.budget?.maxTokens !== void 0) budget2.push(`${s.budget.maxTokens} tokens`);
9643
+ bits.push(`\u9884\u7B97\u4E0A\u9650 ${budget2.length > 0 ? budget2.join(" / ") : "\u672A\u8BBE"}`);
9528
9644
  return [
9529
9645
  "<settings>",
9530
9646
  bits.join(" \xB7 "),
@@ -9641,7 +9757,7 @@ function extensionFor(mediaType) {
9641
9757
  if (sub === "jpeg") return ".jpg";
9642
9758
  return /^[a-z0-9]{1,8}$/.test(sub) ? `.${sub}` : ".bin";
9643
9759
  }
9644
- function persist(bytes, mediaType, dir, hint) {
9760
+ function persist2(bytes, mediaType, dir, hint) {
9645
9761
  const digest2 = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
9646
9762
  const safeHint = (hint ?? "").replace(/[^A-Za-z0-9_-]/g, "").slice(0, 40);
9647
9763
  const stem = safeHint ? `${safeHint}-${digest2}` : digest2;
@@ -9682,7 +9798,7 @@ function admitArtifact(raw, policy) {
9682
9798
  const bytes = Buffer.from(raw.base64, "base64");
9683
9799
  const byteLen = bytes.length;
9684
9800
  const size = raw.kind === "image" ? readImageSize(bytes) : void 0;
9685
- const path = policy.artifactsDir ? persist(bytes, raw.mediaType, policy.artifactsDir, raw.filename) : void 0;
9801
+ const path = policy.artifactsDir ? persist2(bytes, raw.mediaType, policy.artifactsDir, raw.filename) : void 0;
9686
9802
  const dims = size ? ` ${size.width}\xD7${size.height}` : "";
9687
9803
  const head = raw.note ? `${raw.note} ` : "";
9688
9804
  const reason9 = rejectionReason(raw, policy, byteLen, size);
@@ -11768,7 +11884,7 @@ function toAbsolute2(path, workDir) {
11768
11884
  }
11769
11885
  var MAX_TOOL_OUTPUT = 3e4;
11770
11886
  var MAX_SUB_CALL_OUTPUT = CODE_MODE_LIMITS.maxValueChars;
11771
- function budgetOf2(tool, depth, path = "success") {
11887
+ function budgetOf3(tool, depth, path = "success") {
11772
11888
  if (tool.governsOwnOutput && path === "success") return Number.POSITIVE_INFINITY;
11773
11889
  return depth > 0 ? MAX_SUB_CALL_OUTPUT : MAX_TOOL_OUTPUT;
11774
11890
  }
@@ -12019,7 +12135,7 @@ var ToolExecutor = class {
12019
12135
  permissionLevel: this.deps.permissionManager?.getLevel() || "auto",
12020
12136
  ...signal ? { signal } : {},
12021
12137
  ...onOutput ? { onOutput } : {},
12022
- outputBudget: budgetOf2(et, depth),
12138
+ outputBudget: budgetOf3(et, depth),
12023
12139
  ...depth < MAX_SUB_CALL_DEPTH ? { callTool: this.subCallChannel(sessionId, signal, span, depth) } : {}
12024
12140
  });
12025
12141
  if (snapshotted.length > 0) {
@@ -12051,7 +12167,7 @@ var ToolExecutor = class {
12051
12167
  success: true,
12052
12168
  output: this.renderOutput(
12053
12169
  et,
12054
- appendArtifactSummaries(text, artifacts, budgetOf2(et, depth), shapeOf(r.output))
12170
+ appendArtifactSummaries(text, artifacts, budgetOf3(et, depth), shapeOf(r.output))
12055
12171
  ),
12056
12172
  ...artifacts.length > 0 ? { artifacts } : {},
12057
12173
  ...diffs.length > 0 ? { diffs } : {}
@@ -12062,7 +12178,7 @@ var ToolExecutor = class {
12062
12178
  success: false,
12063
12179
  output: this.renderOutput(
12064
12180
  et,
12065
- truncateForModel(failureText(r), budgetOf2(et, depth, "failure"), "text")
12181
+ truncateForModel(failureText(r), budgetOf3(et, depth, "failure"), "text")
12066
12182
  )
12067
12183
  });
12068
12184
  } catch (err) {
@@ -12239,13 +12355,13 @@ function stripHistoryImages(history) {
12239
12355
  });
12240
12356
  return { messages, stripped };
12241
12357
  }
12242
- function budgetStop(budget, messages) {
12243
- const breach = budget.breachReason();
12244
- if (breach) return { message: budget.describeBreach(breach), breach };
12358
+ function budgetStop(budget2, messages) {
12359
+ const breach = budget2.breachReason();
12360
+ if (breach) return { message: budget2.describeBreach(breach), breach };
12245
12361
  const imageTokens = pendingImageTokens(messages);
12246
- const projected = budget.projectedBreach(imageTokens);
12362
+ const projected = budget2.projectedBreach(imageTokens);
12247
12363
  if (!projected) return void 0;
12248
- return { message: budget.describeProjectedBreach(projected, imageTokens), breach: projected };
12364
+ return { message: budget2.describeProjectedBreach(projected, imageTokens), breach: projected };
12249
12365
  }
12250
12366
  var AgentLoop = class {
12251
12367
  config;
@@ -12481,7 +12597,7 @@ var AgentLoop = class {
12481
12597
  const addRun = (u) => {
12482
12598
  runUsage = runUsage ? addUsage(runUsage, u) : u;
12483
12599
  };
12484
- const budget = new BudgetGuard({
12600
+ const budget2 = new BudgetGuard({
12485
12601
  ...this.config.budget ? { budget: this.config.budget } : {},
12486
12602
  ...this.config.modelPricing ? { pricing: this.config.modelPricing } : {},
12487
12603
  ...input.maxCallCostUsd !== void 0 ? { maxCallCostUsd: input.maxCallCostUsd } : {},
@@ -12493,7 +12609,7 @@ var AgentLoop = class {
12493
12609
  const finish = (reason9, breach) => ({
12494
12610
  type: "finish",
12495
12611
  reason: reason9,
12496
- usage: budget.snapshot(),
12612
+ usage: budget2.snapshot(),
12497
12613
  ...runUsage ? { runUsage } : {},
12498
12614
  ...lastModel ? { model: lastModel } : {},
12499
12615
  ...lastRequestedModel ? { requestedModel: lastRequestedModel } : {},
@@ -12545,8 +12661,8 @@ var AgentLoop = class {
12545
12661
  await this.prompt.syncMemory(userText, lastText);
12546
12662
  return;
12547
12663
  }
12548
- budget.syncFromStore();
12549
- const stop = budgetStop(budget, messages);
12664
+ budget2.syncFromStore();
12665
+ const stop = budgetStop(budget2, messages);
12550
12666
  if (stop) {
12551
12667
  yield { type: "text-delta", text: stop.message };
12552
12668
  await this.prompt.syncMemory(userText, lastText || stop.message);
@@ -12598,8 +12714,8 @@ var AgentLoop = class {
12598
12714
  }
12599
12715
  if (rescued.usage) {
12600
12716
  addRun(rescued.usage);
12601
- yield* budget.toEvents(
12602
- budget.add(rescued.usage, this.rateOf(rescued.model)),
12717
+ yield* budget2.toEvents(
12718
+ budget2.add(rescued.usage, this.rateOf(rescued.model)),
12603
12719
  runUsage
12604
12720
  );
12605
12721
  }
@@ -12675,7 +12791,7 @@ var AgentLoop = class {
12675
12791
  });
12676
12792
  if (rescued.usage) {
12677
12793
  addRun(rescued.usage);
12678
- yield* budget.toEvents(budget.add(rescued.usage, this.rateOf(rescued.model)), runUsage);
12794
+ yield* budget2.toEvents(budget2.add(rescued.usage, this.rateOf(rescued.model)), runUsage);
12679
12795
  }
12680
12796
  if (!shrank(messages, rescued.messages)) throw err;
12681
12797
  messages = rescued.messages;
@@ -12732,14 +12848,14 @@ var AgentLoop = class {
12732
12848
  "epoch.token_type": "cache_write"
12733
12849
  });
12734
12850
  }
12735
- const status = budget.add(usage, this.rateOf(result.model));
12851
+ const status = budget2.add(usage, this.rateOf(result.model));
12736
12852
  input.onTurnUsage?.(
12737
12853
  status.turnCostUsd === void 0 ? usage : { ...usage, costUsd: status.turnCostUsd }
12738
12854
  );
12739
12855
  addRun(usage);
12740
12856
  lastModel = result.model;
12741
12857
  lastRequestedModel = result.requestedModel;
12742
- yield* budget.toEvents(status, runUsage);
12858
+ yield* budget2.toEvents(status, runUsage);
12743
12859
  }
12744
12860
  const verdict = mergeVerdicts(guard?.verdict(), reasoningGuard?.verdict());
12745
12861
  if (verdict) this.reportCompliance(verdict, "output");
@@ -12976,8 +13092,8 @@ ${finalText}` };
12976
13092
  messages = compacted.messages;
12977
13093
  if (compacted.usage) {
12978
13094
  addRun(compacted.usage);
12979
- yield* budget.toEvents(
12980
- budget.add(compacted.usage, this.rateOf(compacted.model)),
13095
+ yield* budget2.toEvents(
13096
+ budget2.add(compacted.usage, this.rateOf(compacted.model)),
12981
13097
  runUsage
12982
13098
  );
12983
13099
  }
@@ -16916,8 +17032,8 @@ var MemoryManager = class {
16916
17032
  async add(target, content) {
16917
17033
  const label = injectionLabelOf(content);
16918
17034
  if (label !== void 0) return this.refuseInjection(label);
16919
- const budget = this.getBudget()[target];
16920
- if (budget.current + content.length > budget.limit) {
17035
+ const budget2 = this.getBudget()[target];
17036
+ if (budget2.current + content.length > budget2.limit) {
16921
17037
  this.consolidationFailures++;
16922
17038
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
16923
17039
  return {
@@ -16964,9 +17080,9 @@ var MemoryManager = class {
16964
17080
  }
16965
17081
  const label = injectionLabelOf(content);
16966
17082
  if (label !== void 0) return this.refuseInjection(label);
16967
- const budget = this.getBudget()[target];
17083
+ const budget2 = this.getBudget()[target];
16968
17084
  const delta = content.length - existing.charLength;
16969
- if (budget.current + delta > budget.limit) {
17085
+ if (budget2.current + delta > budget2.limit) {
16970
17086
  this.consolidationFailures++;
16971
17087
  if (this.consolidationFailures > MAX_CONSOLIDATION_FAILURES_PER_TURN) {
16972
17088
  return { success: false, done: true, error: "Consolidation limit reached" };
@@ -17171,11 +17287,11 @@ var MemoryManager = class {
17171
17287
  for (const target of ["memory", "user"]) {
17172
17288
  const rows = kept.filter((e) => e.target === target);
17173
17289
  if (rows.length === 0) continue;
17174
- const budget = this.getBudget()[target];
17290
+ const budget2 = this.getBudget()[target];
17175
17291
  const label = target === "user" ? "USER PROFILE (who the user is)" : "MEMORY (your personal notes)";
17176
17292
  sections.push(
17177
17293
  [
17178
- `${label} [${rows.length} entries \xB7 ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars]`,
17294
+ `${label} [${rows.length} entries \xB7 ${budget2.current.toLocaleString()}/${budget2.limit.toLocaleString()} chars]`,
17179
17295
  ...rows.map((e) => this.indexLine(e))
17180
17296
  ].join("\n")
17181
17297
  );
@@ -17206,7 +17322,7 @@ var MemoryManager = class {
17206
17322
  return `${e.id} ${e.summary} (${e.charLength} chars)`;
17207
17323
  }
17208
17324
  buildConsolidateHint(target) {
17209
- const budget = this.getBudget()[target];
17325
+ const budget2 = this.getBudget()[target];
17210
17326
  const heat = /* @__PURE__ */ new Map();
17211
17327
  for (const row of this.db.getAll(target)) {
17212
17328
  const id = this.idByContent.get(contentKey(row.target, row.content));
@@ -17214,7 +17330,7 @@ var MemoryManager = class {
17214
17330
  }
17215
17331
  const candidates2 = this.indexEntries(target).sort((a, b) => (heat.get(a.id) ?? 0) - (heat.get(b.id) ?? 0) || b.charLength - a.charLength).slice(0, 5);
17216
17332
  return [
17217
- `Memory at ${budget.current.toLocaleString()}/${budget.limit.toLocaleString()} chars (${budget.percent}%) for ${target}. Cannot add new entry.`,
17333
+ `Memory at ${budget2.current.toLocaleString()}/${budget2.limit.toLocaleString()} chars (${budget2.percent}%) for ${target}. Cannot add new entry.`,
17218
17334
  "",
17219
17335
  "To make room, work by id \u2014 NOT by old_text. The MEMORY INDEX only shows a truncated",
17220
17336
  "first line, so text copied from it will never match an entry exactly.",
@@ -17628,6 +17744,28 @@ function parseTurnFacts(json) {
17628
17744
  function encodeTurnFacts(facts) {
17629
17745
  return facts ? JSON.stringify(facts) : null;
17630
17746
  }
17747
+ function parseUserCommand(json) {
17748
+ if (typeof json !== "string" || json.length === 0) return void 0;
17749
+ let raw;
17750
+ try {
17751
+ raw = JSON.parse(json);
17752
+ } catch {
17753
+ return void 0;
17754
+ }
17755
+ if (!isRecord4(raw)) return void 0;
17756
+ const name = str3(raw["name"]);
17757
+ if (!name) return void 0;
17758
+ const displayName = str3(raw["displayName"]);
17759
+ const args = str3(raw["args"]);
17760
+ return {
17761
+ name,
17762
+ ...displayName ? { displayName } : {},
17763
+ ...args ? { args } : {}
17764
+ };
17765
+ }
17766
+ function encodeUserCommand(command) {
17767
+ return command ? JSON.stringify(command) : null;
17768
+ }
17631
17769
  // src/session/db.ts
17632
17770
  var NS3 = "session";
17633
17771
  var log2 = createLogger("session-db");
@@ -17923,7 +18061,7 @@ var FTS_EXTERNAL_SCHEMA = `
17923
18061
  var FTS_EXTERNAL_MIGRATIONS = [
17924
18062
  {
17925
18063
  id: "fts-external-content",
17926
- version: 14,
18064
+ version: 16,
17927
18065
  description: "\u4E24\u5F20 FTS \u8868\u6539\u6210\u5916\u90E8\u5185\u5BB9\u8868\uFF08\u6B63\u6587\u4E0D\u518D\u5B58\u4E09\u4EFD\uFF0C2026-08-21\uFF09",
17928
18066
  up: (db) => {
17929
18067
  db.exec(FTS_EXTERNAL_SCHEMA);
@@ -17935,6 +18073,14 @@ var FTS_EXTERNAL_MIGRATIONS = [
17935
18073
  db.prepare(`INSERT INTO ${table}(${table}, rank) VALUES('integrity-check', 1)`).run();
17936
18074
  }
17937
18075
  }
18076
+ },
18077
+ {
18078
+ id: "messages-command",
18079
+ version: 15,
18080
+ description: "\u6D88\u606F\u8868\u52A0 command \u4E00\u5217\uFF08\u8FD9\u6761\u7528\u6237\u6D88\u606F\u662F\u6309\u54EA\u6761\u659C\u6760\u547D\u4EE4\u53D1\u7684\uFF0C2026-10-21\uFF09",
18081
+ up: (db) => {
18082
+ db.exec(`ALTER TABLE messages ADD COLUMN command TEXT`);
18083
+ }
17938
18084
  }
17939
18085
  ];
17940
18086
  var SessionDB = class {
@@ -18128,8 +18274,8 @@ var SessionDB = class {
18128
18274
  insertMessage(msg) {
18129
18275
  const info = this.db.prepare(
18130
18276
  `
18131
- 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)
18132
- VALUES (@sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, 1, 0, 0)
18277
+ INSERT INTO messages (session_id, role, content, tool_call_id, tool_calls, tool_results, parts, command, tool_name, timestamp, token_count, finish_reason, reasoning, turn_facts, active, compacted, observed)
18278
+ VALUES (@sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @command, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, 1, 0, 0)
18133
18279
  `
18134
18280
  ).run({
18135
18281
  ...msg,
@@ -18137,6 +18283,7 @@ var SessionDB = class {
18137
18283
  toolCalls: encodeColumn(msg.toolCalls),
18138
18284
  toolResults: encodeColumn(msg.toolResults),
18139
18285
  parts: encodeColumn(msg.parts),
18286
+ command: encodeUserCommand(msg.command),
18140
18287
  toolName: msg.toolName ?? null,
18141
18288
  tokenCount: msg.tokenCount ?? null,
18142
18289
  finishReason: msg.finishReason ?? null,
@@ -18402,8 +18549,8 @@ var SessionDB = class {
18402
18549
  forkMessages(fromSessionId, toSessionId, upToIndex) {
18403
18550
  const messages = this.db.prepare("SELECT * FROM messages WHERE session_id = ? AND id <= ? ORDER BY timestamp ASC").all(fromSessionId, upToIndex);
18404
18551
  const stmt = this.db.prepare(`
18405
- 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)
18406
- VALUES (@sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, @active, @compacted, @observed)
18552
+ INSERT INTO messages (session_id, role, content, tool_call_id, tool_calls, tool_results, parts, command, tool_name, timestamp, token_count, finish_reason, reasoning, turn_facts, active, compacted, observed)
18553
+ VALUES (@sessionId, @role, @content, @toolCallId, @toolCalls, @toolResults, @parts, @command, @toolName, @timestamp, @tokenCount, @finishReason, @reasoning, @turnFacts, @active, @compacted, @observed)
18407
18554
  `);
18408
18555
  for (const m of messages) {
18409
18556
  const { id: _id, ...rest } = m;
@@ -18415,6 +18562,7 @@ var SessionDB = class {
18415
18562
  toolCalls: rest["tool_calls"] ?? null,
18416
18563
  toolResults: rest["tool_results"] ?? null,
18417
18564
  parts: rest["parts"] ?? null,
18565
+ command: rest["command"] ?? null,
18418
18566
  toolName: rest["tool_name"] ?? null,
18419
18567
  timestamp: rest["timestamp"] ?? Date.now(),
18420
18568
  tokenCount: rest["token_count"] ?? null,
@@ -18496,6 +18644,7 @@ var SessionDB = class {
18496
18644
  toolCalls: parseToolCalls(row["tool_calls"]),
18497
18645
  toolResults: parseToolResults(row["tool_results"]),
18498
18646
  parts: parseParts(row["parts"]),
18647
+ command: parseUserCommand(row["command"]),
18499
18648
  turnFacts: parseTurnFacts(row["turn_facts"]),
18500
18649
  toolName: row["tool_name"],
18501
18650
  timestamp: row["timestamp"],
@@ -19087,7 +19236,7 @@ var SessionManager = class {
19087
19236
  search(input) {
19088
19237
  return this.db.search(input);
19089
19238
  }
19090
- async compact(sessionId, summarize3) {
19239
+ async compact(sessionId, summarize2) {
19091
19240
  const messages = this.db.getMessages(sessionId);
19092
19241
  const beforeCount = messages.length;
19093
19242
  const IMPORTANT_ROUNDS = 5;
@@ -19098,17 +19247,17 @@ var SessionManager = class {
19098
19247
  0,
19099
19248
  Math.max(0, keyMessages.length - IMPORTANT_ROUNDS * 2)
19100
19249
  );
19101
- const summary = toSummarize.length === 0 ? "" : await this.buildSummary(toSummarize, summarize3) ?? outlineOf(toSummarize);
19250
+ const summary = toSummarize.length === 0 ? "" : await this.buildSummary(toSummarize, summarize2) ?? outlineOf(toSummarize);
19102
19251
  return this.db.compact(sessionId, summary, beforeCount, IMPORTANT_ROUNDS * 2);
19103
19252
  }
19104
19253
  archiveAndCompact(sessionId, input) {
19105
19254
  return this.db.archiveAndCompact(sessionId, input);
19106
19255
  }
19107
- async buildSummary(msgs, summarize3) {
19108
- if (!summarize3) return null;
19256
+ async buildSummary(msgs, summarize2) {
19257
+ if (!summarize2) return null;
19109
19258
  const transcript = msgs.map((m) => `[${m.role}] ${m.content}`).join("\n\n");
19110
19259
  try {
19111
- const result = await summarize3(transcript);
19260
+ const result = await summarize2(transcript);
19112
19261
  return result?.trim() ? result.trim() : null;
19113
19262
  } catch {
19114
19263
  return null;
@@ -19296,8 +19445,8 @@ var GoalService = class {
19296
19445
  const text = objective.trim();
19297
19446
  if (!text) return { ok: false, reason: "empty-objective" };
19298
19447
  if (text.length > MAX_OBJECTIVE_CHARS) return { ok: false, reason: "objective-too-long" };
19299
- const budget = maxRounds ?? DEFAULT_GOAL_MAX_ROUNDS;
19300
- if (!Number.isInteger(budget) || budget < 1 || budget > MAX_GOAL_MAX_ROUNDS) {
19448
+ const budget2 = maxRounds ?? DEFAULT_GOAL_MAX_ROUNDS;
19449
+ if (!Number.isInteger(budget2) || budget2 < 1 || budget2 > MAX_GOAL_MAX_ROUNDS) {
19301
19450
  return { ok: false, reason: "bad-budget" };
19302
19451
  }
19303
19452
  const existing = this.current(sessionId);
@@ -19309,7 +19458,7 @@ var GoalService = class {
19309
19458
  objective: text,
19310
19459
  phase: "active",
19311
19460
  block: void 0,
19312
- maxRounds: budget,
19461
+ maxRounds: budget2,
19313
19462
  roundsUsed: 0,
19314
19463
  completeEvidence: void 0,
19315
19464
  createdAt: at,
@@ -23437,7 +23586,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23437
23586
  const dir = join(staging, category, name);
23438
23587
  mkdirSync(dir, { recursive: true });
23439
23588
  copyFileSync(join(source, SKILL_FILE), join(dir, SKILL_FILE));
23440
- let budget = MAX_FILES_PER_SKILL - 1;
23589
+ let budget2 = MAX_FILES_PER_SKILL - 1;
23441
23590
  for (const entry of readEntries2(source, label, issues, lang)) {
23442
23591
  if (entry.name === SKILL_FILE) continue;
23443
23592
  if (!entry.isDir || !SKILL_ASSET_DIRS.includes(entry.name)) {
@@ -23447,7 +23596,7 @@ function stageSkillDir(source, staging, category, name, skills, issues, lang) {
23447
23596
  });
23448
23597
  continue;
23449
23598
  }
23450
- budget = copyTree(join(source, entry.name), join(dir, entry.name), label, budget, issues, lang);
23599
+ budget2 = copyTree(join(source, entry.name), join(dir, entry.name), label, budget2, issues, lang);
23451
23600
  }
23452
23601
  skills.push({ name, category, dir });
23453
23602
  }
@@ -23506,8 +23655,8 @@ function placeOf(root, parts, lang) {
23506
23655
  const category = parts.at(-2) ?? "general";
23507
23656
  return { name, category: skillSegmentIssue(category, lang) === void 0 ? category : "general" };
23508
23657
  }
23509
- function copyTree(from, to, label, budget, issues, lang) {
23510
- let left = budget;
23658
+ function copyTree(from, to, label, budget2, issues, lang) {
23659
+ let left = budget2;
23511
23660
  mkdirSync(to, { recursive: true });
23512
23661
  for (const entry of readEntries2(from, label, issues, lang)) {
23513
23662
  if (left <= 0) {
@@ -24571,11 +24720,11 @@ function readOne(path, opts, usedBytes) {
24571
24720
  }
24572
24721
  const remaining = MAX_ATTACH_TOTAL_BYTES - usedBytes;
24573
24722
  if (remaining <= 0) return { part: { type: "file", path, omitted: "over-limit", bytes: size } };
24574
- const budget = Math.min(MAX_ATTACH_BYTES, remaining);
24575
- if (raw.length <= budget) {
24723
+ const budget2 = Math.min(MAX_ATTACH_BYTES, remaining);
24724
+ if (raw.length <= budget2) {
24576
24725
  return { part: { type: "file", path, text: raw.toString("utf8"), bytes: size } };
24577
24726
  }
24578
- const { text, truncatedTo } = truncateToBudget(raw, budget);
24727
+ const { text, truncatedTo } = truncateToBudget(raw, budget2);
24579
24728
  return {
24580
24729
  part: { type: "file", path, text, truncatedTo, bytes: size },
24581
24730
  notice: t("mentions.truncated", {