oasis_test_v2 2.2.82 → 2.2.84

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 +1196 -458
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1347,6 +1347,21 @@ function actualModelId(model) {
1347
1347
  if (!t) return void 0;
1348
1348
  return isPseudoModelId(t) ? void 0 : t;
1349
1349
  }
1350
+ function modelSourceRank(model, source) {
1351
+ if (!actualModelId(model)) return -1;
1352
+ return source ? MODEL_SOURCE_RANK[source] ?? 1 : 1;
1353
+ }
1354
+ function preferRunModel(existing, incoming, tie = "keep") {
1355
+ const incomingModel = actualModelId(incoming.effectiveModel);
1356
+ if (!incomingModel) return null;
1357
+ const existingRank = modelSourceRank(existing.effectiveModel, existing.modelSource);
1358
+ const incomingRank = modelSourceRank(incomingModel, incoming.modelSource);
1359
+ if (incomingRank > existingRank) return { effectiveModel: incomingModel, modelSource: incoming.modelSource ?? null };
1360
+ if (incomingRank === existingRank && tie === "replace") {
1361
+ return { effectiveModel: incomingModel, modelSource: incoming.modelSource ?? null };
1362
+ }
1363
+ return null;
1364
+ }
1350
1365
  function legacyScopeIsEmpty(scope2) {
1351
1366
  return scope2.artifactIds.length === 0 && scope2.chatSessionIds.length === 0 && (scope2.runtimeInstanceIds?.length ?? 0) === 0;
1352
1367
  }
@@ -1356,11 +1371,20 @@ function legacyScopeMatcher(scope2) {
1356
1371
  const runtimes = new Set(scope2.runtimeInstanceIds ?? []);
1357
1372
  return (run) => run.artifactId != null && artifacts.has(run.artifactId) || run.chatSessionId != null && sessions.has(run.chatSessionId) || run.runtimeInstanceId != null && runtimes.has(run.runtimeInstanceId);
1358
1373
  }
1359
- var PSEUDO_MODEL_IDS, DISPLAY_CNY_PER_USD, USAGE_POINT_FINAL_SEQ;
1374
+ var PSEUDO_MODEL_IDS, MODEL_SOURCE_RANK, DISPLAY_CNY_PER_USD, USAGE_POINT_FINAL_SEQ;
1360
1375
  var init_trace = __esm({
1361
1376
  "../contract/src/trace.ts"() {
1362
1377
  "use strict";
1363
1378
  PSEUDO_MODEL_IDS = ["default", "default[]", "auto"];
1379
+ MODEL_SOURCE_RANK = {
1380
+ runtime: 2,
1381
+ runtime_rollout: 2,
1382
+ requested: 1,
1383
+ session_override: 1,
1384
+ actor_config: 1,
1385
+ runtime_default: 1,
1386
+ unknown: 0
1387
+ };
1364
1388
  DISPLAY_CNY_PER_USD = 7.2;
1365
1389
  USAGE_POINT_FINAL_SEQ = 2147483647;
1366
1390
  }
@@ -8506,14 +8530,46 @@ async function runACPSession(job, cfg) {
8506
8530
  let runtimeReportedModel;
8507
8531
  let requestedModelAccepted;
8508
8532
  const reportedModel = () => pickReportedModel({ runtime: [routedModel, runtimeReportedModel], requested: requestedModelAccepted });
8533
+ let turnIndex = 0;
8534
+ let turnStartedMs = Date.now();
8535
+ let freshSession = true;
8536
+ let routedPollInflight = false;
8509
8537
  const pollRoutedModel = async () => {
8510
- if (routedModel || !cfg.readRoutedModel || !capturedSessionId) return;
8511
- if (actualModelId(runtimeReportedModel)) return;
8538
+ if (!cfg.readRoutedModel || !capturedSessionId || exited) return false;
8539
+ if (actualModelId(runtimeReportedModel)) return true;
8540
+ if (routedPollInflight) return false;
8541
+ routedPollInflight = true;
8542
+ const turn = turnIndex;
8512
8543
  try {
8513
- routedModel = await cfg.readRoutedModel(capturedSessionId);
8544
+ const found2 = actualModelId(await cfg.readRoutedModel(capturedSessionId, {
8545
+ turnStartedMs,
8546
+ freshSession
8547
+ }));
8548
+ if (!found2 || turn !== turnIndex) return false;
8549
+ if (found2 !== routedModel) {
8550
+ routedModel = found2;
8551
+ emitUsageCheckpoint();
8552
+ }
8553
+ return true;
8514
8554
  } catch {
8555
+ return false;
8556
+ } finally {
8557
+ routedPollInflight = false;
8515
8558
  }
8516
- if (routedModel) emitUsageCheckpoint();
8559
+ };
8560
+ const ROUTED_MODEL_POLL_DELAYS_MS = [0, 150, 400, 900, 2e3];
8561
+ let routedSamplingTurn = -1;
8562
+ const startRoutedModelSampling = () => {
8563
+ if (!cfg.readRoutedModel || routedSamplingTurn === turnIndex) return;
8564
+ routedSamplingTurn = turnIndex;
8565
+ const turn = turnIndex;
8566
+ void (async () => {
8567
+ for (const delay of ROUTED_MODEL_POLL_DELAYS_MS) {
8568
+ if (delay > 0) await new Promise((r) => setTimeout(r, delay));
8569
+ if (exited || turn !== turnIndex) return;
8570
+ if (await pollRoutedModel()) return;
8571
+ }
8572
+ })();
8517
8573
  };
8518
8574
  const modelForUsage = () => reportedModel()?.model;
8519
8575
  const usageCheckpointCbs = [];
@@ -8639,11 +8695,15 @@ ${note}` : note };
8639
8695
  },
8640
8696
  (text7) => {
8641
8697
  confirmAppendBatch();
8698
+ startRoutedModelSampling();
8642
8699
  if (cfg.classifyPromptFailure && turnMessage.length < PROMPT_TURN_MESSAGE_MAX) turnMessage += text7;
8643
8700
  for (const cb of outputCbs) cb(text7);
8644
8701
  },
8645
8702
  (e) => {
8646
- if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) confirmAppendBatch();
8703
+ if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) {
8704
+ confirmAppendBatch();
8705
+ startRoutedModelSampling();
8706
+ }
8647
8707
  for (const cb of telemetryCbs) cb(e);
8648
8708
  },
8649
8709
  // runtime 自己用 stopReason=cancelled 收尾时也保留取消语义;此前误记成 error,
@@ -8712,6 +8772,7 @@ ${note}` : note };
8712
8772
  sessionId = job.runtimeSessionId;
8713
8773
  ambiguousResume = true;
8714
8774
  }
8775
+ if (sessionId) freshSession = false;
8715
8776
  } catch {
8716
8777
  sessionId = "";
8717
8778
  }
@@ -8725,6 +8786,7 @@ ${note}` : note };
8725
8786
  sessionId = extractSessionID(sessionRes);
8726
8787
  runtimeReportedModel = extractCurrentModelID(sessionRes) ?? runtimeReportedModel;
8727
8788
  if (!sessionId) throw new Error("session/new returned no sessionId");
8789
+ freshSession = true;
8728
8790
  }
8729
8791
  capturedSessionId = sessionId;
8730
8792
  if (resolvedModel2) {
@@ -8761,6 +8823,8 @@ ${task}` : task;
8761
8823
  const sendPrompt = async (text7) => {
8762
8824
  turnMessage = "";
8763
8825
  turnStderr = [];
8826
+ turnIndex += 1;
8827
+ turnStartedMs = Date.now();
8764
8828
  const p2 = client.request("session/prompt", { sessionId, prompt: [{ type: "text", text: text7 }] });
8765
8829
  inflightStopReason = p2.then((r) => extractStopReason(r), () => "");
8766
8830
  try {
@@ -8780,6 +8844,7 @@ ${task}` : task;
8780
8844
  sessionId = extractSessionID(sessionRes);
8781
8845
  runtimeReportedModel = extractCurrentModelID(sessionRes) ?? runtimeReportedModel;
8782
8846
  if (!sessionId) throw new Error("session/new returned no sessionId");
8847
+ freshSession = true;
8783
8848
  capturedSessionId = sessionId;
8784
8849
  emitUsageCheckpoint();
8785
8850
  promptResult = await sendPrompt(userText);
@@ -9548,14 +9613,59 @@ function pickCursorModelName(value2) {
9548
9613
  };
9549
9614
  return visit(value2, 0) ?? fallback;
9550
9615
  }
9551
- function modelNameInCell(cell) {
9552
- if (typeof cell !== "string" || !cell.includes("modelName")) return void 0;
9616
+ function parseCell(cell) {
9617
+ if (typeof cell !== "string") return void 0;
9553
9618
  try {
9554
- return pickCursorModelName(JSON.parse(cell));
9619
+ return JSON.parse(cell);
9555
9620
  } catch {
9556
9621
  return void 0;
9557
9622
  }
9558
9623
  }
9624
+ function looksLikeTimeKey(key2) {
9625
+ const tokens2 = key2.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
9626
+ const last = tokens2[tokens2.length - 1];
9627
+ if (!last || !TIME_KEY_TAILS.includes(last)) return false;
9628
+ return last !== "at" || tokens2.length > 1;
9629
+ }
9630
+ function cursorTimeValueMs(value2) {
9631
+ if (typeof value2 === "bigint") return cursorTimeValueMs(Number(value2));
9632
+ if (typeof value2 === "number") {
9633
+ if (!Number.isFinite(value2)) return void 0;
9634
+ if (value2 > 1e15) return Math.floor(value2 / 1e3);
9635
+ if (value2 > 1e12) return value2;
9636
+ if (value2 > 1e9) return value2 * 1e3;
9637
+ return void 0;
9638
+ }
9639
+ if (typeof value2 === "string") {
9640
+ const t = value2.trim();
9641
+ if (!t) return void 0;
9642
+ if (/^\d+$/.test(t)) return cursorTimeValueMs(Number(t));
9643
+ const ms = Date.parse(t);
9644
+ return Number.isFinite(ms) ? ms : void 0;
9645
+ }
9646
+ return void 0;
9647
+ }
9648
+ function cursorRowTimeMs(row, parsedCells) {
9649
+ let best;
9650
+ const take = (v2) => {
9651
+ const ms = cursorTimeValueMs(v2);
9652
+ if (ms !== void 0 && (best === void 0 || ms > best)) best = ms;
9653
+ };
9654
+ for (const [key2, value2] of Object.entries(row)) if (looksLikeTimeKey(key2)) take(value2);
9655
+ const visit = (node2, depth) => {
9656
+ if (depth > 8 || node2 === null || typeof node2 !== "object") return;
9657
+ if (Array.isArray(node2)) {
9658
+ for (const item of node2) visit(item, depth + 1);
9659
+ return;
9660
+ }
9661
+ for (const [key2, value2] of Object.entries(node2)) {
9662
+ if (looksLikeTimeKey(key2)) take(value2);
9663
+ visit(value2, depth + 1);
9664
+ }
9665
+ };
9666
+ for (const parsed of parsedCells) visit(parsed, 0);
9667
+ return best;
9668
+ }
9559
9669
  async function openReadOnly(file) {
9560
9670
  try {
9561
9671
  const sqlite = await import("node:sqlite");
@@ -9564,7 +9674,9 @@ async function openReadOnly(file) {
9564
9674
  return null;
9565
9675
  }
9566
9676
  }
9567
- async function readCursorRoutedModel(sessionId, env = process.env) {
9677
+ async function readCursorRoutedModel(sessionId, window3) {
9678
+ const env = window3.env ?? process.env;
9679
+ const earliest = window3.turnStartedMs - TURN_CLOCK_TOLERANCE_MS;
9568
9680
  const file = cursorAcpSessionStorePath(sessionId, env);
9569
9681
  if (!file) return void 0;
9570
9682
  const db = await openReadOnly(file);
@@ -9580,10 +9692,17 @@ async function readCursorRoutedModel(sessionId, env = process.env) {
9580
9692
  continue;
9581
9693
  }
9582
9694
  for (const row of rows) {
9583
- for (const cell of Object.values(row)) {
9584
- const model = modelNameInCell(cell);
9585
- if (model) return model;
9695
+ const cells = Object.values(row);
9696
+ if (!cells.some((cell) => typeof cell === "string" && cell.includes("modelName"))) continue;
9697
+ const parsed = cells.map(parseCell);
9698
+ const model = parsed.map((value2) => value2 === void 0 ? void 0 : pickCursorModelName(value2)).find((name) => !!name);
9699
+ if (!model) continue;
9700
+ const rowMs = cursorRowTimeMs(row, parsed);
9701
+ if (rowMs !== void 0) {
9702
+ if (rowMs >= earliest) return model;
9703
+ continue;
9586
9704
  }
9705
+ if (window3.freshSession) return model;
9587
9706
  }
9588
9707
  }
9589
9708
  return void 0;
@@ -9596,7 +9715,7 @@ async function readCursorRoutedModel(sessionId, env = process.env) {
9596
9715
  }
9597
9716
  }
9598
9717
  }
9599
- var fs10, os6, path12, MAX_TABLES, MAX_ROWS_PER_TABLE;
9718
+ var fs10, os6, path12, MAX_TABLES, MAX_ROWS_PER_TABLE, TURN_CLOCK_TOLERANCE_MS, TIME_KEY_TAILS;
9600
9719
  var init_local_store = __esm({
9601
9720
  "../adapters/src/cursor/local-store.ts"() {
9602
9721
  "use strict";
@@ -9605,6 +9724,8 @@ var init_local_store = __esm({
9605
9724
  path12 = __toESM(require("node:path"), 1);
9606
9725
  MAX_TABLES = 24;
9607
9726
  MAX_ROWS_PER_TABLE = 200;
9727
+ TURN_CLOCK_TOLERANCE_MS = 1e3;
9728
+ TIME_KEY_TAILS = ["at", "ts", "time", "timestamp", "date", "millis", "epoch"];
9608
9729
  }
9609
9730
  });
9610
9731
 
@@ -9652,7 +9773,7 @@ var init_cursor = __esm({
9652
9773
  // 采集点 4:「自动」档下 cursor 对外只报 `default[]`(不是型号、查不到价,v2 上 385 条记的就是它),
9653
9774
  // 而它自己的 `~/.cursor/acp-sessions/<会话号>/store.db` 里逐条回复都带实际路由到的模型。
9654
9775
  // 这是 cursor 的内部格式:读不到就退回会话级自报值,绝不报错(见 local-store.ts)。
9655
- readRoutedModel: (sessionId) => readCursorRoutedModel(sessionId),
9776
+ readRoutedModel: (sessionId, window3) => readCursorRoutedModel(sessionId, window3),
9656
9777
  ...this.opts.syncSkills ? { syncSkills: this.opts.syncSkills } : {}
9657
9778
  });
9658
9779
  }
@@ -35188,7 +35309,7 @@ var require_websocket = __commonJS({
35188
35309
  var http2 = require("http");
35189
35310
  var net = require("net");
35190
35311
  var tls = require("tls");
35191
- var { randomBytes: randomBytes14, createHash: createHash45 } = require("crypto");
35312
+ var { randomBytes: randomBytes15, createHash: createHash45 } = require("crypto");
35192
35313
  var { Duplex, Readable } = require("stream");
35193
35314
  var { URL: URL2 } = require("url");
35194
35315
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -35726,7 +35847,7 @@ var require_websocket = __commonJS({
35726
35847
  }
35727
35848
  }
35728
35849
  const defaultPort = isSecure ? 443 : 80;
35729
- const key2 = randomBytes14(16).toString("base64");
35850
+ const key2 = randomBytes15(16).toString("base64");
35730
35851
  const request2 = isSecure ? https.request : http2.request;
35731
35852
  const protocolSet = /* @__PURE__ */ new Set();
35732
35853
  let perMessageDeflate;
@@ -130137,7 +130258,7 @@ var require_utils3 = __commonJS({
130137
130258
  var nodeCrypto = require("crypto");
130138
130259
  module2.exports = {
130139
130260
  postgresMd5PasswordHash,
130140
- randomBytes: randomBytes14,
130261
+ randomBytes: randomBytes15,
130141
130262
  deriveKey,
130142
130263
  sha256: sha2563,
130143
130264
  hashByName,
@@ -130147,7 +130268,7 @@ var require_utils3 = __commonJS({
130147
130268
  var webCrypto = nodeCrypto.webcrypto || globalThis.crypto;
130148
130269
  var subtleCrypto = webCrypto.subtle;
130149
130270
  var textEncoder = new TextEncoder();
130150
- function randomBytes14(length) {
130271
+ function randomBytes15(length) {
130151
130272
  return webCrypto.getRandomValues(Buffer.alloc(length));
130152
130273
  }
130153
130274
  async function md5(string7) {
@@ -140939,16 +141060,32 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
140939
141060
  //
140940
141061
  // 这些字段不能成为「没做完也能交」的出口:真正未满足交付标准时继续完成,外部阻塞走 oasis gap。
140941
141062
  // 四项本身只传递一份已达标成果的非阻塞残余信息,不参与 conclude 判据。
140942
- ...args.action === "review" ? [] : [
141063
+ //
141064
+ // ★ 2026-09-23(语菲,验收用例 H-01):此前只列参数名、没说挂在哪条命令上,agent 常把它们写到
141065
+ // `oasis conclude` 上——conclude 只收 `--note`,其余参数被静默丢掉(v2 上 143 次执行里写过这四项
141066
+ // 16 次,6 次写在 conclude 上、全丢)。所以点名「是 propose 的参数」并给一条**追加式**示例:
141067
+ // 只示范「在那条命令末尾加上什么」,不给整条 propose——design(交稿目录不是 deliverable/)、
141068
+ // 分批续跑(要带 --checkpoint --continue)各有自己的提交命令,照抄一整条会交错目录或漏参数。
141069
+ // 「没有就连参数名也不写」:只写参数名不跟值会被 parseArgs 拒(CLI 侧已对这四项放宽为空值,
141070
+ // 但未升级的节点包仍会拒),提示词也把这条路堵上。
141071
+ //
141072
+ // 集成会话(integrating || queueTaskDue)不出这段:那一档「怎么提交」明令「绝不要用 oasis propose」,
141073
+ // 而 `oasis integrate` 也不收这四项——再教「这是 propose 的参数」只会把它引去 propose,
141074
+ // 队列从 N 变 N+1、随后 conclude 撞 queue not quiescent(本文件「怎么提交」那段注释记的卡死形状)。
141075
+ ...args.action === "review" || integrating || queueTaskDue ? [] : [
140943
141076
  ``,
140944
141077
  `### \u4EA4\u4ED8\u65F6\u7684\u975E\u963B\u585E\u4FE1\u606F`,
140945
- `\`--reason\` \u5199\u660E\u76F8\u5BF9\u4E0A\u4E00\u7248\u6539\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u3001\u53EF\u80FD\u5F71\u54CD\u4EC0\u4E48\uFF1B\u9996\u6B21\u7248\u672C\u8BF4\u660E\u672C\u7248\u5B8C\u6210\u5185\u5BB9\u3002`,
140946
- `\u53EA\u6709\u5F53\u524D\u7248\u672C\u5DF2\u7ECF\u8FBE\u5230\u4EA4\u4ED8\u6807\u51C6\u65F6\uFF0C\u624D\u53EF\u4EE5\u8BB0\u5F55\uFF1A`,
141078
+ `\u4E0B\u9762\u51E0\u9879\u662F\u4EA4\u4ED8\u547D\u4EE4\uFF08\`oasis propose\`\uFF09\u7684\u53C2\u6570\uFF0C\u76F4\u63A5\u52A0\u5728\u90A3\u6761\u547D\u4EE4\u540E\u9762\uFF0C\u548C \`--reason\` \u5199\u5728\u4E00\u8D77\uFF1B\`oasis conclude\` \u53EA\u6536 \`--note\`\uFF0C\u5199\u5728 conclude \u4E0A\u7684\u5176\u5B83\u53C2\u6570\u4E0D\u4F1A\u88AB\u8BB0\u5F55\u3002`,
141079
+ `\`--reason\` \u5199\u660E\u76F8\u5BF9\u4E0A\u4E00\u7248\u6539\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u3001\u53EF\u80FD\u5F71\u54CD\u4EC0\u4E48\uFF1B\u9996\u6B21\u7248\u672C\u8BF4\u660E\u672C\u7248\u5B8C\u6210\u601D\u8DEF\u548C\u5185\u5BB9\u3002`,
141080
+ `\u53EA\u6709\u5F53\u524D\u7248\u672C\u5DF2\u7ECF\u8FBE\u5230\u4EA4\u4ED8\u6807\u51C6\u65F6\uFF0C\u624D\u53EF\u4EE5\u8BB0\u5F55\uFF08\u6BCF\u9879\u53EF\u4EE5\u5199\u591A\u6761\uFF0C\u7528\u82F1\u6587\u5206\u53F7 \`;\` \u9694\u5F00\uFF1B\u4EE5\u4E0B\u67D0\u4E00\u9879\u5982\u679C\u6CA1\u6709\u5C31\u4E0D\u5199\uFF0C\u540C\u65F6\u8BE5\u53C2\u6570\u4E5F\u4E0D\u5199\uFF09\uFF1A`,
140947
141081
  `- \`--unresolved\`\uFF1A\u4EFB\u52A1\u8303\u56F4\u4E4B\u5916\u7684\u5DF2\u77E5\u95EE\u9898\uFF1B`,
140948
141082
  `- \`--risks\`\uFF1A\u6210\u679C\u53EF\u4EA4\u4ED8\uFF0C\u4F46\u4F7F\u7528\u65F6\u9700\u6CE8\u610F\u7684\u6B8B\u4F59\u98CE\u9669\uFF1B`,
140949
141083
  `- \`--gaps\`\uFF1A\u4E0D\u5C5E\u4E8E\u672C\u6B21\u5F3A\u5236\u9A8C\u6536\u6761\u4EF6\u7684\u9A8C\u8BC1\u8303\u56F4\u9650\u5236\uFF1B`,
140950
141084
  `- \`--next\`\uFF1A\u4E0B\u6E38\u5982\u4F55\u4F7F\u7528\u6210\u679C\uFF0C\u4E0D\u662F\u4F60\u5269\u4F59\u6CA1\u505A\u7684\u5DE5\u4F5C\u3002`,
140951
- `\u672A\u8FBE\u5230\u4EA4\u4ED8\u6807\u51C6\u7684\u4E8B\u9879\u4E0D\u80FD\u653E\u5728\u8FD9\u91CC\uFF0C\u5E94\u7EE7\u7EED\u5B8C\u6210\u6216\u8FD0\u884C \`oasis gap\`\u3002`,
141085
+ `\u4F8B\u5982\u5728\u90A3\u6761\u547D\u4EE4\u672B\u5C3E\u52A0\u4E0A\uFF1A\`--risks "<\u98CE\u9669\u4E00>; <\u98CE\u9669\u4E8C>" --next "<\u4E0B\u6E38\u600E\u4E48\u7528>"\``,
141086
+ `\u672A\u8FBE\u5230\u4EA4\u4ED8\u6807\u51C6\u7684\u4E8B\u9879\u4E0D\u80FD\u653E\u5728\u8FD9\u91CC\uFF0C\u5E94\u7EE7\u7EED\u5B8C\u6210\u6216\u8FD0\u884C \`oasis gap\`\u3002`
141087
+ ],
141088
+ ...args.action === "review" ? [] : [
140952
141089
  ``,
140953
141090
  `### \u4F1A\u8BDD\u53EF\u80FD\u88AB\u780D\uFF1A\u4E2D\u9014\u7559\u4E2A\u65AD\u70B9`,
140954
141091
  // ★ 2026-08-18:`continuity-checkpoint` 此前在任务书里出现 **0 次**——说明书只在
@@ -208705,11 +208842,17 @@ var init_app_adopt = __esm({
208705
208842
  });
208706
208843
 
208707
208844
  // ../connectors/src/github/app-manifest.ts
208845
+ function randomAppNameSuffix() {
208846
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
208847
+ return Array.from((0, import_node_crypto42.randomBytes)(4), (b2) => alphabet[b2 % alphabet.length]).join("");
208848
+ }
208708
208849
  function buildAppManifest(args) {
208709
208850
  const base = args.baseUrl.replace(/\/+$/, "");
208851
+ const suffix = args.nameSuffix ?? randomAppNameSuffix();
208852
+ const room = GITHUB_APP_NAME_MAX - args.namePrefix.length - suffix.length - 2;
208853
+ const middle = args.employeeSlug.slice(0, Math.max(0, room)).replace(/-+$/, "");
208710
208854
  return {
208711
- name: `${args.namePrefix}-${args.employeeSlug}`.slice(0, 34),
208712
- // GitHub App 名上限 34 字符
208855
+ name: [args.namePrefix, middle, suffix].filter(Boolean).join("-"),
208713
208856
  url: base,
208714
208857
  redirect_url: `${base}${args.redirectPath}`,
208715
208858
  public: false,
@@ -208787,10 +208930,11 @@ function checkAppOwnership(conv, want) {
208787
208930
  }
208788
208931
  return null;
208789
208932
  }
208790
- var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208933
+ var import_node_crypto42, GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, GITHUB_APP_NAME_MAX, PENDING_TTL_MS, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208791
208934
  var init_app_manifest = __esm({
208792
208935
  "../connectors/src/github/app-manifest.ts"() {
208793
208936
  "use strict";
208937
+ import_node_crypto42 = require("node:crypto");
208794
208938
  GITHUB_APP_DEFAULT_PERMISSIONS = {
208795
208939
  contents: "write",
208796
208940
  // clone / push
@@ -208819,6 +208963,7 @@ var init_app_manifest = __esm({
208819
208963
  { key: "workflows", label: "\u5DE5\u4F5C\u6D41\u6587\u4EF6\uFF08\u8BFB\u5199\uFF09", desc: "\u4FEE\u6539 .github/workflows \u4E0B\u7684 CI \u914D\u7F6E" },
208820
208964
  { key: "issues", label: "Issue\uFF08\u8BFB\u5199\uFF09", desc: "\u521B\u5EFA\u4E0E\u8BC4\u8BBA Issue" }
208821
208965
  ];
208966
+ GITHUB_APP_NAME_MAX = 34;
208822
208967
  PENDING_TTL_MS = 15 * 60 * 1e3;
208823
208968
  REQUIRED_WRITE_PERMISSIONS = ["contents", "pull_requests"];
208824
208969
  PERMISSION_RANK = { read: 1, write: 2, admin: 3 };
@@ -208835,6 +208980,195 @@ var init_app_manifest = __esm({
208835
208980
  }
208836
208981
  });
208837
208982
 
208983
+ // ../connectors/src/github/app-inventory.ts
208984
+ function permissionVerdict(perms) {
208985
+ const missingRequired = REQUIRED_WRITE.filter((k2) => perms[k2] !== "write" && perms[k2] !== "admin");
208986
+ const excess = excessAppPermissions(perms);
208987
+ return {
208988
+ missingRequired: [...missingRequired],
208989
+ missing: missingAppPermissions(perms),
208990
+ excess,
208991
+ usable: missingRequired.length === 0 && excess.length === 0
208992
+ };
208993
+ }
208994
+ async function listOrgInstalledApps(args, deps = {}) {
208995
+ const d = { ...defaultDeps3, ...deps };
208996
+ let resp;
208997
+ try {
208998
+ resp = await d.fetch(`${GITHUB_API2}/orgs/${encodeURIComponent(args.org)}/installations?per_page=100`, {
208999
+ headers: {
209000
+ authorization: `Bearer ${args.token}`,
209001
+ accept: "application/vnd.github+json",
209002
+ "x-github-api-version": "2022-11-28"
209003
+ }
209004
+ });
209005
+ } catch (e) {
209006
+ return { status: "error", message: String(e) };
209007
+ }
209008
+ if (resp.status === 401 || resp.status === 403) return { status: "needsAuth", httpStatus: resp.status };
209009
+ const body2 = await resp.json().catch(() => ({}));
209010
+ if (!resp.ok) return { status: "error", message: `GitHub \u8FD4\u56DE HTTP ${resp.status} ${String(body2["message"] ?? "")}`.trim() };
209011
+ const list2 = Array.isArray(body2["installations"]) ? body2["installations"] : [];
209012
+ const all2 = list2.flatMap((raw) => {
209013
+ const item = raw;
209014
+ const appId = item["app_id"] === void 0 ? "" : String(item["app_id"]);
209015
+ const installationId = item["id"] === void 0 ? "" : String(item["id"]);
209016
+ if (!appId || !installationId) return [];
209017
+ const rawPerms = item["permissions"];
209018
+ const permissions = {};
209019
+ for (const [k2, v2] of Object.entries(rawPerms ?? {})) if (typeof v2 === "string") permissions[k2] = v2;
209020
+ return [{ appId, slug: String(item["app_slug"] ?? ""), installationId, org: args.org, permissions }];
209021
+ });
209022
+ const owned = await Promise.all(all2.map(async (app) => {
209023
+ if (!app.slug) return app;
209024
+ try {
209025
+ const r = await d.fetch(`${GITHUB_API2}/apps/${encodeURIComponent(app.slug)}`, {
209026
+ headers: {
209027
+ authorization: `Bearer ${args.token}`,
209028
+ accept: "application/vnd.github+json",
209029
+ "x-github-api-version": "2022-11-28"
209030
+ }
209031
+ });
209032
+ if (!r.ok) return app;
209033
+ const meta = await r.json().catch(() => ({}));
209034
+ const login = meta.owner?.login;
209035
+ return login && login.toLowerCase() !== args.org.toLowerCase() ? null : app;
209036
+ } catch {
209037
+ return app;
209038
+ }
209039
+ }));
209040
+ return { status: "ok", apps: owned.filter((a) => a !== null) };
209041
+ }
209042
+ async function uninstallAppInstallation(args, deps = {}) {
209043
+ const d = { ...defaultDeps3, ...deps };
209044
+ const jwt = createAppJwt(args.appId, args.privateKeyPem, d.nowMs());
209045
+ const resp = await d.fetch(`${GITHUB_API2}/app/installations/${encodeURIComponent(args.installationId)}`, {
209046
+ method: "DELETE",
209047
+ headers: {
209048
+ authorization: `Bearer ${jwt}`,
209049
+ accept: "application/vnd.github+json",
209050
+ "x-github-api-version": "2022-11-28"
209051
+ }
209052
+ });
209053
+ if (resp.status === 204 || resp.status === 404) return;
209054
+ const body2 = await resp.json().catch(() => ({}));
209055
+ throw new Error(`github app: uninstall failed HTTP ${resp.status} ${String(body2["message"] ?? "")}`.trim());
209056
+ }
209057
+ function appSettingsUrl(owner, slug6) {
209058
+ const s2 = encodeURIComponent(slug6);
209059
+ return owner.kind === "org" ? `https://github.com/organizations/${encodeURIComponent(owner.login)}/settings/apps/${s2}` : `https://github.com/settings/apps/${s2}`;
209060
+ }
209061
+ function appInstallPageUrl(slug6) {
209062
+ return `https://github.com/apps/${encodeURIComponent(slug6)}/installations/new`;
209063
+ }
209064
+ function appDeleteUrl(owner, slug6) {
209065
+ return `${appSettingsUrl(owner, slug6)}/advanced`;
209066
+ }
209067
+ function ownerMatches(app, owner) {
209068
+ if (owner.kind === "org") {
209069
+ return app.ownerType === "Organization" && app.ownerLogin.toLowerCase() === owner.login.toLowerCase();
209070
+ }
209071
+ if (app.ownerType !== "User") return false;
209072
+ return !owner.login || app.ownerLogin.toLowerCase() === owner.login.toLowerCase();
209073
+ }
209074
+ function buildReuseCandidates(args) {
209075
+ const { owner } = args;
209076
+ const known = new Set(args.oasisApps.map((a2) => a2.appId));
209077
+ const a = args.oasisApps.filter((app) => ownerMatches(app, owner)).map((app) => {
209078
+ const verdict = app.permissions ? permissionVerdict(app.permissions) : null;
209079
+ const selectable = verdict ? verdict.usable : true;
209080
+ const action = !selectable ? "blocked" : app.installationIds.length === 0 ? "install" : "bind";
209081
+ return {
209082
+ kind: "oasis",
209083
+ action,
209084
+ appId: app.appId,
209085
+ slug: app.slug,
209086
+ ...app.installationIds[0] ? { installationId: app.installationIds[0] } : {},
209087
+ usedBy: app.usedBy,
209088
+ isOrgDefault: app.isOrgDefault,
209089
+ permissionsUnknown: !verdict,
209090
+ verdict,
209091
+ selectable,
209092
+ settingsUrl: appSettingsUrl(owner, app.slug),
209093
+ permissionsUrl: `${appSettingsUrl(owner, app.slug)}/permissions`,
209094
+ installUrl: appInstallPageUrl(app.slug)
209095
+ };
209096
+ });
209097
+ const b2 = owner.kind === "org" ? args.installed.filter((i) => !known.has(i.appId) && i.org.toLowerCase() === owner.login.toLowerCase()).map((i) => {
209098
+ const verdict = permissionVerdict(i.permissions);
209099
+ return {
209100
+ kind: "installed",
209101
+ action: verdict.usable ? "import" : "blocked",
209102
+ appId: i.appId,
209103
+ slug: i.slug,
209104
+ installationId: i.installationId,
209105
+ usedBy: [],
209106
+ isOrgDefault: false,
209107
+ permissionsUnknown: false,
209108
+ verdict,
209109
+ selectable: verdict.usable,
209110
+ settingsUrl: appSettingsUrl(owner, i.slug),
209111
+ permissionsUrl: `${appSettingsUrl(owner, i.slug)}/permissions`,
209112
+ installUrl: appInstallPageUrl(i.slug)
209113
+ };
209114
+ }) : [];
209115
+ return [...a, ...b2].sort((x2, y) => Number(y.selectable) - Number(x2.selectable));
209116
+ }
209117
+ function buildAppInventory(args) {
209118
+ const rows = /* @__PURE__ */ new Map();
209119
+ const ownerOf = (login, type) => !login ? null : type === "Organization" ? { kind: "org", login } : { kind: "user", login };
209120
+ for (const app of args.oasisApps) {
209121
+ const inUse = app.isOrgDefault || app.usedBy.length > 0;
209122
+ const owner = ownerOf(app.ownerLogin, app.ownerType);
209123
+ rows.set(app.appId, {
209124
+ appId: app.appId,
209125
+ slug: app.slug,
209126
+ ownerLogin: app.ownerLogin,
209127
+ hasKey: true,
209128
+ usedBy: app.usedBy,
209129
+ isOrgDefault: app.isOrgDefault,
209130
+ inUse,
209131
+ installationIds: [...app.installationIds],
209132
+ canUninstall: !inUse && app.installationIds.length > 0,
209133
+ deleteUrl: owner && app.slug ? appDeleteUrl(owner, app.slug) : ""
209134
+ });
209135
+ }
209136
+ for (const i of args.installed) {
209137
+ const prev = rows.get(i.appId);
209138
+ if (prev) {
209139
+ if (!prev.installationIds.includes(i.installationId)) prev.installationIds.push(i.installationId);
209140
+ prev.canUninstall = !prev.inUse && prev.installationIds.length > 0;
209141
+ if (!prev.ownerLogin) prev.ownerLogin = i.org;
209142
+ if (!prev.deleteUrl && prev.slug) prev.deleteUrl = appDeleteUrl({ kind: "org", login: i.org }, prev.slug);
209143
+ continue;
209144
+ }
209145
+ rows.set(i.appId, {
209146
+ appId: i.appId,
209147
+ slug: i.slug,
209148
+ ownerLogin: i.org,
209149
+ hasKey: false,
209150
+ usedBy: [],
209151
+ isOrgDefault: false,
209152
+ inUse: false,
209153
+ installationIds: [i.installationId],
209154
+ canUninstall: false,
209155
+ deleteUrl: i.slug ? appDeleteUrl({ kind: "org", login: i.org }, i.slug) : ""
209156
+ });
209157
+ }
209158
+ return [...rows.values()].sort((x2, y) => Number(x2.inUse) - Number(y.inUse) || x2.slug.localeCompare(y.slug));
209159
+ }
209160
+ var GITHUB_API2, defaultDeps3, REQUIRED_WRITE;
209161
+ var init_app_inventory = __esm({
209162
+ "../connectors/src/github/app-inventory.ts"() {
209163
+ "use strict";
209164
+ init_app_auth();
209165
+ init_app_manifest();
209166
+ GITHUB_API2 = "https://api.github.com";
209167
+ defaultDeps3 = { fetch: (...a) => globalThis.fetch(...a), nowMs: () => Date.now() };
209168
+ REQUIRED_WRITE = ["contents", "pull_requests"];
209169
+ }
209170
+ });
209171
+
208838
209172
  // ../connectors/src/registry.ts
208839
209173
  function createConnectorBySlug(slug6) {
208840
209174
  switch (slug6) {
@@ -209291,6 +209625,7 @@ var init_src8 = __esm({
209291
209625
  init_app_auth();
209292
209626
  init_app_adopt();
209293
209627
  init_app_manifest();
209628
+ init_app_inventory();
209294
209629
  init_registry2();
209295
209630
  init_provision();
209296
209631
  init_prepare_job();
@@ -209299,6 +209634,118 @@ var init_src8 = __esm({
209299
209634
  }
209300
209635
  });
209301
209636
 
209637
+ // ../server/src/github-app-inventory.ts
209638
+ async function collectOasisGithubApps(svc) {
209639
+ const rows = (await svc.listVariables()).filter((r) => r.scope === "global" || r.scope === "personal");
209640
+ const agents = await svc.listActors({ kind: "agent", status: "active" });
209641
+ const nameOf2 = new Map(agents.map((a) => [a.id, a.name || a.id]));
209642
+ const plain = (key2, actorId) => rows.find((r) => r.key === key2 && r.actorId === actorId && !r.encrypted)?.maskedValue ?? "";
209643
+ const byAppId = /* @__PURE__ */ new Map();
209644
+ for (const r of rows) {
209645
+ if (r.key !== "GITHUB_APP_ID" || r.encrypted || !r.maskedValue) continue;
209646
+ const e = byAppId.get(r.maskedValue) ?? {
209647
+ slug: "",
209648
+ installationId: "",
209649
+ keyActorId: void 0,
209650
+ usedBy: [],
209651
+ isOrgDefault: false,
209652
+ sources: []
209653
+ };
209654
+ e.sources.push(r.actorId);
209655
+ if (!r.actorId) e.isOrgDefault = true;
209656
+ else if (nameOf2.has(r.actorId) && !e.usedBy.includes(r.actorId)) e.usedBy.push(r.actorId);
209657
+ byAppId.set(r.maskedValue, e);
209658
+ }
209659
+ const out = await Promise.all([...byAppId.entries()].map(async ([appId, e]) => {
209660
+ let pem = null;
209661
+ let keyActorId;
209662
+ for (const src of [...e.sources].sort((a, b2) => Number(Boolean(a)) - Number(Boolean(b2)))) {
209663
+ pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", src).catch(() => null);
209664
+ if (pem) {
209665
+ keyActorId = src;
209666
+ break;
209667
+ }
209668
+ }
209669
+ if (!pem) return null;
209670
+ const slug6 = plain("GITHUB_APP_SLUG", keyActorId);
209671
+ const storedInstallationId = plain("GITHUB_APP_INSTALLATION_ID", keyActorId);
209672
+ const [meta, installs] = await Promise.all([
209673
+ fetchAppSelfMetadata({ appId, privateKeyPem: pem }).catch(() => null),
209674
+ listAppInstallations({ appId, privateKeyPem: pem }).catch(() => null)
209675
+ ]);
209676
+ const stored = installs?.find((i) => i.installationId === storedInstallationId) ?? installs?.[0];
209677
+ const permissions = stored && Object.keys(stored.permissions).length > 0 ? stored.permissions : meta?.permissions ?? null;
209678
+ const app = {
209679
+ appId,
209680
+ slug: meta?.slug || slug6,
209681
+ ownerLogin: meta?.ownerLogin ?? "",
209682
+ ownerType: meta?.ownerType ?? "",
209683
+ usedBy: e.usedBy,
209684
+ usedByNames: e.usedBy.map((id) => nameOf2.get(id) ?? id),
209685
+ isOrgDefault: e.isOrgDefault,
209686
+ // 列不出来(私钥失效等)时退回库里记的那一处,卸载时 404 也当成功。
209687
+ installationIds: installs ? installs.map((i) => i.installationId) : storedInstallationId ? [storedInstallationId] : [],
209688
+ permissions,
209689
+ keyActorId
209690
+ };
209691
+ return app;
209692
+ }));
209693
+ return out.filter((a) => a !== null);
209694
+ }
209695
+ async function ghUser(token, path47) {
209696
+ const r = await fetch(`https://api.github.com${path47}`, {
209697
+ headers: {
209698
+ authorization: `Bearer ${token}`,
209699
+ accept: "application/vnd.github+json",
209700
+ "x-github-api-version": "2022-11-28"
209701
+ }
209702
+ });
209703
+ return { status: r.status, body: r.ok ? await r.json().catch(() => null) : null };
209704
+ }
209705
+ async function githubLoginOf(token) {
209706
+ const r = await ghUser(token, "/user").catch(() => null);
209707
+ return r?.body?.login ?? "";
209708
+ }
209709
+ async function listMemberships(token) {
209710
+ const r = await ghUser(
209711
+ token,
209712
+ "/user/memberships/orgs?state=active&per_page=100"
209713
+ );
209714
+ if (!r.body) return null;
209715
+ return r.body.flatMap((m2) => m2.organization?.login ? [{ login: m2.organization.login, role: m2.role ?? "member" }] : []);
209716
+ }
209717
+ async function orgInstalledAppsFor(token, org) {
209718
+ if (!token) return { state: { status: "needsAuth" }, apps: [] };
209719
+ const m2 = await ghUser(token, `/user/memberships/orgs/${encodeURIComponent(org)}`);
209720
+ if (m2.status === 401) return { state: { status: "needsAuth" }, apps: [] };
209721
+ if (m2.body && m2.body.role !== "admin") return { state: { status: "notAdmin" }, apps: [] };
209722
+ if (!m2.body && m2.status === 404) return { state: { status: "notAdmin" }, apps: [] };
209723
+ if (!m2.body) return { state: { status: "needsAuth" }, apps: [] };
209724
+ const r = await listOrgInstalledApps({ token, org });
209725
+ if (r.status === "ok") return { state: { status: "ok" }, apps: r.apps };
209726
+ if (r.status === "needsAuth") return { state: { status: "needsAuth" }, apps: [] };
209727
+ return { state: { status: "error", message: r.message }, apps: [] };
209728
+ }
209729
+ async function pickUserToken(svc) {
209730
+ const rows = await svc.listVariables();
209731
+ const sources = [void 0, ...rows.filter((r) => r.key === "GITHUB_TOKEN" && r.scope === "personal" && r.actorId).map((r) => r.actorId)];
209732
+ const tried = /* @__PURE__ */ new Set();
209733
+ for (const src of sources) {
209734
+ const token = await svc.revealVariable("GITHUB_TOKEN", src).catch(() => null);
209735
+ if (!token || tried.has(token)) continue;
209736
+ tried.add(token);
209737
+ const memberships = await listMemberships(token).catch(() => null);
209738
+ if (memberships) return { token, memberships };
209739
+ }
209740
+ return null;
209741
+ }
209742
+ var init_github_app_inventory = __esm({
209743
+ "../server/src/github-app-inventory.ts"() {
209744
+ "use strict";
209745
+ init_src8();
209746
+ }
209747
+ });
209748
+
209302
209749
  // ../server/src/server.ts
209303
209750
  function isBundleFrozen(err) {
209304
209751
  return typeof err === "object" && err !== null && err.code === "TENANT_BUNDLE_FROZEN";
@@ -212740,7 +213187,7 @@ async function startOasisServer(opts) {
212740
213187
  entry = {
212741
213188
  // 旧 CLI 未带 waiterId 时给它塞一个服务端本地生成的——它绝不可能出现在 leave 上,
212742
213189
  // 也就绝不会误命中 leftWaiterIds、绝不会被别人的 leave 摘掉。行为与旧 CLI 相同。
212743
- waiterId: clientWaiterId ?? `server:${(0, import_node_crypto42.randomUUID)()}`,
213190
+ waiterId: clientWaiterId ?? `server:${(0, import_node_crypto43.randomUUID)()}`,
212744
213191
  onResolve,
212745
213192
  expiresAt: expiresAt2,
212746
213193
  cleanup: () => clearTimeout(expiryTimer),
@@ -214878,57 +215325,71 @@ ${composed}`;
214878
215325
  }
214879
215326
  if (url.pathname === "/api/connectors/github/app/available" && req.method === "POST") {
214880
215327
  try {
215328
+ const { actorId, owner } = JSON.parse(rawBody || "{}");
214881
215329
  const svc = await connectorActorsService();
214882
215330
  if (!svc) throw new Error("actors service unavailable");
214883
- const rows = await svc.listVariables();
214884
- const plain = (key2, actorId) => rows.find((r) => r.key === key2 && r.actorId === actorId && !r.encrypted)?.maskedValue ?? "";
214885
- const byAppId = /* @__PURE__ */ new Map();
214886
- for (const r of rows) {
214887
- if (r.key !== "GITHUB_APP_ID" || r.encrypted || !r.maskedValue) continue;
214888
- const appId = r.maskedValue;
214889
- const prev = byAppId.get(appId);
214890
- if (prev) {
214891
- prev.usedBy += 1;
214892
- if (!r.actorId && prev.scope === "personal") {
214893
- prev.scope = "global";
214894
- delete prev.actorId;
214895
- prev.installationId = plain("GITHUB_APP_INSTALLATION_ID");
214896
- prev.slug = plain("GITHUB_APP_SLUG");
214897
- }
214898
- continue;
214899
- }
214900
- byAppId.set(appId, {
214901
- appId,
214902
- slug: plain("GITHUB_APP_SLUG", r.actorId),
214903
- installationId: plain("GITHUB_APP_INSTALLATION_ID", r.actorId),
214904
- scope: r.actorId ? "personal" : "global",
214905
- ...r.actorId ? { actorId: r.actorId } : {},
214906
- usedBy: 1
214907
- });
215331
+ const token = await svc.revealResolvedVariable("GITHUB_TOKEN", actorId);
215332
+ const appOwner = owner?.kind === "org" && owner.login ? { kind: "org", login: owner.login } : { kind: "user", ...token ? { login: await githubLoginOf(token) } : {} };
215333
+ const [oasisApps, installed] = await Promise.all([
215334
+ collectOasisGithubApps(svc),
215335
+ appOwner.kind === "org" ? orgInstalledAppsFor(token, appOwner.login) : Promise.resolve({ state: { status: "ok" }, apps: [] })
215336
+ ]);
215337
+ const names = new Map(oasisApps.map((a) => [a.appId, a.usedByNames]));
215338
+ const candidates = buildReuseCandidates({ owner: appOwner, oasisApps, installed: installed.apps }).map((c) => ({ ...c, usedBy: names.get(c.appId) ?? [] }));
215339
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ candidates, installed: installed.state }));
215340
+ } catch (e) {
215341
+ res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
215342
+ }
215343
+ return;
215344
+ }
215345
+ if (url.pathname === "/api/connectors/github/app/inventory" && req.method === "POST") {
215346
+ try {
215347
+ const svc = await connectorActorsService();
215348
+ if (!svc) throw new Error("actors service unavailable");
215349
+ const [oasisApps, picked] = await Promise.all([collectOasisGithubApps(svc), pickUserToken(svc)]);
215350
+ const adminOrgs = picked ? picked.memberships.filter((m2) => m2.role === "admin").map((m2) => m2.login) : [];
215351
+ const perOrg = picked ? await Promise.all(adminOrgs.map((org) => orgInstalledAppsFor(picked.token, org))) : [];
215352
+ const rows = buildAppInventory({ oasisApps, installed: perOrg.flatMap((r) => r.apps) });
215353
+ const names = new Map(oasisApps.map((a) => [a.appId, a.usedByNames]));
215354
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
215355
+ rows: rows.map((r) => ({ ...r, usedBy: names.get(r.appId) ?? [] })),
215356
+ /** 组织里装着的那部分列没列出来:needsAuth = 没有可用的 GitHub 登录;notAdmin = 不是任何组织的管理员。 */
215357
+ installed: !picked ? { status: "needsAuth" } : adminOrgs.length === 0 ? { status: "notAdmin" } : perOrg.find((r) => r.state.status !== "ok")?.state ?? { status: "ok" },
215358
+ orgs: adminOrgs
215359
+ }));
215360
+ } catch (e) {
215361
+ res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
215362
+ }
215363
+ return;
215364
+ }
215365
+ if (url.pathname === "/api/connectors/github/app/uninstall" && req.method === "POST") {
215366
+ try {
215367
+ const { appIds } = JSON.parse(rawBody || "{}");
215368
+ if (!Array.isArray(appIds) || appIds.length === 0 || !appIds.every((x2) => typeof x2 === "string")) {
215369
+ res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8BF7\u5148\u52FE\u9009\u8981\u5378\u8F7D\u7684 App" }));
215370
+ return;
214908
215371
  }
214909
- const apps = await Promise.all([...byAppId.values()].map(async (a) => {
214910
- const base = {
214911
- appId: a.appId,
214912
- slug: a.slug,
214913
- account: a.slug ? `${a.slug}[bot]` : a.appId,
214914
- isOrgDefault: a.scope === "global",
214915
- usedBy: a.usedBy,
214916
- ...a.slug ? { settingsUrl: appPermissionSettingsUrl({ slug: a.slug }) } : {}
214917
- };
215372
+ const svc = await connectorActorsService();
215373
+ if (!svc) throw new Error("actors service unavailable");
215374
+ const apps = new Map((await collectOasisGithubApps(svc)).map((a) => [a.appId, a]));
215375
+ const results = await Promise.all(appIds.map(async (appId) => {
215376
+ const app = apps.get(appId);
215377
+ if (!app) return { appId, ok: false, error: "Oasis \u6CA1\u6709\u8FD9\u4E2A App \u7684\u79C1\u94A5\uFF0C\u5378\u8F7D\u4E0D\u4E86\uFF0C\u8BF7\u53BB GitHub \u9875\u9762\u5904\u7406" };
215378
+ if (app.isOrgDefault || app.usedBy.length > 0) {
215379
+ return { appId, slug: app.slug, ok: false, error: `\u8FD8\u6709 agent \u5728\u7528\uFF08${app.isOrgDefault ? "\u7EC4\u7EC7\u9ED8\u8BA4" : app.usedByNames.join("\u3001")}\uFF09\uFF0C\u4E0D\u80FD\u5378\u8F7D` };
215380
+ }
215381
+ const pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", app.keyActorId);
215382
+ if (!pem) return { appId, slug: app.slug, ok: false, error: "\u79C1\u94A5\u8BFB\u4E0D\u51FA\u6765\uFF0C\u5378\u8F7D\u4E0D\u4E86" };
214918
215383
  try {
214919
- const pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", a.actorId);
214920
- if (!pem || !a.installationId) return { ...base, permissionsUnknown: true };
214921
- const perms = await fetchInstallationPermissions({
214922
- appId: a.appId,
214923
- privateKeyPem: pem,
214924
- installationId: a.installationId
214925
- });
214926
- return { ...base, missing: missingAppPermissions(perms) };
214927
- } catch {
214928
- return { ...base, permissionsUnknown: true };
215384
+ for (const installationId of app.installationIds) {
215385
+ await uninstallAppInstallation({ appId, privateKeyPem: pem, installationId });
215386
+ }
215387
+ return { appId, slug: app.slug, ok: true };
215388
+ } catch (e) {
215389
+ return { appId, slug: app.slug, ok: false, error: `GitHub \u62D2\u7EDD\u4E86\u5378\u8F7D\uFF1A${String(e).replace(/^Error:\s*/, "")}` };
214929
215390
  }
214930
215391
  }));
214931
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ apps }));
215392
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ results }));
214932
215393
  } catch (e) {
214933
215394
  res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
214934
215395
  }
@@ -214957,9 +215418,11 @@ ${composed}`;
214957
215418
  return;
214958
215419
  }
214959
215420
  const pick3 = (key2) => rows.find((r) => r.key === key2 && r.actorId === srcActor && !r.encrypted)?.maskedValue ?? "";
214960
- const installationId = pick3("GITHUB_APP_INSTALLATION_ID");
215421
+ const storedInstallationId = pick3("GITHUB_APP_INSTALLATION_ID");
215422
+ const live = await listAppInstallations({ appId, privateKeyPem: pem }).catch(() => null);
215423
+ const installationId = live ? (live.find((i) => i.installationId === storedInstallationId) ?? live[0])?.installationId ?? "" : storedInstallationId;
214961
215424
  if (!installationId) {
214962
- res.writeHead(409, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8FD9\u4E2A App \u8FD8\u6CA1\u6709\u5B89\u88C5\u8BB0\u5F55\uFF0C\u65E0\u6CD5\u94F8 token" }));
215425
+ res.writeHead(409, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8FD9\u4E2A App \u8FD8\u6CA1\u88C5\u5230\u4EFB\u4F55\u8D26\u53F7\u2014\u2014\u5148\u5728 GitHub \u4E0A\u5B89\u88C5\u5B83\uFF0C\u88C5\u597D\u540E\u518D\u70B9\u8FDE\u63A5" }));
214963
215426
  return;
214964
215427
  }
214965
215428
  const slug6 = pick3("GITHUB_APP_SLUG");
@@ -214997,7 +215460,7 @@ ${composed}`;
214997
215460
  const msg = String(e);
214998
215461
  bad(
214999
215462
  400,
215000
- /HTTP 401/.test(msg) ? "App ID \u4E0E\u79C1\u94A5\u5BF9\u4E0D\u4E0A\u2014\u2014\u786E\u8BA4 App ID \u6284\u81EA\u540C\u4E00\u4E2A App \u7684\u8BBE\u7F6E\u9875" : /私钥解析失败/.test(msg) ? msg.replace(/^Error:\s*/, "").replace(/^github app:\s*/, "") : `GitHub \u62D2\u7EDD\u4E86\u8FD9\u5BF9\u51ED\u636E\uFF1A${msg}`
215463
+ /HTTP 401/.test(msg) ? "\u8FD9\u4E2A\u79C1\u94A5\u548C\u6240\u9009\u7684 App \u5BF9\u4E0D\u4E0A\u2014\u2014\u8BF7\u786E\u8BA4\u5B83\u662F\u5728\u8FD9\u4E2A App \u7684\u8BBE\u7F6E\u9875\u70B9 Generate a private key \u4E0B\u8F7D\u7684\u90A3\u4E00\u4EFD\uFF08\u624B\u586B App ID \u65F6\u4E5F\u6838\u5BF9\u4E00\u4E0B\u6709\u6CA1\u6709\u6284\u9519\uFF09" : /私钥解析失败/.test(msg) ? msg.replace(/^Error:\s*/, "").replace(/^github app:\s*/, "") : `GitHub \u62D2\u7EDD\u4E86\u8FD9\u5BF9\u51ED\u636E\uFF1A${msg}`
215001
215464
  );
215002
215465
  return;
215003
215466
  }
@@ -215667,14 +216130,14 @@ async function markDeliveredWithRetry(store, messageId, attempts = 3) {
215667
216130
  }
215668
216131
  return false;
215669
216132
  }
215670
- 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;
216133
+ var http, import_node_crypto43, 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;
215671
216134
  var init_server3 = __esm({
215672
216135
  "../server/src/server.ts"() {
215673
216136
  "use strict";
215674
216137
  init_src();
215675
216138
  init_file_limits2();
215676
216139
  http = __toESM(require("node:http"), 1);
215677
- import_node_crypto42 = require("node:crypto");
216140
+ import_node_crypto43 = require("node:crypto");
215678
216141
  init_chat_session();
215679
216142
  init_chat_system_message();
215680
216143
  init_sealed_node_receipt();
@@ -215740,6 +216203,7 @@ var init_server3 = __esm({
215740
216203
  init_child_live_turn();
215741
216204
  init_registry_audit_sink();
215742
216205
  init_src8();
216206
+ init_github_app_inventory();
215743
216207
  init_continuation3();
215744
216208
  CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
215745
216209
  defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
@@ -215854,7 +216318,7 @@ function createNodeTokenStore(file) {
215854
216318
  return {
215855
216319
  issue(nodeId) {
215856
216320
  const table = read3();
215857
- const token = `ont_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
216321
+ const token = `ont_${(0, import_node_crypto44.randomBytes)(24).toString("base64url")}`;
215858
216322
  table[token] = nodeId;
215859
216323
  write(table);
215860
216324
  return token;
@@ -215914,7 +216378,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215914
216378
  return {
215915
216379
  issue(name, ttlMs = defaultTtlMs, companyId) {
215916
216380
  const m2 = prune();
215917
- const token = `ent_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
216381
+ const token = `ent_${(0, import_node_crypto44.randomBytes)(24).toString("base64url")}`;
215918
216382
  const expiresAt2 = Date.now() + ttlMs;
215919
216383
  m2.set(token, { name, expiresAt: expiresAt2, ...companyId !== void 0 ? { companyId } : {} });
215920
216384
  write(m2);
@@ -215963,33 +216427,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215963
216427
  }
215964
216428
  };
215965
216429
  }
215966
- var import_node_crypto43, fs22, path21, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
216430
+ var import_node_crypto44, fs22, path21, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
215967
216431
  var init_tokens = __esm({
215968
216432
  "../server/src/tokens.ts"() {
215969
216433
  "use strict";
215970
- import_node_crypto43 = require("node:crypto");
216434
+ import_node_crypto44 = require("node:crypto");
215971
216435
  fs22 = __toESM(require("node:fs"), 1);
215972
216436
  path21 = __toESM(require("node:path"), 1);
215973
216437
  SESSION_TOKEN_PREFIX = "oat_v2_";
215974
216438
  b64url2 = (value2) => Buffer.from(value2).toString("base64url");
215975
216439
  fromB64url = (value2) => Buffer.from(value2, "base64url").toString("utf8");
215976
216440
  readOrCreateSecret = (file) => {
215977
- if (!file) return (0, import_node_crypto43.randomBytes)(32);
216441
+ if (!file) return (0, import_node_crypto44.randomBytes)(32);
215978
216442
  try {
215979
216443
  const raw = fs22.readFileSync(file, "utf8").trim();
215980
216444
  if (raw) return Buffer.from(raw, "base64url");
215981
216445
  } catch {
215982
216446
  }
215983
- const secret = (0, import_node_crypto43.randomBytes)(32);
216447
+ const secret = (0, import_node_crypto44.randomBytes)(32);
215984
216448
  fs22.mkdirSync(path21.dirname(file), { recursive: true });
215985
216449
  fs22.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215986
216450
  return secret;
215987
216451
  };
215988
- sign = (secret, payload2) => (0, import_node_crypto43.createHmac)("sha256", secret).update(payload2).digest("base64url");
216452
+ sign = (secret, payload2) => (0, import_node_crypto44.createHmac)("sha256", secret).update(payload2).digest("base64url");
215989
216453
  signatureMatches = (actual, expected) => {
215990
216454
  const a = Buffer.from(actual);
215991
216455
  const b2 = Buffer.from(expected);
215992
- return a.length === b2.length && (0, import_node_crypto43.timingSafeEqual)(a, b2);
216456
+ return a.length === b2.length && (0, import_node_crypto44.timingSafeEqual)(a, b2);
215993
216457
  };
215994
216458
  isTokenClaims = (value2) => {
215995
216459
  if (value2 === null || typeof value2 !== "object") return false;
@@ -216051,31 +216515,31 @@ function createDataplaneTokenIssuer(opts = {}) {
216051
216515
  }
216052
216516
  };
216053
216517
  }
216054
- var import_node_crypto44, fs23, path22, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
216518
+ var import_node_crypto45, fs23, path22, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
216055
216519
  var init_session_dataplane_token = __esm({
216056
216520
  "../server/src/session-dataplane-token.ts"() {
216057
216521
  "use strict";
216058
- import_node_crypto44 = require("node:crypto");
216522
+ import_node_crypto45 = require("node:crypto");
216059
216523
  fs23 = __toESM(require("node:fs"), 1);
216060
216524
  path22 = __toESM(require("node:path"), 1);
216061
216525
  init_src();
216062
216526
  PREFIX2 = "osd_";
216063
216527
  b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
216064
216528
  fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
216065
- sign2 = (secret, payload2) => (0, import_node_crypto44.createHmac)("sha256", secret).update(payload2).digest("base64url");
216529
+ sign2 = (secret, payload2) => (0, import_node_crypto45.createHmac)("sha256", secret).update(payload2).digest("base64url");
216066
216530
  sigMatches = (actual, expected) => {
216067
216531
  const a = Buffer.from(actual);
216068
216532
  const b2 = Buffer.from(expected);
216069
- return a.length === b2.length && (0, import_node_crypto44.timingSafeEqual)(a, b2);
216533
+ return a.length === b2.length && (0, import_node_crypto45.timingSafeEqual)(a, b2);
216070
216534
  };
216071
216535
  readOrCreateSecret2 = (file) => {
216072
- if (!file) return (0, import_node_crypto44.randomBytes)(32);
216536
+ if (!file) return (0, import_node_crypto45.randomBytes)(32);
216073
216537
  try {
216074
216538
  const raw = fs23.readFileSync(file, "utf8").trim();
216075
216539
  if (raw) return Buffer.from(raw, "base64url");
216076
216540
  } catch {
216077
216541
  }
216078
- const secret = (0, import_node_crypto44.randomBytes)(32);
216542
+ const secret = (0, import_node_crypto45.randomBytes)(32);
216079
216543
  fs23.mkdirSync(path22.dirname(file), { recursive: true });
216080
216544
  fs23.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
216081
216545
  return secret;
@@ -218877,11 +219341,11 @@ function onlyHits(pool, q2) {
218877
219341
  if (!q2.requireMatch || q2.keywords.length === 0) return [...pool];
218878
219342
  return pool.filter((r) => memoryMatchScore(r, q2.keywords) > 0);
218879
219343
  }
218880
- var import_node_crypto45, MemoryActorMemoryStore, toIndexEntry;
219344
+ var import_node_crypto46, MemoryActorMemoryStore, toIndexEntry;
218881
219345
  var init_memory_actor_memory_store = __esm({
218882
219346
  "../testkit/src/memory-actor-memory-store.ts"() {
218883
219347
  "use strict";
218884
- import_node_crypto45 = require("node:crypto");
219348
+ import_node_crypto46 = require("node:crypto");
218885
219349
  init_src();
218886
219350
  MemoryActorMemoryStore = class {
218887
219351
  rows = /* @__PURE__ */ new Map();
@@ -218935,7 +219399,7 @@ var init_memory_actor_memory_store = __esm({
218935
219399
  async write(input, now) {
218936
219400
  if (input.memId === void 0) {
218937
219401
  const rec2 = {
218938
- memId: `mem:${(0, import_node_crypto45.randomUUID)()}`,
219402
+ memId: `mem:${(0, import_node_crypto46.randomUUID)()}`,
218939
219403
  actorId: input.actorId,
218940
219404
  projectId: input.projectId,
218941
219405
  keywords: [...input.keywords],
@@ -220634,13 +221098,13 @@ var init_stats = __esm({
220634
221098
 
220635
221099
  // ../server/src/domains/projects/project-id.ts
220636
221100
  function randomProjectId() {
220637
- return projectIdFromUuid((0, import_node_crypto46.randomUUID)());
221101
+ return projectIdFromUuid((0, import_node_crypto47.randomUUID)());
220638
221102
  }
220639
- var import_node_crypto46;
221103
+ var import_node_crypto47;
220640
221104
  var init_project_id = __esm({
220641
221105
  "../server/src/domains/projects/project-id.ts"() {
220642
221106
  "use strict";
220643
- import_node_crypto46 = require("node:crypto");
221107
+ import_node_crypto47 = require("node:crypto");
220644
221108
  init_src();
220645
221109
  }
220646
221110
  });
@@ -222715,12 +223179,12 @@ function applyTurnPatch(t, patch) {
222715
223179
  ...patch.lastError !== void 0 ? { lastError: patch.lastError } : {}
222716
223180
  };
222717
223181
  }
222718
- var import_node_crypto47, fs24, path23, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
223182
+ var import_node_crypto48, fs24, path23, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
222719
223183
  var init_dev_store = __esm({
222720
223184
  "../server/src/dev-store.ts"() {
222721
223185
  "use strict";
222722
223186
  init_src();
222723
- import_node_crypto47 = require("node:crypto");
223187
+ import_node_crypto48 = require("node:crypto");
222724
223188
  fs24 = __toESM(require("node:fs"), 1);
222725
223189
  path23 = __toESM(require("node:path"), 1);
222726
223190
  init_src();
@@ -222774,7 +223238,7 @@ var init_dev_store = __esm({
222774
223238
  return path23.join(this.dir, hash3);
222775
223239
  }
222776
223240
  async put(bytes2) {
222777
- const hash3 = (0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex");
223241
+ const hash3 = (0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex");
222778
223242
  const file = this.fileOf(hash3);
222779
223243
  if (!fs24.existsSync(file)) fs24.writeFileSync(file, bytes2);
222780
223244
  return hash3;
@@ -224069,7 +224533,7 @@ var init_company_engine_router = __esm({
224069
224533
 
224070
224534
  // ../server/src/skill-market-client.ts
224071
224535
  function digestOf2(bytes2) {
224072
- return `sha256:${(0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex")}`;
224536
+ return `sha256:${(0, import_node_crypto49.createHash)("sha256").update(bytes2).digest("hex")}`;
224073
224537
  }
224074
224538
  function createSkillMarketClientFromEnv(env = process.env) {
224075
224539
  const base = env["SKILL_MARKET_BASE_URL"];
@@ -224080,11 +224544,11 @@ function createSkillMarketClientFromEnv(env = process.env) {
224080
224544
  function sleep4(ms) {
224081
224545
  return new Promise((r) => setTimeout(r, ms));
224082
224546
  }
224083
- var import_node_crypto48, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
224547
+ var import_node_crypto49, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
224084
224548
  var init_skill_market_client = __esm({
224085
224549
  "../server/src/skill-market-client.ts"() {
224086
224550
  "use strict";
224087
- import_node_crypto48 = require("node:crypto");
224551
+ import_node_crypto49 = require("node:crypto");
224088
224552
  init_src();
224089
224553
  DisabledSkillMarketClient = class {
224090
224554
  async listSkills(_input) {
@@ -224935,7 +225399,7 @@ async function copySessionOutputs(deps) {
224935
225399
  continue;
224936
225400
  }
224937
225401
  const saved = existing?.[file.path];
224938
- if (saved && savedContentHash(saved) === (0, import_node_crypto49.createHash)("sha256").update(bytes2).digest("hex")) {
225402
+ if (saved && savedContentHash(saved) === (0, import_node_crypto50.createHash)("sha256").update(bytes2).digest("hex")) {
224939
225403
  const aligned = { ...saved, size: bytes2.length, mtime: file.mtime };
224940
225404
  delete aligned.editedAt;
224941
225405
  realigned.push(aligned);
@@ -225032,11 +225496,11 @@ function createOutputCopyScheduler(deps) {
225032
225496
  }
225033
225497
  };
225034
225498
  }
225035
- var import_node_crypto49, OUTPUT_COPY_LIMITS;
225499
+ var import_node_crypto50, OUTPUT_COPY_LIMITS;
225036
225500
  var init_outputs_copy = __esm({
225037
225501
  "../server/src/domains/chat-sessions/outputs-copy.ts"() {
225038
225502
  "use strict";
225039
- import_node_crypto49 = require("node:crypto");
225503
+ import_node_crypto50 = require("node:crypto");
225040
225504
  init_src();
225041
225505
  OUTPUT_COPY_LIMITS = {
225042
225506
  maxFileBytes: FILE_CONTENT_MAX_BYTES,
@@ -225421,11 +225885,11 @@ function typeDefError(def, mode) {
225421
225885
  }
225422
225886
  return null;
225423
225887
  }
225424
- var import_node_crypto50, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
225888
+ var import_node_crypto51, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
225425
225889
  var init_types_admin = __esm({
225426
225890
  "../server/src/types-admin.ts"() {
225427
225891
  "use strict";
225428
- import_node_crypto50 = require("node:crypto");
225892
+ import_node_crypto51 = require("node:crypto");
225429
225893
  init_zod();
225430
225894
  init_src7();
225431
225895
  init_config_audit();
@@ -225552,7 +226016,7 @@ var init_types_admin = __esm({
225552
226016
  async trace(change, target, by, detail) {
225553
226017
  try {
225554
226018
  await this.audit?.({
225555
- id: `reg_${(0, import_node_crypto50.randomUUID)()}`,
226019
+ id: `reg_${(0, import_node_crypto51.randomUUID)()}`,
225556
226020
  actor: by ?? SYSTEM_CONFIG_ACTOR,
225557
226021
  kind: "registry_change",
225558
226022
  target,
@@ -225700,11 +226164,11 @@ function roleDefError(def, mode) {
225700
226164
  }
225701
226165
  return null;
225702
226166
  }
225703
- var import_node_crypto51, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
226167
+ var import_node_crypto52, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
225704
226168
  var init_roles_admin = __esm({
225705
226169
  "../server/src/roles-admin.ts"() {
225706
226170
  "use strict";
225707
- import_node_crypto51 = require("node:crypto");
226171
+ import_node_crypto52 = require("node:crypto");
225708
226172
  init_zod();
225709
226173
  init_src7();
225710
226174
  init_config_audit();
@@ -225729,7 +226193,7 @@ var init_roles_admin = __esm({
225729
226193
  async trace(change, target, by, detail) {
225730
226194
  try {
225731
226195
  await this.deps.audit?.({
225732
- id: `reg_${(0, import_node_crypto51.randomUUID)()}`,
226196
+ id: `reg_${(0, import_node_crypto52.randomUUID)()}`,
225733
226197
  actor: by ?? SYSTEM_CONFIG_ACTOR,
225734
226198
  kind: "registry_change",
225735
226199
  target,
@@ -226920,14 +227384,13 @@ function frameTokenTotal(usage) {
226920
227384
  return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
226921
227385
  }
226922
227386
  async function backfillRunModel(target, run, model, modelSource, warn) {
226923
- const effectiveModel = actualModelId(model);
226924
- if (!effectiveModel) return;
226925
- if (hasModelName(run.effectiveModel)) return;
227387
+ const patch = preferRunModel(
227388
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
227389
+ { effectiveModel: model, modelSource: modelSource === "requested" ? "requested" : "runtime" }
227390
+ );
227391
+ if (!patch) return;
226926
227392
  try {
226927
- await target.store.updateRun(target.runId, {
226928
- effectiveModel,
226929
- modelSource: modelSource === "requested" ? "requested" : "runtime"
226930
- });
227393
+ await target.store.updateRun(target.runId, patch);
226931
227394
  } catch (err) {
226932
227395
  warn(`[usage-ledger] run ${target.runId} \u7684\u6A21\u578B\u540D\u8865\u5199\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4F1A\u8BDD\uFF09\uFF1A${String(err)}`);
226933
227396
  }
@@ -227693,8 +228156,10 @@ var init_sink = __esm({
227693
228156
  const isErr = status === "failed" || status === "timeout";
227694
228157
  const errorFields = isErr ? { errorCode: update.exit.reason ?? "error", errorMessage: formatExitError(update.exit) } : {};
227695
228158
  const exitModel = actualModelId(update.exit.model);
227696
- const model = exitModel ?? actualModelId(st.observedModel) ?? null;
227697
- const modelSource = exitModel && update.exit.modelSource === "requested" ? "requested" : "runtime";
228159
+ const observedModel = actualModelId(st.observedModel);
228160
+ const exitIsRequestedOnly = update.exit.modelSource === "requested";
228161
+ const model = (exitIsRequestedOnly ? observedModel ?? exitModel : exitModel ?? observedModel) ?? null;
228162
+ const modelSource = model && model === exitModel && exitIsRequestedOnly && !observedModel ? "requested" : "runtime";
227698
228163
  const exitRtId = typeof update.exit.runtimeSessionId === "string" && update.exit.runtimeSessionId.trim() ? update.exit.runtimeSessionId.trim() : void 0;
227699
228164
  if (exitRtId) st.baseMetadata = { ...st.baseMetadata, runtimeSessionId: exitRtId };
227700
228165
  const exitWorkdir = typeof update.exit.workdirRef === "string" && update.exit.workdirRef.trim() ? update.exit.workdirRef.trim() : void 0;
@@ -227725,6 +228190,12 @@ var init_sink = __esm({
227725
228190
  createdAt: update.exit.at
227726
228191
  });
227727
228192
  }
228193
+ const currentRun = model ? await this.store.getRun(st.runId).catch(() => null) : null;
228194
+ const modelPatch = model ? preferRunModel(
228195
+ { effectiveModel: currentRun?.effectiveModel, modelSource: currentRun?.modelSource },
228196
+ { effectiveModel: model, modelSource },
228197
+ "replace"
228198
+ ) : null;
227728
228199
  const finished = await this.store.updateRun(st.runId, {
227729
228200
  status,
227730
228201
  endedAt: update.exit.at,
@@ -227740,7 +228211,7 @@ var init_sink = __esm({
227740
228211
  // token/成本来自 adapter 解析 stream-json(含 cache);不带则不写、保持 null。
227741
228212
  ...update.exit.usage ? { usage: update.exit.usage } : {},
227742
228213
  // effectiveModel/modelSource 喂用量聚合(聚合键含 model);与 chat/finishRun 路径一致。
227743
- ...model ? { effectiveModel: model, modelSource } : {},
228214
+ ...modelPatch ?? {},
227744
228215
  // metadata 整列替换:必须带上 baseMetadata(bundleManifest/runtimeSessionId)一起写回。
227745
228216
  // model 有则并入(前端 findModelInValue 直接读 metadata.model)。**不能只在有 model 时才写**
227746
228217
  // ——否则无 model 的 run(如回捕型 runtime 没报模型)会把 exit 校正过的会话号丢掉。
@@ -228095,10 +228566,8 @@ function wireRecoveredChatTurn(deps) {
228095
228566
  live.emit({ type: "done", ...plan.runtimeSessionId ? { sessionId: plan.runtimeSessionId } : {} });
228096
228567
  live.finish(failed ? "error" : "done");
228097
228568
  deps.unregister();
228098
- const nativeSessionMetadata = info.runtimeSessionId && info.runtimeSessionId !== plan.runtimeSessionId ? nativeRuntimeSessionMetadata(
228099
- (await trace.getRun(plan.runId).catch(() => null))?.metadata,
228100
- info.runtimeSessionId
228101
- ) : void 0;
228569
+ const currentRun = await trace.getRun(plan.runId).catch(() => null);
228570
+ const nativeSessionMetadata = info.runtimeSessionId && info.runtimeSessionId !== plan.runtimeSessionId ? nativeRuntimeSessionMetadata(currentRun?.metadata, info.runtimeSessionId) : void 0;
228102
228571
  enqueueTrace(async () => {
228103
228572
  await appender.append([{
228104
228573
  eventType: "run.finished",
@@ -228107,7 +228576,7 @@ function wireRecoveredChatTurn(deps) {
228107
228576
  startedAt: finishedAt
228108
228577
  }]);
228109
228578
  await appender.update({
228110
- ...runTerminalPatch(info, plan.startedAt, finishedAt),
228579
+ ...runTerminalPatch(info, plan.startedAt, finishedAt, currentRun),
228111
228580
  // 运行时自己退干净了,但平台没把输出接全——运行记录不许写「成功」(Bug 0123)。
228112
228581
  // 退出帧本身已经是失败时保持它的归因,只补一句缺口说明。
228113
228582
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
@@ -228331,7 +228800,7 @@ async function reconcileChatExitFrame(deps) {
228331
228800
  ];
228332
228801
  await deps.trace.appendEvents(row.runId, seqless.map((e, i) => ({ ...e, seq: baseSeq + i + 1 })));
228333
228802
  await deps.trace.updateRun(row.runId, {
228334
- ...runTerminalPatch(info, run.startedAt, finishedAt),
228803
+ ...runTerminalPatch(info, run.startedAt, finishedAt, run),
228335
228804
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
228336
228805
  // 首轮占位会话号 → runtime 报回的原生号(Bug 0171,判据见本函数上方那一段)。
228337
228806
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
@@ -228560,6 +229029,7 @@ var init_chat_recovery = __esm({
228560
229029
  "use strict";
228561
229030
  init_file_restore();
228562
229031
  init_continuation3();
229032
+ init_src();
228563
229033
  init_chat_parts();
228564
229034
  init_sink();
228565
229035
  init_usage_ledger();
@@ -228587,7 +229057,7 @@ var init_chat_recovery = __esm({
228587
229057
  const existing = formatExitError(info);
228588
229058
  return { errorMessage: existing ? `${existing}\uFF1B${gap}` : gap };
228589
229059
  };
228590
- runTerminalPatch = (info, startedAt, endedAt) => ({
229060
+ runTerminalPatch = (info, startedAt, endedAt, current) => ({
228591
229061
  status: classifySessionExitStatus(info),
228592
229062
  endedAt,
228593
229063
  durationMs: Date.parse(endedAt) - Date.parse(startedAt),
@@ -228600,15 +229070,19 @@ var init_chat_recovery = __esm({
228600
229070
  // 不能用 null 覆盖,否则恢复轮的 token 会掉进「未知模型」桶。
228601
229071
  // 伪型号(`default[]` / `auto`)不是模型名,不写;请求值(`requested`)与实际值分开记来源
228602
229072
  // ——重挂路与 chat 主路、派发路三处必须同一判据,否则同一条 run 换条路收尾归属就变了。
228603
- ...chatExitModelFields(info.model, null, null, info.modelSource)
229073
+ ...preferRunModel(
229074
+ { effectiveModel: current?.effectiveModel, modelSource: current?.modelSource },
229075
+ chatExitModelFields(info.model, null, null, info.modelSource),
229076
+ "replace"
229077
+ ) ?? {}
228604
229078
  });
228605
229079
  }
228606
229080
  });
228607
229081
 
228608
229082
  // ../server/src/auth/crypto.ts
228609
229083
  function hashPassword(password) {
228610
- const salt = (0, import_node_crypto52.randomBytes)(16);
228611
- const dk = (0, import_node_crypto52.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
229084
+ const salt = (0, import_node_crypto53.randomBytes)(16);
229085
+ const dk = (0, import_node_crypto53.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
228612
229086
  return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
228613
229087
  }
228614
229088
  function verifyPassword(password, stored) {
@@ -228625,8 +229099,8 @@ function verifyPassword(password, stored) {
228625
229099
  return false;
228626
229100
  }
228627
229101
  if (expected.length === 0) return false;
228628
- const dk = (0, import_node_crypto52.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
228629
- return dk.length === expected.length && (0, import_node_crypto52.timingSafeEqual)(dk, expected);
229102
+ const dk = (0, import_node_crypto53.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
229103
+ return dk.length === expected.length && (0, import_node_crypto53.timingSafeEqual)(dk, expected);
228630
229104
  }
228631
229105
  function b64urlJson(value2) {
228632
229106
  return Buffer.from(JSON.stringify(value2)).toString("base64url");
@@ -228634,17 +229108,17 @@ function b64urlJson(value2) {
228634
229108
  function signSession(claims, secret) {
228635
229109
  const head = b64urlJson({ alg: "HS256", typ: "JWT" });
228636
229110
  const body2 = b64urlJson(claims);
228637
- const sig = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
229111
+ const sig = (0, import_node_crypto53.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228638
229112
  return `${head}.${body2}.${sig}`;
228639
229113
  }
228640
229114
  function verifySession(token, secret, nowMs) {
228641
229115
  const parts = token.split(".");
228642
229116
  if (parts.length !== 3) return null;
228643
229117
  const [head, body2, sig] = parts;
228644
- const expected = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
229118
+ const expected = (0, import_node_crypto53.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228645
229119
  const got = Buffer.from(sig);
228646
229120
  const exp = Buffer.from(expected);
228647
- if (got.length !== exp.length || !(0, import_node_crypto52.timingSafeEqual)(got, exp)) return null;
229121
+ if (got.length !== exp.length || !(0, import_node_crypto53.timingSafeEqual)(got, exp)) return null;
228648
229122
  let claims;
228649
229123
  try {
228650
229124
  claims = JSON.parse(Buffer.from(body2, "base64url").toString("utf8"));
@@ -228656,21 +229130,21 @@ function verifySession(token, secret, nowMs) {
228656
229130
  return claims;
228657
229131
  }
228658
229132
  function generateCode() {
228659
- return String((0, import_node_crypto52.randomInt)(0, 1e6)).padStart(6, "0");
229133
+ return String((0, import_node_crypto53.randomInt)(0, 1e6)).padStart(6, "0");
228660
229134
  }
228661
229135
  function hashCode(code2, secret) {
228662
- return (0, import_node_crypto52.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
229136
+ return (0, import_node_crypto53.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
228663
229137
  }
228664
229138
  function safeEqualHash(a, b2) {
228665
229139
  const ba = Buffer.from(a);
228666
229140
  const bb = Buffer.from(b2);
228667
- return ba.length === bb.length && (0, import_node_crypto52.timingSafeEqual)(ba, bb);
229141
+ return ba.length === bb.length && (0, import_node_crypto53.timingSafeEqual)(ba, bb);
228668
229142
  }
228669
- var import_node_crypto52, SCRYPT_N, SCRYPT_KEYLEN;
229143
+ var import_node_crypto53, SCRYPT_N, SCRYPT_KEYLEN;
228670
229144
  var init_crypto2 = __esm({
228671
229145
  "../server/src/auth/crypto.ts"() {
228672
229146
  "use strict";
228673
- import_node_crypto52 = require("node:crypto");
229147
+ import_node_crypto53 = require("node:crypto");
228674
229148
  SCRYPT_N = 16384;
228675
229149
  SCRYPT_KEYLEN = 32;
228676
229150
  }
@@ -230054,18 +230528,18 @@ function variableKeyFromEnv(env = process.env) {
230054
230528
  return buf;
230055
230529
  }
230056
230530
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
230057
- return (0, import_node_crypto53.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
230531
+ return (0, import_node_crypto54.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
230058
230532
  }
230059
230533
  function dedupe2(ids2) {
230060
230534
  return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
230061
230535
  }
230062
- var import_node_crypto53, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
230536
+ var import_node_crypto54, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
230063
230537
  var init_service5 = __esm({
230064
230538
  "../server/src/domains/actors/service.ts"() {
230065
230539
  "use strict";
230066
230540
  init_file_limits2();
230067
230541
  init_keyed_mutex();
230068
- import_node_crypto53 = require("node:crypto");
230542
+ import_node_crypto54 = require("node:crypto");
230069
230543
  init_skill_fetcher();
230070
230544
  init_identity();
230071
230545
  init_migrate();
@@ -230689,7 +231163,7 @@ ${input.description}
230689
231163
  */
230690
231164
  buildSkillFile(skillId, path47, content3, now) {
230691
231165
  const bytes2 = new TextEncoder().encode(content3);
230692
- const blobHash = (0, import_node_crypto53.createHash)("sha256").update(bytes2).digest("hex");
231166
+ const blobHash = (0, import_node_crypto54.createHash)("sha256").update(bytes2).digest("hex");
230693
231167
  return { skillId, path: path47, content: content3, blobHash, size: bytes2.length, updatedAt: now };
230694
231168
  }
230695
231169
  /**
@@ -231812,15 +232286,15 @@ ${input.description}
231812
232286
  return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
231813
232287
  }
231814
232288
  encrypt(plain) {
231815
- const iv = (0, import_node_crypto53.randomBytes)(12);
231816
- const cipher = (0, import_node_crypto53.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
232289
+ const iv = (0, import_node_crypto54.randomBytes)(12);
232290
+ const cipher = (0, import_node_crypto54.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
231817
232291
  const enc3 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
231818
232292
  return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc3.toString("base64")].join(".");
231819
232293
  }
231820
232294
  decrypt(packed) {
231821
232295
  const [iv, tag, data] = packed.split(".");
231822
232296
  if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
231823
- const decipher = (0, import_node_crypto53.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
232297
+ const decipher = (0, import_node_crypto54.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
231824
232298
  decipher.setAuthTag(Buffer.from(tag, "base64"));
231825
232299
  return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
231826
232300
  }
@@ -232434,7 +232908,7 @@ async function listSkillMetadataForActor(args) {
232434
232908
  usedDirs.add(dir);
232435
232909
  const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
232436
232910
  let latest = "1970-01-01T00:00:00.000Z";
232437
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232911
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232438
232912
  for (const m2 of sorted) {
232439
232913
  if (m2.updatedAt > latest) latest = m2.updatedAt;
232440
232914
  hasher.update(m2.path);
@@ -232459,11 +232933,11 @@ async function listSkillMetadataForActor(args) {
232459
232933
  const dir = sanitizeSkillDir(c.name, c.slug);
232460
232934
  if (usedDirs.has(dir)) continue;
232461
232935
  usedDirs.add(dir);
232462
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232936
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232463
232937
  for (const p2 of paths) {
232464
232938
  hasher.update(p2);
232465
232939
  hasher.update("\0");
232466
- hasher.update((0, import_node_crypto54.createHash)("sha256").update(c.files[p2]).digest("hex"));
232940
+ hasher.update((0, import_node_crypto55.createHash)("sha256").update(c.files[p2]).digest("hex"));
232467
232941
  hasher.update("\0");
232468
232942
  }
232469
232943
  out.push({
@@ -232484,11 +232958,11 @@ async function listSkillMetadataForActor(args) {
232484
232958
  const dir = sanitizeSkillDir(b2.name, b2.id);
232485
232959
  if (usedDirs.has(dir)) continue;
232486
232960
  usedDirs.add(dir);
232487
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232961
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232488
232962
  for (const p2 of paths) {
232489
232963
  hasher.update(p2);
232490
232964
  hasher.update("\0");
232491
- hasher.update((0, import_node_crypto54.createHash)("sha256").update(b2.files[p2]).digest("hex"));
232965
+ hasher.update((0, import_node_crypto55.createHash)("sha256").update(b2.files[p2]).digest("hex"));
232492
232966
  hasher.update("\0");
232493
232967
  }
232494
232968
  out.push({
@@ -232508,7 +232982,7 @@ function builtinSkillToFiles(b2) {
232508
232982
  skillId: `builtin:${b2.id}`,
232509
232983
  path: path47,
232510
232984
  content: content3,
232511
- blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
232985
+ blobHash: (0, import_node_crypto55.createHash)("sha256").update(content3).digest("hex"),
232512
232986
  size: Buffer.byteLength(content3, "utf8"),
232513
232987
  updatedAt: "1970-01-01T00:00:00.000Z"
232514
232988
  }));
@@ -232532,7 +233006,7 @@ function sanitizeSkillDir(name, fallbackId) {
232532
233006
  if (byName) return byName;
232533
233007
  const byId = fallbackId ? fold2(fallbackId) : "";
232534
233008
  if (byId) return byId;
232535
- return `skill-${(0, import_node_crypto54.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
233009
+ return `skill-${(0, import_node_crypto55.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
232536
233010
  }
232537
233011
  async function materializeSkillFiles(args) {
232538
233012
  const skills = await args.service.listInstalledSkillsForActor(args.actorId);
@@ -232565,7 +233039,7 @@ function connectorSkillToFiles(c) {
232565
233039
  skillId: c.id,
232566
233040
  path: path47,
232567
233041
  content: content3,
232568
- blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
233042
+ blobHash: (0, import_node_crypto55.createHash)("sha256").update(content3).digest("hex"),
232569
233043
  size: Buffer.byteLength(content3, "utf8"),
232570
233044
  updatedAt: "1970-01-01T00:00:00.000Z"
232571
233045
  }));
@@ -232582,11 +233056,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
232582
233056
  ...s2.version !== void 0 ? { version: s2.version } : {}
232583
233057
  }));
232584
233058
  }
232585
- var import_node_crypto54;
233059
+ var import_node_crypto55;
232586
233060
  var init_skill_materializer = __esm({
232587
233061
  "../server/src/domains/actors/skill-materializer.ts"() {
232588
233062
  "use strict";
232589
- import_node_crypto54 = require("node:crypto");
233063
+ import_node_crypto55 = require("node:crypto");
232590
233064
  }
232591
233065
  });
232592
233066
 
@@ -234693,7 +235167,7 @@ function createActorsDomain(opts) {
234693
235167
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
234694
235168
  audit: async (entry) => {
234695
235169
  await opts.audit?.({
234696
- id: `reg_${(0, import_node_crypto55.randomUUID)()}`,
235170
+ id: `reg_${(0, import_node_crypto56.randomUUID)()}`,
234697
235171
  actor: entry.by,
234698
235172
  kind: "registry_change",
234699
235173
  target: entry.actorId,
@@ -234733,11 +235207,11 @@ function createActorsDomain(opts) {
234733
235207
  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 } : {}, ...opts.agentManageGuard ? { agentManageGuard: opts.agentManageGuard } : {} })
234734
235208
  };
234735
235209
  }
234736
- var import_node_crypto55;
235210
+ var import_node_crypto56;
234737
235211
  var init_actors = __esm({
234738
235212
  "../server/src/domains/actors/index.ts"() {
234739
235213
  "use strict";
234740
- import_node_crypto55 = require("node:crypto");
235214
+ import_node_crypto56 = require("node:crypto");
234741
235215
  init_service5();
234742
235216
  init_routes3();
234743
235217
  init_service5();
@@ -236426,6 +236900,13 @@ function isPendingActor(id) {
236426
236900
  function isAgentActor(id) {
236427
236901
  return typeof id === "string" && id.startsWith("actor:agent:");
236428
236902
  }
236903
+ function isBlockingBackpressure(i) {
236904
+ if (i.kind === "gap" || i.kind === "escalation") return false;
236905
+ if (i.intent === "advisory") return false;
236906
+ const from = i.raisedByNodeId;
236907
+ const about = i.aboutNodeId;
236908
+ return !!from && !!about && from !== about;
236909
+ }
236429
236910
  function isDispatchLayerFailure(detail) {
236430
236911
  const reason = detail?.reason;
236431
236912
  return typeof reason === "string" && reason.startsWith("dispatch ");
@@ -236650,12 +237131,8 @@ function buildWorkorderActivity(input) {
236650
237131
  } else if (r.decision === "approved" || r.decision === "completed") h.to = h.to ?? r.decidedAt ?? r.at;
236651
237132
  }
236652
237133
  for (const i of snap.issues) {
236653
- if (i.kind === "gap" || i.kind === "escalation") continue;
236654
- if (i.intent === "advisory") continue;
236655
- const from = i.raisedByNodeId;
236656
- const about = i.aboutNodeId;
236657
- if (!from || !about || from === about) continue;
236658
- addHold(from, { kind: "backpressure", from: i.createdAt, to: i.resolvedAt ?? null, upstream: about });
237134
+ if (!isBlockingBackpressure(i)) continue;
237135
+ addHold(i.raisedByNodeId, { kind: "backpressure", from: i.createdAt, to: i.resolvedAt ?? null, upstream: i.aboutNodeId });
236659
237136
  }
236660
237137
  const HOLD_RANK = { paused: 0, continuationDenied: 1, continuation: 2, backpressure: 3 };
236661
237138
  const holdOf2 = (w2) => {
@@ -236675,11 +237152,50 @@ function buildWorkorderActivity(input) {
236675
237152
  };
236676
237153
  const lastStampOf = (w2) => [w2.createdAt, w2.startedAt, w2.endedAt, w2.deadAt, w2.cancelledAt].filter((t) => !!t).sort().at(-1);
236677
237154
  const concludedRootBriefNodeIds = /* @__PURE__ */ new Set();
237155
+ const blockingBackpressure = snap.issues.filter((i) => isBlockingBackpressure(i));
237156
+ const blockingBackpressureIds = new Set(blockingBackpressure.map((i) => i.id));
237157
+ const backpressureRaisedBy = /* @__PURE__ */ new Map();
237158
+ for (const i of [...blockingBackpressure].sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id))) {
237159
+ const from = i.raisedByNodeId;
237160
+ backpressureRaisedBy.set(from, [...backpressureRaisedBy.get(from) ?? [], i]);
237161
+ }
237162
+ const backpressureOnRaiserCard = /* @__PURE__ */ new Set();
237163
+ const backpressureDriving = /* @__PURE__ */ new Map();
237164
+ {
237165
+ const add = (workId, issueId) => {
237166
+ if (!issueId || !blockingBackpressureIds.has(issueId)) return;
237167
+ const list2 = backpressureDriving.get(workId) ?? [];
237168
+ if (!list2.includes(issueId)) list2.push(issueId);
237169
+ backpressureDriving.set(workId, list2);
237170
+ };
237171
+ for (const rec2 of events) {
237172
+ const ev = rec2.event;
237173
+ if (ev.kind !== "work.create" || !ev.workId) continue;
237174
+ for (const r of ev.reasons ?? []) if (r.kind === "issue") add(ev.workId, r.issueId);
237175
+ }
237176
+ for (const w2 of snap.works) for (const id of w2.frozenIssueIds ?? []) add(w2.id, id);
237177
+ }
237178
+ const backpressureTargetName = (nodeId) => nodeId && rootBriefNodeIds.has(nodeId) ? "\u4EFB\u52A1\u8BF4\u660E" : nodeName(nodeId);
237179
+ const backpressureOwnerOf = (nodeId) => {
237180
+ const assignee = nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0;
237181
+ return assignee && !isPendingActor(assignee) ? assignee : ownerId;
237182
+ };
237183
+ const backpressureRaisersOf = (workId) => {
237184
+ const ids2 = backpressureDriving.get(workId);
237185
+ if (!ids2?.length) return void 0;
237186
+ const authors = [...new Set(ids2.map((id) => issueById.get(id)?.authorActorId).filter((a) => !!a))];
237187
+ return authors.length ? authors.map(nameOf2).join("\u3001") : void 0;
237188
+ };
236678
237189
  for (const [nodeId, works] of worksOfNode) {
236679
237190
  const interrupts = [...interruptsOfNode.get(nodeId) ?? []].sort((a, b2) => a.at.localeCompare(b2.at) || a.issueId.localeCompare(b2.issueId));
236680
237191
  const trips = stopLossOfNode.get(nodeId) ?? [];
236681
237192
  const interventions = [...interventionsOfNode.get(nodeId) ?? []].sort();
236682
237193
  const humanBetween = (prev, w2) => !!prev && interventions.some((t) => t >= lastStampOf(prev) && t <= w2.createdAt);
237194
+ const upstreamRoundBetween = (prev, w2) => {
237195
+ if (!prev) return false;
237196
+ const ancestors = ancestorsOf(nodeId);
237197
+ return snap.works.some((u) => u.lane !== "reply" && ancestors.has(u.nodeId) && u.createdAt > prev.createdAt && u.createdAt <= w2.createdAt);
237198
+ };
236683
237199
  const node2 = nodeById.get(nodeId);
236684
237200
  const nodeCancelledAt = node2?.cancelledAt ?? null;
236685
237201
  const name = nodeName(nodeId);
@@ -236691,17 +237207,20 @@ function buildWorkorderActivity(input) {
236691
237207
  const st = statusOf2(w2);
236692
237208
  const nextStart = nextStartOf.get(w2.id) ?? null;
236693
237209
  const interrupt = interrupts.find((t) => t.at >= w2.createdAt && (!nextStart || t.at < nextStart));
237210
+ const roundEndedAt = w2.endedAt ?? w2.deadAt ?? w2.cancelledAt;
237211
+ const raisedHere = !w2.conclusion && !interrupt ? (backpressureRaisedBy.get(nodeId) ?? []).filter((i) => i.createdAt >= w2.createdAt && (!nextStart || i.createdAt < nextStart) && (!roundEndedAt || i.createdAt <= roundEndedAt)) : [];
237212
+ const halted = !!interrupt || raisedHere.length > 0;
236694
237213
  const tripsHere = trips.filter((t) => t.at >= w2.createdAt && (!nextStart || t.at < nextStart));
236695
237214
  const removedHere = !!nodeCancelledAt && nodeCancelledAt >= w2.createdAt && (!next || nodeCancelledAt < next.createdAt);
236696
237215
  const reassigned = !!next && next.assigneeActorId !== w2.assigneeActorId;
236697
237216
  const killedByPlan = !!(w2.deadAt || w2.cancelledAt) && !!next && (reassigned || (next.nodeVersion ?? 0) > (w2.nodeVersion ?? 0));
236698
237217
  const killedAtOfRound = w2.deadAt ?? w2.cancelledAt;
236699
- const supersededBy = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && killedAtOfRound && next && !killedByWorkorderPause(w2, killedAtOfRound) ? supersedingUpstreamVersion(nodeId, w2, killedAtOfRound, next) : void 0;
236700
- const failedRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry" || noOutputRound(w2));
237218
+ const supersededBy = !w2.conclusion && !halted && !removedHere && !killedByPlan && killedAtOfRound && next && !killedByWorkorderPause(w2, killedAtOfRound) ? supersedingUpstreamVersion(nodeId, w2, killedAtOfRound, next) : void 0;
237219
+ const failedRound = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry" || noOutputRound(w2));
236701
237220
  const foldableFailure = failedRound && !(killedAtOfRound && killedByWorkorderPause(w2, killedAtOfRound));
236702
- const ackedRound = st === "success" && !!w2.upstreamAck && !w2.conclusion && artifacts.length === 0 && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound;
236703
- const held = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound && !ackedRound ? holdOf2(w2) : void 0;
236704
- const handedOverRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound && !held && !ackedRound && st === "success" && !!w2.endedAt && !!next;
237221
+ const ackedRound = st === "success" && !!w2.upstreamAck && !w2.conclusion && artifacts.length === 0 && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound;
237222
+ const held = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound && !ackedRound ? holdOf2(w2) : void 0;
237223
+ const handedOverRound = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound && !held && !ackedRound && st === "success" && !!w2.endedAt && !!next;
236705
237224
  const refiredByFuse = tripsHere.some((t) => t.kind === "output_stall");
236706
237225
  const brokenByHuman = humanBetween(works[idx - 1], w2) && !refiredByFuse;
236707
237226
  if (foldableFailure && fold2 && !brokenByHuman) {
@@ -236714,7 +237233,7 @@ function buildWorkorderActivity(input) {
236714
237233
  if (tripsHere.length) fold2.stopLossTrips = [...fold2.stopLossTrips ?? [], ...tripsHere];
236715
237234
  continue;
236716
237235
  }
236717
- const absorb2 = fold2 && !brokenByHuman && !!w2.conclusion && fold2.executorId === w2.assigneeActorId ? fold2 : null;
237236
+ const absorb2 = fold2 && !brokenByHuman && !!w2.conclusion && fold2.executorId === w2.assigneeActorId && !upstreamRoundBetween(works[idx - 1], w2) ? fold2 : null;
236718
237237
  fold2 = null;
236719
237238
  if (handedOverRound && handoverFold && handoverFold.executorId === w2.assigneeActorId) {
236720
237239
  handoverFold.traceWorkIds.push(w2.id);
@@ -236749,6 +237268,7 @@ function buildWorkorderActivity(input) {
236749
237268
  }
236750
237269
  executionCards.push(card2);
236751
237270
  cardOfWork.set(w2.id, card2);
237271
+ if (isHumanActorId(card2.executorId) && backpressureDriving.has(w2.id)) card2.humanTrace = true;
236752
237272
  if (w2.conclusion) {
236753
237273
  card2.phase = "done";
236754
237274
  if (rootBriefNodeIds.has(nodeId)) {
@@ -236764,6 +237284,10 @@ function buildWorkorderActivity(input) {
236764
237284
  card2.status = awaits ? ACTIVITY_COPY.doneAwaitingReview(name) : ACTIVITY_COPY.done(name);
236765
237285
  }
236766
237286
  }
237287
+ const bpRaisers = backpressureRaisersOf(w2.id);
237288
+ if (bpRaisers && (card2.status === ACTIVITY_COPY.done(name) || card2.status === ACTIVITY_COPY.goalConfirmed || card2.status === ACTIVITY_COPY.goalRevised)) {
237289
+ card2.status = ACTIVITY_COPY.backpressureAddressed(bpRaisers, backpressureTargetName(nodeId));
237290
+ }
236767
237291
  } else if (interrupt) {
236768
237292
  card2.phase = "stuck";
236769
237293
  card2.segmentOpen = false;
@@ -236772,6 +237296,18 @@ function buildWorkorderActivity(input) {
236772
237296
  card2.actions = [{ kind: "open_thread", label: BUTTON.view, target: gapThreadSessionId(interrupt.authorId), variant: "quiet" }];
236773
237297
  }
236774
237298
  if (interrupt.at > card2.updatedAt) card2.updatedAt = interrupt.at;
237299
+ } else if (raisedHere.length) {
237300
+ card2.phase = "stuck";
237301
+ card2.segmentOpen = false;
237302
+ card2.awaitingHuman = false;
237303
+ const upstreams = [...new Set(raisedHere.map((i) => i.aboutNodeId))];
237304
+ const targets = upstreams.map(backpressureTargetName);
237305
+ const owners = [...new Set(upstreams.map((u) => nameOf2(backpressureOwnerOf(u))))];
237306
+ card2.status = artifacts.length ? ACTIVITY_COPY.backpressureAfterStage(name, targets, owners) : ACTIVITY_COPY.backpressure(nameOf2(card2.executorId), targets, owners);
237307
+ for (const i of raisedHere) {
237308
+ backpressureOnRaiserCard.add(i.id);
237309
+ if (i.createdAt > card2.updatedAt) card2.updatedAt = i.createdAt;
237310
+ }
236775
237311
  } else if (supersededBy) {
236776
237312
  card2.phase = "stuck";
236777
237313
  card2.segmentOpen = false;
@@ -236934,7 +237470,7 @@ function buildWorkorderActivity(input) {
236934
237470
  const forcedBy = str5(reasons.find((r) => r.kind === "forced")?.actorId);
236935
237471
  const parentWorkId = str5(workById.get(workId)?.parentWorkId);
236936
237472
  const reworkOfRejected = !!parentWorkId && workById.get(parentWorkId)?.acceptanceState === "rejected";
236937
- const status = reasonKinds.has("rework") || reworkOfRejected ? ACTIVITY_COPY.reworking(nodeName(nodeId)) : reasonKinds.has("resumed") ? ACTIVITY_COPY.resumed(nameOf2(d.executorId), nodeName(nodeId)) : reasonKinds.has("spec_changed") ? ACTIVITY_COPY.specChanged(nodeName(nodeId)) : reasonKinds.has("issue") ? ACTIVITY_COPY.issueDriven(nodeName(nodeId)) : forcedBy ? ACTIVITY_COPY.forcedRerun(nameOf2(forcedBy), nodeName(nodeId)) : ACTIVITY_COPY.working(nodeName(nodeId));
237473
+ const status = reasonKinds.has("rework") || reworkOfRejected ? ACTIVITY_COPY.reworking(nodeName(nodeId)) : reasonKinds.has("resumed") ? ACTIVITY_COPY.resumed(nameOf2(d.executorId), nodeName(nodeId)) : reasonKinds.has("spec_changed") ? ACTIVITY_COPY.specChanged(nodeName(nodeId)) : reasonKinds.has("issue") ? backpressureRaisersOf(workId) ? ACTIVITY_COPY.backpressureInProgress(backpressureRaisersOf(workId), backpressureTargetName(nodeId)) : ACTIVITY_COPY.issueDriven(nodeName(nodeId)) : forcedBy ? ACTIVITY_COPY.forcedRerun(nameOf2(forcedBy), nodeName(nodeId)) : ACTIVITY_COPY.working(nodeName(nodeId));
236938
237474
  for (const r of reasons) {
236939
237475
  if (r.kind !== "issue") continue;
236940
237476
  const issueId = str5(r.issueId);
@@ -237052,6 +237588,7 @@ function buildWorkorderActivity(input) {
237052
237588
  const raisedFrom = str5(ev.raisedByNodeId);
237053
237589
  const isBackpressure = !isEscalation && !isGap && !!raisedFrom && raisedFrom !== nodeId;
237054
237590
  if (isBackpressure) backpressureIssueIds.add(issueId);
237591
+ if (isBackpressure && backpressureOnRaiserCard.has(issueId)) break;
237055
237592
  const body2 = str5(ev.body) ?? "";
237056
237593
  if (isEscalation) {
237057
237594
  const linkedGapId = str5(ev.gapId);
@@ -237106,8 +237643,8 @@ function buildWorkorderActivity(input) {
237106
237643
  ...isBackpressure ? { segmentOpen: false, awaitingHuman: false } : {},
237107
237644
  status: isBackpressure ? ACTIVITY_COPY.backpressure(
237108
237645
  nameOf2(authorId),
237109
- nodeName(nodeId),
237110
- nameOf2((nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0) ?? ownerId)
237646
+ [backpressureTargetName(nodeId)],
237647
+ [nameOf2((nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0) ?? ownerId)]
237111
237648
  ) : ACTIVITY_COPY.commentRaised(nameOf2(authorId), nameOf2(commentTargetId ?? ownerId), nodeName(nodeId)),
237112
237649
  /* **正文一律不上卡**:缺口去缺口线、回压去产物批注、留言去留言线。
237113
237650
  此前只有缺口/回压被排除,留言仍是「正文当小状态 + 展开区再印一遍」,
@@ -237441,7 +237978,7 @@ function buildWorkorderActivity(input) {
237441
237978
  card2.phaseLabel = PHASE_LABEL.escalationPending;
237442
237979
  card2.segmentOpen = true;
237443
237980
  card2.awaitingHuman = true;
237444
- card2.status = ACTIVITY_COPY.backpressureAwaitingOwner(nameOf2(card2.executorId), nodeName(card2.nodeId));
237981
+ card2.status = ACTIVITY_COPY.backpressureAwaitingOwner(nameOf2(card2.executorId), backpressureTargetName(card2.nodeId));
237445
237982
  card2.actions = [{ kind: "open_issue", label: BUTTON.handle, target: issueId, variant: "primary" }];
237446
237983
  }
237447
237984
  for (const card2 of executionCards) {
@@ -237644,7 +238181,7 @@ function buildWorkorderActivity(input) {
237644
238181
  artifacts: d.artifacts,
237645
238182
  actions: d.actions,
237646
238183
  ...traced && d.traceAvailable ? { traceAvailable: true } : {},
237647
- ...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced ? { traceAvailable: true } : {} } : {},
238184
+ ...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced || d.humanTrace ? { traceAvailable: true } : {} } : {},
237648
238185
  .../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(traced ? runIdOfCard(d) : void 0)
237649
238186
  };
237650
238187
  });
@@ -237901,6 +238438,7 @@ var init_activity2 = __esm({
237901
238438
  heldByContinuation: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u672C\u6279\u7EED\u8DD1\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u7B49\u5F85\u6279\u51C6\u7EE7\u7EED\u3002`,
237902
238439
  heldByContinuationDenied: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u7EED\u8DD1\u7533\u8BF7\u672A\u83B7\u6279\u51C6\uFF0C\u8282\u70B9\u5DF2\u6682\u505C\uFF0C\u7B49\u5F85\u653E\u884C\u3002`,
237903
238440
  heldByWorkorderPause: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u4EFB\u52A1\u5DF2\u6682\u505C\uFF0C\u7B49\u5F85\u6062\u590D\u3002`,
238441
+ /** 只剩「交完稿、这一轮结束之后才补提」的那一档;同一轮里提的见 {@link ACTIVITY_COPY.backpressureAfterStage}(ADR 0623 补章)。 */
237904
238442
  heldByBackpressure: (node2, upstreams) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\uFF0C\u7B49\u5F85${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u5904\u7406\u4FEE\u6539\u8981\u6C42\u540E\u518D\u6536\u53E3\u3002`,
237905
238443
  /** `works.status=retry`:这一轮没跑成,等引擎重派。谁按的重试由 `plan.node_retry` 补进这句。 */
237906
238444
  awaitingRedispatch: (executor, node2) => `${executor} \u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u6210\u300A${node2}\u300B\uFF0C\u7B49\u5F85\u91CD\u6D3E\u3002`,
@@ -237953,8 +238491,27 @@ var init_activity2 = __esm({
237953
238491
  (「{X} 遇到处理不了的问题,需要 @{Y} 的协助」)——**别在别处复用**:系统代开的两种止损
237954
238492
  上报根本没有人「开口」,套这句话是假话(生产上它已经把 `actor:system` 渲染成了句子的主语)。
237955
238493
  ①「缺口卡」也删掉(F4):它和执行卡挂的是同一段运行、同一份轨迹,点哪张打开的都一样。 */
237956
- /** 回压(下游对上游提修改要求)——发起人 2026-09-08 认可的措辞。 */
237957
- backpressure: (raiser, upstream, owner) => `${raiser} \u5BF9\u300A${upstream}\u300B\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 @${owner} \u5904\u7406\u3002`,
238494
+ /**
238495
+ * 回压(下游对上游提修改要求)——发起人 2026-09-08 认可的措辞。
238496
+ *
238497
+ * 2026-09-23 起它是**提出方那一轮执行卡的结局句**(奕璇定,与缺口 F4 同一做法):提要求这件事就发生在
238498
+ * 那一轮里,不再单独出一张 `issue:` 卡。同一轮对几个上游提了要求时并成一句(《A》、《B》 / @X、@Y)。
238499
+ * 🚨 **之后一个字不改**:要求关掉了也不翻署名、不改写成「已基于…更新了一版」——那是被要求方那张卡的事。
238500
+ */
238501
+ backpressure: (raiser, upstreams, owners) => `${raiser} \u5BF9${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 ${owners.map((o) => `@${o}`).join("\u3001")} \u5904\u7406\u3002`,
238502
+ /**
238503
+ * 同上,但提出方**这一轮先交了阶段成果**(propose 了、没 conclude)、同一轮又提了修改要求(奕璇 2026-09-23 定,
238504
+ * ADR 0623 补章):此前这一档出两张卡——自己那轮「已交付,等待《X》处理修改要求后再收口」+ 一张 `issue:` 回压卡。
238505
+ * 合成这一句:「交了什么」由卡上 / 轨迹里的交付物说,句子说「停在哪、等谁」。之后同样一个字不改。
238506
+ */
238507
+ backpressureAfterStage: (node2, upstreams, owners) => `\u300A${node2}\u300B\u5DF2\u63D0\u4EA4\u9636\u6BB5\u6210\u679C\uFF0C\u5BF9${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 ${owners.map((o) => `@${o}`).join("\u3001")} \u5904\u7406\u3002`,
238508
+ /** 被要求方是 agent、正在按修改要求改的那一轮(取代通用的「正在根据反馈调整」:说清是谁提的)。 */
238509
+ backpressureInProgress: (raisers, node2) => `\u6B63\u5728\u6839\u636E ${raisers} \u7684\u4FEE\u6539\u8981\u6C42\u8C03\u6574\u300A${node2}\u300B\u3002`,
238510
+ /**
238511
+ * 被要求方按修改要求交出了新一版(agent / 真人同一句)。取代「《X》已完成」「已修订目标」——
238512
+ * 此前同一件事还另有一张回压卡收口成「{X} 已基于 {Z} 的留言更新了一版」,一件事三张卡。
238513
+ */
238514
+ backpressureAddressed: (raisers, node2) => `\u5DF2\u6309 ${raisers} \u7684\u4FEE\u6539\u8981\u6C42\u66F4\u65B0\u300A${node2}\u300B\u3002`,
237958
238515
  /**
237959
238516
  * 被回压节点的负责人是**真人**时,系统为他开的那一轮(奕璇 2026-09-22 定):这一轮不会自己跑,
237960
238517
  * 是在等他动手——不能再说「正在根据反馈调整」(`issueDriven`),那句会让一张没人在动的卡挂成「进行中」。
@@ -238113,6 +238670,26 @@ function buildWorkorderActivityTrace(input) {
238113
238670
  if (!briefing) continue;
238114
238671
  firstBriefing ??= briefing;
238115
238672
  const sources = sourcesOfWork(work, input, briefing);
238673
+ const humanBackpressureRound = isHumanActorId(work.assigneeActorId) && sources.some((src) => {
238674
+ const issue2 = src.kind === "comment" ? snap.issues.find((i) => i.id === src.issueId) : void 0;
238675
+ return !!issue2 && isBlockingBackpressure(issue2);
238676
+ });
238677
+ if (humanBackpressureRound) {
238678
+ const before = previousVersionOf(work, snap);
238679
+ works.push({
238680
+ workId,
238681
+ at: work.createdAt,
238682
+ nodeTitle: briefing.nodeTitle,
238683
+ ...briefing.spec ? { spec: briefing.spec } : {},
238684
+ sources,
238685
+ attempts: [],
238686
+ artifacts: [
238687
+ ...before ? artifactsOfWork(before, input).map((a) => ({ ...a, name: `${a.name}\uFF08\u6539\u524D\uFF09` })) : [],
238688
+ ...artifactsOfWork(work, input).map((a) => ({ ...a, name: `${a.name}\uFF08\u6539\u540E\uFF09` }))
238689
+ ]
238690
+ });
238691
+ continue;
238692
+ }
238116
238693
  const artifacts = single.attempts.at(-1)?.artifacts ?? artifactsOfWork(work, input);
238117
238694
  works.push({
238118
238695
  workId,
@@ -238186,6 +238763,13 @@ function sourcesOfWork(work, input, briefing) {
238186
238763
  ...briefing.spec ? { content: briefing.spec } : {}
238187
238764
  }];
238188
238765
  }
238766
+ function previousVersionOf(work, snap) {
238767
+ const delivered = new Set(snap.artifacts.map((a) => a.workId));
238768
+ const parentId = work.parentWorkId;
238769
+ const parent = parentId ? snap.works.find((w2) => w2.id === parentId && delivered.has(w2.id)) : void 0;
238770
+ if (parent) return parent;
238771
+ return snap.works.filter((w2) => w2.nodeId === work.nodeId && w2.id !== work.id && w2.createdAt < work.createdAt && delivered.has(w2.id)).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id)).at(-1);
238772
+ }
238189
238773
  function artifactsOfWork(work, input) {
238190
238774
  const snap = input.snap;
238191
238775
  const workId = work.id;
@@ -238344,6 +238928,7 @@ var init_activity_trace = __esm({
238344
238928
  "use strict";
238345
238929
  init_src6();
238346
238930
  init_review_activity();
238931
+ init_src();
238347
238932
  init_activity2();
238348
238933
  init_workorder_manager();
238349
238934
  init_src7();
@@ -239255,11 +239840,11 @@ function fromRow(row) {
239255
239840
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
239256
239841
  return r;
239257
239842
  }
239258
- var import_node_crypto56, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
239843
+ var import_node_crypto57, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
239259
239844
  var init_notification_store = __esm({
239260
239845
  "../server/src/domains/collab/notification-store.ts"() {
239261
239846
  "use strict";
239262
- import_node_crypto56 = require("node:crypto");
239847
+ import_node_crypto57 = require("node:crypto");
239263
239848
  import_node_fs16 = __toESM(require("node:fs"), 1);
239264
239849
  DEFAULT_PAGE = 50;
239265
239850
  MAX_PAGE = 200;
@@ -239292,7 +239877,7 @@ var init_notification_store = __esm({
239292
239877
  async insert(n) {
239293
239878
  const existing = this.findLive(n.recipient, n.sourceKey, n.category);
239294
239879
  if (existing) return existing;
239295
- const row = { ...n, id: `ntf:${(0, import_node_crypto56.randomUUID)()}`, updatedAt: n.enteredAt };
239880
+ const row = { ...n, id: `ntf:${(0, import_node_crypto57.randomUUID)()}`, updatedAt: n.enteredAt };
239296
239881
  this.rows.set(row.id, row);
239297
239882
  this.save();
239298
239883
  return row;
@@ -239425,7 +240010,7 @@ var init_notification_store = __esm({
239425
240010
  return new _PostgresNotificationStore(sql, schema);
239426
240011
  }
239427
240012
  async insert(n) {
239428
- const id = `ntf:${(0, import_node_crypto56.randomUUID)()}`;
240013
+ const id = `ntf:${(0, import_node_crypto57.randomUUID)()}`;
239429
240014
  const r = await this.sql.query(
239430
240015
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
239431
240016
  title, workspace_title, entered_at, updated_at, status, read_at, item)
@@ -240554,13 +241139,13 @@ function nameKey(name) {
240554
241139
  function cleanName(name) {
240555
241140
  return name.trim().replace(/\s+/g, " ");
240556
241141
  }
240557
- var import_node_fs17, import_node_path21, import_node_crypto57, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
241142
+ var import_node_fs17, import_node_path21, import_node_crypto58, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
240558
241143
  var init_tags = __esm({
240559
241144
  "../server/src/domains/collab/tags.ts"() {
240560
241145
  "use strict";
240561
241146
  import_node_fs17 = __toESM(require("node:fs"), 1);
240562
241147
  import_node_path21 = __toESM(require("node:path"), 1);
240563
- import_node_crypto57 = __toESM(require("node:crypto"), 1);
241148
+ import_node_crypto58 = __toESM(require("node:crypto"), 1);
240564
241149
  SEP = "::";
240565
241150
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
240566
241151
  prefixOf = (companyId) => `${companyId}${SEP}`;
@@ -240578,7 +241163,7 @@ var init_tags = __esm({
240578
241163
  if (!name) return null;
240579
241164
  if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
240580
241165
  const tag = {
240581
- id: `tag-${import_node_crypto57.default.randomBytes(6).toString("hex")}`,
241166
+ id: `tag-${import_node_crypto58.default.randomBytes(6).toString("hex")}`,
240582
241167
  name,
240583
241168
  ...input.color?.trim() ? { color: input.color.trim() } : {},
240584
241169
  ...input.description?.trim() ? { description: input.description.trim() } : {}
@@ -241967,11 +242552,11 @@ async function isEmployedElsewhere(store, companyId, accountId) {
241967
242552
  const memberships = await store.listMembershipsForAccount(accountId);
241968
242553
  return memberships.some((m2) => m2.companyId !== companyId);
241969
242554
  }
241970
- 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;
242555
+ var import_node_crypto59, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
241971
242556
  var init_service6 = __esm({
241972
242557
  "../server/src/domains/companies/service.ts"() {
241973
242558
  "use strict";
241974
- import_node_crypto58 = require("node:crypto");
242559
+ import_node_crypto59 = require("node:crypto");
241975
242560
  init_console_url();
241976
242561
  ROLES = ["owner", "admin", "member"];
241977
242562
  INVITABLE_ROLES = ["admin", "member"];
@@ -242070,9 +242655,13 @@ var init_service6 = __esm({
242070
242655
  await this.store.updateCompany(id, { name });
242071
242656
  return { ...company, name };
242072
242657
  }
242073
- /** 设置/清除公司头像引用("blob:<哈希>";null = 回到默认首字母标识)。 */
242658
+ /**
242659
+ * 设置/清除公司头像引用("blob:<哈希>";null = 回到默认首字母标识)。
242660
+ * 权限(本组织 owner/admin)由路由层 `requireOwnerOrAdmin` 强制;这里只守「组织还可用」
242661
+ * ——与本域其余写面同一条判据(D8.2:闸不只长在路由上),解散之后 404 COMPANY_NOT_FOUND。
242662
+ */
242074
242663
  async setAvatar(id, ref2) {
242075
- const company = await this.requireCompany(id);
242664
+ const company = await this.requireActiveCompany(id);
242076
242665
  await this.store.updateCompany(id, { avatar: ref2 });
242077
242666
  const next = ref2 === null ? (({ avatar: _drop, ...rest }) => rest)(company) : { ...company, avatar: ref2 };
242078
242667
  return next;
@@ -242086,7 +242675,7 @@ var init_service6 = __esm({
242086
242675
  const existing = await this.store.getAccountByEmail(normalized4);
242087
242676
  if (existing) return existing;
242088
242677
  const account = {
242089
- id: `actor:human:${(0, import_node_crypto58.randomUUID)()}`,
242678
+ id: `actor:human:${(0, import_node_crypto59.randomUUID)()}`,
242090
242679
  email: normalized4,
242091
242680
  name: name ?? normalized4,
242092
242681
  status: "active"
@@ -242484,7 +243073,7 @@ var init_service6 = __esm({
242484
243073
  }
242485
243074
  const nowMs = Date.parse(this.now());
242486
243075
  const invitation = {
242487
- id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
243076
+ id: `invitation:${(0, import_node_crypto59.randomUUID)()}`,
242488
243077
  companyId,
242489
243078
  email: mail,
242490
243079
  role,
@@ -242615,7 +243204,7 @@ var init_service6 = __esm({
242615
243204
  const nowMs = Date.parse(this.now());
242616
243205
  const link = {
242617
243206
  companyId,
242618
- token: (0, import_node_crypto58.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
243207
+ token: (0, import_node_crypto59.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
242619
243208
  role: INVITE_LINK_ROLE,
242620
243209
  createdAt: new Date(nowMs).toISOString(),
242621
243210
  expiresAt: new Date(nowMs + INVITE_LINK_TTL_MS).toISOString(),
@@ -242672,7 +243261,7 @@ var init_service6 = __esm({
242672
243261
  const reusable = existing.find((inv) => !this.isExpired(inv));
242673
243262
  if (reusable) return reusable;
242674
243263
  const invitation = {
242675
- id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
243264
+ id: `invitation:${(0, import_node_crypto59.randomUUID)()}`,
242676
243265
  companyId: link.companyId,
242677
243266
  email: mail,
242678
243267
  role: link.role,
@@ -242718,9 +243307,9 @@ var init_service6 = __esm({
242718
243307
  * 登录身份查自己那条 membership;不是成员 → 404。
242719
243308
  */
242720
243309
  async completeMemberOnboarding(companyId, accountId) {
242721
- await this.requireCompany(companyId);
242722
243310
  const member = await this.store.getMember(companyId, accountId);
242723
243311
  if (!member) throw new CompanyError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${accountId}`);
243312
+ await this.requireActiveCompany(companyId);
242724
243313
  if (member.onboardingCompletedAt) return member;
242725
243314
  const next = { ...member, onboardingCompletedAt: this.now() };
242726
243315
  await this.store.upsertMember(next);
@@ -242744,9 +243333,9 @@ var init_service6 = __esm({
242744
243333
  async setMemberOnboardingStep(companyId, accountId, step) {
242745
243334
  const trimmed = step?.trim();
242746
243335
  if (!trimmed) throw new CompanyError(400, "BAD_REQUEST", "step \u4E0D\u80FD\u4E3A\u7A7A");
242747
- await this.requireCompany(companyId);
242748
243336
  const member = await this.store.getMember(companyId, accountId);
242749
243337
  if (!member) throw new CompanyError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${accountId}`);
243338
+ await this.requireActiveCompany(companyId);
242750
243339
  const next = { ...member, onboardingStep: trimmed };
242751
243340
  await this.store.upsertMember(next);
242752
243341
  return next;
@@ -242906,11 +243495,11 @@ var init_service6 = __esm({
242906
243495
  });
242907
243496
 
242908
243497
  // ../server/src/domains/companies/offboarding.ts
242909
- var import_node_crypto59, OWNER_PROTECTED_MESSAGE, MemberOffboardingService;
243498
+ var import_node_crypto60, OWNER_PROTECTED_MESSAGE, MemberOffboardingService;
242910
243499
  var init_offboarding = __esm({
242911
243500
  "../server/src/domains/companies/offboarding.ts"() {
242912
243501
  "use strict";
242913
- import_node_crypto59 = require("node:crypto");
243502
+ import_node_crypto60 = require("node:crypto");
242914
243503
  init_service6();
242915
243504
  OWNER_PROTECTED_MESSAGE = "\u7EC4\u7EC7\u7BA1\u7406\u5458\u4E0D\u53EF\u88AB\u79FB\u9664\uFF0C\u53EA\u80FD\u7531\u672C\u4EBA\u8F6C\u4EA4\u6216\u89E3\u6563\u7EC4\u7EC7";
242916
243505
  MemberOffboardingService = class {
@@ -242987,7 +243576,7 @@ var init_offboarding = __esm({
242987
243576
  const { ownedAgents, inFlight } = await this.snapshot(companyId, accountId);
242988
243577
  const at2 = this.now();
242989
243578
  const started = await this.store.startMemberOffboarding({
242990
- operationId: `off:${(0, import_node_crypto59.randomUUID)()}`,
243579
+ operationId: `off:${(0, import_node_crypto60.randomUUID)()}`,
242991
243580
  companyId,
242992
243581
  accountId,
242993
243582
  initiatedBy,
@@ -243219,6 +243808,13 @@ function companiesDomain(opts) {
243219
243808
  await requireActiveCompany(companyId);
243220
243809
  return me;
243221
243810
  };
243811
+ const requireActiveSelfMember = async (req, companyId) => {
243812
+ const me = await whoami(req);
243813
+ const member = await service.getMember(companyId, me.accountId);
243814
+ if (!member) throw new ApiError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${me.accountId}`);
243815
+ await requireActiveCompany(companyId);
243816
+ return { me, member };
243817
+ };
243222
243818
  return (router) => {
243223
243819
  router.get("/api/companies", async (req) => {
243224
243820
  const me = await whoami(req);
@@ -243268,13 +243864,17 @@ function companiesDomain(opts) {
243268
243864
  return { status: 201, body: company };
243269
243865
  });
243270
243866
  router.post("/api/companies/:id/avatar", async (req) => {
243867
+ const cid = req.params.id;
243868
+ await requireOwnerOrAdmin(req, cid);
243271
243869
  if (!blobs) throw new ApiError(501, "NOT_IMPLEMENTED", "blob \u5B58\u50A8\u672A\u63A5\u5165");
243272
243870
  const ref2 = await storeImageBlob(blobs, req.body?.data);
243273
- const company = await service.setAvatar(req.params.id, ref2).catch(mapErr);
243871
+ const company = await service.setAvatar(cid, ref2).catch(mapErr);
243274
243872
  return { status: 201, body: { avatar: company.avatar } };
243275
243873
  });
243276
243874
  router.delete("/api/companies/:id/avatar", async (req) => {
243277
- await service.setAvatar(req.params.id, null).catch(mapErr);
243875
+ const cid = req.params.id;
243876
+ await requireOwnerOrAdmin(req, cid);
243877
+ await service.setAvatar(cid, null).catch(mapErr);
243278
243878
  return { status: 200, body: { ok: true } };
243279
243879
  });
243280
243880
  router.get("/api/companies/:id/members", async (req) => {
@@ -243284,23 +243884,23 @@ function companiesDomain(opts) {
243284
243884
  return { status: 200, body: { items } };
243285
243885
  });
243286
243886
  router.get("/api/companies/:id/members/me", async (req) => {
243287
- const me = await whoami(req);
243288
- const member = await service.getMember(req.params.id, me.accountId);
243289
- if (!member) throw new ApiError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${me.accountId}`);
243887
+ const { member } = await requireActiveSelfMember(req, req.params.id);
243290
243888
  return { status: 200, body: member };
243291
243889
  });
243292
243890
  router.post("/api/companies/:id/members/me/onboarding-complete", async (req) => {
243293
- const me = await whoami(req);
243294
- const member = await service.completeMemberOnboarding(req.params.id, me.accountId).catch(mapErr);
243891
+ const cid = req.params.id;
243892
+ const { me } = await requireActiveSelfMember(req, cid);
243893
+ const member = await service.completeMemberOnboarding(cid, me.accountId).catch(mapErr);
243295
243894
  return { status: 200, body: member };
243296
243895
  });
243297
243896
  router.post("/api/companies/:id/members/me/onboarding-step", async (req) => {
243298
- const me = await whoami(req);
243897
+ const cid = req.params.id;
243898
+ const { me } = await requireActiveSelfMember(req, cid);
243299
243899
  const body2 = req.body ?? {};
243300
243900
  if (typeof body2.step !== "string") {
243301
243901
  throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 step: string");
243302
243902
  }
243303
- const member = await service.setMemberOnboardingStep(req.params.id, me.accountId, body2.step).catch(mapErr);
243903
+ const member = await service.setMemberOnboardingStep(cid, me.accountId, body2.step).catch(mapErr);
243304
243904
  return { status: 200, body: member };
243305
243905
  });
243306
243906
  router.patch("/api/companies/:id/members/:accountId", async (req) => {
@@ -243598,18 +244198,18 @@ var init_routes6 = __esm({
243598
244198
 
243599
244199
  // ../server/src/domains/companies/agent-credentials.ts
243600
244200
  function hashAgentToken(token, secret) {
243601
- return HASH_PREFIX + (0, import_node_crypto60.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
244201
+ return HASH_PREFIX + (0, import_node_crypto61.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
243602
244202
  }
243603
244203
  function safeEqual(a, b2) {
243604
244204
  const ba = Buffer.from(a);
243605
244205
  const bb = Buffer.from(b2);
243606
- return ba.length === bb.length && (0, import_node_crypto60.timingSafeEqual)(ba, bb);
244206
+ return ba.length === bb.length && (0, import_node_crypto61.timingSafeEqual)(ba, bb);
243607
244207
  }
243608
- var import_node_crypto60, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
244208
+ var import_node_crypto61, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
243609
244209
  var init_agent_credentials = __esm({
243610
244210
  "../server/src/domains/companies/agent-credentials.ts"() {
243611
244211
  "use strict";
243612
- import_node_crypto60 = require("node:crypto");
244212
+ import_node_crypto61 = require("node:crypto");
243613
244213
  init_service6();
243614
244214
  HASH_PREFIX = "ac1$";
243615
244215
  TOKEN_PREFIX = "oak_";
@@ -243634,7 +244234,7 @@ var init_agent_credentials = __esm({
243634
244234
  if (employee.status !== "active") {
243635
244235
  throw new CompanyError(409, "AGENT_LEFT", `\u6570\u5B57\u5458\u5DE5\u5DF2\u79BB\u5F00\uFF0C\u4E0D\u80FD\u7B7E\u53D1\u51ED\u8BC1\uFF1A${agentId}`);
243636
244236
  }
243637
- const token = `${TOKEN_PREFIX}${(0, import_node_crypto60.randomBytes)(32).toString("base64url")}`;
244237
+ const token = `${TOKEN_PREFIX}${(0, import_node_crypto61.randomBytes)(32).toString("base64url")}`;
243638
244238
  const credential = {
243639
244239
  tokenHash: hashAgentToken(token, this.secret),
243640
244240
  companyId,
@@ -244146,7 +244746,7 @@ function gatewayInstanceSlug(gatewayUrl) {
244146
244746
  }
244147
244747
  function connectInstanceName(gatewayUrl, token, companyId) {
244148
244748
  const scope2 = companyId?.trim();
244149
- 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";
244749
+ const suffix = scope2 ? `c${(0, import_node_crypto62.createHash)("sha256").update(`${gatewayUrl}\0${scope2}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
244150
244750
  const base = gatewayInstanceSlug(gatewayUrl);
244151
244751
  return base.slice(0, Math.max(1, 64 - suffix.length - 1)) + "-" + suffix;
244152
244752
  }
@@ -244475,12 +245075,12 @@ function buildNpxCmd(p2) {
244475
245075
  const prefix = daemonNpmPrefix();
244476
245076
  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)}`;
244477
245077
  }
244478
- var import_node_crypto61;
245078
+ var import_node_crypto62;
244479
245079
  var init_connect_script = __esm({
244480
245080
  "../server/src/domains/nodes/connect-script.ts"() {
244481
245081
  "use strict";
244482
245082
  init_src();
244483
- import_node_crypto61 = require("node:crypto");
245083
+ import_node_crypto62 = require("node:crypto");
244484
245084
  }
244485
245085
  });
244486
245086
 
@@ -244940,7 +245540,7 @@ function nodesDomain(deps) {
244940
245540
  if (!daemon.meta.adapters.some((kind) => QUOTA_CAPABLE_KINDS.has(kind))) {
244941
245541
  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" };
244942
245542
  }
244943
- const requestId = (0, import_node_crypto62.randomUUID)();
245543
+ const requestId = (0, import_node_crypto63.randomUUID)();
244944
245544
  const task = new Promise((resolve12) => {
244945
245545
  const timer = setTimeout(() => {
244946
245546
  quotaRefreshWaiters.delete(requestId);
@@ -245316,7 +245916,7 @@ function nodesDomain(deps) {
245316
245916
  }
245317
245917
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
245318
245918
  } else {
245319
- nodeId = `node-${(0, import_node_crypto62.randomUUID)().slice(0, 8)}`;
245919
+ nodeId = `node-${(0, import_node_crypto63.randomUUID)().slice(0, 8)}`;
245320
245920
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
245321
245921
  }
245322
245922
  const existingNode = await deps.nodeStore.getNode(nodeId);
@@ -245582,16 +246182,16 @@ function normalizeGatewayUrl(raw) {
245582
246182
  }
245583
246183
  }
245584
246184
  function newNodeId() {
245585
- return "node-" + (0, import_node_crypto62.randomUUID)().replace(/-/g, "").slice(0, 12);
246185
+ return "node-" + (0, import_node_crypto63.randomUUID)().replace(/-/g, "").slice(0, 12);
245586
246186
  }
245587
- var import_node_crypto62, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
246187
+ var import_node_crypto63, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
245588
246188
  var init_routes7 = __esm({
245589
246189
  "../server/src/domains/nodes/routes.ts"() {
245590
246190
  "use strict";
245591
246191
  init_src();
245592
246192
  init_faults();
245593
246193
  init_runtime_status();
245594
- import_node_crypto62 = require("node:crypto");
246194
+ import_node_crypto63 = require("node:crypto");
245595
246195
  import_node_fs18 = require("node:fs");
245596
246196
  import_node_url6 = require("node:url");
245597
246197
  import_node_path22 = require("node:path");
@@ -247258,7 +247858,7 @@ function createBundle(root, target, input) {
247258
247858
  }
247259
247859
  }
247260
247860
  const bytes2 = fs29.statSync(absBundle).size;
247261
- const digest = (0, import_node_crypto63.createHash)("sha256").update(fs29.readFileSync(absBundle)).digest("hex");
247861
+ const digest = (0, import_node_crypto64.createHash)("sha256").update(fs29.readFileSync(absBundle)).digest("hex");
247262
247862
  const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
247263
247863
  return {
247264
247864
  absoluteBundlePath: absBundle,
@@ -247354,12 +247954,12 @@ function classifyGitError(fallback, err, where) {
247354
247954
  function oneLine2(msg) {
247355
247955
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
247356
247956
  }
247357
- var import_node_child_process17, import_node_crypto63, fs29, os8, path26, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
247957
+ var import_node_child_process17, import_node_crypto64, fs29, os8, path26, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
247358
247958
  var init_readonly_bundle = __esm({
247359
247959
  "../server/src/git/readonly-bundle.ts"() {
247360
247960
  "use strict";
247361
247961
  import_node_child_process17 = require("node:child_process");
247362
- import_node_crypto63 = require("node:crypto");
247962
+ import_node_crypto64 = require("node:crypto");
247363
247963
  fs29 = __toESM(require("node:fs"), 1);
247364
247964
  os8 = __toESM(require("node:os"), 1);
247365
247965
  path26 = __toESM(require("node:path"), 1);
@@ -247380,7 +247980,7 @@ var init_readonly_bundle = __esm({
247380
247980
  // ../server/src/domains/collab/create-seeded-workorder.ts
247381
247981
  function makeSeededWorkorderCreator(deps) {
247382
247982
  const locks = createKeyedMutex();
247383
- const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto64.randomUUID)().slice(0, 8)}`);
247983
+ const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto65.randomUUID)().slice(0, 8)}`);
247384
247984
  const create = async (input) => {
247385
247985
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
247386
247986
  const kernel = company?.kernel ?? deps.kernel;
@@ -247467,7 +248067,7 @@ function makeSeededWorkorderCreator(deps) {
247467
248067
  return await finishCreation({ workspace, rootArtifactId: planned.rootArtifactId, spawned: planned.spawned });
247468
248068
  };
247469
248069
  return async (input) => {
247470
- const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto64.randomUUID)()}`);
248070
+ const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto65.randomUUID)()}`);
247471
248071
  try {
247472
248072
  return await create(input);
247473
248073
  } finally {
@@ -247476,7 +248076,7 @@ function makeSeededWorkorderCreator(deps) {
247476
248076
  };
247477
248077
  }
247478
248078
  function workspaceForKey(key2) {
247479
- return `ws:wo-${(0, import_node_crypto64.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
248079
+ return `ws:wo-${(0, import_node_crypto65.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
247480
248080
  }
247481
248081
  function existingWorkorder(snapshot, workspace, actor) {
247482
248082
  if (!snapshot?.nodes.length) return null;
@@ -247488,11 +248088,11 @@ function existingWorkorder(snapshot, workspace, actor) {
247488
248088
  spawned: snapshot.nodes.map((node2) => ({ id: node2.id, type: node2.type, owner: node2.assigneeActorId ?? actor }))
247489
248089
  };
247490
248090
  }
247491
- var import_node_crypto64;
248091
+ var import_node_crypto65;
247492
248092
  var init_create_seeded_workorder = __esm({
247493
248093
  "../server/src/domains/collab/create-seeded-workorder.ts"() {
247494
248094
  "use strict";
247495
- import_node_crypto64 = require("node:crypto");
248095
+ import_node_crypto65 = require("node:crypto");
247496
248096
  init_uncategorized_project();
247497
248097
  init_src();
247498
248098
  init_planner();
@@ -247718,8 +248318,16 @@ var init_service8 = __esm({
247718
248318
  errorCode: input.errorCode ?? null,
247719
248319
  errorMessage: input.errorMessage ?? null,
247720
248320
  usage: input.usage ?? null,
247721
- effectiveModel: input.effectiveModel ?? run.effectiveModel ?? null,
247722
- modelSource: input.modelSource ?? run.modelSource ?? null,
248321
+ // 模型那两格按取值优先级挑(上游诊断 ④),不是无条件「收尾值优先」:`input` 里可能只剩
248322
+ // 请求值(claude 一条 init 都没吐就退出、ACP `set_model` 被接受但运行时从没回报),
248323
+ // 而 run 行上可能已经由运行中检查点补上了运行时回报的**实际值**——请求值顶掉实际值,
248324
+ // 这一轮就永远记在一个没跑过的型号头上(PR #2213 评审阻断项 1)。同档以收尾值为准
248325
+ // (`replace`):收尾只写一次,且它是这一轮解析链的最终结论。
248326
+ ...preferRunModel(
248327
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
248328
+ { effectiveModel: input.effectiveModel, modelSource: input.modelSource },
248329
+ "replace"
248330
+ ) ?? { effectiveModel: run.effectiveModel ?? null, modelSource: run.modelSource ?? null },
247723
248331
  transcriptRef: input.transcriptRef ?? null
247724
248332
  };
247725
248333
  const updated = await this.store.updateRun(id, patch);
@@ -248995,12 +249603,12 @@ function stateFromAttempt(attempt, handoff, checkpoint, now, stalledAfterMs, ope
248995
249603
  if (attempt.status === "no-output") return handoff?.unresolvedIssues.length ? "blocked" : "no_change";
248996
249604
  return "failed";
248997
249605
  }
248998
- var import_node_crypto65, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
249606
+ var import_node_crypto66, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
248999
249607
  var init_service9 = __esm({
249000
249608
  "../server/src/domains/execution-continuity/service.ts"() {
249001
249609
  "use strict";
249002
249610
  init_src();
249003
- import_node_crypto65 = require("node:crypto");
249611
+ import_node_crypto66 = require("node:crypto");
249004
249612
  init_src7();
249005
249613
  BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
249006
249614
  HANDOFF_OUTCOMES2 = /* @__PURE__ */ new Set(["succeeded", "no-output", "failed", "cancelled", "timeout", "orphaned"]);
@@ -249231,7 +249839,7 @@ var init_service9 = __esm({
249231
249839
  if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
249232
249840
  const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
249233
249841
  if (!injectedResume) return null;
249234
- const actualHash = (0, import_node_crypto65.createHash)("sha256").update(injectedResume).digest("hex");
249842
+ const actualHash = (0, import_node_crypto66.createHash)("sha256").update(injectedResume).digest("hex");
249235
249843
  if (actualHash !== expectedHash) return null;
249236
249844
  const recoverySnapshot = parseResumePackSnapshot(injectedResume);
249237
249845
  if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
@@ -250413,7 +251021,7 @@ function createChatSessionsDomain(opts) {
250413
251021
  if (!text7.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
250414
251022
  const now = Date.now();
250415
251023
  sweepDrafts(now);
250416
- const id = `cd_${(0, import_node_crypto66.randomUUID)()}`;
251024
+ const id = `cd_${(0, import_node_crypto67.randomUUID)()}`;
250417
251025
  drafts.set(id, { text: text7, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
250418
251026
  return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
250419
251027
  });
@@ -250490,7 +251098,7 @@ function createChatSessionsDomain(opts) {
250490
251098
  const runtimeId = agentDefault.binding?.nodeId ?? "unknown";
250491
251099
  const now = (/* @__PURE__ */ new Date()).toISOString();
250492
251100
  const session = {
250493
- id: (0, import_node_crypto66.randomUUID)(),
251101
+ id: (0, import_node_crypto67.randomUUID)(),
250494
251102
  humanActorId: req.auth.actor,
250495
251103
  // 注:这里**刻意不写 companyId**。新会话要不要带公司归属,按 ws:wo-6d588f8e 的 ADR
250496
251104
  // (每公司 schema、最终移除表内 company 列)统一定,不在本节点自行发明。
@@ -250625,7 +251233,7 @@ function createChatSessionsDomain(opts) {
250625
251233
  if (body2.role !== "user" && body2.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
250626
251234
  const role = body2.role;
250627
251235
  const msg = await store2.appendMessage({
250628
- id: (0, import_node_crypto66.randomUUID)(),
251236
+ id: (0, import_node_crypto67.randomUUID)(),
250629
251237
  sessionId: req.params.id,
250630
251238
  role,
250631
251239
  content: role === "user" ? stripInjectedChatContext(body2.content) : body2.content,
@@ -250667,7 +251275,7 @@ function createChatSessionsDomain(opts) {
250667
251275
  const known = key2 ? cur.find((x2) => x2.clientKey === key2) : void 0;
250668
251276
  if (!known && cur.length >= QUEUE_MAX) throw new ApiError(409, "QUEUE_FULL", `\u961F\u5217\u5DF2\u6EE1\uFF08\u4E0A\u9650 ${QUEUE_MAX} \u6761\uFF09`);
250669
251277
  const item = await store2.enqueuePendingMessage({
250670
- id: (0, import_node_crypto66.randomUUID)(),
251278
+ id: (0, import_node_crypto67.randomUUID)(),
250671
251279
  chatSessionId: sid,
250672
251280
  text: text7,
250673
251281
  ...attachments.length ? { attachments } : {},
@@ -251520,12 +252128,12 @@ async function legacySendFailureCards(entries, deps) {
251520
252128
  }
251521
252129
  return out;
251522
252130
  }
251523
- 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;
252131
+ var import_node_crypto67, DEFAULT_SNAPSHOT_ITEMS, INFLIGHT_INSERT_SETTLE_WAIT_MS, MAX_SNAPSHOT_ITEMS, PROJECTED_CARD_LIMIT, SUBMISSION_NOT_DELIVERED_NOTE, SUBMISSION_UNCONFIRMED_NOTE, LEGACY_CARD_LIMIT;
251524
252132
  var init_chat_sessions2 = __esm({
251525
252133
  "../server/src/domains/chat-sessions/index.ts"() {
251526
252134
  "use strict";
251527
252135
  init_file_limits2();
251528
- import_node_crypto66 = require("node:crypto");
252136
+ import_node_crypto67 = require("node:crypto");
251529
252137
  init_src();
251530
252138
  init_chat_session();
251531
252139
  init_persistent_files();
@@ -252985,7 +253593,7 @@ function busyError(chatSessionId, blockedBy) {
252985
253593
  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`;
252986
253594
  }
252987
253595
  function genWebhookToken() {
252988
- return `owt_${(0, import_node_crypto67.randomBytes)(32).toString("base64url")}`;
253596
+ return `owt_${(0, import_node_crypto68.randomBytes)(32).toString("base64url")}`;
252989
253597
  }
252990
253598
  function validateTitleTemplate(tpl) {
252991
253599
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -253041,11 +253649,11 @@ function appendSourceNote(brief, automation, source, atIso) {
253041
253649
  ---
253042
253650
  \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`;
253043
253651
  }
253044
- 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;
253652
+ var import_node_crypto68, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
253045
253653
  var init_service10 = __esm({
253046
253654
  "../server/src/domains/automations/service.ts"() {
253047
253655
  "use strict";
253048
- import_node_crypto67 = require("node:crypto");
253656
+ import_node_crypto68 = require("node:crypto");
253049
253657
  init_src();
253050
253658
  init_registry3();
253051
253659
  init_attribution();
@@ -253859,11 +254467,11 @@ function verifySignature(rawBody, headers, secret) {
253859
254467
  if (!secret) return "not_required";
253860
254468
  const given = header1(headers, "x-hub-signature-256");
253861
254469
  if (!given) return "missing";
253862
- const expected = `sha256=${(0, import_node_crypto68.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
254470
+ const expected = `sha256=${(0, import_node_crypto69.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
253863
254471
  const a = Buffer.from(given);
253864
254472
  const b2 = Buffer.from(expected);
253865
254473
  if (a.length !== b2.length) return "invalid";
253866
- return (0, import_node_crypto68.timingSafeEqual)(a, b2) ? "valid" : "invalid";
254474
+ return (0, import_node_crypto69.timingSafeEqual)(a, b2) ? "valid" : "invalid";
253867
254475
  }
253868
254476
  function eventAllowed(filters, envelope) {
253869
254477
  if (!filters || filters.length === 0) return true;
@@ -253893,11 +254501,11 @@ function splitEvent(envelope) {
253893
254501
  }
253894
254502
  return { eventName, actionCandidates: candidates };
253895
254503
  }
253896
- var import_node_crypto68, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
254504
+ var import_node_crypto69, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
253897
254505
  var init_webhook = __esm({
253898
254506
  "../server/src/domains/automations/webhook.ts"() {
253899
254507
  "use strict";
253900
- import_node_crypto68 = require("node:crypto");
254508
+ import_node_crypto69 = require("node:crypto");
253901
254509
  MAX_BODY_BYTES = 256 * 1024;
253902
254510
  AutomationWebhookService = class {
253903
254511
  store;
@@ -255049,7 +255657,7 @@ function playbooksDomain(opts) {
255049
255657
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
255050
255658
  try {
255051
255659
  await opts.audit?.({
255052
- id: `reg_${(0, import_node_crypto69.randomUUID)()}`,
255660
+ id: `reg_${(0, import_node_crypto70.randomUUID)()}`,
255053
255661
  actor: req.auth.actor,
255054
255662
  kind: "registry_change",
255055
255663
  target: `playbook:${ref2}#${nodeKey}`,
@@ -255066,11 +255674,11 @@ function playbooksDomain(opts) {
255066
255674
  });
255067
255675
  };
255068
255676
  }
255069
- var import_node_crypto69;
255677
+ var import_node_crypto70;
255070
255678
  var init_routes12 = __esm({
255071
255679
  "../server/src/domains/playbooks/routes.ts"() {
255072
255680
  "use strict";
255073
- import_node_crypto69 = require("node:crypto");
255681
+ import_node_crypto70 = require("node:crypto");
255074
255682
  init_router();
255075
255683
  init_registry3();
255076
255684
  init_planner();
@@ -255199,17 +255807,17 @@ function completionFingerprint(workspace, members) {
255199
255807
  workspace,
255200
255808
  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 }))
255201
255809
  });
255202
- return (0, import_node_crypto70.createHash)("sha256").update(canonical2).digest("hex");
255810
+ return (0, import_node_crypto71.createHash)("sha256").update(canonical2).digest("hex");
255203
255811
  }
255204
255812
  function eventConsumptionKey(args) {
255205
255813
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
255206
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto70.createHash)("sha256").update(raw).digest("hex")}`;
255814
+ return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto71.createHash)("sha256").update(raw).digest("hex")}`;
255207
255815
  }
255208
- var import_node_crypto70, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
255816
+ var import_node_crypto71, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
255209
255817
  var init_project_event_poller = __esm({
255210
255818
  "../server/src/automations/project-event-poller.ts"() {
255211
255819
  "use strict";
255212
- import_node_crypto70 = require("node:crypto");
255820
+ import_node_crypto71 = require("node:crypto");
255213
255821
  EVENT_KEY_VERSION = "v1";
255214
255822
  EMPTY_METRICS = () => ({
255215
255823
  scanDurationMs: 0,
@@ -255419,7 +256027,7 @@ function queryInputReceipt(hub, nodeId, dispatchId, clientMessageKey, pending, t
255419
256027
  const viaSession = hub.hasSession?.(dispatchId) === true;
255420
256028
  const supported = viaSession ? hub.supportsInputReceiptQuery?.(dispatchId) === true : daemonMetaOf(hub, nodeId)?.supportsInputReceiptQuery === true;
255421
256029
  if (!supported) return Promise.resolve({ status: "unknown", reason: "unsupported" });
255422
- const requestId = (0, import_node_crypto71.randomUUID)();
256030
+ const requestId = (0, import_node_crypto72.randomUUID)();
255423
256031
  return new Promise((resolve12) => {
255424
256032
  const timer = setTimeout(() => {
255425
256033
  pending.delete(requestId);
@@ -255481,7 +256089,7 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
255481
256089
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
255482
256090
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
255483
256091
  }
255484
- const requestId = (0, import_node_crypto71.randomUUID)();
256092
+ const requestId = (0, import_node_crypto72.randomUUID)();
255485
256093
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
255486
256094
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
255487
256095
  return new Promise((resolve12) => {
@@ -255494,11 +256102,11 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
255494
256102
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve12, timer });
255495
256103
  });
255496
256104
  }
255497
- 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;
256105
+ var import_node_crypto72, 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;
255498
256106
  var init_daemon_adapter = __esm({
255499
256107
  "../server/src/daemon-adapter.ts"() {
255500
256108
  "use strict";
255501
- import_node_crypto71 = require("node:crypto");
256109
+ import_node_crypto72 = require("node:crypto");
255502
256110
  init_src();
255503
256111
  STASH_TTL_MS = 5 * 6e4;
255504
256112
  STASH_MAX_FRAMES_PER_ID = 1e4;
@@ -255938,7 +256546,7 @@ var init_daemon_adapter = __esm({
255938
256546
  async spawn(job) {
255939
256547
  const nodeId = job.binding?.nodeId;
255940
256548
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
255941
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto71.randomUUID)()}`;
256549
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto72.randomUUID)()}`;
255942
256550
  if (job.dispatchId && this.pending.has(dispatchId)) {
255943
256551
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
255944
256552
  }
@@ -256194,7 +256802,7 @@ var init_daemon_adapter = __esm({
256194
256802
  return daemon?.meta.supportsWorkdirWalk === true;
256195
256803
  }
256196
256804
  request(nodeId, buildFrame) {
256197
- const requestId = (0, import_node_crypto71.randomUUID)();
256805
+ const requestId = (0, import_node_crypto72.randomUUID)();
256198
256806
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
256199
256807
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
256200
256808
  return new Promise((resolve12) => {
@@ -256302,13 +256910,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
256302
256910
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
256303
256911
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
256304
256912
  }
256305
- var import_node_crypto72, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
256913
+ var import_node_crypto73, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
256306
256914
  var init_engine = __esm({
256307
256915
  "../server/src/mirror/engine.ts"() {
256308
256916
  "use strict";
256309
- import_node_crypto72 = require("node:crypto");
256917
+ import_node_crypto73 = require("node:crypto");
256310
256918
  init_src();
256311
- sha = (s2) => (0, import_node_crypto72.createHash)("sha256").update(s2, "utf8").digest("hex");
256919
+ sha = (s2) => (0, import_node_crypto73.createHash)("sha256").update(s2, "utf8").digest("hex");
256312
256920
  enc2 = (s2) => new TextEncoder().encode(s2);
256313
256921
  dec = (b2) => new TextDecoder().decode(b2);
256314
256922
  MirrorEngine = class {
@@ -256589,11 +257197,11 @@ function humanExitCause(exit3) {
256589
257197
  const base = (exit3.reason ? label2[exit3.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit3.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
256590
257198
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
256591
257199
  }
256592
- 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;
257200
+ var import_node_crypto74, 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;
256593
257201
  var init_worker = __esm({
256594
257202
  "../server/src/coordinator/worker.ts"() {
256595
257203
  "use strict";
256596
- import_node_crypto73 = require("node:crypto");
257204
+ import_node_crypto74 = require("node:crypto");
256597
257205
  init_src7();
256598
257206
  init_identity();
256599
257207
  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
@@ -256863,7 +257471,7 @@ ${HIGH_RISK_COMMANDS}`;
256863
257471
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
256864
257472
  }
256865
257473
  evaluationKey(kind, artifactId, evidence) {
256866
- const fingerprint2 = (0, import_node_crypto73.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
257474
+ const fingerprint2 = (0, import_node_crypto74.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
256867
257475
  return `${kind}:${artifactId}:${fingerprint2}`;
256868
257476
  }
256869
257477
  artifactEvidence(id) {
@@ -257252,8 +257860,8 @@ ${ctx.nodeFault}
257252
257860
  const taskWithRejections = rejectedSection ? `${task}
257253
257861
 
257254
257862
  ${rejectedSection}` : task;
257255
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto73.randomUUID)();
257256
- const coordinatorRunId = `coordinate:${(0, import_node_crypto73.randomUUID)()}`;
257863
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto74.randomUUID)();
257864
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto74.randomUUID)()}`;
257257
257865
  const taskWithContext = coordinatorContext ? [
257258
257866
  taskWithRejections,
257259
257867
  ``,
@@ -257297,7 +257905,7 @@ ${rejectedSection}` : task;
257297
257905
  const startedAtMs = Date.now();
257298
257906
  let trajectoryStarted = false;
257299
257907
  if (this.deps.trajectory) {
257300
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto73.createHash)("sha256").update(content3).digest("hex")]));
257908
+ const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto74.createHash)("sha256").update(content3).digest("hex")]));
257301
257909
  const session = {
257302
257910
  runId: coordinatorRunId,
257303
257911
  runtimeSessionId,
@@ -257508,11 +258116,11 @@ var init_pg_ident = __esm({
257508
258116
  });
257509
258117
 
257510
258118
  // ../storage/src/postgres.ts
257511
- var import_node_crypto74, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
258119
+ var import_node_crypto75, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
257512
258120
  var init_postgres = __esm({
257513
258121
  "../storage/src/postgres.ts"() {
257514
258122
  "use strict";
257515
- import_node_crypto74 = require("node:crypto");
258123
+ import_node_crypto75 = require("node:crypto");
257516
258124
  init_pg_ident();
257517
258125
  init_esm();
257518
258126
  init_src();
@@ -257692,7 +258300,7 @@ var init_postgres = __esm({
257692
258300
  return new _PostgresBlobStore(pool, schema);
257693
258301
  }
257694
258302
  async put(bytes2) {
257695
- const hash3 = (0, import_node_crypto74.createHash)("sha256").update(bytes2).digest("hex");
258303
+ const hash3 = (0, import_node_crypto75.createHash)("sha256").update(bytes2).digest("hex");
257696
258304
  await this.pool.query(
257697
258305
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
257698
258306
  [hash3, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -257740,7 +258348,7 @@ function stageAtLeast(stage, min2) {
257740
258348
  return STAGES.indexOf(stage) >= STAGES.indexOf(min2);
257741
258349
  }
257742
258350
  function newVariableId() {
257743
- return `var_${(0, import_node_crypto75.randomUUID)()}`;
258351
+ return `var_${(0, import_node_crypto76.randomUUID)()}`;
257744
258352
  }
257745
258353
  async function declareRecordsAware(db) {
257746
258354
  await db.query(`SELECT set_config('${RECORDS_AWARE_SETTING}', 'on', true)`);
@@ -258096,11 +258704,11 @@ async function listBindingTargetIds(db, s2, kind) {
258096
258704
  [kind]
258097
258705
  )).rows.map((x2) => x2.target_id);
258098
258706
  }
258099
- var import_node_crypto75, STAGES, RECORDS_AWARE_SETTING, markLockName;
258707
+ var import_node_crypto76, STAGES, RECORDS_AWARE_SETTING, markLockName;
258100
258708
  var init_variable_bindings = __esm({
258101
258709
  "../storage/src/variable-bindings.ts"() {
258102
258710
  "use strict";
258103
- import_node_crypto75 = require("node:crypto");
258711
+ import_node_crypto76 = require("node:crypto");
258104
258712
  init_src();
258105
258713
  STAGES = ["off", "dual", "read", "records"];
258106
258714
  RECORDS_AWARE_SETTING = "oasis.variable_records_aware";
@@ -268162,11 +268770,11 @@ var init_postgres_delegations = __esm({
268162
268770
  });
268163
268771
 
268164
268772
  // ../storage/src/postgres-nodes.ts
268165
- var import_node_crypto76, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
268773
+ var import_node_crypto77, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
268166
268774
  var init_postgres_nodes = __esm({
268167
268775
  "../storage/src/postgres-nodes.ts"() {
268168
268776
  "use strict";
268169
- import_node_crypto76 = require("node:crypto");
268777
+ import_node_crypto77 = require("node:crypto");
268170
268778
  init_pg_ident();
268171
268779
  init_esm();
268172
268780
  ident13 = (s2) => {
@@ -268516,7 +269124,7 @@ var init_postgres_nodes = __esm({
268516
269124
  return store;
268517
269125
  }
268518
269126
  issue(nodeId) {
268519
- const token = `ont_${(0, import_node_crypto76.randomBytes)(24).toString("base64url")}`;
269127
+ const token = `ont_${(0, import_node_crypto77.randomBytes)(24).toString("base64url")}`;
268520
269128
  this.cache.set(token, nodeId);
268521
269129
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
268522
269130
  return token;
@@ -268818,11 +269426,11 @@ var init_postgres_type_registry = __esm({
268818
269426
  });
268819
269427
 
268820
269428
  // ../storage/src/postgres-actor-memory.ts
268821
- var import_node_crypto77, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
269429
+ var import_node_crypto78, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
268822
269430
  var init_postgres_actor_memory = __esm({
268823
269431
  "../storage/src/postgres-actor-memory.ts"() {
268824
269432
  "use strict";
268825
- import_node_crypto77 = require("node:crypto");
269433
+ import_node_crypto78 = require("node:crypto");
268826
269434
  init_pg_ident();
268827
269435
  init_src();
268828
269436
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -269032,7 +269640,7 @@ var init_postgres_actor_memory = __esm({
269032
269640
  }
269033
269641
  async write(input, now) {
269034
269642
  if (input.memId === void 0) {
269035
- const memId = `mem:${(0, import_node_crypto77.randomUUID)()}`;
269643
+ const memId = `mem:${(0, import_node_crypto78.randomUUID)()}`;
269036
269644
  const r2 = await this.pool.query(
269037
269645
  `INSERT INTO ${this.s}.actor_memories
269038
269646
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -270603,14 +271211,14 @@ function companySchemaName(companyId) {
270603
271211
  if (typeof companyId !== "string" || !companyId.trim()) {
270604
271212
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
270605
271213
  }
270606
- const hex = (0, import_node_crypto78.createHash)("sha256").update(companyId, "utf8").digest("hex");
271214
+ const hex = (0, import_node_crypto79.createHash)("sha256").update(companyId, "utf8").digest("hex");
270607
271215
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
270608
271216
  }
270609
- var import_node_crypto78, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
271217
+ var import_node_crypto79, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
270610
271218
  var init_tenant_schema_mapping = __esm({
270611
271219
  "../storage/src/tenant-schema-mapping.ts"() {
270612
271220
  "use strict";
270613
- import_node_crypto78 = require("node:crypto");
271221
+ import_node_crypto79 = require("node:crypto");
270614
271222
  init_esm();
270615
271223
  init_pg_ident();
270616
271224
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -270684,7 +271292,7 @@ var init_tenant_schema_mapping = __esm({
270684
271292
 
270685
271293
  // ../storage/src/company-data-plane-schema.ts
270686
271294
  function canonicalTypeDefHash(def) {
270687
- return `sha256:${(0, import_node_crypto79.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
271295
+ return `sha256:${(0, import_node_crypto80.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
270688
271296
  }
270689
271297
  function defaultArtifactTypeTemplate() {
270690
271298
  return [
@@ -270866,11 +271474,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
270866
271474
  }
270867
271475
  return { inserted };
270868
271476
  }
270869
- var import_node_crypto79, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
271477
+ var import_node_crypto80, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
270870
271478
  var init_company_data_plane_schema = __esm({
270871
271479
  "../storage/src/company-data-plane-schema.ts"() {
270872
271480
  "use strict";
270873
- import_node_crypto79 = require("node:crypto");
271481
+ import_node_crypto80 = require("node:crypto");
270874
271482
  init_esm();
270875
271483
  init_pg_ident();
270876
271484
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -270949,11 +271557,11 @@ function rowToFile2(row) {
270949
271557
  updatedAt: new Date(row.updated_at).toISOString()
270950
271558
  };
270951
271559
  }
270952
- var import_node_crypto80, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
271560
+ var import_node_crypto81, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
270953
271561
  var init_company_owned_registry_store = __esm({
270954
271562
  "../storage/src/company-owned-registry-store.ts"() {
270955
271563
  "use strict";
270956
- import_node_crypto80 = require("node:crypto");
271564
+ import_node_crypto81 = require("node:crypto");
270957
271565
  init_esm();
270958
271566
  init_src();
270959
271567
  init_pg_ident();
@@ -271338,7 +271946,7 @@ var init_company_owned_registry_store = __esm({
271338
271946
  enabled: skill.enabled,
271339
271947
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
271340
271948
  });
271341
- return (0, import_node_crypto80.createHash)("sha256").update(body2).digest("hex");
271949
+ return (0, import_node_crypto81.createHash)("sha256").update(body2).digest("hex");
271342
271950
  }
271343
271951
  };
271344
271952
  }
@@ -274148,7 +274756,7 @@ function sourceSetConservation(input) {
274148
274756
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
274149
274757
  }
274150
274758
  function payloadChecksum(value2) {
274151
- return (0, import_node_crypto81.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
274759
+ return (0, import_node_crypto82.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
274152
274760
  }
274153
274761
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
274154
274762
  const cs = quoteIdent2(companySchema);
@@ -274540,11 +275148,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
274540
275148
  sequences
274541
275149
  };
274542
275150
  }
274543
- var import_node_crypto81, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
275151
+ var import_node_crypto82, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
274544
275152
  var init_tenant_migration_operator = __esm({
274545
275153
  "../storage/src/tenant-migration-operator.ts"() {
274546
275154
  "use strict";
274547
- import_node_crypto81 = require("node:crypto");
275155
+ import_node_crypto82 = require("node:crypto");
274548
275156
  init_esm();
274549
275157
  init_pg_ident();
274550
275158
  init_tenancy_attribution();
@@ -274780,8 +275388,8 @@ var init_tenant_new_company_provision = __esm({
274780
275388
  // ../storage/src/knowledge-snapshot-crypto.ts
274781
275389
  function sealSnapshot(snapshot, key2) {
274782
275390
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
274783
- const iv = (0, import_node_crypto82.randomBytes)(12);
274784
- const cipher = (0, import_node_crypto82.createCipheriv)("aes-256-gcm", bytes(key2), iv);
275391
+ const iv = (0, import_node_crypto83.randomBytes)(12);
275392
+ const cipher = (0, import_node_crypto83.createCipheriv)("aes-256-gcm", bytes(key2), iv);
274785
275393
  cipher.setAAD(aad2(snapshot));
274786
275394
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
274787
275395
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -274791,7 +275399,7 @@ function openSnapshot(snapshot, key2) {
274791
275399
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
274792
275400
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
274793
275401
  const [iv, tag, data] = packed;
274794
- const decipher = (0, import_node_crypto82.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
275402
+ const decipher = (0, import_node_crypto83.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
274795
275403
  decipher.setAAD(aad2(snapshot));
274796
275404
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
274797
275405
  return {
@@ -274799,11 +275407,11 @@ function openSnapshot(snapshot, key2) {
274799
275407
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
274800
275408
  };
274801
275409
  }
274802
- var import_node_crypto82, PREFIX3, bytes, aad2;
275410
+ var import_node_crypto83, PREFIX3, bytes, aad2;
274803
275411
  var init_knowledge_snapshot_crypto = __esm({
274804
275412
  "../storage/src/knowledge-snapshot-crypto.ts"() {
274805
275413
  "use strict";
274806
- import_node_crypto82 = require("node:crypto");
275414
+ import_node_crypto83 = require("node:crypto");
274807
275415
  PREFIX3 = "oasis-knowledge:v1:";
274808
275416
  bytes = (key2) => {
274809
275417
  const value2 = Buffer.from(key2);
@@ -275695,7 +276303,7 @@ function reportedCost(v2) {
275695
276303
  }
275696
276304
  function newUsageBackfillManifestId(now = /* @__PURE__ */ new Date()) {
275697
276305
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
275698
- return `ulb-${stamp}-${(0, import_node_crypto83.randomBytes)(3).toString("hex")}`;
276306
+ return `ulb-${stamp}-${(0, import_node_crypto84.randomBytes)(3).toString("hex")}`;
275699
276307
  }
275700
276308
  async function backfillUsageLedger(ledger, opts) {
275701
276309
  if (!opts.dryRun && !opts.manifestId) throw new Error("execute \u9700\u8981 manifestId");
@@ -276244,11 +276852,11 @@ async function audit(c, s2, manifestId, runId, field, before, after, createdAt)
276244
276852
  [manifestId, runId, field, before == null ? null : JSON.stringify(before), after == null ? null : JSON.stringify(after), createdAt]
276245
276853
  );
276246
276854
  }
276247
- var import_node_crypto83, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
276855
+ var import_node_crypto84, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
276248
276856
  var init_usage_ledger_backfill = __esm({
276249
276857
  "../storage/src/usage-ledger-backfill.ts"() {
276250
276858
  "use strict";
276251
- import_node_crypto83 = require("node:crypto");
276859
+ import_node_crypto84 = require("node:crypto");
276252
276860
  init_src();
276253
276861
  init_pg_ident();
276254
276862
  init_postgres_trace();
@@ -276827,31 +277435,64 @@ var init_connector_tool_versions = __esm({
276827
277435
  });
276828
277436
 
276829
277437
  // ../server/src/design/board-watch.ts
276830
- function decideBoardLetter(c, nowMs, quietMs) {
276831
- if (c.hasOpenWatchLetter) return false;
276832
- if (c.freshTerminalRunId !== null) return true;
276833
- if (c.boardMtime === null) return false;
276834
- if (!c.hasRevision) return false;
276835
- if (c.lastOpAtMs === null || c.boardMtime <= c.lastOpAtMs) return false;
276836
- if (nowMs - c.boardMtime < quietMs) return false;
276837
- if (c.lastWatchMtime !== null && c.boardMtime <= c.lastWatchMtime) return false;
276838
- return true;
277438
+ function parseOdTime(v2) {
277439
+ if (typeof v2 === "number") return Number.isFinite(v2) ? v2 : null;
277440
+ if (typeof v2 !== "string" || v2.trim().length === 0) return null;
277441
+ const s2 = v2.trim();
277442
+ if (/^\d+(\.\d+)?$/.test(s2)) return Number(s2);
277443
+ const t = Date.parse(s2);
277444
+ return Number.isFinite(t) ? t : null;
277445
+ }
277446
+ function nonEmpty2(v2) {
277447
+ return typeof v2 === "string" && v2.trim().length > 0 ? v2 : null;
276839
277448
  }
276840
- var MARKER_PREFIX, boardWatchMarker, parseMarker, RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, TERMINAL_RUN_STATUS, BoardWatcher;
277449
+ function roundOfMessage(m2, conversationId = null) {
277450
+ if (m2["role"] !== "assistant") return null;
277451
+ const status = nonEmpty2(m2["runStatus"]);
277452
+ if (status === null || !TERMINAL_RUN_STATUS.has(status)) return null;
277453
+ const endedAt = parseOdTime(m2["endedAt"]);
277454
+ if (endedAt === null) return null;
277455
+ const runId = nonEmpty2(m2["runId"]);
277456
+ const messageId = nonEmpty2(m2["id"]);
277457
+ if (runId === null && messageId === null) return null;
277458
+ const files = Array.isArray(m2["producedFiles"]) ? m2["producedFiles"] : [];
277459
+ const producedFiles = files.map((f2) => typeof f2 === "string" ? f2 : f2 && typeof f2 === "object" ? nonEmpty2(f2["path"]) ?? nonEmpty2(f2["name"]) : null).filter((s2) => s2 !== null);
277460
+ const { error: error2, errorCode } = messageError(m2, status);
277461
+ return {
277462
+ key: runId ?? `msg:${messageId}`,
277463
+ conversationId,
277464
+ runId,
277465
+ status,
277466
+ agentId: nonEmpty2(m2["agentId"]),
277467
+ endedAt,
277468
+ producedFiles,
277469
+ error: error2,
277470
+ errorCode
277471
+ };
277472
+ }
277473
+ function messageError(m2, status) {
277474
+ const events = Array.isArray(m2["events"]) ? m2["events"] : [];
277475
+ for (let i = events.length - 1; i >= 0; i--) {
277476
+ const e = events[i];
277477
+ if (!e || typeof e !== "object") continue;
277478
+ const ev = e;
277479
+ if (ev["kind"] !== "status" || ev["label"] !== "error") continue;
277480
+ const detail = nonEmpty2(ev["detail"]);
277481
+ if (detail !== null) return { error: detail, errorCode: nonEmpty2(ev["code"]) };
277482
+ }
277483
+ if (status !== "succeeded") {
277484
+ const content3 = nonEmpty2(m2["content"]);
277485
+ if (content3 !== null) return { error: content3, errorCode: null };
277486
+ }
277487
+ return { error: null, errorCode: null };
277488
+ }
277489
+ var RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, SINCE_MARKER_PREFIX, boardWatchSinceMarker, parseSinceMarker, TERMINAL_RUN_STATUS, MAX_REASON_CHARS, MAX_LISTED_FILES, BoardWatcher;
276841
277490
  var init_board_watch = __esm({
276842
277491
  "../server/src/design/board-watch.ts"() {
276843
277492
  "use strict";
276844
277493
  init_src7();
276845
- MARKER_PREFIX = "[od-board-watch mtime:";
276846
- boardWatchMarker = (mtimeMs) => `${MARKER_PREFIX}${mtimeMs}]`;
276847
- parseMarker = (body2) => {
276848
- const i = body2.indexOf(MARKER_PREFIX);
276849
- if (i < 0) return null;
276850
- const n = Number.parseInt(body2.slice(i + MARKER_PREFIX.length), 10);
276851
- return Number.isFinite(n) ? n : null;
276852
- };
276853
277494
  RUN_MARKER_PREFIX = "[od-board-watch run:";
276854
- boardWatchRunMarker = (runId) => `${RUN_MARKER_PREFIX}${runId}]`;
277495
+ boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
276855
277496
  parseRunMarker = (body2) => {
276856
277497
  const i = body2.indexOf(RUN_MARKER_PREFIX);
276857
277498
  if (i < 0) return null;
@@ -276860,113 +277501,183 @@ var init_board_watch = __esm({
276860
277501
  const id = body2.slice(i + RUN_MARKER_PREFIX.length, j);
276861
277502
  return id.length > 0 ? id : null;
276862
277503
  };
277504
+ SINCE_MARKER_PREFIX = "[od-board-watch since:";
277505
+ boardWatchSinceMarker = (sinceMs) => `${SINCE_MARKER_PREFIX}${sinceMs}]`;
277506
+ parseSinceMarker = (body2) => {
277507
+ const i = body2.indexOf(SINCE_MARKER_PREFIX);
277508
+ if (i < 0) return null;
277509
+ const n = Number.parseInt(body2.slice(i + SINCE_MARKER_PREFIX.length), 10);
277510
+ return Number.isFinite(n) ? n : null;
277511
+ };
276863
277512
  TERMINAL_RUN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
277513
+ MAX_REASON_CHARS = 500;
277514
+ MAX_LISTED_FILES = 10;
276864
277515
  BoardWatcher = class {
276865
277516
  constructor(deps) {
276866
277517
  this.deps = deps;
276867
- }
276868
- odGet(path47) {
277518
+ if (!deps.kernel && !(deps.listCompanies && deps.kernelFor)) throw new Error("BoardWatcher \u9700\u8981 kernel\uFF0C\u6216 listCompanies + kernelFor");
277519
+ }
277520
+ /** `<公司>\0<artifactId>` → 本进程第一次读成功时钉住的起算点(还没写过带 since 的信时用),免得在第一封信之前随时间往后漂。 */
277521
+ pinnedSince = /* @__PURE__ */ new Map();
277522
+ /** `<公司>\0<boardKey>\0<conversationId>` → 本进程上次把这个对话的轮次处理完时它的 updatedAt。 */
277523
+ seenConvAt = /* @__PURE__ */ new Map();
277524
+ /** 公司 → 上次记下的失败原文(同样的错只记一次;成功一轮就清掉)。 */
277525
+ companyLastError = /* @__PURE__ */ new Map();
277526
+ async odGet(path47) {
276869
277527
  const fetchImpl = this.deps.fetchImpl ?? fetch;
276870
- return fetchImpl(`${this.deps.odBase}${path47}`, this.deps.odToken ? { headers: { authorization: `Bearer ${this.deps.odToken}` } } : void 0);
276871
- }
276872
- /** 查一块板的交付物(排 task-context/、只算带 artifactManifest 的)。404/网络错 → null(板不存在)。 */
276873
- async boardFiles(boardKey) {
276874
277528
  try {
276875
- const res = await this.odGet(`/api/projects/${encodeURIComponent(boardKey)}/files`);
276876
- if (!res.ok) return null;
276877
- const body2 = await res.json();
276878
- return (body2.files ?? []).filter((f2) => !f2.path.startsWith("task-context/") && f2.artifactManifest !== void 0).map((f2) => ({ path: f2.path, kind: f2.kind ?? null, mtime: f2.mtime ?? null }));
276879
- } catch {
276880
- return null;
277529
+ const res = await fetchImpl(`${this.deps.odBase}${path47}`, this.deps.odToken ? { headers: { authorization: `Bearer ${this.deps.odToken}` } } : void 0);
277530
+ if (res.status === 401 || res.status === 403) return { kind: "denied", status: res.status };
277531
+ if (res.status === 404) return { kind: "missing" };
277532
+ if (!res.ok) return { kind: "error", detail: `HTTP ${res.status}` };
277533
+ return { kind: "ok", value: await res.json() };
277534
+ } catch (err) {
277535
+ return { kind: "error", detail: String(err) };
276881
277536
  }
276882
277537
  }
276883
277538
  /**
276884
- * 查一块板的 run 列表(`GET /api/runs?projectId=`,与 CLI `design status` 同源)。
276885
- * run 注册表是 od 的**内存态**——重启即空,查不到不代表没跑过;所以这里失败一律降级成空数组,
276886
- * 由板文件 mtime 那条慢路径兜底,绝不因为查不到 run 就压掉写信。
277539
+ * 列出一块板上结束时间可能晚于 since 的轮次(跨对话;顺序不重要,调用方按 endedAt 排)。
277540
+ *
277541
+ * 省流:od 的 assistant 消息带整段 events 与 content,每分钟把每个对话的 /messages 全拉一遍,轮次多的板
277542
+ * 一分钟就是几 MB。所以先用 /conversations 列表里的 updatedAt / latestRun.endedAt(取较大)筛:
277543
+ * - 不晚于 since → 这个对话里不可能有要写信的轮次,跳过;
277544
+ * - 和本进程上次看完时一样 → 没变,跳过(seenConvAt,serve 重启即清空,退回只按 since 筛,run marker 去重兜着)。
277545
+ * 两个时间都没有的对话照拉,不猜。返回每个拉过的对话的 updatedAt,由 tick 在它的轮次都写完信后记进 seenConvAt。
276887
277546
  */
276888
- async boardRuns(boardKey) {
276889
- try {
276890
- const res = await this.odGet(`/api/runs?projectId=${encodeURIComponent(boardKey)}`);
276891
- if (!res.ok) return [];
276892
- const body2 = await res.json();
276893
- return (body2.runs ?? []).map((r) => ({ runId: String(r["runId"] ?? r["id"] ?? ""), status: String(r["status"] ?? "unknown") })).filter((r) => r.runId.length > 0);
276894
- } catch {
276895
- return [];
276896
- }
277547
+ async boardRounds(company, boardKey, since) {
277548
+ const base = `/api/projects/${encodeURIComponent(boardKey)}/conversations`;
277549
+ const convs = await this.odGet(base);
277550
+ if (convs.kind !== "ok") return convs;
277551
+ const rounds = [];
277552
+ const fetched = /* @__PURE__ */ new Map();
277553
+ for (const c of convs.value.conversations ?? []) {
277554
+ const cid = nonEmpty2(c["id"]);
277555
+ if (cid === null) continue;
277556
+ const latestRun = c["latestRun"] && typeof c["latestRun"] === "object" ? c["latestRun"] : {};
277557
+ const times = [parseOdTime(c["updatedAt"]), parseOdTime(latestRun["endedAt"])].filter((t) => t !== null);
277558
+ const convAt = times.length > 0 ? Math.max(...times) : null;
277559
+ if (convAt !== null && (convAt <= since || this.seenConvAt.get(`${company}\0${boardKey}\0${cid}`) === convAt)) continue;
277560
+ const msgs = await this.odGet(`${base}/${encodeURIComponent(cid)}/messages`);
277561
+ if (msgs.kind === "missing") continue;
277562
+ if (msgs.kind !== "ok") return msgs;
277563
+ fetched.set(cid, convAt);
277564
+ for (const m2 of msgs.value.messages ?? []) {
277565
+ if (!m2 || typeof m2 !== "object") continue;
277566
+ const r = roundOfMessage(m2, cid);
277567
+ if (r !== null) rounds.push(r);
277568
+ }
277569
+ }
277570
+ return { kind: "ok", value: { rounds, fetched } };
277571
+ }
277572
+ /** 失败轮消息上没有错误原文时,去 `/api/runs` 按 runId 找 od 记的 error;找不到(含 od 重启过)→ null。 */
277573
+ async runErrorFromRegistry(boardKey, runId) {
277574
+ const res = await this.odGet(`/api/runs?projectId=${encodeURIComponent(boardKey)}`);
277575
+ if (res.kind !== "ok") return null;
277576
+ const hit = (res.value.runs ?? []).find((r) => String(r["runId"] ?? r["id"] ?? "") === runId);
277577
+ return hit ? nonEmpty2(hit["error"]) : null;
276897
277578
  }
276898
277579
  async tick(now = Date.now()) {
276899
- const { kernel, boardKeyOf, log: log3 } = this.deps;
276900
- const quietMs = this.deps.quietMs ?? 10 * 6e4;
277580
+ const { log: log3 } = this.deps;
277581
+ const denied = [];
277582
+ const { listCompanies, kernelFor } = this.deps;
277583
+ if (listCompanies && kernelFor) {
277584
+ for (const companyId of await listCompanies()) {
277585
+ try {
277586
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied);
277587
+ this.companyLastError.delete(companyId);
277588
+ } catch (err) {
277589
+ const msg = String(err);
277590
+ if (this.companyLastError.get(companyId) === msg) continue;
277591
+ this.companyLastError.set(companyId, msg);
277592
+ log3?.(`[board-watch] [${companyId}] \u8FD9\u4E00\u8F6E\u6CA1\u5DE1\u6210\uFF08\u5176\u4F59\u516C\u53F8\u7EE7\u7EED\uFF09\uFF1A${msg}`);
277593
+ }
277594
+ }
277595
+ } else {
277596
+ await this.tickCompany("", this.deps.kernel, now, denied);
277597
+ }
277598
+ if (denied.length > 0) {
277599
+ const codes = [...new Set(denied.map((d) => d.status))].join("/");
277600
+ const eg = denied[0];
277601
+ log3?.(
277602
+ `[board-watch] od \u62D2\u7EDD\u4E86\u5DE1\u68C0\u8BF7\u6C42\uFF08HTTP ${codes}\uFF0C${denied.length} \u5757\u677F\uFF0C\u5982 ${eg.company ? `[${eg.company}] ` : ""}${eg.boardKey}\uFF09\uFF1A${this.deps.odToken ? "\u5E26\u4E86 OASIS_OPEN_DESIGN_TOKEN \u4F46 od \u4E0D\u8BA4\uFF0C\u6838\u5BF9\u5B83\u4E0E od \u7684 OD_API_TOKEN \u662F\u5426\u4E00\u81F4" : "\u6CA1\u914D OASIS_OPEN_DESIGN_TOKEN\uFF0Cod \u8981\u4EE4\u724C"}\u3002\u8FD9\u4E9B\u677F\u672C\u8F6E\u6CA1\u770B\u6210`
277603
+ );
277604
+ }
277605
+ }
277606
+ /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277607
+ async tickCompany(company, kernel, now, denied) {
277608
+ const { boardKeyOf, log: log3 } = this.deps;
277609
+ const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
276901
277610
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
276902
277611
  const actor = this.deps.actor ?? "actor:system";
277612
+ const tag = company ? `[${company}] ` : "";
276903
277613
  const model = kernel.model;
276904
277614
  let written = 0;
276905
- for (const a of model.artifacts.values()) {
276906
- if (a.type !== "design") continue;
276907
- if (lifecycleOf(model, a.id) !== "active") continue;
277615
+ for (const a of [...model.artifacts.values()]) {
276908
277616
  if (written >= maxPerTick) {
276909
- log3?.(`[board-watch] \u672C\u8F6E\u5199\u4FE1\u5DF2\u8FBE\u4E0A\u9650 ${maxPerTick}\uFF0C\u5176\u4F59\u677F\u4E0B\u8F6E\u518D\u770B`);
277617
+ log3?.(`[board-watch] ${tag}\u672C\u8F6E\u5199\u4FE1\u5DF2\u8FBE\u4E0A\u9650 ${maxPerTick}\uFF0C\u5176\u4F59\u677F\u4E0B\u8F6E\u518D\u770B`);
276910
277618
  break;
276911
277619
  }
277620
+ if (a.type !== "design") continue;
277621
+ if (lifecycleOf(model, a.id) !== "active") continue;
276912
277622
  const boardKey = boardKeyOf(a.id);
276913
- const files = await this.boardFiles(boardKey);
276914
- if (files === null) continue;
276915
- const runs = await this.boardRuns(boardKey);
276916
- if (files.length === 0 && runs.length === 0) continue;
276917
- const boardMtime = files.reduce((mx, f2) => f2.mtime !== null && (mx === null || f2.mtime > mx) ? f2.mtime : mx, null);
276918
- const watchLetters = annotationsOf(model, a.id).filter(
276919
- (x2) => x2.author === actor && (parseMarker(x2.body) !== null || parseRunMarker(x2.body) !== null)
276920
- );
276921
- const letteredRuns = new Set(
276922
- watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null)
276923
- );
276924
- const newestTerminalRun = runs.find((r) => TERMINAL_RUN_STATUS.has(r.status)) ?? null;
276925
- const lastOpAt = model.lastOpAt.get(a.id);
276926
- const candidate = {
276927
- boardMtime,
276928
- hasRevision: revisionsOf(model, a.id).length > 0,
276929
- lastOpAtMs: lastOpAt ? Date.parse(lastOpAt) : null,
276930
- hasOpenWatchLetter: watchLetters.some((x2) => x2.state === "open"),
276931
- lastWatchMtime: watchLetters.reduce((mx, x2) => {
276932
- const m2 = parseMarker(x2.body);
276933
- return m2 !== null && (mx === null || m2 > mx) ? m2 : mx;
276934
- }, null),
276935
- freshTerminalRunId: newestTerminalRun !== null && !letteredRuns.has(newestTerminalRun.runId) ? newestTerminalRun.runId : null
276936
- };
276937
- if (!decideBoardLetter(candidate, now, quietMs)) continue;
276938
- const fmt = (fs52) => fs52.slice(0, 5).map((f2) => `- ${f2.path}\uFF08${f2.kind ?? "?"}\uFF09\xB7 ${f2.mtime ? new Date(f2.mtime).toISOString() : "?"}`).join("\n");
276939
- let body2;
276940
- let logLine;
276941
- if (candidate.freshTerminalRunId !== null) {
276942
- const status = newestTerminalRun.status;
276943
- const ok3 = status === "succeeded";
276944
- const listed = files.length > 0 ? fmt([...files].sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0))) : "\uFF08\u677F\u4E0A\u6682\u65E0\u4EA4\u4ED8\u7269\uFF09";
276945
- body2 = [
276946
- // 两个 marker 都带:run marker 管快路径去重,mtime marker 顺手把慢路径也盖住——
276947
- // 否则这封信被 resolve 后,慢路径会就**同一批板内容**再写一封(两路去重各管各的)。
276948
- boardMtime !== null ? `${boardWatchRunMarker(candidate.freshTerminalRunId)} ${boardWatchMarker(boardMtime)}` : boardWatchRunMarker(candidate.freshTerminalRunId),
276949
- ok3 ? `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8DD1\u5B8C\u4E86\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\uFF0C\u8D27\u5728\u677F\u4E0A\u7B49\u4F60\u6536\uFF1A` : `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8FD9\u4E00\u8F6E**\u6CA1\u8DD1\u6210**\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\u3002\u677F\u4E0A\u5F53\u524D\u5185\u5BB9\uFF1A`,
276950
- listed,
276951
- ``,
276952
- ok3 ? `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${a.id} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002` : `\u5148 \`oasis design status ${a.id}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\uFF1B\u6CA1\u6709\u5C31\u628A\u9700\u6C42\u4E0E\u771F\u5B9E\u6570\u636E\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E \`oasis design chat ${a.id} --message "<\u2026>" --async\`\uFF0C\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${a.id} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
276953
- ].join("\n");
276954
- logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u5F15\u64CE\u6536\u5C3E\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} ${status}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
276955
- } else {
276956
- const newest = files.filter((f2) => f2.mtime !== null && candidate.lastOpAtMs !== null && f2.mtime > candidate.lastOpAtMs).sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0));
276957
- body2 = [
276958
- boardWatchMarker(boardMtime),
276959
- `\u753B\u677F ${boardKey} \u4E0A\u6709 Oasis \u4E4B\u5916\u7684\u65B0\u52A8\u9759\uFF08\u4EBA\u5728\u677F\u4E0A\u5BF9\u8BDD/\u6539\u7A3F\uFF0C\u6216\u5F15\u64CE\u4EA7\u4E86\u6CA1\u4EBA\u6536\u7684\u65B0\u7248\uFF09\uFF0C\u6BD4\u672C\u4EA7\u7269\u6700\u8FD1\u4E00\u6B21\u64CD\u4F5C\uFF08${lastOpAt ?? "?"}\uFF09\u65B0\u3001\u4E14\u5DF2\u9759\u9ED8 ${Math.round(quietMs / 6e4)} \u5206\u949F\uFF1A`,
276960
- fmt(newest.length > 0 ? newest : files),
276961
- ``,
276962
- `\u8BF7\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5185\u5BB9\u5408\u610F\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u6536\u5F55\u677F\u4E0A\u4EBA\u5DE5\u8FED\u4EE3>"\` \u6536\u5F55\u4E3A\u65B0\u7248\u672C\uFF1B\u4E0D\u5408\u610F/\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u7248\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
276963
- ].join("\n");
276964
- logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u6709\u65B0\u5185\u5BB9\uFF08mtime=${new Date(boardMtime).toISOString()}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
277623
+ const pinKey = `${company}\0${a.id}`;
277624
+ const watchLetters = annotationsOf(model, a.id).filter((x2) => x2.author === actor && x2.body.includes("[od-board-watch "));
277625
+ const lettered = new Set(watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null));
277626
+ const recordedSince = watchLetters.reduce((mn, x2) => {
277627
+ const s2 = parseSinceMarker(x2.body);
277628
+ return s2 !== null && (mn === null || s2 < mn) ? s2 : mn;
277629
+ }, null);
277630
+ const since = recordedSince ?? this.pinnedSince.get(pinKey) ?? now - graceMs;
277631
+ const read3 = await this.boardRounds(company, boardKey, since);
277632
+ if (read3.kind === "missing") continue;
277633
+ if (read3.kind === "denied") {
277634
+ denied.push({ company, boardKey, status: read3.status });
277635
+ continue;
276965
277636
  }
276966
- await kernel.annotate({ artifactId: a.id, actor, body: body2, kind: "comment" });
276967
- written++;
276968
- log3?.(logLine);
277637
+ if (read3.kind === "error") {
277638
+ log3?.(`[board-watch] ${tag}\u8BFB\u753B\u677F ${boardKey} \u5931\u8D25\uFF08${read3.detail}\uFF09\uFF0C\u672C\u8F6E\u8DF3\u8FC7`);
277639
+ continue;
277640
+ }
277641
+ if (recordedSince === null && !this.pinnedSince.has(pinKey)) this.pinnedSince.set(pinKey, since);
277642
+ const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key)).sort((x2, y) => x2.endedAt - y.endedAt);
277643
+ const seen = /* @__PURE__ */ new Set();
277644
+ for (const r of fresh) {
277645
+ if (written >= maxPerTick) break;
277646
+ if (seen.has(r.key)) continue;
277647
+ seen.add(r.key);
277648
+ const ok3 = r.status === "succeeded";
277649
+ const reason = ok3 ? null : r.error ?? (r.runId !== null ? await this.runErrorFromRegistry(boardKey, r.runId) : null);
277650
+ await kernel.annotate({ artifactId: a.id, actor, body: this.letterBody(a.id, boardKey, r, since, reason), kind: "comment" });
277651
+ written++;
277652
+ log3?.(`[board-watch] ${tag}${a.id} \u2190 \u677F ${boardKey} \u4E00\u8F6E\u5BF9\u8BDD\u7ED3\u675F\uFF08${r.key.slice(0, 12)} ${r.status}${r.agentId ? ` ${r.agentId}` : ""}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`);
277653
+ }
277654
+ const pending = new Set(fresh.filter((r) => !seen.has(r.key)).map((r) => r.conversationId));
277655
+ for (const [cid, convAt] of read3.value.fetched) {
277656
+ if (convAt !== null && !pending.has(cid)) this.seenConvAt.set(`${company}\0${boardKey}\0${cid}`, convAt);
277657
+ }
277658
+ }
277659
+ }
277660
+ letterBody(artifactId, boardKey, r, since, reason) {
277661
+ const marker = `${boardWatchRunMarker(r.key)} ${boardWatchSinceMarker(since)}`;
277662
+ const round = `${r.runId !== null ? `run ${r.runId.slice(0, 8)}` : `\u6D88\u606F ${r.key.slice(4, 12)}`}\uFF0C\u5F15\u64CE=${r.agentId ?? "?"}\uFF0C\u7ED3\u675F\u4E8E ${new Date(r.endedAt).toISOString()}`;
277663
+ if (r.status === "succeeded") {
277664
+ const files = r.producedFiles.length > 0 ? [...r.producedFiles.slice(0, MAX_LISTED_FILES).map((f2) => `- ${f2}`), ...r.producedFiles.length > MAX_LISTED_FILES ? [`- \u2026\u53E6 ${r.producedFiles.length - MAX_LISTED_FILES} \u4E2A`] : []].join("\n") : "\uFF08\u8FD9\u4E00\u8F6E\u6CA1\u6709\u767B\u8BB0\u4EA7\u51FA\u6587\u4EF6\uFF09";
277665
+ return [
277666
+ marker,
277667
+ `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD\u8DD1\u5B8C\u4E86\uFF08${round}\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u4EA7\u51FA\uFF1A`,
277668
+ files,
277669
+ ``,
277670
+ `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277671
+ ].join("\n");
276969
277672
  }
277673
+ const quoted = reason === null ? null : reason.length > MAX_REASON_CHARS ? `${reason.slice(0, MAX_REASON_CHARS)}\u2026` : reason;
277674
+ return [
277675
+ marker,
277676
+ `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD**\u6CA1\u8DD1\u6210**\uFF08\u7EC8\u6001=${r.status}\uFF0C${round}\uFF09\u3002`,
277677
+ quoted !== null ? `od \u8BB0\u7684\u539F\u56E0\uFF08\u539F\u6587\uFF09\uFF1A${quoted}${r.errorCode !== null ? `\uFF08\u9519\u8BEF\u7801 ${r.errorCode}\uFF09` : ""}` : `od \u6CA1\u6709\u8BB0\u5F55\u8FD9\u4E00\u8F6E\u7684\u5931\u8D25\u539F\u56E0\u3002`,
277678
+ ``,
277679
+ `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\uFF1B\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277680
+ ].join("\n");
276970
277681
  }
276971
277682
  };
276972
277683
  }
@@ -277848,8 +278559,8 @@ __export(src_exports2, {
277848
278559
  bindWorkorderProject: () => bindWorkorderProject,
277849
278560
  bindingResolverFromRegistry: () => bindingResolverFromRegistry,
277850
278561
  bindingRuntimeState: () => bindingRuntimeState,
277851
- boardWatchMarker: () => boardWatchMarker,
277852
278562
  boardWatchRunMarker: () => boardWatchRunMarker,
278563
+ boardWatchSinceMarker: () => boardWatchSinceMarker,
277853
278564
  broadcastMessageId: () => broadcastMessageId,
277854
278565
  broadcastMessageText: () => broadcastMessageText,
277855
278566
  bucketRuntimeRuns: () => bucketRuntimeRuns,
@@ -277953,7 +278664,6 @@ __export(src_exports2, {
277953
278664
  cronNext: () => cronNext,
277954
278665
  cronNextN: () => cronNextN,
277955
278666
  deadlineClockTick: () => deadlineClockTick,
277956
- decideBoardLetter: () => decideBoardLetter,
277957
278667
  defaultAvatarRefOf: () => defaultAvatarRefOf,
277958
278668
  delegationArtifactsView: () => delegationArtifactsView,
277959
278669
  delegationBundleAdditions: () => delegationBundleAdditions,
@@ -278104,6 +278814,7 @@ __export(src_exports2, {
278104
278814
  parseConnectorSkillId: () => parseConnectorSkillId,
278105
278815
  parseKnowledgeAnswerOutput: () => parseKnowledgeAnswerOutput,
278106
278816
  parseKnowledgePlan: () => parseKnowledgePlan,
278817
+ parseOdTime: () => parseOdTime,
278107
278818
  parseReadScope: () => parseReadScope,
278108
278819
  parseSkillFrontmatter: () => parseSkillFrontmatter,
278109
278820
  parseSymrefHead: () => parseSymrefHead,
@@ -278193,6 +278904,7 @@ __export(src_exports2, {
278193
278904
  retireCdGateInTypeStore: () => retireCdGateInTypeStore,
278194
278905
  rolesDomain: () => rolesDomain,
278195
278906
  rollbackOrgIdentity: () => rollbackOrgIdentity,
278907
+ roundOfMessage: () => roundOfMessage,
278196
278908
  routeFusedSignal: () => routeFusedSignal,
278197
278909
  runtimeConnectionStatus: () => runtimeConnectionStatus,
278198
278910
  runtimeDriftBlock: () => runtimeDriftBlock,
@@ -278758,14 +279470,14 @@ init_src();
278758
279470
  var os16 = __toESM(require("node:os"));
278759
279471
  var path46 = __toESM(require("node:path"));
278760
279472
  var import_node_child_process26 = require("node:child_process");
278761
- var import_node_crypto94 = require("node:crypto");
279473
+ var import_node_crypto95 = require("node:crypto");
278762
279474
 
278763
279475
  // ../cli/src/cli.ts
278764
279476
  init_src();
278765
279477
  var fs44 = __toESM(require("node:fs"), 1);
278766
279478
  var os13 = __toESM(require("node:os"), 1);
278767
279479
  var path39 = __toESM(require("node:path"), 1);
278768
- var import_node_crypto92 = require("node:crypto");
279480
+ var import_node_crypto93 = require("node:crypto");
278769
279481
 
278770
279482
  // ../cli/src/serve.ts
278771
279483
  init_src11();
@@ -278777,11 +279489,11 @@ var fs36 = __toESM(require("node:fs"), 1);
278777
279489
  var os9 = __toESM(require("node:os"), 1);
278778
279490
  var path30 = __toESM(require("node:path"), 1);
278779
279491
  var import_node_child_process18 = require("node:child_process");
278780
- var import_node_crypto88 = require("node:crypto");
279492
+ var import_node_crypto89 = require("node:crypto");
278781
279493
  var import_node_url7 = require("node:url");
278782
279494
 
278783
279495
  // ../cli/src/artifact-discovery-source.ts
278784
- var import_node_crypto84 = require("node:crypto");
279496
+ var import_node_crypto85 = require("node:crypto");
278785
279497
  init_src7();
278786
279498
  var ArtifactDiscoverySourceUnreadableError = class extends Error {
278787
279499
  constructor(message) {
@@ -278811,7 +279523,7 @@ async function resolveArtifactDiscoverySourceContent(blobs, input) {
278811
279523
  throw new ArtifactDiscoverySourceUnreadableError(`Artifact Revision ${input.revisionId} \u7684 contentKind \u4E0D\u53D7\u652F\u6301\uFF1A${String(input.contentKind)}`);
278812
279524
  }
278813
279525
  const rootBytes = await blobs.get(input.contentRef);
278814
- const sourceFingerprint = (0, import_node_crypto84.createHash)("sha256").update(rootBytes).digest("hex");
279526
+ const sourceFingerprint = (0, import_node_crypto85.createHash)("sha256").update(rootBytes).digest("hex");
278815
279527
  if (input.contentKind === "inline-blob") {
278816
279528
  const sourceContent = decodeText3(rootBytes);
278817
279529
  if (sourceContent === null) {
@@ -278835,7 +279547,7 @@ ${entry.text}`).join("\n\n"),
278835
279547
  }
278836
279548
 
278837
279549
  // ../server/src/domains/discussions/service.ts
278838
- var import_node_crypto85 = require("node:crypto");
279550
+ var import_node_crypto86 = require("node:crypto");
278839
279551
  init_zod();
278840
279552
  init_discussions();
278841
279553
  init_router();
@@ -278897,7 +279609,7 @@ function remapTopics(topics, removed) {
278897
279609
  return [...out].map(([topic_id, summary]) => ({ topic_id, summary }));
278898
279610
  }
278899
279611
  var entryId = (projectId2, id) => JSON.stringify([projectId2, id]);
278900
- var hash2 = (s2) => (0, import_node_crypto85.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
279612
+ var hash2 = (s2) => (0, import_node_crypto86.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
278901
279613
  var canonical = (s2) => s2.normalize("NFKC").trim().toLocaleLowerCase();
278902
279614
  function decisionFingerprint(d) {
278903
279615
  return hash2(JSON.stringify([d.statement, d.rationale, d.alternatives]));
@@ -279117,7 +279829,7 @@ var DiscussionService = class {
279117
279829
  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");
279118
279830
  }
279119
279831
  }
279120
- const operation = (value2) => tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
279832
+ const operation = (value2) => tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279121
279833
  ...value2,
279122
279834
  projectId: projectId2,
279123
279835
  actorId: ctx.actorId,
@@ -279134,7 +279846,7 @@ var DiscussionService = class {
279134
279846
  if (id.startsWith("new:")) {
279135
279847
  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`);
279136
279848
  const existing = [...topicMap.values()].find((x2) => canonical(x2.title) === canonical(t.title));
279137
- id = existing?.topicId ?? `topic_${(0, import_node_crypto85.randomUUID)()}`;
279849
+ id = existing?.topicId ?? `topic_${(0, import_node_crypto86.randomUUID)()}`;
279138
279850
  if (!existing) createdTopics.add(id);
279139
279851
  } else if (!topicMap.has(id)) invalid("topics.topic_id", `\u8BAE\u9898 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u8BAE\u9898\u4F7F\u7528 new: \u524D\u7F00`);
279140
279852
  topicAliases.set(t.topic_id, id);
@@ -279186,7 +279898,7 @@ var DiscussionService = class {
279186
279898
  if (id.startsWith("new:")) {
279187
279899
  const candidateKey = JSON.stringify([topicId, canonical(d.record.statement)]);
279188
279900
  const existing = [...decisionMap.values()].find((x2) => x2.topicId === topicId && canonical(x2.record.statement) === canonical(d.record.statement));
279189
- id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto85.randomUUID)()}`;
279901
+ id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto86.randomUUID)()}`;
279190
279902
  candidateIds.set(candidateKey, id);
279191
279903
  } else if (!decisionMap.has(id)) invalid("decision_id", `\u51B3\u7B56 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u51B3\u7B56\u4F7F\u7528 new: \u524D\u7F00`);
279192
279904
  decisionAliases.set(d.decision_id, id);
@@ -279448,7 +280160,7 @@ var DiscussionService = class {
279448
280160
  ...row.value.confirmation ? { confirmation: { ...row.value.confirmation, fingerprint: decisionFingerprint(record9) } } : {}
279449
280161
  };
279450
280162
  await tx.put("decision", row.id, value2);
279451
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
280163
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279452
280164
  action: "edit",
279453
280165
  kind: "decision",
279454
280166
  projectId: projectId2,
@@ -279512,7 +280224,7 @@ var DiscussionService = class {
279512
280224
  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`);
279513
280225
  const summary = given.get(row.id);
279514
280226
  await tx.put("discussion", row.id, { ...current.value, record: { ...current.value.record, summary } });
279515
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
280227
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279516
280228
  action: "edit",
279517
280229
  kind: "discussion",
279518
280230
  projectId: current.value.record.project_id,
@@ -279528,7 +280240,7 @@ var DiscussionService = class {
279528
280240
  });
279529
280241
  }
279530
280242
  async operation(tx, actorId, kind, id, from, to) {
279531
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
280243
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
279532
280244
  }
279533
280245
  };
279534
280246
  function pickDiscussionContent(d) {
@@ -279828,7 +280540,7 @@ var DiscussionContext = class {
279828
280540
  };
279829
280541
 
279830
280542
  // ../server/src/domains/discussions/worker.ts
279831
- var import_node_crypto86 = require("node:crypto");
280543
+ var import_node_crypto87 = require("node:crypto");
279832
280544
  var DISCUSSION_IDLE_MS = 15 * 6e4;
279833
280545
  var DISCUSSION_MAX_CORRECTIONS = 3;
279834
280546
  var DISCUSSION_LEASE_MS = 2 * 6e4;
@@ -279854,7 +280566,7 @@ var DiscussionWorker = class {
279854
280566
  if (await this.isReplying(sessionId)) return false;
279855
280567
  const cutoff = eligibleCutoff(await this.service.chat.listMessages(sessionId), this.service.now());
279856
280568
  if (!cutoff) return false;
279857
- const owner = (0, import_node_crypto86.randomUUID)();
280569
+ const owner = (0, import_node_crypto87.randomUUID)();
279858
280570
  const claimed = await this.service.store.transaction(async (tx) => {
279859
280571
  const old = (await tx.get("job", sessionId))?.value;
279860
280572
  if (old?.status === "running" && (old.leaseUntil ?? 0) > this.service.now()) return null;
@@ -280576,7 +281288,7 @@ function createDiscussionRuntime(options) {
280576
281288
  }
280577
281289
 
280578
281290
  // ../server/src/domains/discussions/store.ts
280579
- var import_node_crypto87 = require("node:crypto");
281291
+ var import_node_crypto88 = require("node:crypto");
280580
281292
  var import_promises17 = require("node:fs/promises");
280581
281293
  var import_node_path27 = require("node:path");
280582
281294
  var key = (kind, id) => JSON.stringify([kind, id]);
@@ -280621,7 +281333,7 @@ var MemoryDiscussionStore = class _MemoryDiscussionStore {
280621
281333
  if (changed) {
280622
281334
  if (this.filename) {
280623
281335
  await (0, import_promises17.mkdir)((0, import_node_path27.dirname)(this.filename), { recursive: true });
280624
- const temp = `${this.filename}.${(0, import_node_crypto87.randomUUID)()}.tmp`;
281336
+ const temp = `${this.filename}.${(0, import_node_crypto88.randomUUID)()}.tmp`;
280625
281337
  await (0, import_promises17.writeFile)(temp, JSON.stringify(next), "utf8");
280626
281338
  await (0, import_promises17.rename)(temp, this.filename);
280627
281339
  }
@@ -281570,7 +282282,7 @@ function oasisWrapperScript() {
281570
282282
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
281571
282283
  const tsx = path30.join(repoRoot, "node_modules/.bin/tsx");
281572
282284
  const main = path30.join(repoRoot, "packages/cli/src/main.ts");
281573
- const key2 = (0, import_node_crypto88.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
282285
+ const key2 = (0, import_node_crypto89.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
281574
282286
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
281575
282287
  const stable = path30.join(os9.tmpdir(), `oasis-cli-wrapper-${uid}-${key2}`);
281576
282288
  let dir;
@@ -281963,7 +282675,7 @@ async function startServe(opts) {
281963
282675
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
281964
282676
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
281965
282677
  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" });
281966
- const requestId = (0, import_node_crypto88.randomUUID)();
282678
+ const requestId = (0, import_node_crypto89.randomUUID)();
281967
282679
  const task = new Promise((resolve12) => {
281968
282680
  const timer = setTimeout(() => {
281969
282681
  modelRefreshWaiters.delete(requestId);
@@ -282792,7 +283504,7 @@ async function startServe(opts) {
282792
283504
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
282793
283505
  if (buf || session.runId) {
282794
283506
  await turnStore.appendMessage({
282795
- id: (0, import_node_crypto88.randomUUID)(),
283507
+ id: (0, import_node_crypto89.randomUUID)(),
282796
283508
  sessionId: chatSessionId,
282797
283509
  role: "assistant",
282798
283510
  content: buf,
@@ -282838,7 +283550,7 @@ async function startServe(opts) {
282838
283550
  chatTargets: {
282839
283551
  inspect: inspectChatTarget,
282840
283552
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
282841
- const sessionId = (0, import_node_crypto88.randomUUID)();
283553
+ const sessionId = (0, import_node_crypto89.randomUUID)();
282842
283554
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
282843
283555
  const binding = await registryStore.getBinding(agentId).catch(() => null);
282844
283556
  await (await chatStoreFor(companyId)).createSession({
@@ -284240,7 +284952,7 @@ async function startServe(opts) {
284240
284952
  }));
284241
284953
  }
284242
284954
  const limits = { wallClockMs: wallClockForKind("planner") };
284243
- const artifactId = `artifact:planner:${(0, import_node_crypto88.randomUUID)()}`;
284955
+ const artifactId = `artifact:planner:${(0, import_node_crypto89.randomUUID)()}`;
284244
284956
  const handle = await chatRemoteAdapter.spawn({
284245
284957
  actor: planner.id,
284246
284958
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -285161,10 +285873,10 @@ async function startServe(opts) {
285161
285873
  if (isDeletedActor(dispatchTarget)) {
285162
285874
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
285163
285875
  }
285164
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto88.randomUUID)());
285876
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto89.randomUUID)());
285165
285877
  const resumeRuntimeSession = Boolean(sessionId);
285166
- const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto88.randomUUID)()}`;
285167
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto88.randomUUID)()}`;
285878
+ const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto89.randomUUID)()}`;
285879
+ const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto89.randomUUID)()}`;
285168
285880
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
285169
285881
  if (stableDispatch && !stableDispatch.shouldDispatch) {
285170
285882
  if (isHumanSubmitDispatchId(requestDispatchId)) {
@@ -285638,7 +286350,13 @@ async function startServe(opts) {
285638
286350
  metadata: { ...base, dispatchId: handle.id },
285639
286351
  // 恢复路径拿不到 dispatchChat 闭包里的 fallback 解析结果,开跑时先写进账本;退出帧若上报
285640
286352
  // 更精确 model 仍会覆盖为 runtime 来源。
285641
- ...chatModel ? { effectiveModel: chatModel, modelSource: chatModelSource } : {}
286353
+ // 这是**请求值**(会话选择 / 员工配置 / runtime 默认),按 ④ 排在实际值之下:采集点 1 的
286354
+ // 首帧检查点可能已经先一步把运行时回报的实际值写进这一行(两条路都是异步入队,谁先到不定),
286355
+ // 预写不能把它顶掉。
286356
+ ...preferRunModel(
286357
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
286358
+ { effectiveModel: chatModel, modelSource: chatModelSource }
286359
+ ) ?? {}
285642
286360
  });
285643
286361
  });
285644
286362
  const buffered = [];
@@ -285710,11 +286428,17 @@ async function startServe(opts) {
285710
286428
  }]);
285711
286429
  const usage = info.usage ?? null;
285712
286430
  const modelFields = chatExitModelFields(info.model, chatModel, chatModelSource, info.modelSource);
286431
+ const needsNativeSessionMetadata = !!info.runtimeSessionId && info.runtimeSessionId !== runtimeSessionId;
286432
+ const currentRun = needsNativeSessionMetadata || "effectiveModel" in modelFields ? await (await traceStoreFor(runCompanyId)).getRun(traceRunId).catch(() => null) : null;
285713
286433
  let nativeSessionMetadata;
285714
- if (info.runtimeSessionId && info.runtimeSessionId !== runtimeSessionId) {
285715
- const run = await (await traceStoreFor(runCompanyId)).getRun(traceRunId).catch(() => null);
285716
- nativeSessionMetadata = nativeRuntimeSessionMetadata(run?.metadata, info.runtimeSessionId);
286434
+ if (needsNativeSessionMetadata) {
286435
+ nativeSessionMetadata = nativeRuntimeSessionMetadata(currentRun?.metadata, info.runtimeSessionId);
285717
286436
  }
286437
+ const modelPatch = preferRunModel(
286438
+ { effectiveModel: currentRun?.effectiveModel, modelSource: currentRun?.modelSource },
286439
+ { effectiveModel: modelFields.effectiveModel, modelSource: modelFields.modelSource },
286440
+ "replace"
286441
+ ) ?? {};
285718
286442
  await traceAppender.update({
285719
286443
  status: classifySessionExitStatus(info),
285720
286444
  endedAt,
@@ -285724,7 +286448,7 @@ async function startServe(opts) {
285724
286448
  ...!isHealthySessionExit(info) && info.reason !== "cancelled" ? { errorCode: info.reason ?? "error", errorMessage: formatExitError(info) } : {},
285725
286449
  transcriptRef: info.transcriptRef ?? null,
285726
286450
  usage,
285727
- ...modelFields,
286451
+ ...modelPatch,
285728
286452
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
285729
286453
  });
285730
286454
  if (usage) await settleRunUsage(await traceStoreFor(runCompanyId), traceRunId);
@@ -286622,7 +287346,7 @@ async function startServe(opts) {
286622
287346
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
286623
287347
  return;
286624
287348
  }
286625
- const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
287349
+ const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto89.randomUUID)()}`;
286626
287350
  try {
286627
287351
  const inserted = await (await dispatchLedgerFor(companyId)).insertOpen({
286628
287352
  id: ledgerId,
@@ -286722,7 +287446,7 @@ async function startServe(opts) {
286722
287446
  const kernelModel = newKernel.model;
286723
287447
  const art = kernelModel.artifacts.get(nodeId);
286724
287448
  const revWid = art?.workspace ?? "";
286725
- const ledgerId = `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
287449
+ const ledgerId = `dispatch:${(0, import_node_crypto89.randomUUID)()}`;
286726
287450
  try {
286727
287451
  await (await dispatchLedgerFor(companyId)).insertOpen({
286728
287452
  id: ledgerId,
@@ -287116,7 +287840,7 @@ async function startServe(opts) {
287116
287840
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
287117
287841
  try {
287118
287842
  const governance = server.knowledgeGovernance;
287119
- const taskFingerprint = (0, import_node_crypto88.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
287843
+ const taskFingerprint = (0, import_node_crypto89.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
287120
287844
  const requestContext = {
287121
287845
  organizationId: companyId,
287122
287846
  actorId,
@@ -288579,7 +289303,12 @@ ${nodeFault}` : "");
288579
289303
  const odBase = (process.env["OASIS_OPEN_DESIGN_URL"] ?? "http://127.0.0.1:7457").replace(/\/$/, "");
288580
289304
  const odToken = process.env["OASIS_OPEN_DESIGN_TOKEN"]?.trim() || void 0;
288581
289305
  const boardWatcher = new BoardWatcher({
288582
- kernel: defaultCompanyKernel,
289306
+ listCompanies: async () => {
289307
+ if (!chatStoreRouter) return [defaultCompanyId];
289308
+ const active = (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id);
289309
+ return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
289310
+ },
289311
+ kernelFor: async (companyId) => (await getEngine(companyId)).kernel,
288583
289312
  odBase,
288584
289313
  ...odToken ? { odToken } : {},
288585
289314
  boardKeyOf: deriveBoardKey,
@@ -288588,7 +289317,7 @@ ${nodeFault}` : "");
288588
289317
  boardWatchTimer = setInterval(() => {
288589
289318
  void boardWatcher.tick().catch((err) => console.error(`[board-watch] tick \u5931\u8D25: ${String(err)}`));
288590
289319
  }, 6e4);
288591
- console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u542F\u7528\uFF08od=${odBase}${odToken ? "\uFF0C\u5E26\u4EE4\u724C" : ""}\uFF0C\u9759\u9ED8\u7A97 10min\uFF0C\u6BCF\u8F6E\u4E0A\u9650 2 \u5C01\u4FE1\uFF1BOASIS_BOARD_WATCH=0 \u53EF\u5173\uFF09`);
289320
+ console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u542F\u7528\uFF08od=${odBase}${odToken ? "\uFF0C\u5E26\u4EE4\u724C" : ""}\uFF0C\u5DE1\u6240\u6709\u516C\u53F8\uFF0C\u6BCF\u7ED3\u675F\u4E00\u8F6E\u5BF9\u8BDD\u5199\u4E00\u5C01\u4FE1\uFF0C\u6BCF\u8F6E\u6BCF\u5BB6\u516C\u53F8\u4E0A\u9650 2 \u5C01\uFF1BOASIS_BOARD_WATCH=0 \u53EF\u5173\uFF09`);
288592
289321
  } else {
288593
289322
  console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u88AB OASIS_BOARD_WATCH=${boardWatchFlag} \u663E\u5F0F\u5173\u95ED`);
288594
289323
  }
@@ -289835,7 +290564,7 @@ var RuntimeQuotaMonitor = class {
289835
290564
  init_wrapper();
289836
290565
  init_src();
289837
290566
  var import_node_os11 = require("node:os");
289838
- var import_node_crypto89 = require("node:crypto");
290567
+ var import_node_crypto90 = require("node:crypto");
289839
290568
  init_src8();
289840
290569
  init_src2();
289841
290570
 
@@ -290739,7 +291468,7 @@ var DaemonWsClient = class {
290739
291468
  */
290740
291469
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
290741
291470
  if (artifactIds.length === 0) return {};
290742
- const requestId = (0, import_node_crypto89.randomUUID)();
291471
+ const requestId = (0, import_node_crypto90.randomUUID)();
290743
291472
  return new Promise((resolve12, reject) => {
290744
291473
  const timer = setTimeout(() => {
290745
291474
  this.gcPending.delete(requestId);
@@ -291153,7 +291882,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
291153
291882
  // ../cli/src/daemon/machine-id.ts
291154
291883
  var import_node_child_process23 = require("node:child_process");
291155
291884
  var import_node_fs24 = require("node:fs");
291156
- var import_node_crypto90 = require("node:crypto");
291885
+ var import_node_crypto91 = require("node:crypto");
291157
291886
  var import_node_os14 = require("node:os");
291158
291887
  function linuxMachineId() {
291159
291888
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -291219,7 +291948,7 @@ var defaultSources = {
291219
291948
  function resolveNodeId(sources = {}) {
291220
291949
  const s2 = { ...defaultSources, ...sources };
291221
291950
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
291222
- const digest = (0, import_node_crypto90.createHash)("sha256").update(material).digest("hex").slice(0, 12);
291951
+ const digest = (0, import_node_crypto91.createHash)("sha256").update(material).digest("hex").slice(0, 12);
291223
291952
  return `node-${digest}`;
291224
291953
  }
291225
291954
 
@@ -293091,13 +293820,13 @@ function ownFlagsFromDecl(command2) {
293091
293820
  }
293092
293821
 
293093
293822
  // ../cli/src/connector-effect-runner.ts
293094
- var import_node_crypto91 = require("node:crypto");
293823
+ var import_node_crypto92 = require("node:crypto");
293095
293824
  var import_node_child_process24 = require("node:child_process");
293096
293825
  init_src8();
293097
293826
  function stableKey(effect) {
293098
293827
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
293099
293828
  const canonical2 = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
293100
- return `sha256:${(0, import_node_crypto91.createHash)("sha256").update(canonical2).digest("hex")}`;
293829
+ return `sha256:${(0, import_node_crypto92.createHash)("sha256").update(canonical2).digest("hex")}`;
293101
293830
  }
293102
293831
  async function runConnectorEffect(connector, toolArgs, deps) {
293103
293832
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -293473,6 +294202,7 @@ ${buildCommandIndex()}
293473
294202
  ${USAGE_FOOTER}`;
293474
294203
  var COMMAND_HELP = buildCommandHelp();
293475
294204
  var HISTORICAL_REVIEW_GUIDANCE = "\u672C\u7968\u4E0D\u53C2\u4E0E\u672C\u8F6E quorum\uFF1B\u4F46\u5B83\u6307\u51FA\u7684\u95EE\u9898\u53EF\u80FD\u4ECD\u7136\u5B58\u5728\uFF0C\u9700\u9010\u6761\u5BF9\u7167 diff \u786E\u8BA4";
294205
+ var EMPTY_OK_NOTE_FLAGS = /* @__PURE__ */ new Set(["unresolved", "risks", "gaps", "next"]);
293476
294206
  function parseArgs(argv) {
293477
294207
  const positional = [];
293478
294208
  const flags = /* @__PURE__ */ new Map();
@@ -293499,6 +294229,10 @@ function parseArgs(argv) {
293499
294229
  const value2 = argv[i + 1];
293500
294230
  const key2 = a.slice(2);
293501
294231
  if (value2 === void 0 || value2.startsWith("--")) {
294232
+ if (EMPTY_OK_NOTE_FLAGS.has(key2)) {
294233
+ flags.set(key2, "");
294234
+ continue;
294235
+ }
293502
294236
  if (!booleanFlags.has(key2)) throw new Error(`flag ${a} \u9700\u8981\u4E00\u4E2A\u503C`);
293503
294237
  flags.set(key2, "true");
293504
294238
  } else {
@@ -293652,7 +294386,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
293652
294386
  const timeoutMs = resolveAuthWaitMs();
293653
294387
  const controller = new AbortController();
293654
294388
  const timer = setTimeout(() => controller.abort(), timeoutMs);
293655
- const waiterId = (0, import_node_crypto92.randomUUID)();
294389
+ const waiterId = (0, import_node_crypto93.randomUUID)();
293656
294390
  process.stderr.write(
293657
294391
  `\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
293658
294392
  `
@@ -293736,7 +294470,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
293736
294470
  }
293737
294471
  }
293738
294472
  }
293739
- var CLI_VERSION = "2.2.82" ? "2.2.82" : "dev";
294473
+ var CLI_VERSION = "2.2.84" ? "2.2.84" : "dev";
293740
294474
  function apiHeaders(token, company) {
293741
294475
  const headers = {
293742
294476
  authorization: `Bearer ${token}`,
@@ -294102,7 +294836,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
294102
294836
  const store = createNodeTokenStore(path39.join(dir, "node-tokens.json"));
294103
294837
  const sub = positional[0];
294104
294838
  if (sub === "issue") {
294105
- const id = flags.get("id") ?? `node-${(0, import_node_crypto92.randomUUID)()}`;
294839
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto93.randomUUID)()}`;
294106
294840
  const token2 = store.issue(id);
294107
294841
  println(token2);
294108
294842
  process.stderr.write(
@@ -295252,7 +295986,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
295252
295986
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
295253
295987
  const goal = flags.get("goal")?.trim();
295254
295988
  const slug6 = flags.get("slug");
295255
- const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto92.randomUUID)());
295989
+ const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto93.randomUUID)());
295256
295990
  const result = await api.request("POST", "/api/projects", {
295257
295991
  ...id ? { id } : {},
295258
295992
  name,
@@ -295603,6 +296337,10 @@ ${res.warning}`);
295603
296337
  note: flags.get("note")
295604
296338
  });
295605
296339
  println(message);
296340
+ const ignoredNotes = [...EMPTY_OK_NOTE_FLAGS].filter((k2) => flags.has(k2)).map((k2) => `--${k2}`);
296341
+ if (ignoredNotes.length > 0) {
296342
+ println(`\u26A0 ${ignoredNotes.join("\u3001")} \u6CA1\u6709\u88AB\u8BB0\u5F55\uFF1Aoasis conclude \u53EA\u6536 --note\u3002\u8FD9\u51E0\u9879\u8981\u5199\u5728 oasis propose \u4E0A\uFF0C\u4E0B\u6B21\u4EA4\u4ED8\u65F6\u4E00\u8D77\u5E26\u4E0A\u3002`);
296343
+ }
295606
296344
  break;
295607
296345
  }
295608
296346
  case "review": {
@@ -297523,13 +298261,13 @@ function runtimeAssetSourceRoots(input) {
297523
298261
  }
297524
298262
 
297525
298263
  // src/install.ts
297526
- var import_node_crypto93 = require("node:crypto");
298264
+ var import_node_crypto94 = require("node:crypto");
297527
298265
  var fs50 = __toESM(require("node:fs"));
297528
298266
  var path45 = __toESM(require("node:path"));
297529
298267
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
297530
298268
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
297531
298269
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
297532
- const digest = (0, import_node_crypto93.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
298270
+ const digest = (0, import_node_crypto94.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
297533
298271
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
297534
298272
  }
297535
298273
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -297652,7 +298390,7 @@ function shimScript() {
297652
298390
  }
297653
298391
 
297654
298392
  // src/index.ts
297655
- var PKG_VERSION = true ? "2.2.82" : "dev";
298393
+ var PKG_VERSION = true ? "2.2.84" : "dev";
297656
298394
  var LOCAL_BIN = localBin();
297657
298395
  var NPM_PREFIX = npmPrefix();
297658
298396
  var INSTANCE = DEFAULT_INSTANCE;
@@ -297960,7 +298698,7 @@ function newInstanceName() {
297960
298698
  const existing = new Set(listInstances());
297961
298699
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
297962
298700
  for (; ; ) {
297963
- const n = `inst-${(0, import_node_crypto94.randomBytes)(3).toString("hex")}`;
298701
+ const n = `inst-${(0, import_node_crypto95.randomBytes)(3).toString("hex")}`;
297964
298702
  if (!existing.has(n)) return n;
297965
298703
  }
297966
298704
  }