oasis_test_v2 2.2.83 → 2.2.85

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 +524 -86
  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
  }
@@ -2953,6 +2977,7 @@ async function fetchResultPackage(base, fetchImpl, runId) {
2953
2977
  function toEpochMs(v2) {
2954
2978
  if (typeof v2 === "number" && Number.isFinite(v2)) return v2;
2955
2979
  if (typeof v2 === "string" && v2.length > 0) {
2980
+ if (/^\d+(\.\d+)?$/.test(v2.trim())) return Number(v2.trim());
2956
2981
  const ms = Date.parse(v2);
2957
2982
  return Number.isNaN(ms) ? void 0 : ms;
2958
2983
  }
@@ -2969,6 +2994,98 @@ function latestSucceededRun(runs) {
2969
2994
  }
2970
2995
  return best;
2971
2996
  }
2997
+ function liveBoardRuns(st) {
2998
+ const out = [];
2999
+ const known = /* @__PURE__ */ new Set();
3000
+ for (const t of st.turns ?? []) {
3001
+ if (t.runId !== null) known.add(t.runId);
3002
+ if (isTerminalRunStatus(t.status)) continue;
3003
+ out.push({ key: t.runId ?? `msg:${t.messageId ?? "?"}`, status: t.status, ...t.agentId ? { agentId: t.agentId } : {}, source: "conversation" });
3004
+ }
3005
+ for (const r of st.runs) {
3006
+ if (known.has(r.runId) || r.runId.length === 0) continue;
3007
+ if (r.status !== "running" && r.status !== "queued") continue;
3008
+ out.push({ key: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, source: "registry" });
3009
+ }
3010
+ return out;
3011
+ }
3012
+ function latestEndedRun(st) {
3013
+ let best;
3014
+ for (const t of st.turns ?? []) {
3015
+ if (!isTerminalRunStatus(t.status)) continue;
3016
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
3017
+ }
3018
+ if (best) return { runId: best.runId, status: best.status, ...best.agentId ? { agentId: best.agentId } : {}, ...best.error ? { error: best.error } : {} };
3019
+ const r = st.runs.find((x2) => isTerminalRunStatus(x2.status));
3020
+ return r ? { runId: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, ...r.error ? { error: r.error } : {} } : void 0;
3021
+ }
3022
+ function snapshotSourceRunId(st) {
3023
+ let best;
3024
+ for (const t of st.turns ?? []) {
3025
+ if (t.status !== "succeeded" || t.runId === null) continue;
3026
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
3027
+ }
3028
+ return best?.runId ?? latestSucceededRun(st.runs)?.runId;
3029
+ }
3030
+ function nonEmptyStr(v2) {
3031
+ return typeof v2 === "string" && v2.trim().length > 0 ? v2 : void 0;
3032
+ }
3033
+ function turnError(m2, status) {
3034
+ const events = Array.isArray(m2["events"]) ? m2["events"] : [];
3035
+ for (let i = events.length - 1; i >= 0; i--) {
3036
+ const e = events[i];
3037
+ if (!e || typeof e !== "object") continue;
3038
+ const ev = e;
3039
+ if (ev["kind"] === "status" && ev["label"] === "error" && nonEmptyStr(ev["detail"])) return ev["detail"];
3040
+ }
3041
+ return status !== "succeeded" && isTerminalRunStatus(status) ? nonEmptyStr(m2["content"]) : void 0;
3042
+ }
3043
+ async function fetchBoardTurns(base, fetchImpl, boardKey) {
3044
+ const convPath = `/api/projects/${encodeURIComponent(boardKey)}/conversations`;
3045
+ const guard = (err) => {
3046
+ if (err instanceof OdHttpError && (err.status === 401 || err.status === 403)) throw err;
3047
+ return null;
3048
+ };
3049
+ let convs;
3050
+ try {
3051
+ convs = (await odApi(base, fetchImpl, "GET", convPath)).conversations ?? [];
3052
+ } catch (err) {
3053
+ if (err instanceof OdHttpError && err.status === 404) return [];
3054
+ return guard(err);
3055
+ }
3056
+ const turns = [];
3057
+ for (const c of convs) {
3058
+ const cid = nonEmptyStr(c["id"]);
3059
+ if (cid === void 0) continue;
3060
+ let msgs;
3061
+ try {
3062
+ msgs = (await odApi(base, fetchImpl, "GET", `${convPath}/${encodeURIComponent(cid)}/messages`)).messages ?? [];
3063
+ } catch (err) {
3064
+ if (err instanceof OdHttpError && err.status === 404) continue;
3065
+ return guard(err);
3066
+ }
3067
+ for (const m2 of msgs) {
3068
+ if (!m2 || typeof m2 !== "object" || m2["role"] !== "assistant") continue;
3069
+ const status = nonEmptyStr(m2["runStatus"]);
3070
+ if (status === void 0) continue;
3071
+ const runId = nonEmptyStr(m2["runId"]) ?? null;
3072
+ const messageId = nonEmptyStr(m2["id"]) ?? null;
3073
+ if (runId === null && messageId === null) continue;
3074
+ const turn = { conversationId: cid, runId, messageId, status };
3075
+ const agentId = nonEmptyStr(m2["agentId"]);
3076
+ if (agentId) turn.agentId = agentId;
3077
+ const error2 = turnError(m2, status);
3078
+ if (error2) turn.error = error2;
3079
+ const startedAt = toEpochMs(m2["startedAt"]) ?? toEpochMs(m2["createdAt"]);
3080
+ if (startedAt !== void 0) turn.startedAt = startedAt;
3081
+ const endedAt = toEpochMs(m2["endedAt"]);
3082
+ if (endedAt !== void 0) turn.endedAt = endedAt;
3083
+ turns.push(turn);
3084
+ }
3085
+ }
3086
+ turns.sort((a, b2) => (b2.startedAt ?? Number.NEGATIVE_INFINITY) - (a.startedAt ?? Number.NEGATIVE_INFINITY) || 0);
3087
+ return turns;
3088
+ }
2972
3089
  async function fetchBoardStatus(base, fetchImpl, artifactId) {
2973
3090
  const boardKey = deriveBoardKey(artifactId);
2974
3091
  let boardExists = true;
@@ -3011,7 +3128,31 @@ async function fetchBoardStatus(base, fetchImpl, artifactId) {
3011
3128
  } catch {
3012
3129
  }
3013
3130
  }
3014
- return { boardKey, boardExists, runs, artifacts };
3131
+ const turns = boardExists ? await fetchBoardTurns(base, fetchImpl, boardKey) : [];
3132
+ return { boardKey, boardExists, runs, turns, artifacts };
3133
+ }
3134
+ async function waitBoardSettled(base, fetchImpl, artifactId, opts = {}) {
3135
+ const interval = opts.intervalMs ?? 2e4;
3136
+ const timeout = opts.timeoutMs ?? 100 * 6e4;
3137
+ const sleep6 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
3138
+ const now = opts.now ?? Date.now;
3139
+ const started = now();
3140
+ let lastKey = null;
3141
+ let waited = false;
3142
+ for (; ; ) {
3143
+ const st = await fetchBoardStatus(base, fetchImpl, artifactId);
3144
+ const elapsedMs = now() - started;
3145
+ if (!st.boardExists) return { status: st, settled: true, waited, live: [], elapsedMs };
3146
+ const live = liveBoardRuns(st);
3147
+ const unreadable = st.turns === null;
3148
+ if (live.length === 0 && !unreadable) return { status: st, settled: true, waited, live, elapsedMs };
3149
+ const key2 = unreadable && live.length === 0 ? "?" : live.map((r) => `${r.key}=${r.status}`).sort().join(",");
3150
+ opts.onPoll?.({ live, elapsedMs, changed: key2 !== lastKey, unreadable });
3151
+ lastKey = key2;
3152
+ if (elapsedMs >= timeout) return { status: st, settled: false, waited: true, live, elapsedMs };
3153
+ waited = true;
3154
+ await sleep6(Math.min(interval, Math.max(0, timeout - elapsedMs)));
3155
+ }
3015
3156
  }
3016
3157
  async function pumpDesignEvents(base, fetchImpl, runId, onLine) {
3017
3158
  const res = await fetchImpl(`${base}/api/runs/${runId}/events`);
@@ -3080,16 +3221,25 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3080
3221
  }
3081
3222
  }
3082
3223
  }
3083
- fs2.writeFileSync(
3084
- path2.join(dir, "board.link"),
3085
- `${publicBase}/projects/${boardKey}
3086
- \u5165\u53E3\u7A3F\uFF1A${entry}
3087
- \u5FEB\u7167\u6765\u6E90 run\uFF1A${runId}
3088
- \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
3089
- `
3090
- );
3224
+ fs2.writeFileSync(path2.join(dir, BOARD_LINK_FILE), formatBoardLink({ publicBase, boardKey, entry, runId }));
3091
3225
  return { dir, entry };
3092
3226
  }
3227
+ function formatBoardLink(args) {
3228
+ return `${args.publicBase}/projects/${args.boardKey}
3229
+ \u5165\u53E3\u7A3F\uFF1A${args.entry}
3230
+ ${SOURCE_RUN_LABEL}${args.runId}
3231
+ \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
3232
+ `;
3233
+ }
3234
+ function parseBoardLinkSourceRun(text7) {
3235
+ for (const line2 of text7.split(/\r?\n/)) {
3236
+ const t = line2.trim();
3237
+ if (!t.startsWith(SOURCE_RUN_LABEL)) continue;
3238
+ const id = t.slice(SOURCE_RUN_LABEL.length).trim();
3239
+ return id.length > 0 && id !== UNKNOWN_SOURCE_RUN ? id : null;
3240
+ }
3241
+ return null;
3242
+ }
3093
3243
  async function startDesignChatTurn(opts) {
3094
3244
  const fetchImpl = withOdToken(opts.fetchImpl ?? fetch, opts.token);
3095
3245
  const boardKey = deriveBoardKey(opts.artifactId);
@@ -3119,7 +3269,7 @@ async function runDesignChatTurn(opts) {
3119
3269
  const { artifacts, fileCount } = await fetchResultPackage(opts.base, fetchImpl, runId);
3120
3270
  return { boardKey, conversationId, runId, userMessageId, artifacts, fileCount };
3121
3271
  }
3122
- var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR;
3272
+ var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR, TERMINAL_TURN_STATUS, isTerminalRunStatus, BOARD_LINK_FILE, SOURCE_RUN_LABEL, UNKNOWN_SOURCE_RUN;
3123
3273
  var init_driver = __esm({
3124
3274
  "../adapters/src/open-design/driver.ts"() {
3125
3275
  "use strict";
@@ -3133,6 +3283,11 @@ var init_driver = __esm({
3133
3283
  }
3134
3284
  };
3135
3285
  TASK_CONTEXT_DIR = "task-context/";
3286
+ TERMINAL_TURN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
3287
+ isTerminalRunStatus = (s2) => TERMINAL_TURN_STATUS.has(s2);
3288
+ BOARD_LINK_FILE = "board.link";
3289
+ SOURCE_RUN_LABEL = "\u5FEB\u7167\u6765\u6E90 run\uFF1A";
3290
+ UNKNOWN_SOURCE_RUN = "status-pull";
3136
3291
  }
3137
3292
  });
3138
3293
 
@@ -8506,14 +8661,46 @@ async function runACPSession(job, cfg) {
8506
8661
  let runtimeReportedModel;
8507
8662
  let requestedModelAccepted;
8508
8663
  const reportedModel = () => pickReportedModel({ runtime: [routedModel, runtimeReportedModel], requested: requestedModelAccepted });
8664
+ let turnIndex = 0;
8665
+ let turnStartedMs = Date.now();
8666
+ let freshSession = true;
8667
+ let routedPollInflight = false;
8509
8668
  const pollRoutedModel = async () => {
8510
- if (routedModel || !cfg.readRoutedModel || !capturedSessionId) return;
8511
- if (actualModelId(runtimeReportedModel)) return;
8669
+ if (!cfg.readRoutedModel || !capturedSessionId || exited) return false;
8670
+ if (actualModelId(runtimeReportedModel)) return true;
8671
+ if (routedPollInflight) return false;
8672
+ routedPollInflight = true;
8673
+ const turn = turnIndex;
8512
8674
  try {
8513
- routedModel = await cfg.readRoutedModel(capturedSessionId);
8675
+ const found2 = actualModelId(await cfg.readRoutedModel(capturedSessionId, {
8676
+ turnStartedMs,
8677
+ freshSession
8678
+ }));
8679
+ if (!found2 || turn !== turnIndex) return false;
8680
+ if (found2 !== routedModel) {
8681
+ routedModel = found2;
8682
+ emitUsageCheckpoint();
8683
+ }
8684
+ return true;
8514
8685
  } catch {
8686
+ return false;
8687
+ } finally {
8688
+ routedPollInflight = false;
8515
8689
  }
8516
- if (routedModel) emitUsageCheckpoint();
8690
+ };
8691
+ const ROUTED_MODEL_POLL_DELAYS_MS = [0, 150, 400, 900, 2e3];
8692
+ let routedSamplingTurn = -1;
8693
+ const startRoutedModelSampling = () => {
8694
+ if (!cfg.readRoutedModel || routedSamplingTurn === turnIndex) return;
8695
+ routedSamplingTurn = turnIndex;
8696
+ const turn = turnIndex;
8697
+ void (async () => {
8698
+ for (const delay of ROUTED_MODEL_POLL_DELAYS_MS) {
8699
+ if (delay > 0) await new Promise((r) => setTimeout(r, delay));
8700
+ if (exited || turn !== turnIndex) return;
8701
+ if (await pollRoutedModel()) return;
8702
+ }
8703
+ })();
8517
8704
  };
8518
8705
  const modelForUsage = () => reportedModel()?.model;
8519
8706
  const usageCheckpointCbs = [];
@@ -8639,11 +8826,15 @@ ${note}` : note };
8639
8826
  },
8640
8827
  (text7) => {
8641
8828
  confirmAppendBatch();
8829
+ startRoutedModelSampling();
8642
8830
  if (cfg.classifyPromptFailure && turnMessage.length < PROMPT_TURN_MESSAGE_MAX) turnMessage += text7;
8643
8831
  for (const cb of outputCbs) cb(text7);
8644
8832
  },
8645
8833
  (e) => {
8646
- if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) confirmAppendBatch();
8834
+ if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) {
8835
+ confirmAppendBatch();
8836
+ startRoutedModelSampling();
8837
+ }
8647
8838
  for (const cb of telemetryCbs) cb(e);
8648
8839
  },
8649
8840
  // runtime 自己用 stopReason=cancelled 收尾时也保留取消语义;此前误记成 error,
@@ -8712,6 +8903,7 @@ ${note}` : note };
8712
8903
  sessionId = job.runtimeSessionId;
8713
8904
  ambiguousResume = true;
8714
8905
  }
8906
+ if (sessionId) freshSession = false;
8715
8907
  } catch {
8716
8908
  sessionId = "";
8717
8909
  }
@@ -8725,6 +8917,7 @@ ${note}` : note };
8725
8917
  sessionId = extractSessionID(sessionRes);
8726
8918
  runtimeReportedModel = extractCurrentModelID(sessionRes) ?? runtimeReportedModel;
8727
8919
  if (!sessionId) throw new Error("session/new returned no sessionId");
8920
+ freshSession = true;
8728
8921
  }
8729
8922
  capturedSessionId = sessionId;
8730
8923
  if (resolvedModel2) {
@@ -8761,6 +8954,8 @@ ${task}` : task;
8761
8954
  const sendPrompt = async (text7) => {
8762
8955
  turnMessage = "";
8763
8956
  turnStderr = [];
8957
+ turnIndex += 1;
8958
+ turnStartedMs = Date.now();
8764
8959
  const p2 = client.request("session/prompt", { sessionId, prompt: [{ type: "text", text: text7 }] });
8765
8960
  inflightStopReason = p2.then((r) => extractStopReason(r), () => "");
8766
8961
  try {
@@ -8780,6 +8975,7 @@ ${task}` : task;
8780
8975
  sessionId = extractSessionID(sessionRes);
8781
8976
  runtimeReportedModel = extractCurrentModelID(sessionRes) ?? runtimeReportedModel;
8782
8977
  if (!sessionId) throw new Error("session/new returned no sessionId");
8978
+ freshSession = true;
8783
8979
  capturedSessionId = sessionId;
8784
8980
  emitUsageCheckpoint();
8785
8981
  promptResult = await sendPrompt(userText);
@@ -9548,14 +9744,59 @@ function pickCursorModelName(value2) {
9548
9744
  };
9549
9745
  return visit(value2, 0) ?? fallback;
9550
9746
  }
9551
- function modelNameInCell(cell) {
9552
- if (typeof cell !== "string" || !cell.includes("modelName")) return void 0;
9747
+ function parseCell(cell) {
9748
+ if (typeof cell !== "string") return void 0;
9553
9749
  try {
9554
- return pickCursorModelName(JSON.parse(cell));
9750
+ return JSON.parse(cell);
9555
9751
  } catch {
9556
9752
  return void 0;
9557
9753
  }
9558
9754
  }
9755
+ function looksLikeTimeKey(key2) {
9756
+ const tokens2 = key2.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
9757
+ const last = tokens2[tokens2.length - 1];
9758
+ if (!last || !TIME_KEY_TAILS.includes(last)) return false;
9759
+ return last !== "at" || tokens2.length > 1;
9760
+ }
9761
+ function cursorTimeValueMs(value2) {
9762
+ if (typeof value2 === "bigint") return cursorTimeValueMs(Number(value2));
9763
+ if (typeof value2 === "number") {
9764
+ if (!Number.isFinite(value2)) return void 0;
9765
+ if (value2 > 1e15) return Math.floor(value2 / 1e3);
9766
+ if (value2 > 1e12) return value2;
9767
+ if (value2 > 1e9) return value2 * 1e3;
9768
+ return void 0;
9769
+ }
9770
+ if (typeof value2 === "string") {
9771
+ const t = value2.trim();
9772
+ if (!t) return void 0;
9773
+ if (/^\d+$/.test(t)) return cursorTimeValueMs(Number(t));
9774
+ const ms = Date.parse(t);
9775
+ return Number.isFinite(ms) ? ms : void 0;
9776
+ }
9777
+ return void 0;
9778
+ }
9779
+ function cursorRowTimeMs(row, parsedCells) {
9780
+ let best;
9781
+ const take = (v2) => {
9782
+ const ms = cursorTimeValueMs(v2);
9783
+ if (ms !== void 0 && (best === void 0 || ms > best)) best = ms;
9784
+ };
9785
+ for (const [key2, value2] of Object.entries(row)) if (looksLikeTimeKey(key2)) take(value2);
9786
+ const visit = (node2, depth) => {
9787
+ if (depth > 8 || node2 === null || typeof node2 !== "object") return;
9788
+ if (Array.isArray(node2)) {
9789
+ for (const item of node2) visit(item, depth + 1);
9790
+ return;
9791
+ }
9792
+ for (const [key2, value2] of Object.entries(node2)) {
9793
+ if (looksLikeTimeKey(key2)) take(value2);
9794
+ visit(value2, depth + 1);
9795
+ }
9796
+ };
9797
+ for (const parsed of parsedCells) visit(parsed, 0);
9798
+ return best;
9799
+ }
9559
9800
  async function openReadOnly(file) {
9560
9801
  try {
9561
9802
  const sqlite = await import("node:sqlite");
@@ -9564,7 +9805,9 @@ async function openReadOnly(file) {
9564
9805
  return null;
9565
9806
  }
9566
9807
  }
9567
- async function readCursorRoutedModel(sessionId, env = process.env) {
9808
+ async function readCursorRoutedModel(sessionId, window3) {
9809
+ const env = window3.env ?? process.env;
9810
+ const earliest = window3.turnStartedMs - TURN_CLOCK_TOLERANCE_MS;
9568
9811
  const file = cursorAcpSessionStorePath(sessionId, env);
9569
9812
  if (!file) return void 0;
9570
9813
  const db = await openReadOnly(file);
@@ -9580,10 +9823,17 @@ async function readCursorRoutedModel(sessionId, env = process.env) {
9580
9823
  continue;
9581
9824
  }
9582
9825
  for (const row of rows) {
9583
- for (const cell of Object.values(row)) {
9584
- const model = modelNameInCell(cell);
9585
- if (model) return model;
9826
+ const cells = Object.values(row);
9827
+ if (!cells.some((cell) => typeof cell === "string" && cell.includes("modelName"))) continue;
9828
+ const parsed = cells.map(parseCell);
9829
+ const model = parsed.map((value2) => value2 === void 0 ? void 0 : pickCursorModelName(value2)).find((name) => !!name);
9830
+ if (!model) continue;
9831
+ const rowMs = cursorRowTimeMs(row, parsed);
9832
+ if (rowMs !== void 0) {
9833
+ if (rowMs >= earliest) return model;
9834
+ continue;
9586
9835
  }
9836
+ if (window3.freshSession) return model;
9587
9837
  }
9588
9838
  }
9589
9839
  return void 0;
@@ -9596,7 +9846,7 @@ async function readCursorRoutedModel(sessionId, env = process.env) {
9596
9846
  }
9597
9847
  }
9598
9848
  }
9599
- var fs10, os6, path12, MAX_TABLES, MAX_ROWS_PER_TABLE;
9849
+ var fs10, os6, path12, MAX_TABLES, MAX_ROWS_PER_TABLE, TURN_CLOCK_TOLERANCE_MS, TIME_KEY_TAILS;
9600
9850
  var init_local_store = __esm({
9601
9851
  "../adapters/src/cursor/local-store.ts"() {
9602
9852
  "use strict";
@@ -9605,6 +9855,8 @@ var init_local_store = __esm({
9605
9855
  path12 = __toESM(require("node:path"), 1);
9606
9856
  MAX_TABLES = 24;
9607
9857
  MAX_ROWS_PER_TABLE = 200;
9858
+ TURN_CLOCK_TOLERANCE_MS = 1e3;
9859
+ TIME_KEY_TAILS = ["at", "ts", "time", "timestamp", "date", "millis", "epoch"];
9608
9860
  }
9609
9861
  });
9610
9862
 
@@ -9652,7 +9904,7 @@ var init_cursor = __esm({
9652
9904
  // 采集点 4:「自动」档下 cursor 对外只报 `default[]`(不是型号、查不到价,v2 上 385 条记的就是它),
9653
9905
  // 而它自己的 `~/.cursor/acp-sessions/<会话号>/store.db` 里逐条回复都带实际路由到的模型。
9654
9906
  // 这是 cursor 的内部格式:读不到就退回会话级自报值,绝不报错(见 local-store.ts)。
9655
- readRoutedModel: (sessionId) => readCursorRoutedModel(sessionId),
9907
+ readRoutedModel: (sessionId, window3) => readCursorRoutedModel(sessionId, window3),
9656
9908
  ...this.opts.syncSkills ? { syncSkills: this.opts.syncSkills } : {}
9657
9909
  });
9658
9910
  }
@@ -140895,14 +141147,19 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
140895
141147
  })() : canvasMode ? [
140896
141148
  // 内核只出「你产什么 + 内核权威锚点」——**设计工作流散文全在你的人设(identity/AGENTS.md「产出流程」)**,
140897
141149
  // 那是单一真源、改它=改配置不发版(提案 design-conversational-driving 治本,2026-07-17)。这里不重复正文,
140898
- // 只钉住内核唯一不能漂的两件事:真实 artifactId、缺前置的逃生口 gap。
141150
+ // 只钉住内核不能漂的几件事:真实 artifactId、缺前置的逃生口 gap,以及「下单后在会话里 status --wait 等结果」
141151
+ //(2026-09-24 加:它直接关系引擎的零产出判定——下单即退出会被判零产出→协议续唤→连败熔断;人设各公司
141152
+ // 各一份、未必同步更新,这条放内核才保证每个设计员工都拿得到)。
140899
141153
  `\u4F60\u8D1F\u8D23 **design \u4EA7\u7269 \`${artifactId}\`**\uFF0C\u8FD0\u884C\u5728**\u901A\u7528\u8FD0\u884C\u65F6**\u4E0A\u2014\u2014\u4F60\u662F\u7532\u65B9\u8BBE\u8BA1\u8D1F\u8D23\u4EBA\uFF0C\u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8**\u534F\u4F5C\u753B\u677F\u5F15\u64CE**\u6253\u78E8\u754C\u9762\u7A3F\u3002`,
140900
141154
  ``,
140901
141155
  `**\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF08\u600E\u4E48\u53D6\u8D27/\u8D77\u8F6E/\u8BFB\u4EA7\u7269/\u4EA4\u7A3F/\u522B\u624B\u6539\u677F\u6587\u4EF6\u2026\uFF09\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\` \u7684\u300C\u4EA7\u51FA\u6D41\u7A0B\uFF08design \u4EA7\u7269\uFF09\u300D**\u2014\u2014\u628A\u91CC\u9762 \`<artifactId>\` \u90FD\u6362\u6210\u672C\u4EA7\u7269 \`${artifactId}\`\u3002`,
140902
141156
  ``,
140903
- `\u5185\u6838\u53EA\u5728\u6B64\u9489\u4E24\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
141157
+ `\u5185\u6838\u53EA\u5728\u6B64\u9489\u51E0\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
140904
141158
  `- **\u4EA4\u7A3F\u8D70 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "\u2026"\`**\uFF08proposed \u624D\u8FDB\u7248\u672C\u53F2\u3001\u4E0B\u6E38\u624D\u62FF\u5F97\u5230\uFF1B\u4E0D propose = head \u4ECD \u2205\uFF09\uFF1B`,
140905
- `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`
141159
+ `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`,
141160
+ `- **\u5728\u672C\u4F1A\u8BDD\u91CC\u7B49\u753B\u677F\u51FA\u7ED3\u679C**\uFF08\u540C\u5F00\u53D1\u7B49 CI\uFF09\uFF1A\u5F00\u5DE5\u5148 \`oasis design status ${artifactId}\`\uFF08\u6062\u590D\u4F18\u5148\uFF0C\u677F\u4E0A\u5DF2\u6709\u8D27/\u5DF2\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u522B\u91CD\u4E0B\uFF09\uFF1B\u4E0B\u5355\u7528 \`oasis design chat ${artifactId} \u2026 --async\`\uFF0C\u7136\u540E**\u7ACB\u523B** \`oasis design status ${artifactId} --wait --pull\` \u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF08\u6700\u957F\u7EA6 100 \u5206\u949F\uFF1B\u963B\u585E\u671F\u95F4\u4E0D\u8017 token\uFF09\u3002\u6210\u529F \u2192 \u6838\u5BF9 \u2192 propose\uFF1B\u5931\u8D25 \u2192 \u6309\u5B83\u7ED9\u7684\u539F\u56E0\u6362\u5F15\u64CE\uFF08\`--agent\`\uFF09\u6216\u8865\u6E05\u9700\u6C42\u540E\u91CD\u4E0B\uFF0C\u518D \`--wait\`\u3002`,
141161
+ ` - **\u4E0D\u8981\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD**\u2014\u2014\u6CA1\u4EA4\u4E1C\u897F\u5C31\u6536\u5DE5\u4F1A\u88AB\u5224\u96F6\u4EA7\u51FA\uFF1B**\u4E5F\u4E0D\u8981\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status \u8F6E\u8BE2**\u2014\u2014\u6BCF\u67E5\u4E00\u6B21\u90FD\u8981\u63A8\u7406\u4E00\u6B21\u3002\u547D\u4EE4\u82E5\u88AB\u6267\u884C\u5DE5\u5177\u7684\u5355\u6761\u8D85\u65F6\u6253\u65AD\u6216\u8D85\u65F6\u9000\u51FA\uFF0C\u539F\u6837\u518D\u8DD1\u4E00\u6B21 \`--wait\` \u63A5\u7740\u7B49\uFF08\u5B83\u7B49\u7684\u662F\u753B\u677F\uFF0C\u4E0D\u4F1A\u91CD\u590D\u4E0B\u5355\uFF09\u3002`,
141162
+ `- \u88AB\u753B\u677F\u5DE1\u68C0\u7684\u4FE1\u5524\u9192\u65F6\uFF1A\u5148\u770B\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355 board.link \u91CC\u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u662F\u4E0D\u662F\u5C31\u662F\u4FE1\u91CC\u90A3\u4E00\u8F6E\u2014\u2014\u662F\u5C31 \`oasis resolve <\u4FE1 id>\`\uFF0C\u522B\u91CD\u590D propose\u3002`
140906
141163
  ] : (() => {
140907
141164
  const codeRepo = args.codeRepo ?? null;
140908
141165
  const repos = typeDef?.contentType === "code" ? codeRepo?.repos : void 0;
@@ -140939,16 +141196,32 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
140939
141196
  //
140940
141197
  // 这些字段不能成为「没做完也能交」的出口:真正未满足交付标准时继续完成,外部阻塞走 oasis gap。
140941
141198
  // 四项本身只传递一份已达标成果的非阻塞残余信息,不参与 conclude 判据。
140942
- ...args.action === "review" ? [] : [
141199
+ //
141200
+ // ★ 2026-09-23(语菲,验收用例 H-01):此前只列参数名、没说挂在哪条命令上,agent 常把它们写到
141201
+ // `oasis conclude` 上——conclude 只收 `--note`,其余参数被静默丢掉(v2 上 143 次执行里写过这四项
141202
+ // 16 次,6 次写在 conclude 上、全丢)。所以点名「是 propose 的参数」并给一条**追加式**示例:
141203
+ // 只示范「在那条命令末尾加上什么」,不给整条 propose——design(交稿目录不是 deliverable/)、
141204
+ // 分批续跑(要带 --checkpoint --continue)各有自己的提交命令,照抄一整条会交错目录或漏参数。
141205
+ // 「没有就连参数名也不写」:只写参数名不跟值会被 parseArgs 拒(CLI 侧已对这四项放宽为空值,
141206
+ // 但未升级的节点包仍会拒),提示词也把这条路堵上。
141207
+ //
141208
+ // 集成会话(integrating || queueTaskDue)不出这段:那一档「怎么提交」明令「绝不要用 oasis propose」,
141209
+ // 而 `oasis integrate` 也不收这四项——再教「这是 propose 的参数」只会把它引去 propose,
141210
+ // 队列从 N 变 N+1、随后 conclude 撞 queue not quiescent(本文件「怎么提交」那段注释记的卡死形状)。
141211
+ ...args.action === "review" || integrating || queueTaskDue ? [] : [
140943
141212
  ``,
140944
141213
  `### \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`,
141214
+ `\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`,
141215
+ `\`--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`,
141216
+ `\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
141217
  `- \`--unresolved\`\uFF1A\u4EFB\u52A1\u8303\u56F4\u4E4B\u5916\u7684\u5DF2\u77E5\u95EE\u9898\uFF1B`,
140948
141218
  `- \`--risks\`\uFF1A\u6210\u679C\u53EF\u4EA4\u4ED8\uFF0C\u4F46\u4F7F\u7528\u65F6\u9700\u6CE8\u610F\u7684\u6B8B\u4F59\u98CE\u9669\uFF1B`,
140949
141219
  `- \`--gaps\`\uFF1A\u4E0D\u5C5E\u4E8E\u672C\u6B21\u5F3A\u5236\u9A8C\u6536\u6761\u4EF6\u7684\u9A8C\u8BC1\u8303\u56F4\u9650\u5236\uFF1B`,
140950
141220
  `- \`--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`,
141221
+ `\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>"\``,
141222
+ `\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`
141223
+ ],
141224
+ ...args.action === "review" ? [] : [
140952
141225
  ``,
140953
141226
  `### \u4F1A\u8BDD\u53EF\u80FD\u88AB\u780D\uFF1A\u4E2D\u9014\u7559\u4E2A\u65AD\u70B9`,
140954
141227
  // ★ 2026-08-18:`continuity-checkpoint` 此前在任务书里出现 **0 次**——说明书只在
@@ -227247,14 +227520,13 @@ function frameTokenTotal(usage) {
227247
227520
  return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
227248
227521
  }
227249
227522
  async function backfillRunModel(target, run, model, modelSource, warn) {
227250
- const effectiveModel = actualModelId(model);
227251
- if (!effectiveModel) return;
227252
- if (hasModelName(run.effectiveModel)) return;
227523
+ const patch = preferRunModel(
227524
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
227525
+ { effectiveModel: model, modelSource: modelSource === "requested" ? "requested" : "runtime" }
227526
+ );
227527
+ if (!patch) return;
227253
227528
  try {
227254
- await target.store.updateRun(target.runId, {
227255
- effectiveModel,
227256
- modelSource: modelSource === "requested" ? "requested" : "runtime"
227257
- });
227529
+ await target.store.updateRun(target.runId, patch);
227258
227530
  } catch (err) {
227259
227531
  warn(`[usage-ledger] run ${target.runId} \u7684\u6A21\u578B\u540D\u8865\u5199\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4F1A\u8BDD\uFF09\uFF1A${String(err)}`);
227260
227532
  }
@@ -228020,8 +228292,10 @@ var init_sink = __esm({
228020
228292
  const isErr = status === "failed" || status === "timeout";
228021
228293
  const errorFields = isErr ? { errorCode: update.exit.reason ?? "error", errorMessage: formatExitError(update.exit) } : {};
228022
228294
  const exitModel = actualModelId(update.exit.model);
228023
- const model = exitModel ?? actualModelId(st.observedModel) ?? null;
228024
- const modelSource = exitModel && update.exit.modelSource === "requested" ? "requested" : "runtime";
228295
+ const observedModel = actualModelId(st.observedModel);
228296
+ const exitIsRequestedOnly = update.exit.modelSource === "requested";
228297
+ const model = (exitIsRequestedOnly ? observedModel ?? exitModel : exitModel ?? observedModel) ?? null;
228298
+ const modelSource = model && model === exitModel && exitIsRequestedOnly && !observedModel ? "requested" : "runtime";
228025
228299
  const exitRtId = typeof update.exit.runtimeSessionId === "string" && update.exit.runtimeSessionId.trim() ? update.exit.runtimeSessionId.trim() : void 0;
228026
228300
  if (exitRtId) st.baseMetadata = { ...st.baseMetadata, runtimeSessionId: exitRtId };
228027
228301
  const exitWorkdir = typeof update.exit.workdirRef === "string" && update.exit.workdirRef.trim() ? update.exit.workdirRef.trim() : void 0;
@@ -228052,6 +228326,12 @@ var init_sink = __esm({
228052
228326
  createdAt: update.exit.at
228053
228327
  });
228054
228328
  }
228329
+ const currentRun = model ? await this.store.getRun(st.runId).catch(() => null) : null;
228330
+ const modelPatch = model ? preferRunModel(
228331
+ { effectiveModel: currentRun?.effectiveModel, modelSource: currentRun?.modelSource },
228332
+ { effectiveModel: model, modelSource },
228333
+ "replace"
228334
+ ) : null;
228055
228335
  const finished = await this.store.updateRun(st.runId, {
228056
228336
  status,
228057
228337
  endedAt: update.exit.at,
@@ -228067,7 +228347,7 @@ var init_sink = __esm({
228067
228347
  // token/成本来自 adapter 解析 stream-json(含 cache);不带则不写、保持 null。
228068
228348
  ...update.exit.usage ? { usage: update.exit.usage } : {},
228069
228349
  // effectiveModel/modelSource 喂用量聚合(聚合键含 model);与 chat/finishRun 路径一致。
228070
- ...model ? { effectiveModel: model, modelSource } : {},
228350
+ ...modelPatch ?? {},
228071
228351
  // metadata 整列替换:必须带上 baseMetadata(bundleManifest/runtimeSessionId)一起写回。
228072
228352
  // model 有则并入(前端 findModelInValue 直接读 metadata.model)。**不能只在有 model 时才写**
228073
228353
  // ——否则无 model 的 run(如回捕型 runtime 没报模型)会把 exit 校正过的会话号丢掉。
@@ -228422,10 +228702,8 @@ function wireRecoveredChatTurn(deps) {
228422
228702
  live.emit({ type: "done", ...plan.runtimeSessionId ? { sessionId: plan.runtimeSessionId } : {} });
228423
228703
  live.finish(failed ? "error" : "done");
228424
228704
  deps.unregister();
228425
- const nativeSessionMetadata = info.runtimeSessionId && info.runtimeSessionId !== plan.runtimeSessionId ? nativeRuntimeSessionMetadata(
228426
- (await trace.getRun(plan.runId).catch(() => null))?.metadata,
228427
- info.runtimeSessionId
228428
- ) : void 0;
228705
+ const currentRun = await trace.getRun(plan.runId).catch(() => null);
228706
+ const nativeSessionMetadata = info.runtimeSessionId && info.runtimeSessionId !== plan.runtimeSessionId ? nativeRuntimeSessionMetadata(currentRun?.metadata, info.runtimeSessionId) : void 0;
228429
228707
  enqueueTrace(async () => {
228430
228708
  await appender.append([{
228431
228709
  eventType: "run.finished",
@@ -228434,7 +228712,7 @@ function wireRecoveredChatTurn(deps) {
228434
228712
  startedAt: finishedAt
228435
228713
  }]);
228436
228714
  await appender.update({
228437
- ...runTerminalPatch(info, plan.startedAt, finishedAt),
228715
+ ...runTerminalPatch(info, plan.startedAt, finishedAt, currentRun),
228438
228716
  // 运行时自己退干净了,但平台没把输出接全——运行记录不许写「成功」(Bug 0123)。
228439
228717
  // 退出帧本身已经是失败时保持它的归因,只补一句缺口说明。
228440
228718
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
@@ -228658,7 +228936,7 @@ async function reconcileChatExitFrame(deps) {
228658
228936
  ];
228659
228937
  await deps.trace.appendEvents(row.runId, seqless.map((e, i) => ({ ...e, seq: baseSeq + i + 1 })));
228660
228938
  await deps.trace.updateRun(row.runId, {
228661
- ...runTerminalPatch(info, run.startedAt, finishedAt),
228939
+ ...runTerminalPatch(info, run.startedAt, finishedAt, run),
228662
228940
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
228663
228941
  // 首轮占位会话号 → runtime 报回的原生号(Bug 0171,判据见本函数上方那一段)。
228664
228942
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
@@ -228887,6 +229165,7 @@ var init_chat_recovery = __esm({
228887
229165
  "use strict";
228888
229166
  init_file_restore();
228889
229167
  init_continuation3();
229168
+ init_src();
228890
229169
  init_chat_parts();
228891
229170
  init_sink();
228892
229171
  init_usage_ledger();
@@ -228914,7 +229193,7 @@ var init_chat_recovery = __esm({
228914
229193
  const existing = formatExitError(info);
228915
229194
  return { errorMessage: existing ? `${existing}\uFF1B${gap}` : gap };
228916
229195
  };
228917
- runTerminalPatch = (info, startedAt, endedAt) => ({
229196
+ runTerminalPatch = (info, startedAt, endedAt, current) => ({
228918
229197
  status: classifySessionExitStatus(info),
228919
229198
  endedAt,
228920
229199
  durationMs: Date.parse(endedAt) - Date.parse(startedAt),
@@ -228927,7 +229206,11 @@ var init_chat_recovery = __esm({
228927
229206
  // 不能用 null 覆盖,否则恢复轮的 token 会掉进「未知模型」桶。
228928
229207
  // 伪型号(`default[]` / `auto`)不是模型名,不写;请求值(`requested`)与实际值分开记来源
228929
229208
  // ——重挂路与 chat 主路、派发路三处必须同一判据,否则同一条 run 换条路收尾归属就变了。
228930
- ...chatExitModelFields(info.model, null, null, info.modelSource)
229209
+ ...preferRunModel(
229210
+ { effectiveModel: current?.effectiveModel, modelSource: current?.modelSource },
229211
+ chatExitModelFields(info.model, null, null, info.modelSource),
229212
+ "replace"
229213
+ ) ?? {}
228931
229214
  });
228932
229215
  }
228933
229216
  });
@@ -248171,8 +248454,16 @@ var init_service8 = __esm({
248171
248454
  errorCode: input.errorCode ?? null,
248172
248455
  errorMessage: input.errorMessage ?? null,
248173
248456
  usage: input.usage ?? null,
248174
- effectiveModel: input.effectiveModel ?? run.effectiveModel ?? null,
248175
- modelSource: input.modelSource ?? run.modelSource ?? null,
248457
+ // 模型那两格按取值优先级挑(上游诊断 ④),不是无条件「收尾值优先」:`input` 里可能只剩
248458
+ // 请求值(claude 一条 init 都没吐就退出、ACP `set_model` 被接受但运行时从没回报),
248459
+ // 而 run 行上可能已经由运行中检查点补上了运行时回报的**实际值**——请求值顶掉实际值,
248460
+ // 这一轮就永远记在一个没跑过的型号头上(PR #2213 评审阻断项 1)。同档以收尾值为准
248461
+ // (`replace`):收尾只写一次,且它是这一轮解析链的最终结论。
248462
+ ...preferRunModel(
248463
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
248464
+ { effectiveModel: input.effectiveModel, modelSource: input.modelSource },
248465
+ "replace"
248466
+ ) ?? { effectiveModel: run.effectiveModel ?? null, modelSource: run.modelSource ?? null },
248176
248467
  transcriptRef: input.transcriptRef ?? null
248177
248468
  };
248178
248469
  const updated = await this.store.updateRun(id, patch);
@@ -277331,11 +277622,18 @@ function messageError(m2, status) {
277331
277622
  }
277332
277623
  return { error: null, errorCode: null };
277333
277624
  }
277625
+ function ownerIsWorking(model, a) {
277626
+ if (a.latestWorkState !== "running") return false;
277627
+ const latest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.lane !== "reply").sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277628
+ return latest !== void 0 && latest.author === a.owner;
277629
+ }
277334
277630
  var RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, SINCE_MARKER_PREFIX, boardWatchSinceMarker, parseSinceMarker, TERMINAL_RUN_STATUS, MAX_REASON_CHARS, MAX_LISTED_FILES, BoardWatcher;
277335
277631
  var init_board_watch = __esm({
277336
277632
  "../server/src/design/board-watch.ts"() {
277337
277633
  "use strict";
277634
+ init_src();
277338
277635
  init_src7();
277636
+ init_src2();
277339
277637
  RUN_MARKER_PREFIX = "[od-board-watch run:";
277340
277638
  boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
277341
277639
  parseRunMarker = (body2) => {
@@ -277368,6 +277666,14 @@ var init_board_watch = __esm({
277368
277666
  seenConvAt = /* @__PURE__ */ new Map();
277369
277667
  /** 公司 → 上次记下的失败原文(同样的错只记一次;成功一轮就清掉)。 */
277370
277668
  companyLastError = /* @__PURE__ */ new Map();
277669
+ /** `<公司>\0<artifactId>\0<轮次键>` → 这一轮已被取走(head 来自它或更晚的一轮),不再重判。只在内存,不写假信。 */
277670
+ takenRounds = /* @__PURE__ */ new Set();
277671
+ /** `<公司>\0<boardKey>` → (runId → endedAt):判断 head 的快照来源 run 是不是「更晚的一轮」用。 */
277672
+ roundEndedAt = /* @__PURE__ */ new Map();
277673
+ /** `<公司>\0<revisionId>` → 该版 board.link 的快照来源 runId(null = 没有/读不到)。revision 内容不可变,缓存安全。 */
277674
+ revisionSourceRun = /* @__PURE__ */ new Map();
277675
+ /** 因 owner 在做而压着没写的 `<公司>\0<artifactId>`(只为日志只打一次)。 */
277676
+ deferredLogged = /* @__PURE__ */ new Set();
277371
277677
  async odGet(path47) {
277372
277678
  const fetchImpl = this.deps.fetchImpl ?? fetch;
277373
277679
  try {
@@ -277428,7 +277734,8 @@ var init_board_watch = __esm({
277428
277734
  if (listCompanies && kernelFor) {
277429
277735
  for (const companyId of await listCompanies()) {
277430
277736
  try {
277431
- await this.tickCompany(companyId, await kernelFor(companyId), now, denied);
277737
+ const blobs = this.deps.blobsFor ? await this.deps.blobsFor(companyId) : void 0;
277738
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied, blobs);
277432
277739
  this.companyLastError.delete(companyId);
277433
277740
  } catch (err) {
277434
277741
  const msg = String(err);
@@ -277438,7 +277745,7 @@ var init_board_watch = __esm({
277438
277745
  }
277439
277746
  }
277440
277747
  } else {
277441
- await this.tickCompany("", this.deps.kernel, now, denied);
277748
+ await this.tickCompany("", this.deps.kernel, now, denied, this.deps.blobs);
277442
277749
  }
277443
277750
  if (denied.length > 0) {
277444
277751
  const codes = [...new Set(denied.map((d) => d.status))].join("/");
@@ -277449,7 +277756,7 @@ var init_board_watch = __esm({
277449
277756
  }
277450
277757
  }
277451
277758
  /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277452
- async tickCompany(company, kernel, now, denied) {
277759
+ async tickCompany(company, kernel, now, denied, blobs) {
277453
277760
  const { boardKeyOf, log: log3 } = this.deps;
277454
277761
  const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
277455
277762
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
@@ -277484,12 +277791,40 @@ var init_board_watch = __esm({
277484
277791
  continue;
277485
277792
  }
277486
277793
  if (recordedSince === null && !this.pinnedSince.has(pinKey)) this.pinnedSince.set(pinKey, since);
277487
- const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key)).sort((x2, y) => x2.endedAt - y.endedAt);
277794
+ const endedAtOf = this.roundEndedAt.get(`${company}\0${boardKey}`) ?? /* @__PURE__ */ new Map();
277795
+ this.roundEndedAt.set(`${company}\0${boardKey}`, endedAtOf);
277796
+ for (const r of read3.value.rounds) if (r.runId !== null) endedAtOf.set(r.runId, r.endedAt);
277797
+ const takenKey = (r) => `${company}\0${a.id}\0${r.key}`;
277798
+ const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key) && !this.takenRounds.has(takenKey(r))).sort((x2, y) => x2.endedAt - y.endedAt);
277799
+ const deferKey = `${company}\0${a.id}`;
277800
+ if (fresh.length > 0 && ownerIsWorking(model, a)) {
277801
+ if (!this.deferredLogged.has(deferKey)) {
277802
+ this.deferredLogged.add(deferKey);
277803
+ log3?.(`[board-watch] ${tag}${a.id} \u7684 owner \u6B63\u5728\u6267\u884C\u8FD9\u4E2A\u8282\u70B9\uFF0C\u677F ${boardKey} \u4E0A ${fresh.length} \u8F6E\u7ED3\u675F\u7684\u5BF9\u8BDD\u5148\u4E0D\u5199\u4FE1\uFF0C\u7B49\u5B83\u8FD9\u8F6E work \u7ED3\u675F\u518D\u770B`);
277804
+ }
277805
+ continue;
277806
+ }
277807
+ this.deferredLogged.delete(deferKey);
277808
+ const sourceRuns = fresh.length > 0 && blobs ? await this.headSourceRuns(company, blobs, model, a, log3, tag) : [];
277809
+ const takenBy = (r) => {
277810
+ for (const src of sourceRuns) {
277811
+ if (r.runId !== null && src === r.runId) return src;
277812
+ const srcEnded = endedAtOf.get(src);
277813
+ if (srcEnded !== void 0 && srcEnded >= r.endedAt) return src;
277814
+ }
277815
+ return null;
277816
+ };
277488
277817
  const seen = /* @__PURE__ */ new Set();
277489
277818
  for (const r of fresh) {
277490
277819
  if (written >= maxPerTick) break;
277491
277820
  if (seen.has(r.key)) continue;
277492
277821
  seen.add(r.key);
277822
+ const src = takenBy(r);
277823
+ if (src !== null) {
277824
+ this.takenRounds.add(takenKey(r));
277825
+ log3?.(`[board-watch] ${tag}${a.id} \u2190 \u677F ${boardKey} \u4E00\u8F6E\u5BF9\u8BDD\u7ED3\u675F\uFF08${r.key.slice(0, 12)} ${r.status}\uFF09\uFF0C\u5F53\u524D\u7248\u672C\u7684\u5FEB\u7167\u6765\u6E90 run ${src} \u5DF2\u8986\u76D6\u5B83 \u2192 \u4E0D\u5199\u4FE1`);
277826
+ continue;
277827
+ }
277493
277828
  const ok3 = r.status === "succeeded";
277494
277829
  const reason = ok3 ? null : r.error ?? (r.runId !== null ? await this.runErrorFromRegistry(boardKey, r.runId) : null);
277495
277830
  await kernel.annotate({ artifactId: a.id, actor, body: this.letterBody(a.id, boardKey, r, since, reason), kind: "comment" });
@@ -277502,6 +277837,36 @@ var init_board_watch = __esm({
277502
277837
  }
277503
277838
  }
277504
277839
  }
277840
+ /**
277841
+ * 当前 head 与最近一版装箱单(两者可能不同:head 在跑空壳/被打回时回落)各自 board.link 里的快照来源 runId。
277842
+ * 读不到(非装箱单、没有 board.link、blob 缺失、占位 status-pull)的不算。
277843
+ */
277844
+ async headSourceRuns(company, blobs, model, a, log3, tag) {
277845
+ const candidates = [];
277846
+ const head = a.currentRev ? model.revisions.get(a.currentRev) : void 0;
277847
+ if (head) candidates.push(head);
277848
+ const latestManifest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.contentKind === "manifest" && r.contentRef.length > 0).sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277849
+ if (latestManifest && latestManifest.id !== head?.id) candidates.push(latestManifest);
277850
+ const out = [];
277851
+ for (const rev of candidates) {
277852
+ if (rev.contentKind !== "manifest" || rev.contentRef.length === 0) continue;
277853
+ const cacheKey = `${company}\0${rev.id}`;
277854
+ let src = this.revisionSourceRun.get(cacheKey);
277855
+ if (src === void 0) {
277856
+ try {
277857
+ const manifest = parseManifest(await blobs.get(rev.contentRef));
277858
+ const entry = manifest.entries.find((e) => e.path === BOARD_LINK_FILE);
277859
+ src = entry ? parseBoardLinkSourceRun(new TextDecoder().decode(await blobs.get(entry.hash))) : null;
277860
+ } catch (err) {
277861
+ log3?.(`[board-watch] ${tag}\u8BFB ${a.id} \u7248\u672C ${rev.id} \u7684 board.link \u5931\u8D25\uFF08${String(err)}\uFF09\uFF0C\u6309\u300C\u4E0D\u77E5\u9053\u6765\u81EA\u54EA\u4E00\u8F6E\u300D\u5904\u7406`);
277862
+ src = null;
277863
+ }
277864
+ this.revisionSourceRun.set(cacheKey, src);
277865
+ }
277866
+ if (src !== null) out.push(src);
277867
+ }
277868
+ return out;
277869
+ }
277505
277870
  letterBody(artifactId, boardKey, r, since, reason) {
277506
277871
  const marker = `${boardWatchRunMarker(r.key)} ${boardWatchSinceMarker(since)}`;
277507
277872
  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()}`;
@@ -277512,7 +277877,8 @@ var init_board_watch = __esm({
277512
277877
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD\u8DD1\u5B8C\u4E86\uFF08${round}\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u4EA7\u51FA\uFF1A`,
277513
277878
  files,
277514
277879
  ``,
277515
- `\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`
277880
+ `\u5148\u770B\u8FD9\u4E00\u8F6E\u662F\u4E0D\u662F\u5DF2\u7ECF\u4EA4\u8FC7\uFF1A\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355\u91CC board.link \u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u5982\u679C\u5C31\u662F ${r.runId ?? "\u8FD9\u4E00\u8F6E"}\uFF0C\u8BF4\u660E\u5DF2\u53D6\u8D70\uFF0C\`oasis resolve <\u672C\u4FE1 id> --as resolved\` \u5373\u53EF\uFF0C\u522B\u91CD\u590D propose\u3002`,
277881
+ `\u6CA1\u4EA4\u8FC7\u5C31\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --wait --pull\`\uFF08\u677F\u4E0A\u8FD8\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u4F1A\u5148\u7B49\u5B83\u8DD1\u5B8C\uFF09\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\`\uFF0C\u968F\u540E \`oasis design status ${artifactId} --wait --pull\` \u7B49\u7ED3\u679C\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`
277516
277882
  ].join("\n");
277517
277883
  }
277518
277884
  const quoted = reason === null ? null : reason.length > MAX_REASON_CHARS ? `${reason.slice(0, MAX_REASON_CHARS)}\u2026` : reason;
@@ -277521,7 +277887,7 @@ var init_board_watch = __esm({
277521
277887
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD**\u6CA1\u8DD1\u6210**\uFF08\u7EC8\u6001=${r.status}\uFF0C${round}\uFF09\u3002`,
277522
277888
  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`,
277523
277889
  ``,
277524
- `\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`
277890
+ `\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} --turn continue --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\u3002\u91CD\u4E0B\u4E4B\u540E\u7ACB\u523B \`oasis design status ${artifactId} --wait --pull\` \u5728\u4F1A\u8BDD\u91CC\u7B49\u7ED3\u679C\uFF0C\u522B\u4E0B\u5355\u5C31\u6536\u5DE5\u3002\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277525
277891
  ].join("\n");
277526
277892
  }
277527
277893
  };
@@ -278654,6 +279020,7 @@ __export(src_exports2, {
278654
279020
  openNewEngine: () => openNewEngine,
278655
279021
  operatorCrossCompanyRead: () => operatorCrossCompanyRead,
278656
279022
  outputsPartProvider: () => outputsPartProvider,
279023
+ ownerIsWorking: () => ownerIsWorking,
278657
279024
  packMatches: () => packMatches,
278658
279025
  parentConversationTaskLines: () => parentConversationTaskLines,
278659
279026
  parseConnectorSkillId: () => parseConnectorSkillId,
@@ -286195,7 +286562,13 @@ async function startServe(opts) {
286195
286562
  metadata: { ...base, dispatchId: handle.id },
286196
286563
  // 恢复路径拿不到 dispatchChat 闭包里的 fallback 解析结果,开跑时先写进账本;退出帧若上报
286197
286564
  // 更精确 model 仍会覆盖为 runtime 来源。
286198
- ...chatModel ? { effectiveModel: chatModel, modelSource: chatModelSource } : {}
286565
+ // 这是**请求值**(会话选择 / 员工配置 / runtime 默认),按 ④ 排在实际值之下:采集点 1 的
286566
+ // 首帧检查点可能已经先一步把运行时回报的实际值写进这一行(两条路都是异步入队,谁先到不定),
286567
+ // 预写不能把它顶掉。
286568
+ ...preferRunModel(
286569
+ { effectiveModel: run.effectiveModel, modelSource: run.modelSource },
286570
+ { effectiveModel: chatModel, modelSource: chatModelSource }
286571
+ ) ?? {}
286199
286572
  });
286200
286573
  });
286201
286574
  const buffered = [];
@@ -286267,11 +286640,17 @@ async function startServe(opts) {
286267
286640
  }]);
286268
286641
  const usage = info.usage ?? null;
286269
286642
  const modelFields = chatExitModelFields(info.model, chatModel, chatModelSource, info.modelSource);
286643
+ const needsNativeSessionMetadata = !!info.runtimeSessionId && info.runtimeSessionId !== runtimeSessionId;
286644
+ const currentRun = needsNativeSessionMetadata || "effectiveModel" in modelFields ? await (await traceStoreFor(runCompanyId)).getRun(traceRunId).catch(() => null) : null;
286270
286645
  let nativeSessionMetadata;
286271
- if (info.runtimeSessionId && info.runtimeSessionId !== runtimeSessionId) {
286272
- const run = await (await traceStoreFor(runCompanyId)).getRun(traceRunId).catch(() => null);
286273
- nativeSessionMetadata = nativeRuntimeSessionMetadata(run?.metadata, info.runtimeSessionId);
286646
+ if (needsNativeSessionMetadata) {
286647
+ nativeSessionMetadata = nativeRuntimeSessionMetadata(currentRun?.metadata, info.runtimeSessionId);
286274
286648
  }
286649
+ const modelPatch = preferRunModel(
286650
+ { effectiveModel: currentRun?.effectiveModel, modelSource: currentRun?.modelSource },
286651
+ { effectiveModel: modelFields.effectiveModel, modelSource: modelFields.modelSource },
286652
+ "replace"
286653
+ ) ?? {};
286275
286654
  await traceAppender.update({
286276
286655
  status: classifySessionExitStatus(info),
286277
286656
  endedAt,
@@ -286281,7 +286660,7 @@ async function startServe(opts) {
286281
286660
  ...!isHealthySessionExit(info) && info.reason !== "cancelled" ? { errorCode: info.reason ?? "error", errorMessage: formatExitError(info) } : {},
286282
286661
  transcriptRef: info.transcriptRef ?? null,
286283
286662
  usage,
286284
- ...modelFields,
286663
+ ...modelPatch,
286285
286664
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
286286
286665
  });
286287
286666
  if (usage) await settleRunUsage(await traceStoreFor(runCompanyId), traceRunId);
@@ -289142,6 +289521,8 @@ ${nodeFault}` : "");
289142
289521
  return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
289143
289522
  },
289144
289523
  kernelFor: async (companyId) => (await getEngine(companyId)).kernel,
289524
+ // 读 head 装箱单里的 board.link(「快照来源 run」)判断这一轮是否已被取走,已取走就不写信
289525
+ blobsFor: async (companyId) => (await getEngine(companyId)).blobs,
289145
289526
  odBase,
289146
289527
  ...odToken ? { odToken } : {},
289147
289528
  boardKeyOf: deriveBoardKey,
@@ -292401,29 +292782,32 @@ var COMMAND_DECLS = {
292401
292782
  flags: [
292402
292783
  { name: "message", desc: "\u6D88\u606F\u5185\u5BB9", required: true },
292403
292784
  { name: "turn", desc: "\u5BF9\u8BDD\u8F6E\u6B21\uFF08first | continue\uFF09" },
292404
- { name: "async", desc: "\u5F02\u6B65\u6A21\u5F0F\uFF08\u8D77\u5B8C\u5373\u8FD4\uFF09" },
292785
+ { name: "async", desc: "\u4E0B\u5355\u5373\u8FD4\uFF1B\u968F\u540E\u7528 design status --wait --pull \u963B\u585E\u7B49\u7ED3\u679C" },
292405
292786
  { name: "system", desc: "\u7CFB\u7EDF\u63D0\u793A" }
292406
292787
  ],
292407
292788
  examples: ['oasis design chat artifact:design:xyz --message "\u628A\u6309\u94AE\u6539\u6210\u84DD\u8272" --turn continue']
292408
292789
  },
292409
292790
  "design status": {
292410
292791
  group: "\u8BBE\u8BA1\u7A3F",
292411
- usage: "oasis design status <artifactId> [--pull]",
292412
- description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002",
292792
+ usage: "oasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]",
292793
+ description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002--wait\uFF1A\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5230\u5168\u90E8\u7ED3\u675F\uFF08\u4E0D\u7BA1\u662F\u8C01\u8D77\u7684\uFF09\uFF0C\u518D\u6253\u5370\u7ED3\u679C\uFF1B\u8D85\u65F6\u975E\u96F6\u9000\u51FA\u3002",
292413
292794
  positional: [
292414
292795
  { name: "<artifactId>", required: true, desc: "\u4EA7\u7269 id" }
292415
292796
  ],
292416
292797
  flags: [
292417
- { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730" }
292798
+ { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730\uFF08\u5E26 --wait \u65F6\uFF1A\u7B49\u5B8C\u4E14\u6700\u8FD1\u4E00\u8F6E\u6210\u529F\u624D\u62C9\uFF09" },
292799
+ { name: "wait", desc: "\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5B83\u4EEC\u7ED3\u675F\uFF08\u540C gh pr checks --watch\uFF09", boolean: true },
292800
+ { name: "wait-timeout", desc: "--wait \u6700\u591A\u7B49\u51E0\u5206\u949F\uFF08\u9ED8\u8BA4 100\uFF09" },
292801
+ { name: "wait-interval", desc: "--wait \u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 20\uFF09" }
292418
292802
  ],
292419
- examples: ["oasis design status artifact:design:xyz --pull"]
292803
+ examples: ["oasis design status artifact:design:xyz --wait --pull", "oasis design status artifact:design:xyz --pull"]
292420
292804
  },
292421
292805
  design: {
292422
292806
  group: "\u8BBE\u8BA1\u7A3F",
292423
292807
  usage: "oasis design <chat|status> <artifactId> ...",
292424
292808
  description: "\u8BBE\u8BA1\u4EA7\u7269\u76F8\u5173\u64CD\u4F5C\u3002\u8FDE\u54EA\u4E2A open-design\uFF1A\u5458\u5DE5\u5F00\u4E86 open-design \u8FDE\u63A5\u5668\u5C31\u8FDE\u5B83\u767B\u8BB0\u7684\u5730\u5740\u5E76\u5E26\u4EE4\u724C\uFF08\u4EFB\u610F\u8282\u70B9\u53EF\u7528\uFF09\uFF0C\u5426\u5219 $OASIS_OPEN_DESIGN_URL\uFF0C\u518D\u5426\u5219\u672C\u673A\u3002",
292425
292809
  subCommands: ["design chat", "design status"],
292426
- examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async']
292810
+ examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async', "oasis design status artifact:design:xyz --wait --pull"]
292427
292811
  },
292428
292812
  // —— 图机制(§6/§7) ——
292429
292813
  annotate: {
@@ -294035,6 +294419,7 @@ ${buildCommandIndex()}
294035
294419
  ${USAGE_FOOTER}`;
294036
294420
  var COMMAND_HELP = buildCommandHelp();
294037
294421
  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";
294422
+ var EMPTY_OK_NOTE_FLAGS = /* @__PURE__ */ new Set(["unresolved", "risks", "gaps", "next"]);
294038
294423
  function parseArgs(argv) {
294039
294424
  const positional = [];
294040
294425
  const flags = /* @__PURE__ */ new Map();
@@ -294061,6 +294446,10 @@ function parseArgs(argv) {
294061
294446
  const value2 = argv[i + 1];
294062
294447
  const key2 = a.slice(2);
294063
294448
  if (value2 === void 0 || value2.startsWith("--")) {
294449
+ if (EMPTY_OK_NOTE_FLAGS.has(key2)) {
294450
+ flags.set(key2, "");
294451
+ continue;
294452
+ }
294064
294453
  if (!booleanFlags.has(key2)) throw new Error(`flag ${a} \u9700\u8981\u4E00\u4E2A\u503C`);
294065
294454
  flags.set(key2, "true");
294066
294455
  } else {
@@ -294096,6 +294485,15 @@ function needPos(positional, i, usage) {
294096
294485
  if (v2 === void 0) throw new Error(`\u7528\u6CD5: ${usage}`);
294097
294486
  return v2;
294098
294487
  }
294488
+ function positiveNumberFlag(flags, name, dflt) {
294489
+ const raw = flags.get(name);
294490
+ if (raw === void 0) return dflt;
294491
+ const n = Number(raw);
294492
+ if (!Number.isFinite(n) || n <= 0) throw new Error(`--${name} \u8981\u4E00\u4E2A\u6B63\u6570\uFF08\u6536\u5230\uFF1A${raw}\uFF09`);
294493
+ return n;
294494
+ }
294495
+ var shortRunKey = (key2) => key2.startsWith("msg:") ? `\u6D88\u606F ${key2.slice(4, 12)}` : `run ${key2.slice(0, 8)}`;
294496
+ var clipReason = (reason) => reason.length > 300 ? `${reason.slice(0, 300)}\u2026` : reason;
294099
294497
  function semicolonList(raw, field) {
294100
294498
  if (raw === void 0) return {};
294101
294499
  const items = raw.split(";").map((x2) => x2.trim()).filter(Boolean);
@@ -294298,7 +294696,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
294298
294696
  }
294299
294697
  }
294300
294698
  }
294301
- var CLI_VERSION = "2.2.83" ? "2.2.83" : "dev";
294699
+ var CLI_VERSION = "2.2.85" ? "2.2.85" : "dev";
294302
294700
  function apiHeaders(token, company) {
294303
294701
  const headers = {
294304
294702
  authorization: `Bearer ${token}`,
@@ -296068,7 +296466,7 @@ ${res.warning}`);
296068
296466
  println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odLabel} \u2026`);
296069
296467
  const started = await startDesignChatTurn(baseTurnOpts);
296070
296468
  println(`[design] \u5DF2\u4E0B\u5355\uFF1Arun ${started.runId.slice(0, 8)}\uFF08board ${started.boardKey}, \u53EF\u89C1 user \u56DE\u5408 ${started.userMessageId.slice(0, 8)}\uFF09\u3002\u5F15\u64CE\u901A\u5E38\u8981 13\u201344 \u5206\u949F\u3002`);
296071
- println(`[design] \u4E4B\u540E\u7528 oasis design status ${artifactId} \u67E5\u8FDB\u5EA6/\u53D6\u8D27\uFF08--pull \u62C9\u4EA7\u7269\u5230\u672C\u5730\u8BFB\uFF09\uFF1B\u4E0D\u5FC5\u5B88\u7740\u7B49\u3002`);
296469
+ println(`[design] \u4E0B\u4E00\u6B65\u7ACB\u523B\u6267\u884C\uFF1Aoasis design status ${artifactId} --wait --pull\uFF08\u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF0C\u6700\u957F\u7B49 100 \u5206\u949F\uFF09\u3002\u522B\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD\uFF0C\u4E5F\u522B\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status\u3002`);
296072
296470
  break;
296073
296471
  }
296074
296472
  println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odLabel} \u2026`);
@@ -296082,46 +296480,82 @@ ${res.warning}`);
296082
296480
  break;
296083
296481
  }
296084
296482
  if (sub === "status") {
296085
- const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull]");
296483
+ const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull] [--wait]");
296086
296484
  const od = resolveOpenDesignEndpoint(flags.get("od-url"));
296087
296485
  const odBase = od.base;
296088
296486
  const odFetch = withOdToken(fetch, od.token);
296089
- const st = await fetchBoardStatus(odBase, odFetch, artifactId);
296487
+ let st;
296488
+ let waitedForRun = false;
296489
+ if (flags.get("wait") === "true") {
296490
+ const timeoutMin = positiveNumberFlag(flags, "wait-timeout", 100);
296491
+ const intervalSec = positiveNumberFlag(flags, "wait-interval", 20);
296492
+ const fmt = (ms) => `${Math.floor(ms / 6e4)} \u5206 ${Math.floor(ms % 6e4 / 1e3)} \u79D2`;
296493
+ const waited = await waitBoardSettled(odBase, odFetch, artifactId, {
296494
+ timeoutMs: timeoutMin * 6e4,
296495
+ intervalMs: intervalSec * 1e3,
296496
+ // 每次轮询打一行撑 liveness;状态没变只打短的一行,别刷屏
296497
+ onPoll: ({ live: live2, elapsedMs, changed, unreadable }) => {
296498
+ if (!changed) return println(`[design] \u2026\u4ECD\u5728\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296499
+ if (live2.length === 0 && unreadable) return println(`[design] \u8FD9\u6B21\u6CA1\u8BFB\u5230\u753B\u677F\u5BF9\u8BDD\u8BB0\u5F55\uFF08od \u6682\u65F6\u4E0D\u901A\uFF1F\uFF09\uFF0C\u63A5\u7740\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296500
+ const runs = live2.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`).join("\uFF1B");
296501
+ println(`[design] \u7B49\u753B\u677F\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\uFF1A${runs}\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF0C\u4E0A\u9650 ${timeoutMin} \u5206\u949F\uFF09`);
296502
+ }
296503
+ });
296504
+ st = waited.status;
296505
+ waitedForRun = waited.waited;
296506
+ if (!waited.settled) {
296507
+ const runs = waited.live.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}`).join("\u3001") || "\u5BF9\u8BDD\u8BB0\u5F55\u8BFB\u4E0D\u5230";
296508
+ println(`[design] \u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF0C\u753B\u677F\u4E0A\u8FD9\u4E00\u8F6E\u8FD8\u6CA1\u7ED3\u675F\uFF08${runs}\uFF09\u2014\u2014\u5F15\u64CE\u8FD8\u5728\u8DD1\uFF0C\u53EF\u7A0D\u540E\u518D oasis design status ${artifactId} --wait${flags.get("pull") === "true" ? " --pull" : ""}\u3002\u522B\u53E6\u8D77\u65B0\u8F6E\u3002`);
296509
+ process.exitCode = 2;
296510
+ break;
296511
+ }
296512
+ if (waitedForRun) println(`[design] \u753B\u677F\u4E0A\u7684\u8F6E\u6B21\u90FD\u5DF2\u7ED3\u675F\uFF08\u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF09\u3002`);
296513
+ } else {
296514
+ st = await fetchBoardStatus(odBase, odFetch, artifactId);
296515
+ }
296090
296516
  if (!st.boardExists) {
296091
- println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async`);
296517
+ println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`);
296092
296518
  break;
296093
296519
  }
296094
- const live = st.runs.filter((r) => r.status === "running" || r.status === "queued");
296520
+ const live = liveBoardRuns(st);
296095
296521
  if (live.length > 0) {
296096
- for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1Arun ${r.runId.slice(0, 8)} \u72B6\u6001=${r.status}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u8FC7\u51E0\u5206\u949F\u518D status\uFF0C\u6216\u672C\u8F6E\u6536\u5DE5\u7B49\u91CD\u6D3E\u3002`);
296522
+ for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1A${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u7528 oasis design status ${artifactId} --wait --pull \u963B\u585E\u7B49\u5B83\u8DD1\u5B8C\u5E76\u62C9\u8D27\u3002`);
296097
296523
  } else if (st.runs.length > 0) {
296098
296524
  for (const r of st.runs.slice(0, 3)) {
296099
296525
  println(`[design] \u8FD1\u671F run\uFF1A${r.runId.slice(0, 8)} \u7EC8\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`);
296100
- if (r.status === "failed" && r.error) {
296101
- const reason = r.error.length > 300 ? `${r.error.slice(0, 300)}\u2026` : r.error;
296102
- println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${reason}`);
296103
- }
296526
+ if (r.status === "failed" && r.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(r.error)}`);
296527
+ }
296528
+ } else if ((st.turns ?? []).length > 0) {
296529
+ for (const t of (st.turns ?? []).slice(0, 3)) {
296530
+ println(`[design] \u8FD1\u671F run\uFF08\u5BF9\u8BDD\u8BB0\u5F55\uFF09\uFF1A${(t.runId ?? t.messageId ?? "?").slice(0, 8)} \u7EC8\u6001=${t.status}${t.agentId ? ` \u5F15\u64CE=${t.agentId}` : ""}`);
296531
+ if (t.status !== "succeeded" && t.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(t.error)}`);
296104
296532
  }
296105
296533
  } else {
296106
296534
  println(`[design] \u65E0\u5728\u4E16 run \u8BB0\u5F55\uFF08od \u91CD\u542F\u540E\u4E3A\u7A7A\uFF0C\u5C5E\u6B63\u5E38\uFF09\u2014\u2014\u770B\u4E0B\u9762\u677F\u4E0A\u4EA7\u7269\u5373\u6301\u4E45\u771F\u76F8\u3002`);
296107
296535
  }
296536
+ const last = latestEndedRun(st);
296537
+ const lastFailed = waitedForRun && last !== void 0 && last.status !== "succeeded";
296538
+ if (lastFailed) {
296539
+ println(`[design] \u521A\u7ED3\u675F\u7684\u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u6210\uFF08\u7EC8\u6001=${last.status}${last.agentId ? ` \u5F15\u64CE=${last.agentId}` : ""}\uFF09${last.error ? `\uFF1A${clipReason(last.error)}` : "\uFF0Cod \u6CA1\u8BB0\u539F\u56E0"}\u3002`);
296540
+ println(`[design] \u50CF\u989D\u5EA6/\u9650\u6D41/\u5F15\u64CE\u6545\u969C \u2192 \u6362\u5F15\u64CE\u91CD\u4E0B\uFF1Aoasis design chat ${artifactId} --turn continue --agent <\u53E6\u4E00\u4E2A\u5F15\u64CE> --message "<\u2026>" --async\uFF1B\u50CF\u9700\u6C42/\u6570\u636E\u4E0D\u6E05 \u2192 \u8865\u6E05\u695A\u540E\u91CD\u4E0B\uFF1B\u4E0B\u5B8C\u7167\u65E7 oasis design status ${artifactId} --wait --pull\u3002`);
296541
+ }
296108
296542
  if (st.artifacts.length === 0) {
296109
- println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? "\u7B49\u5F15\u64CE\u8DD1\u5B8C\u518D\u6765\u3002" : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async`}`);
296543
+ println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? `\u7B49\u5F15\u64CE\u8DD1\u5B8C\uFF1Aoasis design status ${artifactId} --wait --pull` : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`}`);
296110
296544
  break;
296111
296545
  }
296112
296546
  println(`[design] \u677F\u4E0A\u4EA4\u4ED8\u7269\uFF08\u6301\u4E45\uFF0C\u65B0\u2192\u65E7\uFF09\uFF1A`);
296113
296547
  for (const a of st.artifacts) println(` - ${a.path}\uFF08${a.kind ?? "?"}\uFF09status=${a.status ?? "?"}${a.mtime ? ` \xB7 ${new Date(a.mtime).toISOString()}` : ""}`);
296114
- if (flags.get("pull") === "true") {
296548
+ if (flags.get("pull") === "true" && !lastFailed) {
296115
296549
  const odPublic = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
296116
- const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, latestSucceededRun(st.runs)?.runId ?? "status-pull");
296550
+ const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, snapshotSourceRunId(st) ?? "status-pull");
296117
296551
  println(`[design] \u5DF2\u62C9\u5230\u672C\u5730\uFF1A${dir2}\uFF08\u5165\u53E3 ${entry}\uFF09`);
296118
296552
  println(`[design] \u8BFB\u5B8C\u6EE1\u610F\u5373\u4EA4\u7A3F\uFF1Aoasis propose ${artifactId} --from-dir ${dir2} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"`);
296119
- } else {
296553
+ } else if (!lastFailed) {
296120
296554
  println(`[design] \u53D6\u8D27\uFF1Aoasis design status ${artifactId} --pull\uFF08\u62C9\u5230\u672C\u5730\u76EE\u5F55\u8BFB\uFF0C\u8BFB\u5B8C propose --from-dir \u4EA4\u7A3F\uFF09`);
296121
296555
  }
296122
296556
  break;
296123
296557
  }
296124
- throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull]");
296558
+ throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]");
296125
296559
  }
296126
296560
  case "merge": {
296127
296561
  const artifactId = needPos(positional, 0, "oasis merge <artifactId> [revisionId]");
@@ -296165,6 +296599,10 @@ ${res.warning}`);
296165
296599
  note: flags.get("note")
296166
296600
  });
296167
296601
  println(message);
296602
+ const ignoredNotes = [...EMPTY_OK_NOTE_FLAGS].filter((k2) => flags.has(k2)).map((k2) => `--${k2}`);
296603
+ if (ignoredNotes.length > 0) {
296604
+ 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`);
296605
+ }
296168
296606
  break;
296169
296607
  }
296170
296608
  case "review": {
@@ -298214,7 +298652,7 @@ function shimScript() {
298214
298652
  }
298215
298653
 
298216
298654
  // src/index.ts
298217
- var PKG_VERSION = true ? "2.2.83" : "dev";
298655
+ var PKG_VERSION = true ? "2.2.85" : "dev";
298218
298656
  var LOCAL_BIN = localBin();
298219
298657
  var NPM_PREFIX = npmPrefix();
298220
298658
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.83",
3
+ "version": "2.2.85",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "5c5d91942396438773ec82f08aae69e2c0f52bf3"
29
+ "sourceHead": "760b80b59c0caa8c7469a804b84360f5ecf3a71a"
30
30
  }
31
31
  }