oasis_test_v2 2.2.83 → 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 +222 -46
  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
  }
@@ -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 次**——说明书只在
@@ -227247,14 +227384,13 @@ function frameTokenTotal(usage) {
227247
227384
  return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
227248
227385
  }
227249
227386
  async function backfillRunModel(target, run, model, modelSource, warn) {
227250
- const effectiveModel = actualModelId(model);
227251
- if (!effectiveModel) return;
227252
- 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;
227253
227392
  try {
227254
- await target.store.updateRun(target.runId, {
227255
- effectiveModel,
227256
- modelSource: modelSource === "requested" ? "requested" : "runtime"
227257
- });
227393
+ await target.store.updateRun(target.runId, patch);
227258
227394
  } catch (err) {
227259
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)}`);
227260
227396
  }
@@ -228020,8 +228156,10 @@ var init_sink = __esm({
228020
228156
  const isErr = status === "failed" || status === "timeout";
228021
228157
  const errorFields = isErr ? { errorCode: update.exit.reason ?? "error", errorMessage: formatExitError(update.exit) } : {};
228022
228158
  const exitModel = actualModelId(update.exit.model);
228023
- const model = exitModel ?? actualModelId(st.observedModel) ?? null;
228024
- 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";
228025
228163
  const exitRtId = typeof update.exit.runtimeSessionId === "string" && update.exit.runtimeSessionId.trim() ? update.exit.runtimeSessionId.trim() : void 0;
228026
228164
  if (exitRtId) st.baseMetadata = { ...st.baseMetadata, runtimeSessionId: exitRtId };
228027
228165
  const exitWorkdir = typeof update.exit.workdirRef === "string" && update.exit.workdirRef.trim() ? update.exit.workdirRef.trim() : void 0;
@@ -228052,6 +228190,12 @@ var init_sink = __esm({
228052
228190
  createdAt: update.exit.at
228053
228191
  });
228054
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;
228055
228199
  const finished = await this.store.updateRun(st.runId, {
228056
228200
  status,
228057
228201
  endedAt: update.exit.at,
@@ -228067,7 +228211,7 @@ var init_sink = __esm({
228067
228211
  // token/成本来自 adapter 解析 stream-json(含 cache);不带则不写、保持 null。
228068
228212
  ...update.exit.usage ? { usage: update.exit.usage } : {},
228069
228213
  // effectiveModel/modelSource 喂用量聚合(聚合键含 model);与 chat/finishRun 路径一致。
228070
- ...model ? { effectiveModel: model, modelSource } : {},
228214
+ ...modelPatch ?? {},
228071
228215
  // metadata 整列替换:必须带上 baseMetadata(bundleManifest/runtimeSessionId)一起写回。
228072
228216
  // model 有则并入(前端 findModelInValue 直接读 metadata.model)。**不能只在有 model 时才写**
228073
228217
  // ——否则无 model 的 run(如回捕型 runtime 没报模型)会把 exit 校正过的会话号丢掉。
@@ -228422,10 +228566,8 @@ function wireRecoveredChatTurn(deps) {
228422
228566
  live.emit({ type: "done", ...plan.runtimeSessionId ? { sessionId: plan.runtimeSessionId } : {} });
228423
228567
  live.finish(failed ? "error" : "done");
228424
228568
  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;
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;
228429
228571
  enqueueTrace(async () => {
228430
228572
  await appender.append([{
228431
228573
  eventType: "run.finished",
@@ -228434,7 +228576,7 @@ function wireRecoveredChatTurn(deps) {
228434
228576
  startedAt: finishedAt
228435
228577
  }]);
228436
228578
  await appender.update({
228437
- ...runTerminalPatch(info, plan.startedAt, finishedAt),
228579
+ ...runTerminalPatch(info, plan.startedAt, finishedAt, currentRun),
228438
228580
  // 运行时自己退干净了,但平台没把输出接全——运行记录不许写「成功」(Bug 0123)。
228439
228581
  // 退出帧本身已经是失败时保持它的归因,只补一句缺口说明。
228440
228582
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
@@ -228658,7 +228800,7 @@ async function reconcileChatExitFrame(deps) {
228658
228800
  ];
228659
228801
  await deps.trace.appendEvents(row.runId, seqless.map((e, i) => ({ ...e, seq: baseSeq + i + 1 })));
228660
228802
  await deps.trace.updateRun(row.runId, {
228661
- ...runTerminalPatch(info, run.startedAt, finishedAt),
228803
+ ...runTerminalPatch(info, run.startedAt, finishedAt, run),
228662
228804
  ...outputGapRunPatch(droppedFrames, exitIsFailure, info),
228663
228805
  // 首轮占位会话号 → runtime 报回的原生号(Bug 0171,判据见本函数上方那一段)。
228664
228806
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
@@ -228887,6 +229029,7 @@ var init_chat_recovery = __esm({
228887
229029
  "use strict";
228888
229030
  init_file_restore();
228889
229031
  init_continuation3();
229032
+ init_src();
228890
229033
  init_chat_parts();
228891
229034
  init_sink();
228892
229035
  init_usage_ledger();
@@ -228914,7 +229057,7 @@ var init_chat_recovery = __esm({
228914
229057
  const existing = formatExitError(info);
228915
229058
  return { errorMessage: existing ? `${existing}\uFF1B${gap}` : gap };
228916
229059
  };
228917
- runTerminalPatch = (info, startedAt, endedAt) => ({
229060
+ runTerminalPatch = (info, startedAt, endedAt, current) => ({
228918
229061
  status: classifySessionExitStatus(info),
228919
229062
  endedAt,
228920
229063
  durationMs: Date.parse(endedAt) - Date.parse(startedAt),
@@ -228927,7 +229070,11 @@ var init_chat_recovery = __esm({
228927
229070
  // 不能用 null 覆盖,否则恢复轮的 token 会掉进「未知模型」桶。
228928
229071
  // 伪型号(`default[]` / `auto`)不是模型名,不写;请求值(`requested`)与实际值分开记来源
228929
229072
  // ——重挂路与 chat 主路、派发路三处必须同一判据,否则同一条 run 换条路收尾归属就变了。
228930
- ...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
+ ) ?? {}
228931
229078
  });
228932
229079
  }
228933
229080
  });
@@ -248171,8 +248318,16 @@ var init_service8 = __esm({
248171
248318
  errorCode: input.errorCode ?? null,
248172
248319
  errorMessage: input.errorMessage ?? null,
248173
248320
  usage: input.usage ?? null,
248174
- effectiveModel: input.effectiveModel ?? run.effectiveModel ?? null,
248175
- 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 },
248176
248331
  transcriptRef: input.transcriptRef ?? null
248177
248332
  };
248178
248333
  const updated = await this.store.updateRun(id, patch);
@@ -286195,7 +286350,13 @@ async function startServe(opts) {
286195
286350
  metadata: { ...base, dispatchId: handle.id },
286196
286351
  // 恢复路径拿不到 dispatchChat 闭包里的 fallback 解析结果,开跑时先写进账本;退出帧若上报
286197
286352
  // 更精确 model 仍会覆盖为 runtime 来源。
286198
- ...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
+ ) ?? {}
286199
286360
  });
286200
286361
  });
286201
286362
  const buffered = [];
@@ -286267,11 +286428,17 @@ async function startServe(opts) {
286267
286428
  }]);
286268
286429
  const usage = info.usage ?? null;
286269
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;
286270
286433
  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);
286434
+ if (needsNativeSessionMetadata) {
286435
+ nativeSessionMetadata = nativeRuntimeSessionMetadata(currentRun?.metadata, info.runtimeSessionId);
286274
286436
  }
286437
+ const modelPatch = preferRunModel(
286438
+ { effectiveModel: currentRun?.effectiveModel, modelSource: currentRun?.modelSource },
286439
+ { effectiveModel: modelFields.effectiveModel, modelSource: modelFields.modelSource },
286440
+ "replace"
286441
+ ) ?? {};
286275
286442
  await traceAppender.update({
286276
286443
  status: classifySessionExitStatus(info),
286277
286444
  endedAt,
@@ -286281,7 +286448,7 @@ async function startServe(opts) {
286281
286448
  ...!isHealthySessionExit(info) && info.reason !== "cancelled" ? { errorCode: info.reason ?? "error", errorMessage: formatExitError(info) } : {},
286282
286449
  transcriptRef: info.transcriptRef ?? null,
286283
286450
  usage,
286284
- ...modelFields,
286451
+ ...modelPatch,
286285
286452
  ...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
286286
286453
  });
286287
286454
  if (usage) await settleRunUsage(await traceStoreFor(runCompanyId), traceRunId);
@@ -294035,6 +294202,7 @@ ${buildCommandIndex()}
294035
294202
  ${USAGE_FOOTER}`;
294036
294203
  var COMMAND_HELP = buildCommandHelp();
294037
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"]);
294038
294206
  function parseArgs(argv) {
294039
294207
  const positional = [];
294040
294208
  const flags = /* @__PURE__ */ new Map();
@@ -294061,6 +294229,10 @@ function parseArgs(argv) {
294061
294229
  const value2 = argv[i + 1];
294062
294230
  const key2 = a.slice(2);
294063
294231
  if (value2 === void 0 || value2.startsWith("--")) {
294232
+ if (EMPTY_OK_NOTE_FLAGS.has(key2)) {
294233
+ flags.set(key2, "");
294234
+ continue;
294235
+ }
294064
294236
  if (!booleanFlags.has(key2)) throw new Error(`flag ${a} \u9700\u8981\u4E00\u4E2A\u503C`);
294065
294237
  flags.set(key2, "true");
294066
294238
  } else {
@@ -294298,7 +294470,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
294298
294470
  }
294299
294471
  }
294300
294472
  }
294301
- var CLI_VERSION = "2.2.83" ? "2.2.83" : "dev";
294473
+ var CLI_VERSION = "2.2.84" ? "2.2.84" : "dev";
294302
294474
  function apiHeaders(token, company) {
294303
294475
  const headers = {
294304
294476
  authorization: `Bearer ${token}`,
@@ -296165,6 +296337,10 @@ ${res.warning}`);
296165
296337
  note: flags.get("note")
296166
296338
  });
296167
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
+ }
296168
296344
  break;
296169
296345
  }
296170
296346
  case "review": {
@@ -298214,7 +298390,7 @@ function shimScript() {
298214
298390
  }
298215
298391
 
298216
298392
  // src/index.ts
298217
- var PKG_VERSION = true ? "2.2.83" : "dev";
298393
+ var PKG_VERSION = true ? "2.2.84" : "dev";
298218
298394
  var LOCAL_BIN = localBin();
298219
298395
  var NPM_PREFIX = npmPrefix();
298220
298396
  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.84",
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": "aa2e3257b82571a32312b457698fdbe5e223d970"
30
30
  }
31
31
  }