oasis_test 0.1.137 → 0.1.138

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 +522 -125
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -719,11 +719,16 @@ var init_link_windows = __esm({
719
719
  browserStaleAfterMs: 45e3,
720
720
  /**
721
721
  * 会话进程发完 `session_exited` 后等 ack 的时限(`session-process.ts`)。
722
- * ⚠ ADR 0300 D7 已判定这个值偏小:实测一次 serve 重启停服 27.9 s,停服窗口内跑完的会话
723
- * 其退出帧**必然**丢失。0300 止血 PR 0 会把它提到 ≥ 90 s 并让未回执的退出帧先落盘,
724
- * 改的就是这一行——本次只收敛位置,不动值。
725
- */
726
- exitAckWaitMs: 1e4,
722
+ *
723
+ * ADR 0300 D7:取 90 s 覆盖一次 serve 重启(实测停服 27.6~27.9 s,数据面会话最慢 38.6 s 连回,
724
+ * 证据 C/D)。会话进程在这段时间里**不退出**、保持数据面重连;serve 回来后 `ws.on("open")` 会
725
+ * 重发 outbox 里未回执的 `session_exited`(`session-process.ts` 的 `for (const m of this.outbox)`),
726
+ * 服务端 `reconcileChatExitFrame` 按 `dispatchId` 幂等收口——退出帧不再随停服窗口蒸发、账本不再
727
+ * 停在 running 等时钟扫描判孤儿。仍小于会话自尽窗 `SESSION_LIMITS.suicideAfterUnreachableMs`(5 min),
728
+ * 等 ack 期间不会先自尽。**更狠的一层**(进程被 TTL/自尽强杀于窗口内时把未回执帧落 `exit.json`、
729
+ * 由 daemon 信使补发)属 D7 后续,另计。
730
+ */
731
+ exitAckWaitMs: 9e4,
727
732
  /** hub 对「暂无人认领」的会话在 serve 冷启动后挂起重判的宽限(`daemon-hub.ts`)。 */
728
733
  hubColdStartGraceMs: 9e4,
729
734
  /** orphan-sweep 判「账上在跑、四张认领表都不认」为孤儿的宽限(`serve.ts`)。 */
@@ -19191,7 +19196,9 @@ var init_kernel_bridge = __esm({
19191
19196
  handActorId: args.hand ?? null,
19192
19197
  ...args.intent !== void 0 ? { intent: args.intent } : {},
19193
19198
  ...args.source !== void 0 ? { source: args.source } : {},
19194
- recipients: [this.model.artifacts.get(args.artifactId)?.owner ?? ""],
19199
+ // Escalation hands responsibility to the workorder owner; never address only the stalled executor.
19200
+ // Human/team lead delivery additionally uses the same live recipient projection as the Inbox.
19201
+ recipients: [...new Set([brief?.owner ?? art?.owner].filter((id) => !!id))],
19195
19202
  attachments: []
19196
19203
  }
19197
19204
  });
@@ -33366,14 +33373,14 @@ var require_axios = __commonJS({
33366
33373
  navigator: _navigator,
33367
33374
  origin
33368
33375
  });
33369
- var platform = {
33376
+ var platform2 = {
33370
33377
  ...utils,
33371
33378
  ...platform$1
33372
33379
  };
33373
33380
  function toURLEncodedForm(data, options) {
33374
- return toFormData(data, new platform.classes.URLSearchParams(), {
33381
+ return toFormData(data, new platform2.classes.URLSearchParams(), {
33375
33382
  visitor: function(value2, key, path40, helpers) {
33376
- if (platform.isNode && utils$1.isBuffer(value2)) {
33383
+ if (platform2.isNode && utils$1.isBuffer(value2)) {
33377
33384
  this.append(key, value2.toString("base64"));
33378
33385
  return false;
33379
33386
  }
@@ -33526,8 +33533,8 @@ var require_axios = __commonJS({
33526
33533
  maxContentLength: -1,
33527
33534
  maxBodyLength: -1,
33528
33535
  env: {
33529
- FormData: platform.classes.FormData,
33530
- Blob: platform.classes.Blob
33536
+ FormData: platform2.classes.FormData,
33537
+ Blob: platform2.classes.Blob
33531
33538
  },
33532
33539
  validateStatus: function validateStatus(status) {
33533
33540
  return status >= 200 && status < 300;
@@ -33890,7 +33897,7 @@ var require_axios = __commonJS({
33890
33897
  }
33891
33898
  var DATA_URL_PATTERN = /^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/;
33892
33899
  function fromDataURI(uri, asBlob, options) {
33893
- const _Blob = options && options.Blob || platform.classes.Blob;
33900
+ const _Blob = options && options.Blob || platform2.classes.Blob;
33894
33901
  const protocol = parseProtocol(uri);
33895
33902
  if (asBlob === void 0 && _Blob) {
33896
33903
  asBlob = true;
@@ -34048,7 +34055,7 @@ var require_axios = __commonJS({
34048
34055
  }
34049
34056
  };
34050
34057
  var readBlob$1 = readBlob;
34051
- var BOUNDARY_ALPHABET = platform.ALPHABET.ALPHA_DIGIT + "-_";
34058
+ var BOUNDARY_ALPHABET = platform2.ALPHABET.ALPHA_DIGIT + "-_";
34052
34059
  var textEncoder = typeof TextEncoder === "function" ? new TextEncoder() : new util__default["default"].TextEncoder();
34053
34060
  var CRLF = "\r\n";
34054
34061
  var CRLF_BYTES = textEncoder.encode(CRLF);
@@ -34094,7 +34101,7 @@ var require_axios = __commonJS({
34094
34101
  const {
34095
34102
  tag = "form-data-boundary",
34096
34103
  size = 25,
34097
- boundary = tag + "-" + platform.generateString(size, BOUNDARY_ALPHABET)
34104
+ boundary = tag + "-" + platform2.generateString(size, BOUNDARY_ALPHABET)
34098
34105
  } = options || {};
34099
34106
  if (!utils$1.isFormData(form)) {
34100
34107
  throw TypeError("FormData instance required");
@@ -34323,7 +34330,7 @@ var require_axios = __commonJS({
34323
34330
  var isBrotliSupported = utils$1.isFunction(zlib__default["default"].createBrotliDecompress);
34324
34331
  var { http: httpFollow, https: httpsFollow } = followRedirects__default["default"];
34325
34332
  var isHttps = /https:?/;
34326
- var supportedProtocols = platform.protocols.map((protocol) => {
34333
+ var supportedProtocols = platform2.protocols.map((protocol) => {
34327
34334
  return protocol + ":";
34328
34335
  });
34329
34336
  var flushOnFinish = (stream2, [throttled, flush]) => {
@@ -34575,7 +34582,7 @@ var require_axios = __commonJS({
34575
34582
  }
34576
34583
  });
34577
34584
  const fullPath = buildFullPath(config2.baseURL, config2.url, config2.allowAbsoluteUrls);
34578
- const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : void 0);
34585
+ const parsed = new URL(fullPath, platform2.hasBrowserEnv ? platform2.origin : void 0);
34579
34586
  const protocol = parsed.protocol || supportedProtocols[0];
34580
34587
  if (protocol === "data:") {
34581
34588
  if (config2.maxContentLength > -1) {
@@ -34983,14 +34990,14 @@ var require_axios = __commonJS({
34983
34990
  }
34984
34991
  });
34985
34992
  };
34986
- var isURLSameOrigin = platform.hasStandardBrowserEnv ? /* @__PURE__ */ ((origin2, isMSIE) => (url2) => {
34987
- url2 = new URL(url2, platform.origin);
34993
+ var isURLSameOrigin = platform2.hasStandardBrowserEnv ? /* @__PURE__ */ ((origin2, isMSIE) => (url2) => {
34994
+ url2 = new URL(url2, platform2.origin);
34988
34995
  return origin2.protocol === url2.protocol && origin2.host === url2.host && (isMSIE || origin2.port === url2.port);
34989
34996
  })(
34990
- new URL(platform.origin),
34991
- platform.navigator && /(msie|trident)/i.test(platform.navigator.userAgent)
34997
+ new URL(platform2.origin),
34998
+ platform2.navigator && /(msie|trident)/i.test(platform2.navigator.userAgent)
34992
34999
  ) : () => true;
34993
- var cookies = platform.hasStandardBrowserEnv ? (
35000
+ var cookies = platform2.hasStandardBrowserEnv ? (
34994
35001
  // Standard browser envs support document.cookie
34995
35002
  {
34996
35003
  write(name, value2, expires, path40, domain2, secure, sameSite) {
@@ -35131,7 +35138,7 @@ var require_axios = __commonJS({
35131
35138
  );
35132
35139
  }
35133
35140
  if (utils$1.isFormData(data)) {
35134
- if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
35141
+ if (platform2.hasStandardBrowserEnv || platform2.hasStandardBrowserWebWorkerEnv) {
35135
35142
  headers.setContentType(void 0);
35136
35143
  } else if (utils$1.isFunction(data.getHeaders)) {
35137
35144
  const formHeaders = data.getHeaders();
@@ -35143,7 +35150,7 @@ var require_axios = __commonJS({
35143
35150
  });
35144
35151
  }
35145
35152
  }
35146
- if (platform.hasStandardBrowserEnv) {
35153
+ if (platform2.hasStandardBrowserEnv) {
35147
35154
  withXSRFToken && utils$1.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
35148
35155
  if (withXSRFToken || withXSRFToken !== false && isURLSameOrigin(newConfig.url)) {
35149
35156
  const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
@@ -35281,7 +35288,7 @@ var require_axios = __commonJS({
35281
35288
  }
35282
35289
  }
35283
35290
  const protocol = parseProtocol(_config.url);
35284
- if (protocol && platform.protocols.indexOf(protocol) === -1) {
35291
+ if (protocol && platform2.protocols.indexOf(protocol) === -1) {
35285
35292
  reject(
35286
35293
  new AxiosError$1(
35287
35294
  "Unsupported protocol " + protocol + ":",
@@ -35441,7 +35448,7 @@ var require_axios = __commonJS({
35441
35448
  const encodeText = isFetchSupported && (typeof TextEncoder$1 === "function" ? /* @__PURE__ */ ((encoder) => (str2) => encoder.encode(str2))(new TextEncoder$1()) : async (str2) => new Uint8Array(await new Request(str2).arrayBuffer()));
35442
35449
  const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
35443
35450
  let duplexAccessed = false;
35444
- const hasContentType = new Request(platform.origin, {
35451
+ const hasContentType = new Request(platform2.origin, {
35445
35452
  body: new ReadableStream$1(),
35446
35453
  method: "POST",
35447
35454
  get duplex() {
@@ -35478,7 +35485,7 @@ var require_axios = __commonJS({
35478
35485
  return body2.size;
35479
35486
  }
35480
35487
  if (utils$1.isSpecCompliantForm(body2)) {
35481
- const _request = new Request(platform.origin, {
35488
+ const _request = new Request(platform2.origin, {
35482
35489
  method: "POST",
35483
35490
  body: body2
35484
35491
  });
@@ -150194,6 +150201,40 @@ var init_live_chat = __esm({
150194
150201
  }
150195
150202
  };
150196
150203
  }
150204
+ /**
150205
+ * serve 重启后**重挂**一个占位轮(ADR 0300 D4)。该会话在本进程注册表里已有轮(真轮或另一个占位轮)
150206
+ * 则不动、返回 null——保证幂等且绝不覆盖真轮。占位轮 status=running、缓冲为空、句柄不带 appendInput
150207
+ * (`canAppend` 因此为 false),`kill()` 走注入的 `onStop`(账本收口)并把本轮标 error 让在挂的观众
150208
+ * 就地收到终止。节点重连时恢复路的 {@link start} 会替掉它。
150209
+ */
150210
+ reopenForRecovery(chatSessionId, opts) {
150211
+ if (this.turns.has(chatSessionId)) return null;
150212
+ let ctrl;
150213
+ const handle = {
150214
+ runtimeSessionId: opts.runtimeSessionId,
150215
+ ...opts.runId ? { runId: opts.runId } : {},
150216
+ canAppendInput: false,
150217
+ kill: () => {
150218
+ try {
150219
+ opts.onStop?.();
150220
+ } finally {
150221
+ ctrl?.finish("error");
150222
+ }
150223
+ }
150224
+ };
150225
+ ctrl = this.start(chatSessionId, handle);
150226
+ const turn = this.turns.get(chatSessionId);
150227
+ if (turn) turn.recovering = true;
150228
+ return ctrl;
150229
+ }
150230
+ /** 占位轮的最后一个观众断开即回收:没真 runtime 喂它,留着只会占内存、也会让 `isRunning` 恒真。 */
150231
+ evictIfEmptyRecovering(chatSessionId, turn) {
150232
+ if (!turn.recovering || turn.status !== "running") return;
150233
+ if (turn.subscribers.size > 0 || turn.liveSubscribers.size > 0) return;
150234
+ if (this.turns.get(chatSessionId) !== turn) return;
150235
+ if (turn.evictTimer) clearTimeout(turn.evictTimer);
150236
+ this.turns.delete(chatSessionId);
150237
+ }
150197
150238
  emitLive(turn, event) {
150198
150239
  turn.liveTurnId = event.turnId;
150199
150240
  const mergeField = this.mergeableLiveField(event);
@@ -150267,6 +150308,7 @@ var init_live_chat = __esm({
150267
150308
  gap,
150268
150309
  detach: () => {
150269
150310
  turn.subscribers.delete(sub);
150311
+ this.evictIfEmptyRecovering(chatSessionId, turn);
150270
150312
  }
150271
150313
  };
150272
150314
  }
@@ -150286,6 +150328,7 @@ var init_live_chat = __esm({
150286
150328
  epoch: turn.liveEpoch,
150287
150329
  detach: () => {
150288
150330
  turn.liveSubscribers.delete(sub);
150331
+ this.evictIfEmptyRecovering(chatSessionId, turn);
150289
150332
  }
150290
150333
  };
150291
150334
  }
@@ -150409,7 +150452,9 @@ var init_live_chat = __esm({
150409
150452
  * 建单草案 submit 发生在这一轮内:用它给草案盖上「产出轮次」标记,供 friday 重进对话页把每版
150410
150453
  * 草案归位到产出它的那条助手消息(message.runId 对齐)。 */
150411
150454
  runFor(chatSessionId) {
150412
- return this.turns.get(chatSessionId)?.runId;
150455
+ const turn = this.turns.get(chatSessionId);
150456
+ if (!turn || turn.recovering) return void 0;
150457
+ return turn.runId;
150413
150458
  }
150414
150459
  /**
150415
150460
  * 反查该会话当前驻留那一轮**这一段**的 assistant 行 id(无进行中/grace 轮、或还没落库时
@@ -150418,7 +150463,9 @@ var init_live_chat = __esm({
150418
150463
  * 两段共用同一个 runId,钉不出前后段的区别,而那正是本字段存在的理由。
150419
150464
  */
150420
150465
  messageFor(chatSessionId) {
150421
- return this.turns.get(chatSessionId)?.assistantMessageId;
150466
+ const turn = this.turns.get(chatSessionId);
150467
+ if (!turn || turn.recovering) return void 0;
150468
+ return turn.assistantMessageId;
150422
150469
  }
150423
150470
  /** 测试/排障用:当前驻留的会话数。 */
150424
150471
  size() {
@@ -184602,16 +184649,16 @@ var init_tool_home = __esm({
184602
184649
  });
184603
184650
 
184604
184651
  // ../connectors/src/_base/install.ts
184605
- function installPlan(cmd, platform, toolsPrefix) {
184652
+ function installPlan(cmd, platform2, toolsPrefix) {
184606
184653
  if (cmd === "lark-cli") {
184607
- const npm = platform === "win32" ? "npm.cmd" : "npm";
184654
+ const npm = platform2 === "win32" ? "npm.cmd" : "npm";
184608
184655
  return [{ file: npm, args: ["install", "-g", "--prefix", toolsPrefix, "@larksuite/cli"], ensureDir: toolsPrefix }];
184609
184656
  }
184610
184657
  const pkg = cmd === "gh" ? { brew: "gh", apt: "gh", dnf: "gh", pacman: "github-cli", apk: "github-cli", winget: "GitHub.cli", choco: "gh", scoop: "gh" } : { brew: "git", apt: "git", dnf: "git", pacman: "git", apk: "git", winget: "Git.Git", choco: "git", scoop: "git" };
184611
- if (platform === "darwin") {
184658
+ if (platform2 === "darwin") {
184612
184659
  return [{ file: "brew", args: ["install", pkg.brew], requires: "brew" }];
184613
184660
  }
184614
- if (platform === "win32") {
184661
+ if (platform2 === "win32") {
184615
184662
  return [
184616
184663
  { file: "winget", args: ["install", "--silent", "--accept-package-agreements", "--accept-source-agreements", "-e", "--id", pkg.winget], requires: "winget" },
184617
184664
  { file: "choco", args: ["install", "-y", pkg.choco], requires: "choco" },
@@ -194314,6 +194361,35 @@ ${task}` : task;
194314
194361
  }
194315
194362
  });
194316
194363
 
194364
+ // ../adapters/src/_core/pdeathsig.ts
194365
+ function pdeathsigAvailable(probe = defaultProbe) {
194366
+ if (cachedAvailable === void 0) cachedAvailable = probe();
194367
+ return cachedAvailable;
194368
+ }
194369
+ function defaultProbe() {
194370
+ if (os6.platform() !== "linux") return false;
194371
+ try {
194372
+ const help = (0, import_node_child_process13.execFileSync)("setpriv", ["--help"], { stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString();
194373
+ return help.includes("--pdeathsig");
194374
+ } catch {
194375
+ return false;
194376
+ }
194377
+ }
194378
+ function pdeathsigWrap(bin, args) {
194379
+ return { bin: "setpriv", args: ["--pdeathsig", "SIGKILL", "--", bin, ...args] };
194380
+ }
194381
+ function shouldBindRuntimePdeathsig() {
194382
+ return process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] === "1" && pdeathsigAvailable();
194383
+ }
194384
+ var import_node_child_process13, os6, cachedAvailable;
194385
+ var init_pdeathsig = __esm({
194386
+ "../adapters/src/_core/pdeathsig.ts"() {
194387
+ "use strict";
194388
+ import_node_child_process13 = require("node:child_process");
194389
+ os6 = __toESM(require("node:os"), 1);
194390
+ }
194391
+ });
194392
+
194317
194393
  // ../adapters/src/_core/subprocess.ts
194318
194394
  async function materialize(job, cfg) {
194319
194395
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
@@ -194416,11 +194492,12 @@ async function runStreamJson(job, cfg) {
194416
194492
  ...cfg.workRoot !== void 0 ? { workRoot: cfg.workRoot } : {},
194417
194493
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
194418
194494
  });
194419
- const child = containerized ? (0, import_node_child_process13.spawn)(containerized.bin, containerized.args, {
194495
+ const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
194496
+ const child = containerized ? (0, import_node_child_process14.spawn)(containerized.bin, containerized.args, {
194420
194497
  cwd: dir,
194421
194498
  env: scrubLeakyEnv(process.env),
194422
194499
  stdio: ["ignore", "pipe", "pipe"]
194423
- }) : (0, import_node_child_process13.spawn)(cfg.bin, args, {
194500
+ }) : (0, import_node_child_process14.spawn)(direct.bin, direct.args, {
194424
194501
  cwd: dir,
194425
194502
  env: buildEnv(job, cfg),
194426
194503
  stdio: ["ignore", "pipe", "pipe"]
@@ -194525,11 +194602,12 @@ async function runOneShotText(job, cfg) {
194525
194602
  ...cfg.workRoot !== void 0 ? { workRoot: cfg.workRoot } : {},
194526
194603
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
194527
194604
  });
194528
- const child = containerized ? (0, import_node_child_process13.spawn)(containerized.bin, containerized.args, {
194605
+ const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
194606
+ const child = containerized ? (0, import_node_child_process14.spawn)(containerized.bin, containerized.args, {
194529
194607
  cwd: dir,
194530
194608
  env: scrubLeakyEnv(process.env),
194531
194609
  stdio: ["ignore", "pipe", "pipe"]
194532
- }) : (0, import_node_child_process13.spawn)(cfg.bin, args, {
194610
+ }) : (0, import_node_child_process14.spawn)(direct.bin, direct.args, {
194533
194611
  cwd: dir,
194534
194612
  env: buildEnv(job, cfg),
194535
194613
  stdio: ["ignore", "pipe", "pipe"]
@@ -194608,11 +194686,11 @@ async function runOneShotText(job, cfg) {
194608
194686
  }
194609
194687
  });
194610
194688
  }
194611
- var import_node_child_process13, import_node_crypto29, fs14, path16, readline4;
194689
+ var import_node_child_process14, import_node_crypto29, fs14, path16, readline4;
194612
194690
  var init_subprocess = __esm({
194613
194691
  "../adapters/src/_core/subprocess.ts"() {
194614
194692
  "use strict";
194615
- import_node_child_process13 = require("node:child_process");
194693
+ import_node_child_process14 = require("node:child_process");
194616
194694
  import_node_crypto29 = require("node:crypto");
194617
194695
  fs14 = __toESM(require("node:fs"), 1);
194618
194696
  path16 = __toESM(require("node:path"), 1);
@@ -194625,6 +194703,7 @@ var init_subprocess = __esm({
194625
194703
  init_transcript();
194626
194704
  init_filter_extra_args();
194627
194705
  init_root_bypass();
194706
+ init_pdeathsig();
194628
194707
  }
194629
194708
  });
194630
194709
 
@@ -198164,6 +198243,22 @@ async function startOasisServer(opts) {
198164
198243
  return;
198165
198244
  }
198166
198245
  const fromSeq = Math.max(0, Number(url.searchParams.get("fromSeq") ?? "0") || 0);
198246
+ const reopenRecoveringTurn = async () => {
198247
+ if (!opts.chatTurns) return;
198248
+ const active = await opts.chatTurns.activeTurn(sid).catch(() => null);
198249
+ if (!active || active.status !== "running") return;
198250
+ liveChat.reopenForRecovery(sid, {
198251
+ runtimeSessionId: cs.runtimeSessionId ?? active.dispatchId ?? "",
198252
+ ...active.assistantRunId ? { runId: active.assistantRunId } : {},
198253
+ onStop: () => {
198254
+ void opts.chatTurns?.settleTurn(active.id, {
198255
+ status: "cancelled",
198256
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
198257
+ lastError: `\u7528\u6237\u5728 serve \u91CD\u542F\u6062\u590D\u7A97\u53E3\u5185\u505C\u6B62\uFF08\u8F6E ${active.id}\uFF09`
198258
+ }).catch(() => void 0);
198259
+ }
198260
+ });
198261
+ };
198167
198262
  const liveProtocol = url.searchParams.get("protocol") === "v2" || wantsLiveProtocol(req);
198168
198263
  if (liveProtocol) {
198169
198264
  const sub2 = (frame) => {
@@ -198172,7 +198267,11 @@ async function startOasisServer(opts) {
198172
198267
  `);
198173
198268
  if (frame.operation === "turn_completed" || frame.operation === "turn_failed") res.end();
198174
198269
  };
198175
- const att2 = liveChat.attachLive(sid, fromSeq, sub2);
198270
+ let att2 = liveChat.attachLive(sid, fromSeq, sub2);
198271
+ if (!att2) {
198272
+ await reopenRecoveringTurn();
198273
+ att2 = liveChat.attachLive(sid, fromSeq, sub2);
198274
+ }
198176
198275
  if (!att2) {
198177
198276
  res.writeHead(204).end();
198178
198277
  return;
@@ -198199,9 +198298,10 @@ async function startOasisServer(opts) {
198199
198298
  res.end();
198200
198299
  return;
198201
198300
  }
198301
+ const attached2 = att2;
198202
198302
  res.on("close", () => {
198203
198303
  stopKa2();
198204
- att2.detach();
198304
+ attached2.detach();
198205
198305
  });
198206
198306
  return;
198207
198307
  }
@@ -198211,7 +198311,11 @@ async function startOasisServer(opts) {
198211
198311
  `);
198212
198312
  if (part.type === "done") res.end();
198213
198313
  };
198214
- const att = liveChat.attach(sid, fromSeq, sub);
198314
+ let att = liveChat.attach(sid, fromSeq, sub);
198315
+ if (!att) {
198316
+ await reopenRecoveringTurn();
198317
+ att = liveChat.attach(sid, fromSeq, sub);
198318
+ }
198215
198319
  if (!att) {
198216
198320
  res.writeHead(204).end();
198217
198321
  return;
@@ -198237,9 +198341,10 @@ async function startOasisServer(opts) {
198237
198341
  res.end();
198238
198342
  return;
198239
198343
  }
198344
+ const attached = att;
198240
198345
  res.on("close", () => {
198241
198346
  stopKa();
198242
- att.detach();
198347
+ attached.detach();
198243
198348
  });
198244
198349
  return;
198245
198350
  }
@@ -205206,6 +205311,7 @@ async function sweepOrphanRuns(deps) {
205206
205311
  const inFlight = await deps.inFlightRunIds();
205207
205312
  const aliveArtifacts = await deps.aliveArtifactIds?.() ?? /* @__PURE__ */ new Set();
205208
205313
  const apiActive = await deps.apiActiveDispatchIds?.() ?? /* @__PURE__ */ new Set();
205314
+ const liveSessions = await deps.liveSessionRunIds?.() ?? /* @__PURE__ */ new Set();
205209
205315
  const orphaned = [];
205210
205316
  const cappedOut = [];
205211
205317
  const rescued = [];
@@ -205231,7 +205337,7 @@ async function sweepOrphanRuns(deps) {
205231
205337
  }
205232
205338
  continue;
205233
205339
  }
205234
- const claimed = inFlight.has(run.id) || run.artifactId !== null && aliveArtifacts.has(run.artifactId) || apiActive.has(run.id);
205340
+ const claimed = inFlight.has(run.id) || run.artifactId !== null && aliveArtifacts.has(run.artifactId) || apiActive.has(run.id) || liveSessions.has(run.id);
205235
205341
  if (!inFlight.has(run.id) && !(run.artifactId !== null && aliveArtifacts.has(run.artifactId)) && apiActive.has(run.id) && ageMs > deps.orphanGraceMs) {
205236
205342
  rescued.push(run.id);
205237
205343
  deps.log?.(
@@ -205268,7 +205374,7 @@ async function sweepDroppedDispatches(deps) {
205268
205374
  const result = {
205269
205375
  scanned: 0,
205270
205376
  redispatched: [],
205271
- skipped: { inFlight: 0, ledgerOpen: 0, backoff: 0, notAgent: 0, failed: 0 }
205377
+ skipped: { inFlight: 0, ledgerOpen: 0, backoff: 0, notAgent: 0, blocked: 0, failed: 0 }
205272
205378
  };
205273
205379
  const createdBefore = new Date(deps.now - deps.graceMs).toISOString();
205274
205380
  const candidates = await deps.listNeverStarted(createdBefore, CANDIDATE_LIMIT);
@@ -205301,6 +205407,10 @@ async function sweepDroppedDispatches(deps) {
205301
205407
  continue;
205302
205408
  }
205303
205409
  try {
205410
+ if (deps.beforeRedispatch && !await deps.beforeRedispatch(c)) {
205411
+ result.skipped.blocked++;
205412
+ continue;
205413
+ }
205304
205414
  await deps.redispatch(c.workId, c.workorderId);
205305
205415
  result.redispatched.push(c.workId);
205306
205416
  } catch {
@@ -205354,6 +205464,35 @@ var init_dispatch_mapping_rebuild = __esm({
205354
205464
  }
205355
205465
  });
205356
205466
 
205467
+ // ../server/src/dispatch-binding-diagnosis.ts
205468
+ async function prepareDroppedDispatch(work, deps) {
205469
+ const { kernel } = deps;
205470
+ await kernel.refreshModel?.();
205471
+ const node2 = kernel.model.artifacts.get(work.nodeId);
205472
+ const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(work.workorderId));
205473
+ const current = snap?.works.find((w2) => w2.id === work.workId);
205474
+ if (!snap || !current || current.status !== "running" || current.startedAt || current.sessionRef || current.endedAt || !snap.nodes.some((n) => n.id === work.nodeId && n.latestWorkId === current.id && !n.cancelledAt) || !node2 || node2.workspace !== work.workorderId || lifecycleOf(kernel.model, node2.id) !== "active" || kernel.model.pausedWorkorders.has(work.workorderId) || isHeld(kernel.model, node2.id)) return false;
205475
+ if (snap.issues.some((i) => i.kind === "escalation" && i.aboutNodeId === node2.id && !i.resolvedAt && i.intent !== "advisory")) return false;
205476
+ if (!work.assigneeActorId) return false;
205477
+ const binding = await deps.bindingFor(work.assigneeActorId);
205478
+ if (!binding?.nodeId) return true;
205479
+ const runtimeNode = await deps.nodeFor(binding.nodeId);
205480
+ if (runtimeNode && runtimeNode.status !== "deleted") return true;
205481
+ await kernel.escalate({
205482
+ artifactId: node2.id,
205483
+ actor: SYSTEM_ACTOR,
205484
+ source: "dispatch:missing-node",
205485
+ reason: `\u8FD9\u4E00\u6B65\u8FD8\u6CA1\u6709\u542F\u52A8\uFF1A\u6267\u884C\u4EBA\u7684\u8FD0\u884C\u65F6\u7ED1\u5B9A\u6307\u5411${runtimeNode ? "\u5DF2\u5220\u9664" : "\u672A\u6CE8\u518C"}\u7684\u8282\u70B9 ${binding.nodeId}\u3002\u8BF7\u6062\u590D\u8BE5\u8282\u70B9\uFF0C\u6216\u4E3A\u6267\u884C\u4EBA ${work.assigneeActorId} \u9009\u62E9\u5DF2\u6709\u4E14\u914D\u7F6E\u6B63\u786E\u7684\u8282\u70B9\uFF1B\u4FEE\u590D\u540E\u5173\u95ED\u8FD9\u6761\u4E0A\u62A5\uFF0C\u6D41\u7A0B\u624D\u4F1A\u7EE7\u7EED\u3002\u672C\u6B21\u68C0\u67E5\u6CA1\u6709\u7EC8\u6B62\u4F1A\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u6D88\u8017 work \u91CD\u8BD5\u6B21\u6570\u3002`
205486
+ });
205487
+ return false;
205488
+ }
205489
+ var init_dispatch_binding_diagnosis = __esm({
205490
+ "../server/src/dispatch-binding-diagnosis.ts"() {
205491
+ "use strict";
205492
+ init_src5();
205493
+ }
205494
+ });
205495
+
205357
205496
  // ../server/src/zero-output-breaker.ts
205358
205497
  function absorb(b2, r, startedMs) {
205359
205498
  b2.count += 1;
@@ -205878,14 +206017,14 @@ function classifyDeployRun(facts) {
205878
206017
  if (facts.anyContainingInProgress) return { kind: "running", note: "\u627F\u8F7D\u672C\u6B21\u4EA4\u4ED8\u7684\u90E8\u7F72 run \u4ECD\u5728\u8FDB\u884C" };
205879
206018
  return { kind: "pending", note: "\u8FD8\u6CA1\u770B\u5230\u627F\u8F7D\u672C\u6B21\u4EA4\u4ED8\u7684\u90E8\u7F72 run" };
205880
206019
  }
205881
- var import_node_child_process14, import_node_util5, execFile5, GhDeployProvider;
206020
+ var import_node_child_process15, import_node_util5, execFile5, GhDeployProvider;
205882
206021
  var init_deploy_provider = __esm({
205883
206022
  "../server/src/git/deploy-provider.ts"() {
205884
206023
  "use strict";
205885
- import_node_child_process14 = require("node:child_process");
206024
+ import_node_child_process15 = require("node:child_process");
205886
206025
  import_node_util5 = require("node:util");
205887
206026
  init_remote_util();
205888
- execFile5 = (0, import_node_util5.promisify)(import_node_child_process14.execFile);
206027
+ execFile5 = (0, import_node_util5.promisify)(import_node_child_process15.execFile);
205889
206028
  GhDeployProvider = class {
205890
206029
  constructor(opts = {}) {
205891
206030
  this.opts = opts;
@@ -206121,14 +206260,14 @@ function makeDefaultBranchResolver(opts) {
206121
206260
  }
206122
206261
  };
206123
206262
  }
206124
- var import_node_child_process15, import_node_util6, execFile6;
206263
+ var import_node_child_process16, import_node_util6, execFile6;
206125
206264
  var init_default_branch = __esm({
206126
206265
  "../server/src/git/default-branch.ts"() {
206127
206266
  "use strict";
206128
- import_node_child_process15 = require("node:child_process");
206267
+ import_node_child_process16 = require("node:child_process");
206129
206268
  import_node_util6 = require("node:util");
206130
206269
  init_remote_util();
206131
- execFile6 = (0, import_node_util6.promisify)(import_node_child_process15.execFile);
206270
+ execFile6 = (0, import_node_util6.promisify)(import_node_child_process16.execFile);
206132
206271
  }
206133
206272
  });
206134
206273
 
@@ -207830,6 +207969,22 @@ var init_daemon_hub = __esm({
207830
207969
  hasSession(dispatchId) {
207831
207970
  return this.sessionSockets.get(dispatchId)?.readyState === import_websocket.default.OPEN;
207832
207971
  }
207972
+ /**
207973
+ * 当前持有**活着数据面连接**的全部 dispatchId(ADR-0300 D8:hub 有活连接即认领)。
207974
+ *
207975
+ * orphan-sweep 的认领来源此前只有 dispatcher 在途表、chat 在途表、节点 hello、HTTP 活性——
207976
+ * 独立会话进程(协调者、D2′ 后搬出 daemon 的会话)**不在这四者里**,但它们自己握着一根到 hub 的
207977
+ * 数据面 WS。这些会话 `run.id === dispatchId`(协调者:`job.dispatchId = coordinate:*`),
207978
+ * 于是「hub 此刻还连着它」就是最直接的判活。不接这条线,7 天里 183 条协调者会话被误判孤儿闭合
207979
+ * (ADR-0300 证据 E)。只增不减:命中只能**阻止**误闭合,永不会导致误闭合。
207980
+ */
207981
+ liveSessionDispatchIds() {
207982
+ const ids2 = /* @__PURE__ */ new Set();
207983
+ for (const [dispatchId, ws] of this.sessionSockets) {
207984
+ if (ws.readyState === import_websocket.default.OPEN) ids2.add(dispatchId);
207985
+ }
207986
+ return ids2;
207987
+ }
207833
207988
  /** 往会话进程直投一帧(kill / append_input / ping)。返回是否写进了 OPEN 的 socket。 */
207834
207989
  sendToSession(dispatchId, msg) {
207835
207990
  const ws = this.sessionSockets.get(dispatchId);
@@ -214668,6 +214823,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
214668
214823
  if (!isOwnerRecipient && !isLeadRecipient) continue;
214669
214824
  const gaps = unresolvedGapsOf(model, a.id);
214670
214825
  const escalations = unresolvedEscalationsOf(model, a.id);
214826
+ const representedEscalations = /* @__PURE__ */ new Set();
214671
214827
  const who = isOwnerRecipient ? "\uFF08\u4F60\u662F\u5DE5\u5355\u5F52\u5C5E\u4EBA\uFF09" : "\uFF08\u4F60\u662F\u5361\u4F4F\u8282\u70B9 owner \u7684\u4E0A\u7EA7\uFF09";
214672
214828
  for (const gap of gaps) {
214673
214829
  const byGapId = escalations.filter((entry) => entry.gapId === gap.gapId);
@@ -214681,6 +214837,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
214681
214837
  const exactEscalation = byGapId.at(-1) ?? (samePartGaps.length === 1 ? samePartEscalations.at(-1) : void 0) ?? (gaps.length === 1 ? partlessEscalations.at(-1) : void 0);
214682
214838
  const gapSince = exactEscalation?.at ?? gap.lastReportedAt ?? model.lastOpAt.get(a.id) ?? "";
214683
214839
  const gapReportCount = gap.reportCount ?? 1;
214840
+ if (exactEscalation) representedEscalations.add(exactEscalation.escalationId);
214684
214841
  gapItems.push({
214685
214842
  kind: "gap-escalation",
214686
214843
  artifactId: a.id,
@@ -214709,8 +214866,8 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
214709
214866
  }
214710
214867
  });
214711
214868
  }
214712
- if (gaps.length === 0 && escalations.length > 0) {
214713
- for (const e of escalations) {
214869
+ if (escalations.length > 0) {
214870
+ for (const e of escalations.filter((e2) => !representedEscalations.has(e2.escalationId))) {
214714
214871
  gapItems.push({
214715
214872
  kind: "gap-escalation",
214716
214873
  artifactId: a.id,
@@ -214961,6 +215118,152 @@ var init_inbox = __esm({
214961
215118
  }
214962
215119
  });
214963
215120
 
215121
+ // ../server/src/domains/collab/escalation-attention.ts
215122
+ function escalationDeliveryConfig(env) {
215123
+ const positive = (key, fallback) => {
215124
+ if (!env[key]) return fallback;
215125
+ const n = Number(env[key]);
215126
+ if (!Number.isFinite(n) || n <= 0) throw new Error(`${key} must be a positive number`);
215127
+ return n;
215128
+ };
215129
+ const policy = {
215130
+ reminderMs: positive("OASIS_ESCALATION_REMINDER_MS", DEFAULT_ESCALATION_REMINDERS.reminderMs),
215131
+ acknowledgedReminderMs: positive("OASIS_ESCALATION_ACK_REMINDER_MS", DEFAULT_ESCALATION_REMINDERS.acknowledgedReminderMs)
215132
+ };
215133
+ const webhooks = /* @__PURE__ */ new Map();
215134
+ if (env.OASIS_ESCALATION_WEBHOOKS) {
215135
+ try {
215136
+ const entries = JSON.parse(env.OASIS_ESCALATION_WEBHOOKS);
215137
+ if (!entries || Array.isArray(entries) || typeof entries !== "object") throw new Error();
215138
+ for (const [companyId, endpoint] of Object.entries(entries)) {
215139
+ const url = new URL(endpoint.url);
215140
+ if (!companyId || !["https:", "http:"].includes(url.protocol) || url.username || url.password || endpoint.token !== void 0 && typeof endpoint.token !== "string") throw new Error();
215141
+ webhooks.set(companyId, endpoint);
215142
+ }
215143
+ } catch {
215144
+ throw new Error("OASIS_ESCALATION_WEBHOOKS must map company IDs to { url, token? } endpoints");
215145
+ }
215146
+ }
215147
+ return { policy, webhooks };
215148
+ }
215149
+ function withEscalationAttention(items, companyId, markers, now, policy = DEFAULT_ESCALATION_REMINDERS) {
215150
+ const byScope = new Map(markers.map((m2) => [m2.scope, m2.readAt]));
215151
+ return items.map((item) => {
215152
+ if (item.kind !== "gap-escalation" || !item.escalationId) return item;
215153
+ const start = Date.parse(item.since);
215154
+ if (!Number.isFinite(start)) return item;
215155
+ const ackAt = byScope.get(escalationAttentionScope(companyId, item.escalationId, "ack"));
215156
+ const ack = ackAt ? Date.parse(ackAt) : NaN;
215157
+ const acknowledged = Number.isFinite(ack) && ack >= start;
215158
+ const firstDue = acknowledged ? ack + policy.acknowledgedReminderMs : start;
215159
+ const due = now >= firstDue;
215160
+ const step = Math.max(0, Math.floor((now - firstDue) / policy.reminderMs));
215161
+ const dueAt = new Date(firstDue + step * policy.reminderMs).toISOString();
215162
+ const deliveredAt = byScope.get(escalationAttentionScope(companyId, item.escalationId, "webhook"));
215163
+ return { ...item, attention: {
215164
+ reminderKey: `${item.escalationId}:${dueAt}`,
215165
+ dueAt,
215166
+ needsAcknowledgement: due,
215167
+ ...acknowledged ? { acknowledgedAt: ackAt } : {},
215168
+ ...deliveredAt ? { deliveredAt } : {}
215169
+ } };
215170
+ });
215171
+ }
215172
+ async function sendEscalationWebhook(url, token, batch, item, deliveryId) {
215173
+ const response = await fetch(url, {
215174
+ method: "POST",
215175
+ redirect: "error",
215176
+ signal: AbortSignal.timeout(1e4),
215177
+ headers: { "content-type": "application/json", ...token ? { authorization: `Bearer ${token}` } : {} },
215178
+ body: JSON.stringify({ deliveryId, companyId: batch.companyId, recipientActorId: batch.actorId, item })
215179
+ });
215180
+ await response.body?.cancel();
215181
+ if (!response.ok) throw new Error(`Delivery HTTP ${response.status}`);
215182
+ }
215183
+ var escalationAttentionScope, DEFAULT_ESCALATION_REMINDERS, EscalationDeliveryWorker;
215184
+ var init_escalation_attention = __esm({
215185
+ "../server/src/domains/collab/escalation-attention.ts"() {
215186
+ "use strict";
215187
+ escalationAttentionScope = (companyId, issueId, kind) => `escalation-${kind}:${JSON.stringify([companyId, issueId])}`;
215188
+ DEFAULT_ESCALATION_REMINDERS = {
215189
+ reminderMs: 15 * 6e4,
215190
+ acknowledgedReminderMs: 4 * 60 * 6e4
215191
+ };
215192
+ EscalationDeliveryWorker = class {
215193
+ constructor(deps) {
215194
+ this.deps = deps;
215195
+ }
215196
+ running = false;
215197
+ stopped = false;
215198
+ cursor = "";
215199
+ /** Already-started transport is bounded by its timeout; do not start new deliveries during shutdown. */
215200
+ stop() {
215201
+ this.stopped = true;
215202
+ }
215203
+ async tick() {
215204
+ if (this.running || this.stopped) return;
215205
+ this.running = true;
215206
+ try {
215207
+ const pending = [];
215208
+ let attempts = 0;
215209
+ for (const batch of await this.deps.batches()) {
215210
+ if (this.stopped) return;
215211
+ const markers = await this.deps.markers.listMarkers(batch.actorId);
215212
+ const items = withEscalationAttention(
215213
+ batch.items,
215214
+ batch.companyId,
215215
+ markers,
215216
+ (this.deps.now ?? Date.now)(),
215217
+ this.deps.policy
215218
+ );
215219
+ for (const item of items) {
215220
+ const attention2 = item.attention;
215221
+ if (!item.escalationId || !attention2?.needsAcknowledgement) continue;
215222
+ if (attention2.deliveredAt && Date.parse(attention2.deliveredAt) >= Date.parse(attention2.dueAt)) continue;
215223
+ pending.push({ batch, item, key: JSON.stringify([batch.companyId, batch.actorId, item.escalationId]) });
215224
+ }
215225
+ }
215226
+ pending.sort((a, b2) => a.key < b2.key ? -1 : a.key > b2.key ? 1 : 0);
215227
+ const afterCursor = pending.findIndex((p2) => p2.key > this.cursor);
215228
+ const start = afterCursor < 0 ? 0 : afterCursor;
215229
+ for (let offset = 0; offset < pending.length; offset++) {
215230
+ if (this.stopped) break;
215231
+ if (attempts++ >= (this.deps.maxPerTick ?? 20)) break;
215232
+ const { batch, item: queued, key } = pending[(start + offset) % pending.length];
215233
+ this.cursor = key;
215234
+ try {
215235
+ const current = this.deps.currentItem ? await this.deps.currentItem(batch, queued.escalationId) : queued;
215236
+ if (!current) continue;
215237
+ const item = withEscalationAttention(
215238
+ [current],
215239
+ batch.companyId,
215240
+ await this.deps.markers.listMarkers(batch.actorId),
215241
+ (this.deps.now ?? Date.now)(),
215242
+ this.deps.policy
215243
+ )[0];
215244
+ const attention2 = item.attention;
215245
+ if (this.stopped || !attention2.needsAcknowledgement || attention2.deliveredAt && Date.parse(attention2.deliveredAt) >= Date.parse(attention2.dueAt)) continue;
215246
+ const id = JSON.stringify([batch.companyId, batch.actorId, attention2.reminderKey]);
215247
+ await this.deps.send(batch, item, id);
215248
+ await this.deps.markers.markRead(
215249
+ batch.actorId,
215250
+ escalationAttentionScope(batch.companyId, item.escalationId, "webhook"),
215251
+ attention2.dueAt
215252
+ );
215253
+ } catch {
215254
+ this.deps.log(`[escalation-delivery] delivery failed; retained for retry (${key})`);
215255
+ }
215256
+ }
215257
+ } catch {
215258
+ this.deps.log("[escalation-delivery] scan failed; will retry");
215259
+ } finally {
215260
+ this.running = false;
215261
+ }
215262
+ }
215263
+ };
215264
+ }
215265
+ });
215266
+
214964
215267
  // ../server/src/domains/collab/inbox-chat.ts
214965
215268
  function mergeInbox(kernelItems, chatItems) {
214966
215269
  if (chatItems.length === 0) return kernelItems;
@@ -216408,7 +216711,23 @@ function collabDomain(opts) {
216408
216711
  const { reviews, works } = collectWaitingRows(snaps, me, policy);
216409
216712
  waitingItems = buildWaitingInbox(reviews, works, policy, (/* @__PURE__ */ new Date()).toISOString(), me);
216410
216713
  }
216411
- return { status: 200, body: { items: mergeInbox(kernelItems, [...chatItems, ...waitingItems]) } };
216714
+ const items = mergeInbox(kernelItems, [...chatItems, ...waitingItems]);
216715
+ return { status: 200, body: { items: opts.readMarkers ? withEscalationAttention(items, req.auth.companyId ?? opts.defaultCompanyId ?? "", markers, Date.now(), opts.escalationReminders) : items } };
216716
+ });
216717
+ router.post("/api/inbox/escalations/:id/acknowledge", async (req) => {
216718
+ if (!opts.readMarkers) return { status: 503, body: { error: { code: "not_configured", message: "\u63A5\u624B\u8BB0\u5F55\u672A\u88C5\u914D" } } };
216719
+ const { kernel, registry: registry2 } = await resolveCtx(req.auth.companyId);
216720
+ await kernel.refreshModel?.();
216721
+ const leadOf = await buildLeadResolver(registry2);
216722
+ const item = buildInbox(kernel.model, req.auth.actor, leadOf).find((i) => i.escalationId === req.params.id);
216723
+ if (!item?.escalationId) return { status: 404, body: { error: { code: "not_found", message: "\u5F85\u529E\u4E0D\u5B58\u5728\u6216\u4E0D\u5C5E\u4E8E\u4F60" } } };
216724
+ const acknowledgedAt = (/* @__PURE__ */ new Date()).toISOString();
216725
+ await opts.readMarkers.markRead(
216726
+ req.auth.actor,
216727
+ escalationAttentionScope(req.auth.companyId ?? opts.defaultCompanyId ?? "", item.escalationId, "ack"),
216728
+ acknowledgedAt
216729
+ );
216730
+ return { status: 200, body: { acknowledgedAt } };
216412
216731
  });
216413
216732
  const companyOf = (req) => req.auth.companyId ?? "company:default";
216414
216733
  const noTagStore = { status: 501, body: { error: { code: "not_implemented", message: "\u672C\u90E8\u7F72\u672A\u542F\u7528\u5DE5\u5355\u6807\u7B7E" } } };
@@ -216647,6 +216966,7 @@ var init_collab = __esm({
216647
216966
  init_workorders();
216648
216967
  init_workorder_detail();
216649
216968
  init_inbox();
216969
+ init_escalation_attention();
216650
216970
  init_inbox_chat();
216651
216971
  init_waiting_inbox();
216652
216972
  init_src3();
@@ -216660,6 +216980,7 @@ var init_collab = __esm({
216660
216980
  init_workorder_detail();
216661
216981
  init_workorder_metrics();
216662
216982
  init_inbox();
216983
+ init_escalation_attention();
216663
216984
  init_letter_read_backfill();
216664
216985
  init_audit();
216665
216986
  init_dashboard();
@@ -216686,7 +217007,7 @@ function prepareReadonlyGitBundles(input) {
216686
217007
  if (repos.length === 0) return null;
216687
217008
  const targets = collectTargets(repos, input.pinnedRevisions);
216688
217009
  if (targets.length === 0) return null;
216689
- const bundleRoot = input.bundleRoot ?? os6.tmpdir();
217010
+ const bundleRoot = input.bundleRoot ?? os7.tmpdir();
216690
217011
  fs23.mkdirSync(bundleRoot, { recursive: true });
216691
217012
  const root = fs23.mkdtempSync(path24.join(bundleRoot, "oasis-git-bundles-"));
216692
217013
  const files = {};
@@ -216833,7 +217154,7 @@ function createBundle(root, target, input) {
216833
217154
  function verifyBundleReadable(dir, bundlePath, _bundleRef, commit, expectedTree, env) {
216834
217155
  fs23.rmSync(dir, { recursive: true, force: true });
216835
217156
  try {
216836
- (0, import_node_child_process16.execFileSync)("git", ["clone", bundlePath, dir], { stdio: "ignore", ...env ? { env } : {} });
217157
+ (0, import_node_child_process17.execFileSync)("git", ["clone", bundlePath, dir], { stdio: "ignore", ...env ? { env } : {} });
216837
217158
  git2(dir, ["cat-file", "-e", `${commit}^{commit}`], env, "COMMIT_NOT_FOUND");
216838
217159
  const head = git2(dir, ["rev-parse", "HEAD"], env, "COMMIT_NOT_FOUND").trim();
216839
217160
  if (head.toLowerCase() !== commit.toLowerCase()) {
@@ -216875,11 +217196,11 @@ function ensureSourceRepo(work, repo, commit, env) {
216875
217196
  function gitInitBare(dir, env) {
216876
217197
  if (fs23.existsSync(path24.join(dir, "objects"))) return;
216877
217198
  fs23.mkdirSync(dir, { recursive: true });
216878
- (0, import_node_child_process16.execFileSync)("git", ["init", "--bare", dir], { stdio: "ignore", ...env ? { env } : {} });
217199
+ (0, import_node_child_process17.execFileSync)("git", ["init", "--bare", dir], { stdio: "ignore", ...env ? { env } : {} });
216879
217200
  }
216880
217201
  function git2(cwd, args, env, fallback, timeout) {
216881
217202
  try {
216882
- return (0, import_node_child_process16.execFileSync)("git", ["-C", cwd, ...args], {
217203
+ return (0, import_node_child_process17.execFileSync)("git", ["-C", cwd, ...args], {
216883
217204
  encoding: "utf8",
216884
217205
  stdio: ["ignore", "pipe", "pipe"],
216885
217206
  ...timeout ? { timeout } : {},
@@ -216903,14 +217224,14 @@ function classifyGitError(fallback, err, where) {
216903
217224
  function oneLine(msg) {
216904
217225
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
216905
217226
  }
216906
- var import_node_child_process16, import_node_crypto45, fs23, os6, path24, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
217227
+ var import_node_child_process17, import_node_crypto45, fs23, os7, path24, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
216907
217228
  var init_readonly_bundle = __esm({
216908
217229
  "../server/src/git/readonly-bundle.ts"() {
216909
217230
  "use strict";
216910
- import_node_child_process16 = require("node:child_process");
217231
+ import_node_child_process17 = require("node:child_process");
216911
217232
  import_node_crypto45 = require("node:crypto");
216912
217233
  fs23 = __toESM(require("node:fs"), 1);
216913
- os6 = __toESM(require("node:os"), 1);
217234
+ os7 = __toESM(require("node:os"), 1);
216914
217235
  path24 = __toESM(require("node:path"), 1);
216915
217236
  init_remote_util();
216916
217237
  GitReadonlyBundleError = class extends Error {
@@ -219417,7 +219738,9 @@ function createChatSessionsDomain(opts) {
219417
219738
  router.get("/api/chat-sessions/:id/turns/active", async (req) => {
219418
219739
  if (!opts.chatTurns) throw new ApiError(404, "NOT_FOUND", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C");
219419
219740
  const session = await store.getSession(req.params.id);
219420
- if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
219741
+ if (!session || session.humanActorId !== req.auth.actor && session.aiActorId !== req.auth.actor) {
219742
+ throw new ApiError(404, "NOT_FOUND", "session not found");
219743
+ }
219421
219744
  const turn = await opts.chatTurns.activeTurn(req.params.id);
219422
219745
  return { status: 200, body: { turn } };
219423
219746
  });
@@ -224127,11 +224450,11 @@ var init_engine = __esm({
224127
224450
  constructor(table = /* @__PURE__ */ new Map()) {
224128
224451
  this.table = table;
224129
224452
  }
224130
- set(platform, platformUserId, actor) {
224131
- this.table.set(`${platform}:${platformUserId}`, actor);
224453
+ set(platform2, platformUserId, actor) {
224454
+ this.table.set(`${platform2}:${platformUserId}`, actor);
224132
224455
  }
224133
- resolve(platform, platformUserId) {
224134
- return this.table.get(`${platform}:${platformUserId}`) ?? null;
224456
+ resolve(platform2, platformUserId) {
224457
+ return this.table.get(`${platform2}:${platformUserId}`) ?? null;
224135
224458
  }
224136
224459
  };
224137
224460
  }
@@ -224670,7 +224993,7 @@ ${HIGH_RISK_COMMANDS}`;
224670
224993
  const downstreams = consumersOf(m2, p2.id).map((a) => a.id);
224671
224994
  const task = [
224672
224995
  `## \u672C\u6B21\u4E3A\u4EC0\u4E48\u5524\u8D77\u4F60`,
224673
- `\u8282\u70B9 **${p2.id}**${brief ? `\uFF08${brief}\uFF09` : ""} \u5DF2\u963B\u585E\u7EA6 **${mins} \u5206\u949F**\u6CA1\u8FDB\u5C55\u3002\u7CFB\u7EDF\u5DF2\u673A\u68B0\u8BCA\u65AD\u3001\u786E\u8BA4**\u8FD9\u662F\u56FE\u75C5\u6216\u673A\u68B0\u5B9A\u4F4D\u4E0D\u5230\u7684\u6B8B\u5DEE**\uFF08\u8D44\u6E90 / \u4EBA / \u8FD0\u884C\u65F6\u7C7B\u75C5\u56E0\u5DF2\u6392\u9664\uFF09\u3002`,
224996
+ `\u8282\u70B9 **${p2.id}**${brief ? `\uFF08${brief}\uFF09` : ""} \u5DF2\u963B\u585E\u7EA6 **${mins} \u5206\u949F**\u6CA1\u8FDB\u5C55\u3002\u673A\u68B0\u8BCA\u65AD\u5C06\u5176\u5F52\u5165**\u56FE\u75C5\u6216\u5C1A\u672A\u5B9A\u4F4D\u7684\u6B8B\u5DEE**\u3002\u672A\u53D1\u73B0\u5DF2\u77E5\u8FD0\u884C\u65F6\u4FE1\u53F7\u4E0D\u7B49\u4E8E\u6392\u9664\u4E86\u8FD0\u884C\u65F6\u6545\u969C\uFF1B\u6CA1\u6709\u5728\u8DD1\u4F1A\u8BDD\u65F6\uFF0C\u8BF7\u5148\u6838\u5BF9\u7ED1\u5B9A\u3001\u8282\u70B9\u6CE8\u518C\u72B6\u6001\u548C\u6D3E\u53D1\u9001\u8FBE\u60C5\u51B5\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u6539\u56FE\u3002`,
224674
224997
  ``,
224675
224998
  `## \u75C5\u5386\uFF08\u673A\u68B0\u8BCA\u65AD\uFF1B\u5148\u8BFB\u8FD9\u4E2A\uFF09`,
224676
224999
  caseFile,
@@ -225801,6 +226124,7 @@ var init_src10 = __esm({
225801
226124
  init_orphan_sweep();
225802
226125
  init_dispatch_drop_sweep();
225803
226126
  init_dispatch_mapping_rebuild();
226127
+ init_dispatch_binding_diagnosis();
225804
226128
  init_zero_output_breaker();
225805
226129
  init_dispatch_exit_reconcile();
225806
226130
  init_session_exit_outcome();
@@ -225839,6 +226163,7 @@ var init_src10 = __esm({
225839
226163
  init_node_store();
225840
226164
  init_tokens();
225841
226165
  init_collab();
226166
+ init_collab();
225842
226167
  init_closure_report();
225843
226168
  init_workorders();
225844
226169
  init_workorder_detail();
@@ -233741,22 +234066,22 @@ var init_trajectory = __esm({
233741
234066
 
233742
234067
  // src/index.ts
233743
234068
  var fs42 = __toESM(require("node:fs"));
233744
- var os13 = __toESM(require("node:os"));
234069
+ var os14 = __toESM(require("node:os"));
233745
234070
  var path39 = __toESM(require("node:path"));
233746
- var import_node_child_process22 = require("node:child_process");
234071
+ var import_node_child_process23 = require("node:child_process");
233747
234072
  var import_node_crypto66 = require("node:crypto");
233748
234073
 
233749
234074
  // ../cli/src/cli.ts
233750
234075
  var fs37 = __toESM(require("node:fs"), 1);
233751
- var os11 = __toESM(require("node:os"), 1);
234076
+ var os12 = __toESM(require("node:os"), 1);
233752
234077
  var path34 = __toESM(require("node:path"), 1);
233753
234078
  var import_node_crypto64 = require("node:crypto");
233754
234079
 
233755
234080
  // ../cli/src/serve.ts
233756
234081
  var fs31 = __toESM(require("node:fs"), 1);
233757
- var os7 = __toESM(require("node:os"), 1);
234082
+ var os8 = __toESM(require("node:os"), 1);
233758
234083
  var path28 = __toESM(require("node:path"), 1);
233759
- var import_node_child_process17 = require("node:child_process");
234084
+ var import_node_child_process18 = require("node:child_process");
233760
234085
  var import_node_crypto60 = require("node:crypto");
233761
234086
  var import_node_url7 = require("node:url");
233762
234087
 
@@ -234333,8 +234658,10 @@ var SESSION_TOKEN_GRACE_MS = 15 * 6e4;
234333
234658
  function makeResumeRetryProxy(first, spawnFallback) {
234334
234659
  const outCbs = [];
234335
234660
  const telCbs = [];
234661
+ const liveCbs = [];
234336
234662
  const outBuf = [];
234337
234663
  const telBuf = [];
234664
+ const liveBuf = [];
234338
234665
  let sawOutput = false;
234339
234666
  let active = first;
234340
234667
  const fanOut = (chunk) => {
@@ -234345,12 +234672,17 @@ function makeResumeRetryProxy(first, spawnFallback) {
234345
234672
  if (telCbs.length) for (const cb of telCbs) cb(e);
234346
234673
  else telBuf.push(e);
234347
234674
  };
234675
+ const fanLive = (e) => {
234676
+ if (liveCbs.length) for (const cb of liveCbs) cb(e);
234677
+ else liveBuf.push(e);
234678
+ };
234348
234679
  const wire = (s2) => {
234349
234680
  s2.onOutput((c) => {
234350
234681
  sawOutput = true;
234351
234682
  fanOut(c);
234352
234683
  });
234353
234684
  s2.onTelemetry?.((e) => fanTel(e));
234685
+ s2.onLiveEvent?.((e) => fanLive(e));
234354
234686
  };
234355
234687
  wire(first);
234356
234688
  const isSessionNotFound2 = (err) => {
@@ -234382,6 +234714,10 @@ function makeResumeRetryProxy(first, spawnFallback) {
234382
234714
  for (const e of telBuf.splice(0)) cb(e);
234383
234715
  telCbs.push(cb);
234384
234716
  },
234717
+ onLiveEvent(cb) {
234718
+ for (const e of liveBuf.splice(0)) cb(e);
234719
+ liveCbs.push(cb);
234720
+ },
234385
234721
  done,
234386
234722
  kill: () => active.kill?.() ?? Promise.resolve(),
234387
234723
  // ADR-0093 chat 道:插入能力必须穿过这层代理,否则「带 sessionId 的 resume」这条**常路径**
@@ -234392,6 +234728,11 @@ function makeResumeRetryProxy(first, spawnFallback) {
234392
234728
  get canAppendInput() {
234393
234729
  return active.canAppendInput !== false && typeof active.appendInput === "function";
234394
234730
  },
234731
+ // supportsLiveProtocol 恒读 active(兜底重派后 active 换成 second):不能按 first 快照,否则回退会话的
234732
+ // 能力位会指向已死的首次尝试。
234733
+ get supportsLiveProtocol() {
234734
+ return active.supportsLiveProtocol === true;
234735
+ },
234395
234736
  get nativeSessionId() {
234396
234737
  return active.nativeSessionId;
234397
234738
  }
@@ -234452,15 +234793,15 @@ function oasisWrapperScript() {
234452
234793
  const main = path28.join(repoRoot, "packages/cli/src/main.ts");
234453
234794
  const key = (0, import_node_crypto60.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
234454
234795
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
234455
- const stable = path28.join(os7.tmpdir(), `oasis-cli-wrapper-${uid}-${key}`);
234796
+ const stable = path28.join(os8.tmpdir(), `oasis-cli-wrapper-${uid}-${key}`);
234456
234797
  let dir;
234457
234798
  try {
234458
234799
  fs31.mkdirSync(stable, { recursive: true, mode: 448 });
234459
234800
  const st = fs31.lstatSync(stable);
234460
234801
  const mine = st.isDirectory() && !st.isSymbolicLink() && (typeof process.getuid !== "function" || st.uid === process.getuid());
234461
- dir = mine ? stable : fs31.mkdtempSync(path28.join(os7.tmpdir(), "oasis-cli-"));
234802
+ dir = mine ? stable : fs31.mkdtempSync(path28.join(os8.tmpdir(), "oasis-cli-"));
234462
234803
  } catch {
234463
- dir = fs31.mkdtempSync(path28.join(os7.tmpdir(), "oasis-cli-"));
234804
+ dir = fs31.mkdtempSync(path28.join(os8.tmpdir(), "oasis-cli-"));
234464
234805
  }
234465
234806
  const wrapper = path28.join(dir, "oasis");
234466
234807
  fs31.writeFileSync(wrapper, `#!/usr/bin/env bash
@@ -234538,7 +234879,7 @@ async function runDeterministicKnowledgeLint(service, config2) {
234538
234879
  if (!wrapper) throw new Error(`\u8FDE\u63A5\u5668 ${config2.connectorSlug} \u5C1A\u672A\u914D\u7F6E\u6216 wrapper \u4E0D\u53EF\u7528`);
234539
234880
  const args = [...connector.knowledgeLintArgs({ libraryRef: config2.libraryRef, rootRef: config2.rootRef })];
234540
234881
  const result = await new Promise((resolveResult) => {
234541
- (0, import_node_child_process17.execFile)(wrapper, args, {
234882
+ (0, import_node_child_process18.execFile)(wrapper, args, {
234542
234883
  env: { ...process.env, ...provision.env },
234543
234884
  timeout: 12e4,
234544
234885
  maxBuffer: 10 * 1024 * 1024
@@ -234808,7 +235149,7 @@ async function startServe(opts) {
234808
235149
  );
234809
235150
  const issuer = createTokenIssuer({ secretFile: path28.join(opts.dir, "session-token-secret") });
234810
235151
  const apiActivity = createApiActivityLedger();
234811
- const operatorActor = `actor:human:${os7.userInfo().username}`;
235152
+ const operatorActor = `actor:human:${os8.userInfo().username}`;
234812
235153
  const operatorToken = issuer.issue(operatorActor);
234813
235154
  fs31.writeFileSync(path28.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
234814
235155
  let nodeTokens = createNodeTokenStore(path28.join(opts.dir, "node-tokens.json"));
@@ -234929,6 +235270,7 @@ async function startServe(opts) {
234929
235270
  let chatSessionStore;
234930
235271
  let chatTurnStore;
234931
235272
  let readMarkerStore;
235273
+ const escalationDelivery = escalationDeliveryConfig(process.env);
234932
235274
  if (pgDsn && pgPool) {
234933
235275
  projectStateStore = await PostgresProjectStateStore.open(pgPool, pgSchema);
234934
235276
  artifactStateStore = await PostgresArtifactStateStore.open(pgPool, pgSchema);
@@ -235963,6 +236305,8 @@ async function startServe(opts) {
235963
236305
  chatSession: chatSessionStore,
235964
236306
  // ADR-0086:chat 知会条目(与 readMarkers 成对)
235965
236307
  readMarkers: readMarkerStore,
236308
+ escalationReminders: escalationDelivery.policy,
236309
+ defaultCompanyId,
235966
236310
  // 工单标签词表(组织级受控、旁账,与 playbook-overrides 同款侧存)。
235967
236311
  tags: workorderTagStore,
235968
236312
  ...opts.sla ? { sla: opts.sla } : {},
@@ -237504,6 +237848,8 @@ async function startServe(opts) {
237504
237848
  ledgerWarn("cancel(map-miss)")(err);
237505
237849
  }
237506
237850
  },
237851
+ // Inbox is the notification contract (ADR 0090). Escalation delivery reconciles persisted Inbox
237852
+ // items below; it must not depend on this one-shot effect or its legacy recipient list.
237507
237853
  async notify(_actorIds, _payload) {
237508
237854
  }
237509
237855
  };
@@ -237524,6 +237870,14 @@ async function startServe(opts) {
237524
237870
  return live;
237525
237871
  },
237526
237872
  isAgent: async (actorId) => await executorKindOf(actorId) === "agent",
237873
+ beforeRedispatch: async (work) => {
237874
+ return prepareDroppedDispatch(work, {
237875
+ kernel: newKernel,
237876
+ // This sweep belongs to the default engine; legacy workorder.companyId is often empty.
237877
+ bindingFor: async (actorId) => (await actors.resolveCtx(defaultCompanyId)).service.resolveBinding()(actorId),
237878
+ nodeFor: (nodeId) => nodeStore.getNode(nodeId)
237879
+ });
237880
+ },
237527
237881
  // resumeEligible=false:补派一律起**全新会话**。引擎那次派发的取值是节点状态的函数
237528
237882
  //(activate.ts:`latestAcceptId == null && !lastProposedRejected`),本模块拿不到,
237529
237883
  // 而两个方向的代价不对称——该续没续只是少一点上下文,不该续却续了就把返工要隔离的
@@ -238371,7 +238725,45 @@ async function startServe(opts) {
238371
238725
  return { fused, starving, stuck, recovered };
238372
238726
  }
238373
238727
  const slaEscalating = /* @__PURE__ */ new Set();
238728
+ const escalationDeliveryWorker = new EscalationDeliveryWorker({
238729
+ markers: readMarkerStore,
238730
+ policy: escalationDelivery.policy,
238731
+ batches: async () => {
238732
+ const batches = [];
238733
+ for (const companyId of escalationDelivery.webhooks.keys()) {
238734
+ const companyEngine = await getEngine(companyId);
238735
+ await companyEngine.kernel.refreshModel();
238736
+ const leadOf2 = await buildLeadResolver(companyEngine.registry);
238737
+ const recipients = /* @__PURE__ */ new Set();
238738
+ for (const node2 of companyEngine.kernel.model.artifacts.values()) {
238739
+ recipients.add(node2.owner);
238740
+ const lead = leadOf2?.(node2.owner);
238741
+ if (lead) recipients.add(lead);
238742
+ }
238743
+ for (const actorId of recipients) {
238744
+ const items = buildInbox(companyEngine.kernel.model, actorId, leadOf2).filter((item) => item.escalationId && item.artifactId && lifecycleOf(companyEngine.kernel.model, item.artifactId) === "active");
238745
+ if (items.length) batches.push({ companyId, actorId, items });
238746
+ }
238747
+ }
238748
+ return batches;
238749
+ },
238750
+ currentItem: async (batch, issueId) => {
238751
+ const companyEngine = await getEngine(batch.companyId);
238752
+ await companyEngine.kernel.refreshModel();
238753
+ const leadOf2 = await buildLeadResolver(companyEngine.registry);
238754
+ return buildInbox(companyEngine.kernel.model, batch.actorId, leadOf2).find((item) => item.escalationId === issueId && item.artifactId && lifecycleOf(companyEngine.kernel.model, item.artifactId) === "active");
238755
+ },
238756
+ send: async (batch, item, deliveryId) => {
238757
+ const endpoint = escalationDelivery.webhooks.get(batch.companyId);
238758
+ await sendEscalationWebhook(endpoint.url, endpoint.token, batch, item, deliveryId);
238759
+ },
238760
+ log: (message) => console.warn(message)
238761
+ });
238762
+ if (!escalationDelivery.webhooks.size) {
238763
+ console.warn("[escalation-delivery] \u7AD9\u5916\u6E20\u9053\u672A\u914D\u7F6E\uFF1A\u5347\u7EA7\u5C06\u901A\u8FC7 Inbox \u5F39\u7A97\u548C\u50AC\u529E\u63D0\u9192\uFF1B\u6D4F\u89C8\u5668\u5173\u95ED\u65F6\u65E0\u6CD5\u7AD9\u5916\u9001\u8FBE\u3002\u914D\u7F6E OASIS_ESCALATION_WEBHOOKS \u53EF\u63A5\u90AE\u4EF6/IM \u7F51\u5173\u3002");
238764
+ }
238374
238765
  const slaTimer = setInterval(() => {
238766
+ void escalationDeliveryWorker.tick();
238375
238767
  void (async () => {
238376
238768
  const now = Date.now();
238377
238769
  for (const a of kernel.model.artifacts.values()) {
@@ -238683,6 +239075,7 @@ ${nodeFault}` : "");
238683
239075
  for (const c of chatLiveSessions.values()) ids2.add(c.sessionId);
238684
239076
  return ids2;
238685
239077
  };
239078
+ const liveSessionRunIds = () => hub?.liveSessionDispatchIds() ?? /* @__PURE__ */ new Set();
238686
239079
  const aliveArtifactIds = () => {
238687
239080
  const ids2 = /* @__PURE__ */ new Set();
238688
239081
  for (const d of hub?.connectedDaemons() ?? []) {
@@ -238713,6 +239106,8 @@ ${nodeFault}` : "");
238713
239106
  aliveArtifactIds,
238714
239107
  apiActiveDispatchIds,
238715
239108
  // D1(ADR-0078):判活第二条线
239109
+ liveSessionRunIds,
239110
+ // ADR-0300 D8:hub 活数据面连接(协调者/独立会话进程)
238716
239111
  closeRun,
238717
239112
  now: Date.now(),
238718
239113
  orphanGraceMs,
@@ -238892,6 +239287,7 @@ ${nodeFault}` : "");
238892
239287
  close: async () => {
238893
239288
  for (const timer of dispatchTimers) clearInterval(timer);
238894
239289
  clearInterval(slaTimer);
239290
+ escalationDeliveryWorker.stop();
238895
239291
  if (mirrorTimer) clearInterval(mirrorTimer);
238896
239292
  if (gitTimer) clearInterval(gitTimer);
238897
239293
  if (coordTimer) clearInterval(coordTimer);
@@ -238991,7 +239387,7 @@ function log2(tag, msg, extra) {
238991
239387
  }
238992
239388
 
238993
239389
  // ../cli/src/daemon/session-lock.ts
238994
- var import_node_child_process18 = require("node:child_process");
239390
+ var import_node_child_process19 = require("node:child_process");
238995
239391
  var fs32 = __toESM(require("node:fs"), 1);
238996
239392
  var path29 = __toESM(require("node:path"), 1);
238997
239393
  var flockAvailable = null;
@@ -238999,7 +239395,7 @@ function hasFlock() {
238999
239395
  if (flockAvailable !== null) return flockAvailable;
239000
239396
  if (process.platform !== "linux") return flockAvailable = false;
239001
239397
  try {
239002
- const r = (0, import_node_child_process18.spawnSync)("flock", ["--version"], { stdio: "ignore", timeout: 3e3 });
239398
+ const r = (0, import_node_child_process19.spawnSync)("flock", ["--version"], { stdio: "ignore", timeout: 3e3 });
239003
239399
  flockAvailable = r.status === 0;
239004
239400
  } catch {
239005
239401
  flockAvailable = false;
@@ -239014,7 +239410,7 @@ function acquireSessionLock(lockPath) {
239014
239410
  fs32.unlinkSync(readyFile);
239015
239411
  } catch {
239016
239412
  }
239017
- const child = (0, import_node_child_process18.spawn)("flock", ["-n", lockPath, "-c", `printf ok > '${readyFile}'; exec cat`], {
239413
+ const child = (0, import_node_child_process19.spawn)("flock", ["-n", lockPath, "-c", `printf ok > '${readyFile}'; exec cat`], {
239018
239414
  stdio: ["pipe", "ignore", "ignore"],
239019
239415
  detached: true
239020
239416
  });
@@ -239025,7 +239421,7 @@ function acquireSessionLock(lockPath) {
239025
239421
  ready = true;
239026
239422
  break;
239027
239423
  }
239028
- (0, import_node_child_process18.spawnSync)("sleep", ["0.02"], { stdio: "ignore" });
239424
+ (0, import_node_child_process19.spawnSync)("sleep", ["0.02"], { stdio: "ignore" });
239029
239425
  }
239030
239426
  try {
239031
239427
  fs32.unlinkSync(readyFile);
@@ -239056,7 +239452,7 @@ function isSessionLocked(lockPath) {
239056
239452
  if (!hasFlock()) return false;
239057
239453
  if (!fs32.existsSync(lockPath)) return false;
239058
239454
  try {
239059
- const r = (0, import_node_child_process18.spawnSync)("flock", ["-n", lockPath, "-c", "true"], { stdio: "ignore", timeout: 3e3 });
239455
+ const r = (0, import_node_child_process19.spawnSync)("flock", ["-n", lockPath, "-c", "true"], { stdio: "ignore", timeout: 3e3 });
239060
239456
  return r.status !== 0;
239061
239457
  } catch {
239062
239458
  return true;
@@ -239132,6 +239528,7 @@ var RECONNECT_MIN_MS = 1e3;
239132
239528
  var RECONNECT_MAX_MS = 3e4;
239133
239529
  var EXIT_ACK_WAIT_MS = LINK_WINDOWS.exitAckWaitMs;
239134
239530
  async function runSessionProcess(specFile, adapter) {
239531
+ process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] = "1";
239135
239532
  const spec = readSpec(specFile);
239136
239533
  const { dispatchId, job, auth, sessionDir } = spec;
239137
239534
  const lock = acquireSessionLock(path30.join(sessionDir, "lock"));
@@ -239395,7 +239792,7 @@ init_src8();
239395
239792
  init_src7();
239396
239793
 
239397
239794
  // ../cli/src/daemon/detect-adapters.ts
239398
- var import_node_child_process19 = require("node:child_process");
239795
+ var import_node_child_process20 = require("node:child_process");
239399
239796
  init_src7();
239400
239797
  var KNOWN2 = [
239401
239798
  { adapter: "claude-code", binary: "claude" },
@@ -239419,7 +239816,7 @@ var LIVE_PROTOCOL_RUNTIMES = /* @__PURE__ */ new Set(["claude-code"]);
239419
239816
  function resolveOnPath2(binary) {
239420
239817
  try {
239421
239818
  const cmd = process.platform === "win32" ? "where" : "which";
239422
- const out = (0, import_node_child_process19.execFileSync)(cmd, [binary], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
239819
+ const out = (0, import_node_child_process20.execFileSync)(cmd, [binary], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
239423
239820
  const first = out.split("\n")[0]?.trim();
239424
239821
  return first || binary;
239425
239822
  } catch {
@@ -239428,7 +239825,7 @@ function resolveOnPath2(binary) {
239428
239825
  }
239429
239826
  function versionOf(binary) {
239430
239827
  try {
239431
- return (0, import_node_child_process19.execFileSync)(binary, ["--version"], {
239828
+ return (0, import_node_child_process20.execFileSync)(binary, ["--version"], {
239432
239829
  encoding: "utf8",
239433
239830
  stdio: ["ignore", "pipe", "ignore"],
239434
239831
  timeout: 1500
@@ -240384,7 +240781,7 @@ async function startNode(opts) {
240384
240781
  }
240385
240782
 
240386
240783
  // ../cli/src/exec.ts
240387
- var os10 = __toESM(require("node:os"), 1);
240784
+ var os11 = __toESM(require("node:os"), 1);
240388
240785
  var UnsafeExecArgvError = class extends Error {
240389
240786
  code = "ERR_UNSAFE_EXEC_ARGV";
240390
240787
  constructor(message) {
@@ -240421,7 +240818,7 @@ function planExec(opts) {
240421
240818
  function computeExitCode(code2, signal) {
240422
240819
  if (code2 !== null) return code2;
240423
240820
  if (signal) {
240424
- const num2 = os10.constants.signals[signal];
240821
+ const num2 = os11.constants.signals[signal];
240425
240822
  return 128 + (typeof num2 === "number" ? num2 : 1);
240426
240823
  }
240427
240824
  return 1;
@@ -240445,7 +240842,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
240445
240842
  }
240446
240843
 
240447
240844
  // ../cli/src/daemon/machine-id.ts
240448
- var import_node_child_process20 = require("node:child_process");
240845
+ var import_node_child_process21 = require("node:child_process");
240449
240846
  var import_node_fs20 = require("node:fs");
240450
240847
  var import_node_crypto62 = require("node:crypto");
240451
240848
  var import_node_os12 = require("node:os");
@@ -240461,7 +240858,7 @@ function linuxMachineId() {
240461
240858
  }
240462
240859
  function macMachineId() {
240463
240860
  try {
240464
- const out = (0, import_node_child_process20.execFileSync)("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
240861
+ const out = (0, import_node_child_process21.execFileSync)("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
240465
240862
  encoding: "utf8",
240466
240863
  timeout: 2e3,
240467
240864
  stdio: ["ignore", "pipe", "ignore"]
@@ -240474,7 +240871,7 @@ function macMachineId() {
240474
240871
  }
240475
240872
  function windowsMachineId() {
240476
240873
  try {
240477
- const out = (0, import_node_child_process20.execFileSync)("reg", [
240874
+ const out = (0, import_node_child_process21.execFileSync)("reg", [
240478
240875
  "query",
240479
240876
  "HKLM\\SOFTWARE\\Microsoft\\Cryptography",
240480
240877
  "/v",
@@ -242184,7 +242581,7 @@ function ownFlagsFromDecl(command) {
242184
242581
 
242185
242582
  // ../cli/src/connector-effect-runner.ts
242186
242583
  var import_node_crypto63 = require("node:crypto");
242187
- var import_node_child_process21 = require("node:child_process");
242584
+ var import_node_child_process22 = require("node:child_process");
242188
242585
  init_src8();
242189
242586
  function stableKey(effect) {
242190
242587
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
@@ -242243,7 +242640,7 @@ function productionApi() {
242243
242640
  }
242244
242641
  function execute(bin, args) {
242245
242642
  return new Promise((resolve10, reject) => {
242246
- const child = (0, import_node_child_process21.spawn)(bin, args, { stdio: "inherit", env: process.env });
242643
+ const child = (0, import_node_child_process22.spawn)(bin, args, { stdio: "inherit", env: process.env });
242247
242644
  child.once("error", reject);
242248
242645
  child.once("exit", (code2, signal) => resolve10(code2 ?? (signal ? 128 : 1)));
242249
242646
  });
@@ -242673,7 +243070,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
242673
243070
  const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
242674
243071
  method: "POST",
242675
243072
  headers: { "content-type": "application/json" },
242676
- body: JSON.stringify({ enrollToken, nodeId, hostname: os11.hostname() })
243073
+ body: JSON.stringify({ enrollToken, nodeId, hostname: os12.hostname() })
242677
243074
  });
242678
243075
  if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
242679
243076
  const body2 = await res.json();
@@ -242706,7 +243103,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
242706
243103
  const binPath = path34.join(prefix, "bin", "oasis_test");
242707
243104
  const svcArgs = `node --server-ws ${serverUrl} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
242708
243105
  if (process.platform === "linux") {
242709
- const svcFile = path34.join(os11.homedir(), ".config/systemd/user/oasis-node.service");
243106
+ const svcFile = path34.join(os12.homedir(), ".config/systemd/user/oasis-node.service");
242710
243107
  fs37.mkdirSync(path34.dirname(svcFile), { recursive: true });
242711
243108
  if (spawnSync3("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
242712
243109
  spawnSync3("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
@@ -242719,7 +243116,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
242719
243116
  `ExecStart=${binPath} ${svcArgs}`,
242720
243117
  // Carry the install-time PATH so the detached daemon detects CLIs in user dirs
242721
243118
  // (~/.npm-global/bin, ~/.local/bin) — systemd --user otherwise gives a minimal PATH.
242722
- `Environment=PATH=${process.env["PATH"] ?? ""}:${path34.join(os11.homedir(), ".npm-global/bin")}:${path34.join(os11.homedir(), ".local/bin")}:/usr/local/bin`,
243119
+ `Environment=PATH=${process.env["PATH"] ?? ""}:${path34.join(os12.homedir(), ".npm-global/bin")}:${path34.join(os12.homedir(), ".local/bin")}:/usr/local/bin`,
242723
243120
  "Restart=on-failure",
242724
243121
  "RestartSec=5",
242725
243122
  "StandardOutput=journal",
@@ -242731,23 +243128,23 @@ async function runCli(argv, println = console.log, progressln = console.error) {
242731
243128
  ].join("\n"));
242732
243129
  spawnSync3("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
242733
243130
  spawnSync3("systemctl", ["--user", "enable", "--now", "oasis-node"], { stdio: "inherit" });
242734
- spawnSync3("loginctl", ["enable-linger", os11.userInfo().username], { stdio: "ignore" });
243131
+ spawnSync3("loginctl", ["enable-linger", os12.userInfo().username], { stdio: "ignore" });
242735
243132
  println("\u2713 Oasis node service started (systemd --user oasis-node)");
242736
243133
  println(" Status : systemctl --user status oasis-node");
242737
243134
  println(" Logs : journalctl --user -u oasis-node -f");
242738
243135
  println(" Stop : systemctl --user stop oasis-node");
242739
243136
  } else if (process.platform === "darwin") {
242740
- const plist = path34.join(os11.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
243137
+ const plist = path34.join(os12.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
242741
243138
  fs37.mkdirSync(path34.dirname(plist), { recursive: true });
242742
243139
  spawnSync3("launchctl", ["unload", plist], { stdio: "ignore" });
242743
243140
  const argXml = [binPath, "node", "--server-ws", serverUrl, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
242744
- fs37.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os11.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os11.homedir()}/.oasis-node.err</string></dict></plist>`);
243141
+ fs37.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os12.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os12.homedir()}/.oasis-node.err</string></dict></plist>`);
242745
243142
  spawnSync3("launchctl", ["load", plist], { stdio: "inherit" });
242746
243143
  println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
242747
- println(` Logs : tail -f ${os11.homedir()}/.oasis-node.log`);
243144
+ println(` Logs : tail -f ${os12.homedir()}/.oasis-node.log`);
242748
243145
  println(` Stop : launchctl unload ${plist}`);
242749
243146
  } else {
242750
- spawnSync3("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${path34.join(os11.homedir(), ".oasis-node.log")} 2>&1 &`], { stdio: "inherit" });
243147
+ spawnSync3("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${path34.join(os12.homedir(), ".oasis-node.log")} 2>&1 &`], { stdio: "inherit" });
242751
243148
  println(`\u2713 Oasis node started in background (nohup). Logs: ~/.oasis-node.log`);
242752
243149
  println(" Note: no automatic restart on reboot in this environment.");
242753
243150
  }
@@ -242846,8 +243243,8 @@ async function runCli(argv, println = console.log, progressln = console.error) {
242846
243243
  if (command === "serve" && positional[0] === "install") {
242847
243244
  const absDir = path34.resolve(dir);
242848
243245
  const name = `oasis-serve-${path34.basename(absDir)}`;
242849
- const unitDir = flags.get("unit-dir") ?? path34.join(os11.homedir(), ".config", "systemd", "user");
242850
- const bin = flags.get("bin") ?? path34.join(os11.homedir(), ".local", "bin", "oasis");
243246
+ const unitDir = flags.get("unit-dir") ?? path34.join(os12.homedir(), ".config", "systemd", "user");
243247
+ const bin = flags.get("bin") ?? path34.join(os12.homedir(), ".local", "bin", "oasis");
242851
243248
  const port = flags.get("port") ?? "7320";
242852
243249
  const extra = (flags.get("dispatch") === "true" ? ` --dispatch true` : "") + (flags.has("model") ? ` --model ${flags.get("model")}` : "");
242853
243250
  const unit = [
@@ -243191,7 +243588,7 @@ Trace: ${run.traceRunId}`);
243191
243588
  if (plan.inject.mode === "env") {
243192
243589
  childEnv[plan.inject.envName] = value2;
243193
243590
  } else {
243194
- tmpDir = await fs37.promises.mkdtemp(path34.join(os11.tmpdir(), "oasis-var-exec-"));
243591
+ tmpDir = await fs37.promises.mkdtemp(path34.join(os12.tmpdir(), "oasis-var-exec-"));
243195
243592
  const keyfile = path34.join(tmpDir, "value");
243196
243593
  await fs37.promises.writeFile(keyfile, value2, { mode: 384 });
243197
243594
  childEnv[plan.inject.fileEnv] = keyfile;
@@ -243965,7 +244362,7 @@ ${res.warning}`);
243965
244362
  for (const a of st.artifacts) println(` - ${a.path}\uFF08${a.kind ?? "?"}\uFF09status=${a.status ?? "?"}${a.mtime ? ` \xB7 ${new Date(a.mtime).toISOString()}` : ""}`);
243966
244363
  if (flags.get("pull") === "true") {
243967
244364
  const odPublic2 = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
243968
- const { dir: dir3, entry: entry2 } = await harvestBoard(odBase2, fetch, odPublic2, os11.tmpdir(), st.boardKey, st.runs[0]?.runId ?? "status-pull");
244365
+ const { dir: dir3, entry: entry2 } = await harvestBoard(odBase2, fetch, odPublic2, os12.tmpdir(), st.boardKey, st.runs[0]?.runId ?? "status-pull");
243969
244366
  println(`[design] \u5DF2\u62C9\u5230\u672C\u5730\uFF1A${dir3}\uFF08\u5165\u53E3 ${entry2}\uFF09`);
243970
244367
  println(`[design] \u8BFB\u5B8C\u6EE1\u610F\u5373\u4EA4\u7A3F\uFF1Aoasis propose ${artifactId2} --from-dir ${dir3} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"`);
243971
244368
  } else {
@@ -245335,10 +245732,10 @@ function runtimeAssetSourceRoots(input) {
245335
245732
 
245336
245733
  // src/paths.ts
245337
245734
  var fs40 = __toESM(require("node:fs"));
245338
- var os12 = __toESM(require("node:os"));
245735
+ var os13 = __toESM(require("node:os"));
245339
245736
  var path37 = __toESM(require("node:path"));
245340
245737
  var DEFAULT_INSTANCE = "default";
245341
- var home = () => os12.homedir();
245738
+ var home = () => os13.homedir();
245342
245739
  var oasisDir = () => path37.join(home(), ".oasis");
245343
245740
  var versionsDir = () => path37.join(oasisDir(), "versions");
245344
245741
  var versionDir = (label) => path37.join(versionsDir(), label);
@@ -245537,7 +245934,7 @@ function shimScript() {
245537
245934
  }
245538
245935
 
245539
245936
  // src/index.ts
245540
- var PKG_VERSION = true ? "0.1.137" : "dev";
245937
+ var PKG_VERSION = true ? "0.1.138" : "dev";
245541
245938
  var LOCAL_BIN = localBin();
245542
245939
  var NPM_PREFIX = npmPrefix();
245543
245940
  var INSTANCE = DEFAULT_INSTANCE;
@@ -245637,7 +246034,7 @@ function installSoftware(label) {
245637
246034
  function setupAutostart(name) {
245638
246035
  const inst = instancePaths(name);
245639
246036
  const cmd = `${process.execPath} "${inst.binFile}" --_daemon --instance ${name}`;
245640
- const home2 = os13.homedir();
246037
+ const home2 = os14.homedir();
245641
246038
  const pnpmHome = process.env["PNPM_HOME"] || path39.join(home2, ".local", "share", "pnpm");
245642
246039
  const extraBins = [
245643
246040
  path39.join(home2, ".npm-global", "bin"),
@@ -245647,7 +246044,7 @@ function setupAutostart(name) {
245647
246044
  ];
245648
246045
  const daemonPath = sanitizeDaemonPath([process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":"));
245649
246046
  if (process.platform === "linux") {
245650
- const unitDir = path39.join(os13.homedir(), ".config", "systemd", "user");
246047
+ const unitDir = path39.join(os14.homedir(), ".config", "systemd", "user");
245651
246048
  fs42.mkdirSync(unitDir, { recursive: true });
245652
246049
  const unit = systemdUnitName(name);
245653
246050
  fs42.mkdirSync(path39.dirname(inst.logFile), { recursive: true });
@@ -245660,19 +246057,19 @@ function setupAutostart(name) {
245660
246057
  }));
245661
246058
  if (name === DEFAULT_INSTANCE) {
245662
246059
  try {
245663
- (0, import_node_child_process22.execSync)(`systemctl --user disable --now ${LEGACY_SYSTEMD_UNIT} 2>/dev/null`, { stdio: "ignore" });
246060
+ (0, import_node_child_process23.execSync)(`systemctl --user disable --now ${LEGACY_SYSTEMD_UNIT} 2>/dev/null`, { stdio: "ignore" });
245664
246061
  } catch {
245665
246062
  }
245666
246063
  }
245667
246064
  try {
245668
- (0, import_node_child_process22.execSync)(`systemctl --user daemon-reload && systemctl --user enable --now ${unit}`, { stdio: "ignore" });
246065
+ (0, import_node_child_process23.execSync)(`systemctl --user daemon-reload && systemctl --user enable --now ${unit}`, { stdio: "ignore" });
245669
246066
  return true;
245670
246067
  } catch {
245671
246068
  }
245672
246069
  return false;
245673
246070
  } else if (process.platform === "darwin") {
245674
246071
  const label = launchdLabel(name);
245675
- const plist = path39.join(os13.homedir(), "Library", "LaunchAgents", `${label}.plist`);
246072
+ const plist = path39.join(os14.homedir(), "Library", "LaunchAgents", `${label}.plist`);
245676
246073
  fs42.mkdirSync(path39.dirname(plist), { recursive: true });
245677
246074
  fs42.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
245678
246075
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -245685,7 +246082,7 @@ function setupAutostart(name) {
245685
246082
  <key>StandardErrorPath</key><string>${inst.logFile}</string>
245686
246083
  </dict></plist>`);
245687
246084
  try {
245688
- (0, import_node_child_process22.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
246085
+ (0, import_node_child_process23.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
245689
246086
  return true;
245690
246087
  } catch {
245691
246088
  }
@@ -245696,7 +246093,7 @@ function setupAutostart(name) {
245696
246093
  function spawnDaemon() {
245697
246094
  fs42.mkdirSync(path39.dirname(LOG_FILE), { recursive: true });
245698
246095
  const log3 = fs42.openSync(LOG_FILE, "a");
245699
- const child = (0, import_node_child_process22.spawn)(process.execPath, [INST.binFile, "--_daemon", "--instance", INSTANCE], {
246096
+ const child = (0, import_node_child_process23.spawn)(process.execPath, [INST.binFile, "--_daemon", "--instance", INSTANCE], {
245700
246097
  detached: true,
245701
246098
  stdio: ["ignore", log3, log3],
245702
246099
  // 与 systemd unit 里那条 Environment 等价——detached 兜底路径不能少,否则同一实例在两条
@@ -245721,24 +246118,24 @@ function stopDaemon(name) {
245721
246118
  }
245722
246119
  if (process.platform === "linux") {
245723
246120
  try {
245724
- (0, import_node_child_process22.execSync)(`systemctl --user stop ${systemdUnitName(name)} 2>/dev/null`, { stdio: "ignore" });
246121
+ (0, import_node_child_process23.execSync)(`systemctl --user stop ${systemdUnitName(name)} 2>/dev/null`, { stdio: "ignore" });
245725
246122
  } catch {
245726
246123
  }
245727
246124
  if (name === DEFAULT_INSTANCE) {
245728
246125
  try {
245729
- (0, import_node_child_process22.execSync)(`systemctl --user stop ${LEGACY_SYSTEMD_UNIT} 2>/dev/null`, { stdio: "ignore" });
246126
+ (0, import_node_child_process23.execSync)(`systemctl --user stop ${LEGACY_SYSTEMD_UNIT} 2>/dev/null`, { stdio: "ignore" });
245730
246127
  } catch {
245731
246128
  }
245732
246129
  }
245733
246130
  } else if (process.platform === "darwin") {
245734
246131
  const label = launchdLabel(name);
245735
246132
  try {
245736
- (0, import_node_child_process22.execSync)(`launchctl unload "$HOME/Library/LaunchAgents/${label}.plist" 2>/dev/null`, { stdio: "ignore" });
246133
+ (0, import_node_child_process23.execSync)(`launchctl unload "$HOME/Library/LaunchAgents/${label}.plist" 2>/dev/null`, { stdio: "ignore" });
245737
246134
  } catch {
245738
246135
  }
245739
246136
  if (name === DEFAULT_INSTANCE && label !== LEGACY_LAUNCHD_LABEL) {
245740
246137
  try {
245741
- (0, import_node_child_process22.execSync)(`launchctl unload "$HOME/Library/LaunchAgents/${LEGACY_LAUNCHD_LABEL}.plist" 2>/dev/null`, { stdio: "ignore" });
246138
+ (0, import_node_child_process23.execSync)(`launchctl unload "$HOME/Library/LaunchAgents/${LEGACY_LAUNCHD_LABEL}.plist" 2>/dev/null`, { stdio: "ignore" });
245742
246139
  } catch {
245743
246140
  }
245744
246141
  }
@@ -245757,7 +246154,7 @@ function selfUpdate() {
245757
246154
  const inner = `{ echo "[oasis] self-update \u2192 ${PKG_NAME}@latest (registry ${OFFICIAL}, \u5931\u8D25\u56DE\u843D\u9ED8\u8BA4\u6E90)"; sleep 2; { ${installOfficial} || ${installFallback}; } && ${printVersion} && ${startCmd}; } >> '${LOG_FILE}' 2>&1`;
245758
246155
  const hasSystemd = process.platform === "linux" && (() => {
245759
246156
  try {
245760
- (0, import_node_child_process22.execSync)("systemd-run --user --scope --quiet -- true", { stdio: "ignore" });
246157
+ (0, import_node_child_process23.execSync)("systemd-run --user --scope --quiet -- true", { stdio: "ignore" });
245761
246158
  return true;
245762
246159
  } catch {
245763
246160
  return false;
@@ -245765,13 +246162,13 @@ function selfUpdate() {
245765
246162
  })();
245766
246163
  const log3 = fs42.openSync(LOG_FILE, "a");
245767
246164
  if (hasSystemd) {
245768
- const child = (0, import_node_child_process22.spawn)("systemd-run", ["--user", "--scope", "--quiet", "sh", "-c", inner], {
246165
+ const child = (0, import_node_child_process23.spawn)("systemd-run", ["--user", "--scope", "--quiet", "sh", "-c", inner], {
245769
246166
  detached: true,
245770
246167
  stdio: ["ignore", log3, log3]
245771
246168
  });
245772
246169
  child.unref();
245773
246170
  } else {
245774
- const child = (0, import_node_child_process22.spawn)("sh", ["-c", inner], { detached: true, stdio: ["ignore", log3, log3] });
246171
+ const child = (0, import_node_child_process23.spawn)("sh", ["-c", inner], { detached: true, stdio: ["ignore", log3, log3] });
245775
246172
  child.unref();
245776
246173
  }
245777
246174
  }
@@ -245833,7 +246230,7 @@ async function startOneInstance(o) {
245833
246230
  const res = await fetch(`${httpBase(serverUrl)}/api/nodes/exchange`, {
245834
246231
  method: "POST",
245835
246232
  headers: { "content-type": "application/json" },
245836
- body: JSON.stringify({ enrollToken, nodeId: derivedNodeId, hostname: os13.hostname() })
246233
+ body: JSON.stringify({ enrollToken, nodeId: derivedNodeId, hostname: os14.hostname() })
245837
246234
  });
245838
246235
  if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
245839
246236
  const body2 = await res.json();
@@ -245884,7 +246281,7 @@ function derivedNodeIdFor(name) {
245884
246281
  if (name === DEFAULT_INSTANCE) return resolveNodeId();
245885
246282
  const user = (() => {
245886
246283
  try {
245887
- return os13.userInfo().username;
246284
+ return os14.userInfo().username;
245888
246285
  } catch {
245889
246286
  return process.env["USER"] ?? process.env["USERNAME"] ?? "unknown";
245890
246287
  }
@@ -245894,7 +246291,7 @@ function derivedNodeIdFor(name) {
245894
246291
  function userScopeAvailable() {
245895
246292
  if (process.platform !== "linux") return false;
245896
246293
  try {
245897
- (0, import_node_child_process22.execSync)("systemd-run --user --scope --quiet -- true", { stdio: "ignore" });
246294
+ (0, import_node_child_process23.execSync)("systemd-run --user --scope --quiet -- true", { stdio: "ignore" });
245898
246295
  return true;
245899
246296
  } catch {
245900
246297
  return false;
@@ -245925,11 +246322,11 @@ function spawnSessionProcess(spec) {
245925
246322
  const logFd = fs42.openSync(path39.join(dir, "session.log"), "a");
245926
246323
  const argv = [INST.binFile, "--_session", specFile];
245927
246324
  const env = { ...process.env, OASIS_CLI_HOME: pinnedDir };
245928
- const child = userScopeAvailable() ? (0, import_node_child_process22.spawn)(
246325
+ const child = userScopeAvailable() ? (0, import_node_child_process23.spawn)(
245929
246326
  "systemd-run",
245930
246327
  ["--user", "--scope", "--quiet", `--setenv=OASIS_CLI_HOME=${pinnedDir}`, process.execPath, ...argv],
245931
246328
  { detached: true, stdio: ["ignore", logFd, logFd], env }
245932
- ) : (0, import_node_child_process22.spawn)(process.execPath, argv, { detached: true, stdio: ["ignore", logFd, logFd], env });
246329
+ ) : (0, import_node_child_process23.spawn)(process.execPath, argv, { detached: true, stdio: ["ignore", logFd, logFd], env });
245933
246330
  child.unref();
245934
246331
  return true;
245935
246332
  } catch (e) {