oasis_test_v2 2.2.84 → 2.2.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +304 -42
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2977,6 +2977,7 @@ async function fetchResultPackage(base, fetchImpl, runId) {
2977
2977
  function toEpochMs(v2) {
2978
2978
  if (typeof v2 === "number" && Number.isFinite(v2)) return v2;
2979
2979
  if (typeof v2 === "string" && v2.length > 0) {
2980
+ if (/^\d+(\.\d+)?$/.test(v2.trim())) return Number(v2.trim());
2980
2981
  const ms = Date.parse(v2);
2981
2982
  return Number.isNaN(ms) ? void 0 : ms;
2982
2983
  }
@@ -2993,6 +2994,98 @@ function latestSucceededRun(runs) {
2993
2994
  }
2994
2995
  return best;
2995
2996
  }
2997
+ function liveBoardRuns(st) {
2998
+ const out = [];
2999
+ const known = /* @__PURE__ */ new Set();
3000
+ for (const t of st.turns ?? []) {
3001
+ if (t.runId !== null) known.add(t.runId);
3002
+ if (isTerminalRunStatus(t.status)) continue;
3003
+ out.push({ key: t.runId ?? `msg:${t.messageId ?? "?"}`, status: t.status, ...t.agentId ? { agentId: t.agentId } : {}, source: "conversation" });
3004
+ }
3005
+ for (const r of st.runs) {
3006
+ if (known.has(r.runId) || r.runId.length === 0) continue;
3007
+ if (r.status !== "running" && r.status !== "queued") continue;
3008
+ out.push({ key: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, source: "registry" });
3009
+ }
3010
+ return out;
3011
+ }
3012
+ function latestEndedRun(st) {
3013
+ let best;
3014
+ for (const t of st.turns ?? []) {
3015
+ if (!isTerminalRunStatus(t.status)) continue;
3016
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
3017
+ }
3018
+ if (best) return { runId: best.runId, status: best.status, ...best.agentId ? { agentId: best.agentId } : {}, ...best.error ? { error: best.error } : {} };
3019
+ const r = st.runs.find((x2) => isTerminalRunStatus(x2.status));
3020
+ return r ? { runId: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, ...r.error ? { error: r.error } : {} } : void 0;
3021
+ }
3022
+ function snapshotSourceRunId(st) {
3023
+ let best;
3024
+ for (const t of st.turns ?? []) {
3025
+ if (t.status !== "succeeded" || t.runId === null) continue;
3026
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
3027
+ }
3028
+ return best?.runId ?? latestSucceededRun(st.runs)?.runId;
3029
+ }
3030
+ function nonEmptyStr(v2) {
3031
+ return typeof v2 === "string" && v2.trim().length > 0 ? v2 : void 0;
3032
+ }
3033
+ function turnError(m2, status) {
3034
+ const events = Array.isArray(m2["events"]) ? m2["events"] : [];
3035
+ for (let i = events.length - 1; i >= 0; i--) {
3036
+ const e = events[i];
3037
+ if (!e || typeof e !== "object") continue;
3038
+ const ev = e;
3039
+ if (ev["kind"] === "status" && ev["label"] === "error" && nonEmptyStr(ev["detail"])) return ev["detail"];
3040
+ }
3041
+ return status !== "succeeded" && isTerminalRunStatus(status) ? nonEmptyStr(m2["content"]) : void 0;
3042
+ }
3043
+ async function fetchBoardTurns(base, fetchImpl, boardKey) {
3044
+ const convPath = `/api/projects/${encodeURIComponent(boardKey)}/conversations`;
3045
+ const guard = (err) => {
3046
+ if (err instanceof OdHttpError && (err.status === 401 || err.status === 403)) throw err;
3047
+ return null;
3048
+ };
3049
+ let convs;
3050
+ try {
3051
+ convs = (await odApi(base, fetchImpl, "GET", convPath)).conversations ?? [];
3052
+ } catch (err) {
3053
+ if (err instanceof OdHttpError && err.status === 404) return [];
3054
+ return guard(err);
3055
+ }
3056
+ const turns = [];
3057
+ for (const c of convs) {
3058
+ const cid = nonEmptyStr(c["id"]);
3059
+ if (cid === void 0) continue;
3060
+ let msgs;
3061
+ try {
3062
+ msgs = (await odApi(base, fetchImpl, "GET", `${convPath}/${encodeURIComponent(cid)}/messages`)).messages ?? [];
3063
+ } catch (err) {
3064
+ if (err instanceof OdHttpError && err.status === 404) continue;
3065
+ return guard(err);
3066
+ }
3067
+ for (const m2 of msgs) {
3068
+ if (!m2 || typeof m2 !== "object" || m2["role"] !== "assistant") continue;
3069
+ const status = nonEmptyStr(m2["runStatus"]);
3070
+ if (status === void 0) continue;
3071
+ const runId = nonEmptyStr(m2["runId"]) ?? null;
3072
+ const messageId = nonEmptyStr(m2["id"]) ?? null;
3073
+ if (runId === null && messageId === null) continue;
3074
+ const turn = { conversationId: cid, runId, messageId, status };
3075
+ const agentId = nonEmptyStr(m2["agentId"]);
3076
+ if (agentId) turn.agentId = agentId;
3077
+ const error2 = turnError(m2, status);
3078
+ if (error2) turn.error = error2;
3079
+ const startedAt = toEpochMs(m2["startedAt"]) ?? toEpochMs(m2["createdAt"]);
3080
+ if (startedAt !== void 0) turn.startedAt = startedAt;
3081
+ const endedAt = toEpochMs(m2["endedAt"]);
3082
+ if (endedAt !== void 0) turn.endedAt = endedAt;
3083
+ turns.push(turn);
3084
+ }
3085
+ }
3086
+ turns.sort((a, b2) => (b2.startedAt ?? Number.NEGATIVE_INFINITY) - (a.startedAt ?? Number.NEGATIVE_INFINITY) || 0);
3087
+ return turns;
3088
+ }
2996
3089
  async function fetchBoardStatus(base, fetchImpl, artifactId) {
2997
3090
  const boardKey = deriveBoardKey(artifactId);
2998
3091
  let boardExists = true;
@@ -3035,7 +3128,31 @@ async function fetchBoardStatus(base, fetchImpl, artifactId) {
3035
3128
  } catch {
3036
3129
  }
3037
3130
  }
3038
- return { boardKey, boardExists, runs, artifacts };
3131
+ const turns = boardExists ? await fetchBoardTurns(base, fetchImpl, boardKey) : [];
3132
+ return { boardKey, boardExists, runs, turns, artifacts };
3133
+ }
3134
+ async function waitBoardSettled(base, fetchImpl, artifactId, opts = {}) {
3135
+ const interval = opts.intervalMs ?? 2e4;
3136
+ const timeout = opts.timeoutMs ?? 100 * 6e4;
3137
+ const sleep6 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
3138
+ const now = opts.now ?? Date.now;
3139
+ const started = now();
3140
+ let lastKey = null;
3141
+ let waited = false;
3142
+ for (; ; ) {
3143
+ const st = await fetchBoardStatus(base, fetchImpl, artifactId);
3144
+ const elapsedMs = now() - started;
3145
+ if (!st.boardExists) return { status: st, settled: true, waited, live: [], elapsedMs };
3146
+ const live = liveBoardRuns(st);
3147
+ const unreadable = st.turns === null;
3148
+ if (live.length === 0 && !unreadable) return { status: st, settled: true, waited, live, elapsedMs };
3149
+ const key2 = unreadable && live.length === 0 ? "?" : live.map((r) => `${r.key}=${r.status}`).sort().join(",");
3150
+ opts.onPoll?.({ live, elapsedMs, changed: key2 !== lastKey, unreadable });
3151
+ lastKey = key2;
3152
+ if (elapsedMs >= timeout) return { status: st, settled: false, waited: true, live, elapsedMs };
3153
+ waited = true;
3154
+ await sleep6(Math.min(interval, Math.max(0, timeout - elapsedMs)));
3155
+ }
3039
3156
  }
3040
3157
  async function pumpDesignEvents(base, fetchImpl, runId, onLine) {
3041
3158
  const res = await fetchImpl(`${base}/api/runs/${runId}/events`);
@@ -3104,16 +3221,25 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3104
3221
  }
3105
3222
  }
3106
3223
  }
3107
- fs2.writeFileSync(
3108
- path2.join(dir, "board.link"),
3109
- `${publicBase}/projects/${boardKey}
3110
- \u5165\u53E3\u7A3F\uFF1A${entry}
3111
- \u5FEB\u7167\u6765\u6E90 run\uFF1A${runId}
3112
- \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
3113
- `
3114
- );
3224
+ fs2.writeFileSync(path2.join(dir, BOARD_LINK_FILE), formatBoardLink({ publicBase, boardKey, entry, runId }));
3115
3225
  return { dir, entry };
3116
3226
  }
3227
+ function formatBoardLink(args) {
3228
+ return `${args.publicBase}/projects/${args.boardKey}
3229
+ \u5165\u53E3\u7A3F\uFF1A${args.entry}
3230
+ ${SOURCE_RUN_LABEL}${args.runId}
3231
+ \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
3232
+ `;
3233
+ }
3234
+ function parseBoardLinkSourceRun(text7) {
3235
+ for (const line2 of text7.split(/\r?\n/)) {
3236
+ const t = line2.trim();
3237
+ if (!t.startsWith(SOURCE_RUN_LABEL)) continue;
3238
+ const id = t.slice(SOURCE_RUN_LABEL.length).trim();
3239
+ return id.length > 0 && id !== UNKNOWN_SOURCE_RUN ? id : null;
3240
+ }
3241
+ return null;
3242
+ }
3117
3243
  async function startDesignChatTurn(opts) {
3118
3244
  const fetchImpl = withOdToken(opts.fetchImpl ?? fetch, opts.token);
3119
3245
  const boardKey = deriveBoardKey(opts.artifactId);
@@ -3143,7 +3269,7 @@ async function runDesignChatTurn(opts) {
3143
3269
  const { artifacts, fileCount } = await fetchResultPackage(opts.base, fetchImpl, runId);
3144
3270
  return { boardKey, conversationId, runId, userMessageId, artifacts, fileCount };
3145
3271
  }
3146
- var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR;
3272
+ var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR, TERMINAL_TURN_STATUS, isTerminalRunStatus, BOARD_LINK_FILE, SOURCE_RUN_LABEL, UNKNOWN_SOURCE_RUN;
3147
3273
  var init_driver = __esm({
3148
3274
  "../adapters/src/open-design/driver.ts"() {
3149
3275
  "use strict";
@@ -3157,6 +3283,11 @@ var init_driver = __esm({
3157
3283
  }
3158
3284
  };
3159
3285
  TASK_CONTEXT_DIR = "task-context/";
3286
+ TERMINAL_TURN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
3287
+ isTerminalRunStatus = (s2) => TERMINAL_TURN_STATUS.has(s2);
3288
+ BOARD_LINK_FILE = "board.link";
3289
+ SOURCE_RUN_LABEL = "\u5FEB\u7167\u6765\u6E90 run\uFF1A";
3290
+ UNKNOWN_SOURCE_RUN = "status-pull";
3160
3291
  }
3161
3292
  });
3162
3293
 
@@ -141016,14 +141147,19 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
141016
141147
  })() : canvasMode ? [
141017
141148
  // 内核只出「你产什么 + 内核权威锚点」——**设计工作流散文全在你的人设(identity/AGENTS.md「产出流程」)**,
141018
141149
  // 那是单一真源、改它=改配置不发版(提案 design-conversational-driving 治本,2026-07-17)。这里不重复正文,
141019
- // 只钉住内核唯一不能漂的两件事:真实 artifactId、缺前置的逃生口 gap。
141150
+ // 只钉住内核不能漂的几件事:真实 artifactId、缺前置的逃生口 gap,以及「下单后在会话里 status --wait 等结果」
141151
+ //(2026-09-24 加:它直接关系引擎的零产出判定——下单即退出会被判零产出→协议续唤→连败熔断;人设各公司
141152
+ // 各一份、未必同步更新,这条放内核才保证每个设计员工都拿得到)。
141020
141153
  `\u4F60\u8D1F\u8D23 **design \u4EA7\u7269 \`${artifactId}\`**\uFF0C\u8FD0\u884C\u5728**\u901A\u7528\u8FD0\u884C\u65F6**\u4E0A\u2014\u2014\u4F60\u662F\u7532\u65B9\u8BBE\u8BA1\u8D1F\u8D23\u4EBA\uFF0C\u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8**\u534F\u4F5C\u753B\u677F\u5F15\u64CE**\u6253\u78E8\u754C\u9762\u7A3F\u3002`,
141021
141154
  ``,
141022
141155
  `**\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF08\u600E\u4E48\u53D6\u8D27/\u8D77\u8F6E/\u8BFB\u4EA7\u7269/\u4EA4\u7A3F/\u522B\u624B\u6539\u677F\u6587\u4EF6\u2026\uFF09\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\` \u7684\u300C\u4EA7\u51FA\u6D41\u7A0B\uFF08design \u4EA7\u7269\uFF09\u300D**\u2014\u2014\u628A\u91CC\u9762 \`<artifactId>\` \u90FD\u6362\u6210\u672C\u4EA7\u7269 \`${artifactId}\`\u3002`,
141023
141156
  ``,
141024
- `\u5185\u6838\u53EA\u5728\u6B64\u9489\u4E24\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
141157
+ `\u5185\u6838\u53EA\u5728\u6B64\u9489\u51E0\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
141025
141158
  `- **\u4EA4\u7A3F\u8D70 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "\u2026"\`**\uFF08proposed \u624D\u8FDB\u7248\u672C\u53F2\u3001\u4E0B\u6E38\u624D\u62FF\u5F97\u5230\uFF1B\u4E0D propose = head \u4ECD \u2205\uFF09\uFF1B`,
141026
- `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`
141159
+ `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`,
141160
+ `- **\u5728\u672C\u4F1A\u8BDD\u91CC\u7B49\u753B\u677F\u51FA\u7ED3\u679C**\uFF08\u540C\u5F00\u53D1\u7B49 CI\uFF09\uFF1A\u5F00\u5DE5\u5148 \`oasis design status ${artifactId}\`\uFF08\u6062\u590D\u4F18\u5148\uFF0C\u677F\u4E0A\u5DF2\u6709\u8D27/\u5DF2\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u522B\u91CD\u4E0B\uFF09\uFF1B\u4E0B\u5355\u7528 \`oasis design chat ${artifactId} \u2026 --async\`\uFF0C\u7136\u540E**\u7ACB\u523B** \`oasis design status ${artifactId} --wait --pull\` \u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF08\u6700\u957F\u7EA6 100 \u5206\u949F\uFF1B\u963B\u585E\u671F\u95F4\u4E0D\u8017 token\uFF09\u3002\u6210\u529F \u2192 \u6838\u5BF9 \u2192 propose\uFF1B\u5931\u8D25 \u2192 \u6309\u5B83\u7ED9\u7684\u539F\u56E0\u6362\u5F15\u64CE\uFF08\`--agent\`\uFF09\u6216\u8865\u6E05\u9700\u6C42\u540E\u91CD\u4E0B\uFF0C\u518D \`--wait\`\u3002`,
141161
+ ` - **\u4E0D\u8981\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD**\u2014\u2014\u6CA1\u4EA4\u4E1C\u897F\u5C31\u6536\u5DE5\u4F1A\u88AB\u5224\u96F6\u4EA7\u51FA\uFF1B**\u4E5F\u4E0D\u8981\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status \u8F6E\u8BE2**\u2014\u2014\u6BCF\u67E5\u4E00\u6B21\u90FD\u8981\u63A8\u7406\u4E00\u6B21\u3002\u547D\u4EE4\u82E5\u88AB\u6267\u884C\u5DE5\u5177\u7684\u5355\u6761\u8D85\u65F6\u6253\u65AD\u6216\u8D85\u65F6\u9000\u51FA\uFF0C\u539F\u6837\u518D\u8DD1\u4E00\u6B21 \`--wait\` \u63A5\u7740\u7B49\uFF08\u5B83\u7B49\u7684\u662F\u753B\u677F\uFF0C\u4E0D\u4F1A\u91CD\u590D\u4E0B\u5355\uFF09\u3002`,
141162
+ `- \u88AB\u753B\u677F\u5DE1\u68C0\u7684\u4FE1\u5524\u9192\u65F6\uFF1A\u5148\u770B\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355 board.link \u91CC\u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u662F\u4E0D\u662F\u5C31\u662F\u4FE1\u91CC\u90A3\u4E00\u8F6E\u2014\u2014\u662F\u5C31 \`oasis resolve <\u4FE1 id>\`\uFF0C\u522B\u91CD\u590D propose\u3002`
141027
141163
  ] : (() => {
141028
141164
  const codeRepo = args.codeRepo ?? null;
141029
141165
  const repos = typeDef?.contentType === "code" ? codeRepo?.repos : void 0;
@@ -277486,11 +277622,18 @@ function messageError(m2, status) {
277486
277622
  }
277487
277623
  return { error: null, errorCode: null };
277488
277624
  }
277625
+ function ownerIsWorking(model, a) {
277626
+ if (a.latestWorkState !== "running") return false;
277627
+ const latest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.lane !== "reply").sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277628
+ return latest !== void 0 && latest.author === a.owner;
277629
+ }
277489
277630
  var RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, SINCE_MARKER_PREFIX, boardWatchSinceMarker, parseSinceMarker, TERMINAL_RUN_STATUS, MAX_REASON_CHARS, MAX_LISTED_FILES, BoardWatcher;
277490
277631
  var init_board_watch = __esm({
277491
277632
  "../server/src/design/board-watch.ts"() {
277492
277633
  "use strict";
277634
+ init_src();
277493
277635
  init_src7();
277636
+ init_src2();
277494
277637
  RUN_MARKER_PREFIX = "[od-board-watch run:";
277495
277638
  boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
277496
277639
  parseRunMarker = (body2) => {
@@ -277523,6 +277666,14 @@ var init_board_watch = __esm({
277523
277666
  seenConvAt = /* @__PURE__ */ new Map();
277524
277667
  /** 公司 → 上次记下的失败原文(同样的错只记一次;成功一轮就清掉)。 */
277525
277668
  companyLastError = /* @__PURE__ */ new Map();
277669
+ /** `<公司>\0<artifactId>\0<轮次键>` → 这一轮已被取走(head 来自它或更晚的一轮),不再重判。只在内存,不写假信。 */
277670
+ takenRounds = /* @__PURE__ */ new Set();
277671
+ /** `<公司>\0<boardKey>` → (runId → endedAt):判断 head 的快照来源 run 是不是「更晚的一轮」用。 */
277672
+ roundEndedAt = /* @__PURE__ */ new Map();
277673
+ /** `<公司>\0<revisionId>` → 该版 board.link 的快照来源 runId(null = 没有/读不到)。revision 内容不可变,缓存安全。 */
277674
+ revisionSourceRun = /* @__PURE__ */ new Map();
277675
+ /** 因 owner 在做而压着没写的 `<公司>\0<artifactId>`(只为日志只打一次)。 */
277676
+ deferredLogged = /* @__PURE__ */ new Set();
277526
277677
  async odGet(path47) {
277527
277678
  const fetchImpl = this.deps.fetchImpl ?? fetch;
277528
277679
  try {
@@ -277583,7 +277734,8 @@ var init_board_watch = __esm({
277583
277734
  if (listCompanies && kernelFor) {
277584
277735
  for (const companyId of await listCompanies()) {
277585
277736
  try {
277586
- await this.tickCompany(companyId, await kernelFor(companyId), now, denied);
277737
+ const blobs = this.deps.blobsFor ? await this.deps.blobsFor(companyId) : void 0;
277738
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied, blobs);
277587
277739
  this.companyLastError.delete(companyId);
277588
277740
  } catch (err) {
277589
277741
  const msg = String(err);
@@ -277593,7 +277745,7 @@ var init_board_watch = __esm({
277593
277745
  }
277594
277746
  }
277595
277747
  } else {
277596
- await this.tickCompany("", this.deps.kernel, now, denied);
277748
+ await this.tickCompany("", this.deps.kernel, now, denied, this.deps.blobs);
277597
277749
  }
277598
277750
  if (denied.length > 0) {
277599
277751
  const codes = [...new Set(denied.map((d) => d.status))].join("/");
@@ -277604,7 +277756,7 @@ var init_board_watch = __esm({
277604
277756
  }
277605
277757
  }
277606
277758
  /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277607
- async tickCompany(company, kernel, now, denied) {
277759
+ async tickCompany(company, kernel, now, denied, blobs) {
277608
277760
  const { boardKeyOf, log: log3 } = this.deps;
277609
277761
  const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
277610
277762
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
@@ -277639,12 +277791,40 @@ var init_board_watch = __esm({
277639
277791
  continue;
277640
277792
  }
277641
277793
  if (recordedSince === null && !this.pinnedSince.has(pinKey)) this.pinnedSince.set(pinKey, since);
277642
- const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key)).sort((x2, y) => x2.endedAt - y.endedAt);
277794
+ const endedAtOf = this.roundEndedAt.get(`${company}\0${boardKey}`) ?? /* @__PURE__ */ new Map();
277795
+ this.roundEndedAt.set(`${company}\0${boardKey}`, endedAtOf);
277796
+ for (const r of read3.value.rounds) if (r.runId !== null) endedAtOf.set(r.runId, r.endedAt);
277797
+ const takenKey = (r) => `${company}\0${a.id}\0${r.key}`;
277798
+ const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key) && !this.takenRounds.has(takenKey(r))).sort((x2, y) => x2.endedAt - y.endedAt);
277799
+ const deferKey = `${company}\0${a.id}`;
277800
+ if (fresh.length > 0 && ownerIsWorking(model, a)) {
277801
+ if (!this.deferredLogged.has(deferKey)) {
277802
+ this.deferredLogged.add(deferKey);
277803
+ log3?.(`[board-watch] ${tag}${a.id} \u7684 owner \u6B63\u5728\u6267\u884C\u8FD9\u4E2A\u8282\u70B9\uFF0C\u677F ${boardKey} \u4E0A ${fresh.length} \u8F6E\u7ED3\u675F\u7684\u5BF9\u8BDD\u5148\u4E0D\u5199\u4FE1\uFF0C\u7B49\u5B83\u8FD9\u8F6E work \u7ED3\u675F\u518D\u770B`);
277804
+ }
277805
+ continue;
277806
+ }
277807
+ this.deferredLogged.delete(deferKey);
277808
+ const sourceRuns = fresh.length > 0 && blobs ? await this.headSourceRuns(company, blobs, model, a, log3, tag) : [];
277809
+ const takenBy = (r) => {
277810
+ for (const src of sourceRuns) {
277811
+ if (r.runId !== null && src === r.runId) return src;
277812
+ const srcEnded = endedAtOf.get(src);
277813
+ if (srcEnded !== void 0 && srcEnded >= r.endedAt) return src;
277814
+ }
277815
+ return null;
277816
+ };
277643
277817
  const seen = /* @__PURE__ */ new Set();
277644
277818
  for (const r of fresh) {
277645
277819
  if (written >= maxPerTick) break;
277646
277820
  if (seen.has(r.key)) continue;
277647
277821
  seen.add(r.key);
277822
+ const src = takenBy(r);
277823
+ if (src !== null) {
277824
+ this.takenRounds.add(takenKey(r));
277825
+ log3?.(`[board-watch] ${tag}${a.id} \u2190 \u677F ${boardKey} \u4E00\u8F6E\u5BF9\u8BDD\u7ED3\u675F\uFF08${r.key.slice(0, 12)} ${r.status}\uFF09\uFF0C\u5F53\u524D\u7248\u672C\u7684\u5FEB\u7167\u6765\u6E90 run ${src} \u5DF2\u8986\u76D6\u5B83 \u2192 \u4E0D\u5199\u4FE1`);
277826
+ continue;
277827
+ }
277648
277828
  const ok3 = r.status === "succeeded";
277649
277829
  const reason = ok3 ? null : r.error ?? (r.runId !== null ? await this.runErrorFromRegistry(boardKey, r.runId) : null);
277650
277830
  await kernel.annotate({ artifactId: a.id, actor, body: this.letterBody(a.id, boardKey, r, since, reason), kind: "comment" });
@@ -277657,6 +277837,36 @@ var init_board_watch = __esm({
277657
277837
  }
277658
277838
  }
277659
277839
  }
277840
+ /**
277841
+ * 当前 head 与最近一版装箱单(两者可能不同:head 在跑空壳/被打回时回落)各自 board.link 里的快照来源 runId。
277842
+ * 读不到(非装箱单、没有 board.link、blob 缺失、占位 status-pull)的不算。
277843
+ */
277844
+ async headSourceRuns(company, blobs, model, a, log3, tag) {
277845
+ const candidates = [];
277846
+ const head = a.currentRev ? model.revisions.get(a.currentRev) : void 0;
277847
+ if (head) candidates.push(head);
277848
+ const latestManifest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.contentKind === "manifest" && r.contentRef.length > 0).sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277849
+ if (latestManifest && latestManifest.id !== head?.id) candidates.push(latestManifest);
277850
+ const out = [];
277851
+ for (const rev of candidates) {
277852
+ if (rev.contentKind !== "manifest" || rev.contentRef.length === 0) continue;
277853
+ const cacheKey = `${company}\0${rev.id}`;
277854
+ let src = this.revisionSourceRun.get(cacheKey);
277855
+ if (src === void 0) {
277856
+ try {
277857
+ const manifest = parseManifest(await blobs.get(rev.contentRef));
277858
+ const entry = manifest.entries.find((e) => e.path === BOARD_LINK_FILE);
277859
+ src = entry ? parseBoardLinkSourceRun(new TextDecoder().decode(await blobs.get(entry.hash))) : null;
277860
+ } catch (err) {
277861
+ log3?.(`[board-watch] ${tag}\u8BFB ${a.id} \u7248\u672C ${rev.id} \u7684 board.link \u5931\u8D25\uFF08${String(err)}\uFF09\uFF0C\u6309\u300C\u4E0D\u77E5\u9053\u6765\u81EA\u54EA\u4E00\u8F6E\u300D\u5904\u7406`);
277862
+ src = null;
277863
+ }
277864
+ this.revisionSourceRun.set(cacheKey, src);
277865
+ }
277866
+ if (src !== null) out.push(src);
277867
+ }
277868
+ return out;
277869
+ }
277660
277870
  letterBody(artifactId, boardKey, r, since, reason) {
277661
277871
  const marker = `${boardWatchRunMarker(r.key)} ${boardWatchSinceMarker(since)}`;
277662
277872
  const round = `${r.runId !== null ? `run ${r.runId.slice(0, 8)}` : `\u6D88\u606F ${r.key.slice(4, 12)}`}\uFF0C\u5F15\u64CE=${r.agentId ?? "?"}\uFF0C\u7ED3\u675F\u4E8E ${new Date(r.endedAt).toISOString()}`;
@@ -277667,7 +277877,8 @@ var init_board_watch = __esm({
277667
277877
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD\u8DD1\u5B8C\u4E86\uFF08${round}\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u4EA7\u51FA\uFF1A`,
277668
277878
  files,
277669
277879
  ``,
277670
- `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277880
+ `\u5148\u770B\u8FD9\u4E00\u8F6E\u662F\u4E0D\u662F\u5DF2\u7ECF\u4EA4\u8FC7\uFF1A\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355\u91CC board.link \u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u5982\u679C\u5C31\u662F ${r.runId ?? "\u8FD9\u4E00\u8F6E"}\uFF0C\u8BF4\u660E\u5DF2\u53D6\u8D70\uFF0C\`oasis resolve <\u672C\u4FE1 id> --as resolved\` \u5373\u53EF\uFF0C\u522B\u91CD\u590D propose\u3002`,
277881
+ `\u6CA1\u4EA4\u8FC7\u5C31\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --wait --pull\`\uFF08\u677F\u4E0A\u8FD8\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u4F1A\u5148\u7B49\u5B83\u8DD1\u5B8C\uFF09\u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF0C\u968F\u540E \`oasis design status ${artifactId} --wait --pull\` \u7B49\u7ED3\u679C\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277671
277882
  ].join("\n");
277672
277883
  }
277673
277884
  const quoted = reason === null ? null : reason.length > MAX_REASON_CHARS ? `${reason.slice(0, MAX_REASON_CHARS)}\u2026` : reason;
@@ -277676,7 +277887,7 @@ var init_board_watch = __esm({
277676
277887
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD**\u6CA1\u8DD1\u6210**\uFF08\u7EC8\u6001=${r.status}\uFF0C${round}\uFF09\u3002`,
277677
277888
  quoted !== null ? `od \u8BB0\u7684\u539F\u56E0\uFF08\u539F\u6587\uFF09\uFF1A${quoted}${r.errorCode !== null ? `\uFF08\u9519\u8BEF\u7801 ${r.errorCode}\uFF09` : ""}` : `od \u6CA1\u6709\u8BB0\u5F55\u8FD9\u4E00\u8F6E\u7684\u5931\u8D25\u539F\u56E0\u3002`,
277678
277889
  ``,
277679
- `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\uFF1B\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277890
+ `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --turn continue --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\u3002\u91CD\u4E0B\u4E4B\u540E\u7ACB\u523B \`oasis design status ${artifactId} --wait --pull\` \u5728\u4F1A\u8BDD\u91CC\u7B49\u7ED3\u679C\uFF0C\u522B\u4E0B\u5355\u5C31\u6536\u5DE5\u3002\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277680
277891
  ].join("\n");
277681
277892
  }
277682
277893
  };
@@ -278809,6 +279020,7 @@ __export(src_exports2, {
278809
279020
  openNewEngine: () => openNewEngine,
278810
279021
  operatorCrossCompanyRead: () => operatorCrossCompanyRead,
278811
279022
  outputsPartProvider: () => outputsPartProvider,
279023
+ ownerIsWorking: () => ownerIsWorking,
278812
279024
  packMatches: () => packMatches,
278813
279025
  parentConversationTaskLines: () => parentConversationTaskLines,
278814
279026
  parseConnectorSkillId: () => parseConnectorSkillId,
@@ -289309,6 +289521,8 @@ ${nodeFault}` : "");
289309
289521
  return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
289310
289522
  },
289311
289523
  kernelFor: async (companyId) => (await getEngine(companyId)).kernel,
289524
+ // 读 head 装箱单里的 board.link(「快照来源 run」)判断这一轮是否已被取走,已取走就不写信
289525
+ blobsFor: async (companyId) => (await getEngine(companyId)).blobs,
289312
289526
  odBase,
289313
289527
  ...odToken ? { odToken } : {},
289314
289528
  boardKeyOf: deriveBoardKey,
@@ -292568,29 +292782,32 @@ var COMMAND_DECLS = {
292568
292782
  flags: [
292569
292783
  { name: "message", desc: "\u6D88\u606F\u5185\u5BB9", required: true },
292570
292784
  { name: "turn", desc: "\u5BF9\u8BDD\u8F6E\u6B21\uFF08first | continue\uFF09" },
292571
- { name: "async", desc: "\u5F02\u6B65\u6A21\u5F0F\uFF08\u8D77\u5B8C\u5373\u8FD4\uFF09" },
292785
+ { name: "async", desc: "\u4E0B\u5355\u5373\u8FD4\uFF1B\u968F\u540E\u7528 design status --wait --pull \u963B\u585E\u7B49\u7ED3\u679C" },
292572
292786
  { name: "system", desc: "\u7CFB\u7EDF\u63D0\u793A" }
292573
292787
  ],
292574
292788
  examples: ['oasis design chat artifact:design:xyz --message "\u628A\u6309\u94AE\u6539\u6210\u84DD\u8272" --turn continue']
292575
292789
  },
292576
292790
  "design status": {
292577
292791
  group: "\u8BBE\u8BA1\u7A3F",
292578
- usage: "oasis design status <artifactId> [--pull]",
292579
- description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002",
292792
+ usage: "oasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]",
292793
+ description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002--wait\uFF1A\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5230\u5168\u90E8\u7ED3\u675F\uFF08\u4E0D\u7BA1\u662F\u8C01\u8D77\u7684\uFF09\uFF0C\u518D\u6253\u5370\u7ED3\u679C\uFF1B\u8D85\u65F6\u975E\u96F6\u9000\u51FA\u3002",
292580
292794
  positional: [
292581
292795
  { name: "<artifactId>", required: true, desc: "\u4EA7\u7269 id" }
292582
292796
  ],
292583
292797
  flags: [
292584
- { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730" }
292798
+ { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730\uFF08\u5E26 --wait \u65F6\uFF1A\u7B49\u5B8C\u4E14\u6700\u8FD1\u4E00\u8F6E\u6210\u529F\u624D\u62C9\uFF09" },
292799
+ { name: "wait", desc: "\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5B83\u4EEC\u7ED3\u675F\uFF08\u540C gh pr checks --watch\uFF09", boolean: true },
292800
+ { name: "wait-timeout", desc: "--wait \u6700\u591A\u7B49\u51E0\u5206\u949F\uFF08\u9ED8\u8BA4 100\uFF09" },
292801
+ { name: "wait-interval", desc: "--wait \u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 20\uFF09" }
292585
292802
  ],
292586
- examples: ["oasis design status artifact:design:xyz --pull"]
292803
+ examples: ["oasis design status artifact:design:xyz --wait --pull", "oasis design status artifact:design:xyz --pull"]
292587
292804
  },
292588
292805
  design: {
292589
292806
  group: "\u8BBE\u8BA1\u7A3F",
292590
292807
  usage: "oasis design <chat|status> <artifactId> ...",
292591
292808
  description: "\u8BBE\u8BA1\u4EA7\u7269\u76F8\u5173\u64CD\u4F5C\u3002\u8FDE\u54EA\u4E2A open-design\uFF1A\u5458\u5DE5\u5F00\u4E86 open-design \u8FDE\u63A5\u5668\u5C31\u8FDE\u5B83\u767B\u8BB0\u7684\u5730\u5740\u5E76\u5E26\u4EE4\u724C\uFF08\u4EFB\u610F\u8282\u70B9\u53EF\u7528\uFF09\uFF0C\u5426\u5219 $OASIS_OPEN_DESIGN_URL\uFF0C\u518D\u5426\u5219\u672C\u673A\u3002",
292592
292809
  subCommands: ["design chat", "design status"],
292593
- examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async']
292810
+ examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async', "oasis design status artifact:design:xyz --wait --pull"]
292594
292811
  },
292595
292812
  // —— 图机制(§6/§7) ——
292596
292813
  annotate: {
@@ -294268,6 +294485,15 @@ function needPos(positional, i, usage) {
294268
294485
  if (v2 === void 0) throw new Error(`\u7528\u6CD5: ${usage}`);
294269
294486
  return v2;
294270
294487
  }
294488
+ function positiveNumberFlag(flags, name, dflt) {
294489
+ const raw = flags.get(name);
294490
+ if (raw === void 0) return dflt;
294491
+ const n = Number(raw);
294492
+ if (!Number.isFinite(n) || n <= 0) throw new Error(`--${name} \u8981\u4E00\u4E2A\u6B63\u6570\uFF08\u6536\u5230\uFF1A${raw}\uFF09`);
294493
+ return n;
294494
+ }
294495
+ var shortRunKey = (key2) => key2.startsWith("msg:") ? `\u6D88\u606F ${key2.slice(4, 12)}` : `run ${key2.slice(0, 8)}`;
294496
+ var clipReason = (reason) => reason.length > 300 ? `${reason.slice(0, 300)}\u2026` : reason;
294271
294497
  function semicolonList(raw, field) {
294272
294498
  if (raw === void 0) return {};
294273
294499
  const items = raw.split(";").map((x2) => x2.trim()).filter(Boolean);
@@ -294470,7 +294696,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
294470
294696
  }
294471
294697
  }
294472
294698
  }
294473
- var CLI_VERSION = "2.2.84" ? "2.2.84" : "dev";
294699
+ var CLI_VERSION = "2.2.85" ? "2.2.85" : "dev";
294474
294700
  function apiHeaders(token, company) {
294475
294701
  const headers = {
294476
294702
  authorization: `Bearer ${token}`,
@@ -296240,7 +296466,7 @@ ${res.warning}`);
296240
296466
  println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odLabel} \u2026`);
296241
296467
  const started = await startDesignChatTurn(baseTurnOpts);
296242
296468
  println(`[design] \u5DF2\u4E0B\u5355\uFF1Arun ${started.runId.slice(0, 8)}\uFF08board ${started.boardKey}, \u53EF\u89C1 user \u56DE\u5408 ${started.userMessageId.slice(0, 8)}\uFF09\u3002\u5F15\u64CE\u901A\u5E38\u8981 13\u201344 \u5206\u949F\u3002`);
296243
- println(`[design] \u4E4B\u540E\u7528 oasis design status ${artifactId} \u67E5\u8FDB\u5EA6/\u53D6\u8D27\uFF08--pull \u62C9\u4EA7\u7269\u5230\u672C\u5730\u8BFB\uFF09\uFF1B\u4E0D\u5FC5\u5B88\u7740\u7B49\u3002`);
296469
+ println(`[design] \u4E0B\u4E00\u6B65\u7ACB\u523B\u6267\u884C\uFF1Aoasis design status ${artifactId} --wait --pull\uFF08\u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF0C\u6700\u957F\u7B49 100 \u5206\u949F\uFF09\u3002\u522B\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD\uFF0C\u4E5F\u522B\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status\u3002`);
296244
296470
  break;
296245
296471
  }
296246
296472
  println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odLabel} \u2026`);
@@ -296254,46 +296480,82 @@ ${res.warning}`);
296254
296480
  break;
296255
296481
  }
296256
296482
  if (sub === "status") {
296257
- const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull]");
296483
+ const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull] [--wait]");
296258
296484
  const od = resolveOpenDesignEndpoint(flags.get("od-url"));
296259
296485
  const odBase = od.base;
296260
296486
  const odFetch = withOdToken(fetch, od.token);
296261
- const st = await fetchBoardStatus(odBase, odFetch, artifactId);
296487
+ let st;
296488
+ let waitedForRun = false;
296489
+ if (flags.get("wait") === "true") {
296490
+ const timeoutMin = positiveNumberFlag(flags, "wait-timeout", 100);
296491
+ const intervalSec = positiveNumberFlag(flags, "wait-interval", 20);
296492
+ const fmt = (ms) => `${Math.floor(ms / 6e4)} \u5206 ${Math.floor(ms % 6e4 / 1e3)} \u79D2`;
296493
+ const waited = await waitBoardSettled(odBase, odFetch, artifactId, {
296494
+ timeoutMs: timeoutMin * 6e4,
296495
+ intervalMs: intervalSec * 1e3,
296496
+ // 每次轮询打一行撑 liveness;状态没变只打短的一行,别刷屏
296497
+ onPoll: ({ live: live2, elapsedMs, changed, unreadable }) => {
296498
+ if (!changed) return println(`[design] \u2026\u4ECD\u5728\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296499
+ if (live2.length === 0 && unreadable) return println(`[design] \u8FD9\u6B21\u6CA1\u8BFB\u5230\u753B\u677F\u5BF9\u8BDD\u8BB0\u5F55\uFF08od \u6682\u65F6\u4E0D\u901A\uFF1F\uFF09\uFF0C\u63A5\u7740\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296500
+ const runs = live2.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`).join("\uFF1B");
296501
+ println(`[design] \u7B49\u753B\u677F\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\uFF1A${runs}\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF0C\u4E0A\u9650 ${timeoutMin} \u5206\u949F\uFF09`);
296502
+ }
296503
+ });
296504
+ st = waited.status;
296505
+ waitedForRun = waited.waited;
296506
+ if (!waited.settled) {
296507
+ const runs = waited.live.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}`).join("\u3001") || "\u5BF9\u8BDD\u8BB0\u5F55\u8BFB\u4E0D\u5230";
296508
+ println(`[design] \u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF0C\u753B\u677F\u4E0A\u8FD9\u4E00\u8F6E\u8FD8\u6CA1\u7ED3\u675F\uFF08${runs}\uFF09\u2014\u2014\u5F15\u64CE\u8FD8\u5728\u8DD1\uFF0C\u53EF\u7A0D\u540E\u518D oasis design status ${artifactId} --wait${flags.get("pull") === "true" ? " --pull" : ""}\u3002\u522B\u53E6\u8D77\u65B0\u8F6E\u3002`);
296509
+ process.exitCode = 2;
296510
+ break;
296511
+ }
296512
+ if (waitedForRun) println(`[design] \u753B\u677F\u4E0A\u7684\u8F6E\u6B21\u90FD\u5DF2\u7ED3\u675F\uFF08\u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF09\u3002`);
296513
+ } else {
296514
+ st = await fetchBoardStatus(odBase, odFetch, artifactId);
296515
+ }
296262
296516
  if (!st.boardExists) {
296263
- println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async`);
296517
+ println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`);
296264
296518
  break;
296265
296519
  }
296266
- const live = st.runs.filter((r) => r.status === "running" || r.status === "queued");
296520
+ const live = liveBoardRuns(st);
296267
296521
  if (live.length > 0) {
296268
- for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1Arun ${r.runId.slice(0, 8)} \u72B6\u6001=${r.status}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u8FC7\u51E0\u5206\u949F\u518D status\uFF0C\u6216\u672C\u8F6E\u6536\u5DE5\u7B49\u91CD\u6D3E\u3002`);
296522
+ for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1A${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u7528 oasis design status ${artifactId} --wait --pull \u963B\u585E\u7B49\u5B83\u8DD1\u5B8C\u5E76\u62C9\u8D27\u3002`);
296269
296523
  } else if (st.runs.length > 0) {
296270
296524
  for (const r of st.runs.slice(0, 3)) {
296271
296525
  println(`[design] \u8FD1\u671F run\uFF1A${r.runId.slice(0, 8)} \u7EC8\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`);
296272
- if (r.status === "failed" && r.error) {
296273
- const reason = r.error.length > 300 ? `${r.error.slice(0, 300)}\u2026` : r.error;
296274
- println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${reason}`);
296275
- }
296526
+ if (r.status === "failed" && r.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(r.error)}`);
296527
+ }
296528
+ } else if ((st.turns ?? []).length > 0) {
296529
+ for (const t of (st.turns ?? []).slice(0, 3)) {
296530
+ println(`[design] \u8FD1\u671F run\uFF08\u5BF9\u8BDD\u8BB0\u5F55\uFF09\uFF1A${(t.runId ?? t.messageId ?? "?").slice(0, 8)} \u7EC8\u6001=${t.status}${t.agentId ? ` \u5F15\u64CE=${t.agentId}` : ""}`);
296531
+ if (t.status !== "succeeded" && t.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(t.error)}`);
296276
296532
  }
296277
296533
  } else {
296278
296534
  println(`[design] \u65E0\u5728\u4E16 run \u8BB0\u5F55\uFF08od \u91CD\u542F\u540E\u4E3A\u7A7A\uFF0C\u5C5E\u6B63\u5E38\uFF09\u2014\u2014\u770B\u4E0B\u9762\u677F\u4E0A\u4EA7\u7269\u5373\u6301\u4E45\u771F\u76F8\u3002`);
296279
296535
  }
296536
+ const last = latestEndedRun(st);
296537
+ const lastFailed = waitedForRun && last !== void 0 && last.status !== "succeeded";
296538
+ if (lastFailed) {
296539
+ println(`[design] \u521A\u7ED3\u675F\u7684\u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u6210\uFF08\u7EC8\u6001=${last.status}${last.agentId ? ` \u5F15\u64CE=${last.agentId}` : ""}\uFF09${last.error ? `\uFF1A${clipReason(last.error)}` : "\uFF0Cod \u6CA1\u8BB0\u539F\u56E0"}\u3002`);
296540
+ println(`[design] \u50CF\u989D\u5EA6/\u9650\u6D41/\u5F15\u64CE\u6545\u969C \u2192 \u6362\u5F15\u64CE\u91CD\u4E0B\uFF1Aoasis design chat ${artifactId} --turn continue --agent <\u53E6\u4E00\u4E2A\u5F15\u64CE> --message "<\u2026>" --async\uFF1B\u50CF\u9700\u6C42/\u6570\u636E\u4E0D\u6E05 \u2192 \u8865\u6E05\u695A\u540E\u91CD\u4E0B\uFF1B\u4E0B\u5B8C\u7167\u65E7 oasis design status ${artifactId} --wait --pull\u3002`);
296541
+ }
296280
296542
  if (st.artifacts.length === 0) {
296281
- println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? "\u7B49\u5F15\u64CE\u8DD1\u5B8C\u518D\u6765\u3002" : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async`}`);
296543
+ println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? `\u7B49\u5F15\u64CE\u8DD1\u5B8C\uFF1Aoasis design status ${artifactId} --wait --pull` : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`}`);
296282
296544
  break;
296283
296545
  }
296284
296546
  println(`[design] \u677F\u4E0A\u4EA4\u4ED8\u7269\uFF08\u6301\u4E45\uFF0C\u65B0\u2192\u65E7\uFF09\uFF1A`);
296285
296547
  for (const a of st.artifacts) println(` - ${a.path}\uFF08${a.kind ?? "?"}\uFF09status=${a.status ?? "?"}${a.mtime ? ` \xB7 ${new Date(a.mtime).toISOString()}` : ""}`);
296286
- if (flags.get("pull") === "true") {
296548
+ if (flags.get("pull") === "true" && !lastFailed) {
296287
296549
  const odPublic = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
296288
- const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, latestSucceededRun(st.runs)?.runId ?? "status-pull");
296550
+ const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, snapshotSourceRunId(st) ?? "status-pull");
296289
296551
  println(`[design] \u5DF2\u62C9\u5230\u672C\u5730\uFF1A${dir2}\uFF08\u5165\u53E3 ${entry}\uFF09`);
296290
296552
  println(`[design] \u8BFB\u5B8C\u6EE1\u610F\u5373\u4EA4\u7A3F\uFF1Aoasis propose ${artifactId} --from-dir ${dir2} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"`);
296291
- } else {
296553
+ } else if (!lastFailed) {
296292
296554
  println(`[design] \u53D6\u8D27\uFF1Aoasis design status ${artifactId} --pull\uFF08\u62C9\u5230\u672C\u5730\u76EE\u5F55\u8BFB\uFF0C\u8BFB\u5B8C propose --from-dir \u4EA4\u7A3F\uFF09`);
296293
296555
  }
296294
296556
  break;
296295
296557
  }
296296
- throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull]");
296558
+ throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]");
296297
296559
  }
296298
296560
  case "merge": {
296299
296561
  const artifactId = needPos(positional, 0, "oasis merge <artifactId> [revisionId]");
@@ -298390,7 +298652,7 @@ function shimScript() {
298390
298652
  }
298391
298653
 
298392
298654
  // src/index.ts
298393
- var PKG_VERSION = true ? "2.2.84" : "dev";
298655
+ var PKG_VERSION = true ? "2.2.85" : "dev";
298394
298656
  var LOCAL_BIN = localBin();
298395
298657
  var NPM_PREFIX = npmPrefix();
298396
298658
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.84",
3
+ "version": "2.2.85",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "aa2e3257b82571a32312b457698fdbe5e223d970"
29
+ "sourceHead": "760b80b59c0caa8c7469a804b84360f5ecf3a71a"
30
30
  }
31
31
  }