adhdev 0.8.33 → 0.8.35

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.
package/dist/index.js CHANGED
@@ -9357,8 +9357,6 @@ ${data.message || ""}`.trim();
9357
9357
  if (blockingModal || this.currentStatus === "waiting_approval") {
9358
9358
  throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
9359
9359
  }
9360
- this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
9361
- this.syncMessageViews();
9362
9360
  this.isWaitingForResponse = true;
9363
9361
  this.responseBuffer = "";
9364
9362
  this.finishRetryCount = 0;
@@ -9390,6 +9388,13 @@ ${data.message || ""}`.trim();
9390
9388
  const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
9391
9389
  const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
9392
9390
  const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
9391
+ let didCommitUserTurn = false;
9392
+ const commitUserTurn = () => {
9393
+ if (didCommitUserTurn) return;
9394
+ didCommitUserTurn = true;
9395
+ this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
9396
+ this.syncMessageViews();
9397
+ };
9393
9398
  if (this.settleTimer) {
9394
9399
  clearTimeout(this.settleTimer);
9395
9400
  this.settleTimer = null;
@@ -9402,109 +9407,128 @@ ${data.message || ""}`.trim();
9402
9407
  if (this.isWaitingForResponse) this.finishResponse();
9403
9408
  }, this.timeouts.maxResponse);
9404
9409
  };
9405
- const submit = () => {
9406
- if (!this.ptyProcess) return;
9407
- this.submitPendingUntil = 0;
9408
- this.recordTrace("submit_write", {
9409
- mode: "submit_key",
9410
- sendKey: this.sendKey,
9411
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9412
- });
9413
- this.ptyProcess.write(this.sendKey);
9414
- const retrySubmitIfStuck = (attempt) => {
9415
- this.submitRetryTimer = null;
9416
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9417
- if (this.currentStatus === "waiting_approval") return;
9418
- if ((this.responseBuffer || "").trim()) return;
9410
+ await new Promise((resolve16) => {
9411
+ let resolved = false;
9412
+ const resolveOnce = () => {
9413
+ if (resolved) return;
9414
+ resolved = true;
9415
+ resolve16();
9416
+ };
9417
+ const submit = () => {
9418
+ if (!this.ptyProcess) {
9419
+ resolveOnce();
9420
+ return;
9421
+ }
9422
+ commitUserTurn();
9423
+ this.submitPendingUntil = 0;
9419
9424
  const screenText = this.terminalScreen.getText();
9420
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9421
- if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText)) return;
9422
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9423
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
9424
9425
  this.recordTrace("submit_write", {
9425
- mode: "submit_retry",
9426
- attempt,
9426
+ mode: "submit_key",
9427
9427
  sendKey: this.sendKey,
9428
9428
  screenText: summarizeCliTraceText(screenText, 500)
9429
9429
  });
9430
9430
  this.ptyProcess.write(this.sendKey);
9431
- if (attempt >= 3) {
9432
- this.submitRetryUsed = true;
9433
- return;
9434
- }
9435
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
9431
+ const retrySubmitIfStuck = (attempt) => {
9432
+ this.submitRetryTimer = null;
9433
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9434
+ if (this.currentStatus === "waiting_approval") return;
9435
+ if ((this.responseBuffer || "").trim()) return;
9436
+ const screenText2 = this.terminalScreen.getText();
9437
+ if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
9438
+ if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText2)) return;
9439
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9440
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
9441
+ this.recordTrace("submit_write", {
9442
+ mode: "submit_retry",
9443
+ attempt,
9444
+ sendKey: this.sendKey,
9445
+ screenText: summarizeCliTraceText(screenText2, 500)
9446
+ });
9447
+ this.ptyProcess.write(this.sendKey);
9448
+ if (attempt >= 3) {
9449
+ this.submitRetryUsed = true;
9450
+ return;
9451
+ }
9452
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
9453
+ };
9454
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
9455
+ startResponseTimeout();
9456
+ resolveOnce();
9436
9457
  };
9437
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
9438
- startResponseTimeout();
9439
- };
9440
- if (this.submitStrategy === "immediate") {
9441
- this.submitPendingUntil = 0;
9458
+ if (this.submitStrategy === "immediate") {
9459
+ commitUserTurn();
9460
+ this.submitPendingUntil = 0;
9461
+ this.recordTrace("submit_write", {
9462
+ mode: "immediate",
9463
+ text: summarizeCliTraceText(text, 500),
9464
+ sendKey: this.sendKey,
9465
+ screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9466
+ });
9467
+ this.ptyProcess.write(text + this.sendKey);
9468
+ this.submitRetryTimer = setTimeout(() => {
9469
+ this.submitRetryTimer = null;
9470
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9471
+ if (this.currentStatus === "waiting_approval") return;
9472
+ if ((this.responseBuffer || "").trim()) return;
9473
+ const screenText = this.terminalScreen.getText();
9474
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9475
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
9476
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9477
+ this.recordTrace("submit_write", {
9478
+ mode: "immediate_retry",
9479
+ attempt: 1,
9480
+ sendKey: this.sendKey,
9481
+ screenText: summarizeCliTraceText(screenText, 500)
9482
+ });
9483
+ this.ptyProcess.write(this.sendKey);
9484
+ this.submitRetryUsed = true;
9485
+ }, retryDelayMs);
9486
+ startResponseTimeout();
9487
+ resolveOnce();
9488
+ return;
9489
+ }
9490
+ if (submitDelayMs > 0) {
9491
+ this.submitPendingUntil = Date.now() + submitDelayMs;
9492
+ }
9493
+ this.ptyProcess.write(text);
9442
9494
  this.recordTrace("submit_write", {
9443
- mode: "immediate",
9495
+ mode: "type_then_submit",
9444
9496
  text: summarizeCliTraceText(text, 500),
9445
9497
  sendKey: this.sendKey,
9446
9498
  screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9447
9499
  });
9448
- this.ptyProcess.write(text + this.sendKey);
9449
- this.submitRetryTimer = setTimeout(() => {
9450
- this.submitRetryTimer = null;
9451
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9452
- if (this.currentStatus === "waiting_approval") return;
9453
- if ((this.responseBuffer || "").trim()) return;
9500
+ const submitStartedAt = Date.now();
9501
+ let lastNormalizedScreen = "";
9502
+ let lastScreenChangeAt = submitStartedAt;
9503
+ const waitForEchoAndSubmit = () => {
9504
+ if (!this.ptyProcess) {
9505
+ resolveOnce();
9506
+ return;
9507
+ }
9508
+ const now = Date.now();
9509
+ const elapsed = now - submitStartedAt;
9454
9510
  const screenText = this.terminalScreen.getText();
9455
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9456
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
9457
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9458
- this.recordTrace("submit_write", {
9459
- mode: "immediate_retry",
9460
- attempt: 1,
9461
- sendKey: this.sendKey,
9462
- screenText: summarizeCliTraceText(screenText, 500)
9463
- });
9464
- this.ptyProcess.write(this.sendKey);
9465
- this.submitRetryUsed = true;
9466
- }, retryDelayMs);
9467
- startResponseTimeout();
9468
- return;
9469
- }
9470
- if (submitDelayMs > 0) {
9471
- this.submitPendingUntil = Date.now() + submitDelayMs;
9472
- }
9473
- this.ptyProcess.write(text);
9474
- this.recordTrace("submit_write", {
9475
- mode: "type_then_submit",
9476
- text: summarizeCliTraceText(text, 500),
9477
- sendKey: this.sendKey,
9478
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9479
- });
9480
- const submitStartedAt = Date.now();
9481
- let lastNormalizedScreen = "";
9482
- let lastScreenChangeAt = submitStartedAt;
9483
- const waitForEchoAndSubmit = () => {
9484
- if (!this.ptyProcess) return;
9485
- const now = Date.now();
9486
- const elapsed = now - submitStartedAt;
9487
- const screenText = this.terminalScreen.getText();
9488
- const normalizedScreen = normalizePromptText(screenText);
9489
- if (normalizedScreen !== lastNormalizedScreen) {
9490
- lastNormalizedScreen = normalizedScreen;
9491
- lastScreenChangeAt = now;
9492
- }
9493
- const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
9494
- if (echoVisible) {
9495
- const screenSettled = now - lastScreenChangeAt >= 500;
9496
- if (elapsed >= submitDelayMs && screenSettled) {
9511
+ const normalizedScreen = normalizePromptText(screenText);
9512
+ if (normalizedScreen !== lastNormalizedScreen) {
9513
+ lastNormalizedScreen = normalizedScreen;
9514
+ lastScreenChangeAt = now;
9515
+ }
9516
+ const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
9517
+ if (echoVisible) {
9518
+ const screenSettled = now - lastScreenChangeAt >= 500;
9519
+ if (elapsed >= submitDelayMs && screenSettled) {
9520
+ submit();
9521
+ return;
9522
+ }
9523
+ }
9524
+ if (elapsed >= maxEchoWaitMs) {
9497
9525
  submit();
9498
9526
  return;
9499
9527
  }
9500
- }
9501
- if (elapsed >= maxEchoWaitMs) {
9502
- submit();
9503
- return;
9504
- }
9505
- setTimeout(waitForEchoAndSubmit, 50);
9506
- };
9507
- waitForEchoAndSubmit();
9528
+ setTimeout(waitForEchoAndSubmit, 50);
9529
+ };
9530
+ waitForEchoAndSubmit();
9531
+ });
9508
9532
  }
9509
9533
  getPartialResponse() {
9510
9534
  if (!this.isWaitingForResponse) return "";
@@ -32849,6 +32873,7 @@ var init_reporter = __esm({
32849
32873
  lastStatusSentAt = 0;
32850
32874
  statusPendingThrottle = false;
32851
32875
  lastP2PStatusHash = "";
32876
+ lastServerStatusHash = "";
32852
32877
  lastStatusSummary = "";
32853
32878
  statusTimer = null;
32854
32879
  p2pTimer = null;
@@ -32859,11 +32884,11 @@ var init_reporter = __esm({
32859
32884
  // ─── Lifecycle ───────────────────────────────────
32860
32885
  startReporting() {
32861
32886
  setTimeout(() => {
32862
- this.sendUnifiedStatusReport().catch((e) => LOG.warn("Status", `Initial report failed: ${e?.message}`));
32887
+ this.sendUnifiedStatusReport({ forceServer: true, reason: "initial" }).catch((e) => LOG.warn("Status", `Initial report failed: ${e?.message}`));
32863
32888
  }, 2e3);
32864
32889
  const scheduleServerReport = () => {
32865
32890
  this.statusTimer = setTimeout(() => {
32866
- this.sendUnifiedStatusReport().catch((e) => LOG.warn("Status", `Periodic report failed: ${e?.message}`));
32891
+ this.sendUnifiedStatusReport({ forceServer: true, reason: "periodic" }).catch((e) => LOG.warn("Status", `Periodic report failed: ${e?.message}`));
32867
32892
  scheduleServerReport();
32868
32893
  }, 3e4);
32869
32894
  };
@@ -33010,24 +33035,24 @@ var init_reporter = __esm({
33010
33035
  cdpConnected: session.cdpConnected,
33011
33036
  currentModel: session.currentModel,
33012
33037
  currentPlan: session.currentPlan,
33013
- currentAutoApprove: session.currentAutoApprove,
33014
- lastUpdated: session.lastUpdated,
33015
- unread: session.unread,
33016
- lastSeenAt: session.lastSeenAt,
33017
- inboxBucket: session.inboxBucket,
33018
- surfaceHidden: session.surfaceHidden,
33019
- controlValues: session.controlValues,
33020
- providerControls: session.providerControls,
33021
- acpConfigOptions: session.acpConfigOptions,
33022
- acpModes: session.acpModes
33038
+ currentAutoApprove: session.currentAutoApprove
33023
33039
  })),
33024
33040
  p2p: payload.p2p,
33025
33041
  timestamp: now,
33026
33042
  detectedIdes: payload.detectedIdes,
33027
33043
  availableProviders: payload.availableProviders
33028
33044
  };
33045
+ const wsHash = this.simpleHash(JSON.stringify({
33046
+ ...wsPayload,
33047
+ timestamp: void 0
33048
+ }));
33049
+ if (!opts?.forceServer && wsHash === this.lastServerStatusHash) {
33050
+ LOG.debug("Server", `skip duplicate status_report${opts?.reason ? ` (${opts.reason})` : ""}`);
33051
+ return;
33052
+ }
33053
+ this.lastServerStatusHash = wsHash;
33029
33054
  serverConn.sendMessage("status_report", wsPayload);
33030
- LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)`);
33055
+ LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
33031
33056
  }
33032
33057
  // ─── P2P ─────────────────────────────────────────
33033
33058
  sendP2PPayload(payload) {
@@ -40493,6 +40518,7 @@ var init_server_connection = __esm({
40493
40518
  reconnectTimer = null;
40494
40519
  pingTimer = null;
40495
40520
  pongTimeout = null;
40521
+ missedPongCount = 0;
40496
40522
  messageHandlers = /* @__PURE__ */ new Map();
40497
40523
  stateChangeCallbacks = [];
40498
40524
  /** Fallback handler for message types without specific on() registration */
@@ -40591,6 +40617,7 @@ var init_server_connection = __esm({
40591
40617
  // --- Private ---
40592
40618
  onOpen() {
40593
40619
  LOG.info("Server", `[ServerConn] WebSocket open, sending auth...`);
40620
+ this.missedPongCount = 0;
40594
40621
  this.setState("authenticating");
40595
40622
  this.send({
40596
40623
  type: "auth",
@@ -40603,12 +40630,15 @@ var init_server_connection = __esm({
40603
40630
  }
40604
40631
  onMessage(text) {
40605
40632
  try {
40633
+ this.clearPongTimeout();
40634
+ this.missedPongCount = 0;
40606
40635
  const message = JSON.parse(text);
40607
40636
  if (message.type === "auth_ok") {
40608
40637
  this.reconnectAttempts = 0;
40609
- this.userPlan = message.payload.plan || "free";
40610
- this.iceServers = message.payload.iceServers || null;
40611
- this.planLimits = message.payload.limits || null;
40638
+ const payload = message.payload;
40639
+ this.userPlan = payload.plan || "free";
40640
+ this.iceServers = payload.iceServers || null;
40641
+ this.planLimits = payload.limits || null;
40612
40642
  if (this.iceServers?.length) {
40613
40643
  const hasTurn = this.iceServers.some((s) => JSON.stringify(s.urls || "").includes("turn"));
40614
40644
  LOG.info("Server", `[ServerConn] ICE servers: ${this.iceServers.length} (TURN: ${hasTurn ? "\u2705" : "\u274C STUN only"})`);
@@ -40702,6 +40732,7 @@ var init_server_connection = __esm({
40702
40732
  this.reconnectTimer = null;
40703
40733
  }
40704
40734
  this.stopHeartbeat();
40735
+ this.missedPongCount = 0;
40705
40736
  }
40706
40737
  // ─── WS Heartbeat (ping/pong) ─────────────────────
40707
40738
  startHeartbeat() {
@@ -40711,9 +40742,14 @@ var init_server_connection = __esm({
40711
40742
  try {
40712
40743
  this.ws.ping();
40713
40744
  this.pongTimeout = setTimeout(() => {
40714
- LOG.info("Server", "[ServerConn] \u26A0 Pong timeout (15s) \u2014 closing zombie WS");
40715
- if (this.ws) {
40745
+ this.pongTimeout = null;
40746
+ this.missedPongCount += 1;
40747
+ const misses = this.missedPongCount;
40748
+ if (misses >= 3 && this.ws) {
40749
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 closing stale WS`);
40716
40750
  this.ws.terminate();
40751
+ } else {
40752
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 keeping WS open and retrying`);
40717
40753
  }
40718
40754
  }, 15e3);
40719
40755
  } catch {
@@ -40725,12 +40761,13 @@ var init_server_connection = __esm({
40725
40761
  clearInterval(this.pingTimer);
40726
40762
  this.pingTimer = null;
40727
40763
  }
40728
- if (this.pongTimeout) {
40729
- clearTimeout(this.pongTimeout);
40730
- this.pongTimeout = null;
40731
- }
40764
+ this.clearPongTimeout();
40732
40765
  }
40733
40766
  onPong() {
40767
+ this.clearPongTimeout();
40768
+ this.missedPongCount = 0;
40769
+ }
40770
+ clearPongTimeout() {
40734
40771
  if (this.pongTimeout) {
40735
40772
  clearTimeout(this.pongTimeout);
40736
40773
  this.pongTimeout = null;
@@ -41080,8 +41117,14 @@ async function initiateConnection(deps, peerId, sharePermission) {
41080
41117
  }
41081
41118
  const pid = peerId || `legacy_${Date.now()}`;
41082
41119
  const existing = deps.peers.get(pid);
41083
- if (existing?.state === "connected") return;
41084
- if (existing?.state === "connecting") disconnectPeer(deps.peers, pid, deps.notifyStateChange);
41120
+ if (existing?.state === "connected") {
41121
+ log(`initiateconnection() ignored for peer ${pid} \u2014 already connected`);
41122
+ return;
41123
+ }
41124
+ if (existing?.state === "connecting") {
41125
+ log(`initiateconnection() ignored for peer ${pid} \u2014 connection already in progress`);
41126
+ return;
41127
+ }
41085
41128
  log(`initiateconnection() for peer ${pid}...`);
41086
41129
  const mod = deps.nodeDatachannel;
41087
41130
  const PeerConnectionCtor = mod.PeerConnection || mod.default?.PeerConnection || mod.default || mod;
@@ -41381,6 +41424,7 @@ var init_daemon_p2p = __esm({
41381
41424
  peers = /* @__PURE__ */ new Map();
41382
41425
  nodeDatachannel = null;
41383
41426
  stateListeners = [];
41427
+ lastNotifiedState = null;
41384
41428
  screenshotSender = new ScreenshotSender();
41385
41429
  // Handler storage — exposed to router via DataChannelHandlers interface
41386
41430
  handlers = {
@@ -41518,6 +41562,8 @@ ${e?.stack || ""}`);
41518
41562
  }
41519
41563
  notifyStateChange = () => {
41520
41564
  const state = this.connectionState;
41565
+ if (state === this.lastNotifiedState) return;
41566
+ this.lastNotifiedState = state;
41521
41567
  this.stateListeners.forEach((fn) => fn(state));
41522
41568
  };
41523
41569
  get connectionManagerDeps() {
@@ -41545,6 +41591,7 @@ ${e?.stack || ""}`);
41545
41591
  for (const peerId of Array.from(this.peers.keys())) {
41546
41592
  this.disconnectPeer(peerId);
41547
41593
  }
41594
+ this.lastNotifiedState = null;
41548
41595
  }
41549
41596
  // ─── Delegated data sending ─────────────────────
41550
41597
  sendStatus(status) {
@@ -48803,7 +48850,7 @@ var init_adhdev_daemon = __esm({
48803
48850
  import_ws3 = require("ws");
48804
48851
  import_chalk2 = __toESM(require("chalk"));
48805
48852
  init_version();
48806
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.33" });
48853
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.35" });
48807
48854
  AdhdevDaemon = class {
48808
48855
  localHttpServer = null;
48809
48856
  localWss = null;
@@ -48981,10 +49028,10 @@ ${err?.stack || ""}`);
48981
49028
  if (state === "connected") {
48982
49029
  LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
48983
49030
  this.statusReporter?.resetP2PHash();
48984
- this.statusReporter?.sendUnifiedStatusReport().catch((e) => LOG.warn("P2P", `Immediate status report failed: ${e?.message}`));
49031
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect" }).catch((e) => LOG.warn("P2P", `Immediate status report failed: ${e?.message}`));
48985
49032
  setTimeout(() => {
48986
49033
  this.statusReporter?.resetP2PHash();
48987
- this.statusReporter?.sendUnifiedStatusReport().catch(() => {
49034
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect-followup" }).catch(() => {
48988
49035
  });
48989
49036
  }, 2e3);
48990
49037
  }
@@ -49023,7 +49070,7 @@ ${err?.stack || ""}`);
49023
49070
  this.serverConn.onStateChange((state) => {
49024
49071
  if (state === "connected") {
49025
49072
  console.log(import_chalk2.default.green(" \u{1F4E1} Connected to ADHDev server"));
49026
- this.statusReporter?.sendUnifiedStatusReport();
49073
+ this.statusReporter?.sendUnifiedStatusReport({ forceServer: true, reason: "ws-connected" });
49027
49074
  } else if (state === "disconnected") {
49028
49075
  console.log(import_chalk2.default.yellow(" \u26A0 Server disconnected, will reconnect..."));
49029
49076
  }