oasis_test 0.1.138 → 0.1.139

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 +78 -11
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -207602,6 +207602,14 @@ var init_daemon_hub = __esm({
207602
207602
  sessionMeta = /* @__PURE__ */ new Map();
207603
207603
  sessionConnectListeners = [];
207604
207604
  sessionDisconnectListeners = [];
207605
+ // ── ADR 0300 D2「日志复制」:帧号 + 累积 ackSeq + 幂等摄入 ────────────────────────────
207606
+ // 只对自报 supportsFrameSeq 的会话生效;老会话这三张表始终为空、逐字走原 outbox 语义。
207607
+ /** 自报支持帧号的 dispatchId。 */
207608
+ frameSeqSupported = /* @__PURE__ */ new Set();
207609
+ /** 每 dispatch 已按序摄入的最高节点帧号(累积水位)。跨重连保留、`session_exited` 后延迟清。 */
207610
+ frameWatermark = /* @__PURE__ */ new Map();
207611
+ /** 每 dispatch 上次回 ackSeq 的时刻——把流式期间的回执节流到 ~250ms 一次。 */
207612
+ lastAckSeqAt = /* @__PURE__ */ new Map();
207605
207613
  /**
207606
207614
  * 「这次派发归谁管、现在什么状态」的认领口。**必须由 hub 汇总,不能各 adapter 自己判**——
207607
207615
  * serve 里同时有两个 DaemonHubAdapter(chat 一个、dispatch 一个),一次派发只属于其中一个。
@@ -207836,12 +207844,33 @@ var init_daemon_hub = __esm({
207836
207844
  for (const l of this.sessionConnectListeners) l({ dispatchId, sessionId: msg.sessionId, nodeId });
207837
207845
  void this.judgeSession(dispatchId, ws);
207838
207846
  }
207847
+ if (msg.supportsFrameSeq) {
207848
+ this.frameSeqSupported.add(dispatchId);
207849
+ this.sendAckSeq(ws, dispatchId, true);
207850
+ }
207839
207851
  return;
207840
207852
  }
207841
207853
  if (!registered) {
207842
207854
  ws.close(1008, "session_hello required first");
207843
207855
  return;
207844
207856
  }
207857
+ if (this.frameSeqSupported.has(dispatchId)) {
207858
+ const seq = typeof msg.seq === "number" ? msg.seq : void 0;
207859
+ if (seq !== void 0) {
207860
+ const wm = this.frameWatermark.get(dispatchId) ?? 0;
207861
+ if (seq <= wm) {
207862
+ this.sendAckSeq(ws, dispatchId, false);
207863
+ return;
207864
+ }
207865
+ this.frameWatermark.set(dispatchId, seq);
207866
+ }
207867
+ for (const listener of this.messageListeners) listener(nodeId, msg);
207868
+ this.onMessage?.(nodeId, msg);
207869
+ const isExit = msg.type === "session_exited";
207870
+ this.sendAckSeq(ws, dispatchId, isExit);
207871
+ if (isExit) this.scheduleFrameStateCleanup(dispatchId);
207872
+ return;
207873
+ }
207845
207874
  for (const listener of this.messageListeners) listener(nodeId, msg);
207846
207875
  this.onMessage?.(nodeId, msg);
207847
207876
  if (msg.type === "session_exited" && ws.readyState === import_websocket.default.OPEN) {
@@ -207857,6 +207886,23 @@ var init_daemon_hub = __esm({
207857
207886
  });
207858
207887
  ws.on("error", (err) => console.error(`[hub] session ${dispatchId} error:`, err.message));
207859
207888
  }
207889
+ /** ADR 0300 D2:回带当前摄入水位的累积回执。流式期间节流到 ~250ms 一次,hello / 收尾帧立即回。 */
207890
+ sendAckSeq(ws, dispatchId, force) {
207891
+ if (ws.readyState !== import_websocket.default.OPEN) return;
207892
+ const now = Date.now();
207893
+ if (!force && now - (this.lastAckSeqAt.get(dispatchId) ?? 0) < 250) return;
207894
+ this.lastAckSeqAt.set(dispatchId, now);
207895
+ ws.send(JSON.stringify({ type: "ack", dispatchId, ackSeq: this.frameWatermark.get(dispatchId) ?? 0 }));
207896
+ }
207897
+ /** `session_exited` 后延迟清帧号状态:给重连重放的收尾帧留一个去重窗口,之后回收避免无界增长。 */
207898
+ scheduleFrameStateCleanup(dispatchId) {
207899
+ const t = setTimeout(() => {
207900
+ this.frameSeqSupported.delete(dispatchId);
207901
+ this.frameWatermark.delete(dispatchId);
207902
+ this.lastAckSeqAt.delete(dispatchId);
207903
+ }, 6e4);
207904
+ t.unref?.();
207905
+ }
207860
207906
  /**
207861
207907
  * 注册一个认领判断。返回:
207862
207908
  * live —— 这次派发在我这儿还活着(别动它)
@@ -239527,6 +239573,8 @@ async function runSession(dispatchId, job, deps) {
239527
239573
  var RECONNECT_MIN_MS = 1e3;
239528
239574
  var RECONNECT_MAX_MS = 3e4;
239529
239575
  var EXIT_ACK_WAIT_MS = LINK_WINDOWS.exitAckWaitMs;
239576
+ var OUTBOX_MAX_FRAMES = 8e3;
239577
+ var isKeyFrame = (m2) => m2.type === "session_started" || m2.type === "session_exited";
239530
239578
  async function runSessionProcess(specFile, adapter) {
239531
239579
  process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] = "1";
239532
239580
  const spec = readSpec(specFile);
@@ -239582,6 +239630,10 @@ var SessionProcessState = class {
239582
239630
  * 而会话其实正在跑。
239583
239631
  */
239584
239632
  outbox = [];
239633
+ /** ADR 0300 D2:每 dispatch 单调帧号,任何副作用之前分配。 */
239634
+ frameSeq = 0;
239635
+ /** 收到过带 `ackSeq` 的 ack = 本服务端会按帧号去重;只有它为真,重连才敢重放**全部**帧(含输出)。 */
239636
+ serverSupportsDedup = false;
239585
239637
  /** append_input 串行链——保证回帧次序 = 到达次序(server 侧按 FIFO 认领等待者)。 */
239586
239638
  appendChain = Promise.resolve();
239587
239639
  async run() {
@@ -239647,9 +239699,12 @@ var SessionProcessState = class {
239647
239699
  daemonVersion: this.spec.daemonVersion,
239648
239700
  // 自报身份要够服务端对上账:chat 道的对账键是 artifactId(run.id=chat-run:* ≠ dispatchId),
239649
239701
  // 只报 dispatchId 的话 serve 重启后它认不出这是条活会话,会当成野进程 evict 掉。
239650
- ...this.spec.job.artifactId ? { jobArtifactId: this.spec.job.artifactId } : {}
239702
+ ...this.spec.job.artifactId ? { jobArtifactId: this.spec.job.artifactId } : {},
239703
+ // ADR 0300 D2:自报支持帧号;新服务端据此回带 ackSeq 的 hello-ack,届时才把 serverSupportsDedup 置真。
239704
+ supportsFrameSeq: true
239651
239705
  });
239652
- for (const m2 of this.outbox) this.rawSend(m2);
239706
+ const toResend = this.serverSupportsDedup ? this.outbox : this.outbox.filter(isKeyFrame);
239707
+ for (const m2 of toResend) this.rawSend(m2);
239653
239708
  });
239654
239709
  ws.on("message", (raw) => {
239655
239710
  let msg;
@@ -239697,8 +239752,14 @@ var SessionProcessState = class {
239697
239752
  }
239698
239753
  case "ack":
239699
239754
  if (msg.dispatchId !== this.spec.dispatchId) return;
239700
- this.outbox.length = 0;
239701
- this.exitAckResolve?.();
239755
+ if (typeof msg.ackSeq === "number") {
239756
+ this.serverSupportsDedup = true;
239757
+ const wm = msg.ackSeq;
239758
+ this.outbox = this.outbox.filter((f2) => (f2.seq ?? 0) > wm);
239759
+ } else {
239760
+ this.outbox.length = 0;
239761
+ }
239762
+ if (this.outbox.length === 0) this.exitAckResolve?.();
239702
239763
  return;
239703
239764
  case "ping":
239704
239765
  this.rawSend({ type: "pong" });
@@ -239713,14 +239774,20 @@ var SessionProcessState = class {
239713
239774
  this.ws.send(JSON.stringify(msg));
239714
239775
  return true;
239715
239776
  }
239716
- /** 必达:先进 outbox 再尽力发;ack 到达才清。 */
239777
+ /** ADR 0300 D2:给帧派单调 seq、进 outbox(超上限丢最老)、再尽力发。返回带 seq 的帧。 */
239778
+ stampAndBuffer(msg) {
239779
+ const framed = { ...msg, seq: ++this.frameSeq };
239780
+ this.outbox.push(framed);
239781
+ if (this.outbox.length > OUTBOX_MAX_FRAMES) this.outbox.splice(0, this.outbox.length - OUTBOX_MAX_FRAMES);
239782
+ return framed;
239783
+ }
239784
+ /** 关键帧(started/exited):必达,进 outbox,ack 到达(按 ackSeq 或整清)才删。 */
239717
239785
  sendKeyFrame(msg) {
239718
- this.outbox.push(msg);
239719
- this.rawSend(msg);
239786
+ this.rawSend(this.stampAndBuffer(msg));
239720
239787
  }
239721
- /** 可丢:连着就发,断着就算了。输出流不值得为它攒内存——真需要补的是必达帧。 */
239788
+ /** 输出/事件帧:ADR 0300 D2 起也进 outbox 带 seq——断线期不再丢;服务端按 ackSeq 累积回执后即从 outbox 排掉。 */
239722
239789
  sendStream(msg) {
239723
- this.rawSend(msg);
239790
+ this.rawSend(this.stampAndBuffer(msg));
239724
239791
  }
239725
239792
  // ── 看门狗(D2′.5 防线 3 + 防线 1 后半)──────────────────────────────────
239726
239793
  startWatchdog() {
@@ -239745,7 +239812,7 @@ var SessionProcessState = class {
239745
239812
  await this.handle?.kill();
239746
239813
  } catch {
239747
239814
  }
239748
- this.rawSend({
239815
+ this.sendKeyFrame({
239749
239816
  type: "session_exited",
239750
239817
  dispatchId: this.spec.dispatchId,
239751
239818
  sessionId: this.handle?.id ?? this.spec.dispatchId,
@@ -245934,7 +246001,7 @@ function shimScript() {
245934
246001
  }
245935
246002
 
245936
246003
  // src/index.ts
245937
- var PKG_VERSION = true ? "0.1.138" : "dev";
246004
+ var PKG_VERSION = true ? "0.1.139" : "dev";
245938
246005
  var LOCAL_BIN = localBin();
245939
246006
  var NPM_PREFIX = npmPrefix();
245940
246007
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.138",
3
+ "version": "0.1.139",
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": "c3de87ec86362ae24eb53a51ba3be9272bd07366"
29
+ "sourceHead": "6e30e49a710877550f9986ad669c0b9ce7de5f74"
30
30
  }
31
31
  }