@adhdev/daemon-core 0.5.61 → 0.5.63

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.d.ts CHANGED
@@ -2484,7 +2484,7 @@ declare const DAEMON_WS_PATH = "/ipc";
2484
2484
  * binary: string — binary name
2485
2485
  * spawn: { command, args, shell, env }
2486
2486
  * patterns: { prompt, generating, approval, ready }
2487
- * timeouts?: { idleFinish, generatingIdle, maxResponse, approvalCooldown, ... }
2487
+ * timeouts?: { idleFinish, generatingIdle, maxResponse, approvalCooldown, outputSettle, ... }
2488
2488
  * cleanOutput(raw, lastUserInput): string
2489
2489
  */
2490
2490
 
@@ -2534,6 +2534,8 @@ interface CliProviderModule {
2534
2534
  maxResponse?: number;
2535
2535
  /** shutdown after kill wait (default 1000ms) */
2536
2536
  shutdownGrace?: number;
2537
+ /** Output settle debounce before evaluating idle/approval (default 300ms) */
2538
+ outputSettle?: number;
2537
2539
  };
2538
2540
  cleanOutput(raw: string, lastUserInput?: string): string;
2539
2541
  }
@@ -2563,6 +2565,8 @@ declare class ProviderCliAdapter implements CliAdapter {
2563
2565
  private lastApprovalResolvedAt;
2564
2566
  private approvalTransitionBuffer;
2565
2567
  private approvalExitTimeout;
2568
+ private settleTimer;
2569
+ private settledBuffer;
2566
2570
  private resizeSuppressUntil;
2567
2571
  private statusHistory;
2568
2572
  private setStatus;
@@ -2574,6 +2578,12 @@ declare class ProviderCliAdapter implements CliAdapter {
2574
2578
  setOnPtyData(callback: (data: string) => void): void;
2575
2579
  spawn(): Promise<void>;
2576
2580
  private handleOutput;
2581
+ /**
2582
+ * Fired after output goes quiet for outputSettle ms.
2583
+ * Evaluates the stabilised buffer for approval, prompt (idle), or timeout.
2584
+ */
2585
+ private scheduleSettle;
2586
+ private evaluateSettled;
2577
2587
  private finishResponse;
2578
2588
  getStatus(): CliSessionStatus;
2579
2589
  sendMessage(text: string): Promise<void>;
package/dist/index.js CHANGED
@@ -2236,29 +2236,18 @@ var ExtensionProviderInstance = class {
2236
2236
  return this.instanceId;
2237
2237
  }
2238
2238
  // ─── status transition detect ──────────────────────────────
2239
- detectTransition(newStatus, data) {
2239
+ // NOTE: Extension transitions are TRACKED but NOT emitted as events.
2240
+ // The parent IdeProviderInstance already emits identical events
2241
+ // (generating_started, generating_completed, waiting_approval)
2242
+ // via its own detectAgentTransitions(). Emitting here would cause
2243
+ // duplicate toasts with slightly different content.
2244
+ detectTransition(newStatus, _data) {
2240
2245
  const now = Date.now();
2241
2246
  const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
2242
2247
  if (agentStatus !== this.lastAgentStatus) {
2243
- const chatTitle = this.provider.name;
2244
2248
  if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
2245
2249
  this.generatingStartedAt = now;
2246
- this.pushEvent({ event: "agent:generating_started", chatTitle, timestamp: now });
2247
- } else if (agentStatus === "waiting_approval") {
2248
- if (!this.generatingStartedAt) this.generatingStartedAt = now;
2249
- const msg = data?.activeModal?.message || data?.modalMessage;
2250
- this.pushEvent({
2251
- event: "agent:waiting_approval",
2252
- chatTitle,
2253
- timestamp: now,
2254
- ideType: this.ideType,
2255
- agentType: this.type,
2256
- modalMessage: msg,
2257
- modalButtons: data?.activeModal?.buttons || data?.modalButtons
2258
- });
2259
2250
  } else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
2260
- const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
2261
- this.pushEvent({ event: "agent:generating_completed", chatTitle, duration, timestamp: now });
2262
2251
  this.generatingStartedAt = 0;
2263
2252
  }
2264
2253
  this.lastAgentStatus = agentStatus;
@@ -7039,7 +7028,8 @@ var ProviderCliAdapter = class {
7039
7028
  generatingIdle: t.generatingIdle ?? 6e3,
7040
7029
  idleFinish: t.idleFinish ?? 5e3,
7041
7030
  maxResponse: t.maxResponse ?? 3e5,
7042
- shutdownGrace: t.shutdownGrace ?? 1e3
7031
+ shutdownGrace: t.shutdownGrace ?? 1e3,
7032
+ outputSettle: t.outputSettle ?? 300
7043
7033
  };
7044
7034
  const rawKeys = provider.approvalKeys;
7045
7035
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
@@ -7072,6 +7062,10 @@ var ProviderCliAdapter = class {
7072
7062
  // Approval state machine
7073
7063
  approvalTransitionBuffer = "";
7074
7064
  approvalExitTimeout = null;
7065
+ // Output settle debounce — fires after PTY output goes quiet
7066
+ settleTimer = null;
7067
+ settledBuffer = "";
7068
+ // snapshot of recentOutputBuffer at settle time
7075
7069
  // Resize redraw suppression
7076
7070
  resizeSuppressUntil = 0;
7077
7071
  // Debug: status transition history
@@ -7213,39 +7207,57 @@ var ProviderCliAdapter = class {
7213
7207
  if (cleanData.trim().length > 5) {
7214
7208
  LOG.debug("CLI", `[${this.cliType}] output chunk (${cleanData.length}): ${cleanData.slice(0, 300).replace(/\n/g, "\\n")}`);
7215
7209
  }
7216
- const hasApproval = patterns.approval.some((p) => p.test(this.recentOutputBuffer));
7217
- if (hasApproval && this.currentStatus !== "waiting_approval") {
7218
- const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
7219
- if (!inCooldown) {
7220
- const ctxLines = this.recentOutputBuffer.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
7210
+ if (!this.isWaitingForResponse) {
7211
+ if (patterns.generating.some((p) => p.test(cleanData))) {
7212
+ if (this.settleTimer) {
7213
+ clearTimeout(this.settleTimer);
7214
+ this.settleTimer = null;
7215
+ }
7221
7216
  this.isWaitingForResponse = true;
7222
- this.setStatus("waiting_approval", "approval_pattern");
7223
- this.recentOutputBuffer = "";
7224
- this.approvalTransitionBuffer = "";
7225
- this.activeModal = {
7226
- message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
7227
- buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
7228
- };
7229
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7230
- if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
7231
- this.approvalExitTimeout = setTimeout(() => {
7232
- if (this.currentStatus === "waiting_approval") {
7233
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
7234
- this.activeModal = null;
7235
- this.lastApprovalResolvedAt = Date.now();
7236
- this.recentOutputBuffer = "";
7237
- this.approvalTransitionBuffer = "";
7238
- this.approvalExitTimeout = null;
7239
- this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
7240
- this.onStatusChange?.();
7241
- }
7242
- }, 6e4);
7217
+ this.responseBuffer = "";
7218
+ this.setStatus("generating", "autonomous_gen");
7219
+ this.onStatusChange?.();
7220
+ }
7221
+ }
7222
+ if (this.isWaitingForResponse) {
7223
+ this.responseBuffer += cleanData;
7224
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
7225
+ if (patterns.generating.some((p) => p.test(cleanData))) {
7226
+ this.setStatus("generating", "still_generating");
7227
+ this.idleTimeout = setTimeout(() => {
7228
+ if (this.isWaitingForResponse) this.finishResponse();
7229
+ }, this.timeouts.generatingIdle);
7243
7230
  this.onStatusChange?.();
7231
+ if (this.settleTimer) {
7232
+ clearTimeout(this.settleTimer);
7233
+ this.settleTimer = null;
7234
+ }
7244
7235
  return;
7245
7236
  }
7246
7237
  }
7247
7238
  if (this.currentStatus === "waiting_approval") {
7248
7239
  this.approvalTransitionBuffer = (this.approvalTransitionBuffer + cleanData).slice(-500);
7240
+ this.scheduleSettle();
7241
+ return;
7242
+ }
7243
+ this.scheduleSettle();
7244
+ }
7245
+ /**
7246
+ * Fired after output goes quiet for outputSettle ms.
7247
+ * Evaluates the stabilised buffer for approval, prompt (idle), or timeout.
7248
+ */
7249
+ scheduleSettle() {
7250
+ if (this.settleTimer) clearTimeout(this.settleTimer);
7251
+ this.settleTimer = setTimeout(() => {
7252
+ this.settleTimer = null;
7253
+ this.settledBuffer = this.recentOutputBuffer;
7254
+ this.evaluateSettled();
7255
+ }, this.timeouts.outputSettle);
7256
+ }
7257
+ evaluateSettled() {
7258
+ const { patterns } = this.provider;
7259
+ const buf = this.settledBuffer;
7260
+ if (this.currentStatus === "waiting_approval") {
7249
7261
  const genResume = patterns.generating.some((p) => p.test(this.approvalTransitionBuffer));
7250
7262
  const promptResume = patterns.prompt.some((p) => p.test(this.approvalTransitionBuffer));
7251
7263
  if (genResume) {
@@ -7272,33 +7284,43 @@ var ProviderCliAdapter = class {
7272
7284
  }
7273
7285
  return;
7274
7286
  }
7275
- if (!this.isWaitingForResponse) {
7276
- if (patterns.generating.some((p) => p.test(cleanData))) {
7287
+ const hasApproval = patterns.approval.some((p) => p.test(buf));
7288
+ if (hasApproval) {
7289
+ const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
7290
+ if (!inCooldown) {
7291
+ const ctxLines = buf.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
7277
7292
  this.isWaitingForResponse = true;
7278
- this.responseBuffer = "";
7279
- this.setStatus("generating", "autonomous_gen");
7293
+ this.setStatus("waiting_approval", "approval_pattern");
7294
+ this.recentOutputBuffer = "";
7295
+ this.approvalTransitionBuffer = "";
7296
+ this.activeModal = {
7297
+ message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
7298
+ buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
7299
+ };
7300
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
7301
+ if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
7302
+ this.approvalExitTimeout = setTimeout(() => {
7303
+ if (this.currentStatus === "waiting_approval") {
7304
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
7305
+ this.activeModal = null;
7306
+ this.lastApprovalResolvedAt = Date.now();
7307
+ this.recentOutputBuffer = "";
7308
+ this.approvalTransitionBuffer = "";
7309
+ this.approvalExitTimeout = null;
7310
+ this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
7311
+ this.onStatusChange?.();
7312
+ }
7313
+ }, 6e4);
7280
7314
  this.onStatusChange?.();
7315
+ return;
7281
7316
  }
7282
7317
  }
7283
7318
  if (this.isWaitingForResponse) {
7284
- this.responseBuffer += cleanData;
7285
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7286
- const stillGenerating = patterns.generating.some((p) => p.test(cleanData));
7287
- if (stillGenerating) {
7288
- this.setStatus("generating", "still_generating");
7289
- this.idleTimeout = setTimeout(() => {
7290
- if (this.isWaitingForResponse) this.finishResponse();
7291
- }, this.timeouts.generatingIdle);
7292
- this.onStatusChange?.();
7293
- return;
7294
- }
7295
- const trailingLines = cleanData.split("\n").slice(-2).join("\n");
7296
- if (patterns.prompt.some((p) => p.test(trailingLines))) {
7297
- const hasApprovalHere = patterns.approval.some((p) => p.test(this.recentOutputBuffer));
7298
- if (!hasApprovalHere) {
7299
- this.finishResponse();
7300
- }
7301
- } else {
7319
+ const trailingLines = buf.split("\n").slice(-3).join("\n");
7320
+ if (patterns.prompt.some((p) => p.test(trailingLines)) && !hasApproval) {
7321
+ this.finishResponse();
7322
+ } else if (!patterns.generating.some((p) => p.test(buf))) {
7323
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
7302
7324
  this.idleTimeout = setTimeout(() => {
7303
7325
  if (this.isWaitingForResponse && this.responseBuffer.trim()) {
7304
7326
  this.finishResponse();
@@ -7370,6 +7392,10 @@ var ProviderCliAdapter = class {
7370
7392
  this.shutdown();
7371
7393
  }
7372
7394
  shutdown() {
7395
+ if (this.settleTimer) {
7396
+ clearTimeout(this.settleTimer);
7397
+ this.settleTimer = null;
7398
+ }
7373
7399
  if (this.approvalExitTimeout) {
7374
7400
  clearTimeout(this.approvalExitTimeout);
7375
7401
  this.approvalExitTimeout = null;
@@ -7441,6 +7467,7 @@ var ProviderCliAdapter = class {
7441
7467
  // Buffers
7442
7468
  startupBuffer: this.startupBuffer.slice(-500),
7443
7469
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
7470
+ settledBuffer: this.settledBuffer.slice(-500),
7444
7471
  responseBuffer: this.responseBuffer.slice(-500),
7445
7472
  approvalTransitionBuffer: this.approvalTransitionBuffer.slice(-500),
7446
7473
  // State