@adhdev/daemon-core 0.5.31 → 0.5.33

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.33",
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",
@@ -47,9 +47,11 @@ export class DaemonAgentStreamManager {
47
47
  if (providerLoader) {
48
48
  const allExtProviders = providerLoader.getByCategory('extension');
49
49
  for (const p of allExtProviders) {
50
- const adapter = new ProviderStreamAdapter(p);
50
+ const resolved = providerLoader.resolve(p.type);
51
+ if (!resolved) continue;
52
+ const adapter = new ProviderStreamAdapter(resolved);
51
53
  this.allAdapters.push(adapter);
52
- this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name})`);
54
+ this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name}) scripts=${Object.keys(resolved.scripts || {}).join(',') || 'none'}`);
53
55
  }
54
56
  }
55
57
  }
@@ -147,6 +149,7 @@ export class DaemonAgentStreamManager {
147
149
  const evaluate: AgentEvaluateFn = (expr, timeout) =>
148
150
  cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
149
151
  const state = await agent.adapter.readChat(evaluate);
152
+ this.logFn(`[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${state.model || ''}${state.status === 'error' ? ' error=' + JSON.stringify((state as any).error || (state as any)._error || 'unknown') : ''}`);
150
153
  agent.lastState = state;
151
154
  agent.lastError = null;
152
155
  if (state.status === 'panel_hidden') {
@@ -133,6 +133,7 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
133
133
  status: 'error',
134
134
  messages: [],
135
135
  inputContent: '',
136
- };
136
+ _error: message,
137
+ } as any;
137
138
  }
138
139
  }
@@ -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;
@@ -28,6 +28,7 @@ export class CliProviderInstance implements ProviderInstance {
28
28
  private monitor: StatusMonitor;
29
29
  private generatingDebounceTimer: NodeJS.Timeout | null = null;
30
30
  private generatingDebouncePending: { chatTitle: string; timestamp: number } | null = null;
31
+ private lastApprovalEventAt = 0;
31
32
  private historyWriter: ChatHistoryWriter;
32
33
  readonly instanceId: string;
33
34
 
@@ -149,7 +150,8 @@ export class CliProviderInstance implements ProviderInstance {
149
150
  this.monitor.reset();
150
151
  }
151
152
 
152
- // ─── Status transition detection (moved from daemon-status.ts) ──────
153
+ private completedDebounceTimer: NodeJS.Timeout | null = null;
154
+ private completedDebouncePending: { chatTitle: string; duration: number; timestamp: number } | null = null;
153
155
 
154
156
  private detectStatusTransition(): void {
155
157
  const now = Date.now();
@@ -161,7 +163,14 @@ export class CliProviderInstance implements ProviderInstance {
161
163
  if (newStatus !== this.lastStatus) {
162
164
  LOG.info('CLI', `[${this.type}] status: ${this.lastStatus} → ${newStatus}`);
163
165
  if (this.lastStatus === 'idle' && newStatus === 'generating') {
164
- this.generatingStartedAt = now;
166
+ // Cancel any pending completed event (multi-step: idle→generating resume)
167
+ if (this.completedDebouncePending) {
168
+ LOG.info('CLI', `[${this.type}] cancelled pending completed (resumed generating)`);
169
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
170
+ this.completedDebouncePending = null;
171
+ }
172
+
173
+ if (!this.generatingStartedAt) this.generatingStartedAt = now;
165
174
  // Defer the generating_started event — if idle comes back within 1s,
166
175
  // the whole started→completed pair was a false positive from PTY noise
167
176
  if (this.generatingDebounceTimer) clearTimeout(this.generatingDebounceTimer);
@@ -180,14 +189,23 @@ export class CliProviderInstance implements ProviderInstance {
180
189
  this.pushEvent({ event: 'agent:generating_started', ...this.generatingDebouncePending });
181
190
  this.generatingDebouncePending = null;
182
191
  }
192
+ // Cancel any pending completed
193
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
194
+ this.completedDebouncePending = null;
195
+
183
196
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
184
197
  const modal = adapterStatus.activeModal;
185
198
  LOG.info('CLI', `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? 'none'}"`);
186
- this.pushEvent({
187
- event: 'agent:waiting_approval', chatTitle, timestamp: now,
188
- modalMessage: modal?.message,
189
- modalButtons: modal?.buttons,
190
- });
199
+ // Only push event if not already in waiting_approval (prevent flood from rapid cycles)
200
+ const approvalCooldown = 5000;
201
+ if (this.lastStatus !== 'waiting_approval' && (!this.lastApprovalEventAt || now - this.lastApprovalEventAt > approvalCooldown)) {
202
+ this.lastApprovalEventAt = now;
203
+ this.pushEvent({
204
+ event: 'agent:waiting_approval', chatTitle, timestamp: now,
205
+ modalMessage: modal?.message,
206
+ modalButtons: modal?.buttons,
207
+ });
208
+ }
191
209
  } else if (newStatus === 'idle' && (this.lastStatus === 'generating' || this.lastStatus === 'waiting_approval')) {
192
210
  const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1000) : 0;
193
211
  // If debounce still pending (generating lasted < 1s), cancel both events
@@ -195,15 +213,27 @@ export class CliProviderInstance implements ProviderInstance {
195
213
  LOG.info('CLI', `[${this.type}] suppressed short generating (${now - this.generatingStartedAt}ms)`);
196
214
  if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
197
215
  this.generatingDebouncePending = null;
216
+ this.generatingStartedAt = 0;
198
217
  } else {
199
- LOG.info('CLI', `[${this.type}] completed in ${duration}s`);
200
- this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now });
218
+ // Debounce completed wait 2s, if still idle then emit
219
+ if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
220
+ this.completedDebouncePending = { chatTitle, duration, timestamp: now };
221
+ this.completedDebounceTimer = setTimeout(() => {
222
+ if (this.completedDebouncePending) {
223
+ LOG.info('CLI', `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
224
+ this.pushEvent({ event: 'agent:generating_completed', ...this.completedDebouncePending });
225
+ this.completedDebouncePending = null;
226
+ this.generatingStartedAt = 0;
227
+ }
228
+ this.completedDebounceTimer = null;
229
+ }, 2000);
201
230
  }
202
- this.generatingStartedAt = 0;
203
231
  } else if (newStatus === 'stopped') {
204
232
  // Cancel any pending debounce
205
233
  if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
206
234
  this.generatingDebouncePending = null;
235
+ if (this.completedDebounceTimer) { clearTimeout(this.completedDebounceTimer); this.completedDebounceTimer = null; }
236
+ this.completedDebouncePending = null;
207
237
  this.pushEvent({ event: 'agent:stopped', chatTitle, timestamp: now });
208
238
  }
209
239
  this.lastStatus = newStatus;