oasis_test 0.1.104 → 0.1.105

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 +261 -75
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -4440,6 +4440,14 @@ function classify(actorId) {
4440
4440
  if (actorId.startsWith("actor:human:")) return "human";
4441
4441
  return "unknown";
4442
4442
  }
4443
+ function pickReminderStep(cadence2, elapsedMs) {
4444
+ let hit = null;
4445
+ for (let i = 0; i < cadence2.length; i++) {
4446
+ const s2 = cadence2[i];
4447
+ if (elapsedMs >= s2.atMs) hit = { step: s2, index: i };
4448
+ }
4449
+ return hit;
4450
+ }
4443
4451
  function mergeConfig(cfg) {
4444
4452
  return {
4445
4453
  version: cfg?.version ?? "code-default-v1",
@@ -16307,7 +16315,11 @@ function previewIntervention(model, plan, config2) {
16307
16315
  if (lifecycleOf(fork, a.id) !== "active") continue;
16308
16316
  for (const e of a.inputs) {
16309
16317
  if (lifecycleOf(fork, e.to) === "sealed:cancelled") {
16310
- violations.push(`\u4F9D\u8D56\u5E9F\u5F03\u8282\u70B9: ${a.id} \u2192 ${e.to}\uFF08cancelled \u4E0D\u53EF\u88AB\u4F9D\u8D56\uFF09`);
16318
+ const consumerWs = fork.artifacts.get(a.id)?.workspace;
16319
+ const cancelledWs = fork.artifacts.get(e.to)?.workspace;
16320
+ violations.push(
16321
+ `\u4F9D\u8D56\u5E9F\u5F03\u8282\u70B9: ${a.id}${consumerWs ? `\uFF08${consumerWs}\uFF09` : ""} \u2192 ${e.to}${cancelledWs ? `\uFF08${cancelledWs}\uFF09` : ""}\uFF08cancelled \u4E0D\u53EF\u88AB\u4F9D\u8D56\uFF09`
16322
+ );
16311
16323
  }
16312
16324
  }
16313
16325
  }
@@ -16581,26 +16593,26 @@ var init_kernel_bridge = __esm({
16581
16593
  convergenceUnified: this.model.convergenceUnified,
16582
16594
  disabledActors: this.model.disabledActors
16583
16595
  });
16584
- for (const [draftId, card] of this.draftCards) {
16585
- const anchorFromPlan = card.plan?.ops.find((o) => "artifactId" in o && o.artifactId) ?? card.plan?.ops.find((o) => "id" in o && o.id);
16586
- const anchorId = card.anchor ?? (anchorFromPlan ? anchorFromPlan.artifactId ?? anchorFromPlan.id : void 0) ?? "";
16587
- const cardWs = card.plan?.workspace ?? this.model.artifacts.get(anchorId)?.workspace ?? card.anchor ?? "";
16596
+ for (const [draftId, card2] of this.draftCards) {
16597
+ const anchorFromPlan = card2.plan?.ops.find((o) => "artifactId" in o && o.artifactId) ?? card2.plan?.ops.find((o) => "id" in o && o.id);
16598
+ const anchorId = card2.anchor ?? (anchorFromPlan ? anchorFromPlan.artifactId ?? anchorFromPlan.id : void 0) ?? "";
16599
+ const cardWs = card2.plan?.workspace ?? this.model.artifacts.get(anchorId)?.workspace ?? card2.anchor ?? "";
16588
16600
  fresh.pendingReviews.set(draftId, {
16589
16601
  draftId,
16590
- plan: card.plan ?? { workspace: cardWs, reason: "", ops: [] },
16591
- changeClass: card.changeClass ?? "B",
16592
- nonMonotonicOps: card.nonMonotonicOps ?? [],
16593
- proposedBy: card.proposedBy,
16594
- origin: card.origin ?? null,
16602
+ plan: card2.plan ?? { workspace: cardWs, reason: "", ops: [] },
16603
+ changeClass: card2.changeClass ?? "B",
16604
+ nonMonotonicOps: card2.nonMonotonicOps ?? [],
16605
+ proposedBy: card2.proposedBy,
16606
+ origin: card2.origin ?? null,
16595
16607
  workspace: cardWs,
16596
16608
  advisory: [],
16597
16609
  downstreamClosure: [],
16598
16610
  at: "",
16599
16611
  // ★ anchor 也兜底到 plan ops 的产物 id——inbox change-confirm 用 r.anchor 找「anchor owner 的
16600
16612
  // 人类上级」(leadOf(r.anchor));graph-edit 卡之前恒空、boss 收不到卡(collab-inbox-audit 实测)。
16601
- anchor: anchorId || card.anchor || "",
16602
- kind: card.kind,
16603
- ...card.command ? { command: { command: card.command, args: card.commandArgs ?? {}, effectLabel: card.effectLabel ?? "" } } : {}
16613
+ anchor: anchorId || card2.anchor || "",
16614
+ kind: card2.kind,
16615
+ ...card2.command ? { command: { command: card2.command, args: card2.commandArgs ?? {}, effectLabel: card2.effectLabel ?? "" } } : {}
16604
16616
  });
16605
16617
  }
16606
16618
  Object.assign(this.model, fresh);
@@ -16698,8 +16710,8 @@ var init_kernel_bridge = __esm({
16698
16710
  typeName: a.type
16699
16711
  };
16700
16712
  }
16701
- cancelCascadeEdges(_id) {
16702
- return [];
16713
+ cancelCascadeEdges(id) {
16714
+ return edgesTouching(this.model, id).map((e) => ({ from: e.artifactId, to: e.to }));
16703
16715
  }
16704
16716
  /* ═══════════════════════ 写方法 ═══════════════════════ */
16705
16717
  // ── 建节点 ──
@@ -17435,6 +17447,13 @@ var init_kernel_bridge = __esm({
17435
17447
  }
17436
17448
  async seal(args) {
17437
17449
  const wid = this.wo(args.artifactId);
17450
+ if (args.reason === "cancelled") {
17451
+ const preserve = args.preserveEdgesAmong;
17452
+ for (const e of edgesTouching(this.model, args.artifactId)) {
17453
+ if (preserve && preserve.has(e.artifactId) && preserve.has(e.to)) continue;
17454
+ await this.unlinkInput({ artifactId: e.artifactId, to: e.to, actor: args.actor });
17455
+ }
17456
+ }
17438
17457
  await this.commit({
17439
17458
  companyId: "",
17440
17459
  workorderId: wid,
@@ -17630,10 +17649,10 @@ var init_kernel_bridge = __esm({
17630
17649
  async proposeCommandAuthorization(args) {
17631
17650
  const originNorm = args.origin ?? null;
17632
17651
  const argsJson = JSON.stringify(args.args ?? {});
17633
- for (const [id, card] of this.draftCards) {
17634
- if (card.kind !== "command" || card.command !== args.command) continue;
17635
- if ((card.origin ?? null) !== originNorm) continue;
17636
- if (JSON.stringify(card.commandArgs ?? {}) !== argsJson) continue;
17652
+ for (const [id, card2] of this.draftCards) {
17653
+ if (card2.kind !== "command" || card2.command !== args.command) continue;
17654
+ if ((card2.origin ?? null) !== originNorm) continue;
17655
+ if (JSON.stringify(card2.commandArgs ?? {}) !== argsJson) continue;
17637
17656
  await this.refreshModel();
17638
17657
  return { draftId: id };
17639
17658
  }
@@ -17651,23 +17670,23 @@ var init_kernel_bridge = __esm({
17651
17670
  return { draftId };
17652
17671
  }
17653
17672
  async applyAuthorizedCommand(args) {
17654
- const card = this.draftCards.get(args.draftId);
17655
- if (!card || card.kind !== "command" || !card.command) {
17673
+ const card2 = this.draftCards.get(args.draftId);
17674
+ if (!card2 || card2.kind !== "command" || !card2.command) {
17656
17675
  throw new Error(`\u4E0D\u662F\u547D\u4EE4\u6388\u6743\u8BF7\u6C42\u6216\u5DF2\u5904\u7406\uFF1A${args.draftId}`);
17657
17676
  }
17658
- const artifactId = card.anchor;
17659
- if (card.command === "forceConclude") {
17677
+ const artifactId = card2.anchor;
17678
+ if (card2.command === "forceConclude") {
17660
17679
  await this.forceConclude({
17661
17680
  artifactId,
17662
17681
  actor: args.actor,
17663
- reason: typeof card.commandArgs?.["reason"] === "string" ? card.commandArgs["reason"] : "\u7BA1\u7406\u8005\u8BF7\u6C42\u3001\u4EBA\u5DF2\u6388\u6743"
17682
+ reason: typeof card2.commandArgs?.["reason"] === "string" ? card2.commandArgs["reason"] : "\u7BA1\u7406\u8005\u8BF7\u6C42\u3001\u4EBA\u5DF2\u6388\u6743"
17664
17683
  });
17665
- } else if (card.command === "resolveEscalation") {
17666
- const escalationId = typeof card.commandArgs?.["escalationId"] === "string" ? card.commandArgs["escalationId"] : void 0;
17684
+ } else if (card2.command === "resolveEscalation") {
17685
+ const escalationId = typeof card2.commandArgs?.["escalationId"] === "string" ? card2.commandArgs["escalationId"] : void 0;
17667
17686
  if (!escalationId) {
17668
17687
  throw new Error(`resolveEscalation \u6388\u6743\u5361\u7F3A escalationId\uFF1A${args.draftId}`);
17669
17688
  }
17670
- const reason = typeof card.commandArgs?.["reason"] === "string" ? card.commandArgs["reason"] : void 0;
17689
+ const reason = typeof card2.commandArgs?.["reason"] === "string" ? card2.commandArgs["reason"] : void 0;
17671
17690
  await this.resolveEscalation({
17672
17691
  artifactId,
17673
17692
  actor: args.actor,
@@ -17675,23 +17694,39 @@ var init_kernel_bridge = __esm({
17675
17694
  ...reason !== void 0 ? { reason } : {}
17676
17695
  });
17677
17696
  } else {
17678
- throw new Error(`\u547D\u4EE4\u6388\u6743\uFF1A\u6682\u4E0D\u652F\u6301\u6267\u884C ${card.command}`);
17697
+ throw new Error(`\u547D\u4EE4\u6388\u6743\uFF1A\u6682\u4E0D\u652F\u6301\u6267\u884C ${card2.command}`);
17679
17698
  }
17680
17699
  this.draftCards.delete(args.draftId);
17681
17700
  await this.refreshModel();
17682
- return { command: card.command, artifactId };
17701
+ return { command: card2.command, artifactId };
17683
17702
  }
17684
17703
  async rejectIntervention(args) {
17685
17704
  this.draftCards.delete(args.draftId);
17686
17705
  await this.refreshModel();
17687
17706
  }
17707
+ /**
17708
+ * 有效 workspace:优先 plan.workspace,缺省时从首个带 workspace 的 spawn op 兜底(与
17709
+ * applyIntervention 里 wid 的兜底同源,见下方 line ~1453)。
17710
+ * ★ 诊断 9f589b81 §② B:建单方案(buildDynamicWorkorderPlan)曾漏传顶层 plan.workspace,导致
17711
+ * preview 的 workspace 过滤短路、拿全公司读模型跑终态校验——别家工单里一条僵尸边就能拒掉无关建单
17712
+ * (事故半径=全组织)。planner 已补 plan.workspace(根治),此兜底再保一层:只要 plan 里有带
17713
+ * workspace 的 spawn op,就绝不静默 fallback 到全公司。两者皆缺才回退全模型(如纯 seal/unlink 单节点
17714
+ * 改图,此时 wo() 反查也能定位,全局校验只是更宽、不误伤建单)。
17715
+ */
17716
+ effectiveWorkspace(plan) {
17717
+ if (plan.workspace) return plan.workspace;
17718
+ const spawn8 = plan.ops.find(
17719
+ (o) => o.action === "spawn" && o.workspace
17720
+ );
17721
+ return spawn8?.workspace;
17722
+ }
17688
17723
  async previewIntervention(plan) {
17689
- const ws = plan.workspace;
17724
+ const ws = this.effectiveWorkspace(plan);
17690
17725
  const scopedModel = ws ? filterReadModelByWorkspace(this.model, ws) : this.model;
17691
17726
  return previewIntervention(scopedModel, plan, { schema: this.schema, roleIndex: this.roleIndex });
17692
17727
  }
17693
17728
  async applyIntervention(plan, _baseSeqs, _actor) {
17694
- const ws = plan.workspace;
17729
+ const ws = this.effectiveWorkspace(plan);
17695
17730
  const scopedModel = ws ? filterReadModelByWorkspace(this.model, ws) : this.model;
17696
17731
  plan = resolveInterventionRefs(scopedModel, plan, this.schema);
17697
17732
  const preview2 = await this.previewIntervention(plan);
@@ -18237,9 +18272,9 @@ var init_crypto = __esm({
18237
18272
  function buildTextCard(text, opts) {
18238
18273
  const elements = [{ tag: "div", text: { tag: "lark_md", content: text || " " } }];
18239
18274
  if (opts?.footer) elements.push({ tag: "note", elements: [{ tag: "lark_md", content: opts.footer }] });
18240
- const card = { config: { wide_screen_mode: true, update_multi: true }, elements };
18241
- if (opts?.header) card["header"] = { title: { tag: "plain_text", content: opts.header } };
18242
- return card;
18275
+ const card2 = { config: { wide_screen_mode: true, update_multi: true }, elements };
18276
+ if (opts?.header) card2["header"] = { title: { tag: "plain_text", content: opts.header } };
18277
+ return card2;
18243
18278
  }
18244
18279
  var BASE, FeishuApiClient;
18245
18280
  var init_api = __esm({
@@ -18318,26 +18353,26 @@ var init_api = __esm({
18318
18353
  };
18319
18354
  }
18320
18355
  /** 发一张 interactive 卡片,返回 message_id(后续 patch 同一条消息实现流式更新)。 */
18321
- async sendCard(chatId, card) {
18356
+ async sendCard(chatId, card2) {
18322
18357
  const token = await this.tenantAccessToken();
18323
18358
  const res = await this.fetchImpl(`${this.base}/open-apis/im/v1/messages?receive_id_type=chat_id`, {
18324
18359
  method: "POST",
18325
18360
  headers: { "content-type": "application/json; charset=utf-8", authorization: `Bearer ${token}` },
18326
- body: JSON.stringify({ receive_id: chatId, msg_type: "interactive", content: JSON.stringify(card) })
18361
+ body: JSON.stringify({ receive_id: chatId, msg_type: "interactive", content: JSON.stringify(card2) })
18327
18362
  });
18328
18363
  const body = await res.json();
18329
18364
  if (body.code !== 0) throw new Error(`feishu send card failed: code=${body.code} msg=${body.msg ?? ""}`);
18330
18365
  return { messageId: body.data?.message_id ?? "" };
18331
18366
  }
18332
18367
  /** 就地更新一张已发出的 interactive 卡片(流式回复靠反复 patch 同一条消息)。 */
18333
- async patchCard(messageId, card) {
18368
+ async patchCard(messageId, card2) {
18334
18369
  const token = await this.tenantAccessToken();
18335
18370
  const res = await this.fetchImpl(
18336
18371
  `${this.base}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
18337
18372
  {
18338
18373
  method: "PATCH",
18339
18374
  headers: { "content-type": "application/json; charset=utf-8", authorization: `Bearer ${token}` },
18340
- body: JSON.stringify({ content: JSON.stringify(card) })
18375
+ body: JSON.stringify({ content: JSON.stringify(card2) })
18341
18376
  }
18342
18377
  );
18343
18378
  const body = await res.json();
@@ -18780,11 +18815,11 @@ var init_service = __esm({
18780
18815
  this.dirty = false;
18781
18816
  const snapshot = this.buf;
18782
18817
  try {
18783
- const card = buildTextCard(snapshot, { footer: "\u25CF \u751F\u6210\u4E2D\u2026" });
18818
+ const card2 = buildTextCard(snapshot, { footer: "\u25CF \u751F\u6210\u4E2D\u2026" });
18784
18819
  if (!this.messageId) {
18785
- this.messageId = (await this.client.sendCard(this.chatId, card)).messageId;
18820
+ this.messageId = (await this.client.sendCard(this.chatId, card2)).messageId;
18786
18821
  } else {
18787
- await this.client.patchCard(this.messageId, card);
18822
+ await this.client.patchCard(this.messageId, card2);
18788
18823
  }
18789
18824
  } catch (e) {
18790
18825
  this.log(`feishu card stream tick failed: ${errMsg(e)}`);
@@ -18798,10 +18833,10 @@ var init_service = __esm({
18798
18833
  clearInterval(this.timer);
18799
18834
  this.timer = null;
18800
18835
  }
18801
- const card = buildTextCard(this.buf || "\uFF08\u65E0\u8F93\u51FA\uFF09");
18836
+ const card2 = buildTextCard(this.buf || "\uFF08\u65E0\u8F93\u51FA\uFF09");
18802
18837
  try {
18803
- if (!this.messageId) await this.client.sendCard(this.chatId, card);
18804
- else await this.client.patchCard(this.messageId, card);
18838
+ if (!this.messageId) await this.client.sendCard(this.chatId, card2);
18839
+ else await this.client.patchCard(this.messageId, card2);
18805
18840
  } catch (e) {
18806
18841
  this.log(`feishu card finalize failed: ${errMsg(e)}`);
18807
18842
  }
@@ -18812,10 +18847,10 @@ var init_service = __esm({
18812
18847
  clearInterval(this.timer);
18813
18848
  this.timer = null;
18814
18849
  }
18815
- const card = buildTextCard(`${this.buf ? this.buf + "\n\n" : ""}\u26A0\uFE0F ${message}`);
18850
+ const card2 = buildTextCard(`${this.buf ? this.buf + "\n\n" : ""}\u26A0\uFE0F ${message}`);
18816
18851
  try {
18817
- if (!this.messageId) await this.client.sendCard(this.chatId, card);
18818
- else await this.client.patchCard(this.messageId, card);
18852
+ if (!this.messageId) await this.client.sendCard(this.chatId, card2);
18853
+ else await this.client.patchCard(this.messageId, card2);
18819
18854
  } catch (e) {
18820
18855
  this.log(`feishu card error-finalize failed: ${errMsg(e)}`);
18821
18856
  }
@@ -127601,14 +127636,14 @@ var require_lib4 = __commonJS({
127601
127636
  });
127602
127637
  }
127603
127638
  };
127604
- function appendErrorFooter(card) {
127605
- const cardObj = card;
127639
+ function appendErrorFooter(card2) {
127640
+ const cardObj = card2;
127606
127641
  const elements = Array.isArray(cardObj === null || cardObj === void 0 ? void 0 : cardObj.elements) ? [...cardObj.elements] : [];
127607
127642
  elements.push({
127608
127643
  tag: "note",
127609
127644
  elements: [{ tag: "plain_text", content: "\u26A0\uFE0F \u751F\u6210\u4E2D\u65AD" }]
127610
127645
  });
127611
- return Object.assign(Object.assign({}, card), { elements });
127646
+ return Object.assign(Object.assign({}, card2), { elements });
127612
127647
  }
127613
127648
  var CardStreamController = class {
127614
127649
  constructor(sender, to, idType, opts, initial) {
@@ -128377,13 +128412,13 @@ ${block}
128377
128412
  });
128378
128413
  }
128379
128414
  // ─── card ─────────────────────────────────────────────
128380
- sendCard(to, idType, card, opts) {
128415
+ sendCard(to, idType, card2, opts) {
128381
128416
  return __awaiter(this, void 0, void 0, function* () {
128382
128417
  const id = yield this.sendOneWithFallback({
128383
128418
  to,
128384
128419
  idType,
128385
128420
  msgType: "interactive",
128386
- content: card,
128421
+ content: card2,
128387
128422
  replyTo: opts.replyTo,
128388
128423
  replyInThread: opts.replyInThread
128389
128424
  });
@@ -128495,11 +128530,11 @@ ${block}
128495
128530
  * and the public `channel.updateCard()` low-level API. Not suitable for
128496
128531
  * text streaming (use native cardkit streaming below for that).
128497
128532
  */
128498
- patchCard(messageId, card) {
128533
+ patchCard(messageId, card2) {
128499
128534
  return __awaiter(this, void 0, void 0, function* () {
128500
128535
  yield this.client.im.v1.message.patch({
128501
128536
  path: { message_id: messageId },
128502
- data: { content: JSON.stringify(card) }
128537
+ data: { content: JSON.stringify(card2) }
128503
128538
  });
128504
128539
  });
128505
128540
  }
@@ -130050,9 +130085,9 @@ ${lines.join("\n")}
130050
130085
  });
130051
130086
  }
130052
130087
  // ─── low-level ─────────────────────────────────────────
130053
- updateCard(messageId, card) {
130088
+ updateCard(messageId, card2) {
130054
130089
  return __awaiter(this, void 0, void 0, function* () {
130055
- yield this.sender.patchCard(messageId, card);
130090
+ yield this.sender.patchCard(messageId, card2);
130056
130091
  });
130057
130092
  }
130058
130093
  /**
@@ -149291,7 +149326,7 @@ async function buildDynamicWorkorderPlan(input) {
149291
149326
  }
149292
149327
  }
149293
149328
  const plan = withClosureReport(
149294
- { reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`, ops },
149329
+ { reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`, ops, workspace: input.workspace },
149295
149330
  { draftedBy: input.actor }
149296
149331
  );
149297
149332
  const closureOp = plan.ops.find(
@@ -179739,6 +179774,126 @@ var init_inbox_chat = __esm({
179739
179774
  }
179740
179775
  });
179741
179776
 
179777
+ // ../server/src/domains/collab/waiting-inbox.ts
179778
+ function card(kind, row, severity, since, summary) {
179779
+ return {
179780
+ kind,
179781
+ ...row.artifactId ? { artifactId: row.artifactId } : {},
179782
+ workspace: row.workorderId,
179783
+ ...row.title ? { title: row.title } : {},
179784
+ since,
179785
+ actionRequired: true,
179786
+ summary,
179787
+ // info 档只提示不算 overdue;warn/urgent 置 overdue(前端红点/排序)。
179788
+ ...severity === "info" ? {} : { overdue: true }
179789
+ };
179790
+ }
179791
+ function buildWaitingInbox(reviews, works, policy, nowIso, me) {
179792
+ const now = Date.parse(nowIso);
179793
+ const out = [];
179794
+ const consider = (rows, scenario, kind) => {
179795
+ for (const r of rows) {
179796
+ if (r.actorId !== me) continue;
179797
+ const start = r.startedAt || r.createdAt;
179798
+ if (r.status === "dead") {
179799
+ out.push(card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`));
179800
+ continue;
179801
+ }
179802
+ if (r.status !== "running") continue;
179803
+ if (classify(me) !== "human") continue;
179804
+ const action = policy.resolve({ executor: "human", scenario }).timeoutAction;
179805
+ if (action.kind !== "remind") continue;
179806
+ const elapsed = Number.isFinite(now) ? now - Date.parse(start) : NaN;
179807
+ if (!Number.isFinite(elapsed)) continue;
179808
+ const picked = pickReminderStep(action.cadence, elapsed);
179809
+ if (!picked) continue;
179810
+ const mins = Math.round(elapsed / 6e4);
179811
+ out.push(card(kind, r, picked.step.severity, start, `\u6709\u4E00\u9879${KIND_LABEL[kind]}\u5728\u7B49\u4F60\uFF0C\u5DF2 ${mins} \u5206\u949F\u672A\u52A8`));
179812
+ }
179813
+ };
179814
+ consider(reviews, "review", "review-waiting");
179815
+ consider(works, "work", "work-waiting");
179816
+ return out;
179817
+ }
179818
+ function waitingWorkspacesFor(model, me) {
179819
+ const out = /* @__PURE__ */ new Set();
179820
+ for (const a of model.artifacts.values()) {
179821
+ if (lifecycleOf(model, a.id) !== "active") continue;
179822
+ if (a.owner === me) {
179823
+ out.add(a.workspace);
179824
+ continue;
179825
+ }
179826
+ if (pendingConcludeAttempt(model, a.id)?.gate?.eligible.includes(me)) out.add(a.workspace);
179827
+ }
179828
+ return out;
179829
+ }
179830
+ function collectWaitingRows(snapshots2, me) {
179831
+ const reviews = [];
179832
+ const works = [];
179833
+ const latestBy = (map, row) => {
179834
+ const prev = map.get(row.nodeId);
179835
+ if (!prev || row.createdAt > prev.createdAt) map.set(row.nodeId, row);
179836
+ };
179837
+ for (const snap of snapshots2) {
179838
+ const nodeTitle = new Map(snap.nodes.map((n) => [n.id, n.title]));
179839
+ const cancelled = new Set(snap.nodes.filter((n) => n.cancelledAt).map((n) => n.id));
179840
+ const outputCountOf = (workId) => snap.artifacts.reduce((n, a) => a.workId === workId ? n + 1 : n, 0);
179841
+ const latestReview = /* @__PURE__ */ new Map();
179842
+ for (const r of snap.reviews) {
179843
+ if (r.reviewerActorId !== me || cancelled.has(r.nodeId)) continue;
179844
+ latestBy(latestReview, r);
179845
+ }
179846
+ for (const r of latestReview.values()) {
179847
+ const st = reviewState(r);
179848
+ if (st !== "running" && st !== "dead") continue;
179849
+ reviews.push({
179850
+ id: r.id,
179851
+ workorderId: snap.workorder.id,
179852
+ artifactId: r.nodeId,
179853
+ actorId: r.reviewerActorId,
179854
+ ...nodeTitle.get(r.nodeId) ? { title: nodeTitle.get(r.nodeId) } : {},
179855
+ createdAt: r.createdAt,
179856
+ startedAt: r.startedAt,
179857
+ status: st
179858
+ });
179859
+ }
179860
+ const latestMain = /* @__PURE__ */ new Map();
179861
+ const latestReply = /* @__PURE__ */ new Map();
179862
+ for (const w2 of snap.works) {
179863
+ if (w2.assigneeActorId !== me || cancelled.has(w2.nodeId)) continue;
179864
+ latestBy(w2.replyToIssueId ? latestReply : latestMain, w2);
179865
+ }
179866
+ for (const w2 of [...latestMain.values(), ...latestReply.values()]) {
179867
+ const st = workState(w2, hasOutput(w2, outputCountOf(w2.id)));
179868
+ if (st !== "running" && st !== "dead") continue;
179869
+ works.push({
179870
+ id: w2.id,
179871
+ workorderId: snap.workorder.id,
179872
+ artifactId: w2.nodeId,
179873
+ actorId: w2.assigneeActorId,
179874
+ ...nodeTitle.get(w2.nodeId) ? { title: nodeTitle.get(w2.nodeId) } : {},
179875
+ createdAt: w2.createdAt,
179876
+ startedAt: w2.startedAt,
179877
+ status: st
179878
+ });
179879
+ }
179880
+ }
179881
+ return { reviews, works };
179882
+ }
179883
+ var KIND_LABEL;
179884
+ var init_waiting_inbox = __esm({
179885
+ "../server/src/domains/collab/waiting-inbox.ts"() {
179886
+ "use strict";
179887
+ init_src4();
179888
+ init_src3();
179889
+ init_src2();
179890
+ KIND_LABEL = {
179891
+ "review-waiting": "\u8BC4\u5BA1",
179892
+ "work-waiting": "\u5904\u7406"
179893
+ };
179894
+ }
179895
+ });
179896
+
179742
179897
  // ../server/src/domains/collab/audit.ts
179743
179898
  function toAuditEntry(op) {
179744
179899
  return {
@@ -179747,7 +179902,7 @@ function toAuditEntry(op) {
179747
179902
  actor: op.actor,
179748
179903
  op: op.kind,
179749
179904
  artifactId: op.artifactId,
179750
- summary: `${KIND_LABEL[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
179905
+ summary: `${KIND_LABEL2[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
179751
179906
  };
179752
179907
  }
179753
179908
  function eventToAuditEntry(rec) {
@@ -179759,7 +179914,7 @@ function eventToAuditEntry(rec) {
179759
179914
  actor: rec.actorId,
179760
179915
  op: ev.kind,
179761
179916
  artifactId,
179762
- summary: `${KIND_LABEL[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
179917
+ summary: `${KIND_LABEL2[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
179763
179918
  };
179764
179919
  }
179765
179920
  function enrichAudit(entry, model, titleOf) {
@@ -179835,12 +179990,12 @@ async function buildAudit(source, q = {}, model) {
179835
179990
  }
179836
179991
  return { items, cursor: truncated ? lastCursor : null };
179837
179992
  }
179838
- var KIND_LABEL;
179993
+ var KIND_LABEL2;
179839
179994
  var init_audit = __esm({
179840
179995
  "../server/src/domains/collab/audit.ts"() {
179841
179996
  "use strict";
179842
179997
  init_node_state();
179843
- KIND_LABEL = {
179998
+ KIND_LABEL2 = {
179844
179999
  // 新引擎事件 kind
179845
180000
  "plan.update_spec": "\u6539\u4EFB\u52A1",
179846
180001
  "plan.add_node": "\u52A0\u8282\u70B9",
@@ -180947,7 +181102,7 @@ function collabDomain(opts) {
180947
181102
  return { status: 200, body: { dispatched: !already } };
180948
181103
  });
180949
181104
  router.get("/api/inbox", async (req) => {
180950
- const { kernel, registry: registry2 } = await resolveCtx(req.auth.companyId);
181105
+ const { kernel, registry: registry2, engineStore } = await resolveCtx(req.auth.companyId);
180951
181106
  const me = req.auth.actor;
180952
181107
  const [leadOf, resolveActor, markers] = await Promise.all([
180953
181108
  buildLeadResolver(registry2),
@@ -180960,7 +181115,18 @@ function collabDomain(opts) {
180960
181115
  const inboxSlaMs = opts.sla?.humanMs ?? 24 * 36e5;
180961
181116
  const kernelItems = buildInbox(kernel.model, me, leadOf, resolveActor, markers, inboxSlaMs, (/* @__PURE__ */ new Date()).toISOString());
180962
181117
  const chatItems = opts.chatSession && opts.readMarkers ? await buildChatInbox(opts.chatSession, markers, me, resolveActor) : [];
180963
- return { status: 200, body: { items: mergeInbox(kernelItems, chatItems) } };
181118
+ let waitingItems = [];
181119
+ if (engineStore) {
181120
+ const snaps = (await Promise.all(
181121
+ [...waitingWorkspacesFor(kernel.model, me)].map(
181122
+ (wid) => engineStore.transaction((tx) => tx.loadWorkorder(wid))
181123
+ )
181124
+ )).filter((s2) => s2 !== null);
181125
+ const { reviews, works } = collectWaitingRows(snaps, me);
181126
+ const policy = opts.policy?.() ?? CODE_DEFAULT_POLICY;
181127
+ waitingItems = buildWaitingInbox(reviews, works, policy, (/* @__PURE__ */ new Date()).toISOString(), me);
181128
+ }
181129
+ return { status: 200, body: { items: mergeInbox(kernelItems, [...chatItems, ...waitingItems]) } };
180964
181130
  });
180965
181131
  const companyOf = (req) => req.auth.companyId ?? "company:default";
180966
181132
  const noTagStore = { status: 501, body: { error: { code: "not_implemented", message: "\u672C\u90E8\u7F72\u672A\u542F\u7528\u5DE5\u5355\u6807\u7B7E" } } };
@@ -181200,6 +181366,8 @@ var init_collab = __esm({
181200
181366
  init_workorder_detail();
181201
181367
  init_inbox();
181202
181368
  init_inbox_chat();
181369
+ init_waiting_inbox();
181370
+ init_src2();
181203
181371
  init_audit();
181204
181372
  init_dashboard();
181205
181373
  init_workorder_metrics();
@@ -194540,6 +194708,8 @@ async function startServe(opts) {
194540
194708
  // 工单标签词表(组织级受控、旁账,与 playbook-overrides 同款侧存)。
194541
194709
  tags: workorderTagStore,
194542
194710
  ...opts.sla ? { sla: opts.sla } : {},
194711
+ // ADR 0147:waiting 投影的 human 阶梯从策略中心取;getter 保证随 SIGHUP/reload 热载而变。
194712
+ policy: () => activeTimeoutPolicy,
194543
194713
  resolveEngine: (cid) => getEngine(cid ?? defaultCompanyId)
194544
194714
  }),
194545
194715
  trace.register,
@@ -201889,7 +202059,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
201889
202059
  }
201890
202060
 
201891
202061
  // src/index.ts
201892
- var PKG_VERSION = true ? "0.1.104" : "dev";
202062
+ var PKG_VERSION = true ? "0.1.105" : "dev";
201893
202063
  var OASIS_DIR = path26.join(os10.homedir(), ".oasis");
201894
202064
  var CONFIG_FILE = path26.join(OASIS_DIR, "node-config.json");
201895
202065
  var PID_FILE = path26.join(OASIS_DIR, "node.pid");
@@ -201992,8 +202162,10 @@ function setupAutostart() {
201992
202162
  ].join("\n"));
201993
202163
  try {
201994
202164
  (0, import_node_child_process17.execSync)("systemctl --user daemon-reload && systemctl --user enable --now oasis-node", { stdio: "ignore" });
202165
+ return true;
201995
202166
  } catch {
201996
202167
  }
202168
+ return false;
201997
202169
  } else if (process.platform === "darwin") {
201998
202170
  const plist = path26.join(os10.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
201999
202171
  fs33.mkdirSync(path26.dirname(plist), { recursive: true });
@@ -202009,9 +202181,12 @@ function setupAutostart() {
202009
202181
  </dict></plist>`);
202010
202182
  try {
202011
202183
  (0, import_node_child_process17.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
202184
+ return true;
202012
202185
  } catch {
202013
202186
  }
202187
+ return false;
202014
202188
  }
202189
+ return false;
202015
202190
  }
202016
202191
  function spawnDaemon() {
202017
202192
  const log3 = fs33.openSync(LOG_FILE, "a");
@@ -202020,9 +202195,7 @@ function spawnDaemon() {
202020
202195
  stdio: ["ignore", log3, log3]
202021
202196
  });
202022
202197
  child.unref();
202023
- const pid = child.pid ?? 0;
202024
- fs33.writeFileSync(PID_FILE, String(pid));
202025
- return pid;
202198
+ return child.pid ?? 0;
202026
202199
  }
202027
202200
  function stopDaemon() {
202028
202201
  const pid = readPid();
@@ -202094,12 +202267,21 @@ void (async () => {
202094
202267
  console.error("[oasis] no config \u2014 run `oasis daemon start` first");
202095
202268
  process.exit(1);
202096
202269
  }
202097
- const alivePid = readPid();
202098
- if (alivePid && alivePid !== process.pid && isRunning2(alivePid)) {
202099
- console.error(`[oasis] daemon already running (PID ${alivePid}) \u2014 exiting duplicate`);
202100
- process.exit(0);
202270
+ try {
202271
+ const fd = fs33.openSync(PID_FILE, "wx");
202272
+ fs33.writeSync(fd, String(process.pid));
202273
+ fs33.closeSync(fd);
202274
+ } catch {
202275
+ const alivePid = readPid();
202276
+ if (alivePid && alivePid !== process.pid && isRunning2(alivePid)) {
202277
+ console.error(`[oasis] daemon already running (PID ${alivePid}) \u2014 exiting duplicate`);
202278
+ process.exit(0);
202279
+ }
202280
+ try {
202281
+ fs33.writeFileSync(PID_FILE, String(process.pid));
202282
+ } catch {
202283
+ }
202101
202284
  }
202102
- fs33.writeFileSync(PID_FILE, String(process.pid));
202103
202285
  const cleanup = () => {
202104
202286
  try {
202105
202287
  fs33.unlinkSync(PID_FILE);
@@ -202193,9 +202375,13 @@ void (async () => {
202193
202375
  saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {}, reportRuntimesOnNextStart: true });
202194
202376
  installBinary();
202195
202377
  stopDaemon();
202196
- setupAutostart();
202197
- const pid = spawnDaemon();
202198
- console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
202378
+ const tookOver = setupAutostart();
202379
+ if (!tookOver) {
202380
+ const pid = spawnDaemon();
202381
+ console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
202382
+ } else {
202383
+ console.log(`\u2713 daemon started via systemd (oasis-node.service)${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
202384
+ }
202199
202385
  console.log(` logs: tail -f ${LOG_FILE}`);
202200
202386
  if (!process.env["PATH"]?.includes(path26.dirname(LOCAL_BIN)))
202201
202387
  console.log(` add to PATH: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.104",
3
+ "version": "0.1.105",
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": "2b9f5db18e1ad2cf93a2e1c7c49aa47765d228c8"
29
+ "sourceHead": "60d88fd92311e481e7bbfe922d75db13a3483643"
30
30
  }
31
31
  }