@adhdev/daemon-core 0.9.82-rc.93 → 0.9.82-rc.94

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.
@@ -74,6 +74,9 @@ export declare class ProviderCliAdapter implements CliAdapter {
74
74
  private idleFinishCandidate;
75
75
  private finishRetryTimer;
76
76
  private finishRetryCount;
77
+ private pendingOutboundQueue;
78
+ private pendingOutboundFlushTimer;
79
+ private pendingOutboundFlushInFlight;
77
80
  private resizeSuppressUntil;
78
81
  private statusHistory;
79
82
  private cliScripts;
@@ -201,6 +204,11 @@ export declare class ProviderCliAdapter implements CliAdapter {
201
204
  private submitImmediatePrompt;
202
205
  private waitForEchoAndSubmit;
203
206
  sendMessage(text: string): Promise<void>;
207
+ private enqueuePendingOutboundMessage;
208
+ private shouldQueuePendingOutboundMessage;
209
+ private schedulePendingOutboundFlush;
210
+ private flushPendingOutboundQueue;
211
+ private sendMessageNow;
204
212
  getPartialResponse(): string;
205
213
  getDebugSnapshot(): Record<string, unknown>;
206
214
  getRuntimeMetadata(): PtyRuntimeMetadata | null;
@@ -26,6 +26,14 @@ export interface CliSessionStatus {
26
26
  message: string;
27
27
  buttons: string[];
28
28
  } | null;
29
+ pendingOutboundCount?: number;
30
+ pendingOutboundMessages?: Array<{
31
+ id: string;
32
+ role: 'user';
33
+ content: string;
34
+ queuedAt: number;
35
+ source: string;
36
+ }>;
29
37
  errorMessage?: string;
30
38
  errorReason?: string;
31
39
  bufferState?: {
package/dist/index.js CHANGED
@@ -4645,6 +4645,9 @@ var init_provider_cli_adapter = __esm({
4645
4645
  idleFinishCandidate = null;
4646
4646
  finishRetryTimer = null;
4647
4647
  finishRetryCount = 0;
4648
+ pendingOutboundQueue = [];
4649
+ pendingOutboundFlushTimer = null;
4650
+ pendingOutboundFlushInFlight = false;
4648
4651
  // Resize redraw suppression
4649
4652
  resizeSuppressUntil = 0;
4650
4653
  // Debug: status transition history
@@ -5665,6 +5668,7 @@ ${lastSnapshot}`;
5665
5668
  this.activeModal = null;
5666
5669
  this.setStatus("idle", "response_finished");
5667
5670
  this.onStatusChange?.();
5671
+ this.schedulePendingOutboundFlush();
5668
5672
  }
5669
5673
  maybeCommitVisibleIdleTranscript(session, parsedMessages) {
5670
5674
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
@@ -5685,6 +5689,7 @@ ${lastSnapshot}`;
5685
5689
  this.activeModal = null;
5686
5690
  this.setStatus("idle", "script_idle_commit");
5687
5691
  this.onStatusChange?.();
5692
+ this.schedulePendingOutboundFlush();
5688
5693
  this.recordTrace("script_idle_commit", {
5689
5694
  messageCount: parsedMessages.length,
5690
5695
  lastAssistant: summarizeCliTraceText(visibleAssistant.content, 320)
@@ -5867,6 +5872,14 @@ ${lastSnapshot}`;
5867
5872
  messages: [],
5868
5873
  workingDir: this.workingDir,
5869
5874
  activeModal: effectiveModal,
5875
+ pendingOutboundCount: this.pendingOutboundQueue.length,
5876
+ pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
5877
+ id: message.id,
5878
+ role: message.role,
5879
+ content: message.content,
5880
+ queuedAt: message.queuedAt,
5881
+ source: message.source
5882
+ })),
5870
5883
  errorMessage: this.parseErrorMessage || void 0,
5871
5884
  errorReason: this.parseErrorMessage ? "parse_error" : void 0,
5872
5885
  ...bufferState ? { bufferState } : {}
@@ -6163,6 +6176,90 @@ ${lastSnapshot}`;
6163
6176
  ), 50);
6164
6177
  }
6165
6178
  async sendMessage(text) {
6179
+ await this.sendMessageNow(text, true);
6180
+ }
6181
+ enqueuePendingOutboundMessage(text, reason) {
6182
+ const content = String(text || "");
6183
+ const duplicate = this.pendingOutboundQueue.some((message2) => message2.content === content);
6184
+ if (duplicate) {
6185
+ this.recordTrace("send_message_queued_duplicate_suppressed", {
6186
+ reason,
6187
+ queueLength: this.pendingOutboundQueue.length,
6188
+ text: summarizeCliTraceText(content, 500)
6189
+ });
6190
+ return;
6191
+ }
6192
+ const queuedAt = Date.now();
6193
+ const message = {
6194
+ id: `${queuedAt}:${this.pendingOutboundQueue.length}:${Math.random().toString(36).slice(2, 10)}`,
6195
+ role: "user",
6196
+ content,
6197
+ queuedAt,
6198
+ source: "sendMessage"
6199
+ };
6200
+ this.pendingOutboundQueue.push(message);
6201
+ this.recordTrace("send_message_queued", {
6202
+ reason,
6203
+ queueLength: this.pendingOutboundQueue.length,
6204
+ queuedAt,
6205
+ text: summarizeCliTraceText(content, 500)
6206
+ });
6207
+ LOG.info("CLI", `[${this.cliType}] queued outbound message while busy (${reason}); queue=${this.pendingOutboundQueue.length}`);
6208
+ this.onStatusChange?.();
6209
+ }
6210
+ shouldQueuePendingOutboundMessage(parsedStatusBeforeSend = null) {
6211
+ if (this.provider.allowInputDuringGeneration === true) return null;
6212
+ if (this.hasActionableApproval()) return null;
6213
+ const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === "string" ? String(parsedStatusBeforeSend.status) : "";
6214
+ if (parsedSessionStatus === "idle" && this.parsedStatusHasFinalAssistantMessage(parsedStatusBeforeSend)) return null;
6215
+ if (this.currentStatus === "generating") return "current_status_generating";
6216
+ if (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating") {
6217
+ const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
6218
+ const parsedHasActionableModal = Boolean(
6219
+ parsedModal && Array.isArray(parsedModal.buttons) && parsedModal.buttons.some((candidate) => typeof candidate === "string" && candidate.trim())
6220
+ );
6221
+ const terminalLooksIdle = this.currentStatus === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && !this.hasActionableApproval() && !parsedHasActionableModal;
6222
+ return terminalLooksIdle ? null : `parsed_status_${parsedSessionStatus}`;
6223
+ }
6224
+ if (this.isWaitingForResponse && this.currentTurnScope) return "active_turn_in_progress";
6225
+ return null;
6226
+ }
6227
+ schedulePendingOutboundFlush(delayMs = 0) {
6228
+ if (this.pendingOutboundFlushTimer) clearTimeout(this.pendingOutboundFlushTimer);
6229
+ this.pendingOutboundFlushTimer = setTimeout(() => {
6230
+ this.pendingOutboundFlushTimer = null;
6231
+ void this.flushPendingOutboundQueue();
6232
+ }, Math.max(0, delayMs));
6233
+ }
6234
+ async flushPendingOutboundQueue() {
6235
+ if (this.pendingOutboundFlushInFlight || this.pendingOutboundQueue.length === 0) return;
6236
+ if (this.currentStatus !== "idle" || this.isWaitingForResponse || this.hasActionableApproval()) return;
6237
+ this.pendingOutboundFlushInFlight = true;
6238
+ try {
6239
+ while (this.pendingOutboundQueue.length > 0) {
6240
+ if (this.currentStatus !== "idle" || this.isWaitingForResponse || this.hasActionableApproval()) break;
6241
+ const next = this.pendingOutboundQueue[0];
6242
+ this.recordTrace("send_message_queue_flush", {
6243
+ id: next.id,
6244
+ queuedAt: next.queuedAt,
6245
+ queueLength: this.pendingOutboundQueue.length,
6246
+ text: summarizeCliTraceText(next.content, 500)
6247
+ });
6248
+ try {
6249
+ await this.sendMessageNow(next.content, false);
6250
+ this.pendingOutboundQueue.shift();
6251
+ this.onStatusChange?.();
6252
+ } catch (error) {
6253
+ LOG.warn("CLI", `[${this.cliType}] queued outbound flush failed: ${error?.message || error}`);
6254
+ this.schedulePendingOutboundFlush(1e3);
6255
+ break;
6256
+ }
6257
+ }
6258
+ } finally {
6259
+ this.pendingOutboundFlushInFlight = false;
6260
+ }
6261
+ }
6262
+ async sendMessageNow(text, allowQueue) {
6166
6263
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
6167
6264
  const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
6168
6265
  const allowInterventionPrompt = allowInputDuringGeneration && this.isWaitingForResponse && !this.hasActionableApproval();
@@ -6173,6 +6270,18 @@ ${lastSnapshot}`;
6173
6270
  await new Promise((resolve16) => setTimeout(resolve16, 50));
6174
6271
  }
6175
6272
  }
6273
+ const parsedStatusBeforeSend = !allowInputDuringGeneration ? (() => {
6274
+ try {
6275
+ return this.getScriptParsedStatus?.() || null;
6276
+ } catch {
6277
+ return null;
6278
+ }
6279
+ })() : null;
6280
+ const queueReason = this.shouldQueuePendingOutboundMessage(parsedStatusBeforeSend);
6281
+ if (allowQueue && queueReason) {
6282
+ this.enqueuePendingOutboundMessage(text, queueReason);
6283
+ return;
6284
+ }
6176
6285
  if (!allowInterventionPrompt) {
6177
6286
  await this.waitForInteractivePrompt();
6178
6287
  }
@@ -6185,13 +6294,6 @@ ${lastSnapshot}`;
6185
6294
  }
6186
6295
  }
6187
6296
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
6188
- const parsedStatusBeforeSend = !allowInputDuringGeneration ? (() => {
6189
- try {
6190
- return this.getScriptParsedStatus?.() || null;
6191
- } catch {
6192
- return null;
6193
- }
6194
- })() : null;
6195
6297
  const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === "string" ? String(parsedStatusBeforeSend.status) : "";
6196
6298
  if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating")) {
6197
6299
  const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
@@ -6200,11 +6302,19 @@ ${lastSnapshot}`;
6200
6302
  );
6201
6303
  const terminalLooksIdle = this.currentStatus === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && !this.hasActionableApproval() && !parsedHasActionableModal;
6202
6304
  if (!terminalLooksIdle) {
6305
+ if (allowQueue) {
6306
+ this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}`);
6307
+ return;
6308
+ }
6203
6309
  throw new Error(`${this.cliName} is still processing the previous prompt`);
6204
6310
  }
6205
6311
  }
6206
6312
  if (this.isWaitingForResponse && !allowInputDuringGeneration) {
6207
6313
  if (!this.clearStaleIdleResponseGuard("send_message_guard") && !this.clearParsedIdleResponseGuard("send_message_parsed_idle_guard", parsedStatusBeforeSend)) {
6314
+ if (allowQueue) {
6315
+ this.enqueuePendingOutboundMessage(text, "waiting_for_response");
6316
+ return;
6317
+ }
6208
6318
  throw new Error(`${this.cliName} is still processing the previous prompt`);
6209
6319
  }
6210
6320
  }
@@ -6439,6 +6549,12 @@ ${lastSnapshot}`;
6439
6549
  this.pendingTerminalQueryTail = "";
6440
6550
  this.ptyOutputChunks = [];
6441
6551
  this.finishRetryCount = 0;
6552
+ if (this.pendingOutboundFlushTimer) {
6553
+ clearTimeout(this.pendingOutboundFlushTimer);
6554
+ this.pendingOutboundFlushTimer = null;
6555
+ }
6556
+ this.pendingOutboundQueue = [];
6557
+ this.pendingOutboundFlushInFlight = false;
6442
6558
  if (this.ptyProcess) {
6443
6559
  this.ptyProcess.write("");
6444
6560
  setTimeout(() => {
@@ -6462,6 +6578,12 @@ ${lastSnapshot}`;
6462
6578
  this.pendingTerminalQueryTail = "";
6463
6579
  this.ptyOutputChunks = [];
6464
6580
  this.finishRetryCount = 0;
6581
+ if (this.pendingOutboundFlushTimer) {
6582
+ clearTimeout(this.pendingOutboundFlushTimer);
6583
+ this.pendingOutboundFlushTimer = null;
6584
+ }
6585
+ this.pendingOutboundQueue = [];
6586
+ this.pendingOutboundFlushInFlight = false;
6465
6587
  if (this.ptyProcess) {
6466
6588
  try {
6467
6589
  if (typeof this.ptyProcess.detach === "function") {
@@ -6501,6 +6623,12 @@ ${lastSnapshot}`;
6501
6623
  this.finishRetryTimer = null;
6502
6624
  }
6503
6625
  this.finishRetryCount = 0;
6626
+ if (this.pendingOutboundFlushTimer) {
6627
+ clearTimeout(this.pendingOutboundFlushTimer);
6628
+ this.pendingOutboundFlushTimer = null;
6629
+ }
6630
+ this.pendingOutboundQueue = [];
6631
+ this.pendingOutboundFlushInFlight = false;
6504
6632
  this.resetTerminalScreen();
6505
6633
  this.ptyProcess?.clearBuffer?.();
6506
6634
  this.onStatusChange?.();
@@ -6627,6 +6755,14 @@ ${lastSnapshot}`;
6627
6755
  rawBufferPreview: this.accumulatedRawBuffer.slice(-1e3),
6628
6756
  sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1e3),
6629
6757
  responseBuffer: this.responseBuffer.slice(-1e3),
6758
+ pendingOutboundQueue: this.pendingOutboundQueue.map((message) => ({
6759
+ id: message.id,
6760
+ role: message.role,
6761
+ content: message.content,
6762
+ queuedAt: message.queuedAt,
6763
+ source: message.source
6764
+ })),
6765
+ pendingOutboundCount: this.pendingOutboundQueue.length,
6630
6766
  lastOutputAt: this.lastOutputAt,
6631
6767
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
6632
6768
  lastScreenChangeAt: this.lastScreenChangeAt,
@@ -15988,7 +16124,7 @@ var RECENT_SEND_WINDOW_MS = 1200;
15988
16124
  var READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25e3;
15989
16125
  var HERMES_CLI_STARTING_SEND_SETTLE_MS = 2e3;
15990
16126
  var CLI_NATIVE_HISTORY_FRESH_MS = 5 * 6e4;
15991
- var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli"]);
16127
+ var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli", "hermes-cli"]);
15992
16128
  var recentSendByTarget = /* @__PURE__ */ new Map();
15993
16129
  function getCurrentProviderType(h, fallback = "") {
15994
16130
  return h.currentSession?.providerType || h.currentProviderType || fallback;
@@ -22868,18 +23004,6 @@ Run 'adhdev doctor' for detailed diagnostics.`
22868
23004
  } else if (currentStatus === "starting") {
22869
23005
  currentStatus = getEffectiveAgentSendStatus(adapter);
22870
23006
  }
22871
- if (BUSY_AGENT_STATUSES.has(currentStatus)) {
22872
- return {
22873
- success: false,
22874
- code: "agent_runtime_busy",
22875
- reason: "agent_runtime_busy",
22876
- retryable: true,
22877
- retryRecommended: true,
22878
- status: currentStatus,
22879
- targetSessionId: args?.targetSessionId,
22880
- error: `CLI agent '${agentType}' is currently ${currentStatus}; retry after the current turn finishes.`
22881
- };
22882
- }
22883
23007
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
22884
23008
  const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
22885
23009
  if (provider?.category === "acp") {
@@ -22890,7 +23014,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
22890
23014
  const message = input.textFallback;
22891
23015
  if (!message) throw new Error("message required for send_chat");
22892
23016
  await adapter.sendMessage(message);
22893
- return { success: true, status: "generating" };
23017
+ return {
23018
+ success: true,
23019
+ status: BUSY_AGENT_STATUSES.has(currentStatus) ? currentStatus : "generating",
23020
+ ...BUSY_AGENT_STATUSES.has(currentStatus) ? { queued: true, queuedReason: "agent_runtime_busy" } : {}
23021
+ };
22894
23022
  } else if (action === "clear_history") {
22895
23023
  if (typeof adapter.clearHistory === "function") adapter.clearHistory();
22896
23024
  return { success: true, cleared: true };