oasis_test_v2 2.2.34 → 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 +1148 -728
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -149,6 +149,9 @@ function normalizeGitRemote(remote) {
149
149
  function projectActorKind(actorId) {
150
150
  return actorId.startsWith("actor:human:") ? "human" : "agent";
151
151
  }
152
+ function projectIdFromUuid(uuid2) {
153
+ return `proj_${uuid2.replace(/-/g, "").slice(0, 8)}`;
154
+ }
152
155
  var init_project = __esm({
153
156
  "../contract/src/project.ts"() {
154
157
  "use strict";
@@ -22500,6 +22503,43 @@ var init_sync_skills = __esm({
22500
22503
 
22501
22504
  // ../adapters/src/_core/materialize-bundle.ts
22502
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
+ }
22503
22543
  for (const [rel, content3] of Object.entries(bundle.files)) {
22504
22544
  const file = path3.join(dir, rel);
22505
22545
  fs3.mkdirSync(path3.dirname(file), { recursive: true });
@@ -22514,12 +22554,20 @@ function materializeBundle(dir, bundle) {
22514
22554
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
22515
22555
  return task;
22516
22556
  }
22517
- var fs3, path3;
22557
+ var fs3, path3, import_node_crypto6, BundleRestoreError;
22518
22558
  var init_materialize_bundle = __esm({
22519
22559
  "../adapters/src/_core/materialize-bundle.ts"() {
22520
22560
  "use strict";
22521
22561
  fs3 = __toESM(require("node:fs"), 1);
22522
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
+ };
22523
22571
  }
22524
22572
  });
22525
22573
 
@@ -22573,7 +22621,7 @@ function resolveWorkRoots(workRoot) {
22573
22621
  }
22574
22622
  function workdirSlug(workdirKey) {
22575
22623
  const safe = workdirKey.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 48);
22576
- 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);
22577
22625
  return `${safe}-${h}`;
22578
22626
  }
22579
22627
  function sessionDirFor(workRoot, runtimeKind, workdirKey) {
@@ -22705,11 +22753,11 @@ function prepareWorkdir(args) {
22705
22753
  function legacyChatSessionDir(workRoot, rtId) {
22706
22754
  return import_node_path.default.join(resolveLegacyWorkRoot(workRoot), "oasis-chat-sessions", rtId.replace(/[^a-zA-Z0-9_-]+/g, "_"));
22707
22755
  }
22708
- 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;
22709
22757
  var init_session_paths = __esm({
22710
22758
  "../adapters/src/_core/session-paths.ts"() {
22711
22759
  "use strict";
22712
- import_node_crypto6 = require("node:crypto");
22760
+ import_node_crypto7 = require("node:crypto");
22713
22761
  import_node_fs2 = __toESM(require("node:fs"), 1);
22714
22762
  import_node_os = __toESM(require("node:os"), 1);
22715
22763
  import_node_path = __toESM(require("node:path"), 1);
@@ -22737,7 +22785,7 @@ function executionEnvHash(p2) {
22737
22785
  binPkg: p2.binPackageDir ?? null,
22738
22786
  extraRo: [...p2.extraRoBindDirs ?? []].sort()
22739
22787
  });
22740
- 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);
22741
22789
  }
22742
22790
  function persistContainerName(workdirKey) {
22743
22791
  return `oasis-wd-${workdirSlug(workdirKey)}`;
@@ -22857,11 +22905,11 @@ function wrapForContainer(p2) {
22857
22905
  function killContainerArgs(containerName) {
22858
22906
  return ["kill", containerName];
22859
22907
  }
22860
- 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;
22861
22909
  var init_container_wrap = __esm({
22862
22910
  "../adapters/src/_core/container-wrap.ts"() {
22863
22911
  "use strict";
22864
- import_node_crypto7 = require("node:crypto");
22912
+ import_node_crypto8 = require("node:crypto");
22865
22913
  path5 = __toESM(require("node:path"), 1);
22866
22914
  init_session_paths();
22867
22915
  CONTAINER_DEFAULT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
@@ -23589,12 +23637,12 @@ function classifyExit(result) {
23589
23637
  if (status === 429 || status === 529) return "rate-limit";
23590
23638
  return "error";
23591
23639
  }
23592
- 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;
23593
23641
  var init_claude_code = __esm({
23594
23642
  "../adapters/src/claude-code/index.ts"() {
23595
23643
  "use strict";
23596
23644
  import_node_child_process3 = require("node:child_process");
23597
- import_node_crypto8 = require("node:crypto");
23645
+ import_node_crypto9 = require("node:crypto");
23598
23646
  fs7 = __toESM(require("node:fs"), 1);
23599
23647
  path7 = __toESM(require("node:path"), 1);
23600
23648
  readline = __toESM(require("node:readline"), 1);
@@ -23674,7 +23722,7 @@ var init_claude_code = __esm({
23674
23722
  }
23675
23723
  async spawn(job) {
23676
23724
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
23677
- 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)}`;
23678
23726
  const dir = job.workdirKey ? prepareWorkdir({
23679
23727
  workRoot: this.opts.workRoot,
23680
23728
  runtimeKind: "claude",
@@ -24087,7 +24135,7 @@ var init_claude_code = __esm({
24087
24135
  const outgoing = report.failed.length ? appendMaterializeWarning(input.text, report) : input.text;
24088
24136
  if (input.interrupt) writeInterrupt();
24089
24137
  diag("append_accepted", { writes, results, interrupt: !!input.interrupt, chars: input.text.length });
24090
- const receiptId = input.requireConsumptionReceipt ? (0, import_node_crypto8.randomUUID)() : void 0;
24138
+ const receiptId = input.requireConsumptionReceipt ? (0, import_node_crypto9.randomUUID)() : void 0;
24091
24139
  if (receiptId) pendingInputReceipts.set(receiptId, input.clientMessageKey);
24092
24140
  const ok3 = writeUserMessage(outgoing, receiptId);
24093
24141
  if (!ok3 && receiptId) pendingInputReceipts.delete(receiptId);
@@ -24227,7 +24275,12 @@ function withResilience(inner, cfg = {}) {
24227
24275
  try {
24228
24276
  handle = await inner.spawn(job);
24229
24277
  } catch (err) {
24230
- 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
+ });
24231
24284
  return;
24232
24285
  }
24233
24286
  const { info, produced } = await runOnce(handle);
@@ -24301,6 +24354,7 @@ var RETRYABLE, sleep2;
24301
24354
  var init_resilient = __esm({
24302
24355
  "../adapters/src/_core/resilient.ts"() {
24303
24356
  "use strict";
24357
+ init_materialize_bundle();
24304
24358
  init_src();
24305
24359
  RETRYABLE = /* @__PURE__ */ new Set(["rate-limit", "timeout", "server-unreachable"]);
24306
24360
  sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -24484,9 +24538,9 @@ function lockPathFor(cacheRoot, actorId) {
24484
24538
  }
24485
24539
  function aggregateSkillContentHash(files) {
24486
24540
  const sorted = [...files].sort((a, b2) => a.path.localeCompare(b2.path));
24487
- const hasher = (0, import_node_crypto9.createHash)("sha256");
24541
+ const hasher = (0, import_node_crypto10.createHash)("sha256");
24488
24542
  for (const f2 of sorted) {
24489
- 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");
24490
24544
  hasher.update(f2.path);
24491
24545
  hasher.update("\0");
24492
24546
  hasher.update(blob);
@@ -24714,11 +24768,11 @@ async function verifyPendingLink(skillsDir, name, pend, log3) {
24714
24768
  }
24715
24769
  return true;
24716
24770
  }
24717
- 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;
24718
24772
  var init_skill_cache = __esm({
24719
24773
  "../adapters/src/_core/skill-cache.ts"() {
24720
24774
  "use strict";
24721
- import_node_crypto9 = require("node:crypto");
24775
+ import_node_crypto10 = require("node:crypto");
24722
24776
  fsp = __toESM(require("node:fs/promises"), 1);
24723
24777
  os4 = __toESM(require("node:os"), 1);
24724
24778
  path8 = __toESM(require("node:path"), 1);
@@ -25367,7 +25421,7 @@ function isResponse(message) {
25367
25421
  }
25368
25422
  async function runProtocolSession(job, cfg) {
25369
25423
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
25370
- 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)}`;
25371
25425
  const dir = job.workdirKey ? prepareWorkdir({
25372
25426
  workRoot: cfg.workRoot,
25373
25427
  runtimeKind: cfg.runtimeKind,
@@ -25610,12 +25664,12 @@ async function runProtocolSession(job, cfg) {
25610
25664
  }
25611
25665
  };
25612
25666
  }
25613
- var import_node_child_process6, import_node_crypto10, path9, ProtocolCommandRejected, ProtocolSessionClosing;
25667
+ var import_node_child_process6, import_node_crypto11, path9, ProtocolCommandRejected, ProtocolSessionClosing;
25614
25668
  var init_protocol_session = __esm({
25615
25669
  "../adapters/src/_core/protocol-session.ts"() {
25616
25670
  "use strict";
25617
25671
  import_node_child_process6 = require("node:child_process");
25618
- import_node_crypto10 = require("node:crypto");
25672
+ import_node_crypto11 = require("node:crypto");
25619
25673
  path9 = __toESM(require("node:path"), 1);
25620
25674
  init_claude_code();
25621
25675
  init_container_runtime();
@@ -26074,13 +26128,13 @@ function codexTurnErrorMessage(value2) {
26074
26128
  const detail = typeof error2?.additionalDetails === "string" ? error2.additionalDetails.trim() : "";
26075
26129
  return [summary, detail].filter(Boolean).join("\n").slice(0, 4e3);
26076
26130
  }
26077
- 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;
26078
26132
  var init_codex = __esm({
26079
26133
  "../adapters/src/codex/index.ts"() {
26080
26134
  "use strict";
26081
26135
  import_node_child_process7 = require("node:child_process");
26082
26136
  init_container_runtime();
26083
- import_node_crypto11 = require("node:crypto");
26137
+ import_node_crypto12 = require("node:crypto");
26084
26138
  fs8 = __toESM(require("node:fs"), 1);
26085
26139
  os5 = __toESM(require("node:os"), 1);
26086
26140
  path10 = __toESM(require("node:path"), 1);
@@ -26368,7 +26422,7 @@ ${task}` : task;
26368
26422
  }
26369
26423
  async spawnExec(job) {
26370
26424
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
26371
- 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)}`;
26372
26426
  const startedAtMs = Date.now();
26373
26427
  const dir = job.workdirKey ? prepareWorkdir({
26374
26428
  workRoot: this.opts.workRoot,
@@ -27179,7 +27233,7 @@ async function runACPSession(job, cfg) {
27179
27233
  }
27180
27234
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
27181
27235
  const binTag = path11.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
27182
- 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)}`;
27183
27237
  const dir = job.workdirKey ? prepareWorkdir({
27184
27238
  workRoot: cfg.workRoot,
27185
27239
  runtimeKind: cfg.runtimeKind ?? "subprocess",
@@ -27592,12 +27646,12 @@ ${task}` : task;
27592
27646
  return run;
27593
27647
  }
27594
27648
  }
27595
- 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;
27596
27650
  var init_acp = __esm({
27597
27651
  "../adapters/src/_core/acp.ts"() {
27598
27652
  "use strict";
27599
27653
  import_node_child_process8 = require("node:child_process");
27600
- import_node_crypto12 = require("node:crypto");
27654
+ import_node_crypto13 = require("node:crypto");
27601
27655
  fs9 = __toESM(require("node:fs"), 1);
27602
27656
  path11 = __toESM(require("node:path"), 1);
27603
27657
  readline3 = __toESM(require("node:readline"), 1);
@@ -28430,7 +28484,7 @@ var init_pdeathsig = __esm({
28430
28484
  // ../adapters/src/_core/subprocess.ts
28431
28485
  async function materialize(job, cfg) {
28432
28486
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
28433
- const id = `${slug6}-${(0, import_node_crypto13.randomUUID)().slice(0, 8)}`;
28487
+ const id = `${slug6}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
28434
28488
  const dir = job.workdirKey ? prepareWorkdir({
28435
28489
  workRoot: cfg.workRoot,
28436
28490
  runtimeKind: cfg.runtimeKind ?? "subprocess",
@@ -28723,12 +28777,12 @@ async function runOneShotText(job, cfg) {
28723
28777
  }
28724
28778
  });
28725
28779
  }
28726
- var import_node_child_process10, import_node_crypto13, fs11, path13, readline4;
28780
+ var import_node_child_process10, import_node_crypto14, fs11, path13, readline4;
28727
28781
  var init_subprocess = __esm({
28728
28782
  "../adapters/src/_core/subprocess.ts"() {
28729
28783
  "use strict";
28730
28784
  import_node_child_process10 = require("node:child_process");
28731
- import_node_crypto13 = require("node:crypto");
28785
+ import_node_crypto14 = require("node:crypto");
28732
28786
  fs11 = __toESM(require("node:fs"), 1);
28733
28787
  path13 = __toESM(require("node:path"), 1);
28734
28788
  readline4 = __toESM(require("node:readline"), 1);
@@ -28785,11 +28839,11 @@ function normalizeOpenClaw(line, state) {
28785
28839
  const text5 = (r.payloads ?? []).map((pl) => pl?.text ?? "").filter(Boolean).join("\n");
28786
28840
  return text5 ? [{ kind: "message", payload: { text: text5 }, outputText: text5 }] : [];
28787
28841
  }
28788
- var import_node_crypto14, isTopLevelClose, OpenClawAdapter;
28842
+ var import_node_crypto15, isTopLevelClose, OpenClawAdapter;
28789
28843
  var init_openclaw = __esm({
28790
28844
  "../adapters/src/openclaw/index.ts"() {
28791
28845
  "use strict";
28792
- import_node_crypto14 = require("node:crypto");
28846
+ import_node_crypto15 = require("node:crypto");
28793
28847
  init_subprocess();
28794
28848
  isTopLevelClose = (line) => line === "}" || line === "}\r";
28795
28849
  OpenClawAdapter = class {
@@ -28798,7 +28852,7 @@ var init_openclaw = __esm({
28798
28852
  }
28799
28853
  spawn(job) {
28800
28854
  const opts = this.opts;
28801
- 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)}`;
28802
28856
  return runStreamJson(job, {
28803
28857
  model: job.model ?? opts.model,
28804
28858
  bin: opts.openClawBin ?? "openclaw",
@@ -29599,6 +29653,7 @@ var init_src6 = __esm({
29599
29653
  init_workdir_gc();
29600
29654
  init_gc_loop();
29601
29655
  init_normalizer2();
29656
+ init_materialize_bundle();
29602
29657
  }
29603
29658
  });
29604
29659
 
@@ -29954,7 +30009,7 @@ function chatItemsDoubleWriteEnabled(env = process.env) {
29954
30009
  return raw !== "" && raw !== "0" && raw !== "false" && raw !== "off" && raw !== "no";
29955
30010
  }
29956
30011
  function fallbackTurnId(runId) {
29957
- 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)()}`;
29958
30013
  }
29959
30014
  async function resolveChatItemsTurnId(items, messageId, fallback) {
29960
30015
  const existing = await items.listItemsByMessage(messageId).catch(() => []);
@@ -29973,20 +30028,20 @@ function projectsIntoContent(kind, role, origin, telemetryTextInContent, attrs =
29973
30028
  return true;
29974
30029
  }
29975
30030
  }
29976
- 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;
29977
30032
  var init_chat_item_ledger = __esm({
29978
30033
  "../server/src/chat-item-ledger.ts"() {
29979
30034
  "use strict";
29980
30035
  init_src();
29981
30036
  init_subagent_cost();
29982
30037
  init_chat_item();
29983
- import_node_crypto15 = require("node:crypto");
30038
+ import_node_crypto16 = require("node:crypto");
29984
30039
  CHAT_ITEMS_DOUBLE_WRITE_ENV = "OASIS_CHAT_ITEMS_DOUBLE_WRITE";
29985
30040
  SEGMENT_END_NOTE = "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09";
29986
30041
  ChatItemLedger = class {
29987
30042
  constructor(deps) {
29988
30043
  this.deps = deps;
29989
- this.newId = deps.newId ?? import_node_crypto15.randomUUID;
30044
+ this.newId = deps.newId ?? import_node_crypto16.randomUUID;
29990
30045
  this.now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
29991
30046
  this.log = deps.log ?? ((m2) => console.warn(m2));
29992
30047
  this.messageId = deps.messageId ?? null;
@@ -30606,7 +30661,7 @@ var init_chat_parts = __esm({
30606
30661
  async function openAssistantRunningRow(deps) {
30607
30662
  const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
30608
30663
  if (typeof deps.chatStore.appendMessage !== "function") return void 0;
30609
- const id = (0, import_node_crypto16.randomUUID)();
30664
+ const id = (0, import_node_crypto17.randomUUID)();
30610
30665
  const at = now();
30611
30666
  try {
30612
30667
  await deps.chatStore.appendMessage({
@@ -30650,7 +30705,7 @@ async function finalizeAssistantRow(deps) {
30650
30705
  if (typeof deps.chatStore.appendMessage !== "function") return;
30651
30706
  try {
30652
30707
  await deps.chatStore.appendMessage({
30653
- id: (0, import_node_crypto16.randomUUID)(),
30708
+ id: (0, import_node_crypto17.randomUUID)(),
30654
30709
  sessionId: deps.chatSessionId,
30655
30710
  role: "assistant",
30656
30711
  content: deps.content,
@@ -30664,6 +30719,13 @@ async function finalizeAssistantRow(deps) {
30664
30719
  } catch {
30665
30720
  }
30666
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
+ }
30667
30729
  async function appendOutputsPart(parts, provider) {
30668
30730
  if (!provider) return parts;
30669
30731
  const part = await provider().catch(() => null);
@@ -30907,11 +30969,11 @@ function runBackgroundAssistantChatTurn(deps) {
30907
30969
  });
30908
30970
  })().catch(() => void 0);
30909
30971
  }
30910
- var import_node_crypto16;
30972
+ var import_node_crypto17;
30911
30973
  var init_chat_assistant_turn = __esm({
30912
30974
  "../server/src/chat-assistant-turn.ts"() {
30913
30975
  "use strict";
30914
- import_node_crypto16 = require("node:crypto");
30976
+ import_node_crypto17 = require("node:crypto");
30915
30977
  init_src6();
30916
30978
  init_chat_item_ledger();
30917
30979
  init_chat_parts();
@@ -30929,7 +30991,7 @@ async function openChatTurn(args) {
30929
30991
  const turns = args.turns;
30930
30992
  if (!turns) return { ok: true, turn: null };
30931
30993
  const nowIso = args.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
30932
- const turnId = (args.newId ?? import_node_crypto17.randomUUID)();
30994
+ const turnId = (args.newId ?? import_node_crypto18.randomUUID)();
30933
30995
  const messageKey = localTurnMessageKey(args.source, turnId);
30934
30996
  const claim = await turns.claimTurn({
30935
30997
  turnId,
@@ -30963,7 +31025,7 @@ async function openChatTurn(args) {
30963
31025
  }
30964
31026
  };
30965
31027
  }
30966
- function assistantRowRecovery(chatStore) {
31028
+ function assistantRowRecovery(chatStore, opts = {}) {
30967
31029
  const rowOf = async (turn) => {
30968
31030
  const runId = turn.assistantRunId;
30969
31031
  if (!runId) return null;
@@ -30979,20 +31041,27 @@ function assistantRowRecovery(chatStore) {
30979
31041
 
30980
31042
  ` : "";
30981
31043
  const content3 = patch.status === "error" ? `${partial2}${ORPHANED_TURN_ASSISTANT_NOTICE}` : row.content ?? "";
31044
+ const outputsPart = opts.outputsPartFor?.(turn.chatSessionId);
30982
31045
  await finalizeAssistantRow({
30983
31046
  chatStore,
30984
31047
  chatSessionId: turn.chatSessionId,
30985
31048
  assistantMsgId: row.id,
30986
31049
  content: content3,
30987
- parts: [],
31050
+ // 不扫产出时照旧传空:finalize 对空 parts 不写这一列,行上原有的 parts 不动。
31051
+ // 要扫就必须带上原有 parts——否则扫到产出时写回去的只剩一张卡,这一轮的过程记录被冲掉。
31052
+ parts: outputsPart ? existingParts(row.parts) : [],
30988
31053
  status: patch.status,
30989
31054
  ...turn.assistantRunId ? { runId: turn.assistantRunId } : {},
30990
31055
  turnId: turn.id,
30991
- now: () => patch.completedAt
31056
+ now: () => patch.completedAt,
31057
+ ...outputsPart ? { outputsPart } : {}
30992
31058
  });
30993
31059
  }
30994
31060
  };
30995
31061
  }
31062
+ function existingParts(parts) {
31063
+ return Array.isArray(parts) ? parts.filter((p2) => p2 && typeof p2 === "object" && typeof p2.type === "string") : [];
31064
+ }
30996
31065
  async function reconcileChatTurn(turn, deps) {
30997
31066
  if (deps.isOwned?.(turn)) return { settled: false, turnId: turn.id, reason: "owned" };
30998
31067
  const rowStatus = deps.assistantRowStatus ? await deps.assistantRowStatus(turn).catch(() => null) : null;
@@ -31097,11 +31166,11 @@ async function sweepStaleChatTurnsAcrossCompanies(deps) {
31097
31166
  }
31098
31167
  return settled;
31099
31168
  }
31100
- var import_node_crypto17, ORPHANED_TURN_ASSISTANT_NOTICE;
31169
+ var import_node_crypto18, ORPHANED_TURN_ASSISTANT_NOTICE;
31101
31170
  var init_chat_turn_gate = __esm({
31102
31171
  "../server/src/chat-turn-gate.ts"() {
31103
31172
  "use strict";
31104
- import_node_crypto17 = require("node:crypto");
31173
+ import_node_crypto18 = require("node:crypto");
31105
31174
  init_chat_assistant_turn();
31106
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";
31107
31176
  }
@@ -31313,11 +31382,11 @@ var init_parse = __esm({
31313
31382
 
31314
31383
  // ../channels/src/feishu/crypto.ts
31315
31384
  function decryptFeishu(encrypt, encryptKey) {
31316
- const key = (0, import_node_crypto18.createHash)("sha256").update(encryptKey).digest();
31385
+ const key = (0, import_node_crypto19.createHash)("sha256").update(encryptKey).digest();
31317
31386
  const data = Buffer.from(encrypt, "base64");
31318
31387
  const iv = data.subarray(0, 16);
31319
31388
  const ciphertext = data.subarray(16);
31320
- 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);
31321
31390
  decipher.setAutoPadding(true);
31322
31391
  return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
31323
31392
  }
@@ -31351,11 +31420,11 @@ function verifyToken(decoded, expected) {
31351
31420
  const token = headerToken ?? decoded["token"];
31352
31421
  return token === expected;
31353
31422
  }
31354
- var import_node_crypto18;
31423
+ var import_node_crypto19;
31355
31424
  var init_crypto = __esm({
31356
31425
  "../channels/src/feishu/crypto.ts"() {
31357
31426
  "use strict";
31358
- import_node_crypto18 = require("node:crypto");
31427
+ import_node_crypto19 = require("node:crypto");
31359
31428
  }
31360
31429
  });
31361
31430
 
@@ -31491,11 +31560,11 @@ var init_src7 = __esm({
31491
31560
  function errMsg(e) {
31492
31561
  return e instanceof Error ? e.message : String(e);
31493
31562
  }
31494
- var import_node_crypto19, FEISHU, ChannelService, CardStreamer;
31563
+ var import_node_crypto20, FEISHU, ChannelService, CardStreamer;
31495
31564
  var init_service = __esm({
31496
31565
  "../server/src/channels/service.ts"() {
31497
31566
  "use strict";
31498
- import_node_crypto19 = require("node:crypto");
31567
+ import_node_crypto20 = require("node:crypto");
31499
31568
  init_chat_turn_gate();
31500
31569
  init_deletion();
31501
31570
  init_src7();
@@ -31577,7 +31646,7 @@ var init_service = __esm({
31577
31646
  return existing.chatSessionId;
31578
31647
  }
31579
31648
  }
31580
- const id = (0, import_node_crypto19.randomUUID)();
31649
+ const id = (0, import_node_crypto20.randomUUID)();
31581
31650
  const now = this.now();
31582
31651
  if (cs) {
31583
31652
  const rt = await this.deps.resolveRuntime?.(a.aiActorId).catch(() => null);
@@ -31613,7 +31682,7 @@ var init_service = __esm({
31613
31682
  if (cs) {
31614
31683
  const sess = await cs.getSession(intent.chatSessionId).catch(() => null);
31615
31684
  resumeRt = sess?.runtimeSessionId ?? void 0;
31616
- 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);
31617
31686
  }
31618
31687
  const opened = await openChatTurn({
31619
31688
  turns: this.deps.chatTurns,
@@ -31651,7 +31720,7 @@ var init_service = __esm({
31651
31720
  const reply = streamer.text();
31652
31721
  if (reply || session.runId) {
31653
31722
  await cs.appendMessage({
31654
- id: (0, import_node_crypto19.randomUUID)(),
31723
+ id: (0, import_node_crypto20.randomUUID)(),
31655
31724
  sessionId: intent.chatSessionId,
31656
31725
  role: "assistant",
31657
31726
  content: reply,
@@ -52730,7 +52799,7 @@ var require_websocket = __commonJS({
52730
52799
  var http2 = require("http");
52731
52800
  var net = require("net");
52732
52801
  var tls = require("tls");
52733
- var { randomBytes: randomBytes13, createHash: createHash39 } = require("crypto");
52802
+ var { randomBytes: randomBytes13, createHash: createHash41 } = require("crypto");
52734
52803
  var { Duplex, Readable } = require("stream");
52735
52804
  var { URL: URL2 } = require("url");
52736
52805
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -53398,7 +53467,7 @@ var require_websocket = __commonJS({
53398
53467
  abortHandshake(websocket, socket, "Invalid Upgrade header");
53399
53468
  return;
53400
53469
  }
53401
- const digest = createHash39("sha1").update(key + GUID).digest("base64");
53470
+ const digest = createHash41("sha1").update(key + GUID).digest("base64");
53402
53471
  if (res.headers["sec-websocket-accept"] !== digest) {
53403
53472
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
53404
53473
  return;
@@ -53767,7 +53836,7 @@ var require_websocket_server = __commonJS({
53767
53836
  var EventEmitter = require("events");
53768
53837
  var http2 = require("http");
53769
53838
  var { Duplex } = require("stream");
53770
- var { createHash: createHash39 } = require("crypto");
53839
+ var { createHash: createHash41 } = require("crypto");
53771
53840
  var extension3 = require_extension();
53772
53841
  var PerMessageDeflate2 = require_permessage_deflate();
53773
53842
  var subprotocol2 = require_subprotocol();
@@ -54074,7 +54143,7 @@ var require_websocket_server = __commonJS({
54074
54143
  );
54075
54144
  }
54076
54145
  if (this._state > RUNNING) return abortHandshake(socket, 503);
54077
- const digest = createHash39("sha1").update(key + GUID).digest("base64");
54146
+ const digest = createHash41("sha1").update(key + GUID).digest("base64");
54078
54147
  const headers = [
54079
54148
  "HTTP/1.1 101 Switching Protocols",
54080
54149
  "Upgrade: websocket",
@@ -162288,13 +162357,13 @@ var init_chat_attachment_files = __esm({
162288
162357
 
162289
162358
  // ../server/src/domains/chat-sessions/append-now.ts
162290
162359
  async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments, retainLocallyOnFailure = false) {
162291
- const newId = deps.newId ?? import_node_crypto20.randomUUID;
162360
+ const newId = deps.newId ?? import_node_crypto21.randomUUID;
162292
162361
  const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
162293
162362
  if (!deps.store.claimPendingInsert) {
162294
162363
  return { accepted: false, reason: "queue-unavailable", queued: false, delivered: "", batch: [] };
162295
162364
  }
162296
162365
  const { submitted, batch } = await deps.store.claimPendingInsert({
162297
- 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(),
162298
162367
  chatSessionId,
162299
162368
  text: text5,
162300
162369
  ...attachments?.length ? { attachments } : {},
@@ -162350,11 +162419,11 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
162350
162419
  }
162351
162420
  return { ...res, queued: !returnToClient, delivered: merged, batch, ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {} };
162352
162421
  }
162353
- var import_node_crypto20;
162422
+ var import_node_crypto21;
162354
162423
  var init_append_now = __esm({
162355
162424
  "../server/src/domains/chat-sessions/append-now.ts"() {
162356
162425
  "use strict";
162357
- import_node_crypto20 = require("node:crypto");
162426
+ import_node_crypto21 = require("node:crypto");
162358
162427
  init_chat_attachment_files();
162359
162428
  }
162360
162429
  });
@@ -163027,7 +163096,7 @@ function slug5(name) {
163027
163096
  return s2 || "user";
163028
163097
  }
163029
163098
  function short6() {
163030
- 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);
163031
163100
  }
163032
163101
  async function assertBindingWritePermission(caller, targetHumanId, deps) {
163033
163102
  if (caller === targetHumanId) return true;
@@ -163092,11 +163161,11 @@ async function resolveWorkorderPrincipals(caller, cliOverrides, deps) {
163092
163161
  }
163093
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` };
163094
163163
  }
163095
- 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;
163096
163165
  var init_assistants = __esm({
163097
163166
  "../server/src/domains/actors/assistants.ts"() {
163098
163167
  "use strict";
163099
- import_node_crypto21 = require("node:crypto");
163168
+ import_node_crypto22 = require("node:crypto");
163100
163169
  init_src();
163101
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";
163102
163171
  DEFAULT_ASSISTANT_SKILLS = [];
@@ -165506,7 +165575,7 @@ var init_continuation3 = __esm({
165506
165575
 
165507
165576
  // ../server/src/chat-workorder-files.ts
165508
165577
  function workOrderFileId(workOrderId, blobId, name) {
165509
- 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)}`;
165510
165579
  }
165511
165580
  async function linkChatAttachmentsToWorkOrder(input) {
165512
165581
  const warnings = [];
@@ -165557,11 +165626,11 @@ async function linkChatAttachmentsToWorkOrder(input) {
165557
165626
  }
165558
165627
  return { linked, warnings };
165559
165628
  }
165560
- var import_node_crypto22;
165629
+ var import_node_crypto23;
165561
165630
  var init_chat_workorder_files = __esm({
165562
165631
  "../server/src/chat-workorder-files.ts"() {
165563
165632
  "use strict";
165564
- import_node_crypto22 = require("node:crypto");
165633
+ import_node_crypto23 = require("node:crypto");
165565
165634
  }
165566
165635
  });
165567
165636
 
@@ -166329,11 +166398,11 @@ var init_config_store = __esm({
166329
166398
  function clone2(value2) {
166330
166399
  return structuredClone(value2);
166331
166400
  }
166332
- var import_node_crypto23, import_promises2, import_node_path5, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
166401
+ var import_node_crypto24, import_promises2, import_node_path5, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
166333
166402
  var init_run_store = __esm({
166334
166403
  "../server/src/domains/knowledge/run-store.ts"() {
166335
166404
  "use strict";
166336
- import_node_crypto23 = require("node:crypto");
166405
+ import_node_crypto24 = require("node:crypto");
166337
166406
  import_promises2 = require("node:fs/promises");
166338
166407
  import_node_path5 = require("node:path");
166339
166408
  MemoryKnowledgeRunStore = class {
@@ -166450,7 +166519,7 @@ var init_run_store = __esm({
166450
166519
  }
166451
166520
  async persist(runs) {
166452
166521
  const shape = { version: 1, runs };
166453
- 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`;
166454
166523
  try {
166455
166524
  await (0, import_promises2.writeFile)(temp, `${JSON.stringify(shape, null, 2)}
166456
166525
  `, { mode: 384 });
@@ -167023,11 +167092,11 @@ function retryableKnowledgeRun(record8) {
167023
167092
  const latestAnswerAttempt = [...record8.attempts ?? []].reverse().find((attempt) => attempt.kind !== "fast");
167024
167093
  return record8.status === "succeeded" && record8.request.action === "ask" && record8.executionHealth === "degraded" && latestAnswerAttempt?.status === "failed";
167025
167094
  }
167026
- 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;
167027
167096
  var init_service2 = __esm({
167028
167097
  "../server/src/domains/knowledge/service.ts"() {
167029
167098
  "use strict";
167030
- import_node_crypto24 = require("node:crypto");
167099
+ import_node_crypto25 = require("node:crypto");
167031
167100
  init_src5();
167032
167101
  init_config_store();
167033
167102
  init_run_store();
@@ -167070,7 +167139,7 @@ var init_service2 = __esm({
167070
167139
  constructor(options) {
167071
167140
  this.options = options;
167072
167141
  this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
167073
- this.id = options.id ?? import_node_crypto24.randomUUID;
167142
+ this.id = options.id ?? import_node_crypto25.randomUUID;
167074
167143
  this.runStore = options.runStore ?? new MemoryKnowledgeRunStore();
167075
167144
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;
167076
167145
  this.leaseMs = options.leaseMs ?? LEASE_MS;
@@ -178017,7 +178086,7 @@ function stableJson(value2) {
178017
178086
  return JSON.stringify(stableValue(value2));
178018
178087
  }
178019
178088
  function sha256(value2) {
178020
- return (0, import_node_crypto25.createHash)("sha256").update(value2).digest("hex");
178089
+ return (0, import_node_crypto26.createHash)("sha256").update(value2).digest("hex");
178021
178090
  }
178022
178091
  function normalizeKnowledgeContent(title, content3) {
178023
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();
@@ -178256,11 +178325,11 @@ function renderUntrustedKnowledgeFragment(input) {
178256
178325
  "</knowledge>"
178257
178326
  ].join("\n");
178258
178327
  }
178259
- 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;
178260
178329
  var init_governance_content = __esm({
178261
178330
  "../server/src/domains/knowledge/governance-content.ts"() {
178262
178331
  "use strict";
178263
- import_node_crypto25 = require("node:crypto");
178332
+ import_node_crypto26 = require("node:crypto");
178264
178333
  init_mdast_util_from_markdown();
178265
178334
  init_mdast_util_gfm();
178266
178335
  init_micromark_extension_gfm();
@@ -188427,11 +188496,11 @@ function approvedSnapshotBody(title, content3) {
188427
188496
  function uniqueStrings(values) {
188428
188497
  return [...new Set(values)].sort();
188429
188498
  }
188430
- var import_node_crypto26, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
188499
+ var import_node_crypto27, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
188431
188500
  var init_governance_service = __esm({
188432
188501
  "../server/src/domains/knowledge/governance-service.ts"() {
188433
188502
  "use strict";
188434
- import_node_crypto26 = require("node:crypto");
188503
+ import_node_crypto27 = require("node:crypto");
188435
188504
  init_governance_content();
188436
188505
  init_knowledge_policy();
188437
188506
  init_network_maintenance();
@@ -188453,7 +188522,7 @@ var init_governance_service = __esm({
188453
188522
  constructor(options) {
188454
188523
  this.options = options;
188455
188524
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
188456
- this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto26.randomUUID)()}`);
188525
+ this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto27.randomUUID)()}`);
188457
188526
  this.versionViewBaseUrl = (options.versionViewBaseUrl ?? "/api/knowledge/items").replace(/\/$/u, "");
188458
188527
  this.projectProvisionRetryDelayMs = Math.max(0, options.projectProvisionRetryDelayMs ?? 6e4);
188459
188528
  this.projectProvisionMaxRetryDelayMs = Math.max(this.projectProvisionRetryDelayMs, options.projectProvisionMaxRetryDelayMs ?? 36e5);
@@ -194144,7 +194213,7 @@ function trustedKnowledgeInvocationId(body2, turn) {
194144
194213
  if (body2.action !== "ask") return void 0;
194145
194214
  const scope = turn?.artifactId ?? turn?.dispatchId;
194146
194215
  if (!scope) return void 0;
194147
- const fingerprint2 = (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify({
194216
+ const fingerprint2 = (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify({
194148
194217
  action: body2.action,
194149
194218
  message: body2.message?.trim() ?? "",
194150
194219
  queryMode: body2.queryMode === "deep" ? "deep" : "fast",
@@ -194299,11 +194368,11 @@ function knowledgeRoutes(service, options = {}) {
194299
194368
  });
194300
194369
  };
194301
194370
  }
194302
- var import_node_crypto27, KNOWLEDGE_WORKORDER_ACTIONS;
194371
+ var import_node_crypto28, KNOWLEDGE_WORKORDER_ACTIONS;
194303
194372
  var init_routes = __esm({
194304
194373
  "../server/src/domains/knowledge/routes.ts"() {
194305
194374
  "use strict";
194306
- import_node_crypto27 = require("node:crypto");
194375
+ import_node_crypto28 = require("node:crypto");
194307
194376
  init_src5();
194308
194377
  init_router();
194309
194378
  init_governance_service();
@@ -195930,7 +195999,7 @@ async function resolveChatAttachmentDiscoverySource(input) {
195930
195999
  sourceType: input.sourceType,
195931
196000
  sourceId: session.id,
195932
196001
  sourceVersionId: attachment.blobRef,
195933
- sourceFingerprint: (0, import_node_crypto28.createHash)("sha256").update(bytes2).digest("hex"),
196002
+ sourceFingerprint: (0, import_node_crypto29.createHash)("sha256").update(bytes2).digest("hex"),
195934
196003
  sourceContent: new TextDecoder().decode(bytes2),
195935
196004
  sourceDisplayName: attachment.name,
195936
196005
  ...session.projectId ? { projectId: session.projectId } : {},
@@ -195941,11 +196010,11 @@ async function resolveChatAttachmentDiscoverySource(input) {
195941
196010
  }
195942
196011
  };
195943
196012
  }
195944
- var import_node_crypto28, TEXT_CONTENT_TYPES;
196013
+ var import_node_crypto29, TEXT_CONTENT_TYPES;
195945
196014
  var init_chat_attachment_discovery = __esm({
195946
196015
  "../server/src/domains/knowledge/chat-attachment-discovery.ts"() {
195947
196016
  "use strict";
195948
- import_node_crypto28 = require("node:crypto");
196017
+ import_node_crypto29 = require("node:crypto");
195949
196018
  TEXT_CONTENT_TYPES = /* @__PURE__ */ new Set([
195950
196019
  "application/json",
195951
196020
  "application/xml",
@@ -196122,8 +196191,8 @@ function aad(snapshot) {
196122
196191
  }
196123
196192
  function sealKnowledgeSnapshot(snapshot, key) {
196124
196193
  if (snapshot.content.startsWith(PREFIX)) return structuredClone(snapshot);
196125
- const iv = (0, import_node_crypto29.randomBytes)(12);
196126
- 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);
196127
196196
  cipher.setAAD(aad(snapshot));
196128
196197
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
196129
196198
  return {
@@ -196136,17 +196205,17 @@ function openKnowledgeSnapshot(snapshot, key) {
196136
196205
  const packed = snapshot.content.slice(PREFIX.length).split(".");
196137
196206
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
196138
196207
  const [iv, tag, data] = packed;
196139
- 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"));
196140
196209
  decipher.setAAD(aad(snapshot));
196141
196210
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
196142
196211
  const content3 = Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8");
196143
196212
  return { ...structuredClone(snapshot), content: content3 };
196144
196213
  }
196145
- var import_node_crypto29, PREFIX;
196214
+ var import_node_crypto30, PREFIX;
196146
196215
  var init_snapshot_crypto = __esm({
196147
196216
  "../server/src/domains/knowledge/snapshot-crypto.ts"() {
196148
196217
  "use strict";
196149
- import_node_crypto29 = require("node:crypto");
196218
+ import_node_crypto30 = require("node:crypto");
196150
196219
  PREFIX = "oasis-knowledge:v1:";
196151
196220
  }
196152
196221
  });
@@ -196229,11 +196298,11 @@ function uniquenessKeys(kind, record8) {
196229
196298
  return [];
196230
196299
  }
196231
196300
  }
196232
- 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;
196233
196302
  var init_governance_store = __esm({
196234
196303
  "../server/src/domains/knowledge/governance-store.ts"() {
196235
196304
  "use strict";
196236
- import_node_crypto30 = require("node:crypto");
196305
+ import_node_crypto31 = require("node:crypto");
196237
196306
  import_promises3 = require("node:fs/promises");
196238
196307
  import_node_path6 = require("node:path");
196239
196308
  init_snapshot_crypto();
@@ -196451,7 +196520,7 @@ var init_governance_store = __esm({
196451
196520
  return result;
196452
196521
  }
196453
196522
  async persist(shape) {
196454
- 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`;
196455
196524
  try {
196456
196525
  await (0, import_promises3.writeFile)(temp, `${JSON.stringify(this.sealSnapshots(shape), null, 2)}
196457
196526
  `, { mode: 384 });
@@ -196487,7 +196556,7 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
196487
196556
  if (error2.code !== "ENOENT") throw error2;
196488
196557
  }
196489
196558
  await (0, import_promises4.mkdir)((0, import_node_path7.dirname)(file), { recursive: true });
196490
- const generated = `oasis-${(0, import_node_crypto31.randomUUID)()}`;
196559
+ const generated = `oasis-${(0, import_node_crypto32.randomUUID)()}`;
196491
196560
  try {
196492
196561
  await (0, import_promises4.writeFile)(file, `${generated}
196493
196562
  `, { encoding: "utf8", mode: 384, flag: "wx" });
@@ -196497,11 +196566,11 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
196497
196566
  return readDeploymentId(file);
196498
196567
  }
196499
196568
  }
196500
- 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;
196501
196570
  var init_deployment_id = __esm({
196502
196571
  "../server/src/domains/knowledge/deployment-id.ts"() {
196503
196572
  "use strict";
196504
- import_node_crypto31 = require("node:crypto");
196573
+ import_node_crypto32 = require("node:crypto");
196505
196574
  import_promises4 = require("node:fs/promises");
196506
196575
  import_node_path7 = require("node:path");
196507
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;
@@ -197198,7 +197267,7 @@ var init_files = __esm({
197198
197267
 
197199
197268
  // ../server/src/domains/delegations/service.ts
197200
197269
  function deterministicUuid(seed) {
197201
- const h = (0, import_node_crypto32.createHash)("sha256").update(seed).digest();
197270
+ const h = (0, import_node_crypto33.createHash)("sha256").update(seed).digest();
197202
197271
  const b2 = Buffer.from(h.subarray(0, 16));
197203
197272
  b2[6] = b2[6] & 15 | 80;
197204
197273
  b2[8] = b2[8] & 63 | 128;
@@ -197255,11 +197324,11 @@ async function seedChildRoundRows(chatStore, childSessionId, round, prompt2, now
197255
197324
  createdAt: now
197256
197325
  });
197257
197326
  }
197258
- 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;
197259
197328
  var init_service3 = __esm({
197260
197329
  "../server/src/domains/delegations/service.ts"() {
197261
197330
  "use strict";
197262
- import_node_crypto32 = require("node:crypto");
197331
+ import_node_crypto33 = require("node:crypto");
197263
197332
  init_src();
197264
197333
  init_parent_conversation();
197265
197334
  init_deletion();
@@ -198461,7 +198530,7 @@ var init_continue_outcome = __esm({
198461
198530
  function trustedDelegationInvocationId(body2, turn) {
198462
198531
  const scope = turn?.artifactId ?? turn?.dispatchId;
198463
198532
  if (!scope) return void 0;
198464
- 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);
198465
198534
  return `dispatch:${scope}:delegation:${fingerprint2}`;
198466
198535
  }
198467
198536
  function callerOf(req, invocationId) {
@@ -198556,11 +198625,11 @@ function delegationRoutes(service) {
198556
198625
  });
198557
198626
  };
198558
198627
  }
198559
- var import_node_crypto33;
198628
+ var import_node_crypto34;
198560
198629
  var init_routes2 = __esm({
198561
198630
  "../server/src/domains/delegations/routes.ts"() {
198562
198631
  "use strict";
198563
- import_node_crypto33 = require("node:crypto");
198632
+ import_node_crypto34 = require("node:crypto");
198564
198633
  init_router();
198565
198634
  init_continue_outcome();
198566
198635
  init_service3();
@@ -200514,7 +200583,7 @@ function createAppJwt(appId, privateKeyPem, nowMs) {
200514
200583
  exp: nowSec + JWT_TTL_SEC,
200515
200584
  iss: appId
200516
200585
  }));
200517
- const signer = (0, import_node_crypto34.createSign)("RSA-SHA256");
200586
+ const signer = (0, import_node_crypto35.createSign)("RSA-SHA256");
200518
200587
  signer.update(`${header}.${payload2}`);
200519
200588
  signer.end();
200520
200589
  try {
@@ -200677,11 +200746,11 @@ async function exchangeManifestCode(code2, deps = {}) {
200677
200746
  ...typeof body2["client_secret"] === "string" ? { clientSecret: body2["client_secret"] } : {}
200678
200747
  };
200679
200748
  }
200680
- 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;
200681
200750
  var init_app_auth = __esm({
200682
200751
  "../connectors/src/github/app-auth.ts"() {
200683
200752
  "use strict";
200684
- import_node_crypto34 = require("node:crypto");
200753
+ import_node_crypto35 = require("node:crypto");
200685
200754
  GITHUB_API = "https://api.github.com";
200686
200755
  JWT_TTL_SEC = 9 * 60;
200687
200756
  JWT_IAT_SKEW_SEC = 60;
@@ -201606,11 +201675,11 @@ function extractOutlineKnowledgeCandidateDrafts(events) {
201606
201675
  }
201607
201676
  return [...drafts.values()];
201608
201677
  }
201609
- 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;
201610
201679
  var init_knowledge_api = __esm({
201611
201680
  "../connectors/src/outline/knowledge-api.ts"() {
201612
201681
  "use strict";
201613
- import_node_crypto35 = require("node:crypto");
201682
+ import_node_crypto36 = require("node:crypto");
201614
201683
  REQUEST_TIMEOUT_MS = 2e4;
201615
201684
  WRITE_ENDPOINTS = /* @__PURE__ */ new Set(["collections.create", "documents.create", "documents.update", "documents.publish"]);
201616
201685
  DEFAULT_WRITE_INTERVAL_MS = process.env["NODE_ENV"] === "test" ? 0 : 250;
@@ -201637,7 +201706,7 @@ var init_knowledge_api = __esm({
201637
201706
  this.baseUrl = canonicalUrl(baseUrl);
201638
201707
  this.lastResponseBaseUrl = this.baseUrl;
201639
201708
  this.token = token;
201640
- 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")}`;
201641
201710
  this.writeIntervalMs = Math.max(0, options.writeIntervalMs ?? DEFAULT_WRITE_INTERVAL_MS);
201642
201711
  this.maxRateLimitRetries = Math.max(0, options.maxRateLimitRetries ?? DEFAULT_RATE_LIMIT_RETRIES);
201643
201712
  this.maxRateLimitDelayMs = Math.max(0, options.maxRateLimitDelayMs ?? DEFAULT_MAX_RATE_LIMIT_DELAY_MS);
@@ -203045,15 +203114,15 @@ function classifyOutlineGovernedMigrationDocument(document4) {
203045
203114
  if (path41.startsWith("wiki/sources/")) return { mapping: "formal", metadata: legacyMetadata(document4, "source_note"), reason: "legacy_source_note" };
203046
203115
  return { mapping: "excluded", reason: "legacy_unmapped_path" };
203047
203116
  }
203048
- 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;
203049
203118
  var init_governed_knowledge = __esm({
203050
203119
  "../connectors/src/outline/governed-knowledge.ts"() {
203051
203120
  "use strict";
203052
- import_node_crypto36 = require("node:crypto");
203121
+ import_node_crypto37 = require("node:crypto");
203053
203122
  init_knowledge_api();
203054
203123
  value = (input) => input && typeof input === "object" && !Array.isArray(input) ? input : null;
203055
203124
  text4 = (input) => typeof input === "string" && input.trim() ? input.trim() : null;
203056
- 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");
203057
203126
  REVISION_VISIBILITY_RETRY_DELAYS_MS = [0, 100, 250, 500, 1e3, 2e3];
203058
203127
  governedProvisionRuns = /* @__PURE__ */ new Map();
203059
203128
  PUBLISH_MARKER2 = /\n?<!--\s*oasis-publish:[\s\S]*?-->\n?/giu;
@@ -205488,14 +205557,7 @@ async function startOasisServer(opts) {
205488
205557
  }
205489
205558
  return null;
205490
205559
  };
205491
- const outputsPartFor = (chatSessionId, companyId) => {
205492
- const scan2 = opts.sessionOutputsScan;
205493
- if (!scan2) return void 0;
205494
- return async () => {
205495
- const files = await scan2({ chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
205496
- return files && files.length > 0 ? { type: "outputs", seq: 0, outputs: files } : null;
205497
- };
205498
- };
205560
+ const outputsPartFor = (chatSessionId, companyId) => outputsPartProvider(opts.sessionOutputsScan, chatSessionId, companyId);
205499
205561
  async function anchorMessageIdFor(origin, storeFor) {
205500
205562
  if (!origin) return void 0;
205501
205563
  const live = liveChat.messageFor(origin);
@@ -205734,10 +205796,10 @@ async function startOasisServer(opts) {
205734
205796
  ...fp(p2)
205735
205797
  }))
205736
205798
  ] : [];
205737
- const { randomUUID: randomUUID41 } = await import("node:crypto");
205799
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
205738
205800
  try {
205739
205801
  await store.enqueuePendingMessage({
205740
- id: randomUUID41(),
205802
+ id: randomUUID43(),
205741
205803
  chatSessionId: input.chatSessionId,
205742
205804
  text: input.text,
205743
205805
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -205770,11 +205832,11 @@ async function startOasisServer(opts) {
205770
205832
  ...f2?.mtime ? { srcMtime: f2.mtime } : {}
205771
205833
  };
205772
205834
  });
205773
- const { randomUUID: randomUUID41 } = await import("node:crypto");
205835
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
205774
205836
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
205775
205837
  try {
205776
205838
  await store.enqueuePendingMessage({
205777
- id: randomUUID41(),
205839
+ id: randomUUID43(),
205778
205840
  chatSessionId: input.chatSessionId,
205779
205841
  text: "",
205780
205842
  createdAt: nowIso,
@@ -207021,7 +207083,7 @@ async function startOasisServer(opts) {
207021
207083
  entry = {
207022
207084
  // 旧 CLI 未带 waiterId 时给它塞一个服务端本地生成的——它绝不可能出现在 leave 上,
207023
207085
  // 也就绝不会误命中 leftWaiterIds、绝不会被别人的 leave 摘掉。行为与旧 CLI 相同。
207024
- waiterId: clientWaiterId ?? `server:${(0, import_node_crypto37.randomUUID)()}`,
207086
+ waiterId: clientWaiterId ?? `server:${(0, import_node_crypto38.randomUUID)()}`,
207025
207087
  onResolve,
207026
207088
  expiresAt: expiresAt2,
207027
207089
  cleanup: () => clearTimeout(expiryTimer),
@@ -207102,7 +207164,7 @@ async function startOasisServer(opts) {
207102
207164
  const openRoom = async () => {
207103
207165
  let chatSessionId2;
207104
207166
  if (chatStore) {
207105
- const { randomUUID: randomUUID41 } = await import("node:crypto");
207167
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
207106
207168
  const known = discussSessions.get(itemKey);
207107
207169
  const knownSession = known ? await chatStore.getSession(known) : null;
207108
207170
  if (knownSession && knownSession.workspace === ctx.workspace && knownSession.humanActorId === ctx.ownerId && (!body2.escalationId || knownSession.escalationId === body2.escalationId)) chatSessionId2 = known;
@@ -207111,7 +207173,7 @@ async function startOasisServer(opts) {
207111
207173
  chatSessionId2 = existing[0]?.id;
207112
207174
  }
207113
207175
  if (!chatSessionId2) {
207114
- chatSessionId2 = randomUUID41();
207176
+ chatSessionId2 = randomUUID43();
207115
207177
  const now = (/* @__PURE__ */ new Date()).toISOString();
207116
207178
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
207117
207179
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
@@ -207980,14 +208042,14 @@ ${composed}`;
207980
208042
  runtimeSessionId: session.nativeSessionId ?? session.id
207981
208043
  }).catch(() => void 0);
207982
208044
  }
207983
- const { randomUUID: randomUUID42 } = await import("node:crypto");
208045
+ const { randomUUID: randomUUID44 } = await import("node:crypto");
207984
208046
  const persistAttachments = effectiveAttachments.map((a) => ({
207985
208047
  name: a.name,
207986
208048
  ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
207987
208049
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
207988
208050
  }));
207989
208051
  await chatStore.appendMessage({
207990
- id: randomUUID42(),
208052
+ id: randomUUID44(),
207991
208053
  sessionId: persistTarget.id,
207992
208054
  role: "user",
207993
208055
  content: persistedUserMessage,
@@ -208462,8 +208524,8 @@ ${composed}`;
208462
208524
  }
208463
208525
  }
208464
208526
  const { spawn: spawn10 } = await import("node:child_process");
208465
- const { randomUUID: randomUUID41 } = await import("node:crypto");
208466
- const sessionId = body2.sessionId ?? randomUUID41();
208527
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
208528
+ const sessionId = body2.sessionId ?? randomUUID43();
208467
208529
  const args = [
208468
208530
  "-p",
208469
208531
  body2.message,
@@ -208759,8 +208821,8 @@ ${composed}`;
208759
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}` }));
208760
208822
  return;
208761
208823
  }
208762
- const { randomUUID: randomUUID41 } = await import("node:crypto");
208763
- const state = randomUUID41();
208824
+ const { randomUUID: randomUUID43 } = await import("node:crypto");
208825
+ const state = randomUUID43();
208764
208826
  const nameSlug = (actorName || actorId?.split(":").pop() || owner?.login || "bot").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "bot";
208765
208827
  const manifest = buildAppManifest({
208766
208828
  employeeSlug: nameSlug,
@@ -209536,12 +209598,12 @@ ${composed}`;
209536
209598
  }
209537
209599
  };
209538
209600
  }
209539
- 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;
209540
209602
  var init_server3 = __esm({
209541
209603
  "../server/src/server.ts"() {
209542
209604
  "use strict";
209543
209605
  http = __toESM(require("node:http"), 1);
209544
- import_node_crypto37 = require("node:crypto");
209606
+ import_node_crypto38 = require("node:crypto");
209545
209607
  init_chat_session();
209546
209608
  init_chat_system_message();
209547
209609
  init_src();
@@ -209700,7 +209762,7 @@ function createNodeTokenStore(file) {
209700
209762
  return {
209701
209763
  issue(nodeId) {
209702
209764
  const table = read();
209703
- const token = `ont_${(0, import_node_crypto38.randomBytes)(24).toString("base64url")}`;
209765
+ const token = `ont_${(0, import_node_crypto39.randomBytes)(24).toString("base64url")}`;
209704
209766
  table[token] = nodeId;
209705
209767
  write(table);
209706
209768
  return token;
@@ -209742,7 +209804,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
209742
209804
  return {
209743
209805
  issue(name, ttlMs = defaultTtlMs, companyId) {
209744
209806
  const m2 = prune();
209745
- const token = `ent_${(0, import_node_crypto38.randomBytes)(24).toString("base64url")}`;
209807
+ const token = `ent_${(0, import_node_crypto39.randomBytes)(24).toString("base64url")}`;
209746
209808
  const expiresAt2 = Date.now() + ttlMs;
209747
209809
  m2.set(token, { name, expiresAt: expiresAt2, ...companyId !== void 0 ? { companyId } : {} });
209748
209810
  write(m2);
@@ -209791,33 +209853,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
209791
209853
  }
209792
209854
  };
209793
209855
  }
209794
- 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;
209795
209857
  var init_tokens = __esm({
209796
209858
  "../server/src/tokens.ts"() {
209797
209859
  "use strict";
209798
- import_node_crypto38 = require("node:crypto");
209860
+ import_node_crypto39 = require("node:crypto");
209799
209861
  fs18 = __toESM(require("node:fs"), 1);
209800
209862
  path19 = __toESM(require("node:path"), 1);
209801
209863
  SESSION_TOKEN_PREFIX = "oat_v2_";
209802
209864
  b64url2 = (value2) => Buffer.from(value2).toString("base64url");
209803
209865
  fromB64url = (value2) => Buffer.from(value2, "base64url").toString("utf8");
209804
209866
  readOrCreateSecret = (file) => {
209805
- if (!file) return (0, import_node_crypto38.randomBytes)(32);
209867
+ if (!file) return (0, import_node_crypto39.randomBytes)(32);
209806
209868
  try {
209807
209869
  const raw = fs18.readFileSync(file, "utf8").trim();
209808
209870
  if (raw) return Buffer.from(raw, "base64url");
209809
209871
  } catch {
209810
209872
  }
209811
- const secret = (0, import_node_crypto38.randomBytes)(32);
209873
+ const secret = (0, import_node_crypto39.randomBytes)(32);
209812
209874
  fs18.mkdirSync(path19.dirname(file), { recursive: true });
209813
209875
  fs18.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
209814
209876
  return secret;
209815
209877
  };
209816
- 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");
209817
209879
  signatureMatches = (actual, expected) => {
209818
209880
  const a = Buffer.from(actual);
209819
209881
  const b2 = Buffer.from(expected);
209820
- 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);
209821
209883
  };
209822
209884
  isTokenClaims = (value2) => {
209823
209885
  if (value2 === null || typeof value2 !== "object") return false;
@@ -209864,31 +209926,31 @@ function createDataplaneTokenIssuer(opts = {}) {
209864
209926
  }
209865
209927
  };
209866
209928
  }
209867
- 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;
209868
209930
  var init_session_dataplane_token = __esm({
209869
209931
  "../server/src/session-dataplane-token.ts"() {
209870
209932
  "use strict";
209871
- import_node_crypto39 = require("node:crypto");
209933
+ import_node_crypto40 = require("node:crypto");
209872
209934
  fs19 = __toESM(require("node:fs"), 1);
209873
209935
  path20 = __toESM(require("node:path"), 1);
209874
209936
  init_src();
209875
209937
  PREFIX2 = "osd_";
209876
209938
  b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
209877
209939
  fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
209878
- 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");
209879
209941
  sigMatches = (actual, expected) => {
209880
209942
  const a = Buffer.from(actual);
209881
209943
  const b2 = Buffer.from(expected);
209882
- 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);
209883
209945
  };
209884
209946
  readOrCreateSecret2 = (file) => {
209885
- if (!file) return (0, import_node_crypto39.randomBytes)(32);
209947
+ if (!file) return (0, import_node_crypto40.randomBytes)(32);
209886
209948
  try {
209887
209949
  const raw = fs19.readFileSync(file, "utf8").trim();
209888
209950
  if (raw) return Buffer.from(raw, "base64url");
209889
209951
  } catch {
209890
209952
  }
209891
- const secret = (0, import_node_crypto39.randomBytes)(32);
209953
+ const secret = (0, import_node_crypto40.randomBytes)(32);
209892
209954
  fs19.mkdirSync(path20.dirname(file), { recursive: true });
209893
209955
  fs19.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
209894
209956
  return secret;
@@ -212034,11 +212096,11 @@ function onlyHits(pool, q2) {
212034
212096
  if (!q2.requireMatch || q2.keywords.length === 0) return [...pool];
212035
212097
  return pool.filter((r) => memoryMatchScore(r, q2.keywords) > 0);
212036
212098
  }
212037
- var import_node_crypto40, MemoryActorMemoryStore, toIndexEntry;
212099
+ var import_node_crypto41, MemoryActorMemoryStore, toIndexEntry;
212038
212100
  var init_memory_actor_memory_store = __esm({
212039
212101
  "../testkit/src/memory-actor-memory-store.ts"() {
212040
212102
  "use strict";
212041
- import_node_crypto40 = require("node:crypto");
212103
+ import_node_crypto41 = require("node:crypto");
212042
212104
  init_src();
212043
212105
  MemoryActorMemoryStore = class {
212044
212106
  rows = /* @__PURE__ */ new Map();
@@ -212092,7 +212154,7 @@ var init_memory_actor_memory_store = __esm({
212092
212154
  async write(input, now) {
212093
212155
  if (input.memId === void 0) {
212094
212156
  const rec = {
212095
- memId: `mem:${(0, import_node_crypto40.randomUUID)()}`,
212157
+ memId: `mem:${(0, import_node_crypto41.randomUUID)()}`,
212096
212158
  actorId: input.actorId,
212097
212159
  projectId: input.projectId,
212098
212160
  keywords: [...input.keywords],
@@ -212824,6 +212886,19 @@ var init_stats = __esm({
212824
212886
  }
212825
212887
  });
212826
212888
 
212889
+ // ../server/src/domains/projects/project-id.ts
212890
+ function randomProjectId() {
212891
+ return projectIdFromUuid((0, import_node_crypto42.randomUUID)());
212892
+ }
212893
+ var import_node_crypto42;
212894
+ var init_project_id = __esm({
212895
+ "../server/src/domains/projects/project-id.ts"() {
212896
+ "use strict";
212897
+ import_node_crypto42 = require("node:crypto");
212898
+ init_src();
212899
+ }
212900
+ });
212901
+
212827
212902
  // ../server/src/domains/projects/service.ts
212828
212903
  function sanitizeCreatorActorId(raw) {
212829
212904
  const id = raw?.trim();
@@ -212884,7 +212959,7 @@ function creatorKindOf(actorId) {
212884
212959
  if (!actorId) return null;
212885
212960
  return actorId.startsWith("actor:human:") ? "human" : "agent";
212886
212961
  }
212887
- function emitChatFolders(items, chatFiles, resolveName) {
212962
+ function emitChatFolders(items, chatFiles, resolveName, taskTitles = /* @__PURE__ */ new Map()) {
212888
212963
  const groups = /* @__PURE__ */ new Map();
212889
212964
  for (const file of chatFiles) {
212890
212965
  const list2 = groups.get(file.chatSessionId) ?? [];
@@ -212904,14 +212979,15 @@ function emitChatFolders(items, chatFiles, resolveName) {
212904
212979
  depth: 0,
212905
212980
  type: "folder",
212906
212981
  size: null,
212907
- taskId: null,
212908
- taskTitle: CHAT_FOLDER_TASK_LABEL,
212982
+ taskId: head.taskId ?? null,
212983
+ taskTitle: head.taskId ? taskTitles.get(head.taskId) ?? head.taskId : CHAT_FOLDER_TASK_LABEL,
212909
212984
  creatorKind: creatorKindOf(head.chatOwnerId),
212910
212985
  creatorId: head.chatOwnerId,
212911
212986
  creatorName: resolveName(head.chatOwnerId),
212912
212987
  updatedAt: latest,
212913
212988
  folderKind: "chat",
212914
- chatSessionId: sessionId
212989
+ chatSessionId: sessionId,
212990
+ ...head.fileIssues?.length ? { fileIssues: head.fileIssues } : {}
212915
212991
  });
212916
212992
  for (const file of group) {
212917
212993
  items.push({
@@ -212925,13 +213001,14 @@ function emitChatFolders(items, chatFiles, resolveName) {
212925
213001
  blobRef: file.blobId,
212926
213002
  contentType: file.contentType ?? null,
212927
213003
  size: file.size,
212928
- taskId: null,
212929
- taskTitle: CHAT_FOLDER_TASK_LABEL,
213004
+ taskId: head.taskId ?? null,
213005
+ taskTitle: head.taskId ? taskTitles.get(head.taskId) ?? head.taskId : CHAT_FOLDER_TASK_LABEL,
212930
213006
  creatorKind: creatorKindOf(file.uploadedBy),
212931
213007
  creatorId: file.uploadedBy ?? null,
212932
213008
  creatorName: file.uploadedBy ? resolveName(file.uploadedBy) : null,
212933
213009
  updatedAt: file.uploadedAt,
212934
- chatSessionId: sessionId
213010
+ chatSessionId: sessionId,
213011
+ ...head.fileIssues?.length ? { fileIssues: head.fileIssues } : {}
212935
213012
  });
212936
213013
  }
212937
213014
  }
@@ -212965,10 +213042,6 @@ function namedError(name, message) {
212965
213042
  err.name = name;
212966
213043
  return err;
212967
213044
  }
212968
- function slugProjectId(name) {
212969
- const slug6 = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
212970
- return `proj_${slug6 || Date.now()}`;
212971
- }
212972
213045
  function dirname24(path41) {
212973
213046
  const norm = path41.replace(/^\/+|\/+$/g, "");
212974
213047
  const idx = norm.lastIndexOf("/");
@@ -213165,6 +213238,7 @@ var init_service4 = __esm({
213165
213238
  init_uncategorized_project();
213166
213239
  init_workorders();
213167
213240
  init_stats();
213241
+ init_project_id();
213168
213242
  init_node_state();
213169
213243
  DEFAULT_AGENT_ACTOR = "actor:agent:system";
213170
213244
  CHAT_FOLDER_TASK_LABEL = "\u5BF9\u8BDD";
@@ -213454,9 +213528,14 @@ var init_service4 = __esm({
213454
213528
  async createProject(input) {
213455
213529
  if (!input.name.trim()) throw new Error("project name is required");
213456
213530
  const now = (/* @__PURE__ */ new Date()).toISOString();
213457
- const id = input.id ?? slugProjectId(input.name);
213458
- const existing = await this.projects.getProject(id);
213459
- if (existing) return this.withProjectDerivedState(existing);
213531
+ let id;
213532
+ if (input.id !== void 0 && input.id.trim() !== "") {
213533
+ id = input.id;
213534
+ const existing = await this.projects.getProject(id);
213535
+ if (existing) return this.withProjectDerivedState(existing);
213536
+ } else {
213537
+ id = await this.allocateProjectId();
213538
+ }
213460
213539
  const createdById = sanitizeCreatorActorId(input.createdBy);
213461
213540
  const project = {
213462
213541
  id,
@@ -213492,6 +213571,14 @@ var init_service4 = __esm({
213492
213571
  }
213493
213572
  return await this.getProject(id) ?? project;
213494
213573
  }
213574
+ /** 给「没带 id 的新建」挑一个本公司还没用过的随机 id(`randomProjectId`);连撞几次说明生成器坏了,直接报错。 */
213575
+ async allocateProjectId() {
213576
+ for (let attempt = 0; attempt < 5; attempt++) {
213577
+ const id = randomProjectId();
213578
+ if (!await this.projects.getProject(id)) return id;
213579
+ }
213580
+ throw new Error("project id allocation failed: random ids kept colliding");
213581
+ }
213495
213582
  async getProject(id) {
213496
213583
  if (isUncategorizedProjectId(id)) return this.buildUncategorizedProject();
213497
213584
  const project = await this.projects.getProject(id);
@@ -213983,7 +214070,7 @@ var init_service4 = __esm({
213983
214070
  updatedAt: file.uploadedAt
213984
214071
  });
213985
214072
  }
213986
- emitChatFolders(items, chatFiles, (actorId) => this.resolveActorName(actorId));
214073
+ emitChatFolders(items, chatFiles, (actorId) => this.resolveActorName(actorId), workorderTitleById);
213987
214074
  fillFolderSizes(items);
213988
214075
  const chatFolderTime = new Map(items.filter((item) => item.folderKind === "chat" && item.chatSessionId).map((item) => [item.chatSessionId, item.updatedAt]));
213989
214076
  items.sort((a, b2) => {
@@ -214834,12 +214921,12 @@ var init_service4 = __esm({
214834
214921
  });
214835
214922
 
214836
214923
  // ../server/src/dev-store.ts
214837
- var import_node_crypto41, 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;
214838
214925
  var init_dev_store = __esm({
214839
214926
  "../server/src/dev-store.ts"() {
214840
214927
  "use strict";
214841
214928
  init_src();
214842
- import_node_crypto41 = require("node:crypto");
214929
+ import_node_crypto43 = require("node:crypto");
214843
214930
  fs20 = __toESM(require("node:fs"), 1);
214844
214931
  path21 = __toESM(require("node:path"), 1);
214845
214932
  init_src();
@@ -214893,7 +214980,7 @@ var init_dev_store = __esm({
214893
214980
  return path21.join(this.dir, hash2);
214894
214981
  }
214895
214982
  async put(bytes2) {
214896
- const hash2 = (0, import_node_crypto41.createHash)("sha256").update(bytes2).digest("hex");
214983
+ const hash2 = (0, import_node_crypto43.createHash)("sha256").update(bytes2).digest("hex");
214897
214984
  const file = this.fileOf(hash2);
214898
214985
  if (!fs20.existsSync(file)) fs20.writeFileSync(file, bytes2);
214899
214986
  return hash2;
@@ -215321,6 +215408,8 @@ var init_dev_store = __esm({
215321
215408
  pending = /* @__PURE__ */ new Map();
215322
215409
  /** `outputs/` 产出文件的服务端副本表(2026-09-14):sessionId → 路径 → 副本。 */
215323
215410
  outputCopies = /* @__PURE__ */ new Map();
215411
+ outputFingerprints = /* @__PURE__ */ new Map();
215412
+ fileIssues = /* @__PURE__ */ new Map();
215324
215413
  pendingSeq = 0;
215325
215414
  async claimPendingInsert(m2) {
215326
215415
  const list2 = this.pending.get(m2.chatSessionId) ?? [];
@@ -215509,6 +215598,8 @@ var init_dev_store = __esm({
215509
215598
  this.sessionWorkOrders.delete(id);
215510
215599
  this.sessionWorkOrderKinds.delete(id);
215511
215600
  this.outputCopies.delete(id);
215601
+ this.outputFingerprints.delete(id);
215602
+ this.fileIssues.delete(id);
215512
215603
  }
215513
215604
  async appendMessage(m2) {
215514
215605
  return (await this.appendMessageOnce(m2)).message;
@@ -215637,6 +215728,22 @@ var init_dev_store = __esm({
215637
215728
  }
215638
215729
  return rows.sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.messageId.localeCompare(right.messageId));
215639
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
+ }
215640
215747
  async readOutputCopies(sessionId) {
215641
215748
  const hit = this.outputCopies.get(sessionId);
215642
215749
  return hit ? { ...hit } : null;
@@ -215697,6 +215804,9 @@ var init_dev_store = __esm({
215697
215804
  const store = new _FileChatSessionStore(file);
215698
215805
  if (fs20.existsSync(file)) {
215699
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);
215700
215810
  for (const s2 of snap.sessions ?? []) store.sessions.set(s2.id, s2);
215701
215811
  for (const [sid, list2] of Object.entries(snap.messages ?? {})) store.messages.set(sid, list2);
215702
215812
  for (const [sid, ids2] of Object.entries(snap.sessionWorkOrders ?? {})) store.sessionWorkOrders.set(sid, new Set(ids2));
@@ -215708,6 +215818,18 @@ var init_dev_store = __esm({
215708
215818
  }
215709
215819
  return store;
215710
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
+ }
215711
215833
  async createSession(s2) {
215712
215834
  await super.createSession(s2);
215713
215835
  this.save();
@@ -215773,6 +215895,9 @@ var init_dev_store = __esm({
215773
215895
  }
215774
215896
  save() {
215775
215897
  const snap = {
215898
+ outputCopies: Object.fromEntries(this.outputCopies),
215899
+ outputFingerprints: Object.fromEntries(this.outputFingerprints),
215900
+ fileIssues: Object.fromEntries(this.fileIssues),
215776
215901
  sessions: [...this.sessions.values()],
215777
215902
  messages: Object.fromEntries(this.messages),
215778
215903
  sessionWorkOrders: Object.fromEntries([...this.sessionWorkOrders].map(([k2, v2]) => [k2, [...v2]])),
@@ -216093,7 +216218,7 @@ var init_company_engine_router = __esm({
216093
216218
 
216094
216219
  // ../server/src/skill-market-client.ts
216095
216220
  function digestOf2(bytes2) {
216096
- return `sha256:${(0, import_node_crypto42.createHash)("sha256").update(bytes2).digest("hex")}`;
216221
+ return `sha256:${(0, import_node_crypto44.createHash)("sha256").update(bytes2).digest("hex")}`;
216097
216222
  }
216098
216223
  function createSkillMarketClientFromEnv(env = process.env) {
216099
216224
  const base = env["SKILL_MARKET_BASE_URL"];
@@ -216104,11 +216229,11 @@ function createSkillMarketClientFromEnv(env = process.env) {
216104
216229
  function sleep4(ms) {
216105
216230
  return new Promise((r) => setTimeout(r, ms));
216106
216231
  }
216107
- var import_node_crypto42, DisabledSkillMarketClient, HttpSkillMarketClient;
216232
+ var import_node_crypto44, DisabledSkillMarketClient, HttpSkillMarketClient;
216108
216233
  var init_skill_market_client = __esm({
216109
216234
  "../server/src/skill-market-client.ts"() {
216110
216235
  "use strict";
216111
- import_node_crypto42 = require("node:crypto");
216236
+ import_node_crypto44 = require("node:crypto");
216112
216237
  init_src();
216113
216238
  DisabledSkillMarketClient = class {
216114
216239
  async listSkills(_input) {
@@ -216592,18 +216717,23 @@ async function scanSessionOutputs(bridge, loc, now = Date.now) {
216592
216717
  if (!walked) return null;
216593
216718
  if (walked.ok) {
216594
216719
  const files2 = walked.entries.filter((entry) => entry.type === "file").map((entry) => ({ path: entry.path, size: entry.size, mtime: entry.mtime }));
216595
- return { files: files2, via: "walk", tree: walked.entries };
216720
+ return { files: files2, via: "walk", tree: walked.entries, ...walked.truncated ? { incomplete: true } : {} };
216596
216721
  }
216597
216722
  if (walked.code === "NOT_FOUND") return { files: [], via: "walk", tree: [] };
216598
216723
  if (walked.code !== "UNSUPPORTED") return null;
216599
216724
  }
216725
+ let incomplete = false;
216600
216726
  const files = await scanLayered(
216601
216727
  (path41) => bridge.list(loc.nodeId, { ...locator, path: path41 }),
216602
- now
216728
+ now,
216729
+ () => {
216730
+ incomplete = true;
216731
+ }
216603
216732
  );
216604
- return files === null ? null : { files, via: "layered" };
216733
+ return files === null ? null : { files, via: "layered", ...incomplete ? { incomplete: true } : {} };
216605
216734
  }
216606
- async function scanLayered(list2, now) {
216735
+ async function scanLayered(list2, now, markIncomplete = () => {
216736
+ }) {
216607
216737
  const deadline = now() + BUDGET_MS;
216608
216738
  const out = [];
216609
216739
  let listCalls = 0;
@@ -216620,23 +216750,41 @@ async function scanLayered(list2, now) {
216620
216750
  for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {
216621
216751
  const nextDirs = [];
216622
216752
  for (const { path: dir, outcome } of level) {
216623
- if (!outcome.ok) continue;
216753
+ if (!outcome.ok) {
216754
+ markIncomplete();
216755
+ continue;
216756
+ }
216757
+ if (outcome.truncated) markIncomplete();
216624
216758
  for (const entry of outcome.entries) {
216625
216759
  if (entry.type === "dir") {
216626
216760
  nextDirs.push(joinRel(dir, entry.name));
216627
216761
  continue;
216628
216762
  }
216629
- if (out.length >= MAX_ENTRIES) continue;
216763
+ if (out.length >= MAX_ENTRIES) {
216764
+ markIncomplete();
216765
+ continue;
216766
+ }
216630
216767
  out.push({ path: joinRel(dir, entry.name), size: entry.size, mtime: entry.mtime });
216631
216768
  }
216632
216769
  }
216633
- if (nextDirs.length === 0 || depth === MAX_DEPTH) break;
216634
- 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
+ }
216635
216779
  const batch = [];
216636
216780
  for (const dir of nextDirs) {
216637
- if (listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
216781
+ if (listCalls >= MAX_LIST_CALLS || now() >= deadline) {
216782
+ markIncomplete();
216783
+ break;
216784
+ }
216638
216785
  const outcome = await listDir(dir).catch(() => null);
216639
216786
  if (outcome) batch.push({ path: dir, outcome });
216787
+ else markIncomplete();
216640
216788
  }
216641
216789
  level = batch;
216642
216790
  }
@@ -216667,7 +216815,7 @@ async function settleSessionOutputs(deps) {
216667
216815
  diff.fingerprints[WALK_TABLE_MARK] = { size: 0, mtime: "" };
216668
216816
  }
216669
216817
  await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
216670
- return diff;
216818
+ return { ...diff, ...scan2.incomplete || scan2.files.some((f2) => f2.size == null || !f2.mtime) ? { scanIncomplete: true } : {} };
216671
216819
  }
216672
216820
  var WALK_MAX_ENTRIES, MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS, WALK_TABLE_MARK;
216673
216821
  var init_outputs_scan = __esm({
@@ -216722,11 +216870,17 @@ async function copySessionOutputs(deps) {
216722
216870
  try {
216723
216871
  existing = await deps.readCopies(deps.sessionId);
216724
216872
  } catch {
216873
+ result.skipped = Object.keys(deps.fingerprints).filter(isOutputPath).map((path41) => ({ path: path41, code: "STORE_FAILED" }));
216725
216874
  return result;
216726
216875
  }
216876
+ const deadline = Date.now() + 2e4;
216727
216877
  const plan = planOutputCopies(deps.fingerprints, existing, limits);
216728
216878
  result.skipped.push(...plan.skipped);
216729
216879
  for (const file of plan.toCopy) {
216880
+ if (Date.now() >= deadline) {
216881
+ result.skipped.push({ path: file.path, code: "BUDGET" });
216882
+ continue;
216883
+ }
216730
216884
  let read;
216731
216885
  try {
216732
216886
  read = await deps.bridge.read(
@@ -216743,6 +216897,14 @@ async function copySessionOutputs(deps) {
216743
216897
  continue;
216744
216898
  }
216745
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
+ }
216746
216908
  let blobRef;
216747
216909
  try {
216748
216910
  blobRef = await deps.putBlob(new Uint8Array(bytes2));
@@ -216772,7 +216934,7 @@ async function copySessionOutputs(deps) {
216772
216934
  return result;
216773
216935
  }
216774
216936
  function createOutputCopyScheduler(deps) {
216775
- const inFlight = /* @__PURE__ */ new Set();
216937
+ const inFlight = /* @__PURE__ */ new Map();
216776
216938
  return {
216777
216939
  schedule(args) {
216778
216940
  const { store, chatSessionId, companyId } = args;
@@ -216780,9 +216942,10 @@ function createOutputCopyScheduler(deps) {
216780
216942
  const mergeCopies = store.mergeOutputCopies?.bind(store);
216781
216943
  if (!readCopies || !mergeCopies) return Promise.resolve(null);
216782
216944
  const key = `${companyId ?? ""}|${chatSessionId}`;
216783
- if (inFlight.has(key)) return Promise.resolve(null);
216784
- inFlight.add(key);
216785
- return (async () => {
216945
+ const prior = inFlight.get(key);
216946
+ let pending;
216947
+ pending = (async () => {
216948
+ await prior;
216786
216949
  try {
216787
216950
  const session = await store.getSession(chatSessionId);
216788
216951
  if (!session || session.visibility === "internal" || session.analyzedRunId) return null;
@@ -216797,6 +216960,21 @@ function createOutputCopyScheduler(deps) {
216797
216960
  putBlob: deps.putBlob,
216798
216961
  ...deps.limits ? { limits: deps.limits } : {}
216799
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);
216800
216978
  if (result.skipped.length > 0) {
216801
216979
  const head = result.skipped.slice(0, 5).map((s2) => `${s2.path}:${s2.code}`).join(", ");
216802
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`);
@@ -216804,11 +216982,14 @@ function createOutputCopyScheduler(deps) {
216804
216982
  return result;
216805
216983
  } catch (err) {
216806
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);
216807
216986
  return null;
216808
216987
  } finally {
216809
- inFlight.delete(key);
216988
+ if (inFlight.get(key) === pending) inFlight.delete(key);
216810
216989
  }
216811
216990
  })();
216991
+ inFlight.set(key, pending);
216992
+ return pending;
216812
216993
  }
216813
216994
  };
216814
216995
  }
@@ -216818,9 +216999,9 @@ var init_outputs_copy = __esm({
216818
216999
  "use strict";
216819
217000
  init_src();
216820
217001
  OUTPUT_COPY_LIMITS = {
216821
- maxFileBytes: 2 * 1024 * 1024,
217002
+ maxFileBytes: 50 * 1024 * 1024,
216822
217003
  maxFiles: 50,
216823
- maxTotalBytes: 50 * 1024 * 1024
217004
+ maxTotalBytes: 100 * 1024 * 1024
216824
217005
  };
216825
217006
  }
216826
217007
  });
@@ -217030,11 +217211,11 @@ function typeDefError(def, mode) {
217030
217211
  }
217031
217212
  return null;
217032
217213
  }
217033
- var import_node_crypto43, 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;
217034
217215
  var init_types_admin = __esm({
217035
217216
  "../server/src/types-admin.ts"() {
217036
217217
  "use strict";
217037
- import_node_crypto43 = require("node:crypto");
217218
+ import_node_crypto45 = require("node:crypto");
217038
217219
  init_zod();
217039
217220
  init_src5();
217040
217221
  init_config_audit();
@@ -217161,7 +217342,7 @@ var init_types_admin = __esm({
217161
217342
  async trace(change, target, by, detail) {
217162
217343
  try {
217163
217344
  await this.audit?.({
217164
- id: `reg_${(0, import_node_crypto43.randomUUID)()}`,
217345
+ id: `reg_${(0, import_node_crypto45.randomUUID)()}`,
217165
217346
  actor: by ?? SYSTEM_CONFIG_ACTOR,
217166
217347
  kind: "registry_change",
217167
217348
  target,
@@ -217309,11 +217490,11 @@ function roleDefError(def, mode) {
217309
217490
  }
217310
217491
  return null;
217311
217492
  }
217312
- var import_node_crypto44, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
217493
+ var import_node_crypto46, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
217313
217494
  var init_roles_admin = __esm({
217314
217495
  "../server/src/roles-admin.ts"() {
217315
217496
  "use strict";
217316
- import_node_crypto44 = require("node:crypto");
217497
+ import_node_crypto46 = require("node:crypto");
217317
217498
  init_zod();
217318
217499
  init_src5();
217319
217500
  init_config_audit();
@@ -217338,7 +217519,7 @@ var init_roles_admin = __esm({
217338
217519
  async trace(change, target, by, detail) {
217339
217520
  try {
217340
217521
  await this.deps.audit?.({
217341
- id: `reg_${(0, import_node_crypto44.randomUUID)()}`,
217522
+ id: `reg_${(0, import_node_crypto46.randomUUID)()}`,
217342
217523
  actor: by ?? SYSTEM_CONFIG_ACTOR,
217343
217524
  kind: "registry_change",
217344
217525
  target,
@@ -218667,6 +218848,373 @@ var init_recovery_plan = __esm({
218667
218848
  }
218668
218849
  });
218669
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
+
218670
219218
  // ../server/src/domains/trace/resilient-appender.ts
218671
219219
  var ResilientTraceAppender;
218672
219220
  var init_resilient_appender = __esm({
@@ -219589,7 +220137,7 @@ function wireRecoveredChatTurn(deps) {
219589
220137
  const segText = itemLedger.projectContent();
219590
220138
  const segParts = collectedParts();
219591
220139
  const closingId = currentMsgId;
219592
- const nextId = (0, import_node_crypto45.randomUUID)();
220140
+ const nextId = (0, import_node_crypto49.randomUUID)();
219593
220141
  currentMsgId = nextId;
219594
220142
  itemLedger.beginSegment();
219595
220143
  live.setAssistantMessageId(nextId);
@@ -219603,7 +220151,7 @@ function wireRecoveredChatTurn(deps) {
219603
220151
  ...segParts.length ? { parts: segParts } : {}
219604
220152
  }).catch(() => void 0);
219605
220153
  await store.appendMessage({
219606
- id: (0, import_node_crypto45.randomUUID)(),
220154
+ id: (0, import_node_crypto49.randomUUID)(),
219607
220155
  sessionId: plan.chatSessionId,
219608
220156
  role: "user",
219609
220157
  content: text5,
@@ -219635,6 +220183,7 @@ function wireRecoveredChatTurn(deps) {
219635
220183
  const done = (async () => {
219636
220184
  const info = await new Promise((resolve10) => handle.onExit(resolve10));
219637
220185
  clearInterval(ckptTimer);
220186
+ await recordSessionFileRestoreExit(chatStore, plan.chatSessionId, info);
219638
220187
  const failed = exitFailed(info);
219639
220188
  if (failed) {
219640
220189
  const errText2 = exitErrorText(info);
@@ -219664,11 +220213,12 @@ function wireRecoveredChatTurn(deps) {
219664
220213
  log3
219665
220214
  );
219666
220215
  for (const item of closedItems) liveChat.publishItemStatus(plan.chatSessionId, item);
220216
+ const finalParts = await appendOutputsPart(parts, info.fileRestoreError ? void 0 : deps.outputsPart);
219667
220217
  await chatStore.updateMessage(currentMsgId, {
219668
220218
  content: itemLedger.projectContent(),
219669
220219
  status: failed ? "error" : "done",
219670
220220
  completedAt: finishedAt,
219671
- ...parts.length ? { parts } : {}
220221
+ ...finalParts.length ? { parts: finalParts } : {}
219672
220222
  }).catch(() => void 0);
219673
220223
  const nextRuntimeSessionId = info.runtimeSessionId ?? plan.runtimeSessionId;
219674
220224
  await chatStore.updateSession(plan.chatSessionId, {
@@ -219742,10 +220292,11 @@ async function sweepDanglingChatTurns(deps) {
219742
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"
219743
220293
  });
219744
220294
  }
220295
+ const finalParts = await appendOutputsPart(parts, deps.outputsPartFor?.(row.sessionId));
219745
220296
  await deps.updateMessage(row.id, {
219746
220297
  status,
219747
220298
  completedAt,
219748
- ...parts.length ? { parts } : {}
220299
+ ...finalParts.length ? { parts: finalParts } : {}
219749
220300
  });
219750
220301
  closed.push(row.id);
219751
220302
  deps.log?.(`[chat-sweep] \u884C ${row.id}\uFF08\u4F1A\u8BDD ${row.sessionId}\uFF09\u6536\u655B\u4E3A ${status}\uFF08${reason}\uFF09`);
@@ -219767,6 +220318,7 @@ async function reconcileChatExitFrame(deps) {
219767
220318
  if (meta?.dispatchId !== deps.dispatchId) continue;
219768
220319
  if (deps.ownedRunId?.(row.sessionId) === row.runId) return false;
219769
220320
  const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
220321
+ const outputsPart = deps.info.fileRestoreError ? void 0 : deps.outputsPartFor?.(row.sessionId);
219770
220322
  if (turn && turn.status !== "reserved" && turn.status !== "running") {
219771
220323
  await reconcileSettledChatTurn({
219772
220324
  plan: {
@@ -219785,11 +220337,13 @@ async function reconcileChatExitFrame(deps) {
219785
220337
  chatStore: deps.chatStore,
219786
220338
  trace: deps.trace,
219787
220339
  ...deps.items ? { items: deps.items } : {},
220340
+ ...outputsPart ? { outputsPart } : {},
219788
220341
  ...deps.log ? { log: deps.log } : {}
219789
220342
  });
219790
220343
  return true;
219791
220344
  }
219792
220345
  const { info } = deps;
220346
+ await recordSessionFileRestoreExit(deps.chatStore, row.sessionId, info);
219793
220347
  const failed = exitFailed(info);
219794
220348
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
219795
220349
  const stash = deps.drainStash(deps.dispatchId);
@@ -219837,11 +220391,12 @@ async function reconcileChatExitFrame(deps) {
219837
220391
  failed ? "failed" : "completed",
219838
220392
  deps.log ?? (() => void 0)
219839
220393
  );
220394
+ const finalParts = await appendOutputsPart(parts, outputsPart);
219840
220395
  await deps.chatStore.updateMessage(row.id, {
219841
220396
  content: itemLedger.projectContent(),
219842
220397
  status: failed ? "error" : "done",
219843
220398
  completedAt: finishedAt,
219844
- ...parts.length ? { parts } : {}
220399
+ ...finalParts.length ? { parts: finalParts } : {}
219845
220400
  });
219846
220401
  const metaRsid = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : void 0;
219847
220402
  const nextRuntimeSessionId = info.runtimeSessionId ?? metaRsid;
@@ -219904,7 +220459,7 @@ async function planChatTurnRecoveryAcrossCompanies(deps) {
219904
220459
  return { groups, settled, scanned, skipped };
219905
220460
  }
219906
220461
  async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219907
- const { listCompanies, storesFor, onCompanyError, onSessionClosed, log: log3, ...rest } = deps;
220462
+ const { listCompanies, storesFor, onCompanyError, onSessionClosed, sessionOutputsScan, log: log3, ...rest } = deps;
219908
220463
  const closed = [];
219909
220464
  for (const companyId of await listCompanies()) {
219910
220465
  try {
@@ -219915,7 +220470,8 @@ async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219915
220470
  listMessages: (sid) => stores.chatStore.listMessages(sid),
219916
220471
  updateMessage: (id, patch) => stores.chatStore.updateMessage(id, patch),
219917
220472
  ...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
219918
- ...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) } : {}
219919
220475
  });
219920
220476
  closed.push(...ids2);
219921
220477
  } catch (err) {
@@ -219925,7 +220481,7 @@ async function sweepDanglingChatTurnsAcrossCompanies(deps) {
219925
220481
  return closed;
219926
220482
  }
219927
220483
  async function reconcileChatExitFrameAcrossCompanies(deps) {
219928
- const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
220484
+ const { listCompanies, storesFor, onCompanyError, sessionOutputsScan, log: log3, ...rest } = deps;
219929
220485
  for (const companyId of await listCompanies()) {
219930
220486
  try {
219931
220487
  const stores = await storesFor(companyId);
@@ -219935,7 +220491,8 @@ async function reconcileChatExitFrameAcrossCompanies(deps) {
219935
220491
  chatStore: stores.chatStore,
219936
220492
  ...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
219937
220493
  ...stores.items ? { items: stores.items } : {},
219938
- ...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) } : {}
219939
220496
  });
219940
220497
  if (handled3) return true;
219941
220498
  } catch (err) {
@@ -219957,9 +220514,11 @@ async function reconcileSettledChatTurn(deps) {
219957
220514
  succeeded ? "completed" : "failed",
219958
220515
  deps.log ?? (() => void 0)
219959
220516
  );
220517
+ const withOutputs = await appendOutputsPart(plan.seedParts, deps.outputsPart);
219960
220518
  await deps.chatStore.updateMessage(plan.assistantMsgId, {
219961
220519
  status: succeeded ? "done" : "error",
219962
- completedAt
220520
+ completedAt,
220521
+ ...withOutputs !== plan.seedParts ? { parts: withOutputs } : {}
219963
220522
  });
219964
220523
  const run = await deps.trace.getRun(plan.runId);
219965
220524
  if (run?.status === "running") {
@@ -219977,6 +220536,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
219977
220536
  const { plan, turn, companyId, stores } = hit;
219978
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`);
219979
220538
  deps.evict(plan.dispatchId);
220539
+ const outputsPart = outputsPartProvider(deps.sessionOutputsScan, plan.chatSessionId, companyId);
219980
220540
  try {
219981
220541
  await reconcileSettledChatTurn({
219982
220542
  plan,
@@ -219984,6 +220544,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
219984
220544
  chatStore: stores.chatStore,
219985
220545
  trace: deps.trace,
219986
220546
  ...stores.items ? { items: stores.items } : {},
220547
+ ...outputsPart ? { outputsPart } : {},
219987
220548
  log: log3
219988
220549
  });
219989
220550
  } catch (err) {
@@ -220019,6 +220580,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220019
220580
  ...companyId ? { companyId } : {}
220020
220581
  });
220021
220582
  } : void 0;
220583
+ const outputsPart = outputsPartProvider(deps.sessionOutputsScan, plan.chatSessionId, companyId);
220022
220584
  const { done } = wireRecoveredChatTurn({
220023
220585
  plan,
220024
220586
  handle,
@@ -220029,6 +220591,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220029
220591
  unregister: () => deps.unregister(plan),
220030
220592
  ...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
220031
220593
  ...onTurnSettled ? { onTurnSettled } : {},
220594
+ ...outputsPart ? { outputsPart } : {},
220032
220595
  ...stores.items ? { items: stores.items, itemsTurnId } : {},
220033
220596
  log: log3
220034
220597
  });
@@ -220042,15 +220605,17 @@ async function recoverChatTurnsAcrossCompanies(deps) {
220042
220605
  }
220043
220606
  return { ...result, recovered };
220044
220607
  }
220045
- var import_node_crypto45, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
220608
+ var import_node_crypto49, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
220046
220609
  var init_chat_recovery = __esm({
220047
220610
  "../server/src/chat-recovery.ts"() {
220048
220611
  "use strict";
220049
- import_node_crypto45 = require("node:crypto");
220612
+ init_file_restore();
220613
+ import_node_crypto49 = require("node:crypto");
220050
220614
  init_chat_parts();
220051
220615
  init_sink();
220052
220616
  init_resilient_appender();
220053
220617
  init_chat_item_ledger();
220618
+ init_chat_assistant_turn();
220054
220619
  init_src6();
220055
220620
  init_session_exit_outcome();
220056
220621
  asObj3 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
@@ -220076,8 +220641,8 @@ var init_chat_recovery = __esm({
220076
220641
 
220077
220642
  // ../server/src/auth/crypto.ts
220078
220643
  function hashPassword(password) {
220079
- const salt = (0, import_node_crypto46.randomBytes)(16);
220080
- const dk = (0, import_node_crypto46.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 });
220081
220646
  return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
220082
220647
  }
220083
220648
  function verifyPassword(password, stored) {
@@ -220094,8 +220659,8 @@ function verifyPassword(password, stored) {
220094
220659
  return false;
220095
220660
  }
220096
220661
  if (expected.length === 0) return false;
220097
- const dk = (0, import_node_crypto46.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
220098
- return dk.length === expected.length && (0, import_node_crypto46.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);
220099
220664
  }
220100
220665
  function b64urlJson(value2) {
220101
220666
  return Buffer.from(JSON.stringify(value2)).toString("base64url");
@@ -220103,17 +220668,17 @@ function b64urlJson(value2) {
220103
220668
  function signSession(claims, secret) {
220104
220669
  const head = b64urlJson({ alg: "HS256", typ: "JWT" });
220105
220670
  const body2 = b64urlJson(claims);
220106
- const sig = (0, import_node_crypto46.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220671
+ const sig = (0, import_node_crypto50.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220107
220672
  return `${head}.${body2}.${sig}`;
220108
220673
  }
220109
220674
  function verifySession(token, secret, nowMs) {
220110
220675
  const parts = token.split(".");
220111
220676
  if (parts.length !== 3) return null;
220112
220677
  const [head, body2, sig] = parts;
220113
- const expected = (0, import_node_crypto46.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220678
+ const expected = (0, import_node_crypto50.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
220114
220679
  const got = Buffer.from(sig);
220115
220680
  const exp = Buffer.from(expected);
220116
- if (got.length !== exp.length || !(0, import_node_crypto46.timingSafeEqual)(got, exp)) return null;
220681
+ if (got.length !== exp.length || !(0, import_node_crypto50.timingSafeEqual)(got, exp)) return null;
220117
220682
  let claims;
220118
220683
  try {
220119
220684
  claims = JSON.parse(Buffer.from(body2, "base64url").toString("utf8"));
@@ -220125,21 +220690,21 @@ function verifySession(token, secret, nowMs) {
220125
220690
  return claims;
220126
220691
  }
220127
220692
  function generateCode() {
220128
- return String((0, import_node_crypto46.randomInt)(0, 1e6)).padStart(6, "0");
220693
+ return String((0, import_node_crypto50.randomInt)(0, 1e6)).padStart(6, "0");
220129
220694
  }
220130
220695
  function hashCode(code2, secret) {
220131
- return (0, import_node_crypto46.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
220696
+ return (0, import_node_crypto50.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
220132
220697
  }
220133
220698
  function safeEqualHash(a, b2) {
220134
220699
  const ba = Buffer.from(a);
220135
220700
  const bb = Buffer.from(b2);
220136
- return ba.length === bb.length && (0, import_node_crypto46.timingSafeEqual)(ba, bb);
220701
+ return ba.length === bb.length && (0, import_node_crypto50.timingSafeEqual)(ba, bb);
220137
220702
  }
220138
- var import_node_crypto46, SCRYPT_N, SCRYPT_KEYLEN;
220703
+ var import_node_crypto50, SCRYPT_N, SCRYPT_KEYLEN;
220139
220704
  var init_crypto2 = __esm({
220140
220705
  "../server/src/auth/crypto.ts"() {
220141
220706
  "use strict";
220142
- import_node_crypto46 = require("node:crypto");
220707
+ import_node_crypto50 = require("node:crypto");
220143
220708
  SCRYPT_N = 16384;
220144
220709
  SCRYPT_KEYLEN = 32;
220145
220710
  }
@@ -221289,16 +221854,16 @@ function variableKeyFromEnv(env = process.env) {
221289
221854
  return buf;
221290
221855
  }
221291
221856
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
221292
- return (0, import_node_crypto47.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
221857
+ return (0, import_node_crypto51.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
221293
221858
  }
221294
221859
  function dedupe(ids2) {
221295
221860
  return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
221296
221861
  }
221297
- var import_node_crypto47, REDACTED_MARKER, ActorsService, mask, changedKeys;
221862
+ var import_node_crypto51, REDACTED_MARKER, ActorsService, mask, changedKeys;
221298
221863
  var init_service5 = __esm({
221299
221864
  "../server/src/domains/actors/service.ts"() {
221300
221865
  "use strict";
221301
- import_node_crypto47 = require("node:crypto");
221866
+ import_node_crypto51 = require("node:crypto");
221302
221867
  init_skill_fetcher();
221303
221868
  init_identity();
221304
221869
  init_migrate();
@@ -221862,7 +222427,7 @@ ${input.description}
221862
222427
  */
221863
222428
  buildSkillFile(skillId, path41, content3, now) {
221864
222429
  const bytes2 = new TextEncoder().encode(content3);
221865
- const blobHash = (0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex");
222430
+ const blobHash = (0, import_node_crypto51.createHash)("sha256").update(bytes2).digest("hex");
221866
222431
  return { skillId, path: path41, content: content3, blobHash, size: bytes2.length, updatedAt: now };
221867
222432
  }
221868
222433
  /**
@@ -222514,15 +223079,15 @@ ${input.description}
222514
223079
  return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
222515
223080
  }
222516
223081
  encrypt(plain) {
222517
- const iv = (0, import_node_crypto47.randomBytes)(12);
222518
- const cipher = (0, import_node_crypto47.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);
222519
223084
  const enc4 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
222520
223085
  return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc4.toString("base64")].join(".");
222521
223086
  }
222522
223087
  decrypt(packed) {
222523
223088
  const [iv, tag, data] = packed.split(".");
222524
223089
  if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
222525
- const decipher = (0, import_node_crypto47.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"));
222526
223091
  decipher.setAuthTag(Buffer.from(tag, "base64"));
222527
223092
  return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
222528
223093
  }
@@ -223140,7 +223705,7 @@ async function listSkillMetadataForActor(args) {
223140
223705
  usedDirs.add(dir);
223141
223706
  const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
223142
223707
  let latest = "1970-01-01T00:00:00.000Z";
223143
- const hasher = (0, import_node_crypto48.createHash)("sha256");
223708
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223144
223709
  for (const m2 of sorted) {
223145
223710
  if (m2.updatedAt > latest) latest = m2.updatedAt;
223146
223711
  hasher.update(m2.path);
@@ -223165,11 +223730,11 @@ async function listSkillMetadataForActor(args) {
223165
223730
  const dir = sanitizeSkillDir(c.name, c.slug);
223166
223731
  if (usedDirs.has(dir)) continue;
223167
223732
  usedDirs.add(dir);
223168
- const hasher = (0, import_node_crypto48.createHash)("sha256");
223733
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223169
223734
  for (const p2 of paths) {
223170
223735
  hasher.update(p2);
223171
223736
  hasher.update("\0");
223172
- hasher.update((0, import_node_crypto48.createHash)("sha256").update(c.files[p2]).digest("hex"));
223737
+ hasher.update((0, import_node_crypto52.createHash)("sha256").update(c.files[p2]).digest("hex"));
223173
223738
  hasher.update("\0");
223174
223739
  }
223175
223740
  out.push({
@@ -223190,11 +223755,11 @@ async function listSkillMetadataForActor(args) {
223190
223755
  const dir = sanitizeSkillDir(b2.name, b2.id);
223191
223756
  if (usedDirs.has(dir)) continue;
223192
223757
  usedDirs.add(dir);
223193
- const hasher = (0, import_node_crypto48.createHash)("sha256");
223758
+ const hasher = (0, import_node_crypto52.createHash)("sha256");
223194
223759
  for (const p2 of paths) {
223195
223760
  hasher.update(p2);
223196
223761
  hasher.update("\0");
223197
- hasher.update((0, import_node_crypto48.createHash)("sha256").update(b2.files[p2]).digest("hex"));
223762
+ hasher.update((0, import_node_crypto52.createHash)("sha256").update(b2.files[p2]).digest("hex"));
223198
223763
  hasher.update("\0");
223199
223764
  }
223200
223765
  out.push({
@@ -223214,7 +223779,7 @@ function builtinSkillToFiles(b2) {
223214
223779
  skillId: `builtin:${b2.id}`,
223215
223780
  path: path41,
223216
223781
  content: content3,
223217
- blobHash: (0, import_node_crypto48.createHash)("sha256").update(content3).digest("hex"),
223782
+ blobHash: (0, import_node_crypto52.createHash)("sha256").update(content3).digest("hex"),
223218
223783
  size: Buffer.byteLength(content3, "utf8"),
223219
223784
  updatedAt: "1970-01-01T00:00:00.000Z"
223220
223785
  }));
@@ -223238,7 +223803,7 @@ function sanitizeSkillDir(name, fallbackId) {
223238
223803
  if (byName) return byName;
223239
223804
  const byId = fallbackId ? fold2(fallbackId) : "";
223240
223805
  if (byId) return byId;
223241
- return `skill-${(0, import_node_crypto48.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)}`;
223242
223807
  }
223243
223808
  async function materializeSkillFiles(args) {
223244
223809
  const skills = await args.service.listInstalledSkillsForActor(args.actorId);
@@ -223271,7 +223836,7 @@ function connectorSkillToFiles(c) {
223271
223836
  skillId: c.id,
223272
223837
  path: path41,
223273
223838
  content: content3,
223274
- blobHash: (0, import_node_crypto48.createHash)("sha256").update(content3).digest("hex"),
223839
+ blobHash: (0, import_node_crypto52.createHash)("sha256").update(content3).digest("hex"),
223275
223840
  size: Buffer.byteLength(content3, "utf8"),
223276
223841
  updatedAt: "1970-01-01T00:00:00.000Z"
223277
223842
  }));
@@ -223288,11 +223853,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
223288
223853
  ...s2.version !== void 0 ? { version: s2.version } : {}
223289
223854
  }));
223290
223855
  }
223291
- var import_node_crypto48;
223856
+ var import_node_crypto52;
223292
223857
  var init_skill_materializer = __esm({
223293
223858
  "../server/src/domains/actors/skill-materializer.ts"() {
223294
223859
  "use strict";
223295
- import_node_crypto48 = require("node:crypto");
223860
+ import_node_crypto52 = require("node:crypto");
223296
223861
  }
223297
223862
  });
223298
223863
 
@@ -224803,7 +225368,7 @@ function createActorsDomain(opts) {
224803
225368
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
224804
225369
  audit: async (entry) => {
224805
225370
  await opts.audit?.({
224806
- id: `reg_${(0, import_node_crypto49.randomUUID)()}`,
225371
+ id: `reg_${(0, import_node_crypto53.randomUUID)()}`,
224807
225372
  actor: entry.by,
224808
225373
  kind: "registry_change",
224809
225374
  target: entry.actorId,
@@ -224843,11 +225408,11 @@ function createActorsDomain(opts) {
224843
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 } : {} })
224844
225409
  };
224845
225410
  }
224846
- var import_node_crypto49;
225411
+ var import_node_crypto53;
224847
225412
  var init_actors = __esm({
224848
225413
  "../server/src/domains/actors/index.ts"() {
224849
225414
  "use strict";
224850
- import_node_crypto49 = require("node:crypto");
225415
+ import_node_crypto53 = require("node:crypto");
224851
225416
  init_service5();
224852
225417
  init_routes3();
224853
225418
  init_service5();
@@ -225017,235 +225582,6 @@ var init_assistant_tier_dispatch = __esm({
225017
225582
  }
225018
225583
  });
225019
225584
 
225020
- // ../server/src/domains/chat-sessions/files-view.ts
225021
- function reasonFromWorkdirCode(code2) {
225022
- switch (code2) {
225023
- case "NODE_OFFLINE":
225024
- return "NODE_OFFLINE";
225025
- case "TIMEOUT":
225026
- return "TIMEOUT";
225027
- default:
225028
- return "ERROR";
225029
- }
225030
- }
225031
- function joinRel2(dir, name) {
225032
- return dir ? `${dir}/${name}` : name;
225033
- }
225034
- function workdirFileItem(rel, name, size, mtime) {
225035
- return {
225036
- id: `workdir:${rel}`,
225037
- name,
225038
- kind: "file",
225039
- // 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
225040
- // 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
225041
- content_type: null,
225042
- size,
225043
- updated_at: mtime,
225044
- source: "workdir",
225045
- blob_ref: null,
225046
- path: rel,
225047
- message_id: null,
225048
- // 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
225049
- // 这里是**按能力不渲染**的判据,不是置灰假钮。
225050
- editable: false
225051
- };
225052
- }
225053
- async function collectWorkdirFiles(bridge, loc, now = Date.now) {
225054
- const items = [];
225055
- if (typeof bridge.walk === "function") {
225056
- const walked = await bridge.walk(
225057
- loc.nodeId,
225058
- { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
225059
- WORKDIR_TREE_MAX_ENTRIES
225060
- );
225061
- if (walked.ok) {
225062
- let truncated2 = walked.truncated === true;
225063
- for (const entry of walked.entries) {
225064
- if (entry.type !== "file") continue;
225065
- if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
225066
- truncated2 = true;
225067
- break;
225068
- }
225069
- const slash = entry.path.lastIndexOf("/");
225070
- items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
225071
- }
225072
- return { ok: true, items, truncated: truncated2 };
225073
- }
225074
- if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
225075
- if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
225076
- }
225077
- const deadline = now() + WORKDIR_WALK_BUDGET_MS;
225078
- let listCalls = 0;
225079
- let truncated = false;
225080
- const listDir = (path41) => {
225081
- listCalls += 1;
225082
- return bridge.list(loc.nodeId, {
225083
- workdirKey: loc.workdirKey,
225084
- runtimeKind: loc.runtimeKind,
225085
- rtId: loc.rtId,
225086
- path: path41
225087
- });
225088
- };
225089
- const root = await listDir("");
225090
- if (!root.ok) {
225091
- if (root.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
225092
- return { ok: false, reason: reasonFromWorkdirCode(root.code) };
225093
- }
225094
- let level = [{ path: "", outcome: root }];
225095
- for (let depth = 0; depth <= WORKDIR_WALK_MAX_DEPTH; depth += 1) {
225096
- const nextDirs = [];
225097
- for (const { path: dir, outcome } of level) {
225098
- if (!outcome.ok) {
225099
- truncated = true;
225100
- continue;
225101
- }
225102
- if (outcome.truncated) truncated = true;
225103
- for (const entry of outcome.entries) {
225104
- if (entry.type === "dir") {
225105
- nextDirs.push(joinRel2(dir, entry.name));
225106
- continue;
225107
- }
225108
- if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
225109
- truncated = true;
225110
- continue;
225111
- }
225112
- items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
225113
- }
225114
- }
225115
- if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
225116
- if (nextDirs.length > 0) truncated = true;
225117
- break;
225118
- }
225119
- const budget = WORKDIR_WALK_MAX_LIST_CALLS - listCalls;
225120
- if (budget <= 0 || now() >= deadline) {
225121
- truncated = true;
225122
- break;
225123
- }
225124
- const take = nextDirs.slice(0, budget);
225125
- if (take.length < nextDirs.length) truncated = true;
225126
- const outcomes = await Promise.all(take.map((p2) => listDir(p2)));
225127
- level = take.map((p2, i) => ({ path: p2, outcome: outcomes[i] }));
225128
- }
225129
- return { ok: true, items, truncated };
225130
- }
225131
- async function collectMessageFiles(messages, blobs) {
225132
- const rows = [];
225133
- for (const message of messages) {
225134
- if (!Array.isArray(message.attachments)) continue;
225135
- message.attachments.forEach((attachment, index2) => {
225136
- if (!attachment || typeof attachment.name !== "string" || !attachment.name) return;
225137
- const blobRef = typeof attachment.blobRef === "string" && attachment.blobRef ? attachment.blobRef : null;
225138
- rows.push({
225139
- item: {
225140
- id: `msg:${message.id}:${index2}`,
225141
- name: attachment.name,
225142
- kind: "file",
225143
- updated_at: attachment.updatedAt ?? message.createdAt,
225144
- source: "message",
225145
- blob_ref: blobRef,
225146
- path: null,
225147
- message_id: message.id,
225148
- // 没有 blobRef 的是**历史纯文本附件**(契约注释:legacy text attachments may only
225149
- // contain `name`)——没有可写回的载体,一律不给编辑入口。
225150
- editable: blobRef !== null && isEditableFileName(attachment.name)
225151
- },
225152
- blobRef,
225153
- declaredType: typeof attachment.contentType === "string" && attachment.contentType ? attachment.contentType : null
225154
- });
225155
- });
225156
- }
225157
- const metaCache = /* @__PURE__ */ new Map();
225158
- const metaOf = (ref2) => {
225159
- if (!blobs) return Promise.resolve(null);
225160
- let hit = metaCache.get(ref2);
225161
- if (!hit) {
225162
- hit = blobs.meta(ref2).catch(() => null);
225163
- metaCache.set(ref2, hit);
225164
- }
225165
- return hit;
225166
- };
225167
- return Promise.all(rows.map(async ({ item, blobRef, declaredType }) => {
225168
- const meta = blobRef ? await metaOf(blobRef) : null;
225169
- return {
225170
- ...item,
225171
- content_type: declaredType ?? meta?.contentType ?? null,
225172
- size: meta?.size ?? null
225173
- };
225174
- }));
225175
- }
225176
- function mergeChatSessionFiles(messageItems, workdirItems, materializedCopies = /* @__PURE__ */ new Set()) {
225177
- const out = [];
225178
- const seenMessageKeys = /* @__PURE__ */ new Set();
225179
- for (const item of [...messageItems].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at))) {
225180
- const key = `${item.name}\0${item.blob_ref ?? ""}`;
225181
- if (seenMessageKeys.has(key)) continue;
225182
- seenMessageKeys.add(key);
225183
- out.push(item);
225184
- }
225185
- const seenWorkdirPaths = /* @__PURE__ */ new Set();
225186
- for (const item of workdirItems) {
225187
- if (item.path && materializedCopies.has(item.path)) continue;
225188
- if (seenWorkdirPaths.has(item.id)) continue;
225189
- seenWorkdirPaths.add(item.id);
225190
- out.push(item);
225191
- }
225192
- out.sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.name.localeCompare(b2.name));
225193
- return out;
225194
- }
225195
- async function findMaterializedAttachmentCopies(messageItems, workdirItems, read) {
225196
- const copies = /* @__PURE__ */ new Set();
225197
- const attachments = messageItems.filter((item) => item.blob_ref && /^[a-f0-9]{64}$/.test(item.blob_ref));
225198
- let budget = CHAT_ATTACHMENT_MAX_BYTES;
225199
- const deadline = Date.now() + WORKDIR_WALK_BUDGET_MS;
225200
- let reads = 0;
225201
- for (const file of workdirItems) {
225202
- const rootFile = file.path === file.name;
225203
- const inputFile = file.path?.startsWith("inputs/attachments/") && !file.path.slice("inputs/attachments/".length).includes("/");
225204
- if (!file.path || !rootFile && !inputFile) continue;
225205
- const candidates = attachments.filter((attachment) => {
225206
- const safe = safeAttachmentName(attachment.name);
225207
- const numbered = file.name.endsWith(`-${safe}`) && /^\d+$/.test(file.name.slice(0, -(safe.length + 1)));
225208
- return (rootFile ? file.name === attachment.name : file.name === safe || numbered) && attachment.size !== null && attachment.size === file.size;
225209
- });
225210
- if (Date.now() >= deadline) break;
225211
- if (!candidates.length || file.size === null || file.size > budget || reads >= 10) continue;
225212
- reads += 1;
225213
- budget -= file.size;
225214
- let timer;
225215
- try {
225216
- const result = await Promise.race([
225217
- read(file.path, file.size),
225218
- new Promise((resolve10) => {
225219
- timer = setTimeout(() => resolve10(null), Math.max(0, deadline - Date.now()));
225220
- })
225221
- ]);
225222
- if (!result?.ok) continue;
225223
- const bytes2 = Buffer.from(result.contentBase64, "base64");
225224
- if (bytes2.length !== file.size) continue;
225225
- const hash2 = (0, import_node_crypto50.createHash)("sha256").update(bytes2).digest("hex");
225226
- if (candidates.some((attachment) => attachment.blob_ref === hash2)) copies.add(file.path);
225227
- } catch {
225228
- } finally {
225229
- if (timer) clearTimeout(timer);
225230
- }
225231
- }
225232
- return copies;
225233
- }
225234
- var import_node_crypto50, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
225235
- var init_files_view = __esm({
225236
- "../server/src/domains/chat-sessions/files-view.ts"() {
225237
- "use strict";
225238
- import_node_crypto50 = require("node:crypto");
225239
- init_chat_attachment_files();
225240
- init_src();
225241
- WORKDIR_WALK_MAX_DEPTH = 3;
225242
- WORKDIR_WALK_MAX_ENTRIES = 200;
225243
- WORKDIR_WALK_MAX_LIST_CALLS = 20;
225244
- WORKDIR_WALK_BUDGET_MS = 1e4;
225245
- WORKDIR_TREE_MAX_ENTRIES = 2e3;
225246
- }
225247
- });
225248
-
225249
225585
  // ../server/src/domains/projects/chat-files.ts
225250
225586
  function isProjectChatSession(session) {
225251
225587
  if (session.visibility === "internal") return false;
@@ -225257,79 +225593,45 @@ function chatFolderTitle(session) {
225257
225593
  const title = session.title?.trim();
225258
225594
  return title ? title : "\u672A\u547D\u540D\u5BF9\u8BDD";
225259
225595
  }
225260
- function outputDisplayName(path41) {
225261
- return path41.startsWith("outputs/") ? path41.slice("outputs/".length) : path41;
225262
- }
225263
225596
  async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
225264
225597
  const scope = isUncategorizedProjectId(projectId2) ? null : projectId2;
225265
- const [attachmentMessages, outputSessions] = await Promise.all([
225266
- chat.listAttachmentMessages ? chat.listAttachmentMessages(scope) : Promise.resolve([]),
225267
- 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 ?? []) ?? []
225268
225602
  ]);
225269
- const sessionIds = [.../* @__PURE__ */ new Set([
225603
+ const ids2 = [.../* @__PURE__ */ new Set([
225270
225604
  ...attachmentMessages.map((m2) => m2.sessionId),
225271
- ...outputSessions.map((s2) => s2.sessionId)
225605
+ ...outputSessions.map((s2) => s2.sessionId),
225606
+ ...taskSessions.map((s2) => s2.id)
225272
225607
  ])];
225273
- const sessions = /* @__PURE__ */ new Map();
225274
- await Promise.all(sessionIds.map(async (id) => {
225275
- const session = await chat.getSession(id);
225276
- if (!session) return;
225277
- if ((session.projectId ?? null) !== scope) return;
225278
- if (!isProjectChatSession(session)) return;
225279
- if (options.isDelegationChild && await options.isDelegationChild(id).catch(() => false)) return;
225280
- sessions.set(id, session);
225281
- }));
225282
225608
  const out = [];
225283
- const base = (session) => ({
225284
- projectId: projectId2,
225285
- chatSessionId: session.id,
225286
- chatTitle: chatFolderTitle(session),
225287
- chatOwnerId: session.humanActorId
225288
- });
225289
- const bySession = /* @__PURE__ */ new Map();
225290
- for (const message of attachmentMessages) {
225291
- if (!sessions.has(message.sessionId)) continue;
225292
- const list2 = bySession.get(message.sessionId) ?? [];
225293
- list2.push(message);
225294
- bySession.set(message.sessionId, list2);
225295
- }
225296
- for (const [sessionId, messages] of bySession) {
225297
- const session = sessions.get(sessionId);
225298
- const rows = await collectMessageFiles(messages.map((m2) => ({ ...m2, id: m2.messageId })), { meta });
225299
- for (const row of mergeChatSessionFiles(rows, [])) {
225300
- if (row.blob_ref === null) continue;
225301
- out.push({
225302
- ...base(session),
225303
- fileId: `chat:${sessionId}:${row.id}`,
225304
- name: row.name,
225305
- blobId: row.blob_ref,
225306
- ...row.content_type ? { contentType: row.content_type } : {},
225307
- size: row.size,
225308
- uploadedBy: session.humanActorId,
225309
- uploadedAt: row.updated_at,
225310
- chatSource: "attachment"
225311
- });
225312
- }
225313
- }
225314
- for (const record8 of outputSessions) {
225315
- const session = sessions.get(record8.sessionId);
225316
- if (!session) continue;
225317
- const seen = new Set(out.filter((file) => file.chatSessionId === session.id).map((file) => `${file.name}\0${file.blobId}`));
225318
- const copies = record8.copies ?? {};
225319
- for (const path41 of Object.keys(copies).sort((a, b2) => a.localeCompare(b2))) {
225320
- const copy = copies[path41];
225321
- if (!copy?.blobRef) continue;
225322
- 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;
225323
225620
  out.push({
225324
- ...base(session),
225325
- fileId: `chat:${session.id}:out:${copy.path}`,
225326
- name: outputDisplayName(copy.path),
225327
- blobId: copy.blobRef,
225328
- ...copy.contentType ? { contentType: copy.contentType } : {},
225329
- size: copy.size,
225330
- uploadedBy: session.aiActorId,
225331
- uploadedAt: Number.isNaN(Date.parse(copy.mtime)) ? copy.copiedAt : new Date(copy.mtime).toISOString(),
225332
- 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 } : {}
225333
225635
  });
225334
225636
  }
225335
225637
  }
@@ -225338,7 +225640,8 @@ async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
225338
225640
  var init_chat_files2 = __esm({
225339
225641
  "../server/src/domains/projects/chat-files.ts"() {
225340
225642
  "use strict";
225341
- init_files_view();
225643
+ init_persistent_files();
225644
+ init_persistent_files();
225342
225645
  init_uncategorized_project();
225343
225646
  }
225344
225647
  });
@@ -226302,7 +226605,8 @@ function toApiProjectFilesView(view) {
226302
226605
  // 对话文件夹(`"chat"`):前端据此换带对话气泡的文件夹图标。其余文件夹与文件行为 null。
226303
226606
  folder_kind: item.folderKind ?? null,
226304
226607
  // 对话文件夹及其子文件所属的对话;前端「所属任务」列的「对话」筛选档靠它。其余行为 null。
226305
- chat_session_id: item.chatSessionId ?? null
226608
+ chat_session_id: item.chatSessionId ?? null,
226609
+ ...item.fileIssues?.length ? { file_issues: item.fileIssues } : {}
226306
226610
  }))
226307
226611
  };
226308
226612
  }
@@ -228626,11 +228930,11 @@ function fromRow(row) {
228626
228930
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
228627
228931
  return r;
228628
228932
  }
228629
- var import_node_crypto51, 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;
228630
228934
  var init_notification_store = __esm({
228631
228935
  "../server/src/domains/collab/notification-store.ts"() {
228632
228936
  "use strict";
228633
- import_node_crypto51 = require("node:crypto");
228937
+ import_node_crypto54 = require("node:crypto");
228634
228938
  import_node_fs14 = __toESM(require("node:fs"), 1);
228635
228939
  DEFAULT_PAGE = 50;
228636
228940
  MAX_PAGE = 200;
@@ -228663,7 +228967,7 @@ var init_notification_store = __esm({
228663
228967
  async insert(n) {
228664
228968
  const existing = this.findLive(n.recipient, n.sourceKey, n.category);
228665
228969
  if (existing) return existing;
228666
- const row = { ...n, id: `ntf:${(0, import_node_crypto51.randomUUID)()}`, updatedAt: n.enteredAt };
228970
+ const row = { ...n, id: `ntf:${(0, import_node_crypto54.randomUUID)()}`, updatedAt: n.enteredAt };
228667
228971
  this.rows.set(row.id, row);
228668
228972
  this.save();
228669
228973
  return row;
@@ -228786,7 +229090,7 @@ var init_notification_store = __esm({
228786
229090
  return new _PostgresNotificationStore(sql, schema);
228787
229091
  }
228788
229092
  async insert(n) {
228789
- const id = `ntf:${(0, import_node_crypto51.randomUUID)()}`;
229093
+ const id = `ntf:${(0, import_node_crypto54.randomUUID)()}`;
228790
229094
  const r = await this.sql.query(
228791
229095
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
228792
229096
  title, workspace_title, entered_at, updated_at, status, read_at, item)
@@ -229737,13 +230041,13 @@ function nameKey(name) {
229737
230041
  function cleanName(name) {
229738
230042
  return name.trim().replace(/\s+/g, " ");
229739
230043
  }
229740
- var import_node_fs15, import_node_path20, import_node_crypto52, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
230044
+ var import_node_fs15, import_node_path20, import_node_crypto55, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
229741
230045
  var init_tags = __esm({
229742
230046
  "../server/src/domains/collab/tags.ts"() {
229743
230047
  "use strict";
229744
230048
  import_node_fs15 = __toESM(require("node:fs"), 1);
229745
230049
  import_node_path20 = __toESM(require("node:path"), 1);
229746
- import_node_crypto52 = __toESM(require("node:crypto"), 1);
230050
+ import_node_crypto55 = __toESM(require("node:crypto"), 1);
229747
230051
  SEP = "::";
229748
230052
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
229749
230053
  prefixOf = (companyId) => `${companyId}${SEP}`;
@@ -229761,7 +230065,7 @@ var init_tags = __esm({
229761
230065
  if (!name) return null;
229762
230066
  if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
229763
230067
  const tag = {
229764
- id: `tag-${import_node_crypto52.default.randomBytes(6).toString("hex")}`,
230068
+ id: `tag-${import_node_crypto55.default.randomBytes(6).toString("hex")}`,
229765
230069
  name,
229766
230070
  ...input.color?.trim() ? { color: input.color.trim() } : {},
229767
230071
  ...input.description?.trim() ? { description: input.description.trim() } : {}
@@ -231007,11 +231311,11 @@ async function isEmployedElsewhere(store, companyId, accountId) {
231007
231311
  const memberships = await store.listMembershipsForAccount(accountId);
231008
231312
  return memberships.some((m2) => m2.companyId !== companyId);
231009
231313
  }
231010
- var import_node_crypto53, 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;
231011
231315
  var init_service6 = __esm({
231012
231316
  "../server/src/domains/companies/service.ts"() {
231013
231317
  "use strict";
231014
- import_node_crypto53 = require("node:crypto");
231318
+ import_node_crypto56 = require("node:crypto");
231015
231319
  init_console_url();
231016
231320
  ROLES = ["owner", "admin", "member"];
231017
231321
  INVITABLE_ROLES = ["admin", "member"];
@@ -231103,7 +231407,7 @@ var init_service6 = __esm({
231103
231407
  const existing = await this.store.getAccountByEmail(normalized4);
231104
231408
  if (existing) return existing;
231105
231409
  const account = {
231106
- id: `actor:human:${(0, import_node_crypto53.randomUUID)()}`,
231410
+ id: `actor:human:${(0, import_node_crypto56.randomUUID)()}`,
231107
231411
  email: normalized4,
231108
231412
  name: name ?? normalized4,
231109
231413
  status: "active"
@@ -231312,7 +231616,7 @@ var init_service6 = __esm({
231312
231616
  }
231313
231617
  const nowMs = Date.parse(this.now());
231314
231618
  const invitation = {
231315
- id: `invitation:${(0, import_node_crypto53.randomUUID)()}`,
231619
+ id: `invitation:${(0, import_node_crypto56.randomUUID)()}`,
231316
231620
  companyId,
231317
231621
  email: mail,
231318
231622
  role,
@@ -231443,7 +231747,7 @@ var init_service6 = __esm({
231443
231747
  const nowMs = Date.parse(this.now());
231444
231748
  const link = {
231445
231749
  companyId,
231446
- token: (0, import_node_crypto53.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
231750
+ token: (0, import_node_crypto56.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
231447
231751
  role: INVITE_LINK_ROLE,
231448
231752
  createdAt: new Date(nowMs).toISOString(),
231449
231753
  expiresAt: new Date(nowMs + INVITE_LINK_TTL_MS).toISOString(),
@@ -231500,7 +231804,7 @@ var init_service6 = __esm({
231500
231804
  const reusable = existing.find((inv) => !this.isExpired(inv));
231501
231805
  if (reusable) return reusable;
231502
231806
  const invitation = {
231503
- id: `invitation:${(0, import_node_crypto53.randomUUID)()}`,
231807
+ id: `invitation:${(0, import_node_crypto56.randomUUID)()}`,
231504
231808
  companyId: link.companyId,
231505
231809
  email: mail,
231506
231810
  role: link.role,
@@ -231738,11 +232042,11 @@ var init_service6 = __esm({
231738
232042
  });
231739
232043
 
231740
232044
  // ../server/src/domains/companies/offboarding.ts
231741
- var import_node_crypto54, MemberOffboardingService;
232045
+ var import_node_crypto57, MemberOffboardingService;
231742
232046
  var init_offboarding = __esm({
231743
232047
  "../server/src/domains/companies/offboarding.ts"() {
231744
232048
  "use strict";
231745
- import_node_crypto54 = require("node:crypto");
232049
+ import_node_crypto57 = require("node:crypto");
231746
232050
  init_service6();
231747
232051
  MemberOffboardingService = class {
231748
232052
  store;
@@ -231814,7 +232118,7 @@ var init_offboarding = __esm({
231814
232118
  const { ownedAgents, inFlight } = await this.snapshot(companyId, accountId);
231815
232119
  const at = this.now();
231816
232120
  const started = await this.store.startMemberOffboarding({
231817
- operationId: `off:${(0, import_node_crypto54.randomUUID)()}`,
232121
+ operationId: `off:${(0, import_node_crypto57.randomUUID)()}`,
231818
232122
  companyId,
231819
232123
  accountId,
231820
232124
  initiatedBy,
@@ -232324,18 +232628,18 @@ var init_routes6 = __esm({
232324
232628
 
232325
232629
  // ../server/src/domains/companies/agent-credentials.ts
232326
232630
  function hashAgentToken(token, secret) {
232327
- return HASH_PREFIX + (0, import_node_crypto55.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");
232328
232632
  }
232329
232633
  function safeEqual(a, b2) {
232330
232634
  const ba = Buffer.from(a);
232331
232635
  const bb = Buffer.from(b2);
232332
- return ba.length === bb.length && (0, import_node_crypto55.timingSafeEqual)(ba, bb);
232636
+ return ba.length === bb.length && (0, import_node_crypto58.timingSafeEqual)(ba, bb);
232333
232637
  }
232334
- var import_node_crypto55, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
232638
+ var import_node_crypto58, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
232335
232639
  var init_agent_credentials = __esm({
232336
232640
  "../server/src/domains/companies/agent-credentials.ts"() {
232337
232641
  "use strict";
232338
- import_node_crypto55 = require("node:crypto");
232642
+ import_node_crypto58 = require("node:crypto");
232339
232643
  init_service6();
232340
232644
  HASH_PREFIX = "ac1$";
232341
232645
  TOKEN_PREFIX = "oak_";
@@ -232360,7 +232664,7 @@ var init_agent_credentials = __esm({
232360
232664
  if (employee.status !== "active") {
232361
232665
  throw new CompanyError(409, "AGENT_LEFT", `\u6570\u5B57\u5458\u5DE5\u5DF2\u79BB\u5F00\uFF0C\u4E0D\u80FD\u7B7E\u53D1\u51ED\u8BC1\uFF1A${agentId}`);
232362
232666
  }
232363
- const token = `${TOKEN_PREFIX}${(0, import_node_crypto55.randomBytes)(32).toString("base64url")}`;
232667
+ const token = `${TOKEN_PREFIX}${(0, import_node_crypto58.randomBytes)(32).toString("base64url")}`;
232364
232668
  const credential = {
232365
232669
  tokenHash: hashAgentToken(token, this.secret),
232366
232670
  companyId,
@@ -232780,7 +233084,7 @@ function gatewayInstanceSlug(gatewayUrl) {
232780
233084
  }
232781
233085
  function connectInstanceName(gatewayUrl, token, companyId) {
232782
233086
  const scope = companyId?.trim();
232783
- const suffix = scope ? `c${(0, import_node_crypto56.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";
232784
233088
  const base = gatewayInstanceSlug(gatewayUrl);
232785
233089
  return base.slice(0, Math.max(1, 64 - suffix.length - 1)) + "-" + suffix;
232786
233090
  }
@@ -233109,12 +233413,12 @@ function buildNpxCmd(p2) {
233109
233413
  const prefix = daemonNpmPrefix();
233110
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)}`;
233111
233415
  }
233112
- var import_node_crypto56;
233416
+ var import_node_crypto59;
233113
233417
  var init_connect_script = __esm({
233114
233418
  "../server/src/domains/nodes/connect-script.ts"() {
233115
233419
  "use strict";
233116
233420
  init_src();
233117
- import_node_crypto56 = require("node:crypto");
233421
+ import_node_crypto59 = require("node:crypto");
233118
233422
  }
233119
233423
  });
233120
233424
 
@@ -233675,7 +233979,7 @@ function nodesDomain(deps) {
233675
233979
  }
233676
233980
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
233677
233981
  } else {
233678
- nodeId = `node-${(0, import_node_crypto57.randomUUID)().slice(0, 8)}`;
233982
+ nodeId = `node-${(0, import_node_crypto60.randomUUID)().slice(0, 8)}`;
233679
233983
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
233680
233984
  }
233681
233985
  const existingNode = await deps.nodeStore.getNode(nodeId);
@@ -233858,13 +234162,13 @@ function normalizeGatewayUrl(raw) {
233858
234162
  }
233859
234163
  }
233860
234164
  function newNodeId() {
233861
- return "node-" + (0, import_node_crypto57.randomUUID)().replace(/-/g, "").slice(0, 12);
234165
+ return "node-" + (0, import_node_crypto60.randomUUID)().replace(/-/g, "").slice(0, 12);
233862
234166
  }
233863
- var import_node_crypto57, import_node_fs16, import_node_url6, import_node_path21;
234167
+ var import_node_crypto60, import_node_fs16, import_node_url6, import_node_path21;
233864
234168
  var init_routes7 = __esm({
233865
234169
  "../server/src/domains/nodes/routes.ts"() {
233866
234170
  "use strict";
233867
- import_node_crypto57 = require("node:crypto");
234171
+ import_node_crypto60 = require("node:crypto");
233868
234172
  import_node_fs16 = require("node:fs");
233869
234173
  import_node_url6 = require("node:url");
233870
234174
  import_node_path21 = require("node:path");
@@ -235055,7 +235359,7 @@ function createBundle(root, target, input) {
235055
235359
  }
235056
235360
  }
235057
235361
  const bytes2 = fs26.statSync(absBundle).size;
235058
- const digest = (0, import_node_crypto58.createHash)("sha256").update(fs26.readFileSync(absBundle)).digest("hex");
235362
+ const digest = (0, import_node_crypto61.createHash)("sha256").update(fs26.readFileSync(absBundle)).digest("hex");
235059
235363
  const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
235060
235364
  return {
235061
235365
  absoluteBundlePath: absBundle,
@@ -235151,12 +235455,12 @@ function classifyGitError(fallback, err, where) {
235151
235455
  function oneLine(msg) {
235152
235456
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
235153
235457
  }
235154
- var import_node_child_process17, import_node_crypto58, 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;
235155
235459
  var init_readonly_bundle = __esm({
235156
235460
  "../server/src/git/readonly-bundle.ts"() {
235157
235461
  "use strict";
235158
235462
  import_node_child_process17 = require("node:child_process");
235159
- import_node_crypto58 = require("node:crypto");
235463
+ import_node_crypto61 = require("node:crypto");
235160
235464
  fs26 = __toESM(require("node:fs"), 1);
235161
235465
  os7 = __toESM(require("node:os"), 1);
235162
235466
  path25 = __toESM(require("node:path"), 1);
@@ -235176,7 +235480,7 @@ var init_readonly_bundle = __esm({
235176
235480
 
235177
235481
  // ../server/src/domains/collab/create-seeded-workorder.ts
235178
235482
  function makeSeededWorkorderCreator(deps) {
235179
- const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto59.randomUUID)().slice(0, 8)}`);
235483
+ const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto62.randomUUID)().slice(0, 8)}`);
235180
235484
  return async (input) => {
235181
235485
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
235182
235486
  const kernel = company?.kernel ?? deps.kernel;
@@ -235269,7 +235573,7 @@ function makeSeededWorkorderCreator(deps) {
235269
235573
  };
235270
235574
  }
235271
235575
  function workspaceForKey(key) {
235272
- return `ws:wo-${(0, import_node_crypto59.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)}`;
235273
235577
  }
235274
235578
  function existingWorkorder(kernel, workspace) {
235275
235579
  const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === workspace);
@@ -235282,11 +235586,11 @@ function existingWorkorder(kernel, workspace) {
235282
235586
  spawned: arts.map((a) => ({ id: a.id, type: a.type, owner: a.owner }))
235283
235587
  };
235284
235588
  }
235285
- var import_node_crypto59, enc2;
235589
+ var import_node_crypto62, enc2;
235286
235590
  var init_create_seeded_workorder = __esm({
235287
235591
  "../server/src/domains/collab/create-seeded-workorder.ts"() {
235288
235592
  "use strict";
235289
- import_node_crypto59 = require("node:crypto");
235593
+ import_node_crypto62 = require("node:crypto");
235290
235594
  init_uncategorized_project();
235291
235595
  init_src();
235292
235596
  init_planner();
@@ -236716,12 +237020,12 @@ function stateFromAttempt(attempt, handoff, checkpoint, now, stalledAfterMs, ope
236716
237020
  if (attempt.status === "no-output") return handoff?.unresolvedIssues.length ? "blocked" : "no_change";
236717
237021
  return "failed";
236718
237022
  }
236719
- var import_node_crypto60, 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;
236720
237024
  var init_service8 = __esm({
236721
237025
  "../server/src/domains/execution-continuity/service.ts"() {
236722
237026
  "use strict";
236723
237027
  init_src();
236724
- import_node_crypto60 = require("node:crypto");
237028
+ import_node_crypto63 = require("node:crypto");
236725
237029
  init_src5();
236726
237030
  BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
236727
237031
  HANDOFF_OUTCOMES2 = /* @__PURE__ */ new Set(["succeeded", "no-output", "failed", "cancelled", "timeout", "orphaned"]);
@@ -236952,7 +237256,7 @@ var init_service8 = __esm({
236952
237256
  if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
236953
237257
  const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
236954
237258
  if (!injectedResume) return null;
236955
- const actualHash = (0, import_node_crypto60.createHash)("sha256").update(injectedResume).digest("hex");
237259
+ const actualHash = (0, import_node_crypto63.createHash)("sha256").update(injectedResume).digest("hex");
236956
237260
  if (actualHash !== expectedHash) return null;
236957
237261
  const recoverySnapshot = parseResumePackSnapshot(injectedResume);
236958
237262
  if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
@@ -237879,6 +238183,10 @@ var init_project_bundle = __esm({
237879
238183
  });
237880
238184
 
237881
238185
  // ../server/src/domains/chat-sessions/index.ts
238186
+ function sessionProjectIdToStore(projectId2) {
238187
+ if (typeof projectId2 !== "string" || projectId2.trim() === "") return null;
238188
+ return isUncategorizedProjectId(projectId2) ? null : projectId2;
238189
+ }
237882
238190
  function parseDurationMs(raw) {
237883
238191
  if (!raw) return null;
237884
238192
  const m2 = /^(\d+)\s*(ms|s|m|h|d)?$/i.exec(raw.trim());
@@ -238052,7 +238360,7 @@ function createChatSessionsDomain(opts) {
238052
238360
  if (!text5.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
238053
238361
  const now = Date.now();
238054
238362
  sweepDrafts(now);
238055
- const id = `cd_${(0, import_node_crypto61.randomUUID)()}`;
238363
+ const id = `cd_${(0, import_node_crypto64.randomUUID)()}`;
238056
238364
  drafts.set(id, { text: text5, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
238057
238365
  return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
238058
238366
  });
@@ -238104,7 +238412,7 @@ function createChatSessionsDomain(opts) {
238104
238412
  const runtimeId = await currentRuntimeId(body2.aiActorId) ?? "unknown";
238105
238413
  const now = (/* @__PURE__ */ new Date()).toISOString();
238106
238414
  const session = {
238107
- id: (0, import_node_crypto61.randomUUID)(),
238415
+ id: (0, import_node_crypto64.randomUUID)(),
238108
238416
  humanActorId: req.auth.actor,
238109
238417
  // 注:这里**刻意不写 companyId**。新会话要不要带公司归属,按 ws:wo-6d588f8e 的 ADR
238110
238418
  // (每公司 schema、最终移除表内 company 列)统一定,不在本节点自行发明。
@@ -238117,7 +238425,7 @@ function createChatSessionsDomain(opts) {
238117
238425
  // 决策 0030 缝合:绑定是会话的语义属性(工单频道/问题会话给、脑暴省略)——发消息端据它带 workspace,
238118
238426
  // 两个入口页面行为一致。区别于 linkWorkOrder(出处关联,建单脑暴也有、不构成绑定)。
238119
238427
  workspace: body2.workspace ?? null,
238120
- projectId: body2.projectId ?? null,
238428
+ projectId: sessionProjectIdToStore(body2.projectId),
238121
238429
  // 会话级模型覆盖(口径 A):建会话时就带上选择;空串/空白归 null = 沿用旧解析,不写员工全局配置。
238122
238430
  model: normalizeModel(body2.model),
238123
238431
  // ADR-0058:分析会话指向被分析 run;非空即"这是分析会话"(不进主聊天列表、进 trace 分析视图)。
@@ -238186,7 +238494,7 @@ function createChatSessionsDomain(opts) {
238186
238494
  ...body2.runtimeId !== void 0 ? { runtimeId: body2.runtimeId } : {},
238187
238495
  ...Object.prototype.hasOwnProperty.call(body2, "runtimeSessionId") ? { runtimeSessionId: body2.runtimeSessionId } : {},
238188
238496
  ...body2.title !== void 0 ? { title: body2.title } : {},
238189
- ...Object.prototype.hasOwnProperty.call(body2, "projectId") ? { projectId: body2.projectId } : {},
238497
+ ...Object.prototype.hasOwnProperty.call(body2, "projectId") ? { projectId: sessionProjectIdToStore(body2.projectId) } : {},
238190
238498
  // 会话级模型覆盖:显式传 null / 空串 = 清除覆盖(回到旧解析);不传这一项 = 不动它。
238191
238499
  ...Object.prototype.hasOwnProperty.call(body2, "model") ? { model: normalizeModel(body2.model) } : {},
238192
238500
  // 只有显式传了才写——改标题 / 换项目 / 换模型都不是「有新动静」,不该顶掉列表顺序与已读水位。
@@ -238210,7 +238518,7 @@ function createChatSessionsDomain(opts) {
238210
238518
  if (body2.role !== "user" && body2.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
238211
238519
  const role = body2.role;
238212
238520
  const msg = await store2.appendMessage({
238213
- id: (0, import_node_crypto61.randomUUID)(),
238521
+ id: (0, import_node_crypto64.randomUUID)(),
238214
238522
  sessionId: req.params.id,
238215
238523
  role,
238216
238524
  content: role === "user" ? stripInjectedChatContext(body2.content) : body2.content,
@@ -238251,7 +238559,7 @@ function createChatSessionsDomain(opts) {
238251
238559
  const known = key ? cur.find((x2) => x2.clientKey === key) : void 0;
238252
238560
  if (!known && cur.length >= QUEUE_MAX) throw new ApiError(409, "QUEUE_FULL", `\u961F\u5217\u5DF2\u6EE1\uFF08\u4E0A\u9650 ${QUEUE_MAX} \u6761\uFF09`);
238253
238561
  const item = await store.enqueuePendingMessage({
238254
- id: (0, import_node_crypto61.randomUUID)(),
238562
+ id: (0, import_node_crypto64.randomUUID)(),
238255
238563
  chatSessionId: sid,
238256
238564
  text: text5,
238257
238565
  ...attachments.length ? { attachments } : {},
@@ -238568,6 +238876,20 @@ function createChatSessionsDomain(opts) {
238568
238876
  return { status: 200, body: body2 };
238569
238877
  });
238570
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
+ }
238571
238893
  const bridge = opts.workdir?.();
238572
238894
  if (!bridge) throw new ApiError(404, "NOT_FOUND", "\u5DE5\u4F5C\u533A\u6587\u4EF6\u6D4F\u89C8\u672A\u542F\u7528");
238573
238895
  const loc = await locateWorkdir(await storeFor(req), req.auth.companyId, req.params.id, req.auth.actor);
@@ -238604,9 +238926,12 @@ function createChatSessionsDomain(opts) {
238604
238926
  const store2 = await storeFor(req);
238605
238927
  const session = await store2.getSession(req.params.id);
238606
238928
  if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
238607
- const attachmentRows = store2.listSessionAttachmentMessages ? await store2.listSessionAttachmentMessages(session.id) : await store2.listMessages(session.id);
238608
238929
  const blobAccess = opts.resolveBlobs ? await opts.resolveBlobs(req.auth?.companyId) : opts.blobs;
238609
- 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) ?? [];
238610
238935
  let workdirItems = [];
238611
238936
  let materializedCopies = /* @__PURE__ */ new Set();
238612
238937
  let workdirAvailable = false;
@@ -238633,8 +238958,13 @@ function createChatSessionsDomain(opts) {
238633
238958
  }
238634
238959
  }
238635
238960
  }
238636
- 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);
238637
238966
  const body2 = {
238967
+ ...fileIssues.length ? { fileIssues } : {},
238638
238968
  total: items.length,
238639
238969
  items,
238640
238970
  workdirAvailable,
@@ -238702,13 +239032,14 @@ function createChatSessionsDomain(opts) {
238702
239032
  });
238703
239033
  };
238704
239034
  }
238705
- var import_node_crypto61, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS;
239035
+ var import_node_crypto64, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS;
238706
239036
  var init_chat_sessions2 = __esm({
238707
239037
  "../server/src/domains/chat-sessions/index.ts"() {
238708
239038
  "use strict";
238709
- import_node_crypto61 = require("node:crypto");
239039
+ import_node_crypto64 = require("node:crypto");
238710
239040
  init_src();
238711
239041
  init_chat_session();
239042
+ init_persistent_files();
238712
239043
  init_files_view();
238713
239044
  init_router();
238714
239045
  init_collab();
@@ -238723,6 +239054,7 @@ var init_chat_sessions2 = __esm({
238723
239054
  init_company_data_store_router();
238724
239055
  init_workorder_terminal();
238725
239056
  init_session_workdir();
239057
+ init_uncategorized_project();
238726
239058
  init_project_bundle();
238727
239059
  DEFAULT_SNAPSHOT_ITEMS = 400;
238728
239060
  MAX_SNAPSHOT_ITEMS = 1e3;
@@ -240110,7 +240442,7 @@ function busyError(chatSessionId, blockedBy) {
240110
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`;
240111
240443
  }
240112
240444
  function genWebhookToken() {
240113
- return `owt_${(0, import_node_crypto62.randomBytes)(32).toString("base64url")}`;
240445
+ return `owt_${(0, import_node_crypto65.randomBytes)(32).toString("base64url")}`;
240114
240446
  }
240115
240447
  function validateTitleTemplate(tpl) {
240116
240448
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -240166,11 +240498,11 @@ function appendSourceNote(brief, automation, source, atIso) {
240166
240498
  ---
240167
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`;
240168
240500
  }
240169
- var import_node_crypto62, 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;
240170
240502
  var init_service9 = __esm({
240171
240503
  "../server/src/domains/automations/service.ts"() {
240172
240504
  "use strict";
240173
- import_node_crypto62 = require("node:crypto");
240505
+ import_node_crypto65 = require("node:crypto");
240174
240506
  init_src();
240175
240507
  init_registry3();
240176
240508
  init_attribution();
@@ -240984,11 +241316,11 @@ function verifySignature(rawBody, headers, secret) {
240984
241316
  if (!secret) return "not_required";
240985
241317
  const given = header1(headers, "x-hub-signature-256");
240986
241318
  if (!given) return "missing";
240987
- const expected = `sha256=${(0, import_node_crypto63.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
241319
+ const expected = `sha256=${(0, import_node_crypto66.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
240988
241320
  const a = Buffer.from(given);
240989
241321
  const b2 = Buffer.from(expected);
240990
241322
  if (a.length !== b2.length) return "invalid";
240991
- return (0, import_node_crypto63.timingSafeEqual)(a, b2) ? "valid" : "invalid";
241323
+ return (0, import_node_crypto66.timingSafeEqual)(a, b2) ? "valid" : "invalid";
240992
241324
  }
240993
241325
  function eventAllowed(filters, envelope) {
240994
241326
  if (!filters || filters.length === 0) return true;
@@ -241018,11 +241350,11 @@ function splitEvent(envelope) {
241018
241350
  }
241019
241351
  return { eventName, actionCandidates: candidates };
241020
241352
  }
241021
- var import_node_crypto63, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
241353
+ var import_node_crypto66, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
241022
241354
  var init_webhook = __esm({
241023
241355
  "../server/src/domains/automations/webhook.ts"() {
241024
241356
  "use strict";
241025
- import_node_crypto63 = require("node:crypto");
241357
+ import_node_crypto66 = require("node:crypto");
241026
241358
  MAX_BODY_BYTES = 256 * 1024;
241027
241359
  AutomationWebhookService = class {
241028
241360
  store;
@@ -242178,7 +242510,7 @@ function playbooksDomain(opts) {
242178
242510
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
242179
242511
  try {
242180
242512
  await opts.audit?.({
242181
- id: `reg_${(0, import_node_crypto64.randomUUID)()}`,
242513
+ id: `reg_${(0, import_node_crypto67.randomUUID)()}`,
242182
242514
  actor: req.auth.actor,
242183
242515
  kind: "registry_change",
242184
242516
  target: `playbook:${ref2}#${nodeKey}`,
@@ -242195,11 +242527,11 @@ function playbooksDomain(opts) {
242195
242527
  });
242196
242528
  };
242197
242529
  }
242198
- var import_node_crypto64;
242530
+ var import_node_crypto67;
242199
242531
  var init_routes12 = __esm({
242200
242532
  "../server/src/domains/playbooks/routes.ts"() {
242201
242533
  "use strict";
242202
- import_node_crypto64 = require("node:crypto");
242534
+ import_node_crypto67 = require("node:crypto");
242203
242535
  init_router();
242204
242536
  init_registry3();
242205
242537
  init_planner();
@@ -242313,17 +242645,17 @@ function completionFingerprint(workspace, members) {
242313
242645
  workspace,
242314
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 }))
242315
242647
  });
242316
- return (0, import_node_crypto65.createHash)("sha256").update(canonical).digest("hex");
242648
+ return (0, import_node_crypto68.createHash)("sha256").update(canonical).digest("hex");
242317
242649
  }
242318
242650
  function eventConsumptionKey(args) {
242319
242651
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
242320
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto65.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")}`;
242321
242653
  }
242322
- var import_node_crypto65, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
242654
+ var import_node_crypto68, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
242323
242655
  var init_project_event_poller = __esm({
242324
242656
  "../server/src/automations/project-event-poller.ts"() {
242325
242657
  "use strict";
242326
- import_node_crypto65 = require("node:crypto");
242658
+ import_node_crypto68 = require("node:crypto");
242327
242659
  EVENT_KEY_VERSION = "v1";
242328
242660
  EMPTY_METRICS = () => ({
242329
242661
  scanDurationMs: 0,
@@ -242534,7 +242866,7 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
242534
242866
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
242535
242867
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
242536
242868
  }
242537
- const requestId = (0, import_node_crypto66.randomUUID)();
242869
+ const requestId = (0, import_node_crypto69.randomUUID)();
242538
242870
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
242539
242871
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
242540
242872
  return new Promise((resolve10) => {
@@ -242547,11 +242879,11 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
242547
242879
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve10, timer });
242548
242880
  });
242549
242881
  }
242550
- var import_node_crypto66, 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;
242551
242883
  var init_daemon_adapter = __esm({
242552
242884
  "../server/src/daemon-adapter.ts"() {
242553
242885
  "use strict";
242554
- import_node_crypto66 = require("node:crypto");
242886
+ import_node_crypto69 = require("node:crypto");
242555
242887
  init_src();
242556
242888
  STASH_TTL_MS = 5 * 6e4;
242557
242889
  STASH_MAX_FRAMES_PER_ID = 500;
@@ -242901,7 +243233,7 @@ var init_daemon_adapter = __esm({
242901
243233
  async spawn(job) {
242902
243234
  const nodeId = job.binding?.nodeId;
242903
243235
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
242904
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto66.randomUUID)()}`;
243236
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto69.randomUUID)()}`;
242905
243237
  if (job.dispatchId && this.pending.has(dispatchId)) {
242906
243238
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
242907
243239
  }
@@ -243138,7 +243470,7 @@ var init_daemon_adapter = __esm({
243138
243470
  return daemon?.meta.supportsWorkdirWalk === true;
243139
243471
  }
243140
243472
  request(nodeId, buildFrame) {
243141
- const requestId = (0, import_node_crypto66.randomUUID)();
243473
+ const requestId = (0, import_node_crypto69.randomUUID)();
243142
243474
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
243143
243475
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
243144
243476
  return new Promise((resolve10) => {
@@ -243246,13 +243578,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
243246
243578
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
243247
243579
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
243248
243580
  }
243249
- var import_node_crypto67, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
243581
+ var import_node_crypto70, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
243250
243582
  var init_engine = __esm({
243251
243583
  "../server/src/mirror/engine.ts"() {
243252
243584
  "use strict";
243253
- import_node_crypto67 = require("node:crypto");
243585
+ import_node_crypto70 = require("node:crypto");
243254
243586
  init_src();
243255
- sha = (s2) => (0, import_node_crypto67.createHash)("sha256").update(s2, "utf8").digest("hex");
243587
+ sha = (s2) => (0, import_node_crypto70.createHash)("sha256").update(s2, "utf8").digest("hex");
243256
243588
  enc3 = (s2) => new TextEncoder().encode(s2);
243257
243589
  dec = (b2) => new TextDecoder().decode(b2);
243258
243590
  MirrorEngine = class {
@@ -243499,11 +243831,11 @@ function humanExitCause(exit3) {
243499
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`;
243500
243832
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
243501
243833
  }
243502
- var import_node_crypto68, 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;
243503
243835
  var init_worker = __esm({
243504
243836
  "../server/src/coordinator/worker.ts"() {
243505
243837
  "use strict";
243506
- import_node_crypto68 = require("node:crypto");
243838
+ import_node_crypto71 = require("node:crypto");
243507
243839
  init_src5();
243508
243840
  init_identity();
243509
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
@@ -243772,7 +244104,7 @@ ${HIGH_RISK_COMMANDS}`;
243772
244104
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
243773
244105
  }
243774
244106
  evaluationKey(kind, artifactId, evidence) {
243775
- const fingerprint2 = (0, import_node_crypto68.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
244107
+ const fingerprint2 = (0, import_node_crypto71.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
243776
244108
  return `${kind}:${artifactId}:${fingerprint2}`;
243777
244109
  }
243778
244110
  artifactEvidence(id) {
@@ -244151,8 +244483,8 @@ ${ctx.nodeFault}
244151
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)}`);
244152
244484
  }
244153
244485
  }
244154
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto68.randomUUID)();
244155
- const coordinatorRunId = `coordinate:${(0, import_node_crypto68.randomUUID)()}`;
244486
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto71.randomUUID)();
244487
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto71.randomUUID)()}`;
244156
244488
  const taskWithContext = coordinatorContext ? [
244157
244489
  task,
244158
244490
  ``,
@@ -244196,7 +244528,7 @@ ${ctx.nodeFault}
244196
244528
  const startedAtMs = Date.now();
244197
244529
  let trajectoryStarted = false;
244198
244530
  if (this.deps.trajectory) {
244199
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto68.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")]));
244200
244532
  const session = {
244201
244533
  runId: coordinatorRunId,
244202
244534
  runtimeSessionId,
@@ -244401,11 +244733,11 @@ var init_pg_ident = __esm({
244401
244733
  });
244402
244734
 
244403
244735
  // ../storage/src/postgres.ts
244404
- var import_node_crypto69, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
244736
+ var import_node_crypto72, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
244405
244737
  var init_postgres = __esm({
244406
244738
  "../storage/src/postgres.ts"() {
244407
244739
  "use strict";
244408
- import_node_crypto69 = require("node:crypto");
244740
+ import_node_crypto72 = require("node:crypto");
244409
244741
  init_pg_ident();
244410
244742
  init_esm();
244411
244743
  init_src();
@@ -244585,7 +244917,7 @@ var init_postgres = __esm({
244585
244917
  return new _PostgresBlobStore(pool, schema);
244586
244918
  }
244587
244919
  async put(bytes2) {
244588
- const hash2 = (0, import_node_crypto69.createHash)("sha256").update(bytes2).digest("hex");
244920
+ const hash2 = (0, import_node_crypto72.createHash)("sha256").update(bytes2).digest("hex");
244589
244921
  await this.pool.query(
244590
244922
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
244591
244923
  [hash2, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -250858,6 +251190,7 @@ var init_postgres_chat_sessions = __esm({
250858
251190
  await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS visibility text`);
250859
251191
  await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS outputs_fingerprints jsonb`);
250860
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`);
250861
251194
  await pool.query(`
250862
251195
  WITH ranked AS (
250863
251196
  SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
@@ -251129,6 +251462,29 @@ var init_postgres_chat_sessions = __esm({
251129
251462
  );
251130
251463
  return r.rows[0]?.outputs_copies ?? null;
251131
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
+ }
251132
251488
  /**
251133
251489
  * 按路径**合并**写入:同一路径新副本覆盖旧副本,其余路径保留。合并在一条 UPDATE 里用 jsonb `||` 完成,
251134
251490
  * 不是读出来改完再整份写回——两次收尾的复制前后脚落库时,后写的不会把先写的吞掉。
@@ -253212,11 +253568,11 @@ var init_postgres_delegations = __esm({
253212
253568
  });
253213
253569
 
253214
253570
  // ../storage/src/postgres-nodes.ts
253215
- var import_node_crypto70, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToRuntime;
253571
+ var import_node_crypto73, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToRuntime;
253216
253572
  var init_postgres_nodes = __esm({
253217
253573
  "../storage/src/postgres-nodes.ts"() {
253218
253574
  "use strict";
253219
- import_node_crypto70 = require("node:crypto");
253575
+ import_node_crypto73 = require("node:crypto");
253220
253576
  init_pg_ident();
253221
253577
  init_esm();
253222
253578
  ident13 = (s2) => {
@@ -253433,7 +253789,7 @@ var init_postgres_nodes = __esm({
253433
253789
  return store;
253434
253790
  }
253435
253791
  issue(nodeId) {
253436
- const token = `ont_${(0, import_node_crypto70.randomBytes)(24).toString("base64url")}`;
253792
+ const token = `ont_${(0, import_node_crypto73.randomBytes)(24).toString("base64url")}`;
253437
253793
  this.cache.set(token, nodeId);
253438
253794
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
253439
253795
  return token;
@@ -253699,11 +254055,11 @@ var init_postgres_type_registry = __esm({
253699
254055
  });
253700
254056
 
253701
254057
  // ../storage/src/postgres-actor-memory.ts
253702
- var import_node_crypto71, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
254058
+ var import_node_crypto74, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
253703
254059
  var init_postgres_actor_memory = __esm({
253704
254060
  "../storage/src/postgres-actor-memory.ts"() {
253705
254061
  "use strict";
253706
- import_node_crypto71 = require("node:crypto");
254062
+ import_node_crypto74 = require("node:crypto");
253707
254063
  init_pg_ident();
253708
254064
  init_src();
253709
254065
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -253913,7 +254269,7 @@ var init_postgres_actor_memory = __esm({
253913
254269
  }
253914
254270
  async write(input, now) {
253915
254271
  if (input.memId === void 0) {
253916
- const memId = `mem:${(0, import_node_crypto71.randomUUID)()}`;
254272
+ const memId = `mem:${(0, import_node_crypto74.randomUUID)()}`;
253917
254273
  const r2 = await this.pool.query(
253918
254274
  `INSERT INTO ${this.s}.actor_memories
253919
254275
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -255360,14 +255716,14 @@ function companySchemaName(companyId) {
255360
255716
  if (typeof companyId !== "string" || !companyId.trim()) {
255361
255717
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
255362
255718
  }
255363
- const hex = (0, import_node_crypto72.createHash)("sha256").update(companyId, "utf8").digest("hex");
255719
+ const hex = (0, import_node_crypto75.createHash)("sha256").update(companyId, "utf8").digest("hex");
255364
255720
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
255365
255721
  }
255366
- var import_node_crypto72, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
255722
+ var import_node_crypto75, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
255367
255723
  var init_tenant_schema_mapping = __esm({
255368
255724
  "../storage/src/tenant-schema-mapping.ts"() {
255369
255725
  "use strict";
255370
- import_node_crypto72 = require("node:crypto");
255726
+ import_node_crypto75 = require("node:crypto");
255371
255727
  init_esm();
255372
255728
  init_pg_ident();
255373
255729
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -255441,7 +255797,7 @@ var init_tenant_schema_mapping = __esm({
255441
255797
 
255442
255798
  // ../storage/src/company-data-plane-schema.ts
255443
255799
  function canonicalTypeDefHash(def) {
255444
- return `sha256:${(0, import_node_crypto73.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")}`;
255445
255801
  }
255446
255802
  function defaultArtifactTypeTemplate() {
255447
255803
  return [
@@ -255623,11 +255979,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
255623
255979
  }
255624
255980
  return { inserted };
255625
255981
  }
255626
- var import_node_crypto73, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
255982
+ var import_node_crypto76, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
255627
255983
  var init_company_data_plane_schema = __esm({
255628
255984
  "../storage/src/company-data-plane-schema.ts"() {
255629
255985
  "use strict";
255630
- import_node_crypto73 = require("node:crypto");
255986
+ import_node_crypto76 = require("node:crypto");
255631
255987
  init_esm();
255632
255988
  init_pg_ident();
255633
255989
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -255707,11 +256063,11 @@ function rowToFile2(row) {
255707
256063
  updatedAt: new Date(row.updated_at).toISOString()
255708
256064
  };
255709
256065
  }
255710
- var import_node_crypto74, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
256066
+ var import_node_crypto77, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
255711
256067
  var init_company_owned_registry_store = __esm({
255712
256068
  "../storage/src/company-owned-registry-store.ts"() {
255713
256069
  "use strict";
255714
- import_node_crypto74 = require("node:crypto");
256070
+ import_node_crypto77 = require("node:crypto");
255715
256071
  init_esm();
255716
256072
  init_src();
255717
256073
  init_pg_ident();
@@ -256097,7 +256453,7 @@ var init_company_owned_registry_store = __esm({
256097
256453
  enabled: skill.enabled,
256098
256454
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
256099
256455
  });
256100
- return (0, import_node_crypto74.createHash)("sha256").update(body2).digest("hex");
256456
+ return (0, import_node_crypto77.createHash)("sha256").update(body2).digest("hex");
256101
256457
  }
256102
256458
  };
256103
256459
  }
@@ -258694,7 +259050,7 @@ function sourceSetConservation(input) {
258694
259050
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
258695
259051
  }
258696
259052
  function payloadChecksum(value2) {
258697
- return (0, import_node_crypto75.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
259053
+ return (0, import_node_crypto78.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
258698
259054
  }
258699
259055
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
258700
259056
  const cs = quoteIdent2(companySchema);
@@ -259082,11 +259438,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
259082
259438
  sequences
259083
259439
  };
259084
259440
  }
259085
- var import_node_crypto75, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
259441
+ var import_node_crypto78, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
259086
259442
  var init_tenant_migration_operator = __esm({
259087
259443
  "../storage/src/tenant-migration-operator.ts"() {
259088
259444
  "use strict";
259089
- import_node_crypto75 = require("node:crypto");
259445
+ import_node_crypto78 = require("node:crypto");
259090
259446
  init_esm();
259091
259447
  init_pg_ident();
259092
259448
  init_tenancy_attribution();
@@ -259318,8 +259674,8 @@ var init_tenant_new_company_provision = __esm({
259318
259674
  // ../storage/src/knowledge-snapshot-crypto.ts
259319
259675
  function sealSnapshot(snapshot, key) {
259320
259676
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
259321
- const iv = (0, import_node_crypto76.randomBytes)(12);
259322
- const cipher = (0, import_node_crypto76.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);
259323
259679
  cipher.setAAD(aad2(snapshot));
259324
259680
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
259325
259681
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -259329,7 +259685,7 @@ function openSnapshot(snapshot, key) {
259329
259685
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
259330
259686
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
259331
259687
  const [iv, tag, data] = packed;
259332
- const decipher = (0, import_node_crypto76.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"));
259333
259689
  decipher.setAAD(aad2(snapshot));
259334
259690
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
259335
259691
  return {
@@ -259337,11 +259693,11 @@ function openSnapshot(snapshot, key) {
259337
259693
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
259338
259694
  };
259339
259695
  }
259340
- var import_node_crypto76, PREFIX3, bytes, aad2;
259696
+ var import_node_crypto79, PREFIX3, bytes, aad2;
259341
259697
  var init_knowledge_snapshot_crypto = __esm({
259342
259698
  "../storage/src/knowledge-snapshot-crypto.ts"() {
259343
259699
  "use strict";
259344
- import_node_crypto76 = require("node:crypto");
259700
+ import_node_crypto79 = require("node:crypto");
259345
259701
  PREFIX3 = "oasis-knowledge:v1:";
259346
259702
  bytes = (key) => {
259347
259703
  const value2 = Buffer.from(key);
@@ -261371,6 +261727,7 @@ var init_src11 = __esm({
261371
261727
  init_chat_recovery();
261372
261728
  init_chat_system_message();
261373
261729
  init_chat_turn_gate();
261730
+ init_chat_assistant_turn();
261374
261731
  init_chat_workorder_files();
261375
261732
  init_chat_broadcast();
261376
261733
  init_workorder_terminal();
@@ -261453,6 +261810,7 @@ var init_src11 = __esm({
261453
261810
  init_run_settlement_wiring();
261454
261811
  init_deadline_clock();
261455
261812
  init_read_model_clock();
261813
+ init_file_restore();
261456
261814
  }
261457
261815
  });
261458
261816
 
@@ -261588,20 +261946,20 @@ var fs45 = __toESM(require("node:fs"));
261588
261946
  var os14 = __toESM(require("node:os"));
261589
261947
  var path40 = __toESM(require("node:path"));
261590
261948
  var import_node_child_process23 = require("node:child_process");
261591
- var import_node_crypto83 = require("node:crypto");
261949
+ var import_node_crypto86 = require("node:crypto");
261592
261950
 
261593
261951
  // ../cli/src/cli.ts
261594
261952
  var fs40 = __toESM(require("node:fs"), 1);
261595
261953
  var os12 = __toESM(require("node:os"), 1);
261596
261954
  var path35 = __toESM(require("node:path"), 1);
261597
- var import_node_crypto81 = require("node:crypto");
261955
+ var import_node_crypto84 = require("node:crypto");
261598
261956
 
261599
261957
  // ../cli/src/serve.ts
261600
261958
  var fs34 = __toESM(require("node:fs"), 1);
261601
261959
  var os8 = __toESM(require("node:os"), 1);
261602
261960
  var path29 = __toESM(require("node:path"), 1);
261603
261961
  var import_node_child_process18 = require("node:child_process");
261604
- var import_node_crypto77 = require("node:crypto");
261962
+ var import_node_crypto80 = require("node:crypto");
261605
261963
  var import_node_url7 = require("node:url");
261606
261964
 
261607
261965
  // ../cli/src/artifact-discovery-source.ts
@@ -262347,7 +262705,7 @@ init_src9();
262347
262705
  init_src();
262348
262706
  init_trajectory();
262349
262707
  function deterministicUuid2(seed) {
262350
- const h = (0, import_node_crypto77.createHash)("sha256").update(seed).digest();
262708
+ const h = (0, import_node_crypto80.createHash)("sha256").update(seed).digest();
262351
262709
  const b2 = Buffer.from(h.subarray(0, 16));
262352
262710
  b2[6] = b2[6] & 15 | 80;
262353
262711
  b2[8] = b2[8] & 63 | 128;
@@ -262547,7 +262905,7 @@ function oasisWrapperScript() {
262547
262905
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
262548
262906
  const tsx = path29.join(repoRoot, "node_modules/.bin/tsx");
262549
262907
  const main = path29.join(repoRoot, "packages/cli/src/main.ts");
262550
- const key = (0, import_node_crypto77.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);
262551
262909
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
262552
262910
  const stable = path29.join(os8.tmpdir(), `oasis-cli-wrapper-${uid}-${key}`);
262553
262911
  let dir;
@@ -262928,7 +263286,7 @@ async function startServe(opts) {
262928
263286
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
262929
263287
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
262930
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" });
262931
- const requestId = (0, import_node_crypto77.randomUUID)();
263289
+ const requestId = (0, import_node_crypto80.randomUUID)();
262932
263290
  const task = new Promise((resolve10) => {
262933
263291
  const timer = setTimeout(() => {
262934
263292
  modelRefreshWaiters.delete(requestId);
@@ -263199,6 +263557,8 @@ async function startServe(opts) {
263199
263557
  listChatFiles: async (companyId, projectId2) => {
263200
263558
  const chat = await chatStoreFor(companyId);
263201
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);
263202
263562
  return collectProjectChatFiles(chat, projectId2, async (ref2) => {
263203
263563
  for (const cabinet of [assets, facts]) {
263204
263564
  try {
@@ -263209,6 +263569,7 @@ async function startServe(opts) {
263209
263569
  }
263210
263570
  return null;
263211
263571
  }, {
263572
+ workOrderIds,
263212
263573
  // 委派子会话不进项目(ADR 0555:它的 outputs/ 是交给协调者的)。闭包在请求时才读
263213
263574
  // `delegationStoreFor`,避开它在下方声明的 TDZ。
263214
263575
  isDelegationChild: async (sessionId) => {
@@ -263680,7 +264041,7 @@ async function startServe(opts) {
263680
264041
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
263681
264042
  if (buf || session.runId) {
263682
264043
  await turnStore.appendMessage({
263683
- id: (0, import_node_crypto77.randomUUID)(),
264044
+ id: (0, import_node_crypto80.randomUUID)(),
263684
264045
  sessionId: chatSessionId,
263685
264046
  role: "assistant",
263686
264047
  content: buf,
@@ -263726,7 +264087,7 @@ async function startServe(opts) {
263726
264087
  chatTargets: {
263727
264088
  inspect: inspectChatTarget,
263728
264089
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
263729
- const sessionId = (0, import_node_crypto77.randomUUID)();
264090
+ const sessionId = (0, import_node_crypto80.randomUUID)();
263730
264091
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
263731
264092
  const binding = await registryStore.getBinding(agentId).catch(() => null);
263732
264093
  await (await chatStoreFor(companyId)).createSession({
@@ -264393,6 +264754,16 @@ async function startServe(opts) {
264393
264754
  delegationStores.set(id, store);
264394
264755
  return store;
264395
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
+ };
264396
264767
  const outputCopyScheduler = createOutputCopyScheduler({
264397
264768
  putBlob: (bytes2) => assets.put(bytes2),
264398
264769
  isDelegationChild: async (companyId, sessionId) => {
@@ -264666,6 +265037,41 @@ async function startServe(opts) {
264666
265037
  const listCompanyMembers = (companyId) => controlPlaneStore.listMembers(companyId ?? defaultCompanyId);
264667
265038
  let notificationLedgerWorker;
264668
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
+ };
264669
265075
  const server = await startOasisServer({
264670
265076
  deployTargets,
264671
265077
  onInterventionDraftResolved: (e) => notificationLedgerWorker?.draftResolved(e.companyId ?? defaultCompanyId, e.draftId, { by: e.by, approved: e.approved }),
@@ -264771,7 +265177,7 @@ async function startServe(opts) {
264771
265177
  }));
264772
265178
  }
264773
265179
  const limits = { wallClockMs: wallClockForKind("planner") };
264774
- const artifactId = `artifact:planner:${(0, import_node_crypto77.randomUUID)()}`;
265180
+ const artifactId = `artifact:planner:${(0, import_node_crypto80.randomUUID)()}`;
264775
265181
  const handle = await chatRemoteAdapter.spawn({
264776
265182
  actor: planner.id,
264777
265183
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -265003,6 +265409,7 @@ async function startServe(opts) {
265003
265409
  // (于是每一行的「大小」都空掉)。顺序与 `/api/blobs/:hash` 那条读路同源。
265004
265410
  // 写回也落资产柜,与附件上传同一个柜,否则新旧两版会分居两处。
265005
265411
  blobs: {
265412
+ get: (ref2) => readChatFileBlob(void 0, ref2),
265006
265413
  meta: async (blobRef) => {
265007
265414
  for (const cabinet of [assets, blobs]) {
265008
265415
  try {
@@ -265021,6 +265428,7 @@ async function startServe(opts) {
265021
265428
  resolveBlobs: async (cid) => {
265022
265429
  const factCabinet = await blobStoreFor(cid);
265023
265430
  return {
265431
+ get: (ref2) => readChatFileBlob(cid, ref2),
265024
265432
  meta: async (blobRef) => {
265025
265433
  for (const cabinet of [assets, factCabinet]) {
265026
265434
  try {
@@ -265108,40 +265516,8 @@ async function startServe(opts) {
265108
265516
  // 三个方法的组合规则住在 `createDelegationWorkdirPort`(`@oasis/server`)——评审
265109
265517
  // wo:acfee725 blocker ② 回归防线:**serve.ts 与单测走同一处**,那样「装配层退回全局 binding」
265110
265518
  // 这类事故在单测里就能被复现。绝不许在这里再写一份。
265111
- /**
265112
- * 会话产出扫描(2026-09-11 产出卡):轮次收尾扫这条会话的 `outputs/`,交出「这一轮变了哪些」。
265113
- *
265114
- * 复用 `delegationWorkdirLocator`(与派发、回流、chat 域**同一个 binding resolver 实例**)——
265115
- * 委派回程读的就是子会话的 `outputs/`,主会话这条只是把同一套读口用到了自己身上。
265116
- *
265117
- * 三处 fail-closed,都返回 `null`(= 这一轮没有产出卡),一处都不猜:
265118
- * · 桥还没建好 / 会话没绑定 / 节点离线 → 拿不到工作区;
265119
- * · store 没有指纹方法(老 store / 文件态装配)→ 没地方记「上一轮长什么样」,
265120
- * 硬扫会把整个 `outputs/` 每轮都当成新增,一口气刷几十张卡;
265121
- * · 解析不出公司 → **绝不回落默认公司**(否则会去别家公司的库里读写指纹)。
265122
- */
265123
- sessionOutputsScan: async ({ chatSessionId, companyId }) => {
265124
- const bridge = workdirBridge;
265125
- if (!bridge) return null;
265126
- const store = chatStoreRouter ? await chatStoreFor(companyId).catch(() => null) : chatSessionStore;
265127
- const read = store?.readOutputFingerprints?.bind(store);
265128
- const write = store?.writeOutputFingerprints?.bind(store);
265129
- if (!read || !write) return null;
265130
- const located = await locateSessionWorkdir(delegationWorkdirLocator, {
265131
- chatSessionId,
265132
- ...companyId !== void 0 ? { companyId } : {}
265133
- });
265134
- if (!located.ok) return null;
265135
- const diff = await settleSessionOutputs({
265136
- bridge,
265137
- loc: located.loc,
265138
- sessionId: chatSessionId,
265139
- readFingerprints: read,
265140
- writeFingerprints: write
265141
- }).catch(() => null);
265142
- if (diff && store) void outputCopyScheduler.schedule({ store, bridge, loc: located.loc, chatSessionId, companyId, fingerprints: diff.fingerprints });
265143
- return diff?.changed ?? null;
265144
- },
265519
+ // 会话产出扫描:实现见上方 `sessionOutputsScan`。serve 重启前后的恢复收尾用的是同一个函数。
265520
+ sessionOutputsScan,
265145
265521
  delegationWorkdir: createDelegationWorkdirPort({
265146
265522
  locator: delegationWorkdirLocator,
265147
265523
  // 桥在 hub 建好后(本函数尾部)才赋值 → getter 每次调用时读最新值;早于此处的构造时 `workdirBridge`
@@ -265673,10 +266049,10 @@ async function startServe(opts) {
265673
266049
  if (isDeletedActor(dispatchTarget)) {
265674
266050
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
265675
266051
  }
265676
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid2(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto77.randomUUID)());
266052
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid2(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto80.randomUUID)());
265677
266053
  const resumeRuntimeSession = Boolean(sessionId);
265678
- const traceRunId = requestDispatchId ? `chat-run:${deterministicUuid2(`chat-trace:${requestDispatchId}`)}` : `chat-run:${(0, import_node_crypto77.randomUUID)()}`;
265679
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid2(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto77.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)()}`;
265680
266056
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
265681
266057
  if (stableDispatch && !stableDispatch.shouldDispatch) {
265682
266058
  return {
@@ -265740,10 +266116,7 @@ async function startServe(opts) {
265740
266116
  }
265741
266117
  }
265742
266118
  }
265743
- const turnChatStore = chatSessionId ? await chatStoreFor(runCompanyId).catch((err) => {
265744
- 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)}`);
265745
- return null;
265746
- }) : null;
266119
+ const turnChatStore = chatSessionId ? await chatStoreFor(runCompanyId) : null;
265747
266120
  const analyzedRunId = chatSessionId ? (await turnChatStore?.getSession(chatSessionId).catch(() => null))?.analyzedRunId ?? void 0 : void 0;
265748
266121
  const actorConfigVersion = await actorService.latestConfig(actorId).then((c) => c?.version ?? null).catch(() => null);
265749
266122
  const candidateTraceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -265774,6 +266147,22 @@ async function startServe(opts) {
265774
266147
  ...analyzedRunId ? { analyzedRunId } : {}
265775
266148
  }
265776
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"));
265777
266166
  const stableTraceWasPresent = Boolean(stableDispatch);
265778
266167
  if (requestDispatchId && !stableDispatch) {
265779
266168
  stableDispatch = await resolveStableChatDispatch({
@@ -265796,13 +266185,20 @@ async function startServe(opts) {
265796
266185
  binding = { actorId, ...stableDispatch.target, status: "active" };
265797
266186
  node2 = await requireDispatchNode(binding);
265798
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;
265799
266200
  }
265800
266201
  }
265801
- const priorChatSession = chatSessionId ? await turnChatStore?.getSession(chatSessionId).catch(() => null) : null;
265802
- const freshChatWorkdir = chatSessionId ? chatNodeChanged(
265803
- { nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
265804
- { nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
265805
- ) : false;
265806
266202
  const existingTrace = stableDispatch?.run ?? null;
265807
266203
  const existingEvents = existingTrace ? await (await traceStoreFor(runCompanyId)).listEvents(traceRunId, {}).catch(() => []) : [];
265808
266204
  const traceStartedAt = existingTrace?.startedAt ?? candidateTraceStartedAt;
@@ -265848,18 +266244,13 @@ async function startServe(opts) {
265848
266244
  const chatResolvedModel = sessionModelOverride ?? actorCtx?.config?.model ?? void 0;
265849
266245
  const chatModel = chatResolvedModel ?? await registryStore.getRuntimeConfig(`runtime:${binding.nodeId}:${binding.runtimeKind}`).then((c) => c?.model).catch(() => void 0) ?? void 0;
265850
266246
  const chatModelSource = sessionModelOverride ? "session_override" : chatResolvedModel ? "actor_config" : chatModel ? "runtime_default" : null;
265851
- const usedRel = /* @__PURE__ */ new Set();
265852
- const preparedAttachments = await buildChatAttachmentFiles(attachments, (ref2) => assets.get(ref2), usedRel);
265853
- for (const miss of preparedAttachments.missing) {
265854
- console.warn(`[chat] \u9644\u4EF6 blob ${miss.blobRef} \u8BFB\u53D6\u5931\u8D25: ${miss.error}`);
265855
- }
265856
- const attachmentFiles = { ...preparedAttachments.files };
266247
+ const attachmentFiles = { ...preparedAttachments.files, ...restore ? { "SESSION_FILES.md": restore.index } : {} };
265857
266248
  const attachmentBinary = { ...preparedAttachments.binaryFiles };
265858
266249
  const attachmentPaths = [...preparedAttachments.paths];
265859
266250
  let turnProject = priorChatSession ? { state: "none" } : { state: "unknown" };
265860
266251
  if (priorChatSession?.projectId) {
265861
266252
  const projectId2 = priorChatSession.projectId;
265862
- 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) => {
265863
266254
  console.warn(`[chat] \u8BFB\u9879\u76EE ${projectId2} \u5931\u8D25\uFF08\u672C\u8F6E\u4E0D\u5199\u9879\u76EE\u6BB5\uFF09: ${String(err)}`);
265864
266255
  return void 0;
265865
266256
  });
@@ -265885,7 +266276,7 @@ async function startServe(opts) {
265885
266276
  ...actorCtx?.config?.prompt ? { personaPrompt: actorCtx.config.prompt } : {},
265886
266277
  ...extraSystemPrompt ? { expertSystemPrompt: extraSystemPrompt } : {}
265887
266278
  });
265888
- 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"] : []];
265889
266280
  const chatMemoryEnabled = !isolatedKnowledgeTurn && actorCtx?.memoryEnabled !== false;
265890
266281
  const chatMemorySvc = chatMemoryEnabled ? await actorMemoryFor(runCompanyId).catch(() => void 0) : void 0;
265891
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(() => []) : [];
@@ -265930,7 +266321,7 @@ async function startServe(opts) {
265930
266321
  actor: actorId,
265931
266322
  actorToken: issueSessionToken(actorId, { artifactId, action: "chat", limits, binding, sessionId: runtimeSessionId, companyId: runCompanyId, ...requestDispatchId ? { dispatchId: requestDispatchId } : {}, ...chatSessionId !== void 0 ? { chatSessionId } : {} }),
265932
266323
  artifactId,
265933
- bundle: { files, ...Object.keys(attachmentBinary).length ? { binaryFiles: attachmentBinary } : {} },
266324
+ bundle: { files, ...Object.keys(attachmentBinary).length ? { binaryFiles: attachmentBinary } : {}, ...restore ? { restoreFiles: restore.restoreFiles } : {} },
265934
266325
  // 人设在最前,固定规则接在它后面:助理那侧是分流正文(§6.5.3,只有普通脑暴那一支有),
265935
266326
  // 专家那侧是 `EXPERT_DELEGATION_SYSTEM_PROMPT`(§6.5.7)。**三者都走 systemPrompt**——
265936
266327
  // 放进 TASK.md 会随 `--resume` 每轮重复、O(N) 累积(§6.1.5)。
@@ -266063,10 +266454,11 @@ async function startServe(opts) {
266063
266454
  }
266064
266455
  });
266065
266456
  const failed = !isHealthySessionExit(info);
266066
- if (!failed) resolve10();
266067
- else {
266068
- reject(buildChatExitError(info));
266069
- }
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));
266070
266462
  });
266071
266463
  });
266072
266464
  return {
@@ -266255,6 +266647,8 @@ async function startServe(opts) {
266255
266647
  onCompanyError: chatRecoveryCompanyWarn(opts2.label),
266256
266648
  // 重挂的那一轮收口时当场外报,与清扫拍同一个接收方(见 `notifyChatTurnSettled`)。
266257
266649
  onTurnSettled: (info) => notifyChatTurnSettled(info),
266650
+ // 重挂 / 已收口两条路收尾时出产出卡(公司由扇出层按行绑)。
266651
+ sessionOutputsScan,
266258
266652
  liveChat,
266259
266653
  trace: runRoutedTrace,
266260
266654
  traceHealth,
@@ -266410,6 +266804,7 @@ async function startServe(opts) {
266410
266804
  listCompanies: chatRecoveryCompanies,
266411
266805
  storesFor: chatRecoveryFor,
266412
266806
  onCompanyError: chatRecoveryCompanyWarn("exit \u5BF9\u8D26"),
266807
+ sessionOutputsScan,
266413
266808
  drainStash: (id) => chatRemoteAdapter?.drainStash(id) ?? { frames: [] },
266414
266809
  log: (m2) => console.log(m2)
266415
266810
  });
@@ -266478,6 +266873,7 @@ async function startServe(opts) {
266478
266873
  listCompanies: chatRecoveryCompanies,
266479
266874
  storesFor: chatRecoveryFor,
266480
266875
  onCompanyError: chatRecoveryCompanyWarn("chat-sweep"),
266876
+ sessionOutputsScan,
266481
266877
  getRun: (id) => fanOutTrace.getRun(id),
266482
266878
  ownedRunId: (sid) => liveChat.runFor(sid),
266483
266879
  now: Date.now(),
@@ -266510,7 +266906,12 @@ async function startServe(opts) {
266510
266906
  storesFor: async (companyId) => {
266511
266907
  const turns = await chatTurnsFor(companyId);
266512
266908
  const messages = await chatStoreFor(companyId);
266513
- return { turns, ...assistantRowRecovery(messages) };
266909
+ return {
266910
+ turns,
266911
+ ...assistantRowRecovery(messages, {
266912
+ outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId)
266913
+ })
266914
+ };
266514
266915
  },
266515
266916
  getRun: (id) => fanOutTrace.getRun(id),
266516
266917
  // 本进程在飞的轮不动——它自己的收尾路径会写终态(liveChat 只认人工/渠道那条,自动化轮
@@ -266803,7 +267204,7 @@ async function startServe(opts) {
266803
267204
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
266804
267205
  return;
266805
267206
  }
266806
- const ledgerId = `dispatch:${(0, import_node_crypto77.randomUUID)()}`;
267207
+ const ledgerId = `dispatch:${(0, import_node_crypto80.randomUUID)()}`;
266807
267208
  try {
266808
267209
  await (await dispatchLedgerFor(companyId)).insertOpen({
266809
267210
  id: ledgerId,
@@ -266888,7 +267289,7 @@ async function startServe(opts) {
266888
267289
  const kernelModel = newKernel.model;
266889
267290
  const art = kernelModel.artifacts.get(nodeId);
266890
267291
  const revWid = art?.workspace ?? "";
266891
- const ledgerId = `dispatch:${(0, import_node_crypto77.randomUUID)()}`;
267292
+ const ledgerId = `dispatch:${(0, import_node_crypto80.randomUUID)()}`;
266892
267293
  try {
266893
267294
  await (await dispatchLedgerFor(companyId)).insertOpen({
266894
267295
  id: ledgerId,
@@ -267266,7 +267667,7 @@ async function startServe(opts) {
267266
267667
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
267267
267668
  try {
267268
267669
  const governance = server.knowledgeGovernance;
267269
- const taskFingerprint = (0, import_node_crypto77.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");
267270
267671
  const requestContext = {
267271
267672
  organizationId: companyId,
267272
267673
  actorId,
@@ -268904,7 +269305,9 @@ async function runSession(dispatchId, job, deps) {
268904
269305
  deps.sink.stream({ type: "session_live_event", dispatchId, event });
268905
269306
  normalizer.onLiveEvent(event);
268906
269307
  });
269308
+ let restoreFailedBeforeStart = false;
268907
269309
  handle.onExit((info) => {
269310
+ if (info.fileRestoreError) restoreFailedBeforeStart = true;
268908
269311
  normalizer.turnFinished(isHealthySessionExit(info) ? "completed" : "failed");
268909
269312
  log2("[node-cli]", ` \u25A0 session_exited id=${handle.id} code=${info.code ?? "killed"} total=${Date.now() - t0}ms`);
268910
269313
  if (info.transcriptRef) log2("[node-cli]", ` transcript: ${info.transcriptRef}`);
@@ -268914,7 +269317,7 @@ async function runSession(dispatchId, job, deps) {
268914
269317
  if (cancelledBeforeStart) {
268915
269318
  log2("[node-cli]", ` dispatch ${dispatchId} \u5728 setup \u671F\u95F4\u5DF2\u88AB fencing\uFF0Cspawn \u540E\u7ACB\u5373 kill`);
268916
269319
  await handle.kill();
268917
- } else {
269320
+ } else if (!restoreFailedBeforeStart) {
268918
269321
  log2("[node-cli]", ` \u2192 session_started id=${handle.id} setup=${Date.now() - t0}ms`);
268919
269322
  deps.sink.keyFrame({ type: "session_started", dispatchId, sessionId: handle.id, jobArtifactId: job.artifactId });
268920
269323
  }
@@ -268922,7 +269325,12 @@ async function runSession(dispatchId, job, deps) {
268922
269325
  deps.onFailure?.();
268923
269326
  log2("[node-cli]", ` spawn failed: ${err}`);
268924
269327
  const errorMessage = String(err?.stack ?? err).slice(0, 8e3);
268925
- 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
+ } });
268926
269334
  }
268927
269335
  }
268928
269336
 
@@ -269213,8 +269621,9 @@ var SessionProcessState = class {
269213
269621
 
269214
269622
  // ../cli/src/daemon/ws-client.ts
269215
269623
  init_wrapper();
269624
+ init_src();
269216
269625
  var import_node_os9 = require("node:os");
269217
- var import_node_crypto78 = require("node:crypto");
269626
+ var import_node_crypto81 = require("node:crypto");
269218
269627
  init_src8();
269219
269628
  init_src6();
269220
269629
 
@@ -269495,10 +269904,15 @@ async function handleWorkdirRead(req) {
269495
269904
  const fresh = await fh.stat().catch(() => null);
269496
269905
  return { ok: false, code: "TOO_LARGE", size: Math.max(fresh?.size ?? 0, bytesRead), absPath: real };
269497
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
+ }
269498
269911
  const bytes2 = buf.subarray(0, bytesRead);
269499
269912
  return {
269500
269913
  ok: true,
269501
269914
  contentBase64: bytes2.toString("base64"),
269915
+ mtime: st.mtime.toISOString(),
269502
269916
  contentType: contentTypeFor(real, bytes2),
269503
269917
  size: bytes2.length
269504
269918
  // 以实际读到的字节数为准,不用任何可能已过时的 stat size
@@ -269751,6 +270165,7 @@ var DaemonWsClient = class {
269751
270165
  connect() {
269752
270166
  if (this.stopped) return;
269753
270167
  const ws = new import_websocket.default(this.serverUrl, {
270168
+ maxPayload: 4 * Math.ceil(FILE_CONTENT_MAX_BYTES / 3) + 64 * 1024,
269754
270169
  headers: {
269755
270170
  "x-daemon-id": this.daemonId,
269756
270171
  // 经公网网关时鉴权靠它(对齐 multica:Authorization 头反代天然透传);内网直连可空
@@ -269986,7 +270401,7 @@ var DaemonWsClient = class {
269986
270401
  */
269987
270402
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
269988
270403
  if (artifactIds.length === 0) return {};
269989
- const requestId = (0, import_node_crypto78.randomUUID)();
270404
+ const requestId = (0, import_node_crypto81.randomUUID)();
269990
270405
  return new Promise((resolve10, reject) => {
269991
270406
  const timer = setTimeout(() => {
269992
270407
  this.gcPending.delete(requestId);
@@ -270128,6 +270543,7 @@ var DaemonWsClient = class {
270128
270543
  supportsNormalizedEvents: true,
270129
270544
  // ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
270130
270545
  supportsWorkdirWalk: true,
270546
+ supportsFileRestore: true,
270131
270547
  ...activeSessions.length > 0 ? { activeSessions } : {},
270132
270548
  // 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
270133
270549
  pendingDispatchIds: [...this.pendingDispatchIds]
@@ -270388,7 +270804,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
270388
270804
  // ../cli/src/daemon/machine-id.ts
270389
270805
  var import_node_child_process21 = require("node:child_process");
270390
270806
  var import_node_fs22 = require("node:fs");
270391
- var import_node_crypto79 = require("node:crypto");
270807
+ var import_node_crypto82 = require("node:crypto");
270392
270808
  var import_node_os12 = require("node:os");
270393
270809
  function linuxMachineId() {
270394
270810
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -270454,7 +270870,7 @@ var defaultSources = {
270454
270870
  function resolveNodeId(sources = {}) {
270455
270871
  const s2 = { ...defaultSources, ...sources };
270456
270872
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
270457
- const digest = (0, import_node_crypto79.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);
270458
270874
  return `node-${digest}`;
270459
270875
  }
270460
270876
 
@@ -271465,7 +271881,7 @@ var COMMAND_DECLS = {
271465
271881
  flags: [
271466
271882
  { name: "name", desc: "\u9879\u76EE\u540D", required: true },
271467
271883
  { name: "goal", desc: "\u9879\u76EE\u76EE\u6807" },
271468
- { name: "slug", desc: "URL slug" },
271884
+ { name: "slug", desc: "\u81EA\u5B9A\u4E49\u9879\u76EE id\uFF08\u53EF\u9009\uFF09\u3002\u4E0D\u7ED9\u5219\u968F\u673A\u751F\u6210 proj_xxxxxxxx\uFF1B\u7ED9\u4E86\u4E14\u8BE5 id \u5DF2\u5B58\u5728\uFF0C\u8FD4\u56DE\u7684\u662F\u5DF2\u6709\u9879\u76EE\uFF08\u4F1A\u63D0\u793A\uFF09" },
271469
271885
  { name: "members", desc: "\u6210\u5458 JSON \u6570\u7EC4" }
271470
271886
  ],
271471
271887
  examples: ['oasis project create --name "My Project" --goal "\u6784\u5EFA\u534F\u4F5C\u5E73\u53F0"']
@@ -272225,13 +272641,13 @@ function ownFlagsFromDecl(command) {
272225
272641
  }
272226
272642
 
272227
272643
  // ../cli/src/connector-effect-runner.ts
272228
- var import_node_crypto80 = require("node:crypto");
272644
+ var import_node_crypto83 = require("node:crypto");
272229
272645
  var import_node_child_process22 = require("node:child_process");
272230
272646
  init_src8();
272231
272647
  function stableKey(effect) {
272232
272648
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
272233
272649
  const canonical = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
272234
- return `sha256:${(0, import_node_crypto80.createHash)("sha256").update(canonical).digest("hex")}`;
272650
+ return `sha256:${(0, import_node_crypto83.createHash)("sha256").update(canonical).digest("hex")}`;
272235
272651
  }
272236
272652
  async function runConnectorEffect(connector, toolArgs, deps) {
272237
272653
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -272467,7 +272883,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
272467
272883
  const timeoutMs = resolveAuthWaitMs();
272468
272884
  const controller = new AbortController();
272469
272885
  const timer = setTimeout(() => controller.abort(), timeoutMs);
272470
- const waiterId = (0, import_node_crypto81.randomUUID)();
272886
+ const waiterId = (0, import_node_crypto84.randomUUID)();
272471
272887
  process.stderr.write(
272472
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
272473
272889
  `
@@ -272890,7 +273306,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
272890
273306
  const store = createNodeTokenStore(path35.join(dir, "node-tokens.json"));
272891
273307
  const sub = positional[0];
272892
273308
  if (sub === "issue") {
272893
- const id = flags.get("id") ?? `node-${(0, import_node_crypto81.randomUUID)()}`;
273309
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto84.randomUUID)()}`;
272894
273310
  const token2 = store.issue(id);
272895
273311
  println(token2);
272896
273312
  process.stderr.write(
@@ -273914,7 +274330,8 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
273914
274330
  const name = need(flags, "name").trim();
273915
274331
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
273916
274332
  const goal = flags.get("goal")?.trim();
273917
- const id = projectSlug(flags.get("slug") ?? name);
274333
+ const slug6 = flags.get("slug");
274334
+ const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto84.randomUUID)());
273918
274335
  const result = await api.request("POST", "/api/projects", {
273919
274336
  ...id ? { id } : {},
273920
274337
  name,
@@ -273922,6 +274339,9 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
273922
274339
  ...flags.has("members") ? { member_ids: parseStringArrayFlag(flags.get("members"), "members") } : {}
273923
274340
  });
273924
274341
  println(JSON.stringify(result, null, 2));
274342
+ if (typeof result?.name === "string" && result.name !== name) {
274343
+ println(`\u26A0\uFE0F \u9879\u76EE id\u300C${result.project_id ?? id}\u300D\u5DF2\u88AB\u300C${result.name}\u300D\u5360\u7528\uFF0C\u4E0A\u9762\u8FD4\u56DE\u7684\u662F\u90A3\u4E2A\u5DF2\u6709\u9879\u76EE\uFF0C\u6CA1\u6709\u65B0\u5EFA\u3002\u6362\u4E00\u4E2A --slug \u6216\u53BB\u6389 --slug \u91CD\u8BD5\u3002`);
274344
+ }
273925
274345
  break;
273926
274346
  }
273927
274347
  if (positional[0] === "member") {
@@ -275903,13 +276323,13 @@ var LEGACY_SYSTEMD_UNIT = "oasis-node.service";
275903
276323
  var LEGACY_LAUNCHD_LABEL = "com.oasis.node";
275904
276324
 
275905
276325
  // src/install.ts
275906
- var import_node_crypto82 = require("node:crypto");
276326
+ var import_node_crypto85 = require("node:crypto");
275907
276327
  var fs44 = __toESM(require("node:fs"));
275908
276328
  var path39 = __toESM(require("node:path"));
275909
276329
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
275910
276330
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
275911
276331
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
275912
- const digest = (0, import_node_crypto82.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);
275913
276333
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
275914
276334
  }
275915
276335
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -276032,7 +276452,7 @@ function shimScript() {
276032
276452
  }
276033
276453
 
276034
276454
  // src/index.ts
276035
- var PKG_VERSION = true ? "2.2.34" : "dev";
276455
+ var PKG_VERSION = true ? "2.2.36" : "dev";
276036
276456
  var LOCAL_BIN = localBin();
276037
276457
  var NPM_PREFIX = npmPrefix();
276038
276458
  var INSTANCE = DEFAULT_INSTANCE;
@@ -276317,7 +276737,7 @@ function newInstanceName() {
276317
276737
  const existing = new Set(listInstances());
276318
276738
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
276319
276739
  for (; ; ) {
276320
- const n = `inst-${(0, import_node_crypto83.randomBytes)(3).toString("hex")}`;
276740
+ const n = `inst-${(0, import_node_crypto86.randomBytes)(3).toString("hex")}`;
276321
276741
  if (!existing.has(n)) return n;
276322
276742
  }
276323
276743
  }