oasis_test_v2 2.2.35 → 2.2.36

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 (2) hide show
  1. package/dist/index.js +1106 -721
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22503,6 +22503,43 @@ var init_sync_skills = __esm({
22503
22503
 
22504
22504
  // ../adapters/src/_core/materialize-bundle.ts
22505
22505
  function materializeBundle(dir, bundle) {
22506
+ for (const [rel, entry] of Object.entries(bundle.restoreFiles ?? {})) {
22507
+ try {
22508
+ const parts = rel.split("/");
22509
+ if (!rel || path3.isAbsolute(rel) || rel.includes("\\") || parts.some((p2) => !p2 || p2 === "." || p2 === "..")) {
22510
+ throw new Error("\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84");
22511
+ }
22512
+ let parent = fs3.realpathSync(dir);
22513
+ for (const component of parts.slice(0, -1)) {
22514
+ parent = path3.join(parent, component);
22515
+ if (!fs3.existsSync(parent)) fs3.mkdirSync(parent);
22516
+ const st = fs3.lstatSync(parent);
22517
+ if (st.isSymbolicLink() || !st.isDirectory()) throw new Error("\u6062\u590D\u8DEF\u5F84\u5305\u542B\u975E\u76EE\u5F55\u6216\u8F6F\u94FE\u63A5");
22518
+ }
22519
+ const file = path3.join(parent, parts.at(-1));
22520
+ let exists = false;
22521
+ try {
22522
+ const st = fs3.lstatSync(file);
22523
+ if (!st.isFile() || st.isSymbolicLink()) throw new Error("\u6062\u590D\u76EE\u6807\u4E0D\u662F\u666E\u901A\u6587\u4EF6");
22524
+ exists = true;
22525
+ } catch (error2) {
22526
+ if (error2.code !== "ENOENT") throw error2;
22527
+ }
22528
+ if (entry.requireExisting && !exists) throw new Error("\u4E0A\u6B21\u672A\u4FDD\u5B58\u7684\u6587\u4EF6\u5DF2\u4E0D\u5728\u672C\u8282\u70B9\uFF0C\u65E0\u6CD5\u6062\u590D\u6700\u65B0\u7248");
22529
+ if (exists && !entry.overwrite) continue;
22530
+ const bytes2 = Buffer.from(entry.contentBase64, "base64");
22531
+ if ((0, import_node_crypto6.createHash)("sha256").update(bytes2).digest("hex") !== entry.sha256) throw new Error("\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
22532
+ const temp = path3.join(parent, `.oasis-restore-${(0, import_node_crypto6.randomUUID)()}`);
22533
+ try {
22534
+ fs3.writeFileSync(temp, bytes2, { flag: "wx", mode: 384 });
22535
+ fs3.renameSync(temp, file);
22536
+ } finally {
22537
+ fs3.rmSync(temp, { force: true });
22538
+ }
22539
+ } catch (error2) {
22540
+ throw new BundleRestoreError(rel, error2 instanceof Error ? error2.message : String(error2));
22541
+ }
22542
+ }
22506
22543
  for (const [rel, content3] of Object.entries(bundle.files)) {
22507
22544
  const file = path3.join(dir, rel);
22508
22545
  fs3.mkdirSync(path3.dirname(file), { recursive: true });
@@ -22517,12 +22554,20 @@ function materializeBundle(dir, bundle) {
22517
22554
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
22518
22555
  return task;
22519
22556
  }
22520
- var fs3, path3;
22557
+ var fs3, path3, import_node_crypto6, BundleRestoreError;
22521
22558
  var init_materialize_bundle = __esm({
22522
22559
  "../adapters/src/_core/materialize-bundle.ts"() {
22523
22560
  "use strict";
22524
22561
  fs3 = __toESM(require("node:fs"), 1);
22525
22562
  path3 = __toESM(require("node:path"), 1);
22563
+ import_node_crypto6 = require("node:crypto");
22564
+ BundleRestoreError = class extends Error {
22565
+ constructor(filePath, detail) {
22566
+ super(`\u6587\u4EF6\u6062\u590D\u5931\u8D25 ${filePath}\uFF1A${detail}`);
22567
+ this.filePath = filePath;
22568
+ this.name = "BundleRestoreError";
22569
+ }
22570
+ };
22526
22571
  }
22527
22572
  });
22528
22573
 
@@ -22576,7 +22621,7 @@ function resolveWorkRoots(workRoot) {
22576
22621
  }
22577
22622
  function workdirSlug(workdirKey) {
22578
22623
  const safe = workdirKey.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 48);
22579
- const h = (0, import_node_crypto6.createHash)("sha256").update(workdirKey).digest("hex").slice(0, 8);
22624
+ const h = (0, import_node_crypto7.createHash)("sha256").update(workdirKey).digest("hex").slice(0, 8);
22580
22625
  return `${safe}-${h}`;
22581
22626
  }
22582
22627
  function sessionDirFor(workRoot, runtimeKind, workdirKey) {
@@ -22708,11 +22753,11 @@ function prepareWorkdir(args) {
22708
22753
  function legacyChatSessionDir(workRoot, rtId) {
22709
22754
  return import_node_path.default.join(resolveLegacyWorkRoot(workRoot), "oasis-chat-sessions", rtId.replace(/[^a-zA-Z0-9_-]+/g, "_"));
22710
22755
  }
22711
- var import_node_crypto6, import_node_fs2, import_node_os, import_node_path, META_FILE, LOCK_FILE, NEW_WORK_ROOT, slug3, LEGACY_ROOTS, LEGACY_ONESHOT_PREFIX;
22756
+ var import_node_crypto7, import_node_fs2, import_node_os, import_node_path, META_FILE, LOCK_FILE, NEW_WORK_ROOT, slug3, LEGACY_ROOTS, LEGACY_ONESHOT_PREFIX;
22712
22757
  var init_session_paths = __esm({
22713
22758
  "../adapters/src/_core/session-paths.ts"() {
22714
22759
  "use strict";
22715
- import_node_crypto6 = require("node:crypto");
22760
+ import_node_crypto7 = require("node:crypto");
22716
22761
  import_node_fs2 = __toESM(require("node:fs"), 1);
22717
22762
  import_node_os = __toESM(require("node:os"), 1);
22718
22763
  import_node_path = __toESM(require("node:path"), 1);
@@ -22740,7 +22785,7 @@ function executionEnvHash(p2) {
22740
22785
  binPkg: p2.binPackageDir ?? null,
22741
22786
  extraRo: [...p2.extraRoBindDirs ?? []].sort()
22742
22787
  });
22743
- return (0, import_node_crypto7.createHash)("sha256").update(material).digest("hex").slice(0, 16);
22788
+ return (0, import_node_crypto8.createHash)("sha256").update(material).digest("hex").slice(0, 16);
22744
22789
  }
22745
22790
  function persistContainerName(workdirKey) {
22746
22791
  return `oasis-wd-${workdirSlug(workdirKey)}`;
@@ -22860,11 +22905,11 @@ function wrapForContainer(p2) {
22860
22905
  function killContainerArgs(containerName) {
22861
22906
  return ["kill", containerName];
22862
22907
  }
22863
- var import_node_crypto7, path5, CONTAINER_DEFAULT_PATH, CONTAINER_HOME, EE_HASH_LABEL;
22908
+ var import_node_crypto8, path5, CONTAINER_DEFAULT_PATH, CONTAINER_HOME, EE_HASH_LABEL;
22864
22909
  var init_container_wrap = __esm({
22865
22910
  "../adapters/src/_core/container-wrap.ts"() {
22866
22911
  "use strict";
22867
- import_node_crypto7 = require("node:crypto");
22912
+ import_node_crypto8 = require("node:crypto");
22868
22913
  path5 = __toESM(require("node:path"), 1);
22869
22914
  init_session_paths();
22870
22915
  CONTAINER_DEFAULT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
@@ -23592,12 +23637,12 @@ function classifyExit(result) {
23592
23637
  if (status === 429 || status === 529) return "rate-limit";
23593
23638
  return "error";
23594
23639
  }
23595
- var import_node_child_process3, import_node_crypto8, fs7, path7, readline, CLAUDE_BLOCKED_FLAGS, SUBAGENT_TRANSCRIPT_MAX_BYTES, SUBAGENT_TERMINAL, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
23640
+ var import_node_child_process3, import_node_crypto9, fs7, path7, readline, CLAUDE_BLOCKED_FLAGS, SUBAGENT_TRANSCRIPT_MAX_BYTES, SUBAGENT_TERMINAL, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
23596
23641
  var init_claude_code = __esm({
23597
23642
  "../adapters/src/claude-code/index.ts"() {
23598
23643
  "use strict";
23599
23644
  import_node_child_process3 = require("node:child_process");
23600
- import_node_crypto8 = require("node:crypto");
23645
+ import_node_crypto9 = require("node:crypto");
23601
23646
  fs7 = __toESM(require("node:fs"), 1);
23602
23647
  path7 = __toESM(require("node:path"), 1);
23603
23648
  readline = __toESM(require("node:readline"), 1);
@@ -23677,7 +23722,7 @@ var init_claude_code = __esm({
23677
23722
  }
23678
23723
  async spawn(job) {
23679
23724
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
23680
- const id = `claude-${slug6}-${(0, import_node_crypto8.randomUUID)().slice(0, 8)}`;
23725
+ const id = `claude-${slug6}-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
23681
23726
  const dir = job.workdirKey ? prepareWorkdir({
23682
23727
  workRoot: this.opts.workRoot,
23683
23728
  runtimeKind: "claude",
@@ -24090,7 +24135,7 @@ var init_claude_code = __esm({
24090
24135
  const outgoing = report.failed.length ? appendMaterializeWarning(input.text, report) : input.text;
24091
24136
  if (input.interrupt) writeInterrupt();
24092
24137
  diag("append_accepted", { writes, results, interrupt: !!input.interrupt, chars: input.text.length });
24093
- const receiptId = input.requireConsumptionReceipt ? (0, import_node_crypto8.randomUUID)() : void 0;
24138
+ const receiptId = input.requireConsumptionReceipt ? (0, import_node_crypto9.randomUUID)() : void 0;
24094
24139
  if (receiptId) pendingInputReceipts.set(receiptId, input.clientMessageKey);
24095
24140
  const ok3 = writeUserMessage(outgoing, receiptId);
24096
24141
  if (!ok3 && receiptId) pendingInputReceipts.delete(receiptId);
@@ -24230,7 +24275,12 @@ function withResilience(inner, cfg = {}) {
24230
24275
  try {
24231
24276
  handle = await inner.spawn(job);
24232
24277
  } catch (err) {
24233
- finish({ code: null, reason: "error", errorMessage: errText(err) });
24278
+ finish({
24279
+ code: null,
24280
+ reason: "error",
24281
+ errorMessage: errText(err),
24282
+ ...err instanceof BundleRestoreError ? { fileRestoreError: { path: err.filePath, message: err.message } } : {}
24283
+ });
24234
24284
  return;
24235
24285
  }
24236
24286
  const { info, produced } = await runOnce(handle);
@@ -24304,6 +24354,7 @@ var RETRYABLE, sleep2;
24304
24354
  var init_resilient = __esm({
24305
24355
  "../adapters/src/_core/resilient.ts"() {
24306
24356
  "use strict";
24357
+ init_materialize_bundle();
24307
24358
  init_src();
24308
24359
  RETRYABLE = /* @__PURE__ */ new Set(["rate-limit", "timeout", "server-unreachable"]);
24309
24360
  sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -24487,9 +24538,9 @@ function lockPathFor(cacheRoot, actorId) {
24487
24538
  }
24488
24539
  function aggregateSkillContentHash(files) {
24489
24540
  const sorted = [...files].sort((a, b2) => a.path.localeCompare(b2.path));
24490
- const hasher = (0, import_node_crypto9.createHash)("sha256");
24541
+ const hasher = (0, import_node_crypto10.createHash)("sha256");
24491
24542
  for (const f2 of sorted) {
24492
- const blob = (0, import_node_crypto9.createHash)("sha256").update(f2.content).digest("hex");
24543
+ const blob = (0, import_node_crypto10.createHash)("sha256").update(f2.content).digest("hex");
24493
24544
  hasher.update(f2.path);
24494
24545
  hasher.update("\0");
24495
24546
  hasher.update(blob);
@@ -24717,11 +24768,11 @@ async function verifyPendingLink(skillsDir, name, pend, log3) {
24717
24768
  }
24718
24769
  return true;
24719
24770
  }
24720
- var import_node_crypto9, fsp, os4, path8, MANIFEST_SCHEMA_VERSION, STALE_LOCK_MS, LOCK_ACQUIRE_TIMEOUT_MS, LOCK_POLL_MS;
24771
+ var import_node_crypto10, fsp, os4, path8, MANIFEST_SCHEMA_VERSION, STALE_LOCK_MS, LOCK_ACQUIRE_TIMEOUT_MS, LOCK_POLL_MS;
24721
24772
  var init_skill_cache = __esm({
24722
24773
  "../adapters/src/_core/skill-cache.ts"() {
24723
24774
  "use strict";
24724
- import_node_crypto9 = require("node:crypto");
24775
+ import_node_crypto10 = require("node:crypto");
24725
24776
  fsp = __toESM(require("node:fs/promises"), 1);
24726
24777
  os4 = __toESM(require("node:os"), 1);
24727
24778
  path8 = __toESM(require("node:path"), 1);
@@ -25370,7 +25421,7 @@ function isResponse(message) {
25370
25421
  }
25371
25422
  async function runProtocolSession(job, cfg) {
25372
25423
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
25373
- const id = `${cfg.runtimeKind}-${slug6}-${(0, import_node_crypto10.randomUUID)().slice(0, 8)}`;
25424
+ const id = `${cfg.runtimeKind}-${slug6}-${(0, import_node_crypto11.randomUUID)().slice(0, 8)}`;
25374
25425
  const dir = job.workdirKey ? prepareWorkdir({
25375
25426
  workRoot: cfg.workRoot,
25376
25427
  runtimeKind: cfg.runtimeKind,
@@ -25613,12 +25664,12 @@ async function runProtocolSession(job, cfg) {
25613
25664
  }
25614
25665
  };
25615
25666
  }
25616
- var import_node_child_process6, import_node_crypto10, path9, ProtocolCommandRejected, ProtocolSessionClosing;
25667
+ var import_node_child_process6, import_node_crypto11, path9, ProtocolCommandRejected, ProtocolSessionClosing;
25617
25668
  var init_protocol_session = __esm({
25618
25669
  "../adapters/src/_core/protocol-session.ts"() {
25619
25670
  "use strict";
25620
25671
  import_node_child_process6 = require("node:child_process");
25621
- import_node_crypto10 = require("node:crypto");
25672
+ import_node_crypto11 = require("node:crypto");
25622
25673
  path9 = __toESM(require("node:path"), 1);
25623
25674
  init_claude_code();
25624
25675
  init_container_runtime();
@@ -26077,13 +26128,13 @@ function codexTurnErrorMessage(value2) {
26077
26128
  const detail = typeof error2?.additionalDetails === "string" ? error2.additionalDetails.trim() : "";
26078
26129
  return [summary, detail].filter(Boolean).join("\n").slice(0, 4e3);
26079
26130
  }
26080
- var import_node_child_process7, import_node_crypto11, fs8, os5, path10, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CODEX_APP_SERVER_BLOCKED_FLAGS, CodexAdapter;
26131
+ var import_node_child_process7, import_node_crypto12, fs8, os5, path10, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CODEX_APP_SERVER_BLOCKED_FLAGS, CodexAdapter;
26081
26132
  var init_codex = __esm({
26082
26133
  "../adapters/src/codex/index.ts"() {
26083
26134
  "use strict";
26084
26135
  import_node_child_process7 = require("node:child_process");
26085
26136
  init_container_runtime();
26086
- import_node_crypto11 = require("node:crypto");
26137
+ import_node_crypto12 = require("node:crypto");
26087
26138
  fs8 = __toESM(require("node:fs"), 1);
26088
26139
  os5 = __toESM(require("node:os"), 1);
26089
26140
  path10 = __toESM(require("node:path"), 1);
@@ -26371,7 +26422,7 @@ ${task}` : task;
26371
26422
  }
26372
26423
  async spawnExec(job) {
26373
26424
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
26374
- const id = `codex-${slug6}-${(0, import_node_crypto11.randomUUID)().slice(0, 8)}`;
26425
+ const id = `codex-${slug6}-${(0, import_node_crypto12.randomUUID)().slice(0, 8)}`;
26375
26426
  const startedAtMs = Date.now();
26376
26427
  const dir = job.workdirKey ? prepareWorkdir({
26377
26428
  workRoot: this.opts.workRoot,
@@ -27182,7 +27233,7 @@ async function runACPSession(job, cfg) {
27182
27233
  }
27183
27234
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
27184
27235
  const binTag = path11.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
27185
- const id = `${binTag}-${slug6}-${(0, import_node_crypto12.randomUUID)().slice(0, 8)}`;
27236
+ const id = `${binTag}-${slug6}-${(0, import_node_crypto13.randomUUID)().slice(0, 8)}`;
27186
27237
  const dir = job.workdirKey ? prepareWorkdir({
27187
27238
  workRoot: cfg.workRoot,
27188
27239
  runtimeKind: cfg.runtimeKind ?? "subprocess",
@@ -27595,12 +27646,12 @@ ${task}` : task;
27595
27646
  return run;
27596
27647
  }
27597
27648
  }
27598
- var import_node_child_process8, import_node_crypto12, fs9, path11, readline3, ACPClient, LEGACY_APPROVE_OPTION_ID;
27649
+ var import_node_child_process8, import_node_crypto13, fs9, path11, readline3, ACPClient, LEGACY_APPROVE_OPTION_ID;
27599
27650
  var init_acp = __esm({
27600
27651
  "../adapters/src/_core/acp.ts"() {
27601
27652
  "use strict";
27602
27653
  import_node_child_process8 = require("node:child_process");
27603
- import_node_crypto12 = require("node:crypto");
27654
+ import_node_crypto13 = require("node:crypto");
27604
27655
  fs9 = __toESM(require("node:fs"), 1);
27605
27656
  path11 = __toESM(require("node:path"), 1);
27606
27657
  readline3 = __toESM(require("node:readline"), 1);
@@ -28433,7 +28484,7 @@ var init_pdeathsig = __esm({
28433
28484
  // ../adapters/src/_core/subprocess.ts
28434
28485
  async function materialize(job, cfg) {
28435
28486
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
28436
- const id = `${slug6}-${(0, import_node_crypto13.randomUUID)().slice(0, 8)}`;
28487
+ const id = `${slug6}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
28437
28488
  const dir = job.workdirKey ? prepareWorkdir({
28438
28489
  workRoot: cfg.workRoot,
28439
28490
  runtimeKind: cfg.runtimeKind ?? "subprocess",
@@ -28726,12 +28777,12 @@ async function runOneShotText(job, cfg) {
28726
28777
  }
28727
28778
  });
28728
28779
  }
28729
- var import_node_child_process10, import_node_crypto13, fs11, path13, readline4;
28780
+ var import_node_child_process10, import_node_crypto14, fs11, path13, readline4;
28730
28781
  var init_subprocess = __esm({
28731
28782
  "../adapters/src/_core/subprocess.ts"() {
28732
28783
  "use strict";
28733
28784
  import_node_child_process10 = require("node:child_process");
28734
- import_node_crypto13 = require("node:crypto");
28785
+ import_node_crypto14 = require("node:crypto");
28735
28786
  fs11 = __toESM(require("node:fs"), 1);
28736
28787
  path13 = __toESM(require("node:path"), 1);
28737
28788
  readline4 = __toESM(require("node:readline"), 1);
@@ -28788,11 +28839,11 @@ function normalizeOpenClaw(line, state) {
28788
28839
  const text5 = (r.payloads ?? []).map((pl) => pl?.text ?? "").filter(Boolean).join("\n");
28789
28840
  return text5 ? [{ kind: "message", payload: { text: text5 }, outputText: text5 }] : [];
28790
28841
  }
28791
- var import_node_crypto14, isTopLevelClose, OpenClawAdapter;
28842
+ var import_node_crypto15, isTopLevelClose, OpenClawAdapter;
28792
28843
  var init_openclaw = __esm({
28793
28844
  "../adapters/src/openclaw/index.ts"() {
28794
28845
  "use strict";
28795
- import_node_crypto14 = require("node:crypto");
28846
+ import_node_crypto15 = require("node:crypto");
28796
28847
  init_subprocess();
28797
28848
  isTopLevelClose = (line) => line === "}" || line === "}\r";
28798
28849
  OpenClawAdapter = class {
@@ -28801,7 +28852,7 @@ var init_openclaw = __esm({
28801
28852
  }
28802
28853
  spawn(job) {
28803
28854
  const opts = this.opts;
28804
- const sessionId = job.runtimeSessionId ?? `oasis-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
28855
+ const sessionId = job.runtimeSessionId ?? `oasis-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
28805
28856
  return runStreamJson(job, {
28806
28857
  model: job.model ?? opts.model,
28807
28858
  bin: opts.openClawBin ?? "openclaw",
@@ -29602,6 +29653,7 @@ var init_src6 = __esm({
29602
29653
  init_workdir_gc();
29603
29654
  init_gc_loop();
29604
29655
  init_normalizer2();
29656
+ init_materialize_bundle();
29605
29657
  }
29606
29658
  });
29607
29659
 
@@ -29957,7 +30009,7 @@ function chatItemsDoubleWriteEnabled(env = process.env) {
29957
30009
  return raw !== "" && raw !== "0" && raw !== "false" && raw !== "off" && raw !== "no";
29958
30010
  }
29959
30011
  function fallbackTurnId(runId) {
29960
- return runId ? `chat-run-turn:${runId}` : `local-turn:${(0, import_node_crypto15.randomUUID)()}`;
30012
+ return runId ? `chat-run-turn:${runId}` : `local-turn:${(0, import_node_crypto16.randomUUID)()}`;
29961
30013
  }
29962
30014
  async function resolveChatItemsTurnId(items, messageId, fallback) {
29963
30015
  const existing = await items.listItemsByMessage(messageId).catch(() => []);
@@ -29976,20 +30028,20 @@ function projectsIntoContent(kind, role, origin, telemetryTextInContent, attrs =
29976
30028
  return true;
29977
30029
  }
29978
30030
  }
29979
- var import_node_crypto15, CHAT_ITEMS_DOUBLE_WRITE_ENV, SEGMENT_END_NOTE, ChatItemLedger;
30031
+ var import_node_crypto16, CHAT_ITEMS_DOUBLE_WRITE_ENV, SEGMENT_END_NOTE, ChatItemLedger;
29980
30032
  var init_chat_item_ledger = __esm({
29981
30033
  "../server/src/chat-item-ledger.ts"() {
29982
30034
  "use strict";
29983
30035
  init_src();
29984
30036
  init_subagent_cost();
29985
30037
  init_chat_item();
29986
- import_node_crypto15 = require("node:crypto");
30038
+ import_node_crypto16 = require("node:crypto");
29987
30039
  CHAT_ITEMS_DOUBLE_WRITE_ENV = "OASIS_CHAT_ITEMS_DOUBLE_WRITE";
29988
30040
  SEGMENT_END_NOTE = "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09";
29989
30041
  ChatItemLedger = class {
29990
30042
  constructor(deps) {
29991
30043
  this.deps = deps;
29992
- this.newId = deps.newId ?? import_node_crypto15.randomUUID;
30044
+ this.newId = deps.newId ?? import_node_crypto16.randomUUID;
29993
30045
  this.now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
29994
30046
  this.log = deps.log ?? ((m2) => console.warn(m2));
29995
30047
  this.messageId = deps.messageId ?? null;
@@ -30609,7 +30661,7 @@ var init_chat_parts = __esm({
30609
30661
  async function openAssistantRunningRow(deps) {
30610
30662
  const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
30611
30663
  if (typeof deps.chatStore.appendMessage !== "function") return void 0;
30612
- const id = (0, import_node_crypto16.randomUUID)();
30664
+ const id = (0, import_node_crypto17.randomUUID)();
30613
30665
  const at = now();
30614
30666
  try {
30615
30667
  await deps.chatStore.appendMessage({
@@ -30653,7 +30705,7 @@ async function finalizeAssistantRow(deps) {
30653
30705
  if (typeof deps.chatStore.appendMessage !== "function") return;
30654
30706
  try {
30655
30707
  await deps.chatStore.appendMessage({
30656
- id: (0, import_node_crypto16.randomUUID)(),
30708
+ id: (0, import_node_crypto17.randomUUID)(),
30657
30709
  sessionId: deps.chatSessionId,
30658
30710
  role: "assistant",
30659
30711
  content: deps.content,
@@ -30667,6 +30719,13 @@ async function finalizeAssistantRow(deps) {
30667
30719
  } catch {
30668
30720
  }
30669
30721
  }
30722
+ function outputsPartProvider(scan2, chatSessionId, companyId) {
30723
+ if (!scan2) return void 0;
30724
+ return async () => {
30725
+ const files = await scan2({ chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
30726
+ return files && files.length > 0 ? { type: "outputs", seq: 0, outputs: files } : null;
30727
+ };
30728
+ }
30670
30729
  async function appendOutputsPart(parts, provider) {
30671
30730
  if (!provider) return parts;
30672
30731
  const part = await provider().catch(() => null);
@@ -30910,11 +30969,11 @@ function runBackgroundAssistantChatTurn(deps) {
30910
30969
  });
30911
30970
  })().catch(() => void 0);
30912
30971
  }
30913
- var import_node_crypto16;
30972
+ var import_node_crypto17;
30914
30973
  var init_chat_assistant_turn = __esm({
30915
30974
  "../server/src/chat-assistant-turn.ts"() {
30916
30975
  "use strict";
30917
- import_node_crypto16 = require("node:crypto");
30976
+ import_node_crypto17 = require("node:crypto");
30918
30977
  init_src6();
30919
30978
  init_chat_item_ledger();
30920
30979
  init_chat_parts();
@@ -30932,7 +30991,7 @@ async function openChatTurn(args) {
30932
30991
  const turns = args.turns;
30933
30992
  if (!turns) return { ok: true, turn: null };
30934
30993
  const nowIso = args.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
30935
- const turnId = (args.newId ?? import_node_crypto17.randomUUID)();
30994
+ const turnId = (args.newId ?? import_node_crypto18.randomUUID)();
30936
30995
  const messageKey = localTurnMessageKey(args.source, turnId);
30937
30996
  const claim = await turns.claimTurn({
30938
30997
  turnId,
@@ -30966,7 +31025,7 @@ async function openChatTurn(args) {
30966
31025
  }
30967
31026
  };
30968
31027
  }
30969
- function assistantRowRecovery(chatStore) {
31028
+ function assistantRowRecovery(chatStore, opts = {}) {
30970
31029
  const rowOf = async (turn) => {
30971
31030
  const runId = turn.assistantRunId;
30972
31031
  if (!runId) return null;
@@ -30982,20 +31041,27 @@ function assistantRowRecovery(chatStore) {
30982
31041
 
30983
31042
  ` : "";
30984
31043
  const content3 = patch.status === "error" ? `${partial2}${ORPHANED_TURN_ASSISTANT_NOTICE}` : row.content ?? "";
31044
+ const outputsPart = opts.outputsPartFor?.(turn.chatSessionId);
30985
31045
  await finalizeAssistantRow({
30986
31046
  chatStore,
30987
31047
  chatSessionId: turn.chatSessionId,
30988
31048
  assistantMsgId: row.id,
30989
31049
  content: content3,
30990
- parts: [],
31050
+ // 不扫产出时照旧传空:finalize 对空 parts 不写这一列,行上原有的 parts 不动。
31051
+ // 要扫就必须带上原有 parts——否则扫到产出时写回去的只剩一张卡,这一轮的过程记录被冲掉。
31052
+ parts: outputsPart ? existingParts(row.parts) : [],
30991
31053
  status: patch.status,
30992
31054
  ...turn.assistantRunId ? { runId: turn.assistantRunId } : {},
30993
31055
  turnId: turn.id,
30994
- now: () => patch.completedAt
31056
+ now: () => patch.completedAt,
31057
+ ...outputsPart ? { outputsPart } : {}
30995
31058
  });
30996
31059
  }
30997
31060
  };
30998
31061
  }
31062
+ function existingParts(parts) {
31063
+ return Array.isArray(parts) ? parts.filter((p2) => p2 && typeof p2 === "object" && typeof p2.type === "string") : [];
31064
+ }
30999
31065
  async function reconcileChatTurn(turn, deps) {
31000
31066
  if (deps.isOwned?.(turn)) return { settled: false, turnId: turn.id, reason: "owned" };
31001
31067
  const rowStatus = deps.assistantRowStatus ? await deps.assistantRowStatus(turn).catch(() => null) : null;
@@ -31100,11 +31166,11 @@ async function sweepStaleChatTurnsAcrossCompanies(deps) {
31100
31166
  }
31101
31167
  return settled;
31102
31168
  }
31103
- var import_node_crypto17, ORPHANED_TURN_ASSISTANT_NOTICE;
31169
+ var import_node_crypto18, ORPHANED_TURN_ASSISTANT_NOTICE;
31104
31170
  var init_chat_turn_gate = __esm({
31105
31171
  "../server/src/chat-turn-gate.ts"() {
31106
31172
  "use strict";
31107
- import_node_crypto17 = require("node:crypto");
31173
+ import_node_crypto18 = require("node:crypto");
31108
31174
  init_chat_assistant_turn();
31109
31175
  ORPHANED_TURN_ASSISTANT_NOTICE = "\u8FD9\u4E00\u8F6E\u6CA1\u6709\u6B63\u5E38\u7ED3\u675F\uFF08\u670D\u52A1\u7AEF\u8FDB\u7A0B\u4E2D\u65AD\uFF09\uFF0C\u6CA1\u6709\u4EA7\u751F\u56DE\u590D\u3002\u4F60\u53EF\u4EE5\u518D\u95EE\u4E00\u6B21\u3002";
31110
31176
  }
@@ -31316,11 +31382,11 @@ var init_parse = __esm({
31316
31382
 
31317
31383
  // ../channels/src/feishu/crypto.ts
31318
31384
  function decryptFeishu(encrypt, encryptKey) {
31319
- const key = (0, import_node_crypto18.createHash)("sha256").update(encryptKey).digest();
31385
+ const key = (0, import_node_crypto19.createHash)("sha256").update(encryptKey).digest();
31320
31386
  const data = Buffer.from(encrypt, "base64");
31321
31387
  const iv = data.subarray(0, 16);
31322
31388
  const ciphertext = data.subarray(16);
31323
- const decipher = (0, import_node_crypto18.createDecipheriv)("aes-256-cbc", key, iv);
31389
+ const decipher = (0, import_node_crypto19.createDecipheriv)("aes-256-cbc", key, iv);
31324
31390
  decipher.setAutoPadding(true);
31325
31391
  return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
31326
31392
  }
@@ -31354,11 +31420,11 @@ function verifyToken(decoded, expected) {
31354
31420
  const token = headerToken ?? decoded["token"];
31355
31421
  return token === expected;
31356
31422
  }
31357
- var import_node_crypto18;
31423
+ var import_node_crypto19;
31358
31424
  var init_crypto = __esm({
31359
31425
  "../channels/src/feishu/crypto.ts"() {
31360
31426
  "use strict";
31361
- import_node_crypto18 = require("node:crypto");
31427
+ import_node_crypto19 = require("node:crypto");
31362
31428
  }
31363
31429
  });
31364
31430
 
@@ -31494,11 +31560,11 @@ var init_src7 = __esm({
31494
31560
  function errMsg(e) {
31495
31561
  return e instanceof Error ? e.message : String(e);
31496
31562
  }
31497
- var import_node_crypto19, FEISHU, ChannelService, CardStreamer;
31563
+ var import_node_crypto20, FEISHU, ChannelService, CardStreamer;
31498
31564
  var init_service = __esm({
31499
31565
  "../server/src/channels/service.ts"() {
31500
31566
  "use strict";
31501
- import_node_crypto19 = require("node:crypto");
31567
+ import_node_crypto20 = require("node:crypto");
31502
31568
  init_chat_turn_gate();
31503
31569
  init_deletion();
31504
31570
  init_src7();
@@ -31580,7 +31646,7 @@ var init_service = __esm({
31580
31646
  return existing.chatSessionId;
31581
31647
  }
31582
31648
  }
31583
- const id = (0, import_node_crypto19.randomUUID)();
31649
+ const id = (0, import_node_crypto20.randomUUID)();
31584
31650
  const now = this.now();
31585
31651
  if (cs) {
31586
31652
  const rt = await this.deps.resolveRuntime?.(a.aiActorId).catch(() => null);
@@ -31616,7 +31682,7 @@ var init_service = __esm({
31616
31682
  if (cs) {
31617
31683
  const sess = await cs.getSession(intent.chatSessionId).catch(() => null);
31618
31684
  resumeRt = sess?.runtimeSessionId ?? void 0;
31619
- await cs.appendMessage({ id: (0, import_node_crypto19.randomUUID)(), sessionId: intent.chatSessionId, role: "user", content: intent.message, createdAt: this.now() }, { source: "channels" }).catch(() => void 0);
31685
+ await cs.appendMessage({ id: (0, import_node_crypto20.randomUUID)(), sessionId: intent.chatSessionId, role: "user", content: intent.message, createdAt: this.now() }, { source: "channels" }).catch(() => void 0);
31620
31686
  }
31621
31687
  const opened = await openChatTurn({
31622
31688
  turns: this.deps.chatTurns,
@@ -31654,7 +31720,7 @@ var init_service = __esm({
31654
31720
  const reply = streamer.text();
31655
31721
  if (reply || session.runId) {
31656
31722
  await cs.appendMessage({
31657
- id: (0, import_node_crypto19.randomUUID)(),
31723
+ id: (0, import_node_crypto20.randomUUID)(),
31658
31724
  sessionId: intent.chatSessionId,
31659
31725
  role: "assistant",
31660
31726
  content: reply,
@@ -52733,7 +52799,7 @@ var require_websocket = __commonJS({
52733
52799
  var http2 = require("http");
52734
52800
  var net = require("net");
52735
52801
  var tls = require("tls");
52736
- var { randomBytes: randomBytes13, createHash: createHash39 } = require("crypto");
52802
+ var { randomBytes: randomBytes13, createHash: createHash41 } = require("crypto");
52737
52803
  var { Duplex, Readable } = require("stream");
52738
52804
  var { URL: URL2 } = require("url");
52739
52805
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -53401,7 +53467,7 @@ var require_websocket = __commonJS({
53401
53467
  abortHandshake(websocket, socket, "Invalid Upgrade header");
53402
53468
  return;
53403
53469
  }
53404
- const digest = createHash39("sha1").update(key + GUID).digest("base64");
53470
+ const digest = createHash41("sha1").update(key + GUID).digest("base64");
53405
53471
  if (res.headers["sec-websocket-accept"] !== digest) {
53406
53472
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
53407
53473
  return;
@@ -53770,7 +53836,7 @@ var require_websocket_server = __commonJS({
53770
53836
  var EventEmitter = require("events");
53771
53837
  var http2 = require("http");
53772
53838
  var { Duplex } = require("stream");
53773
- var { createHash: createHash39 } = require("crypto");
53839
+ var { createHash: createHash41 } = require("crypto");
53774
53840
  var extension3 = require_extension();
53775
53841
  var PerMessageDeflate2 = require_permessage_deflate();
53776
53842
  var subprotocol2 = require_subprotocol();
@@ -54077,7 +54143,7 @@ var require_websocket_server = __commonJS({
54077
54143
  );
54078
54144
  }
54079
54145
  if (this._state > RUNNING) return abortHandshake(socket, 503);
54080
- const digest = createHash39("sha1").update(key + GUID).digest("base64");
54146
+ const digest = createHash41("sha1").update(key + GUID).digest("base64");
54081
54147
  const headers = [
54082
54148
  "HTTP/1.1 101 Switching Protocols",
54083
54149
  "Upgrade: websocket",
@@ -162291,13 +162357,13 @@ var init_chat_attachment_files = __esm({
162291
162357
 
162292
162358
  // ../server/src/domains/chat-sessions/append-now.ts
162293
162359
  async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments, retainLocallyOnFailure = false) {
162294
- const newId = deps.newId ?? import_node_crypto20.randomUUID;
162360
+ const newId = deps.newId ?? import_node_crypto21.randomUUID;
162295
162361
  const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
162296
162362
  if (!deps.store.claimPendingInsert) {
162297
162363
  return { accepted: false, reason: "queue-unavailable", queued: false, delivered: "", batch: [] };
162298
162364
  }
162299
162365
  const { submitted, batch } = await deps.store.claimPendingInsert({
162300
- id: clientKey ? `append-request:${(0, import_node_crypto20.createHash)("sha256").update(JSON.stringify([chatSessionId, clientKey])).digest("hex")}` : newId(),
162366
+ id: clientKey ? `append-request:${(0, import_node_crypto21.createHash)("sha256").update(JSON.stringify([chatSessionId, clientKey])).digest("hex")}` : newId(),
162301
162367
  chatSessionId,
162302
162368
  text: text5,
162303
162369
  ...attachments?.length ? { attachments } : {},
@@ -162353,11 +162419,11 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
162353
162419
  }
162354
162420
  return { ...res, queued: !returnToClient, delivered: merged, batch, ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {} };
162355
162421
  }
162356
- var import_node_crypto20;
162422
+ var import_node_crypto21;
162357
162423
  var init_append_now = __esm({
162358
162424
  "../server/src/domains/chat-sessions/append-now.ts"() {
162359
162425
  "use strict";
162360
- import_node_crypto20 = require("node:crypto");
162426
+ import_node_crypto21 = require("node:crypto");
162361
162427
  init_chat_attachment_files();
162362
162428
  }
162363
162429
  });
@@ -163030,7 +163096,7 @@ function slug5(name) {
163030
163096
  return s2 || "user";
163031
163097
  }
163032
163098
  function short6() {
163033
- return (0, import_node_crypto21.randomBytes)(4).toString("hex").slice(0, 6);
163099
+ return (0, import_node_crypto22.randomBytes)(4).toString("hex").slice(0, 6);
163034
163100
  }
163035
163101
  async function assertBindingWritePermission(caller, targetHumanId, deps) {
163036
163102
  if (caller === targetHumanId) return true;
@@ -163095,11 +163161,11 @@ async function resolveWorkorderPrincipals(caller, cliOverrides, deps) {
163095
163161
  }
163096
163162
  return { error: "UNBOUND_AGENT_NO_OWNER", message: `owner \u65E0\u6CD5\u89E3\u6790\uFF1A\u8C03\u7528\u65B9 ${caller} \u65E2\u975E\u5728\u5C97\u771F\u4EBA\u4E5F\u975E\u5DF2\u7ED1\u52A9\u7406` };
163097
163163
  }
163098
- var import_node_crypto21, DEFAULT_ASSISTANT_PROMPT, DEFAULT_ASSISTANT_SKILLS, DEFAULT_ASSISTANT_RUNTIME_KIND, ASSISTANT_ARCHIVED_CODE, AssistantLazyCreateError, AssistantsService;
163164
+ var import_node_crypto22, DEFAULT_ASSISTANT_PROMPT, DEFAULT_ASSISTANT_SKILLS, DEFAULT_ASSISTANT_RUNTIME_KIND, ASSISTANT_ARCHIVED_CODE, AssistantLazyCreateError, AssistantsService;
163099
163165
  var init_assistants = __esm({
163100
163166
  "../server/src/domains/actors/assistants.ts"() {
163101
163167
  "use strict";
163102
- import_node_crypto21 = require("node:crypto");
163168
+ import_node_crypto22 = require("node:crypto");
163103
163169
  init_src();
163104
163170
  DEFAULT_ASSISTANT_PROMPT = "\u4F60\u662F\u8FD9\u540D\u771F\u4EBA\u5458\u5DE5\u7684\u4E13\u5C5E\u4E2A\u4EBA\u52A9\u7406\u3002\u4F60\u4EE5\u5176\u300C\u5DE5\u5355\u7BA1\u7406\u8005\u300D\u8EAB\u4EFD\u4EE3\u8DD1\uFF0C\u9075\u5FAA\u4E09\u6863\u6743\u9650\u6A21\u578B\uFF1A\u6863 A\uFF08\u52A0\u8FB9\u3001\u65B0\u5EFA\u8282\u70B9\u3001\u6539\u6807\u9898\u4E0E brief\u3001\u5E38\u89C4\u8BC4\u8BBA\u7B54\u7591\u3001\u63A8\u8FDB\u81EA\u5DF1\u63A5\u7684\u6D3B\uFF09\u53EF\u9006\u6539\u52A8\u76F4\u63A5\u751F\u6548\uFF1B\u6863 B\uFF08\u65AD\u8FB9\u3001\u5E9F\u8282\u70B9\u3001\u6539\u6D3E\u3001\u64A4\u90E8\u4EF6\uFF09\u9500\u6BC1\u6027\u6539\u52A8\u81EA\u52A8\u6253\u5305\u6210\u5F85\u786E\u8BA4\u63D0\u6848\uFF0C\u4EA4\u7531\u672C\u4EBA\u786E\u8BA4\u540E\u751F\u6548\uFF1B\u6863 C\uFF08force-conclude\u3001promote \u7B49\u6CBB\u7406\u547D\u4EE4\uFF09\u8D70\u4EBA\u5DE5\u6388\u6743\u5361\u3002\u4F60\u4E0D\u627F\u62C5\u4E1A\u52A1\u4E13\u4E1A\u5C97\u4F4D\u804C\u8D23\uFF0C\u53EA\u5728\u5176\u6388\u6743\u8303\u56F4\u5185\u534F\u52A9\u5EFA\u5355\u3001\u8DDF\u8FDB\u4E0E\u6C9F\u901A\u3002";
163105
163171
  DEFAULT_ASSISTANT_SKILLS = [];
@@ -165509,7 +165575,7 @@ var init_continuation3 = __esm({
165509
165575
 
165510
165576
  // ../server/src/chat-workorder-files.ts
165511
165577
  function workOrderFileId(workOrderId, blobId, name) {
165512
- return `wof:${(0, import_node_crypto22.createHash)("sha256").update(`${workOrderId}|${blobId}|${name}`).digest("hex").slice(0, 32)}`;
165578
+ return `wof:${(0, import_node_crypto23.createHash)("sha256").update(`${workOrderId}|${blobId}|${name}`).digest("hex").slice(0, 32)}`;
165513
165579
  }
165514
165580
  async function linkChatAttachmentsToWorkOrder(input) {
165515
165581
  const warnings = [];
@@ -165560,11 +165626,11 @@ async function linkChatAttachmentsToWorkOrder(input) {
165560
165626
  }
165561
165627
  return { linked, warnings };
165562
165628
  }
165563
- var import_node_crypto22;
165629
+ var import_node_crypto23;
165564
165630
  var init_chat_workorder_files = __esm({
165565
165631
  "../server/src/chat-workorder-files.ts"() {
165566
165632
  "use strict";
165567
- import_node_crypto22 = require("node:crypto");
165633
+ import_node_crypto23 = require("node:crypto");
165568
165634
  }
165569
165635
  });
165570
165636
 
@@ -166332,11 +166398,11 @@ var init_config_store = __esm({
166332
166398
  function clone2(value2) {
166333
166399
  return structuredClone(value2);
166334
166400
  }
166335
- var import_node_crypto23, import_promises2, import_node_path5, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
166401
+ var import_node_crypto24, import_promises2, import_node_path5, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
166336
166402
  var init_run_store = __esm({
166337
166403
  "../server/src/domains/knowledge/run-store.ts"() {
166338
166404
  "use strict";
166339
- import_node_crypto23 = require("node:crypto");
166405
+ import_node_crypto24 = require("node:crypto");
166340
166406
  import_promises2 = require("node:fs/promises");
166341
166407
  import_node_path5 = require("node:path");
166342
166408
  MemoryKnowledgeRunStore = class {
@@ -166453,7 +166519,7 @@ var init_run_store = __esm({
166453
166519
  }
166454
166520
  async persist(runs) {
166455
166521
  const shape = { version: 1, runs };
166456
- const temp = `${this.file}.${process.pid}.${(0, import_node_crypto23.randomUUID)()}.tmp`;
166522
+ const temp = `${this.file}.${process.pid}.${(0, import_node_crypto24.randomUUID)()}.tmp`;
166457
166523
  try {
166458
166524
  await (0, import_promises2.writeFile)(temp, `${JSON.stringify(shape, null, 2)}
166459
166525
  `, { mode: 384 });
@@ -167026,11 +167092,11 @@ function retryableKnowledgeRun(record8) {
167026
167092
  const latestAnswerAttempt = [...record8.attempts ?? []].reverse().find((attempt) => attempt.kind !== "fast");
167027
167093
  return record8.status === "succeeded" && record8.request.action === "ask" && record8.executionHealth === "degraded" && latestAnswerAttempt?.status === "failed";
167028
167094
  }
167029
- var import_node_crypto24, ACTION_LABEL, STATUS_MARKER, FINAL_ANSWER_MARKER, ACTIVE_STATUSES, RETRYABLE_STATUSES, HEARTBEAT_INTERVAL_MS, LEASE_MS, MAX_CONCURRENT_READS, ACTION_FLOW, KnowledgeService;
167095
+ var import_node_crypto25, ACTION_LABEL, STATUS_MARKER, FINAL_ANSWER_MARKER, ACTIVE_STATUSES, RETRYABLE_STATUSES, HEARTBEAT_INTERVAL_MS, LEASE_MS, MAX_CONCURRENT_READS, ACTION_FLOW, KnowledgeService;
167030
167096
  var init_service2 = __esm({
167031
167097
  "../server/src/domains/knowledge/service.ts"() {
167032
167098
  "use strict";
167033
- import_node_crypto24 = require("node:crypto");
167099
+ import_node_crypto25 = require("node:crypto");
167034
167100
  init_src5();
167035
167101
  init_config_store();
167036
167102
  init_run_store();
@@ -167073,7 +167139,7 @@ var init_service2 = __esm({
167073
167139
  constructor(options) {
167074
167140
  this.options = options;
167075
167141
  this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
167076
- this.id = options.id ?? import_node_crypto24.randomUUID;
167142
+ this.id = options.id ?? import_node_crypto25.randomUUID;
167077
167143
  this.runStore = options.runStore ?? new MemoryKnowledgeRunStore();
167078
167144
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;
167079
167145
  this.leaseMs = options.leaseMs ?? LEASE_MS;
@@ -178020,7 +178086,7 @@ function stableJson(value2) {
178020
178086
  return JSON.stringify(stableValue(value2));
178021
178087
  }
178022
178088
  function sha256(value2) {
178023
- return (0, import_node_crypto25.createHash)("sha256").update(value2).digest("hex");
178089
+ return (0, import_node_crypto26.createHash)("sha256").update(value2).digest("hex");
178024
178090
  }
178025
178091
  function normalizeKnowledgeContent(title, content3) {
178026
178092
  const body2 = content3.normalize("NFC").replace(/\r\n?/gu, "\n").replace(PUBLISH_MARKER, "\n").split("\n").map((line) => line.replace(/[\t ]+$/gu, "")).join("\n").replace(/\n{3,}/gu, "\n\n").trim();
@@ -178259,11 +178325,11 @@ function renderUntrustedKnowledgeFragment(input) {
178259
178325
  "</knowledge>"
178260
178326
  ].join("\n");
178261
178327
  }
178262
- var import_node_crypto25, KNOWLEDGE_NORMALIZER_VERSION, KNOWLEDGE_PACK_TEMPLATE_VERSION, PUBLISH_MARKER, STOPWORDS, LEXICAL_STOPWORDS, lexicalSegmenters, KNOWLEDGE_LEXICAL_INDEX_VERSION, KNOWLEDGE_MAINTENANCE_RELEVANCE_THRESHOLD, KNOWLEDGE_LAYOUT_TERMS;
178328
+ var import_node_crypto26, KNOWLEDGE_NORMALIZER_VERSION, KNOWLEDGE_PACK_TEMPLATE_VERSION, PUBLISH_MARKER, STOPWORDS, LEXICAL_STOPWORDS, lexicalSegmenters, KNOWLEDGE_LEXICAL_INDEX_VERSION, KNOWLEDGE_MAINTENANCE_RELEVANCE_THRESHOLD, KNOWLEDGE_LAYOUT_TERMS;
178263
178329
  var init_governance_content = __esm({
178264
178330
  "../server/src/domains/knowledge/governance-content.ts"() {
178265
178331
  "use strict";
178266
- import_node_crypto25 = require("node:crypto");
178332
+ import_node_crypto26 = require("node:crypto");
178267
178333
  init_mdast_util_from_markdown();
178268
178334
  init_mdast_util_gfm();
178269
178335
  init_micromark_extension_gfm();
@@ -188430,11 +188496,11 @@ function approvedSnapshotBody(title, content3) {
188430
188496
  function uniqueStrings(values) {
188431
188497
  return [...new Set(values)].sort();
188432
188498
  }
188433
- var import_node_crypto26, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
188499
+ var import_node_crypto27, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
188434
188500
  var init_governance_service = __esm({
188435
188501
  "../server/src/domains/knowledge/governance-service.ts"() {
188436
188502
  "use strict";
188437
- import_node_crypto26 = require("node:crypto");
188503
+ import_node_crypto27 = require("node:crypto");
188438
188504
  init_governance_content();
188439
188505
  init_knowledge_policy();
188440
188506
  init_network_maintenance();
@@ -188456,7 +188522,7 @@ var init_governance_service = __esm({
188456
188522
  constructor(options) {
188457
188523
  this.options = options;
188458
188524
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
188459
- this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto26.randomUUID)()}`);
188525
+ this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto27.randomUUID)()}`);
188460
188526
  this.versionViewBaseUrl = (options.versionViewBaseUrl ?? "/api/knowledge/items").replace(/\/$/u, "");
188461
188527
  this.projectProvisionRetryDelayMs = Math.max(0, options.projectProvisionRetryDelayMs ?? 6e4);
188462
188528
  this.projectProvisionMaxRetryDelayMs = Math.max(this.projectProvisionRetryDelayMs, options.projectProvisionMaxRetryDelayMs ?? 36e5);
@@ -194147,7 +194213,7 @@ function trustedKnowledgeInvocationId(body2, turn) {
194147
194213
  if (body2.action !== "ask") return void 0;
194148
194214
  const scope = turn?.artifactId ?? turn?.dispatchId;
194149
194215
  if (!scope) return void 0;
194150
- const fingerprint2 = (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify({
194216
+ const fingerprint2 = (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify({
194151
194217
  action: body2.action,
194152
194218
  message: body2.message?.trim() ?? "",
194153
194219
  queryMode: body2.queryMode === "deep" ? "deep" : "fast",
@@ -194302,11 +194368,11 @@ function knowledgeRoutes(service, options = {}) {
194302
194368
  });
194303
194369
  };
194304
194370
  }
194305
- var import_node_crypto27, KNOWLEDGE_WORKORDER_ACTIONS;
194371
+ var import_node_crypto28, KNOWLEDGE_WORKORDER_ACTIONS;
194306
194372
  var init_routes = __esm({
194307
194373
  "../server/src/domains/knowledge/routes.ts"() {
194308
194374
  "use strict";
194309
- import_node_crypto27 = require("node:crypto");
194375
+ import_node_crypto28 = require("node:crypto");
194310
194376
  init_src5();
194311
194377
  init_router();
194312
194378
  init_governance_service();
@@ -195933,7 +195999,7 @@ async function resolveChatAttachmentDiscoverySource(input) {
195933
195999
  sourceType: input.sourceType,
195934
196000
  sourceId: session.id,
195935
196001
  sourceVersionId: attachment.blobRef,
195936
- sourceFingerprint: (0, import_node_crypto28.createHash)("sha256").update(bytes2).digest("hex"),
196002
+ sourceFingerprint: (0, import_node_crypto29.createHash)("sha256").update(bytes2).digest("hex"),
195937
196003
  sourceContent: new TextDecoder().decode(bytes2),
195938
196004
  sourceDisplayName: attachment.name,
195939
196005
  ...session.projectId ? { projectId: session.projectId } : {},
@@ -195944,11 +196010,11 @@ async function resolveChatAttachmentDiscoverySource(input) {
195944
196010
  }
195945
196011
  };
195946
196012
  }
195947
- var import_node_crypto28, TEXT_CONTENT_TYPES;
196013
+ var import_node_crypto29, TEXT_CONTENT_TYPES;
195948
196014
  var init_chat_attachment_discovery = __esm({
195949
196015
  "../server/src/domains/knowledge/chat-attachment-discovery.ts"() {
195950
196016
  "use strict";
195951
- import_node_crypto28 = require("node:crypto");
196017
+ import_node_crypto29 = require("node:crypto");
195952
196018
  TEXT_CONTENT_TYPES = /* @__PURE__ */ new Set([
195953
196019
  "application/json",
195954
196020
  "application/xml",
@@ -196125,8 +196191,8 @@ function aad(snapshot) {
196125
196191
  }
196126
196192
  function sealKnowledgeSnapshot(snapshot, key) {
196127
196193
  if (snapshot.content.startsWith(PREFIX)) return structuredClone(snapshot);
196128
- const iv = (0, import_node_crypto29.randomBytes)(12);
196129
- const cipher = (0, import_node_crypto29.createCipheriv)("aes-256-gcm", keyBytes(key), iv);
196194
+ const iv = (0, import_node_crypto30.randomBytes)(12);
196195
+ const cipher = (0, import_node_crypto30.createCipheriv)("aes-256-gcm", keyBytes(key), iv);
196130
196196
  cipher.setAAD(aad(snapshot));
196131
196197
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
196132
196198
  return {
@@ -196139,17 +196205,17 @@ function openKnowledgeSnapshot(snapshot, key) {
196139
196205
  const packed = snapshot.content.slice(PREFIX.length).split(".");
196140
196206
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
196141
196207
  const [iv, tag, data] = packed;
196142
- const decipher = (0, import_node_crypto29.createDecipheriv)("aes-256-gcm", keyBytes(key), Buffer.from(iv, "base64url"));
196208
+ const decipher = (0, import_node_crypto30.createDecipheriv)("aes-256-gcm", keyBytes(key), Buffer.from(iv, "base64url"));
196143
196209
  decipher.setAAD(aad(snapshot));
196144
196210
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
196145
196211
  const content3 = Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8");
196146
196212
  return { ...structuredClone(snapshot), content: content3 };
196147
196213
  }
196148
- var import_node_crypto29, PREFIX;
196214
+ var import_node_crypto30, PREFIX;
196149
196215
  var init_snapshot_crypto = __esm({
196150
196216
  "../server/src/domains/knowledge/snapshot-crypto.ts"() {
196151
196217
  "use strict";
196152
- import_node_crypto29 = require("node:crypto");
196218
+ import_node_crypto30 = require("node:crypto");
196153
196219
  PREFIX = "oasis-knowledge:v1:";
196154
196220
  }
196155
196221
  });
@@ -196232,11 +196298,11 @@ function uniquenessKeys(kind, record8) {
196232
196298
  return [];
196233
196299
  }
196234
196300
  }
196235
- var import_node_crypto30, import_promises3, import_node_path6, RECORD_KINDS, MemoryKnowledgeGovernanceStore, FileKnowledgeGovernanceStore;
196301
+ var import_node_crypto31, import_promises3, import_node_path6, RECORD_KINDS, MemoryKnowledgeGovernanceStore, FileKnowledgeGovernanceStore;
196236
196302
  var init_governance_store = __esm({
196237
196303
  "../server/src/domains/knowledge/governance-store.ts"() {
196238
196304
  "use strict";
196239
- import_node_crypto30 = require("node:crypto");
196305
+ import_node_crypto31 = require("node:crypto");
196240
196306
  import_promises3 = require("node:fs/promises");
196241
196307
  import_node_path6 = require("node:path");
196242
196308
  init_snapshot_crypto();
@@ -196454,7 +196520,7 @@ var init_governance_store = __esm({
196454
196520
  return result;
196455
196521
  }
196456
196522
  async persist(shape) {
196457
- const temp = `${this.file}.${process.pid}.${(0, import_node_crypto30.randomUUID)()}.tmp`;
196523
+ const temp = `${this.file}.${process.pid}.${(0, import_node_crypto31.randomUUID)()}.tmp`;
196458
196524
  try {
196459
196525
  await (0, import_promises3.writeFile)(temp, `${JSON.stringify(this.sealSnapshots(shape), null, 2)}
196460
196526
  `, { mode: 384 });
@@ -196490,7 +196556,7 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
196490
196556
  if (error2.code !== "ENOENT") throw error2;
196491
196557
  }
196492
196558
  await (0, import_promises4.mkdir)((0, import_node_path7.dirname)(file), { recursive: true });
196493
- const generated = `oasis-${(0, import_node_crypto31.randomUUID)()}`;
196559
+ const generated = `oasis-${(0, import_node_crypto32.randomUUID)()}`;
196494
196560
  try {
196495
196561
  await (0, import_promises4.writeFile)(file, `${generated}
196496
196562
  `, { encoding: "utf8", mode: 384, flag: "wx" });
@@ -196500,11 +196566,11 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
196500
196566
  return readDeploymentId(file);
196501
196567
  }
196502
196568
  }
196503
- var import_node_crypto31, import_promises4, import_node_path7, VALID_DEPLOYMENT_ID;
196569
+ var import_node_crypto32, import_promises4, import_node_path7, VALID_DEPLOYMENT_ID;
196504
196570
  var init_deployment_id = __esm({
196505
196571
  "../server/src/domains/knowledge/deployment-id.ts"() {
196506
196572
  "use strict";
196507
- import_node_crypto31 = require("node:crypto");
196573
+ import_node_crypto32 = require("node:crypto");
196508
196574
  import_promises4 = require("node:fs/promises");
196509
196575
  import_node_path7 = require("node:path");
196510
196576
  VALID_DEPLOYMENT_ID = /^oasis-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
@@ -197201,7 +197267,7 @@ var init_files = __esm({
197201
197267
 
197202
197268
  // ../server/src/domains/delegations/service.ts
197203
197269
  function deterministicUuid(seed) {
197204
- const h = (0, import_node_crypto32.createHash)("sha256").update(seed).digest();
197270
+ const h = (0, import_node_crypto33.createHash)("sha256").update(seed).digest();
197205
197271
  const b2 = Buffer.from(h.subarray(0, 16));
197206
197272
  b2[6] = b2[6] & 15 | 80;
197207
197273
  b2[8] = b2[8] & 63 | 128;
@@ -197258,11 +197324,11 @@ async function seedChildRoundRows(chatStore, childSessionId, round, prompt2, now
197258
197324
  createdAt: now
197259
197325
  });
197260
197326
  }
197261
- var import_node_crypto32, DelegationError, TITLE_TASK_CHARS, DIGEST_CHARS, PENDING_QUEUE_OWNER, DISPATCH_LEASE_MS, CONSECUTIVE_FAIL_LIMIT, DelegationService;
197327
+ var import_node_crypto33, DelegationError, TITLE_TASK_CHARS, DIGEST_CHARS, PENDING_QUEUE_OWNER, DISPATCH_LEASE_MS, CONSECUTIVE_FAIL_LIMIT, DelegationService;
197262
197328
  var init_service3 = __esm({
197263
197329
  "../server/src/domains/delegations/service.ts"() {
197264
197330
  "use strict";
197265
- import_node_crypto32 = require("node:crypto");
197331
+ import_node_crypto33 = require("node:crypto");
197266
197332
  init_src();
197267
197333
  init_parent_conversation();
197268
197334
  init_deletion();
@@ -198464,7 +198530,7 @@ var init_continue_outcome = __esm({
198464
198530
  function trustedDelegationInvocationId(body2, turn) {
198465
198531
  const scope = turn?.artifactId ?? turn?.dispatchId;
198466
198532
  if (!scope) return void 0;
198467
- const fingerprint2 = (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify({ to: body2.to?.trim() ?? "", task: body2.task ?? "", as: body2.as?.trim() ?? "", files: body2.files ?? [] })).digest("hex").slice(0, 20);
198533
+ const fingerprint2 = (0, import_node_crypto34.createHash)("sha256").update(JSON.stringify({ to: body2.to?.trim() ?? "", task: body2.task ?? "", as: body2.as?.trim() ?? "", files: body2.files ?? [] })).digest("hex").slice(0, 20);
198468
198534
  return `dispatch:${scope}:delegation:${fingerprint2}`;
198469
198535
  }
198470
198536
  function callerOf(req, invocationId) {
@@ -198559,11 +198625,11 @@ function delegationRoutes(service) {
198559
198625
  });
198560
198626
  };
198561
198627
  }
198562
- var import_node_crypto33;
198628
+ var import_node_crypto34;
198563
198629
  var init_routes2 = __esm({
198564
198630
  "../server/src/domains/delegations/routes.ts"() {
198565
198631
  "use strict";
198566
- import_node_crypto33 = require("node:crypto");
198632
+ import_node_crypto34 = require("node:crypto");
198567
198633
  init_router();
198568
198634
  init_continue_outcome();
198569
198635
  init_service3();
@@ -200517,7 +200583,7 @@ function createAppJwt(appId, privateKeyPem, nowMs) {
200517
200583
  exp: nowSec + JWT_TTL_SEC,
200518
200584
  iss: appId
200519
200585
  }));
200520
- const signer = (0, import_node_crypto34.createSign)("RSA-SHA256");
200586
+ const signer = (0, import_node_crypto35.createSign)("RSA-SHA256");
200521
200587
  signer.update(`${header}.${payload2}`);
200522
200588
  signer.end();
200523
200589
  try {
@@ -200680,11 +200746,11 @@ async function exchangeManifestCode(code2, deps = {}) {
200680
200746
  ...typeof body2["client_secret"] === "string" ? { clientSecret: body2["client_secret"] } : {}
200681
200747
  };
200682
200748
  }
200683
- var import_node_crypto34, GITHUB_API, JWT_TTL_SEC, JWT_IAT_SKEW_SEC, defaultDeps2;
200749
+ var import_node_crypto35, GITHUB_API, JWT_TTL_SEC, JWT_IAT_SKEW_SEC, defaultDeps2;
200684
200750
  var init_app_auth = __esm({
200685
200751
  "../connectors/src/github/app-auth.ts"() {
200686
200752
  "use strict";
200687
- import_node_crypto34 = require("node:crypto");
200753
+ import_node_crypto35 = require("node:crypto");
200688
200754
  GITHUB_API = "https://api.github.com";
200689
200755
  JWT_TTL_SEC = 9 * 60;
200690
200756
  JWT_IAT_SKEW_SEC = 60;
@@ -201609,11 +201675,11 @@ function extractOutlineKnowledgeCandidateDrafts(events) {
201609
201675
  }
201610
201676
  return [...drafts.values()];
201611
201677
  }
201612
- var import_node_crypto35, REQUEST_TIMEOUT_MS, WRITE_ENDPOINTS, DEFAULT_WRITE_INTERVAL_MS, DEFAULT_RATE_LIMIT_RETRIES, DEFAULT_MAX_RATE_LIMIT_DELAY_MS, OutlineKnowledgeApiError, writeCoordinators, OutlineKnowledgeApi, INIT_DEFINITIONS, REPAIRABLE_LINT_CODES;
201678
+ var import_node_crypto36, REQUEST_TIMEOUT_MS, WRITE_ENDPOINTS, DEFAULT_WRITE_INTERVAL_MS, DEFAULT_RATE_LIMIT_RETRIES, DEFAULT_MAX_RATE_LIMIT_DELAY_MS, OutlineKnowledgeApiError, writeCoordinators, OutlineKnowledgeApi, INIT_DEFINITIONS, REPAIRABLE_LINT_CODES;
201613
201679
  var init_knowledge_api = __esm({
201614
201680
  "../connectors/src/outline/knowledge-api.ts"() {
201615
201681
  "use strict";
201616
- import_node_crypto35 = require("node:crypto");
201682
+ import_node_crypto36 = require("node:crypto");
201617
201683
  REQUEST_TIMEOUT_MS = 2e4;
201618
201684
  WRITE_ENDPOINTS = /* @__PURE__ */ new Set(["collections.create", "documents.create", "documents.update", "documents.publish"]);
201619
201685
  DEFAULT_WRITE_INTERVAL_MS = process.env["NODE_ENV"] === "test" ? 0 : 250;
@@ -201640,7 +201706,7 @@ var init_knowledge_api = __esm({
201640
201706
  this.baseUrl = canonicalUrl(baseUrl);
201641
201707
  this.lastResponseBaseUrl = this.baseUrl;
201642
201708
  this.token = token;
201643
- this.coordinatorKey = `${this.baseUrl}:${(0, import_node_crypto35.createHash)("sha256").update(token).digest("hex")}`;
201709
+ this.coordinatorKey = `${this.baseUrl}:${(0, import_node_crypto36.createHash)("sha256").update(token).digest("hex")}`;
201644
201710
  this.writeIntervalMs = Math.max(0, options.writeIntervalMs ?? DEFAULT_WRITE_INTERVAL_MS);
201645
201711
  this.maxRateLimitRetries = Math.max(0, options.maxRateLimitRetries ?? DEFAULT_RATE_LIMIT_RETRIES);
201646
201712
  this.maxRateLimitDelayMs = Math.max(0, options.maxRateLimitDelayMs ?? DEFAULT_MAX_RATE_LIMIT_DELAY_MS);
@@ -203048,15 +203114,15 @@ function classifyOutlineGovernedMigrationDocument(document4) {
203048
203114
  if (path41.startsWith("wiki/sources/")) return { mapping: "formal", metadata: legacyMetadata(document4, "source_note"), reason: "legacy_source_note" };
203049
203115
  return { mapping: "excluded", reason: "legacy_unmapped_path" };
203050
203116
  }
203051
- var import_node_crypto36, value, text4, hash, REVISION_VISIBILITY_RETRY_DELAYS_MS, governedProvisionRuns, PUBLISH_MARKER2, PUBLISH_MARKER_PRESENT;
203117
+ var import_node_crypto37, value, text4, hash, REVISION_VISIBILITY_RETRY_DELAYS_MS, governedProvisionRuns, PUBLISH_MARKER2, PUBLISH_MARKER_PRESENT;
203052
203118
  var init_governed_knowledge = __esm({
203053
203119
  "../connectors/src/outline/governed-knowledge.ts"() {
203054
203120
  "use strict";
203055
- import_node_crypto36 = require("node:crypto");
203121
+ import_node_crypto37 = require("node:crypto");
203056
203122
  init_knowledge_api();
203057
203123
  value = (input) => input && typeof input === "object" && !Array.isArray(input) ? input : null;
203058
203124
  text4 = (input) => typeof input === "string" && input.trim() ? input.trim() : null;
203059
- hash = (input) => (0, import_node_crypto36.createHash)("sha256").update(input.normalize("NFC").replace(/\r\n?/gu, "\n")).digest("hex");
203125
+ hash = (input) => (0, import_node_crypto37.createHash)("sha256").update(input.normalize("NFC").replace(/\r\n?/gu, "\n")).digest("hex");
203060
203126
  REVISION_VISIBILITY_RETRY_DELAYS_MS = [0, 100, 250, 500, 1e3, 2e3];
203061
203127
  governedProvisionRuns = /* @__PURE__ */ new Map();
203062
203128
  PUBLISH_MARKER2 = /\n?<!--\s*oasis-publish:[\s\S]*?-->\n?/giu;
@@ -205491,14 +205557,7 @@ async function startOasisServer(opts) {
205491
205557
  }
205492
205558
  return null;
205493
205559
  };
205494
- const outputsPartFor = (chatSessionId, companyId) => {
205495
- const scan2 = opts.sessionOutputsScan;
205496
- if (!scan2) return void 0;
205497
- return async () => {
205498
- const files = await scan2({ chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
205499
- return files && files.length > 0 ? { type: "outputs", seq: 0, outputs: files } : null;
205500
- };
205501
- };
205560
+ const outputsPartFor = (chatSessionId, companyId) => outputsPartProvider(opts.sessionOutputsScan, chatSessionId, companyId);
205502
205561
  async function anchorMessageIdFor(origin, storeFor) {
205503
205562
  if (!origin) return void 0;
205504
205563
  const live = liveChat.messageFor(origin);
@@ -205737,10 +205796,10 @@ async function startOasisServer(opts) {
205737
205796
  ...fp(p2)
205738
205797
  }))
205739
205798
  ] : [];
205740
- const { randomUUID: randomUUID42 } = await import("node:crypto");
205799
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
205741
205800
  try {
205742
205801
  await store.enqueuePendingMessage({
205743
- id: randomUUID42(),
205802
+ id: randomUUID43(),
205744
205803
  chatSessionId: input.chatSessionId,
205745
205804
  text: input.text,
205746
205805
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -205773,11 +205832,11 @@ async function startOasisServer(opts) {
205773
205832
  ...f2?.mtime ? { srcMtime: f2.mtime } : {}
205774
205833
  };
205775
205834
  });
205776
- const { randomUUID: randomUUID42 } = await import("node:crypto");
205835
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
205777
205836
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
205778
205837
  try {
205779
205838
  await store.enqueuePendingMessage({
205780
- id: randomUUID42(),
205839
+ id: randomUUID43(),
205781
205840
  chatSessionId: input.chatSessionId,
205782
205841
  text: "",
205783
205842
  createdAt: nowIso,
@@ -207024,7 +207083,7 @@ async function startOasisServer(opts) {
207024
207083
  entry = {
207025
207084
  // 旧 CLI 未带 waiterId 时给它塞一个服务端本地生成的——它绝不可能出现在 leave 上,
207026
207085
  // 也就绝不会误命中 leftWaiterIds、绝不会被别人的 leave 摘掉。行为与旧 CLI 相同。
207027
- waiterId: clientWaiterId ?? `server:${(0, import_node_crypto37.randomUUID)()}`,
207086
+ waiterId: clientWaiterId ?? `server:${(0, import_node_crypto38.randomUUID)()}`,
207028
207087
  onResolve,
207029
207088
  expiresAt: expiresAt2,
207030
207089
  cleanup: () => clearTimeout(expiryTimer),
@@ -207105,7 +207164,7 @@ async function startOasisServer(opts) {
207105
207164
  const openRoom = async () => {
207106
207165
  let chatSessionId2;
207107
207166
  if (chatStore) {
207108
- const { randomUUID: randomUUID42 } = await import("node:crypto");
207167
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
207109
207168
  const known = discussSessions.get(itemKey);
207110
207169
  const knownSession = known ? await chatStore.getSession(known) : null;
207111
207170
  if (knownSession && knownSession.workspace === ctx.workspace && knownSession.humanActorId === ctx.ownerId && (!body2.escalationId || knownSession.escalationId === body2.escalationId)) chatSessionId2 = known;
@@ -207114,7 +207173,7 @@ async function startOasisServer(opts) {
207114
207173
  chatSessionId2 = existing[0]?.id;
207115
207174
  }
207116
207175
  if (!chatSessionId2) {
207117
- chatSessionId2 = randomUUID42();
207176
+ chatSessionId2 = randomUUID43();
207118
207177
  const now = (/* @__PURE__ */ new Date()).toISOString();
207119
207178
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
207120
207179
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
@@ -207983,14 +208042,14 @@ ${composed}`;
207983
208042
  runtimeSessionId: session.nativeSessionId ?? session.id
207984
208043
  }).catch(() => void 0);
207985
208044
  }
207986
- const { randomUUID: randomUUID43 } = await import("node:crypto");
208045
+ const { randomUUID: randomUUID44 } = await import("node:crypto");
207987
208046
  const persistAttachments = effectiveAttachments.map((a) => ({
207988
208047
  name: a.name,
207989
208048
  ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
207990
208049
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
207991
208050
  }));
207992
208051
  await chatStore.appendMessage({
207993
- id: randomUUID43(),
208052
+ id: randomUUID44(),
207994
208053
  sessionId: persistTarget.id,
207995
208054
  role: "user",
207996
208055
  content: persistedUserMessage,
@@ -208465,8 +208524,8 @@ ${composed}`;
208465
208524
  }
208466
208525
  }
208467
208526
  const { spawn: spawn10 } = await import("node:child_process");
208468
- const { randomUUID: randomUUID42 } = await import("node:crypto");
208469
- const sessionId = body2.sessionId ?? randomUUID42();
208527
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
208528
+ const sessionId = body2.sessionId ?? randomUUID43();
208470
208529
  const args = [
208471
208530
  "-p",
208472
208531
  body2.message,
@@ -208762,8 +208821,8 @@ ${composed}`;
208762
208821
  res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: `\u56DE\u8C03\u5730\u5740\u4E0D\u53EF\u7528\uFF0C\u5DF2\u4E2D\u6B62\uFF08\u907F\u514D\u5728 GitHub \u4E0A\u7559\u4E0B\u5B64\u513F App\uFF09\uFF1A${e.message}` }));
208763
208822
  return;
208764
208823
  }
208765
- const { randomUUID: randomUUID42 } = await import("node:crypto");
208766
- const state = randomUUID42();
208824
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
208825
+ const state = randomUUID43();
208767
208826
  const nameSlug = (actorName || actorId?.split(":").pop() || owner?.login || "bot").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "bot";
208768
208827
  const manifest = buildAppManifest({
208769
208828
  employeeSlug: nameSlug,
@@ -209539,12 +209598,12 @@ ${composed}`;
209539
209598
  }
209540
209599
  };
209541
209600
  }
209542
- var http, import_node_crypto37, githubAppPending, connectorAuthSnapshots, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
209601
+ var http, import_node_crypto38, githubAppPending, connectorAuthSnapshots, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
209543
209602
  var init_server3 = __esm({
209544
209603
  "../server/src/server.ts"() {
209545
209604
  "use strict";
209546
209605
  http = __toESM(require("node:http"), 1);
209547
- import_node_crypto37 = require("node:crypto");
209606
+ import_node_crypto38 = require("node:crypto");
209548
209607
  init_chat_session();
209549
209608
  init_chat_system_message();
209550
209609
  init_src();
@@ -209703,7 +209762,7 @@ function createNodeTokenStore(file) {
209703
209762
  return {
209704
209763
  issue(nodeId) {
209705
209764
  const table = read();
209706
- const token = `ont_${(0, import_node_crypto38.randomBytes)(24).toString("base64url")}`;
209765
+ const token = `ont_${(0, import_node_crypto39.randomBytes)(24).toString("base64url")}`;
209707
209766
  table[token] = nodeId;
209708
209767
  write(table);
209709
209768
  return token;
@@ -209745,7 +209804,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
209745
209804
  return {
209746
209805
  issue(name, ttlMs = defaultTtlMs, companyId) {
209747
209806
  const m2 = prune();
209748
- const token = `ent_${(0, import_node_crypto38.randomBytes)(24).toString("base64url")}`;
209807
+ const token = `ent_${(0, import_node_crypto39.randomBytes)(24).toString("base64url")}`;
209749
209808
  const expiresAt2 = Date.now() + ttlMs;
209750
209809
  m2.set(token, { name, expiresAt: expiresAt2, ...companyId !== void 0 ? { companyId } : {} });
209751
209810
  write(m2);
@@ -209794,33 +209853,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
209794
209853
  }
209795
209854
  };
209796
209855
  }
209797
- var import_node_crypto38, fs18, path19, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
209856
+ var import_node_crypto39, fs18, path19, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
209798
209857
  var init_tokens = __esm({
209799
209858
  "../server/src/tokens.ts"() {
209800
209859
  "use strict";
209801
- import_node_crypto38 = require("node:crypto");
209860
+ import_node_crypto39 = require("node:crypto");
209802
209861
  fs18 = __toESM(require("node:fs"), 1);
209803
209862
  path19 = __toESM(require("node:path"), 1);
209804
209863
  SESSION_TOKEN_PREFIX = "oat_v2_";
209805
209864
  b64url2 = (value2) => Buffer.from(value2).toString("base64url");
209806
209865
  fromB64url = (value2) => Buffer.from(value2, "base64url").toString("utf8");
209807
209866
  readOrCreateSecret = (file) => {
209808
- if (!file) return (0, import_node_crypto38.randomBytes)(32);
209867
+ if (!file) return (0, import_node_crypto39.randomBytes)(32);
209809
209868
  try {
209810
209869
  const raw = fs18.readFileSync(file, "utf8").trim();
209811
209870
  if (raw) return Buffer.from(raw, "base64url");
209812
209871
  } catch {
209813
209872
  }
209814
- const secret = (0, import_node_crypto38.randomBytes)(32);
209873
+ const secret = (0, import_node_crypto39.randomBytes)(32);
209815
209874
  fs18.mkdirSync(path19.dirname(file), { recursive: true });
209816
209875
  fs18.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
209817
209876
  return secret;
209818
209877
  };
209819
- sign = (secret, payload2) => (0, import_node_crypto38.createHmac)("sha256", secret).update(payload2).digest("base64url");
209878
+ sign = (secret, payload2) => (0, import_node_crypto39.createHmac)("sha256", secret).update(payload2).digest("base64url");
209820
209879
  signatureMatches = (actual, expected) => {
209821
209880
  const a = Buffer.from(actual);
209822
209881
  const b2 = Buffer.from(expected);
209823
- return a.length === b2.length && (0, import_node_crypto38.timingSafeEqual)(a, b2);
209882
+ return a.length === b2.length && (0, import_node_crypto39.timingSafeEqual)(a, b2);
209824
209883
  };
209825
209884
  isTokenClaims = (value2) => {
209826
209885
  if (value2 === null || typeof value2 !== "object") return false;
@@ -209867,31 +209926,31 @@ function createDataplaneTokenIssuer(opts = {}) {
209867
209926
  }
209868
209927
  };
209869
209928
  }
209870
- var import_node_crypto39, fs19, path20, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims;
209929
+ var import_node_crypto40, fs19, path20, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims;
209871
209930
  var init_session_dataplane_token = __esm({
209872
209931
  "../server/src/session-dataplane-token.ts"() {
209873
209932
  "use strict";
209874
- import_node_crypto39 = require("node:crypto");
209933
+ import_node_crypto40 = require("node:crypto");
209875
209934
  fs19 = __toESM(require("node:fs"), 1);
209876
209935
  path20 = __toESM(require("node:path"), 1);
209877
209936
  init_src();
209878
209937
  PREFIX2 = "osd_";
209879
209938
  b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
209880
209939
  fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
209881
- sign2 = (secret, payload2) => (0, import_node_crypto39.createHmac)("sha256", secret).update(payload2).digest("base64url");
209940
+ sign2 = (secret, payload2) => (0, import_node_crypto40.createHmac)("sha256", secret).update(payload2).digest("base64url");
209882
209941
  sigMatches = (actual, expected) => {
209883
209942
  const a = Buffer.from(actual);
209884
209943
  const b2 = Buffer.from(expected);
209885
- return a.length === b2.length && (0, import_node_crypto39.timingSafeEqual)(a, b2);
209944
+ return a.length === b2.length && (0, import_node_crypto40.timingSafeEqual)(a, b2);
209886
209945
  };
209887
209946
  readOrCreateSecret2 = (file) => {
209888
- if (!file) return (0, import_node_crypto39.randomBytes)(32);
209947
+ if (!file) return (0, import_node_crypto40.randomBytes)(32);
209889
209948
  try {
209890
209949
  const raw = fs19.readFileSync(file, "utf8").trim();
209891
209950
  if (raw) return Buffer.from(raw, "base64url");
209892
209951
  } catch {
209893
209952
  }
209894
- const secret = (0, import_node_crypto39.randomBytes)(32);
209953
+ const secret = (0, import_node_crypto40.randomBytes)(32);
209895
209954
  fs19.mkdirSync(path20.dirname(file), { recursive: true });
209896
209955
  fs19.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
209897
209956
  return secret;
@@ -212037,11 +212096,11 @@ function onlyHits(pool, q2) {
212037
212096
  if (!q2.requireMatch || q2.keywords.length === 0) return [...pool];
212038
212097
  return pool.filter((r) => memoryMatchScore(r, q2.keywords) > 0);
212039
212098
  }
212040
- var import_node_crypto40, MemoryActorMemoryStore, toIndexEntry;
212099
+ var import_node_crypto41, MemoryActorMemoryStore, toIndexEntry;
212041
212100
  var init_memory_actor_memory_store = __esm({
212042
212101
  "../testkit/src/memory-actor-memory-store.ts"() {
212043
212102
  "use strict";
212044
- import_node_crypto40 = require("node:crypto");
212103
+ import_node_crypto41 = require("node:crypto");
212045
212104
  init_src();
212046
212105
  MemoryActorMemoryStore = class {
212047
212106
  rows = /* @__PURE__ */ new Map();
@@ -212095,7 +212154,7 @@ var init_memory_actor_memory_store = __esm({
212095
212154
  async write(input, now) {
212096
212155
  if (input.memId === void 0) {
212097
212156
  const rec = {
212098
- memId: `mem:${(0, import_node_crypto40.randomUUID)()}`,
212157
+ memId: `mem:${(0, import_node_crypto41.randomUUID)()}`,
212099
212158
  actorId: input.actorId,
212100
212159
  projectId: input.projectId,
212101
212160
  keywords: [...input.keywords],
@@ -212829,13 +212888,13 @@ var init_stats = __esm({
212829
212888
 
212830
212889
  // ../server/src/domains/projects/project-id.ts
212831
212890
  function randomProjectId() {
212832
- return projectIdFromUuid((0, import_node_crypto41.randomUUID)());
212891
+ return projectIdFromUuid((0, import_node_crypto42.randomUUID)());
212833
212892
  }
212834
- var import_node_crypto41;
212893
+ var import_node_crypto42;
212835
212894
  var init_project_id = __esm({
212836
212895
  "../server/src/domains/projects/project-id.ts"() {
212837
212896
  "use strict";
212838
- import_node_crypto41 = require("node:crypto");
212897
+ import_node_crypto42 = require("node:crypto");
212839
212898
  init_src();
212840
212899
  }
212841
212900
  });
@@ -212900,7 +212959,7 @@ function creatorKindOf(actorId) {
212900
212959
  if (!actorId) return null;
212901
212960
  return actorId.startsWith("actor:human:") ? "human" : "agent";
212902
212961
  }
212903
- function emitChatFolders(items, chatFiles, resolveName) {
212962
+ function emitChatFolders(items, chatFiles, resolveName, taskTitles = /* @__PURE__ */ new Map()) {
212904
212963
  const groups = /* @__PURE__ */ new Map();
212905
212964
  for (const file of chatFiles) {
212906
212965
  const list2 = groups.get(file.chatSessionId) ?? [];
@@ -212920,14 +212979,15 @@ function emitChatFolders(items, chatFiles, resolveName) {
212920
212979
  depth: 0,
212921
212980
  type: "folder",
212922
212981
  size: null,
212923
- taskId: null,
212924
- taskTitle: CHAT_FOLDER_TASK_LABEL,
212982
+ taskId: head.taskId ?? null,
212983
+ taskTitle: head.taskId ? taskTitles.get(head.taskId) ?? head.taskId : CHAT_FOLDER_TASK_LABEL,
212925
212984
  creatorKind: creatorKindOf(head.chatOwnerId),
212926
212985
  creatorId: head.chatOwnerId,
212927
212986
  creatorName: resolveName(head.chatOwnerId),
212928
212987
  updatedAt: latest,
212929
212988
  folderKind: "chat",
212930
- chatSessionId: sessionId
212989
+ chatSessionId: sessionId,
212990
+ ...head.fileIssues?.length ? { fileIssues: head.fileIssues } : {}
212931
212991
  });
212932
212992
  for (const file of group) {
212933
212993
  items.push({
@@ -212941,13 +213001,14 @@ function emitChatFolders(items, chatFiles, resolveName) {
212941
213001
  blobRef: file.blobId,
212942
213002
  contentType: file.contentType ?? null,
212943
213003
  size: file.size,
212944
- taskId: null,
212945
- taskTitle: CHAT_FOLDER_TASK_LABEL,
213004
+ taskId: head.taskId ?? null,
213005
+ taskTitle: head.taskId ? taskTitles.get(head.taskId) ?? head.taskId : CHAT_FOLDER_TASK_LABEL,
212946
213006
  creatorKind: creatorKindOf(file.uploadedBy),
212947
213007
  creatorId: file.uploadedBy ?? null,
212948
213008
  creatorName: file.uploadedBy ? resolveName(file.uploadedBy) : null,
212949
213009
  updatedAt: file.uploadedAt,
212950
- chatSessionId: sessionId
213010
+ chatSessionId: sessionId,
213011
+ ...head.fileIssues?.length ? { fileIssues: head.fileIssues } : {}
212951
213012
  });
212952
213013
  }
212953
213014
  }
@@ -214009,7 +214070,7 @@ var init_service4 = __esm({
214009
214070
  updatedAt: file.uploadedAt
214010
214071
  });
214011
214072
  }
214012
- emitChatFolders(items, chatFiles, (actorId) => this.resolveActorName(actorId));
214073
+ emitChatFolders(items, chatFiles, (actorId) => this.resolveActorName(actorId), workorderTitleById);
214013
214074
  fillFolderSizes(items);
214014
214075
  const chatFolderTime = new Map(items.filter((item) => item.folderKind === "chat" && item.chatSessionId).map((item) => [item.chatSessionId, item.updatedAt]));
214015
214076
  items.sort((a, b2) => {
@@ -214860,12 +214921,12 @@ var init_service4 = __esm({
214860
214921
  });
214861
214922
 
214862
214923
  // ../server/src/dev-store.ts
214863
- var import_node_crypto42, fs20, path21, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
214924
+ var import_node_crypto43, fs20, path21, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
214864
214925
  var init_dev_store = __esm({
214865
214926
  "../server/src/dev-store.ts"() {
214866
214927
  "use strict";
214867
214928
  init_src();
214868
- import_node_crypto42 = require("node:crypto");
214929
+ import_node_crypto43 = require("node:crypto");
214869
214930
  fs20 = __toESM(require("node:fs"), 1);
214870
214931
  path21 = __toESM(require("node:path"), 1);
214871
214932
  init_src();
@@ -214919,7 +214980,7 @@ var init_dev_store = __esm({
214919
214980
  return path21.join(this.dir, hash2);
214920
214981
  }
214921
214982
  async put(bytes2) {
214922
- const hash2 = (0, import_node_crypto42.createHash)("sha256").update(bytes2).digest("hex");
214983
+ const hash2 = (0, import_node_crypto43.createHash)("sha256").update(bytes2).digest("hex");
214923
214984
  const file = this.fileOf(hash2);
214924
214985
  if (!fs20.existsSync(file)) fs20.writeFileSync(file, bytes2);
214925
214986
  return hash2;
@@ -215347,6 +215408,8 @@ var init_dev_store = __esm({
215347
215408
  pending = /* @__PURE__ */ new Map();
215348
215409
  /** `outputs/` 产出文件的服务端副本表(2026-09-14):sessionId → 路径 → 副本。 */
215349
215410
  outputCopies = /* @__PURE__ */ new Map();
215411
+ outputFingerprints = /* @__PURE__ */ new Map();
215412
+ fileIssues = /* @__PURE__ */ new Map();
215350
215413
  pendingSeq = 0;
215351
215414
  async claimPendingInsert(m2) {
215352
215415
  const list2 = this.pending.get(m2.chatSessionId) ?? [];
@@ -215535,6 +215598,8 @@ var init_dev_store = __esm({
215535
215598
  this.sessionWorkOrders.delete(id);
215536
215599
  this.sessionWorkOrderKinds.delete(id);
215537
215600
  this.outputCopies.delete(id);
215601
+ this.outputFingerprints.delete(id);
215602
+ this.fileIssues.delete(id);
215538
215603
  }
215539
215604
  async appendMessage(m2) {
215540
215605
  return (await this.appendMessageOnce(m2)).message;
@@ -215663,6 +215728,22 @@ var init_dev_store = __esm({
215663
215728
  }
215664
215729
  return rows.sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.messageId.localeCompare(right.messageId));
215665
215730
  }
215731
+ async readOutputFingerprints(sessionId) {
215732
+ return this.outputFingerprints.get(sessionId) ?? null;
215733
+ }
215734
+ async writeOutputFingerprints(sessionId, fingerprints) {
215735
+ if (this.sessions.has(sessionId)) this.outputFingerprints.set(sessionId, { ...fingerprints });
215736
+ }
215737
+ async readFileIssues(sessionId) {
215738
+ return (this.fileIssues.get(sessionId) ?? []).map((issue2) => ({ ...issue2 }));
215739
+ }
215740
+ async writeFileIssues(sessionId, phase, issues) {
215741
+ if (!this.sessions.has(sessionId)) return;
215742
+ this.fileIssues.set(sessionId, [...(this.fileIssues.get(sessionId) ?? []).filter((i) => i.phase !== phase), ...issues]);
215743
+ }
215744
+ async listFileSessionsByWorkspaces(workspaces) {
215745
+ return [...this.sessions.values()].filter((s2) => !!s2.workspace && workspaces.includes(s2.workspace) && s2.visibility !== "internal" && !s2.analyzedRunId).map((s2) => ({ ...s2 }));
215746
+ }
215666
215747
  async readOutputCopies(sessionId) {
215667
215748
  const hit = this.outputCopies.get(sessionId);
215668
215749
  return hit ? { ...hit } : null;
@@ -215723,6 +215804,9 @@ var init_dev_store = __esm({
215723
215804
  const store = new _FileChatSessionStore(file);
215724
215805
  if (fs20.existsSync(file)) {
215725
215806
  const snap = JSON.parse(fs20.readFileSync(file, "utf8"));
215807
+ for (const [sid, copies] of Object.entries(snap.outputCopies ?? {})) store.outputCopies.set(sid, copies);
215808
+ for (const [sid, fps] of Object.entries(snap.outputFingerprints ?? {})) store.outputFingerprints.set(sid, fps);
215809
+ for (const [sid, issues] of Object.entries(snap.fileIssues ?? {})) store.fileIssues.set(sid, issues);
215726
215810
  for (const s2 of snap.sessions ?? []) store.sessions.set(s2.id, s2);
215727
215811
  for (const [sid, list2] of Object.entries(snap.messages ?? {})) store.messages.set(sid, list2);
215728
215812
  for (const [sid, ids2] of Object.entries(snap.sessionWorkOrders ?? {})) store.sessionWorkOrders.set(sid, new Set(ids2));
@@ -215734,6 +215818,18 @@ var init_dev_store = __esm({
215734
215818
  }
215735
215819
  return store;
215736
215820
  }
215821
+ async mergeOutputCopies(id, copies) {
215822
+ await super.mergeOutputCopies(id, copies);
215823
+ this.save();
215824
+ }
215825
+ async writeOutputFingerprints(id, fps) {
215826
+ await super.writeOutputFingerprints(id, fps);
215827
+ this.save();
215828
+ }
215829
+ async writeFileIssues(id, phase, issues) {
215830
+ await super.writeFileIssues(id, phase, issues);
215831
+ this.save();
215832
+ }
215737
215833
  async createSession(s2) {
215738
215834
  await super.createSession(s2);
215739
215835
  this.save();
@@ -215799,6 +215895,9 @@ var init_dev_store = __esm({
215799
215895
  }
215800
215896
  save() {
215801
215897
  const snap = {
215898
+ outputCopies: Object.fromEntries(this.outputCopies),
215899
+ outputFingerprints: Object.fromEntries(this.outputFingerprints),
215900
+ fileIssues: Object.fromEntries(this.fileIssues),
215802
215901
  sessions: [...this.sessions.values()],
215803
215902
  messages: Object.fromEntries(this.messages),
215804
215903
  sessionWorkOrders: Object.fromEntries([...this.sessionWorkOrders].map(([k2, v2]) => [k2, [...v2]])),
@@ -216119,7 +216218,7 @@ var init_company_engine_router = __esm({
216119
216218
 
216120
216219
  // ../server/src/skill-market-client.ts
216121
216220
  function digestOf2(bytes2) {
216122
- return `sha256:${(0, import_node_crypto43.createHash)("sha256").update(bytes2).digest("hex")}`;
216221
+ return `sha256:${(0, import_node_crypto44.createHash)("sha256").update(bytes2).digest("hex")}`;
216123
216222
  }
216124
216223
  function createSkillMarketClientFromEnv(env = process.env) {
216125
216224
  const base = env["SKILL_MARKET_BASE_URL"];
@@ -216130,11 +216229,11 @@ function createSkillMarketClientFromEnv(env = process.env) {
216130
216229
  function sleep4(ms) {
216131
216230
  return new Promise((r) => setTimeout(r, ms));
216132
216231
  }
216133
- var import_node_crypto43, DisabledSkillMarketClient, HttpSkillMarketClient;
216232
+ var import_node_crypto44, DisabledSkillMarketClient, HttpSkillMarketClient;
216134
216233
  var init_skill_market_client = __esm({
216135
216234
  "../server/src/skill-market-client.ts"() {
216136
216235
  "use strict";
216137
- import_node_crypto43 = require("node:crypto");
216236
+ import_node_crypto44 = require("node:crypto");
216138
216237
  init_src();
216139
216238
  DisabledSkillMarketClient = class {
216140
216239
  async listSkills(_input) {
@@ -216618,18 +216717,23 @@ async function scanSessionOutputs(bridge, loc, now = Date.now) {
216618
216717
  if (!walked) return null;
216619
216718
  if (walked.ok) {
216620
216719
  const files2 = walked.entries.filter((entry) => entry.type === "file").map((entry) => ({ path: entry.path, size: entry.size, mtime: entry.mtime }));
216621
- return { files: files2, via: "walk", tree: walked.entries };
216720
+ return { files: files2, via: "walk", tree: walked.entries, ...walked.truncated ? { incomplete: true } : {} };
216622
216721
  }
216623
216722
  if (walked.code === "NOT_FOUND") return { files: [], via: "walk", tree: [] };
216624
216723
  if (walked.code !== "UNSUPPORTED") return null;
216625
216724
  }
216725
+ let incomplete = false;
216626
216726
  const files = await scanLayered(
216627
216727
  (path41) => bridge.list(loc.nodeId, { ...locator, path: path41 }),
216628
- now
216728
+ now,
216729
+ () => {
216730
+ incomplete = true;
216731
+ }
216629
216732
  );
216630
- return files === null ? null : { files, via: "layered" };
216733
+ return files === null ? null : { files, via: "layered", ...incomplete ? { incomplete: true } : {} };
216631
216734
  }
216632
- async function scanLayered(list2, now) {
216735
+ async function scanLayered(list2, now, markIncomplete = () => {
216736
+ }) {
216633
216737
  const deadline = now() + BUDGET_MS;
216634
216738
  const out = [];
216635
216739
  let listCalls = 0;
@@ -216646,23 +216750,41 @@ async function scanLayered(list2, now) {
216646
216750
  for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {
216647
216751
  const nextDirs = [];
216648
216752
  for (const { path: dir, outcome } of level) {
216649
- if (!outcome.ok) continue;
216753
+ if (!outcome.ok) {
216754
+ markIncomplete();
216755
+ continue;
216756
+ }
216757
+ if (outcome.truncated) markIncomplete();
216650
216758
  for (const entry of outcome.entries) {
216651
216759
  if (entry.type === "dir") {
216652
216760
  nextDirs.push(joinRel(dir, entry.name));
216653
216761
  continue;
216654
216762
  }
216655
- if (out.length >= MAX_ENTRIES) continue;
216763
+ if (out.length >= MAX_ENTRIES) {
216764
+ markIncomplete();
216765
+ continue;
216766
+ }
216656
216767
  out.push({ path: joinRel(dir, entry.name), size: entry.size, mtime: entry.mtime });
216657
216768
  }
216658
216769
  }
216659
- if (nextDirs.length === 0 || depth === MAX_DEPTH) break;
216660
- if (out.length >= MAX_ENTRIES || listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
216770
+ if (nextDirs.length === 0) break;
216771
+ if (depth === MAX_DEPTH) {
216772
+ markIncomplete();
216773
+ break;
216774
+ }
216775
+ if (out.length >= MAX_ENTRIES || listCalls >= MAX_LIST_CALLS || now() >= deadline) {
216776
+ markIncomplete();
216777
+ break;
216778
+ }
216661
216779
  const batch = [];
216662
216780
  for (const dir of nextDirs) {
216663
- if (listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
216781
+ if (listCalls >= MAX_LIST_CALLS || now() >= deadline) {
216782
+ markIncomplete();
216783
+ break;
216784
+ }
216664
216785
  const outcome = await listDir(dir).catch(() => null);
216665
216786
  if (outcome) batch.push({ path: dir, outcome });
216787
+ else markIncomplete();
216666
216788
  }
216667
216789
  level = batch;
216668
216790
  }
@@ -216693,7 +216815,7 @@ async function settleSessionOutputs(deps) {
216693
216815
  diff.fingerprints[WALK_TABLE_MARK] = { size: 0, mtime: "" };
216694
216816
  }
216695
216817
  await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
216696
- return diff;
216818
+ return { ...diff, ...scan2.incomplete || scan2.files.some((f2) => f2.size == null || !f2.mtime) ? { scanIncomplete: true } : {} };
216697
216819
  }
216698
216820
  var WALK_MAX_ENTRIES, MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS, WALK_TABLE_MARK;
216699
216821
  var init_outputs_scan = __esm({
@@ -216748,11 +216870,17 @@ async function copySessionOutputs(deps) {
216748
216870
  try {
216749
216871
  existing = await deps.readCopies(deps.sessionId);
216750
216872
  } catch {
216873
+ result.skipped = Object.keys(deps.fingerprints).filter(isOutputPath).map((path41) => ({ path: path41, code: "STORE_FAILED" }));
216751
216874
  return result;
216752
216875
  }
216876
+ const deadline = Date.now() + 2e4;
216753
216877
  const plan = planOutputCopies(deps.fingerprints, existing, limits);
216754
216878
  result.skipped.push(...plan.skipped);
216755
216879
  for (const file of plan.toCopy) {
216880
+ if (Date.now() >= deadline) {
216881
+ result.skipped.push({ path: file.path, code: "BUDGET" });
216882
+ continue;
216883
+ }
216756
216884
  let read;
216757
216885
  try {
216758
216886
  read = await deps.bridge.read(
@@ -216769,6 +216897,14 @@ async function copySessionOutputs(deps) {
216769
216897
  continue;
216770
216898
  }
216771
216899
  const bytes2 = Buffer.from(read.contentBase64, "base64");
216900
+ if (bytes2.length > limits.maxFileBytes) {
216901
+ result.skipped.push({ path: file.path, code: "TOO_LARGE" });
216902
+ continue;
216903
+ }
216904
+ if (bytes2.length !== file.size || read.mtime && read.mtime !== file.mtime) {
216905
+ result.skipped.push({ path: file.path, code: "FILE_CHANGED" });
216906
+ continue;
216907
+ }
216772
216908
  let blobRef;
216773
216909
  try {
216774
216910
  blobRef = await deps.putBlob(new Uint8Array(bytes2));
@@ -216798,7 +216934,7 @@ async function copySessionOutputs(deps) {
216798
216934
  return result;
216799
216935
  }
216800
216936
  function createOutputCopyScheduler(deps) {
216801
- const inFlight = /* @__PURE__ */ new Set();
216937
+ const inFlight = /* @__PURE__ */ new Map();
216802
216938
  return {
216803
216939
  schedule(args) {
216804
216940
  const { store, chatSessionId, companyId } = args;
@@ -216806,9 +216942,10 @@ function createOutputCopyScheduler(deps) {
216806
216942
  const mergeCopies = store.mergeOutputCopies?.bind(store);
216807
216943
  if (!readCopies || !mergeCopies) return Promise.resolve(null);
216808
216944
  const key = `${companyId ?? ""}|${chatSessionId}`;
216809
- if (inFlight.has(key)) return Promise.resolve(null);
216810
- inFlight.add(key);
216811
- return (async () => {
216945
+ const prior = inFlight.get(key);
216946
+ let pending;
216947
+ pending = (async () => {
216948
+ await prior;
216812
216949
  try {
216813
216950
  const session = await store.getSession(chatSessionId);
216814
216951
  if (!session || session.visibility === "internal" || session.analyzedRunId) return null;
@@ -216823,6 +216960,21 @@ function createOutputCopyScheduler(deps) {
216823
216960
  putBlob: deps.putBlob,
216824
216961
  ...deps.limits ? { limits: deps.limits } : {}
216825
216962
  });
216963
+ const messages = {
216964
+ TOO_LARGE: "\u8D85\u8FC7\u5355\u6587\u4EF6\u4FDD\u5B58\u4E0A\u9650 50 MiB",
216965
+ BUDGET: "\u672C\u8F6E\u4FDD\u5B58\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u4E0B\u4E00\u8F6E\u91CD\u8BD5",
216966
+ READ_FAILED: "\u539F\u8282\u70B9\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25",
216967
+ STORE_FAILED: "\u670D\u52A1\u7AEF\u6587\u4EF6\u4FDD\u5B58\u5931\u8D25",
216968
+ FILE_CHANGED: "\u8BFB\u53D6\u671F\u95F4\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u8BD5"
216969
+ };
216970
+ const previousIssues = await store.readFileIssues?.(chatSessionId) ?? [];
216971
+ const settled = new Set(Object.keys(args.fingerprints));
216972
+ const issues = [
216973
+ ...previousIssues.filter((i) => i.phase === "save" && i.path !== "outputs/" && !settled.has(i.path)),
216974
+ ...result.skipped.map((s2) => ({ phase: "save", path: s2.path, code: s2.code, message: messages[s2.code] }))
216975
+ ];
216976
+ if (args.scanIncomplete) issues.push({ phase: "save", path: "outputs/", code: "SCAN_INCOMPLETE", message: "\u4EA7\u51FA\u76EE\u5F55\u672A\u80FD\u5B8C\u6574\u626B\u63CF\uFF0C\u90E8\u5206\u6587\u4EF6\u53EF\u80FD\u5C1A\u672A\u4FDD\u5B58" });
216977
+ await store.writeFileIssues?.(chatSessionId, "save", issues);
216826
216978
  if (result.skipped.length > 0) {
216827
216979
  const head = result.skipped.slice(0, 5).map((s2) => `${s2.path}:${s2.code}`).join(", ");
216828
216980
  deps.log?.(`[outputs-copy] ${chatSessionId}: \u590D\u5236 ${result.copied.length} \u4E2A\uFF0C\u8DF3\u8FC7 ${result.skipped.length} \u4E2A\uFF08${head}${result.skipped.length > 5 ? " \u2026" : ""}\uFF09`);
@@ -216830,11 +216982,14 @@ function createOutputCopyScheduler(deps) {
216830
216982
  return result;
216831
216983
  } catch (err) {
216832
216984
  deps.log?.(`[outputs-copy] ${chatSessionId}: ${err instanceof Error ? err.message : String(err)}`);
216985
+ await store.writeFileIssues?.(chatSessionId, "save", [{ phase: "save", path: "outputs/", code: "STORE_FAILED", message: "\u4EA7\u51FA\u4FDD\u5B58\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5" }]).catch(() => void 0);
216833
216986
  return null;
216834
216987
  } finally {
216835
- inFlight.delete(key);
216988
+ if (inFlight.get(key) === pending) inFlight.delete(key);
216836
216989
  }
216837
216990
  })();
216991
+ inFlight.set(key, pending);
216992
+ return pending;
216838
216993
  }
216839
216994
  };
216840
216995
  }
@@ -216844,9 +216999,9 @@ var init_outputs_copy = __esm({
216844
216999
  "use strict";
216845
217000
  init_src();
216846
217001
  OUTPUT_COPY_LIMITS = {
216847
- maxFileBytes: 2 * 1024 * 1024,
217002
+ maxFileBytes: 50 * 1024 * 1024,
216848
217003
  maxFiles: 50,
216849
- maxTotalBytes: 50 * 1024 * 1024
217004
+ maxTotalBytes: 100 * 1024 * 1024
216850
217005
  };
216851
217006
  }
216852
217007
  });
@@ -217056,11 +217211,11 @@ function typeDefError(def, mode) {
217056
217211
  }
217057
217212
  return null;
217058
217213
  }
217059
- var import_node_crypto44, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
217214
+ var import_node_crypto45, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
217060
217215
  var init_types_admin = __esm({
217061
217216
  "../server/src/types-admin.ts"() {
217062
217217
  "use strict";
217063
- import_node_crypto44 = require("node:crypto");
217218
+ import_node_crypto45 = require("node:crypto");
217064
217219
  init_zod();
217065
217220
  init_src5();
217066
217221
  init_config_audit();
@@ -217187,7 +217342,7 @@ var init_types_admin = __esm({
217187
217342
  async trace(change, target, by, detail) {
217188
217343
  try {
217189
217344
  await this.audit?.({
217190
- id: `reg_${(0, import_node_crypto44.randomUUID)()}`,
217345
+ id: `reg_${(0, import_node_crypto45.randomUUID)()}`,
217191
217346
  actor: by ?? SYSTEM_CONFIG_ACTOR,
217192
217347
  kind: "registry_change",
217193
217348
  target,
@@ -217335,11 +217490,11 @@ function roleDefError(def, mode) {
217335
217490
  }
217336
217491
  return null;
217337
217492
  }
217338
- var import_node_crypto45, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
217493
+ var import_node_crypto46, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
217339
217494
  var init_roles_admin = __esm({
217340
217495
  "../server/src/roles-admin.ts"() {
217341
217496
  "use strict";
217342
- import_node_crypto45 = require("node:crypto");
217497
+ import_node_crypto46 = require("node:crypto");
217343
217498
  init_zod();
217344
217499
  init_src5();
217345
217500
  init_config_audit();
@@ -217364,7 +217519,7 @@ var init_roles_admin = __esm({
217364
217519
  async trace(change, target, by, detail) {
217365
217520
  try {
217366
217521
  await this.deps.audit?.({
217367
- id: `reg_${(0, import_node_crypto45.randomUUID)()}`,
217522
+ id: `reg_${(0, import_node_crypto46.randomUUID)()}`,
217368
217523
  actor: by ?? SYSTEM_CONFIG_ACTOR,
217369
217524
  kind: "registry_change",
217370
217525
  target,
@@ -218693,6 +218848,373 @@ var init_recovery_plan = __esm({
218693
218848
  }
218694
218849
  });
218695
218850
 
218851
+ // ../server/src/domains/chat-sessions/files-view.ts
218852
+ function reasonFromWorkdirCode(code2) {
218853
+ switch (code2) {
218854
+ case "NODE_OFFLINE":
218855
+ return "NODE_OFFLINE";
218856
+ case "TIMEOUT":
218857
+ return "TIMEOUT";
218858
+ default:
218859
+ return "ERROR";
218860
+ }
218861
+ }
218862
+ function joinRel2(dir, name) {
218863
+ return dir ? `${dir}/${name}` : name;
218864
+ }
218865
+ function workdirFileItem(rel, name, size, mtime) {
218866
+ return {
218867
+ id: `workdir:${rel}`,
218868
+ name,
218869
+ kind: "file",
218870
+ // 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
218871
+ // 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
218872
+ content_type: null,
218873
+ size,
218874
+ updated_at: mtime,
218875
+ source: "workdir",
218876
+ blob_ref: null,
218877
+ path: rel,
218878
+ message_id: null,
218879
+ // 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
218880
+ // 这里是**按能力不渲染**的判据,不是置灰假钮。
218881
+ editable: false
218882
+ };
218883
+ }
218884
+ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
218885
+ const items = [];
218886
+ if (typeof bridge.walk === "function") {
218887
+ const walked = await bridge.walk(
218888
+ loc.nodeId,
218889
+ { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
218890
+ WORKDIR_TREE_MAX_ENTRIES
218891
+ );
218892
+ if (walked.ok) {
218893
+ let truncated2 = walked.truncated === true;
218894
+ for (const entry of walked.entries) {
218895
+ if (entry.type !== "file") continue;
218896
+ if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
218897
+ truncated2 = true;
218898
+ break;
218899
+ }
218900
+ const slash = entry.path.lastIndexOf("/");
218901
+ items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
218902
+ }
218903
+ return { ok: true, items, truncated: truncated2 };
218904
+ }
218905
+ if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
218906
+ if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
218907
+ }
218908
+ const deadline = now() + WORKDIR_WALK_BUDGET_MS;
218909
+ let listCalls = 0;
218910
+ let truncated = false;
218911
+ const listDir = (path41) => {
218912
+ listCalls += 1;
218913
+ return bridge.list(loc.nodeId, {
218914
+ workdirKey: loc.workdirKey,
218915
+ runtimeKind: loc.runtimeKind,
218916
+ rtId: loc.rtId,
218917
+ path: path41
218918
+ });
218919
+ };
218920
+ const root = await listDir("");
218921
+ if (!root.ok) {
218922
+ if (root.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
218923
+ return { ok: false, reason: reasonFromWorkdirCode(root.code) };
218924
+ }
218925
+ let level = [{ path: "", outcome: root }];
218926
+ for (let depth = 0; depth <= WORKDIR_WALK_MAX_DEPTH; depth += 1) {
218927
+ const nextDirs = [];
218928
+ for (const { path: dir, outcome } of level) {
218929
+ if (!outcome.ok) {
218930
+ truncated = true;
218931
+ continue;
218932
+ }
218933
+ if (outcome.truncated) truncated = true;
218934
+ for (const entry of outcome.entries) {
218935
+ if (entry.type === "dir") {
218936
+ nextDirs.push(joinRel2(dir, entry.name));
218937
+ continue;
218938
+ }
218939
+ if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
218940
+ truncated = true;
218941
+ continue;
218942
+ }
218943
+ items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
218944
+ }
218945
+ }
218946
+ if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
218947
+ if (nextDirs.length > 0) truncated = true;
218948
+ break;
218949
+ }
218950
+ const budget = WORKDIR_WALK_MAX_LIST_CALLS - listCalls;
218951
+ if (budget <= 0 || now() >= deadline) {
218952
+ truncated = true;
218953
+ break;
218954
+ }
218955
+ const take = nextDirs.slice(0, budget);
218956
+ if (take.length < nextDirs.length) truncated = true;
218957
+ const outcomes = await Promise.all(take.map((p2) => listDir(p2)));
218958
+ level = take.map((p2, i) => ({ path: p2, outcome: outcomes[i] }));
218959
+ }
218960
+ return { ok: true, items, truncated };
218961
+ }
218962
+ async function collectMessageFiles(messages, blobs) {
218963
+ const rows = [];
218964
+ for (const message of messages) {
218965
+ if (!Array.isArray(message.attachments)) continue;
218966
+ message.attachments.forEach((attachment, index2) => {
218967
+ if (!attachment || typeof attachment.name !== "string" || !attachment.name) return;
218968
+ const blobRef = typeof attachment.blobRef === "string" && attachment.blobRef ? attachment.blobRef : null;
218969
+ rows.push({
218970
+ item: {
218971
+ id: `msg:${message.id}:${index2}`,
218972
+ name: attachment.name,
218973
+ kind: "file",
218974
+ updated_at: attachment.updatedAt ?? message.createdAt,
218975
+ source: "message",
218976
+ blob_ref: blobRef,
218977
+ path: null,
218978
+ message_id: message.id,
218979
+ // 没有 blobRef 的是**历史纯文本附件**(契约注释:legacy text attachments may only
218980
+ // contain `name`)——没有可写回的载体,一律不给编辑入口。
218981
+ editable: blobRef !== null && isEditableFileName(attachment.name)
218982
+ },
218983
+ blobRef,
218984
+ declaredType: typeof attachment.contentType === "string" && attachment.contentType ? attachment.contentType : null
218985
+ });
218986
+ });
218987
+ }
218988
+ const metaCache = /* @__PURE__ */ new Map();
218989
+ const metaOf = (ref2) => {
218990
+ if (!blobs) return Promise.resolve(null);
218991
+ let hit = metaCache.get(ref2);
218992
+ if (!hit) {
218993
+ hit = blobs.meta(ref2).catch(() => null);
218994
+ metaCache.set(ref2, hit);
218995
+ }
218996
+ return hit;
218997
+ };
218998
+ return Promise.all(rows.map(async ({ item, blobRef, declaredType }) => {
218999
+ const meta = blobRef ? await metaOf(blobRef) : null;
219000
+ return {
219001
+ ...item,
219002
+ content_type: declaredType ?? meta?.contentType ?? null,
219003
+ size: meta?.size ?? null
219004
+ };
219005
+ }));
219006
+ }
219007
+ function mergeChatSessionFiles(messageItems, workdirItems, materializedCopies = /* @__PURE__ */ new Set()) {
219008
+ const out = [];
219009
+ const seenMessageKeys = /* @__PURE__ */ new Set();
219010
+ for (const item of [...messageItems].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at))) {
219011
+ const key = `${item.name}\0${item.blob_ref ?? ""}`;
219012
+ if (seenMessageKeys.has(key)) continue;
219013
+ seenMessageKeys.add(key);
219014
+ out.push(item);
219015
+ }
219016
+ const seenWorkdirPaths = /* @__PURE__ */ new Set();
219017
+ for (const item of workdirItems) {
219018
+ if (item.path && materializedCopies.has(item.path)) continue;
219019
+ if (seenWorkdirPaths.has(item.id)) continue;
219020
+ seenWorkdirPaths.add(item.id);
219021
+ out.push(item);
219022
+ }
219023
+ out.sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.name.localeCompare(b2.name));
219024
+ return out;
219025
+ }
219026
+ async function findMaterializedAttachmentCopies(messageItems, workdirItems, read) {
219027
+ const copies = /* @__PURE__ */ new Set();
219028
+ const attachments = messageItems.filter((item) => item.blob_ref && /^[a-f0-9]{64}$/.test(item.blob_ref));
219029
+ let budget = CHAT_ATTACHMENT_MAX_BYTES;
219030
+ const deadline = Date.now() + WORKDIR_WALK_BUDGET_MS;
219031
+ let reads = 0;
219032
+ for (const file of workdirItems) {
219033
+ const rootFile = file.path === file.name;
219034
+ const inputFile = file.path?.startsWith("inputs/attachments/") && !file.path.slice("inputs/attachments/".length).includes("/");
219035
+ if (!file.path || !rootFile && !inputFile) continue;
219036
+ const candidates = attachments.filter((attachment) => {
219037
+ const safe = safeAttachmentName(attachment.name);
219038
+ const numbered = file.name.endsWith(`-${safe}`) && /^\d+$/.test(file.name.slice(0, -(safe.length + 1)));
219039
+ return (rootFile ? file.name === attachment.name : file.name === safe || numbered) && attachment.size !== null && attachment.size === file.size;
219040
+ });
219041
+ if (Date.now() >= deadline) break;
219042
+ if (!candidates.length || file.size === null || file.size > budget || reads >= 10) continue;
219043
+ reads += 1;
219044
+ budget -= file.size;
219045
+ let timer;
219046
+ try {
219047
+ const result = await Promise.race([
219048
+ read(file.path, file.size),
219049
+ new Promise((resolve10) => {
219050
+ timer = setTimeout(() => resolve10(null), Math.max(0, deadline - Date.now()));
219051
+ })
219052
+ ]);
219053
+ if (!result?.ok) continue;
219054
+ const bytes2 = Buffer.from(result.contentBase64, "base64");
219055
+ if (bytes2.length !== file.size) continue;
219056
+ const hash2 = (0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex");
219057
+ if (candidates.some((attachment) => attachment.blob_ref === hash2)) copies.add(file.path);
219058
+ } catch {
219059
+ } finally {
219060
+ if (timer) clearTimeout(timer);
219061
+ }
219062
+ }
219063
+ return copies;
219064
+ }
219065
+ var import_node_crypto47, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
219066
+ var init_files_view = __esm({
219067
+ "../server/src/domains/chat-sessions/files-view.ts"() {
219068
+ "use strict";
219069
+ import_node_crypto47 = require("node:crypto");
219070
+ init_chat_attachment_files();
219071
+ init_src();
219072
+ WORKDIR_WALK_MAX_DEPTH = 3;
219073
+ WORKDIR_WALK_MAX_ENTRIES = 200;
219074
+ WORKDIR_WALK_MAX_LIST_CALLS = 20;
219075
+ WORKDIR_WALK_BUDGET_MS = 1e4;
219076
+ WORKDIR_TREE_MAX_ENTRIES = 2e3;
219077
+ }
219078
+ });
219079
+
219080
+ // ../server/src/domains/chat-sessions/persistent-files.ts
219081
+ function outputDisplayName(path41) {
219082
+ return path41.startsWith("outputs/") ? path41.slice("outputs/".length) : path41;
219083
+ }
219084
+ async function collectPersistentSessionFiles(messages, copies, blobs, includeOutputAliases = false) {
219085
+ const attachments = mergeChatSessionFiles(await collectMessageFiles(messages, blobs), []);
219086
+ const outputs = Object.values(copies ?? {}).filter((copy) => Boolean(copy.blobRef)).filter((copy) => includeOutputAliases || !attachments.some((a) => a.name === outputDisplayName(copy.path) && a.blob_ref === copy.blobRef)).map((copy) => ({
219087
+ // 保留历史产出卡的 id;引用和读取来源升级为服务端副本。
219088
+ id: `workdir:${copy.path}`,
219089
+ name: outputDisplayName(copy.path),
219090
+ kind: "file",
219091
+ content_type: copy.contentType,
219092
+ size: copy.size,
219093
+ updated_at: Number.isNaN(Date.parse(copy.mtime)) ? copy.copiedAt : new Date(copy.mtime).toISOString(),
219094
+ source: "output",
219095
+ blob_ref: copy.blobRef,
219096
+ path: copy.path,
219097
+ message_id: null,
219098
+ editable: false
219099
+ }));
219100
+ return [...attachments, ...outputs].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.id.localeCompare(b2.id));
219101
+ }
219102
+ async function readPersistentSessionFiles(store, sessionId, blobs, includeOutputAliases = false) {
219103
+ const [messages, copies] = await Promise.all([
219104
+ store.listSessionAttachmentMessages ? store.listSessionAttachmentMessages(sessionId) : store.listMessages(sessionId),
219105
+ store.readOutputCopies?.(sessionId) ?? Promise.resolve(null)
219106
+ ]);
219107
+ return collectPersistentSessionFiles(messages, copies, blobs, includeOutputAliases);
219108
+ }
219109
+ var init_persistent_files = __esm({
219110
+ "../server/src/domains/chat-sessions/persistent-files.ts"() {
219111
+ "use strict";
219112
+ init_files_view();
219113
+ }
219114
+ });
219115
+
219116
+ // ../server/src/domains/chat-sessions/file-restore.ts
219117
+ async function recordSessionFileRestoreExit(store, sessionId, info) {
219118
+ if (info.fileRestoreError) await store.writeFileIssues?.(sessionId, "restore", [{
219119
+ phase: "restore",
219120
+ path: info.fileRestoreError.path,
219121
+ code: "MATERIALIZE_FAILED",
219122
+ message: info.fileRestoreError.message
219123
+ }]);
219124
+ }
219125
+ function attachmentRestorePath(name, ref2) {
219126
+ const key = (0, import_node_crypto48.createHash)("sha256").update(`${name}\0${ref2}`).digest("hex").slice(0, 12);
219127
+ return `inputs/history/${key}/${safeAttachmentName(name)}`;
219128
+ }
219129
+ async function prepareSessionFileRestore(input) {
219130
+ const issues = [];
219131
+ const restoreFiles = {};
219132
+ let items;
219133
+ try {
219134
+ items = await readPersistentSessionFiles(input.store, input.sessionId, void 0, true);
219135
+ } catch {
219136
+ const issue2 = { phase: "restore", path: "", code: "INDEX_UNAVAILABLE", message: "\u6587\u4EF6\u6E05\u5355\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5" };
219137
+ await input.store.writeFileIssues?.(input.sessionId, "restore", [issue2]);
219138
+ throw new ApiError(503, "CHAT_FILE_RESTORE_FAILED", issue2.message);
219139
+ }
219140
+ const saveIssues = (await input.store.readFileIssues?.(input.sessionId) ?? []).filter((i) => i.phase === "save");
219141
+ const fingerprints = await input.store.readOutputFingerprints?.(input.sessionId) ?? {};
219142
+ const copies = await input.store.readOutputCopies?.(input.sessionId) ?? {};
219143
+ const outputPaths = new Set(Object.keys(fingerprints).filter((p2) => p2.startsWith("outputs/")));
219144
+ for (const message of await input.store.listMessages(input.sessionId)) {
219145
+ if (!Array.isArray(message.parts)) continue;
219146
+ for (const part of message.parts) {
219147
+ if (!part || typeof part !== "object" || Array.isArray(part) || part.type !== "outputs" || !Array.isArray(part.outputs)) continue;
219148
+ for (const file of part.outputs) if (file && typeof file === "object" && !Array.isArray(file) && typeof file.path === "string" && file.path.startsWith("outputs/")) outputPaths.add(file.path);
219149
+ }
219150
+ }
219151
+ for (const path41 of outputPaths) {
219152
+ const copy = copies[path41], fp = fingerprints[path41];
219153
+ if ((!copy || fp && (copy.size !== fp.size || copy.mtime !== fp.mtime)) && !saveIssues.some((i) => i.path === path41)) {
219154
+ saveIssues.push({ phase: "save", path: path41, code: "UNSAVED_OUTPUT", message: "\u5386\u53F2\u4EA7\u51FA\u5C1A\u65E0\u6700\u65B0\u6301\u4E45\u526F\u672C\uFF0C\u8BF7\u5728\u539F\u8282\u70B9\u5B8C\u6210\u4FDD\u5B58\u540E\u518D\u5207\u6362" });
219155
+ }
219156
+ }
219157
+ if (input.fresh) issues.push(...saveIssues.map((i) => ({ ...i, phase: "restore", message: `\u539F\u6587\u4EF6\u5C1A\u672A\u4FDD\u5B58\uFF1A${i.message}` })));
219158
+ const lines = ["# \u4F1A\u8BDD\u6587\u4EF6", "", "\u4EE5\u4E0B\u6587\u4EF6\u6765\u81EA\u672C\u4F1A\u8BDD\u7684\u6301\u4E45\u8BB0\u5F55\u3002\u5386\u53F2\u8F93\u5165\u6309\u7248\u672C\u4FDD\u7559\uFF1Boutputs \u662F\u6700\u8FD1\u4FDD\u5B58\u7684\u4EA7\u51FA\u3002", ""];
219159
+ let total = 0;
219160
+ const seen = /* @__PURE__ */ new Set();
219161
+ const add = async (name, ref2, path41, replace2) => {
219162
+ if (seen.has(path41)) return;
219163
+ seen.add(path41);
219164
+ if (!ref2) {
219165
+ issues.push({ phase: "restore", path: path41, code: "MISSING_REFERENCE", message: `${name} \u6CA1\u6709\u53EF\u6062\u590D\u7684\u539F\u4EF6\uFF0C\u8BF7\u91CD\u65B0\u4E0A\u4F20` });
219166
+ return;
219167
+ }
219168
+ try {
219169
+ if (Object.keys(restoreFiles).length >= RESTORE_MAX_FILES) throw new Error("\u8D85\u8FC7\u5355\u8F6E\u6062\u590D\u6587\u4EF6\u6570\u91CF\u4E0A\u9650");
219170
+ const bytes2 = await input.readBlob(ref2);
219171
+ if (total + bytes2.byteLength > RESTORE_MAX_BYTES) throw new Error("\u8D85\u8FC7\u5355\u8F6E\u6062\u590D\u603B\u91CF 100 MiB");
219172
+ const hash2 = (0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex");
219173
+ if (hash2 !== ref2.replace(/^blob:/, "")) throw new Error("\u539F\u4EF6\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
219174
+ total += bytes2.byteLength;
219175
+ restoreFiles[path41] = { contentBase64: Buffer.from(bytes2).toString("base64"), sha256: hash2, overwrite: replace2 };
219176
+ lines.push(`- ${JSON.stringify(name)} \u2192 ${JSON.stringify(path41)}`);
219177
+ } catch (error2) {
219178
+ issues.push({ phase: "restore", path: path41, code: "FILE_UNAVAILABLE", message: `${name}\uFF1A${error2 instanceof Error ? error2.message : "\u8BFB\u53D6\u5931\u8D25"}` });
219179
+ }
219180
+ };
219181
+ for (const item of items) {
219182
+ const path41 = item.source === "output" ? item.path : attachmentRestorePath(item.name, item.blob_ref ?? item.id);
219183
+ await add(item.name, item.blob_ref, path41, item.source === "message" || input.fresh);
219184
+ if (item.source === "message") {
219185
+ await add(item.name, item.blob_ref, `inputs/attachments/${safeAttachmentName(item.name)}`, true);
219186
+ }
219187
+ }
219188
+ if (!input.fresh) for (const issue2 of saveIssues) {
219189
+ if (!issue2.path.startsWith("outputs/")) continue;
219190
+ restoreFiles[issue2.path] = { contentBase64: "", sha256: (0, import_node_crypto48.createHash)("sha256").update("").digest("hex"), overwrite: false, requireExisting: true };
219191
+ lines.push(`- ${JSON.stringify(issue2.path)} \u5C1A\u672A\u4FDD\u5B58\uFF0C\u5FC5\u987B\u4FDD\u7559\u672C\u673A\u539F\u6587\u4EF6\uFF1A${issue2.message}`);
219192
+ }
219193
+ for (const attachment of input.attachments ?? []) {
219194
+ if (attachment.blobRef && !attachment.path) {
219195
+ await add(attachment.name, attachment.blobRef, attachmentRestorePath(attachment.name, attachment.blobRef), false);
219196
+ }
219197
+ }
219198
+ if (input.supportsFileRestore === false && Object.keys(restoreFiles).length) {
219199
+ issues.push({ phase: "restore", path: "", code: "NODE_UPGRADE_REQUIRED", message: "\u5F53\u524D\u8282\u70B9\u5C1A\u4E0D\u652F\u6301\u6587\u4EF6\u6062\u590D\uFF0C\u8BF7\u5347\u7EA7\u8282\u70B9\u540E\u91CD\u8BD5" });
219200
+ }
219201
+ await input.store.writeFileIssues?.(input.sessionId, "restore", issues);
219202
+ if (issues.length) throw new ApiError(409, "CHAT_FILE_RESTORE_FAILED", `\u6587\u4EF6\u6062\u590D\u672A\u5B8C\u6210\uFF1A${issues.map((i) => i.message).join("\uFF1B")}`);
219203
+ return { restoreFiles, index: lines.join("\n") };
219204
+ }
219205
+ var import_node_crypto48, RESTORE_MAX_BYTES, RESTORE_MAX_FILES;
219206
+ var init_file_restore = __esm({
219207
+ "../server/src/domains/chat-sessions/file-restore.ts"() {
219208
+ "use strict";
219209
+ import_node_crypto48 = require("node:crypto");
219210
+ init_chat_attachment_files();
219211
+ init_router();
219212
+ init_persistent_files();
219213
+ RESTORE_MAX_BYTES = 100 * 1024 * 1024;
219214
+ RESTORE_MAX_FILES = 500;
219215
+ }
219216
+ });
219217
+
218696
219218
  // ../server/src/domains/trace/resilient-appender.ts
218697
219219
  var ResilientTraceAppender;
218698
219220
  var init_resilient_appender = __esm({
@@ -219615,7 +220137,7 @@ function wireRecoveredChatTurn(deps) {
219615
220137
  const segText = itemLedger.projectContent();
219616
220138
  const segParts = collectedParts();
219617
220139
  const closingId = currentMsgId;
219618
- const nextId = (0, import_node_crypto46.randomUUID)();
220140
+ const nextId = (0, import_node_crypto49.randomUUID)();
219619
220141
  currentMsgId = nextId;
219620
220142
  itemLedger.beginSegment();
219621
220143
  live.setAssistantMessageId(nextId);
@@ -219629,7 +220151,7 @@ function wireRecoveredChatTurn(deps) {
219629
220151
  ...segParts.length ? { parts: segParts } : {}
219630
220152
  }).catch(() => void 0);
219631
220153
  await store.appendMessage({
219632
- id: (0, import_node_crypto46.randomUUID)(),
220154
+ id: (0, import_node_crypto49.randomUUID)(),
219633
220155
  sessionId: plan.chatSessionId,
219634
220156
  role: "user",
219635
220157
  content: text5,
@@ -219661,6 +220183,7 @@ function wireRecoveredChatTurn(deps) {
219661
220183
  const done = (async () => {
219662
220184
  const info = await new Promise((resolve10) => handle.onExit(resolve10));
219663
220185
  clearInterval(ckptTimer);
220186
+ await recordSessionFileRestoreExit(chatStore, plan.chatSessionId, info);
219664
220187
  const failed = exitFailed(info);
219665
220188
  if (failed) {
219666
220189
  const errText2 = exitErrorText(info);
@@ -219690,11 +220213,12 @@ function wireRecoveredChatTurn(deps) {
219690
220213
  log3
219691
220214
  );
219692
220215
  for (const item of closedItems) liveChat.publishItemStatus(plan.chatSessionId, item);
220216
+ const finalParts = await appendOutputsPart(parts, info.fileRestoreError ? void 0 : deps.outputsPart);
219693
220217
  await chatStore.updateMessage(currentMsgId, {
219694
220218
  content: itemLedger.projectContent(),
219695
220219
  status: failed ? "error" : "done",
219696
220220
  completedAt: finishedAt,
219697
- ...parts.length ? { parts } : {}
220221
+ ...finalParts.length ? { parts: finalParts } : {}
219698
220222
  }).catch(() => void 0);
219699
220223
  const nextRuntimeSessionId = info.runtimeSessionId ?? plan.runtimeSessionId;
219700
220224
  await chatStore.updateSession(plan.chatSessionId, {
@@ -219768,10 +220292,11 @@ async function sweepDanglingChatTurns(deps) {
219768
220292
  text: "\u8FD9\u4E00\u8F6E\u5728\u6267\u884C\u4E2D\u88AB\u6253\u65AD\uFF08serve \u91CD\u542F\u6216\u8282\u70B9\u9000\u51FA\uFF09\uFF0C\u672A\u80FD\u5B8C\u6210\u2014\u2014\u4EE5\u4E0A\u4E3A\u4E2D\u65AD\u524D\u5DF2\u751F\u6210\u7684\u5185\u5BB9\u3002"
219769
220293
  });
219770
220294
  }
220295
+ const finalParts = await appendOutputsPart(parts, deps.outputsPartFor?.(row.sessionId));
219771
220296
  await deps.updateMessage(row.id, {
219772
220297
  status,
219773
220298
  completedAt,
219774
- ...parts.length ? { parts } : {}
220299
+ ...finalParts.length ? { parts: finalParts } : {}
219775
220300
  });
219776
220301
  closed.push(row.id);
219777
220302
  deps.log?.(`[chat-sweep] \u884C ${row.id}\uFF08\u4F1A\u8BDD ${row.sessionId}\uFF09\u6536\u655B\u4E3A ${status}\uFF08${reason}\uFF09`);
@@ -219793,6 +220318,7 @@ async function reconcileChatExitFrame(deps) {
219793
220318
  if (meta?.dispatchId !== deps.dispatchId) continue;
219794
220319
  if (deps.ownedRunId?.(row.sessionId) === row.runId) return false;
219795
220320
  const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
220321
+ const outputsPart = deps.info.fileRestoreError ? void 0 : deps.outputsPartFor?.(row.sessionId);
219796
220322
  if (turn && turn.status !== "reserved" && turn.status !== "running") {
219797
220323
  await reconcileSettledChatTurn({
219798
220324
  plan: {
@@ -219811,11 +220337,13 @@ async function reconcileChatExitFrame(deps) {
219811
220337
  chatStore: deps.chatStore,
219812
220338
  trace: deps.trace,
219813
220339
  ...deps.items ? { items: deps.items } : {},
220340
+ ...outputsPart ? { outputsPart } : {},
219814
220341
  ...deps.log ? { log: deps.log } : {}
219815
220342
  });
219816
220343
  return true;
219817
220344
  }
219818
220345
  const { info } = deps;
220346
+ await recordSessionFileRestoreExit(deps.chatStore, row.sessionId, info);
219819
220347
  const failed = exitFailed(info);
219820
220348
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
219821
220349
  const stash = deps.drainStash(deps.dispatchId);
@@ -219863,11 +220391,12 @@ async function reconcileChatExitFrame(deps) {
219863
220391
  failed ? "failed" : "completed",
219864
220392
  deps.log ?? (() => void 0)
219865
220393
  );
220394
+ const finalParts = await appendOutputsPart(parts, outputsPart);
219866
220395
  await deps.chatStore.updateMessage(row.id, {
219867
220396
  content: itemLedger.projectContent(),
219868
220397
  status: failed ? "error" : "done",
219869
220398
  completedAt: finishedAt,
219870
- ...parts.length ? { parts } : {}
220399
+ ...finalParts.length ? { parts: finalParts } : {}
219871
220400
  });
219872
220401
  const metaRsid = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : void 0;
219873
220402
  const nextRuntimeSessionId = info.runtimeSessionId ?? metaRsid;
@@ -219930,7 +220459,7 @@ async function planChatTurnRecoveryAcrossCompanies(deps) {
219930
220459
  return { groups, settled, scanned, skipped };
219931
220460
  }
219932
220461
  async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219933
- const { listCompanies, storesFor, onCompanyError, onSessionClosed, log: log3, ...rest } = deps;
220462
+ const { listCompanies, storesFor, onCompanyError, onSessionClosed, sessionOutputsScan, log: log3, ...rest } = deps;
219934
220463
  const closed = [];
219935
220464
  for (const companyId of await listCompanies()) {
219936
220465
  try {
@@ -219941,7 +220470,8 @@ async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219941
220470
  listMessages: (sid) => stores.chatStore.listMessages(sid),
219942
220471
  updateMessage: (id, patch) => stores.chatStore.updateMessage(id, patch),
219943
220472
  ...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
219944
- ...onSessionClosed ? { onSessionClosed: (sid, reason, messageId) => onSessionClosed(sid, reason, messageId, companyId) } : {}
220473
+ ...onSessionClosed ? { onSessionClosed: (sid, reason, messageId) => onSessionClosed(sid, reason, messageId, companyId) } : {},
220474
+ ...sessionOutputsScan ? { outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId) } : {}
219945
220475
  });
219946
220476
  closed.push(...ids2);
219947
220477
  } catch (err) {
@@ -219951,7 +220481,7 @@ async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219951
220481
  return closed;
219952
220482
  }
219953
220483
  async function reconcileChatExitFrameAcrossCompanies(deps) {
219954
- const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
220484
+ const { listCompanies, storesFor, onCompanyError, sessionOutputsScan, log: log3, ...rest } = deps;
219955
220485
  for (const companyId of await listCompanies()) {
219956
220486
  try {
219957
220487
  const stores = await storesFor(companyId);
@@ -219961,7 +220491,8 @@ async function reconcileChatExitFrameAcrossCompanies(deps) {
219961
220491
  chatStore: stores.chatStore,
219962
220492
  ...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
219963
220493
  ...stores.items ? { items: stores.items } : {},
219964
- ...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {}
220494
+ ...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
220495
+ ...sessionOutputsScan ? { outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId) } : {}
219965
220496
  });
219966
220497
  if (handled3) return true;
219967
220498
  } catch (err) {
@@ -219983,9 +220514,11 @@ async function reconcileSettledChatTurn(deps) {
219983
220514
  succeeded ? "completed" : "failed",
219984
220515
  deps.log ?? (() => void 0)
219985
220516
  );
220517
+ const withOutputs = await appendOutputsPart(plan.seedParts, deps.outputsPart);
219986
220518
  await deps.chatStore.updateMessage(plan.assistantMsgId, {
219987
220519
  status: succeeded ? "done" : "error",
219988
- completedAt
220520
+ completedAt,
220521
+ ...withOutputs !== plan.seedParts ? { parts: withOutputs } : {}
219989
220522
  });
219990
220523
  const run = await deps.trace.getRun(plan.runId);
219991
220524
  if (run?.status === "running") {
@@ -220003,6 +220536,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220003
220536
  const { plan, turn, companyId, stores } = hit;
220004
220537
  log3(`[chat-claim] company=${companyId ?? "<default>"} session=${plan.chatSessionId} run=${plan.runId} dispatch=${plan.dispatchId} \u7ED3\u8BBA=settled\uFF08\u4F9D\u636E\uFF1A\u540C\u516C\u53F8\u8F6E\u6B21 ${turn.id} \u5DF2 ${turn.status}\uFF0CcompletedAt=${turn.completedAt ?? "-"}\uFF09`);
220005
220538
  deps.evict(plan.dispatchId);
220539
+ const outputsPart = outputsPartProvider(deps.sessionOutputsScan, plan.chatSessionId, companyId);
220006
220540
  try {
220007
220541
  await reconcileSettledChatTurn({
220008
220542
  plan,
@@ -220010,6 +220544,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220010
220544
  chatStore: stores.chatStore,
220011
220545
  trace: deps.trace,
220012
220546
  ...stores.items ? { items: stores.items } : {},
220547
+ ...outputsPart ? { outputsPart } : {},
220013
220548
  log: log3
220014
220549
  });
220015
220550
  } catch (err) {
@@ -220045,6 +220580,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220045
220580
  ...companyId ? { companyId } : {}
220046
220581
  });
220047
220582
  } : void 0;
220583
+ const outputsPart = outputsPartProvider(deps.sessionOutputsScan, plan.chatSessionId, companyId);
220048
220584
  const { done } = wireRecoveredChatTurn({
220049
220585
  plan,
220050
220586
  handle,
@@ -220055,6 +220591,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220055
220591
  unregister: () => deps.unregister(plan),
220056
220592
  ...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
220057
220593
  ...onTurnSettled ? { onTurnSettled } : {},
220594
+ ...outputsPart ? { outputsPart } : {},
220058
220595
  ...stores.items ? { items: stores.items, itemsTurnId } : {},
220059
220596
  log: log3
220060
220597
  });
@@ -220068,15 +220605,17 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220068
220605
  }
220069
220606
  return { ...result, recovered };
220070
220607
  }
220071
- var import_node_crypto46, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
220608
+ var import_node_crypto49, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
220072
220609
  var init_chat_recovery = __esm({
220073
220610
  "../server/src/chat-recovery.ts"() {
220074
220611
  "use strict";
220075
- import_node_crypto46 = require("node:crypto");
220612
+ init_file_restore();
220613
+ import_node_crypto49 = require("node:crypto");
220076
220614
  init_chat_parts();
220077
220615
  init_sink();
220078
220616
  init_resilient_appender();
220079
220617
  init_chat_item_ledger();
220618
+ init_chat_assistant_turn();
220080
220619
  init_src6();
220081
220620
  init_session_exit_outcome();
220082
220621
  asObj3 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
@@ -220102,8 +220641,8 @@ var init_chat_recovery = __esm({
220102
220641
 
220103
220642
  // ../server/src/auth/crypto.ts
220104
220643
  function hashPassword(password) {
220105
- const salt = (0, import_node_crypto47.randomBytes)(16);
220106
- const dk = (0, import_node_crypto47.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
220644
+ const salt = (0, import_node_crypto50.randomBytes)(16);
220645
+ const dk = (0, import_node_crypto50.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
220107
220646
  return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
220108
220647
  }
220109
220648
  function verifyPassword(password, stored) {
@@ -220120,8 +220659,8 @@ function verifyPassword(password, stored) {
220120
220659
  return false;
220121
220660
  }
220122
220661
  if (expected.length === 0) return false;
220123
- const dk = (0, import_node_crypto47.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
220124
- return dk.length === expected.length && (0, import_node_crypto47.timingSafeEqual)(dk, expected);
220662
+ const dk = (0, import_node_crypto50.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
220663
+ return dk.length === expected.length && (0, import_node_crypto50.timingSafeEqual)(dk, expected);
220125
220664
  }
220126
220665
  function b64urlJson(value2) {
220127
220666
  return Buffer.from(JSON.stringify(value2)).toString("base64url");
@@ -220129,17 +220668,17 @@ function b64urlJson(value2) {
220129
220668
  function signSession(claims, secret) {
220130
220669
  const head = b64urlJson({ alg: "HS256", typ: "JWT" });
220131
220670
  const body2 = b64urlJson(claims);
220132
- const sig = (0, import_node_crypto47.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220671
+ const sig = (0, import_node_crypto50.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220133
220672
  return `${head}.${body2}.${sig}`;
220134
220673
  }
220135
220674
  function verifySession(token, secret, nowMs) {
220136
220675
  const parts = token.split(".");
220137
220676
  if (parts.length !== 3) return null;
220138
220677
  const [head, body2, sig] = parts;
220139
- const expected = (0, import_node_crypto47.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220678
+ const expected = (0, import_node_crypto50.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220140
220679
  const got = Buffer.from(sig);
220141
220680
  const exp = Buffer.from(expected);
220142
- if (got.length !== exp.length || !(0, import_node_crypto47.timingSafeEqual)(got, exp)) return null;
220681
+ if (got.length !== exp.length || !(0, import_node_crypto50.timingSafeEqual)(got, exp)) return null;
220143
220682
  let claims;
220144
220683
  try {
220145
220684
  claims = JSON.parse(Buffer.from(body2, "base64url").toString("utf8"));
@@ -220151,21 +220690,21 @@ function verifySession(token, secret, nowMs) {
220151
220690
  return claims;
220152
220691
  }
220153
220692
  function generateCode() {
220154
- return String((0, import_node_crypto47.randomInt)(0, 1e6)).padStart(6, "0");
220693
+ return String((0, import_node_crypto50.randomInt)(0, 1e6)).padStart(6, "0");
220155
220694
  }
220156
220695
  function hashCode(code2, secret) {
220157
- return (0, import_node_crypto47.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
220696
+ return (0, import_node_crypto50.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
220158
220697
  }
220159
220698
  function safeEqualHash(a, b2) {
220160
220699
  const ba = Buffer.from(a);
220161
220700
  const bb = Buffer.from(b2);
220162
- return ba.length === bb.length && (0, import_node_crypto47.timingSafeEqual)(ba, bb);
220701
+ return ba.length === bb.length && (0, import_node_crypto50.timingSafeEqual)(ba, bb);
220163
220702
  }
220164
- var import_node_crypto47, SCRYPT_N, SCRYPT_KEYLEN;
220703
+ var import_node_crypto50, SCRYPT_N, SCRYPT_KEYLEN;
220165
220704
  var init_crypto2 = __esm({
220166
220705
  "../server/src/auth/crypto.ts"() {
220167
220706
  "use strict";
220168
- import_node_crypto47 = require("node:crypto");
220707
+ import_node_crypto50 = require("node:crypto");
220169
220708
  SCRYPT_N = 16384;
220170
220709
  SCRYPT_KEYLEN = 32;
220171
220710
  }
@@ -221315,16 +221854,16 @@ function variableKeyFromEnv(env = process.env) {
221315
221854
  return buf;
221316
221855
  }
221317
221856
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
221318
- return (0, import_node_crypto48.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
221857
+ return (0, import_node_crypto51.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
221319
221858
  }
221320
221859
  function dedupe(ids2) {
221321
221860
  return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
221322
221861
  }
221323
- var import_node_crypto48, REDACTED_MARKER, ActorsService, mask, changedKeys;
221862
+ var import_node_crypto51, REDACTED_MARKER, ActorsService, mask, changedKeys;
221324
221863
  var init_service5 = __esm({
221325
221864
  "../server/src/domains/actors/service.ts"() {
221326
221865
  "use strict";
221327
- import_node_crypto48 = require("node:crypto");
221866
+ import_node_crypto51 = require("node:crypto");
221328
221867
  init_skill_fetcher();
221329
221868
  init_identity();
221330
221869
  init_migrate();
@@ -221888,7 +222427,7 @@ ${input.description}
221888
222427
  */
221889
222428
  buildSkillFile(skillId, path41, content3, now) {
221890
222429
  const bytes2 = new TextEncoder().encode(content3);
221891
- const blobHash = (0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex");
222430
+ const blobHash = (0, import_node_crypto51.createHash)("sha256").update(bytes2).digest("hex");
221892
222431
  return { skillId, path: path41, content: content3, blobHash, size: bytes2.length, updatedAt: now };
221893
222432
  }
221894
222433
  /**
@@ -222540,15 +223079,15 @@ ${input.description}
222540
223079
  return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
222541
223080
  }
222542
223081
  encrypt(plain) {
222543
- const iv = (0, import_node_crypto48.randomBytes)(12);
222544
- const cipher = (0, import_node_crypto48.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
223082
+ const iv = (0, import_node_crypto51.randomBytes)(12);
223083
+ const cipher = (0, import_node_crypto51.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
222545
223084
  const enc4 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
222546
223085
  return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc4.toString("base64")].join(".");
222547
223086
  }
222548
223087
  decrypt(packed) {
222549
223088
  const [iv, tag, data] = packed.split(".");
222550
223089
  if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
222551
- const decipher = (0, import_node_crypto48.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
223090
+ const decipher = (0, import_node_crypto51.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
222552
223091
  decipher.setAuthTag(Buffer.from(tag, "base64"));
222553
223092
  return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
222554
223093
  }
@@ -223166,7 +223705,7 @@ async function listSkillMetadataForActor(args) {
223166
223705
  usedDirs.add(dir);
223167
223706
  const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
223168
223707
  let latest = "1970-01-01T00:00:00.000Z";
223169
- const hasher = (0, import_node_crypto49.createHash)("sha256");
223708
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223170
223709
  for (const m2 of sorted) {
223171
223710
  if (m2.updatedAt > latest) latest = m2.updatedAt;
223172
223711
  hasher.update(m2.path);
@@ -223191,11 +223730,11 @@ async function listSkillMetadataForActor(args) {
223191
223730
  const dir = sanitizeSkillDir(c.name, c.slug);
223192
223731
  if (usedDirs.has(dir)) continue;
223193
223732
  usedDirs.add(dir);
223194
- const hasher = (0, import_node_crypto49.createHash)("sha256");
223733
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223195
223734
  for (const p2 of paths) {
223196
223735
  hasher.update(p2);
223197
223736
  hasher.update("\0");
223198
- hasher.update((0, import_node_crypto49.createHash)("sha256").update(c.files[p2]).digest("hex"));
223737
+ hasher.update((0, import_node_crypto52.createHash)("sha256").update(c.files[p2]).digest("hex"));
223199
223738
  hasher.update("\0");
223200
223739
  }
223201
223740
  out.push({
@@ -223216,11 +223755,11 @@ async function listSkillMetadataForActor(args) {
223216
223755
  const dir = sanitizeSkillDir(b2.name, b2.id);
223217
223756
  if (usedDirs.has(dir)) continue;
223218
223757
  usedDirs.add(dir);
223219
- const hasher = (0, import_node_crypto49.createHash)("sha256");
223758
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223220
223759
  for (const p2 of paths) {
223221
223760
  hasher.update(p2);
223222
223761
  hasher.update("\0");
223223
- hasher.update((0, import_node_crypto49.createHash)("sha256").update(b2.files[p2]).digest("hex"));
223762
+ hasher.update((0, import_node_crypto52.createHash)("sha256").update(b2.files[p2]).digest("hex"));
223224
223763
  hasher.update("\0");
223225
223764
  }
223226
223765
  out.push({
@@ -223240,7 +223779,7 @@ function builtinSkillToFiles(b2) {
223240
223779
  skillId: `builtin:${b2.id}`,
223241
223780
  path: path41,
223242
223781
  content: content3,
223243
- blobHash: (0, import_node_crypto49.createHash)("sha256").update(content3).digest("hex"),
223782
+ blobHash: (0, import_node_crypto52.createHash)("sha256").update(content3).digest("hex"),
223244
223783
  size: Buffer.byteLength(content3, "utf8"),
223245
223784
  updatedAt: "1970-01-01T00:00:00.000Z"
223246
223785
  }));
@@ -223264,7 +223803,7 @@ function sanitizeSkillDir(name, fallbackId) {
223264
223803
  if (byName) return byName;
223265
223804
  const byId = fallbackId ? fold2(fallbackId) : "";
223266
223805
  if (byId) return byId;
223267
- return `skill-${(0, import_node_crypto49.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
223806
+ return `skill-${(0, import_node_crypto52.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
223268
223807
  }
223269
223808
  async function materializeSkillFiles(args) {
223270
223809
  const skills = await args.service.listInstalledSkillsForActor(args.actorId);
@@ -223297,7 +223836,7 @@ function connectorSkillToFiles(c) {
223297
223836
  skillId: c.id,
223298
223837
  path: path41,
223299
223838
  content: content3,
223300
- blobHash: (0, import_node_crypto49.createHash)("sha256").update(content3).digest("hex"),
223839
+ blobHash: (0, import_node_crypto52.createHash)("sha256").update(content3).digest("hex"),
223301
223840
  size: Buffer.byteLength(content3, "utf8"),
223302
223841
  updatedAt: "1970-01-01T00:00:00.000Z"
223303
223842
  }));
@@ -223314,11 +223853,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
223314
223853
  ...s2.version !== void 0 ? { version: s2.version } : {}
223315
223854
  }));
223316
223855
  }
223317
- var import_node_crypto49;
223856
+ var import_node_crypto52;
223318
223857
  var init_skill_materializer = __esm({
223319
223858
  "../server/src/domains/actors/skill-materializer.ts"() {
223320
223859
  "use strict";
223321
- import_node_crypto49 = require("node:crypto");
223860
+ import_node_crypto52 = require("node:crypto");
223322
223861
  }
223323
223862
  });
223324
223863
 
@@ -224829,7 +225368,7 @@ function createActorsDomain(opts) {
224829
225368
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
224830
225369
  audit: async (entry) => {
224831
225370
  await opts.audit?.({
224832
- id: `reg_${(0, import_node_crypto50.randomUUID)()}`,
225371
+ id: `reg_${(0, import_node_crypto53.randomUUID)()}`,
224833
225372
  actor: entry.by,
224834
225373
  kind: "registry_change",
224835
225374
  target: entry.actorId,
@@ -224869,11 +225408,11 @@ function createActorsDomain(opts) {
224869
225408
  register: actorsDomain({ resolveCtx, ...opts.resolveTrace ? { resolveTrace: opts.resolveTrace } : {}, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
224870
225409
  };
224871
225410
  }
224872
- var import_node_crypto50;
225411
+ var import_node_crypto53;
224873
225412
  var init_actors = __esm({
224874
225413
  "../server/src/domains/actors/index.ts"() {
224875
225414
  "use strict";
224876
- import_node_crypto50 = require("node:crypto");
225415
+ import_node_crypto53 = require("node:crypto");
224877
225416
  init_service5();
224878
225417
  init_routes3();
224879
225418
  init_service5();
@@ -225043,235 +225582,6 @@ var init_assistant_tier_dispatch = __esm({
225043
225582
  }
225044
225583
  });
225045
225584
 
225046
- // ../server/src/domains/chat-sessions/files-view.ts
225047
- function reasonFromWorkdirCode(code2) {
225048
- switch (code2) {
225049
- case "NODE_OFFLINE":
225050
- return "NODE_OFFLINE";
225051
- case "TIMEOUT":
225052
- return "TIMEOUT";
225053
- default:
225054
- return "ERROR";
225055
- }
225056
- }
225057
- function joinRel2(dir, name) {
225058
- return dir ? `${dir}/${name}` : name;
225059
- }
225060
- function workdirFileItem(rel, name, size, mtime) {
225061
- return {
225062
- id: `workdir:${rel}`,
225063
- name,
225064
- kind: "file",
225065
- // 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
225066
- // 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
225067
- content_type: null,
225068
- size,
225069
- updated_at: mtime,
225070
- source: "workdir",
225071
- blob_ref: null,
225072
- path: rel,
225073
- message_id: null,
225074
- // 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
225075
- // 这里是**按能力不渲染**的判据,不是置灰假钮。
225076
- editable: false
225077
- };
225078
- }
225079
- async function collectWorkdirFiles(bridge, loc, now = Date.now) {
225080
- const items = [];
225081
- if (typeof bridge.walk === "function") {
225082
- const walked = await bridge.walk(
225083
- loc.nodeId,
225084
- { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
225085
- WORKDIR_TREE_MAX_ENTRIES
225086
- );
225087
- if (walked.ok) {
225088
- let truncated2 = walked.truncated === true;
225089
- for (const entry of walked.entries) {
225090
- if (entry.type !== "file") continue;
225091
- if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
225092
- truncated2 = true;
225093
- break;
225094
- }
225095
- const slash = entry.path.lastIndexOf("/");
225096
- items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
225097
- }
225098
- return { ok: true, items, truncated: truncated2 };
225099
- }
225100
- if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
225101
- if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
225102
- }
225103
- const deadline = now() + WORKDIR_WALK_BUDGET_MS;
225104
- let listCalls = 0;
225105
- let truncated = false;
225106
- const listDir = (path41) => {
225107
- listCalls += 1;
225108
- return bridge.list(loc.nodeId, {
225109
- workdirKey: loc.workdirKey,
225110
- runtimeKind: loc.runtimeKind,
225111
- rtId: loc.rtId,
225112
- path: path41
225113
- });
225114
- };
225115
- const root = await listDir("");
225116
- if (!root.ok) {
225117
- if (root.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
225118
- return { ok: false, reason: reasonFromWorkdirCode(root.code) };
225119
- }
225120
- let level = [{ path: "", outcome: root }];
225121
- for (let depth = 0; depth <= WORKDIR_WALK_MAX_DEPTH; depth += 1) {
225122
- const nextDirs = [];
225123
- for (const { path: dir, outcome } of level) {
225124
- if (!outcome.ok) {
225125
- truncated = true;
225126
- continue;
225127
- }
225128
- if (outcome.truncated) truncated = true;
225129
- for (const entry of outcome.entries) {
225130
- if (entry.type === "dir") {
225131
- nextDirs.push(joinRel2(dir, entry.name));
225132
- continue;
225133
- }
225134
- if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
225135
- truncated = true;
225136
- continue;
225137
- }
225138
- items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
225139
- }
225140
- }
225141
- if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
225142
- if (nextDirs.length > 0) truncated = true;
225143
- break;
225144
- }
225145
- const budget = WORKDIR_WALK_MAX_LIST_CALLS - listCalls;
225146
- if (budget <= 0 || now() >= deadline) {
225147
- truncated = true;
225148
- break;
225149
- }
225150
- const take = nextDirs.slice(0, budget);
225151
- if (take.length < nextDirs.length) truncated = true;
225152
- const outcomes = await Promise.all(take.map((p2) => listDir(p2)));
225153
- level = take.map((p2, i) => ({ path: p2, outcome: outcomes[i] }));
225154
- }
225155
- return { ok: true, items, truncated };
225156
- }
225157
- async function collectMessageFiles(messages, blobs) {
225158
- const rows = [];
225159
- for (const message of messages) {
225160
- if (!Array.isArray(message.attachments)) continue;
225161
- message.attachments.forEach((attachment, index2) => {
225162
- if (!attachment || typeof attachment.name !== "string" || !attachment.name) return;
225163
- const blobRef = typeof attachment.blobRef === "string" && attachment.blobRef ? attachment.blobRef : null;
225164
- rows.push({
225165
- item: {
225166
- id: `msg:${message.id}:${index2}`,
225167
- name: attachment.name,
225168
- kind: "file",
225169
- updated_at: attachment.updatedAt ?? message.createdAt,
225170
- source: "message",
225171
- blob_ref: blobRef,
225172
- path: null,
225173
- message_id: message.id,
225174
- // 没有 blobRef 的是**历史纯文本附件**(契约注释:legacy text attachments may only
225175
- // contain `name`)——没有可写回的载体,一律不给编辑入口。
225176
- editable: blobRef !== null && isEditableFileName(attachment.name)
225177
- },
225178
- blobRef,
225179
- declaredType: typeof attachment.contentType === "string" && attachment.contentType ? attachment.contentType : null
225180
- });
225181
- });
225182
- }
225183
- const metaCache = /* @__PURE__ */ new Map();
225184
- const metaOf = (ref2) => {
225185
- if (!blobs) return Promise.resolve(null);
225186
- let hit = metaCache.get(ref2);
225187
- if (!hit) {
225188
- hit = blobs.meta(ref2).catch(() => null);
225189
- metaCache.set(ref2, hit);
225190
- }
225191
- return hit;
225192
- };
225193
- return Promise.all(rows.map(async ({ item, blobRef, declaredType }) => {
225194
- const meta = blobRef ? await metaOf(blobRef) : null;
225195
- return {
225196
- ...item,
225197
- content_type: declaredType ?? meta?.contentType ?? null,
225198
- size: meta?.size ?? null
225199
- };
225200
- }));
225201
- }
225202
- function mergeChatSessionFiles(messageItems, workdirItems, materializedCopies = /* @__PURE__ */ new Set()) {
225203
- const out = [];
225204
- const seenMessageKeys = /* @__PURE__ */ new Set();
225205
- for (const item of [...messageItems].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at))) {
225206
- const key = `${item.name}\0${item.blob_ref ?? ""}`;
225207
- if (seenMessageKeys.has(key)) continue;
225208
- seenMessageKeys.add(key);
225209
- out.push(item);
225210
- }
225211
- const seenWorkdirPaths = /* @__PURE__ */ new Set();
225212
- for (const item of workdirItems) {
225213
- if (item.path && materializedCopies.has(item.path)) continue;
225214
- if (seenWorkdirPaths.has(item.id)) continue;
225215
- seenWorkdirPaths.add(item.id);
225216
- out.push(item);
225217
- }
225218
- out.sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.name.localeCompare(b2.name));
225219
- return out;
225220
- }
225221
- async function findMaterializedAttachmentCopies(messageItems, workdirItems, read) {
225222
- const copies = /* @__PURE__ */ new Set();
225223
- const attachments = messageItems.filter((item) => item.blob_ref && /^[a-f0-9]{64}$/.test(item.blob_ref));
225224
- let budget = CHAT_ATTACHMENT_MAX_BYTES;
225225
- const deadline = Date.now() + WORKDIR_WALK_BUDGET_MS;
225226
- let reads = 0;
225227
- for (const file of workdirItems) {
225228
- const rootFile = file.path === file.name;
225229
- const inputFile = file.path?.startsWith("inputs/attachments/") && !file.path.slice("inputs/attachments/".length).includes("/");
225230
- if (!file.path || !rootFile && !inputFile) continue;
225231
- const candidates = attachments.filter((attachment) => {
225232
- const safe = safeAttachmentName(attachment.name);
225233
- const numbered = file.name.endsWith(`-${safe}`) && /^\d+$/.test(file.name.slice(0, -(safe.length + 1)));
225234
- return (rootFile ? file.name === attachment.name : file.name === safe || numbered) && attachment.size !== null && attachment.size === file.size;
225235
- });
225236
- if (Date.now() >= deadline) break;
225237
- if (!candidates.length || file.size === null || file.size > budget || reads >= 10) continue;
225238
- reads += 1;
225239
- budget -= file.size;
225240
- let timer;
225241
- try {
225242
- const result = await Promise.race([
225243
- read(file.path, file.size),
225244
- new Promise((resolve10) => {
225245
- timer = setTimeout(() => resolve10(null), Math.max(0, deadline - Date.now()));
225246
- })
225247
- ]);
225248
- if (!result?.ok) continue;
225249
- const bytes2 = Buffer.from(result.contentBase64, "base64");
225250
- if (bytes2.length !== file.size) continue;
225251
- const hash2 = (0, import_node_crypto51.createHash)("sha256").update(bytes2).digest("hex");
225252
- if (candidates.some((attachment) => attachment.blob_ref === hash2)) copies.add(file.path);
225253
- } catch {
225254
- } finally {
225255
- if (timer) clearTimeout(timer);
225256
- }
225257
- }
225258
- return copies;
225259
- }
225260
- var import_node_crypto51, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
225261
- var init_files_view = __esm({
225262
- "../server/src/domains/chat-sessions/files-view.ts"() {
225263
- "use strict";
225264
- import_node_crypto51 = require("node:crypto");
225265
- init_chat_attachment_files();
225266
- init_src();
225267
- WORKDIR_WALK_MAX_DEPTH = 3;
225268
- WORKDIR_WALK_MAX_ENTRIES = 200;
225269
- WORKDIR_WALK_MAX_LIST_CALLS = 20;
225270
- WORKDIR_WALK_BUDGET_MS = 1e4;
225271
- WORKDIR_TREE_MAX_ENTRIES = 2e3;
225272
- }
225273
- });
225274
-
225275
225585
  // ../server/src/domains/projects/chat-files.ts
225276
225586
  function isProjectChatSession(session) {
225277
225587
  if (session.visibility === "internal") return false;
@@ -225283,79 +225593,45 @@ function chatFolderTitle(session) {
225283
225593
  const title = session.title?.trim();
225284
225594
  return title ? title : "\u672A\u547D\u540D\u5BF9\u8BDD";
225285
225595
  }
225286
- function outputDisplayName(path41) {
225287
- return path41.startsWith("outputs/") ? path41.slice("outputs/".length) : path41;
225288
- }
225289
225596
  async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
225290
225597
  const scope = isUncategorizedProjectId(projectId2) ? null : projectId2;
225291
- const [attachmentMessages, outputSessions] = await Promise.all([
225292
- chat.listAttachmentMessages ? chat.listAttachmentMessages(scope) : Promise.resolve([]),
225293
- chat.listOutputCopies ? chat.listOutputCopies(scope) : Promise.resolve([])
225598
+ const [attachmentMessages, outputSessions, taskSessions] = await Promise.all([
225599
+ chat.listAttachmentMessages?.(scope) ?? [],
225600
+ chat.listOutputCopies?.(scope) ?? [],
225601
+ chat.listFileSessionsByWorkspaces?.(options.workOrderIds ?? []) ?? []
225294
225602
  ]);
225295
- const sessionIds = [.../* @__PURE__ */ new Set([
225603
+ const ids2 = [.../* @__PURE__ */ new Set([
225296
225604
  ...attachmentMessages.map((m2) => m2.sessionId),
225297
- ...outputSessions.map((s2) => s2.sessionId)
225605
+ ...outputSessions.map((s2) => s2.sessionId),
225606
+ ...taskSessions.map((s2) => s2.id)
225298
225607
  ])];
225299
- const sessions = /* @__PURE__ */ new Map();
225300
- await Promise.all(sessionIds.map(async (id) => {
225301
- const session = await chat.getSession(id);
225302
- if (!session) return;
225303
- if ((session.projectId ?? null) !== scope) return;
225304
- if (!isProjectChatSession(session)) return;
225305
- if (options.isDelegationChild && await options.isDelegationChild(id).catch(() => false)) return;
225306
- sessions.set(id, session);
225307
- }));
225308
225608
  const out = [];
225309
- const base = (session) => ({
225310
- projectId: projectId2,
225311
- chatSessionId: session.id,
225312
- chatTitle: chatFolderTitle(session),
225313
- chatOwnerId: session.humanActorId
225314
- });
225315
- const bySession = /* @__PURE__ */ new Map();
225316
- for (const message of attachmentMessages) {
225317
- if (!sessions.has(message.sessionId)) continue;
225318
- const list2 = bySession.get(message.sessionId) ?? [];
225319
- list2.push(message);
225320
- bySession.set(message.sessionId, list2);
225321
- }
225322
- for (const [sessionId, messages] of bySession) {
225323
- const session = sessions.get(sessionId);
225324
- const rows = await collectMessageFiles(messages.map((m2) => ({ ...m2, id: m2.messageId })), { meta });
225325
- for (const row of mergeChatSessionFiles(rows, [])) {
225326
- if (row.blob_ref === null) continue;
225327
- out.push({
225328
- ...base(session),
225329
- fileId: `chat:${sessionId}:${row.id}`,
225330
- name: row.name,
225331
- blobId: row.blob_ref,
225332
- ...row.content_type ? { contentType: row.content_type } : {},
225333
- size: row.size,
225334
- uploadedBy: session.humanActorId,
225335
- uploadedAt: row.updated_at,
225336
- chatSource: "attachment"
225337
- });
225338
- }
225339
- }
225340
- for (const record8 of outputSessions) {
225341
- const session = sessions.get(record8.sessionId);
225342
- if (!session) continue;
225343
- const seen = new Set(out.filter((file) => file.chatSessionId === session.id).map((file) => `${file.name}\0${file.blobId}`));
225344
- const copies = record8.copies ?? {};
225345
- for (const path41 of Object.keys(copies).sort((a, b2) => a.localeCompare(b2))) {
225346
- const copy = copies[path41];
225347
- if (!copy?.blobRef) continue;
225348
- if (seen.has(`${copy.name}\0${copy.blobRef}`)) continue;
225609
+ for (const id of ids2) {
225610
+ const session = await chat.getSession(id);
225611
+ if (!session || session.visibility === "internal" || session.analyzedRunId) continue;
225612
+ if (session.workspace) {
225613
+ if (!options.workOrderIds?.includes(session.workspace)) continue;
225614
+ } else if ((session.projectId ?? null) !== scope || !isProjectChatSession(session)) continue;
225615
+ if (options.isDelegationChild && await options.isDelegationChild(id)) continue;
225616
+ const items = await readPersistentSessionFiles(chat, id, { meta });
225617
+ const fileIssues = await chat.readFileIssues?.(id) ?? [];
225618
+ for (const item of items) {
225619
+ if (!item.blob_ref) continue;
225349
225620
  out.push({
225350
- ...base(session),
225351
- fileId: `chat:${session.id}:out:${copy.path}`,
225352
- name: outputDisplayName(copy.path),
225353
- blobId: copy.blobRef,
225354
- ...copy.contentType ? { contentType: copy.contentType } : {},
225355
- size: copy.size,
225356
- uploadedBy: session.aiActorId,
225357
- uploadedAt: Number.isNaN(Date.parse(copy.mtime)) ? copy.copiedAt : new Date(copy.mtime).toISOString(),
225358
- chatSource: "output"
225621
+ projectId: projectId2,
225622
+ ...fileIssues.length ? { fileIssues } : {},
225623
+ chatSessionId: id,
225624
+ chatTitle: chatFolderTitle(session),
225625
+ chatOwnerId: session.humanActorId,
225626
+ fileId: `chat:${id}:${item.id}`,
225627
+ name: item.name,
225628
+ blobId: item.blob_ref,
225629
+ ...item.content_type ? { contentType: item.content_type } : {},
225630
+ size: item.size,
225631
+ uploadedBy: item.source === "output" ? session.aiActorId : session.humanActorId,
225632
+ uploadedAt: item.updated_at,
225633
+ chatSource: item.source === "output" ? "output" : "attachment",
225634
+ ...session.workspace ? { taskId: session.workspace } : {}
225359
225635
  });
225360
225636
  }
225361
225637
  }
@@ -225364,7 +225640,8 @@ async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
225364
225640
  var init_chat_files2 = __esm({
225365
225641
  "../server/src/domains/projects/chat-files.ts"() {
225366
225642
  "use strict";
225367
- init_files_view();
225643
+ init_persistent_files();
225644
+ init_persistent_files();
225368
225645
  init_uncategorized_project();
225369
225646
  }
225370
225647
  });
@@ -226328,7 +226605,8 @@ function toApiProjectFilesView(view) {
226328
226605
  // 对话文件夹(`"chat"`):前端据此换带对话气泡的文件夹图标。其余文件夹与文件行为 null。
226329
226606
  folder_kind: item.folderKind ?? null,
226330
226607
  // 对话文件夹及其子文件所属的对话;前端「所属任务」列的「对话」筛选档靠它。其余行为 null。
226331
- chat_session_id: item.chatSessionId ?? null
226608
+ chat_session_id: item.chatSessionId ?? null,
226609
+ ...item.fileIssues?.length ? { file_issues: item.fileIssues } : {}
226332
226610
  }))
226333
226611
  };
226334
226612
  }
@@ -228652,11 +228930,11 @@ function fromRow(row) {
228652
228930
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
228653
228931
  return r;
228654
228932
  }
228655
- var import_node_crypto52, import_node_fs14, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
228933
+ var import_node_crypto54, import_node_fs14, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
228656
228934
  var init_notification_store = __esm({
228657
228935
  "../server/src/domains/collab/notification-store.ts"() {
228658
228936
  "use strict";
228659
- import_node_crypto52 = require("node:crypto");
228937
+ import_node_crypto54 = require("node:crypto");
228660
228938
  import_node_fs14 = __toESM(require("node:fs"), 1);
228661
228939
  DEFAULT_PAGE = 50;
228662
228940
  MAX_PAGE = 200;
@@ -228689,7 +228967,7 @@ var init_notification_store = __esm({
228689
228967
  async insert(n) {
228690
228968
  const existing = this.findLive(n.recipient, n.sourceKey, n.category);
228691
228969
  if (existing) return existing;
228692
- const row = { ...n, id: `ntf:${(0, import_node_crypto52.randomUUID)()}`, updatedAt: n.enteredAt };
228970
+ const row = { ...n, id: `ntf:${(0, import_node_crypto54.randomUUID)()}`, updatedAt: n.enteredAt };
228693
228971
  this.rows.set(row.id, row);
228694
228972
  this.save();
228695
228973
  return row;
@@ -228812,7 +229090,7 @@ var init_notification_store = __esm({
228812
229090
  return new _PostgresNotificationStore(sql, schema);
228813
229091
  }
228814
229092
  async insert(n) {
228815
- const id = `ntf:${(0, import_node_crypto52.randomUUID)()}`;
229093
+ const id = `ntf:${(0, import_node_crypto54.randomUUID)()}`;
228816
229094
  const r = await this.sql.query(
228817
229095
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
228818
229096
  title, workspace_title, entered_at, updated_at, status, read_at, item)
@@ -229763,13 +230041,13 @@ function nameKey(name) {
229763
230041
  function cleanName(name) {
229764
230042
  return name.trim().replace(/\s+/g, " ");
229765
230043
  }
229766
- var import_node_fs15, import_node_path20, import_node_crypto53, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
230044
+ var import_node_fs15, import_node_path20, import_node_crypto55, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
229767
230045
  var init_tags = __esm({
229768
230046
  "../server/src/domains/collab/tags.ts"() {
229769
230047
  "use strict";
229770
230048
  import_node_fs15 = __toESM(require("node:fs"), 1);
229771
230049
  import_node_path20 = __toESM(require("node:path"), 1);
229772
- import_node_crypto53 = __toESM(require("node:crypto"), 1);
230050
+ import_node_crypto55 = __toESM(require("node:crypto"), 1);
229773
230051
  SEP = "::";
229774
230052
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
229775
230053
  prefixOf = (companyId) => `${companyId}${SEP}`;
@@ -229787,7 +230065,7 @@ var init_tags = __esm({
229787
230065
  if (!name) return null;
229788
230066
  if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
229789
230067
  const tag = {
229790
- id: `tag-${import_node_crypto53.default.randomBytes(6).toString("hex")}`,
230068
+ id: `tag-${import_node_crypto55.default.randomBytes(6).toString("hex")}`,
229791
230069
  name,
229792
230070
  ...input.color?.trim() ? { color: input.color.trim() } : {},
229793
230071
  ...input.description?.trim() ? { description: input.description.trim() } : {}
@@ -231033,11 +231311,11 @@ async function isEmployedElsewhere(store, companyId, accountId) {
231033
231311
  const memberships = await store.listMembershipsForAccount(accountId);
231034
231312
  return memberships.some((m2) => m2.companyId !== companyId);
231035
231313
  }
231036
- var import_node_crypto54, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
231314
+ var import_node_crypto56, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
231037
231315
  var init_service6 = __esm({
231038
231316
  "../server/src/domains/companies/service.ts"() {
231039
231317
  "use strict";
231040
- import_node_crypto54 = require("node:crypto");
231318
+ import_node_crypto56 = require("node:crypto");
231041
231319
  init_console_url();
231042
231320
  ROLES = ["owner", "admin", "member"];
231043
231321
  INVITABLE_ROLES = ["admin", "member"];
@@ -231129,7 +231407,7 @@ var init_service6 = __esm({
231129
231407
  const existing = await this.store.getAccountByEmail(normalized4);
231130
231408
  if (existing) return existing;
231131
231409
  const account = {
231132
- id: `actor:human:${(0, import_node_crypto54.randomUUID)()}`,
231410
+ id: `actor:human:${(0, import_node_crypto56.randomUUID)()}`,
231133
231411
  email: normalized4,
231134
231412
  name: name ?? normalized4,
231135
231413
  status: "active"
@@ -231338,7 +231616,7 @@ var init_service6 = __esm({
231338
231616
  }
231339
231617
  const nowMs = Date.parse(this.now());
231340
231618
  const invitation = {
231341
- id: `invitation:${(0, import_node_crypto54.randomUUID)()}`,
231619
+ id: `invitation:${(0, import_node_crypto56.randomUUID)()}`,
231342
231620
  companyId,
231343
231621
  email: mail,
231344
231622
  role,
@@ -231469,7 +231747,7 @@ var init_service6 = __esm({
231469
231747
  const nowMs = Date.parse(this.now());
231470
231748
  const link = {
231471
231749
  companyId,
231472
- token: (0, import_node_crypto54.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
231750
+ token: (0, import_node_crypto56.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
231473
231751
  role: INVITE_LINK_ROLE,
231474
231752
  createdAt: new Date(nowMs).toISOString(),
231475
231753
  expiresAt: new Date(nowMs + INVITE_LINK_TTL_MS).toISOString(),
@@ -231526,7 +231804,7 @@ var init_service6 = __esm({
231526
231804
  const reusable = existing.find((inv) => !this.isExpired(inv));
231527
231805
  if (reusable) return reusable;
231528
231806
  const invitation = {
231529
- id: `invitation:${(0, import_node_crypto54.randomUUID)()}`,
231807
+ id: `invitation:${(0, import_node_crypto56.randomUUID)()}`,
231530
231808
  companyId: link.companyId,
231531
231809
  email: mail,
231532
231810
  role: link.role,
@@ -231764,11 +232042,11 @@ var init_service6 = __esm({
231764
232042
  });
231765
232043
 
231766
232044
  // ../server/src/domains/companies/offboarding.ts
231767
- var import_node_crypto55, MemberOffboardingService;
232045
+ var import_node_crypto57, MemberOffboardingService;
231768
232046
  var init_offboarding = __esm({
231769
232047
  "../server/src/domains/companies/offboarding.ts"() {
231770
232048
  "use strict";
231771
- import_node_crypto55 = require("node:crypto");
232049
+ import_node_crypto57 = require("node:crypto");
231772
232050
  init_service6();
231773
232051
  MemberOffboardingService = class {
231774
232052
  store;
@@ -231840,7 +232118,7 @@ var init_offboarding = __esm({
231840
232118
  const { ownedAgents, inFlight } = await this.snapshot(companyId, accountId);
231841
232119
  const at = this.now();
231842
232120
  const started = await this.store.startMemberOffboarding({
231843
- operationId: `off:${(0, import_node_crypto55.randomUUID)()}`,
232121
+ operationId: `off:${(0, import_node_crypto57.randomUUID)()}`,
231844
232122
  companyId,
231845
232123
  accountId,
231846
232124
  initiatedBy,
@@ -232350,18 +232628,18 @@ var init_routes6 = __esm({
232350
232628
 
232351
232629
  // ../server/src/domains/companies/agent-credentials.ts
232352
232630
  function hashAgentToken(token, secret) {
232353
- return HASH_PREFIX + (0, import_node_crypto56.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
232631
+ return HASH_PREFIX + (0, import_node_crypto58.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
232354
232632
  }
232355
232633
  function safeEqual(a, b2) {
232356
232634
  const ba = Buffer.from(a);
232357
232635
  const bb = Buffer.from(b2);
232358
- return ba.length === bb.length && (0, import_node_crypto56.timingSafeEqual)(ba, bb);
232636
+ return ba.length === bb.length && (0, import_node_crypto58.timingSafeEqual)(ba, bb);
232359
232637
  }
232360
- var import_node_crypto56, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
232638
+ var import_node_crypto58, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
232361
232639
  var init_agent_credentials = __esm({
232362
232640
  "../server/src/domains/companies/agent-credentials.ts"() {
232363
232641
  "use strict";
232364
- import_node_crypto56 = require("node:crypto");
232642
+ import_node_crypto58 = require("node:crypto");
232365
232643
  init_service6();
232366
232644
  HASH_PREFIX = "ac1$";
232367
232645
  TOKEN_PREFIX = "oak_";
@@ -232386,7 +232664,7 @@ var init_agent_credentials = __esm({
232386
232664
  if (employee.status !== "active") {
232387
232665
  throw new CompanyError(409, "AGENT_LEFT", `\u6570\u5B57\u5458\u5DE5\u5DF2\u79BB\u5F00\uFF0C\u4E0D\u80FD\u7B7E\u53D1\u51ED\u8BC1\uFF1A${agentId}`);
232388
232666
  }
232389
- const token = `${TOKEN_PREFIX}${(0, import_node_crypto56.randomBytes)(32).toString("base64url")}`;
232667
+ const token = `${TOKEN_PREFIX}${(0, import_node_crypto58.randomBytes)(32).toString("base64url")}`;
232390
232668
  const credential = {
232391
232669
  tokenHash: hashAgentToken(token, this.secret),
232392
232670
  companyId,
@@ -232806,7 +233084,7 @@ function gatewayInstanceSlug(gatewayUrl) {
232806
233084
  }
232807
233085
  function connectInstanceName(gatewayUrl, token, companyId) {
232808
233086
  const scope = companyId?.trim();
232809
- const suffix = scope ? `c${(0, import_node_crypto57.createHash)("sha256").update(`${gatewayUrl}\0${scope}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
233087
+ const suffix = scope ? `c${(0, import_node_crypto59.createHash)("sha256").update(`${gatewayUrl}\0${scope}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
232810
233088
  const base = gatewayInstanceSlug(gatewayUrl);
232811
233089
  return base.slice(0, Math.max(1, 64 - suffix.length - 1)) + "-" + suffix;
232812
233090
  }
@@ -233135,12 +233413,12 @@ function buildNpxCmd(p2) {
233135
233413
  const prefix = daemonNpmPrefix();
233136
233414
  return `npm install -g --prefer-online --prefix ${prefix} ${DAEMON_NPM_PACKAGE}@latest && ${prefix}/bin/oasis daemon start --instance ${shellQuote(instanceName)} --server-ws ${shellQuote(p2.gatewayUrl)}${companyPart}${namePart} --pkg ${shellQuote(DAEMON_NPM_PACKAGE)} --upgrade --enroll-token ${shellQuote(p2.token)}`;
233137
233415
  }
233138
- var import_node_crypto57;
233416
+ var import_node_crypto59;
233139
233417
  var init_connect_script = __esm({
233140
233418
  "../server/src/domains/nodes/connect-script.ts"() {
233141
233419
  "use strict";
233142
233420
  init_src();
233143
- import_node_crypto57 = require("node:crypto");
233421
+ import_node_crypto59 = require("node:crypto");
233144
233422
  }
233145
233423
  });
233146
233424
 
@@ -233701,7 +233979,7 @@ function nodesDomain(deps) {
233701
233979
  }
233702
233980
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
233703
233981
  } else {
233704
- nodeId = `node-${(0, import_node_crypto58.randomUUID)().slice(0, 8)}`;
233982
+ nodeId = `node-${(0, import_node_crypto60.randomUUID)().slice(0, 8)}`;
233705
233983
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
233706
233984
  }
233707
233985
  const existingNode = await deps.nodeStore.getNode(nodeId);
@@ -233884,13 +234162,13 @@ function normalizeGatewayUrl(raw) {
233884
234162
  }
233885
234163
  }
233886
234164
  function newNodeId() {
233887
- return "node-" + (0, import_node_crypto58.randomUUID)().replace(/-/g, "").slice(0, 12);
234165
+ return "node-" + (0, import_node_crypto60.randomUUID)().replace(/-/g, "").slice(0, 12);
233888
234166
  }
233889
- var import_node_crypto58, import_node_fs16, import_node_url6, import_node_path21;
234167
+ var import_node_crypto60, import_node_fs16, import_node_url6, import_node_path21;
233890
234168
  var init_routes7 = __esm({
233891
234169
  "../server/src/domains/nodes/routes.ts"() {
233892
234170
  "use strict";
233893
- import_node_crypto58 = require("node:crypto");
234171
+ import_node_crypto60 = require("node:crypto");
233894
234172
  import_node_fs16 = require("node:fs");
233895
234173
  import_node_url6 = require("node:url");
233896
234174
  import_node_path21 = require("node:path");
@@ -235081,7 +235359,7 @@ function createBundle(root, target, input) {
235081
235359
  }
235082
235360
  }
235083
235361
  const bytes2 = fs26.statSync(absBundle).size;
235084
- const digest = (0, import_node_crypto59.createHash)("sha256").update(fs26.readFileSync(absBundle)).digest("hex");
235362
+ const digest = (0, import_node_crypto61.createHash)("sha256").update(fs26.readFileSync(absBundle)).digest("hex");
235085
235363
  const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
235086
235364
  return {
235087
235365
  absoluteBundlePath: absBundle,
@@ -235177,12 +235455,12 @@ function classifyGitError(fallback, err, where) {
235177
235455
  function oneLine(msg) {
235178
235456
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
235179
235457
  }
235180
- var import_node_child_process17, import_node_crypto59, fs26, os7, path25, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
235458
+ var import_node_child_process17, import_node_crypto61, fs26, os7, path25, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
235181
235459
  var init_readonly_bundle = __esm({
235182
235460
  "../server/src/git/readonly-bundle.ts"() {
235183
235461
  "use strict";
235184
235462
  import_node_child_process17 = require("node:child_process");
235185
- import_node_crypto59 = require("node:crypto");
235463
+ import_node_crypto61 = require("node:crypto");
235186
235464
  fs26 = __toESM(require("node:fs"), 1);
235187
235465
  os7 = __toESM(require("node:os"), 1);
235188
235466
  path25 = __toESM(require("node:path"), 1);
@@ -235202,7 +235480,7 @@ var init_readonly_bundle = __esm({
235202
235480
 
235203
235481
  // ../server/src/domains/collab/create-seeded-workorder.ts
235204
235482
  function makeSeededWorkorderCreator(deps) {
235205
- const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto60.randomUUID)().slice(0, 8)}`);
235483
+ const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto62.randomUUID)().slice(0, 8)}`);
235206
235484
  return async (input) => {
235207
235485
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
235208
235486
  const kernel = company?.kernel ?? deps.kernel;
@@ -235295,7 +235573,7 @@ function makeSeededWorkorderCreator(deps) {
235295
235573
  };
235296
235574
  }
235297
235575
  function workspaceForKey(key) {
235298
- return `ws:wo-${(0, import_node_crypto60.createHash)("sha256").update(key).digest("hex").slice(0, 8)}`;
235576
+ return `ws:wo-${(0, import_node_crypto62.createHash)("sha256").update(key).digest("hex").slice(0, 8)}`;
235299
235577
  }
235300
235578
  function existingWorkorder(kernel, workspace) {
235301
235579
  const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === workspace);
@@ -235308,11 +235586,11 @@ function existingWorkorder(kernel, workspace) {
235308
235586
  spawned: arts.map((a) => ({ id: a.id, type: a.type, owner: a.owner }))
235309
235587
  };
235310
235588
  }
235311
- var import_node_crypto60, enc2;
235589
+ var import_node_crypto62, enc2;
235312
235590
  var init_create_seeded_workorder = __esm({
235313
235591
  "../server/src/domains/collab/create-seeded-workorder.ts"() {
235314
235592
  "use strict";
235315
- import_node_crypto60 = require("node:crypto");
235593
+ import_node_crypto62 = require("node:crypto");
235316
235594
  init_uncategorized_project();
235317
235595
  init_src();
235318
235596
  init_planner();
@@ -236742,12 +237020,12 @@ function stateFromAttempt(attempt, handoff, checkpoint, now, stalledAfterMs, ope
236742
237020
  if (attempt.status === "no-output") return handoff?.unresolvedIssues.length ? "blocked" : "no_change";
236743
237021
  return "failed";
236744
237022
  }
236745
- var import_node_crypto61, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
237023
+ var import_node_crypto63, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
236746
237024
  var init_service8 = __esm({
236747
237025
  "../server/src/domains/execution-continuity/service.ts"() {
236748
237026
  "use strict";
236749
237027
  init_src();
236750
- import_node_crypto61 = require("node:crypto");
237028
+ import_node_crypto63 = require("node:crypto");
236751
237029
  init_src5();
236752
237030
  BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
236753
237031
  HANDOFF_OUTCOMES2 = /* @__PURE__ */ new Set(["succeeded", "no-output", "failed", "cancelled", "timeout", "orphaned"]);
@@ -236978,7 +237256,7 @@ var init_service8 = __esm({
236978
237256
  if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
236979
237257
  const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
236980
237258
  if (!injectedResume) return null;
236981
- const actualHash = (0, import_node_crypto61.createHash)("sha256").update(injectedResume).digest("hex");
237259
+ const actualHash = (0, import_node_crypto63.createHash)("sha256").update(injectedResume).digest("hex");
236982
237260
  if (actualHash !== expectedHash) return null;
236983
237261
  const recoverySnapshot = parseResumePackSnapshot(injectedResume);
236984
237262
  if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
@@ -238082,7 +238360,7 @@ function createChatSessionsDomain(opts) {
238082
238360
  if (!text5.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
238083
238361
  const now = Date.now();
238084
238362
  sweepDrafts(now);
238085
- const id = `cd_${(0, import_node_crypto62.randomUUID)()}`;
238363
+ const id = `cd_${(0, import_node_crypto64.randomUUID)()}`;
238086
238364
  drafts.set(id, { text: text5, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
238087
238365
  return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
238088
238366
  });
@@ -238134,7 +238412,7 @@ function createChatSessionsDomain(opts) {
238134
238412
  const runtimeId = await currentRuntimeId(body2.aiActorId) ?? "unknown";
238135
238413
  const now = (/* @__PURE__ */ new Date()).toISOString();
238136
238414
  const session = {
238137
- id: (0, import_node_crypto62.randomUUID)(),
238415
+ id: (0, import_node_crypto64.randomUUID)(),
238138
238416
  humanActorId: req.auth.actor,
238139
238417
  // 注:这里**刻意不写 companyId**。新会话要不要带公司归属,按 ws:wo-6d588f8e 的 ADR
238140
238418
  // (每公司 schema、最终移除表内 company 列)统一定,不在本节点自行发明。
@@ -238240,7 +238518,7 @@ function createChatSessionsDomain(opts) {
238240
238518
  if (body2.role !== "user" && body2.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
238241
238519
  const role = body2.role;
238242
238520
  const msg = await store2.appendMessage({
238243
- id: (0, import_node_crypto62.randomUUID)(),
238521
+ id: (0, import_node_crypto64.randomUUID)(),
238244
238522
  sessionId: req.params.id,
238245
238523
  role,
238246
238524
  content: role === "user" ? stripInjectedChatContext(body2.content) : body2.content,
@@ -238281,7 +238559,7 @@ function createChatSessionsDomain(opts) {
238281
238559
  const known = key ? cur.find((x2) => x2.clientKey === key) : void 0;
238282
238560
  if (!known && cur.length >= QUEUE_MAX) throw new ApiError(409, "QUEUE_FULL", `\u961F\u5217\u5DF2\u6EE1\uFF08\u4E0A\u9650 ${QUEUE_MAX} \u6761\uFF09`);
238283
238561
  const item = await store.enqueuePendingMessage({
238284
- id: (0, import_node_crypto62.randomUUID)(),
238562
+ id: (0, import_node_crypto64.randomUUID)(),
238285
238563
  chatSessionId: sid,
238286
238564
  text: text5,
238287
238565
  ...attachments.length ? { attachments } : {},
@@ -238598,6 +238876,20 @@ function createChatSessionsDomain(opts) {
238598
238876
  return { status: 200, body: body2 };
238599
238877
  });
238600
238878
  router.get("/api/chat-sessions/:id/workdir/file", async (req) => {
238879
+ const store2 = await storeFor(req);
238880
+ const session = await store2.getSession(req.params.id);
238881
+ if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
238882
+ const copy = (await store2.readOutputCopies?.(session.id))?.[req.query?.get("path") ?? ""];
238883
+ if (copy) {
238884
+ const blobs = opts.resolveBlobs ? await opts.resolveBlobs(req.auth.companyId) : opts.blobs;
238885
+ if (!blobs?.get) throw new ApiError(503, "BLOB_STORE_UNAVAILABLE", "\u6587\u4EF6\u5B58\u50A8\u6682\u4E0D\u53EF\u7528");
238886
+ const bytes2 = Buffer.from(await blobs.get(copy.blobRef));
238887
+ return { status: 200, body: bytes2, headers: {
238888
+ "content-type": copy.contentType ?? "application/octet-stream",
238889
+ "content-length": String(bytes2.length),
238890
+ "cache-control": "no-store"
238891
+ } };
238892
+ }
238601
238893
  const bridge = opts.workdir?.();
238602
238894
  if (!bridge) throw new ApiError(404, "NOT_FOUND", "\u5DE5\u4F5C\u533A\u6587\u4EF6\u6D4F\u89C8\u672A\u542F\u7528");
238603
238895
  const loc = await locateWorkdir(await storeFor(req), req.auth.companyId, req.params.id, req.auth.actor);
@@ -238634,9 +238926,12 @@ function createChatSessionsDomain(opts) {
238634
238926
  const store2 = await storeFor(req);
238635
238927
  const session = await store2.getSession(req.params.id);
238636
238928
  if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
238637
- const attachmentRows = store2.listSessionAttachmentMessages ? await store2.listSessionAttachmentMessages(session.id) : await store2.listMessages(session.id);
238638
238929
  const blobAccess = opts.resolveBlobs ? await opts.resolveBlobs(req.auth?.companyId) : opts.blobs;
238639
- const messageItems = await collectMessageFiles(attachmentRows, blobAccess);
238930
+ const persistentItems = await readPersistentSessionFiles(store2, session.id, blobAccess, true);
238931
+ const messageItems = persistentItems.filter((item) => item.source === "message");
238932
+ const allOutputs = persistentItems.filter((item) => item.source === "output");
238933
+ const savedOutputs = allOutputs.filter((output) => !messageItems.some((m2) => m2.name === output.name && m2.blob_ref === output.blob_ref));
238934
+ const fileIssues = await store2.readFileIssues?.(session.id) ?? [];
238640
238935
  let workdirItems = [];
238641
238936
  let materializedCopies = /* @__PURE__ */ new Set();
238642
238937
  let workdirAvailable = false;
@@ -238663,8 +238958,13 @@ function createChatSessionsDomain(opts) {
238663
238958
  }
238664
238959
  }
238665
238960
  }
238666
- const items = mergeChatSessionFiles(messageItems, workdirItems, materializedCopies);
238961
+ const archivedPaths = new Set(allOutputs.map((item) => item.path));
238962
+ const items = mergeChatSessionFiles(messageItems, [
238963
+ ...savedOutputs,
238964
+ ...workdirItems.filter((item) => !archivedPaths.has(item.path) && !item.path?.startsWith("inputs/history/"))
238965
+ ], materializedCopies);
238667
238966
  const body2 = {
238967
+ ...fileIssues.length ? { fileIssues } : {},
238668
238968
  total: items.length,
238669
238969
  items,
238670
238970
  workdirAvailable,
@@ -238732,13 +239032,14 @@ function createChatSessionsDomain(opts) {
238732
239032
  });
238733
239033
  };
238734
239034
  }
238735
- var import_node_crypto62, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS;
239035
+ var import_node_crypto64, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS;
238736
239036
  var init_chat_sessions2 = __esm({
238737
239037
  "../server/src/domains/chat-sessions/index.ts"() {
238738
239038
  "use strict";
238739
- import_node_crypto62 = require("node:crypto");
239039
+ import_node_crypto64 = require("node:crypto");
238740
239040
  init_src();
238741
239041
  init_chat_session();
239042
+ init_persistent_files();
238742
239043
  init_files_view();
238743
239044
  init_router();
238744
239045
  init_collab();
@@ -240141,7 +240442,7 @@ function busyError(chatSessionId, blockedBy) {
240141
240442
  return `\u76EE\u6807\u4F1A\u8BDD ${chatSessionId} \u6B63\u6709\u4E00\u8F6E\u5728\u8DD1${blockedBy ? `\uFF08${blockedBy}\uFF09` : ""}\u2014\u2014\u672C\u6B21\u89E6\u53D1\u5DF2\u4E22\u5F03\uFF0C\u4E0D\u6392\u961F\u3001\u4E0D\u8865\u8DD1`;
240142
240443
  }
240143
240444
  function genWebhookToken() {
240144
- return `owt_${(0, import_node_crypto63.randomBytes)(32).toString("base64url")}`;
240445
+ return `owt_${(0, import_node_crypto65.randomBytes)(32).toString("base64url")}`;
240145
240446
  }
240146
240447
  function validateTitleTemplate(tpl) {
240147
240448
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -240197,11 +240498,11 @@ function appendSourceNote(brief, automation, source, atIso) {
240197
240498
  ---
240198
240499
  \u672C\u5DE5\u5355\u7531\u81EA\u52A8\u5316\u300C${automation.name}\u300D\u4E8E ${atIso} \u89E6\u53D1\uFF08\u6765\u6E90\uFF1A${source}\uFF09\u3002\u5F00\u5DE5\u540E\u8BF7\u628A\u5DE5\u5355\u6807\u9898\u6539\u6210\u51C6\u786E\u53CD\u6620\u5B9E\u9645\u5DE5\u4F5C\u7684\u63CF\u8FF0\u3002`;
240199
240500
  }
240200
- var import_node_crypto63, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
240501
+ var import_node_crypto65, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
240201
240502
  var init_service9 = __esm({
240202
240503
  "../server/src/domains/automations/service.ts"() {
240203
240504
  "use strict";
240204
- import_node_crypto63 = require("node:crypto");
240505
+ import_node_crypto65 = require("node:crypto");
240205
240506
  init_src();
240206
240507
  init_registry3();
240207
240508
  init_attribution();
@@ -241015,11 +241316,11 @@ function verifySignature(rawBody, headers, secret) {
241015
241316
  if (!secret) return "not_required";
241016
241317
  const given = header1(headers, "x-hub-signature-256");
241017
241318
  if (!given) return "missing";
241018
- const expected = `sha256=${(0, import_node_crypto64.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
241319
+ const expected = `sha256=${(0, import_node_crypto66.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
241019
241320
  const a = Buffer.from(given);
241020
241321
  const b2 = Buffer.from(expected);
241021
241322
  if (a.length !== b2.length) return "invalid";
241022
- return (0, import_node_crypto64.timingSafeEqual)(a, b2) ? "valid" : "invalid";
241323
+ return (0, import_node_crypto66.timingSafeEqual)(a, b2) ? "valid" : "invalid";
241023
241324
  }
241024
241325
  function eventAllowed(filters, envelope) {
241025
241326
  if (!filters || filters.length === 0) return true;
@@ -241049,11 +241350,11 @@ function splitEvent(envelope) {
241049
241350
  }
241050
241351
  return { eventName, actionCandidates: candidates };
241051
241352
  }
241052
- var import_node_crypto64, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
241353
+ var import_node_crypto66, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
241053
241354
  var init_webhook = __esm({
241054
241355
  "../server/src/domains/automations/webhook.ts"() {
241055
241356
  "use strict";
241056
- import_node_crypto64 = require("node:crypto");
241357
+ import_node_crypto66 = require("node:crypto");
241057
241358
  MAX_BODY_BYTES = 256 * 1024;
241058
241359
  AutomationWebhookService = class {
241059
241360
  store;
@@ -242209,7 +242510,7 @@ function playbooksDomain(opts) {
242209
242510
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
242210
242511
  try {
242211
242512
  await opts.audit?.({
242212
- id: `reg_${(0, import_node_crypto65.randomUUID)()}`,
242513
+ id: `reg_${(0, import_node_crypto67.randomUUID)()}`,
242213
242514
  actor: req.auth.actor,
242214
242515
  kind: "registry_change",
242215
242516
  target: `playbook:${ref2}#${nodeKey}`,
@@ -242226,11 +242527,11 @@ function playbooksDomain(opts) {
242226
242527
  });
242227
242528
  };
242228
242529
  }
242229
- var import_node_crypto65;
242530
+ var import_node_crypto67;
242230
242531
  var init_routes12 = __esm({
242231
242532
  "../server/src/domains/playbooks/routes.ts"() {
242232
242533
  "use strict";
242233
- import_node_crypto65 = require("node:crypto");
242534
+ import_node_crypto67 = require("node:crypto");
242234
242535
  init_router();
242235
242536
  init_registry3();
242236
242537
  init_planner();
@@ -242344,17 +242645,17 @@ function completionFingerprint(workspace, members) {
242344
242645
  workspace,
242345
242646
  members: [...members].sort((a, b2) => a.artifactId < b2.artifactId ? -1 : a.artifactId > b2.artifactId ? 1 : 0).map((m2) => ({ artifactId: m2.artifactId, currentRevisionId: m2.currentRevisionId ?? null }))
242346
242647
  });
242347
- return (0, import_node_crypto66.createHash)("sha256").update(canonical).digest("hex");
242648
+ return (0, import_node_crypto68.createHash)("sha256").update(canonical).digest("hex");
242348
242649
  }
242349
242650
  function eventConsumptionKey(args) {
242350
242651
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
242351
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto66.createHash)("sha256").update(raw).digest("hex")}`;
242652
+ return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto68.createHash)("sha256").update(raw).digest("hex")}`;
242352
242653
  }
242353
- var import_node_crypto66, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
242654
+ var import_node_crypto68, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
242354
242655
  var init_project_event_poller = __esm({
242355
242656
  "../server/src/automations/project-event-poller.ts"() {
242356
242657
  "use strict";
242357
- import_node_crypto66 = require("node:crypto");
242658
+ import_node_crypto68 = require("node:crypto");
242358
242659
  EVENT_KEY_VERSION = "v1";
242359
242660
  EMPTY_METRICS = () => ({
242360
242661
  scanDurationMs: 0,
@@ -242565,7 +242866,7 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
242565
242866
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
242566
242867
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
242567
242868
  }
242568
- const requestId = (0, import_node_crypto67.randomUUID)();
242869
+ const requestId = (0, import_node_crypto69.randomUUID)();
242569
242870
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
242570
242871
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
242571
242872
  return new Promise((resolve10) => {
@@ -242578,11 +242879,11 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
242578
242879
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve10, timer });
242579
242880
  });
242580
242881
  }
242581
- var import_node_crypto67, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_IDS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
242882
+ var import_node_crypto69, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_IDS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
242582
242883
  var init_daemon_adapter = __esm({
242583
242884
  "../server/src/daemon-adapter.ts"() {
242584
242885
  "use strict";
242585
- import_node_crypto67 = require("node:crypto");
242886
+ import_node_crypto69 = require("node:crypto");
242586
242887
  init_src();
242587
242888
  STASH_TTL_MS = 5 * 6e4;
242588
242889
  STASH_MAX_FRAMES_PER_ID = 500;
@@ -242932,7 +243233,7 @@ var init_daemon_adapter = __esm({
242932
243233
  async spawn(job) {
242933
243234
  const nodeId = job.binding?.nodeId;
242934
243235
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
242935
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto67.randomUUID)()}`;
243236
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto69.randomUUID)()}`;
242936
243237
  if (job.dispatchId && this.pending.has(dispatchId)) {
242937
243238
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
242938
243239
  }
@@ -243169,7 +243470,7 @@ var init_daemon_adapter = __esm({
243169
243470
  return daemon?.meta.supportsWorkdirWalk === true;
243170
243471
  }
243171
243472
  request(nodeId, buildFrame) {
243172
- const requestId = (0, import_node_crypto67.randomUUID)();
243473
+ const requestId = (0, import_node_crypto69.randomUUID)();
243173
243474
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
243174
243475
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
243175
243476
  return new Promise((resolve10) => {
@@ -243277,13 +243578,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
243277
243578
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
243278
243579
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
243279
243580
  }
243280
- var import_node_crypto68, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
243581
+ var import_node_crypto70, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
243281
243582
  var init_engine = __esm({
243282
243583
  "../server/src/mirror/engine.ts"() {
243283
243584
  "use strict";
243284
- import_node_crypto68 = require("node:crypto");
243585
+ import_node_crypto70 = require("node:crypto");
243285
243586
  init_src();
243286
- sha = (s2) => (0, import_node_crypto68.createHash)("sha256").update(s2, "utf8").digest("hex");
243587
+ sha = (s2) => (0, import_node_crypto70.createHash)("sha256").update(s2, "utf8").digest("hex");
243287
243588
  enc3 = (s2) => new TextEncoder().encode(s2);
243288
243589
  dec = (b2) => new TextDecoder().decode(b2);
243289
243590
  MirrorEngine = class {
@@ -243530,11 +243831,11 @@ function humanExitCause(exit3) {
243530
243831
  const base = (exit3.reason ? label[exit3.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit3.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
243531
243832
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
243532
243833
  }
243533
- var import_node_crypto69, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
243834
+ var import_node_crypto71, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
243534
243835
  var init_worker = __esm({
243535
243836
  "../server/src/coordinator/worker.ts"() {
243536
243837
  "use strict";
243537
- import_node_crypto69 = require("node:crypto");
243838
+ import_node_crypto71 = require("node:crypto");
243538
243839
  init_src5();
243539
243840
  init_identity();
243540
243841
  AUTONOMOUS_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\u2014\u2014\u804C\u8D23\u662F\u8BA9\u5B83\u987A\u7545\u8DD1\u5B8C\u3002\u7CFB\u7EDF\u5728\u67D0\u4E2A\u8282\u70B9\u5361\u4F4F\u3001\u673A\u68B0\u5206\u8BCA\u786E\u8BA4"\u9700\u8981\u4F60"\u65F6\u5524\u8D77\u4F60\uFF08\u65E0\u4EBA\u5728\u573A\uFF0C\u4F60\u8FD9\u4E00\u8F6E\u628A\u80FD\u505A\u7684\u505A\u6389\uFF09\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A
@@ -243803,7 +244104,7 @@ ${HIGH_RISK_COMMANDS}`;
243803
244104
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
243804
244105
  }
243805
244106
  evaluationKey(kind, artifactId, evidence) {
243806
- const fingerprint2 = (0, import_node_crypto69.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
244107
+ const fingerprint2 = (0, import_node_crypto71.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
243807
244108
  return `${kind}:${artifactId}:${fingerprint2}`;
243808
244109
  }
243809
244110
  artifactEvidence(id) {
@@ -244182,8 +244483,8 @@ ${ctx.nodeFault}
244182
244483
  this.deps.log?.(`[coordinator] ${workspace} \u5168\u5C40\u4E0A\u4E0B\u6587\u8BFB\u53D6\u5931\u8D25\uFF0C\u7EE7\u7EED\u6CBF\u7528\u539F\u534F\u8C03\u8005\u4EFB\u52A1\uFF1A${String(error2)}`);
244183
244484
  }
244184
244485
  }
244185
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto69.randomUUID)();
244186
- const coordinatorRunId = `coordinate:${(0, import_node_crypto69.randomUUID)()}`;
244486
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto71.randomUUID)();
244487
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto71.randomUUID)()}`;
244187
244488
  const taskWithContext = coordinatorContext ? [
244188
244489
  task,
244189
244490
  ``,
@@ -244227,7 +244528,7 @@ ${ctx.nodeFault}
244227
244528
  const startedAtMs = Date.now();
244228
244529
  let trajectoryStarted = false;
244229
244530
  if (this.deps.trajectory) {
244230
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto69.createHash)("sha256").update(content3).digest("hex")]));
244531
+ const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto71.createHash)("sha256").update(content3).digest("hex")]));
244231
244532
  const session = {
244232
244533
  runId: coordinatorRunId,
244233
244534
  runtimeSessionId,
@@ -244432,11 +244733,11 @@ var init_pg_ident = __esm({
244432
244733
  });
244433
244734
 
244434
244735
  // ../storage/src/postgres.ts
244435
- var import_node_crypto70, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
244736
+ var import_node_crypto72, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
244436
244737
  var init_postgres = __esm({
244437
244738
  "../storage/src/postgres.ts"() {
244438
244739
  "use strict";
244439
- import_node_crypto70 = require("node:crypto");
244740
+ import_node_crypto72 = require("node:crypto");
244440
244741
  init_pg_ident();
244441
244742
  init_esm();
244442
244743
  init_src();
@@ -244616,7 +244917,7 @@ var init_postgres = __esm({
244616
244917
  return new _PostgresBlobStore(pool, schema);
244617
244918
  }
244618
244919
  async put(bytes2) {
244619
- const hash2 = (0, import_node_crypto70.createHash)("sha256").update(bytes2).digest("hex");
244920
+ const hash2 = (0, import_node_crypto72.createHash)("sha256").update(bytes2).digest("hex");
244620
244921
  await this.pool.query(
244621
244922
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
244622
244923
  [hash2, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -250889,6 +251190,7 @@ var init_postgres_chat_sessions = __esm({
250889
251190
  await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS visibility text`);
250890
251191
  await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS outputs_fingerprints jsonb`);
250891
251192
  await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS outputs_copies jsonb`);
251193
+ await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS file_sync_issues jsonb`);
250892
251194
  await pool.query(`
250893
251195
  WITH ranked AS (
250894
251196
  SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
@@ -251160,6 +251462,29 @@ var init_postgres_chat_sessions = __esm({
251160
251462
  );
251161
251463
  return r.rows[0]?.outputs_copies ?? null;
251162
251464
  }
251465
+ async readFileIssues(sessionId) {
251466
+ const r = await this.pool.query(
251467
+ `SELECT file_sync_issues FROM ${this.s}.chat_sessions WHERE id = $1 AND company_id = $2`,
251468
+ [sessionId, this.companyId]
251469
+ );
251470
+ return Object.values(r.rows[0]?.file_sync_issues ?? {}).flat();
251471
+ }
251472
+ async writeFileIssues(sessionId, phase, issues) {
251473
+ await this.pool.query(
251474
+ `UPDATE ${this.s}.chat_sessions SET file_sync_issues = COALESCE(file_sync_issues, '{}'::jsonb) || $1::jsonb
251475
+ WHERE id = $2 AND company_id = $3`,
251476
+ [JSON.stringify({ [phase]: issues }), sessionId, this.companyId]
251477
+ );
251478
+ }
251479
+ async listFileSessionsByWorkspaces(workspaces) {
251480
+ if (!workspaces.length) return [];
251481
+ const r = await this.pool.query(
251482
+ `SELECT * FROM ${this.s}.chat_sessions WHERE company_id = $1 AND workspace = ANY($2::text[])
251483
+ AND visibility IS DISTINCT FROM 'internal' AND analyzed_run_id IS NULL ORDER BY id`,
251484
+ [this.companyId, workspaces]
251485
+ );
251486
+ return r.rows.map(rowToSession);
251487
+ }
251163
251488
  /**
251164
251489
  * 按路径**合并**写入:同一路径新副本覆盖旧副本,其余路径保留。合并在一条 UPDATE 里用 jsonb `||` 完成,
251165
251490
  * 不是读出来改完再整份写回——两次收尾的复制前后脚落库时,后写的不会把先写的吞掉。
@@ -253243,11 +253568,11 @@ var init_postgres_delegations = __esm({
253243
253568
  });
253244
253569
 
253245
253570
  // ../storage/src/postgres-nodes.ts
253246
- var import_node_crypto71, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToRuntime;
253571
+ var import_node_crypto73, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToRuntime;
253247
253572
  var init_postgres_nodes = __esm({
253248
253573
  "../storage/src/postgres-nodes.ts"() {
253249
253574
  "use strict";
253250
- import_node_crypto71 = require("node:crypto");
253575
+ import_node_crypto73 = require("node:crypto");
253251
253576
  init_pg_ident();
253252
253577
  init_esm();
253253
253578
  ident13 = (s2) => {
@@ -253464,7 +253789,7 @@ var init_postgres_nodes = __esm({
253464
253789
  return store;
253465
253790
  }
253466
253791
  issue(nodeId) {
253467
- const token = `ont_${(0, import_node_crypto71.randomBytes)(24).toString("base64url")}`;
253792
+ const token = `ont_${(0, import_node_crypto73.randomBytes)(24).toString("base64url")}`;
253468
253793
  this.cache.set(token, nodeId);
253469
253794
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
253470
253795
  return token;
@@ -253730,11 +254055,11 @@ var init_postgres_type_registry = __esm({
253730
254055
  });
253731
254056
 
253732
254057
  // ../storage/src/postgres-actor-memory.ts
253733
- var import_node_crypto72, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
254058
+ var import_node_crypto74, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
253734
254059
  var init_postgres_actor_memory = __esm({
253735
254060
  "../storage/src/postgres-actor-memory.ts"() {
253736
254061
  "use strict";
253737
- import_node_crypto72 = require("node:crypto");
254062
+ import_node_crypto74 = require("node:crypto");
253738
254063
  init_pg_ident();
253739
254064
  init_src();
253740
254065
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -253944,7 +254269,7 @@ var init_postgres_actor_memory = __esm({
253944
254269
  }
253945
254270
  async write(input, now) {
253946
254271
  if (input.memId === void 0) {
253947
- const memId = `mem:${(0, import_node_crypto72.randomUUID)()}`;
254272
+ const memId = `mem:${(0, import_node_crypto74.randomUUID)()}`;
253948
254273
  const r2 = await this.pool.query(
253949
254274
  `INSERT INTO ${this.s}.actor_memories
253950
254275
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -255391,14 +255716,14 @@ function companySchemaName(companyId) {
255391
255716
  if (typeof companyId !== "string" || !companyId.trim()) {
255392
255717
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
255393
255718
  }
255394
- const hex = (0, import_node_crypto73.createHash)("sha256").update(companyId, "utf8").digest("hex");
255719
+ const hex = (0, import_node_crypto75.createHash)("sha256").update(companyId, "utf8").digest("hex");
255395
255720
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
255396
255721
  }
255397
- var import_node_crypto73, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
255722
+ var import_node_crypto75, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
255398
255723
  var init_tenant_schema_mapping = __esm({
255399
255724
  "../storage/src/tenant-schema-mapping.ts"() {
255400
255725
  "use strict";
255401
- import_node_crypto73 = require("node:crypto");
255726
+ import_node_crypto75 = require("node:crypto");
255402
255727
  init_esm();
255403
255728
  init_pg_ident();
255404
255729
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -255472,7 +255797,7 @@ var init_tenant_schema_mapping = __esm({
255472
255797
 
255473
255798
  // ../storage/src/company-data-plane-schema.ts
255474
255799
  function canonicalTypeDefHash(def) {
255475
- return `sha256:${(0, import_node_crypto74.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
255800
+ return `sha256:${(0, import_node_crypto76.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
255476
255801
  }
255477
255802
  function defaultArtifactTypeTemplate() {
255478
255803
  return [
@@ -255654,11 +255979,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
255654
255979
  }
255655
255980
  return { inserted };
255656
255981
  }
255657
- var import_node_crypto74, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
255982
+ var import_node_crypto76, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
255658
255983
  var init_company_data_plane_schema = __esm({
255659
255984
  "../storage/src/company-data-plane-schema.ts"() {
255660
255985
  "use strict";
255661
- import_node_crypto74 = require("node:crypto");
255986
+ import_node_crypto76 = require("node:crypto");
255662
255987
  init_esm();
255663
255988
  init_pg_ident();
255664
255989
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -255738,11 +256063,11 @@ function rowToFile2(row) {
255738
256063
  updatedAt: new Date(row.updated_at).toISOString()
255739
256064
  };
255740
256065
  }
255741
- var import_node_crypto75, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
256066
+ var import_node_crypto77, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
255742
256067
  var init_company_owned_registry_store = __esm({
255743
256068
  "../storage/src/company-owned-registry-store.ts"() {
255744
256069
  "use strict";
255745
- import_node_crypto75 = require("node:crypto");
256070
+ import_node_crypto77 = require("node:crypto");
255746
256071
  init_esm();
255747
256072
  init_src();
255748
256073
  init_pg_ident();
@@ -256128,7 +256453,7 @@ var init_company_owned_registry_store = __esm({
256128
256453
  enabled: skill.enabled,
256129
256454
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
256130
256455
  });
256131
- return (0, import_node_crypto75.createHash)("sha256").update(body2).digest("hex");
256456
+ return (0, import_node_crypto77.createHash)("sha256").update(body2).digest("hex");
256132
256457
  }
256133
256458
  };
256134
256459
  }
@@ -258725,7 +259050,7 @@ function sourceSetConservation(input) {
258725
259050
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
258726
259051
  }
258727
259052
  function payloadChecksum(value2) {
258728
- return (0, import_node_crypto76.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
259053
+ return (0, import_node_crypto78.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
258729
259054
  }
258730
259055
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
258731
259056
  const cs = quoteIdent2(companySchema);
@@ -259113,11 +259438,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
259113
259438
  sequences
259114
259439
  };
259115
259440
  }
259116
- var import_node_crypto76, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
259441
+ var import_node_crypto78, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
259117
259442
  var init_tenant_migration_operator = __esm({
259118
259443
  "../storage/src/tenant-migration-operator.ts"() {
259119
259444
  "use strict";
259120
- import_node_crypto76 = require("node:crypto");
259445
+ import_node_crypto78 = require("node:crypto");
259121
259446
  init_esm();
259122
259447
  init_pg_ident();
259123
259448
  init_tenancy_attribution();
@@ -259349,8 +259674,8 @@ var init_tenant_new_company_provision = __esm({
259349
259674
  // ../storage/src/knowledge-snapshot-crypto.ts
259350
259675
  function sealSnapshot(snapshot, key) {
259351
259676
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
259352
- const iv = (0, import_node_crypto77.randomBytes)(12);
259353
- const cipher = (0, import_node_crypto77.createCipheriv)("aes-256-gcm", bytes(key), iv);
259677
+ const iv = (0, import_node_crypto79.randomBytes)(12);
259678
+ const cipher = (0, import_node_crypto79.createCipheriv)("aes-256-gcm", bytes(key), iv);
259354
259679
  cipher.setAAD(aad2(snapshot));
259355
259680
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
259356
259681
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -259360,7 +259685,7 @@ function openSnapshot(snapshot, key) {
259360
259685
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
259361
259686
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
259362
259687
  const [iv, tag, data] = packed;
259363
- const decipher = (0, import_node_crypto77.createDecipheriv)("aes-256-gcm", bytes(key), Buffer.from(iv, "base64url"));
259688
+ const decipher = (0, import_node_crypto79.createDecipheriv)("aes-256-gcm", bytes(key), Buffer.from(iv, "base64url"));
259364
259689
  decipher.setAAD(aad2(snapshot));
259365
259690
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
259366
259691
  return {
@@ -259368,11 +259693,11 @@ function openSnapshot(snapshot, key) {
259368
259693
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
259369
259694
  };
259370
259695
  }
259371
- var import_node_crypto77, PREFIX3, bytes, aad2;
259696
+ var import_node_crypto79, PREFIX3, bytes, aad2;
259372
259697
  var init_knowledge_snapshot_crypto = __esm({
259373
259698
  "../storage/src/knowledge-snapshot-crypto.ts"() {
259374
259699
  "use strict";
259375
- import_node_crypto77 = require("node:crypto");
259700
+ import_node_crypto79 = require("node:crypto");
259376
259701
  PREFIX3 = "oasis-knowledge:v1:";
259377
259702
  bytes = (key) => {
259378
259703
  const value2 = Buffer.from(key);
@@ -261402,6 +261727,7 @@ var init_src11 = __esm({
261402
261727
  init_chat_recovery();
261403
261728
  init_chat_system_message();
261404
261729
  init_chat_turn_gate();
261730
+ init_chat_assistant_turn();
261405
261731
  init_chat_workorder_files();
261406
261732
  init_chat_broadcast();
261407
261733
  init_workorder_terminal();
@@ -261484,6 +261810,7 @@ var init_src11 = __esm({
261484
261810
  init_run_settlement_wiring();
261485
261811
  init_deadline_clock();
261486
261812
  init_read_model_clock();
261813
+ init_file_restore();
261487
261814
  }
261488
261815
  });
261489
261816
 
@@ -261619,20 +261946,20 @@ var fs45 = __toESM(require("node:fs"));
261619
261946
  var os14 = __toESM(require("node:os"));
261620
261947
  var path40 = __toESM(require("node:path"));
261621
261948
  var import_node_child_process23 = require("node:child_process");
261622
- var import_node_crypto84 = require("node:crypto");
261949
+ var import_node_crypto86 = require("node:crypto");
261623
261950
 
261624
261951
  // ../cli/src/cli.ts
261625
261952
  var fs40 = __toESM(require("node:fs"), 1);
261626
261953
  var os12 = __toESM(require("node:os"), 1);
261627
261954
  var path35 = __toESM(require("node:path"), 1);
261628
- var import_node_crypto82 = require("node:crypto");
261955
+ var import_node_crypto84 = require("node:crypto");
261629
261956
 
261630
261957
  // ../cli/src/serve.ts
261631
261958
  var fs34 = __toESM(require("node:fs"), 1);
261632
261959
  var os8 = __toESM(require("node:os"), 1);
261633
261960
  var path29 = __toESM(require("node:path"), 1);
261634
261961
  var import_node_child_process18 = require("node:child_process");
261635
- var import_node_crypto78 = require("node:crypto");
261962
+ var import_node_crypto80 = require("node:crypto");
261636
261963
  var import_node_url7 = require("node:url");
261637
261964
 
261638
261965
  // ../cli/src/artifact-discovery-source.ts
@@ -262378,7 +262705,7 @@ init_src9();
262378
262705
  init_src();
262379
262706
  init_trajectory();
262380
262707
  function deterministicUuid2(seed) {
262381
- const h = (0, import_node_crypto78.createHash)("sha256").update(seed).digest();
262708
+ const h = (0, import_node_crypto80.createHash)("sha256").update(seed).digest();
262382
262709
  const b2 = Buffer.from(h.subarray(0, 16));
262383
262710
  b2[6] = b2[6] & 15 | 80;
262384
262711
  b2[8] = b2[8] & 63 | 128;
@@ -262578,7 +262905,7 @@ function oasisWrapperScript() {
262578
262905
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
262579
262906
  const tsx = path29.join(repoRoot, "node_modules/.bin/tsx");
262580
262907
  const main = path29.join(repoRoot, "packages/cli/src/main.ts");
262581
- const key = (0, import_node_crypto78.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
262908
+ const key = (0, import_node_crypto80.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
262582
262909
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
262583
262910
  const stable = path29.join(os8.tmpdir(), `oasis-cli-wrapper-${uid}-${key}`);
262584
262911
  let dir;
@@ -262959,7 +263286,7 @@ async function startServe(opts) {
262959
263286
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
262960
263287
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
262961
263288
  if (!online) return Promise.resolve({ ok: false, reason: "\u8282\u70B9\u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u7528\u7684\u662F\u5B83\u4E0A\u6B21\u62A5\u7684\u6E05\u5355" });
262962
- const requestId = (0, import_node_crypto78.randomUUID)();
263289
+ const requestId = (0, import_node_crypto80.randomUUID)();
262963
263290
  const task = new Promise((resolve10) => {
262964
263291
  const timer = setTimeout(() => {
262965
263292
  modelRefreshWaiters.delete(requestId);
@@ -263230,6 +263557,8 @@ async function startServe(opts) {
263230
263557
  listChatFiles: async (companyId, projectId2) => {
263231
263558
  const chat = await chatStoreFor(companyId);
263232
263559
  const facts = await blobStoreFor(companyId);
263560
+ const taskBindings = await (await artifactStateFor(companyId)).listWorkspaceBindings();
263561
+ const workOrderIds = taskBindings.filter((b2) => b2.projectId === projectId2 || isUncategorizedProjectId(projectId2) && !b2.projectId).map((b2) => b2.workOrderId);
263233
263562
  return collectProjectChatFiles(chat, projectId2, async (ref2) => {
263234
263563
  for (const cabinet of [assets, facts]) {
263235
263564
  try {
@@ -263240,6 +263569,7 @@ async function startServe(opts) {
263240
263569
  }
263241
263570
  return null;
263242
263571
  }, {
263572
+ workOrderIds,
263243
263573
  // 委派子会话不进项目(ADR 0555:它的 outputs/ 是交给协调者的)。闭包在请求时才读
263244
263574
  // `delegationStoreFor`,避开它在下方声明的 TDZ。
263245
263575
  isDelegationChild: async (sessionId) => {
@@ -263711,7 +264041,7 @@ async function startServe(opts) {
263711
264041
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
263712
264042
  if (buf || session.runId) {
263713
264043
  await turnStore.appendMessage({
263714
- id: (0, import_node_crypto78.randomUUID)(),
264044
+ id: (0, import_node_crypto80.randomUUID)(),
263715
264045
  sessionId: chatSessionId,
263716
264046
  role: "assistant",
263717
264047
  content: buf,
@@ -263757,7 +264087,7 @@ async function startServe(opts) {
263757
264087
  chatTargets: {
263758
264088
  inspect: inspectChatTarget,
263759
264089
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
263760
- const sessionId = (0, import_node_crypto78.randomUUID)();
264090
+ const sessionId = (0, import_node_crypto80.randomUUID)();
263761
264091
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
263762
264092
  const binding = await registryStore.getBinding(agentId).catch(() => null);
263763
264093
  await (await chatStoreFor(companyId)).createSession({
@@ -264424,6 +264754,16 @@ async function startServe(opts) {
264424
264754
  delegationStores.set(id, store);
264425
264755
  return store;
264426
264756
  };
264757
+ const readChatFileBlob = async (companyId, ref2) => {
264758
+ const facts = await blobStoreFor(companyId);
264759
+ for (const cabinet of [assets, facts]) {
264760
+ try {
264761
+ return await cabinet.get(ref2);
264762
+ } catch {
264763
+ }
264764
+ }
264765
+ throw new Error("\u6587\u4EF6\u539F\u4EF6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u91CD\u8BD5\u6216\u91CD\u65B0\u4E0A\u4F20");
264766
+ };
264427
264767
  const outputCopyScheduler = createOutputCopyScheduler({
264428
264768
  putBlob: (bytes2) => assets.put(bytes2),
264429
264769
  isDelegationChild: async (companyId, sessionId) => {
@@ -264697,6 +265037,41 @@ async function startServe(opts) {
264697
265037
  const listCompanyMembers = (companyId) => controlPlaneStore.listMembers(companyId ?? defaultCompanyId);
264698
265038
  let notificationLedgerWorker;
264699
265039
  let notificationLedgerTimer;
265040
+ const sessionOutputsScan = async ({ chatSessionId, companyId }) => {
265041
+ const bridge = workdirBridge;
265042
+ if (!bridge) return null;
265043
+ const store = chatStoreRouter ? await chatStoreFor(companyId).catch(() => null) : chatSessionStore;
265044
+ const read = store?.readOutputFingerprints?.bind(store);
265045
+ const write = store?.writeOutputFingerprints?.bind(store);
265046
+ if (!read || !write) return null;
265047
+ const recorded = await store?.getSession(chatSessionId);
265048
+ const located = recorded?.runtimeId && recorded.runtimeKind ? { ok: true, loc: {
265049
+ nodeId: recorded.runtimeId,
265050
+ runtimeKind: recorded.runtimeKind,
265051
+ rtId: recorded.runtimeSessionId,
265052
+ workdirKey: `chat:${chatSessionId}`
265053
+ } } : await locateSessionWorkdir(delegationWorkdirLocator, { chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
265054
+ if (located.ok && foreignNodeBlock(await nodeStore.getNode(located.loc.nodeId), companyId ?? defaultCompanyId)) return null;
265055
+ if (!located.ok) return null;
265056
+ const diff = await settleSessionOutputs({
265057
+ bridge,
265058
+ loc: located.loc,
265059
+ sessionId: chatSessionId,
265060
+ readFingerprints: read,
265061
+ writeFingerprints: write
265062
+ }).catch(() => null);
265063
+ if (diff && store) await outputCopyScheduler.schedule({
265064
+ store,
265065
+ bridge,
265066
+ loc: located.loc,
265067
+ chatSessionId,
265068
+ companyId,
265069
+ fingerprints: diff.fingerprints,
265070
+ scanIncomplete: diff.scanIncomplete
265071
+ });
265072
+ else await store?.writeFileIssues?.(chatSessionId, "save", [{ phase: "save", path: "outputs/", code: "SCAN_UNAVAILABLE", message: "\u539F\u8282\u70B9\u4EA7\u51FA\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u672A\u80FD\u786E\u8BA4\u672C\u8F6E\u6587\u4EF6\u5DF2\u4FDD\u5B58" }]);
265073
+ return diff?.changed ?? null;
265074
+ };
264700
265075
  const server = await startOasisServer({
264701
265076
  deployTargets,
264702
265077
  onInterventionDraftResolved: (e) => notificationLedgerWorker?.draftResolved(e.companyId ?? defaultCompanyId, e.draftId, { by: e.by, approved: e.approved }),
@@ -264802,7 +265177,7 @@ async function startServe(opts) {
264802
265177
  }));
264803
265178
  }
264804
265179
  const limits = { wallClockMs: wallClockForKind("planner") };
264805
- const artifactId = `artifact:planner:${(0, import_node_crypto78.randomUUID)()}`;
265180
+ const artifactId = `artifact:planner:${(0, import_node_crypto80.randomUUID)()}`;
264806
265181
  const handle = await chatRemoteAdapter.spawn({
264807
265182
  actor: planner.id,
264808
265183
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -265034,6 +265409,7 @@ async function startServe(opts) {
265034
265409
  // (于是每一行的「大小」都空掉)。顺序与 `/api/blobs/:hash` 那条读路同源。
265035
265410
  // 写回也落资产柜,与附件上传同一个柜,否则新旧两版会分居两处。
265036
265411
  blobs: {
265412
+ get: (ref2) => readChatFileBlob(void 0, ref2),
265037
265413
  meta: async (blobRef) => {
265038
265414
  for (const cabinet of [assets, blobs]) {
265039
265415
  try {
@@ -265052,6 +265428,7 @@ async function startServe(opts) {
265052
265428
  resolveBlobs: async (cid) => {
265053
265429
  const factCabinet = await blobStoreFor(cid);
265054
265430
  return {
265431
+ get: (ref2) => readChatFileBlob(cid, ref2),
265055
265432
  meta: async (blobRef) => {
265056
265433
  for (const cabinet of [assets, factCabinet]) {
265057
265434
  try {
@@ -265139,40 +265516,8 @@ async function startServe(opts) {
265139
265516
  // 三个方法的组合规则住在 `createDelegationWorkdirPort`(`@oasis/server`)——评审
265140
265517
  // wo:acfee725 blocker ② 回归防线:**serve.ts 与单测走同一处**,那样「装配层退回全局 binding」
265141
265518
  // 这类事故在单测里就能被复现。绝不许在这里再写一份。
265142
- /**
265143
- * 会话产出扫描(2026-09-11 产出卡):轮次收尾扫这条会话的 `outputs/`,交出「这一轮变了哪些」。
265144
- *
265145
- * 复用 `delegationWorkdirLocator`(与派发、回流、chat 域**同一个 binding resolver 实例**)——
265146
- * 委派回程读的就是子会话的 `outputs/`,主会话这条只是把同一套读口用到了自己身上。
265147
- *
265148
- * 三处 fail-closed,都返回 `null`(= 这一轮没有产出卡),一处都不猜:
265149
- * · 桥还没建好 / 会话没绑定 / 节点离线 → 拿不到工作区;
265150
- * · store 没有指纹方法(老 store / 文件态装配)→ 没地方记「上一轮长什么样」,
265151
- * 硬扫会把整个 `outputs/` 每轮都当成新增,一口气刷几十张卡;
265152
- * · 解析不出公司 → **绝不回落默认公司**(否则会去别家公司的库里读写指纹)。
265153
- */
265154
- sessionOutputsScan: async ({ chatSessionId, companyId }) => {
265155
- const bridge = workdirBridge;
265156
- if (!bridge) return null;
265157
- const store = chatStoreRouter ? await chatStoreFor(companyId).catch(() => null) : chatSessionStore;
265158
- const read = store?.readOutputFingerprints?.bind(store);
265159
- const write = store?.writeOutputFingerprints?.bind(store);
265160
- if (!read || !write) return null;
265161
- const located = await locateSessionWorkdir(delegationWorkdirLocator, {
265162
- chatSessionId,
265163
- ...companyId !== void 0 ? { companyId } : {}
265164
- });
265165
- if (!located.ok) return null;
265166
- const diff = await settleSessionOutputs({
265167
- bridge,
265168
- loc: located.loc,
265169
- sessionId: chatSessionId,
265170
- readFingerprints: read,
265171
- writeFingerprints: write
265172
- }).catch(() => null);
265173
- if (diff && store) void outputCopyScheduler.schedule({ store, bridge, loc: located.loc, chatSessionId, companyId, fingerprints: diff.fingerprints });
265174
- return diff?.changed ?? null;
265175
- },
265519
+ // 会话产出扫描:实现见上方 `sessionOutputsScan`。serve 重启前后的恢复收尾用的是同一个函数。
265520
+ sessionOutputsScan,
265176
265521
  delegationWorkdir: createDelegationWorkdirPort({
265177
265522
  locator: delegationWorkdirLocator,
265178
265523
  // 桥在 hub 建好后(本函数尾部)才赋值 → getter 每次调用时读最新值;早于此处的构造时 `workdirBridge`
@@ -265704,10 +266049,10 @@ async function startServe(opts) {
265704
266049
  if (isDeletedActor(dispatchTarget)) {
265705
266050
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
265706
266051
  }
265707
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid2(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto78.randomUUID)());
266052
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid2(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto80.randomUUID)());
265708
266053
  const resumeRuntimeSession = Boolean(sessionId);
265709
- const traceRunId = requestDispatchId ? `chat-run:${deterministicUuid2(`chat-trace:${requestDispatchId}`)}` : `chat-run:${(0, import_node_crypto78.randomUUID)()}`;
265710
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid2(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto78.randomUUID)()}`;
266054
+ const traceRunId = requestDispatchId ? `chat-run:${deterministicUuid2(`chat-trace:${requestDispatchId}`)}` : `chat-run:${(0, import_node_crypto80.randomUUID)()}`;
266055
+ const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid2(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto80.randomUUID)()}`;
265711
266056
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
265712
266057
  if (stableDispatch && !stableDispatch.shouldDispatch) {
265713
266058
  return {
@@ -265771,10 +266116,7 @@ async function startServe(opts) {
265771
266116
  }
265772
266117
  }
265773
266118
  }
265774
- const turnChatStore = chatSessionId ? await chatStoreFor(runCompanyId).catch((err) => {
265775
- console.warn(`[chat] \u53D6\u516C\u53F8 ${runCompanyId} \u7684\u4F1A\u8BDD\u5B58\u50A8\u5931\u8D25\uFF08\u672C\u8F6E\u6309\u65E0\u4F1A\u8BDD\u8BB0\u5F55\u5904\u7406\uFF09: ${String(err)}`);
265776
- return null;
265777
- }) : null;
266119
+ const turnChatStore = chatSessionId ? await chatStoreFor(runCompanyId) : null;
265778
266120
  const analyzedRunId = chatSessionId ? (await turnChatStore?.getSession(chatSessionId).catch(() => null))?.analyzedRunId ?? void 0 : void 0;
265779
266121
  const actorConfigVersion = await actorService.latestConfig(actorId).then((c) => c?.version ?? null).catch(() => null);
265780
266122
  const candidateTraceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -265805,6 +266147,22 @@ async function startServe(opts) {
265805
266147
  ...analyzedRunId ? { analyzedRunId } : {}
265806
266148
  }
265807
266149
  });
266150
+ const priorChatSession = chatSessionId ? await turnChatStore?.getSession(chatSessionId) : null;
266151
+ let freshChatWorkdir = chatSessionId ? chatNodeChanged(
266152
+ { nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
266153
+ { nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
266154
+ ) : false;
266155
+ let restore = !isolatedKnowledgeTurn && chatSessionId && turnChatStore ? await prepareSessionFileRestore({
266156
+ store: turnChatStore,
266157
+ sessionId: chatSessionId,
266158
+ readBlob: (ref2) => readChatFileBlob(runCompanyId, ref2),
266159
+ attachments,
266160
+ fresh: freshChatWorkdir,
266161
+ supportsFileRestore: node2.meta.supportsFileRestore === true
266162
+ }) : void 0;
266163
+ const usedRel = /* @__PURE__ */ new Set();
266164
+ const preparedAttachments = await buildChatAttachmentFiles(attachments, (ref2) => readChatFileBlob(runCompanyId, ref2), usedRel);
266165
+ if (preparedAttachments.missing.length) throw new ApiError(409, "CHAT_FILE_RESTORE_FAILED", preparedAttachments.missing.map((m2) => `${m2.name}\uFF1A${m2.error}`).join("\uFF1B"));
265808
266166
  const stableTraceWasPresent = Boolean(stableDispatch);
265809
266167
  if (requestDispatchId && !stableDispatch) {
265810
266168
  stableDispatch = await resolveStableChatDispatch({
@@ -265827,13 +266185,20 @@ async function startServe(opts) {
265827
266185
  binding = { actorId, ...stableDispatch.target, status: "active" };
265828
266186
  node2 = await requireDispatchNode(binding);
265829
266187
  requireNodeConnectors();
266188
+ freshChatWorkdir = chatNodeChanged(
266189
+ { nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
266190
+ { nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
266191
+ );
266192
+ restore = !isolatedKnowledgeTurn && chatSessionId && turnChatStore ? await prepareSessionFileRestore({
266193
+ store: turnChatStore,
266194
+ sessionId: chatSessionId,
266195
+ readBlob: (ref2) => readChatFileBlob(runCompanyId, ref2),
266196
+ attachments,
266197
+ fresh: freshChatWorkdir,
266198
+ supportsFileRestore: node2.meta.supportsFileRestore === true
266199
+ }) : void 0;
265830
266200
  }
265831
266201
  }
265832
- const priorChatSession = chatSessionId ? await turnChatStore?.getSession(chatSessionId).catch(() => null) : null;
265833
- const freshChatWorkdir = chatSessionId ? chatNodeChanged(
265834
- { nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
265835
- { nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
265836
- ) : false;
265837
266202
  const existingTrace = stableDispatch?.run ?? null;
265838
266203
  const existingEvents = existingTrace ? await (await traceStoreFor(runCompanyId)).listEvents(traceRunId, {}).catch(() => []) : [];
265839
266204
  const traceStartedAt = existingTrace?.startedAt ?? candidateTraceStartedAt;
@@ -265879,18 +266244,13 @@ async function startServe(opts) {
265879
266244
  const chatResolvedModel = sessionModelOverride ?? actorCtx?.config?.model ?? void 0;
265880
266245
  const chatModel = chatResolvedModel ?? await registryStore.getRuntimeConfig(`runtime:${binding.nodeId}:${binding.runtimeKind}`).then((c) => c?.model).catch(() => void 0) ?? void 0;
265881
266246
  const chatModelSource = sessionModelOverride ? "session_override" : chatResolvedModel ? "actor_config" : chatModel ? "runtime_default" : null;
265882
- const usedRel = /* @__PURE__ */ new Set();
265883
- const preparedAttachments = await buildChatAttachmentFiles(attachments, (ref2) => assets.get(ref2), usedRel);
265884
- for (const miss of preparedAttachments.missing) {
265885
- console.warn(`[chat] \u9644\u4EF6 blob ${miss.blobRef} \u8BFB\u53D6\u5931\u8D25: ${miss.error}`);
265886
- }
265887
- const attachmentFiles = { ...preparedAttachments.files };
266247
+ const attachmentFiles = { ...preparedAttachments.files, ...restore ? { "SESSION_FILES.md": restore.index } : {} };
265888
266248
  const attachmentBinary = { ...preparedAttachments.binaryFiles };
265889
266249
  const attachmentPaths = [...preparedAttachments.paths];
265890
266250
  let turnProject = priorChatSession ? { state: "none" } : { state: "unknown" };
265891
266251
  if (priorChatSession?.projectId) {
265892
266252
  const projectId2 = priorChatSession.projectId;
265893
- const projectBundle = await projectStateFor(runCompanyId).then((projects) => buildChatProjectBundle(projectId2, projects, (blobId) => assets.get(blobId))).catch((err) => {
266253
+ const projectBundle = await projectStateFor(runCompanyId).then((projects) => buildChatProjectBundle(projectId2, projects, (blobId) => readChatFileBlob(runCompanyId, blobId))).catch((err) => {
265894
266254
  console.warn(`[chat] \u8BFB\u9879\u76EE ${projectId2} \u5931\u8D25\uFF08\u672C\u8F6E\u4E0D\u5199\u9879\u76EE\u6BB5\uFF09: ${String(err)}`);
265895
266255
  return void 0;
265896
266256
  });
@@ -265916,7 +266276,7 @@ async function startServe(opts) {
265916
266276
  ...actorCtx?.config?.prompt ? { personaPrompt: actorCtx.config.prompt } : {},
265917
266277
  ...extraSystemPrompt ? { expertSystemPrompt: extraSystemPrompt } : {}
265918
266278
  });
265919
- const taskHeader = chatPrompts.taskHeaderLines;
266279
+ const taskHeader = [...chatPrompts.taskHeaderLines, ...restore ? ["\u672C\u4F1A\u8BDD\u7684\u5386\u53F2\u6587\u4EF6\u4E0E\u5DF2\u4FDD\u5B58\u4EA7\u51FA\u89C1 SESSION_FILES.md\uFF0C\u53EF\u6309\u5217\u51FA\u7684\u76F8\u5BF9\u8DEF\u5F84\u8BFB\u53D6\u3002"] : []];
265920
266280
  const chatMemoryEnabled = !isolatedKnowledgeTurn && actorCtx?.memoryEnabled !== false;
265921
266281
  const chatMemorySvc = chatMemoryEnabled ? await actorMemoryFor(runCompanyId).catch(() => void 0) : void 0;
265922
266282
  const chatMemoryLines = chatMemorySvc ? await chatMemorySvc.list({ actorId }, { scope: "all", keywords: ngramQueryTerms([message]), limit: MEMORY_INJECT_MAX, offset: 0 }).then((out) => [...memoryIndexLines(out.items), ...memoryHowToLines()]).catch(() => []) : [];
@@ -265961,7 +266321,7 @@ async function startServe(opts) {
265961
266321
  actor: actorId,
265962
266322
  actorToken: issueSessionToken(actorId, { artifactId, action: "chat", limits, binding, sessionId: runtimeSessionId, companyId: runCompanyId, ...requestDispatchId ? { dispatchId: requestDispatchId } : {}, ...chatSessionId !== void 0 ? { chatSessionId } : {} }),
265963
266323
  artifactId,
265964
- bundle: { files, ...Object.keys(attachmentBinary).length ? { binaryFiles: attachmentBinary } : {} },
266324
+ bundle: { files, ...Object.keys(attachmentBinary).length ? { binaryFiles: attachmentBinary } : {}, ...restore ? { restoreFiles: restore.restoreFiles } : {} },
265965
266325
  // 人设在最前,固定规则接在它后面:助理那侧是分流正文(§6.5.3,只有普通脑暴那一支有),
265966
266326
  // 专家那侧是 `EXPERT_DELEGATION_SYSTEM_PROMPT`(§6.5.7)。**三者都走 systemPrompt**——
265967
266327
  // 放进 TASK.md 会随 `--resume` 每轮重复、O(N) 累积(§6.1.5)。
@@ -266094,10 +266454,11 @@ async function startServe(opts) {
266094
266454
  }
266095
266455
  });
266096
266456
  const failed = !isHealthySessionExit(info);
266097
- if (!failed) resolve10();
266098
- else {
266099
- reject(buildChatExitError(info));
266100
- }
266457
+ const recordRestore = turnChatStore && chatSessionId ? recordSessionFileRestoreExit(turnChatStore, chatSessionId, info) : Promise.resolve();
266458
+ void recordRestore.then(() => {
266459
+ if (!failed) resolve10();
266460
+ else reject(buildChatExitError(info));
266461
+ }, (error2) => reject(error2));
266101
266462
  });
266102
266463
  });
266103
266464
  return {
@@ -266286,6 +266647,8 @@ async function startServe(opts) {
266286
266647
  onCompanyError: chatRecoveryCompanyWarn(opts2.label),
266287
266648
  // 重挂的那一轮收口时当场外报,与清扫拍同一个接收方(见 `notifyChatTurnSettled`)。
266288
266649
  onTurnSettled: (info) => notifyChatTurnSettled(info),
266650
+ // 重挂 / 已收口两条路收尾时出产出卡(公司由扇出层按行绑)。
266651
+ sessionOutputsScan,
266289
266652
  liveChat,
266290
266653
  trace: runRoutedTrace,
266291
266654
  traceHealth,
@@ -266441,6 +266804,7 @@ async function startServe(opts) {
266441
266804
  listCompanies: chatRecoveryCompanies,
266442
266805
  storesFor: chatRecoveryFor,
266443
266806
  onCompanyError: chatRecoveryCompanyWarn("exit \u5BF9\u8D26"),
266807
+ sessionOutputsScan,
266444
266808
  drainStash: (id) => chatRemoteAdapter?.drainStash(id) ?? { frames: [] },
266445
266809
  log: (m2) => console.log(m2)
266446
266810
  });
@@ -266509,6 +266873,7 @@ async function startServe(opts) {
266509
266873
  listCompanies: chatRecoveryCompanies,
266510
266874
  storesFor: chatRecoveryFor,
266511
266875
  onCompanyError: chatRecoveryCompanyWarn("chat-sweep"),
266876
+ sessionOutputsScan,
266512
266877
  getRun: (id) => fanOutTrace.getRun(id),
266513
266878
  ownedRunId: (sid) => liveChat.runFor(sid),
266514
266879
  now: Date.now(),
@@ -266541,7 +266906,12 @@ async function startServe(opts) {
266541
266906
  storesFor: async (companyId) => {
266542
266907
  const turns = await chatTurnsFor(companyId);
266543
266908
  const messages = await chatStoreFor(companyId);
266544
- return { turns, ...assistantRowRecovery(messages) };
266909
+ return {
266910
+ turns,
266911
+ ...assistantRowRecovery(messages, {
266912
+ outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId)
266913
+ })
266914
+ };
266545
266915
  },
266546
266916
  getRun: (id) => fanOutTrace.getRun(id),
266547
266917
  // 本进程在飞的轮不动——它自己的收尾路径会写终态(liveChat 只认人工/渠道那条,自动化轮
@@ -266834,7 +267204,7 @@ async function startServe(opts) {
266834
267204
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
266835
267205
  return;
266836
267206
  }
266837
- const ledgerId = `dispatch:${(0, import_node_crypto78.randomUUID)()}`;
267207
+ const ledgerId = `dispatch:${(0, import_node_crypto80.randomUUID)()}`;
266838
267208
  try {
266839
267209
  await (await dispatchLedgerFor(companyId)).insertOpen({
266840
267210
  id: ledgerId,
@@ -266919,7 +267289,7 @@ async function startServe(opts) {
266919
267289
  const kernelModel = newKernel.model;
266920
267290
  const art = kernelModel.artifacts.get(nodeId);
266921
267291
  const revWid = art?.workspace ?? "";
266922
- const ledgerId = `dispatch:${(0, import_node_crypto78.randomUUID)()}`;
267292
+ const ledgerId = `dispatch:${(0, import_node_crypto80.randomUUID)()}`;
266923
267293
  try {
266924
267294
  await (await dispatchLedgerFor(companyId)).insertOpen({
266925
267295
  id: ledgerId,
@@ -267297,7 +267667,7 @@ async function startServe(opts) {
267297
267667
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
267298
267668
  try {
267299
267669
  const governance = server.knowledgeGovernance;
267300
- const taskFingerprint = (0, import_node_crypto78.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
267670
+ const taskFingerprint = (0, import_node_crypto80.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
267301
267671
  const requestContext = {
267302
267672
  organizationId: companyId,
267303
267673
  actorId,
@@ -268935,7 +269305,9 @@ async function runSession(dispatchId, job, deps) {
268935
269305
  deps.sink.stream({ type: "session_live_event", dispatchId, event });
268936
269306
  normalizer.onLiveEvent(event);
268937
269307
  });
269308
+ let restoreFailedBeforeStart = false;
268938
269309
  handle.onExit((info) => {
269310
+ if (info.fileRestoreError) restoreFailedBeforeStart = true;
268939
269311
  normalizer.turnFinished(isHealthySessionExit(info) ? "completed" : "failed");
268940
269312
  log2("[node-cli]", ` \u25A0 session_exited id=${handle.id} code=${info.code ?? "killed"} total=${Date.now() - t0}ms`);
268941
269313
  if (info.transcriptRef) log2("[node-cli]", ` transcript: ${info.transcriptRef}`);
@@ -268945,7 +269317,7 @@ async function runSession(dispatchId, job, deps) {
268945
269317
  if (cancelledBeforeStart) {
268946
269318
  log2("[node-cli]", ` dispatch ${dispatchId} \u5728 setup \u671F\u95F4\u5DF2\u88AB fencing\uFF0Cspawn \u540E\u7ACB\u5373 kill`);
268947
269319
  await handle.kill();
268948
- } else {
269320
+ } else if (!restoreFailedBeforeStart) {
268949
269321
  log2("[node-cli]", ` \u2192 session_started id=${handle.id} setup=${Date.now() - t0}ms`);
268950
269322
  deps.sink.keyFrame({ type: "session_started", dispatchId, sessionId: handle.id, jobArtifactId: job.artifactId });
268951
269323
  }
@@ -268953,7 +269325,12 @@ async function runSession(dispatchId, job, deps) {
268953
269325
  deps.onFailure?.();
268954
269326
  log2("[node-cli]", ` spawn failed: ${err}`);
268955
269327
  const errorMessage = String(err?.stack ?? err).slice(0, 8e3);
268956
- deps.sink.keyFrame({ type: "session_exited", dispatchId, sessionId: "spawn-failed", info: { code: null, reason: "error", errorMessage } });
269328
+ deps.sink.keyFrame({ type: "session_exited", dispatchId, sessionId: "spawn-failed", info: {
269329
+ code: null,
269330
+ reason: "error",
269331
+ errorMessage,
269332
+ ...err instanceof BundleRestoreError ? { fileRestoreError: { path: err.filePath, message: err.message } } : {}
269333
+ } });
268957
269334
  }
268958
269335
  }
268959
269336
 
@@ -269244,8 +269621,9 @@ var SessionProcessState = class {
269244
269621
 
269245
269622
  // ../cli/src/daemon/ws-client.ts
269246
269623
  init_wrapper();
269624
+ init_src();
269247
269625
  var import_node_os9 = require("node:os");
269248
- var import_node_crypto79 = require("node:crypto");
269626
+ var import_node_crypto81 = require("node:crypto");
269249
269627
  init_src8();
269250
269628
  init_src6();
269251
269629
 
@@ -269526,10 +269904,15 @@ async function handleWorkdirRead(req) {
269526
269904
  const fresh = await fh.stat().catch(() => null);
269527
269905
  return { ok: false, code: "TOO_LARGE", size: Math.max(fresh?.size ?? 0, bytesRead), absPath: real };
269528
269906
  }
269907
+ const afterRead = await fh.stat();
269908
+ if (afterRead.size !== st.size || afterRead.mtimeMs !== st.mtimeMs) {
269909
+ return { ok: false, code: "INTERNAL", message: "\u6587\u4EF6\u5728\u8BFB\u53D6\u671F\u95F4\u53D1\u751F\u53D8\u5316" };
269910
+ }
269529
269911
  const bytes2 = buf.subarray(0, bytesRead);
269530
269912
  return {
269531
269913
  ok: true,
269532
269914
  contentBase64: bytes2.toString("base64"),
269915
+ mtime: st.mtime.toISOString(),
269533
269916
  contentType: contentTypeFor(real, bytes2),
269534
269917
  size: bytes2.length
269535
269918
  // 以实际读到的字节数为准,不用任何可能已过时的 stat size
@@ -269782,6 +270165,7 @@ var DaemonWsClient = class {
269782
270165
  connect() {
269783
270166
  if (this.stopped) return;
269784
270167
  const ws = new import_websocket.default(this.serverUrl, {
270168
+ maxPayload: 4 * Math.ceil(FILE_CONTENT_MAX_BYTES / 3) + 64 * 1024,
269785
270169
  headers: {
269786
270170
  "x-daemon-id": this.daemonId,
269787
270171
  // 经公网网关时鉴权靠它(对齐 multica:Authorization 头反代天然透传);内网直连可空
@@ -270017,7 +270401,7 @@ var DaemonWsClient = class {
270017
270401
  */
270018
270402
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
270019
270403
  if (artifactIds.length === 0) return {};
270020
- const requestId = (0, import_node_crypto79.randomUUID)();
270404
+ const requestId = (0, import_node_crypto81.randomUUID)();
270021
270405
  return new Promise((resolve10, reject) => {
270022
270406
  const timer = setTimeout(() => {
270023
270407
  this.gcPending.delete(requestId);
@@ -270159,6 +270543,7 @@ var DaemonWsClient = class {
270159
270543
  supportsNormalizedEvents: true,
270160
270544
  // ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
270161
270545
  supportsWorkdirWalk: true,
270546
+ supportsFileRestore: true,
270162
270547
  ...activeSessions.length > 0 ? { activeSessions } : {},
270163
270548
  // 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
270164
270549
  pendingDispatchIds: [...this.pendingDispatchIds]
@@ -270419,7 +270804,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
270419
270804
  // ../cli/src/daemon/machine-id.ts
270420
270805
  var import_node_child_process21 = require("node:child_process");
270421
270806
  var import_node_fs22 = require("node:fs");
270422
- var import_node_crypto80 = require("node:crypto");
270807
+ var import_node_crypto82 = require("node:crypto");
270423
270808
  var import_node_os12 = require("node:os");
270424
270809
  function linuxMachineId() {
270425
270810
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -270485,7 +270870,7 @@ var defaultSources = {
270485
270870
  function resolveNodeId(sources = {}) {
270486
270871
  const s2 = { ...defaultSources, ...sources };
270487
270872
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
270488
- const digest = (0, import_node_crypto80.createHash)("sha256").update(material).digest("hex").slice(0, 12);
270873
+ const digest = (0, import_node_crypto82.createHash)("sha256").update(material).digest("hex").slice(0, 12);
270489
270874
  return `node-${digest}`;
270490
270875
  }
270491
270876
 
@@ -272256,13 +272641,13 @@ function ownFlagsFromDecl(command) {
272256
272641
  }
272257
272642
 
272258
272643
  // ../cli/src/connector-effect-runner.ts
272259
- var import_node_crypto81 = require("node:crypto");
272644
+ var import_node_crypto83 = require("node:crypto");
272260
272645
  var import_node_child_process22 = require("node:child_process");
272261
272646
  init_src8();
272262
272647
  function stableKey(effect) {
272263
272648
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
272264
272649
  const canonical = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
272265
- return `sha256:${(0, import_node_crypto81.createHash)("sha256").update(canonical).digest("hex")}`;
272650
+ return `sha256:${(0, import_node_crypto83.createHash)("sha256").update(canonical).digest("hex")}`;
272266
272651
  }
272267
272652
  async function runConnectorEffect(connector, toolArgs, deps) {
272268
272653
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -272498,7 +272883,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
272498
272883
  const timeoutMs = resolveAuthWaitMs();
272499
272884
  const controller = new AbortController();
272500
272885
  const timer = setTimeout(() => controller.abort(), timeoutMs);
272501
- const waiterId = (0, import_node_crypto82.randomUUID)();
272886
+ const waiterId = (0, import_node_crypto84.randomUUID)();
272502
272887
  process.stderr.write(
272503
272888
  `\u7B49\u5F85\u4EBA\u5DE5\u6388\u6743\u4E2D\u2026\u2026\u6700\u591A ${Math.round(timeoutMs / 1e3)} \u79D2\uFF0C\u8D85\u65F6\u4F1A\u81EA\u52A8\u8F6C\u4E3A\u5F85\u5BA1\uFF1B\u8FD9\u4E0D\u662F\u9519\u8BEF\uFF0C\u8BF7\u52FF\u91CD\u8BD5\u540C\u4E00\u6761\u547D\u4EE4\u3002
272504
272889
  `
@@ -272921,7 +273306,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
272921
273306
  const store = createNodeTokenStore(path35.join(dir, "node-tokens.json"));
272922
273307
  const sub = positional[0];
272923
273308
  if (sub === "issue") {
272924
- const id = flags.get("id") ?? `node-${(0, import_node_crypto82.randomUUID)()}`;
273309
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto84.randomUUID)()}`;
272925
273310
  const token2 = store.issue(id);
272926
273311
  println(token2);
272927
273312
  process.stderr.write(
@@ -273946,7 +274331,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
273946
274331
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
273947
274332
  const goal = flags.get("goal")?.trim();
273948
274333
  const slug6 = flags.get("slug");
273949
- const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto82.randomUUID)());
274334
+ const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto84.randomUUID)());
273950
274335
  const result = await api.request("POST", "/api/projects", {
273951
274336
  ...id ? { id } : {},
273952
274337
  name,
@@ -275938,13 +276323,13 @@ var LEGACY_SYSTEMD_UNIT = "oasis-node.service";
275938
276323
  var LEGACY_LAUNCHD_LABEL = "com.oasis.node";
275939
276324
 
275940
276325
  // src/install.ts
275941
- var import_node_crypto83 = require("node:crypto");
276326
+ var import_node_crypto85 = require("node:crypto");
275942
276327
  var fs44 = __toESM(require("node:fs"));
275943
276328
  var path39 = __toESM(require("node:path"));
275944
276329
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
275945
276330
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
275946
276331
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
275947
- const digest = (0, import_node_crypto83.createHash)("sha256").update(fs44.readFileSync(sourceFile)).digest("hex").slice(0, 8);
276332
+ const digest = (0, import_node_crypto85.createHash)("sha256").update(fs44.readFileSync(sourceFile)).digest("hex").slice(0, 8);
275948
276333
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
275949
276334
  }
275950
276335
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -276067,7 +276452,7 @@ function shimScript() {
276067
276452
  }
276068
276453
 
276069
276454
  // src/index.ts
276070
- var PKG_VERSION = true ? "2.2.35" : "dev";
276455
+ var PKG_VERSION = true ? "2.2.36" : "dev";
276071
276456
  var LOCAL_BIN = localBin();
276072
276457
  var NPM_PREFIX = npmPrefix();
276073
276458
  var INSTANCE = DEFAULT_INSTANCE;
@@ -276352,7 +276737,7 @@ function newInstanceName() {
276352
276737
  const existing = new Set(listInstances());
276353
276738
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
276354
276739
  for (; ; ) {
276355
- const n = `inst-${(0, import_node_crypto84.randomBytes)(3).toString("hex")}`;
276740
+ const n = `inst-${(0, import_node_crypto86.randomBytes)(3).toString("hex")}`;
276356
276741
  if (!existing.has(n)) return n;
276357
276742
  }
276358
276743
  }