oasis_test_v2 2.2.98 → 2.2.99

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 +163 -35
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -13674,12 +13674,13 @@ async function recordChatTurnInterruption(deps) {
13674
13674
  let version2 = await items.sessionVersionCursor(sessionId).catch(() => 0);
13675
13675
  const touched = [];
13676
13676
  const at2 = now();
13677
- for (const row of rows) {
13677
+ for (const row of deps.cause === "user-stop" ? [] : rows) {
13678
13678
  if (row.status !== "streaming") continue;
13679
13679
  const updated = await items.applyOp(row.id, { op: "set_status", status: "interrupted" }, { at: at2, version: ++version2, sessionId });
13680
13680
  if (updated) touched.push(updated);
13681
13681
  }
13682
- const payload2 = deps.cause === "sweep-orphan" ? {
13682
+ if (existing && isUserStopPayload(existing.payload)) return touched;
13683
+ const payload2 = deps.cause === "user-stop" ? { code: "error", note: CHAT_INTERRUPT_NOTICE["user-stop"], reason: "cancelled", closedBy: deps.reason, cancelledBy: "user" } : deps.cause === "sweep-orphan" ? {
13683
13684
  code: "error",
13684
13685
  note: deps.cancelledBy ? chatCancelNote(deps.cancelledBy) : CHAT_INTERRUPT_NOTICE[deps.cause],
13685
13686
  reason: "cancelled",
@@ -13713,6 +13714,9 @@ async function recordChatTurnInterruption(deps) {
13713
13714
  return [];
13714
13715
  }
13715
13716
  }
13717
+ function isUserStopPayload(payload2) {
13718
+ return !!payload2 && typeof payload2 === "object" && !Array.isArray(payload2) && payload2.cancelledBy === "user";
13719
+ }
13716
13720
  var chatInterruptItemId, CHAT_INTERRUPT_NOTICE;
13717
13721
  var init_chat_interrupt_notice = __esm({
13718
13722
  "../server/src/chat-interrupt-notice.ts"() {
@@ -13724,7 +13728,10 @@ var init_chat_interrupt_notice = __esm({
13724
13728
  CHAT_INTERRUPT_NOTICE = {
13725
13729
  "sweep-orphan": CHAT_CANCELLED_NOTE,
13726
13730
  "sweep-absolute-cap": "\u8FD9\u4E00\u8F6E\u8FD0\u884C\u65F6\u95F4\u8D85\u8FC7\u4E86\u4E0A\u9650\uFF0C\u5DF2\u88AB\u7CFB\u7EDF\u7EC8\u6B62\u3002\u4E0A\u9762\u662F\u7EC8\u6B62\u524D\u5DF2\u7ECF\u751F\u6210\u7684\u5185\u5BB9\uFF0C\u53EF\u4EE5\u628A\u4EFB\u52A1\u62C6\u5C0F\u4E00\u4E9B\u518D\u53D1\u4E00\u6B21\u3002",
13727
- "run-failed": "\u8FD9\u4E00\u8F6E\u6CA1\u6709\u6B63\u5E38\u7ED3\u675F\uFF08\u8FD0\u884C\u88AB\u4E2D\u65AD\uFF09\uFF0C\u6CA1\u80FD\u8DD1\u5B8C\u3002\u4E0A\u9762\u662F\u4E2D\u65AD\u524D\u5DF2\u7ECF\u751F\u6210\u7684\u5185\u5BB9\uFF0C\u4F60\u53EF\u4EE5\u518D\u53D1\u4E00\u6B21\u3002"
13731
+ "run-failed": "\u8FD9\u4E00\u8F6E\u6CA1\u6709\u6B63\u5E38\u7ED3\u675F\uFF08\u8FD0\u884C\u88AB\u4E2D\u65AD\uFF09\uFF0C\u6CA1\u80FD\u8DD1\u5B8C\u3002\u4E0A\u9762\u662F\u4E2D\u65AD\u524D\u5DF2\u7ECF\u751F\u6210\u7684\u5185\u5BB9\uFF0C\u4F60\u53EF\u4EE5\u518D\u53D1\u4E00\u6B21\u3002",
13732
+ // Bug 0228:serve 重启窗口里点停止,注册表里没有这一轮、只能在台账上收(`chat-turn-stop.ts`)。
13733
+ // 与在飞路径用户停止同一句、同一格归因;不借 `sweep-orphan` 那一支,免得日志里「用户停的」长成孤儿清扫。
13734
+ "user-stop": CHAT_CANCEL_NOTE.user
13728
13735
  };
13729
13736
  }
13730
13737
  });
@@ -131263,7 +131270,7 @@ async function renewInjectedCredentials(injected, fetchCredentials) {
131263
131270
  log("[node-connectors]", `\u7EED\u671F\uFF1A${slug6} \u6362\u5230\u65B0\u4EE4\u724C`);
131264
131271
  } else {
131265
131272
  outcome.unchanged.push(slug6);
131266
- log("[node-connectors]", `\u7EED\u671F\uFF1A${slug6} server \u8FD4\u56DE\u7684\u4ECD\u662F\u65E7\u4EE4\u724C\uFF08\u672A\u5230\u6362\u65B0\u95E8\u69DB\uFF09`);
131273
+ log("[node-connectors]", `\u7EED\u671F\uFF1A${slug6} server \u8FD4\u56DE\u7684\u4ECD\u662F\u65E7\u4EE4\u724C\uFF08\u539F\u56E0\u89C1 server \u65E5\u5FD7 [credential-refresh]\uFF09`);
131267
131274
  }
131268
131275
  } catch (err) {
131269
131276
  outcome.failed.push({ slug: slug6, error: String(err) });
@@ -176539,6 +176546,58 @@ var init_chat_workorder_files = __esm({
176539
176546
  }
176540
176547
  });
176541
176548
 
176549
+ // ../server/src/chat-turn-stop.ts
176550
+ async function stopLedgerTurn(deps) {
176551
+ const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
176552
+ const log3 = deps.log ?? (() => {
176553
+ });
176554
+ let turn = deps.turn ?? await deps.turns.activeTurn(deps.sessionId);
176555
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt += 1) {
176556
+ if (!turn || turn.status !== "reserved" && turn.status !== "running") return { stopped: false, reason: "no-live-turn" };
176557
+ if (deps.expectedRunId && turn.assistantRunId && turn.assistantRunId !== deps.expectedRunId) {
176558
+ return { stopped: false, reason: "turn-changed" };
176559
+ }
176560
+ const completedAt = now();
176561
+ const ok3 = await deps.turns.transitionTurn(
176562
+ turn.id,
176563
+ { status: turn.status, dispatchId: turn.dispatchId },
176564
+ { status: "cancelled", completedAt, lastError: `\u7528\u6237\u505C\u6B62\uFF08\u672C\u8FDB\u7A0B\u6CE8\u518C\u8868\u91CC\u6CA1\u6709\u8FD9\u4E00\u8F6E\uFF0C\u6309\u53F0\u8D26\u6536\uFF1B\u539F\u72B6\u6001 ${turn.status}\uFF09` }
176565
+ );
176566
+ if (ok3) {
176567
+ log3(`[chat-stop] \u4F1A\u8BDD ${deps.sessionId} \u8F6E ${turn.id} \u6309\u53F0\u8D26\u505C\u6B62\uFF08${turn.status} \u2192 cancelled\uFF09`);
176568
+ await recordUserStopNotice(deps, turn, completedAt);
176569
+ return { stopped: true, ledger: true };
176570
+ }
176571
+ turn = await deps.turns.getTurn(turn.id);
176572
+ }
176573
+ return { stopped: false, reason: "turn-changed" };
176574
+ }
176575
+ async function recordUserStopNotice(deps, turn, completedAt) {
176576
+ if (!deps.items) return;
176577
+ const messageId = turn.assistantRunId && deps.findAssistantMessageId ? await deps.findAssistantMessageId(turn.assistantRunId).catch(() => void 0) : void 0;
176578
+ const turnId = messageId ? await resolveChatItemsTurnId(deps.items, messageId, turn.id) : turn.id;
176579
+ await recordChatTurnInterruption({
176580
+ items: deps.items,
176581
+ sessionId: deps.sessionId,
176582
+ turnId,
176583
+ runId: turn.assistantRunId,
176584
+ messageId,
176585
+ cause: "user-stop",
176586
+ reason: "user-stop",
176587
+ now: () => completedAt,
176588
+ ...deps.log ? { log: deps.log } : {}
176589
+ });
176590
+ }
176591
+ var MAX_CAS_ATTEMPTS;
176592
+ var init_chat_turn_stop = __esm({
176593
+ "../server/src/chat-turn-stop.ts"() {
176594
+ "use strict";
176595
+ init_chat_interrupt_notice();
176596
+ init_chat_item_ledger();
176597
+ MAX_CAS_ATTEMPTS = 3;
176598
+ }
176599
+ });
176600
+
176542
176601
  // ../server/src/chat-broadcast.ts
176543
176602
  function isFreshTerminal(terminalAt, nowMs, maxAgeMs) {
176544
176603
  if (!terminalAt) return true;
@@ -211147,6 +211206,10 @@ function writeApiError(res, err) {
211147
211206
  error: { code: err.code, message: err.message, ...err.details ?? {} }
211148
211207
  }));
211149
211208
  }
211209
+ async function findAssistantMessageIdByRun(store, sessionId, runId) {
211210
+ const rows = await (await store())?.listMessages(sessionId, 20).catch(() => []) ?? [];
211211
+ return rows.find((m2) => m2.role === "assistant" && m2.runId === runId)?.id;
211212
+ }
211150
211213
  function toApiError(err) {
211151
211214
  if (err instanceof ApiError) return err;
211152
211215
  const message = err instanceof Error ? err.message : String(err);
@@ -212730,6 +212793,7 @@ async function startOasisServer(opts) {
212730
212793
  }) : void 0;
212731
212794
  knowledgeDomain?.register(domainRouter);
212732
212795
  const liveChat = opts.liveChat ?? new LiveChatRegistry();
212796
+ const serveBootedAtMs = Date.now();
212733
212797
  const resolveChatSessionsForDelegation = opts.chatSessionFor ?? (opts.chatSession ? async () => opts.chatSession : void 0);
212734
212798
  let delegationService = null;
212735
212799
  if (opts.delegationStoreFor && resolveChatSessionsForDelegation && opts.actors && opts.dispatchChat) {
@@ -215166,12 +215230,15 @@ async function startOasisServer(opts) {
215166
215230
  liveChat.reopenForRecovery(sid, {
215167
215231
  runtimeSessionId: cs.runtimeSessionId ?? active.dispatchId ?? "",
215168
215232
  ...active.assistantRunId ? { runId: active.assistantRunId } : {},
215233
+ // 占位轮被 `/stop` 停掉:与注册表落空时同一个收法(条件迁移 + 用户停止结局项,Bug 0228)。
215169
215234
  onStop: () => {
215170
- void turns.settleTurn(active.id, {
215171
- status: "cancelled",
215172
- completedAt: (/* @__PURE__ */ new Date()).toISOString(),
215173
- lastError: `\u7528\u6237\u5728 serve \u91CD\u542F\u6062\u590D\u7A97\u53E3\u5185\u505C\u6B62\uFF08\u8F6E ${active.id}\uFF09`
215174
- }).catch(() => void 0);
215235
+ void (async () => stopLedgerTurn({
215236
+ turns,
215237
+ sessionId: sid,
215238
+ turn: active,
215239
+ items: opts.resolveChatItems ? await opts.resolveChatItems(currentCompanyId).catch(() => void 0) : opts.chatItems,
215240
+ findAssistantMessageId: (runId) => findAssistantMessageIdByRun(chatStoreNowOrNull, sid, runId)
215241
+ }))().catch(() => void 0);
215175
215242
  }
215176
215243
  });
215177
215244
  };
@@ -215335,7 +215402,32 @@ async function startOasisServer(opts) {
215335
215402
  if (typeof b2.expectedRunId === "string" && b2.expectedRunId) expectedRunId = b2.expectedRunId;
215336
215403
  } catch {
215337
215404
  }
215338
- const outcome = liveChat.stop(sid, expectedRunId);
215405
+ let outcome = liveChat.stop(sid, expectedRunId);
215406
+ if (!outcome.stopped && outcome.reason === "no-live-turn") {
215407
+ const turns = opts.resolveChatTurns ? await opts.resolveChatTurns(currentCompanyId).catch(() => void 0) : opts.chatTurns;
215408
+ const active = turns ? await turns.activeTurn(sid).catch(() => null) : null;
215409
+ if (turns && active) {
215410
+ const openedByThisProcess = Date.parse(active.reservedAt) >= serveBootedAtMs;
215411
+ if (openedByThisProcess) {
215412
+ await waitForAdmittedTurnStart(sid, { liveChat, ledgerTurnRunning: async () => true, timeoutMs: STOP_TURN_START_WAIT_MS });
215413
+ outcome = liveChat.stop(sid, expectedRunId);
215414
+ }
215415
+ if (!outcome.stopped && outcome.reason === "no-live-turn") {
215416
+ outcome = await stopLedgerTurn({
215417
+ turns,
215418
+ sessionId: sid,
215419
+ expectedRunId,
215420
+ ...openedByThisProcess ? {} : { turn: active },
215421
+ items: opts.resolveChatItems ? await opts.resolveChatItems(currentCompanyId).catch(() => void 0) : opts.chatItems,
215422
+ findAssistantMessageId: (runId) => findAssistantMessageIdByRun(chatStoreNowOrNull, sid, runId),
215423
+ log: (m2) => console.log(m2)
215424
+ }).catch((err) => {
215425
+ console.warn(`[chat-stop] \u4F1A\u8BDD ${sid} \u6309\u53F0\u8D26\u505C\u6B62\u5931\u8D25: ${String(err)}`);
215426
+ return { stopped: false, reason: "turn-changed" };
215427
+ });
215428
+ }
215429
+ }
215430
+ }
215339
215431
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(outcome));
215340
215432
  return;
215341
215433
  }
@@ -217432,7 +217524,7 @@ async function markDeliveredWithRetry(store, messageId, attempts = 3) {
217432
217524
  }
217433
217525
  return false;
217434
217526
  }
217435
- var http, import_node_crypto44, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, COMPANY_FROM_PENDING_CALLBACKS, defaultResolveActor, enc, APPEND_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
217527
+ var http, import_node_crypto44, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, COMPANY_FROM_PENDING_CALLBACKS, defaultResolveActor, enc, APPEND_TURN_START_WAIT_MS, STOP_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
217436
217528
  var init_server3 = __esm({
217437
217529
  "../server/src/server.ts"() {
217438
217530
  "use strict";
@@ -217498,6 +217590,7 @@ var init_server3 = __esm({
217498
217590
  init_chat_workorder_files();
217499
217591
  init_chat_item_ledger();
217500
217592
  init_chat_cancel_attribution();
217593
+ init_chat_turn_stop();
217501
217594
  init_chat_assistant_turn();
217502
217595
  init_src2();
217503
217596
  init_chat_broadcast();
@@ -217519,6 +217612,7 @@ var init_server3 = __esm({
217519
217612
  defaultResolveActor = (token2) => token2.startsWith("token:") ? token2.slice("token:".length) : null;
217520
217613
  enc = (s2) => new TextEncoder().encode(s2);
217521
217614
  APPEND_TURN_START_WAIT_MS = 1e4;
217615
+ STOP_TURN_START_WAIT_MS = 5e3;
217522
217616
  STREAMING_PATHS = ["/api/events", "/api/chat", "/api/trajectory/events", "/api/intervention/wait"];
217523
217617
  PLANNER_BLOCKING_CODES = /* @__PURE__ */ new Set([
217524
217618
  "title_collision",
@@ -238094,10 +238188,10 @@ function stepRef(nodeId, title) {
238094
238188
  return token("s", nodeId ?? "", title);
238095
238189
  }
238096
238190
  function actorRef2(actorId, name) {
238097
- return actorId ? token("a", actorId, name) : clean(name);
238191
+ return actorId ? token("a", actorId, name) : withAt(clean(name));
238098
238192
  }
238099
238193
  function mentionRef(actorId, name) {
238100
- return actorId ? token("m", actorId, name) : `@${clean(name)}`;
238194
+ return actorId ? token("m", actorId, name) : withAt(clean(name));
238101
238195
  }
238102
238196
  function tokenize(s2) {
238103
238197
  const out = [];
@@ -238141,9 +238235,9 @@ function sentenceSegments(s2) {
238141
238235
  return out;
238142
238236
  }
238143
238237
  function plainSentence(s2) {
238144
- return tokenize(s2).map((t) => typeof t === "string" ? t : t.kind === "s" ? `\u300C${t.name}\u300D` : t.kind === "m" ? `@${t.name}` : t.name).join("");
238238
+ return tokenize(s2).map((t) => typeof t === "string" ? t : t.kind === "s" ? `\u300C${t.name}\u300D` : withAt(t.name)).join("");
238145
238239
  }
238146
- var OPEN2, SEP, CLOSE2, MARKS, clean, token;
238240
+ var OPEN2, SEP, CLOSE2, MARKS, clean, withAt, token, plainActor;
238147
238241
  var init_activity_text = __esm({
238148
238242
  "../server/src/domains/collab/activity-text.ts"() {
238149
238243
  "use strict";
@@ -238152,7 +238246,9 @@ var init_activity_text = __esm({
238152
238246
  CLOSE2 = "\uE002";
238153
238247
  MARKS = /[-]/g;
238154
238248
  clean = (s2) => s2.replace(MARKS, "");
238249
+ withAt = (name) => name.startsWith("@") ? name : `@${name}`;
238155
238250
  token = (kind, id, name) => `${OPEN2}${kind}${SEP}${clean(id)}${SEP}${clean(name)}${CLOSE2}`;
238251
+ plainActor = (name) => withAt(clean(name));
238156
238252
  }
238157
238253
  });
238158
238254
 
@@ -238625,7 +238721,7 @@ function buildWorkorderActivity(input) {
238625
238721
  const managerId = resolveWorkorderManager(snap);
238626
238722
  const ownerId = snap.workorder.ownerActorId;
238627
238723
  const nameOf2 = (id) => id ? ref2(id).name ?? id : "\u2014";
238628
- const personOf = (id) => actorRef2(id ?? void 0, nameOf2(id));
238724
+ const personOf = (id) => id ? actorRef2(id, nameOf2(id)) : nameOf2(id);
238629
238725
  const mentionOf = (id) => mentionRef(id ?? void 0, nameOf2(id));
238630
238726
  const managerName = () => personOf(managerId);
238631
238727
  const nodeTitleOf = (nodeId) => {
@@ -240891,7 +240987,10 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
240891
240987
  ...extra?.avatar ? { avatar: extra.avatar } : {}
240892
240988
  };
240893
240989
  };
240894
- const actorName = (id) => resolveActor?.(id)?.name ?? "";
240990
+ const atName = (id) => {
240991
+ const name = resolveActor?.(id)?.name;
240992
+ return name ? plainActor(name) : "";
240993
+ };
240895
240994
  const nowMs = nowIso ? Date.parse(nowIso) : Number.NaN;
240896
240995
  const overdueSince = (sinceIso) => {
240897
240996
  if (slaMs === void 0 || !Number.isFinite(nowMs) || !sinceIso) return void 0;
@@ -241028,7 +241127,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241028
241127
  // 续跑申请的入口在任务动态那张续跑申请卡上(卡 id = 申请 id,见 activity.ts `continuationCards`)。
241029
241128
  ...r.continuation ? { activityCardId: r.continuation.requestId } : {},
241030
241129
  ...anchorArt ? { title: nodeLabel(anchorArt) } : {},
241031
- detail: `${actorName(r.proposedBy) || "\u7BA1\u7406\u8005"}\uFF1A${r.command ? commandCardLine(r.command) : firstSentence3(r.plan.reason)}`,
241130
+ detail: `${atName(r.proposedBy) || "\u7BA1\u7406\u8005"}\uFF1A${r.command ? commandCardLine(r.command) : firstSentence3(r.plan.reason)}`,
241032
241131
  ...r.kind !== "command" && opCount > 0 ? { impact: `\u6D89\u53CA ${opCount} \u5904\u6539\u52A8${r.nonMonotonicOps.length > 0 ? " \xB7 \u542B\u9500\u6BC1\u6027" : ""}` } : {},
241033
241132
  actor: actorRef3(r.proposedBy),
241034
241133
  ...overdueField(r.at)
@@ -241069,7 +241168,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241069
241168
  actionRequired: true,
241070
241169
  summary: `\u6709\u4EBA\u5728\u300C${label2}\u300D\u7684\u4FE1\u91CC @ \u4E86\u4F60\uFF0C\u7B49\u4F60\u56DE\u8BDD\uFF1A${(lastPost?.body ?? an.body).slice(0, 100)}`,
241071
241170
  title: label2,
241072
- detail: `${(lastPost ? actorName(lastPost.author) : "") || "\u6709\u4EBA"} @ \u4E86\u4F60\uFF1A${firstSentence3(lastPost?.body ?? an.body)}`,
241171
+ detail: `${(lastPost ? atName(lastPost.author) : "") || "\u6709\u4EBA"} @ \u4E86\u4F60\uFF1A${firstSentence3(lastPost?.body ?? an.body)}`,
241073
241172
  ...lastPost ? { actor: actorRef3(lastPost.author) } : {},
241074
241173
  ...overdueField(an.createdAt)
241075
241174
  });
@@ -241094,7 +241193,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241094
241193
  actionRequired: false,
241095
241194
  summary: `\u4F60\u7684\u4FE1\u6709\u65B0\u52A8\u9759\uFF08${phrase}\uFF09\u300C${nodeLabel(a)}\u300D\uFF1A${lastNote.slice(0, 100)}\u2014\u2014\u4E0D\u8BA4\u53EF\u53EF\u5728\u4FE1\u4E2D\u56DE\u5E16`,
241096
241195
  title: nodeLabel(a),
241097
- detail: `${actorName(activity.by) || "\u5BF9\u65B9"} \u56DE\u5E94\u4E86\u4F60\u7684\u4FE1\uFF1A${firstSentence3(lastNote)}`,
241196
+ detail: `${atName(activity.by) || "\u5BF9\u65B9"} \u56DE\u5E94\u4E86\u4F60\u7684\u4FE1\uFF1A${firstSentence3(lastNote)}`,
241098
241197
  actor: actorRef3(activity.by)
241099
241198
  // 通知型不判 overdue(诊断明说),不设 overdue 字段。
241100
241199
  });
@@ -241114,7 +241213,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241114
241213
  actionRequired: true,
241115
241214
  summary: `\u5F85\u4F60\u9A8C\u6536\uFF1A${label2}\uFF08${pendingHead.id}\uFF09\u2014\u2014\u8BE5\u7248\u7B54\u590D\u4E86\u4F60\u7684\u4FE1`,
241116
241215
  title: label2,
241117
- detail: `${actorName(pendingHead.author) || "agent"} \u4EA4\u4E86\u65B0\u7248\u7B54\u590D\u4F60\u7684\u4FE1\uFF1A${firstSentence3(pendingHead.reason)}`,
241216
+ detail: `${atName(pendingHead.author) || "agent"} \u4EA4\u4E86\u65B0\u7248\u7B54\u590D\u4F60\u7684\u4FE1\uFF1A${firstSentence3(pendingHead.reason)}`,
241118
241217
  actor: actorRef3(pendingHead.author),
241119
241218
  ...overdueField(pendingHead.createdAt)
241120
241219
  });
@@ -241141,7 +241240,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241141
241240
  actionRequired: true,
241142
241241
  summary: `\u5F85\u4F60\u5BA1\u6279\uFF1A${label2}`,
241143
241242
  title: label2,
241144
- ...gp ? { detail: `${actorName(gp.headAuthor) || "\u4F5C\u8005"} \u4EA4\u4E86\u65B0\u7248\uFF1A${firstSentence3(headReason)}`, actor: actorRef3(gp.headAuthor) } : {},
241243
+ ...gp ? { detail: `${atName(gp.headAuthor) || "\u4F5C\u8005"} \u4EA4\u4E86\u65B0\u7248\uFF1A${firstSentence3(headReason)}`, actor: actorRef3(gp.headAuthor) } : {},
241145
241244
  ...impactParts.length > 0 ? { impact: impactParts.join(" \xB7 ") } : {},
241146
241245
  ...overdueField(gate.at)
241147
241246
  });
@@ -241164,7 +241263,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
241164
241263
  actionRequired: true,
241165
241264
  summary: `\u4EA7\u51FA\u88AB\u6253\u56DE\uFF0C\u9700\u8FD4\u5DE5\uFF1A${label2}`,
241166
241265
  title: label2,
241167
- ...rejectVote ? { detail: `${actorName(rejectVote.author) || "\u8BC4\u5BA1"} \u6253\u56DE\uFF1A${firstSentence3(rejectVote.note ?? "\u89C1\u8BC4\u5BA1\u610F\u89C1")}`, actor: actorRef3(rejectVote.author) } : {},
241266
+ ...rejectVote ? { detail: `${atName(rejectVote.author) || "\u8BC4\u5BA1"} \u6253\u56DE\uFF1A${firstSentence3(rejectVote.note ?? "\u89C1\u8BC4\u5BA1\u610F\u89C1")}`, actor: actorRef3(rejectVote.author) } : {},
241168
241267
  ...reworkDs > 0 ? { impact: `\u6321\u7740\u4E0B\u6E38 ${reworkDs} \u4E2A\u8282\u70B9` } : {},
241169
241268
  ...overdueField(latest.createdAt)
241170
241269
  });
@@ -241284,6 +241383,7 @@ var init_inbox = __esm({
241284
241383
  init_src8();
241285
241384
  init_src();
241286
241385
  init_node_state();
241386
+ init_activity_text();
241287
241387
  C_ACTION_LABEL = {
241288
241388
  unlink: "\u65AD\u5F00\u4F9D\u8D56\u8FB9",
241289
241389
  pin: "\u6539\u9489\u7248\u672C",
@@ -247596,11 +247696,12 @@ function nodesDomain(deps) {
247596
247696
  ...typeof r.presentOnMachine === "boolean" ? { presentOnMachine: r.presentOnMachine } : {}
247597
247697
  };
247598
247698
  }
247599
- function revokeNodeAccess(nodeId) {
247699
+ function revokeNodeAccess(nodeId, opts = {}) {
247700
+ const tellMachine = opts.tellMachine ?? true;
247600
247701
  const daemon = deps.getHub()?.connectedDaemons().find((d) => d.daemonId === nodeId);
247601
- daemon?.send({ type: "node_deleted", nodeId });
247702
+ if (tellMachine) daemon?.send({ type: "node_deleted", nodeId });
247602
247703
  for (const { token: token2, nodeId: nid } of deps.nodeTokens.list()) {
247603
- if (nid === nodeId) deps.nodeTokens.revoke(token2, "node-deleted");
247704
+ if (nid === nodeId) deps.nodeTokens.revoke(token2, tellMachine ? "node-deleted" : "unspecified");
247604
247705
  }
247605
247706
  daemon?.close();
247606
247707
  }
@@ -247608,9 +247709,9 @@ function nodesDomain(deps) {
247608
247709
  const restartEmptyClock = (nodeId) => {
247609
247710
  emptySince.set(nodeId, Date.now());
247610
247711
  };
247611
- async function removeNode(nodeId, releaseAs) {
247712
+ async function removeNode(nodeId, releaseAs, opts = {}) {
247612
247713
  const kinds = [...new Set((await deps.nodeStore.listRuntimes(nodeId)).map((r) => r.kind))];
247613
- revokeNodeAccess(nodeId);
247714
+ revokeNodeAccess(nodeId, opts);
247614
247715
  await deps.nodeStore.deleteNode(nodeId);
247615
247716
  emptySince.delete(nodeId);
247616
247717
  const released = [];
@@ -247647,13 +247748,25 @@ function nodesDomain(deps) {
247647
247748
  if (removed.length > 0) deps.onStatusChange?.();
247648
247749
  return removed;
247649
247750
  }
247751
+ async function purgeUnownedNodes() {
247752
+ const removed = [];
247753
+ for (const n of await deps.nodeStore.listUnownedNodes()) {
247754
+ const current = await deps.nodeStore.getNode(n.id);
247755
+ if (!current || current.companyId) continue;
247756
+ await removeNode(n.id, void 0, { tellMachine: false });
247757
+ removed.push(n.id);
247758
+ console.log(`[nodes] \u4F5C\u5E9F\u6CA1\u6709\u5F52\u5C5E\u516C\u53F8\u7684\u8282\u70B9\uFF1A${n.id}\uFF08\u4E3B\u673A\u540D ${n.hostname}${n.name ? `\uFF0C\u540D\u79F0 ${n.name}` : ""}\uFF0C\u72B6\u6001 ${n.status}\uFF09\u2014\u2014\u5DF2\u64A4\u51ED\u8BC1\u3001\u65AD\u5F00\u8FDE\u63A5\u3001\u5220\u9664\u8BB0\u5F55\uFF0C\u673A\u5668\u672C\u5730\u4E0D\u52A8\uFF1B\u91CD\u65B0\u8FD0\u884C\u63A5\u5165\u547D\u4EE4\u5373\u6309\u65B0\u673A\u5668\u63A5\u5165`);
247759
+ }
247760
+ if (removed.length > 0) deps.onStatusChange?.();
247761
+ return removed;
247762
+ }
247650
247763
  if (deps.emptyNodeSweep) {
247651
247764
  const { graceMs, intervalMs } = deps.emptyNodeSweep;
247652
247765
  let sweeping = false;
247653
247766
  const timer = setInterval(() => {
247654
247767
  if (sweeping) return;
247655
247768
  sweeping = true;
247656
- void sweepEmptyNodes(graceMs).catch((err) => console.warn(`[nodes] \u7A7A\u8282\u70B9\u6E05\u626B\u5931\u8D25\uFF08\u4E0B\u4E00\u62CD\u518D\u8BD5\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`)).finally(() => {
247769
+ void purgeUnownedNodes().then(() => sweepEmptyNodes(graceMs)).catch((err) => console.warn(`[nodes] \u7A7A\u8282\u70B9\u6E05\u626B\u5931\u8D25\uFF08\u4E0B\u4E00\u62CD\u518D\u8BD5\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`)).finally(() => {
247657
247770
  sweeping = false;
247658
247771
  });
247659
247772
  }, intervalMs);
@@ -248160,6 +248273,12 @@ function nodesDomain(deps) {
248160
248273
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
248161
248274
  }
248162
248275
  const existingNode = await deps.nodeStore.getNode(nodeId);
248276
+ if (existingNode && !existingNode.companyId && entry.companyId) {
248277
+ return {
248278
+ status: 409,
248279
+ body: { error: "NODE_UNOWNED", message: "\u8FD9\u53F0\u673A\u5668\u5728\u7CFB\u7EDF\u91CC\u7684\u65E7\u8BB0\u5F55\u6CA1\u6709\u5F52\u5C5E\u516C\u53F8\uFF0C\u4E0D\u80FD\u88AB\u63A5\u5165\uFF1B\u7CFB\u7EDF\u4F1A\u5728\u7EA6\u4E00\u5206\u949F\u5185\u6E05\u6389\u8FD9\u6761\u65E7\u8BB0\u5F55\uFF0C\u4E4B\u540E\u91CD\u65B0\u8FD0\u884C\u63A5\u5165\u547D\u4EE4\u5373\u53EF\u6309\u65B0\u673A\u5668\u63A5\u5165" }
248280
+ };
248281
+ }
248163
248282
  if (existingNode?.companyId && entry.companyId && existingNode.companyId !== entry.companyId) {
248164
248283
  return { status: 409, body: { error: "\u8BE5\u8282\u70B9\u65E0\u6CD5\u7528\u5F53\u524D\u5165\u7F51\u4EE4\u724C\u63A5\u7BA1" } };
248165
248284
  }
@@ -249169,6 +249288,9 @@ var init_node_store = __esm({
249169
249288
  const live = [...this.nodes.values()].filter((n) => n.status !== "deleted");
249170
249289
  return filter?.companyId === void 0 ? live : live.filter((n) => n.companyId === filter.companyId);
249171
249290
  }
249291
+ async listUnownedNodes() {
249292
+ return [...this.nodes.values()].filter((n) => !n.companyId);
249293
+ }
249172
249294
  async listRuntimes(nodeId, filter) {
249173
249295
  let all2 = [...this.runtimes.values()];
249174
249296
  if (nodeId) all2 = all2.filter((r) => r.nodeId === nodeId);
@@ -253341,11 +253463,12 @@ function missingNodeFacts(body2) {
253341
253463
  }
253342
253464
  function renderEscalationOpeningBody(facts) {
253343
253465
  const body2 = facts.body.trim();
253466
+ const who = facts.actorName ? plainActor(facts.actorName) : void 0;
253344
253467
  if (facts.kind === "reviewer_unavailable") {
253345
253468
  const { reviewerId, role } = reviewerUnavailableFacts(body2);
253346
253469
  if (!reviewerId) return body2;
253347
253470
  return [
253348
- `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D\u9501\u5B9A\u7684\u5BA1\u6838\u4EBA${facts.actorName ? ` ${facts.actorName}` : ""}\uFF08${role} \u89D2\u8272\uFF09\u73B0\u5728\u4E0D\u53EF\u7528\u2014\u2014\u7194\u65AD\u3001\u79BB\u5C97\uFF0C\u6216\u8005\u5DF2\u88AB\u79FB\u51FA\u8FD9\u4E2A\u89D2\u8272\u2014\u2014\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u6D3E\u4E0D\u51FA\u53BB\u3002`,
253471
+ `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D\u9501\u5B9A\u7684\u5BA1\u6838\u4EBA${who ? ` ${who}` : ""}\uFF08${role} \u89D2\u8272\uFF09\u73B0\u5728\u4E0D\u53EF\u7528\u2014\u2014\u7194\u65AD\u3001\u79BB\u5C97\uFF0C\u6216\u8005\u5DF2\u88AB\u79FB\u51FA\u8FD9\u4E2A\u89D2\u8272\u2014\u2014\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u6D3E\u4E0D\u51FA\u53BB\u3002`,
253349
253472
  "\u9700\u8981\u4F60\u5B9A\u4E00\u4E0B\uFF1A\u6362\u4E00\u4E2A\u5BA1\u6838\u4EBA\uFF0C\u8FD8\u662F\u7B49\u539F\u5BA1\u6838\u4EBA\u6062\u590D\u3002"
253350
253473
  ].join("\n\n");
253351
253474
  }
@@ -253353,7 +253476,7 @@ function renderEscalationOpeningBody(facts) {
253353
253476
  const { gone, runtimeNode } = missingNodeFacts(body2);
253354
253477
  if (!runtimeNode) return body2;
253355
253478
  return [
253356
- `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D\u8FD8\u6CA1\u6709\u542F\u52A8\uFF1A${facts.actorName ?? "\u6267\u884C\u4EBA"}\u7ED1\u5B9A\u7684\u8FD0\u884C\u8282\u70B9 ${runtimeNode} ${gone === "\u672A\u6CE8\u518C" ? "\u6CA1\u6709\u6CE8\u518C\u8FC7" : "\u5DF2\u7ECF\u88AB\u5220\u9664"}\uFF0C\u6D3E\u4E0D\u51FA\u53BB\u3002\u8FD9\u6B21\u68C0\u67E5\u6CA1\u6709\u7EC8\u6B62\u4EFB\u4F55\u4F1A\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u6D88\u8017\u91CD\u8BD5\u6B21\u6570\u3002`,
253479
+ `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D\u8FD8\u6CA1\u6709\u542F\u52A8\uFF1A${who ?? "\u6267\u884C\u4EBA"}\u7ED1\u5B9A\u7684\u8FD0\u884C\u8282\u70B9 ${runtimeNode} ${gone === "\u672A\u6CE8\u518C" ? "\u6CA1\u6709\u6CE8\u518C\u8FC7" : "\u5DF2\u7ECF\u88AB\u5220\u9664"}\uFF0C\u6D3E\u4E0D\u51FA\u53BB\u3002\u8FD9\u6B21\u68C0\u67E5\u6CA1\u6709\u7EC8\u6B62\u4EFB\u4F55\u4F1A\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u6D88\u8017\u91CD\u8BD5\u6B21\u6570\u3002`,
253357
253480
  "\u9700\u8981\u4F60\u6062\u590D\u90A3\u53F0\u8282\u70B9\uFF0C\u6216\u8005\u7ED9\u6267\u884C\u4EBA\u6539\u7ED1\u4E00\u53F0\u5728\u7EBF\u7684\u8282\u70B9\uFF1B\u4FEE\u597D\u4E4B\u540E\u544A\u8BC9\u6211\uFF0C\u6211\u6765\u628A\u8FD9\u4E00\u6B65\u653E\u51FA\u53BB\u3002"
253358
253481
  ].join("\n\n");
253359
253482
  }
@@ -253362,7 +253485,7 @@ function renderEscalationOpeningBody(facts) {
253362
253485
  const { count: count3, lastReason } = retryFacts(body2);
253363
253486
  if (!count3) return body2;
253364
253487
  return [
253365
- `${facts.actorName ?? "\u8FD9\u4E00\u6B65\u7684\u6267\u884C\u4EBA"}${at(facts.nodeLabel)}\u8FDE\u7EED ${count3.n} \u8F6E\u90FD\u6CA1\u8DD1\u6210\uFF0C\u81EA\u52A8\u91CD\u8BD5\u5DF2\u7ECF\u7528\u5B8C\u2014\u2014\u8FD9\u4E00\u6B65\u4E0D\u4F1A\u518D\u81EA\u52A8\u91CD\u6765\u4E86\u3002`,
253488
+ `${who ?? "\u8FD9\u4E00\u6B65\u7684\u6267\u884C\u4EBA"}${at(facts.nodeLabel)}\u8FDE\u7EED ${count3.n} \u8F6E\u90FD\u6CA1\u8DD1\u6210\uFF0C\u81EA\u52A8\u91CD\u8BD5\u5DF2\u7ECF\u7528\u5B8C\u2014\u2014\u8FD9\u4E00\u6B65\u4E0D\u4F1A\u518D\u81EA\u52A8\u91CD\u6765\u4E86\u3002`,
253366
253489
  ...lastReason ? [`\u6700\u540E\u4E00\u6B21\u5931\u8D25\u8BB0\u4E0B\u7684\u539F\u56E0\uFF1A${lastReason}`] : [],
253367
253490
  "\u9700\u8981\u4F60\u5B9A\u4E00\u4E0B\u63A5\u4E0B\u6765\u600E\u4E48\u529E\uFF1A\u6309\u539F\u65B9\u6848\u518D\u8BD5\u4E00\u6B21\u3001\u6539\u4E86\u4EFB\u52A1\u8BF4\u660E\u518D\u8DD1\uFF0C\u8FD8\u662F\u6362\u4E2A\u4EBA\u505A\u3002"
253368
253491
  ].join("\n\n");
@@ -253377,7 +253500,7 @@ function renderEscalationOpeningBody(facts) {
253377
253500
  }
253378
253501
  const { count: count2, minutes, cost, remark, stalled } = stallFacts(body2);
253379
253502
  if (!count2) return body2;
253380
- const subject = stalled ? `${facts.actorName ?? (stalled === "review" ? "\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u5458" : "\u8FD9\u4E00\u6B65\u7684\u6267\u884C\u4EBA")}${at(facts.nodeLabel)}` : `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D`;
253503
+ const subject = stalled ? `${who ?? (stalled === "review" ? "\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u5458" : "\u8FD9\u4E00\u6B65\u7684\u6267\u884C\u4EBA")}${at(facts.nodeLabel)}` : `\u300C${facts.nodeLabel ?? "\u8FD9\u4E00\u6B65"}\u300D`;
253381
253504
  const window3 = minutes !== void 0 ? `\u6700\u8FD1 ${minutes} \u5206\u949F\u91CC` : "\u6700\u8FD1\u4E00\u6BB5\u65F6\u95F4\u91CC";
253382
253505
  const spent = cost ? `\uFF08\u8FD9\u4E9B\u7A7A\u8DD1\u5408\u8BA1\u5DF2\u7ECF\u82B1\u6389\u7EA6 ${cost}\uFF09` : "";
253383
253506
  const each = count2.unit === "\u8F6E" ? "\u6BCF\u4E00\u8F6E" : "\u6BCF\u4E00\u6B21";
@@ -253393,6 +253516,7 @@ var num3, at;
253393
253516
  var init_escalation_opening_body = __esm({
253394
253517
  "../server/src/domains/collab/escalation-opening-body.ts"() {
253395
253518
  "use strict";
253519
+ init_activity_text();
253396
253520
  num3 = (v2) => {
253397
253521
  if (v2 === void 0) return void 0;
253398
253522
  const n = Number(v2);
@@ -272051,6 +272175,10 @@ var init_postgres_nodes = __esm({
272051
272175
  const r = filter?.companyId !== void 0 ? await this.pool.query(`SELECT * FROM ${this.s}.nodes WHERE status <> 'deleted' AND company_id = $1 ORDER BY enrolled_at ASC`, [filter.companyId]) : await this.pool.query(`SELECT * FROM ${this.s}.nodes WHERE status <> 'deleted' ORDER BY enrolled_at ASC`);
272052
272176
  return r.rows.map(rowToNode);
272053
272177
  }
272178
+ async listUnownedNodes() {
272179
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.nodes WHERE company_id IS NULL ORDER BY enrolled_at ASC`);
272180
+ return r.rows.map(rowToNode);
272181
+ }
272054
272182
  async listRuntimes(nodeId, filter) {
272055
272183
  const where = [];
272056
272184
  const params = [];
@@ -298232,7 +298360,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
298232
298360
  }
298233
298361
  }
298234
298362
  }
298235
- var CLI_VERSION = "2.2.98" ? "2.2.98" : "dev";
298363
+ var CLI_VERSION = "2.2.99" ? "2.2.99" : "dev";
298236
298364
  function apiHeaders(token2, company) {
298237
298365
  const headers = {
298238
298366
  authorization: `Bearer ${token2}`,
@@ -302203,7 +302331,7 @@ function shimScript() {
302203
302331
  }
302204
302332
 
302205
302333
  // src/index.ts
302206
- var PKG_VERSION = true ? "2.2.98" : "dev";
302334
+ var PKG_VERSION = true ? "2.2.99" : "dev";
302207
302335
  var LOCAL_BIN = localBin();
302208
302336
  var NPM_PREFIX = npmPrefix();
302209
302337
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.98",
3
+ "version": "2.2.99",
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": "9232426433ddd120620c9d131d9f3fd247d726eb"
29
+ "sourceHead": "705a5a2b945e6e24cd1ddefef8d0e6e148574808"
30
30
  }
31
31
  }