adhdev 0.8.34 → 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,6 +40630,8 @@ 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;
@@ -40703,6 +40732,7 @@ var init_server_connection = __esm({
40703
40732
  this.reconnectTimer = null;
40704
40733
  }
40705
40734
  this.stopHeartbeat();
40735
+ this.missedPongCount = 0;
40706
40736
  }
40707
40737
  // ─── WS Heartbeat (ping/pong) ─────────────────────
40708
40738
  startHeartbeat() {
@@ -40712,9 +40742,14 @@ var init_server_connection = __esm({
40712
40742
  try {
40713
40743
  this.ws.ping();
40714
40744
  this.pongTimeout = setTimeout(() => {
40715
- LOG.info("Server", "[ServerConn] \u26A0 Pong timeout (15s) \u2014 closing zombie WS");
40716
- 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`);
40717
40750
  this.ws.terminate();
40751
+ } else {
40752
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 keeping WS open and retrying`);
40718
40753
  }
40719
40754
  }, 15e3);
40720
40755
  } catch {
@@ -40726,12 +40761,13 @@ var init_server_connection = __esm({
40726
40761
  clearInterval(this.pingTimer);
40727
40762
  this.pingTimer = null;
40728
40763
  }
40729
- if (this.pongTimeout) {
40730
- clearTimeout(this.pongTimeout);
40731
- this.pongTimeout = null;
40732
- }
40764
+ this.clearPongTimeout();
40733
40765
  }
40734
40766
  onPong() {
40767
+ this.clearPongTimeout();
40768
+ this.missedPongCount = 0;
40769
+ }
40770
+ clearPongTimeout() {
40735
40771
  if (this.pongTimeout) {
40736
40772
  clearTimeout(this.pongTimeout);
40737
40773
  this.pongTimeout = null;
@@ -41081,8 +41117,14 @@ async function initiateConnection(deps, peerId, sharePermission) {
41081
41117
  }
41082
41118
  const pid = peerId || `legacy_${Date.now()}`;
41083
41119
  const existing = deps.peers.get(pid);
41084
- if (existing?.state === "connected") return;
41085
- 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
+ }
41086
41128
  log(`initiateconnection() for peer ${pid}...`);
41087
41129
  const mod = deps.nodeDatachannel;
41088
41130
  const PeerConnectionCtor = mod.PeerConnection || mod.default?.PeerConnection || mod.default || mod;
@@ -41382,6 +41424,7 @@ var init_daemon_p2p = __esm({
41382
41424
  peers = /* @__PURE__ */ new Map();
41383
41425
  nodeDatachannel = null;
41384
41426
  stateListeners = [];
41427
+ lastNotifiedState = null;
41385
41428
  screenshotSender = new ScreenshotSender();
41386
41429
  // Handler storage — exposed to router via DataChannelHandlers interface
41387
41430
  handlers = {
@@ -41519,6 +41562,8 @@ ${e?.stack || ""}`);
41519
41562
  }
41520
41563
  notifyStateChange = () => {
41521
41564
  const state = this.connectionState;
41565
+ if (state === this.lastNotifiedState) return;
41566
+ this.lastNotifiedState = state;
41522
41567
  this.stateListeners.forEach((fn) => fn(state));
41523
41568
  };
41524
41569
  get connectionManagerDeps() {
@@ -41546,6 +41591,7 @@ ${e?.stack || ""}`);
41546
41591
  for (const peerId of Array.from(this.peers.keys())) {
41547
41592
  this.disconnectPeer(peerId);
41548
41593
  }
41594
+ this.lastNotifiedState = null;
41549
41595
  }
41550
41596
  // ─── Delegated data sending ─────────────────────
41551
41597
  sendStatus(status) {
@@ -48804,7 +48850,7 @@ var init_adhdev_daemon = __esm({
48804
48850
  import_ws3 = require("ws");
48805
48851
  import_chalk2 = __toESM(require("chalk"));
48806
48852
  init_version();
48807
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.34" });
48853
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.35" });
48808
48854
  AdhdevDaemon = class {
48809
48855
  localHttpServer = null;
48810
48856
  localWss = null;
@@ -48982,10 +49028,10 @@ ${err?.stack || ""}`);
48982
49028
  if (state === "connected") {
48983
49029
  LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
48984
49030
  this.statusReporter?.resetP2PHash();
48985
- 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}`));
48986
49032
  setTimeout(() => {
48987
49033
  this.statusReporter?.resetP2PHash();
48988
- this.statusReporter?.sendUnifiedStatusReport().catch(() => {
49034
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect-followup" }).catch(() => {
48989
49035
  });
48990
49036
  }, 2e3);
48991
49037
  }
@@ -49024,7 +49070,7 @@ ${err?.stack || ""}`);
49024
49070
  this.serverConn.onStateChange((state) => {
49025
49071
  if (state === "connected") {
49026
49072
  console.log(import_chalk2.default.green(" \u{1F4E1} Connected to ADHDev server"));
49027
- this.statusReporter?.sendUnifiedStatusReport();
49073
+ this.statusReporter?.sendUnifiedStatusReport({ forceServer: true, reason: "ws-connected" });
49028
49074
  } else if (state === "disconnected") {
49029
49075
  console.log(import_chalk2.default.yellow(" \u26A0 Server disconnected, will reconnect..."));
49030
49076
  }