oasis_test 0.1.107 → 0.1.109

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 +379 -86
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -993,20 +993,27 @@ function pinnedReviewerOf(model, artifactId, role) {
993
993
  }
994
994
  return best?.actor ?? null;
995
995
  }
996
+ function latestAttempt(attempts) {
997
+ let best = null;
998
+ for (const a of attempts) {
999
+ if (best === null || a.at > best.at || a.at === best.at && a.seq >= best.seq) best = a;
1000
+ }
1001
+ return best;
1002
+ }
996
1003
  function concludeMilestones(model, id) {
997
1004
  return (model.concludeAttempts.get(id) ?? []).filter(
998
1005
  (a) => attemptStatus(model, a) === "satisfied"
999
1006
  );
1000
1007
  }
1001
1008
  function pendingConcludeAttempt(model, id) {
1002
- const last = model.concludeAttempts.get(id)?.at(-1);
1009
+ const last = latestAttempt(model.concludeAttempts.get(id) ?? []);
1003
1010
  return last && attemptStatus(model, last) === "pending" ? last : null;
1004
1011
  }
1005
1012
  function isConcluded(model, id) {
1006
1013
  return concludeMilestones(model, id).length > 0;
1007
1014
  }
1008
1015
  function concludedHead(model, id) {
1009
- return concludeMilestones(model, id).at(-1)?.head ?? null;
1016
+ return latestAttempt(concludeMilestones(model, id))?.head ?? null;
1010
1017
  }
1011
1018
  function revisionsOf(model, id) {
1012
1019
  return [...model.revisions.values()].filter((r) => r.artifactId === id);
@@ -4068,6 +4075,41 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
4068
4075
  });
4069
4076
  })()
4070
4077
  ),
4078
+ // ★ 2026-08-18:交稿时把「系统看不出来的四件事」一并带上。
4079
+ //
4080
+ // 为什么必须写在**任务书**里:adapter 只把 TASK.md 当提示词喂给模型,其余 bundle 文件(包括
4081
+ // execution/CONTINUITY.md)只是躺在工作目录里、没人读。`--reason` 之所以交付过的节点 100% 都有,
4082
+ // 正因为它既进了任务书又是必填;这四项此前只活在 `oasis help propose` 里——next 上节点全部更新到
4083
+ // 认得它们之后的 10 小时里,两次真实交付**一次都没带**(实测 0/2)。
4084
+ //
4085
+ // 给 `none` 出口是刻意的:逼着写自由文本只会得到「暂无」这种假保证,而「确认没有」与「没人写」
4086
+ // 对下游是两条完全不同的信息(渲染侧据此分流,见 renderDeliveredWorkHandoff)。
4087
+ ...args.action === "review" ? [] : [
4088
+ ``,
4089
+ `### \u4EA4\u7A3F\u65F6\u4E00\u5E76\u5E26\u4E0A\uFF1A\u4E0B\u6E38\u8981\u7528\u7684\u56DB\u4EF6\u4E8B`,
4090
+ `\u8FD9\u56DB\u9879\u4F1A\u539F\u6837\u51FA\u73B0\u5728\u4E0B\u6E38\u8282\u70B9\u7684 \`inputs/<\u672C\u8282\u70B9>@<\u7248\u672C>/${UPSTREAM_HANDOFF_BASENAME}\` \u91CC\u3002\u5206\u53F7\u5206\u9694\uFF1B**\u786E\u5B9E\u6CA1\u6709\u5C31\u5199 \`none\`\uFF0C\u522B\u7F16\u300C\u6682\u65E0\u300D**\uFF1A`,
4091
+ `- \`--gaps\` **\u4F60\u6CA1\u80FD\u6838\u9A8C\u7684\u90E8\u5206** \u2014\u2014 \u6700\u8981\u7D27\u7684\u4E00\u9879\u3002\u4E0B\u6E38\u9ED8\u8BA4\u300C\u4E0A\u6E38\u4EA4\u4E86\u5C31\u662F\u9A8C\u8FC7\u4E86\u300D\uFF0C\u4F60\u4E0D\u5199\uFF0C\u6CA1\u9A8C\u7684\u90A3\u5757\u5C31\u76F4\u63A5\u8FDB\u4E0B\u4E00\u73AF\u3002\u4F8B\uFF1A\`--gaps "\u6CA1\u505A\u538B\u6D4B;\u53EA\u5728 dev \u9A8C\u8FC7"\``,
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
+ `- \`--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
+ `- \`--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**`,
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
+ `\`\`\``,
4106
+ `oasis continuity-checkpoint --trigger stage_completed \\`,
4107
+ ` --completed "\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4,\u9017\u53F7\u5206\u9694\uFF08\u7528\u7A33\u5B9A\u7684\u540D\u5B57\uFF0C\u91CD\u8DD1\u65F6\u540C\u4E00\u6B65\u540C\u4E00\u4E2A\u540D\uFF09" \\`,
4108
+ ` --pending "\u8FD8\u5269\u4EC0\u4E48\u6CA1\u505A" \\`,
4109
+ ` --recoverability full|partial|none`,
4110
+ `\`\`\``,
4111
+ `**\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`
4112
+ ],
4071
4113
  // 兜底(§10):上次失败了 → 明确告诉它发生了什么 + 怎么破(首选 add-revision 拆出去)
4072
4114
  ...args.lastFailure ? args.lastFailure.resumed ? [
4073
4115
  // 续跑分支:上文还在它眼前,别再说"没留下任何产出"(自相矛盾),改成"接着干 + 先核现场"。
@@ -7994,14 +8036,13 @@ FROM ${schema}.workflow_nodes n
7994
8036
  LEFT JOIN ${schema}.works lw ON lw.id = n.latest_work_id
7995
8037
  LEFT JOIN ${schema}.works ls ON ls.id = n.latest_success_work_id;
7996
8038
 
8039
+ -- workorder_view \u5DF2\u4E0B\u7EBF\uFF08\u5DE5\u5355 1aa1f35b\uFF09\uFF1A\u5B83\u53EA\u989D\u5916\u7ED9\u51FA total_count / done_count \u4E24\u4E2A\u8FDB\u5EA6\u6570\uFF0C\u5168\u4ED3\u96F6\u6D88\u8D39\u65B9\uFF0C
8040
+ -- \u4E14\u53E3\u5F84\u4E0E\u540E\u7AEF\u771F\u6E90\uFF08server summaryOfWorkspace \u7684 progressExcludingBrief\uFF09\u4E0D\u540C\u2014\u2014total \u53EA\u6392 cancelled\u3001done \u7528
8041
+ -- latest_accept_id IS NOT NULL\uFF08\u66FE\u88AB\u9A8C\u6536\u8FC7\u81F3\u5C11\u4E00\u6B21\uFF09\uFF0C\u800C\u771F\u6E90\u6392\u9664 brief + \u5DF2\u4F5C\u5E9F\u3001\u6309\u300C\u9A8C\u6536\u843D\u5728\u5F53\u524D\u7248\u300D\u7B97\u3002
8042
+ -- \u4FDD\u7559\u4E00\u4EFD\u89C6\u56FE\u65E2\u6CA1\u4EBA\u8BFB\u3001\u53E3\u5F84\u53C8\u5BF9\u4E0D\u4E0A\uFF0C\u53EA\u4F1A\u662F\u7B2C\u4E09\u5904\u5BF9\u4E0D\u4E0A\u7684\u8FDB\u5EA6\u6570\u3002\u8FD9\u91CC\u4E0D\u518D CREATE\uFF1B\u4E0B\u9762\u7684 DROP \u8BA9\u5B58\u91CF\u5E93
8043
+ -- \u5728\u4E0B\u6B21 migrate \u65F6\u628A\u65E7\u89C6\u56FE\u5220\u6389\uFF08ENGINE_DDL \u6BB5\u4EA6\u6709\u4E00\u5904 DROP\uFF0C\u4E8C\u8005\u90FD\u7559\u4EE5\u9632\u91CD\u6392\uFF09\u3002\u5DE5\u5355\u8FDB\u5EA6\u7EDF\u4E00\u8D70\u540E\u7AEF\u8BFB\u6A21\u578B\uFF0C
8044
+ -- \u9700\u8981 SQL \u4FA7\u7EDF\u8BA1\u65F6\u53E6\u7ACB\u4E0E\u771F\u6E90\u540C\u53E3\u5F84\u7684\u89C6\u56FE\u5E76\u6CE8\u660E\u670D\u52A1\u5BF9\u8C61\uFF0C\u522B\u590D\u6D3B\u8FD9\u4E24\u4E2A\u6570\u3002
7997
8045
  DROP VIEW IF EXISTS ${schema}.workorder_view;
7998
- CREATE VIEW ${schema}.workorder_view AS
7999
- SELECT o.*,
8000
- (SELECT count(*) FROM ${schema}.workflow_nodes n
8001
- WHERE n.workorder_id = o.id AND n.cancelled_at IS NULL) AS total_count,
8002
- (SELECT count(*) FROM ${schema}.workflow_nodes n
8003
- WHERE n.workorder_id = o.id AND n.cancelled_at IS NULL AND n.latest_accept_id IS NOT NULL) AS done_count
8004
- FROM ${schema}.workorders o;
8005
8046
  `;
8006
8047
  }
8007
8048
  var init_schema2 = __esm({
@@ -13215,7 +13256,10 @@ var init_store_postgres = __esm({
13215
13256
  JOIN ${this.s}.workflow_nodes n ON n.id = r.node_id WHERE n.workorder_id = $1`,
13216
13257
  [id]
13217
13258
  );
13218
- const works = await this.c.query(`SELECT * FROM ${this.s}.works WHERE workorder_id = $1`, [id]);
13259
+ const works = await this.c.query(
13260
+ `SELECT * FROM ${this.s}.works WHERE workorder_id = $1 ORDER BY created_at ASC, id ASC`,
13261
+ [id]
13262
+ );
13219
13263
  const arts = await this.c.query(
13220
13264
  `SELECT a.* FROM ${this.s}.work_artifacts a JOIN ${this.s}.works w ON w.id = a.work_id WHERE w.workorder_id = $1`,
13221
13265
  [id]
@@ -14063,6 +14107,11 @@ var init_plan = __esm({
14063
14107
  if (!node || node.cancelledAt) return;
14064
14108
  killLatestWork(state, e.nodeId, ctx.at);
14065
14109
  killNodeReviews(state, e.nodeId, ctx.at);
14110
+ for (const issue2 of state.allIssues()) {
14111
+ if (issue2.resolvedAt !== null) continue;
14112
+ if (issue2.aboutNodeId !== e.nodeId && issue2.raisedByNodeId !== e.nodeId) continue;
14113
+ state.updateIssue(issue2.id, { resolvedAt: ctx.at, resolution: "wontfix" });
14114
+ }
14066
14115
  state.updateNode(e.nodeId, { cancelledAt: ctx.at, updatedAt: ctx.at });
14067
14116
  for (const edge of state.edgesFrom(e.nodeId)) {
14068
14117
  const dn = state.node(edge.toNodeId);
@@ -16007,6 +16056,7 @@ function bridgeLoadReadModel(input) {
16007
16056
  const holds = /* @__PURE__ */ new Map();
16008
16057
  for (const node of allNodes) {
16009
16058
  if (!node.latestWorkId) continue;
16059
+ if (node.cancelledAt) continue;
16010
16060
  const lw = allWorks.find((w2) => w2.id === node.latestWorkId);
16011
16061
  if (!lw || !lw.deadAt) continue;
16012
16062
  if (node.latestAcceptId === node.latestWorkId) continue;
@@ -149101,6 +149151,7 @@ function stallOf(model, id, runtime, dispatcherSignals) {
149101
149151
  if (gaps.length > 0) {
149102
149152
  return { kind: "waiting_human", action: "resolve_gap", detail: `\u5361\u5728\u7F3A\u53E3\u4E0A\u5F85\u4EBA\u8865\uFF1A${gaps[0].description}` };
149103
149153
  }
149154
+ if (isDeliveredSettled(model, id)) return null;
149104
149155
  if (isHeld(model, id)) {
149105
149156
  const h = holdOf(model, id);
149106
149157
  return { kind: "held", action: "resume", detail: `\u5DF2\u6682\u505C\u6D3E\u53D1${h?.reason ? `\uFF1A${h.reason}` : ""}\u2014\u2014\u5F85\u6062\u590D`, ...h?.at ? { since: h.at } : {} };
@@ -149146,7 +149197,7 @@ function stallOf(model, id, runtime, dispatcherSignals) {
149146
149197
  if (dep) return { kind: "waiting_system", ref: dep.to, detail: `\u7B49\u4E0A\u6E38\u6536\u53E3\uFF1A${dep.to}` };
149147
149198
  return { kind: "waiting_system", detail: `\u963B\u585E\u4E2D\uFF1A${b2.reasons[0] ?? "\u5F85\u524D\u7F6E\u5C31\u7EEA"}` };
149148
149199
  }
149149
- const queueCause = lifecycleOf(model, id) === "active" ? queueBlockCauseOf(model, id) : null;
149200
+ const queueCause = queueBlockCauseOf(model, id);
149150
149201
  if (queueCause !== null) {
149151
149202
  const pending = queueOf(model, id);
149152
149203
  const since = pending[0]?.createdAt;
@@ -149973,9 +150024,10 @@ function summaryOfWorkspace(model, workspace, arts, ref2, resolveProject, dispat
149973
150024
  owner: ref2(seed.owner),
149974
150025
  participants: participantIds.map(ref2),
149975
150026
  artifactCount: arts.length,
150027
+ // 原始计数(含 brief、含已作废)——仅作旧客户端回退;界面真源是下面的 progressExcludingBrief。
149976
150028
  progress: { concluded, total: arts.length },
149977
- // ADR 0088 D1:两个派生位内联,前端不再按行拉详情。口径逐字对齐它替代的旧前端函数。
149978
- progressExcludingBrief: progressExcludingBrief(arts, members, { concluded, total: arts.length }),
150029
+ // ADR 0088 D1:派生位内联,前端不再按行拉详情。这是进度的唯一真源(排除 brief + 已作废,见函数注释)。
150030
+ progressExcludingBrief: progressExcludingBrief(arts, members),
149979
150031
  ...runtimeAlertOf(arts, members, runtimeOf) ?? {},
149980
150032
  updatedAt: lastOps[lastOps.length - 1] ?? "",
149981
150033
  projectId: resolveProject?.(workspace) ?? null,
@@ -149990,20 +150042,17 @@ function tagsOf(seed) {
149990
150042
  if (!Array.isArray(raw)) return [];
149991
150043
  return [...new Set(raw.map((v2) => typeof v2 === "string" ? v2.trim() : "").filter(Boolean))];
149992
150044
  }
149993
- function progressExcludingBrief(arts, members, progress) {
149994
- let briefCount = 0;
149995
- let briefConcluded = 0;
150045
+ function progressExcludingBrief(arts, members) {
150046
+ let total = 0;
150047
+ let concluded = 0;
149996
150048
  for (let i = 0; i < arts.length; i++) {
149997
- if (arts[i].type !== "brief") continue;
149998
- briefCount++;
149999
- const s2 = deriveNodeStage(members[i]);
150000
- if (s2 === "concluded" || s2 === "sealed") briefConcluded++;
150049
+ const info = members[i];
150050
+ if (arts[i].type === "brief") continue;
150051
+ if (info.sealed) continue;
150052
+ total++;
150053
+ if (info.concluded) concluded++;
150001
150054
  }
150002
- if (briefCount === 0) return progress;
150003
- return {
150004
- concluded: Math.max(0, progress.concluded - briefConcluded),
150005
- total: Math.max(0, progress.total - briefCount)
150006
- };
150055
+ return { concluded, total };
150007
150056
  }
150008
150057
  function runtimeAlertOf(arts, members, runtimeOf) {
150009
150058
  if (!runtimeOf) return void 0;
@@ -158830,6 +158879,8 @@ async function listModels(runtime, executablePath) {
158830
158879
  return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "kimi", "kimi", ["acp"]) ?? []);
158831
158880
  case "kiro":
158832
158881
  return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "kiro-cli", "kiro", ["acp"]) ?? []);
158882
+ case "reasonix":
158883
+ return cachedDiscovery(runtime, async () => await discoverACPModels(executablePath ?? "reasonix", "reasonix", ["acp"]) ?? []);
158833
158884
  case "openclaw":
158834
158885
  return cachedDiscovery(runtime, () => discoverOpenclawModels(executablePath));
158835
158886
  default:
@@ -159376,6 +159427,7 @@ var init_codex = __esm({
159376
159427
  "../adapters/src/codex/index.ts"() {
159377
159428
  "use strict";
159378
159429
  import_node_child_process9 = require("node:child_process");
159430
+ init_container_runtime();
159379
159431
  import_node_crypto15 = require("node:crypto");
159380
159432
  fs10 = __toESM(require("node:fs"), 1);
159381
159433
  os5 = __toESM(require("node:os"), 1);
@@ -159421,9 +159473,6 @@ var init_codex = __esm({
159421
159473
  */
159422
159474
  capabilities = { appendInput: false };
159423
159475
  async spawn(job) {
159424
- if (job.executionEnv) {
159425
- throw new Error("executionEnv\uFF1Acodex runtime \u5C1A\u672A\u652F\u6301\u5BB9\u5668\u5316\u6267\u884C\uFF08ADR-0147\uFF09\uFF0C\u8BF7\u6539\u6D3E claude-code \u5458\u5DE5\u6216\u53BB\u6389\u8BE5\u8282\u70B9\u7684 executionEnv \u58F0\u660E");
159426
- }
159427
159476
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
159428
159477
  const id = `codex-${slug6}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
159429
159478
  const startedAtMs = Date.now();
@@ -159483,7 +159532,21 @@ var init_codex = __esm({
159483
159532
  ),
159484
159533
  promptViaStdin ? "-" : prompt
159485
159534
  ];
159486
- const child = (0, import_node_child_process9.spawn)(this.opts.codexBin ?? "codex", args, {
159535
+ const containerized = await maybeContainerize({
159536
+ bin: this.opts.codexBin ?? "codex",
159537
+ args,
159538
+ cwd: dir,
159539
+ job,
159540
+ ...this.opts.workRoot !== void 0 ? { workRoot: this.opts.workRoot } : {},
159541
+ ...this.opts.env !== void 0 ? { extraEnvEarly: this.opts.env } : {}
159542
+ });
159543
+ const child = containerized ? (0, import_node_child_process9.spawn)(containerized.bin, containerized.args, {
159544
+ cwd: dir,
159545
+ env: scrubLeakyEnv(process.env),
159546
+ // stdin 的开合口径与宿主分支一致:codex 只在走 stdin 时才该收到管道(见下方长注释)。
159547
+ // 容器包装本身带 `-i`,podman 会把这条管道透传给容器内进程。
159548
+ stdio: [promptViaStdin ? "pipe" : "ignore", "pipe", "pipe"]
159549
+ }) : (0, import_node_child_process9.spawn)(this.opts.codexBin ?? "codex", args, {
159487
159550
  cwd: dir,
159488
159551
  env: {
159489
159552
  ...scrubLeakyEnv(process.env),
@@ -159598,6 +159661,7 @@ var init_codex = __esm({
159598
159661
  };
159599
159662
  const timer = setTimeout(() => {
159600
159663
  killReason ??= "timeout";
159664
+ if (containerized) killContainerBestEffort(containerized.containerName, containerized.mode);
159601
159665
  child.kill("SIGKILL");
159602
159666
  }, job.limits.wallClockMs);
159603
159667
  child.on("error", () => {
@@ -159625,13 +159689,16 @@ var init_codex = __esm({
159625
159689
  ...telemetry.usage ? { usage: telemetry.usage } : {},
159626
159690
  ...model ? { model } : {},
159627
159691
  ...runtimeSessionId ? { runtimeSessionId } : {},
159628
- ...errorMessage ? { errorMessage } : {}
159692
+ ...errorMessage ? { errorMessage } : {},
159693
+ // 跑在哪个镜像上要落账:同一题换了镜像,分数就不可比(ADR-0147 M5)
159694
+ ...containerized?.imageDigest ? { imageDigest: containerized.imageDigest } : {}
159629
159695
  });
159630
159696
  });
159631
159697
  return {
159632
159698
  id,
159633
159699
  async kill() {
159634
159700
  killReason ??= "cancelled";
159701
+ if (containerized) killContainerBestEffort(containerized.containerName, containerized.mode);
159635
159702
  child.kill("SIGKILL");
159636
159703
  },
159637
159704
  onExit(cb) {
@@ -160073,6 +160140,20 @@ function joinOutputParts(parts) {
160073
160140
  if (parts.every((p2) => typeof p2 === "string")) return parts.join("");
160074
160141
  return parts;
160075
160142
  }
160143
+ function pickApprovalOptionId(params) {
160144
+ const raw = params?.options;
160145
+ if (!Array.isArray(raw)) return LEGACY_APPROVE_OPTION_ID;
160146
+ const options = raw.filter(
160147
+ (o) => typeof o?.optionId === "string"
160148
+ );
160149
+ if (options.length === 0) return LEGACY_APPROVE_OPTION_ID;
160150
+ const byId = options.find((o) => o.optionId === LEGACY_APPROVE_OPTION_ID)?.optionId;
160151
+ const byKind = (kind) => options.find((o) => o.kind === kind)?.optionId;
160152
+ const nonReject = options.find(
160153
+ (o) => !String(o.kind ?? o.optionId).startsWith("reject")
160154
+ )?.optionId;
160155
+ return byId ?? byKind("allow_always") ?? byKind("allow_once") ?? nonReject ?? LEGACY_APPROVE_OPTION_ID;
160156
+ }
160076
160157
  function normalizeACPUpdate(data) {
160077
160158
  const d = data;
160078
160159
  if (!d) return ["", data];
@@ -160321,7 +160402,8 @@ async function runACPSession(job, cfg) {
160321
160402
  if (reason === "cancelled") finish({ code: null, reason: "cancelled", transcriptRef });
160322
160403
  },
160323
160404
  () => streamingCurrentTurn,
160324
- cfg.finalUsageIsAuthoritative === true
160405
+ cfg.finalUsageIsAuthoritative === true,
160406
+ cfg.vendorUsageNotification
160325
160407
  );
160326
160408
  usageClient = client;
160327
160409
  const rl = readline3.createInterface({ input: child.stdout });
@@ -160491,7 +160573,7 @@ ${task}` : task;
160491
160573
  }
160492
160574
  };
160493
160575
  }
160494
- var import_node_child_process10, import_node_crypto16, fs11, path11, readline3, ACPClient;
160576
+ var import_node_child_process10, import_node_crypto16, fs11, path11, readline3, ACPClient, LEGACY_APPROVE_OPTION_ID;
160495
160577
  var init_acp = __esm({
160496
160578
  "../adapters/src/_core/acp.ts"() {
160497
160579
  "use strict";
@@ -160510,13 +160592,14 @@ var init_acp = __esm({
160510
160592
  init_transcript();
160511
160593
  init_filter_extra_args();
160512
160594
  ACPClient = class {
160513
- constructor(write, onOutput, onTelemetry, onStop, acceptNotification = () => true, finalUsageIsAuthoritative = false) {
160595
+ constructor(write, onOutput, onTelemetry, onStop, acceptNotification = () => true, finalUsageIsAuthoritative = false, vendorUsage) {
160514
160596
  this.write = write;
160515
160597
  this.onOutput = onOutput;
160516
160598
  this.onTelemetry = onTelemetry;
160517
160599
  this.onStop = onStop;
160518
160600
  this.acceptNotification = acceptNotification;
160519
160601
  this.finalUsageIsAuthoritative = finalUsageIsAuthoritative;
160602
+ this.vendorUsage = vendorUsage;
160520
160603
  }
160521
160604
  nextId = 0;
160522
160605
  pending = /* @__PURE__ */ new Map();
@@ -160530,7 +160613,37 @@ var init_acp = __esm({
160530
160613
  * 是否还在流。每收到一条 session/update / session/notification 就 +1,主循环轮询该值。
160531
160614
  */
160532
160615
  notificationCount = 0;
160616
+ /** 厂商私有用量:已完成轮次的累计 + 当前轮的最新快照,两者相加即 this.usage。 */
160617
+ vendorTurnBase = newUsageAcc();
160618
+ vendorTurnSnapshot = newUsageAcc();
160619
+ applyVendorUsage(params) {
160620
+ const picked = this.vendorUsage?.pick(params);
160621
+ if (!picked) return;
160622
+ this.vendorTurnSnapshot = {
160623
+ inputTokens: picked.inputTokens ?? 0,
160624
+ outputTokens: picked.outputTokens ?? 0,
160625
+ cacheReadTokens: picked.cacheReadTokens ?? 0,
160626
+ cacheCreationTokens: picked.cacheCreationTokens ?? 0
160627
+ };
160628
+ this.usage = {
160629
+ inputTokens: this.vendorTurnBase.inputTokens + this.vendorTurnSnapshot.inputTokens,
160630
+ outputTokens: this.vendorTurnBase.outputTokens + this.vendorTurnSnapshot.outputTokens,
160631
+ cacheReadTokens: this.vendorTurnBase.cacheReadTokens + this.vendorTurnSnapshot.cacheReadTokens,
160632
+ cacheCreationTokens: this.vendorTurnBase.cacheCreationTokens + this.vendorTurnSnapshot.cacheCreationTokens
160633
+ };
160634
+ }
160635
+ /** 新一轮 prompt 发出前,把上一轮的快照折进基数(快照是「当前轮」口径,会归零重来)。 */
160636
+ foldVendorTurn() {
160637
+ this.vendorTurnBase = {
160638
+ inputTokens: this.vendorTurnBase.inputTokens + this.vendorTurnSnapshot.inputTokens,
160639
+ outputTokens: this.vendorTurnBase.outputTokens + this.vendorTurnSnapshot.outputTokens,
160640
+ cacheReadTokens: this.vendorTurnBase.cacheReadTokens + this.vendorTurnSnapshot.cacheReadTokens,
160641
+ cacheCreationTokens: this.vendorTurnBase.cacheCreationTokens + this.vendorTurnSnapshot.cacheCreationTokens
160642
+ };
160643
+ this.vendorTurnSnapshot = newUsageAcc();
160644
+ }
160533
160645
  request(method, params) {
160646
+ if (this.vendorUsage && method === "session/prompt") this.foldVendorTurn();
160534
160647
  const id = this.nextId++;
160535
160648
  return new Promise((resolve10, reject) => {
160536
160649
  this.pending.set(id, { resolve: resolve10, reject, method });
@@ -160582,11 +160695,15 @@ var init_acp = __esm({
160582
160695
  handleAgentRequest(raw) {
160583
160696
  const method = raw["method"];
160584
160697
  const id = raw["id"];
160585
- 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}` } };
160698
+ 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}` } };
160586
160699
  this.write(JSON.stringify(resp) + "\n");
160587
160700
  }
160588
160701
  handleNotification(raw) {
160589
160702
  const method = raw["method"];
160703
+ if (this.vendorUsage && method === this.vendorUsage.method) {
160704
+ this.applyVendorUsage(raw["params"]);
160705
+ return;
160706
+ }
160590
160707
  if (method !== "session/update" && method !== "session/notification") return;
160591
160708
  const params = raw["params"];
160592
160709
  if (!params?.update) return;
@@ -160745,6 +160862,7 @@ var init_acp = __esm({
160745
160862
  });
160746
160863
  }
160747
160864
  };
160865
+ LEGACY_APPROVE_OPTION_ID = "approve_for_session";
160748
160866
  }
160749
160867
  });
160750
160868
 
@@ -160846,6 +160964,72 @@ var init_kiro = __esm({
160846
160964
  }
160847
160965
  });
160848
160966
 
160967
+ // ../adapters/src/reasonix/index.ts
160968
+ var REASONIX_BLOCKED_FLAGS, REASONIX_USAGE_NOTIFICATION, ReasonixAdapter;
160969
+ var init_reasonix = __esm({
160970
+ "../adapters/src/reasonix/index.ts"() {
160971
+ "use strict";
160972
+ init_acp();
160973
+ REASONIX_BLOCKED_FLAGS = /* @__PURE__ */ new Map([
160974
+ ["--model", "value"],
160975
+ ["--planner", "value"],
160976
+ ["--sandbox-network", "value"],
160977
+ ["--sandbox-bash", "value"],
160978
+ ["--workspace-only", "boolean"]
160979
+ ]);
160980
+ REASONIX_USAGE_NOTIFICATION = {
160981
+ method: "_reasonix.io/session/status_update",
160982
+ pick(params) {
160983
+ const rec = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
160984
+ const num2 = (v2) => typeof v2 === "number" && Number.isFinite(v2) && v2 >= 0 ? v2 : void 0;
160985
+ const turn = rec(rec(rec(rec(params)?.["status"])?.["usage"])?.["turn"]);
160986
+ if (!turn) return void 0;
160987
+ const promptTokens = num2(turn["promptTokens"]);
160988
+ const cacheHit = num2(turn["cacheHitTokens"]);
160989
+ const cacheMiss = num2(turn["cacheMissTokens"]);
160990
+ const inputTokens = cacheMiss ?? (promptTokens === void 0 ? void 0 : Math.max(0, promptTokens - (cacheHit ?? 0)));
160991
+ const outputTokens = num2(turn["completionTokens"]);
160992
+ if (inputTokens === void 0 && outputTokens === void 0 && cacheHit === void 0) return void 0;
160993
+ return {
160994
+ ...inputTokens !== void 0 ? { inputTokens } : {},
160995
+ ...outputTokens !== void 0 ? { outputTokens } : {},
160996
+ ...cacheHit !== void 0 ? { cacheReadTokens: cacheHit } : {}
160997
+ };
160998
+ }
160999
+ };
161000
+ ReasonixAdapter = class {
161001
+ constructor(opts = {}) {
161002
+ this.opts = opts;
161003
+ }
161004
+ /** ADR-0093:ACP 引擎原生支持对话追加(同 session 二次 session/prompt)。 */
161005
+ capabilities = { appendInput: true };
161006
+ spawn(job) {
161007
+ return runACPSession(job, {
161008
+ bin: this.opts.reasonixBin ?? "reasonix",
161009
+ args: [
161010
+ "acp",
161011
+ "--planner",
161012
+ "auto",
161013
+ "--sandbox-network",
161014
+ "auto",
161015
+ "--sandbox-bash",
161016
+ "auto",
161017
+ "--workspace-only"
161018
+ ],
161019
+ model: this.opts.model,
161020
+ resumeMethod: "session/resume",
161021
+ vendorUsageNotification: REASONIX_USAGE_NOTIFICATION,
161022
+ blockedFlags: REASONIX_BLOCKED_FLAGS,
161023
+ workRoot: this.opts.workRoot,
161024
+ cleanupWorkdir: this.opts.cleanupWorkdir,
161025
+ env: this.opts.env,
161026
+ extraArgs: this.opts.extraArgs
161027
+ });
161028
+ }
161029
+ };
161030
+ }
161031
+ });
161032
+
160849
161033
  // ../adapters/src/_core/subprocess.ts
160850
161034
  async function materialize(job, cfg) {
160851
161035
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
@@ -161926,6 +162110,7 @@ var init_src6 = __esm({
161926
162110
  init_hermes();
161927
162111
  init_kimi();
161928
162112
  init_kiro();
162113
+ init_reasonix();
161929
162114
  init_cursor();
161930
162115
  init_codebuddy();
161931
162116
  init_gemini();
@@ -163913,6 +164098,7 @@ async function startOasisServer(opts) {
163913
164098
  ...tokenClaims.artifactId !== void 0 ? { artifactId: tokenClaims.artifactId } : {},
163914
164099
  durableRefs: tokenClaims.artifactId !== void 0 ? [{ kind: "artifact", id: tokenClaims.artifactId, label: body.command }] : [],
163915
164100
  at: (/* @__PURE__ */ new Date()).toISOString(),
164101
+ ...listArg("pendingSteps") ? { pendingSteps: listArg("pendingSteps") } : {},
163916
164102
  ...delivered ? { delivered } : {}
163917
164103
  });
163918
164104
  } catch (error2) {
@@ -166274,7 +166460,14 @@ var init_memory_registry_store = __esm({
166274
166460
  this.skillFiles.delete(skillId);
166275
166461
  }
166276
166462
  async putVariable(v2) {
166277
- this.variables.set(this._varKey(v2.key, v2.actorId, v2.projectId), { ...v2 });
166463
+ const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
166464
+ const ex = this.variables.get(mk);
166465
+ this.variables.set(mk, { ...v2, ...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {} });
166466
+ }
166467
+ async touchVariableUsage(key, actorId, projectId, when) {
166468
+ const mk = this._varKey(key, actorId, projectId);
166469
+ const ex = this.variables.get(mk);
166470
+ if (ex) this.variables.set(mk, { ...ex, lastUsedAt: when });
166278
166471
  }
166279
166472
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
166280
166473
  const mk = this._varKey(key, actorId);
@@ -174422,9 +174615,37 @@ ${input.description}
174422
174615
  // 非连接器写入到此 deliveryMode 必非空(上面已拦);连接器写入沿用 env(其值经 sensitiveVars 剔除,不进 env)。
174423
174616
  deliveryMode: v2.deliveryMode ?? "env",
174424
174617
  valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
174425
- updatedAt: this.now()
174618
+ updatedAt: this.now(),
174619
+ // ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
174620
+ // 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
174621
+ expiresAt: v2.expiresAt
174426
174622
  });
174427
174623
  }
174624
+ /** ADR 凭据保险库补章 §D2.3:某一行变量「明文真被取用」→ 刷 lastUsedAt。行按 (key, actorId?, projectId?)
174625
+ * 唯一键定位(与 put/delete 同址)。store 未实现该方法时静默跳过(不影响主功能)。 */
174626
+ async touchVariableUsage(key, origin, when) {
174627
+ await this.opts.store.touchVariableUsage?.(key, origin?.actorId, origin?.projectId, when ?? this.now());
174628
+ }
174629
+ /**
174630
+ * ADR 凭据保险库补章 §D1.2:**人类控制台** reveal 的取值——不走 agent 的 `resolveActorProvisionEntries`
174631
+ * 解析链(人类既无 personal 变量也无派单 artifactId,那条链恒空)。直接按存储唯一键取该行、解出明文。
174632
+ * - `selector` 由控制台列表行给出(personal 带 actorId、project 带 projectId、global 两者皆空)——
174633
+ * 与 `GET /api/variables`「对所有人类返回全量」的可见口径一致(补章 §D1.3),列得到即 reveal 得到。
174634
+ * - 命中返回明文 + 该行的 origin(供刷 lastUsedAt);未命中(无此行)返回 null(路由映射 403,与 agent 同壳)。
174635
+ */
174636
+ async revealHumanValue(key, selector) {
174637
+ const wantActor = selector?.actorId;
174638
+ const wantProject = selector?.projectId;
174639
+ const rows = await this.opts.store.listVariables();
174640
+ const row = rows.find(
174641
+ (r) => r.key === key && (r.actorId ?? void 0) === (wantActor ?? void 0) && (r.projectId ?? void 0) === (wantProject ?? void 0)
174642
+ );
174643
+ if (!row) return null;
174644
+ return {
174645
+ value: this.decodeCiphertext(row.valueEncrypted),
174646
+ origin: { ...row.actorId !== void 0 ? { actorId: row.actorId } : {}, ...row.projectId !== void 0 ? { projectId: row.projectId } : {} }
174647
+ };
174648
+ }
174428
174649
  /** 更新已有变量的 encrypted 属性(重新加密或解密),不改其他字段 */
174429
174650
  async patchVariableEncryption(key, actorId, encrypted) {
174430
174651
  const ct = await this.opts.store.getVariableCiphertext(key, actorId);
@@ -174463,7 +174684,10 @@ ${input.description}
174463
174684
  // 缺省视为 env(ADR §4.4;存量行读侧统一回填)
174464
174685
  encrypted: !isPlain,
174465
174686
  maskedValue: isPlain ? r.valueEncrypted.slice(6) : mask(this.decrypt(r.valueEncrypted)),
174466
- updatedAt: r.updatedAt
174687
+ updatedAt: r.updatedAt,
174688
+ // ADR 凭据保险库补章 §D2.1:到期/取用时间戳透传(展示层据此染色;补章 §D2.4)。
174689
+ ...r.expiresAt !== void 0 ? { expiresAt: r.expiresAt } : {},
174690
+ ...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {}
174467
174691
  };
174468
174692
  });
174469
174693
  }
@@ -174556,7 +174780,12 @@ ${input.description}
174556
174780
  const gated = (row) => !row.connectorId || enabledConnectors.has(row.connectorId);
174557
174781
  const put = (row) => {
174558
174782
  if (!gated(row)) return;
174559
- merged.set(row.key, { key: row.key, value: this.decodeCiphertext(row.valueEncrypted), deliveryMode: row.deliveryMode ?? "env" });
174783
+ merged.set(row.key, {
174784
+ key: row.key,
174785
+ value: this.decodeCiphertext(row.valueEncrypted),
174786
+ deliveryMode: row.deliveryMode ?? "env",
174787
+ origin: { ...row.actorId !== void 0 ? { actorId: row.actorId } : {}, ...row.projectId !== void 0 ? { projectId: row.projectId } : {} }
174788
+ });
174560
174789
  };
174561
174790
  for (const row of rows) {
174562
174791
  if (row.scope === "global" && !row.actorId) put(row);
@@ -174576,7 +174805,7 @@ ${input.description}
174576
174805
  async resolveRevealValue(actorId, key, opts) {
174577
174806
  const entries = await this.resolveActorProvisionEntries(actorId, { ...opts, refreshConnectors: false });
174578
174807
  const hit = entries.find((e) => e.key === key);
174579
- return hit ? { value: hit.value } : null;
174808
+ return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
174580
174809
  }
174581
174810
  encrypt(plain) {
174582
174811
  const iv = (0, import_node_crypto26.randomBytes)(12);
@@ -175805,6 +176034,21 @@ var init_connector_skills = __esm({
175805
176034
  function isKnownRevealSource(source) {
175806
176035
  return CREDENTIAL_REVEAL_SOURCES.has(source) || source.startsWith("agent-skill:");
175807
176036
  }
176037
+ function clientMetaFromReq(req) {
176038
+ const fwd = req.raw?.headers?.["x-forwarded-for"];
176039
+ const fwdFirst = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0]?.trim();
176040
+ const clientIp = fwdFirst || req.raw?.socket?.remoteAddress || void 0;
176041
+ const ua = req.raw?.headers?.["user-agent"];
176042
+ const userAgent = (Array.isArray(ua) ? ua[0] : ua) || void 0;
176043
+ return { ...clientIp ? { clientIp } : {}, ...userAgent ? { userAgent } : {} };
176044
+ }
176045
+ function normalizeExpiresAt(raw) {
176046
+ if (raw === void 0 || raw === null || raw === "") return void 0;
176047
+ if (typeof raw !== "string") throw new ApiError(400, "BAD_REQUEST", "expiresAt \u987B\u4E3A ISO-8601 \u5B57\u7B26\u4E32");
176048
+ const t = Date.parse(raw);
176049
+ if (Number.isNaN(t)) throw new ApiError(400, "BAD_REQUEST", `expiresAt \u4E0D\u662F\u5408\u6CD5\u65E5\u671F\uFF1A${raw}`);
176050
+ return new Date(t).toISOString();
176051
+ }
175808
176052
  function positionsFromPayload(body) {
175809
176053
  if (!body) return void 0;
175810
176054
  if (Object.prototype.hasOwnProperty.call(body, "roles")) {
@@ -175839,6 +176083,8 @@ function actorsDomain(opts) {
175839
176083
  const { resolveCtx } = opts;
175840
176084
  const revealLimits = opts.credentialRevealLimits ?? { perSession: 200, perSessionPerKey: 20 };
175841
176085
  const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
176086
+ const consoleRevealLimits = opts.credentialRevealLimits?.console ?? { perSession: 60, perSessionPerKey: 10 };
176087
+ const consoleRevealLimiter = new RevealRateLimiter(consoleRevealLimits.perSession, consoleRevealLimits.perSessionPerKey);
175842
176088
  return (router) => {
175843
176089
  router.get("/api/actors", async (req) => {
175844
176090
  const { service } = await resolveCtx(req.auth.companyId);
@@ -176125,11 +176371,6 @@ function actorsDomain(opts) {
176125
176371
  }
176126
176372
  };
176127
176373
  const auditVariableChange = async (req, kind, variableKey, targetScope) => {
176128
- const fwd = req.raw?.headers?.["x-forwarded-for"];
176129
- const fwdFirst = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0]?.trim();
176130
- const clientIp = fwdFirst || req.raw?.socket?.remoteAddress || void 0;
176131
- const ua = req.raw?.headers?.["user-agent"];
176132
- const userAgent = (Array.isArray(ua) ? ua[0] : ua) || void 0;
176133
176374
  await opts.credentialAudit?.({
176134
176375
  kind,
176135
176376
  actor: req.auth.actor,
@@ -176137,8 +176378,7 @@ function actorsDomain(opts) {
176137
176378
  targetScope,
176138
176379
  source: "console",
176139
176380
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
176140
- ...clientIp ? { clientIp } : {},
176141
- ...userAgent ? { userAgent } : {}
176381
+ ...clientMetaFromReq(req)
176142
176382
  });
176143
176383
  };
176144
176384
  router.get("/api/variables", async (req) => {
@@ -176151,6 +176391,7 @@ function actorsDomain(opts) {
176151
176391
  const { service } = await resolveCtx(req.auth.companyId);
176152
176392
  const b2 = req.body;
176153
176393
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
176394
+ const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
176154
176395
  if (b2.scope !== "global" && b2.scope !== "personal" && b2.scope !== "project") {
176155
176396
  throw new ApiError(
176156
176397
  400,
@@ -176191,7 +176432,8 @@ function actorsDomain(opts) {
176191
176432
  ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
176192
176433
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
176193
176434
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
176194
- ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
176435
+ ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
176436
+ expiresAt: expiresAt2
176195
176437
  });
176196
176438
  await auditVariableChange(
176197
176439
  req,
@@ -176229,6 +176471,7 @@ function actorsDomain(opts) {
176229
176471
  const actorId = req.params.id;
176230
176472
  const b2 = req.body;
176231
176473
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
176474
+ const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
176232
176475
  if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
176233
176476
  throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env\u3001ref \u6216 broker");
176234
176477
  }
@@ -176259,7 +176502,8 @@ function actorsDomain(opts) {
176259
176502
  ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
176260
176503
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
176261
176504
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
176262
- ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
176505
+ ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
176506
+ expiresAt: expiresAt2
176263
176507
  });
176264
176508
  await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
176265
176509
  return { status: 201, body: { ok: true } };
@@ -176280,25 +176524,33 @@ function actorsDomain(opts) {
176280
176524
  const source = typeof b2?.source === "string" && b2.source.trim() || "agent-cli";
176281
176525
  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`);
176282
176526
  const actor = req.auth.actor;
176527
+ const isHuman2 = isHumanActor(actor);
176528
+ const isConsole = source === "console";
176529
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
176283
176530
  const artifactId = req.auth.dispatch?.artifactId;
176284
176531
  const sessionId = req.auth.dispatch?.sessionId;
176285
- const now = Date.now();
176286
- const timestamp = new Date(now).toISOString();
176287
- const sessionKey = sessionId ?? `${actor}::${artifactId ?? "no-artifact"}`;
176288
176532
  const dispatchFields = { ...sessionId ? { sessionId } : {}, ...artifactId ? { artifactId } : {} };
176289
- const throttled = revealLimiter.check(sessionKey, key, now);
176533
+ const consoleMeta = isConsole ? clientMetaFromReq(req) : {};
176534
+ if (isConsole && !isHuman2) {
176535
+ await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason: "no_permission", ...dispatchFields, ...consoleMeta });
176536
+ return { status: 403, body: { code: "denied", key } };
176537
+ }
176538
+ const now = Date.now();
176539
+ const sessionKey = isConsole ? actor : sessionId ?? `${actor}::${artifactId ?? "no-artifact"}`;
176540
+ const limiter = isConsole ? consoleRevealLimiter : revealLimiter;
176541
+ const throttled = limiter.check(sessionKey, key, now);
176290
176542
  if (throttled) {
176291
- await opts.credentialAudit?.({ kind: "credential_reveal_throttled", actor, variableKey: key, source, timestamp, scope: throttled, ...dispatchFields });
176543
+ await opts.credentialAudit?.({ kind: "credential_reveal_throttled", actor, variableKey: key, source, timestamp, scope: throttled, ...dispatchFields, ...consoleMeta });
176292
176544
  return { status: 429, body: { code: "throttled", key, scope: throttled } };
176293
176545
  }
176294
- const scope = artifactId && opts.resolveDispatchScope ? await opts.resolveDispatchScope(artifactId) : {};
176295
- const resolved = await service.resolveRevealValue(actor, key, scope);
176546
+ 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) : {});
176296
176547
  if (!resolved) {
176297
176548
  const reason = await service.hasVariableKey(key) ? "no_permission" : "not_found";
176298
- await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason, ...dispatchFields });
176549
+ await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason, ...dispatchFields, ...consoleMeta });
176299
176550
  return { status: 403, body: { code: "denied", key } };
176300
176551
  }
176301
- await opts.credentialAudit?.({ kind: "credential_reveal", actor, variableKey: key, source, timestamp, ...dispatchFields });
176552
+ await opts.credentialAudit?.({ kind: "credential_reveal", actor, variableKey: key, source, timestamp, ...dispatchFields, ...consoleMeta });
176553
+ await service.touchVariableUsage(key, resolved.origin, timestamp);
176302
176554
  return { status: 200, body: { value: resolved.value } };
176303
176555
  });
176304
176556
  router.get("/api/variables/reveal-audit", async (req) => {
@@ -176801,7 +177053,7 @@ var init_routes2 = __esm({
176801
177053
  init_image_upload();
176802
177054
  init_skill_materializer();
176803
177055
  init_connector_skills();
176804
- CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "agent-exec", "smoke-test", "oasis-internal"]);
177056
+ CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "agent-exec", "smoke-test", "oasis-internal", "console"]);
176805
177057
  RevealRateLimiter = class {
176806
177058
  constructor(perSession, perSessionPerKey, ttlMs = 2 * 60 * 6e4) {
176807
177059
  this.perSession = perSession;
@@ -183017,8 +183269,8 @@ function executionContinuityDomain(service) {
183017
183269
  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)) {
183018
183270
  throw new ApiError(400, "BAD_REQUEST", "handoff arrays contain invalid values");
183019
183271
  }
183020
- if (!isUserHandoff(b2.userHandoff)) {
183021
- 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");
183272
+ if (b2.userHandoff !== void 0 && b2.userHandoff !== null && !isUserHandoff(b2.userHandoff)) {
183273
+ throw new ApiError(400, "BAD_REQUEST", "userHandoff \u5F62\u72B6\u975E\u6CD5\uFF08\u53EF\u4EE5\u4E0D\u7ED9\uFF1B\u7ED9\u4E86\u5C31\u8981\u5408\u6CD5\uFF09");
183022
183274
  }
183023
183275
  const handoff = await service.handoff({
183024
183276
  attemptId,
@@ -183069,10 +183321,20 @@ function renderResumePack(pack) {
183069
183321
  if (!pack.previousAttempt) lines.push("- This is the first attempt.");
183070
183322
  else {
183071
183323
  lines.push(`- Attempt ${pack.previousAttempt.attemptNo ?? "?"}: ${pack.previousHandoff?.summary || pack.previousAttempt.status}`);
183072
- if (pack.latestCheckpoint) {
183073
- lines.push(`- Recoverability: ${pack.latestCheckpoint.recoverability}`);
183074
- for (const step of pack.latestCheckpoint.pendingSteps) lines.push(` - pending: ${step}`);
183075
- for (const ref2 of pack.latestCheckpoint.durableRefs) lines.push(` - durable: ${ref2.kind}:${ref2.id}${ref2.version ? `@${ref2.version}` : ""}`);
183324
+ const cp = pack.latestCheckpoint;
183325
+ const done = cp?.completedStepIds ?? [];
183326
+ const durable = (cp?.durableRefs.length ? cp.durableRefs : pack.previousHandoff?.changedOutputs) ?? [];
183327
+ if (cp) lines.push(`- Recoverability: ${cp.recoverability}`);
183328
+ if (done.length > 0) lines.push(`- \u4E0A\u4E00\u8D9F\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4\uFF1A${done.join("\u3001")}`);
183329
+ for (const ref2 of durable) {
183330
+ lines.push(` - durable: ${ref2.kind}:${ref2.id}${ref2.version ? `@${ref2.version}` : ""}${ref2.label ? ` \u2014 ${ref2.label}` : ""}`);
183331
+ }
183332
+ if (cp && cp.pendingSteps.length > 0) {
183333
+ for (const step of cp.pendingSteps) lines.push(` - pending: ${step}`);
183334
+ } else {
183335
+ lines.push(
183336
+ 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"
183337
+ );
183076
183338
  }
183077
183339
  }
183078
183340
  lines.push("", "## External effects");
@@ -183181,6 +183443,7 @@ function renderCoordinatorView(view) {
183181
183443
  function shouldInjectResumePack(pack) {
183182
183444
  const status = pack?.previousAttempt?.status;
183183
183445
  if (!status) return false;
183446
+ if (status === "running" || status === "queued") return false;
183184
183447
  return status !== "succeeded" && status !== "no-output";
183185
183448
  }
183186
183449
  function renderContinuityProtocol() {
@@ -183413,9 +183676,6 @@ var init_service7 = __esm({
183413
183676
  async handoff(input) {
183414
183677
  if (!HANDOFF_OUTCOMES2.has(input.outcome)) throw new Error(`invalid handoff outcome: ${input.outcome}`);
183415
183678
  if (!input.summary.trim()) throw new Error("handoff summary is required");
183416
- if (input.producedBy === "agent" && !input.userHandoff) {
183417
- throw new Error("agent-authored handoff requires userHandoff");
183418
- }
183419
183679
  const userHandoff = input.userHandoff ? normalizeUserHandoff(input.outcome, input.changedOutputs ?? [], input.userHandoff) : void 0;
183420
183680
  const attempt = await this.awaitAttempt(input.attemptId);
183421
183681
  const handoff = await this.afterAttemptVisible(() => this.store.putHandoff({
@@ -183619,8 +183879,7 @@ var init_service7 = __esm({
183619
183879
  "failed"
183620
183880
  ].map((state) => [state, nodes.filter((node) => node.nodeState === state).length]));
183621
183881
  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";
183622
- 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) ?? "";
183623
- return { workOrderId, mode: this.mode, collaborationStatus, handoffSummary, progress, nodes, dependencyBlocks, todo: todos };
183882
+ return { workOrderId, mode: this.mode, collaborationStatus, progress, nodes, dependencyBlocks, todo: todos };
183624
183883
  }
183625
183884
  };
183626
183885
  }
@@ -183662,15 +183921,20 @@ var init_handoff_work_bridge = __esm({
183662
183921
 
183663
183922
  // ../server/src/domains/execution-continuity/upstream-handoff-input.ts
183664
183923
  function renderRefs(refs) {
183665
- return refs.map(
183666
- (ref2) => `- ${ref2.kind}:${ref2.id}${ref2.version ? `@${ref2.version}` : ""}${ref2.label ? ` \u2014 ${ref2.label}` : ""}`
183667
- );
183924
+ return refs.map((ref2) => {
183925
+ const label = ref2.id.startsWith(`${ref2.kind}:`) ? ref2.id : `${ref2.kind}:${ref2.id}`;
183926
+ return `- ${label}${ref2.version ? `@${ref2.version}` : ""}${ref2.label ? ` \u2014 ${ref2.label}` : ""}`;
183927
+ });
183668
183928
  }
183669
183929
  function bounded(text) {
183670
183930
  const t = text.trim();
183671
183931
  return [...t].length <= INJECTED_TEXT_LIMIT ? t : `${[...t].slice(0, INJECTED_TEXT_LIMIT).join("")}\u2026\uFF08\u539F\u6587\u8FC7\u957F\uFF0C\u6B64\u5904\u622A\u65AD\uFF1B\u5B8C\u6574\u5185\u5BB9\u89C1\u8BE5\u4E0A\u6E38\u8282\u70B9\u7684\u4EA4\u4ED8\u8BF4\u660E\uFF09`;
183672
183932
  }
183933
+ function isExplicitNone(values) {
183934
+ return values.length === 1 && NONE_TOKENS.has((values[0] ?? "").trim().toLowerCase());
183935
+ }
183673
183936
  function renderList(title, values) {
183937
+ if (isExplicitNone(values)) return [`## ${title}`, "- \u6267\u884C\u4EBA\u786E\u8BA4\uFF1A\u65E0", ""];
183674
183938
  return [
183675
183939
  `## ${title}`,
183676
183940
  ...values.length > 0 ? values.map((value) => `- ${value}`) : ["- None"],
@@ -183750,8 +184014,7 @@ function createUpstreamHandoffInputResolver(opts) {
183750
184014
  const edge = snapshot.edges.find(
183751
184015
  (candidate) => candidate.fromNodeId === input.upstreamNodeId && candidate.toNodeId === input.downstreamNodeId
183752
184016
  );
183753
- const downstream = snapshot.nodes.find((node) => node.id === input.downstreamNodeId);
183754
- const businessEdge = edge?.kind === "data" && edge.required && downstream?.fields?.[BUSINESS_HANDOFF_POLICY_FIELD] !== BUSINESS_HANDOFF_POLICY_EXEMPT;
184017
+ const businessEdge = edge?.kind === "data" && edge.required;
183755
184018
  if (!businessEdge) return null;
183756
184019
  if (edge.pinnedWorkId !== input.pinnedWorkId) {
183757
184020
  warn(
@@ -183813,12 +184076,12 @@ function createUpstreamHandoffInputResolver(opts) {
183813
184076
  }
183814
184077
  };
183815
184078
  }
183816
- var INJECTED_TEXT_LIMIT;
184079
+ var INJECTED_TEXT_LIMIT, NONE_TOKENS;
183817
184080
  var init_upstream_handoff_input = __esm({
183818
184081
  "../server/src/domains/execution-continuity/upstream-handoff-input.ts"() {
183819
184082
  "use strict";
183820
- init_src3();
183821
184083
  INJECTED_TEXT_LIMIT = 4e3;
184084
+ NONE_TOKENS = /* @__PURE__ */ new Set(["none", "n/a", "na", "nil", "\u65E0", "\u6CA1\u6709", "\u6682\u65E0"]);
183822
184085
  }
183823
184086
  });
183824
184087
 
@@ -188029,7 +188292,10 @@ function rowToVariable(row) {
188029
188292
  // **不塌成 env**,否则未知档位读回来变明文;仅 null/空回填 "env"(存量行)。派单侧按未知档保守处理。
188030
188293
  deliveryMode: typeof row.delivery_mode === "string" && row.delivery_mode ? row.delivery_mode : "env",
188031
188294
  valueEncrypted: row.value_encrypted,
188032
- updatedAt: new Date(row.updated_at).toISOString()
188295
+ updatedAt: new Date(row.updated_at).toISOString(),
188296
+ // ADR 凭据保险库补章 §D2.1:到期/取用时间戳(text 列存 ISO 字符串,原样带出;null/空 = undefined)。
188297
+ ...row.expires_at ? { expiresAt: row.expires_at } : {},
188298
+ ...row.last_used_at ? { lastUsedAt: row.last_used_at } : {}
188033
188299
  };
188034
188300
  }
188035
188301
  var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
@@ -188192,6 +188458,8 @@ var init_postgres_registry = __esm({
188192
188458
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS project_id text`);
188193
188459
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS environment text`);
188194
188460
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS delivery_mode text`);
188461
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
188462
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
188195
188463
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
188196
188464
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
188197
188465
  await pool.query(`
@@ -188668,10 +188936,10 @@ var init_postgres_registry = __esm({
188668
188936
  /* ---------- 变量 ---------- */
188669
188937
  async putVariable(v2) {
188670
188938
  await this.pool.query(
188671
- `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at)
188672
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
188939
+ `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at)
188940
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
188673
188941
  ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
188674
- SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9`,
188942
+ 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`,
188675
188943
  [
188676
188944
  v2.key,
188677
188945
  v2.scope,
@@ -188681,10 +188949,18 @@ var init_postgres_registry = __esm({
188681
188949
  v2.overrides ?? null,
188682
188950
  v2.deliveryMode ?? null,
188683
188951
  v2.valueEncrypted,
188684
- v2.updatedAt
188952
+ v2.updatedAt,
188953
+ v2.expiresAt ?? null
188685
188954
  ]
188686
188955
  );
188687
188956
  }
188957
+ async touchVariableUsage(key, actorId, projectId, when) {
188958
+ await this.pool.query(
188959
+ `UPDATE ${this.s}.variables SET last_used_at=$4
188960
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=COALESCE($3::text,'')`,
188961
+ [key, actorId ?? null, projectId ?? null, when]
188962
+ );
188963
+ }
188688
188964
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
188689
188965
  const aid = actorId ?? null;
188690
188966
  await this.pool.query(
@@ -194415,6 +194691,15 @@ async function buildActorProvision(service, actorId, scope, onBrokerRefused) {
194415
194691
  const brokerRefused = [];
194416
194692
  applyRefDelivery(env, entries, void 0, (k2) => brokerRefused.push(k2));
194417
194693
  if (onBrokerRefused) for (const k2 of brokerRefused) await onBrokerRefused(k2);
194694
+ const brokerSet = new Set(brokerRefused);
194695
+ for (const e of entries) {
194696
+ if (brokerSet.has(e.key)) continue;
194697
+ try {
194698
+ await service.touchVariableUsage(e.key, e.origin);
194699
+ } catch (err) {
194700
+ console.warn(`[credential] touchVariableUsage(${e.key}) \u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u6D3E\u5355\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`);
194701
+ }
194702
+ }
194418
194703
  return {
194419
194704
  env,
194420
194705
  wrapperPaths: [oasisWrapperScript(), ...provision.wrapperPaths],
@@ -195726,7 +196011,10 @@ async function startServe(opts) {
195726
196011
  ...await materializeSkillFiles({ service: actors.service, actorId, runtimeKind })
195727
196012
  }),
195728
196013
  uploadChatAttachment: async ({ contentBase64 }) => ({ blobRef: await assets.put(new Uint8Array(Buffer.from(contentBase64, "base64"))) }),
195729
- onDispatchCommandCompleted: async ({ dispatchId, command, durableRefs, at, delivered }) => {
196014
+ onDispatchCommandCompleted: async ({ dispatchId, command, durableRefs, at, delivered, pendingSteps }) => {
196015
+ if (pendingSteps && pendingSteps.length > 0) {
196016
+ traceStoreSink.checkpointState(dispatchId, { pendingSteps: [...pendingSteps] });
196017
+ }
195730
196018
  traceStoreSink.checkpoint(dispatchId, {
195731
196019
  trigger: "important_output",
195732
196020
  // 交作业的那条命令带着版本号(`wk:<节点>:<n>`),比笼统的「动过这个产物」有用得多——
@@ -197696,12 +197984,13 @@ var KNOWN2 = [
197696
197984
  { adapter: "copilot", binary: "copilot" },
197697
197985
  { adapter: "kimi", binary: "kimi" },
197698
197986
  { adapter: "kiro", binary: "kiro-cli" },
197987
+ { adapter: "reasonix", binary: "reasonix" },
197699
197988
  { adapter: "pi", binary: "pi" },
197700
197989
  { adapter: "openclaw", binary: "openclaw" },
197701
197990
  { adapter: "codebuddy", binary: "codebuddy" },
197702
197991
  { adapter: "antigravity", binary: "agy" }
197703
197992
  ];
197704
- var NATIVE_APPEND_RUNTIMES = /* @__PURE__ */ new Set(["hermes", "gemini", "kimi", "kiro"]);
197993
+ var NATIVE_APPEND_RUNTIMES = /* @__PURE__ */ new Set(["hermes", "gemini", "kimi", "kiro", "reasonix"]);
197705
197994
  function onPath(binary) {
197706
197995
  try {
197707
197996
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -198505,6 +198794,7 @@ async function startNode(opts) {
198505
198794
  copilot: new CopilotAdapter(),
198506
198795
  kimi: new KimiAdapter(),
198507
198796
  kiro: new KiroAdapter(),
198797
+ reasonix: new ReasonixAdapter(),
198508
198798
  pi: new PiAdapter(),
198509
198799
  openclaw: new OpenClawAdapter(),
198510
198800
  codebuddy: new CodebuddyAdapter(),
@@ -198989,7 +199279,8 @@ var COMMAND_DECLS = {
198989
199279
  { 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" },
198990
199280
  { name: "risks", desc: "\u4E0B\u6E38\u8BE5\u77E5\u9053\u7684\u98CE\u9669\uFF08\u5206\u53F7\u5206\u9694\uFF09" },
198991
199281
  { name: "next", desc: "\u5EFA\u8BAE\u4E0B\u6E38\u63A5\u7740\u505A\u4EC0\u4E48\uFF08\u5206\u53F7\u5206\u9694\uFF09" },
198992
- { 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" }
199282
+ { 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" },
199283
+ { 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" }
198993
199284
  ],
198994
199285
  examples: [
198995
199286
  'oasis propose artifact:dev:abc-123 --from-dir deliverable/ --reason "\u4FEE\u590D\u767B\u5F55\u9875\u767D\u5C4F"',
@@ -201675,7 +201966,9 @@ ${res.warning}`);
201675
201966
  ...semicolonList(flags.get("unresolved"), "unresolvedIssues"),
201676
201967
  ...semicolonList(flags.get("risks"), "risks"),
201677
201968
  ...semicolonList(flags.get("next"), "nextActions"),
201678
- ...semicolonList(flags.get("gaps"), "verificationGaps")
201969
+ ...semicolonList(flags.get("gaps"), "verificationGaps"),
201970
+ // 「还剩什么没做」:唯一系统推不出来的字段,挂在本来就要跑的命令上(见 server 的 pendingSteps 注释)
201971
+ ...semicolonList(flags.get("pending"), "pendingSteps")
201679
201972
  });
201680
201973
  println(message);
201681
201974
  break;
@@ -202949,7 +203242,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
202949
203242
  }
202950
203243
 
202951
203244
  // src/index.ts
202952
- var PKG_VERSION = true ? "0.1.107" : "dev";
203245
+ var PKG_VERSION = true ? "0.1.109" : "dev";
202953
203246
  var OASIS_DIR = path28.join(os11.homedir(), ".oasis");
202954
203247
  var CONFIG_FILE = path28.join(OASIS_DIR, "node-config.json");
202955
203248
  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.107",
3
+ "version": "0.1.109",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"