oasis_test_v2 2.2.77 → 2.2.79

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 +1126 -717
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -455,6 +455,13 @@ var init_inbox_read = __esm({
455
455
  }
456
456
  });
457
457
 
458
+ // ../contract/src/connector-auth-pending.ts
459
+ var init_connector_auth_pending = __esm({
460
+ "../contract/src/connector-auth-pending.ts"() {
461
+ "use strict";
462
+ }
463
+ });
464
+
458
465
  // ../contract/src/notifications.ts
459
466
  var LEDGER_INBOX_KINDS;
460
467
  var init_notifications = __esm({
@@ -1047,12 +1054,13 @@ function sanitizeRuntimeQuotas(value2, now = Date.now()) {
1047
1054
  }
1048
1055
  return result;
1049
1056
  }
1050
- var RUNTIME_QUOTA_POLL_MS, RUNTIME_QUOTA_STALE_MS, statuses;
1057
+ var RUNTIME_QUOTA_POLL_MS, RUNTIME_QUOTA_STALE_MS, RUNTIME_QUOTA_REFRESH_MIN_GAP_MS, statuses;
1051
1058
  var init_runtime_quota = __esm({
1052
1059
  "../contract/src/runtime-quota.ts"() {
1053
1060
  "use strict";
1054
1061
  RUNTIME_QUOTA_POLL_MS = 5 * 6e4;
1055
1062
  RUNTIME_QUOTA_STALE_MS = 2 * RUNTIME_QUOTA_POLL_MS;
1063
+ RUNTIME_QUOTA_REFRESH_MIN_GAP_MS = 3e4;
1056
1064
  statuses = /* @__PURE__ */ new Set(["ok", "unavailable", "unauthenticated", "unsupported", "error", "rate_limited"]);
1057
1065
  }
1058
1066
  });
@@ -2277,6 +2285,7 @@ var init_src = __esm({
2277
2285
  init_annotation();
2278
2286
  init_collab_view();
2279
2287
  init_inbox_read();
2288
+ init_connector_auth_pending();
2280
2289
  init_notifications();
2281
2290
  init_workorder_quality();
2282
2291
  init_workorder_attachments();
@@ -2562,7 +2571,7 @@ var init_session_runtime_target = __esm({
2562
2571
  this.name = "SessionRuntimeOverrideError";
2563
2572
  }
2564
2573
  };
2565
- CLEAR_SESSION_RUNTIME_HINT = "\u5728\u8FD0\u884C\u65F6\u9009\u62E9\u91CC\u70B9\u300C\u6062\u590D\u4E3A\u5458\u5DE5\u9ED8\u8BA4\u8FD0\u884C\u914D\u7F6E\u300D\u5373\u53EF\u7EE7\u7EED\uFF08\u4F1A\u628A\u8FD9\u4F4D Agent \u6B64\u523B\u7684\u9ED8\u8BA4\u914D\u7F6E\u6284\u8FDB\u8FD9\u6761\u4F1A\u8BDD\uFF09\u3002";
2574
+ CLEAR_SESSION_RUNTIME_HINT = "\u5728\u8F93\u5165\u6846\u5E95\u90E8\u7684\u8FD0\u884C\u65F6\u9009\u62E9\u91CC\u6362\u4E00\u53F0\u53EF\u7528\u7684\u670D\u52A1\u5668\u6216\u8FD0\u884C\u65F6\u5373\u53EF\u7EE7\u7EED\uFF08\u5458\u5DE5\u81EA\u5DF1\u7684\u90A3\u4E00\u5957\u6807\u7740\u300C\u5F53\u524D\u5458\u5DE5\u6240\u5728\u670D\u52A1\u5668\u300D\u300C\u5F53\u524D\u5458\u5DE5\u8FD0\u884C\u65F6\u300D\uFF09\u3002";
2566
2575
  }
2567
2576
  });
2568
2577
 
@@ -164597,7 +164606,7 @@ var init_initialize_workorder = __esm({
164597
164606
  function hasRejectionEvidence(reason) {
164598
164607
  return DEFINITELY_NOT_DELIVERED.has(reason ?? "");
164599
164608
  }
164600
- var DEFINITELY_NOT_DELIVERED, INSERT_UNCERTAIN_NOTE;
164609
+ var DEFINITELY_NOT_DELIVERED, INSERT_UNCERTAIN_NOTE, INSERT_RETURNED_NOTE;
164601
164610
  var init_chat_insert_evidence = __esm({
164602
164611
  "../server/src/chat-insert-evidence.ts"() {
164603
164612
  "use strict";
@@ -164611,6 +164620,7 @@ var init_chat_insert_evidence = __esm({
164611
164620
  "not-consumed"
164612
164621
  ]);
164613
164622
  INSERT_UNCERTAIN_NOTE = "\u8FD9\u6B21\u63D2\u5165\u7ED3\u679C\u4E0D\u786E\u5B9A\uFF1A\u65E0\u6CD5\u786E\u8BA4 agent \u662F\u5426\u5DF2\u8BFB\u5230\u3002";
164623
+ INSERT_RETURNED_NOTE = "\u8FD9\u53E5\u8BDD\u6CA1\u6709\u4EA4\u5230 agent \u624B\u4E0A\uFF1A\u63D2\u8BDD\u65F6\u9875\u9762\u5DF2\u5237\u65B0\u6216\u5173\u95ED\uFF0C\u6CA1\u80FD\u5728\u672C\u8F6E\u7ED3\u675F\u540E\u81EA\u52A8\u53D1\u51FA\u3002";
164614
164624
  }
164615
164625
  });
164616
164626
 
@@ -165880,14 +165890,15 @@ async function appendNowThroughQueue(chatSessionId, text7, deps, clientKey, atta
165880
165890
  if (!batch.length) {
165881
165891
  const state = submitted.state ?? "queued";
165882
165892
  const returned = state === "cancelled" && submitted.stateReason?.startsWith("client-retained:");
165893
+ const landed = state === "cancelled" && submitted.stateReason?.startsWith("client-gone:");
165883
165894
  const reason = state === "delivering" ? "in-flight" : returned ? submitted.stateReason.slice("client-retained:".length) : "unconfirmed";
165884
165895
  return {
165885
165896
  accepted: state === "delivered",
165886
165897
  ...state !== "delivered" ? { reason } : {},
165887
- queued: !returned,
165898
+ queued: !returned && !landed,
165888
165899
  delivered: "",
165889
165900
  batch: [submitted],
165890
- insertStatus: insertStatusOfRow(submitted)
165901
+ insertStatus: landed ? "uncertain" : insertStatusOfRow(submitted)
165891
165902
  };
165892
165903
  }
165893
165904
  const prepared = await buildChatAttachmentFiles(
@@ -165909,8 +165920,13 @@ async function appendNowThroughQueue(chatSessionId, text7, deps, clientKey, atta
165909
165920
  const recordOutcome = async (res) => {
165910
165921
  const nextState = res.accepted ? "delivered" : hasRejectionEvidence(res.reason) ? "queued" : "unconfirmed";
165911
165922
  const stateReason = res.detail ? `${res.reason ?? (res.accepted ? "delivered" : "")}:${res.detail}` : res.reason;
165912
- const returnToClient = retainLocallyOnFailure && !!clientKey && nextState === "queued";
165913
- if (returnToClient && deps.store.markPendingMessages) {
165923
+ const wantsReturn = retainLocallyOnFailure && !!clientKey && nextState === "queued";
165924
+ const orphanedReturn = wantsReturn && deps.clientAttached?.() === false;
165925
+ const returnToClient = wantsReturn && !orphanedReturn;
165926
+ if (orphanedReturn && deps.store.markPendingMessages) {
165927
+ await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled", `client-gone:${res.reason}`);
165928
+ await deps.store.markPendingMessages(chatSessionId, ids2.filter((id) => id !== submitted.id), nextState, stateReason);
165929
+ } else if (returnToClient && deps.store.markPendingMessages) {
165914
165930
  await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled", `client-retained:${res.reason}`);
165915
165931
  await deps.store.markPendingMessages(chatSessionId, ids2.filter((id) => id !== submitted.id), nextState, stateReason);
165916
165932
  } else {
@@ -165920,7 +165936,15 @@ async function appendNowThroughQueue(chatSessionId, text7, deps, clientKey, atta
165920
165936
  if (returnToClient) await deps.store.removePendingMessage(chatSessionId, submitted.id);
165921
165937
  }
165922
165938
  const insertStatus = nextState === "delivered" ? "inserted" : nextState === "unconfirmed" ? "uncertain" : "not-inserted";
165923
- return { ...res, queued: !returnToClient, delivered: merged, batch, insertStatus, ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {} };
165939
+ return {
165940
+ ...res,
165941
+ queued: !returnToClient && !orphanedReturn,
165942
+ delivered: merged,
165943
+ batch,
165944
+ insertStatus,
165945
+ ...orphanedReturn ? { orphanedReturn: true } : {},
165946
+ ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {}
165947
+ };
165924
165948
  };
165925
165949
  let settle;
165926
165950
  const settled = new Promise((resolve12) => {
@@ -165954,82 +165978,6 @@ var init_append_now = __esm({
165954
165978
  }
165955
165979
  });
165956
165980
 
165957
- // ../server/src/domains/chat-sessions/insert-status.ts
165958
- async function resolveInsertStatus(deps, chatSessionId, clientKey) {
165959
- const id = insertRequestId(chatSessionId, clientKey);
165960
- if (!deps.store.getPendingMessage) return { status: "uncertain" };
165961
- let row = await deps.store.getPendingMessage(chatSessionId, id);
165962
- if (row?.state !== "delivering") return { status: insertStatusOfRow(row) };
165963
- if (deps.isTurnRunning(chatSessionId)) return { status: "in-flight" };
165964
- if (isInsertInflight(id)) {
165965
- await waitForInflightInsert(id, deps.settleWaitMs ?? 1e4);
165966
- row = await deps.store.getPendingMessage(chatSessionId, id);
165967
- return { status: insertStatusOfRow(row) };
165968
- }
165969
- const running = settlingOrphans.get(id);
165970
- if (running) return running;
165971
- const settling = settleOrphan(deps, chatSessionId, row).finally(() => {
165972
- settlingOrphans.delete(id);
165973
- });
165974
- settlingOrphans.set(id, settling);
165975
- return settling;
165976
- }
165977
- async function settleOrphan(deps, chatSessionId, row) {
165978
- const ref2 = row.deliveryRef;
165979
- const answer = ref2?.nodeId && ref2.dispatchId && deps.confirm ? await deps.confirm({ nodeId: ref2.nodeId, dispatchId: ref2.dispatchId, runtimeKey: ref2.runtimeKey }).catch(() => ({ status: "unknown", reason: "query-failed" })) : { status: "unknown", reason: ref2 ? "no-node" : "no-delivery-ref" };
165980
- const trace = ref2 && deps.store.listPendingTrace ? await deps.store.listPendingTrace(chatSessionId).catch(() => []) : [];
165981
- const batch = trace.filter((r) => r.state === "delivering" && r.deliveryRef?.runtimeKey === ref2?.runtimeKey);
165982
- if (!batch.some((r) => r.id === row.id)) batch.push(row);
165983
- const ids2 = batch.map((r) => r.id);
165984
- deps.log?.(`[chat-insert] \u5B64\u513F\u63D2\u5165\u7ED3\u7B97 sid=${chatSessionId} row=${row.id} \u2192 ${answer.status}${answer.reason ? `(${answer.reason})` : ""}`);
165985
- if (answer.status === "consumed") {
165986
- await backfill(deps, chatSessionId, batch, row).catch((err) => {
165987
- deps.log?.(`[chat-insert] \u8865\u884C\u5931\u8D25\uFF08\u4ECD\u6309\u5DF2\u63D2\u5165\u5904\u7406\uFF0Cruntime \u786E\u5B9E\u8BFB\u5230\u4E86\uFF09sid=${chatSessionId}: ${String(err)}`);
165988
- });
165989
- await deps.store.markPendingMessages?.(chatSessionId, ids2, "delivered", "delivered:node-confirmed-orphan");
165990
- return { status: "inserted" };
165991
- }
165992
- if (answer.status === "not-consumed") {
165993
- await deps.store.markPendingMessages?.(chatSessionId, [row.id], "cancelled", "client-retained:not-consumed");
165994
- const rest = ids2.filter((x2) => x2 !== row.id);
165995
- if (rest.length) await deps.store.markPendingMessages?.(chatSessionId, rest, "queued", `not-consumed:${answer.reason ?? ""}`);
165996
- return { status: "not-inserted" };
165997
- }
165998
- await deps.store.markPendingMessages?.(chatSessionId, ids2, "unconfirmed", `unconfirmed:${answer.reason ?? "unknown"}`);
165999
- return { status: "uncertain" };
166000
- }
166001
- async function backfill(deps, chatSessionId, batch, row) {
166002
- if (!deps.items) return;
166003
- const ref2 = row.deliveryRef;
166004
- const sorted = [...batch].sort((a, b2) => a.seq - b2.seq);
166005
- const ledger = new ChatItemLedger({
166006
- items: deps.items,
166007
- versionSeed: await deps.items.sessionVersionCursor(chatSessionId).catch(() => 0),
166008
- sessionId: chatSessionId,
166009
- turnId: ref2?.turnId ?? `insert-backfill:${row.id}`,
166010
- ...deps.log ? { log: deps.log } : {}
166011
- });
166012
- const text7 = sorted.map((m2) => m2.text).filter(Boolean).join("\n\n");
166013
- const attachments = sorted.flatMap((m2) => (m2.attachments ?? []).map((a) => ({
166014
- name: a.name,
166015
- ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
166016
- ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
166017
- })));
166018
- const clientKeys = sorted.map((m2) => m2.clientKey).filter((k2) => !!k2);
166019
- ledger.recordUserInput(text7, attachments, clientKeys, ref2?.ord !== void 0 ? { ord: ref2.ord } : void 0);
166020
- await ledger.drain();
166021
- if (ledger.failures > 0) throw new Error("chat_items \u5199\u5165\u5931\u8D25");
166022
- }
166023
- var settlingOrphans;
166024
- var init_insert_status = __esm({
166025
- "../server/src/domains/chat-sessions/insert-status.ts"() {
166026
- "use strict";
166027
- init_chat_item_ledger();
166028
- init_append_now();
166029
- settlingOrphans = /* @__PURE__ */ new Map();
166030
- }
166031
- });
166032
-
166033
165981
  // ../server/src/domains/chat-sessions/append-progress.ts
166034
165982
  function wantsAppendProgress(req) {
166035
165983
  const accept = req.headers.accept;
@@ -169950,6 +169898,15 @@ async function probeChatSubmission(args) {
169950
169898
  }
169951
169899
  }
169952
169900
  const base = { persisted: message !== null, attempts: attempt };
169901
+ if (!lastTurn && chatStore?.getPendingMessage) {
169902
+ const insert = await read("message", () => chatStore.getPendingMessage(sid, insertRequestId(sid, csid)));
169903
+ if (insert?.clientKey === csid) {
169904
+ const landed = insert.state === "unconfirmed" || insert.state === "cancelled" && (insert.stateReason ?? "").startsWith("client-gone:");
169905
+ if (insert.state === "delivered") return { status: { state: "delivered", persisted: true, attempts: attempt, retryable: false }, nextAttempt: attempt, message, lastTurn };
169906
+ if (insert.state === "delivering") return { status: { state: "pending", ...base, retryable: false }, nextAttempt: attempt, message, lastTurn };
169907
+ if (landed) return { status: { state: "not_delivered", ...base, retryable: true }, nextAttempt: attempt, message, lastTurn };
169908
+ }
169909
+ }
169953
169910
  let status;
169954
169911
  if (!turns) {
169955
169912
  if (message && messageDelivered(message)) status = { state: "delivered", ...base, retryable: false };
@@ -170067,6 +170024,7 @@ var init_chat_submission = __esm({
170067
170024
  init_src();
170068
170025
  init_chat_turn_gate();
170069
170026
  init_stable_chat_dispatch_ids();
170027
+ init_append_now();
170070
170028
  MAX_SUBMIT_ATTEMPTS = 20;
170071
170029
  LEGACY_LOOKBACK_MESSAGES = 200;
170072
170030
  LEGACY_LATER_TURNS_LIMIT = 50;
@@ -170088,6 +170046,56 @@ var init_chat_submission = __esm({
170088
170046
  }
170089
170047
  });
170090
170048
 
170049
+ // ../server/src/domains/chat-sessions/insert-undelivered.ts
170050
+ async function landUndeliveredInsert(input) {
170051
+ const { store, itemStore, chatSessionId: sid, clientKey, text: text7, note } = input;
170052
+ const log3 = input.log ?? ((m2) => console.warn(m2));
170053
+ try {
170054
+ if (!itemStore || !chatItemsDoubleWriteEnabled()) return;
170055
+ const ledger = new ChatItemLedger({
170056
+ items: itemStore,
170057
+ versionSeed: await itemStore.sessionVersionCursor(sid).catch(() => 0),
170058
+ sessionId: sid,
170059
+ turnId: fallbackTurnId(),
170060
+ log: log3
170061
+ });
170062
+ const refs = input.attachments.map((a) => ({
170063
+ name: a.name,
170064
+ ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
170065
+ ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
170066
+ }));
170067
+ ledger.recordRejectedSubmission({
170068
+ user: { text: text7, clientSubmitId: clientKey, ...refs.length ? { attachments: refs } : {} },
170069
+ failure: { reason: note, retryable: true }
170070
+ });
170071
+ await ledger.drain();
170072
+ if (ledger.failures > 0) return;
170073
+ const now = (/* @__PURE__ */ new Date()).toISOString();
170074
+ await store.appendMessage({
170075
+ id: (0, import_node_crypto26.randomUUID)(),
170076
+ sessionId: sid,
170077
+ role: "user",
170078
+ content: text7,
170079
+ createdAt: now,
170080
+ messageKey: chatSubmitMessageKey(sid, clientKey),
170081
+ ...refs.length ? { attachments: refs } : {},
170082
+ needsAgentDelivery: true
170083
+ });
170084
+ await store.updateSession(sid, { touchedAt: now }).catch(() => void 0);
170085
+ } catch (err) {
170086
+ log3(`[chat] \u63D2\u8BDD\u6CA1\u4EA4\u5230 agent \u624B\u4E0A\u3001\u843D\u672A\u9001\u8FBE user \u884C\u5931\u8D25\uFF08session=${sid} key=${clientKey}\uFF09: ${String(err)}`);
170087
+ }
170088
+ }
170089
+ var import_node_crypto26;
170090
+ var init_insert_undelivered = __esm({
170091
+ "../server/src/domains/chat-sessions/insert-undelivered.ts"() {
170092
+ "use strict";
170093
+ import_node_crypto26 = require("node:crypto");
170094
+ init_src();
170095
+ init_chat_item_ledger();
170096
+ }
170097
+ });
170098
+
170091
170099
  // ../server/src/chat-workorder-files.ts
170092
170100
  function workOrderFileEditTarget(workOrderId, file) {
170093
170101
  if (!isEditableFileName(file.name)) return null;
@@ -170097,7 +170105,7 @@ function workOrderFileEditTarget(workOrderId, file) {
170097
170105
  return { kind: "work-order-file", workOrderId, fileId: file.fileId, name: file.name };
170098
170106
  }
170099
170107
  function workOrderFileId(workOrderId, blobId, name) {
170100
- return `wof:${(0, import_node_crypto26.createHash)("sha256").update(`${workOrderId}|${blobId}|${name}`).digest("hex").slice(0, 32)}`;
170108
+ return `wof:${(0, import_node_crypto27.createHash)("sha256").update(`${workOrderId}|${blobId}|${name}`).digest("hex").slice(0, 32)}`;
170101
170109
  }
170102
170110
  async function linkChatAttachmentsToWorkOrder(input) {
170103
170111
  const warnings = [];
@@ -170148,11 +170156,11 @@ async function linkChatAttachmentsToWorkOrder(input) {
170148
170156
  }
170149
170157
  return { linked, warnings };
170150
170158
  }
170151
- var import_node_crypto26;
170159
+ var import_node_crypto27;
170152
170160
  var init_chat_workorder_files = __esm({
170153
170161
  "../server/src/chat-workorder-files.ts"() {
170154
170162
  "use strict";
170155
- import_node_crypto26 = require("node:crypto");
170163
+ import_node_crypto27 = require("node:crypto");
170156
170164
  init_src();
170157
170165
  }
170158
170166
  });
@@ -170943,11 +170951,11 @@ var init_config_store = __esm({
170943
170951
  function clone2(value2) {
170944
170952
  return structuredClone(value2);
170945
170953
  }
170946
- var import_node_crypto27, import_promises3, import_node_path6, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
170954
+ var import_node_crypto28, import_promises3, import_node_path6, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
170947
170955
  var init_run_store = __esm({
170948
170956
  "../server/src/domains/knowledge/run-store.ts"() {
170949
170957
  "use strict";
170950
- import_node_crypto27 = require("node:crypto");
170958
+ import_node_crypto28 = require("node:crypto");
170951
170959
  import_promises3 = require("node:fs/promises");
170952
170960
  import_node_path6 = require("node:path");
170953
170961
  MemoryKnowledgeRunStore = class {
@@ -171064,7 +171072,7 @@ var init_run_store = __esm({
171064
171072
  }
171065
171073
  async persist(runs) {
171066
171074
  const shape = { version: 1, runs };
171067
- const temp = `${this.file}.${process.pid}.${(0, import_node_crypto27.randomUUID)()}.tmp`;
171075
+ const temp = `${this.file}.${process.pid}.${(0, import_node_crypto28.randomUUID)()}.tmp`;
171068
171076
  try {
171069
171077
  await (0, import_promises3.writeFile)(temp, `${JSON.stringify(shape, null, 2)}
171070
171078
  `, { mode: 384 });
@@ -171713,12 +171721,12 @@ function retryableKnowledgeRun(record9) {
171713
171721
  const latestAnswerAttempt = [...record9.attempts ?? []].reverse().find((attempt) => attempt.kind !== "fast");
171714
171722
  return record9.status === "succeeded" && record9.request.action === "ask" && record9.executionHealth === "degraded" && latestAnswerAttempt?.status === "failed";
171715
171723
  }
171716
- var import_node_crypto28, ACTION_LABEL, STATUS_MARKER, FINAL_ANSWER_MARKER, ACTIVE_STATUSES, RETRYABLE_STATUSES, HEARTBEAT_INTERVAL_MS, LEASE_MS, MAX_CONCURRENT_READS, ACTION_FLOW, KnowledgeService;
171724
+ var import_node_crypto29, ACTION_LABEL, STATUS_MARKER, FINAL_ANSWER_MARKER, ACTIVE_STATUSES, RETRYABLE_STATUSES, HEARTBEAT_INTERVAL_MS, LEASE_MS, MAX_CONCURRENT_READS, ACTION_FLOW, KnowledgeService;
171717
171725
  var init_service2 = __esm({
171718
171726
  "../server/src/domains/knowledge/service.ts"() {
171719
171727
  "use strict";
171720
171728
  init_file_limits2();
171721
- import_node_crypto28 = require("node:crypto");
171729
+ import_node_crypto29 = require("node:crypto");
171722
171730
  init_src7();
171723
171731
  init_config_store();
171724
171732
  init_run_store();
@@ -171761,7 +171769,7 @@ var init_service2 = __esm({
171761
171769
  constructor(options) {
171762
171770
  this.options = options;
171763
171771
  this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
171764
- this.id = options.id ?? import_node_crypto28.randomUUID;
171772
+ this.id = options.id ?? import_node_crypto29.randomUUID;
171765
171773
  this.runStore = options.runStore ?? new MemoryKnowledgeRunStore();
171766
171774
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;
171767
171775
  this.leaseMs = options.leaseMs ?? LEASE_MS;
@@ -182712,7 +182720,7 @@ function stableJson2(value2) {
182712
182720
  return JSON.stringify(stableValue(value2));
182713
182721
  }
182714
182722
  function sha256(value2) {
182715
- return (0, import_node_crypto29.createHash)("sha256").update(value2).digest("hex");
182723
+ return (0, import_node_crypto30.createHash)("sha256").update(value2).digest("hex");
182716
182724
  }
182717
182725
  function normalizeKnowledgeContent(title, content3) {
182718
182726
  const body2 = content3.normalize("NFC").replace(/\r\n?/gu, "\n").replace(PUBLISH_MARKER, "\n").split("\n").map((line2) => line2.replace(/[\t ]+$/gu, "")).join("\n").replace(/\n{3,}/gu, "\n\n").trim();
@@ -182954,11 +182962,11 @@ function renderUntrustedKnowledgeFragment(input) {
182954
182962
  "</knowledge>"
182955
182963
  ].join("\n");
182956
182964
  }
182957
- var import_node_crypto29, KNOWLEDGE_NORMALIZER_VERSION, KNOWLEDGE_PACK_TEMPLATE_VERSION, PUBLISH_MARKER, STOPWORDS, LEXICAL_STOPWORDS, lexicalSegmenters, KNOWLEDGE_LEXICAL_INDEX_VERSION, KNOWLEDGE_MAINTENANCE_RELEVANCE_THRESHOLD, KNOWLEDGE_LAYOUT_TERMS;
182965
+ var import_node_crypto30, KNOWLEDGE_NORMALIZER_VERSION, KNOWLEDGE_PACK_TEMPLATE_VERSION, PUBLISH_MARKER, STOPWORDS, LEXICAL_STOPWORDS, lexicalSegmenters, KNOWLEDGE_LEXICAL_INDEX_VERSION, KNOWLEDGE_MAINTENANCE_RELEVANCE_THRESHOLD, KNOWLEDGE_LAYOUT_TERMS;
182958
182966
  var init_governance_content = __esm({
182959
182967
  "../server/src/domains/knowledge/governance-content.ts"() {
182960
182968
  "use strict";
182961
- import_node_crypto29 = require("node:crypto");
182969
+ import_node_crypto30 = require("node:crypto");
182962
182970
  init_mdast_util_from_markdown();
182963
182971
  init_mdast_util_gfm();
182964
182972
  init_micromark_extension_gfm();
@@ -193125,11 +193133,11 @@ function approvedSnapshotBody(title, content3) {
193125
193133
  function uniqueStrings(values) {
193126
193134
  return [...new Set(values)].sort();
193127
193135
  }
193128
- var import_node_crypto30, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
193136
+ var import_node_crypto31, KnowledgeGovernanceError, put2, REMEDIATION_ACTION_BY_TASK, KnowledgeGovernanceService;
193129
193137
  var init_governance_service = __esm({
193130
193138
  "../server/src/domains/knowledge/governance-service.ts"() {
193131
193139
  "use strict";
193132
- import_node_crypto30 = require("node:crypto");
193140
+ import_node_crypto31 = require("node:crypto");
193133
193141
  init_governance_content();
193134
193142
  init_knowledge_policy();
193135
193143
  init_network_maintenance();
@@ -193151,7 +193159,7 @@ var init_governance_service = __esm({
193151
193159
  constructor(options) {
193152
193160
  this.options = options;
193153
193161
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
193154
- this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto30.randomUUID)()}`);
193162
+ this.id = options.id ?? ((prefix) => `${prefix}_${(0, import_node_crypto31.randomUUID)()}`);
193155
193163
  this.versionViewBaseUrl = (options.versionViewBaseUrl ?? "/api/knowledge/items").replace(/\/$/u, "");
193156
193164
  this.projectProvisionRetryDelayMs = Math.max(0, options.projectProvisionRetryDelayMs ?? 6e4);
193157
193165
  this.projectProvisionMaxRetryDelayMs = Math.max(this.projectProvisionRetryDelayMs, options.projectProvisionMaxRetryDelayMs ?? 36e5);
@@ -198842,7 +198850,7 @@ function trustedKnowledgeInvocationId(body2, turn) {
198842
198850
  if (body2.action !== "ask") return void 0;
198843
198851
  const scope2 = turn?.artifactId ?? turn?.dispatchId;
198844
198852
  if (!scope2) return void 0;
198845
- const fingerprint2 = (0, import_node_crypto31.createHash)("sha256").update(JSON.stringify({
198853
+ const fingerprint2 = (0, import_node_crypto32.createHash)("sha256").update(JSON.stringify({
198846
198854
  action: body2.action,
198847
198855
  message: body2.message?.trim() ?? "",
198848
198856
  queryMode: body2.queryMode === "deep" ? "deep" : "fast",
@@ -198997,11 +199005,11 @@ function knowledgeRoutes(service, options = {}) {
198997
199005
  });
198998
199006
  };
198999
199007
  }
199000
- var import_node_crypto31, KNOWLEDGE_WORKORDER_ACTIONS;
199008
+ var import_node_crypto32, KNOWLEDGE_WORKORDER_ACTIONS;
199001
199009
  var init_routes = __esm({
199002
199010
  "../server/src/domains/knowledge/routes.ts"() {
199003
199011
  "use strict";
199004
- import_node_crypto31 = require("node:crypto");
199012
+ import_node_crypto32 = require("node:crypto");
199005
199013
  init_src7();
199006
199014
  init_router();
199007
199015
  init_governance_service();
@@ -200629,7 +200637,7 @@ async function resolveChatAttachmentDiscoverySource(input) {
200629
200637
  sourceType: input.sourceType,
200630
200638
  sourceId: session.id,
200631
200639
  sourceVersionId: attachment.blobRef,
200632
- sourceFingerprint: (0, import_node_crypto32.createHash)("sha256").update(bytes2).digest("hex"),
200640
+ sourceFingerprint: (0, import_node_crypto33.createHash)("sha256").update(bytes2).digest("hex"),
200633
200641
  sourceContent: new TextDecoder().decode(bytes2),
200634
200642
  sourceDisplayName: attachment.name,
200635
200643
  ...session.projectId ? { projectId: session.projectId } : {},
@@ -200640,11 +200648,11 @@ async function resolveChatAttachmentDiscoverySource(input) {
200640
200648
  }
200641
200649
  };
200642
200650
  }
200643
- var import_node_crypto32, TEXT_CONTENT_TYPES;
200651
+ var import_node_crypto33, TEXT_CONTENT_TYPES;
200644
200652
  var init_chat_attachment_discovery = __esm({
200645
200653
  "../server/src/domains/knowledge/chat-attachment-discovery.ts"() {
200646
200654
  "use strict";
200647
- import_node_crypto32 = require("node:crypto");
200655
+ import_node_crypto33 = require("node:crypto");
200648
200656
  TEXT_CONTENT_TYPES = /* @__PURE__ */ new Set([
200649
200657
  "application/json",
200650
200658
  "application/xml",
@@ -200820,8 +200828,8 @@ function aad(snapshot) {
200820
200828
  }
200821
200829
  function sealKnowledgeSnapshot(snapshot, key2) {
200822
200830
  if (snapshot.content.startsWith(PREFIX)) return structuredClone(snapshot);
200823
- const iv = (0, import_node_crypto33.randomBytes)(12);
200824
- const cipher = (0, import_node_crypto33.createCipheriv)("aes-256-gcm", keyBytes(key2), iv);
200831
+ const iv = (0, import_node_crypto34.randomBytes)(12);
200832
+ const cipher = (0, import_node_crypto34.createCipheriv)("aes-256-gcm", keyBytes(key2), iv);
200825
200833
  cipher.setAAD(aad(snapshot));
200826
200834
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
200827
200835
  return {
@@ -200834,7 +200842,7 @@ function openKnowledgeSnapshot(snapshot, key2) {
200834
200842
  const packed = snapshot.content.slice(PREFIX.length).split(".");
200835
200843
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
200836
200844
  const [iv, tag, data] = packed;
200837
- const decipher = (0, import_node_crypto33.createDecipheriv)("aes-256-gcm", keyBytes(key2), Buffer.from(iv, "base64url"));
200845
+ const decipher = (0, import_node_crypto34.createDecipheriv)("aes-256-gcm", keyBytes(key2), Buffer.from(iv, "base64url"));
200838
200846
  decipher.setAAD(aad(snapshot));
200839
200847
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
200840
200848
  const content3 = Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8");
@@ -200843,11 +200851,11 @@ function openKnowledgeSnapshot(snapshot, key2) {
200843
200851
  function isSealedKnowledgeSnapshotContent(content3) {
200844
200852
  return content3.startsWith(PREFIX);
200845
200853
  }
200846
- var import_node_crypto33, PREFIX;
200854
+ var import_node_crypto34, PREFIX;
200847
200855
  var init_snapshot_crypto = __esm({
200848
200856
  "../server/src/domains/knowledge/snapshot-crypto.ts"() {
200849
200857
  "use strict";
200850
- import_node_crypto33 = require("node:crypto");
200858
+ import_node_crypto34 = require("node:crypto");
200851
200859
  PREFIX = "oasis-knowledge:v1:";
200852
200860
  }
200853
200861
  });
@@ -200930,11 +200938,11 @@ function uniquenessKeys(kind, record9) {
200930
200938
  return [];
200931
200939
  }
200932
200940
  }
200933
- var import_node_crypto34, import_promises4, import_node_path7, RECORD_KINDS, MemoryKnowledgeGovernanceStore, FileKnowledgeGovernanceStore;
200941
+ var import_node_crypto35, import_promises4, import_node_path7, RECORD_KINDS, MemoryKnowledgeGovernanceStore, FileKnowledgeGovernanceStore;
200934
200942
  var init_governance_store = __esm({
200935
200943
  "../server/src/domains/knowledge/governance-store.ts"() {
200936
200944
  "use strict";
200937
- import_node_crypto34 = require("node:crypto");
200945
+ import_node_crypto35 = require("node:crypto");
200938
200946
  import_promises4 = require("node:fs/promises");
200939
200947
  import_node_path7 = require("node:path");
200940
200948
  init_snapshot_crypto();
@@ -201152,7 +201160,7 @@ var init_governance_store = __esm({
201152
201160
  return result;
201153
201161
  }
201154
201162
  async persist(shape) {
201155
- const temp = `${this.file}.${process.pid}.${(0, import_node_crypto34.randomUUID)()}.tmp`;
201163
+ const temp = `${this.file}.${process.pid}.${(0, import_node_crypto35.randomUUID)()}.tmp`;
201156
201164
  try {
201157
201165
  await (0, import_promises4.writeFile)(temp, `${JSON.stringify(this.sealSnapshots(shape), null, 2)}
201158
201166
  `, { mode: 384 });
@@ -201188,7 +201196,7 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
201188
201196
  if (error2.code !== "ENOENT") throw error2;
201189
201197
  }
201190
201198
  await (0, import_promises5.mkdir)((0, import_node_path8.dirname)(file), { recursive: true });
201191
- const generated = `oasis-${(0, import_node_crypto35.randomUUID)()}`;
201199
+ const generated = `oasis-${(0, import_node_crypto36.randomUUID)()}`;
201192
201200
  try {
201193
201201
  await (0, import_promises5.writeFile)(file, `${generated}
201194
201202
  `, { encoding: "utf8", mode: 384, flag: "wx" });
@@ -201198,11 +201206,11 @@ async function loadOrCreateKnowledgeDeploymentId(file) {
201198
201206
  return readDeploymentId(file);
201199
201207
  }
201200
201208
  }
201201
- var import_node_crypto35, import_promises5, import_node_path8, VALID_DEPLOYMENT_ID;
201209
+ var import_node_crypto36, import_promises5, import_node_path8, VALID_DEPLOYMENT_ID;
201202
201210
  var init_deployment_id = __esm({
201203
201211
  "../server/src/domains/knowledge/deployment-id.ts"() {
201204
201212
  "use strict";
201205
- import_node_crypto35 = require("node:crypto");
201213
+ import_node_crypto36 = require("node:crypto");
201206
201214
  import_promises5 = require("node:fs/promises");
201207
201215
  import_node_path8 = require("node:path");
201208
201216
  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;
@@ -202083,7 +202091,7 @@ function roundFailureText(kind, error2) {
202083
202091
  return kind === "timeout" ? `\u8FD9\u4E00\u6B21\u6267\u884C\u8DD1\u6EE1\u4E86\u5355\u6B21\u8FD0\u884C\u7684\u65F6\u95F4\u4E0A\u9650\uFF08${wallClockText()}\uFF09\uFF0C\u88AB\u7CFB\u7EDF\u505C\u4E0B\u4E86\uFF08${error2}\uFF09` : `\u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u5B8C\uFF1A${error2}`;
202084
202092
  }
202085
202093
  function deterministicUuid2(seed) {
202086
- const h = (0, import_node_crypto36.createHash)("sha256").update(seed).digest();
202094
+ const h = (0, import_node_crypto37.createHash)("sha256").update(seed).digest();
202087
202095
  const b2 = Buffer.from(h.subarray(0, 16));
202088
202096
  b2[6] = b2[6] & 15 | 80;
202089
202097
  b2[8] = b2[8] & 63 | 128;
@@ -202138,11 +202146,11 @@ async function seedChildRoundRows(chatStore, childSessionId, round, prompt2, now
202138
202146
  createdAt: now
202139
202147
  });
202140
202148
  }
202141
- var import_node_crypto36, DelegationError, DIGEST_CHARS, PENDING_QUEUE_OWNER, DISPATCH_LEASE_MS, CONSECUTIVE_FAIL_LIMIT, roundTurnKey, roundTurnId, DelegationService;
202149
+ var import_node_crypto37, DelegationError, DIGEST_CHARS, PENDING_QUEUE_OWNER, DISPATCH_LEASE_MS, CONSECUTIVE_FAIL_LIMIT, roundTurnKey, roundTurnId, DelegationService;
202142
202150
  var init_service3 = __esm({
202143
202151
  "../server/src/domains/delegations/service.ts"() {
202144
202152
  "use strict";
202145
- import_node_crypto36 = require("node:crypto");
202153
+ import_node_crypto37 = require("node:crypto");
202146
202154
  init_src();
202147
202155
  init_chat_exit();
202148
202156
  init_parent_conversation();
@@ -203450,7 +203458,7 @@ var init_continue_outcome = __esm({
203450
203458
  function trustedDelegationInvocationId(body2, turn) {
203451
203459
  const scope2 = turn?.artifactId ?? turn?.dispatchId;
203452
203460
  if (!scope2) return void 0;
203453
- const fingerprint2 = (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify({ to: body2.to?.trim() ?? "", task: body2.task ?? "", as: body2.as?.trim() ?? "", files: body2.files ?? [] })).digest("hex").slice(0, 20);
203461
+ const fingerprint2 = (0, import_node_crypto38.createHash)("sha256").update(JSON.stringify({ to: body2.to?.trim() ?? "", task: body2.task ?? "", as: body2.as?.trim() ?? "", files: body2.files ?? [] })).digest("hex").slice(0, 20);
203454
203462
  return `dispatch:${scope2}:delegation:${fingerprint2}`;
203455
203463
  }
203456
203464
  function callerOf(req, invocationId) {
@@ -203545,11 +203553,11 @@ function delegationRoutes(service) {
203545
203553
  });
203546
203554
  };
203547
203555
  }
203548
- var import_node_crypto37;
203556
+ var import_node_crypto38;
203549
203557
  var init_routes2 = __esm({
203550
203558
  "../server/src/domains/delegations/routes.ts"() {
203551
203559
  "use strict";
203552
- import_node_crypto37 = require("node:crypto");
203560
+ import_node_crypto38 = require("node:crypto");
203553
203561
  init_router();
203554
203562
  init_continue_outcome();
203555
203563
  init_service3();
@@ -204460,6 +204468,60 @@ var init_child_live_turn = __esm({
204460
204468
  }
204461
204469
  });
204462
204470
 
204471
+ // ../server/src/domains/actors/registry-audit-sink.ts
204472
+ function createRegistryAuditSink(file) {
204473
+ const listeners = /* @__PURE__ */ new Set();
204474
+ const notify = (msg) => {
204475
+ for (const listener of listeners) listener(msg);
204476
+ };
204477
+ return {
204478
+ /** 登记审计:落盘 + 推 registry 通知(前端据此重拉登记类投影)。 */
204479
+ registryAudit: (record9) => {
204480
+ fs20.appendFile(file, JSON.stringify(record9) + "\n", () => {
204481
+ });
204482
+ notify({ kind: record9.kind, actor: record9.actor, target: record9.target, timestamp: record9.timestamp });
204483
+ },
204484
+ /**
204485
+ * 凭据审计(ADR 凭据保险库 §4.7):与登记审计同落一份旁账,按 kind 前缀 `credential_` 区分。
204486
+ *
204487
+ * **只落盘、不推 registry 通知**(D-10 推送面)。registry 通知是实例级的,`/api/events` 原样转发给
204488
+ * 任何已认证的连接、不分组织;凭据事件推进去 = 别家组织开一条 SSE 就能实时看到「谁在读/改哪个变量」。
204489
+ * 仓内没有任何消费方订它(2.0 前端只订无内容的 runtime-status,而后者本来就不被凭据事件唤醒)。
204490
+ * 要看凭据审计走 `GET /api/variables/reveal-audit`(按请求方组织过滤)。将来若需要实时推送,
204491
+ * 得先让通知带组织、`/api/events` 按组织过滤(recvvzbhOGiS7l 的完整修复),再放回来。
204492
+ */
204493
+ credentialAudit: (record9) => {
204494
+ fs20.appendFile(file, JSON.stringify(record9) + "\n", () => {
204495
+ });
204496
+ },
204497
+ /** runtime 状态变了:只发无内容的失效提示(ADR 0576)。 */
204498
+ notifyRuntimeStatus: () => {
204499
+ notify({ kind: "runtime_status_changed", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
204500
+ },
204501
+ /** 这些人的铃铛有新通知了:各发一条无内容的失效提示,前端据此重拉台账、弹页内提示。 */
204502
+ notifyNotificationsChanged: (recipients) => {
204503
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
204504
+ for (const target of new Set(recipients)) notify({ kind: NOTIFICATIONS_CHANGED, target, timestamp });
204505
+ },
204506
+ notifications: {
204507
+ subscribe(listener) {
204508
+ listeners.add(listener);
204509
+ return () => {
204510
+ listeners.delete(listener);
204511
+ };
204512
+ }
204513
+ }
204514
+ };
204515
+ }
204516
+ var fs20, NOTIFICATIONS_CHANGED;
204517
+ var init_registry_audit_sink = __esm({
204518
+ "../server/src/domains/actors/registry-audit-sink.ts"() {
204519
+ "use strict";
204520
+ fs20 = __toESM(require("node:fs"), 1);
204521
+ NOTIFICATIONS_CHANGED = "notifications_changed";
204522
+ }
204523
+ });
204524
+
204463
204525
  // ../connectors/src/_base/wrapper-assets.ts
204464
204526
  function isUsableWrapper(scriptPath) {
204465
204527
  if (!(0, import_node_fs7.existsSync)(scriptPath)) return false;
@@ -205588,7 +205650,7 @@ function createAppJwt(appId, privateKeyPem, nowMs) {
205588
205650
  exp: nowSec + JWT_TTL_SEC,
205589
205651
  iss: appId
205590
205652
  }));
205591
- const signer = (0, import_node_crypto38.createSign)("RSA-SHA256");
205653
+ const signer = (0, import_node_crypto39.createSign)("RSA-SHA256");
205592
205654
  signer.update(`${header}.${payload2}`);
205593
205655
  signer.end();
205594
205656
  try {
@@ -205751,11 +205813,11 @@ async function exchangeManifestCode(code2, deps = {}) {
205751
205813
  ...typeof body2["client_secret"] === "string" ? { clientSecret: body2["client_secret"] } : {}
205752
205814
  };
205753
205815
  }
205754
- var import_node_crypto38, GITHUB_API, JWT_TTL_SEC, JWT_IAT_SKEW_SEC, defaultDeps2;
205816
+ var import_node_crypto39, GITHUB_API, JWT_TTL_SEC, JWT_IAT_SKEW_SEC, defaultDeps2;
205755
205817
  var init_app_auth = __esm({
205756
205818
  "../connectors/src/github/app-auth.ts"() {
205757
205819
  "use strict";
205758
- import_node_crypto38 = require("node:crypto");
205820
+ import_node_crypto39 = require("node:crypto");
205759
205821
  GITHUB_API = "https://api.github.com";
205760
205822
  JWT_TTL_SEC = 9 * 60;
205761
205823
  JWT_IAT_SKEW_SEC = 60;
@@ -206680,11 +206742,11 @@ function extractOutlineKnowledgeCandidateDrafts(events) {
206680
206742
  }
206681
206743
  return [...drafts.values()];
206682
206744
  }
206683
- var import_node_crypto39, 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;
206745
+ var import_node_crypto40, 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;
206684
206746
  var init_knowledge_api = __esm({
206685
206747
  "../connectors/src/outline/knowledge-api.ts"() {
206686
206748
  "use strict";
206687
- import_node_crypto39 = require("node:crypto");
206749
+ import_node_crypto40 = require("node:crypto");
206688
206750
  REQUEST_TIMEOUT_MS = 2e4;
206689
206751
  WRITE_ENDPOINTS = /* @__PURE__ */ new Set(["collections.create", "documents.create", "documents.update", "documents.publish"]);
206690
206752
  DEFAULT_WRITE_INTERVAL_MS = process.env["NODE_ENV"] === "test" ? 0 : 250;
@@ -206711,7 +206773,7 @@ var init_knowledge_api = __esm({
206711
206773
  this.baseUrl = canonicalUrl(baseUrl);
206712
206774
  this.lastResponseBaseUrl = this.baseUrl;
206713
206775
  this.token = token;
206714
- this.coordinatorKey = `${this.baseUrl}:${(0, import_node_crypto39.createHash)("sha256").update(token).digest("hex")}`;
206776
+ this.coordinatorKey = `${this.baseUrl}:${(0, import_node_crypto40.createHash)("sha256").update(token).digest("hex")}`;
206715
206777
  this.writeIntervalMs = Math.max(0, options.writeIntervalMs ?? DEFAULT_WRITE_INTERVAL_MS);
206716
206778
  this.maxRateLimitRetries = Math.max(0, options.maxRateLimitRetries ?? DEFAULT_RATE_LIMIT_RETRIES);
206717
206779
  this.maxRateLimitDelayMs = Math.max(0, options.maxRateLimitDelayMs ?? DEFAULT_MAX_RATE_LIMIT_DELAY_MS);
@@ -208119,15 +208181,15 @@ function classifyOutlineGovernedMigrationDocument(document4) {
208119
208181
  if (path46.startsWith("wiki/sources/")) return { mapping: "formal", metadata: legacyMetadata(document4, "source_note"), reason: "legacy_source_note" };
208120
208182
  return { mapping: "excluded", reason: "legacy_unmapped_path" };
208121
208183
  }
208122
- var import_node_crypto40, value, text4, hash, REVISION_VISIBILITY_RETRY_DELAYS_MS, governedProvisionRuns, PUBLISH_MARKER2, PUBLISH_MARKER_PRESENT;
208184
+ var import_node_crypto41, value, text4, hash, REVISION_VISIBILITY_RETRY_DELAYS_MS, governedProvisionRuns, PUBLISH_MARKER2, PUBLISH_MARKER_PRESENT;
208123
208185
  var init_governed_knowledge = __esm({
208124
208186
  "../connectors/src/outline/governed-knowledge.ts"() {
208125
208187
  "use strict";
208126
- import_node_crypto40 = require("node:crypto");
208188
+ import_node_crypto41 = require("node:crypto");
208127
208189
  init_knowledge_api();
208128
208190
  value = (input) => input && typeof input === "object" && !Array.isArray(input) ? input : null;
208129
208191
  text4 = (input) => typeof input === "string" && input.trim() ? input.trim() : null;
208130
- hash = (input) => (0, import_node_crypto40.createHash)("sha256").update(input.normalize("NFC").replace(/\r\n?/gu, "\n")).digest("hex");
208192
+ hash = (input) => (0, import_node_crypto41.createHash)("sha256").update(input.normalize("NFC").replace(/\r\n?/gu, "\n")).digest("hex");
208131
208193
  REVISION_VISIBILITY_RETRY_DELAYS_MS = [0, 100, 250, 500, 1e3, 2e3];
208132
208194
  governedProvisionRuns = /* @__PURE__ */ new Map();
208133
208195
  PUBLISH_MARKER2 = /\n?<!--\s*oasis-publish:[\s\S]*?-->\n?/giu;
@@ -208385,7 +208447,9 @@ function buildAppManifest(args) {
208385
208447
  default_permissions: { ...GITHUB_APP_DEFAULT_PERMISSIONS },
208386
208448
  // 暂不订阅 webhook:本方案只需要主动调 API,开着反而多一份要维护的密钥与端点。
208387
208449
  hook_attributes: { url: `${base}/api/connectors/github/app/webhook`, active: false },
208388
- callback_urls: [`${base}/api/connectors/github/app/oauth-callback`]
208450
+ callback_urls: [`${base}/api/connectors/github/app/oauth-callback`],
208451
+ setup_url: `${base}${args.setupPath}`,
208452
+ setup_on_update: true
208389
208453
  };
208390
208454
  }
208391
208455
  function manifestFormAction(owner, state) {
@@ -208454,7 +208518,7 @@ function checkAppOwnership(conv, want) {
208454
208518
  }
208455
208519
  return null;
208456
208520
  }
208457
- var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, PendingAppCreations, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208521
+ var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208458
208522
  var init_app_manifest = __esm({
208459
208523
  "../connectors/src/github/app-manifest.ts"() {
208460
208524
  "use strict";
@@ -208487,31 +208551,6 @@ var init_app_manifest = __esm({
208487
208551
  { key: "issues", label: "Issue\uFF08\u8BFB\u5199\uFF09", desc: "\u521B\u5EFA\u4E0E\u8BC4\u8BBA Issue" }
208488
208552
  ];
208489
208553
  PENDING_TTL_MS = 15 * 60 * 1e3;
208490
- PendingAppCreations = class {
208491
- map = /* @__PURE__ */ new Map();
208492
- put(state, v2) {
208493
- this.sweep(v2.createdAtMs);
208494
- this.map.set(state, v2);
208495
- }
208496
- get(state, nowMs) {
208497
- const v2 = this.map.get(state);
208498
- if (!v2) return void 0;
208499
- if (nowMs - v2.createdAtMs > PENDING_TTL_MS) {
208500
- this.map.delete(state);
208501
- return void 0;
208502
- }
208503
- return v2;
208504
- }
208505
- /** 过期条目清理——每次写入顺带做,不另起定时器。 */
208506
- sweep(nowMs) {
208507
- for (const [k2, v2] of this.map) {
208508
- if (nowMs - v2.createdAtMs > PENDING_TTL_MS) this.map.delete(k2);
208509
- }
208510
- }
208511
- get size() {
208512
- return this.map.size;
208513
- }
208514
- };
208515
208554
  REQUIRED_WRITE_PERMISSIONS = ["contents", "pull_requests"];
208516
208555
  PERMISSION_RANK = { read: 1, write: 2, admin: 3 };
208517
208556
  FORBIDDEN_IMPORT_PERMISSION_KEYS = [
@@ -208995,6 +209034,16 @@ var init_src8 = __esm({
208995
209034
  function isBundleFrozen(err) {
208996
209035
  return typeof err === "object" && err !== null && err.code === "TENANT_BUNDLE_FROZEN";
208997
209036
  }
209037
+ async function lookupGithubAppPending(store, state) {
209038
+ const missing = "\u627E\u4E0D\u5230\u8FD9\u6B21\u8FDE\u63A5\u7684\u8BB0\u5F55\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u70B9\u4E00\u6B21\u300C\u8FDE\u63A5\u300D";
209039
+ if (!store || !state) return { message: missing };
209040
+ const hit = await store.get(state, Date.now());
209041
+ if (hit.status === "expired") {
209042
+ return { message: "\u8FD9\u6B21\u8FDE\u63A5\u4ECE\u5F00\u59CB\u5230\u73B0\u5728\u5DF2\u8D85\u8FC7 15 \u5206\u949F\uFF0C\u5DF2\u7ECF\u5931\u6548\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u70B9\u4E00\u6B21\u300C\u8FDE\u63A5\u300D" };
209043
+ }
209044
+ if (hit.status === "missing") return { message: missing };
209045
+ return { pending: hit.value.value, message: "" };
209046
+ }
208998
209047
  function resolveCallbackBase(supplied, requestHost) {
208999
209048
  if (!supplied || !supplied.trim()) {
209000
209049
  throw new Error("\u7F3A\u5C11 callbackBase\uFF1A\u524D\u7AEF\u9700\u62A5\u4E0A window.location.origin + API_BASE");
@@ -210769,7 +210818,7 @@ async function startOasisServer(opts) {
210769
210818
  }
210770
210819
  return void 0;
210771
210820
  }
210772
- const appendNowThroughQueue2 = async (sid, text7, clientKey, attachments, perCompanyStore, retainLocallyOnFailure = false, onAwaitingConsumption, resend = false, itemStore) => {
210821
+ const appendNowThroughQueue2 = async (sid, text7, clientKey, attachments, perCompanyStore, retainLocallyOnFailure = false, onAwaitingConsumption, resend = false, itemStore, clientAttached) => {
210773
210822
  if (perCompanyStore === null) {
210774
210823
  throw new Error(
210775
210824
  `appendNowThroughQueue: per-company chat store unresolved (sid=${sid})`
@@ -210802,52 +210851,22 @@ async function startOasisServer(opts) {
210802
210851
  }
210803
210852
  }
210804
210853
  throw new Error(`blob ${blobRef} \u4E24\u4E2A\u67DC\u91CC\u90FD\u6CA1\u6709`);
210805
- }
210854
+ },
210855
+ ...clientAttached ? { clientAttached } : {}
210806
210856
  }, clientKey, attachments, retainLocallyOnFailure, resend ? { resend: true } : void 0);
210807
- if (r.insertStatus === "uncertain" && clientKey) {
210857
+ if ((r.insertStatus === "uncertain" || r.orphanedReturn) && clientKey) {
210808
210858
  const shown = r.batch.map((m2) => m2.text).filter(Boolean).join("\n\n") || text7;
210809
- await landUndeliveredInsert(sid, clientKey, shown, attachments ?? [], store, itemStore);
210810
- }
210811
- return { accepted: r.accepted, ...r.reason ? { reason: r.reason } : {}, queued: r.queued, insertStatus: r.insertStatus };
210812
- };
210813
- const landUndeliveredInsert = async (sid, clientKey, text7, attachments, store, itemStore) => {
210814
- try {
210815
- if (!itemStore || !chatItemsDoubleWriteEnabled()) return;
210816
- const ledger = new ChatItemLedger({
210817
- items: itemStore,
210818
- versionSeed: await itemStore.sessionVersionCursor(sid).catch(() => 0),
210819
- sessionId: sid,
210820
- turnId: fallbackTurnId(),
210821
- log: (m2) => console.warn(m2)
210822
- });
210823
- const refs = attachments.map((a) => ({
210824
- name: a.name,
210825
- ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
210826
- ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
210827
- }));
210828
- ledger.recordRejectedSubmission({
210829
- user: { text: text7, clientSubmitId: clientKey, ...refs.length ? { attachments: refs } : {} },
210830
- // 文案取自 turn-copy 那一句的前半(ADR 0615 决策 10):行为上「不确定」与「没送到」完全一样
210831
- // (都不自动发、都等人点),所以只差这一句话。
210832
- failure: { reason: INSERT_UNCERTAIN_NOTE, retryable: true }
210833
- });
210834
- await ledger.drain();
210835
- if (ledger.failures > 0) return;
210836
- const now = (/* @__PURE__ */ new Date()).toISOString();
210837
- await store.appendMessage({
210838
- id: (0, import_node_crypto41.randomUUID)(),
210839
- sessionId: sid,
210840
- role: "user",
210841
- content: text7,
210842
- createdAt: now,
210843
- messageKey: chatSubmitMessageKey(sid, clientKey),
210844
- ...refs.length ? { attachments: refs } : {},
210845
- needsAgentDelivery: true
210859
+ await landUndeliveredInsert({
210860
+ store,
210861
+ itemStore,
210862
+ chatSessionId: sid,
210863
+ clientKey,
210864
+ text: shown,
210865
+ attachments: attachments ?? [],
210866
+ note: r.orphanedReturn ? INSERT_RETURNED_NOTE : INSERT_UNCERTAIN_NOTE
210846
210867
  });
210847
- await store.updateSession(sid, { touchedAt: now }).catch(() => void 0);
210848
- } catch (err) {
210849
- console.warn(`[chat] \u63D2\u8BDD\u7ED3\u679C\u4E0D\u786E\u5B9A\u3001\u843D\u672A\u9001\u8FBE user \u884C\u5931\u8D25\uFF08session=${sid} key=${clientKey}\uFF09: ${String(err)}`);
210850
210868
  }
210869
+ return { accepted: r.accepted, ...r.reason ? { reason: r.reason } : {}, queued: r.queued, insertStatus: r.insertStatus };
210851
210870
  };
210852
210871
  const GRAPH_EDIT_SUMMARY_MAX = 60;
210853
210872
  const truncateSummary = (s2) => {
@@ -211109,10 +211128,10 @@ async function startOasisServer(opts) {
211109
211128
  ...fp(p2)
211110
211129
  }))
211111
211130
  ] : [];
211112
- const { randomUUID: randomUUID47 } = await import("node:crypto");
211131
+ const { randomUUID: randomUUID48 } = await import("node:crypto");
211113
211132
  try {
211114
211133
  await store.enqueuePendingMessage({
211115
- id: randomUUID47(),
211134
+ id: randomUUID48(),
211116
211135
  chatSessionId: input.chatSessionId,
211117
211136
  text: input.text,
211118
211137
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -211145,11 +211164,11 @@ async function startOasisServer(opts) {
211145
211164
  ...f2?.mtime ? { srcMtime: f2.mtime } : {}
211146
211165
  };
211147
211166
  });
211148
- const { randomUUID: randomUUID47 } = await import("node:crypto");
211167
+ const { randomUUID: randomUUID48 } = await import("node:crypto");
211149
211168
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
211150
211169
  try {
211151
211170
  await store.enqueuePendingMessage({
211152
- id: randomUUID47(),
211171
+ id: randomUUID48(),
211153
211172
  chatSessionId: input.chatSessionId,
211154
211173
  text: "",
211155
211174
  createdAt: nowIso,
@@ -211559,7 +211578,7 @@ async function startOasisServer(opts) {
211559
211578
  return;
211560
211579
  }
211561
211580
  if (url.pathname.startsWith("/api/files/")) {
211562
- const company = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
211581
+ const company = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname, req.method) : void 0;
211563
211582
  if (company?.kind === "ambiguous") {
211564
211583
  writeApiError(res, companyAmbiguousError(actor, company.companies));
211565
211584
  return;
@@ -211596,7 +211615,7 @@ async function startOasisServer(opts) {
211596
211615
  return;
211597
211616
  }
211598
211617
  const sessionKey = (req.headers.authorization ?? "").startsWith("Bearer ") ? req.headers.authorization.slice("Bearer ".length) : actor;
211599
- const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
211618
+ const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname, req.method) : void 0;
211600
211619
  const currentCompanyId = companyCtx?.kind === "ok" ? companyCtx.companyId : void 0;
211601
211620
  const fileLimits = await opts.fileLimits?.get(currentCompanyId) ?? { ...DEFAULT_FILE_LIMITS };
211602
211621
  const requestMaxBytes = fileUploadRequestMaxBytes(fileLimits);
@@ -212451,7 +212470,7 @@ async function startOasisServer(opts) {
212451
212470
  entry = {
212452
212471
  // 旧 CLI 未带 waiterId 时给它塞一个服务端本地生成的——它绝不可能出现在 leave 上,
212453
212472
  // 也就绝不会误命中 leftWaiterIds、绝不会被别人的 leave 摘掉。行为与旧 CLI 相同。
212454
- waiterId: clientWaiterId ?? `server:${(0, import_node_crypto41.randomUUID)()}`,
212473
+ waiterId: clientWaiterId ?? `server:${(0, import_node_crypto42.randomUUID)()}`,
212455
212474
  onResolve,
212456
212475
  expiresAt: expiresAt2,
212457
212476
  cleanup: () => clearTimeout(expiryTimer),
@@ -212532,7 +212551,7 @@ async function startOasisServer(opts) {
212532
212551
  const openRoom = async () => {
212533
212552
  let chatSessionId2;
212534
212553
  if (chatStore) {
212535
- const { randomUUID: randomUUID47 } = await import("node:crypto");
212554
+ const { randomUUID: randomUUID48 } = await import("node:crypto");
212536
212555
  const known = discussSessions.get(itemKey);
212537
212556
  const knownSession = known ? await chatStore.getSession(known) : null;
212538
212557
  if (knownSession && knownSession.workspace === ctx.workspace && knownSession.humanActorId === ctx.ownerId && (!body2.escalationId || knownSession.escalationId === body2.escalationId)) chatSessionId2 = known;
@@ -212541,7 +212560,7 @@ async function startOasisServer(opts) {
212541
212560
  chatSessionId2 = existing[0]?.id;
212542
212561
  }
212543
212562
  if (!chatSessionId2) {
212544
- chatSessionId2 = randomUUID47();
212563
+ chatSessionId2 = randomUUID48();
212545
212564
  const now = (/* @__PURE__ */ new Date()).toISOString();
212546
212565
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
212547
212566
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
@@ -212796,6 +212815,19 @@ async function startOasisServer(opts) {
212796
212815
  changed();
212797
212816
  return;
212798
212817
  }
212818
+ if (topics?.size === 1 && topics.has("notifications")) {
212819
+ const changed = () => res.write('data: {"channel":"notifications"}\n\n');
212820
+ const unsubscribe = opts.notifications?.subscribe((n) => {
212821
+ if (n.kind === NOTIFICATIONS_CHANGED && n.target === actor) changed();
212822
+ });
212823
+ const heartbeat = setInterval(() => res.write(":heartbeat\n\n"), 15e3);
212824
+ req.on("close", () => {
212825
+ clearInterval(heartbeat);
212826
+ unsubscribe?.();
212827
+ });
212828
+ changed();
212829
+ return;
212830
+ }
212799
212831
  const wantRegistry = topics === null || topics.has("registry");
212800
212832
  const wsAllowed = (ws) => topics === null || ws !== void 0 && topics.has(ws);
212801
212833
  let closed = false;
@@ -212804,9 +212836,10 @@ async function startOasisServer(opts) {
212804
212836
 
212805
212837
  `);
212806
212838
  };
212807
- const unsubNotify = wantRegistry ? opts.notifications?.subscribe(
212808
- (n) => send({ channel: "registry", kind: n.kind, actor: n.actor, target: n.target, timestamp: n.timestamp })
212809
- ) : void 0;
212839
+ const unsubNotify = wantRegistry ? opts.notifications?.subscribe((n) => {
212840
+ if (n.kind === NOTIFICATIONS_CHANGED) return;
212841
+ send({ channel: "registry", kind: n.kind, actor: n.actor, target: n.target, timestamp: n.timestamp });
212842
+ }) : void 0;
212810
212843
  if (engineEvents) {
212811
212844
  let lastSeq = 0;
212812
212845
  const unsub = engineEvents((records) => {
@@ -213245,31 +213278,11 @@ async function startOasisServer(opts) {
213245
213278
  timeoutMs: APPEND_TURN_START_WAIT_MS
213246
213279
  });
213247
213280
  const appendItemStore = opts.resolveChatItems ? await opts.resolveChatItems(currentCompanyId).catch(() => void 0) : opts.chatItems;
213248
- await respondAppend(res, wantsAppendProgress(req), (onAwaitingConsumption) => appendNowThroughQueue2(sid, text7, clientKey, insertAttachments, perCompanyStore, retainLocallyOnFailure, onAwaitingConsumption, resend, appendItemStore));
213249
- return;
213250
- }
213251
- if (req.method === "GET" && /^\/api\/chat-sessions\/[^/]+\/insert-status$/.test(url.pathname)) {
213252
- const sid = decodeURIComponent(url.pathname.split("/")[3] ?? "");
213253
- const perCompanyStore = await chatStoreNowOrNull();
213254
- const cs = await perCompanyStore?.getSession(sid).catch(() => null) ?? null;
213255
- if (!perCompanyStore || !cs || cs.humanActorId !== actor) {
213256
- res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "CHAT_SESSION_NOT_FOUND" }));
213257
- return;
213258
- }
213259
- const clientKey = url.searchParams.get("clientKey") ?? "";
213260
- if (!clientKey) {
213261
- res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "CLIENT_KEY_REQUIRED" }));
213262
- return;
213263
- }
213264
- const items = opts.resolveChatItems ? await opts.resolveChatItems(currentCompanyId).catch(() => void 0) : opts.chatItems;
213265
- const result = await resolveInsertStatus({
213266
- store: perCompanyStore,
213267
- isTurnRunning: (id) => liveChat.isRunning(id),
213268
- ...opts.confirmInputReceipt ? { confirm: opts.confirmInputReceipt } : {},
213269
- ...items ? { items } : {},
213270
- log: (m2) => console.warn(m2)
213271
- }, sid, clientKey);
213272
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ clientKey, ...result }));
213281
+ let clientGone = false;
213282
+ res.once("close", () => {
213283
+ if (!res.writableEnded) clientGone = true;
213284
+ });
213285
+ await respondAppend(res, wantsAppendProgress(req), (onAwaitingConsumption) => appendNowThroughQueue2(sid, text7, clientKey, insertAttachments, perCompanyStore, retainLocallyOnFailure, onAwaitingConsumption, resend, appendItemStore, () => !clientGone));
213273
213286
  return;
213274
213287
  }
213275
213288
  if (url.pathname === "/api/chat" && req.method === "POST") {
@@ -213653,7 +213666,7 @@ ${composed}`;
213653
213666
  if (rejectedLedger.failures === 0 && retriedRow) {
213654
213667
  turnPersisted = true;
213655
213668
  } else if (rejectedLedger.failures === 0) {
213656
- const { randomUUID: randomUUID48 } = await import("node:crypto");
213669
+ const { randomUUID: randomUUID49 } = await import("node:crypto");
213657
213670
  const now = (/* @__PURE__ */ new Date()).toISOString();
213658
213671
  const rejectedAttachments = effectiveAttachments.map((a) => ({
213659
213672
  name: a.name,
@@ -213661,7 +213674,7 @@ ${composed}`;
213661
213674
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
213662
213675
  }));
213663
213676
  await chatStore.appendMessage({
213664
- id: randomUUID48(),
213677
+ id: randomUUID49(),
213665
213678
  sessionId: persistTarget.id,
213666
213679
  role: "user",
213667
213680
  content: persistedUserMessage,
@@ -213746,14 +213759,14 @@ ${composed}`;
213746
213759
  runtimeSessionId: session.nativeSessionId ?? session.id
213747
213760
  }).catch(() => void 0);
213748
213761
  }
213749
- const { randomUUID: randomUUID48 } = await import("node:crypto");
213762
+ const { randomUUID: randomUUID49 } = await import("node:crypto");
213750
213763
  const persistAttachments = effectiveAttachments.map((a) => ({
213751
213764
  name: a.name,
213752
213765
  ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
213753
213766
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
213754
213767
  }));
213755
213768
  if (!retriedRow) await chatStore.appendMessage({
213756
- id: randomUUID48(),
213769
+ id: randomUUID49(),
213757
213770
  sessionId: persistTarget.id,
213758
213771
  role: "user",
213759
213772
  content: persistedUserMessage,
@@ -214202,8 +214215,8 @@ ${composed}`;
214202
214215
  }
214203
214216
  }
214204
214217
  const { spawn: spawn11 } = await import("node:child_process");
214205
- const { randomUUID: randomUUID47 } = await import("node:crypto");
214206
- const sessionId = body2.sessionId ?? randomUUID47();
214218
+ const { randomUUID: randomUUID48 } = await import("node:crypto");
214219
+ const sessionId = body2.sessionId ?? randomUUID48();
214207
214220
  const args = [
214208
214221
  "-p",
214209
214222
  body2.message,
@@ -214292,33 +214305,32 @@ ${composed}`;
214292
214305
  }
214293
214306
  const svc = await connectorActorsService();
214294
214307
  if (!svc) throw new Error("actors service unavailable");
214295
- const cacheKey = `${currentCompanyId ?? ""}::${actorId}::${connectorId}`;
214308
+ const snapshots2 = opts.connectorAuthPending?.snapshots;
214309
+ if (!snapshots2) throw new Error("connector auth snapshot store unavailable");
214310
+ const snapKey = { companyId: currentCompanyId ?? "", actorId, connectorId };
214296
214311
  const now = Date.now();
214297
- for (const [k2, v2] of connectorAuthSnapshots) {
214298
- if (now - v2.at > CONNECTOR_AUTH_SNAPSHOT_TTL_MS) connectorAuthSnapshots.delete(k2);
214299
- }
214300
214312
  if (op === "begin") {
214301
214313
  const rows = await svc.snapshotActorConnectorVars(actorId, connectorId);
214302
- connectorAuthSnapshots.set(cacheKey, { rows, at: now });
214314
+ await snapshots2.put(snapKey, rows, now, now + CONNECTOR_AUTH_SNAPSHOT_TTL_MS);
214303
214315
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, snapshot: rows.length }));
214304
214316
  return;
214305
214317
  }
214306
214318
  if (op === "commit") {
214307
- connectorAuthSnapshots.delete(cacheKey);
214319
+ await snapshots2.delete(snapKey);
214308
214320
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
214309
214321
  return;
214310
214322
  }
214311
- const snap = connectorAuthSnapshots.get(cacheKey);
214312
- if (!snap) {
214323
+ const snapRows = await snapshots2.get(snapKey, now);
214324
+ if (!snapRows) {
214313
214325
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: false, reason: "no_snapshot" }));
214314
214326
  return;
214315
214327
  }
214316
214328
  const out = await svc.restoreActorConnectorVars(
214317
214329
  actorId,
214318
214330
  connectorId,
214319
- snap.rows
214331
+ snapRows
214320
214332
  );
214321
- connectorAuthSnapshots.delete(cacheKey);
214333
+ await snapshots2.delete(snapKey);
214322
214334
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, ...out }));
214323
214335
  } catch (e) {
214324
214336
  res.writeHead(500).end(JSON.stringify({ error: String(e) }));
@@ -214499,24 +214511,35 @@ ${composed}`;
214499
214511
  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}` }));
214500
214512
  return;
214501
214513
  }
214502
- const { randomUUID: randomUUID47 } = await import("node:crypto");
214503
- const state = randomUUID47();
214514
+ const { randomUUID: randomUUID48 } = await import("node:crypto");
214515
+ const state = randomUUID48();
214504
214516
  const nameSlug = (actorName || actorId?.split(":").pop() || owner?.login || "bot").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "bot";
214505
214517
  const manifest = buildAppManifest({
214506
214518
  employeeSlug: nameSlug,
214507
214519
  namePrefix: "oasis",
214508
214520
  baseUrl: base,
214509
- redirectPath: "/api/connectors/github/app/manifest-callback"
214521
+ redirectPath: "/api/connectors/github/app/manifest-callback",
214522
+ setupPath: "/api/connectors/github/app/setup-callback"
214510
214523
  });
214511
- githubAppPending.put(state, {
214524
+ const pendingStore = opts.connectorAuthPending?.githubApp;
214525
+ if (!pendingStore) throw new Error("github app pending store unavailable");
214526
+ const createdAtMs = Date.now();
214527
+ const entry = {
214512
214528
  ...actorId ? { actorId } : {},
214513
214529
  // 回调那一步没有鉴权头,解析不出公司——只能在这里记下来带过去。
214514
214530
  ...currentCompanyId ? { companyId: currentCompanyId } : {},
214515
214531
  employeeSlug: nameSlug,
214516
- createdAtMs: Date.now(),
214532
+ createdAtMs,
214517
214533
  // 存下这次选的归属——回调时要拿它跟 GitHub 返回的 owner 比对。
214518
214534
  wantOwnerKind: kind,
214519
214535
  ...kind === "org" && owner?.login ? { wantOwnerLogin: owner.login } : {}
214536
+ };
214537
+ await pendingStore.put({
214538
+ state,
214539
+ ...currentCompanyId ? { companyId: currentCompanyId } : {},
214540
+ value: { ...entry },
214541
+ createdAtMs,
214542
+ expiresAtMs: createdAtMs + PENDING_TTL_MS
214520
214543
  });
214521
214544
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
214522
214545
  state,
@@ -214831,16 +214854,22 @@ ${composed}`;
214831
214854
  if (url.pathname === "/api/connectors/github/app/manifest-callback" && req.method === "GET") {
214832
214855
  const code2 = url.searchParams.get("code") ?? "";
214833
214856
  const state = url.searchParams.get("state") ?? "";
214834
- const pending = githubAppPending.get(state, Date.now());
214835
- const fail = (msg) => {
214836
- if (pending) {
214837
- pending.done = true;
214838
- pending.error = msg;
214857
+ const pendingStore = opts.connectorAuthPending?.githubApp;
214858
+ const found2 = await lookupGithubAppPending(pendingStore, state);
214859
+ const pending = found2.pending;
214860
+ const fail = async (msg) => {
214861
+ if (pending && pendingStore) {
214862
+ await pendingStore.patch(state, { done: true, error: msg }).catch(() => {
214863
+ });
214839
214864
  }
214840
214865
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(callbackHtml(false, msg));
214841
214866
  };
214842
- if (!code2 || !pending) {
214843
- fail("state \u5DF2\u8FC7\u671F\u6216\u65E0\u6548\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u8BD5");
214867
+ if (!pending) {
214868
+ await fail(found2.message);
214869
+ return;
214870
+ }
214871
+ if (!code2) {
214872
+ await fail("GitHub \u6CA1\u6709\u5E26\u56DE\u5EFA\u597D\u7684 App \u4FE1\u606F\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u70B9\u4E00\u6B21\u300C\u8FDE\u63A5\u300D");
214844
214873
  return;
214845
214874
  }
214846
214875
  try {
@@ -214850,7 +214879,7 @@ ${composed}`;
214850
214879
  ...pending.wantOwnerKind ? { ownerKind: pending.wantOwnerKind } : {}
214851
214880
  });
214852
214881
  if (problem) {
214853
- fail(problem.message);
214882
+ await fail(problem.message);
214854
214883
  return;
214855
214884
  }
214856
214885
  const svc = await connectorActorsService(pending.companyId);
@@ -214862,29 +214891,77 @@ ${composed}`;
214862
214891
  if (conv.slug) await put3("GITHUB_APP_SLUG", conv.slug, false);
214863
214892
  if (conv.clientId) await put3("GITHUB_APP_CLIENT_ID", conv.clientId, false);
214864
214893
  if (conv.clientSecret) await put3("GITHUB_APP_CLIENT_SECRET", conv.clientSecret, true);
214865
- pending.appId = conv.appId;
214866
- pending.privateKeyPem = conv.privateKeyPem;
214867
- if (conv.slug) pending.slug = conv.slug;
214868
- pending.ownerLogin = conv.ownerLogin;
214869
- pending.ownerType = conv.ownerType;
214870
- pending.permissions = conv.permissions;
214871
- pending.account = conv.slug ? `${conv.slug}[bot]` : conv.appId;
214894
+ const progress = {
214895
+ appId: conv.appId,
214896
+ ...conv.slug ? { slug: conv.slug } : {},
214897
+ // 收尾那一屏要用:这个 bot 到手的是哪些权限、归属是谁(拼权限设置页地址要 ownerType)。
214898
+ ownerLogin: conv.ownerLogin,
214899
+ ownerType: conv.ownerType,
214900
+ permissions: conv.permissions,
214901
+ account: conv.slug ? `${conv.slug}[bot]` : conv.appId
214902
+ };
214903
+ await pendingStore.patch(state, progress);
214872
214904
  if (!conv.slug) {
214873
214905
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(callbackHtml(false, "App \u5DF2\u521B\u5EFA\uFF0C\u4F46\u672A\u62FF\u5230 slug\u2014\u2014\u8BF7\u624B\u52A8\u5728 GitHub \u4E0A\u5B89\u88C5\u5B83"));
214874
214906
  return;
214875
214907
  }
214876
214908
  res.writeHead(302, { location: appInstallUrl(conv.slug, state) }).end();
214877
214909
  } catch (e) {
214878
- fail(String(e));
214910
+ await fail(String(e));
214879
214911
  }
214880
214912
  return;
214881
214913
  }
214914
+ const finishGithubAppInstall = async (state, pending, pendingStore) => {
214915
+ if (pending.done && pending.error) return { ok: false, error: pending.error };
214916
+ if (pending.done) return { ok: true, ...pending.account ? { account: pending.account } : {} };
214917
+ if (!pending.appId) return { ok: false, error: "pending" };
214918
+ try {
214919
+ const keySvc = await connectorActorsService(pending.companyId);
214920
+ if (!keySvc) throw new Error("actors service unavailable");
214921
+ const privateKeyPem = await keySvc.revealVariable("GITHUB_APP_PRIVATE_KEY", pending.actorId);
214922
+ if (!privateKeyPem) throw new Error("GitHub App \u7684\u79C1\u94A5\u4E0D\u5728\u53D8\u91CF\u91CC\u4E86\uFF08\u53EF\u80FD\u88AB\u5220\u9664\uFF09\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u8FDE\u63A5");
214923
+ const installs = await listAppInstallations({
214924
+ appId: pending.appId,
214925
+ privateKeyPem
214926
+ });
214927
+ const first = installs[0];
214928
+ if (!first) return { ok: false, error: "pending" };
214929
+ const svc = await connectorActorsService(pending.companyId);
214930
+ if (!svc) throw new Error("actors service unavailable");
214931
+ const scope2 = pending.actorId ? { scope: "personal", actorId: pending.actorId } : { scope: "global" };
214932
+ const put3 = (key2, value2, encrypted) => svc.putVariable({ key: key2, value: value2, ...scope2, connectorId: "github", encrypted });
214933
+ await put3("GITHUB_APP_INSTALLATION_ID", first.installationId, false);
214934
+ const minted = await mintInstallationToken({
214935
+ appId: pending.appId,
214936
+ privateKeyPem,
214937
+ installationId: first.installationId
214938
+ });
214939
+ if (pending.actorId) {
214940
+ await put3("GITHUB_APP_TOKEN", minted.token, true);
214941
+ await put3("GITHUB_APP_TOKEN_EXPIRES_AT", String(minted.expiresAtMs), false);
214942
+ }
214943
+ if (pending.slug) {
214944
+ const bot = await fetchAppBotIdentity(pending.slug, { token: minted.token });
214945
+ await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
214946
+ await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${pending.slug}[bot]`, false);
214947
+ }
214948
+ await pendingStore.patch(state, { done: true });
214949
+ return { ok: true, account: pending.account ?? first.account };
214950
+ } catch (e) {
214951
+ const error2 = String(e);
214952
+ await pendingStore.patch(state, { error: error2 }).catch(() => {
214953
+ });
214954
+ return { ok: false, error: error2 };
214955
+ }
214956
+ };
214882
214957
  if (url.pathname === "/api/connectors/github/app/status" && req.method === "POST") {
214883
- const { state } = JSON.parse(rawBody || "{}");
214884
- const pending = state ? githubAppPending.get(state, Date.now()) : void 0;
214958
+ const { state = "" } = JSON.parse(rawBody || "{}");
214959
+ const pendingStore = opts.connectorAuthPending?.githubApp;
214960
+ const found2 = await lookupGithubAppPending(pendingStore, state);
214961
+ const pending = found2.pending;
214885
214962
  const reply = (body2) => res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(body2));
214886
- if (!pending) {
214887
- reply({ ok: false, error: "expired" });
214963
+ if (!pending || !pendingStore) {
214964
+ reply({ ok: false, error: found2.message });
214888
214965
  return;
214889
214966
  }
214890
214967
  if (pending.error) {
@@ -214909,46 +214986,28 @@ ${composed}`;
214909
214986
  reply({ ok: true, account: pending.account, ...permissionView() });
214910
214987
  return;
214911
214988
  }
214912
- if (!pending.appId || !pending.privateKeyPem) {
214913
- reply({ ok: false, error: "pending" });
214989
+ const result = await finishGithubAppInstall(state, pending, pendingStore);
214990
+ if (result.ok) reply({ ok: true, account: result.account ?? pending.account, ...permissionView() });
214991
+ else reply({ ok: false, error: result.error });
214992
+ return;
214993
+ }
214994
+ if (url.pathname === "/api/connectors/github/app/setup-callback" && req.method === "GET") {
214995
+ const state = url.searchParams.get("state") ?? "";
214996
+ const html = (ok3, msg) => res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(callbackHtml(ok3, msg));
214997
+ const pendingStore = opts.connectorAuthPending?.githubApp;
214998
+ const found2 = await lookupGithubAppPending(pendingStore, state);
214999
+ if (!found2.pending || !pendingStore) {
215000
+ html(false, state ? found2.message : "GitHub \u4E0A\u7684\u5B89\u88C5\u5DF2\u751F\u6548\uFF1B\u5982\u679C\u63A7\u5236\u53F0\u91CC\u8FD8\u6CA1\u663E\u793A\u5DF2\u8FDE\u63A5\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u70B9\u4E00\u6B21\u300C\u8FDE\u63A5\u300D");
214914
215001
  return;
214915
215002
  }
214916
- try {
214917
- const installs = await listAppInstallations({
214918
- appId: pending.appId,
214919
- privateKeyPem: pending.privateKeyPem
214920
- });
214921
- const first = installs[0];
214922
- if (!first) {
214923
- reply({ ok: false, error: "pending" });
214924
- return;
214925
- }
214926
- const svc = await connectorActorsService();
214927
- if (!svc) throw new Error("actors service unavailable");
214928
- const scope2 = pending.actorId ? { scope: "personal", actorId: pending.actorId } : { scope: "global" };
214929
- const put3 = (key2, value2, encrypted) => svc.putVariable({ key: key2, value: value2, ...scope2, connectorId: "github", encrypted });
214930
- await put3("GITHUB_APP_INSTALLATION_ID", first.installationId, false);
214931
- const minted = await mintInstallationToken({
214932
- appId: pending.appId,
214933
- privateKeyPem: pending.privateKeyPem,
214934
- installationId: first.installationId
214935
- });
214936
- if (pending.actorId) {
214937
- await put3("GITHUB_APP_TOKEN", minted.token, true);
214938
- await put3("GITHUB_APP_TOKEN_EXPIRES_AT", String(minted.expiresAtMs), false);
214939
- }
214940
- if (pending.slug) {
214941
- const bot = await fetchAppBotIdentity(pending.slug, { token: minted.token });
214942
- await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
214943
- await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${pending.slug}[bot]`, false);
214944
- }
214945
- pending.done = true;
214946
- delete pending.privateKeyPem;
214947
- reply({ ok: true, account: pending.account ?? first.account, ...permissionView() });
214948
- } catch (e) {
214949
- pending.error = String(e);
214950
- reply({ ok: false, error: pending.error });
215003
+ if (url.searchParams.get("setup_action") === "request") {
215004
+ html(false, "\u5DF2\u5411\u7EC4\u7EC7\u7BA1\u7406\u5458\u63D0\u4EA4\u5B89\u88C5\u7533\u8BF7\uFF0C\u6279\u51C6\u540E\u56DE\u5230\u63A7\u5236\u53F0\u91CD\u65B0\u70B9\u4E00\u6B21\u300C\u8FDE\u63A5\u300D");
215005
+ return;
214951
215006
  }
215007
+ const result = await finishGithubAppInstall(state, found2.pending, pendingStore);
215008
+ if (result.ok) html(true, `\u5DF2\u8FDE\u63A5\u4E3A ${result.account ?? found2.pending.account ?? "GitHub App"}`);
215009
+ else if (result.error === "pending") html(false, "GitHub \u8FD8\u6CA1\u67E5\u5230\u8FD9\u6B21\u5B89\u88C5\uFF0C\u8BF7\u56DE\u5230\u63A7\u5236\u53F0\u7A0D\u540E\u91CD\u8BD5");
215010
+ else html(false, result.error);
214952
215011
  return;
214953
215012
  }
214954
215013
  if (url.pathname === "/api/connectors/feishu/login/start" && req.method === "POST") {
@@ -215325,14 +215384,14 @@ async function markDeliveredWithRetry(store, messageId, attempts = 3) {
215325
215384
  }
215326
215385
  return false;
215327
215386
  }
215328
- var http, import_node_crypto41, githubAppPending, connectorAuthSnapshots, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, APPEND_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
215387
+ var http, import_node_crypto42, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, APPEND_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
215329
215388
  var init_server3 = __esm({
215330
215389
  "../server/src/server.ts"() {
215331
215390
  "use strict";
215332
215391
  init_src();
215333
215392
  init_file_limits2();
215334
215393
  http = __toESM(require("node:http"), 1);
215335
- import_node_crypto41 = require("node:crypto");
215394
+ import_node_crypto42 = require("node:crypto");
215336
215395
  init_chat_session();
215337
215396
  init_chat_system_message();
215338
215397
  init_sealed_node_receipt();
@@ -215364,7 +215423,6 @@ var init_server3 = __esm({
215364
215423
  init_live_chat();
215365
215424
  init_chat_turn_wait();
215366
215425
  init_append_now();
215367
- init_insert_status();
215368
215426
  init_append_progress();
215369
215427
  init_workorder_drafts();
215370
215428
  init_draft_edit();
@@ -215385,6 +215443,7 @@ var init_server3 = __esm({
215385
215443
  init_chat_turn_gate();
215386
215444
  init_chat_submission();
215387
215445
  init_chat_insert_evidence();
215446
+ init_insert_undelivered();
215388
215447
  init_chat_workorder_files();
215389
215448
  init_chat_item_ledger();
215390
215449
  init_chat_cancel_attribution();
@@ -215396,10 +215455,9 @@ var init_server3 = __esm({
215396
215455
  init_knowledge2();
215397
215456
  init_assembly();
215398
215457
  init_child_live_turn();
215458
+ init_registry_audit_sink();
215399
215459
  init_src8();
215400
215460
  init_continuation3();
215401
- githubAppPending = new PendingAppCreations();
215402
- connectorAuthSnapshots = /* @__PURE__ */ new Map();
215403
215461
  CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
215404
215462
  defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
215405
215463
  enc = (s2) => new TextEncoder().encode(s2);
@@ -215495,25 +215553,25 @@ function createTokenIssuer(opts = {}) {
215495
215553
  function createNodeTokenStore(file) {
215496
215554
  const read3 = () => {
215497
215555
  try {
215498
- return JSON.parse(fs20.readFileSync(file, "utf8"));
215556
+ return JSON.parse(fs21.readFileSync(file, "utf8"));
215499
215557
  } catch {
215500
215558
  return {};
215501
215559
  }
215502
215560
  };
215503
- const write = (table) => fs20.writeFileSync(file, JSON.stringify(table, null, 2), { mode: 384 });
215561
+ const write = (table) => fs21.writeFileSync(file, JSON.stringify(table, null, 2), { mode: 384 });
215504
215562
  const revokedFile = file.replace(/(\.json)?$/, "-revoked.json");
215505
215563
  const readRevoked = () => {
215506
215564
  try {
215507
- return JSON.parse(fs20.readFileSync(revokedFile, "utf8"));
215565
+ return JSON.parse(fs21.readFileSync(revokedFile, "utf8"));
215508
215566
  } catch {
215509
215567
  return {};
215510
215568
  }
215511
215569
  };
215512
- const writeRevoked = (table) => fs20.writeFileSync(revokedFile, JSON.stringify(table, null, 2), { mode: 384 });
215570
+ const writeRevoked = (table) => fs21.writeFileSync(revokedFile, JSON.stringify(table, null, 2), { mode: 384 });
215513
215571
  return {
215514
215572
  issue(nodeId) {
215515
215573
  const table = read3();
215516
- const token = `ont_${(0, import_node_crypto42.randomBytes)(24).toString("base64url")}`;
215574
+ const token = `ont_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
215517
215575
  table[token] = nodeId;
215518
215576
  write(table);
215519
215577
  return token;
@@ -215553,14 +215611,14 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215553
215611
  const read3 = () => {
215554
215612
  if (!file) return table;
215555
215613
  try {
215556
- return new Map(Object.entries(JSON.parse(fs20.readFileSync(file, "utf8"))));
215614
+ return new Map(Object.entries(JSON.parse(fs21.readFileSync(file, "utf8"))));
215557
215615
  } catch {
215558
215616
  return /* @__PURE__ */ new Map();
215559
215617
  }
215560
215618
  };
215561
215619
  const write = (m2) => {
215562
215620
  if (!file) return;
215563
- fs20.writeFileSync(file, JSON.stringify(Object.fromEntries(m2), null, 2), { mode: 384 });
215621
+ fs21.writeFileSync(file, JSON.stringify(Object.fromEntries(m2), null, 2), { mode: 384 });
215564
215622
  };
215565
215623
  const table = /* @__PURE__ */ new Map();
215566
215624
  const prune = () => {
@@ -215573,7 +215631,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215573
215631
  return {
215574
215632
  issue(name, ttlMs = defaultTtlMs, companyId) {
215575
215633
  const m2 = prune();
215576
- const token = `ent_${(0, import_node_crypto42.randomBytes)(24).toString("base64url")}`;
215634
+ const token = `ent_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
215577
215635
  const expiresAt2 = Date.now() + ttlMs;
215578
215636
  m2.set(token, { name, expiresAt: expiresAt2, ...companyId !== void 0 ? { companyId } : {} });
215579
215637
  write(m2);
@@ -215622,33 +215680,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215622
215680
  }
215623
215681
  };
215624
215682
  }
215625
- var import_node_crypto42, fs20, path20, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
215683
+ var import_node_crypto43, fs21, path20, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
215626
215684
  var init_tokens = __esm({
215627
215685
  "../server/src/tokens.ts"() {
215628
215686
  "use strict";
215629
- import_node_crypto42 = require("node:crypto");
215630
- fs20 = __toESM(require("node:fs"), 1);
215687
+ import_node_crypto43 = require("node:crypto");
215688
+ fs21 = __toESM(require("node:fs"), 1);
215631
215689
  path20 = __toESM(require("node:path"), 1);
215632
215690
  SESSION_TOKEN_PREFIX = "oat_v2_";
215633
215691
  b64url2 = (value2) => Buffer.from(value2).toString("base64url");
215634
215692
  fromB64url = (value2) => Buffer.from(value2, "base64url").toString("utf8");
215635
215693
  readOrCreateSecret = (file) => {
215636
- if (!file) return (0, import_node_crypto42.randomBytes)(32);
215694
+ if (!file) return (0, import_node_crypto43.randomBytes)(32);
215637
215695
  try {
215638
- const raw = fs20.readFileSync(file, "utf8").trim();
215696
+ const raw = fs21.readFileSync(file, "utf8").trim();
215639
215697
  if (raw) return Buffer.from(raw, "base64url");
215640
215698
  } catch {
215641
215699
  }
215642
- const secret = (0, import_node_crypto42.randomBytes)(32);
215643
- fs20.mkdirSync(path20.dirname(file), { recursive: true });
215644
- fs20.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215700
+ const secret = (0, import_node_crypto43.randomBytes)(32);
215701
+ fs21.mkdirSync(path20.dirname(file), { recursive: true });
215702
+ fs21.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215645
215703
  return secret;
215646
215704
  };
215647
- sign = (secret, payload2) => (0, import_node_crypto42.createHmac)("sha256", secret).update(payload2).digest("base64url");
215705
+ sign = (secret, payload2) => (0, import_node_crypto43.createHmac)("sha256", secret).update(payload2).digest("base64url");
215648
215706
  signatureMatches = (actual, expected) => {
215649
215707
  const a = Buffer.from(actual);
215650
215708
  const b2 = Buffer.from(expected);
215651
- return a.length === b2.length && (0, import_node_crypto42.timingSafeEqual)(a, b2);
215709
+ return a.length === b2.length && (0, import_node_crypto43.timingSafeEqual)(a, b2);
215652
215710
  };
215653
215711
  isTokenClaims = (value2) => {
215654
215712
  if (value2 === null || typeof value2 !== "object") return false;
@@ -215670,6 +215728,9 @@ var init_tokens = __esm({
215670
215728
  function isOperatorRequest(auth) {
215671
215729
  return auth.scopes?.includes(OPERATOR_SCOPE) === true;
215672
215730
  }
215731
+ function operatorCrossCompanyRead(auth, method) {
215732
+ return method === "GET" && isOperatorRequest(auth);
215733
+ }
215673
215734
  var OPERATOR_SCOPE;
215674
215735
  var init_operator = __esm({
215675
215736
  "../server/src/auth/operator.ts"() {
@@ -215707,33 +215768,33 @@ function createDataplaneTokenIssuer(opts = {}) {
215707
215768
  }
215708
215769
  };
215709
215770
  }
215710
- var import_node_crypto43, fs21, path21, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
215771
+ var import_node_crypto44, fs22, path21, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
215711
215772
  var init_session_dataplane_token = __esm({
215712
215773
  "../server/src/session-dataplane-token.ts"() {
215713
215774
  "use strict";
215714
- import_node_crypto43 = require("node:crypto");
215715
- fs21 = __toESM(require("node:fs"), 1);
215775
+ import_node_crypto44 = require("node:crypto");
215776
+ fs22 = __toESM(require("node:fs"), 1);
215716
215777
  path21 = __toESM(require("node:path"), 1);
215717
215778
  init_src();
215718
215779
  PREFIX2 = "osd_";
215719
215780
  b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
215720
215781
  fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
215721
- sign2 = (secret, payload2) => (0, import_node_crypto43.createHmac)("sha256", secret).update(payload2).digest("base64url");
215782
+ sign2 = (secret, payload2) => (0, import_node_crypto44.createHmac)("sha256", secret).update(payload2).digest("base64url");
215722
215783
  sigMatches = (actual, expected) => {
215723
215784
  const a = Buffer.from(actual);
215724
215785
  const b2 = Buffer.from(expected);
215725
- return a.length === b2.length && (0, import_node_crypto43.timingSafeEqual)(a, b2);
215786
+ return a.length === b2.length && (0, import_node_crypto44.timingSafeEqual)(a, b2);
215726
215787
  };
215727
215788
  readOrCreateSecret2 = (file) => {
215728
- if (!file) return (0, import_node_crypto43.randomBytes)(32);
215789
+ if (!file) return (0, import_node_crypto44.randomBytes)(32);
215729
215790
  try {
215730
- const raw = fs21.readFileSync(file, "utf8").trim();
215791
+ const raw = fs22.readFileSync(file, "utf8").trim();
215731
215792
  if (raw) return Buffer.from(raw, "base64url");
215732
215793
  } catch {
215733
215794
  }
215734
- const secret = (0, import_node_crypto43.randomBytes)(32);
215735
- fs21.mkdirSync(path21.dirname(file), { recursive: true });
215736
- fs21.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215795
+ const secret = (0, import_node_crypto44.randomBytes)(32);
215796
+ fs22.mkdirSync(path21.dirname(file), { recursive: true });
215797
+ fs22.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215737
215798
  return secret;
215738
215799
  };
215739
215800
  isClaims = (v2) => {
@@ -218406,11 +218467,11 @@ function onlyHits(pool, q2) {
218406
218467
  if (!q2.requireMatch || q2.keywords.length === 0) return [...pool];
218407
218468
  return pool.filter((r) => memoryMatchScore(r, q2.keywords) > 0);
218408
218469
  }
218409
- var import_node_crypto44, MemoryActorMemoryStore, toIndexEntry;
218470
+ var import_node_crypto45, MemoryActorMemoryStore, toIndexEntry;
218410
218471
  var init_memory_actor_memory_store = __esm({
218411
218472
  "../testkit/src/memory-actor-memory-store.ts"() {
218412
218473
  "use strict";
218413
- import_node_crypto44 = require("node:crypto");
218474
+ import_node_crypto45 = require("node:crypto");
218414
218475
  init_src();
218415
218476
  MemoryActorMemoryStore = class {
218416
218477
  rows = /* @__PURE__ */ new Map();
@@ -218464,7 +218525,7 @@ var init_memory_actor_memory_store = __esm({
218464
218525
  async write(input, now) {
218465
218526
  if (input.memId === void 0) {
218466
218527
  const rec2 = {
218467
- memId: `mem:${(0, import_node_crypto44.randomUUID)()}`,
218528
+ memId: `mem:${(0, import_node_crypto45.randomUUID)()}`,
218468
218529
  actorId: input.actorId,
218469
218530
  projectId: input.projectId,
218470
218531
  keywords: [...input.keywords],
@@ -219275,13 +219336,13 @@ var init_stats = __esm({
219275
219336
 
219276
219337
  // ../server/src/domains/projects/project-id.ts
219277
219338
  function randomProjectId() {
219278
- return projectIdFromUuid((0, import_node_crypto45.randomUUID)());
219339
+ return projectIdFromUuid((0, import_node_crypto46.randomUUID)());
219279
219340
  }
219280
- var import_node_crypto45;
219341
+ var import_node_crypto46;
219281
219342
  var init_project_id = __esm({
219282
219343
  "../server/src/domains/projects/project-id.ts"() {
219283
219344
  "use strict";
219284
- import_node_crypto45 = require("node:crypto");
219345
+ import_node_crypto46 = require("node:crypto");
219285
219346
  init_src();
219286
219347
  }
219287
219348
  });
@@ -221356,13 +221417,13 @@ function applyTurnPatch(t, patch) {
221356
221417
  ...patch.lastError !== void 0 ? { lastError: patch.lastError } : {}
221357
221418
  };
221358
221419
  }
221359
- var import_node_crypto46, fs22, path22, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
221420
+ var import_node_crypto47, fs23, path22, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
221360
221421
  var init_dev_store = __esm({
221361
221422
  "../server/src/dev-store.ts"() {
221362
221423
  "use strict";
221363
221424
  init_src();
221364
- import_node_crypto46 = require("node:crypto");
221365
- fs22 = __toESM(require("node:fs"), 1);
221425
+ import_node_crypto47 = require("node:crypto");
221426
+ fs23 = __toESM(require("node:fs"), 1);
221366
221427
  path22 = __toESM(require("node:path"), 1);
221367
221428
  init_src();
221368
221429
  init_src9();
@@ -221376,8 +221437,8 @@ var init_dev_store = __esm({
221376
221437
  }
221377
221438
  static async open(file) {
221378
221439
  const memory = new MemoryOplogStore();
221379
- if (fs22.existsSync(file)) {
221380
- for (const line2 of fs22.readFileSync(file, "utf8").split("\n")) {
221440
+ if (fs23.existsSync(file)) {
221441
+ for (const line2 of fs23.readFileSync(file, "utf8").split("\n")) {
221381
221442
  if (!line2.trim()) continue;
221382
221443
  const stored = JSON.parse(line2);
221383
221444
  const { artifactId, seq, ...op } = stored;
@@ -221385,15 +221446,15 @@ var init_dev_store = __esm({
221385
221446
  if (assigned !== seq) throw new Error(`corrupt ndjson log: seq mismatch at ${line2}`);
221386
221447
  }
221387
221448
  } else {
221388
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
221389
- fs22.writeFileSync(file, "");
221449
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
221450
+ fs23.writeFileSync(file, "");
221390
221451
  }
221391
221452
  return new _NdjsonOplogStore(file, memory);
221392
221453
  }
221393
221454
  async append(artifactId, op, expectedSeq) {
221394
221455
  const seq = await this.memory.append(artifactId, op, expectedSeq);
221395
221456
  const stored = { ...op, artifactId, seq };
221396
- fs22.appendFileSync(this.file, JSON.stringify(stored) + "\n");
221457
+ fs23.appendFileSync(this.file, JSON.stringify(stored) + "\n");
221397
221458
  return seq;
221398
221459
  }
221399
221460
  read(artifactId, fromSeq) {
@@ -221409,39 +221470,39 @@ var init_dev_store = __esm({
221409
221470
  DirBlobStore = class {
221410
221471
  constructor(dir) {
221411
221472
  this.dir = dir;
221412
- fs22.mkdirSync(dir, { recursive: true });
221473
+ fs23.mkdirSync(dir, { recursive: true });
221413
221474
  }
221414
221475
  fileOf(hash3) {
221415
221476
  return path22.join(this.dir, hash3);
221416
221477
  }
221417
221478
  async put(bytes2) {
221418
- const hash3 = (0, import_node_crypto46.createHash)("sha256").update(bytes2).digest("hex");
221479
+ const hash3 = (0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex");
221419
221480
  const file = this.fileOf(hash3);
221420
- if (!fs22.existsSync(file)) fs22.writeFileSync(file, bytes2);
221481
+ if (!fs23.existsSync(file)) fs23.writeFileSync(file, bytes2);
221421
221482
  return hash3;
221422
221483
  }
221423
221484
  async meta(hash3) {
221424
221485
  const file = this.fileOf(hash3);
221425
- if (!fs22.existsSync(file)) return null;
221426
- const size = fs22.statSync(file).size;
221427
- const fd = fs22.openSync(file, "r");
221486
+ if (!fs23.existsSync(file)) return null;
221487
+ const size = fs23.statSync(file).size;
221488
+ const fd = fs23.openSync(file, "r");
221428
221489
  const head = Buffer.alloc(16);
221429
- fs22.readSync(fd, head, 0, 16, 0);
221430
- fs22.closeSync(fd);
221490
+ fs23.readSync(fd, head, 0, 16, 0);
221491
+ fs23.closeSync(fd);
221431
221492
  const contentType = sniffContentType(new Uint8Array(head));
221432
221493
  return { size, ...contentType ? { contentType } : {} };
221433
221494
  }
221434
221495
  async get(hash3) {
221435
221496
  const file = this.fileOf(hash3);
221436
- if (!fs22.existsSync(file)) throw new BlobNotFoundError(hash3);
221437
- return new Uint8Array(fs22.readFileSync(file));
221497
+ if (!fs23.existsSync(file)) throw new BlobNotFoundError(hash3);
221498
+ return new Uint8Array(fs23.readFileSync(file));
221438
221499
  }
221439
221500
  async has(hash3) {
221440
- return fs22.existsSync(this.fileOf(hash3));
221501
+ return fs23.existsSync(this.fileOf(hash3));
221441
221502
  }
221442
221503
  async sweep(reachable) {
221443
- for (const name of fs22.readdirSync(this.dir)) {
221444
- if (!reachable.has(name)) fs22.unlinkSync(this.fileOf(name));
221504
+ for (const name of fs23.readdirSync(this.dir)) {
221505
+ if (!reachable.has(name)) fs23.unlinkSync(this.fileOf(name));
221445
221506
  }
221446
221507
  }
221447
221508
  };
@@ -221470,7 +221531,7 @@ var init_dev_store = __esm({
221470
221531
  this.file = file;
221471
221532
  }
221472
221533
  static async open(file) {
221473
- const seed = fs22.existsSync(file) ? JSON.parse(fs22.readFileSync(file, "utf8")) : [];
221534
+ const seed = fs23.existsSync(file) ? JSON.parse(fs23.readFileSync(file, "utf8")) : [];
221474
221535
  return new _FileTypeRegistryStore(file, seed);
221475
221536
  }
221476
221537
  async put(def) {
@@ -221483,8 +221544,8 @@ var init_dev_store = __esm({
221483
221544
  }
221484
221545
  async save() {
221485
221546
  const tmp = `${this.file}.tmp`;
221486
- fs22.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
221487
- fs22.renameSync(tmp, this.file);
221547
+ fs23.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
221548
+ fs23.renameSync(tmp, this.file);
221488
221549
  }
221489
221550
  };
221490
221551
  FileRoleRegistryStore = class _FileRoleRegistryStore extends MemoryRoleRegistryStore {
@@ -221493,7 +221554,7 @@ var init_dev_store = __esm({
221493
221554
  this.file = file;
221494
221555
  }
221495
221556
  static async open(file) {
221496
- const seed = fs22.existsSync(file) ? JSON.parse(fs22.readFileSync(file, "utf8")) : [];
221557
+ const seed = fs23.existsSync(file) ? JSON.parse(fs23.readFileSync(file, "utf8")) : [];
221497
221558
  return new _FileRoleRegistryStore(file, seed);
221498
221559
  }
221499
221560
  async put(def) {
@@ -221506,8 +221567,8 @@ var init_dev_store = __esm({
221506
221567
  }
221507
221568
  async save() {
221508
221569
  const tmp = `${this.file}.tmp`;
221509
- fs22.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
221510
- fs22.renameSync(tmp, this.file);
221570
+ fs23.writeFileSync(tmp, JSON.stringify(await this.list(), null, 2));
221571
+ fs23.renameSync(tmp, this.file);
221511
221572
  }
221512
221573
  };
221513
221574
  FileRegistryStore = class _FileRegistryStore extends MemoryRegistryStore {
@@ -221525,8 +221586,8 @@ var init_dev_store = __esm({
221525
221586
  }
221526
221587
  static async open(file) {
221527
221588
  const store = new _FileRegistryStore(file);
221528
- if (fs22.existsSync(file)) {
221529
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
221589
+ if (fs23.existsSync(file)) {
221590
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
221530
221591
  for (const a of snap.actors) await MemoryRegistryStore.prototype.upsertActor.call(store, a);
221531
221592
  for (const c of snap.configs) await MemoryRegistryStore.prototype.appendConfig.call(store, c);
221532
221593
  for (const t of snap.teams) await MemoryRegistryStore.prototype.upsertTeam.call(store, t);
@@ -221567,7 +221628,7 @@ var init_dev_store = __esm({
221567
221628
  skillFiles: skillFilesEntries,
221568
221629
  connections
221569
221630
  };
221570
- fs22.writeFileSync(this.file, JSON.stringify(snap, null, 2));
221631
+ fs23.writeFileSync(this.file, JSON.stringify(snap, null, 2));
221571
221632
  }
221572
221633
  };
221573
221634
  FileAssistantBindStore = class _FileAssistantBindStore extends MemoryAssistantBindStore {
@@ -221585,11 +221646,11 @@ var init_dev_store = __esm({
221585
221646
  }
221586
221647
  static async open(file) {
221587
221648
  const store = new _FileAssistantBindStore(file);
221588
- if (fs22.existsSync(file)) store._load(JSON.parse(fs22.readFileSync(file, "utf8")));
221649
+ if (fs23.existsSync(file)) store._load(JSON.parse(fs23.readFileSync(file, "utf8")));
221589
221650
  return store;
221590
221651
  }
221591
221652
  async save() {
221592
- fs22.writeFileSync(this.file, JSON.stringify(this._dump(), null, 2));
221653
+ fs23.writeFileSync(this.file, JSON.stringify(this._dump(), null, 2));
221593
221654
  }
221594
221655
  };
221595
221656
  FileHumanPrefsStore = class _FileHumanPrefsStore extends MemoryHumanPrefsStore {
@@ -221607,11 +221668,11 @@ var init_dev_store = __esm({
221607
221668
  }
221608
221669
  static async open(file) {
221609
221670
  const store = new _FileHumanPrefsStore(file);
221610
- if (fs22.existsSync(file)) store._load(JSON.parse(fs22.readFileSync(file, "utf8")));
221671
+ if (fs23.existsSync(file)) store._load(JSON.parse(fs23.readFileSync(file, "utf8")));
221611
221672
  return store;
221612
221673
  }
221613
221674
  async save() {
221614
- fs22.writeFileSync(this.file, JSON.stringify(this._dump(), null, 2));
221675
+ fs23.writeFileSync(this.file, JSON.stringify(this._dump(), null, 2));
221615
221676
  }
221616
221677
  };
221617
221678
  FileProjectStateStore = class _FileProjectStateStore extends MemoryProjectStateStore {
@@ -221621,8 +221682,8 @@ var init_dev_store = __esm({
221621
221682
  }
221622
221683
  static async open(file) {
221623
221684
  const store = new _FileProjectStateStore(file);
221624
- if (fs22.existsSync(file)) {
221625
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
221685
+ if (fs23.existsSync(file)) {
221686
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
221626
221687
  for (const project of snap.projects ?? []) await MemoryProjectStateStore.prototype.upsertProject.call(store, project);
221627
221688
  const membersByProject = /* @__PURE__ */ new Map();
221628
221689
  for (const member of snap.members ?? []) {
@@ -221634,8 +221695,8 @@ var init_dev_store = __esm({
221634
221695
  await MemoryProjectStateStore.prototype.replaceProjectMembers.call(store, projectId2, members);
221635
221696
  }
221636
221697
  } else {
221637
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
221638
- fs22.writeFileSync(file, JSON.stringify({ projects: [], members: [] }, null, 2));
221698
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
221699
+ fs23.writeFileSync(file, JSON.stringify({ projects: [], members: [] }, null, 2));
221639
221700
  }
221640
221701
  return store;
221641
221702
  }
@@ -221654,7 +221715,7 @@ var init_dev_store = __esm({
221654
221715
  async save() {
221655
221716
  const projects = await this.listProjects();
221656
221717
  const members = (await Promise.all(projects.map((project) => this.listProjectMembers(project.id)))).flat();
221657
- fs22.writeFileSync(this.file, JSON.stringify({ projects, members }, null, 2));
221718
+ fs23.writeFileSync(this.file, JSON.stringify({ projects, members }, null, 2));
221658
221719
  }
221659
221720
  };
221660
221721
  FileArtifactStateStore = class _FileArtifactStateStore extends MemoryArtifactStateStore {
@@ -221664,8 +221725,8 @@ var init_dev_store = __esm({
221664
221725
  }
221665
221726
  static async open(file) {
221666
221727
  const store = new _FileArtifactStateStore(file);
221667
- if (fs22.existsSync(file)) {
221668
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
221728
+ if (fs23.existsSync(file)) {
221729
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
221669
221730
  for (const artifact of snap.artifacts ?? []) await MemoryArtifactStateStore.prototype.upsertArtifact.call(store, artifact);
221670
221731
  for (const revision of snap.revisions ?? []) await MemoryArtifactStateStore.prototype.upsertRevision.call(store, revision);
221671
221732
  for (const event of snap.events ?? []) await MemoryArtifactStateStore.prototype.appendEvent.call(store, event);
@@ -221675,8 +221736,8 @@ var init_dev_store = __esm({
221675
221736
  for (const ws of snap.workspaceDispatchHeld ?? []) await MemoryArtifactStateStore.prototype.setWorkspaceDispatchHold.call(store, ws, true);
221676
221737
  for (const f2 of snap.workOrderFiles ?? []) await MemoryArtifactStateStore.prototype.addWorkOrderFile.call(store, f2);
221677
221738
  } else {
221678
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
221679
- fs22.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [], workOrderFiles: [] }, null, 2));
221739
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
221740
+ fs23.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [], workOrderFiles: [] }, null, 2));
221680
221741
  }
221681
221742
  return store;
221682
221743
  }
@@ -221713,7 +221774,7 @@ var init_dev_store = __esm({
221713
221774
  await this.save();
221714
221775
  }
221715
221776
  async save() {
221716
- fs22.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
221777
+ fs23.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
221717
221778
  }
221718
221779
  };
221719
221780
  FileTraceStore = class _FileTraceStore extends MemoryTraceStore {
@@ -221740,25 +221801,25 @@ var init_dev_store = __esm({
221740
221801
  return path22.join(this.eventsDir, `${encodeURIComponent(runId)}.ndjson`);
221741
221802
  }
221742
221803
  appendLine(file, obj2) {
221743
- fs22.appendFileSync(file, JSON.stringify(obj2) + "\n");
221804
+ fs23.appendFileSync(file, JSON.stringify(obj2) + "\n");
221744
221805
  }
221745
221806
  static replay(file, fn) {
221746
- if (!fs22.existsSync(file)) return;
221747
- for (const line2 of fs22.readFileSync(file, "utf8").split("\n")) {
221807
+ if (!fs23.existsSync(file)) return;
221808
+ for (const line2 of fs23.readFileSync(file, "utf8").split("\n")) {
221748
221809
  if (!line2.trim()) continue;
221749
221810
  fn(JSON.parse(line2));
221750
221811
  }
221751
221812
  }
221752
221813
  static async open(dir) {
221753
221814
  const store = new _FileTraceStore(dir);
221754
- fs22.mkdirSync(store.eventsDir, { recursive: true });
221815
+ fs23.mkdirSync(store.eventsDir, { recursive: true });
221755
221816
  _FileTraceStore.replay(store.runsFile, (r) => store.restoreRun(r));
221756
- for (const name of fs22.readdirSync(store.eventsDir)) {
221817
+ for (const name of fs23.readdirSync(store.eventsDir)) {
221757
221818
  _FileTraceStore.replay(path22.join(store.eventsDir, name), (e) => store.restoreEvent(e));
221758
221819
  }
221759
221820
  _FileTraceStore.replay(store.toolCallsFile, (c) => store.restoreToolCall(c));
221760
221821
  _FileTraceStore.replay(store.linksFile, (l) => store.restoreLink(l));
221761
- store.restoreContinuity(fs22.existsSync(store.continuityFile) ? JSON.parse(fs22.readFileSync(store.continuityFile, "utf8")) : {});
221822
+ store.restoreContinuity(fs23.existsSync(store.continuityFile) ? JSON.parse(fs23.readFileSync(store.continuityFile, "utf8")) : {});
221762
221823
  return store;
221763
221824
  }
221764
221825
  async createRun(input) {
@@ -221810,8 +221871,8 @@ var init_dev_store = __esm({
221810
221871
  }
221811
221872
  saveContinuity() {
221812
221873
  const tmp = `${this.continuityFile}.tmp`;
221813
- fs22.writeFileSync(tmp, JSON.stringify(this.continuitySnapshot(), null, 2));
221814
- fs22.renameSync(tmp, this.continuityFile);
221874
+ fs23.writeFileSync(tmp, JSON.stringify(this.continuitySnapshot(), null, 2));
221875
+ fs23.renameSync(tmp, this.continuityFile);
221815
221876
  }
221816
221877
  async appendEvents(runId, inputs) {
221817
221878
  const existing = await super.listEvents(runId, {});
@@ -222276,8 +222337,8 @@ var init_dev_store = __esm({
222276
222337
  }
222277
222338
  static async open(file) {
222278
222339
  const store = new _FileChatSessionStore(file);
222279
- if (fs22.existsSync(file)) {
222280
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
222340
+ if (fs23.existsSync(file)) {
222341
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
222281
222342
  for (const [sid, copies] of Object.entries(snap.outputCopies ?? {})) store.outputCopies.set(sid, copies);
222282
222343
  for (const [sid, fps] of Object.entries(snap.outputFingerprints ?? {})) store.outputFingerprints.set(sid, fps);
222283
222344
  for (const [sid, issues] of Object.entries(snap.fileIssues ?? {})) store.fileIssues.set(sid, issues);
@@ -222287,8 +222348,8 @@ var init_dev_store = __esm({
222287
222348
  for (const [sid, kinds] of Object.entries(snap.sessionWorkOrderKinds ?? {})) store.sessionWorkOrderKinds.set(sid, new Map(Object.entries(kinds)));
222288
222349
  for (const [sid, list2] of Object.entries(snap.pending ?? {})) store.pending.set(sid, list2);
222289
222350
  } else {
222290
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
222291
- fs22.writeFileSync(file, JSON.stringify({ sessions: [], messages: {}, sessionWorkOrders: {} }, null, 2));
222351
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
222352
+ fs23.writeFileSync(file, JSON.stringify({ sessions: [], messages: {}, sessionWorkOrders: {} }, null, 2));
222292
222353
  }
222293
222354
  return store;
222294
222355
  }
@@ -222382,7 +222443,7 @@ var init_dev_store = __esm({
222382
222443
  sessionWorkOrderKinds: Object.fromEntries([...this.sessionWorkOrderKinds].map(([k2, v2]) => [k2, Object.fromEntries(v2)])),
222383
222444
  pending: Object.fromEntries(this.pending)
222384
222445
  };
222385
- fs22.writeFileSync(this.file, JSON.stringify(snap, null, 2));
222446
+ fs23.writeFileSync(this.file, JSON.stringify(snap, null, 2));
222386
222447
  }
222387
222448
  };
222388
222449
  MemoryChatTurnStore = class {
@@ -222465,17 +222526,17 @@ var init_dev_store = __esm({
222465
222526
  }
222466
222527
  static async open(file) {
222467
222528
  const store = new _FileChatTurnStore(file);
222468
- if (fs22.existsSync(file)) {
222469
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
222529
+ if (fs23.existsSync(file)) {
222530
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
222470
222531
  for (const t of snap.turns ?? []) store.turns.set(t.id, t);
222471
222532
  } else {
222472
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
222473
- fs22.writeFileSync(file, JSON.stringify({ turns: [] }, null, 2));
222533
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
222534
+ fs23.writeFileSync(file, JSON.stringify({ turns: [] }, null, 2));
222474
222535
  }
222475
222536
  return store;
222476
222537
  }
222477
222538
  persist() {
222478
- fs22.writeFileSync(this.file, JSON.stringify({ turns: [...this.turns.values()] }, null, 2));
222539
+ fs23.writeFileSync(this.file, JSON.stringify({ turns: [...this.turns.values()] }, null, 2));
222479
222540
  }
222480
222541
  };
222481
222542
  MemoryReadMarkerStore = class _MemoryReadMarkerStore {
@@ -222501,12 +222562,12 @@ var init_dev_store = __esm({
222501
222562
  }
222502
222563
  static async open(file) {
222503
222564
  const store = new _FileReadMarkerStore(file);
222504
- if (fs22.existsSync(file)) {
222505
- const snap = JSON.parse(fs22.readFileSync(file, "utf8"));
222565
+ if (fs23.existsSync(file)) {
222566
+ const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
222506
222567
  for (const m2 of snap.markers ?? []) store.markers.set(`${m2.actorId}\0${m2.scope}`, m2);
222507
222568
  } else {
222508
- fs22.mkdirSync(path22.dirname(file), { recursive: true });
222509
- fs22.writeFileSync(file, JSON.stringify({ markers: [] }, null, 2));
222569
+ fs23.mkdirSync(path22.dirname(file), { recursive: true });
222570
+ fs23.writeFileSync(file, JSON.stringify({ markers: [] }, null, 2));
222510
222571
  }
222511
222572
  return store;
222512
222573
  }
@@ -222515,18 +222576,18 @@ var init_dev_store = __esm({
222515
222576
  this.save();
222516
222577
  }
222517
222578
  save() {
222518
- fs22.writeFileSync(this.file, JSON.stringify({ markers: [...this.markers.values()] }, null, 2));
222579
+ fs23.writeFileSync(this.file, JSON.stringify({ markers: [...this.markers.values()] }, null, 2));
222519
222580
  }
222520
222581
  };
222521
222582
  }
222522
222583
  });
222523
222584
 
222524
222585
  // ../server/src/control-plane-file-store.ts
222525
- var fs23, path23, MUTATORS2, SNAPSHOT_VERSION, FileControlPlaneStore;
222586
+ var fs24, path23, MUTATORS2, SNAPSHOT_VERSION, FileControlPlaneStore;
222526
222587
  var init_control_plane_file_store = __esm({
222527
222588
  "../server/src/control-plane-file-store.ts"() {
222528
222589
  "use strict";
222529
- fs23 = __toESM(require("node:fs"), 1);
222590
+ fs24 = __toESM(require("node:fs"), 1);
222530
222591
  path23 = __toESM(require("node:path"), 1);
222531
222592
  init_src9();
222532
222593
  MUTATORS2 = [
@@ -222569,8 +222630,8 @@ var init_control_plane_file_store = __esm({
222569
222630
  }
222570
222631
  static async open(file) {
222571
222632
  const store = new _FileControlPlaneStore(file);
222572
- if (fs23.existsSync(file)) {
222573
- const snap = JSON.parse(fs23.readFileSync(file, "utf8"));
222633
+ if (fs24.existsSync(file)) {
222634
+ const snap = JSON.parse(fs24.readFileSync(file, "utf8"));
222574
222635
  const needsOnboardingBackfill = (snap.version ?? 1) < 2;
222575
222636
  const backfillAt = (/* @__PURE__ */ new Date()).toISOString();
222576
222637
  for (const c of snap.companies ?? []) await MemoryControlPlaneStore.prototype.createCompany.call(store, c);
@@ -222588,7 +222649,7 @@ var init_control_plane_file_store = __esm({
222588
222649
  for (const op of snap.offboardings ?? []) store.offboardings.set(`${op.companyId}\0${op.operationId}`, op);
222589
222650
  if (needsOnboardingBackfill) await store.save();
222590
222651
  } else {
222591
- fs23.mkdirSync(path23.dirname(file), { recursive: true });
222652
+ fs24.mkdirSync(path23.dirname(file), { recursive: true });
222592
222653
  }
222593
222654
  return store;
222594
222655
  }
@@ -222625,7 +222686,7 @@ var init_control_plane_file_store = __esm({
222625
222686
  // 交接操作无按公司列举的读面(按 operationId / accountId 定位),直接读 protected map。
222626
222687
  offboardings: [...this.offboardings.values()]
222627
222688
  };
222628
- fs23.writeFileSync(this.file, JSON.stringify(snap, null, 2));
222689
+ fs24.writeFileSync(this.file, JSON.stringify(snap, null, 2));
222629
222690
  }
222630
222691
  };
222631
222692
  }
@@ -222701,7 +222762,7 @@ var init_company_engine_router = __esm({
222701
222762
 
222702
222763
  // ../server/src/skill-market-client.ts
222703
222764
  function digestOf2(bytes2) {
222704
- return `sha256:${(0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex")}`;
222765
+ return `sha256:${(0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex")}`;
222705
222766
  }
222706
222767
  function createSkillMarketClientFromEnv(env = process.env) {
222707
222768
  const base = env["SKILL_MARKET_BASE_URL"];
@@ -222712,11 +222773,11 @@ function createSkillMarketClientFromEnv(env = process.env) {
222712
222773
  function sleep4(ms) {
222713
222774
  return new Promise((r) => setTimeout(r, ms));
222714
222775
  }
222715
- var import_node_crypto47, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
222776
+ var import_node_crypto48, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
222716
222777
  var init_skill_market_client = __esm({
222717
222778
  "../server/src/skill-market-client.ts"() {
222718
222779
  "use strict";
222719
- import_node_crypto47 = require("node:crypto");
222780
+ import_node_crypto48 = require("node:crypto");
222720
222781
  init_src();
222721
222782
  DisabledSkillMarketClient = class {
222722
222783
  async listSkills(_input) {
@@ -223567,7 +223628,7 @@ async function copySessionOutputs(deps) {
223567
223628
  continue;
223568
223629
  }
223569
223630
  const saved = existing?.[file.path];
223570
- if (saved && savedContentHash(saved) === (0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex")) {
223631
+ if (saved && savedContentHash(saved) === (0, import_node_crypto49.createHash)("sha256").update(bytes2).digest("hex")) {
223571
223632
  const aligned = { ...saved, size: bytes2.length, mtime: file.mtime };
223572
223633
  delete aligned.editedAt;
223573
223634
  realigned.push(aligned);
@@ -223664,11 +223725,11 @@ function createOutputCopyScheduler(deps) {
223664
223725
  }
223665
223726
  };
223666
223727
  }
223667
- var import_node_crypto48, OUTPUT_COPY_LIMITS;
223728
+ var import_node_crypto49, OUTPUT_COPY_LIMITS;
223668
223729
  var init_outputs_copy = __esm({
223669
223730
  "../server/src/domains/chat-sessions/outputs-copy.ts"() {
223670
223731
  "use strict";
223671
- import_node_crypto48 = require("node:crypto");
223732
+ import_node_crypto49 = require("node:crypto");
223672
223733
  init_src();
223673
223734
  OUTPUT_COPY_LIMITS = {
223674
223735
  maxFileBytes: FILE_CONTENT_MAX_BYTES,
@@ -223883,11 +223944,11 @@ function typeDefError(def, mode) {
223883
223944
  }
223884
223945
  return null;
223885
223946
  }
223886
- var import_node_crypto49, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
223947
+ var import_node_crypto50, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
223887
223948
  var init_types_admin = __esm({
223888
223949
  "../server/src/types-admin.ts"() {
223889
223950
  "use strict";
223890
- import_node_crypto49 = require("node:crypto");
223951
+ import_node_crypto50 = require("node:crypto");
223891
223952
  init_zod();
223892
223953
  init_src7();
223893
223954
  init_config_audit();
@@ -224014,7 +224075,7 @@ var init_types_admin = __esm({
224014
224075
  async trace(change, target, by, detail) {
224015
224076
  try {
224016
224077
  await this.audit?.({
224017
- id: `reg_${(0, import_node_crypto49.randomUUID)()}`,
224078
+ id: `reg_${(0, import_node_crypto50.randomUUID)()}`,
224018
224079
  actor: by ?? SYSTEM_CONFIG_ACTOR,
224019
224080
  kind: "registry_change",
224020
224081
  target,
@@ -224162,11 +224223,11 @@ function roleDefError(def, mode) {
224162
224223
  }
224163
224224
  return null;
224164
224225
  }
224165
- var import_node_crypto50, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
224226
+ var import_node_crypto51, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
224166
224227
  var init_roles_admin = __esm({
224167
224228
  "../server/src/roles-admin.ts"() {
224168
224229
  "use strict";
224169
- import_node_crypto50 = require("node:crypto");
224230
+ import_node_crypto51 = require("node:crypto");
224170
224231
  init_zod();
224171
224232
  init_src7();
224172
224233
  init_config_audit();
@@ -224191,7 +224252,7 @@ var init_roles_admin = __esm({
224191
224252
  async trace(change, target, by, detail) {
224192
224253
  try {
224193
224254
  await this.deps.audit?.({
224194
- id: `reg_${(0, import_node_crypto50.randomUUID)()}`,
224255
+ id: `reg_${(0, import_node_crypto51.randomUUID)()}`,
224195
224256
  actor: by ?? SYSTEM_CONFIG_ACTOR,
224196
224257
  kind: "registry_change",
224197
224258
  target,
@@ -224204,8 +224265,13 @@ var init_roles_admin = __esm({
224204
224265
  list() {
224205
224266
  return this.deps.store.list();
224206
224267
  }
224207
- async usage(name) {
224208
- return (await this.usageMany([name]))[name];
224268
+ /**
224269
+ * @param companyId 按哪家公司的员工数人头(Bug 0245)。岗位目录全公司共用一份,但「谁在岗」是各公司自己的——
224270
+ * GET /api/roles 必须传请求所属公司,否则落到默认公司那份早已不再更新的旧名单,所有公司都报同一组错数。
224271
+ * 缺省 = 默认公司(改名 / 弃用 / 删除这几条写路径暂维持原口径)。
224272
+ */
224273
+ async usage(name, companyId) {
224274
+ return (await this.usageMany([name], companyId))[name];
224209
224275
  }
224210
224276
  /**
224211
224277
  * ADR 0088 D4:多个岗位的用量**一次取数**。此前 `/api/roles` 对每个岗位各调一次 `usage()`,
@@ -224213,9 +224279,9 @@ var init_roles_admin = __esm({
224213
224279
  * ——13 个岗位 = 13 份全表。员工页首屏门控在这条上(listRoles 与 listActors 并行等齐才渲染)。
224214
224280
  * 三份数据只取一次,逐岗位在内存算。单岗位口径与旧 `usage()` 逐字段一致(后者现转调本方法)。
224215
224281
  */
224216
- async usageMany(names) {
224282
+ async usageMany(names, companyId) {
224217
224283
  const [actors, types2, owners] = await Promise.all([
224218
- this.deps.actors(),
224284
+ this.deps.actors(companyId),
224219
224285
  this.deps.types(),
224220
224286
  this.deps.artifactOwners?.() ?? Promise.resolve([])
224221
224287
  ]);
@@ -227050,8 +227116,8 @@ var init_chat_recovery = __esm({
227050
227116
 
227051
227117
  // ../server/src/auth/crypto.ts
227052
227118
  function hashPassword(password) {
227053
- const salt = (0, import_node_crypto51.randomBytes)(16);
227054
- const dk = (0, import_node_crypto51.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
227119
+ const salt = (0, import_node_crypto52.randomBytes)(16);
227120
+ const dk = (0, import_node_crypto52.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
227055
227121
  return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
227056
227122
  }
227057
227123
  function verifyPassword(password, stored) {
@@ -227068,8 +227134,8 @@ function verifyPassword(password, stored) {
227068
227134
  return false;
227069
227135
  }
227070
227136
  if (expected.length === 0) return false;
227071
- const dk = (0, import_node_crypto51.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
227072
- return dk.length === expected.length && (0, import_node_crypto51.timingSafeEqual)(dk, expected);
227137
+ const dk = (0, import_node_crypto52.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
227138
+ return dk.length === expected.length && (0, import_node_crypto52.timingSafeEqual)(dk, expected);
227073
227139
  }
227074
227140
  function b64urlJson(value2) {
227075
227141
  return Buffer.from(JSON.stringify(value2)).toString("base64url");
@@ -227077,17 +227143,17 @@ function b64urlJson(value2) {
227077
227143
  function signSession(claims, secret) {
227078
227144
  const head = b64urlJson({ alg: "HS256", typ: "JWT" });
227079
227145
  const body2 = b64urlJson(claims);
227080
- const sig = (0, import_node_crypto51.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
227146
+ const sig = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
227081
227147
  return `${head}.${body2}.${sig}`;
227082
227148
  }
227083
227149
  function verifySession(token, secret, nowMs) {
227084
227150
  const parts = token.split(".");
227085
227151
  if (parts.length !== 3) return null;
227086
227152
  const [head, body2, sig] = parts;
227087
- const expected = (0, import_node_crypto51.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
227153
+ const expected = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
227088
227154
  const got = Buffer.from(sig);
227089
227155
  const exp = Buffer.from(expected);
227090
- if (got.length !== exp.length || !(0, import_node_crypto51.timingSafeEqual)(got, exp)) return null;
227156
+ if (got.length !== exp.length || !(0, import_node_crypto52.timingSafeEqual)(got, exp)) return null;
227091
227157
  let claims;
227092
227158
  try {
227093
227159
  claims = JSON.parse(Buffer.from(body2, "base64url").toString("utf8"));
@@ -227099,21 +227165,21 @@ function verifySession(token, secret, nowMs) {
227099
227165
  return claims;
227100
227166
  }
227101
227167
  function generateCode() {
227102
- return String((0, import_node_crypto51.randomInt)(0, 1e6)).padStart(6, "0");
227168
+ return String((0, import_node_crypto52.randomInt)(0, 1e6)).padStart(6, "0");
227103
227169
  }
227104
227170
  function hashCode(code2, secret) {
227105
- return (0, import_node_crypto51.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
227171
+ return (0, import_node_crypto52.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
227106
227172
  }
227107
227173
  function safeEqualHash(a, b2) {
227108
227174
  const ba = Buffer.from(a);
227109
227175
  const bb = Buffer.from(b2);
227110
- return ba.length === bb.length && (0, import_node_crypto51.timingSafeEqual)(ba, bb);
227176
+ return ba.length === bb.length && (0, import_node_crypto52.timingSafeEqual)(ba, bb);
227111
227177
  }
227112
- var import_node_crypto51, SCRYPT_N, SCRYPT_KEYLEN;
227178
+ var import_node_crypto52, SCRYPT_N, SCRYPT_KEYLEN;
227113
227179
  var init_crypto2 = __esm({
227114
227180
  "../server/src/auth/crypto.ts"() {
227115
227181
  "use strict";
227116
- import_node_crypto51 = require("node:crypto");
227182
+ import_node_crypto52 = require("node:crypto");
227117
227183
  SCRYPT_N = 16384;
227118
227184
  SCRYPT_KEYLEN = 32;
227119
227185
  }
@@ -228497,18 +228563,18 @@ function variableKeyFromEnv(env = process.env) {
228497
228563
  return buf;
228498
228564
  }
228499
228565
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
228500
- return (0, import_node_crypto52.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
228566
+ return (0, import_node_crypto53.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
228501
228567
  }
228502
228568
  function dedupe2(ids2) {
228503
228569
  return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
228504
228570
  }
228505
- var import_node_crypto52, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
228571
+ var import_node_crypto53, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
228506
228572
  var init_service5 = __esm({
228507
228573
  "../server/src/domains/actors/service.ts"() {
228508
228574
  "use strict";
228509
228575
  init_file_limits2();
228510
228576
  init_keyed_mutex();
228511
- import_node_crypto52 = require("node:crypto");
228577
+ import_node_crypto53 = require("node:crypto");
228512
228578
  init_skill_fetcher();
228513
228579
  init_identity();
228514
228580
  init_migrate();
@@ -229132,7 +229198,7 @@ ${input.description}
229132
229198
  */
229133
229199
  buildSkillFile(skillId, path46, content3, now) {
229134
229200
  const bytes2 = new TextEncoder().encode(content3);
229135
- const blobHash = (0, import_node_crypto52.createHash)("sha256").update(bytes2).digest("hex");
229201
+ const blobHash = (0, import_node_crypto53.createHash)("sha256").update(bytes2).digest("hex");
229136
229202
  return { skillId, path: path46, content: content3, blobHash, size: bytes2.length, updatedAt: now };
229137
229203
  }
229138
229204
  /**
@@ -230255,15 +230321,15 @@ ${input.description}
230255
230321
  return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
230256
230322
  }
230257
230323
  encrypt(plain) {
230258
- const iv = (0, import_node_crypto52.randomBytes)(12);
230259
- const cipher = (0, import_node_crypto52.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
230324
+ const iv = (0, import_node_crypto53.randomBytes)(12);
230325
+ const cipher = (0, import_node_crypto53.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
230260
230326
  const enc3 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
230261
230327
  return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc3.toString("base64")].join(".");
230262
230328
  }
230263
230329
  decrypt(packed) {
230264
230330
  const [iv, tag, data] = packed.split(".");
230265
230331
  if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
230266
- const decipher = (0, import_node_crypto52.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
230332
+ const decipher = (0, import_node_crypto53.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
230267
230333
  decipher.setAuthTag(Buffer.from(tag, "base64"));
230268
230334
  return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
230269
230335
  }
@@ -230876,7 +230942,7 @@ async function listSkillMetadataForActor(args) {
230876
230942
  usedDirs.add(dir);
230877
230943
  const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
230878
230944
  let latest = "1970-01-01T00:00:00.000Z";
230879
- const hasher = (0, import_node_crypto53.createHash)("sha256");
230945
+ const hasher = (0, import_node_crypto54.createHash)("sha256");
230880
230946
  for (const m2 of sorted) {
230881
230947
  if (m2.updatedAt > latest) latest = m2.updatedAt;
230882
230948
  hasher.update(m2.path);
@@ -230901,11 +230967,11 @@ async function listSkillMetadataForActor(args) {
230901
230967
  const dir = sanitizeSkillDir(c.name, c.slug);
230902
230968
  if (usedDirs.has(dir)) continue;
230903
230969
  usedDirs.add(dir);
230904
- const hasher = (0, import_node_crypto53.createHash)("sha256");
230970
+ const hasher = (0, import_node_crypto54.createHash)("sha256");
230905
230971
  for (const p2 of paths) {
230906
230972
  hasher.update(p2);
230907
230973
  hasher.update("\0");
230908
- hasher.update((0, import_node_crypto53.createHash)("sha256").update(c.files[p2]).digest("hex"));
230974
+ hasher.update((0, import_node_crypto54.createHash)("sha256").update(c.files[p2]).digest("hex"));
230909
230975
  hasher.update("\0");
230910
230976
  }
230911
230977
  out.push({
@@ -230926,11 +230992,11 @@ async function listSkillMetadataForActor(args) {
230926
230992
  const dir = sanitizeSkillDir(b2.name, b2.id);
230927
230993
  if (usedDirs.has(dir)) continue;
230928
230994
  usedDirs.add(dir);
230929
- const hasher = (0, import_node_crypto53.createHash)("sha256");
230995
+ const hasher = (0, import_node_crypto54.createHash)("sha256");
230930
230996
  for (const p2 of paths) {
230931
230997
  hasher.update(p2);
230932
230998
  hasher.update("\0");
230933
- hasher.update((0, import_node_crypto53.createHash)("sha256").update(b2.files[p2]).digest("hex"));
230999
+ hasher.update((0, import_node_crypto54.createHash)("sha256").update(b2.files[p2]).digest("hex"));
230934
231000
  hasher.update("\0");
230935
231001
  }
230936
231002
  out.push({
@@ -230950,7 +231016,7 @@ function builtinSkillToFiles(b2) {
230950
231016
  skillId: `builtin:${b2.id}`,
230951
231017
  path: path46,
230952
231018
  content: content3,
230953
- blobHash: (0, import_node_crypto53.createHash)("sha256").update(content3).digest("hex"),
231019
+ blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
230954
231020
  size: Buffer.byteLength(content3, "utf8"),
230955
231021
  updatedAt: "1970-01-01T00:00:00.000Z"
230956
231022
  }));
@@ -230974,7 +231040,7 @@ function sanitizeSkillDir(name, fallbackId) {
230974
231040
  if (byName) return byName;
230975
231041
  const byId = fallbackId ? fold2(fallbackId) : "";
230976
231042
  if (byId) return byId;
230977
- return `skill-${(0, import_node_crypto53.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
231043
+ return `skill-${(0, import_node_crypto54.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
230978
231044
  }
230979
231045
  async function materializeSkillFiles(args) {
230980
231046
  const skills = await args.service.listInstalledSkillsForActor(args.actorId);
@@ -231007,7 +231073,7 @@ function connectorSkillToFiles(c) {
231007
231073
  skillId: c.id,
231008
231074
  path: path46,
231009
231075
  content: content3,
231010
- blobHash: (0, import_node_crypto53.createHash)("sha256").update(content3).digest("hex"),
231076
+ blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
231011
231077
  size: Buffer.byteLength(content3, "utf8"),
231012
231078
  updatedAt: "1970-01-01T00:00:00.000Z"
231013
231079
  }));
@@ -231024,11 +231090,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
231024
231090
  ...s2.version !== void 0 ? { version: s2.version } : {}
231025
231091
  }));
231026
231092
  }
231027
- var import_node_crypto53;
231093
+ var import_node_crypto54;
231028
231094
  var init_skill_materializer = __esm({
231029
231095
  "../server/src/domains/actors/skill-materializer.ts"() {
231030
231096
  "use strict";
231031
- import_node_crypto53 = require("node:crypto");
231097
+ import_node_crypto54 = require("node:crypto");
231032
231098
  }
231033
231099
  });
231034
231100
 
@@ -232800,54 +232866,6 @@ var init_credential_audit_log = __esm({
232800
232866
  }
232801
232867
  });
232802
232868
 
232803
- // ../server/src/domains/actors/registry-audit-sink.ts
232804
- function createRegistryAuditSink(file) {
232805
- const listeners = /* @__PURE__ */ new Set();
232806
- const notify = (msg) => {
232807
- for (const listener of listeners) listener(msg);
232808
- };
232809
- return {
232810
- /** 登记审计:落盘 + 推 registry 通知(前端据此重拉登记类投影)。 */
232811
- registryAudit: (record9) => {
232812
- fs24.appendFile(file, JSON.stringify(record9) + "\n", () => {
232813
- });
232814
- notify({ kind: record9.kind, actor: record9.actor, target: record9.target, timestamp: record9.timestamp });
232815
- },
232816
- /**
232817
- * 凭据审计(ADR 凭据保险库 §4.7):与登记审计同落一份旁账,按 kind 前缀 `credential_` 区分。
232818
- *
232819
- * **只落盘、不推 registry 通知**(D-10 推送面)。registry 通知是实例级的,`/api/events` 原样转发给
232820
- * 任何已认证的连接、不分组织;凭据事件推进去 = 别家组织开一条 SSE 就能实时看到「谁在读/改哪个变量」。
232821
- * 仓内没有任何消费方订它(2.0 前端只订无内容的 runtime-status,而后者本来就不被凭据事件唤醒)。
232822
- * 要看凭据审计走 `GET /api/variables/reveal-audit`(按请求方组织过滤)。将来若需要实时推送,
232823
- * 得先让通知带组织、`/api/events` 按组织过滤(recvvzbhOGiS7l 的完整修复),再放回来。
232824
- */
232825
- credentialAudit: (record9) => {
232826
- fs24.appendFile(file, JSON.stringify(record9) + "\n", () => {
232827
- });
232828
- },
232829
- /** runtime 状态变了:只发无内容的失效提示(ADR 0576)。 */
232830
- notifyRuntimeStatus: () => {
232831
- notify({ kind: "runtime_status_changed", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
232832
- },
232833
- notifications: {
232834
- subscribe(listener) {
232835
- listeners.add(listener);
232836
- return () => {
232837
- listeners.delete(listener);
232838
- };
232839
- }
232840
- }
232841
- };
232842
- }
232843
- var fs24;
232844
- var init_registry_audit_sink = __esm({
232845
- "../server/src/domains/actors/registry-audit-sink.ts"() {
232846
- "use strict";
232847
- fs24 = __toESM(require("node:fs"), 1);
232848
- }
232849
- });
232850
-
232851
232869
  // ../server/src/domains/actors/stop-on-delete.ts
232852
232870
  function planStopForDeletedActor(actorId, open3) {
232853
232871
  const mine = open3.filter((row) => row.actorId === actorId);
@@ -233074,7 +233092,7 @@ function createActorsDomain(opts) {
233074
233092
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
233075
233093
  audit: async (entry) => {
233076
233094
  await opts.audit?.({
233077
- id: `reg_${(0, import_node_crypto54.randomUUID)()}`,
233095
+ id: `reg_${(0, import_node_crypto55.randomUUID)()}`,
233078
233096
  actor: entry.by,
233079
233097
  kind: "registry_change",
233080
233098
  target: entry.actorId,
@@ -233114,11 +233132,11 @@ function createActorsDomain(opts) {
233114
233132
  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.companyMemberRole ? { companyMemberRole: opts.companyMemberRole } : {}, ...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 } : {} })
233115
233133
  };
233116
233134
  }
233117
- var import_node_crypto54;
233135
+ var import_node_crypto55;
233118
233136
  var init_actors = __esm({
233119
233137
  "../server/src/domains/actors/index.ts"() {
233120
233138
  "use strict";
233121
- import_node_crypto54 = require("node:crypto");
233139
+ import_node_crypto55 = require("node:crypto");
233122
233140
  init_service5();
233123
233141
  init_routes3();
233124
233142
  init_service5();
@@ -234811,6 +234829,9 @@ function isDispatchLayerFailure(detail) {
234811
234829
  const reason = detail?.reason;
234812
234830
  return typeof reason === "string" && reason.startsWith("dispatch ");
234813
234831
  }
234832
+ function externalRefOf(a) {
234833
+ return a.contentKind === "external" && a.contentRef ? { externalRef: a.contentRef } : {};
234834
+ }
234814
234835
  function buildWorkorderActivity(input) {
234815
234836
  const { workorderId, snap, events } = input;
234816
234837
  const ref2 = input.ref ?? ((id) => ({ id }));
@@ -234838,7 +234859,7 @@ function buildWorkorderActivity(input) {
234838
234859
  } else if (isRootBrief) {
234839
234860
  list2.push({ id: a.id, revisionId: a.workId, name: "\u4EFB\u52A1\u76EE\u6807", path: "\u4EFB\u52A1\u76EE\u6807.md", nodeId });
234840
234861
  } else {
234841
- list2.push({ id: a.id, revisionId: a.workId, name: a.name, nodeId });
234862
+ list2.push({ id: a.id, revisionId: a.workId, name: a.name, nodeId, ...externalRefOf(a) });
234842
234863
  }
234843
234864
  artifactsByWork.set(a.workId, list2);
234844
234865
  }
@@ -235197,6 +235218,7 @@ function buildWorkorderActivity(input) {
235197
235218
  const workOfIssue = /* @__PURE__ */ new Map();
235198
235219
  const reasonWorksOfIssue = /* @__PURE__ */ new Map();
235199
235220
  const recoveryLetterIssueIds = /* @__PURE__ */ new Set();
235221
+ const backpressureIssueIds = /* @__PURE__ */ new Set();
235200
235222
  const plainComments = [];
235201
235223
  const escalations = [];
235202
235224
  const gapCreatedSeq = /* @__PURE__ */ new Map();
@@ -235428,6 +235450,7 @@ function buildWorkorderActivity(input) {
235428
235450
  const isGap = issueKind === "gap";
235429
235451
  const raisedFrom = str5(ev.raisedByNodeId);
235430
235452
  const isBackpressure = !isEscalation && !isGap && !!raisedFrom && raisedFrom !== nodeId;
235453
+ if (isBackpressure) backpressureIssueIds.add(issueId);
235431
235454
  const body2 = str5(ev.body) ?? "";
235432
235455
  if (isEscalation) {
235433
235456
  const linkedGapId = str5(ev.gapId);
@@ -235475,6 +235498,11 @@ function buildWorkorderActivity(input) {
235475
235498
  /* 回压是**执行中断**(这一轮走不下去了,要外部介入);唤醒信那档不是中断,仍是进行中。
235476
235499
  缺口走不到这里了(F4 在上面 break)。 */
235477
235500
  phase: isBackpressure ? "stuck" : "running",
235501
+ /* 回压卡**不喊**:砖红只给「此刻等人动手」的那张(奕璇 2026-09-22)。轮到动手的是被回压节点的
235502
+ 负责人——真人时由他那一轮喊(砖红 + 去处理),agent 时它自己在跑(暖橙)。提要求这一段在
235503
+ 开口那一刻就结束了,与缺口中断的执行卡同一条先例(F1:上一段结束就转灰,该喊的是下游那张)。
235504
+ 不写这两个字段时前端退回老规则(开着 + stuck ⇒ 砖红),同一件事页面上会有两张红卡。 */
235505
+ ...isBackpressure ? { segmentOpen: false, awaitingHuman: false } : {},
235478
235506
  status: isBackpressure ? ACTIVITY_COPY.backpressure(
235479
235507
  nameOf2(authorId),
235480
235508
  nodeName(nodeId),
@@ -235484,11 +235512,15 @@ function buildWorkorderActivity(input) {
235484
235512
  此前只有缺口/回压被排除,留言仍是「正文当小状态 + 展开区再印一遍」,
235485
235513
  全库实测最长 8096 字印两遍(`ws:wo-56bba3c1`),一张卡顶掉整屏。 */
235486
235514
  artifacts: [],
235487
- /* 缺口不出按钮(处置在缺口线上,由「任务内沟通」那一列进);
235488
- 留言给「查看沟通」直达留言线——线是投影出来的、恒存在,id 这里就能算;
235489
- 回压仍给「去处理」落到产物预览栏(那儿才有批注的处置面)。
235490
- 留言算不出对面是谁(该产物没负责人)时退回「去处理」,别给一个开不了的落点。 */
235491
- actions: isBackpressure || !commentTargetId ? [{ kind: "open_issue", label: BUTTON.handle, target: issueId }] : [{ kind: "open_thread", label: BUTTON.view, target: commentThreadSessionId(authorId, commentTargetId), variant: "quiet" }]
235515
+ /* 按钮只长在「人能点、而且轮到人动手」的那张卡上(奕璇 2026-09-22):
235516
+ · 回压卡**一个按钮都没有**:它署名的是提要求的一方(多为 agent),轮到动手的是被回压节点的
235517
+ 负责人。负责人是真人时「去处理」挂在系统给他开的那一轮上(见事件循环之后
235518
+ `backpressureAwaitingOwner` 那一段),落点、target 与原先这颗一样;负责人是 agent 时
235519
+ 它自己会跑,没有人要动手。
235520
+ · 唤醒信给「查看沟通」直达留言线——线是投影出来的、恒存在,id 这里就能算。
235521
+ · 唤醒信算不出对面是谁(该产物没负责人 / 负责人就是作者)时退回「去处理」,但**只在署名的
235522
+ 是真人时**给:agent / 系统署名的卡挂「去处理」,点的人进去没有一件是他该做的事。 */
235523
+ actions: isBackpressure ? [] : !commentTargetId ? isHumanActorId(authorId) ? [{ kind: "open_issue", label: BUTTON.handle, target: issueId }] : [] : [{ kind: "open_thread", label: BUTTON.view, target: commentThreadSessionId(authorId, commentTargetId), variant: "quiet" }]
235492
235524
  });
235493
235525
  break;
235494
235526
  }
@@ -235799,6 +235831,18 @@ function buildWorkorderActivity(input) {
235799
235831
  const message = (input.runFailures ?? []).filter((f2) => f2.nodeId === card2.nodeId && f2.startedAt >= card2.at && f2.startedAt <= trip.at && f2.message.trim()).sort((a, b2) => a.startedAt.localeCompare(b2.startedAt)).at(-1)?.message;
235800
235832
  return message ? firstSentence(message) : void 0;
235801
235833
  };
235834
+ for (const card2 of executionCards) {
235835
+ const workId = card2.openWorkId;
235836
+ if (!workId || !isHumanActorId(card2.executorId)) continue;
235837
+ const issueId = [...backpressureIssueIds].find((id) => reasonWorksOfIssue.get(id)?.includes(workId) && !issueById.get(id)?.resolvedAt && !plainResolveEvents.has(id));
235838
+ if (!issueId) continue;
235839
+ card2.phase = "not_started";
235840
+ card2.phaseLabel = PHASE_LABEL.escalationPending;
235841
+ card2.segmentOpen = true;
235842
+ card2.awaitingHuman = true;
235843
+ card2.status = ACTIVITY_COPY.backpressureAwaitingOwner(nameOf2(card2.executorId), nodeName(card2.nodeId));
235844
+ card2.actions = [{ kind: "open_issue", label: BUTTON.handle, target: issueId, variant: "primary" }];
235845
+ }
235802
235846
  for (const card2 of executionCards) {
235803
235847
  const trips = card2.stopLossTrips;
235804
235848
  if (!trips?.length) continue;
@@ -236310,6 +236354,11 @@ var init_activity2 = __esm({
236310
236354
  ①「缺口卡」也删掉(F4):它和执行卡挂的是同一段运行、同一份轨迹,点哪张打开的都一样。 */
236311
236355
  /** 回压(下游对上游提修改要求)——发起人 2026-09-08 认可的措辞。 */
236312
236356
  backpressure: (raiser, upstream, owner) => `${raiser} \u5BF9\u300A${upstream}\u300B\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 @${owner} \u5904\u7406\u3002`,
236357
+ /**
236358
+ * 被回压节点的负责人是**真人**时,系统为他开的那一轮(奕璇 2026-09-22 定):这一轮不会自己跑,
236359
+ * 是在等他动手——不能再说「正在根据反馈调整」(`issueDriven`),那句会让一张没人在动的卡挂成「进行中」。
236360
+ */
236361
+ backpressureAwaitingOwner: (owner, node2) => `\u7B49 @${owner} \u6839\u636E\u4FEE\u6539\u8981\u6C42\u8C03\u6574\u300A${node2}\u300B\u3002`,
236313
236362
  /**
236314
236363
  * 留言 / 批注——**一句索引,正文不上卡**(发起人 2026-09-09 定)。
236315
236364
  *
@@ -236393,7 +236442,10 @@ var init_activity2 = __esm({
236393
236442
  * 所以仍然要 override,只是换成和 `stuck` 一族同一个词。
236394
236443
  */
236395
236444
  failed: "\u6267\u884C\u4E2D\u65AD",
236396
- /** 升级卡没人处理那一档。**不写「待你处理」**:别人看到的字要和该处理的人看到的一样。 */
236445
+ /**
236446
+ * 升级卡没人处理那一档。**不写「待你处理」**:别人看到的字要和该处理的人看到的一样。
236447
+ * 被回压的真人负责人那一轮(等他按修改要求动手)也用这个词——同样是「这一段卡在人身上」。
236448
+ */
236397
236449
  escalationPending: "\u5F85\u5904\u7406"
236398
236450
  };
236399
236451
  COORDINATOR_CARD_PREFIX = "coordinator:";
@@ -236552,7 +236604,7 @@ function artifactsOfWork(work, input) {
236552
236604
  } else if (isRootBrief) {
236553
236605
  list2.push({ id: a.id, revisionId: a.workId, name: "\u4EFB\u52A1\u76EE\u6807", path: "\u4EFB\u52A1\u76EE\u6807.md", nodeId: artifactNodeId });
236554
236606
  } else {
236555
- list2.push({ id: a.id, revisionId: a.workId, name: a.name, nodeId: artifactNodeId });
236607
+ list2.push({ id: a.id, revisionId: a.workId, name: a.name, nodeId: artifactNodeId, ...externalRefOf(a) });
236556
236608
  }
236557
236609
  }
236558
236610
  return list2;
@@ -237602,11 +237654,11 @@ function fromRow(row) {
237602
237654
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
237603
237655
  return r;
237604
237656
  }
237605
- var import_node_crypto55, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
237657
+ var import_node_crypto56, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
237606
237658
  var init_notification_store = __esm({
237607
237659
  "../server/src/domains/collab/notification-store.ts"() {
237608
237660
  "use strict";
237609
- import_node_crypto55 = require("node:crypto");
237661
+ import_node_crypto56 = require("node:crypto");
237610
237662
  import_node_fs16 = __toESM(require("node:fs"), 1);
237611
237663
  DEFAULT_PAGE = 50;
237612
237664
  MAX_PAGE = 200;
@@ -237639,7 +237691,7 @@ var init_notification_store = __esm({
237639
237691
  async insert(n) {
237640
237692
  const existing = this.findLive(n.recipient, n.sourceKey, n.category);
237641
237693
  if (existing) return existing;
237642
- const row = { ...n, id: `ntf:${(0, import_node_crypto55.randomUUID)()}`, updatedAt: n.enteredAt };
237694
+ const row = { ...n, id: `ntf:${(0, import_node_crypto56.randomUUID)()}`, updatedAt: n.enteredAt };
237643
237695
  this.rows.set(row.id, row);
237644
237696
  this.save();
237645
237697
  return row;
@@ -237772,7 +237824,7 @@ var init_notification_store = __esm({
237772
237824
  return new _PostgresNotificationStore(sql, schema);
237773
237825
  }
237774
237826
  async insert(n) {
237775
- const id = `ntf:${(0, import_node_crypto55.randomUUID)()}`;
237827
+ const id = `ntf:${(0, import_node_crypto56.randomUUID)()}`;
237776
237828
  const r = await this.sql.query(
237777
237829
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
237778
237830
  title, workspace_title, entered_at, updated_at, status, read_at, item)
@@ -239780,13 +239832,13 @@ function nameKey(name) {
239780
239832
  function cleanName(name) {
239781
239833
  return name.trim().replace(/\s+/g, " ");
239782
239834
  }
239783
- var import_node_fs17, import_node_path21, import_node_crypto56, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
239835
+ var import_node_fs17, import_node_path21, import_node_crypto57, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
239784
239836
  var init_tags = __esm({
239785
239837
  "../server/src/domains/collab/tags.ts"() {
239786
239838
  "use strict";
239787
239839
  import_node_fs17 = __toESM(require("node:fs"), 1);
239788
239840
  import_node_path21 = __toESM(require("node:path"), 1);
239789
- import_node_crypto56 = __toESM(require("node:crypto"), 1);
239841
+ import_node_crypto57 = __toESM(require("node:crypto"), 1);
239790
239842
  SEP = "::";
239791
239843
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
239792
239844
  prefixOf = (companyId) => `${companyId}${SEP}`;
@@ -239804,7 +239856,7 @@ var init_tags = __esm({
239804
239856
  if (!name) return null;
239805
239857
  if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
239806
239858
  const tag = {
239807
- id: `tag-${import_node_crypto56.default.randomBytes(6).toString("hex")}`,
239859
+ id: `tag-${import_node_crypto57.default.randomBytes(6).toString("hex")}`,
239808
239860
  name,
239809
239861
  ...input.color?.trim() ? { color: input.color.trim() } : {},
239810
239862
  ...input.description?.trim() ? { description: input.description.trim() } : {}
@@ -241193,11 +241245,11 @@ async function isEmployedElsewhere(store, companyId, accountId) {
241193
241245
  const memberships = await store.listMembershipsForAccount(accountId);
241194
241246
  return memberships.some((m2) => m2.companyId !== companyId);
241195
241247
  }
241196
- var import_node_crypto57, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
241248
+ var import_node_crypto58, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
241197
241249
  var init_service6 = __esm({
241198
241250
  "../server/src/domains/companies/service.ts"() {
241199
241251
  "use strict";
241200
- import_node_crypto57 = require("node:crypto");
241252
+ import_node_crypto58 = require("node:crypto");
241201
241253
  init_console_url();
241202
241254
  ROLES = ["owner", "admin", "member"];
241203
241255
  INVITABLE_ROLES = ["admin", "member"];
@@ -241289,7 +241341,7 @@ var init_service6 = __esm({
241289
241341
  const existing = await this.store.getAccountByEmail(normalized4);
241290
241342
  if (existing) return existing;
241291
241343
  const account = {
241292
- id: `actor:human:${(0, import_node_crypto57.randomUUID)()}`,
241344
+ id: `actor:human:${(0, import_node_crypto58.randomUUID)()}`,
241293
241345
  email: normalized4,
241294
241346
  name: name ?? normalized4,
241295
241347
  status: "active"
@@ -241498,7 +241550,7 @@ var init_service6 = __esm({
241498
241550
  }
241499
241551
  const nowMs = Date.parse(this.now());
241500
241552
  const invitation = {
241501
- id: `invitation:${(0, import_node_crypto57.randomUUID)()}`,
241553
+ id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
241502
241554
  companyId,
241503
241555
  email: mail,
241504
241556
  role,
@@ -241629,7 +241681,7 @@ var init_service6 = __esm({
241629
241681
  const nowMs = Date.parse(this.now());
241630
241682
  const link = {
241631
241683
  companyId,
241632
- token: (0, import_node_crypto57.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
241684
+ token: (0, import_node_crypto58.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
241633
241685
  role: INVITE_LINK_ROLE,
241634
241686
  createdAt: new Date(nowMs).toISOString(),
241635
241687
  expiresAt: new Date(nowMs + INVITE_LINK_TTL_MS).toISOString(),
@@ -241686,7 +241738,7 @@ var init_service6 = __esm({
241686
241738
  const reusable = existing.find((inv) => !this.isExpired(inv));
241687
241739
  if (reusable) return reusable;
241688
241740
  const invitation = {
241689
- id: `invitation:${(0, import_node_crypto57.randomUUID)()}`,
241741
+ id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
241690
241742
  companyId: link.companyId,
241691
241743
  email: mail,
241692
241744
  role: link.role,
@@ -241924,11 +241976,11 @@ var init_service6 = __esm({
241924
241976
  });
241925
241977
 
241926
241978
  // ../server/src/domains/companies/offboarding.ts
241927
- var import_node_crypto58, MemberOffboardingService;
241979
+ var import_node_crypto59, MemberOffboardingService;
241928
241980
  var init_offboarding = __esm({
241929
241981
  "../server/src/domains/companies/offboarding.ts"() {
241930
241982
  "use strict";
241931
- import_node_crypto58 = require("node:crypto");
241983
+ import_node_crypto59 = require("node:crypto");
241932
241984
  init_service6();
241933
241985
  MemberOffboardingService = class {
241934
241986
  store;
@@ -242000,7 +242052,7 @@ var init_offboarding = __esm({
242000
242052
  const { ownedAgents, inFlight } = await this.snapshot(companyId, accountId);
242001
242053
  const at2 = this.now();
242002
242054
  const started = await this.store.startMemberOffboarding({
242003
- operationId: `off:${(0, import_node_crypto58.randomUUID)()}`,
242055
+ operationId: `off:${(0, import_node_crypto59.randomUUID)()}`,
242004
242056
  companyId,
242005
242057
  accountId,
242006
242058
  initiatedBy,
@@ -242510,18 +242562,18 @@ var init_routes6 = __esm({
242510
242562
 
242511
242563
  // ../server/src/domains/companies/agent-credentials.ts
242512
242564
  function hashAgentToken(token, secret) {
242513
- return HASH_PREFIX + (0, import_node_crypto59.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
242565
+ return HASH_PREFIX + (0, import_node_crypto60.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
242514
242566
  }
242515
242567
  function safeEqual(a, b2) {
242516
242568
  const ba = Buffer.from(a);
242517
242569
  const bb = Buffer.from(b2);
242518
- return ba.length === bb.length && (0, import_node_crypto59.timingSafeEqual)(ba, bb);
242570
+ return ba.length === bb.length && (0, import_node_crypto60.timingSafeEqual)(ba, bb);
242519
242571
  }
242520
- var import_node_crypto59, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
242572
+ var import_node_crypto60, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
242521
242573
  var init_agent_credentials = __esm({
242522
242574
  "../server/src/domains/companies/agent-credentials.ts"() {
242523
242575
  "use strict";
242524
- import_node_crypto59 = require("node:crypto");
242576
+ import_node_crypto60 = require("node:crypto");
242525
242577
  init_service6();
242526
242578
  HASH_PREFIX = "ac1$";
242527
242579
  TOKEN_PREFIX = "oak_";
@@ -242546,7 +242598,7 @@ var init_agent_credentials = __esm({
242546
242598
  if (employee.status !== "active") {
242547
242599
  throw new CompanyError(409, "AGENT_LEFT", `\u6570\u5B57\u5458\u5DE5\u5DF2\u79BB\u5F00\uFF0C\u4E0D\u80FD\u7B7E\u53D1\u51ED\u8BC1\uFF1A${agentId}`);
242548
242600
  }
242549
- const token = `${TOKEN_PREFIX}${(0, import_node_crypto59.randomBytes)(32).toString("base64url")}`;
242601
+ const token = `${TOKEN_PREFIX}${(0, import_node_crypto60.randomBytes)(32).toString("base64url")}`;
242550
242602
  const credential = {
242551
242603
  tokenHash: hashAgentToken(token, this.secret),
242552
242604
  companyId,
@@ -243037,7 +243089,7 @@ function gatewayInstanceSlug(gatewayUrl) {
243037
243089
  }
243038
243090
  function connectInstanceName(gatewayUrl, token, companyId) {
243039
243091
  const scope2 = companyId?.trim();
243040
- const suffix = scope2 ? `c${(0, import_node_crypto60.createHash)("sha256").update(`${gatewayUrl}\0${scope2}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
243092
+ const suffix = scope2 ? `c${(0, import_node_crypto61.createHash)("sha256").update(`${gatewayUrl}\0${scope2}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
243041
243093
  const base = gatewayInstanceSlug(gatewayUrl);
243042
243094
  return base.slice(0, Math.max(1, 64 - suffix.length - 1)) + "-" + suffix;
243043
243095
  }
@@ -243366,12 +243418,12 @@ function buildNpxCmd(p2) {
243366
243418
  const prefix = daemonNpmPrefix();
243367
243419
  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)}`;
243368
243420
  }
243369
- var import_node_crypto60;
243421
+ var import_node_crypto61;
243370
243422
  var init_connect_script = __esm({
243371
243423
  "../server/src/domains/nodes/connect-script.ts"() {
243372
243424
  "use strict";
243373
243425
  init_src();
243374
- import_node_crypto60 = require("node:crypto");
243426
+ import_node_crypto61 = require("node:crypto");
243375
243427
  }
243376
243428
  });
243377
243429
 
@@ -243731,6 +243783,10 @@ function nodesDomain(deps) {
243731
243783
  }
243732
243784
  if (rows.length > 0) console.log(`[nodes] \u989D\u5EA6\u6C34\u5408\uFF1A\u4ECE\u5E93\u91CC\u8BFB\u56DE ${rows.length} \u6761\u8FD0\u884C\u65F6\u989D\u5EA6`);
243733
243785
  }).catch((err) => console.warn(`[nodes] \u989D\u5EA6\u6C34\u5408\u5931\u8D25\uFF08\u7167\u65E7\u7B49\u4E0B\u4E00\u6B21\u91C7\u96C6\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`));
243786
+ const quotaRefreshWaiters = /* @__PURE__ */ new Map();
243787
+ const quotaRefreshInFlight = /* @__PURE__ */ new Map();
243788
+ const QUOTA_REFRESH_TIMEOUT_MS = 4e4;
243789
+ const QUOTA_CAPABLE_KINDS = /* @__PURE__ */ new Set(["codex", "claude-code"]);
243734
243790
  const UPDATE_TIMEOUT_MS = 15e4;
243735
243791
  const nodeUpdates = /* @__PURE__ */ new Map();
243736
243792
  function runtimeInfoOf(r) {
@@ -243812,6 +243868,45 @@ function nodesDomain(deps) {
243812
243868
  }
243813
243869
  return { version: null, source: null };
243814
243870
  }
243871
+ async function refreshNodeQuotas(nodeId) {
243872
+ const running = quotaRefreshInFlight.get(nodeId);
243873
+ if (running) return running;
243874
+ const hub = deps.getHub();
243875
+ if (!hub) return { ok: false, code: "no-gateway", reason: "\u672C\u90E8\u7F72\u6CA1\u6709\u8282\u70B9\u7F51\u5173\uFF0C\u591F\u4E0D\u5230\u90A3\u53F0\u673A\u5668" };
243876
+ const daemon = hub.connectedDaemons().find((d) => d.daemonId === nodeId);
243877
+ if (!daemon) {
243878
+ return { ok: false, code: "offline", reason: "\u8FD9\u53F0\u8282\u70B9\u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u770B\u5230\u7684\u662F\u5B83\u6700\u540E\u4E00\u6B21\u4E0A\u62A5\u7684\u989D\u5EA6\uFF1B\u628A\u673A\u5668\u8FDE\u56DE\u6765\u4E4B\u540E\u624D\u80FD\u91CD\u91C7" };
243879
+ }
243880
+ if (!daemon.meta.supportsQuotaRefresh) {
243881
+ return { ok: false, code: "unsupported-version", reason: "\u8FD9\u53F0\u8282\u70B9\u7684\u7A0B\u5E8F\u7248\u672C\u8FD8\u4E0D\u652F\u6301\u989D\u5EA6\u91CD\u91C7\uFF0C\u66F4\u65B0\u8282\u70B9\u4E4B\u540E\u53EF\u7528" };
243882
+ }
243883
+ if (!daemon.meta.adapters.some((kind) => QUOTA_CAPABLE_KINDS.has(kind))) {
243884
+ return { ok: false, code: "no-quota-runtime", reason: "\u8FD9\u53F0\u673A\u5668\u4E0A\u6CA1\u6709\u4F1A\u4E0A\u62A5\u989D\u5EA6\u7684\u8FD0\u884C\u65F6\uFF08\u53EA\u6709 Claude Code \u548C Codex \u80FD\u67E5\u5230\u8BA2\u9605\u989D\u5EA6\uFF09" };
243885
+ }
243886
+ const requestId = (0, import_node_crypto62.randomUUID)();
243887
+ const task = new Promise((resolve12) => {
243888
+ const timer = setTimeout(() => {
243889
+ quotaRefreshWaiters.delete(requestId);
243890
+ resolve12({ ok: false, code: "timeout", reason: `\u8FD9\u53F0\u8282\u70B9\u6CA1\u5728 ${Math.round(QUOTA_REFRESH_TIMEOUT_MS / 1e3)} \u79D2\u5185\u56DE\u8BDD\uFF0C\u9875\u9762\u4E0A\u4ECD\u662F\u5B83\u4E0A\u6B21\u62A5\u7684\u989D\u5EA6` });
243891
+ }, QUOTA_REFRESH_TIMEOUT_MS);
243892
+ quotaRefreshWaiters.set(requestId, {
243893
+ // 节点自己说没采(连点被最小间隔挡下、这台机器上采集出错)→ 原样把它那句话带回前端。
243894
+ resolve: (r) => resolve12(r.ok ? { ok: true } : { ok: false, code: "node", ...r.reason ? { reason: r.reason } : {} }),
243895
+ timer
243896
+ });
243897
+ const sent = hub.dispatch(nodeId, { type: "refresh_quotas", requestId });
243898
+ if (sent) return;
243899
+ const waiter = quotaRefreshWaiters.get(requestId);
243900
+ if (!waiter) return;
243901
+ quotaRefreshWaiters.delete(requestId);
243902
+ clearTimeout(waiter.timer);
243903
+ resolve12({ ok: false, code: "offline", reason: "\u6307\u4EE4\u6CA1\u9001\u5230\u8FD9\u53F0\u8282\u70B9\uFF08\u8FDE\u63A5\u521A\u65AD\u5F00\uFF09\uFF0C\u9875\u9762\u4E0A\u4ECD\u662F\u5B83\u4E0A\u6B21\u62A5\u7684\u989D\u5EA6" });
243904
+ }).finally(() => {
243905
+ quotaRefreshInFlight.delete(nodeId);
243906
+ });
243907
+ quotaRefreshInFlight.set(nodeId, task);
243908
+ return task;
243909
+ }
243815
243910
  let hubWired = false;
243816
243911
  function wireHub(hub) {
243817
243912
  if (hubWired) return;
@@ -243848,6 +243943,14 @@ function nodesDomain(deps) {
243848
243943
  }
243849
243944
  })().catch((err) => console.warn(`[nodes] \u6545\u969C\u81EA\u52A8\u89E3\u9664\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`));
243850
243945
  });
243946
+ hub.addMessageListener((_daemonId, msg) => {
243947
+ if (msg.type !== "quota_refresh_result") return;
243948
+ const waiter = quotaRefreshWaiters.get(msg.requestId);
243949
+ if (!waiter) return;
243950
+ quotaRefreshWaiters.delete(msg.requestId);
243951
+ clearTimeout(waiter.timer);
243952
+ waiter.resolve({ ok: msg.ok, ...msg.reason ? { reason: msg.reason } : {} });
243953
+ });
243851
243954
  hub.addMessageListener(async (daemonId, msg) => {
243852
243955
  if (msg.type !== "hello") return;
243853
243956
  const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
@@ -244156,7 +244259,7 @@ function nodesDomain(deps) {
244156
244259
  }
244157
244260
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
244158
244261
  } else {
244159
- nodeId = `node-${(0, import_node_crypto61.randomUUID)().slice(0, 8)}`;
244262
+ nodeId = `node-${(0, import_node_crypto62.randomUUID)().slice(0, 8)}`;
244160
244263
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
244161
244264
  }
244162
244265
  const existingNode = await deps.nodeStore.getNode(nodeId);
@@ -244269,6 +244372,22 @@ function nodesDomain(deps) {
244269
244372
  nodeUpdates.set(nodeId, tracked);
244270
244373
  return { status: 200, body: { ok: true, alreadyLatest: false, updateStatus: publicUpdateStatus(tracked, activeRunCount) } };
244271
244374
  });
244375
+ router.post("/api/nodes/:nodeId/quotas/refresh", async (req) => {
244376
+ const nodeId = req.params["nodeId"];
244377
+ if (!nodeId) return { status: 400, body: { error: "missing nodeId" } };
244378
+ const scope2 = req.auth.companyId !== void 0 ? { companyId: req.auth.companyId } : void 0;
244379
+ const node2 = (await deps.nodeStore.listNodes(scope2)).find((n) => n.id === nodeId);
244380
+ if (!node2) return { status: 404, body: { error: "NODE_NOT_FOUND", message: "\u8282\u70B9\u4E0D\u5B58\u5728\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7EC4\u7EC7" } };
244381
+ const outcome = await refreshNodeQuotas(nodeId);
244382
+ return {
244383
+ status: 200,
244384
+ body: {
244385
+ refreshed: outcome.ok,
244386
+ ...outcome.code ? { code: outcome.code } : {},
244387
+ ...outcome.reason ? { reason: outcome.reason } : {}
244388
+ }
244389
+ };
244390
+ });
244272
244391
  router.delete("/api/runtimes/:runtimeId", async (req) => {
244273
244392
  const runtimeId = req.params["runtimeId"];
244274
244393
  if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
@@ -244406,16 +244525,16 @@ function normalizeGatewayUrl(raw) {
244406
244525
  }
244407
244526
  }
244408
244527
  function newNodeId() {
244409
- return "node-" + (0, import_node_crypto61.randomUUID)().replace(/-/g, "").slice(0, 12);
244528
+ return "node-" + (0, import_node_crypto62.randomUUID)().replace(/-/g, "").slice(0, 12);
244410
244529
  }
244411
- var import_node_crypto61, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
244530
+ var import_node_crypto62, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
244412
244531
  var init_routes7 = __esm({
244413
244532
  "../server/src/domains/nodes/routes.ts"() {
244414
244533
  "use strict";
244415
244534
  init_src();
244416
244535
  init_faults();
244417
244536
  init_runtime_status();
244418
- import_node_crypto61 = require("node:crypto");
244537
+ import_node_crypto62 = require("node:crypto");
244419
244538
  import_node_fs18 = require("node:fs");
244420
244539
  import_node_url6 = require("node:url");
244421
244540
  import_node_path22 = require("node:path");
@@ -244912,9 +245031,9 @@ var init_types4 = __esm({
244912
245031
  // ../server/src/domains/roles/index.ts
244913
245032
  function rolesDomain(deps) {
244914
245033
  return (router) => {
244915
- router.get("/api/roles", async () => {
245034
+ router.get("/api/roles", async (req) => {
244916
245035
  const roles = await deps.admin.list();
244917
- const usageByRole = await deps.admin.usageMany(roles.map((r) => r.name));
245036
+ const usageByRole = await deps.admin.usageMany(roles.map((r) => r.name), req.auth.companyId);
244918
245037
  const items = roles.map((r) => {
244919
245038
  const u = usageByRole[r.name];
244920
245039
  return {
@@ -244933,7 +245052,7 @@ function rolesDomain(deps) {
244933
245052
  if (!name) return { status: 400, body: { error: "missing name" } };
244934
245053
  const roles = await deps.admin.list();
244935
245054
  if (!roles.some((r) => r.name === name)) return { status: 404, body: { error: `\u5C97\u4F4D\u4E0D\u5B58\u5728: ${name}` } };
244936
- return { status: 200, body: { usage: await deps.admin.usage(name) } };
245055
+ return { status: 200, body: { usage: await deps.admin.usage(name, req.auth.companyId) } };
244937
245056
  });
244938
245057
  router.post("/api/roles", async (req) => {
244939
245058
  const r = await deps.admin.add(req.body ?? {}, req.auth.actor);
@@ -245689,6 +245808,12 @@ var init_notification_reconciler = __esm({
245689
245808
  const ctx = await this.deps.load(companyId);
245690
245809
  if (!ctx) return EMPTY;
245691
245810
  const stats = await this.reconcileWith(companyId, st, ctx);
245811
+ if (stats.notified) {
245812
+ try {
245813
+ this.deps.onNotified?.(companyId, stats.notified);
245814
+ } catch {
245815
+ }
245816
+ }
245692
245817
  const tookMs = this.now - startedAt;
245693
245818
  if (tookMs > SLOW_RECONCILE_MS) {
245694
245819
  this.deps.log(`[notification-ledger] ${companyId} \u5BF9\u8D26\u8017\u65F6 ${tookMs}ms\uFF08\u6210\u5458 ${ctx.humans.length}\uFF0C\u5F85\u5904\u7406 ${stats.pending}\uFF0C\u672C\u6B21\u65B0\u589E ${stats.inserted} / \u7ED3\u675F ${stats.closed}\uFF09`);
@@ -245712,6 +245837,7 @@ var init_notification_reconciler = __esm({
245712
245837
  const nowMs = this.now;
245713
245838
  const nowIso = new Date(nowMs).toISOString();
245714
245839
  const stats = { inserted: 0, refreshed: 0, closed: 0, pending: 0 };
245840
+ const notified = /* @__PURE__ */ new Set();
245715
245841
  const ttl = this.deps.snapshotTtlMs ?? 5 * 6e4;
245716
245842
  const loadSnapshot2 = async (wid) => {
245717
245843
  const hit = st.snaps.get(wid);
@@ -245789,6 +245915,7 @@ var init_notification_reconciler = __esm({
245789
245915
  });
245790
245916
  st.lastSeenAt.set(inserted.id, nowIso);
245791
245917
  stats.inserted++;
245918
+ notified.add(c.recipient);
245792
245919
  }
245793
245920
  const eventsOf = /* @__PURE__ */ new Map();
245794
245921
  const cancelledOf = /* @__PURE__ */ new Map();
@@ -245805,7 +245932,7 @@ var init_notification_reconciler = __esm({
245805
245932
  st.lastSeenAt.delete(row.id);
245806
245933
  }
245807
245934
  stats.pending = current.size;
245808
- stats.notices = await this.reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2);
245935
+ stats.notices = await this.reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2, notified);
245809
245936
  if (nowMs - st.lastPurgeAt > (this.deps.purgeEveryMs ?? 6 * 36e5)) {
245810
245937
  st.lastPurgeAt = nowMs;
245811
245938
  const purged = await ctx.store.purgeBefore(new Date(nowMs - (this.deps.retentionMs ?? 90 * DAY2)).toISOString());
@@ -245814,6 +245941,7 @@ var init_notification_reconciler = __esm({
245814
245941
  for (const [id, d] of st.drafts) if (nowMs - d.at > DRAFT_OUTCOME_TTL_MS) st.drafts.delete(id);
245815
245942
  if (waitingForGrace) this.schedule(companyId, this.graceMs);
245816
245943
  st.primed = true;
245944
+ if (notified.size > 0) stats.notified = [...notified];
245817
245945
  return stats;
245818
245946
  }
245819
245947
  async judge(st, ctx, model, row, nowMs, nowIso, eventsOf, cancelledOf) {
@@ -245855,7 +245983,7 @@ var init_notification_reconciler = __esm({
245855
245983
  * 早就结束的任务不能在上线那一刻集体「任务已完成」一遍。之后每拍新看到的变化都发。
245856
245984
  * 来源键 = `<种类>:<工单>@<那件事的 seq>`:同一件事重启后再算一遍撞唯一约束即收手;任务重开后再完成是新的一件事。
245857
245985
  */
245858
- async reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2) {
245986
+ async reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2, notified) {
245859
245987
  const store = ctx.engineStore;
245860
245988
  if (!store) return 0;
245861
245989
  const members = new Set(ctx.humans);
@@ -245877,6 +246005,7 @@ var init_notification_reconciler = __esm({
245877
246005
  ...trigger ? { actorId: trigger } : {},
245878
246006
  enteredAt: new Date(rec2.createdAt).toISOString()
245879
246007
  });
246008
+ notified.add(recipient);
245880
246009
  sent++;
245881
246010
  }
245882
246011
  };
@@ -246072,7 +246201,7 @@ function createBundle(root, target, input) {
246072
246201
  }
246073
246202
  }
246074
246203
  const bytes2 = fs28.statSync(absBundle).size;
246075
- const digest = (0, import_node_crypto62.createHash)("sha256").update(fs28.readFileSync(absBundle)).digest("hex");
246204
+ const digest = (0, import_node_crypto63.createHash)("sha256").update(fs28.readFileSync(absBundle)).digest("hex");
246076
246205
  const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
246077
246206
  return {
246078
246207
  absoluteBundlePath: absBundle,
@@ -246168,12 +246297,12 @@ function classifyGitError(fallback, err, where) {
246168
246297
  function oneLine2(msg) {
246169
246298
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
246170
246299
  }
246171
- var import_node_child_process17, import_node_crypto62, fs28, os7, path25, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
246300
+ var import_node_child_process17, import_node_crypto63, fs28, os7, path25, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
246172
246301
  var init_readonly_bundle = __esm({
246173
246302
  "../server/src/git/readonly-bundle.ts"() {
246174
246303
  "use strict";
246175
246304
  import_node_child_process17 = require("node:child_process");
246176
- import_node_crypto62 = require("node:crypto");
246305
+ import_node_crypto63 = require("node:crypto");
246177
246306
  fs28 = __toESM(require("node:fs"), 1);
246178
246307
  os7 = __toESM(require("node:os"), 1);
246179
246308
  path25 = __toESM(require("node:path"), 1);
@@ -246194,7 +246323,7 @@ var init_readonly_bundle = __esm({
246194
246323
  // ../server/src/domains/collab/create-seeded-workorder.ts
246195
246324
  function makeSeededWorkorderCreator(deps) {
246196
246325
  const locks = createKeyedMutex();
246197
- const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto63.randomUUID)().slice(0, 8)}`);
246326
+ const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto64.randomUUID)().slice(0, 8)}`);
246198
246327
  const create = async (input) => {
246199
246328
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
246200
246329
  const kernel = company?.kernel ?? deps.kernel;
@@ -246281,7 +246410,7 @@ function makeSeededWorkorderCreator(deps) {
246281
246410
  return await finishCreation({ workspace, rootArtifactId: planned.rootArtifactId, spawned: planned.spawned });
246282
246411
  };
246283
246412
  return async (input) => {
246284
- const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto63.randomUUID)()}`);
246413
+ const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto64.randomUUID)()}`);
246285
246414
  try {
246286
246415
  return await create(input);
246287
246416
  } finally {
@@ -246290,7 +246419,7 @@ function makeSeededWorkorderCreator(deps) {
246290
246419
  };
246291
246420
  }
246292
246421
  function workspaceForKey(key2) {
246293
- return `ws:wo-${(0, import_node_crypto63.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
246422
+ return `ws:wo-${(0, import_node_crypto64.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
246294
246423
  }
246295
246424
  function existingWorkorder(snapshot, workspace, actor) {
246296
246425
  if (!snapshot?.nodes.length) return null;
@@ -246302,11 +246431,11 @@ function existingWorkorder(snapshot, workspace, actor) {
246302
246431
  spawned: snapshot.nodes.map((node2) => ({ id: node2.id, type: node2.type, owner: node2.assigneeActorId ?? actor }))
246303
246432
  };
246304
246433
  }
246305
- var import_node_crypto63;
246434
+ var import_node_crypto64;
246306
246435
  var init_create_seeded_workorder = __esm({
246307
246436
  "../server/src/domains/collab/create-seeded-workorder.ts"() {
246308
246437
  "use strict";
246309
- import_node_crypto63 = require("node:crypto");
246438
+ import_node_crypto64 = require("node:crypto");
246310
246439
  init_uncategorized_project();
246311
246440
  init_src();
246312
246441
  init_planner();
@@ -247809,12 +247938,12 @@ function stateFromAttempt(attempt, handoff, checkpoint, now, stalledAfterMs, ope
247809
247938
  if (attempt.status === "no-output") return handoff?.unresolvedIssues.length ? "blocked" : "no_change";
247810
247939
  return "failed";
247811
247940
  }
247812
- var import_node_crypto64, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
247941
+ var import_node_crypto65, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
247813
247942
  var init_service9 = __esm({
247814
247943
  "../server/src/domains/execution-continuity/service.ts"() {
247815
247944
  "use strict";
247816
247945
  init_src();
247817
- import_node_crypto64 = require("node:crypto");
247946
+ import_node_crypto65 = require("node:crypto");
247818
247947
  init_src7();
247819
247948
  BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
247820
247949
  HANDOFF_OUTCOMES2 = /* @__PURE__ */ new Set(["succeeded", "no-output", "failed", "cancelled", "timeout", "orphaned"]);
@@ -248045,7 +248174,7 @@ var init_service9 = __esm({
248045
248174
  if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
248046
248175
  const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
248047
248176
  if (!injectedResume) return null;
248048
- const actualHash = (0, import_node_crypto64.createHash)("sha256").update(injectedResume).digest("hex");
248177
+ const actualHash = (0, import_node_crypto65.createHash)("sha256").update(injectedResume).digest("hex");
248049
248178
  if (actualHash !== expectedHash) return null;
248050
248179
  const recoverySnapshot = parseResumePackSnapshot(injectedResume);
248051
248180
  if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
@@ -248789,6 +248918,87 @@ var init_reply_anchors = __esm({
248789
248918
  }
248790
248919
  });
248791
248920
 
248921
+ // ../server/src/domains/chat-sessions/insert-status.ts
248922
+ async function resolveInsertStatus(deps, chatSessionId, clientKey) {
248923
+ const id = insertRequestId(chatSessionId, clientKey);
248924
+ if (!deps.store.getPendingMessage) return { status: "uncertain" };
248925
+ let row = await deps.store.getPendingMessage(chatSessionId, id);
248926
+ if (row?.state !== "delivering") return { status: insertStatusOfRow(row) };
248927
+ if (deps.isTurnRunning(chatSessionId)) return { status: "in-flight" };
248928
+ if (isInsertInflight(id)) {
248929
+ await waitForInflightInsert(id, deps.settleWaitMs ?? 1e4);
248930
+ row = await deps.store.getPendingMessage(chatSessionId, id);
248931
+ return { status: insertStatusOfRow(row) };
248932
+ }
248933
+ const running = settlingOrphans.get(id);
248934
+ if (running) return running;
248935
+ const settling = settleOrphan(deps, chatSessionId, row).finally(() => {
248936
+ settlingOrphans.delete(id);
248937
+ });
248938
+ settlingOrphans.set(id, settling);
248939
+ return settling;
248940
+ }
248941
+ async function settleOrphan(deps, chatSessionId, row) {
248942
+ const ref2 = row.deliveryRef;
248943
+ const answer = ref2?.nodeId && ref2.dispatchId && deps.confirm ? await deps.confirm({ nodeId: ref2.nodeId, dispatchId: ref2.dispatchId, runtimeKey: ref2.runtimeKey }).catch(() => ({ status: "unknown", reason: "query-failed" })) : { status: "unknown", reason: ref2 ? "no-node" : "no-delivery-ref" };
248944
+ const trace = ref2 && deps.store.listPendingTrace ? await deps.store.listPendingTrace(chatSessionId).catch(() => []) : [];
248945
+ const batch = trace.filter((r) => r.state === "delivering" && r.deliveryRef?.runtimeKey === ref2?.runtimeKey);
248946
+ if (!batch.some((r) => r.id === row.id)) batch.push(row);
248947
+ const ids2 = batch.map((r) => r.id);
248948
+ deps.log?.(`[chat-insert] \u5B64\u513F\u63D2\u5165\u7ED3\u7B97 sid=${chatSessionId} row=${row.id} \u2192 ${answer.status}${answer.reason ? `(${answer.reason})` : ""}`);
248949
+ if (answer.status === "consumed") {
248950
+ await backfill(deps, chatSessionId, batch, row).catch((err) => {
248951
+ deps.log?.(`[chat-insert] \u8865\u884C\u5931\u8D25\uFF08\u4ECD\u6309\u5DF2\u63D2\u5165\u5904\u7406\uFF0Cruntime \u786E\u5B9E\u8BFB\u5230\u4E86\uFF09sid=${chatSessionId}: ${String(err)}`);
248952
+ });
248953
+ await deps.store.markPendingMessages?.(chatSessionId, ids2, "delivered", "delivered:node-confirmed-orphan");
248954
+ return { status: "inserted" };
248955
+ }
248956
+ if (answer.status === "not-consumed") {
248957
+ await deps.store.markPendingMessages?.(chatSessionId, [row.id], "cancelled", "client-gone:not-consumed");
248958
+ const rest = ids2.filter((x2) => x2 !== row.id);
248959
+ if (rest.length) await deps.store.markPendingMessages?.(chatSessionId, rest, "queued", `not-consumed:${answer.reason ?? ""}`);
248960
+ if (row.clientKey) await deps.landUndelivered?.({ clientKey: row.clientKey, rows: [row], note: INSERT_RETURNED_NOTE });
248961
+ return { status: "not-inserted" };
248962
+ }
248963
+ await deps.store.markPendingMessages?.(chatSessionId, ids2, "unconfirmed", `unconfirmed:${answer.reason ?? "unknown"}`);
248964
+ if (row.clientKey) {
248965
+ await deps.landUndelivered?.({ clientKey: row.clientKey, rows: [...batch].sort((a, b2) => a.seq - b2.seq), note: INSERT_UNCERTAIN_NOTE });
248966
+ }
248967
+ return { status: "uncertain" };
248968
+ }
248969
+ async function backfill(deps, chatSessionId, batch, row) {
248970
+ if (!deps.items) return;
248971
+ const ref2 = row.deliveryRef;
248972
+ const sorted = [...batch].sort((a, b2) => a.seq - b2.seq);
248973
+ const ledger = new ChatItemLedger({
248974
+ items: deps.items,
248975
+ versionSeed: await deps.items.sessionVersionCursor(chatSessionId).catch(() => 0),
248976
+ sessionId: chatSessionId,
248977
+ turnId: ref2?.turnId ?? `insert-backfill:${row.id}`,
248978
+ ...deps.log ? { log: deps.log } : {}
248979
+ });
248980
+ const text7 = sorted.map((m2) => m2.text).filter(Boolean).join("\n\n");
248981
+ const attachments = sorted.flatMap((m2) => (m2.attachments ?? []).map((a) => ({
248982
+ name: a.name,
248983
+ ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
248984
+ ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
248985
+ })));
248986
+ const clientKeys = sorted.map((m2) => m2.clientKey).filter((k2) => !!k2);
248987
+ ledger.recordUserInput(text7, attachments, clientKeys, ref2?.ord !== void 0 ? { ord: ref2.ord } : void 0);
248988
+ await ledger.drain();
248989
+ if (ledger.failures > 0) throw new Error("chat_items \u5199\u5165\u5931\u8D25");
248990
+ }
248991
+ var settlingOrphans;
248992
+ var init_insert_status = __esm({
248993
+ "../server/src/domains/chat-sessions/insert-status.ts"() {
248994
+ "use strict";
248995
+ init_chat_item_ledger();
248996
+ init_chat_insert_evidence();
248997
+ init_append_now();
248998
+ settlingOrphans = /* @__PURE__ */ new Map();
248999
+ }
249000
+ });
249001
+
248792
249002
  // ../server/src/domains/chat-sessions/agent-history.ts
248793
249003
  async function resolveChatHistoryViewer(auth, getSession) {
248794
249004
  if (isHumanActorId(auth.actor)) return { kind: "human", actor: auth.actor };
@@ -248991,6 +249201,29 @@ function throwWorkdirError(code2) {
248991
249201
  }
248992
249202
  }
248993
249203
  function createChatSessionsDomain(opts) {
249204
+ const settleInFlightInserts = async ({ store: store2, items, chatSessionId }) => {
249205
+ const rows = await store2.listInFlightInserts(chatSessionId);
249206
+ for (const row of rows) {
249207
+ if (!row.clientKey) continue;
249208
+ await resolveInsertStatus({
249209
+ store: store2,
249210
+ isTurnRunning: (id) => opts.liveChat?.isRunning(id) ?? false,
249211
+ ...opts.confirmInputReceipt ? { confirm: opts.confirmInputReceipt } : {},
249212
+ items,
249213
+ settleWaitMs: INFLIGHT_INSERT_SETTLE_WAIT_MS,
249214
+ landUndelivered: ({ clientKey, rows: batch, note }) => landUndeliveredInsert({
249215
+ store: store2,
249216
+ itemStore: items,
249217
+ chatSessionId,
249218
+ clientKey,
249219
+ text: batch.map((m2) => m2.text).filter(Boolean).join("\n\n"),
249220
+ attachments: batch.flatMap((m2) => m2.attachments ?? []),
249221
+ note
249222
+ }),
249223
+ log: (m2) => console.warn(m2)
249224
+ }, chatSessionId, row.clientKey);
249225
+ }
249226
+ };
248994
249227
  const { store, registry: registry2 } = opts;
248995
249228
  const bindingFor = opts.resolveBinding ?? bindingResolverFromRegistry(registry2);
248996
249229
  async function storeFor(req) {
@@ -249123,7 +249356,7 @@ function createChatSessionsDomain(opts) {
249123
249356
  if (!text7.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
249124
249357
  const now = Date.now();
249125
249358
  sweepDrafts(now);
249126
- const id = `cd_${(0, import_node_crypto65.randomUUID)()}`;
249359
+ const id = `cd_${(0, import_node_crypto66.randomUUID)()}`;
249127
249360
  drafts.set(id, { text: text7, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
249128
249361
  return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
249129
249362
  });
@@ -249200,7 +249433,7 @@ function createChatSessionsDomain(opts) {
249200
249433
  const runtimeId = agentDefault.binding?.nodeId ?? "unknown";
249201
249434
  const now = (/* @__PURE__ */ new Date()).toISOString();
249202
249435
  const session = {
249203
- id: (0, import_node_crypto65.randomUUID)(),
249436
+ id: (0, import_node_crypto66.randomUUID)(),
249204
249437
  humanActorId: req.auth.actor,
249205
249438
  // 注:这里**刻意不写 companyId**。新会话要不要带公司归属,按 ws:wo-6d588f8e 的 ADR
249206
249439
  // (每公司 schema、最终移除表内 company 列)统一定,不在本节点自行发明。
@@ -249319,19 +249552,6 @@ function createChatSessionsDomain(opts) {
249319
249552
  });
249320
249553
  return { status: 200, body: await store2.getSession(req.params.id) };
249321
249554
  });
249322
- router.post("/api/chat-sessions/:id/runtime/reset-to-agent-default", async (req) => {
249323
- const store2 = await storeFor(req);
249324
- const session = await store2.getSession(req.params.id);
249325
- if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
249326
- const agentDefault = await agentDefaultRuntime(req.auth.companyId, session.aiActorId);
249327
- const initial = chatSessionInitialRuntime({ binding: agentDefault.binding, agentModel: agentDefault.model });
249328
- await store2.updateSession(req.params.id, {
249329
- overrideNodeId: initial.overrideNodeId,
249330
- overrideRuntimeKind: initial.overrideRuntimeKind,
249331
- model: initial.model
249332
- });
249333
- return { status: 200, body: await store2.getSession(req.params.id) };
249334
- });
249335
249555
  router.delete("/api/chat-sessions/:id", async (req) => {
249336
249556
  const store2 = await storeFor(req);
249337
249557
  const session = await store2.getSession(req.params.id);
@@ -249348,7 +249568,7 @@ function createChatSessionsDomain(opts) {
249348
249568
  if (body2.role !== "user" && body2.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
249349
249569
  const role = body2.role;
249350
249570
  const msg = await store2.appendMessage({
249351
- id: (0, import_node_crypto65.randomUUID)(),
249571
+ id: (0, import_node_crypto66.randomUUID)(),
249352
249572
  sessionId: req.params.id,
249353
249573
  role,
249354
249574
  content: role === "user" ? stripInjectedChatContext(body2.content) : body2.content,
@@ -249390,7 +249610,7 @@ function createChatSessionsDomain(opts) {
249390
249610
  const known = key2 ? cur.find((x2) => x2.clientKey === key2) : void 0;
249391
249611
  if (!known && cur.length >= QUEUE_MAX) throw new ApiError(409, "QUEUE_FULL", `\u961F\u5217\u5DF2\u6EE1\uFF08\u4E0A\u9650 ${QUEUE_MAX} \u6761\uFF09`);
249392
249612
  const item = await store2.enqueuePendingMessage({
249393
- id: (0, import_node_crypto65.randomUUID)(),
249613
+ id: (0, import_node_crypto66.randomUUID)(),
249394
249614
  chatSessionId: sid,
249395
249615
  text: text7,
249396
249616
  ...attachments.length ? { attachments } : {},
@@ -249440,6 +249660,11 @@ function createChatSessionsDomain(opts) {
249440
249660
  const stale = Number.isFinite(startedAt) && Date.now() - startedAt >= LINK_WINDOWS.chatTurnPhaseStaleAfterMs;
249441
249661
  return { turnId: activeTurnRow.id, phase: stale ? "recovering" : "running" };
249442
249662
  })();
249663
+ if (sinceVersion === 0 && beforeOrd === void 0 && session?.humanActorId === req.auth.actor && store2.listInFlightInserts) {
249664
+ await settleInFlightInserts({ store: store2, items, chatSessionId }).catch((e) => {
249665
+ console.warn(`[chat] \u5728\u98DE\u63D2\u8BDD\u7ED3\u7B97\u5931\u8D25\uFF08session=${chatSessionId}\uFF09\uFF0C\u8FD9\u6B21\u5FEB\u7167\u7167\u65E7\u6295\u5F71: ${String(e)}`);
249666
+ });
249667
+ }
249443
249668
  const snapshotEntries = [];
249444
249669
  let nextSinceVersion = sinceVersion;
249445
249670
  let hasMoreBefore = false;
@@ -250238,12 +250463,12 @@ async function legacySendFailureCards(entries, deps) {
250238
250463
  }
250239
250464
  return out;
250240
250465
  }
250241
- var import_node_crypto65, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS, PROJECTED_CARD_LIMIT, SUBMISSION_NOT_DELIVERED_NOTE, SUBMISSION_UNCONFIRMED_NOTE, LEGACY_CARD_LIMIT;
250466
+ var import_node_crypto66, DEFAULT_SNAPSHOT_ITEMS, INFLIGHT_INSERT_SETTLE_WAIT_MS, MAX_SNAPSHOT_ITEMS, PROJECTED_CARD_LIMIT, SUBMISSION_NOT_DELIVERED_NOTE, SUBMISSION_UNCONFIRMED_NOTE, LEGACY_CARD_LIMIT;
250242
250467
  var init_chat_sessions2 = __esm({
250243
250468
  "../server/src/domains/chat-sessions/index.ts"() {
250244
250469
  "use strict";
250245
250470
  init_file_limits2();
250246
- import_node_crypto65 = require("node:crypto");
250471
+ import_node_crypto66 = require("node:crypto");
250247
250472
  init_src();
250248
250473
  init_chat_session();
250249
250474
  init_persistent_files();
@@ -250259,6 +250484,8 @@ var init_chat_sessions2 = __esm({
250259
250484
  init_service3();
250260
250485
  init_reply_anchors();
250261
250486
  init_injected_context();
250487
+ init_insert_status();
250488
+ init_insert_undelivered();
250262
250489
  init_agent_history();
250263
250490
  init_deletion();
250264
250491
  init_chat_turn_gate();
@@ -250270,6 +250497,7 @@ var init_chat_sessions2 = __esm({
250270
250497
  init_chat_submission();
250271
250498
  init_project_bundle();
250272
250499
  DEFAULT_SNAPSHOT_ITEMS = 400;
250500
+ INFLIGHT_INSERT_SETTLE_WAIT_MS = 6e3;
250273
250501
  MAX_SNAPSHOT_ITEMS = 1e3;
250274
250502
  PROJECTED_CARD_LIMIT = 10;
250275
250503
  SUBMISSION_NOT_DELIVERED_NOTE = "\u8FD9\u6761\u6D88\u606F\u6CA1\u6709\u53D1\u51FA\uFF0C\u53EF\u4EE5\u91CD\u8BD5";
@@ -251700,7 +251928,7 @@ function busyError(chatSessionId, blockedBy) {
251700
251928
  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`;
251701
251929
  }
251702
251930
  function genWebhookToken() {
251703
- return `owt_${(0, import_node_crypto66.randomBytes)(32).toString("base64url")}`;
251931
+ return `owt_${(0, import_node_crypto67.randomBytes)(32).toString("base64url")}`;
251704
251932
  }
251705
251933
  function validateTitleTemplate(tpl) {
251706
251934
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -251756,11 +251984,11 @@ function appendSourceNote(brief, automation, source, atIso) {
251756
251984
  ---
251757
251985
  \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`;
251758
251986
  }
251759
- var import_node_crypto66, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
251987
+ var import_node_crypto67, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
251760
251988
  var init_service10 = __esm({
251761
251989
  "../server/src/domains/automations/service.ts"() {
251762
251990
  "use strict";
251763
- import_node_crypto66 = require("node:crypto");
251991
+ import_node_crypto67 = require("node:crypto");
251764
251992
  init_src();
251765
251993
  init_registry3();
251766
251994
  init_attribution();
@@ -252574,11 +252802,11 @@ function verifySignature(rawBody, headers, secret) {
252574
252802
  if (!secret) return "not_required";
252575
252803
  const given = header1(headers, "x-hub-signature-256");
252576
252804
  if (!given) return "missing";
252577
- const expected = `sha256=${(0, import_node_crypto67.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
252805
+ const expected = `sha256=${(0, import_node_crypto68.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
252578
252806
  const a = Buffer.from(given);
252579
252807
  const b2 = Buffer.from(expected);
252580
252808
  if (a.length !== b2.length) return "invalid";
252581
- return (0, import_node_crypto67.timingSafeEqual)(a, b2) ? "valid" : "invalid";
252809
+ return (0, import_node_crypto68.timingSafeEqual)(a, b2) ? "valid" : "invalid";
252582
252810
  }
252583
252811
  function eventAllowed(filters, envelope) {
252584
252812
  if (!filters || filters.length === 0) return true;
@@ -252608,11 +252836,11 @@ function splitEvent(envelope) {
252608
252836
  }
252609
252837
  return { eventName, actionCandidates: candidates };
252610
252838
  }
252611
- var import_node_crypto67, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
252839
+ var import_node_crypto68, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
252612
252840
  var init_webhook = __esm({
252613
252841
  "../server/src/domains/automations/webhook.ts"() {
252614
252842
  "use strict";
252615
- import_node_crypto67 = require("node:crypto");
252843
+ import_node_crypto68 = require("node:crypto");
252616
252844
  MAX_BODY_BYTES = 256 * 1024;
252617
252845
  AutomationWebhookService = class {
252618
252846
  store;
@@ -253764,7 +253992,7 @@ function playbooksDomain(opts) {
253764
253992
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
253765
253993
  try {
253766
253994
  await opts.audit?.({
253767
- id: `reg_${(0, import_node_crypto68.randomUUID)()}`,
253995
+ id: `reg_${(0, import_node_crypto69.randomUUID)()}`,
253768
253996
  actor: req.auth.actor,
253769
253997
  kind: "registry_change",
253770
253998
  target: `playbook:${ref2}#${nodeKey}`,
@@ -253781,11 +254009,11 @@ function playbooksDomain(opts) {
253781
254009
  });
253782
254010
  };
253783
254011
  }
253784
- var import_node_crypto68;
254012
+ var import_node_crypto69;
253785
254013
  var init_routes12 = __esm({
253786
254014
  "../server/src/domains/playbooks/routes.ts"() {
253787
254015
  "use strict";
253788
- import_node_crypto68 = require("node:crypto");
254016
+ import_node_crypto69 = require("node:crypto");
253789
254017
  init_router();
253790
254018
  init_registry3();
253791
254019
  init_planner();
@@ -253914,17 +254142,17 @@ function completionFingerprint(workspace, members) {
253914
254142
  workspace,
253915
254143
  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 }))
253916
254144
  });
253917
- return (0, import_node_crypto69.createHash)("sha256").update(canonical2).digest("hex");
254145
+ return (0, import_node_crypto70.createHash)("sha256").update(canonical2).digest("hex");
253918
254146
  }
253919
254147
  function eventConsumptionKey(args) {
253920
254148
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
253921
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto69.createHash)("sha256").update(raw).digest("hex")}`;
254149
+ return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto70.createHash)("sha256").update(raw).digest("hex")}`;
253922
254150
  }
253923
- var import_node_crypto69, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
254151
+ var import_node_crypto70, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
253924
254152
  var init_project_event_poller = __esm({
253925
254153
  "../server/src/automations/project-event-poller.ts"() {
253926
254154
  "use strict";
253927
- import_node_crypto69 = require("node:crypto");
254155
+ import_node_crypto70 = require("node:crypto");
253928
254156
  EVENT_KEY_VERSION = "v1";
253929
254157
  EMPTY_METRICS = () => ({
253930
254158
  scanDurationMs: 0,
@@ -254134,7 +254362,7 @@ function queryInputReceipt(hub, nodeId, dispatchId, clientMessageKey, pending, t
254134
254362
  const viaSession = hub.hasSession?.(dispatchId) === true;
254135
254363
  const supported = viaSession ? hub.supportsInputReceiptQuery?.(dispatchId) === true : daemonMetaOf(hub, nodeId)?.supportsInputReceiptQuery === true;
254136
254364
  if (!supported) return Promise.resolve({ status: "unknown", reason: "unsupported" });
254137
- const requestId = (0, import_node_crypto70.randomUUID)();
254365
+ const requestId = (0, import_node_crypto71.randomUUID)();
254138
254366
  return new Promise((resolve12) => {
254139
254367
  const timer = setTimeout(() => {
254140
254368
  pending.delete(requestId);
@@ -254196,7 +254424,7 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
254196
254424
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
254197
254425
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
254198
254426
  }
254199
- const requestId = (0, import_node_crypto70.randomUUID)();
254427
+ const requestId = (0, import_node_crypto71.randomUUID)();
254200
254428
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
254201
254429
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
254202
254430
  return new Promise((resolve12) => {
@@ -254209,11 +254437,11 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
254209
254437
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve12, timer });
254210
254438
  });
254211
254439
  }
254212
- var import_node_crypto70, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
254440
+ var import_node_crypto71, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
254213
254441
  var init_daemon_adapter = __esm({
254214
254442
  "../server/src/daemon-adapter.ts"() {
254215
254443
  "use strict";
254216
- import_node_crypto70 = require("node:crypto");
254444
+ import_node_crypto71 = require("node:crypto");
254217
254445
  init_src();
254218
254446
  STASH_TTL_MS = 5 * 6e4;
254219
254447
  STASH_MAX_FRAMES_PER_ID = 1e4;
@@ -254653,7 +254881,7 @@ var init_daemon_adapter = __esm({
254653
254881
  async spawn(job) {
254654
254882
  const nodeId = job.binding?.nodeId;
254655
254883
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
254656
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto70.randomUUID)()}`;
254884
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto71.randomUUID)()}`;
254657
254885
  if (job.dispatchId && this.pending.has(dispatchId)) {
254658
254886
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
254659
254887
  }
@@ -254909,7 +255137,7 @@ var init_daemon_adapter = __esm({
254909
255137
  return daemon?.meta.supportsWorkdirWalk === true;
254910
255138
  }
254911
255139
  request(nodeId, buildFrame) {
254912
- const requestId = (0, import_node_crypto70.randomUUID)();
255140
+ const requestId = (0, import_node_crypto71.randomUUID)();
254913
255141
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
254914
255142
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
254915
255143
  return new Promise((resolve12) => {
@@ -255017,13 +255245,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
255017
255245
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
255018
255246
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
255019
255247
  }
255020
- var import_node_crypto71, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
255248
+ var import_node_crypto72, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
255021
255249
  var init_engine = __esm({
255022
255250
  "../server/src/mirror/engine.ts"() {
255023
255251
  "use strict";
255024
- import_node_crypto71 = require("node:crypto");
255252
+ import_node_crypto72 = require("node:crypto");
255025
255253
  init_src();
255026
- sha = (s2) => (0, import_node_crypto71.createHash)("sha256").update(s2, "utf8").digest("hex");
255254
+ sha = (s2) => (0, import_node_crypto72.createHash)("sha256").update(s2, "utf8").digest("hex");
255027
255255
  enc2 = (s2) => new TextEncoder().encode(s2);
255028
255256
  dec = (b2) => new TextDecoder().decode(b2);
255029
255257
  MirrorEngine = class {
@@ -255304,11 +255532,11 @@ function humanExitCause(exit3) {
255304
255532
  const base = (exit3.reason ? label2[exit3.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit3.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
255305
255533
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
255306
255534
  }
255307
- var import_node_crypto72, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
255535
+ var import_node_crypto73, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
255308
255536
  var init_worker = __esm({
255309
255537
  "../server/src/coordinator/worker.ts"() {
255310
255538
  "use strict";
255311
- import_node_crypto72 = require("node:crypto");
255539
+ import_node_crypto73 = require("node:crypto");
255312
255540
  init_src7();
255313
255541
  init_identity();
255314
255542
  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
@@ -255578,7 +255806,7 @@ ${HIGH_RISK_COMMANDS}`;
255578
255806
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
255579
255807
  }
255580
255808
  evaluationKey(kind, artifactId, evidence) {
255581
- const fingerprint2 = (0, import_node_crypto72.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
255809
+ const fingerprint2 = (0, import_node_crypto73.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
255582
255810
  return `${kind}:${artifactId}:${fingerprint2}`;
255583
255811
  }
255584
255812
  artifactEvidence(id) {
@@ -255967,8 +256195,8 @@ ${ctx.nodeFault}
255967
256195
  const taskWithRejections = rejectedSection ? `${task}
255968
256196
 
255969
256197
  ${rejectedSection}` : task;
255970
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto72.randomUUID)();
255971
- const coordinatorRunId = `coordinate:${(0, import_node_crypto72.randomUUID)()}`;
256198
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto73.randomUUID)();
256199
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto73.randomUUID)()}`;
255972
256200
  const taskWithContext = coordinatorContext ? [
255973
256201
  taskWithRejections,
255974
256202
  ``,
@@ -256012,7 +256240,7 @@ ${rejectedSection}` : task;
256012
256240
  const startedAtMs = Date.now();
256013
256241
  let trajectoryStarted = false;
256014
256242
  if (this.deps.trajectory) {
256015
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto72.createHash)("sha256").update(content3).digest("hex")]));
256243
+ const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto73.createHash)("sha256").update(content3).digest("hex")]));
256016
256244
  const session = {
256017
256245
  runId: coordinatorRunId,
256018
256246
  runtimeSessionId,
@@ -256223,11 +256451,11 @@ var init_pg_ident = __esm({
256223
256451
  });
256224
256452
 
256225
256453
  // ../storage/src/postgres.ts
256226
- var import_node_crypto73, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
256454
+ var import_node_crypto74, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
256227
256455
  var init_postgres = __esm({
256228
256456
  "../storage/src/postgres.ts"() {
256229
256457
  "use strict";
256230
- import_node_crypto73 = require("node:crypto");
256458
+ import_node_crypto74 = require("node:crypto");
256231
256459
  init_pg_ident();
256232
256460
  init_esm();
256233
256461
  init_src();
@@ -256407,7 +256635,7 @@ var init_postgres = __esm({
256407
256635
  return new _PostgresBlobStore(pool, schema);
256408
256636
  }
256409
256637
  async put(bytes2) {
256410
- const hash3 = (0, import_node_crypto73.createHash)("sha256").update(bytes2).digest("hex");
256638
+ const hash3 = (0, import_node_crypto74.createHash)("sha256").update(bytes2).digest("hex");
256411
256639
  await this.pool.query(
256412
256640
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
256413
256641
  [hash3, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -256455,7 +256683,7 @@ function stageAtLeast(stage, min2) {
256455
256683
  return STAGES.indexOf(stage) >= STAGES.indexOf(min2);
256456
256684
  }
256457
256685
  function newVariableId() {
256458
- return `var_${(0, import_node_crypto74.randomUUID)()}`;
256686
+ return `var_${(0, import_node_crypto75.randomUUID)()}`;
256459
256687
  }
256460
256688
  async function declareRecordsAware(db) {
256461
256689
  await db.query(`SELECT set_config('${RECORDS_AWARE_SETTING}', 'on', true)`);
@@ -256811,11 +257039,11 @@ async function listBindingTargetIds(db, s2, kind) {
256811
257039
  [kind]
256812
257040
  )).rows.map((x2) => x2.target_id);
256813
257041
  }
256814
- var import_node_crypto74, STAGES, RECORDS_AWARE_SETTING, markLockName;
257042
+ var import_node_crypto75, STAGES, RECORDS_AWARE_SETTING, markLockName;
256815
257043
  var init_variable_bindings = __esm({
256816
257044
  "../storage/src/variable-bindings.ts"() {
256817
257045
  "use strict";
256818
- import_node_crypto74 = require("node:crypto");
257046
+ import_node_crypto75 = require("node:crypto");
256819
257047
  init_src();
256820
257048
  STAGES = ["off", "dual", "read", "records"];
256821
257049
  RECORDS_AWARE_SETTING = "oasis.variable_records_aware";
@@ -266543,11 +266771,11 @@ var init_postgres_delegations = __esm({
266543
266771
  });
266544
266772
 
266545
266773
  // ../storage/src/postgres-nodes.ts
266546
- var import_node_crypto75, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
266774
+ var import_node_crypto76, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
266547
266775
  var init_postgres_nodes = __esm({
266548
266776
  "../storage/src/postgres-nodes.ts"() {
266549
266777
  "use strict";
266550
- import_node_crypto75 = require("node:crypto");
266778
+ import_node_crypto76 = require("node:crypto");
266551
266779
  init_pg_ident();
266552
266780
  init_esm();
266553
266781
  ident13 = (s2) => {
@@ -266897,7 +267125,7 @@ var init_postgres_nodes = __esm({
266897
267125
  return store;
266898
267126
  }
266899
267127
  issue(nodeId) {
266900
- const token = `ont_${(0, import_node_crypto75.randomBytes)(24).toString("base64url")}`;
267128
+ const token = `ont_${(0, import_node_crypto76.randomBytes)(24).toString("base64url")}`;
266901
267129
  this.cache.set(token, nodeId);
266902
267130
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
266903
267131
  return token;
@@ -267199,11 +267427,11 @@ var init_postgres_type_registry = __esm({
267199
267427
  });
267200
267428
 
267201
267429
  // ../storage/src/postgres-actor-memory.ts
267202
- var import_node_crypto76, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
267430
+ var import_node_crypto77, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
267203
267431
  var init_postgres_actor_memory = __esm({
267204
267432
  "../storage/src/postgres-actor-memory.ts"() {
267205
267433
  "use strict";
267206
- import_node_crypto76 = require("node:crypto");
267434
+ import_node_crypto77 = require("node:crypto");
267207
267435
  init_pg_ident();
267208
267436
  init_src();
267209
267437
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -267413,7 +267641,7 @@ var init_postgres_actor_memory = __esm({
267413
267641
  }
267414
267642
  async write(input, now) {
267415
267643
  if (input.memId === void 0) {
267416
- const memId = `mem:${(0, import_node_crypto76.randomUUID)()}`;
267644
+ const memId = `mem:${(0, import_node_crypto77.randomUUID)()}`;
267417
267645
  const r2 = await this.pool.query(
267418
267646
  `INSERT INTO ${this.s}.actor_memories
267419
267647
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -267651,6 +267879,124 @@ var init_postgres_inbox_read = __esm({
267651
267879
  }
267652
267880
  });
267653
267881
 
267882
+ // ../storage/src/postgres-connector-auth-pending.ts
267883
+ var ident17, PostgresGithubAppPendingStore, PostgresConnectorAuthSnapshotStore;
267884
+ var init_postgres_connector_auth_pending = __esm({
267885
+ "../storage/src/postgres-connector-auth-pending.ts"() {
267886
+ "use strict";
267887
+ init_esm();
267888
+ init_pg_ident();
267889
+ ident17 = (s2) => {
267890
+ quoteIdent2(s2);
267891
+ return s2;
267892
+ };
267893
+ PostgresGithubAppPendingStore = class _PostgresGithubAppPendingStore {
267894
+ constructor(pool, schema) {
267895
+ this.pool = pool;
267896
+ this.s = `"${ident17(schema)}"`;
267897
+ }
267898
+ s;
267899
+ static async open(pool, schema = "public") {
267900
+ const s2 = ident17(schema);
267901
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
267902
+ await pool.query(`
267903
+ CREATE TABLE IF NOT EXISTS "${s2}".github_app_pending (
267904
+ state text PRIMARY KEY,
267905
+ company_id text,
267906
+ data jsonb NOT NULL,
267907
+ created_at timestamptz NOT NULL,
267908
+ expires_at timestamptz NOT NULL
267909
+ )`);
267910
+ return new _PostgresGithubAppPendingStore(pool, schema);
267911
+ }
267912
+ async put(entry) {
267913
+ await this.pool.query(
267914
+ `DELETE FROM ${this.s}.github_app_pending WHERE expires_at < $1`,
267915
+ [new Date(entry.createdAtMs)]
267916
+ );
267917
+ await this.pool.query(
267918
+ `INSERT INTO ${this.s}.github_app_pending (state, company_id, data, created_at, expires_at)
267919
+ VALUES ($1, $2, $3::jsonb, $4, $5)
267920
+ ON CONFLICT (state) DO UPDATE SET company_id = EXCLUDED.company_id, data = EXCLUDED.data,
267921
+ created_at = EXCLUDED.created_at, expires_at = EXCLUDED.expires_at`,
267922
+ [
267923
+ entry.state,
267924
+ entry.companyId ?? null,
267925
+ JSON.stringify(entry.value),
267926
+ new Date(entry.createdAtMs),
267927
+ new Date(entry.expiresAtMs)
267928
+ ]
267929
+ );
267930
+ }
267931
+ async get(state, nowMs) {
267932
+ if (!state) return { status: "missing" };
267933
+ const r = await this.pool.query(
267934
+ `SELECT company_id, data, expires_at FROM ${this.s}.github_app_pending WHERE state = $1`,
267935
+ [state]
267936
+ );
267937
+ const row = r.rows[0];
267938
+ if (!row) return { status: "missing" };
267939
+ if (row.expires_at.getTime() < nowMs) return { status: "expired" };
267940
+ return { status: "found", value: { ...row.company_id ? { companyId: row.company_id } : {}, value: row.data } };
267941
+ }
267942
+ async patch(state, fields) {
267943
+ await this.pool.query(
267944
+ `UPDATE ${this.s}.github_app_pending SET data = data || $2::jsonb WHERE state = $1`,
267945
+ [state, JSON.stringify(fields)]
267946
+ );
267947
+ }
267948
+ };
267949
+ PostgresConnectorAuthSnapshotStore = class _PostgresConnectorAuthSnapshotStore {
267950
+ constructor(pool, schema) {
267951
+ this.pool = pool;
267952
+ this.s = `"${ident17(schema)}"`;
267953
+ }
267954
+ s;
267955
+ static async open(pool, schema = "public") {
267956
+ const s2 = ident17(schema);
267957
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
267958
+ await pool.query(`
267959
+ CREATE TABLE IF NOT EXISTS "${s2}".connector_auth_snapshots (
267960
+ company_id text NOT NULL,
267961
+ actor_id text NOT NULL,
267962
+ connector_id text NOT NULL,
267963
+ rows jsonb NOT NULL,
267964
+ created_at timestamptz NOT NULL,
267965
+ expires_at timestamptz NOT NULL,
267966
+ PRIMARY KEY (company_id, actor_id, connector_id)
267967
+ )`);
267968
+ return new _PostgresConnectorAuthSnapshotStore(pool, schema);
267969
+ }
267970
+ async put(key2, rows, nowMs, expiresAtMs) {
267971
+ await this.pool.query(`DELETE FROM ${this.s}.connector_auth_snapshots WHERE expires_at < $1`, [new Date(nowMs)]);
267972
+ await this.pool.query(
267973
+ `INSERT INTO ${this.s}.connector_auth_snapshots (company_id, actor_id, connector_id, rows, created_at, expires_at)
267974
+ VALUES ($1, $2, $3, $4::jsonb, $5, $6)
267975
+ ON CONFLICT (company_id, actor_id, connector_id) DO UPDATE SET rows = EXCLUDED.rows,
267976
+ created_at = EXCLUDED.created_at, expires_at = EXCLUDED.expires_at`,
267977
+ [key2.companyId, key2.actorId, key2.connectorId, JSON.stringify(rows), new Date(nowMs), new Date(expiresAtMs)]
267978
+ );
267979
+ }
267980
+ async get(key2, nowMs) {
267981
+ const r = await this.pool.query(
267982
+ `SELECT rows, expires_at FROM ${this.s}.connector_auth_snapshots
267983
+ WHERE company_id = $1 AND actor_id = $2 AND connector_id = $3`,
267984
+ [key2.companyId, key2.actorId, key2.connectorId]
267985
+ );
267986
+ const row = r.rows[0];
267987
+ if (!row || row.expires_at.getTime() < nowMs) return void 0;
267988
+ return row.rows;
267989
+ }
267990
+ async delete(key2) {
267991
+ await this.pool.query(
267992
+ `DELETE FROM ${this.s}.connector_auth_snapshots WHERE company_id = $1 AND actor_id = $2 AND connector_id = $3`,
267993
+ [key2.companyId, key2.actorId, key2.connectorId]
267994
+ );
267995
+ }
267996
+ };
267997
+ }
267998
+ });
267999
+
267654
268000
  // ../storage/src/postgres-dispatches.ts
267655
268001
  function rowToRecord2(r) {
267656
268002
  return {
@@ -267672,18 +268018,18 @@ function rowToRecord2(r) {
267672
268018
  protocolRecoveryRound: r.protocol_recovery_round ?? null
267673
268019
  };
267674
268020
  }
267675
- var ident17, PostgresDispatchStore;
268021
+ var ident18, PostgresDispatchStore;
267676
268022
  var init_postgres_dispatches = __esm({
267677
268023
  "../storage/src/postgres-dispatches.ts"() {
267678
268024
  "use strict";
267679
- ident17 = (schema) => schema.replace(/[^a-zA-Z0-9_]/g, "");
268025
+ ident18 = (schema) => schema.replace(/[^a-zA-Z0-9_]/g, "");
267680
268026
  PostgresDispatchStore = class _PostgresDispatchStore {
267681
268027
  constructor(pool, s2) {
267682
268028
  this.pool = pool;
267683
268029
  this.s = s2;
267684
268030
  }
267685
268031
  static async open(pool, schema = "public") {
267686
- const s2 = ident17(schema);
268032
+ const s2 = ident18(schema);
267687
268033
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
267688
268034
  await pool.query(`
267689
268035
  CREATE TABLE IF NOT EXISTS "${s2}".dispatches (
@@ -268866,14 +269212,14 @@ function companySchemaName(companyId) {
268866
269212
  if (typeof companyId !== "string" || !companyId.trim()) {
268867
269213
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
268868
269214
  }
268869
- const hex = (0, import_node_crypto77.createHash)("sha256").update(companyId, "utf8").digest("hex");
269215
+ const hex = (0, import_node_crypto78.createHash)("sha256").update(companyId, "utf8").digest("hex");
268870
269216
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
268871
269217
  }
268872
- var import_node_crypto77, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
269218
+ var import_node_crypto78, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
268873
269219
  var init_tenant_schema_mapping = __esm({
268874
269220
  "../storage/src/tenant-schema-mapping.ts"() {
268875
269221
  "use strict";
268876
- import_node_crypto77 = require("node:crypto");
269222
+ import_node_crypto78 = require("node:crypto");
268877
269223
  init_esm();
268878
269224
  init_pg_ident();
268879
269225
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -268947,7 +269293,7 @@ var init_tenant_schema_mapping = __esm({
268947
269293
 
268948
269294
  // ../storage/src/company-data-plane-schema.ts
268949
269295
  function canonicalTypeDefHash(def) {
268950
- return `sha256:${(0, import_node_crypto78.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
269296
+ return `sha256:${(0, import_node_crypto79.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
268951
269297
  }
268952
269298
  function defaultArtifactTypeTemplate() {
268953
269299
  return [
@@ -269129,11 +269475,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
269129
269475
  }
269130
269476
  return { inserted };
269131
269477
  }
269132
- var import_node_crypto78, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
269478
+ var import_node_crypto79, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
269133
269479
  var init_company_data_plane_schema = __esm({
269134
269480
  "../storage/src/company-data-plane-schema.ts"() {
269135
269481
  "use strict";
269136
- import_node_crypto78 = require("node:crypto");
269482
+ import_node_crypto79 = require("node:crypto");
269137
269483
  init_esm();
269138
269484
  init_pg_ident();
269139
269485
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -269212,11 +269558,11 @@ function rowToFile2(row) {
269212
269558
  updatedAt: new Date(row.updated_at).toISOString()
269213
269559
  };
269214
269560
  }
269215
- var import_node_crypto79, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
269561
+ var import_node_crypto80, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
269216
269562
  var init_company_owned_registry_store = __esm({
269217
269563
  "../storage/src/company-owned-registry-store.ts"() {
269218
269564
  "use strict";
269219
- import_node_crypto79 = require("node:crypto");
269565
+ import_node_crypto80 = require("node:crypto");
269220
269566
  init_esm();
269221
269567
  init_src();
269222
269568
  init_pg_ident();
@@ -269601,7 +269947,7 @@ var init_company_owned_registry_store = __esm({
269601
269947
  enabled: skill.enabled,
269602
269948
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
269603
269949
  });
269604
- return (0, import_node_crypto79.createHash)("sha256").update(body2).digest("hex");
269950
+ return (0, import_node_crypto80.createHash)("sha256").update(body2).digest("hex");
269605
269951
  }
269606
269952
  };
269607
269953
  }
@@ -272411,7 +272757,7 @@ function sourceSetConservation(input) {
272411
272757
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
272412
272758
  }
272413
272759
  function payloadChecksum(value2) {
272414
- return (0, import_node_crypto80.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
272760
+ return (0, import_node_crypto81.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
272415
272761
  }
272416
272762
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
272417
272763
  const cs = quoteIdent2(companySchema);
@@ -272803,11 +273149,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
272803
273149
  sequences
272804
273150
  };
272805
273151
  }
272806
- var import_node_crypto80, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
273152
+ var import_node_crypto81, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
272807
273153
  var init_tenant_migration_operator = __esm({
272808
273154
  "../storage/src/tenant-migration-operator.ts"() {
272809
273155
  "use strict";
272810
- import_node_crypto80 = require("node:crypto");
273156
+ import_node_crypto81 = require("node:crypto");
272811
273157
  init_esm();
272812
273158
  init_pg_ident();
272813
273159
  init_tenancy_attribution();
@@ -273043,8 +273389,8 @@ var init_tenant_new_company_provision = __esm({
273043
273389
  // ../storage/src/knowledge-snapshot-crypto.ts
273044
273390
  function sealSnapshot(snapshot, key2) {
273045
273391
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
273046
- const iv = (0, import_node_crypto81.randomBytes)(12);
273047
- const cipher = (0, import_node_crypto81.createCipheriv)("aes-256-gcm", bytes(key2), iv);
273392
+ const iv = (0, import_node_crypto82.randomBytes)(12);
273393
+ const cipher = (0, import_node_crypto82.createCipheriv)("aes-256-gcm", bytes(key2), iv);
273048
273394
  cipher.setAAD(aad2(snapshot));
273049
273395
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
273050
273396
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -273054,7 +273400,7 @@ function openSnapshot(snapshot, key2) {
273054
273400
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
273055
273401
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
273056
273402
  const [iv, tag, data] = packed;
273057
- const decipher = (0, import_node_crypto81.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
273403
+ const decipher = (0, import_node_crypto82.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
273058
273404
  decipher.setAAD(aad2(snapshot));
273059
273405
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
273060
273406
  return {
@@ -273062,11 +273408,11 @@ function openSnapshot(snapshot, key2) {
273062
273408
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
273063
273409
  };
273064
273410
  }
273065
- var import_node_crypto81, PREFIX3, bytes, aad2;
273411
+ var import_node_crypto82, PREFIX3, bytes, aad2;
273066
273412
  var init_knowledge_snapshot_crypto = __esm({
273067
273413
  "../storage/src/knowledge-snapshot-crypto.ts"() {
273068
273414
  "use strict";
273069
- import_node_crypto81 = require("node:crypto");
273415
+ import_node_crypto82 = require("node:crypto");
273070
273416
  PREFIX3 = "oasis-knowledge:v1:";
273071
273417
  bytes = (key2) => {
273072
273418
  const value2 = Buffer.from(key2);
@@ -273114,7 +273460,7 @@ function assertRecordScope(record9, scope2) {
273114
273460
  function equal(left, right) {
273115
273461
  return JSON.stringify(left) === JSON.stringify(right);
273116
273462
  }
273117
- var ident18, TABLES, TABLE_BY_KIND, PostgresKnowledgeGovernanceStore;
273463
+ var ident19, TABLES, TABLE_BY_KIND, PostgresKnowledgeGovernanceStore;
273118
273464
  var init_postgres_knowledge = __esm({
273119
273465
  "../storage/src/postgres-knowledge.ts"() {
273120
273466
  "use strict";
@@ -273122,7 +273468,7 @@ var init_postgres_knowledge = __esm({
273122
273468
  init_pg_ident();
273123
273469
  init_pg_sanitize();
273124
273470
  init_knowledge_snapshot_crypto();
273125
- ident18 = (value2) => {
273471
+ ident19 = (value2) => {
273126
273472
  quoteIdent2(value2);
273127
273473
  return value2;
273128
273474
  };
@@ -273280,11 +273626,11 @@ var init_postgres_knowledge = __esm({
273280
273626
  constructor(pool, schema, snapshotKey2) {
273281
273627
  this.pool = pool;
273282
273628
  this.snapshotKey = snapshotKey2;
273283
- this.s = `"${ident18(schema)}"`;
273629
+ this.s = `"${ident19(schema)}"`;
273284
273630
  }
273285
273631
  s;
273286
273632
  static async open(pool, schema = "public", snapshotKey2) {
273287
- const safe = ident18(schema);
273633
+ const safe = ident19(schema);
273288
273634
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${safe}"`);
273289
273635
  for (const spec of TABLES) {
273290
273636
  await pool.query(`
@@ -273958,7 +274304,7 @@ function reportedCost(v2) {
273958
274304
  }
273959
274305
  function newUsageBackfillManifestId(now = /* @__PURE__ */ new Date()) {
273960
274306
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
273961
- return `ulb-${stamp}-${(0, import_node_crypto82.randomBytes)(3).toString("hex")}`;
274307
+ return `ulb-${stamp}-${(0, import_node_crypto83.randomBytes)(3).toString("hex")}`;
273962
274308
  }
273963
274309
  async function backfillUsageLedger(ledger, opts) {
273964
274310
  if (!opts.dryRun && !opts.manifestId) throw new Error("execute \u9700\u8981 manifestId");
@@ -274392,11 +274738,11 @@ async function audit(c, s2, manifestId, runId, field, before, after, createdAt)
274392
274738
  [manifestId, runId, field, before == null ? null : JSON.stringify(before), after == null ? null : JSON.stringify(after), createdAt]
274393
274739
  );
274394
274740
  }
274395
- var import_node_crypto82, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, emptyStep, fourOf, sameUsage, ChatRunLookup;
274741
+ var import_node_crypto83, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, emptyStep, fourOf, sameUsage, ChatRunLookup;
274396
274742
  var init_usage_ledger_backfill = __esm({
274397
274743
  "../storage/src/usage-ledger-backfill.ts"() {
274398
274744
  "use strict";
274399
- import_node_crypto82 = require("node:crypto");
274745
+ import_node_crypto83 = require("node:crypto");
274400
274746
  init_src();
274401
274747
  init_pg_ident();
274402
274748
  init_postgres_trace();
@@ -274528,9 +274874,11 @@ __export(src_exports, {
274528
274874
  PostgresChatItemStore: () => PostgresChatItemStore,
274529
274875
  PostgresChatSessionStore: () => PostgresChatSessionStore,
274530
274876
  PostgresChatTurnStore: () => PostgresChatTurnStore,
274877
+ PostgresConnectorAuthSnapshotStore: () => PostgresConnectorAuthSnapshotStore,
274531
274878
  PostgresControlPlaneStore: () => PostgresControlPlaneStore,
274532
274879
  PostgresDelegationStore: () => PostgresDelegationStore,
274533
274880
  PostgresDispatchStore: () => PostgresDispatchStore,
274881
+ PostgresGithubAppPendingStore: () => PostgresGithubAppPendingStore,
274534
274882
  PostgresHumanPrefsStore: () => PostgresHumanPrefsStore,
274535
274883
  PostgresKnowledgeGovernanceStore: () => PostgresKnowledgeGovernanceStore,
274536
274884
  PostgresModelPriceStore: () => PostgresModelPriceStore,
@@ -274664,6 +275012,7 @@ var init_src10 = __esm({
274664
275012
  init_postgres_type_registry();
274665
275013
  init_postgres_actor_memory();
274666
275014
  init_postgres_inbox_read();
275015
+ init_postgres_connector_auth_pending();
274667
275016
  init_postgres_dispatches();
274668
275017
  init_tenancy_inventory();
274669
275018
  init_tenancy_move();
@@ -275893,6 +276242,7 @@ __export(src_exports2, {
275893
276242
  MirrorEngine: () => MirrorEngine,
275894
276243
  MissingCompanyContextError: () => MissingCompanyContextError,
275895
276244
  NODE_HEARTBEAT_STALE_MS: () => NODE_HEARTBEAT_STALE_MS,
276245
+ NOTIFICATIONS_CHANGED: () => NOTIFICATIONS_CHANGED,
275896
276246
  NPM_PROBE_TIMEOUT_MS: () => NPM_PROBE_TIMEOUT_MS,
275897
276247
  NdjsonOplogStore: () => NdjsonOplogStore,
275898
276248
  NodeHealthTracker: () => NodeHealthTracker,
@@ -276210,6 +276560,7 @@ __export(src_exports2, {
276210
276560
  openChatTurn: () => openChatTurn,
276211
276561
  openKnowledgeSnapshot: () => openKnowledgeSnapshot,
276212
276562
  openNewEngine: () => openNewEngine,
276563
+ operatorCrossCompanyRead: () => operatorCrossCompanyRead,
276213
276564
  outputsPartProvider: () => outputsPartProvider,
276214
276565
  packMatches: () => packMatches,
276215
276566
  parentConversationTaskLines: () => parentConversationTaskLines,
@@ -276811,14 +277162,14 @@ init_src();
276811
277162
  var os15 = __toESM(require("node:os"));
276812
277163
  var path45 = __toESM(require("node:path"));
276813
277164
  var import_node_child_process26 = require("node:child_process");
276814
- var import_node_crypto93 = require("node:crypto");
277165
+ var import_node_crypto94 = require("node:crypto");
276815
277166
 
276816
277167
  // ../cli/src/cli.ts
276817
277168
  init_src();
276818
277169
  var fs43 = __toESM(require("node:fs"), 1);
276819
277170
  var os12 = __toESM(require("node:os"), 1);
276820
277171
  var path38 = __toESM(require("node:path"), 1);
276821
- var import_node_crypto91 = require("node:crypto");
277172
+ var import_node_crypto92 = require("node:crypto");
276822
277173
 
276823
277174
  // ../cli/src/serve.ts
276824
277175
  init_src11();
@@ -276830,11 +277181,11 @@ var fs35 = __toESM(require("node:fs"), 1);
276830
277181
  var os8 = __toESM(require("node:os"), 1);
276831
277182
  var path29 = __toESM(require("node:path"), 1);
276832
277183
  var import_node_child_process18 = require("node:child_process");
276833
- var import_node_crypto87 = require("node:crypto");
277184
+ var import_node_crypto88 = require("node:crypto");
276834
277185
  var import_node_url7 = require("node:url");
276835
277186
 
276836
277187
  // ../cli/src/artifact-discovery-source.ts
276837
- var import_node_crypto83 = require("node:crypto");
277188
+ var import_node_crypto84 = require("node:crypto");
276838
277189
  init_src7();
276839
277190
  var ArtifactDiscoverySourceUnreadableError = class extends Error {
276840
277191
  constructor(message) {
@@ -276864,7 +277215,7 @@ async function resolveArtifactDiscoverySourceContent(blobs, input) {
276864
277215
  throw new ArtifactDiscoverySourceUnreadableError(`Artifact Revision ${input.revisionId} \u7684 contentKind \u4E0D\u53D7\u652F\u6301\uFF1A${String(input.contentKind)}`);
276865
277216
  }
276866
277217
  const rootBytes = await blobs.get(input.contentRef);
276867
- const sourceFingerprint = (0, import_node_crypto83.createHash)("sha256").update(rootBytes).digest("hex");
277218
+ const sourceFingerprint = (0, import_node_crypto84.createHash)("sha256").update(rootBytes).digest("hex");
276868
277219
  if (input.contentKind === "inline-blob") {
276869
277220
  const sourceContent = decodeText3(rootBytes);
276870
277221
  if (sourceContent === null) {
@@ -276888,7 +277239,7 @@ ${entry.text}`).join("\n\n"),
276888
277239
  }
276889
277240
 
276890
277241
  // ../server/src/domains/discussions/service.ts
276891
- var import_node_crypto84 = require("node:crypto");
277242
+ var import_node_crypto85 = require("node:crypto");
276892
277243
  init_zod();
276893
277244
  init_discussions();
276894
277245
  init_router();
@@ -276950,7 +277301,7 @@ function remapTopics(topics, removed) {
276950
277301
  return [...out].map(([topic_id, summary]) => ({ topic_id, summary }));
276951
277302
  }
276952
277303
  var entryId = (projectId2, id) => JSON.stringify([projectId2, id]);
276953
- var hash2 = (s2) => (0, import_node_crypto84.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
277304
+ var hash2 = (s2) => (0, import_node_crypto85.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
276954
277305
  var canonical = (s2) => s2.normalize("NFKC").trim().toLocaleLowerCase();
276955
277306
  function decisionFingerprint(d) {
276956
277307
  return hash2(JSON.stringify([d.statement, d.rationale, d.alternatives]));
@@ -277170,7 +277521,7 @@ var DiscussionService = class {
277170
277521
  throw new ApiError(409, "DISCUSSION_CONFLICT", "\u9879\u76EE\u8BAE\u9898\u6216\u51B3\u7B56\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u4FEE\u6B63");
277171
277522
  }
277172
277523
  }
277173
- const operation = (value2) => tx.put("operation", (0, import_node_crypto84.randomUUID)(), {
277524
+ const operation = (value2) => tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
277174
277525
  ...value2,
277175
277526
  projectId: projectId2,
277176
277527
  actorId: ctx.actorId,
@@ -277187,7 +277538,7 @@ var DiscussionService = class {
277187
277538
  if (id.startsWith("new:")) {
277188
277539
  if (BUG_TOPIC.test(t.title.trim())) invalid("topics.title", `\u300C${t.title}\u300D\u662F\u5355\u4E2A Bug\uFF1A\u4FEE\u590D\u60C5\u51B5\u5199\u8FDB\u8BA8\u8BBA\u8BB0\u5F55\uFF0C\u5B9A\u51FA\u7684\u89C4\u5219\u6302\u5230\u5BF9\u5E94\u529F\u80FD\u8BAE\u9898\u4E0B\uFF0C\u4E0D\u6309 Bug \u53F7\u5EFA\u8BAE\u9898`);
277189
277540
  const existing = [...topicMap.values()].find((x2) => canonical(x2.title) === canonical(t.title));
277190
- id = existing?.topicId ?? `topic_${(0, import_node_crypto84.randomUUID)()}`;
277541
+ id = existing?.topicId ?? `topic_${(0, import_node_crypto85.randomUUID)()}`;
277191
277542
  if (!existing) createdTopics.add(id);
277192
277543
  } else if (!topicMap.has(id)) invalid("topics.topic_id", `\u8BAE\u9898 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u8BAE\u9898\u4F7F\u7528 new: \u524D\u7F00`);
277193
277544
  topicAliases.set(t.topic_id, id);
@@ -277239,7 +277590,7 @@ var DiscussionService = class {
277239
277590
  if (id.startsWith("new:")) {
277240
277591
  const candidateKey = JSON.stringify([topicId, canonical(d.record.statement)]);
277241
277592
  const existing = [...decisionMap.values()].find((x2) => x2.topicId === topicId && canonical(x2.record.statement) === canonical(d.record.statement));
277242
- id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto84.randomUUID)()}`;
277593
+ id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto85.randomUUID)()}`;
277243
277594
  candidateIds.set(candidateKey, id);
277244
277595
  } else if (!decisionMap.has(id)) invalid("decision_id", `\u51B3\u7B56 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u51B3\u7B56\u4F7F\u7528 new: \u524D\u7F00`);
277245
277596
  decisionAliases.set(d.decision_id, id);
@@ -277501,7 +277852,7 @@ var DiscussionService = class {
277501
277852
  ...row.value.confirmation ? { confirmation: { ...row.value.confirmation, fingerprint: decisionFingerprint(record9) } } : {}
277502
277853
  };
277503
277854
  await tx.put("decision", row.id, value2);
277504
- await tx.put("operation", (0, import_node_crypto84.randomUUID)(), {
277855
+ await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
277505
277856
  action: "edit",
277506
277857
  kind: "decision",
277507
277858
  projectId: projectId2,
@@ -277565,7 +277916,7 @@ var DiscussionService = class {
277565
277916
  if (!current || current.revision !== row.revision) throw new ApiError(409, "DISCUSSION_CONFLICT", `\u8BA8\u8BBA ${row.id} \u5DF2\u88AB\u4FEE\u6539\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u91CD\u8BD5`);
277566
277917
  const summary = given.get(row.id);
277567
277918
  await tx.put("discussion", row.id, { ...current.value, record: { ...current.value.record, summary } });
277568
- await tx.put("operation", (0, import_node_crypto84.randomUUID)(), {
277919
+ await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
277569
277920
  action: "edit",
277570
277921
  kind: "discussion",
277571
277922
  projectId: current.value.record.project_id,
@@ -277581,7 +277932,7 @@ var DiscussionService = class {
277581
277932
  });
277582
277933
  }
277583
277934
  async operation(tx, actorId, kind, id, from, to) {
277584
- await tx.put("operation", (0, import_node_crypto84.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
277935
+ await tx.put("operation", (0, import_node_crypto85.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
277585
277936
  }
277586
277937
  };
277587
277938
  function pickDiscussionContent(d) {
@@ -277881,7 +278232,7 @@ var DiscussionContext = class {
277881
278232
  };
277882
278233
 
277883
278234
  // ../server/src/domains/discussions/worker.ts
277884
- var import_node_crypto85 = require("node:crypto");
278235
+ var import_node_crypto86 = require("node:crypto");
277885
278236
  var DISCUSSION_IDLE_MS = 15 * 6e4;
277886
278237
  var DISCUSSION_MAX_CORRECTIONS = 3;
277887
278238
  var DISCUSSION_LEASE_MS = 2 * 6e4;
@@ -277907,7 +278258,7 @@ var DiscussionWorker = class {
277907
278258
  if (await this.isReplying(sessionId)) return false;
277908
278259
  const cutoff = eligibleCutoff(await this.service.chat.listMessages(sessionId), this.service.now());
277909
278260
  if (!cutoff) return false;
277910
- const owner = (0, import_node_crypto85.randomUUID)();
278261
+ const owner = (0, import_node_crypto86.randomUUID)();
277911
278262
  const claimed = await this.service.store.transaction(async (tx) => {
277912
278263
  const old = (await tx.get("job", sessionId))?.value;
277913
278264
  if (old?.status === "running" && (old.leaseUntil ?? 0) > this.service.now()) return null;
@@ -278629,7 +278980,7 @@ function createDiscussionRuntime(options) {
278629
278980
  }
278630
278981
 
278631
278982
  // ../server/src/domains/discussions/store.ts
278632
- var import_node_crypto86 = require("node:crypto");
278983
+ var import_node_crypto87 = require("node:crypto");
278633
278984
  var import_promises17 = require("node:fs/promises");
278634
278985
  var import_node_path27 = require("node:path");
278635
278986
  var key = (kind, id) => JSON.stringify([kind, id]);
@@ -278674,7 +279025,7 @@ var MemoryDiscussionStore = class _MemoryDiscussionStore {
278674
279025
  if (changed) {
278675
279026
  if (this.filename) {
278676
279027
  await (0, import_promises17.mkdir)((0, import_node_path27.dirname)(this.filename), { recursive: true });
278677
- const temp = `${this.filename}.${(0, import_node_crypto86.randomUUID)()}.tmp`;
279028
+ const temp = `${this.filename}.${(0, import_node_crypto87.randomUUID)()}.tmp`;
278678
279029
  await (0, import_promises17.writeFile)(temp, JSON.stringify(next), "utf8");
278679
279030
  await (0, import_promises17.rename)(temp, this.filename);
278680
279031
  }
@@ -279623,7 +279974,7 @@ function oasisWrapperScript() {
279623
279974
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
279624
279975
  const tsx = path29.join(repoRoot, "node_modules/.bin/tsx");
279625
279976
  const main = path29.join(repoRoot, "packages/cli/src/main.ts");
279626
- const key2 = (0, import_node_crypto87.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
279977
+ const key2 = (0, import_node_crypto88.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
279627
279978
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
279628
279979
  const stable = path29.join(os8.tmpdir(), `oasis-cli-wrapper-${uid}-${key2}`);
279629
279980
  let dir;
@@ -280016,7 +280367,7 @@ async function startServe(opts) {
280016
280367
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
280017
280368
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
280018
280369
  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" });
280019
- const requestId = (0, import_node_crypto87.randomUUID)();
280370
+ const requestId = (0, import_node_crypto88.randomUUID)();
280020
280371
  const task = new Promise((resolve12) => {
280021
280372
  const timer = setTimeout(() => {
280022
280373
  modelRefreshWaiters.delete(requestId);
@@ -280817,7 +281168,7 @@ async function startServe(opts) {
280817
281168
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
280818
281169
  if (buf || session.runId) {
280819
281170
  await turnStore.appendMessage({
280820
- id: (0, import_node_crypto87.randomUUID)(),
281171
+ id: (0, import_node_crypto88.randomUUID)(),
280821
281172
  sessionId: chatSessionId,
280822
281173
  role: "assistant",
280823
281174
  content: buf,
@@ -280863,7 +281214,7 @@ async function startServe(opts) {
280863
281214
  chatTargets: {
280864
281215
  inspect: inspectChatTarget,
280865
281216
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
280866
- const sessionId = (0, import_node_crypto87.randomUUID)();
281217
+ const sessionId = (0, import_node_crypto88.randomUUID)();
280867
281218
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
280868
281219
  const binding = await registryStore.getBinding(agentId).catch(() => null);
280869
281220
  await (await chatStoreFor(companyId)).createSession({
@@ -281056,7 +281407,8 @@ async function startServe(opts) {
281056
281407
  })();
281057
281408
  }
281058
281409
  await syncRegistryRolesToKernel(registryStore, defaultCompanyKernel);
281059
- const resolveCompanyContext = async (actor, headers, pathname) => {
281410
+ const operatorScopesOf = (authorization) => authorization.startsWith("Bearer ") ? issuer.resolveClaims(authorization.slice("Bearer ".length))?.scopes : void 0;
281411
+ const resolveCompanyContext = async (actor, headers, pathname, method) => {
281060
281412
  if (isCompanyCrossTenantPath(pathname)) {
281061
281413
  return { kind: "exempt" };
281062
281414
  }
@@ -281079,7 +281431,11 @@ async function startServe(opts) {
281079
281431
  }
281080
281432
  const ctx = await companies.service.resolveCurrentCompany(me.accountId, selector);
281081
281433
  if (!ctx) return { kind: "not-found", ...selector !== void 0 ? { selector } : {} };
281082
- return ctx.isMember ? { kind: "ok", companyId: ctx.company.id } : { kind: "forbidden" };
281434
+ if (ctx.isMember) return { kind: "ok", companyId: ctx.company.id };
281435
+ if (operatorCrossCompanyRead({ scopes: operatorScopesOf(authz) }, method)) {
281436
+ return { kind: "ok", companyId: ctx.company.id };
281437
+ }
281438
+ return { kind: "forbidden" };
281083
281439
  };
281084
281440
  const resolveActor = (token) => issuer.resolve(token) ?? (opts.allowSimpleTokens && token.startsWith("token:") ? token.slice("token:".length) : null);
281085
281441
  const AGENT_PRINCIPAL_CACHE_MAX = 4096;
@@ -281625,9 +281981,9 @@ async function startServe(opts) {
281625
281981
  return engine2;
281626
281982
  };
281627
281983
  const liveKernels = () => engineRouter.liveEngines().map((e) => e.kernel);
281628
- const countRoleHolders = async (role) => (await roleHolders()).filter((a) => a.status !== "disabled" && a.roles?.includes(role)).length;
281629
- const roleHolders = async () => {
281630
- const employees = await registryStore.listActors();
281984
+ const countRoleHolders = async (role, companyId) => (await roleHolders(companyId)).filter((a) => a.status !== "disabled" && a.roles?.includes(role)).length;
281985
+ const roleHolders = async (companyId) => {
281986
+ const employees = await (await actors.resolveCtx(companyId ?? defaultCompanyId)).service.listActors();
281631
281987
  const ids2 = new Set(employees.map((a) => a.id));
281632
281988
  const systemActors = kernelRegistry.filter((e) => !ids2.has(e.actor)).map((e) => ({ id: e.actor, roles: e.roles, status: "active" }));
281633
281989
  return [...employees, ...systemActors];
@@ -282163,6 +282519,11 @@ async function startServe(opts) {
282163
282519
  // CO-301 阶段1:请求热路径按当前公司经路由层取引擎(恒等到默认公司;per-company 懒建是后续阶段)。
282164
282520
  resolveEngine: (companyId) => getEngine(companyId ?? defaultCompanyId),
282165
282521
  actors,
282522
+ // Bug 0246:GitHub App 待回调登记与重连快照落共享 schema——服务器重启 / 换副本都读得到。
282523
+ connectorAuthPending: {
282524
+ githubApp: await PostgresGithubAppPendingStore.open(pgPool, sharedSchema),
282525
+ snapshots: await PostgresConnectorAuthSnapshotStore.open(pgPool, sharedSchema)
282526
+ },
282166
282527
  port: opts.port ?? 7320,
282167
282528
  projectState: projectStateStore,
282168
282529
  artifactState: artifactStateStore,
@@ -282213,7 +282574,7 @@ async function startServe(opts) {
282213
282574
  }));
282214
282575
  }
282215
282576
  const limits = { wallClockMs: wallClockForKind("planner") };
282216
- const artifactId = `artifact:planner:${(0, import_node_crypto87.randomUUID)()}`;
282577
+ const artifactId = `artifact:planner:${(0, import_node_crypto88.randomUUID)()}`;
282217
282578
  const handle = await chatRemoteAdapter.spawn({
282218
282579
  actor: planner.id,
282219
282580
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -282302,7 +282663,7 @@ async function startServe(opts) {
282302
282663
  await rebuildLiveSchema();
282303
282664
  for (const d of schema) engine2.kernel.setType(d);
282304
282665
  },
282305
- countRoleHolders,
282666
+ (role) => countRoleHolders(role, cid),
282306
282667
  registryAudit
282307
282668
  );
282308
282669
  }
@@ -282441,9 +282802,9 @@ async function startServe(opts) {
282441
282802
  // store(`public.actor_bindings`),org-registry cutover 之后它是冻结旧镜像——只留给
282442
282803
  // 本域其它老读口,工作区定位一律不许再用它。
282443
282804
  resolveBinding: resolveCompanyBinding,
282444
- /* ADR 0613:建会话 / 「恢复为员工默认运行配置」这两下要读员工详情页配的型号,按**当前公司**读
282805
+ /* ADR 0613:建会话那一下要读员工详情页配的型号,按**当前公司**读
282445
282806
  (与 `resolveBinding` 同一条理由:全局 `registry` 对已切 `actor-config` 的公司是冻结旧镜像)。
282446
- ⚠ 只有这两下读它;解析每一轮的模型依旧不读员工配置(ADR 0610 D1)。 */
282807
+ ⚠ 只有这一下读它;解析每一轮的模型依旧不读员工配置(ADR 0610 D1)。 */
282447
282808
  resolveActorModel: resolveAgentConfiguredModel,
282448
282809
  // 按 runId 路由:切换后新会话的事件落在本公司库里,共享 store 会一条都补不出 parts。
282449
282810
  // ⚠ `runRoutedTrace` 声明在本行**之后**:包成箭头函数避 TDZ(本域只需要 listEvents)。
@@ -282531,6 +282892,8 @@ async function startServe(opts) {
282531
282892
  // live 注册表算 `frameCursor`。与 server.ts 的双写解析同一层,与 attach 路径共享 registry。
282532
282893
  resolveChatItems: (companyId) => chatItemsFor(companyId),
282533
282894
  liveChat,
282895
+ // ADR 0615 B3:快照读口给 serve 重启留下的孤儿插话落结论时问节点——与 `/append` 同一条网关。
282896
+ confirmInputReceipt: (ref2) => chatRemoteAdapter ? chatRemoteAdapter.confirmInputReceipt(ref2.nodeId, ref2.dispatchId, ref2.runtimeKey) : Promise.resolve({ status: "unknown", reason: "node-gateway-not-ready" }),
282534
282897
  // ADR-0587:列表行派生 `unread`,与 collab 域收件箱读同一份按公司的水位(无 PG 时闭包自己回落单实例)。
282535
282898
  resolveReadMarkers: readMarkerStoreFor,
282536
282899
  // ADR 0610 D4:`GET /api/chat-sessions/:id/usage` 的取数口——**与 collab 域是同一个实例**,
@@ -283134,8 +283497,6 @@ async function startServe(opts) {
283134
283497
  discussSessions: new FileSideMap(path29.join(opts.dir, "discuss-sessions.json")),
283135
283498
  escalateDiscussRetain: new FileSideMap(path29.join(opts.dir, "escalate-discuss-retain.json")),
283136
283499
  // 切片 2b:代署 escalate 发起会话保留(落盘)
283137
- // Bug 0107:插入结果读口结算孤儿插入行(serve 在途中重启)时问节点——走 chat 那条网关。
283138
- confirmInputReceipt: (ref2) => chatRemoteAdapter ? chatRemoteAdapter.confirmInputReceipt(ref2.nodeId, ref2.dispatchId, ref2.runtimeKey) : Promise.resolve({ status: "unknown", reason: "node-gateway-not-ready" }),
283139
283500
  dispatchChat: automationDispatchChat = async (chatReq) => {
283140
283501
  const dispatchChatImpl = async ({ actorId, message, dispatchId: requestDispatchId, admitDispatch, executionProfile, knowledgeAttemptKind, requiredConnectorSlugs, env: requestEnv, sessionId, chatSessionId, chatTurnId, attachments, workspace, companyId, model: sessionModelOverride, parentConversationSnapshot, extraSystemPrompt, delegatedChildTurn, workspaceBundle }) => {
283141
283502
  const isolatedKnowledgeTurn = executionProfile === "knowledge-single-turn";
@@ -283158,10 +283519,10 @@ async function startServe(opts) {
283158
283519
  if (isDeletedActor(dispatchTarget)) {
283159
283520
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
283160
283521
  }
283161
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto87.randomUUID)());
283522
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto88.randomUUID)());
283162
283523
  const resumeRuntimeSession = Boolean(sessionId);
283163
- const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto87.randomUUID)()}`;
283164
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto87.randomUUID)()}`;
283524
+ const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto88.randomUUID)()}`;
283525
+ const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto88.randomUUID)()}`;
283165
283526
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
283166
283527
  if (stableDispatch && !stableDispatch.shouldDispatch) {
283167
283528
  if (isHumanSubmitDispatchId(requestDispatchId)) {
@@ -284611,7 +284972,7 @@ async function startServe(opts) {
284611
284972
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
284612
284973
  return;
284613
284974
  }
284614
- const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto87.randomUUID)()}`;
284975
+ const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
284615
284976
  try {
284616
284977
  const inserted = await (await dispatchLedgerFor(companyId)).insertOpen({
284617
284978
  id: ledgerId,
@@ -284711,7 +285072,7 @@ async function startServe(opts) {
284711
285072
  const kernelModel = newKernel.model;
284712
285073
  const art = kernelModel.artifacts.get(nodeId);
284713
285074
  const revWid = art?.workspace ?? "";
284714
- const ledgerId = `dispatch:${(0, import_node_crypto87.randomUUID)()}`;
285075
+ const ledgerId = `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
284715
285076
  try {
284716
285077
  await (await dispatchLedgerFor(companyId)).insertOpen({
284717
285078
  id: ledgerId,
@@ -285105,7 +285466,7 @@ async function startServe(opts) {
285105
285466
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
285106
285467
  try {
285107
285468
  const governance = server.knowledgeGovernance;
285108
- const taskFingerprint = (0, import_node_crypto87.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
285469
+ const taskFingerprint = (0, import_node_crypto88.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
285109
285470
  const requestContext = {
285110
285471
  organizationId: companyId,
285111
285472
  actorId,
@@ -285832,7 +286193,9 @@ async function startServe(opts) {
285832
286193
  slaMs: humanMs
285833
286194
  };
285834
286195
  },
285835
- log: (message) => console.warn(message)
286196
+ log: (message) => console.warn(message),
286197
+ // 新进了通知就推给收件人本人(/api/events?topics=notifications):铃铛角标与页内提示不等 30 秒轮询。
286198
+ onNotified: (_companyId, recipients) => auditSink.notifyNotificationsChanged(recipients)
285836
286199
  });
285837
286200
  notificationLedgerWorker = worker;
285838
286201
  const ledgerSubscribed = /* @__PURE__ */ new Set();
@@ -287699,11 +288062,12 @@ async function readClaudeQuota(binary, signal, deps = {}) {
287699
288062
  // ../cli/src/daemon/quota/monitor.ts
287700
288063
  var read2 = (runtime, signal) => runtime.kind === "codex" ? readCodexQuota(runtime.binary ?? "codex", signal) : readClaudeQuota(runtime.binary ?? "claude", signal, { version: runtime.version });
287701
288064
  var RuntimeQuotaMonitor = class {
287702
- constructor(runtimes, send, reader = read2, intervalMs = RUNTIME_QUOTA_POLL_MS) {
288065
+ constructor(runtimes, send, reader = read2, intervalMs = RUNTIME_QUOTA_POLL_MS, minGapMs = RUNTIME_QUOTA_REFRESH_MIN_GAP_MS) {
287703
288066
  this.runtimes = runtimes;
287704
288067
  this.send = send;
287705
288068
  this.reader = reader;
287706
288069
  this.intervalMs = intervalMs;
288070
+ this.minGapMs = minGapMs;
287707
288071
  }
287708
288072
  cache = [];
287709
288073
  collectedAt;
@@ -287733,6 +288097,41 @@ var RuntimeQuotaMonitor = class {
287733
288097
  });
287734
288098
  return this.pending;
287735
288099
  }
288100
+ /**
288101
+ * 人点了页面上的「刷新」(服务端下发 `refresh_quotas`,ADR 0620):**现在就去问上游**。
288102
+ *
288103
+ * 与 {@link refresh} 的唯一区别就是**不看那 5 分钟**——那条节流是背景轮询的节奏,
288104
+ * 对一个正盯着页面等结果的人没有任何意义。换上的是一条窄得多的闸:
288105
+ * {@link RUNTIME_QUOTA_REFRESH_MIN_GAP_MS}(30 秒)。连点被它挡下,且挡下时**如实说**
288106
+ * 「刚在 N 秒前采过」,不假装采了一次(发起人方申的定则:会静默降级的动作必须回报走了哪条路)。
288107
+ *
288108
+ * provider 自己给的 `retryAt` 退避**不在**绕过范围内:那是上游明说的「别来了」,
288109
+ * 谁点按钮都不该越过它。被它挡住的那个 runtime 在 `collect` 里照旧沿用上一次的结果。
288110
+ */
288111
+ forceRefresh() {
288112
+ if (this.stopped) return Promise.resolve({ ok: false, reason: "\u8FD9\u53F0\u8282\u70B9\u6B63\u5728\u505C\u673A\uFF0C\u6CA1\u6709\u91CD\u91C7\u989D\u5EA6" });
288113
+ if (this.collectableKinds().length === 0) {
288114
+ return Promise.resolve({ ok: false, reason: "\u8FD9\u53F0\u673A\u5668\u4E0A\u6CA1\u6709\u4F1A\u4E0A\u62A5\u989D\u5EA6\u7684\u8FD0\u884C\u65F6\uFF08\u53EA\u6709 Claude Code \u548C Codex \u80FD\u67E5\u5230\u8BA2\u9605\u989D\u5EA6\uFF09" });
288115
+ }
288116
+ if (this.pending) return this.pending.then(() => ({ ok: true }));
288117
+ const sinceMs = this.collectedAt === void 0 ? Number.POSITIVE_INFINITY : Date.now() - this.collectedAt;
288118
+ if (sinceMs < this.minGapMs) {
288119
+ return Promise.resolve({
288120
+ ok: false,
288121
+ reason: `\u989D\u5EA6\u521A\u5728 ${Math.max(1, Math.round(sinceMs / 1e3))} \u79D2\u524D\u91C7\u8FC7\uFF0C\u9875\u9762\u4E0A\u5C31\u662F\u6700\u65B0\u6570\u636E\uFF1B${Math.ceil((this.minGapMs - sinceMs) / 1e3)} \u79D2\u540E\u53EF\u4EE5\u518D\u91C7\u4E00\u6B21`
288122
+ });
288123
+ }
288124
+ this.pending = this.collect().finally(() => {
288125
+ this.pending = void 0;
288126
+ });
288127
+ return this.pending.then(() => ({ ok: true }));
288128
+ }
288129
+ /** 这台机器上真的会上报额度的 runtime(白名单硬编码在 collect 里,两处取同一份判据)。 */
288130
+ collectableKinds() {
288131
+ const kinds = [];
288132
+ for (const r of this.runtimes) if (r.kind === "codex" || r.kind === "claude-code") kinds.push(r.kind);
288133
+ return kinds;
288134
+ }
287736
288135
  async collect() {
287737
288136
  const result = [];
287738
288137
  for (const runtime of this.runtimes) {
@@ -287763,7 +288162,7 @@ var RuntimeQuotaMonitor = class {
287763
288162
  init_wrapper();
287764
288163
  init_src();
287765
288164
  var import_node_os11 = require("node:os");
287766
- var import_node_crypto88 = require("node:crypto");
288165
+ var import_node_crypto89 = require("node:crypto");
287767
288166
  init_src8();
287768
288167
  init_src2();
287769
288168
 
@@ -288582,6 +288981,13 @@ var DaemonWsClient = class {
288582
288981
  void this.reportRuntimeModels({ force: true, requestId: msg.requestId });
288583
288982
  break;
288584
288983
  }
288984
+ case "refresh_quotas": {
288985
+ const requestId = msg.requestId;
288986
+ void this.quotaMonitor.forceRefresh().catch((err) => ({ ok: false, reason: `\u91CD\u91C7\u989D\u5EA6\u65F6\u51FA\u9519\uFF1A${err instanceof Error ? err.message : String(err)}` })).then((r) => {
288987
+ this.send({ type: "quota_refresh_result", requestId, ok: r.ok, ...r.reason ? { reason: r.reason } : {} });
288988
+ });
288989
+ break;
288990
+ }
288585
288991
  case "update": {
288586
288992
  this.pendingUpdatePkg = msg.pkg;
288587
288993
  if (!this.onUpdate) {
@@ -288660,7 +289066,7 @@ var DaemonWsClient = class {
288660
289066
  */
288661
289067
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
288662
289068
  if (artifactIds.length === 0) return {};
288663
- const requestId = (0, import_node_crypto88.randomUUID)();
289069
+ const requestId = (0, import_node_crypto89.randomUUID)();
288664
289070
  return new Promise((resolve12, reject) => {
288665
289071
  const timer = setTimeout(() => {
288666
289072
  this.gcPending.delete(requestId);
@@ -288808,6 +289214,9 @@ var DaemonWsClient = class {
288808
289214
  supportsChunkedMessages: true,
288809
289215
  // Bug 0107:本 daemon 认 `input_receipt_query`。恒 true——handler 随包发。
288810
289216
  supportsInputReceiptQuery: true,
289217
+ // ADR 0620:本 daemon 认 `refresh_quotas`(人点刷新时现去问一遍上游额度)。恒 true——
289218
+ // 采集器随包发。服务端据此对老节点当场回「升级后可用」,不必发一帧出去等超时。
289219
+ supportsQuotaRefresh: true,
288811
289220
  ...activeSessions.length > 0 ? { activeSessions } : {},
288812
289221
  // 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
288813
289222
  pendingDispatchIds: [...this.pendingDispatchIds]
@@ -289071,7 +289480,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
289071
289480
  // ../cli/src/daemon/machine-id.ts
289072
289481
  var import_node_child_process23 = require("node:child_process");
289073
289482
  var import_node_fs24 = require("node:fs");
289074
- var import_node_crypto89 = require("node:crypto");
289483
+ var import_node_crypto90 = require("node:crypto");
289075
289484
  var import_node_os14 = require("node:os");
289076
289485
  function linuxMachineId() {
289077
289486
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -289137,7 +289546,7 @@ var defaultSources = {
289137
289546
  function resolveNodeId(sources = {}) {
289138
289547
  const s2 = { ...defaultSources, ...sources };
289139
289548
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
289140
- const digest = (0, import_node_crypto89.createHash)("sha256").update(material).digest("hex").slice(0, 12);
289549
+ const digest = (0, import_node_crypto90.createHash)("sha256").update(material).digest("hex").slice(0, 12);
289141
289550
  return `node-${digest}`;
289142
289551
  }
289143
289552
 
@@ -291009,13 +291418,13 @@ function ownFlagsFromDecl(command2) {
291009
291418
  }
291010
291419
 
291011
291420
  // ../cli/src/connector-effect-runner.ts
291012
- var import_node_crypto90 = require("node:crypto");
291421
+ var import_node_crypto91 = require("node:crypto");
291013
291422
  var import_node_child_process24 = require("node:child_process");
291014
291423
  init_src8();
291015
291424
  function stableKey(effect) {
291016
291425
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
291017
291426
  const canonical2 = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
291018
- return `sha256:${(0, import_node_crypto90.createHash)("sha256").update(canonical2).digest("hex")}`;
291427
+ return `sha256:${(0, import_node_crypto91.createHash)("sha256").update(canonical2).digest("hex")}`;
291019
291428
  }
291020
291429
  async function runConnectorEffect(connector, toolArgs, deps) {
291021
291430
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -291570,7 +291979,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
291570
291979
  const timeoutMs = resolveAuthWaitMs();
291571
291980
  const controller = new AbortController();
291572
291981
  const timer = setTimeout(() => controller.abort(), timeoutMs);
291573
- const waiterId = (0, import_node_crypto91.randomUUID)();
291982
+ const waiterId = (0, import_node_crypto92.randomUUID)();
291574
291983
  process.stderr.write(
291575
291984
  `\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
291576
291985
  `
@@ -291654,7 +292063,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
291654
292063
  }
291655
292064
  }
291656
292065
  }
291657
- var CLI_VERSION = "2.2.77" ? "2.2.77" : "dev";
292066
+ var CLI_VERSION = "2.2.79" ? "2.2.79" : "dev";
291658
292067
  function apiHeaders(token, company) {
291659
292068
  const headers = {
291660
292069
  authorization: `Bearer ${token}`,
@@ -292020,7 +292429,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
292020
292429
  const store = createNodeTokenStore(path38.join(dir, "node-tokens.json"));
292021
292430
  const sub = positional[0];
292022
292431
  if (sub === "issue") {
292023
- const id = flags.get("id") ?? `node-${(0, import_node_crypto91.randomUUID)()}`;
292432
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto92.randomUUID)()}`;
292024
292433
  const token2 = store.issue(id);
292025
292434
  println(token2);
292026
292435
  process.stderr.write(
@@ -293159,7 +293568,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
293159
293568
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
293160
293569
  const goal = flags.get("goal")?.trim();
293161
293570
  const slug6 = flags.get("slug");
293162
- const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto91.randomUUID)());
293571
+ const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto92.randomUUID)());
293163
293572
  const result = await api.request("POST", "/api/projects", {
293164
293573
  ...id ? { id } : {},
293165
293574
  name,
@@ -295424,13 +295833,13 @@ function runtimeAssetSourceRoots(input) {
295424
295833
  }
295425
295834
 
295426
295835
  // src/install.ts
295427
- var import_node_crypto92 = require("node:crypto");
295836
+ var import_node_crypto93 = require("node:crypto");
295428
295837
  var fs49 = __toESM(require("node:fs"));
295429
295838
  var path44 = __toESM(require("node:path"));
295430
295839
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
295431
295840
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
295432
295841
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
295433
- const digest = (0, import_node_crypto92.createHash)("sha256").update(fs49.readFileSync(sourceFile)).digest("hex").slice(0, 8);
295842
+ const digest = (0, import_node_crypto93.createHash)("sha256").update(fs49.readFileSync(sourceFile)).digest("hex").slice(0, 8);
295434
295843
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
295435
295844
  }
295436
295845
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -295553,7 +295962,7 @@ function shimScript() {
295553
295962
  }
295554
295963
 
295555
295964
  // src/index.ts
295556
- var PKG_VERSION = true ? "2.2.77" : "dev";
295965
+ var PKG_VERSION = true ? "2.2.79" : "dev";
295557
295966
  var LOCAL_BIN = localBin();
295558
295967
  var NPM_PREFIX = npmPrefix();
295559
295968
  var INSTANCE = DEFAULT_INSTANCE;
@@ -295861,7 +296270,7 @@ function newInstanceName() {
295861
296270
  const existing = new Set(listInstances());
295862
296271
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
295863
296272
  for (; ; ) {
295864
- const n = `inst-${(0, import_node_crypto93.randomBytes)(3).toString("hex")}`;
296273
+ const n = `inst-${(0, import_node_crypto94.randomBytes)(3).toString("hex")}`;
295865
296274
  if (!existing.has(n)) return n;
295866
296275
  }
295867
296276
  }