@tansr/sdk 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +100 -2
  2. package/dist/index.js +1197 -322
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,20 +16,20 @@ var TOOL_EFFECTS = ["irreversible", "financial", "external", "affects-others"];
16
16
  // ../kernel/src/world/default-fs.ts
17
17
  import fs from "node:fs/promises";
18
18
  var defaultFs = {
19
- stat(path28) {
20
- return fs.stat(path28);
19
+ stat(path29) {
20
+ return fs.stat(path29);
21
21
  },
22
- lstat(path28) {
23
- return fs.lstat(path28);
22
+ lstat(path29) {
23
+ return fs.lstat(path29);
24
24
  },
25
- realpath(path28) {
26
- return fs.realpath(path28);
25
+ realpath(path29) {
26
+ return fs.realpath(path29);
27
27
  },
28
- readFile(path28) {
29
- return fs.readFile(path28);
28
+ readFile(path29) {
29
+ return fs.readFile(path29);
30
30
  },
31
- async readFilePrefix(path28, bytes) {
32
- const handle = await fs.open(path28, "r");
31
+ async readFilePrefix(path29, bytes) {
32
+ const handle = await fs.open(path29, "r");
33
33
  try {
34
34
  const prefix = Buffer.alloc(bytes);
35
35
  const { bytesRead } = await handle.read(prefix, 0, bytes, 0);
@@ -38,17 +38,17 @@ var defaultFs = {
38
38
  await handle.close();
39
39
  }
40
40
  },
41
- async writeFile(path28, data) {
42
- await fs.writeFile(path28, data);
41
+ async writeFile(path29, data) {
42
+ await fs.writeFile(path29, data);
43
43
  },
44
- async appendFile(path28, data) {
45
- await fs.appendFile(path28, data);
44
+ async appendFile(path29, data) {
45
+ await fs.appendFile(path29, data);
46
46
  },
47
- async mkdir(path28, opts) {
48
- await fs.mkdir(path28, opts);
47
+ async mkdir(path29, opts) {
48
+ await fs.mkdir(path29, opts);
49
49
  },
50
- readdir(path28) {
51
- return fs.readdir(path28, { withFileTypes: true });
50
+ readdir(path29) {
51
+ return fs.readdir(path29, { withFileTypes: true });
52
52
  }
53
53
  };
54
54
 
@@ -3456,29 +3456,29 @@ function buildSpillStubText(input) {
3456
3456
  ].join("\n");
3457
3457
  }
3458
3458
  function assertSpillStoreInvariants() {
3459
- const fail = (detail) => {
3459
+ const fail2 = (detail) => {
3460
3460
  throw new Error(`spill-store 不变式违约(装载期 fail-fast):${detail}`);
3461
3461
  };
3462
3462
  const nameRe = /^[A-Za-z0-9._-]+$/;
3463
3463
  if (!nameRe.test(SPILL_DIR_NAME)) {
3464
- fail(`SPILL_DIR_NAME 必须为安全文件名词形,得到 ${JSON.stringify(SPILL_DIR_NAME)}`);
3464
+ fail2(`SPILL_DIR_NAME 必须为安全文件名词形,得到 ${JSON.stringify(SPILL_DIR_NAME)}`);
3465
3465
  }
3466
3466
  if (!nameRe.test(SPILL_PRUNE_FILE_PREFIX)) {
3467
- fail(`SPILL_PRUNE_FILE_PREFIX 必须为安全文件名词形,得到 ${JSON.stringify(SPILL_PRUNE_FILE_PREFIX)}`);
3467
+ fail2(`SPILL_PRUNE_FILE_PREFIX 必须为安全文件名词形,得到 ${JSON.stringify(SPILL_PRUNE_FILE_PREFIX)}`);
3468
3468
  }
3469
3469
  if (!Number.isInteger(SPILL_HASH_HEX_CHARS) || SPILL_HASH_HEX_CHARS < 16 || SPILL_HASH_HEX_CHARS > 64) {
3470
- fail(`SPILL_HASH_HEX_CHARS 必须在 [16, 64](sha256 hex 截断域),得到 ${SPILL_HASH_HEX_CHARS}`);
3470
+ fail2(`SPILL_HASH_HEX_CHARS 必须在 [16, 64](sha256 hex 截断域),得到 ${SPILL_HASH_HEX_CHARS}`);
3471
3471
  }
3472
3472
  const emptyHash = hashSpillContent("");
3473
3473
  if (emptyHash !== "e3b0c44298fc1c149afbf4c8996fb924".slice(0, SPILL_HASH_HEX_CHARS)) {
3474
- fail(`hashSpillContent 算法漂移:sha256('') 截断应为公知值,得到 ${emptyHash}`);
3474
+ fail2(`hashSpillContent 算法漂移:sha256('') 截断应为公知值,得到 ${emptyHash}`);
3475
3475
  }
3476
3476
  if (spillFileName("content", emptyHash) === spillFileName("prune", emptyHash)) {
3477
- fail("content 与 prune 命名空间的文件名必须互异");
3477
+ fail2("content 与 prune 命名空间的文件名必须互异");
3478
3478
  }
3479
3479
  const sample = buildSpillStubText({ path: "X:/s/spill/abc.txt", totalChars: 12345 });
3480
3480
  if (!sample.includes("12345") || !sample.includes("X:/s/spill/abc.txt") || !sample.includes("Read")) {
3481
- fail("buildSpillStubText 必须恒含原始规模、真实路径与 Read 指引");
3481
+ fail2("buildSpillStubText 必须恒含原始规模、真实路径与 Read 指引");
3482
3482
  }
3483
3483
  }
3484
3484
  assertSpillStoreInvariants();
@@ -4342,63 +4342,63 @@ var CONTEXT_BUDGET_REMINDER_CRITICAL_FRACTION = 0.1;
4342
4342
  var CONTEXT_BUDGET_REMINDER_ROUND_TOKENS = 1e3;
4343
4343
  var CONTEXT_BUDGET_REMINDER_MIN_BUDGET_TOKENS = 2e4;
4344
4344
  function assertEntryBudgetInvariants() {
4345
- const fail = (detail) => {
4345
+ const fail2 = (detail) => {
4346
4346
  throw new Error(`entry-budgets 不变式违约(装载期 fail-fast):${detail}`);
4347
4347
  };
4348
4348
  if (DEFAULT_TOOL_RESULT_BUDGET_CHARS !== 5e4) {
4349
- fail(
4349
+ fail2(
4350
4350
  `DEFAULT_TOOL_RESULT_BUDGET_CHARS 必须为 50000(用户拍板 2026-08-21 维持不动),得到 ${DEFAULT_TOOL_RESULT_BUDGET_CHARS}`
4351
4351
  );
4352
4352
  }
4353
4353
  if (PERSISTED_PREVIEW_HEAD_CHARS + PERSISTED_PREVIEW_TAIL_CHARS >= DEFAULT_TOOL_RESULT_BUDGET_CHARS) {
4354
- fail("clamp 存根预览(head+tail)必须严格小于工具结果预算");
4354
+ fail2("clamp 存根预览(head+tail)必须严格小于工具结果预算");
4355
4355
  }
4356
4356
  for (const [name, value] of [
4357
4357
  ["READ_MAX_LINES", READ_MAX_LINES],
4358
4358
  ["READ_MAX_LINE_CHARS", READ_MAX_LINE_CHARS],
4359
4359
  ["READ_MAX_FULL_BYTES", READ_MAX_FULL_BYTES]
4360
4360
  ]) {
4361
- if (!Number.isInteger(value) || value <= 0) fail(`${name} 必须为正整数,得到 ${value}`);
4361
+ if (!Number.isInteger(value) || value <= 0) fail2(`${name} 必须为正整数,得到 ${value}`);
4362
4362
  }
4363
4363
  if (!Number.isInteger(PRUNE_MIN_RESULT_CHARS) || !Number.isInteger(PRUNE_HEAD_CHARS) || !Number.isInteger(PRUNE_TAIL_CHARS) || PRUNE_HEAD_CHARS < 0 || PRUNE_TAIL_CHARS < 0 || PRUNE_MIN_RESULT_CHARS <= 0) {
4364
- fail("剪枝常量必须为非负整数且门槛为正");
4364
+ fail2("剪枝常量必须为非负整数且门槛为正");
4365
4365
  }
4366
4366
  if (PRUNE_HEAD_CHARS + PRUNE_TAIL_CHARS + PRUNE_MARKER_MAX_CHARS >= PRUNE_MIN_RESULT_CHARS) {
4367
- fail(
4367
+ fail2(
4368
4368
  `剪后产物上界(head ${PRUNE_HEAD_CHARS} + tail ${PRUNE_TAIL_CHARS} + 标记 ≤${PRUNE_MARKER_MAX_CHARS})必须严格小于剪枝门槛 ${PRUNE_MIN_RESULT_CHARS}——否则剪后仍是候选,重复触发字节漂移`
4369
4369
  );
4370
4370
  }
4371
4371
  if (PRUNE_MIN_RESULT_CHARS >= DEFAULT_TOOL_RESULT_BUDGET_CHARS) {
4372
- fail("剪枝门槛必须低于工具结果 clamp 预算(50K 之上无剪枝对象)");
4372
+ fail2("剪枝门槛必须低于工具结果 clamp 预算(50K 之上无剪枝对象)");
4373
4373
  }
4374
4374
  if (!Number.isInteger(SUBAGENT_SUMMARY_MAX_TOKENS) || SUBAGENT_SUMMARY_MAX_TOKENS <= 0) {
4375
- fail(`SUBAGENT_SUMMARY_MAX_TOKENS 必须为正整数,得到 ${SUBAGENT_SUMMARY_MAX_TOKENS}`);
4375
+ fail2(`SUBAGENT_SUMMARY_MAX_TOKENS 必须为正整数,得到 ${SUBAGENT_SUMMARY_MAX_TOKENS}`);
4376
4376
  }
4377
4377
  if (!Number.isInteger(DEFAULT_OUTPUT_CAP_TOKENS) || DEFAULT_OUTPUT_CAP_TOKENS < 1024) {
4378
- fail(`DEFAULT_OUTPUT_CAP_TOKENS 必须为 ≥1024 的整数(输出预算地板),得到 ${DEFAULT_OUTPUT_CAP_TOKENS}`);
4378
+ fail2(`DEFAULT_OUTPUT_CAP_TOKENS 必须为 ≥1024 的整数(输出预算地板),得到 ${DEFAULT_OUTPUT_CAP_TOKENS}`);
4379
4379
  }
4380
4380
  if (!Number.isInteger(OUTPUT_CAP_ESCALATION_TOKENS) || OUTPUT_CAP_ESCALATION_TOKENS <= DEFAULT_OUTPUT_CAP_TOKENS) {
4381
- fail(
4381
+ fail2(
4382
4382
  `OUTPUT_CAP_ESCALATION_TOKENS(${OUTPUT_CAP_ESCALATION_TOKENS})必须为严格大于缺省输出帽 ${DEFAULT_OUTPUT_CAP_TOKENS} 的整数——否则触帽升档恒无增益`
4383
4383
  );
4384
4384
  }
4385
4385
  if (!(CONTEXT_BUDGET_REMINDER_CRITICAL_FRACTION > 0) || !(CONTEXT_BUDGET_REMINDER_CRITICAL_FRACTION < CONTEXT_BUDGET_REMINDER_LOW_FRACTION) || !(CONTEXT_BUDGET_REMINDER_LOW_FRACTION < 1)) {
4386
- fail(
4386
+ fail2(
4387
4387
  `预算提醒档位必须满足 0 < critical(${CONTEXT_BUDGET_REMINDER_CRITICAL_FRACTION}) < low(${CONTEXT_BUDGET_REMINDER_LOW_FRACTION}) < 1`
4388
4388
  );
4389
4389
  }
4390
4390
  if (!Number.isInteger(CONTEXT_BUDGET_REMINDER_ROUND_TOKENS) || CONTEXT_BUDGET_REMINDER_ROUND_TOKENS <= 0) {
4391
- fail(
4391
+ fail2(
4392
4392
  `CONTEXT_BUDGET_REMINDER_ROUND_TOKENS 必须为正整数,得到 ${CONTEXT_BUDGET_REMINDER_ROUND_TOKENS}`
4393
4393
  );
4394
4394
  }
4395
4395
  if (!Number.isInteger(CONTEXT_BUDGET_REMINDER_MIN_BUDGET_TOKENS) || CONTEXT_BUDGET_REMINDER_MIN_BUDGET_TOKENS < CONTEXT_BUDGET_REMINDER_ROUND_TOKENS) {
4396
- fail(
4396
+ fail2(
4397
4397
  `CONTEXT_BUDGET_REMINDER_MIN_BUDGET_TOKENS(${CONTEXT_BUDGET_REMINDER_MIN_BUDGET_TOKENS})必须为 ≥ 取整粒度 ${CONTEXT_BUDGET_REMINDER_ROUND_TOKENS} 的整数`
4398
4398
  );
4399
4399
  }
4400
4400
  if (EMPTY_TOOL_RESULT_TEXT.length === 0 || EMPTY_TOOL_ERROR_TEXT.length === 0) {
4401
- fail("空结果短桩短语不得为空串");
4401
+ fail2("空结果短桩短语不得为空串");
4402
4402
  }
4403
4403
  }
4404
4404
  assertEntryBudgetInvariants();
@@ -4602,74 +4602,74 @@ var LIFECYCLE_REPLAY_ORDER = [
4602
4602
  "evict"
4603
4603
  ];
4604
4604
  function assertHistoryLifecycleInvariants() {
4605
- const fail = (detail) => {
4605
+ const fail2 = (detail) => {
4606
4606
  throw new Error(`历史生命周期状态机不变式违约(装载期 fail-fast):${detail}`);
4607
4607
  };
4608
4608
  if (new Set(HISTORY_LIFECYCLE_STATES).size !== HISTORY_LIFECYCLE_STATES.length) {
4609
- fail("态集成员必须互异");
4609
+ fail2("态集成员必须互异");
4610
4610
  }
4611
4611
  if (!PRUNE_MARKER_PREFIX.startsWith("[") || PRUNE_MARKER_PREFIX.length < 8) {
4612
- fail(`PRUNE_MARKER_PREFIX 词形异常(冻结字节被改动):${JSON.stringify(PRUNE_MARKER_PREFIX)}`);
4612
+ fail2(`PRUNE_MARKER_PREFIX 词形异常(冻结字节被改动):${JSON.stringify(PRUNE_MARKER_PREFIX)}`);
4613
4613
  }
4614
4614
  const ids = /* @__PURE__ */ new Set();
4615
4615
  let lastStage = -1;
4616
4616
  const stateIndex = (s) => HISTORY_LIFECYCLE_STATES.indexOf(s);
4617
4617
  for (const transition of LIFECYCLE_TRANSITIONS) {
4618
- if (ids.has(transition.id)) fail(`迁移 id 重复:${transition.id}`);
4618
+ if (ids.has(transition.id)) fail2(`迁移 id 重复:${transition.id}`);
4619
4619
  ids.add(transition.id);
4620
4620
  if (transition.stage <= lastStage) {
4621
- fail(`迁移 stage 必须严格递增(全序仲裁的根):${transition.id} stage=${transition.stage}`);
4621
+ fail2(`迁移 stage 必须严格递增(全序仲裁的根):${transition.id} stage=${transition.stage}`);
4622
4622
  }
4623
4623
  lastStage = transition.stage;
4624
4624
  const toIndex = stateIndex(transition.to);
4625
- if (toIndex < 0) fail(`迁移 ${transition.id} 目标态不在态集:${transition.to}`);
4625
+ if (toIndex < 0) fail2(`迁移 ${transition.id} 目标态不在态集:${transition.to}`);
4626
4626
  if (transition.from !== "ingestion") {
4627
4627
  for (const from of transition.from) {
4628
- if (stateIndex(from) < 0) fail(`迁移 ${transition.id} 源态不在态集:${from}`);
4628
+ if (stateIndex(from) < 0) fail2(`迁移 ${transition.id} 源态不在态集:${from}`);
4629
4629
  if (stateIndex(from) >= toIndex) {
4630
- fail(`迁移 ${transition.id} 违反单向前进(${from} → ${transition.to}):恒无复活迁移`);
4630
+ fail2(`迁移 ${transition.id} 违反单向前进(${from} → ${transition.to}):恒无复活迁移`);
4631
4631
  }
4632
4632
  }
4633
4633
  }
4634
4634
  }
4635
4635
  for (const id of [...LIFECYCLE_BARRIER_ORDER, ...LIFECYCLE_REPLAY_ORDER]) {
4636
- if (!ids.has(id)) fail(`仲裁/重演序引用未登记迁移:${id}`);
4636
+ if (!ids.has(id)) fail2(`仲裁/重演序引用未登记迁移:${id}`);
4637
4637
  }
4638
4638
  if (!Number.isInteger(DEFAULT_KEEP_RECENT_ROUNDS) || DEFAULT_KEEP_RECENT_ROUNDS < 1) {
4639
- fail(`DEFAULT_KEEP_RECENT_ROUNDS 必须为 ≥1 整数,得到 ${DEFAULT_KEEP_RECENT_ROUNDS}`);
4639
+ fail2(`DEFAULT_KEEP_RECENT_ROUNDS 必须为 ≥1 整数,得到 ${DEFAULT_KEEP_RECENT_ROUNDS}`);
4640
4640
  }
4641
4641
  if (!Number.isInteger(DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS) || DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS < 1) {
4642
- fail(
4642
+ fail2(
4643
4643
  `DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS 必须为 ≥1 整数,得到 ${DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS}`
4644
4644
  );
4645
4645
  }
4646
4646
  if (!Number.isInteger(DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS) || DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS <= 0) {
4647
- fail(
4647
+ fail2(
4648
4648
  `DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS 必须为正整数,得到 ${DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS}`
4649
4649
  );
4650
4650
  }
4651
4651
  if (PRUNE_MIN_RESULT_CHARS <= DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS) {
4652
- fail(
4652
+ fail2(
4653
4653
  `剪枝门槛(${PRUNE_MIN_RESULT_CHARS})必须严格大于淘汰门槛(${DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS}):prune 是 evict 的前置温和档`
4654
4654
  );
4655
4655
  }
4656
4656
  if (PRUNE_HEAD_CHARS + PRUNE_TAIL_CHARS < DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS) {
4657
- fail(
4657
+ fail2(
4658
4658
  `剪后产物下界(head ${PRUNE_HEAD_CHARS} + tail ${PRUNE_TAIL_CHARS})必须 ≥ 淘汰门槛 ${DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS}——否则 pruned 态脱离 evict 候选域,两档接力断链`
4659
4659
  );
4660
4660
  }
4661
4661
  if (DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS >= DEFAULT_TOOL_RESULT_BUDGET_CHARS) {
4662
- fail(
4662
+ fail2(
4663
4663
  `淘汰门槛(${DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS})必须小于入史 clamp 预算(${DEFAULT_TOOL_RESULT_BUDGET_CHARS}):淘汰重演的文件在场性判据依赖两带不重叠`
4664
4664
  );
4665
4665
  }
4666
4666
  if (DEGRADED_ESCALATED_KEEP_RECENT_TOOL_RESULTS > DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS) {
4667
- fail(
4667
+ fail2(
4668
4668
  `劣化升级保护窗(${DEGRADED_ESCALATED_KEEP_RECENT_TOOL_RESULTS})不得大于默认窗(${DEFAULT_MICROCOMPACT_KEEP_RECENT_TOOL_RESULTS}):升级恒收窄`
4669
4669
  );
4670
4670
  }
4671
4671
  if (DEGRADED_ESCALATED_MIN_RESULT_CHARS > DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS) {
4672
- fail(
4672
+ fail2(
4673
4673
  `劣化升级淘汰门槛(${DEGRADED_ESCALATED_MIN_RESULT_CHARS})不得大于默认门槛(${DEFAULT_MICROCOMPACT_MIN_RESULT_CHARS}):升级恒收窄`
4674
4674
  );
4675
4675
  }
@@ -11845,10 +11845,10 @@ function matchHardSafety(input, config = {}) {
11845
11845
  if (typeof command !== "string") return null;
11846
11846
  const structure = parseCommandStructure(command);
11847
11847
  if (!structure.ok) return null;
11848
- for (const pipeline2 of structure.pipelines) {
11849
- const hasEgress = pipeline2.commands.some((c) => isEgressCommand(c, extraEgress));
11848
+ for (const pipeline3 of structure.pipelines) {
11849
+ const hasEgress = pipeline3.commands.some((c) => isEgressCommand(c, extraEgress));
11850
11850
  if (!hasEgress) continue;
11851
- const refsSecret = pipeline2.commands.some((c) => nodeReferencesSecret(c, input.cwd));
11851
+ const refsSecret = pipeline3.commands.some((c) => nodeReferencesSecret(c, input.cwd));
11852
11852
  if (refsSecret) {
11853
11853
  return {
11854
11854
  label: "secret-egress",
@@ -11942,13 +11942,13 @@ function canonicalCwd(cwd) {
11942
11942
  const resolved = resolveForPermission(cwd, ".");
11943
11943
  return resolved.kind === "ok" ? resolved.path : null;
11944
11944
  }
11945
- function canonicalPathEligible(cwdCanon, path28, policy, guard) {
11946
- if (hasWindowsDeviceSegment(path28)) return false;
11947
- if (path28 === cwdCanon) return !policy.strict;
11948
- if (!path28.startsWith(`${cwdCanon}/`)) return false;
11949
- if (guard.isProtectedContentPath(path28)) return false;
11950
- if (matchProtectedPath(path28, null) !== null) return false;
11951
- const segments = path28.slice(cwdCanon.length + 1).split("/");
11945
+ function canonicalPathEligible(cwdCanon, path29, policy, guard) {
11946
+ if (hasWindowsDeviceSegment(path29)) return false;
11947
+ if (path29 === cwdCanon) return !policy.strict;
11948
+ if (!path29.startsWith(`${cwdCanon}/`)) return false;
11949
+ if (guard.isProtectedContentPath(path29)) return false;
11950
+ if (matchProtectedPath(path29, null) !== null) return false;
11951
+ const segments = path29.slice(cwdCanon.length + 1).split("/");
11952
11952
  for (const [index, seg] of segments.entries()) {
11953
11953
  const hasGlob = GLOB_CHAR_RE.test(seg);
11954
11954
  if (hasGlob && seg.startsWith(".")) return false;
@@ -12132,9 +12132,9 @@ function verifyBundle(bundle, opts = {}) {
12132
12132
  // ../kernel/src/permissions/soften-radius.ts
12133
12133
  var PROTECTED_PATH_RULE_PREFIX = "protected-path(";
12134
12134
  var MCP_TOOL_PREFIX = "mcp__";
12135
- function pathInsideRoot(path28, root) {
12135
+ function pathInsideRoot(path29, root) {
12136
12136
  if (root === null) return false;
12137
- return path28 === root || path28.startsWith(root.endsWith("/") ? root : `${root}/`);
12137
+ return path29 === root || path29.startsWith(root.endsWith("/") ? root : `${root}/`);
12138
12138
  }
12139
12139
  function classifySoftenFace(input, base) {
12140
12140
  if (base.matchedRule?.startsWith(PROTECTED_PATH_RULE_PREFIX) === true) return "F6";
@@ -14096,14 +14096,14 @@ var DispatchingToolExecutor = class _DispatchingToolExecutor {
14096
14096
  let internalError;
14097
14097
  const onCtxAbort = () => wake.wake();
14098
14098
  ctx.signal.addEventListener("abort", onCtxAbort, { once: true });
14099
- const drain = function* () {
14099
+ const drain2 = function* () {
14100
14100
  while (queued.length > 0) {
14101
14101
  yield { t: "event", body: queued.shift() };
14102
14102
  }
14103
14103
  };
14104
14104
  const waitUntil = async function* (cond) {
14105
14105
  for (; ; ) {
14106
- yield* drain();
14106
+ yield* drain2();
14107
14107
  if (cond()) return;
14108
14108
  await wake.wait();
14109
14109
  }
@@ -14131,7 +14131,7 @@ var DispatchingToolExecutor = class _DispatchingToolExecutor {
14131
14131
  };
14132
14132
  for (let i = 0; i < calls.length; i++) {
14133
14133
  let call = calls[i];
14134
- yield* drain();
14134
+ yield* drain2();
14135
14135
  if (ctx.signal.aborted) {
14136
14136
  sawAbort = true;
14137
14137
  fillSyntheticFrom(i);
@@ -14558,7 +14558,7 @@ var DispatchingToolExecutor = class _DispatchingToolExecutor {
14558
14558
  }
14559
14559
  try {
14560
14560
  yield* waitUntil(() => inFlight === 0);
14561
- yield* drain();
14561
+ yield* drain2();
14562
14562
  } finally {
14563
14563
  ctx.signal.removeEventListener("abort", onCtxAbort);
14564
14564
  }
@@ -14838,10 +14838,10 @@ async function renameWithRetry(from, to, deps = defaultRenameDeps) {
14838
14838
  await deps.rename(from, to);
14839
14839
  return;
14840
14840
  } catch (err) {
14841
- const delay = RENAME_RETRY_DELAYS_MS[attempt];
14841
+ const delay2 = RENAME_RETRY_DELAYS_MS[attempt];
14842
14842
  const transient = deps.platform === "win32" && isErrnoException(err) && err.code !== void 0 && RENAME_TRANSIENT_CODES.has(err.code);
14843
- if (!transient || delay === void 0) throw err;
14844
- await deps.sleep(delay);
14843
+ if (!transient || delay2 === void 0) throw err;
14844
+ await deps.sleep(delay2);
14845
14845
  }
14846
14846
  }
14847
14847
  }
@@ -15110,13 +15110,13 @@ async function externalizeOne(block, sessionDir) {
15110
15110
  const bytes = Buffer.from(block["data"], "base64");
15111
15111
  const hex = createHash6("sha256").update(bytes).digest("hex");
15112
15112
  const file = attachmentFilePath(sessionDir, hex);
15113
- let exists = true;
15113
+ let exists2 = true;
15114
15114
  try {
15115
15115
  await access(file);
15116
15116
  } catch {
15117
- exists = false;
15117
+ exists2 = false;
15118
15118
  }
15119
- if (!exists) {
15119
+ if (!exists2) {
15120
15120
  await mkdir2(attachmentsDirPath(sessionDir), { recursive: true });
15121
15121
  await writeFileDurable(file, bytes);
15122
15122
  }
@@ -15416,13 +15416,13 @@ async function effectiveSegments(sessionDir, manifest) {
15416
15416
  out.push(segment);
15417
15417
  continue;
15418
15418
  }
15419
- let exists = true;
15419
+ let exists2 = true;
15420
15420
  try {
15421
15421
  await stat2(segmentFilePath(sessionDir, segment.n));
15422
15422
  } catch {
15423
- exists = false;
15423
+ exists2 = false;
15424
15424
  }
15425
- if (isSegmentEffective(segment, exists)) out.push(segment);
15425
+ if (isSegmentEffective(segment, exists2)) out.push(segment);
15426
15426
  }
15427
15427
  return out;
15428
15428
  }
@@ -15699,9 +15699,9 @@ async function settleManifest(sessionDir) {
15699
15699
  segments.push(segment);
15700
15700
  continue;
15701
15701
  }
15702
- const exists = await fileExists(segmentFilePath(sessionDir, segment.n));
15702
+ const exists2 = await fileExists(segmentFilePath(sessionDir, segment.n));
15703
15703
  changed = true;
15704
- if (isSegmentEffective(segment, exists)) segments.push({ ...segment, status: "sealed" });
15704
+ if (isSegmentEffective(segment, exists2)) segments.push({ ...segment, status: "sealed" });
15705
15705
  }
15706
15706
  const last = segments[segments.length - 1];
15707
15707
  const liveFirstSeq = last === void 0 ? 0 : last.lastSeq + 1;
@@ -16343,8 +16343,8 @@ var VERSIONED_MANIFEST = /\/manifest\.g\d{9}-[0-9a-f]+\.json$/;
16343
16343
  function isManifestObjectKey(key2) {
16344
16344
  return key2.endsWith(`/${MANIFEST_OBJECT_NAME}`) || VERSIONED_MANIFEST.test(key2);
16345
16345
  }
16346
- function segmentKey(ref, segment, codec, sha256) {
16347
- return `${volumePrefix(ref)}seg-${String(segment.n).padStart(6, "0")}-${segment.firstSeq}-${segment.lastSeq}-${sha256.slice(0, 12)}.${codecFileExtension(codec)}`;
16346
+ function segmentKey(ref, segment, codec, sha2562) {
16347
+ return `${volumePrefix(ref)}seg-${String(segment.n).padStart(6, "0")}-${segment.firstSeq}-${segment.lastSeq}-${sha2562.slice(0, 12)}.${codecFileExtension(codec)}`;
16348
16348
  }
16349
16349
  function deriveVolumeId(segments) {
16350
16350
  const first = segments.find((s) => s.n === 1);
@@ -16827,9 +16827,881 @@ function createColdManifestStore(options) {
16827
16827
  };
16828
16828
  }
16829
16829
 
16830
+ // ../kernel/src/journal/segmented/fs-blob-store.ts
16831
+ import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
16832
+ import { createReadStream as createReadStream2, createWriteStream as createWriteStream2 } from "node:fs";
16833
+ import { link, mkdir as mkdir4, readdir, readFile as readFile6, rm as rm5, stat as stat3 } from "node:fs/promises";
16834
+ import path8 from "node:path";
16835
+ import { Transform as Transform2 } from "node:stream";
16836
+ import { pipeline as pipeline2 } from "node:stream/promises";
16837
+ var KEY_PART2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
16838
+ function fsKeyParts(key2) {
16839
+ if (key2.length === 0 || key2.includes("\\") || key2.startsWith("/") || key2.endsWith("/")) {
16840
+ throw new StoreError("permanent", `非法对象键 ${JSON.stringify(key2)}:禁空键 / 反斜杠 / 首尾分隔符`);
16841
+ }
16842
+ const parts = key2.split("/");
16843
+ for (const part of parts) {
16844
+ if (!KEY_PART2.test(part) || part === "." || part === "..") {
16845
+ throw new StoreError("permanent", `非法对象键段 ${JSON.stringify(part)}(键 ${JSON.stringify(key2)}):仅允许字母数字与 . _ -,且须以字母数字开头`);
16846
+ }
16847
+ }
16848
+ return parts;
16849
+ }
16850
+ async function exists(p) {
16851
+ try {
16852
+ await stat3(p);
16853
+ return true;
16854
+ } catch {
16855
+ return false;
16856
+ }
16857
+ }
16858
+ var KeyLocks = class {
16859
+ #tails = /* @__PURE__ */ new Map();
16860
+ async run(key2, op) {
16861
+ const prev = this.#tails.get(key2) ?? Promise.resolve();
16862
+ let release;
16863
+ const mine = new Promise((resolve2) => {
16864
+ release = resolve2;
16865
+ });
16866
+ const tail = prev.then(() => mine);
16867
+ this.#tails.set(key2, tail);
16868
+ await prev;
16869
+ try {
16870
+ return await op();
16871
+ } finally {
16872
+ release();
16873
+ if (this.#tails.get(key2) === tail) this.#tails.delete(key2);
16874
+ }
16875
+ }
16876
+ };
16877
+ function createFsBlobStore(options) {
16878
+ const root = path8.resolve(options.dir);
16879
+ const objectsDir = path8.join(root, "objects");
16880
+ const metaDir = path8.join(root, "meta");
16881
+ const tmpDir = path8.join(root, "tmp");
16882
+ const capabilities = {
16883
+ consistency: "strong",
16884
+ conditionalPut: true,
16885
+ rangeGet: true,
16886
+ maxObjectBytes: Number.MAX_SAFE_INTEGER,
16887
+ list: "prefix",
16888
+ batchDelete: 1e3,
16889
+ ...options.capabilities
16890
+ };
16891
+ const locks = new KeyLocks();
16892
+ let prepared = null;
16893
+ const prepare = () => {
16894
+ prepared ??= (async () => {
16895
+ await mkdir4(objectsDir, { recursive: true });
16896
+ await mkdir4(metaDir, { recursive: true });
16897
+ await rm5(tmpDir, { recursive: true, force: true }).catch(() => void 0);
16898
+ await mkdir4(tmpDir, { recursive: true });
16899
+ })();
16900
+ return prepared;
16901
+ };
16902
+ const objectPath = (key2) => path8.join(objectsDir, ...fsKeyParts(key2));
16903
+ const metaPath = (key2) => `${path8.join(metaDir, ...fsKeyParts(key2))}.json`;
16904
+ async function readMeta(key2) {
16905
+ let raw;
16906
+ try {
16907
+ raw = await readFile6(metaPath(key2), "utf8");
16908
+ } catch (err) {
16909
+ if (isErrnoException(err) && err.code === "ENOENT") return null;
16910
+ throw toStoreError(err);
16911
+ }
16912
+ try {
16913
+ return JSON.parse(raw);
16914
+ } catch {
16915
+ throw new StoreError("permanent", `对象 meta 损坏:${key2}`);
16916
+ }
16917
+ }
16918
+ async function spool(body, signal) {
16919
+ const tmp = path8.join(tmpDir, `${process.pid.toString(36)}-${randomBytes2(8).toString("hex")}`);
16920
+ const hash = createHash9("sha256");
16921
+ let bytes = 0;
16922
+ const counter = new Transform2({
16923
+ transform(chunk, _enc, callback) {
16924
+ hash.update(chunk);
16925
+ bytes += chunk.length;
16926
+ callback(null, chunk);
16927
+ }
16928
+ });
16929
+ try {
16930
+ await pipeline2(webToNode(body), counter, createWriteStream2(tmp), signal === void 0 ? {} : { signal });
16931
+ } catch (err) {
16932
+ await rm5(tmp, { force: true }).catch(() => void 0);
16933
+ if (signal?.aborted === true || err instanceof Error && err.name === "AbortError") {
16934
+ throw new StoreError("transient", "aborted", { cause: err });
16935
+ }
16936
+ throw toStoreError(err);
16937
+ }
16938
+ return { tmp, sha256: hash.digest("hex"), bytes };
16939
+ }
16940
+ async function writeMeta(key2, meta) {
16941
+ const p = metaPath(key2);
16942
+ await mkdir4(path8.dirname(p), { recursive: true });
16943
+ await writeFileDurable(p, `${JSON.stringify(meta)}
16944
+ `);
16945
+ }
16946
+ async function placeExclusive(tmp, target) {
16947
+ try {
16948
+ await link(tmp, target);
16949
+ await rm5(tmp, { force: true }).catch(() => void 0);
16950
+ return "created";
16951
+ } catch (err) {
16952
+ if (isErrnoException(err)) {
16953
+ if (err.code === "EEXIST") return "exists";
16954
+ if (err.code === "EPERM" || err.code === "ENOSYS" || err.code === "ENOTSUP" || err.code === "EXDEV") {
16955
+ if (await exists(target)) return "exists";
16956
+ await renameWithRetry(tmp, target);
16957
+ return "created";
16958
+ }
16959
+ }
16960
+ throw err;
16961
+ }
16962
+ }
16963
+ const store = {
16964
+ capabilities,
16965
+ async put(key2, body, meta, opts = {}) {
16966
+ const target = objectPath(key2);
16967
+ await prepare();
16968
+ if (opts.signal?.aborted === true) {
16969
+ await body.cancel().catch(() => void 0);
16970
+ throw new StoreError("transient", "aborted", { cause: opts.signal.reason });
16971
+ }
16972
+ const spooled = await spool(body, opts.signal);
16973
+ try {
16974
+ if (spooled.sha256 !== meta.sha256) {
16975
+ throw new StoreError("permanent", `sha256 不符:体 ${spooled.sha256} ≠ meta ${meta.sha256}(${key2})`);
16976
+ }
16977
+ const etag = spooled.sha256;
16978
+ const stored = { ...meta, bytes: spooled.bytes, etag };
16979
+ return await locks.run(key2, async () => {
16980
+ await mkdir4(path8.dirname(target), { recursive: true });
16981
+ if (opts.ifNoneMatch === "*") {
16982
+ const outcome = await placeExclusive(spooled.tmp, target);
16983
+ if (outcome === "exists") {
16984
+ const current = await readMeta(key2);
16985
+ if (current === null || current.etag !== etag) {
16986
+ throw new StoreError("precondition_failed", `ifNoneMatch:* 但对象已存在:${key2}`);
16987
+ }
16988
+ return { etag };
16989
+ }
16990
+ await writeMeta(key2, stored);
16991
+ return { etag };
16992
+ }
16993
+ if (opts.ifMatch !== void 0) {
16994
+ const current = await readMeta(key2);
16995
+ if (current === null || current.etag !== opts.ifMatch) {
16996
+ throw new StoreError("precondition_failed", `ifMatch ${opts.ifMatch} 但现行 etag ${current?.etag ?? "(absent)"}:${key2}`);
16997
+ }
16998
+ }
16999
+ await renameWithRetry(spooled.tmp, target);
17000
+ await writeMeta(key2, stored);
17001
+ return { etag };
17002
+ });
17003
+ } catch (err) {
17004
+ throw toStoreError(err);
17005
+ } finally {
17006
+ await rm5(spooled.tmp, { force: true }).catch(() => void 0);
17007
+ }
17008
+ },
17009
+ async get(key2, range, signal) {
17010
+ const target = objectPath(key2);
17011
+ await prepare();
17012
+ if (signal?.aborted === true) throw new StoreError("transient", "aborted", { cause: signal.reason });
17013
+ const meta = await readMeta(key2);
17014
+ if (meta === null || !await exists(target)) throw new StoreError("not_found", `对象不存在:${key2}`);
17015
+ const streamOptions = { highWaterMark: 256 * 1024 };
17016
+ if (range !== void 0) {
17017
+ if (range.start >= meta.bytes) return new ReadableStream({ start: (c) => c.close() });
17018
+ streamOptions.start = range.start;
17019
+ if (range.end !== void 0) streamOptions.end = Math.min(range.end, meta.bytes - 1);
17020
+ }
17021
+ const source = createReadStream2(target, streamOptions);
17022
+ if (signal !== void 0) {
17023
+ const abort = () => {
17024
+ source.destroy(new StoreError("transient", "aborted", { cause: signal.reason }));
17025
+ };
17026
+ signal.addEventListener("abort", abort, { once: true });
17027
+ source.once("close", () => signal.removeEventListener("abort", abort));
17028
+ }
17029
+ return nodeToWeb(source);
17030
+ },
17031
+ async head(key2) {
17032
+ await prepare();
17033
+ const meta = await readMeta(key2);
17034
+ if (meta === null || !await exists(objectPath(key2))) return null;
17035
+ return { ...meta };
17036
+ },
17037
+ async *list(prefix, cursor, signal) {
17038
+ await prepare();
17039
+ const slash = prefix.lastIndexOf("/");
17040
+ const dirPart = slash === -1 ? "" : prefix.slice(0, slash);
17041
+ const dirParts = dirPart === "" ? [] : fsKeyParts(dirPart);
17042
+ const startDir = path8.join(objectsDir, ...dirParts);
17043
+ if (!await exists(startDir)) return;
17044
+ const keys = [];
17045
+ const walk = async (dir, keyPrefix) => {
17046
+ let entries;
17047
+ try {
17048
+ entries = await readdir(dir, { withFileTypes: true });
17049
+ } catch (err) {
17050
+ if (isErrnoException(err) && err.code === "ENOENT") return;
17051
+ throw toStoreError(err);
17052
+ }
17053
+ for (const entry of entries) {
17054
+ const key2 = keyPrefix === "" ? entry.name : `${keyPrefix}/${entry.name}`;
17055
+ if (entry.isDirectory()) await walk(path8.join(dir, entry.name), key2);
17056
+ else if (entry.isFile() && key2.startsWith(prefix)) keys.push(key2);
17057
+ }
17058
+ };
17059
+ await walk(startDir, dirPart);
17060
+ keys.sort();
17061
+ for (const key2 of keys) {
17062
+ if (signal?.aborted === true) throw new StoreError("transient", "aborted", { cause: signal.reason });
17063
+ if (cursor !== void 0 && key2 <= cursor) continue;
17064
+ const meta = await readMeta(key2);
17065
+ if (meta === null) continue;
17066
+ yield { key: key2, meta: { ...meta } };
17067
+ }
17068
+ },
17069
+ async delete(keys) {
17070
+ await prepare();
17071
+ for (const key2 of keys) {
17072
+ await rm5(objectPath(key2), { force: true }).catch((err) => {
17073
+ throw toStoreError(err);
17074
+ });
17075
+ await rm5(metaPath(key2), { force: true }).catch((err) => {
17076
+ throw toStoreError(err);
17077
+ });
17078
+ }
17079
+ }
17080
+ };
17081
+ return store;
17082
+ }
17083
+
16830
17084
  // ../kernel/src/journal/segmented/conformance.ts
17085
+ import { createHash as createHash10 } from "node:crypto";
16831
17086
  var CHUNK = 256 * 1024;
16832
17087
  var MIB = 1024 * 1024;
17088
+ function bytesOf(size, seed) {
17089
+ const out = new Uint8Array(size);
17090
+ let x = seed * 2654435761 + 1 >>> 0;
17091
+ for (let i = 0; i < size; i++) {
17092
+ x = x * 1664525 + 1013904223 >>> 0;
17093
+ out[i] = x >>> 24;
17094
+ }
17095
+ return out;
17096
+ }
17097
+ function sha256(bytes) {
17098
+ return createHash10("sha256").update(bytes).digest("hex");
17099
+ }
17100
+ function toStream(bytes, chunk = CHUNK) {
17101
+ let offset = 0;
17102
+ return new ReadableStream({
17103
+ pull(controller) {
17104
+ if (offset >= bytes.byteLength) {
17105
+ controller.close();
17106
+ return;
17107
+ }
17108
+ const end = Math.min(bytes.byteLength, offset + chunk);
17109
+ controller.enqueue(bytes.subarray(offset, end));
17110
+ offset = end;
17111
+ }
17112
+ });
17113
+ }
17114
+ async function collect(body) {
17115
+ const chunks = [];
17116
+ let total = 0;
17117
+ const reader = body.getReader();
17118
+ try {
17119
+ for (; ; ) {
17120
+ const { done, value } = await reader.read();
17121
+ if (done) break;
17122
+ chunks.push(value);
17123
+ total += value.byteLength;
17124
+ }
17125
+ } finally {
17126
+ reader.releaseLock();
17127
+ }
17128
+ const out = new Uint8Array(total);
17129
+ let o = 0;
17130
+ for (const c of chunks) {
17131
+ out.set(c, o);
17132
+ o += c.byteLength;
17133
+ }
17134
+ return out;
17135
+ }
17136
+ async function drain(body) {
17137
+ let total = 0;
17138
+ const reader = body.getReader();
17139
+ try {
17140
+ for (; ; ) {
17141
+ const { done, value } = await reader.read();
17142
+ if (done) break;
17143
+ total += value.byteLength;
17144
+ }
17145
+ } finally {
17146
+ reader.releaseLock();
17147
+ }
17148
+ return total;
17149
+ }
17150
+ function equalBytes(a, b) {
17151
+ if (a.byteLength !== b.byteLength) return false;
17152
+ for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
17153
+ return true;
17154
+ }
17155
+ function metaFor(bytes, contentType = "application/octet-stream") {
17156
+ return { bytes: bytes.byteLength, contentType, sha256: sha256(bytes) };
17157
+ }
17158
+ function largeStream(total, seed) {
17159
+ let produced = 0;
17160
+ let x = seed * 2654435761 + 1 >>> 0;
17161
+ return new ReadableStream({
17162
+ pull(controller) {
17163
+ if (produced >= total) {
17164
+ controller.close();
17165
+ return;
17166
+ }
17167
+ const size = Math.min(CHUNK, total - produced);
17168
+ const chunk = new Uint8Array(size);
17169
+ for (let i = 0; i < size; i++) {
17170
+ x = x * 1664525 + 1013904223 >>> 0;
17171
+ chunk[i] = x >>> 24;
17172
+ }
17173
+ produced += size;
17174
+ controller.enqueue(chunk);
17175
+ }
17176
+ });
17177
+ }
17178
+ async function largeSha256(total, seed) {
17179
+ const hash = createHash10("sha256");
17180
+ const reader = largeStream(total, seed).getReader();
17181
+ for (; ; ) {
17182
+ const { done, value } = await reader.read();
17183
+ if (done) break;
17184
+ hash.update(value);
17185
+ }
17186
+ return hash.digest("hex");
17187
+ }
17188
+ function sampleRss() {
17189
+ const base = process.memoryUsage().rss;
17190
+ let peak = base;
17191
+ const timer = setInterval(() => {
17192
+ peak = Math.max(peak, process.memoryUsage().rss);
17193
+ }, 20);
17194
+ return {
17195
+ stop() {
17196
+ clearInterval(timer);
17197
+ return Math.max(peak, process.memoryUsage().rss) - base;
17198
+ }
17199
+ };
17200
+ }
17201
+ var fmtMiB = (n) => `${(n / MIB).toFixed(1)} MiB`;
17202
+ var CaseFailure = class extends Error {
17203
+ };
17204
+ var fail = (detail) => {
17205
+ throw new CaseFailure(detail);
17206
+ };
17207
+ var ensure = (cond, detail) => {
17208
+ if (!cond) fail(detail);
17209
+ };
17210
+ var isStoreErrorOf = (err, kind) => err instanceof StoreError && err.kind === kind;
17211
+ async function runStorageConformance(store, options = {}) {
17212
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
17213
+ const t0 = performance.now();
17214
+ const caps = store.capabilities;
17215
+ const largeBytes = options.largeObjectBytes ?? 16 * MIB;
17216
+ const rssBudget = options.rssBudgetBytes ?? 64 * MIB;
17217
+ const eventualMs = options.eventualConsistencyMs ?? 0;
17218
+ const prefix = options.keyPrefix ?? `conformance-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
17219
+ const concurrency = Math.max(2, options.concurrency ?? 16);
17220
+ const created = /* @__PURE__ */ new Set();
17221
+ const key2 = (name) => {
17222
+ const k = `${prefix}/${name}`;
17223
+ created.add(k);
17224
+ return k;
17225
+ };
17226
+ async function waitVisible(listPrefix, expected) {
17227
+ const deadline = performance.now() + eventualMs;
17228
+ for (; ; ) {
17229
+ for await (const entry of store.list(listPrefix, void 0, options.signal)) {
17230
+ if (entry.key === expected) return true;
17231
+ }
17232
+ if (performance.now() >= deadline) return false;
17233
+ await new Promise((r) => setTimeout(r, Math.min(50, Math.max(5, eventualMs / 20))));
17234
+ }
17235
+ }
17236
+ const cases = [];
17237
+ async function run(id, fn) {
17238
+ const start = performance.now();
17239
+ try {
17240
+ const detail = await fn();
17241
+ const skipped = detail.startsWith("skipped:");
17242
+ cases.push({ id, passed: true, detail, ...skipped ? { skipped: true } : {}, durationMs: Math.round(performance.now() - start) });
17243
+ } catch (err) {
17244
+ const detail = err instanceof CaseFailure ? err.message : `异常:${err instanceof Error ? `${err.name}: ${err.message}` : String(err)}`;
17245
+ cases.push({ id, passed: false, detail, durationMs: Math.round(performance.now() - start) });
17246
+ }
17247
+ }
17248
+ await run("idempotent_put", async () => {
17249
+ const k = key2("idempotent");
17250
+ const bytes = bytesOf(7e4, 1);
17251
+ const meta = metaFor(bytes);
17252
+ const r1 = await store.put(k, toStream(bytes), meta);
17253
+ const r2 = await store.put(k, toStream(bytes), meta);
17254
+ ensure(r1.etag === void 0 || r2.etag === void 0 || r1.etag === r2.etag, `同内容重复 put 的 etag 不同:${r1.etag} / ${r2.etag}`);
17255
+ const head = await store.head(k);
17256
+ ensure(head !== null, "put 后 head 为 null");
17257
+ ensure(head.bytes === bytes.byteLength, `head.bytes ${head.bytes} ≠ ${bytes.byteLength}`);
17258
+ ensure(head.sha256 === meta.sha256, "head.sha256 与 meta 不符");
17259
+ ensure(head.contentType === meta.contentType, "head.contentType 与 meta 不符");
17260
+ const back = await collect(await store.get(k));
17261
+ ensure(equalBytes(back, bytes), "get 回读与 put 体不同");
17262
+ return `2 次 put 成功,head/get 一致(${bytes.byteLength} B)`;
17263
+ });
17264
+ await run("range_get", async () => {
17265
+ if (!caps.rangeGet) return "skipped: capabilities.rangeGet:false";
17266
+ const k = key2("range");
17267
+ const bytes = bytesOf(1e5, 2);
17268
+ await store.put(k, toStream(bytes), metaFor(bytes));
17269
+ const mid = await collect(await store.get(k, { start: 10, end: 19 }));
17270
+ ensure(equalBytes(mid, bytes.subarray(10, 20)), `Range [10,19] 回读 ${mid.byteLength} B 与切片不同`);
17271
+ const tail = await collect(await store.get(k, { start: bytes.byteLength - 5 }));
17272
+ ensure(equalBytes(tail, bytes.subarray(bytes.byteLength - 5)), "Range 缺省 end 到尾不符");
17273
+ const clamped = await collect(await store.get(k, { start: bytes.byteLength - 3, end: bytes.byteLength + 1e3 }));
17274
+ ensure(equalBytes(clamped, bytes.subarray(bytes.byteLength - 3)), "Range end 越界未钳到尾");
17275
+ const first = await collect(await store.get(k, { start: 0, end: 0 }));
17276
+ ensure(first.byteLength === 1 && first[0] === bytes[0], "Range [0,0] 应回 1 字节");
17277
+ return "中段 / 到尾 / 越界钳 / 单字节 Range 皆正确";
17278
+ });
17279
+ await run("concurrent_put", async () => {
17280
+ const items = Array.from({ length: concurrency }, (_, i) => ({ k: key2(`concurrent/${String(i).padStart(3, "0")}`), bytes: bytesOf(2e4 + i * 7, 100 + i) }));
17281
+ await Promise.all(items.map((it) => store.put(it.k, toStream(it.bytes), metaFor(it.bytes))));
17282
+ for (const it of items) {
17283
+ const head = await store.head(it.k);
17284
+ ensure(head !== null && head.sha256 === sha256(it.bytes), `并发 put 后 ${it.k} head 缺失或哈希不符`);
17285
+ ensure(equalBytes(await collect(await store.get(it.k)), it.bytes), `并发 put 后 ${it.k} 内容不符`);
17286
+ }
17287
+ return `${concurrency} 键并发 put 全部一致`;
17288
+ });
17289
+ await run("partial_put_invisible", async () => {
17290
+ const k = key2("partial");
17291
+ const bytes = bytesOf(3e5, 4);
17292
+ const meta = metaFor(bytes);
17293
+ let sent = 0;
17294
+ const broken = new ReadableStream({
17295
+ pull(controller) {
17296
+ if (sent >= 2) {
17297
+ controller.error(new Error("conformance: connection dropped"));
17298
+ return;
17299
+ }
17300
+ controller.enqueue(bytes.subarray(sent * CHUNK, Math.min(bytes.byteLength, (sent + 1) * CHUNK)));
17301
+ sent++;
17302
+ }
17303
+ });
17304
+ let threw = false;
17305
+ try {
17306
+ await store.put(k, broken, meta);
17307
+ } catch {
17308
+ threw = true;
17309
+ }
17310
+ ensure(threw, "体流中断的 put 未失败");
17311
+ ensure(await store.head(k) === null, "体流中断后留下半对象(head 非 null)");
17312
+ let threw2 = null;
17313
+ try {
17314
+ await store.put(k, toStream(bytes), { ...meta, sha256: "f".repeat(64) });
17315
+ } catch (err) {
17316
+ threw2 = err;
17317
+ }
17318
+ ensure(threw2 !== null, "sha256 不符的 put 未失败");
17319
+ ensure(await store.head(k) === null, "sha256 不符后留下对象");
17320
+ return `体流中断 / sha256 不符均失败且无半对象${isStoreErrorOf(threw2, "permanent") ? "(sha256 不符归 permanent)" : ""}`;
17321
+ });
17322
+ await run("list_prefix_cursor", async () => {
17323
+ if (caps.list === "none") return "skipped: capabilities.list:'none'";
17324
+ const names = ["a-000", "a-001", "a-002", "b-000", "b-001"];
17325
+ const keys = names.map((n) => key2(`listing/${n}`));
17326
+ for (const [i, k] of keys.entries()) {
17327
+ const bytes = bytesOf(1e3 + i, 200 + i);
17328
+ await store.put(k, toStream(bytes), metaFor(bytes));
17329
+ }
17330
+ const listPrefix = `${prefix}/listing/`;
17331
+ for (const k of keys) ensure(await waitVisible(listPrefix, k), `${k} 在 ${eventualMs} ms 内未经 list 可见`);
17332
+ const all = [];
17333
+ for await (const e of store.list(listPrefix)) {
17334
+ all.push(e.key);
17335
+ ensure(e.meta.sha256.length === 64, `list 条目 meta.sha256 形状不对:${e.key}`);
17336
+ }
17337
+ ensure(all.length === keys.length, `list 返回 ${all.length} 项 ≠ ${keys.length}`);
17338
+ for (let i = 1; i < all.length; i++) ensure(all[i - 1] < all[i], "list 非字典升序");
17339
+ const sub = [];
17340
+ for await (const e of store.list(`${prefix}/listing/a-`)) sub.push(e.key);
17341
+ ensure(sub.length === 3 && sub.every((k) => k.includes("/a-")), `名前缀 list 应 3 项,得 ${sub.length}`);
17342
+ const rest = [];
17343
+ for await (const e of store.list(listPrefix, all[1])) rest.push(e.key);
17344
+ ensure(rest.length === all.length - 2 && rest[0] === all[2], `cursor 分页不正确:${rest.join(",")}`);
17345
+ const none = [];
17346
+ for await (const e of store.list(`${prefix}/nothing-here/`)) none.push(e.key);
17347
+ ensure(none.length === 0, "空前缀 list 应无项");
17348
+ return `${keys.length} 键前缀 / 名前缀 / cursor 分页 / 空前缀正确(可见窗口 ${eventualMs} ms)`;
17349
+ });
17350
+ await run("large_object_streaming", async () => {
17351
+ const k = key2("large");
17352
+ const digest = await largeSha256(largeBytes, 6);
17353
+ const meta = { bytes: largeBytes, contentType: "application/octet-stream", sha256: digest };
17354
+ const putSampler = sampleRss();
17355
+ await store.put(k, largeStream(largeBytes, 6), meta);
17356
+ const putDelta = putSampler.stop();
17357
+ const head = await store.head(k);
17358
+ ensure(head !== null && head.bytes === largeBytes, "large head 缺失或字节数不符");
17359
+ const getSampler = sampleRss();
17360
+ const drained = await drain(await store.get(k));
17361
+ const getDelta = getSampler.stop();
17362
+ ensure(drained === largeBytes, `get 回读 ${drained} B ≠ ${largeBytes}`);
17363
+ ensure(putDelta < rssBudget, `put RSS 峰值增量 ${fmtMiB(putDelta)} ≥ 预算 ${fmtMiB(rssBudget)}`);
17364
+ ensure(getDelta < rssBudget, `get RSS 峰值增量 ${fmtMiB(getDelta)} ≥ 预算 ${fmtMiB(rssBudget)}`);
17365
+ return `${fmtMiB(largeBytes)}:put RSS Δ ${fmtMiB(putDelta)} / get RSS Δ ${fmtMiB(getDelta)} < ${fmtMiB(rssBudget)}`;
17366
+ });
17367
+ await run("abort_signal", async () => {
17368
+ const k = key2("abort");
17369
+ const bytes = bytesOf(4e5, 7);
17370
+ const meta = metaFor(bytes);
17371
+ const pre = new AbortController();
17372
+ pre.abort(new Error("conformance: pre-aborted"));
17373
+ let threw = false;
17374
+ try {
17375
+ await store.put(k, toStream(bytes), meta, { signal: pre.signal });
17376
+ } catch {
17377
+ threw = true;
17378
+ }
17379
+ ensure(threw, "预中止 signal 的 put 未失败");
17380
+ ensure(await store.head(k) === null, "预中止 put 留下对象");
17381
+ const mid = new AbortController();
17382
+ let pulls = 0;
17383
+ const midBody = new ReadableStream({
17384
+ pull(controller) {
17385
+ if (pulls === 1) mid.abort(new Error("conformance: mid abort"));
17386
+ if (pulls * CHUNK >= bytes.byteLength) {
17387
+ controller.close();
17388
+ return;
17389
+ }
17390
+ controller.enqueue(bytes.subarray(pulls * CHUNK, Math.min(bytes.byteLength, (pulls + 1) * CHUNK)));
17391
+ pulls++;
17392
+ }
17393
+ });
17394
+ let threwMid = false;
17395
+ try {
17396
+ await store.put(k, midBody, meta, { signal: mid.signal });
17397
+ } catch {
17398
+ threwMid = true;
17399
+ }
17400
+ ensure(threwMid, "中途中止的 put 未失败");
17401
+ ensure(await store.head(k) === null, "中途中止 put 留下半对象");
17402
+ await store.put(k, toStream(bytes), meta);
17403
+ const getAbort = new AbortController();
17404
+ getAbort.abort(new Error("conformance: get aborted"));
17405
+ let getThrew = false;
17406
+ try {
17407
+ const body = await store.get(k, void 0, getAbort.signal);
17408
+ await drain(body);
17409
+ } catch {
17410
+ getThrew = true;
17411
+ }
17412
+ ensure(getThrew, "预中止 signal 的 get 未失败(既未拒绝也未以流出错)");
17413
+ return "预中止 / 中途中止 put 干净失败无半对象;预中止 get 失败";
17414
+ });
17415
+ await run("manifest_cas", async () => {
17416
+ if (!caps.conditionalPut) return "skipped: capabilities.conditionalPut:false(须挂 SessionIndexStore)";
17417
+ const k = key2("manifest.json");
17418
+ const v1 = new TextEncoder().encode('{"generation":1}\n');
17419
+ const v2 = new TextEncoder().encode('{"generation":2}\n');
17420
+ const v3 = new TextEncoder().encode('{"generation":3}\n');
17421
+ const r1 = await store.put(k, toStream(v1), metaFor(v1, "application/json"), { ifNoneMatch: "*" });
17422
+ ensure(r1.etag !== void 0, "conditionalPut:true 的 put 须返回 etag");
17423
+ let conflict = null;
17424
+ try {
17425
+ await store.put(k, toStream(v2), metaFor(v2, "application/json"), { ifNoneMatch: "*" });
17426
+ } catch (err) {
17427
+ conflict = err;
17428
+ }
17429
+ ensure(isStoreErrorOf(conflict, "precondition_failed"), `ifNoneMatch:* 对已存在键应 precondition_failed,实为 ${String(conflict)}`);
17430
+ ensure(equalBytes(await collect(await store.get(k)), v1), "ifNoneMatch 冲突后内容被改");
17431
+ const head1 = await store.head(k);
17432
+ ensure(head1 !== null && head1.etag === r1.etag, `head.etag(${head1?.etag})应等于 put 返回 etag(${r1.etag})`);
17433
+ const r2 = await store.put(k, toStream(v2), metaFor(v2, "application/json"), { ifMatch: r1.etag });
17434
+ ensure(r2.etag !== void 0 && r2.etag !== r1.etag, "ifMatch 改写后 etag 应变化");
17435
+ let stale = null;
17436
+ try {
17437
+ await store.put(k, toStream(v3), metaFor(v3, "application/json"), { ifMatch: r1.etag });
17438
+ } catch (err) {
17439
+ stale = err;
17440
+ }
17441
+ ensure(isStoreErrorOf(stale, "precondition_failed"), `过期 etag 的 ifMatch 应 precondition_failed,实为 ${String(stale)}`);
17442
+ ensure(equalBytes(await collect(await store.get(k)), v2), "过期 ifMatch 冲突后内容被改");
17443
+ let missing = null;
17444
+ try {
17445
+ await store.put(key2("manifest-missing.json"), toStream(v1), metaFor(v1, "application/json"), { ifMatch: "nope" });
17446
+ } catch (err) {
17447
+ missing = err;
17448
+ }
17449
+ ensure(isStoreErrorOf(missing, "precondition_failed"), "ifMatch 对不存在键应 precondition_failed");
17450
+ return "ifNoneMatch:* 首写 / 冲突 412;ifMatch etag 改写 / 过期 412 / 缺席 412;head 回带 etag";
17451
+ });
17452
+ await run("delete_idempotent", async () => {
17453
+ const k = key2("delete");
17454
+ const bytes = bytesOf(1234, 9);
17455
+ await store.put(k, toStream(bytes), metaFor(bytes));
17456
+ await store.delete([k]);
17457
+ ensure(await store.head(k) === null, "delete 后 head 仍非 null");
17458
+ let notFound = null;
17459
+ try {
17460
+ await drain(await store.get(k));
17461
+ } catch (err) {
17462
+ notFound = err;
17463
+ }
17464
+ ensure(notFound !== null, "delete 后 get 未失败");
17465
+ await store.delete([k]);
17466
+ await store.delete([key2("never-existed")]);
17467
+ const batch = Math.max(1, Math.min(caps.batchDelete ?? 1, 8));
17468
+ const many = Array.from({ length: batch }, (_, i) => key2(`delete-batch/${i}`));
17469
+ for (const [i, mk] of many.entries()) {
17470
+ const b = bytesOf(100 + i, 300 + i);
17471
+ await store.put(mk, toStream(b), metaFor(b));
17472
+ }
17473
+ await store.delete(many);
17474
+ for (const mk of many) ensure(await store.head(mk) === null, `批删后 ${mk} 仍在`);
17475
+ return `在场 / 重复 / 不存在 / 批量 ${batch} 键删除皆幂等`;
17476
+ });
17477
+ await run("capabilities_truthful", async () => {
17478
+ const notes = [];
17479
+ const missing = key2("missing");
17480
+ ensure(await store.head(missing) === null, "head 不存在键应返 null 而非抛");
17481
+ let getErr = null;
17482
+ try {
17483
+ await drain(await store.get(missing));
17484
+ } catch (err) {
17485
+ getErr = err;
17486
+ }
17487
+ ensure(getErr !== null, "get 不存在键应失败");
17488
+ notes.push(isStoreErrorOf(getErr, "not_found") ? "get 缺席 → not_found" : `get 缺席 → ${toStoreError(getErr).kind}(建议 not_found)`);
17489
+ ensure(caps.consistency === "strong" || caps.consistency === "eventual", `consistency 值非法:${String(caps.consistency)}`);
17490
+ ensure(Number.isFinite(caps.maxObjectBytes) && caps.maxObjectBytes > 0, "maxObjectBytes 须为正有限数");
17491
+ ensure(caps.list === "prefix" || caps.list === "none", `list 值非法:${String(caps.list)}`);
17492
+ ensure(caps.batchDelete === void 0 || Number.isInteger(caps.batchDelete) && caps.batchDelete >= 1, "batchDelete 须为 ≥1 整数");
17493
+ const bytes = bytesOf(5e4, 10);
17494
+ const k = key2("truthful");
17495
+ const r = await store.put(k, toStream(bytes), metaFor(bytes));
17496
+ if (caps.rangeGet) {
17497
+ const part = await collect(await store.get(k, { start: 100, end: 199 }));
17498
+ ensure(equalBytes(part, bytes.subarray(100, 200)), "声明 rangeGet:true 但 Range 读不正确");
17499
+ notes.push("rangeGet 属实");
17500
+ }
17501
+ if (caps.conditionalPut) {
17502
+ ensure(r.etag !== void 0, "声明 conditionalPut:true 但 put 未返回 etag");
17503
+ const head = await store.head(k);
17504
+ ensure(head?.etag === r.etag, "声明 conditionalPut:true 但 head 未回带 etag");
17505
+ let conflict = null;
17506
+ try {
17507
+ const other = bytesOf(5e4, 11);
17508
+ await store.put(k, toStream(other), metaFor(other), { ifNoneMatch: "*" });
17509
+ } catch (err) {
17510
+ conflict = err;
17511
+ }
17512
+ ensure(isStoreErrorOf(conflict, "precondition_failed"), "声明 conditionalPut:true 但 ifNoneMatch:* 未拒绝");
17513
+ notes.push("conditionalPut 属实");
17514
+ }
17515
+ if (caps.list === "prefix") {
17516
+ ensure(await waitVisible(`${prefix}/truthful`, k), "声明 list:prefix 但 put 后 list 不见");
17517
+ notes.push("list:prefix 属实");
17518
+ }
17519
+ if (caps.consistency === "strong") {
17520
+ ensure(await store.head(k) !== null, "声明 strong 但写后 head 不可见");
17521
+ notes.push("strong 写后即读属实");
17522
+ }
17523
+ return notes.join(";");
17524
+ });
17525
+ if (options.keepObjects !== true) {
17526
+ const keys = [...created];
17527
+ const batch = Math.max(1, caps.batchDelete ?? 1);
17528
+ for (let i = 0; i < keys.length; i += batch) {
17529
+ await store.delete(keys.slice(i, i + batch)).catch(() => void 0);
17530
+ }
17531
+ }
17532
+ return {
17533
+ cases,
17534
+ passed: cases.every((c) => c.passed),
17535
+ capabilities: { ...caps },
17536
+ startedAt,
17537
+ durationMs: Math.round(performance.now() - t0)
17538
+ };
17539
+ }
17540
+
17541
+ // ../kernel/src/journal/segmented/chaos-blob-store.ts
17542
+ function matches(pred, key2) {
17543
+ if (pred === void 0) return false;
17544
+ return typeof pred === "boolean" ? pred : pred(key2);
17545
+ }
17546
+ var delay = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
17547
+ function createChaosBlobStore(inner, faults = {}, now = Date.now) {
17548
+ const putAt = /* @__PURE__ */ new Map();
17549
+ let putsSeen = 0;
17550
+ const stats = {
17551
+ putFailures: 0,
17552
+ putTruncated: 0,
17553
+ listHidden: 0,
17554
+ listDropped: 0,
17555
+ corruptedGets: 0,
17556
+ abortedGets: 0,
17557
+ calls: { put: 0, get: 0, head: 0, list: 0, delete: 0 }
17558
+ };
17559
+ const latency = async () => {
17560
+ const l = faults.latencyMs;
17561
+ if (l === void 0) return;
17562
+ const ms = typeof l === "number" ? l : l.min + Math.random() * Math.max(0, l.max - l.min);
17563
+ if (ms > 0) await delay(ms);
17564
+ };
17565
+ const injectedPutError = (key2, detail) => {
17566
+ const kind = faults.putErrorKind ?? "transient";
17567
+ return new StoreError(kind, `chaos: ${detail} (${key2})`, kind === "transient" && faults.retryAfterMs !== void 0 ? { retryAfterMs: faults.retryAfterMs } : {});
17568
+ };
17569
+ const shouldFailPut = (key2) => {
17570
+ const f = faults.failPut;
17571
+ if (f === void 0) return false;
17572
+ if (typeof f === "number") return putsSeen <= f;
17573
+ return matches(f, key2);
17574
+ };
17575
+ const truncateBody = (body, afterBytes, key2) => {
17576
+ let seen = 0;
17577
+ return body.pipeThrough(
17578
+ new TransformStream({
17579
+ transform(chunk, controller) {
17580
+ if (seen >= afterBytes) {
17581
+ controller.error(injectedPutError(key2, `connection dropped after ${afterBytes} bytes`));
17582
+ return;
17583
+ }
17584
+ const take = Math.min(chunk.byteLength, afterBytes - seen);
17585
+ seen += take;
17586
+ controller.enqueue(chunk.subarray(0, take));
17587
+ if (seen >= afterBytes) controller.error(injectedPutError(key2, `connection dropped after ${afterBytes} bytes`));
17588
+ }
17589
+ })
17590
+ );
17591
+ };
17592
+ const capabilities = {
17593
+ ...inner.capabilities,
17594
+ get consistency() {
17595
+ return (faults.eventualListLagMs ?? 0) > 0 || faults.dropListEntries !== void 0 ? "eventual" : inner.capabilities.consistency;
17596
+ }
17597
+ };
17598
+ return {
17599
+ faults,
17600
+ stats,
17601
+ inner,
17602
+ capabilities,
17603
+ async put(key2, body, meta, opts) {
17604
+ stats.calls.put++;
17605
+ putsSeen++;
17606
+ await latency();
17607
+ if (shouldFailPut(key2)) {
17608
+ stats.putFailures++;
17609
+ await body.cancel().catch(() => void 0);
17610
+ throw injectedPutError(key2, "put rejected");
17611
+ }
17612
+ const after = faults.failPutAfterBytes;
17613
+ if (after !== void 0) {
17614
+ stats.putTruncated++;
17615
+ try {
17616
+ return await inner.put(key2, truncateBody(body, after, key2), meta, opts);
17617
+ } catch (err) {
17618
+ if (err instanceof StoreError && err.kind === faults.putErrorKind) throw err;
17619
+ throw injectedPutError(key2, `connection dropped after ${after} bytes`);
17620
+ }
17621
+ }
17622
+ const result = await inner.put(key2, body, meta, opts);
17623
+ putAt.set(key2, now());
17624
+ return result;
17625
+ },
17626
+ async get(key2, range, signal) {
17627
+ stats.calls.get++;
17628
+ await latency();
17629
+ const body = await inner.get(key2, range, signal);
17630
+ if (matches(faults.corruptSegmentKeys, key2)) {
17631
+ stats.corruptedGets++;
17632
+ let flipped = false;
17633
+ return body.pipeThrough(
17634
+ new TransformStream({
17635
+ transform(chunk, controller) {
17636
+ if (!flipped && chunk.byteLength > 0) {
17637
+ const copy = new Uint8Array(chunk);
17638
+ copy[0] = copy[0] ^ 255;
17639
+ flipped = true;
17640
+ controller.enqueue(copy);
17641
+ return;
17642
+ }
17643
+ controller.enqueue(chunk);
17644
+ }
17645
+ })
17646
+ );
17647
+ }
17648
+ if (matches(faults.abortMidStream, key2)) {
17649
+ stats.abortedGets++;
17650
+ let passed = 0;
17651
+ return body.pipeThrough(
17652
+ new TransformStream({
17653
+ transform(chunk, controller) {
17654
+ if (passed === 0) {
17655
+ controller.enqueue(chunk);
17656
+ passed++;
17657
+ return;
17658
+ }
17659
+ controller.error(new StoreError("transient", `chaos: download aborted mid-stream (${key2})`));
17660
+ },
17661
+ flush(controller) {
17662
+ if (passed <= 1) controller.error(new StoreError("transient", `chaos: download aborted mid-stream (${key2})`));
17663
+ }
17664
+ })
17665
+ );
17666
+ }
17667
+ return body;
17668
+ },
17669
+ async head(key2, signal) {
17670
+ stats.calls.head++;
17671
+ await latency();
17672
+ return inner.head(key2, signal);
17673
+ },
17674
+ async *list(prefix, cursor, signal) {
17675
+ stats.calls.list++;
17676
+ await latency();
17677
+ const lag = faults.eventualListLagMs ?? 0;
17678
+ const drop = faults.dropListEntries;
17679
+ for await (const entry of inner.list(prefix, cursor, signal)) {
17680
+ if (lag > 0) {
17681
+ const at = putAt.get(entry.key);
17682
+ if (at !== void 0 && now() - at < lag) {
17683
+ stats.listHidden++;
17684
+ continue;
17685
+ }
17686
+ }
17687
+ if (drop !== void 0) {
17688
+ const dropped = typeof drop === "number" ? Math.random() < drop : matches(drop, entry.key);
17689
+ if (dropped) {
17690
+ stats.listDropped++;
17691
+ continue;
17692
+ }
17693
+ }
17694
+ yield entry;
17695
+ }
17696
+ },
17697
+ async delete(keys, signal) {
17698
+ stats.calls.delete++;
17699
+ await latency();
17700
+ await inner.delete(keys, signal);
17701
+ for (const key2 of keys) putAt.delete(key2);
17702
+ }
17703
+ };
17704
+ }
16833
17705
 
16834
17706
  // ../kernel/src/journal/segmented/store-metrics.ts
16835
17707
  var STORE_BUCKETS_MS = DURATION_BUCKETS_MS.filter((b) => b >= 5);
@@ -16861,8 +17733,8 @@ function storeMetricsFor(registry = defaultMetrics) {
16861
17733
  }
16862
17734
 
16863
17735
  // ../kernel/src/journal/segmented/cold-tier.ts
16864
- import { mkdir as mkdir4, rm as rm5, stat as stat3 } from "node:fs/promises";
16865
- import path8 from "node:path";
17736
+ import { mkdir as mkdir5, rm as rm6, stat as stat4 } from "node:fs/promises";
17737
+ import path9 from "node:path";
16866
17738
  async function observed(metrics, op, fn, bytes) {
16867
17739
  const t0 = performance.now();
16868
17740
  try {
@@ -17089,13 +17961,13 @@ function createColdTier(options) {
17089
17961
  return state.sessionMeta;
17090
17962
  }
17091
17963
  async function uploadOne(state, segment) {
17092
- const filePath = path8.join(state.ref.sessionDir, segment.file);
17964
+ const filePath = path9.join(state.ref.sessionDir, segment.file);
17093
17965
  const volume = await currentVolume(state);
17094
17966
  const stillHot = volume?.manifest.segments.find((s) => s.n === segment.n && s.lastHash === segment.lastHash);
17095
17967
  if (volume === null || stillHot === void 0) return;
17096
17968
  let rawBytes;
17097
17969
  try {
17098
- rawBytes = (await stat3(filePath)).size;
17970
+ rawBytes = (await stat4(filePath)).size;
17099
17971
  } catch (err) {
17100
17972
  if (isErrnoException(err) && err.code === "ENOENT") {
17101
17973
  const laterHot = volume.manifest.segments.some((s) => s.n > segment.n && s.status === "sealed");
@@ -17161,7 +18033,7 @@ function createColdTier(options) {
17161
18033
  emit({ type: "store.segment_committed", session: state.ref, segment, key: key2, bytes: entry.bytes, generation: snapshot.manifest.generation });
17162
18034
  } finally {
17163
18035
  state.inflight.delete(key2);
17164
- await rm5(spoolPath, { force: true }).catch(() => void 0);
18036
+ await rm6(spoolPath, { force: true }).catch(() => void 0);
17165
18037
  }
17166
18038
  }
17167
18039
  async function evictIfNeeded() {
@@ -17187,7 +18059,7 @@ function createColdTier(options) {
17187
18059
  continue;
17188
18060
  }
17189
18061
  try {
17190
- await rm5(path8.join(c.state.ref.sessionDir, segment.file), { force: true });
18062
+ await rm6(path9.join(c.state.ref.sessionDir, segment.file), { force: true });
17191
18063
  } catch {
17192
18064
  continue;
17193
18065
  }
@@ -17217,7 +18089,7 @@ function createColdTier(options) {
17217
18089
  if (segment.status !== "sealed") continue;
17218
18090
  let size;
17219
18091
  try {
17220
- size = (await stat3(path8.join(state.ref.sessionDir, segment.file))).size;
18092
+ size = (await stat4(path9.join(state.ref.sessionDir, segment.file))).size;
17221
18093
  } catch {
17222
18094
  continue;
17223
18095
  }
@@ -17244,7 +18116,7 @@ function createColdTier(options) {
17244
18116
  while (active < upload.concurrency && runQueue.length > 0) {
17245
18117
  const state = runQueue.shift();
17246
18118
  active++;
17247
- void drain(state).finally(() => {
18119
+ void drain2(state).finally(() => {
17248
18120
  active--;
17249
18121
  state.scheduled = false;
17250
18122
  if (!closed && state.pending.length > 0 && !state.stopped && !state.paused && states.has(state.ref.sessionDir)) schedule(state);
@@ -17256,7 +18128,7 @@ function createColdTier(options) {
17256
18128
  });
17257
18129
  }
17258
18130
  }
17259
- async function drain(state) {
18131
+ async function drain2(state) {
17260
18132
  try {
17261
18133
  if (!state.reconciled) await reconcile(state);
17262
18134
  while (state.pending.length > 0 && !state.stopped && !closed) {
@@ -17399,7 +18271,7 @@ function createColdTier(options) {
17399
18271
  const last = manifest.segments[manifest.segments.length - 1];
17400
18272
  const base = { meta, segments: manifest.segments.length, lastSeq: last?.lastSeq ?? -1, volumeId: manifest.volumeId, generation: manifest.generation };
17401
18273
  if (hot !== null) return { ...base, restored: false };
17402
- await mkdir4(session.sessionDir, { recursive: true });
18274
+ await mkdir5(session.sessionDir, { recursive: true });
17403
18275
  await writeManifest(session.sessionDir, coldManifestToHot(manifest));
17404
18276
  const state = states.get(session.sessionDir);
17405
18277
  if (state !== void 0) {
@@ -17458,11 +18330,11 @@ function createColdTier(options) {
17458
18330
  }
17459
18331
 
17460
18332
  // ../kernel/src/journal/session-index.ts
17461
- import { appendFile as appendFile2, readFile as readFile6, readdir, rm as rm6, stat as stat4 } from "node:fs/promises";
17462
- import path9 from "node:path";
18333
+ import { appendFile as appendFile2, readFile as readFile7, readdir as readdir2, rm as rm7, stat as stat5 } from "node:fs/promises";
18334
+ import path10 from "node:path";
17463
18335
  var SESSION_INDEX_FILE_NAME = "index.jsonl";
17464
18336
  function sessionIndexFilePath(storageRoot) {
17465
- return path9.join(storageRoot, SESSION_INDEX_FILE_NAME);
18337
+ return path10.join(storageRoot, SESSION_INDEX_FILE_NAME);
17466
18338
  }
17467
18339
  var COMPACT_SLACK_ROWS = 256;
17468
18340
  var COMPACT_RATIO = 4;
@@ -17477,7 +18349,7 @@ var SessionIndex = class {
17477
18349
  constructor(options) {
17478
18350
  this.#root = options.storageRoot;
17479
18351
  this.#file = sessionIndexFilePath(options.storageRoot);
17480
- this.#sessionsDir = path9.join(options.storageRoot, "sessions");
18352
+ this.#sessionsDir = path10.join(options.storageRoot, "sessions");
17481
18353
  this.#readMeta = options.readMeta;
17482
18354
  }
17483
18355
  /** 全部存活条目(无序;调用方自行排序/截断) */
@@ -17541,14 +18413,14 @@ var SessionIndex = class {
17541
18413
  }
17542
18414
  async #dirMtime() {
17543
18415
  try {
17544
- return (await stat4(this.#sessionsDir)).mtimeMs;
18416
+ return (await stat5(this.#sessionsDir)).mtimeMs;
17545
18417
  } catch {
17546
18418
  return -1;
17547
18419
  }
17548
18420
  }
17549
18421
  async #fileSize() {
17550
18422
  try {
17551
- return (await stat4(this.#file)).size;
18423
+ return (await stat5(this.#file)).size;
17552
18424
  } catch {
17553
18425
  return -1;
17554
18426
  }
@@ -17585,7 +18457,7 @@ var SessionIndex = class {
17585
18457
  async #loadFile() {
17586
18458
  let raw;
17587
18459
  try {
17588
- raw = await readFile6(this.#file, "utf8");
18460
+ raw = await readFile7(this.#file, "utf8");
17589
18461
  } catch (err) {
17590
18462
  if (isErrnoException(err) && err.code === "ENOENT") return null;
17591
18463
  throw err;
@@ -17626,13 +18498,13 @@ var SessionIndex = class {
17626
18498
  const entries = /* @__PURE__ */ new Map();
17627
18499
  let names = [];
17628
18500
  try {
17629
- names = await readdir(this.#sessionsDir, { withFileTypes: true });
18501
+ names = await readdir2(this.#sessionsDir, { withFileTypes: true });
17630
18502
  } catch {
17631
18503
  names = [];
17632
18504
  }
17633
18505
  for (const entry of names) {
17634
18506
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
17635
- const meta = await this.#readMeta(path9.join(this.#sessionsDir, entry.name), entry.name);
18507
+ const meta = await this.#readMeta(path10.join(this.#sessionsDir, entry.name), entry.name);
17636
18508
  if (meta !== null) entries.set(meta.sessionId, meta);
17637
18509
  }
17638
18510
  const state = { entries, fileSize: -1, dirMtimeMs, rows: 0 };
@@ -17652,7 +18524,7 @@ var SessionIndex = class {
17652
18524
  ` : "";
17653
18525
  try {
17654
18526
  if (state.entries.size === 0 && state.dirMtimeMs === -1) {
17655
- await rm6(this.#file, { force: true }).catch(() => void 0);
18527
+ await rm7(this.#file, { force: true }).catch(() => void 0);
17656
18528
  state.fileSize = -1;
17657
18529
  state.rows = 0;
17658
18530
  return;
@@ -17683,9 +18555,9 @@ var SessionIndex = class {
17683
18555
  };
17684
18556
 
17685
18557
  // ../kernel/src/journal/checkpoints.ts
17686
- import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
17687
- import { access as access3, mkdir as mkdir5, readdir as readdir2, readFile as readFile7, rm as rm7 } from "node:fs/promises";
17688
- import path10 from "node:path";
18558
+ import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
18559
+ import { access as access3, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, rm as rm8 } from "node:fs/promises";
18560
+ import path11 from "node:path";
17689
18561
  import { z as z17 } from "zod";
17690
18562
  var ContextCheckpointTriggerSchema = z17.enum([
17691
18563
  "manual",
@@ -17739,7 +18611,7 @@ function newCheckpointId(now = Date.now()) {
17739
18611
  ulidLastRandom[i] = 0;
17740
18612
  }
17741
18613
  } else {
17742
- ulidLastRandom = Array.from(randomBytes2(16), (b) => b & 31);
18614
+ ulidLastRandom = Array.from(randomBytes3(16), (b) => b & 31);
17743
18615
  ulidLastTime = now;
17744
18616
  }
17745
18617
  let timePart = "";
@@ -17762,13 +18634,13 @@ var META_SUFFIX = ".meta.json";
17762
18634
  var BODY_SUFFIX = ".json";
17763
18635
  function checkpointsDirPath(root, sessionId) {
17764
18636
  assertValidSessionId(sessionId);
17765
- return path10.join(root, sessionId);
18637
+ return path11.join(root, sessionId);
17766
18638
  }
17767
18639
  function bodyFilePath(dir, checkpointId) {
17768
- return path10.join(dir, `${checkpointId}${BODY_SUFFIX}`);
18640
+ return path11.join(dir, `${checkpointId}${BODY_SUFFIX}`);
17769
18641
  }
17770
18642
  function metaFilePath2(dir, checkpointId) {
17771
- return path10.join(dir, `${checkpointId}${META_SUFFIX}`);
18643
+ return path11.join(dir, `${checkpointId}${META_SUFFIX}`);
17772
18644
  }
17773
18645
  var isRecord3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
17774
18646
  var REF_PATTERN2 = /^sha256:([0-9a-f]{64})$/;
@@ -17800,7 +18672,7 @@ async function storeImages(messages, cpDir, attachmentsFrom) {
17800
18672
  `messages 含 ${refs.length} 个已外置($ref)image 块,须以 opts.attachmentsFrom 指明附件来源会话目录`
17801
18673
  );
17802
18674
  }
17803
- await mkdir5(attachmentsDirPath(cpDir), { recursive: true });
18675
+ await mkdir6(attachmentsDirPath(cpDir), { recursive: true });
17804
18676
  for (const hex of refs) {
17805
18677
  const target = attachmentFilePath(cpDir, hex);
17806
18678
  let targetExists = true;
@@ -17812,7 +18684,7 @@ async function storeImages(messages, cpDir, attachmentsFrom) {
17812
18684
  if (targetExists) continue;
17813
18685
  let bytes;
17814
18686
  try {
17815
- bytes = await readFile7(attachmentFilePath(attachmentsFrom, hex));
18687
+ bytes = await readFile8(attachmentFilePath(attachmentsFrom, hex));
17816
18688
  } catch (err) {
17817
18689
  if (isErrnoException(err) && err.code === "ENOENT") continue;
17818
18690
  throw err;
@@ -17829,7 +18701,7 @@ async function storeImages(messages, cpDir, attachmentsFrom) {
17829
18701
  async function writeCheckpoint(root, cp, opts = {}) {
17830
18702
  assertValidCheckpointId(cp.checkpointId);
17831
18703
  const dir = checkpointsDirPath(root, cp.sessionId);
17832
- await mkdir5(dir, { recursive: true });
18704
+ await mkdir6(dir, { recursive: true });
17833
18705
  const { messages } = repairHistoryPairing(cp.messages);
17834
18706
  const { messages: _ignored, ...head } = cp;
17835
18707
  const metaCandidate = { ...head, schemaVersion: 1, messageCount: messages.length };
@@ -17852,7 +18724,7 @@ async function writeCheckpoint(root, cp, opts = {}) {
17852
18724
  async function readJsonFile(filePath) {
17853
18725
  let raw;
17854
18726
  try {
17855
- raw = await readFile7(filePath, "utf8");
18727
+ raw = await readFile8(filePath, "utf8");
17856
18728
  } catch (err) {
17857
18729
  if (isErrnoException(err) && err.code === "ENOENT") return null;
17858
18730
  throw err;
@@ -17904,7 +18776,7 @@ async function listCheckpoints(root, sessionId) {
17904
18776
  const dir = checkpointsDirPath(root, sessionId);
17905
18777
  let names;
17906
18778
  try {
17907
- names = await readdir2(dir);
18779
+ names = await readdir3(dir);
17908
18780
  } catch (err) {
17909
18781
  if (isErrnoException(err) && err.code === "ENOENT") return [];
17910
18782
  throw err;
@@ -17944,12 +18816,12 @@ async function deleteCheckpoint(root, sessionId, checkpointId, opts = {}) {
17944
18816
  const refs = opts.gcAttachments === false ? [] : await collectBodyRefs(bodyFilePath(dir, checkpointId));
17945
18817
  let existed = true;
17946
18818
  try {
17947
- await rm7(bodyFilePath(dir, checkpointId));
18819
+ await rm8(bodyFilePath(dir, checkpointId));
17948
18820
  } catch (err) {
17949
18821
  if (!isErrnoException(err) || err.code !== "ENOENT") throw err;
17950
18822
  existed = false;
17951
18823
  }
17952
- await rm7(metaFilePath2(dir, checkpointId), { force: true });
18824
+ await rm8(metaFilePath2(dir, checkpointId), { force: true });
17953
18825
  if (refs.length > 0) await gcAttachments(dir, refs);
17954
18826
  return existed;
17955
18827
  }
@@ -17967,7 +18839,7 @@ async function gcAttachments(dir, candidates) {
17967
18839
  const pending = new Set(candidates);
17968
18840
  let names;
17969
18841
  try {
17970
- names = await readdir2(dir);
18842
+ names = await readdir3(dir);
17971
18843
  } catch (err) {
17972
18844
  if (isErrnoException(err) && err.code === "ENOENT") return;
17973
18845
  throw err;
@@ -17977,10 +18849,10 @@ async function gcAttachments(dir, candidates) {
17977
18849
  if (!name.endsWith(BODY_SUFFIX) || name.endsWith(META_SUFFIX)) continue;
17978
18850
  const id = name.slice(0, -BODY_SUFFIX.length);
17979
18851
  if (!CHECKPOINT_ID_PATTERN.test(id)) continue;
17980
- for (const hex of await collectBodyRefs(path10.join(dir, name))) pending.delete(hex);
18852
+ for (const hex of await collectBodyRefs(path11.join(dir, name))) pending.delete(hex);
17981
18853
  }
17982
18854
  for (const hex of pending) {
17983
- await rm7(attachmentFilePath(dir, hex), { force: true });
18855
+ await rm8(attachmentFilePath(dir, hex), { force: true });
17984
18856
  }
17985
18857
  }
17986
18858
  async function applyRetention(root, sessionId, reference, retention) {
@@ -18026,7 +18898,7 @@ function detachInlineImage(block, attachments) {
18026
18898
  if (!isRecord3(block)) return block;
18027
18899
  if (block["t"] === "image" && typeof block["data"] === "string" && block["$ref"] === void 0) {
18028
18900
  const bytes = Buffer.from(block["data"], "base64");
18029
- const hex = createHash9("sha256").update(bytes).digest("hex");
18901
+ const hex = createHash11("sha256").update(bytes).digest("hex");
18030
18902
  attachments[hex] = bytes.toString("base64");
18031
18903
  return { t: "image", mime: block["mime"], $ref: `sha256:${hex}` };
18032
18904
  }
@@ -18087,9 +18959,9 @@ function decodeCheckpointImport(bytes, target) {
18087
18959
  );
18088
18960
  }
18089
18961
  for (const [hex, b64] of Object.entries(bundle.attachments)) {
18090
- const bytesOf = Buffer.from(b64, "base64");
18091
- if (bytesOf.toString("base64") !== b64.replace(/\s+/g, "")) throw importInvalid(`附件 ${hex} 不是合法 base64`);
18092
- const digest = createHash9("sha256").update(bytesOf).digest("hex");
18962
+ const bytesOf2 = Buffer.from(b64, "base64");
18963
+ if (bytesOf2.toString("base64") !== b64.replace(/\s+/g, "")) throw importInvalid(`附件 ${hex} 不是合法 base64`);
18964
+ const digest = createHash11("sha256").update(bytesOf2).digest("hex");
18093
18965
  if (digest !== hex) throw importInvalid(`附件 ${hex} 字节摘要不符(实为 ${digest})`);
18094
18966
  }
18095
18967
  const messages = [];
@@ -18120,11 +18992,11 @@ function decodeCheckpointImport(bytes, target) {
18120
18992
  }
18121
18993
 
18122
18994
  // ../kernel/src/journal/rotate.ts
18123
- import { access as access4, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, rm as rm8, stat as stat5 } from "node:fs/promises";
18124
- import path11 from "node:path";
18995
+ import { access as access4, mkdir as mkdir7, readdir as readdir4, readFile as readFile9, rm as rm9, stat as stat6 } from "node:fs/promises";
18996
+ import path12 from "node:path";
18125
18997
  var ROTATION_NEXT_FILE_NAME = "journal.next.jsonl";
18126
18998
  function rotationNextPath(sessionDir) {
18127
- return path11.join(sessionDir, ROTATION_NEXT_FILE_NAME);
18999
+ return path12.join(sessionDir, ROTATION_NEXT_FILE_NAME);
18128
19000
  }
18129
19001
  async function fileExists2(filePath) {
18130
19002
  try {
@@ -18159,28 +19031,28 @@ async function moveTempAttachments(tempDir, sessionDir) {
18159
19031
  const tempAttachments = attachmentsDirPath(tempDir);
18160
19032
  let names = [];
18161
19033
  try {
18162
- names = await readdir3(tempAttachments);
19034
+ names = await readdir4(tempAttachments);
18163
19035
  } catch {
18164
19036
  names = [];
18165
19037
  }
18166
19038
  if (names.length === 0) return;
18167
19039
  const target = attachmentsDirPath(sessionDir);
18168
- await mkdir6(target, { recursive: true });
19040
+ await mkdir7(target, { recursive: true });
18169
19041
  for (const name of names) {
18170
- await renameWithRetry(path11.join(tempAttachments, name), path11.join(target, name));
19042
+ await renameWithRetry(path12.join(tempAttachments, name), path12.join(target, name));
18171
19043
  }
18172
19044
  }
18173
19045
  async function copyReferencedAttachments(tempDir, sessionDir, from) {
18174
19046
  const { records } = await readAll(tempDir, { rehydrateImages: false });
18175
19047
  const refs = collectImageRefs2(records.map((r) => r.payload));
18176
19048
  if (refs.length === 0) return;
18177
- await mkdir6(attachmentsDirPath(sessionDir), { recursive: true });
19049
+ await mkdir7(attachmentsDirPath(sessionDir), { recursive: true });
18178
19050
  for (const hex of refs) {
18179
19051
  const target = attachmentFilePath(sessionDir, hex);
18180
19052
  if (await fileExists2(target)) continue;
18181
19053
  let bytes;
18182
19054
  try {
18183
- bytes = await readFile8(attachmentFilePath(from, hex));
19055
+ bytes = await readFile9(attachmentFilePath(from, hex));
18184
19056
  } catch (err) {
18185
19057
  if (isErrnoException(err) && err.code === "ENOENT") continue;
18186
19058
  throw err;
@@ -18196,8 +19068,8 @@ async function settleVolume(sessionDir) {
18196
19068
  return true;
18197
19069
  }
18198
19070
  async function rotateVolume(sessionDir, build, opts = {}) {
18199
- const tempDir = path11.join(sessionDir, `.rotate-${process.pid}-${Date.now().toString(36)}`);
18200
- await mkdir6(tempDir, { recursive: true });
19071
+ const tempDir = path12.join(sessionDir, `.rotate-${process.pid}-${Date.now().toString(36)}`);
19072
+ await mkdir7(tempDir, { recursive: true });
18201
19073
  try {
18202
19074
  const writer = await JournalWriter.open(tempDir);
18203
19075
  let cursor;
@@ -18213,16 +19085,16 @@ async function rotateVolume(sessionDir, build, opts = {}) {
18213
19085
  }
18214
19086
  await renameWithRetry(journalFilePath(tempDir), rotationNextPath(sessionDir));
18215
19087
  await settleVolume(sessionDir);
18216
- const { mtimeMs } = await stat5(journalFilePath(sessionDir));
19088
+ const { mtimeMs } = await stat6(journalFilePath(sessionDir));
18217
19089
  return { ...cursor, mtimeMs };
18218
19090
  } finally {
18219
- await rm8(tempDir, { recursive: true, force: true }).catch(() => void 0);
19091
+ await rm9(tempDir, { recursive: true, force: true }).catch(() => void 0);
18220
19092
  }
18221
19093
  }
18222
19094
 
18223
19095
  // ../kernel/src/journal/fork.ts
18224
19096
  import { randomUUID as randomUUID7 } from "node:crypto";
18225
- import { access as access5, mkdir as mkdir7, readFile as readFile9, stat as stat6 } from "node:fs/promises";
19097
+ import { access as access5, mkdir as mkdir8, readFile as readFile10, stat as stat7 } from "node:fs/promises";
18226
19098
  var FORK_DEFAULT_PRODUCER = "kernel";
18227
19099
  function journalKindOf(message) {
18228
19100
  if (message.role === "assistant") return "assistant_message";
@@ -18258,13 +19130,13 @@ async function pathExists(filePath) {
18258
19130
  }
18259
19131
  async function copyRefAttachments(refs, from, sessionDir) {
18260
19132
  if (refs.length === 0) return;
18261
- await mkdir7(attachmentsDirPath(sessionDir), { recursive: true });
19133
+ await mkdir8(attachmentsDirPath(sessionDir), { recursive: true });
18262
19134
  for (const hex of refs) {
18263
19135
  const target = attachmentFilePath(sessionDir, hex);
18264
19136
  if (await pathExists(target)) continue;
18265
19137
  let bytes;
18266
19138
  try {
18267
- bytes = await readFile9(attachmentFilePath(from, hex));
19139
+ bytes = await readFile10(attachmentFilePath(from, hex));
18268
19140
  } catch (err) {
18269
19141
  if (isErrnoException(err) && err.code === "ENOENT") continue;
18270
19142
  throw err;
@@ -18359,27 +19231,27 @@ async function forkSessionFromCheckpoint(input) {
18359
19231
  } finally {
18360
19232
  await lock.release();
18361
19233
  }
18362
- const { mtimeMs } = await stat6(journalFilePath(sessionDir));
19234
+ const { mtimeMs } = await stat7(journalFilePath(sessionDir));
18363
19235
  return { sessionId, sessionDir, cursor: { ...cursor, mtimeMs } };
18364
19236
  }
18365
19237
 
18366
19238
  // ../kernel/src/session/cwd.ts
18367
- import { realpath, stat as stat7 } from "node:fs/promises";
18368
- import path12 from "node:path";
19239
+ import { realpath, stat as stat8 } from "node:fs/promises";
19240
+ import path13 from "node:path";
18369
19241
  var CASE_INSENSITIVE = process.platform === "win32";
18370
19242
  function insideRealSubtree(child, base) {
18371
19243
  const c = CASE_INSENSITIVE ? child.toLowerCase() : child;
18372
19244
  const b = CASE_INSENSITIVE ? base.toLowerCase() : base;
18373
- const rel = path12.relative(b, c);
18374
- return rel === "" || !rel.startsWith("..") && !path12.isAbsolute(rel);
19245
+ const rel = path13.relative(b, c);
19246
+ return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
18375
19247
  }
18376
19248
  async function probeDirectory(candidate) {
18377
- if (!path12.isAbsolute(candidate)) {
19249
+ if (!path13.isAbsolute(candidate)) {
18378
19250
  return { ok: false, reason: "not_absolute", detail: `cwd 须为绝对路径(不展开 ~):${candidate}` };
18379
19251
  }
18380
19252
  let isDirectory;
18381
19253
  try {
18382
- isDirectory = (await stat7(candidate)).isDirectory();
19254
+ isDirectory = (await stat8(candidate)).isDirectory();
18383
19255
  } catch (err) {
18384
19256
  const code = isErrnoException(err) ? err.code : void 0;
18385
19257
  return { ok: false, reason: "not_found", detail: `cwd 不可访问(${code ?? "io error"}):${candidate}` };
@@ -18407,7 +19279,7 @@ async function resolveSessionCwd(input) {
18407
19279
  };
18408
19280
  }
18409
19281
  if ("resolve" in policy) {
18410
- if (!path12.isAbsolute(requested)) {
19282
+ if (!path13.isAbsolute(requested)) {
18411
19283
  return { ok: false, reason: "not_absolute", detail: `cwd 须为绝对路径(不展开 ~):${requested}` };
18412
19284
  }
18413
19285
  let resolved;
@@ -18422,7 +19294,7 @@ async function resolveSessionCwd(input) {
18422
19294
  }
18423
19295
  const probed2 = await probeDirectory(resolved);
18424
19296
  if (!probed2.ok) return probed2;
18425
- return { ok: true, cwd: path12.resolve(resolved) };
19297
+ return { ok: true, cwd: path13.resolve(resolved) };
18426
19298
  }
18427
19299
  const probed = await probeDirectory(requested);
18428
19300
  if (!probed.ok) return probed;
@@ -18433,7 +19305,7 @@ async function resolveSessionCwd(input) {
18433
19305
  } catch {
18434
19306
  continue;
18435
19307
  }
18436
- if (insideRealSubtree(probed.real, realRoot)) return { ok: true, cwd: path12.resolve(requested) };
19308
+ if (insideRealSubtree(probed.real, realRoot)) return { ok: true, cwd: path13.resolve(requested) };
18437
19309
  }
18438
19310
  return {
18439
19311
  ok: false,
@@ -18443,7 +19315,7 @@ async function resolveSessionCwd(input) {
18443
19315
  }
18444
19316
 
18445
19317
  // ../kernel/src/tools/search/path-utils.ts
18446
- import * as path13 from "node:path";
19318
+ import * as path14 from "node:path";
18447
19319
  function toPosixPath(p) {
18448
19320
  return p.replaceAll("\\", "/");
18449
19321
  }
@@ -18451,10 +19323,10 @@ function normalizeDriveLetter(p) {
18451
19323
  return /^[A-Za-z]:/.test(p) ? p.charAt(0).toLowerCase() + p.slice(1) : p;
18452
19324
  }
18453
19325
  function isAbsolutePath(p) {
18454
- return path13.win32.isAbsolute(p) || path13.posix.isAbsolute(p);
19326
+ return path14.win32.isAbsolute(p) || path14.posix.isAbsolute(p);
18455
19327
  }
18456
19328
  function canonicalize(p) {
18457
- return normalizeDriveLetter(toPosixPath(path13.resolve(p)));
19329
+ return normalizeDriveLetter(toPosixPath(path14.resolve(p)));
18458
19330
  }
18459
19331
  function resolvePathArg(p, cwd) {
18460
19332
  const target = p ?? cwd;
@@ -18836,7 +19708,7 @@ var SESSION_EVENT_VOCABULARY = {
18836
19708
  var SESSION_EVENT_KINDS = Object.keys(SESSION_EVENT_VOCABULARY);
18837
19709
 
18838
19710
  // ../kernel/src/tools/files/read.ts
18839
- import path14 from "node:path";
19711
+ import path15 from "node:path";
18840
19712
  import { z as z18 } from "zod";
18841
19713
 
18842
19714
  // ../kernel/src/tools/files/encoding.ts
@@ -18918,19 +19790,19 @@ function truncateLine(line) {
18918
19790
  return `${line.slice(0, cut)}…[line truncated: ${line.length} chars total; use Grep to inspect the rest]`;
18919
19791
  }
18920
19792
  async function findSimilarFiles(fs3, filePath) {
18921
- const dir = path14.dirname(filePath);
18922
- const targetBase = path14.basename(filePath);
18923
- const targetName = path14.parse(filePath).name.toLowerCase();
19793
+ const dir = path15.dirname(filePath);
19794
+ const targetBase = path15.basename(filePath);
19795
+ const targetName = path15.parse(filePath).name.toLowerCase();
18924
19796
  try {
18925
19797
  const entries = await fs3.readdir(dir);
18926
19798
  return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).filter(
18927
- (name) => name.toLowerCase() !== targetBase.toLowerCase() && path14.parse(name).name.toLowerCase() === targetName
18928
- ).slice(0, 3).map((name) => path14.join(dir, name));
19799
+ (name) => name.toLowerCase() !== targetBase.toLowerCase() && path15.parse(name).name.toLowerCase() === targetName
19800
+ ).slice(0, 3).map((name) => path15.join(dir, name));
18929
19801
  } catch {
18930
19802
  return [];
18931
19803
  }
18932
19804
  }
18933
- async function executeImageRead(resolved, args, ctx, fs3, stat9, options) {
19805
+ async function executeImageRead(resolved, args, ctx, fs3, stat10, options) {
18934
19806
  let mode;
18935
19807
  try {
18936
19808
  mode = options.imageInput?.();
@@ -18949,9 +19821,9 @@ async function executeImageRead(resolved, args, ctx, fs3, stat9, options) {
18949
19821
  "offset/limit apply to text files only. Call Read again without offset/limit to read this image file."
18950
19822
  );
18951
19823
  }
18952
- if (stat9.size > READ_IMAGE_RAW_MAX_BYTES) {
19824
+ if (stat10.size > READ_IMAGE_RAW_MAX_BYTES) {
18953
19825
  return errorResult(
18954
- `Image file is ${stat9.size} bytes, which exceeds the per-image limit of ${READ_IMAGE_RAW_MAX_BYTES} bytes (5 MiB base64-encoded, aligned with the platform cap). Downscale or compress the image, then retry.`
19826
+ `Image file is ${stat10.size} bytes, which exceeds the per-image limit of ${READ_IMAGE_RAW_MAX_BYTES} bytes (5 MiB base64-encoded, aligned with the platform cap). Downscale or compress the image, then retry.`
18955
19827
  );
18956
19828
  }
18957
19829
  let buf;
@@ -18966,10 +19838,10 @@ async function executeImageRead(resolved, args, ctx, fs3, stat9, options) {
18966
19838
  const probed = probeImage(buf);
18967
19839
  if (probed === null) {
18968
19840
  return errorResult(
18969
- `File has an image extension but its content is not a readable PNG/JPEG/GIF/WebP image (bad or truncated header): ${path14.basename(resolved)}. If it is actually a text file, rename it; otherwise re-export the image and retry.`
19841
+ `File has an image extension but its content is not a readable PNG/JPEG/GIF/WebP image (bad or truncated header): ${path15.basename(resolved)}. If it is actually a text file, rename it; otherwise re-export the image and retry.`
18970
19842
  );
18971
19843
  }
18972
- registerFileRead(ctx, normalizeFileKey(resolved), stat9.mtimeMs);
19844
+ registerFileRead(ctx, normalizeFileKey(resolved), stat10.mtimeMs);
18973
19845
  const data = {
18974
19846
  path: resolved,
18975
19847
  mime: probed.mime,
@@ -18977,7 +19849,7 @@ async function executeImageRead(resolved, args, ctx, fs3, stat9, options) {
18977
19849
  height: probed.height,
18978
19850
  bytes: buf.length
18979
19851
  };
18980
- const meta = `Image file: ${path14.basename(resolved)}
19852
+ const meta = `Image file: ${path15.basename(resolved)}
18981
19853
  Format: ${probed.mime}
18982
19854
  Dimensions: ${probed.width}x${probed.height} px
18983
19855
  Size: ${buf.length} bytes`;
@@ -19000,8 +19872,8 @@ function createReadTool(options = {}) {
19000
19872
  isConcurrencySafe: true,
19001
19873
  touchedPathsOf(args) {
19002
19874
  const parsed = ReadArgsSchema.safeParse(args);
19003
- if (!parsed.success || !path14.isAbsolute(parsed.data.file_path)) return [];
19004
- return [path14.resolve(parsed.data.file_path)];
19875
+ if (!parsed.success || !path15.isAbsolute(parsed.data.file_path)) return [];
19876
+ return [path15.resolve(parsed.data.file_path)];
19005
19877
  },
19006
19878
  async execute(args, ctx) {
19007
19879
  if (ctx.signal.aborted) {
@@ -19015,15 +19887,15 @@ function createReadTool(options = {}) {
19015
19887
  if (invalid) {
19016
19888
  return invalid;
19017
19889
  }
19018
- const resolved = path14.resolve(args.file_path);
19890
+ const resolved = path15.resolve(args.file_path);
19019
19891
  const fs3 = fsOf(ctx);
19020
19892
  const realTarget = await checkRealTarget("Read", resolved, { cwd: ctx.cwd, access: "read", fs: fs3 });
19021
19893
  if (!realTarget.ok) {
19022
19894
  return errorResult(realTarget.reason);
19023
19895
  }
19024
- let stat9;
19896
+ let stat10;
19025
19897
  try {
19026
- stat9 = await fs3.stat(resolved);
19898
+ stat10 = await fs3.stat(resolved);
19027
19899
  } catch (err) {
19028
19900
  if (fsErrorCode(err) === "ENOENT") {
19029
19901
  const similar = await findSimilarFiles(fs3, resolved);
@@ -19032,11 +19904,11 @@ function createReadTool(options = {}) {
19032
19904
  }
19033
19905
  return errorResult(`Failed to read file: ${errorMessageOf(err)}`);
19034
19906
  }
19035
- if (stat9.isDirectory()) {
19907
+ if (stat10.isDirectory()) {
19036
19908
  return errorResult(`Path is a directory, not a file: ${resolved}.`);
19037
19909
  }
19038
- if (isImageFileExtension(path14.extname(resolved))) {
19039
- return executeImageRead(resolved, args, ctx, fs3, stat9, options);
19910
+ if (isImageFileExtension(path15.extname(resolved))) {
19911
+ return executeImageRead(resolved, args, ctx, fs3, stat10, options);
19040
19912
  }
19041
19913
  const fileKey = normalizeFileKey(resolved);
19042
19914
  const effectiveOffset = args.offset ?? 1;
@@ -19044,7 +19916,7 @@ function createReadTool(options = {}) {
19044
19916
  const dupWindow = takeDuplicateReadWindow(
19045
19917
  ctx,
19046
19918
  fileKey,
19047
- stat9.mtimeMs,
19919
+ stat10.mtimeMs,
19048
19920
  effectiveOffset,
19049
19921
  effectiveLimit
19050
19922
  );
@@ -19061,9 +19933,9 @@ function createReadTool(options = {}) {
19061
19933
  data2
19062
19934
  );
19063
19935
  }
19064
- if (stat9.size > READ_MAX_FULL_BYTES && args.offset === void 0 && args.limit === void 0) {
19936
+ if (stat10.size > READ_MAX_FULL_BYTES && args.offset === void 0 && args.limit === void 0) {
19065
19937
  return errorResult(
19066
- `File is ${stat9.size} bytes, which exceeds the ${READ_MAX_FULL_BYTES}-byte limit for reading the whole file at once (large files inflate context: this one would be roughly ${Math.ceil(stat9.size / 4)}+ tokens). Use the offset and limit parameters to read it in pages, or use Grep to locate the relevant sections first.`
19938
+ `File is ${stat10.size} bytes, which exceeds the ${READ_MAX_FULL_BYTES}-byte limit for reading the whole file at once (large files inflate context: this one would be roughly ${Math.ceil(stat10.size / 4)}+ tokens). Use the offset and limit parameters to read it in pages, or use Grep to locate the relevant sections first.`
19067
19939
  );
19068
19940
  }
19069
19941
  let buf;
@@ -19082,7 +19954,7 @@ function createReadTool(options = {}) {
19082
19954
  );
19083
19955
  }
19084
19956
  if (decoded.text.length === 0) {
19085
- registerFileRead(ctx, fileKey, stat9.mtimeMs, {
19957
+ registerFileRead(ctx, fileKey, stat10.mtimeMs, {
19086
19958
  offset: effectiveOffset,
19087
19959
  limit: effectiveLimit,
19088
19960
  totalLines: 0,
@@ -19096,7 +19968,7 @@ function createReadTool(options = {}) {
19096
19968
  const startLine = effectiveOffset;
19097
19969
  const maxLines = effectiveLimit;
19098
19970
  if (startLine > totalLines) {
19099
- registerFileRead(ctx, fileKey, stat9.mtimeMs, {
19971
+ registerFileRead(ctx, fileKey, stat10.mtimeMs, {
19100
19972
  offset: effectiveOffset,
19101
19973
  limit: effectiveLimit,
19102
19974
  totalLines,
@@ -19115,7 +19987,7 @@ function createReadTool(options = {}) {
19115
19987
  text += `
19116
19988
  … (showing lines ${startLine}-${endLine} of ${totalLines}; use offset=${endLine + 1} to continue)`;
19117
19989
  }
19118
- registerFileRead(ctx, fileKey, stat9.mtimeMs, {
19990
+ registerFileRead(ctx, fileKey, stat10.mtimeMs, {
19119
19991
  offset: effectiveOffset,
19120
19992
  limit: effectiveLimit,
19121
19993
  totalLines,
@@ -19129,7 +20001,7 @@ function createReadTool(options = {}) {
19129
20001
  var ReadTool = createReadTool();
19130
20002
 
19131
20003
  // ../kernel/src/tools/files/write.ts
19132
- import path15 from "node:path";
20004
+ import path16 from "node:path";
19133
20005
  import { z as z19 } from "zod";
19134
20006
  var WriteArgsSchema = z19.object({
19135
20007
  file_path: z19.string().describe("Absolute path to the file to write. Relative paths are rejected."),
@@ -19148,8 +20020,8 @@ var WriteTool = {
19148
20020
  isConcurrencySafe: false,
19149
20021
  mutatedPathsOf(args) {
19150
20022
  const parsed = WriteArgsSchema.safeParse(args);
19151
- if (!parsed.success || !path15.isAbsolute(parsed.data.file_path)) return [];
19152
- return [path15.resolve(parsed.data.file_path)];
20023
+ if (!parsed.success || !path16.isAbsolute(parsed.data.file_path)) return [];
20024
+ return [path16.resolve(parsed.data.file_path)];
19153
20025
  },
19154
20026
  async execute(args, ctx) {
19155
20027
  if (ctx.signal.aborted) {
@@ -19163,7 +20035,7 @@ var WriteTool = {
19163
20035
  if (invalid) {
19164
20036
  return invalid;
19165
20037
  }
19166
- const resolved = path15.resolve(args.file_path);
20038
+ const resolved = path16.resolve(args.file_path);
19167
20039
  const key2 = normalizeFileKey(resolved);
19168
20040
  const fs3 = fsOf(ctx);
19169
20041
  const realTarget = await checkRealTarget("Write", resolved, {
@@ -19205,7 +20077,7 @@ var WriteTool = {
19205
20077
  }
19206
20078
  } else {
19207
20079
  try {
19208
- await fs3.mkdir(path15.dirname(resolved), { recursive: true });
20080
+ await fs3.mkdir(path16.dirname(resolved), { recursive: true });
19209
20081
  } catch (err) {
19210
20082
  return errorResult(`Failed to create parent directories: ${errorMessageOf(err)}`);
19211
20083
  }
@@ -19261,7 +20133,7 @@ ${memoryNearLimitNote(health)}`, data);
19261
20133
  };
19262
20134
 
19263
20135
  // ../kernel/src/tools/files/edit.ts
19264
- import path16 from "node:path";
20136
+ import path17 from "node:path";
19265
20137
  import { z as z20 } from "zod";
19266
20138
  var EditArgsSchema = z20.object({
19267
20139
  file_path: z20.string().describe("Absolute path to the file to edit. Relative paths are rejected."),
@@ -19304,8 +20176,8 @@ var EditTool = {
19304
20176
  isConcurrencySafe: false,
19305
20177
  mutatedPathsOf(args) {
19306
20178
  const parsed = EditArgsSchema.safeParse(args);
19307
- if (!parsed.success || !path16.isAbsolute(parsed.data.file_path)) return [];
19308
- return [path16.resolve(parsed.data.file_path)];
20179
+ if (!parsed.success || !path17.isAbsolute(parsed.data.file_path)) return [];
20180
+ return [path17.resolve(parsed.data.file_path)];
19309
20181
  },
19310
20182
  async execute(args, ctx) {
19311
20183
  if (ctx.signal.aborted) {
@@ -19319,7 +20191,7 @@ var EditTool = {
19319
20191
  if (invalid) {
19320
20192
  return invalid;
19321
20193
  }
19322
- const resolved = path16.resolve(args.file_path);
20194
+ const resolved = path17.resolve(args.file_path);
19323
20195
  const key2 = normalizeFileKey(resolved);
19324
20196
  const fs3 = fsOf(ctx);
19325
20197
  const realTarget = await checkRealTarget("Edit", resolved, {
@@ -19330,19 +20202,19 @@ var EditTool = {
19330
20202
  if (!realTarget.ok) {
19331
20203
  return errorResult(realTarget.reason);
19332
20204
  }
19333
- let stat9;
20205
+ let stat10;
19334
20206
  try {
19335
- stat9 = await fs3.stat(resolved);
20207
+ stat10 = await fs3.stat(resolved);
19336
20208
  } catch (err) {
19337
20209
  if (fsErrorCode(err) === "ENOENT") {
19338
20210
  return errorResult(`File does not exist: ${resolved}. Use the Write tool to create a new file.`);
19339
20211
  }
19340
20212
  return errorResult(`Failed to access file: ${errorMessageOf(err)}`);
19341
20213
  }
19342
- if (stat9.isDirectory()) {
20214
+ if (stat10.isDirectory()) {
19343
20215
  return errorResult(`Path is a directory, not a file: ${resolved}.`);
19344
20216
  }
19345
- const guard = checkStaleWriteGuard(ctx, key2, stat9.mtimeMs, "editing");
20217
+ const guard = checkStaleWriteGuard(ctx, key2, stat10.mtimeMs, "editing");
19346
20218
  if (guard) {
19347
20219
  return guard;
19348
20220
  }
@@ -19447,7 +20319,7 @@ ${memoryNearLimitNote(health)}`, data);
19447
20319
 
19448
20320
  // ../kernel/src/tools/search/glob-tool.ts
19449
20321
  import { z as z21 } from "zod";
19450
- import path17 from "node:path";
20322
+ import path18 from "node:path";
19451
20323
 
19452
20324
  // ../kernel/src/tools/search/walker.ts
19453
20325
  var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
@@ -19541,7 +20413,7 @@ var globTool = {
19541
20413
  return errorResult3(`path does not exist: ${root}`);
19542
20414
  }
19543
20415
  if (!rootStat.isDirectory()) return errorResult3(`path is not a directory: ${root}`);
19544
- const realRoot = await checkRealTarget("Glob", path17.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
20416
+ const realRoot = await checkRealTarget("Glob", path18.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
19545
20417
  if (!realRoot.ok) return errorResult3(realRoot.reason);
19546
20418
  let matcher;
19547
20419
  try {
@@ -19580,7 +20452,7 @@ var globTool = {
19580
20452
 
19581
20453
  // ../kernel/src/tools/search/grep-tool.ts
19582
20454
  import { z as z22 } from "zod";
19583
- import path18 from "node:path";
20455
+ import path19 from "node:path";
19584
20456
 
19585
20457
  // ../kernel/src/tools/search/js-engine.ts
19586
20458
  var GREP_MAX_FILE_SIZE = 4 * 1024 * 1024;
@@ -20054,7 +20926,7 @@ var grepTool = {
20054
20926
  return finish(errorResult4(`path does not exist: ${root}`));
20055
20927
  }
20056
20928
  if (!rootStat.isDirectory()) return finish(errorResult4(`path is not a directory: ${root}`));
20057
- const realRoot = await checkRealTarget("Grep", path18.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
20929
+ const realRoot = await checkRealTarget("Grep", path19.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
20058
20930
  if (!realRoot.ok) return finish(errorResult4(realRoot.reason));
20059
20931
  try {
20060
20932
  new RegExp(args.pattern, args.case_insensitive ? "i" : "");
@@ -20139,7 +21011,7 @@ var grepTool = {
20139
21011
 
20140
21012
  // ../kernel/src/tools/search/list-tool.ts
20141
21013
  import { z as z23 } from "zod";
20142
- import path19 from "node:path";
21014
+ import path20 from "node:path";
20143
21015
  var LIST_MAX_ENTRIES = 500;
20144
21016
  var ListArgsSchema = z23.object({
20145
21017
  path: z23.string().min(1),
@@ -20169,7 +21041,7 @@ var listTool = {
20169
21041
  touchedPathsOf(args) {
20170
21042
  const parsed = ListArgsSchema.safeParse(args);
20171
21043
  if (!parsed.success || !isAbsolutePath(parsed.data.path)) return [];
20172
- return [path19.resolve(parsed.data.path)];
21044
+ return [path20.resolve(parsed.data.path)];
20173
21045
  },
20174
21046
  async execute(args, ctx) {
20175
21047
  if (ctx.signal.aborted) return errorResult5("aborted");
@@ -20183,7 +21055,7 @@ var listTool = {
20183
21055
  return errorResult5(`path does not exist: ${root}`);
20184
21056
  }
20185
21057
  if (!rootStat.isDirectory()) return errorResult5(`path is not a directory: ${root}`);
20186
- const realRoot = await checkRealTarget("List", path19.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
21058
+ const realRoot = await checkRealTarget("List", path20.resolve(root), { cwd: ctx.cwd, access: "read", fs: fs3 });
20187
21059
  if (!realRoot.ok) return errorResult5(realRoot.reason);
20188
21060
  let ignoreMatchers = [];
20189
21061
  try {
@@ -20713,8 +21585,8 @@ ${tail}`;
20713
21585
  };
20714
21586
 
20715
21587
  // ../kernel/src/tools/shell/output-file.ts
20716
- import { createWriteStream as createWriteStream2, mkdirSync } from "node:fs";
20717
- import * as path20 from "node:path";
21588
+ import { createWriteStream as createWriteStream3, mkdirSync } from "node:fs";
21589
+ import * as path21 from "node:path";
20718
21590
  var SHELL_OUTPUT_SPILL_THRESHOLD_CHARS = 5e4;
20719
21591
  var SHELL_OUTPUT_DIR_NAME = "shell-output";
20720
21592
  function sanitizeFileStem(stem) {
@@ -20723,7 +21595,7 @@ function sanitizeFileStem(stem) {
20723
21595
  }
20724
21596
  function shellOutputFilePath(sessionCwd, toolCallId) {
20725
21597
  const stem = toolCallId !== void 0 && toolCallId.trim().length > 0 ? sanitizeFileStem(toolCallId) : `shell-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
20726
- return path20.join(sessionCwd, ".tansr", SHELL_OUTPUT_DIR_NAME, `${stem}.log`);
21598
+ return path21.join(sessionCwd, ".tansr", SHELL_OUTPUT_DIR_NAME, `${stem}.log`);
20727
21599
  }
20728
21600
  var ShellOutputSink = class {
20729
21601
  filePath;
@@ -20759,8 +21631,8 @@ var ShellOutputSink = class {
20759
21631
  forceOpen() {
20760
21632
  if (this.#stream !== void 0 || this.#writeError !== void 0 || this.#closed) return;
20761
21633
  try {
20762
- mkdirSync(path20.dirname(this.filePath), { recursive: true });
20763
- const stream = createWriteStream2(this.filePath, { encoding: "utf8" });
21634
+ mkdirSync(path21.dirname(this.filePath), { recursive: true });
21635
+ const stream = createWriteStream3(this.filePath, { encoding: "utf8" });
20764
21636
  stream.on("error", (err) => {
20765
21637
  this.#writeError = err.message;
20766
21638
  this.#stream = void 0;
@@ -23188,7 +24060,7 @@ function applyToolOverrides(filtered, overrides) {
23188
24060
  }
23189
24061
 
23190
24062
  // ../kernel/src/agents/subagent-memory-fence.ts
23191
- import path21 from "node:path";
24063
+ import path22 from "node:path";
23192
24064
  function subagentMemoryFenceOf(ctx) {
23193
24065
  if (ctx === void 0 || ctx.dirs.length === 0) return void 0;
23194
24066
  return {
@@ -23203,7 +24075,7 @@ function writeTargetOf(args, cwd) {
23203
24075
  if (args === null || typeof args !== "object") return null;
23204
24076
  const raw = args.file_path;
23205
24077
  if (typeof raw !== "string" || raw.length === 0) return null;
23206
- return path21.isAbsolute(raw) ? path21.resolve(raw) : path21.resolve(cwd, raw);
24078
+ return path22.isAbsolute(raw) ? path22.resolve(raw) : path22.resolve(cwd, raw);
23207
24079
  }
23208
24080
  function fencedSubagentWriteTool(tool, fence) {
23209
24081
  return {
@@ -23507,10 +24379,10 @@ async function runSubagent(options) {
23507
24379
  }
23508
24380
 
23509
24381
  // ../kernel/src/tools/task/background.ts
23510
- import * as path22 from "node:path";
24382
+ import * as path23 from "node:path";
23511
24383
  var TASK_AGENT_OUTPUT_DIR_NAME = "agent-output";
23512
24384
  function taskAgentOutputFilePath(sessionCwd, agentId) {
23513
- return path22.join(sessionCwd, ".tansr", TASK_AGENT_OUTPUT_DIR_NAME, `${sanitizeFileStem(agentId)}.md`);
24385
+ return path23.join(sessionCwd, ".tansr", TASK_AGENT_OUTPUT_DIR_NAME, `${sanitizeFileStem(agentId)}.md`);
23514
24386
  }
23515
24387
  function taskSettlementStopReason(reason, aborted) {
23516
24388
  if (aborted) return "aborted";
@@ -23646,35 +24518,35 @@ function resolveSubagentDispatch(input) {
23646
24518
  return { kind: "inherit", reason: "economy_unavailable" };
23647
24519
  }
23648
24520
  function assertDispatchPolicyInvariants() {
23649
- const fail = (detail) => {
24521
+ const fail2 = (detail) => {
23650
24522
  throw new Error(`dispatch-policy 不变式违约(装载期 fail-fast):${detail}`);
23651
24523
  };
23652
24524
  const tierList = SUBAGENT_DISPATCH_TIERS;
23653
24525
  const defaultTier = DEFAULT_SUBAGENT_DISPATCH_TIER;
23654
- if (tierList.length === 0) fail("档位词表不得为空");
24526
+ if (tierList.length === 0) fail2("档位词表不得为空");
23655
24527
  if (new Set(tierList).size !== tierList.length) {
23656
- fail("档位词表存在重复项");
24528
+ fail2("档位词表存在重复项");
23657
24529
  }
23658
24530
  if (!tierList.includes("inherit")) {
23659
- fail("档位词表必含 'inherit'(fail-safe 锚:未知类型/回落链尽头的现状档)");
24531
+ fail2("档位词表必含 'inherit'(fail-safe 锚:未知类型/回落链尽头的现状档)");
23660
24532
  }
23661
24533
  if (defaultTier !== "inherit") {
23662
- fail(
24534
+ fail2(
23663
24535
  `缺省档位必须为 'inherit'(新增/未知类型恒不降档的 fail-safe 语义),得到 '${defaultTier}'`
23664
24536
  );
23665
24537
  }
23666
24538
  const tiers = new Set(SUBAGENT_DISPATCH_TIERS);
23667
24539
  for (const [name, tier] of Object.entries(SUBAGENT_TYPE_TIER_MAP)) {
23668
24540
  if (!AGENT_NAME_RE.test(name)) {
23669
- fail(`映射表键 '${name}' 不符合代理名约束(${String(AGENT_NAME_RE)})`);
24541
+ fail2(`映射表键 '${name}' 不符合代理名约束(${String(AGENT_NAME_RE)})`);
23670
24542
  }
23671
24543
  if (!tiers.has(tier)) {
23672
- fail(`映射表值 '${tier}'(键 '${name}')不在档位词表内`);
24544
+ fail2(`映射表值 '${tier}'(键 '${name}')不在档位词表内`);
23673
24545
  }
23674
24546
  }
23675
24547
  for (const template of BUILTIN_AGENT_TEMPLATES) {
23676
24548
  if (SUBAGENT_TYPE_TIER_MAP[template.name] === void 0) {
23677
- fail(
24549
+ fail2(
23678
24550
  `内置模板 '${template.name}' 未在 SUBAGENT_TYPE_TIER_MAP 显式登记档位——新增内置类型必须做一次显式档位决策(fail-safe 建制)`
23679
24551
  );
23680
24552
  }
@@ -25294,8 +26166,8 @@ function suggestedAudioName(audio) {
25294
26166
  async function sinkInlineAudio(sink, audio) {
25295
26167
  try {
25296
26168
  const bytes = new Uint8Array(Buffer.from(audio.b64, "base64"));
25297
- const path28 = await sink(bytes, audio.mime, suggestedAudioName(audio));
25298
- return { path: path28 };
26169
+ const path29 = await sink(bytes, audio.mime, suggestedAudioName(audio));
26170
+ return { path: path29 };
25299
26171
  } catch (err) {
25300
26172
  return { error: err instanceof Error ? err.message : String(err) };
25301
26173
  }
@@ -25407,7 +26279,7 @@ import { spawnSync as spawnSync2 } from "node:child_process";
25407
26279
 
25408
26280
  // ../kernel/src/tools/mcp/win32-spawn.ts
25409
26281
  import fs2 from "node:fs";
25410
- import path23 from "node:path";
26282
+ import path24 from "node:path";
25411
26283
  var CMD_META_CHARS = /([()\][%!^"`<>&|;, *?])/g;
25412
26284
  var SPAWNABLE_EXTS = [".COM", ".EXE", ".BAT", ".CMD"];
25413
26285
  function escapeCmdCommand(command) {
@@ -25446,7 +26318,7 @@ function defaultFileExists(filePath) {
25446
26318
  }
25447
26319
  }
25448
26320
  function resolveCommandFile(command, env, cwd, fileExists3) {
25449
- const w = path23.win32;
26321
+ const w = path24.win32;
25450
26322
  const exts = spawnableExts(envLookup(env, "PATHEXT"));
25451
26323
  const bases = [];
25452
26324
  if (command.includes("/") || command.includes("\\")) {
@@ -25480,7 +26352,7 @@ function wrapWithComSpec(target, args, env) {
25480
26352
  function planStdioSpawn(command, args, options) {
25481
26353
  const platform = options.platform ?? process.platform;
25482
26354
  if (platform !== "win32") return { file: command, args };
25483
- const ext = path23.win32.extname(command).toUpperCase();
26355
+ const ext = path24.win32.extname(command).toUpperCase();
25484
26356
  if (ext === ".EXE" || ext === ".COM") return { file: command, args };
25485
26357
  if (ext === ".CMD" || ext === ".BAT") return wrapWithComSpec(command, args, options.env);
25486
26358
  if (ext !== "") return { file: command, args };
@@ -25488,7 +26360,7 @@ function planStdioSpawn(command, args, options) {
25488
26360
  const fileExists3 = options.fileExists ?? defaultFileExists;
25489
26361
  const resolved = resolveCommandFile(command, options.env, cwd, fileExists3);
25490
26362
  if (resolved === void 0) return { file: command, args };
25491
- const resolvedExt = path23.win32.extname(resolved).toUpperCase();
26363
+ const resolvedExt = path24.win32.extname(resolved).toUpperCase();
25492
26364
  if (resolvedExt === ".CMD" || resolvedExt === ".BAT") {
25493
26365
  return wrapWithComSpec(resolved, args, options.env);
25494
26366
  }
@@ -26827,7 +27699,7 @@ async function connectHttpMcpServer(config, options) {
26827
27699
  }
26828
27700
 
26829
27701
  // ../kernel/src/tools/mcp/bridge.ts
26830
- import { createHash as createHash10 } from "node:crypto";
27702
+ import { createHash as createHash12 } from "node:crypto";
26831
27703
  import { z as z40 } from "zod";
26832
27704
  var MCP_TOOL_NAME_PREFIX = "mcp__";
26833
27705
  var MCP_REMOTE_TOOL_NAME_RE = /^[A-Za-z0-9_.-]{1,128}$/;
@@ -26835,7 +27707,7 @@ var MAX_LIST_PAGES = 64;
26835
27707
  function buildMcpToolName(serverName, toolName2) {
26836
27708
  const full = `${MCP_TOOL_NAME_PREFIX}${serverName}__${toolName2}`;
26837
27709
  if (full.length <= MCP_BRIDGED_TOOL_NAME_MAX_CHARS) return full;
26838
- const hash = createHash10("sha256").update(full, "utf8").digest("hex").slice(0, MCP_NAME_FOLD_HASH_CHARS);
27710
+ const hash = createHash12("sha256").update(full, "utf8").digest("hex").slice(0, MCP_NAME_FOLD_HASH_CHARS);
26839
27711
  const prefix = `${MCP_TOOL_NAME_PREFIX}${serverName}__`;
26840
27712
  const tailBudget = MCP_BRIDGED_TOOL_NAME_MAX_CHARS - prefix.length;
26841
27713
  if (tailBudget >= MCP_NAME_FOLD_HASH_CHARS + 1) {
@@ -27724,15 +28596,15 @@ var McpConnectionPool = class {
27724
28596
  * `mcp.closed(server_exit|error)` → `mcp.reconnecting`(RFC-SC-3 R4:重连帧
27725
28597
  * 不携原因,原因在**前帧** closed / connect_failed;SC-45 直透卷钉住此序)。
27726
28598
  */
27727
- #observerFor(slot, link) {
28599
+ #observerFor(slot, link2) {
27728
28600
  return (event) => {
27729
28601
  try {
27730
28602
  this.#onEvent?.(event);
27731
28603
  } finally {
27732
28604
  if (event.type === "mcp.closed" && event.server === this.serverName) {
27733
- link.dead = true;
27734
- if (event.reason !== "closed" && link.connection !== void 0) {
27735
- this.#memberDown(slot, link.connection);
28605
+ link2.dead = true;
28606
+ if (event.reason !== "closed" && link2.connection !== void 0) {
28607
+ this.#memberDown(slot, link2.connection);
27736
28608
  }
27737
28609
  }
27738
28610
  }
@@ -27823,12 +28695,12 @@ var McpConnectionPool = class {
27823
28695
  */
27824
28696
  async #attempt(slot, reason, automatic) {
27825
28697
  const attemptStartedAt = this.#now();
27826
- const link = { connection: void 0, dead: false };
28698
+ const link2 = { connection: void 0, dead: false };
27827
28699
  let connection;
27828
28700
  try {
27829
- connection = await this.#connectMember(slot.index, this.#observerFor(slot, link));
27830
- link.connection = connection;
27831
- if (link.dead || isClosedConnection(connection)) {
28701
+ connection = await this.#connectMember(slot.index, this.#observerFor(slot, link2));
28702
+ link2.connection = connection;
28703
+ if (link2.dead || isClosedConnection(connection)) {
27832
28704
  throw new McpError("transport_closed", `MCP server "${this.serverName}" closed the connection during setup.`, {
27833
28705
  server: this.serverName
27834
28706
  });
@@ -27836,7 +28708,7 @@ var McpConnectionPool = class {
27836
28708
  if (this.#onMemberUp !== void 0) {
27837
28709
  await this.#onMemberUp({ slot: slot.index, connection, reason });
27838
28710
  }
27839
- if (link.dead || isClosedConnection(connection)) {
28711
+ if (link2.dead || isClosedConnection(connection)) {
27840
28712
  throw new McpError("transport_closed", `MCP server "${this.serverName}" closed the connection during discovery.`, {
27841
28713
  server: this.serverName
27842
28714
  });
@@ -27847,7 +28719,7 @@ var McpConnectionPool = class {
27847
28719
  return { ok: false, error: this.#closedError() };
27848
28720
  }
27849
28721
  slot.connection = connection;
27850
- slot.link = link;
28722
+ slot.link = link2;
27851
28723
  slot.generation += 1;
27852
28724
  slot.connectedAt = this.#now();
27853
28725
  slot.exhausted = false;
@@ -27980,7 +28852,7 @@ function resolveMcpServerGovernance(config, layers = {}) {
27980
28852
  }
27981
28853
 
27982
28854
  // ../kernel/src/tools/mcp/catalog-store.ts
27983
- import { createHash as createHash11 } from "node:crypto";
28855
+ import { createHash as createHash13 } from "node:crypto";
27984
28856
  var MCP_CATALOG_STORE_VERSION = 1;
27985
28857
  function stableStringify2(value) {
27986
28858
  if (value === null || typeof value !== "object") {
@@ -27994,10 +28866,10 @@ function stableStringify2(value) {
27994
28866
  return `{${parts.join(",")}}`;
27995
28867
  }
27996
28868
  function computeMcpServerConfigFingerprint(config) {
27997
- return createHash11("sha256").update(stableStringify2(config), "utf8").digest("hex");
28869
+ return createHash13("sha256").update(stableStringify2(config), "utf8").digest("hex");
27998
28870
  }
27999
28871
  function computeMcpToolDefinitionHash(tool) {
28000
- return createHash11("sha256").update(
28872
+ return createHash13("sha256").update(
28001
28873
  stableStringify2({
28002
28874
  name: tool.name,
28003
28875
  description: tool.description,
@@ -28611,7 +29483,7 @@ async function initMcpLazyManager(toolset, servers, options = {}) {
28611
29483
  import os3 from "node:os";
28612
29484
 
28613
29485
  // ../kernel/src/hooks/config.ts
28614
- import { createHash as createHash12 } from "node:crypto";
29486
+ import { createHash as createHash14 } from "node:crypto";
28615
29487
 
28616
29488
  // ../kernel/src/config/merge.ts
28617
29489
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
@@ -28641,12 +29513,12 @@ function deepMergeRawLayers(rawsAscending) {
28641
29513
  }
28642
29514
  return structuredClone(acc);
28643
29515
  }
28644
- function displayPath2(path28) {
28645
- return path28.map(String).join(".");
29516
+ function displayPath2(path29) {
29517
+ return path29.map(String).join(".");
28646
29518
  }
28647
- function getAtPath(root, path28) {
29519
+ function getAtPath(root, path29) {
28648
29520
  let cur = root;
28649
- for (const seg of path28) {
29521
+ for (const seg of path29) {
28650
29522
  if (typeof seg === "number") {
28651
29523
  if (!Array.isArray(cur) || seg < 0 || seg >= cur.length) return void 0;
28652
29524
  cur = cur[seg];
@@ -28657,11 +29529,11 @@ function getAtPath(root, path28) {
28657
29529
  }
28658
29530
  return cur;
28659
29531
  }
28660
- function hasPath(root, path28) {
28661
- if (path28.length === 0) return true;
29532
+ function hasPath(root, path29) {
29533
+ if (path29.length === 0) return true;
28662
29534
  let cur = root;
28663
- for (let i = 0; i < path28.length; i++) {
28664
- const seg = path28[i];
29535
+ for (let i = 0; i < path29.length; i++) {
29536
+ const seg = path29[i];
28665
29537
  if (typeof seg === "number") {
28666
29538
  if (!Array.isArray(cur) || seg < 0 || seg >= cur.length) return false;
28667
29539
  cur = cur[seg];
@@ -28672,25 +29544,25 @@ function hasPath(root, path28) {
28672
29544
  }
28673
29545
  return true;
28674
29546
  }
28675
- function sanitizeDropPath(root, path28) {
29547
+ function sanitizeDropPath(root, path29) {
28676
29548
  let cur = root;
28677
- for (let i = 0; i < path28.length; i++) {
28678
- const seg = path28[i];
29549
+ for (let i = 0; i < path29.length; i++) {
29550
+ const seg = path29[i];
28679
29551
  const next = getAtPath(cur, [seg]);
28680
- if (Array.isArray(next) && i < path28.length - 1) {
28681
- return path28.slice(0, i + 1);
29552
+ if (Array.isArray(next) && i < path29.length - 1) {
29553
+ return path29.slice(0, i + 1);
28682
29554
  }
28683
- if (typeof path28[i + 1] === "number") {
28684
- return path28.slice(0, i + 1);
29555
+ if (typeof path29[i + 1] === "number") {
29556
+ return path29.slice(0, i + 1);
28685
29557
  }
28686
29558
  cur = next;
28687
29559
  }
28688
- return path28;
29560
+ return path29;
28689
29561
  }
28690
- function deletePath(root, path28) {
28691
- if (path28.length === 0) return;
28692
- const parent = getAtPath(root, path28.slice(0, -1));
28693
- const leaf = path28[path28.length - 1];
29562
+ function deletePath(root, path29) {
29563
+ if (path29.length === 0) return;
29564
+ const parent = getAtPath(root, path29.slice(0, -1));
29565
+ const leaf = path29[path29.length - 1];
28694
29566
  if (isPlainObject2(parent) && typeof leaf === "string") {
28695
29567
  delete parent[leaf];
28696
29568
  }
@@ -28780,16 +29652,16 @@ function canonicalJson(value) {
28780
29652
  return JSON.stringify(value) ?? "null";
28781
29653
  }
28782
29654
  function configHash8(dedupeKey) {
28783
- return createHash12("sha256").update(dedupeKey, "utf8").digest("hex").slice(0, 8);
29655
+ return createHash14("sha256").update(dedupeKey, "utf8").digest("hex").slice(0, 8);
28784
29656
  }
28785
- function parseExecutor(value, layer, path28, file, addDiagnostic) {
29657
+ function parseExecutor(value, layer, path29, file, addDiagnostic) {
28786
29658
  const fileRef = file !== void 0 ? { file } : {};
28787
29659
  const invalid = (params) => {
28788
29660
  addDiagnostic({
28789
29661
  severity: "error",
28790
29662
  code: "hook_invalid_value",
28791
29663
  layer,
28792
- path: path28,
29664
+ path: path29,
28793
29665
  ...fileRef,
28794
29666
  params
28795
29667
  });
@@ -28837,7 +29709,7 @@ function parseExecutor(value, layer, path28, file, addDiagnostic) {
28837
29709
  severity: "error",
28838
29710
  code: "hook_url_invalid",
28839
29711
  layer,
28840
- path: `${path28}.url`,
29712
+ path: `${path29}.url`,
28841
29713
  ...fileRef,
28842
29714
  params: { reason: "unparsable" }
28843
29715
  });
@@ -28848,7 +29720,7 @@ function parseExecutor(value, layer, path28, file, addDiagnostic) {
28848
29720
  severity: "error",
28849
29721
  code: "hook_url_invalid",
28850
29722
  layer,
28851
- path: `${path28}.url`,
29723
+ path: `${path29}.url`,
28852
29724
  ...fileRef,
28853
29725
  params: { reason: "unsupported_protocol", protocol: parsed.protocol }
28854
29726
  });
@@ -29439,7 +30311,7 @@ function migrateLayerRaw(raw) {
29439
30311
 
29440
30312
  // ../kernel/src/config/paths.ts
29441
30313
  import { promises as fsPromises } from "node:fs";
29442
- import path24 from "node:path";
30314
+ import path25 from "node:path";
29443
30315
  var SETTINGS_FILE_NAME = "settings.json";
29444
30316
  var LOCAL_SETTINGS_FILE_NAME = "settings.local.json";
29445
30317
  var TANSR_DIR_NAME = ".tansr";
@@ -29466,27 +30338,27 @@ function normalizeForCompare(p, platform) {
29466
30338
  function defaultManagedPaths(platform, env) {
29467
30339
  if (platform === "win32") {
29468
30340
  const programData = readTrimmedEnv2(env, "PROGRAMDATA") ?? "C:\\ProgramData";
29469
- return [path24.join(programData, "tansr", MANAGED_SETTINGS_FILE_NAME)];
30341
+ return [path25.join(programData, "tansr", MANAGED_SETTINGS_FILE_NAME)];
29470
30342
  }
29471
- return [path24.posix.join("/etc", "tansr", MANAGED_SETTINGS_FILE_NAME)];
30343
+ return [path25.posix.join("/etc", "tansr", MANAGED_SETTINGS_FILE_NAME)];
29472
30344
  }
29473
30345
  async function discoverLayerFiles(options) {
29474
30346
  const { fs: fs3, env, platform } = options;
29475
30347
  const configDirOverride = readTrimmedEnv2(env, "TANSR_CONFIG_DIR");
29476
- const userDir = configDirOverride ?? path24.join(options.homedir, TANSR_DIR_NAME);
29477
- const userFile = path24.join(userDir, SETTINGS_FILE_NAME);
29478
- const homeKey = normalizeForCompare(path24.resolve(options.homedir), platform);
30348
+ const userDir = configDirOverride ?? path25.join(options.homedir, TANSR_DIR_NAME);
30349
+ const userFile = path25.join(userDir, SETTINGS_FILE_NAME);
30350
+ const homeKey = normalizeForCompare(path25.resolve(options.homedir), platform);
29479
30351
  let projectRoot;
29480
- const boundary = options.boundary === void 0 ? void 0 : normalizeForCompare(path24.resolve(options.boundary), platform);
29481
- let cursor = path24.resolve(options.cwd);
30352
+ const boundary = options.boundary === void 0 ? void 0 : normalizeForCompare(path25.resolve(options.boundary), platform);
30353
+ let cursor = path25.resolve(options.cwd);
29482
30354
  for (let depth = 0; depth < 256; depth++) {
29483
30355
  const homeAnchorInvisible = normalizeForCompare(cursor, platform) === homeKey;
29484
- if (!homeAnchorInvisible && await fs3.directoryExists(path24.join(cursor, TANSR_DIR_NAME))) {
30356
+ if (!homeAnchorInvisible && await fs3.directoryExists(path25.join(cursor, TANSR_DIR_NAME))) {
29485
30357
  projectRoot = cursor;
29486
30358
  break;
29487
30359
  }
29488
30360
  if (boundary !== void 0 && normalizeForCompare(cursor, platform) === boundary) break;
29489
- const parent = path24.dirname(cursor);
30361
+ const parent = path25.dirname(cursor);
29490
30362
  if (parent === cursor) break;
29491
30363
  cursor = parent;
29492
30364
  }
@@ -29495,8 +30367,8 @@ async function discoverLayerFiles(options) {
29495
30367
  managedCandidates,
29496
30368
  ...projectRoot !== void 0 ? {
29497
30369
  projectRoot,
29498
- projectSharedFile: path24.join(projectRoot, TANSR_DIR_NAME, SETTINGS_FILE_NAME),
29499
- projectLocalFile: path24.join(projectRoot, TANSR_DIR_NAME, LOCAL_SETTINGS_FILE_NAME)
30370
+ projectSharedFile: path25.join(projectRoot, TANSR_DIR_NAME, SETTINGS_FILE_NAME),
30371
+ projectLocalFile: path25.join(projectRoot, TANSR_DIR_NAME, LOCAL_SETTINGS_FILE_NAME)
29500
30372
  } : {},
29501
30373
  userFile
29502
30374
  };
@@ -30083,8 +30955,8 @@ function issueExpected(issue) {
30083
30955
  return issue.code;
30084
30956
  }
30085
30957
  }
30086
- function attributeLayer(layersDescending, path28) {
30087
- return layersDescending.find((entry) => hasPath(entry.raw, path28));
30958
+ function attributeLayer(layersDescending, path29) {
30959
+ return layersDescending.find((entry) => hasPath(entry.raw, path29));
30088
30960
  }
30089
30961
  function scanUnknownKeys(raw, parsed, pathSoFar, layersDescending, addDiagnostic) {
30090
30962
  if (!isPlainObject2(raw) || !isPlainObject2(parsed)) return;
@@ -31182,7 +32054,7 @@ var HookEngine = class {
31182
32054
  // ../kernel/src/skills/discover.ts
31183
32055
  import { promises as fsPromises2 } from "node:fs";
31184
32056
  import os4 from "node:os";
31185
- import path25 from "node:path";
32057
+ import path26 from "node:path";
31186
32058
 
31187
32059
  // ../kernel/src/skills/types.ts
31188
32060
  var SKILLS_DIR_NAME = "skills";
@@ -31402,8 +32274,8 @@ async function scanFileSource(source, skillsDir, fs3, diagnostics) {
31402
32274
  const seen = /* @__PURE__ */ new Set();
31403
32275
  for (const dirName of [...subdirs].sort()) {
31404
32276
  if (dirName.startsWith(".") || dirName.startsWith("_")) continue;
31405
- const dir = path25.join(skillsDir, dirName);
31406
- const file = path25.join(dir, SKILL_FILE_NAME);
32277
+ const dir = path26.join(skillsDir, dirName);
32278
+ const file = path26.join(dir, SKILL_FILE_NAME);
31407
32279
  if (!SKILL_NAME_RE.test(dirName)) {
31408
32280
  diagnostics.push({ severity: "warning", code: "name_invalid", source, skill: dirName, file });
31409
32281
  continue;
@@ -31452,7 +32324,7 @@ async function discoverSkills(options = {}) {
31452
32324
  const platform = options.platform ?? process.platform;
31453
32325
  const homedir = options.homedir ?? os4.homedir();
31454
32326
  const cwd = options.cwd ?? process.cwd();
31455
- const explicitRoot = options.projectRoot === void 0 ? void 0 : path25.resolve(options.projectRoot);
32327
+ const explicitRoot = options.projectRoot === void 0 ? void 0 : path26.resolve(options.projectRoot);
31456
32328
  const discovered = await discoverLayerFiles({
31457
32329
  cwd: explicitRoot ?? cwd,
31458
32330
  homedir,
@@ -31463,13 +32335,13 @@ async function discoverSkills(options = {}) {
31463
32335
  ...explicitRoot !== void 0 ? { boundary: explicitRoot } : options.boundary !== void 0 ? { boundary: options.boundary } : {}
31464
32336
  });
31465
32337
  const builtinEntries = collectBuiltin(options.builtinSkills ?? [], diagnostics);
31466
- const userSkillsDir = path25.join(path25.dirname(discovered.userFile), SKILLS_DIR_NAME);
32338
+ const userSkillsDir = path26.join(path26.dirname(discovered.userFile), SKILLS_DIR_NAME);
31467
32339
  const userOutcome = await scanFileSource("user", userSkillsDir, fs3, diagnostics);
31468
32340
  const projectRoot = explicitRoot ?? discovered.projectRoot;
31469
32341
  let projectSkillsDir;
31470
32342
  let projectOutcome = { status: "missing" };
31471
32343
  if (projectRoot !== void 0) {
31472
- projectSkillsDir = path25.join(projectRoot, TANSR_DIR_NAME, SKILLS_DIR_NAME);
32344
+ projectSkillsDir = path26.join(projectRoot, TANSR_DIR_NAME, SKILLS_DIR_NAME);
31473
32345
  projectOutcome = await scanFileSource("project", projectSkillsDir, fs3, diagnostics);
31474
32346
  }
31475
32347
  const userEntries = userOutcome.status === "scanned" ? userOutcome.entries : [];
@@ -31543,7 +32415,7 @@ async function discoverSkills(options = {}) {
31543
32415
  }
31544
32416
 
31545
32417
  // ../kernel/src/skills/registry.ts
31546
- import path26 from "node:path";
32418
+ import path27 from "node:path";
31547
32419
  function errCode2(err) {
31548
32420
  if (err !== null && typeof err === "object" && "code" in err) {
31549
32421
  const code = err.code;
@@ -31606,7 +32478,7 @@ var SkillRegistry = class {
31606
32478
  /** 技能目录绝对路径(正文相对引用锚点;builtin 无) */
31607
32479
  static baseDirOf(entry) {
31608
32480
  if (entry.dir !== void 0) return entry.dir;
31609
- if (entry.file !== void 0) return path26.dirname(entry.file);
32481
+ if (entry.file !== void 0) return path27.dirname(entry.file);
31610
32482
  return void 0;
31611
32483
  }
31612
32484
  };
@@ -31787,7 +32659,7 @@ var TansrSdkError = class extends Error {
31787
32659
  };
31788
32660
 
31789
32661
  // src/define-tool.ts
31790
- function specToZod(spec, path28) {
32662
+ function specToZod(spec, path29) {
31791
32663
  let schema;
31792
32664
  switch (spec.type) {
31793
32665
  case "string":
@@ -31800,24 +32672,24 @@ function specToZod(spec, path28) {
31800
32672
  schema = z46.boolean();
31801
32673
  break;
31802
32674
  case "array":
31803
- schema = z46.array(spec.items !== void 0 ? specToZod(spec.items, `${path28}.items`) : z46.unknown());
32675
+ schema = z46.array(spec.items !== void 0 ? specToZod(spec.items, `${path29}.items`) : z46.unknown());
31804
32676
  break;
31805
32677
  case "object":
31806
- schema = spec.properties !== void 0 ? propertiesToZodObject(spec.properties, path28) : z46.record(z46.unknown());
32678
+ schema = spec.properties !== void 0 ? propertiesToZodObject(spec.properties, path29) : z46.record(z46.unknown());
31807
32679
  break;
31808
32680
  default:
31809
32681
  throw new TansrSdkError(
31810
32682
  "invalid_options",
31811
- `defineTool: parameter "${path28}" has unknown type "${String(spec.type)}"; use one of 'string' | 'number' | 'boolean' | 'array' | 'object'.`
32683
+ `defineTool: parameter "${path29}" has unknown type "${String(spec.type)}"; use one of 'string' | 'number' | 'boolean' | 'array' | 'object'.`
31812
32684
  );
31813
32685
  }
31814
32686
  if (spec.description !== void 0) schema = schema.describe(spec.description);
31815
32687
  return schema;
31816
32688
  }
31817
- function propertiesToZodObject(properties, path28) {
32689
+ function propertiesToZodObject(properties, path29) {
31818
32690
  const shape = {};
31819
32691
  for (const [key2, spec] of Object.entries(properties)) {
31820
- const field = specToZod(spec, path28 === "" ? key2 : `${path28}.${key2}`);
32692
+ const field = specToZod(spec, path29 === "" ? key2 : `${path29}.${key2}`);
31821
32693
  shape[key2] = spec.optional === true ? field.optional() : field;
31822
32694
  }
31823
32695
  return z46.object(shape);
@@ -31926,7 +32798,7 @@ function defineTool(options) {
31926
32798
  }
31927
32799
 
31928
32800
  // src/skills.ts
31929
- import path27 from "node:path";
32801
+ import path28 from "node:path";
31930
32802
  function yamlQuote(value) {
31931
32803
  return '"' + value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r\n?/g, "\n").replace(/\n/g, "\\n").replace(/\t/g, "\\t") + '"';
31932
32804
  }
@@ -31960,9 +32832,9 @@ function defineSkill(options) {
31960
32832
  ];
31961
32833
  return { name: options.name, content: lines.join("\n") };
31962
32834
  }
31963
- var NO_DISCOVERY_ROOT = path27.resolve(path27.sep, ".tansr-sdk-no-discovery");
32835
+ var NO_DISCOVERY_ROOT = path28.resolve(path28.sep, ".tansr-sdk-no-discovery");
31964
32836
  function noDiscoveryFs(inner) {
31965
- const inVoid = (target) => path27.resolve(target).startsWith(NO_DISCOVERY_ROOT);
32837
+ const inVoid = (target) => path28.resolve(target).startsWith(NO_DISCOVERY_ROOT);
31966
32838
  const enoent = (target) => {
31967
32839
  const err = new Error(`ENOENT: no such file or directory ${target}`);
31968
32840
  err.code = "ENOENT";
@@ -31976,7 +32848,7 @@ function noDiscoveryFs(inner) {
31976
32848
  }
31977
32849
  async function assembleSdkSkills(options) {
31978
32850
  const fs3 = noDiscoveryFs(options.fs ?? defaultSkillsFileSystem());
31979
- const dirs = (options.dirs ?? []).map((dir) => path27.resolve(dir));
32851
+ const dirs = (options.dirs ?? []).map((dir) => path28.resolve(dir));
31980
32852
  for (const dir of dirs) {
31981
32853
  if (!await fs3.directoryExists(dir)) {
31982
32854
  throw new TansrSdkError(
@@ -34175,7 +35047,7 @@ var OpenAIResponsesAdapter = class {
34175
35047
  };
34176
35048
 
34177
35049
  // ../providers/src/retry/attempt-observer.ts
34178
- import { createHash as createHash13 } from "node:crypto";
35050
+ import { createHash as createHash15 } from "node:crypto";
34179
35051
  var PROVIDER_CALL_META_FIELD = "callMeta";
34180
35052
  function providerCallMetaOf(options) {
34181
35053
  if (options === void 0) return void 0;
@@ -34204,12 +35076,12 @@ function endpointKeyOf(baseUrl) {
34204
35076
  let normalized;
34205
35077
  try {
34206
35078
  const url = new URL(baseUrl);
34207
- const path28 = url.pathname.replace(/\/+$/, "");
34208
- normalized = `${url.protocol}//${url.host}${path28}`.toLowerCase();
35079
+ const path29 = url.pathname.replace(/\/+$/, "");
35080
+ normalized = `${url.protocol}//${url.host}${path29}`.toLowerCase();
34209
35081
  } catch {
34210
35082
  normalized = baseUrl.trim().replace(/\/+$/, "").toLowerCase();
34211
35083
  }
34212
- return createHash13("sha256").update(normalized, "utf8").digest("hex").slice(0, 16);
35084
+ return createHash15("sha256").update(normalized, "utf8").digest("hex").slice(0, 16);
34213
35085
  }
34214
35086
  function safeAttemptCall(fn) {
34215
35087
  if (fn === void 0) return;
@@ -34362,16 +35234,16 @@ function sendableTwpReasoningContent(features) {
34362
35234
  }
34363
35235
 
34364
35236
  // ../providers/src/twp/signing.ts
34365
- import { createHash as createHash14, createHmac, randomBytes as randomBytes3 } from "node:crypto";
35237
+ import { createHash as createHash16, createHmac, randomBytes as randomBytes4 } from "node:crypto";
34366
35238
  var TWP_SIGNING_ALGORITHM = "TWP1-HMAC-SHA256";
34367
35239
  var TWP_NONCE_LENGTH = 24;
34368
35240
  function deriveTwpSigningKey(secretKey) {
34369
- return createHash14("sha256").update(secretKey, "utf8").digest();
35241
+ return createHash16("sha256").update(secretKey, "utf8").digest();
34370
35242
  }
34371
35243
  function sha256Hex2(data) {
34372
- return createHash14("sha256").update(typeof data === "string" ? Buffer.from(data, "utf8") : data).digest("hex");
35244
+ return createHash16("sha256").update(typeof data === "string" ? Buffer.from(data, "utf8") : data).digest("hex");
34373
35245
  }
34374
- function generateTwpNonce(random = randomBytes3) {
35246
+ function generateTwpNonce(random = randomBytes4) {
34375
35247
  return random(TWP_NONCE_LENGTH / 2).toString("hex");
34376
35248
  }
34377
35249
  function buildTwpSigningString(input) {
@@ -35388,9 +36260,9 @@ var MissingApiKeyError = class extends ProviderRegistryError {
35388
36260
  var CyclicFallbackError = class extends ProviderRegistryError {
35389
36261
  /** 成环路径,末项为再次出现的别名,如 ['main','fast','main'] */
35390
36262
  path;
35391
- constructor(path28) {
35392
- super("cyclic_fallback", `Cyclic fallback chain: ${path28.join(" -> ")}`);
35393
- this.path = path28;
36263
+ constructor(path29) {
36264
+ super("cyclic_fallback", `Cyclic fallback chain: ${path29.join(" -> ")}`);
36265
+ this.path = path29;
35394
36266
  }
35395
36267
  };
35396
36268
 
@@ -35878,9 +36750,9 @@ function pushUnique(out, seen, resolved) {
35878
36750
  seen.add(dedupeKey);
35879
36751
  out.push(resolved);
35880
36752
  }
35881
- function expandInto(config, alias, path28, out, seen) {
35882
- if (path28.includes(alias)) throw new CyclicFallbackError([...path28, alias]);
35883
- const nextPath = [...path28, alias];
36753
+ function expandInto(config, alias, path29, out, seen) {
36754
+ if (path29.includes(alias)) throw new CyclicFallbackError([...path29, alias]);
36755
+ const nextPath = [...path29, alias];
35884
36756
  pushUnique(out, seen, resolveAliasOrRef(config, alias));
35885
36757
  for (const entry of config.fallbacks[alias] ?? []) {
35886
36758
  if (entry.includes("/")) {
@@ -37973,7 +38845,7 @@ async function assembleTooling(options) {
37973
38845
  }
37974
38846
 
37975
38847
  // src/adjudication/prompts.ts
37976
- import { createHash as createHash15 } from "node:crypto";
38848
+ import { createHash as createHash17 } from "node:crypto";
37977
38849
  var STAGE1_MAX_TOKENS = 64;
37978
38850
  var STAGE2_MAX_TOKENS = 1024;
37979
38851
  var TWO_STAGE_TIMEOUT_MS = 15e3;
@@ -38070,7 +38942,7 @@ function serializeTimeoutPolicy(policy) {
38070
38942
  ].join(";");
38071
38943
  }
38072
38944
  function makeProtocolFingerprint(components) {
38073
- return createHash15("sha256").update(components.join("\n\0")).digest("hex").slice(0, 16);
38945
+ return createHash17("sha256").update(components.join("\n\0")).digest("hex").slice(0, 16);
38074
38946
  }
38075
38947
  var PAYLOAD_VERSION_COMPONENT = "payload:v4-engine-facts-shell";
38076
38948
  var SETTLE_COMPONENT = "settle:verdict-first";
@@ -41137,7 +42009,7 @@ async function createSession(options = {}) {
41137
42009
 
41138
42010
  // src/sessions/file-store.ts
41139
42011
  import { randomUUID as randomUUID14 } from "node:crypto";
41140
- import { mkdir as mkdir8, readFile as readFile10, rm as rm9, stat as stat8 } from "node:fs/promises";
42012
+ import { mkdir as mkdir9, readFile as readFile11, rm as rm10, stat as stat9 } from "node:fs/promises";
41141
42013
  var LOCAL_END_USER_KEY = "local";
41142
42014
  var MESSAGE_KINDS2 = /* @__PURE__ */ new Set([
41143
42015
  "user_prompt",
@@ -41160,7 +42032,7 @@ function sessionTitleOf(messages) {
41160
42032
  }
41161
42033
  async function readRawMeta(sessionDir) {
41162
42034
  try {
41163
- const parsed = JSON.parse(await readFile10(metaFilePath(sessionDir), "utf8"));
42035
+ const parsed = JSON.parse(await readFile11(metaFilePath(sessionDir), "utf8"));
41164
42036
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
41165
42037
  return parsed;
41166
42038
  } catch {
@@ -41295,7 +42167,7 @@ function createFileSessionStore(options) {
41295
42167
  cursors.set(sessionId, tail);
41296
42168
  }
41297
42169
  async function withMtime(sessionDir, cursor) {
41298
- const { mtimeMs } = await stat8(journalFilePath(sessionDir));
42170
+ const { mtimeMs } = await stat9(journalFilePath(sessionDir));
41299
42171
  return { ...cursor, mtimeMs };
41300
42172
  }
41301
42173
  async function patchRawMeta(sessionDir, mutate) {
@@ -41364,20 +42236,20 @@ function createFileSessionStore(options) {
41364
42236
  if (await readRawMeta(sessionDir) !== null) return false;
41365
42237
  let existed = true;
41366
42238
  try {
41367
- await stat8(sessionDir);
42239
+ await stat9(sessionDir);
41368
42240
  } catch {
41369
42241
  existed = false;
41370
42242
  }
41371
- await mkdir8(sessionDir, { recursive: true });
42243
+ await mkdir9(sessionDir, { recursive: true });
41372
42244
  let restored;
41373
42245
  try {
41374
42246
  restored = await tier.restore({ sessionId, endUserKey: LOCAL_END_USER_KEY, sessionDir });
41375
42247
  } catch (err) {
41376
- if (!existed) await rm9(sessionDir, { recursive: true, force: true }).catch(() => void 0);
42248
+ if (!existed) await rm10(sessionDir, { recursive: true, force: true }).catch(() => void 0);
41377
42249
  throw readError(sessionId, err);
41378
42250
  }
41379
42251
  if (restored === null) {
41380
- if (!existed) await rm9(sessionDir, { recursive: true, force: true }).catch(() => void 0);
42252
+ if (!existed) await rm10(sessionDir, { recursive: true, force: true }).catch(() => void 0);
41381
42253
  return false;
41382
42254
  }
41383
42255
  const cold = restored.meta;
@@ -41611,7 +42483,7 @@ function createFileSessionStore(options) {
41611
42483
  tier.unbind(sessionDir);
41612
42484
  await tier.remove({ sessionId, endUserKey: LOCAL_END_USER_KEY });
41613
42485
  }
41614
- await rm9(sessionDir, { recursive: true, force: true });
42486
+ await rm10(sessionDir, { recursive: true, force: true });
41615
42487
  await index.remove(sessionId);
41616
42488
  });
41617
42489
  },
@@ -42670,7 +43542,7 @@ function createNarrator(source, options) {
42670
43542
  }
42671
43543
 
42672
43544
  // src/platform/bundle-cache.ts
42673
- import { createHash as createHash16 } from "node:crypto";
43545
+ import { createHash as createHash18 } from "node:crypto";
42674
43546
  var DEFAULT_BUNDLE_CACHE_TTL_MS = 6e4;
42675
43547
  var DEFAULT_MAX_BUNDLE_ENTRIES = 16;
42676
43548
  var DEFAULT_MAX_FEATURE_ENTRIES = 1e5;
@@ -42710,7 +43582,7 @@ var Lru = class {
42710
43582
  }
42711
43583
  };
42712
43584
  function tokenScope(token) {
42713
- return createHash16("sha256").update(token, "utf8").digest("hex");
43585
+ return createHash18("sha256").update(token, "utf8").digest("hex");
42714
43586
  }
42715
43587
  function bundleKey(base, appId) {
42716
43588
  return `${base}\0${appId ?? ""}`;
@@ -42971,9 +43843,11 @@ export {
42971
43843
  createAppTokenFetch,
42972
43844
  createBlockListClassifier,
42973
43845
  createBundleCache,
43846
+ createChaosBlobStore,
42974
43847
  createClassifierTranscriptRecorder,
42975
43848
  createFileCheckpointStore,
42976
43849
  createFileSessionStore,
43850
+ createFsBlobStore,
42977
43851
  createImageGenTool2 as createImageGenTool,
42978
43852
  createMcpHost,
42979
43853
  createMemoryBlobStore,
@@ -43033,6 +43907,7 @@ export {
43033
43907
  resolveStageHandle,
43034
43908
  runAgent,
43035
43909
  runIdleCompaction,
43910
+ runStorageConformance,
43036
43911
  serializeTimeoutPolicy,
43037
43912
  setSessionViewDelivery,
43038
43913
  singleStageProtocolFingerprintFor,