oasis_test 0.1.108 → 0.1.110

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 +471 -77
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4092,7 +4092,26 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
4092
4092
  `- \`--unresolved\` **\u4F60\u77E5\u9053\u4F46\u6CA1\u89E3\u51B3\u7684** \u2014\u2014 \u514D\u5F97\u4E0B\u6E38\u91CD\u590D\u8E29\u4F60\u5DF2\u7ECF\u8E29\u8FC7\u7684\u5751\u3002\u4F8B\uFF1A\`--unresolved "\u7EBF\u4E0A\u914D\u989D\u6CA1\u786E\u8BA4"\``,
4093
4093
  `- \`--risks\` **\u4EA4\u4ED8\u7269\u672C\u8EAB\u6CA1\u9519\u3001\u4F46\u7528\u7684\u65F6\u5019\u8981\u5F53\u5FC3\u7684** \u2014\u2014 \u4F8B\uFF1A\`--risks "\u5E76\u53D1\u8C03\u7528\u53EF\u80FD\u89E6\u53D1\u9650\u6D41"\``,
4094
4094
  `- \`--next\` **\u5EFA\u8BAE\u4E0B\u6E38\u5148\u505A\u54EA\u4E00\u6B65**\uFF08\u53EF\u9009\uFF09\u2014\u2014 \u4F8B\uFF1A\`--next "\u5148\u8DD1\u4E00\u6B21\u5C0F\u6D41\u91CF\u7070\u5EA6"\``,
4095
- `\u5199 \`none\` = \u300C\u6211\u786E\u8BA4\u6CA1\u6709\u300D\uFF1B\u4E0D\u5199 = \u300C\u6CA1\u4EBA\u5199\u8FC7\u300D\u3002**\u8FD9\u4E24\u4EF6\u4E8B\u5BF9\u4E0B\u6E38\u5B8C\u5168\u4E0D\u540C\uFF0C\u522B\u6DF7\u3002**`
4095
+ `\u5199 \`none\` = \u300C\u6211\u786E\u8BA4\u6CA1\u6709\u300D\uFF1B\u4E0D\u5199 = \u300C\u6CA1\u4EBA\u5199\u8FC7\u300D\u3002**\u8FD9\u4E24\u4EF6\u4E8B\u5BF9\u4E0B\u6E38\u5B8C\u5168\u4E0D\u540C\uFF0C\u522B\u6DF7\u3002**`,
4096
+ ``,
4097
+ `### \u4F1A\u8BDD\u53EF\u80FD\u88AB\u780D\uFF1A\u4E2D\u9014\u7559\u4E2A\u65AD\u70B9`,
4098
+ // ★ 2026-08-18:`continuity-checkpoint` 此前在任务书里出现 **0 次**——说明书只在
4099
+ // `execution/CONTINUITY.md`(没人读)和 `oasis help` 里。线上 160 条 checkpoint 只有 6 条带
4100
+ // pendingSteps,且全部来自同一个自己跑 `oasis help continuity-checkpoint` 摸出来的 agent。
4101
+ // 「知道的会用、且用得很好」已被实证,缺的只是这段话。
4102
+ // 刻意不写「过去 24 小时发生 N 次」这类统计:任务书是长期提示词,写死的数字会过期变成假话。
4103
+ `\u4F60\u8FD9\u4E00\u8F6E\u968F\u65F6\u53EF\u80FD\u88AB\u780D\u2014\u2014\u8D85\u65F6\u3001\u989D\u5EA6\u6253\u6EE1\u3001\u8282\u70B9\u6389\u7EBF\u90FD\u4F1A\uFF0C\u800C\u4E14**\u662F\u5E38\u4E8B**\u3002\u771F\u88AB\u780D\u65F6\uFF0C\u4E0B\u4E00\u8D9F\u63A5\u624B\u7684\u4EBA\u53EA\u770B\u5F97\u5230\u300C\u4F60\u4E3A\u4EC0\u4E48\u65AD\u300D\uFF0C\u770B\u4E0D\u5230\u300C\u4F60\u5E72\u5230\u54EA\u300D\u2014\u2014\u9664\u975E\u4F60\u7559\u4E86\u65AD\u70B9\u3002`,
4104
+ `\u505A\u5B8C\u4E00\u4E2A\u9636\u6BB5\u3001\u6216\u8981\u5F00\u59CB\u4E00\u4EF6\u53EF\u80FD\u5F88\u4E45\u7684\u4E8B\u4E4B\u524D\uFF0C\u987A\u624B\u8BB0\u4E00\u6761\uFF08\u51E0\u79D2\u8FD4\u56DE\uFF0C\u4E0D\u6253\u65AD\u4F60\uFF09\uFF1A`,
4105
+ // ★ 参数必须是 **JSON string array**(CLI 侧 `JSON.parse` 后校验,见 cli.ts parseStringArrayFlag)。
4106
+ // 这里给的是能逐字复制的真例子——写成自然语言一句话会当场抛 `--pending 必须是 JSON string array`。
4107
+ `\`\`\``,
4108
+ `oasis continuity-checkpoint --trigger stage_completed \\`,
4109
+ ` --completed '["\u8BFB\u9700\u6C42","\u6539 auth \u4E2D\u95F4\u4EF6","\u8DD1\u5355\u6D4B"]' \\`,
4110
+ ` --pending '["\u8865\u96C6\u6210\u6D4B\u8BD5","\u5199\u7ED3\u6848\u62A5\u544A"]' \\`,
4111
+ ` --recoverability partial`,
4112
+ `\`\`\``,
4113
+ `\u4E24\u4E2A\u6570\u7EC4\u90FD\u662F JSON\uFF08\u5355\u5F15\u53F7\u5305\u4F4F\u3001\u91CC\u9762\u7528\u53CC\u5F15\u53F7\uFF09\u3002\u6B65\u9AA4\u540D\u7528\u7A33\u5B9A\u7684\u8BF4\u6CD5\u2014\u2014\u91CD\u8DD1\u65F6\u540C\u4E00\u6B65\u7528\u540C\u4E00\u4E2A\u540D\uFF0C\u4E0B\u4E00\u8D9F\u624D\u8BA4\u5F97\u51FA\u54EA\u4E9B\u5DF2\u7ECF\u505A\u5B8C\u3002\`--recoverability\` \u4E09\u9009\u4E00\uFF1A\`full\`=\u63A5\u624B\u7684\u4EBA\u7167\u7740\u5C31\u80FD\u7EED\u4E0A\u3001\`partial\`=\u80FD\u7EED\u4F46\u8981\u91CD\u505A\u4E00\u90E8\u5206\u3001\`none\`=\u7B49\u4E8E\u4ECE\u5934\u518D\u6765\u3002`,
4114
+ `**\u53EA\u6709\u4F60\u77E5\u9053\u300C\u8FD8\u5269\u4EC0\u4E48\u300D**\u2014\u2014\u7CFB\u7EDF\u80FD\u81EA\u52A8\u8BB0\u4E0B\u4F60\u8DD1\u8FC7\u54EA\u4E9B\u547D\u4EE4\u3001\u4EA4\u5230\u4E86\u54EA\u4E00\u7248\uFF0C\u4F46\u63A8\u4E0D\u51FA\u4F60\u6253\u7B97\u5E72\u4EC0\u4E48\u3002\u522B\u5199\u7A7A\u8BDD\uFF1B\u6CA1\u4EC0\u4E48\u53EF\u5199\u5C31\u4E0D\u5199\uFF0C\u522B\u4E3A\u4E86\u586B\u800C\u586B\u3002`
4096
4115
  ],
4097
4116
  // 兜底(§10):上次失败了 → 明确告诉它发生了什么 + 怎么破(首选 add-revision 拆出去)
4098
4117
  ...args.lastFailure ? args.lastFailure.resumed ? [
@@ -158863,6 +158882,8 @@ async function listModels(runtime, executablePath) {
158863
158882
  return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "kimi", "kimi", ["acp"]) ?? []);
158864
158883
  case "kiro":
158865
158884
  return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "kiro-cli", "kiro", ["acp"]) ?? []);
158885
+ case "reasonix":
158886
+ return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "reasonix", "reasonix", ["acp"]) ?? []);
158866
158887
  case "openclaw":
158867
158888
  return cachedDiscovery(runtime, () => discoverOpenclawModels(executablePath));
158868
158889
  default:
@@ -158888,7 +158909,12 @@ async function cachedDiscovery(key, fn) {
158888
158909
  if (entry && now < entry.expiresAt) {
158889
158910
  return entry.models;
158890
158911
  }
158891
- const models = await fn();
158912
+ let models;
158913
+ try {
158914
+ models = await fn();
158915
+ } catch {
158916
+ return [];
158917
+ }
158892
158918
  modelCache.set(key, { models, expiresAt: now + CACHE_TTL_MS });
158893
158919
  return models;
158894
158920
  }
@@ -158958,12 +158984,77 @@ async function discoverCursorModels(bin) {
158958
158984
  async function discoverOpenCodeModels(bin) {
158959
158985
  throw new Error("discoverOpenCodeModels: TODO");
158960
158986
  }
158987
+ function isPiDiscoveryNoise(line) {
158988
+ const lower = line.toLowerCase();
158989
+ if (lower.includes("no models match pattern")) return true;
158990
+ if (lower.startsWith("warning:") || lower.startsWith("error:") || lower.startsWith("info:")) return true;
158991
+ return line.includes("`") || lower.includes("--help") || lower.includes("usage:") || lower.includes("unknown flag") || lower.includes("unknown command");
158992
+ }
158993
+ function parsePiModels(output) {
158994
+ const models = [];
158995
+ const seen = /* @__PURE__ */ new Set();
158996
+ for (const raw of output.split("\n")) {
158997
+ const line = raw.trim();
158998
+ if (!line || isPiDiscoveryNoise(line)) continue;
158999
+ const fields = line.split(/\s+/).filter(Boolean);
159000
+ if (fields.length === 0) continue;
159001
+ const first = fields[0];
159002
+ if (first.toLowerCase() === "provider") continue;
159003
+ let id;
159004
+ if (/[:/]/.test(first)) {
159005
+ id = first.replace(":", "/");
159006
+ } else if (fields.length >= 2) {
159007
+ id = `${first}/${fields[1]}`;
159008
+ } else {
159009
+ continue;
159010
+ }
159011
+ const slash = id.indexOf("/");
159012
+ if (slash <= 0 || slash === id.length - 1) continue;
159013
+ if (seen.has(id)) continue;
159014
+ seen.add(id);
159015
+ models.push({ id, label: id, provider: id.slice(0, slash) });
159016
+ }
159017
+ return models;
159018
+ }
158961
159019
  async function discoverPiModels(bin) {
158962
- throw new Error("discoverPiModels: TODO");
159020
+ const out = await runCapture(bin ?? "pi", ["--list-models"]);
159021
+ return out === void 0 ? [] : parsePiModels(out);
158963
159022
  }
158964
159023
  async function discoverAntigravityModels(bin) {
158965
159024
  throw new Error("discoverAntigravityModels: TODO");
158966
159025
  }
159026
+ async function runCapture(bin, args, timeoutMs = 15e3) {
159027
+ return await new Promise((resolve10) => {
159028
+ let child;
159029
+ try {
159030
+ child = (0, import_node_child_process8.spawn)(bin, args, { stdio: ["ignore", "pipe", "ignore"], env: { ...process.env } });
159031
+ } catch {
159032
+ resolve10(void 0);
159033
+ return;
159034
+ }
159035
+ let out = "";
159036
+ let settled = false;
159037
+ const done = (v2) => {
159038
+ if (!settled) {
159039
+ settled = true;
159040
+ clearTimeout(timer);
159041
+ resolve10(v2);
159042
+ }
159043
+ };
159044
+ const timer = setTimeout(() => {
159045
+ try {
159046
+ child.kill("SIGKILL");
159047
+ } catch {
159048
+ }
159049
+ done(void 0);
159050
+ }, timeoutMs);
159051
+ child.stdout?.on("data", (d) => {
159052
+ out += d.toString();
159053
+ });
159054
+ child.on("error", () => done(void 0));
159055
+ child.on("close", (code) => done(code === 0 ? out : void 0));
159056
+ });
159057
+ }
158967
159058
  async function discoverACPModels(bin, provider, args = ["acp"]) {
158968
159059
  const child = (() => {
158969
159060
  try {
@@ -160122,6 +160213,20 @@ function joinOutputParts(parts) {
160122
160213
  if (parts.every((p2) => typeof p2 === "string")) return parts.join("");
160123
160214
  return parts;
160124
160215
  }
160216
+ function pickApprovalOptionId(params) {
160217
+ const raw = params?.options;
160218
+ if (!Array.isArray(raw)) return LEGACY_APPROVE_OPTION_ID;
160219
+ const options = raw.filter(
160220
+ (o) => typeof o?.optionId === "string"
160221
+ );
160222
+ if (options.length === 0) return LEGACY_APPROVE_OPTION_ID;
160223
+ const byId = options.find((o) => o.optionId === LEGACY_APPROVE_OPTION_ID)?.optionId;
160224
+ const byKind = (kind) => options.find((o) => o.kind === kind)?.optionId;
160225
+ const nonReject = options.find(
160226
+ (o) => !String(o.kind ?? o.optionId).startsWith("reject")
160227
+ )?.optionId;
160228
+ return byId ?? byKind("allow_always") ?? byKind("allow_once") ?? nonReject ?? LEGACY_APPROVE_OPTION_ID;
160229
+ }
160125
160230
  function normalizeACPUpdate(data) {
160126
160231
  const d = data;
160127
160232
  if (!d) return ["", data];
@@ -160370,7 +160475,8 @@ async function runACPSession(job, cfg) {
160370
160475
  if (reason === "cancelled") finish({ code: null, reason: "cancelled", transcriptRef });
160371
160476
  },
160372
160477
  () => streamingCurrentTurn,
160373
- cfg.finalUsageIsAuthoritative === true
160478
+ cfg.finalUsageIsAuthoritative === true,
160479
+ cfg.vendorUsageNotification
160374
160480
  );
160375
160481
  usageClient = client;
160376
160482
  const rl = readline3.createInterface({ input: child.stdout });
@@ -160540,7 +160646,7 @@ ${task}` : task;
160540
160646
  }
160541
160647
  };
160542
160648
  }
160543
- var import_node_child_process10, import_node_crypto16, fs11, path11, readline3, ACPClient;
160649
+ var import_node_child_process10, import_node_crypto16, fs11, path11, readline3, ACPClient, LEGACY_APPROVE_OPTION_ID;
160544
160650
  var init_acp = __esm({
160545
160651
  "../adapters/src/_core/acp.ts"() {
160546
160652
  "use strict";
@@ -160559,13 +160665,14 @@ var init_acp = __esm({
160559
160665
  init_transcript();
160560
160666
  init_filter_extra_args();
160561
160667
  ACPClient = class {
160562
- constructor(write, onOutput, onTelemetry, onStop, acceptNotification = () => true, finalUsageIsAuthoritative = false) {
160668
+ constructor(write, onOutput, onTelemetry, onStop, acceptNotification = () => true, finalUsageIsAuthoritative = false, vendorUsage) {
160563
160669
  this.write = write;
160564
160670
  this.onOutput = onOutput;
160565
160671
  this.onTelemetry = onTelemetry;
160566
160672
  this.onStop = onStop;
160567
160673
  this.acceptNotification = acceptNotification;
160568
160674
  this.finalUsageIsAuthoritative = finalUsageIsAuthoritative;
160675
+ this.vendorUsage = vendorUsage;
160569
160676
  }
160570
160677
  nextId = 0;
160571
160678
  pending = /* @__PURE__ */ new Map();
@@ -160579,7 +160686,37 @@ var init_acp = __esm({
160579
160686
  * 是否还在流。每收到一条 session/update / session/notification 就 +1,主循环轮询该值。
160580
160687
  */
160581
160688
  notificationCount = 0;
160689
+ /** 厂商私有用量:已完成轮次的累计 + 当前轮的最新快照,两者相加即 this.usage。 */
160690
+ vendorTurnBase = newUsageAcc();
160691
+ vendorTurnSnapshot = newUsageAcc();
160692
+ applyVendorUsage(params) {
160693
+ const picked = this.vendorUsage?.pick(params);
160694
+ if (!picked) return;
160695
+ this.vendorTurnSnapshot = {
160696
+ inputTokens: picked.inputTokens ?? 0,
160697
+ outputTokens: picked.outputTokens ?? 0,
160698
+ cacheReadTokens: picked.cacheReadTokens ?? 0,
160699
+ cacheCreationTokens: picked.cacheCreationTokens ?? 0
160700
+ };
160701
+ this.usage = {
160702
+ inputTokens: this.vendorTurnBase.inputTokens + this.vendorTurnSnapshot.inputTokens,
160703
+ outputTokens: this.vendorTurnBase.outputTokens + this.vendorTurnSnapshot.outputTokens,
160704
+ cacheReadTokens: this.vendorTurnBase.cacheReadTokens + this.vendorTurnSnapshot.cacheReadTokens,
160705
+ cacheCreationTokens: this.vendorTurnBase.cacheCreationTokens + this.vendorTurnSnapshot.cacheCreationTokens
160706
+ };
160707
+ }
160708
+ /** 新一轮 prompt 发出前,把上一轮的快照折进基数(快照是「当前轮」口径,会归零重来)。 */
160709
+ foldVendorTurn() {
160710
+ this.vendorTurnBase = {
160711
+ inputTokens: this.vendorTurnBase.inputTokens + this.vendorTurnSnapshot.inputTokens,
160712
+ outputTokens: this.vendorTurnBase.outputTokens + this.vendorTurnSnapshot.outputTokens,
160713
+ cacheReadTokens: this.vendorTurnBase.cacheReadTokens + this.vendorTurnSnapshot.cacheReadTokens,
160714
+ cacheCreationTokens: this.vendorTurnBase.cacheCreationTokens + this.vendorTurnSnapshot.cacheCreationTokens
160715
+ };
160716
+ this.vendorTurnSnapshot = newUsageAcc();
160717
+ }
160582
160718
  request(method, params) {
160719
+ if (this.vendorUsage && method === "session/prompt") this.foldVendorTurn();
160583
160720
  const id = this.nextId++;
160584
160721
  return new Promise((resolve10, reject) => {
160585
160722
  this.pending.set(id, { resolve: resolve10, reject, method });
@@ -160631,11 +160768,15 @@ var init_acp = __esm({
160631
160768
  handleAgentRequest(raw) {
160632
160769
  const method = raw["method"];
160633
160770
  const id = raw["id"];
160634
- const resp = method === "session/request_permission" ? { jsonrpc: "2.0", id, result: { outcome: { outcome: "selected", optionId: "approve_for_session" } } } : { jsonrpc: "2.0", id, error: { code: -32601, message: `method not found: ${method}` } };
160771
+ const resp = method === "session/request_permission" ? { jsonrpc: "2.0", id, result: { outcome: { outcome: "selected", optionId: pickApprovalOptionId(raw["params"]) } } } : { jsonrpc: "2.0", id, error: { code: -32601, message: `method not found: ${method}` } };
160635
160772
  this.write(JSON.stringify(resp) + "\n");
160636
160773
  }
160637
160774
  handleNotification(raw) {
160638
160775
  const method = raw["method"];
160776
+ if (this.vendorUsage && method === this.vendorUsage.method) {
160777
+ this.applyVendorUsage(raw["params"]);
160778
+ return;
160779
+ }
160639
160780
  if (method !== "session/update" && method !== "session/notification") return;
160640
160781
  const params = raw["params"];
160641
160782
  if (!params?.update) return;
@@ -160794,6 +160935,7 @@ var init_acp = __esm({
160794
160935
  });
160795
160936
  }
160796
160937
  };
160938
+ LEGACY_APPROVE_OPTION_ID = "approve_for_session";
160797
160939
  }
160798
160940
  });
160799
160941
 
@@ -160895,6 +161037,72 @@ var init_kiro = __esm({
160895
161037
  }
160896
161038
  });
160897
161039
 
161040
+ // ../adapters/src/reasonix/index.ts
161041
+ var REASONIX_BLOCKED_FLAGS, REASONIX_USAGE_NOTIFICATION, ReasonixAdapter;
161042
+ var init_reasonix = __esm({
161043
+ "../adapters/src/reasonix/index.ts"() {
161044
+ "use strict";
161045
+ init_acp();
161046
+ REASONIX_BLOCKED_FLAGS = /* @__PURE__ */ new Map([
161047
+ ["--model", "value"],
161048
+ ["--planner", "value"],
161049
+ ["--sandbox-network", "value"],
161050
+ ["--sandbox-bash", "value"],
161051
+ ["--workspace-only", "boolean"]
161052
+ ]);
161053
+ REASONIX_USAGE_NOTIFICATION = {
161054
+ method: "_reasonix.io/session/status_update",
161055
+ pick(params) {
161056
+ const rec = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
161057
+ const num2 = (v2) => typeof v2 === "number" && Number.isFinite(v2) && v2 >= 0 ? v2 : void 0;
161058
+ const turn = rec(rec(rec(rec(params)?.["status"])?.["usage"])?.["turn"]);
161059
+ if (!turn) return void 0;
161060
+ const promptTokens = num2(turn["promptTokens"]);
161061
+ const cacheHit = num2(turn["cacheHitTokens"]);
161062
+ const cacheMiss = num2(turn["cacheMissTokens"]);
161063
+ const inputTokens = cacheMiss ?? (promptTokens === void 0 ? void 0 : Math.max(0, promptTokens - (cacheHit ?? 0)));
161064
+ const outputTokens = num2(turn["completionTokens"]);
161065
+ if (inputTokens === void 0 && outputTokens === void 0 && cacheHit === void 0) return void 0;
161066
+ return {
161067
+ ...inputTokens !== void 0 ? { inputTokens } : {},
161068
+ ...outputTokens !== void 0 ? { outputTokens } : {},
161069
+ ...cacheHit !== void 0 ? { cacheReadTokens: cacheHit } : {}
161070
+ };
161071
+ }
161072
+ };
161073
+ ReasonixAdapter = class {
161074
+ constructor(opts = {}) {
161075
+ this.opts = opts;
161076
+ }
161077
+ /** ADR-0093:ACP 引擎原生支持对话追加(同 session 二次 session/prompt)。 */
161078
+ capabilities = { appendInput: true };
161079
+ spawn(job) {
161080
+ return runACPSession(job, {
161081
+ bin: this.opts.reasonixBin ?? "reasonix",
161082
+ args: [
161083
+ "acp",
161084
+ "--planner",
161085
+ "auto",
161086
+ "--sandbox-network",
161087
+ "auto",
161088
+ "--sandbox-bash",
161089
+ "auto",
161090
+ "--workspace-only"
161091
+ ],
161092
+ model: this.opts.model,
161093
+ resumeMethod: "session/resume",
161094
+ vendorUsageNotification: REASONIX_USAGE_NOTIFICATION,
161095
+ blockedFlags: REASONIX_BLOCKED_FLAGS,
161096
+ workRoot: this.opts.workRoot,
161097
+ cleanupWorkdir: this.opts.cleanupWorkdir,
161098
+ env: this.opts.env,
161099
+ extraArgs: this.opts.extraArgs
161100
+ });
161101
+ }
161102
+ };
161103
+ }
161104
+ });
161105
+
160898
161106
  // ../adapters/src/_core/subprocess.ts
160899
161107
  async function materialize(job, cfg) {
160900
161108
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
@@ -161975,6 +162183,7 @@ var init_src6 = __esm({
161975
162183
  init_hermes();
161976
162184
  init_kimi();
161977
162185
  init_kiro();
162186
+ init_reasonix();
161978
162187
  init_cursor();
161979
162188
  init_codebuddy();
161980
162189
  init_gemini();
@@ -163962,6 +164171,7 @@ async function startOasisServer(opts) {
163962
164171
  ...tokenClaims.artifactId !== void 0 ? { artifactId: tokenClaims.artifactId } : {},
163963
164172
  durableRefs: tokenClaims.artifactId !== void 0 ? [{ kind: "artifact", id: tokenClaims.artifactId, label: body.command }] : [],
163964
164173
  at: (/* @__PURE__ */ new Date()).toISOString(),
164174
+ ...listArg("pendingSteps") ? { pendingSteps: listArg("pendingSteps") } : {},
163965
164175
  ...delivered ? { delivered } : {}
163966
164176
  });
163967
164177
  } catch (error2) {
@@ -166323,7 +166533,14 @@ var init_memory_registry_store = __esm({
166323
166533
  this.skillFiles.delete(skillId);
166324
166534
  }
166325
166535
  async putVariable(v2) {
166326
- this.variables.set(this._varKey(v2.key, v2.actorId, v2.projectId), { ...v2 });
166536
+ const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
166537
+ const ex = this.variables.get(mk);
166538
+ this.variables.set(mk, { ...v2, ...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {} });
166539
+ }
166540
+ async touchVariableUsage(key, actorId, projectId, when) {
166541
+ const mk = this._varKey(key, actorId, projectId);
166542
+ const ex = this.variables.get(mk);
166543
+ if (ex) this.variables.set(mk, { ...ex, lastUsedAt: when });
166327
166544
  }
166328
166545
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
166329
166546
  const mk = this._varKey(key, actorId);
@@ -166577,12 +166794,11 @@ function systemHandoffFor(run, checkpoint = null, effects = []) {
166577
166794
  summary: `[system_reconstructed] attempt=${run.id} outcome=${outcome} reason=${terminalReason}; ${progress}`.slice(0, 400),
166578
166795
  changedOutputs: structuredClone(run.outputRefs),
166579
166796
  unresolvedIssues: [],
166580
- risks: [
166581
- ...run.errorMessage ? [run.errorMessage.slice(0, 400)] : [],
166582
- ...pendingEffects || failedEffects ? [`external effects: pending=${pendingEffects}, failed=${failedEffects}`] : []
166583
- ],
166797
+ // 与 PG 触发器同口径(2026-08-18):risks / verificationGaps 只装业务语义的东西。会话怎么死的
166798
+ // 已在 summary reason= 段里,不再冒充成「交付物的风险」;「没有断点」也不是「验证缺口」。
166799
+ risks: pendingEffects || failedEffects ? [`external effects: pending=${pendingEffects}, failed=${failedEffects}`] : [],
166584
166800
  nextActions: structuredClone(checkpoint?.pendingSteps ?? []),
166585
- verificationGaps: checkpoint ? [] : ["No valid pre-terminal checkpoint was available."],
166801
+ verificationGaps: [],
166586
166802
  userHandoff: null,
166587
166803
  createdAt: run.endedAt ?? run.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
166588
166804
  };
@@ -166972,6 +167188,13 @@ var init_memory_trace_store = __esm({
166972
167188
  const handoff = this.handoffs.get(attemptId);
166973
167189
  return handoff ? structuredClone(handoff) : null;
166974
167190
  }
167191
+ async getHandoffByOutputVersion(version2) {
167192
+ const hits = [...this.handoffs.values()].filter((h) => h.changedOutputs.some((ref2) => ref2.version === version2));
167193
+ if (hits.length === 0) return null;
167194
+ let best = hits[0];
167195
+ for (const h of hits.slice(1)) if (h.createdAt >= best.createdAt) best = h;
167196
+ return structuredClone(best);
167197
+ }
166975
167198
  async listAttempts(workOrderId) {
166976
167199
  return [...this.runs.values()].filter((run) => run.workOrderId === workOrderId && run.nodeId !== null).sort((a, b2) => (a.nodeId ?? "").localeCompare(b2.nodeId ?? "") || (a.attemptNo ?? 0) - (b2.attemptNo ?? 0)).map((run) => this.decorate(structuredClone(run)));
166977
167200
  }
@@ -174471,9 +174694,37 @@ ${input.description}
174471
174694
  // 非连接器写入到此 deliveryMode 必非空(上面已拦);连接器写入沿用 env(其值经 sensitiveVars 剔除,不进 env)。
174472
174695
  deliveryMode: v2.deliveryMode ?? "env",
174473
174696
  valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
174474
- updatedAt: this.now()
174697
+ updatedAt: this.now(),
174698
+ // ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
174699
+ // 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
174700
+ expiresAt: v2.expiresAt
174475
174701
  });
174476
174702
  }
174703
+ /** ADR 凭据保险库补章 §D2.3:某一行变量「明文真被取用」→ 刷 lastUsedAt。行按 (key, actorId?, projectId?)
174704
+ * 唯一键定位(与 put/delete 同址)。store 未实现该方法时静默跳过(不影响主功能)。 */
174705
+ async touchVariableUsage(key, origin, when) {
174706
+ await this.opts.store.touchVariableUsage?.(key, origin?.actorId, origin?.projectId, when ?? this.now());
174707
+ }
174708
+ /**
174709
+ * ADR 凭据保险库补章 §D1.2:**人类控制台** reveal 的取值——不走 agent 的 `resolveActorProvisionEntries`
174710
+ * 解析链(人类既无 personal 变量也无派单 artifactId,那条链恒空)。直接按存储唯一键取该行、解出明文。
174711
+ * - `selector` 由控制台列表行给出(personal 带 actorId、project 带 projectId、global 两者皆空)——
174712
+ * 与 `GET /api/variables`「对所有人类返回全量」的可见口径一致(补章 §D1.3),列得到即 reveal 得到。
174713
+ * - 命中返回明文 + 该行的 origin(供刷 lastUsedAt);未命中(无此行)返回 null(路由映射 403,与 agent 同壳)。
174714
+ */
174715
+ async revealHumanValue(key, selector) {
174716
+ const wantActor = selector?.actorId;
174717
+ const wantProject = selector?.projectId;
174718
+ const rows = await this.opts.store.listVariables();
174719
+ const row = rows.find(
174720
+ (r) => r.key === key && (r.actorId ?? void 0) === (wantActor ?? void 0) && (r.projectId ?? void 0) === (wantProject ?? void 0)
174721
+ );
174722
+ if (!row) return null;
174723
+ return {
174724
+ value: this.decodeCiphertext(row.valueEncrypted),
174725
+ origin: { ...row.actorId !== void 0 ? { actorId: row.actorId } : {}, ...row.projectId !== void 0 ? { projectId: row.projectId } : {} }
174726
+ };
174727
+ }
174477
174728
  /** 更新已有变量的 encrypted 属性(重新加密或解密),不改其他字段 */
174478
174729
  async patchVariableEncryption(key, actorId, encrypted) {
174479
174730
  const ct = await this.opts.store.getVariableCiphertext(key, actorId);
@@ -174512,7 +174763,10 @@ ${input.description}
174512
174763
  // 缺省视为 env(ADR §4.4;存量行读侧统一回填)
174513
174764
  encrypted: !isPlain,
174514
174765
  maskedValue: isPlain ? r.valueEncrypted.slice(6) : mask(this.decrypt(r.valueEncrypted)),
174515
- updatedAt: r.updatedAt
174766
+ updatedAt: r.updatedAt,
174767
+ // ADR 凭据保险库补章 §D2.1:到期/取用时间戳透传(展示层据此染色;补章 §D2.4)。
174768
+ ...r.expiresAt !== void 0 ? { expiresAt: r.expiresAt } : {},
174769
+ ...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {}
174516
174770
  };
174517
174771
  });
174518
174772
  }
@@ -174605,7 +174859,12 @@ ${input.description}
174605
174859
  const gated = (row) => !row.connectorId || enabledConnectors.has(row.connectorId);
174606
174860
  const put = (row) => {
174607
174861
  if (!gated(row)) return;
174608
- merged.set(row.key, { key: row.key, value: this.decodeCiphertext(row.valueEncrypted), deliveryMode: row.deliveryMode ?? "env" });
174862
+ merged.set(row.key, {
174863
+ key: row.key,
174864
+ value: this.decodeCiphertext(row.valueEncrypted),
174865
+ deliveryMode: row.deliveryMode ?? "env",
174866
+ origin: { ...row.actorId !== void 0 ? { actorId: row.actorId } : {}, ...row.projectId !== void 0 ? { projectId: row.projectId } : {} }
174867
+ });
174609
174868
  };
174610
174869
  for (const row of rows) {
174611
174870
  if (row.scope === "global" && !row.actorId) put(row);
@@ -174625,7 +174884,7 @@ ${input.description}
174625
174884
  async resolveRevealValue(actorId, key, opts) {
174626
174885
  const entries = await this.resolveActorProvisionEntries(actorId, { ...opts, refreshConnectors: false });
174627
174886
  const hit = entries.find((e) => e.key === key);
174628
- return hit ? { value: hit.value } : null;
174887
+ return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
174629
174888
  }
174630
174889
  encrypt(plain) {
174631
174890
  const iv = (0, import_node_crypto26.randomBytes)(12);
@@ -175854,6 +176113,21 @@ var init_connector_skills = __esm({
175854
176113
  function isKnownRevealSource(source) {
175855
176114
  return CREDENTIAL_REVEAL_SOURCES.has(source) || source.startsWith("agent-skill:");
175856
176115
  }
176116
+ function clientMetaFromReq(req) {
176117
+ const fwd = req.raw?.headers?.["x-forwarded-for"];
176118
+ const fwdFirst = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0]?.trim();
176119
+ const clientIp = fwdFirst || req.raw?.socket?.remoteAddress || void 0;
176120
+ const ua = req.raw?.headers?.["user-agent"];
176121
+ const userAgent = (Array.isArray(ua) ? ua[0] : ua) || void 0;
176122
+ return { ...clientIp ? { clientIp } : {}, ...userAgent ? { userAgent } : {} };
176123
+ }
176124
+ function normalizeExpiresAt(raw) {
176125
+ if (raw === void 0 || raw === null || raw === "") return void 0;
176126
+ if (typeof raw !== "string") throw new ApiError(400, "BAD_REQUEST", "expiresAt \u987B\u4E3A ISO-8601 \u5B57\u7B26\u4E32");
176127
+ const t = Date.parse(raw);
176128
+ if (Number.isNaN(t)) throw new ApiError(400, "BAD_REQUEST", `expiresAt \u4E0D\u662F\u5408\u6CD5\u65E5\u671F\uFF1A${raw}`);
176129
+ return new Date(t).toISOString();
176130
+ }
175857
176131
  function positionsFromPayload(body) {
175858
176132
  if (!body) return void 0;
175859
176133
  if (Object.prototype.hasOwnProperty.call(body, "roles")) {
@@ -175888,6 +176162,8 @@ function actorsDomain(opts) {
175888
176162
  const { resolveCtx } = opts;
175889
176163
  const revealLimits = opts.credentialRevealLimits ?? { perSession: 200, perSessionPerKey: 20 };
175890
176164
  const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
176165
+ const consoleRevealLimits = opts.credentialRevealLimits?.console ?? { perSession: 60, perSessionPerKey: 10 };
176166
+ const consoleRevealLimiter = new RevealRateLimiter(consoleRevealLimits.perSession, consoleRevealLimits.perSessionPerKey);
175891
176167
  return (router) => {
175892
176168
  router.get("/api/actors", async (req) => {
175893
176169
  const { service } = await resolveCtx(req.auth.companyId);
@@ -176174,11 +176450,6 @@ function actorsDomain(opts) {
176174
176450
  }
176175
176451
  };
176176
176452
  const auditVariableChange = async (req, kind, variableKey, targetScope) => {
176177
- const fwd = req.raw?.headers?.["x-forwarded-for"];
176178
- const fwdFirst = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0]?.trim();
176179
- const clientIp = fwdFirst || req.raw?.socket?.remoteAddress || void 0;
176180
- const ua = req.raw?.headers?.["user-agent"];
176181
- const userAgent = (Array.isArray(ua) ? ua[0] : ua) || void 0;
176182
176453
  await opts.credentialAudit?.({
176183
176454
  kind,
176184
176455
  actor: req.auth.actor,
@@ -176186,8 +176457,7 @@ function actorsDomain(opts) {
176186
176457
  targetScope,
176187
176458
  source: "console",
176188
176459
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
176189
- ...clientIp ? { clientIp } : {},
176190
- ...userAgent ? { userAgent } : {}
176460
+ ...clientMetaFromReq(req)
176191
176461
  });
176192
176462
  };
176193
176463
  router.get("/api/variables", async (req) => {
@@ -176200,6 +176470,7 @@ function actorsDomain(opts) {
176200
176470
  const { service } = await resolveCtx(req.auth.companyId);
176201
176471
  const b2 = req.body;
176202
176472
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
176473
+ const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
176203
176474
  if (b2.scope !== "global" && b2.scope !== "personal" && b2.scope !== "project") {
176204
176475
  throw new ApiError(
176205
176476
  400,
@@ -176240,7 +176511,8 @@ function actorsDomain(opts) {
176240
176511
  ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
176241
176512
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
176242
176513
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
176243
- ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
176514
+ ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
176515
+ expiresAt: expiresAt2
176244
176516
  });
176245
176517
  await auditVariableChange(
176246
176518
  req,
@@ -176278,6 +176550,7 @@ function actorsDomain(opts) {
176278
176550
  const actorId = req.params.id;
176279
176551
  const b2 = req.body;
176280
176552
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
176553
+ const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
176281
176554
  if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
176282
176555
  throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env\u3001ref \u6216 broker");
176283
176556
  }
@@ -176308,7 +176581,8 @@ function actorsDomain(opts) {
176308
176581
  ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
176309
176582
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
176310
176583
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
176311
- ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
176584
+ ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
176585
+ expiresAt: expiresAt2
176312
176586
  });
176313
176587
  await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
176314
176588
  return { status: 201, body: { ok: true } };
@@ -176329,25 +176603,33 @@ function actorsDomain(opts) {
176329
176603
  const source = typeof b2?.source === "string" && b2.source.trim() || "agent-cli";
176330
176604
  if (!isKnownRevealSource(source)) throw new ApiError(400, "BAD_REQUEST", `\u672A\u8BC6\u522B\u7684 source\u300C${source}\u300D\uFF08\xA74.5.4\uFF1A\u53EA\u63A5\u53D7\u8BC6\u522B\u503C\uFF09`);
176331
176605
  const actor = req.auth.actor;
176606
+ const isHuman2 = isHumanActor(actor);
176607
+ const isConsole = source === "console";
176608
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
176332
176609
  const artifactId = req.auth.dispatch?.artifactId;
176333
176610
  const sessionId = req.auth.dispatch?.sessionId;
176334
- const now = Date.now();
176335
- const timestamp = new Date(now).toISOString();
176336
- const sessionKey = sessionId ?? `${actor}::${artifactId ?? "no-artifact"}`;
176337
176611
  const dispatchFields = { ...sessionId ? { sessionId } : {}, ...artifactId ? { artifactId } : {} };
176338
- const throttled = revealLimiter.check(sessionKey, key, now);
176612
+ const consoleMeta = isConsole ? clientMetaFromReq(req) : {};
176613
+ if (isConsole && !isHuman2) {
176614
+ await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason: "no_permission", ...dispatchFields, ...consoleMeta });
176615
+ return { status: 403, body: { code: "denied", key } };
176616
+ }
176617
+ const now = Date.now();
176618
+ const sessionKey = isConsole ? actor : sessionId ?? `${actor}::${artifactId ?? "no-artifact"}`;
176619
+ const limiter = isConsole ? consoleRevealLimiter : revealLimiter;
176620
+ const throttled = limiter.check(sessionKey, key, now);
176339
176621
  if (throttled) {
176340
- await opts.credentialAudit?.({ kind: "credential_reveal_throttled", actor, variableKey: key, source, timestamp, scope: throttled, ...dispatchFields });
176622
+ await opts.credentialAudit?.({ kind: "credential_reveal_throttled", actor, variableKey: key, source, timestamp, scope: throttled, ...dispatchFields, ...consoleMeta });
176341
176623
  return { status: 429, body: { code: "throttled", key, scope: throttled } };
176342
176624
  }
176343
- const scope = artifactId && opts.resolveDispatchScope ? await opts.resolveDispatchScope(artifactId) : {};
176344
- const resolved = await service.resolveRevealValue(actor, key, scope);
176625
+ const resolved = isHuman2 ? await service.revealHumanValue(key, { ...b2?.actorId ? { actorId: b2.actorId } : {}, ...b2?.projectId ? { projectId: b2.projectId } : {} }) : await service.resolveRevealValue(actor, key, artifactId && opts.resolveDispatchScope ? await opts.resolveDispatchScope(artifactId) : {});
176345
176626
  if (!resolved) {
176346
176627
  const reason = await service.hasVariableKey(key) ? "no_permission" : "not_found";
176347
- await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason, ...dispatchFields });
176628
+ await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason, ...dispatchFields, ...consoleMeta });
176348
176629
  return { status: 403, body: { code: "denied", key } };
176349
176630
  }
176350
- await opts.credentialAudit?.({ kind: "credential_reveal", actor, variableKey: key, source, timestamp, ...dispatchFields });
176631
+ await opts.credentialAudit?.({ kind: "credential_reveal", actor, variableKey: key, source, timestamp, ...dispatchFields, ...consoleMeta });
176632
+ await service.touchVariableUsage(key, resolved.origin, timestamp);
176351
176633
  return { status: 200, body: { value: resolved.value } };
176352
176634
  });
176353
176635
  router.get("/api/variables/reveal-audit", async (req) => {
@@ -176850,7 +177132,7 @@ var init_routes2 = __esm({
176850
177132
  init_image_upload();
176851
177133
  init_skill_materializer();
176852
177134
  init_connector_skills();
176853
- CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "agent-exec", "smoke-test", "oasis-internal"]);
177135
+ CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "agent-exec", "smoke-test", "oasis-internal", "console"]);
176854
177136
  RevealRateLimiter = class {
176855
177137
  constructor(perSession, perSessionPerKey, ttlMs = 2 * 60 * 6e4) {
176856
177138
  this.perSession = perSession;
@@ -183066,8 +183348,8 @@ function executionContinuityDomain(service) {
183066
183348
  if (b2.changedOutputs !== void 0 && !isRefArray(b2.changedOutputs) || b2.unresolvedIssues !== void 0 && !isStringArray(b2.unresolvedIssues) || b2.risks !== void 0 && !isStringArray(b2.risks) || b2.nextActions !== void 0 && !isStringArray(b2.nextActions) || b2.verificationGaps !== void 0 && !isStringArray(b2.verificationGaps)) {
183067
183349
  throw new ApiError(400, "BAD_REQUEST", "handoff arrays contain invalid values");
183068
183350
  }
183069
- if (!isUserHandoff(b2.userHandoff)) {
183070
- throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11\u5408\u6CD5\u7684 userHandoff\uFF1BAgent \u5FC5\u987B\u63D0\u4EA4\u672C\u6B21\u5DE5\u4F5C\u7684\u4E1A\u52A1\u4EA4\u63A5\u6458\u8981");
183351
+ if (b2.userHandoff !== void 0 && b2.userHandoff !== null && !isUserHandoff(b2.userHandoff)) {
183352
+ throw new ApiError(400, "BAD_REQUEST", "userHandoff \u5F62\u72B6\u975E\u6CD5\uFF08\u53EF\u4EE5\u4E0D\u7ED9\uFF1B\u7ED9\u4E86\u5C31\u8981\u5408\u6CD5\uFF09");
183071
183353
  }
183072
183354
  const handoff = await service.handoff({
183073
183355
  attemptId,
@@ -183118,10 +183400,20 @@ function renderResumePack(pack) {
183118
183400
  if (!pack.previousAttempt) lines.push("- This is the first attempt.");
183119
183401
  else {
183120
183402
  lines.push(`- Attempt ${pack.previousAttempt.attemptNo ?? "?"}: ${pack.previousHandoff?.summary || pack.previousAttempt.status}`);
183121
- if (pack.latestCheckpoint) {
183122
- lines.push(`- Recoverability: ${pack.latestCheckpoint.recoverability}`);
183123
- for (const step of pack.latestCheckpoint.pendingSteps) lines.push(` - pending: ${step}`);
183124
- for (const ref2 of pack.latestCheckpoint.durableRefs) lines.push(` - durable: ${ref2.kind}:${ref2.id}${ref2.version ? `@${ref2.version}` : ""}`);
183403
+ const cp = pack.latestCheckpoint;
183404
+ const done = cp?.completedStepIds ?? [];
183405
+ const durable = (cp?.durableRefs.length ? cp.durableRefs : pack.previousHandoff?.changedOutputs) ?? [];
183406
+ if (cp) lines.push(`- Recoverability: ${cp.recoverability}`);
183407
+ if (done.length > 0) lines.push(`- \u4E0A\u4E00\u8D9F\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4\uFF1A${done.join("\u3001")}`);
183408
+ for (const ref2 of durable) {
183409
+ lines.push(` - durable: ${ref2.kind}:${ref2.id}${ref2.version ? `@${ref2.version}` : ""}${ref2.label ? ` \u2014 ${ref2.label}` : ""}`);
183410
+ }
183411
+ if (cp && cp.pendingSteps.length > 0) {
183412
+ for (const step of cp.pendingSteps) lines.push(` - pending: ${step}`);
183413
+ } else {
183414
+ lines.push(
183415
+ durable.length > 0 || done.length > 0 ? "- \u672A\u5B8C\u6210\u7684\u6B65\u9AA4\uFF1A**\u672A\u77E5**\uFF08\u4E0A\u4E00\u8D9F\u6CA1\u7559 checkpoint\uFF09\u2014\u2014\u4EE5\u4E0A\u9762\u8FD9\u4E9B\u5DF2\u843D\u5730\u7684\u4EA7\u51FA\u4E3A\u51C6\uFF0C\u81EA\u5DF1\u6838\u5BF9\u8FD8\u7F3A\u4EC0\u4E48\uFF0C\u522B\u4ECE\u5934\u91CD\u505A\u3002" : "- \u4E0A\u4E00\u8D9F\u6CA1\u7559\u4E0B\u4EFB\u4F55\u53EF\u6062\u590D\u7684\u75D5\u8FF9\uFF08\u6CA1\u8DD1\u5230\u4EFB\u4F55\u6301\u4E45\u5316\u547D\u4EE4\uFF09\u2014\u2014\u6309\u9996\u6B21\u6267\u884C\u5904\u7406\u3002"
183416
+ );
183125
183417
  }
183126
183418
  }
183127
183419
  lines.push("", "## External effects");
@@ -183230,6 +183522,7 @@ function renderCoordinatorView(view) {
183230
183522
  function shouldInjectResumePack(pack) {
183231
183523
  const status = pack?.previousAttempt?.status;
183232
183524
  if (!status) return false;
183525
+ if (status === "running" || status === "queued") return false;
183233
183526
  return status !== "succeeded" && status !== "no-output";
183234
183527
  }
183235
183528
  function renderContinuityProtocol() {
@@ -183462,9 +183755,6 @@ var init_service7 = __esm({
183462
183755
  async handoff(input) {
183463
183756
  if (!HANDOFF_OUTCOMES2.has(input.outcome)) throw new Error(`invalid handoff outcome: ${input.outcome}`);
183464
183757
  if (!input.summary.trim()) throw new Error("handoff summary is required");
183465
- if (input.producedBy === "agent" && !input.userHandoff) {
183466
- throw new Error("agent-authored handoff requires userHandoff");
183467
- }
183468
183758
  const userHandoff = input.userHandoff ? normalizeUserHandoff(input.outcome, input.changedOutputs ?? [], input.userHandoff) : void 0;
183469
183759
  const attempt = await this.awaitAttempt(input.attemptId);
183470
183760
  const handoff = await this.afterAttemptVisible(() => this.store.putHandoff({
@@ -183668,8 +183958,7 @@ var init_service7 = __esm({
183668
183958
  "failed"
183669
183959
  ].map((state) => [state, nodes.filter((node) => node.nodeState === state).length]));
183670
183960
  const collaborationStatus = nodes.length > 0 && nodes.every((node) => DONE_STATES.has(node.nodeState)) ? "completed" : nodes.some((node) => BLOCKING_STATES.has(node.nodeState)) ? "needs_coordination" : "in_progress";
183671
- const handoffSummary = [...nodes].sort((a, b2) => Number(BLOCKING_STATES.has(b2.nodeState)) - Number(BLOCKING_STATES.has(a.nodeState))).map((node) => node.summary.trim()).find(Boolean) ?? "";
183672
- return { workOrderId, mode: this.mode, collaborationStatus, handoffSummary, progress, nodes, dependencyBlocks, todo: todos };
183961
+ return { workOrderId, mode: this.mode, collaborationStatus, progress, nodes, dependencyBlocks, todo: todos };
183673
183962
  }
183674
183963
  };
183675
183964
  }
@@ -183804,8 +184093,7 @@ function createUpstreamHandoffInputResolver(opts) {
183804
184093
  const edge = snapshot.edges.find(
183805
184094
  (candidate) => candidate.fromNodeId === input.upstreamNodeId && candidate.toNodeId === input.downstreamNodeId
183806
184095
  );
183807
- const downstream = snapshot.nodes.find((node) => node.id === input.downstreamNodeId);
183808
- const businessEdge = edge?.kind === "data" && edge.required && downstream?.fields?.[BUSINESS_HANDOFF_POLICY_FIELD] !== BUSINESS_HANDOFF_POLICY_EXEMPT;
184096
+ const businessEdge = edge?.kind === "data" && edge.required;
183809
184097
  if (!businessEdge) return null;
183810
184098
  if (edge.pinnedWorkId !== input.pinnedWorkId) {
183811
184099
  warn(
@@ -183831,7 +184119,7 @@ function createUpstreamHandoffInputResolver(opts) {
183831
184119
  version: input.pinnedWorkId,
183832
184120
  ...upstreamNode?.title ? { label: upstreamNode.title } : {}
183833
184121
  }];
183834
- const deliveredRow = typeof work.sessionRef === "string" && work.sessionRef ? await opts.getHandoff(work.sessionRef).catch(() => null) : null;
184122
+ const deliveredRow = await opts.getHandoffByOutputVersion(input.pinnedWorkId).catch(() => null);
183835
184123
  const systemHandoff = () => renderDeliveredWorkHandoff({
183836
184124
  upstreamNodeId: input.upstreamNodeId,
183837
184125
  upstreamTitle: upstreamNode?.title,
@@ -183871,7 +184159,6 @@ var INJECTED_TEXT_LIMIT, NONE_TOKENS;
183871
184159
  var init_upstream_handoff_input = __esm({
183872
184160
  "../server/src/domains/execution-continuity/upstream-handoff-input.ts"() {
183873
184161
  "use strict";
183874
- init_src3();
183875
184162
  INJECTED_TEXT_LIMIT = 4e3;
183876
184163
  NONE_TOKENS = /* @__PURE__ */ new Set(["none", "n/a", "na", "nil", "\u65E0", "\u6CA1\u6709", "\u6682\u65E0"]);
183877
184164
  }
@@ -187514,8 +187801,30 @@ ${ctx.nodeFault}
187514
187801
  ...prov?.connectorCreds && prov.connectorCreds.length > 0 ? { connectorCreds: prov.connectorCreds } : {}
187515
187802
  };
187516
187803
  this.deps.log?.(`[coordinator] ${logMsg}`);
187804
+ const coordinatorRunId = `coordinate:${runtimeSessionId}`;
187805
+ if (this.deps.recordCoordinatorRun) {
187806
+ await this.deps.recordCoordinatorRun({
187807
+ runId: coordinatorRunId,
187808
+ actorId,
187809
+ artifactId,
187810
+ workOrderId: workspace ?? null,
187811
+ runtimeSessionId,
187812
+ bundleFiles: Object.keys(job.bundle.files)
187813
+ }).catch((error2) => {
187814
+ this.deps.log?.(`[coordinator] \u8D26\u672C\u767B\u8BB0\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u4F1A\u8BDD\uFF09\uFF1A${String(error2)}`);
187815
+ });
187816
+ }
187517
187817
  const handle = await this.deps.adapter.spawn(job);
187518
187818
  const exit = await new Promise((resolve10) => handle.onExit(resolve10));
187819
+ if (this.deps.finishCoordinatorRun) {
187820
+ await this.deps.finishCoordinatorRun(coordinatorRunId, {
187821
+ status: exit.code === 0 ? "succeeded" : "failed",
187822
+ exitCode: exit.code,
187823
+ ...exit.reason ? { exitReason: String(exit.reason) } : {}
187824
+ }).catch((error2) => {
187825
+ this.deps.log?.(`[coordinator] \u8D26\u672C\u6536\u5C3E\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u4F1A\u8BDD\uFF09\uFF1A${String(error2)}`);
187826
+ });
187827
+ }
187519
187828
  await this.finalizeStage(token);
187520
187829
  this.registerRetained(artifactId, actorId, runtimeSessionId);
187521
187830
  return { sessionId: runtimeSessionId, managerActorId: actorId, exit };
@@ -188084,7 +188393,10 @@ function rowToVariable(row) {
188084
188393
  // **不塌成 env**,否则未知档位读回来变明文;仅 null/空回填 "env"(存量行)。派单侧按未知档保守处理。
188085
188394
  deliveryMode: typeof row.delivery_mode === "string" && row.delivery_mode ? row.delivery_mode : "env",
188086
188395
  valueEncrypted: row.value_encrypted,
188087
- updatedAt: new Date(row.updated_at).toISOString()
188396
+ updatedAt: new Date(row.updated_at).toISOString(),
188397
+ // ADR 凭据保险库补章 §D2.1:到期/取用时间戳(text 列存 ISO 字符串,原样带出;null/空 = undefined)。
188398
+ ...row.expires_at ? { expiresAt: row.expires_at } : {},
188399
+ ...row.last_used_at ? { lastUsedAt: row.last_used_at } : {}
188088
188400
  };
188089
188401
  }
188090
188402
  var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
@@ -188247,6 +188559,8 @@ var init_postgres_registry = __esm({
188247
188559
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS project_id text`);
188248
188560
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS environment text`);
188249
188561
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS delivery_mode text`);
188562
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
188563
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
188250
188564
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
188251
188565
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
188252
188566
  await pool.query(`
@@ -188723,10 +189037,10 @@ var init_postgres_registry = __esm({
188723
189037
  /* ---------- 变量 ---------- */
188724
189038
  async putVariable(v2) {
188725
189039
  await this.pool.query(
188726
- `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at)
188727
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
189040
+ `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at)
189041
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
188728
189042
  ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
188729
- SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9`,
189043
+ SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10`,
188730
189044
  [
188731
189045
  v2.key,
188732
189046
  v2.scope,
@@ -188736,10 +189050,18 @@ var init_postgres_registry = __esm({
188736
189050
  v2.overrides ?? null,
188737
189051
  v2.deliveryMode ?? null,
188738
189052
  v2.valueEncrypted,
188739
- v2.updatedAt
189053
+ v2.updatedAt,
189054
+ v2.expiresAt ?? null
188740
189055
  ]
188741
189056
  );
188742
189057
  }
189058
+ async touchVariableUsage(key, actorId, projectId, when) {
189059
+ await this.pool.query(
189060
+ `UPDATE ${this.s}.variables SET last_used_at=$4
189061
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=COALESCE($3::text,'')`,
189062
+ [key, actorId ?? null, projectId ?? null, when]
189063
+ );
189064
+ }
188743
189065
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
188744
189066
  const aid = actorId ?? null;
188745
189067
  await this.pool.query(
@@ -190668,6 +190990,9 @@ var init_postgres_trace = __esm({
190668
190990
  await pool.query(`ALTER TABLE "${s2}".node_handoffs ADD COLUMN IF NOT EXISTS part text`);
190669
190991
  await pool.query(`ALTER TABLE "${s2}".node_handoffs ADD COLUMN IF NOT EXISTS user_handoff jsonb`);
190670
190992
  await pool.query(`ALTER TABLE "${s2}".node_handoffs DROP CONSTRAINT IF EXISTS node_handoffs_summary_check`);
190993
+ await pool.query(
190994
+ `CREATE INDEX IF NOT EXISTS node_handoffs_changed_outputs_idx ON "${s2}".node_handoffs USING GIN (changed_outputs)`
190995
+ );
190671
190996
  await pool.query(`
190672
190997
  UPDATE "${s2}".node_handoffs h
190673
190998
  SET job_key=COALESCE(NULLIF(h.job_key, ''), r.job_key, 'attempt:' || h.attempt_id), part=r.part
@@ -190777,15 +191102,26 @@ var init_postgres_trace = __esm({
190777
191102
  END,
190778
191103
  400
190779
191104
  );
191105
+ -- 2026-08-18\uFF1Arisks / verification_gaps \u53EA\u88C5 **\u4E1A\u52A1\u8BED\u4E49** \u7684\u4E1C\u897F\u3002
191106
+ --
191107
+ -- \u75C5\u6839\uFF1A\u8FD9\u91CC\u539F\u672C\u628A NEW.error_message \u585E\u8FDB risks\u3001\u628A\u300C\u6CA1\u6709\u53EF\u7528\u65AD\u70B9\u300D\u585E\u8FDB verification_gaps\u3002
191108
+ -- \u4E24\u8005\u90FD\u4E0D\u662F\u5B83\u4EEC\u5B57\u9762\u7684\u610F\u601D\u2014\u2014\u7EBF\u4E0A\u771F\u5B9E\u503C\u662F
191109
+ -- risks = ["[success] You've hit your session limit - resets 4:40pm (UTC)"]
191110
+ -- verification_gaps = ["No valid pre-terminal checkpoint was available."]
191111
+ -- \u524D\u8005\u662F\u300C\u8FD9\u6B21\u4F1A\u8BDD\u600E\u4E48\u6B7B\u7684\u300D\uFF0C\u540E\u8005\u662F\u300C\u7CFB\u7EDF\u81EA\u5DF1\u6CA1\u6599\u300D\uFF0C\u90FD\u4E0D\u662F\u300C\u4EA4\u4ED8\u7269\u6709\u4EC0\u4E48\u98CE\u9669 / \u54EA\u91CC\u6CA1\u6838\u9A8C\u300D\u3002
191112
+ -- \u5371\u5BB3\u6709\u4E8C\uFF1A(1) \u4EA4\u4ED8\u6210\u529F\u4E4B\u540E\u4F1A\u8BDD\u624D\u8D85\u65F6\u7684\u60C5\u5F62\uFF0C\u8FD9\u4E9B\u4F1A\u88AB\u586B\u8FDB\u7A7A\u5B57\u6BB5\u5E76 **\u771F\u7684\u88AB\u4E0B\u6E38\u8BFB\u5230**\uFF1B
191113
+ -- (2) \u7EDF\u8BA1\u586B\u5145\u7387\u65F6\u5168\u662F\u566A\u97F3\uFF0C\u770B\u8D77\u6765 80% \u6709\u503C\uFF0C\u5176\u5B9E\u6CA1\u4E00\u6761\u662F\u6267\u884C\u4EBA\u5199\u7684\u3002
191114
+ --
191115
+ -- \u4E24\u6761\u4FE1\u606F\u90FD\u4E0D\u4E22\uFF1A\u7EC8\u6001\u539F\u56E0\u5DF2\u5728 summary \u7684 reason= \u6BB5\u91CC\uFF08terminal_reason \u5C31\u53D6\u81EA
191116
+ -- error_message\uFF09\uFF0C\u300C\u6CA1\u6709\u65AD\u70B9\u300D\u4E5F\u5DF2\u5728 summary \u7684 no valid pre-terminal checkpoint \u91CC\u3002
191117
+ -- \u8FD9\u91CC\u53EA\u662F\u4E0D\u518D\u628A\u5B83\u4EEC\u5192\u5145\u6210\u4E1A\u52A1\u5B57\u6BB5\u3002
191118
+ --
191119
+ -- \u5916\u90E8\u6548\u5E94\u7559\u5728 risks\uFF1A\u534A\u6210\u54C1\u526F\u4F5C\u7528\uFF08\u5DF2\u53D1\u51FA\u7684\u8BF7\u6C42 / \u5EFA\u4E86\u4E00\u534A\u7684\u8D44\u6E90\uFF09\u786E\u5B9E\u662F\u4E0B\u6E38\u8BE5\u77E5\u9053\u7684\u98CE\u9669\u3002
190780
191120
  reconstructed_risks :=
190781
- CASE WHEN NEW.error_message IS NULL THEN '[]'::jsonb
190782
- ELSE jsonb_build_array(left(NEW.error_message, 400)) END ||
190783
191121
  CASE WHEN COALESCE(pending_effects, 0) = 0 AND COALESCE(failed_effects, 0) = 0 THEN '[]'::jsonb
190784
191122
  ELSE jsonb_build_array('external effects: pending=' || COALESCE(pending_effects, 0) ||
190785
191123
  ', failed=' || COALESCE(failed_effects, 0)) END;
190786
- reconstructed_gaps := CASE WHEN latest_checkpoint_id IS NULL
190787
- THEN jsonb_build_array('No valid pre-terminal checkpoint was available.')
190788
- ELSE '[]'::jsonb END;
191124
+ reconstructed_gaps := '[]'::jsonb;
190789
191125
  INSERT INTO "${s2}".node_handoffs AS current_handoff
190790
191126
  (handoff_id, attempt_id, job_key, part, produced_by, outcome, summary, changed_outputs,
190791
191127
  unresolved_issues, risks, next_actions, verification_gaps, user_handoff, created_at)
@@ -191733,6 +192069,15 @@ var init_postgres_trace = __esm({
191733
192069
  const r = await this.pool.query(`SELECT * FROM ${this.s}.node_handoffs WHERE attempt_id=$1`, [attemptId]);
191734
192070
  return r.rows[0] ? rowToHandoff(r.rows[0]) : null;
191735
192071
  }
192072
+ async getHandoffByOutputVersion(version2) {
192073
+ const r = await this.pool.query(
192074
+ `SELECT * FROM ${this.s}.node_handoffs
192075
+ WHERE changed_outputs @> $1::jsonb
192076
+ ORDER BY created_at DESC LIMIT 1`,
192077
+ [JSON.stringify([{ version: version2 }])]
192078
+ );
192079
+ return r.rows[0] ? rowToHandoff(r.rows[0]) : null;
192080
+ }
191736
192081
  async listAttempts(workOrderId) {
191737
192082
  const r = await this.pool.query(
191738
192083
  `SELECT r.*, (SELECT count(*) FROM ${this.s}.agent_tool_calls tc WHERE tc.run_id=r.id)::int AS tool_call_count
@@ -194470,6 +194815,15 @@ async function buildActorProvision(service, actorId, scope, onBrokerRefused) {
194470
194815
  const brokerRefused = [];
194471
194816
  applyRefDelivery(env, entries, void 0, (k2) => brokerRefused.push(k2));
194472
194817
  if (onBrokerRefused) for (const k2 of brokerRefused) await onBrokerRefused(k2);
194818
+ const brokerSet = new Set(brokerRefused);
194819
+ for (const e of entries) {
194820
+ if (brokerSet.has(e.key)) continue;
194821
+ try {
194822
+ await service.touchVariableUsage(e.key, e.origin);
194823
+ } catch (err) {
194824
+ console.warn(`[credential] touchVariableUsage(${e.key}) \u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u6D3E\u5355\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`);
194825
+ }
194826
+ }
194473
194827
  return {
194474
194828
  env,
194475
194829
  wrapperPaths: [oasisWrapperScript(), ...provision.wrapperPaths],
@@ -195781,7 +196135,10 @@ async function startServe(opts) {
195781
196135
  ...await materializeSkillFiles({ service: actors.service, actorId, runtimeKind })
195782
196136
  }),
195783
196137
  uploadChatAttachment: async ({ contentBase64 }) => ({ blobRef: await assets.put(new Uint8Array(Buffer.from(contentBase64, "base64"))) }),
195784
- onDispatchCommandCompleted: async ({ dispatchId, command, durableRefs, at, delivered }) => {
196138
+ onDispatchCommandCompleted: async ({ dispatchId, command, durableRefs, at, delivered, pendingSteps }) => {
196139
+ if (pendingSteps && pendingSteps.length > 0) {
196140
+ traceStoreSink.checkpointState(dispatchId, { pendingSteps: [...pendingSteps] });
196141
+ }
195785
196142
  traceStoreSink.checkpoint(dispatchId, {
195786
196143
  trigger: "important_output",
195787
196144
  // 交作业的那条命令带着版本号(`wk:<节点>:<n>`),比笼统的「动过这个产物」有用得多——
@@ -196909,7 +197266,8 @@ async function startServe(opts) {
196909
197266
  if (!store) throw new Error(`engine store unavailable for ${companyId}`);
196910
197267
  return store.transaction((tx) => tx.loadWorkorder(workOrderId));
196911
197268
  },
196912
- getHandoff: (attemptId) => traceStore.getHandoff(attemptId)
197269
+ getHandoff: (attemptId) => traceStore.getHandoff(attemptId),
197270
+ getHandoffByOutputVersion: (version2) => traceStore.getHandoffByOutputVersion(version2)
196913
197271
  }),
196914
197272
  executionContinuityInstructions: continuityMode === "off" ? renderBusinessHandoffProtocol() : renderContinuityProtocol(),
196915
197273
  // ★ 恢复包注入(2026-08-17):从「只有 resume 实验组才给」改成「只要账本开着就给」——
@@ -197529,6 +197887,31 @@ ${st.detail}
197529
197887
  const view = await executionContinuity.service.coordinatorView(workOrderId, defaultCompanyId);
197530
197888
  return view ? renderCoordinatorView(view) : null;
197531
197889
  },
197890
+ // D2(2026-08-18):协调者会话进执行账本。inputRefs 记下 bundle 里实际发出的文件——
197891
+ // 这是唯一能证明「COORDINATOR_VIEW.md 到底有没有随会话发出去」的痕迹。两处都 fail-open。
197892
+ recordCoordinatorRun: async (input) => {
197893
+ await traceStore.createRun({
197894
+ id: input.runId,
197895
+ actorId: input.actorId,
197896
+ runtimeKind: "claude-code",
197897
+ triggerKind: "intervention",
197898
+ artifactId: input.artifactId,
197899
+ nodeId: input.artifactId,
197900
+ workOrderId: input.workOrderId,
197901
+ runtimeSessionId: input.runtimeSessionId,
197902
+ jobKey: `coordinate::${input.artifactId}`,
197903
+ action: "coordinate",
197904
+ inputRefs: input.bundleFiles.map((name) => ({ kind: "bundle", id: name }))
197905
+ });
197906
+ },
197907
+ finishCoordinatorRun: async (runId, outcome) => {
197908
+ await traceStore.updateRun(runId, {
197909
+ status: outcome.status,
197910
+ exitCode: outcome.exitCode,
197911
+ endedAt: (/* @__PURE__ */ new Date()).toISOString(),
197912
+ ...outcome.exitReason ? { exitReason: outcome.exitReason } : {}
197913
+ });
197914
+ },
197532
197915
  // 阻塞诊断(proposal D1/D3):派发器运行时信号(queued/fused/backoff/hasInFlight)注入诊断,
197533
197916
  // 让协调者把"资源 / 运行时"病因和"图病"分开——资源排队的节点不会被误唤去砍流程。
197534
197917
  runtimeSignals: (id) => {
@@ -197751,12 +198134,13 @@ var KNOWN2 = [
197751
198134
  { adapter: "copilot", binary: "copilot" },
197752
198135
  { adapter: "kimi", binary: "kimi" },
197753
198136
  { adapter: "kiro", binary: "kiro-cli" },
198137
+ { adapter: "reasonix", binary: "reasonix" },
197754
198138
  { adapter: "pi", binary: "pi" },
197755
198139
  { adapter: "openclaw", binary: "openclaw" },
197756
198140
  { adapter: "codebuddy", binary: "codebuddy" },
197757
198141
  { adapter: "antigravity", binary: "agy" }
197758
198142
  ];
197759
- var NATIVE_APPEND_RUNTIMES = /* @__PURE__ */ new Set(["hermes", "gemini", "kimi", "kiro"]);
198143
+ var NATIVE_APPEND_RUNTIMES = /* @__PURE__ */ new Set(["hermes", "gemini", "kimi", "kiro", "reasonix"]);
197760
198144
  function onPath(binary) {
197761
198145
  try {
197762
198146
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -198560,6 +198944,7 @@ async function startNode(opts) {
198560
198944
  copilot: new CopilotAdapter(),
198561
198945
  kimi: new KimiAdapter(),
198562
198946
  kiro: new KiroAdapter(),
198947
+ reasonix: new ReasonixAdapter(),
198563
198948
  pi: new PiAdapter(),
198564
198949
  openclaw: new OpenClawAdapter(),
198565
198950
  codebuddy: new CodebuddyAdapter(),
@@ -199044,7 +199429,8 @@ var COMMAND_DECLS = {
199044
199429
  { name: "unresolved", desc: "\u7559\u7ED9\u4E0B\u6E38\u7684\u672A\u51B3\u4E8B\u9879\uFF08\u5206\u53F7\u5206\u9694\uFF09\u3002\u5199\u8FDB\u672C\u8282\u70B9\u7684\u6280\u672F\u4EA4\u63A5\uFF0C\u4E0B\u6E38\u5F00\u5DE5\u524D\u4F1A\u8BFB\u5230" },
199045
199430
  { name: "risks", desc: "\u4E0B\u6E38\u8BE5\u77E5\u9053\u7684\u98CE\u9669\uFF08\u5206\u53F7\u5206\u9694\uFF09" },
199046
199431
  { name: "next", desc: "\u5EFA\u8BAE\u4E0B\u6E38\u63A5\u7740\u505A\u4EC0\u4E48\uFF08\u5206\u53F7\u5206\u9694\uFF09" },
199047
- { name: "gaps", desc: "\u4F60\u6CA1\u80FD\u6838\u9A8C\u7684\u90E8\u5206\uFF08\u5206\u53F7\u5206\u9694\uFF09\u2014\u2014\u522B\u8BA9\u4E0B\u6E38\u4EE5\u4E3A\u5DF2\u9A8C\u8FC7" }
199432
+ { name: "gaps", desc: "\u4F60\u6CA1\u80FD\u6838\u9A8C\u7684\u90E8\u5206\uFF08\u5206\u53F7\u5206\u9694\uFF09\u2014\u2014\u522B\u8BA9\u4E0B\u6E38\u4EE5\u4E3A\u5DF2\u9A8C\u8FC7" },
199433
+ { name: "pending", desc: "\u8FD8\u5269\u4EC0\u4E48\u6CA1\u505A\uFF08\u5206\u53F7\u5206\u9694\uFF09\u3002\u5B58\u8FDB\u672C\u8F6E checkpoint\uFF1A\u8FD9\u4E00\u8D9F\u82E5\u88AB\u780D\uFF08\u8D85\u65F6/\u989D\u5EA6/\u6389\u7EBF\uFF09\uFF0C\u4E0B\u4E00\u8D9F\u63A5\u624B\u7684\u4EBA\u636E\u5B83\u63A5\u7740\u505A" }
199048
199434
  ],
199049
199435
  examples: [
199050
199436
  'oasis propose artifact:dev:abc-123 --from-dir deliverable/ --reason "\u4FEE\u590D\u767B\u5F55\u9875\u767D\u5C4F"',
@@ -199217,13 +199603,13 @@ var COMMAND_DECLS = {
199217
199603
  },
199218
199604
  "continuity-checkpoint": {
199219
199605
  group: "\u56FE\u673A\u5236\uFF08\xA76/\xA77\uFF09",
199220
- usage: "oasis continuity-checkpoint --pending <json-string-array> --recoverability full|partial|none [--trigger plan_completed|stage_completed] [--refs <json-array>] [--completed <json-string-array>]",
199606
+ usage: "oasis continuity-checkpoint --pending <list> --recoverability full|partial|none [--trigger plan_completed|stage_completed] [--refs <json-array>] [--completed <list>]",
199221
199607
  description: "\u4E3A\u5F53\u524D\u6D3E\u53D1 attempt \u8FFD\u52A0\u53EF\u6062\u590D\u68C0\u67E5\u70B9\uFF08\u81EA\u52A8\u4FDD\u7559\u6700\u65B0\u4E94\u4E2A\uFF09\u3002",
199222
199608
  flags: [
199223
- { name: "pending", desc: "\u5F85\u5B8C\u6210\u6B65\u9AA4 JSON string array", required: true },
199609
+ { name: "pending", desc: `\u5F85\u5B8C\u6210\u6B65\u9AA4\u3002JSON \u6570\u7EC4 '["a","b"]' \u6216\u5206\u53F7\u5206\u9694 "a; b" \u90FD\u6536`, required: true },
199224
199610
  { name: "recoverability", desc: "\u6062\u590D\u7A0B\u5EA6\uFF08full | partial | none\uFF09", required: true },
199225
199611
  { name: "refs", desc: "\u5DF2\u6301\u4E45\u5316\u4EA7\u51FA\u5F15\u7528 JSON array" },
199226
- { name: "completed", desc: "\u5DF2\u5B8C\u6210\u7A33\u5B9A\u6B65\u9AA4 id JSON string array" },
199612
+ { name: "completed", desc: "\u5DF2\u5B8C\u6210\u7684\u7A33\u5B9A\u6B65\u9AA4 id\u3002JSON \u6570\u7EC4\u6216\u5206\u53F7\u5206\u9694\u90FD\u6536" },
199227
199613
  { name: "trigger", desc: "\u7ED3\u6784\u5316\u8FB9\u754C\uFF08plan_completed | stage_completed\uFF1B\u7701\u7565\u4E3A\u666E\u901A agent checkpoint\uFF09" }
199228
199614
  ],
199229
199615
  examples: [`oasis continuity-checkpoint --pending '["run tests"]' --recoverability full --refs '[{"kind":"file","id":"src/x.ts"}]'`]
@@ -200336,6 +200722,19 @@ function semicolonList(raw, field) {
200336
200722
  const items = raw.split(";").map((x2) => x2.trim()).filter(Boolean);
200337
200723
  return items.length > 0 ? { [field]: items } : {};
200338
200724
  }
200725
+ function parseStringArrayFlag(value, name) {
200726
+ const trimmed = value.trim();
200727
+ if (trimmed.startsWith("[")) {
200728
+ const parsed = JSON.parse(trimmed);
200729
+ if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
200730
+ throw new Error(`--${name} \u5FC5\u987B\u662F JSON string array`);
200731
+ }
200732
+ return parsed;
200733
+ }
200734
+ const items = trimmed.split(";").map((x2) => x2.trim()).filter(Boolean);
200735
+ if (items.length === 0) throw new Error(`--${name} \u4E0D\u80FD\u4E3A\u7A7A\uFF08JSON string array\uFF0C\u6216\u5206\u53F7\u5206\u9694\uFF09`);
200736
+ return items;
200737
+ }
200339
200738
  async function readStdin() {
200340
200739
  if (process.stdin.isTTY) return "";
200341
200740
  const chunks = [];
@@ -200896,13 +201295,6 @@ async function runCli(argv, println = console.log, progressln = console.error) {
200896
201295
  }
200897
201296
  return parsed;
200898
201297
  };
200899
- const parseStringArrayFlag = (value, name) => {
200900
- const parsed = JSON.parse(value);
200901
- if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
200902
- throw new Error(`--${name} \u5FC5\u987B\u662F JSON string array`);
200903
- }
200904
- return parsed;
200905
- };
200906
201298
  const parseArrayFlag = (value, name) => {
200907
201299
  const parsed = JSON.parse(value);
200908
201300
  if (!Array.isArray(parsed)) throw new Error(`--${name} \u5FC5\u987B\u662F JSON array`);
@@ -201730,7 +202122,9 @@ ${res.warning}`);
201730
202122
  ...semicolonList(flags.get("unresolved"), "unresolvedIssues"),
201731
202123
  ...semicolonList(flags.get("risks"), "risks"),
201732
202124
  ...semicolonList(flags.get("next"), "nextActions"),
201733
- ...semicolonList(flags.get("gaps"), "verificationGaps")
202125
+ ...semicolonList(flags.get("gaps"), "verificationGaps"),
202126
+ // 「还剩什么没做」:唯一系统推不出来的字段,挂在本来就要跑的命令上(见 server 的 pendingSteps 注释)
202127
+ ...semicolonList(flags.get("pending"), "pendingSteps")
201734
202128
  });
201735
202129
  println(message);
201736
202130
  break;
@@ -203004,7 +203398,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
203004
203398
  }
203005
203399
 
203006
203400
  // src/index.ts
203007
- var PKG_VERSION = true ? "0.1.108" : "dev";
203401
+ var PKG_VERSION = true ? "0.1.110" : "dev";
203008
203402
  var OASIS_DIR = path28.join(os11.homedir(), ".oasis");
203009
203403
  var CONFIG_FILE = path28.join(OASIS_DIR, "node-config.json");
203010
203404
  var PID_FILE = path28.join(OASIS_DIR, "node.pid");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.108",
3
+ "version": "0.1.110",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"