@adhdev/daemon-core 0.5.31 → 0.5.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.5.31",
3
+ "version": "0.5.32",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -95,9 +95,16 @@ export interface CliProviderModule {
95
95
 
96
96
  function stripAnsi(str: string): string {
97
97
  // eslint-disable-next-line no-control-regex
98
- return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '')
98
+ return str
99
+ // Cursor movement sequences → space (prevents word concatenation)
100
+ .replace(/\x1B\[\d*[A-HJKSTfG]/g, ' ')
101
+ // SGR and other CSI sequences → remove
102
+ .replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '')
103
+ // OSC sequences (title bar etc)
99
104
  .replace(/\x1B\][^\x07]*\x07/g, '')
100
- .replace(/\x1B\][^\x1B]*\x1B\\/g, '');
105
+ .replace(/\x1B\][^\x1B]*\x1B\\/g, '')
106
+ // Collapse multiple spaces
107
+ .replace(/ +/g, ' ');
101
108
  }
102
109
 
103
110
  function findBinary(name: string): string {
@@ -469,6 +476,10 @@ export class ProviderCliAdapter implements CliAdapter {
469
476
  }
470
477
 
471
478
  // ─── Phase 2: Approval detect
479
+ // DEBUG: log recent output for approval pattern debugging
480
+ if (cleanData.trim().length > 5) {
481
+ LOG.debug('CLI', `[${this.cliType}] output chunk (${cleanData.length}): ${cleanData.slice(0, 300).replace(/\n/g, '\\n')}`);
482
+ }
472
483
  const hasApproval = patterns.approval.some(p => p.test(this.recentOutputBuffer));
473
484
  if (hasApproval && this.currentStatus !== 'waiting_approval') {
474
485
  const inCooldown = this.lastApprovalResolvedAt && (Date.now() - this.lastApprovalResolvedAt) < this.timeouts.approvalCooldown;
@@ -149,7 +149,8 @@ export class CliProviderInstance implements ProviderInstance {
149
149
  this.monitor.reset();
150
150
  }
151
151
 
152
- // ─── Status transition detection (moved from daemon-status.ts) ──────
152
+ private completedDebounceTimer: NodeJS.Timeout | null = null;
153
+ private completedDebouncePending: { chatTitle: string; duration: number; timestamp: number } | null = null;
153
154
 
154
155
  private detectStatusTransition(): void {
155
156
  const now = Date.now();
@@ -161,7 +162,14 @@ export class CliProviderInstance implements ProviderInstance {
161
162
  if (newStatus !== this.lastStatus) {
162
163
  LOG.info('CLI', `[${this.type}] status: ${this.lastStatus} → ${newStatus}`);
163
164
  if (this.lastStatus === 'idle' && newStatus === 'generating') {
164
- this.generatingStartedAt = now;
165
+ // Cancel any pending completed event (multi-step: idle→generating resume)
166
+ if (this.completedDebouncePending) {
167
+ LOG.info('CLI', `[${this.type}] cancelled pending completed (resumed generating)`);
168
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
169
+ this.completedDebouncePending = null;
170
+ }
171
+
172
+ if (!this.generatingStartedAt) this.generatingStartedAt = now;
165
173
  // Defer the generating_started event — if idle comes back within 1s,
166
174
  // the whole started→completed pair was a false positive from PTY noise
167
175
  if (this.generatingDebounceTimer) clearTimeout(this.generatingDebounceTimer);
@@ -180,6 +188,10 @@ export class CliProviderInstance implements ProviderInstance {
180
188
  this.pushEvent({ event: 'agent:generating_started', ...this.generatingDebouncePending });
181
189
  this.generatingDebouncePending = null;
182
190
  }
191
+ // Cancel any pending completed
192
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
193
+ this.completedDebouncePending = null;
194
+
183
195
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
184
196
  const modal = adapterStatus.activeModal;
185
197
  LOG.info('CLI', `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? 'none'}"`);
@@ -195,15 +207,27 @@ export class CliProviderInstance implements ProviderInstance {
195
207
  LOG.info('CLI', `[${this.type}] suppressed short generating (${now - this.generatingStartedAt}ms)`);
196
208
  if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
197
209
  this.generatingDebouncePending = null;
210
+ this.generatingStartedAt = 0;
198
211
  } else {
199
- LOG.info('CLI', `[${this.type}] completed in ${duration}s`);
200
- this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now });
212
+ // Debounce completed wait 2s, if still idle then emit
213
+ if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
214
+ this.completedDebouncePending = { chatTitle, duration, timestamp: now };
215
+ this.completedDebounceTimer = setTimeout(() => {
216
+ if (this.completedDebouncePending) {
217
+ LOG.info('CLI', `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
218
+ this.pushEvent({ event: 'agent:generating_completed', ...this.completedDebouncePending });
219
+ this.completedDebouncePending = null;
220
+ this.generatingStartedAt = 0;
221
+ }
222
+ this.completedDebounceTimer = null;
223
+ }, 2000);
201
224
  }
202
- this.generatingStartedAt = 0;
203
225
  } else if (newStatus === 'stopped') {
204
226
  // Cancel any pending debounce
205
227
  if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
206
228
  this.generatingDebouncePending = null;
229
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
230
+ this.completedDebouncePending = null;
207
231
  this.pushEvent({ event: 'agent:stopped', chatTitle, timestamp: now });
208
232
  }
209
233
  this.lastStatus = newStatus;