@adhdev/daemon-core 0.9.82-rc.204 → 0.9.82-rc.205
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 +79 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -24
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/driver.d.ts +8 -0
- package/package.json +1 -1
- package/src/providers/cli-provider-instance.ts +23 -17
- package/src/providers/spec/driver.ts +96 -9
|
@@ -135,6 +135,8 @@ export declare class SpecDriver {
|
|
|
135
135
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
136
136
|
* downshift. */
|
|
137
137
|
private busyExpiryTimer;
|
|
138
|
+
/** Set true while reevaluate() is invoked from busyExpiryTimer callback. */
|
|
139
|
+
private busyExpiryFired;
|
|
138
140
|
/** Pending idle-commit timer. Armed when the evaluator first returns idle;
|
|
139
141
|
* fires after idle_hold_ms if no non-idle reading has cancelled it. */
|
|
140
142
|
private idleHoldTimer;
|
|
@@ -162,6 +164,12 @@ export declare class SpecDriver {
|
|
|
162
164
|
label: string;
|
|
163
165
|
at: number;
|
|
164
166
|
durationMs: number;
|
|
167
|
+
reason: string;
|
|
168
|
+
matchedStateId?: string;
|
|
169
|
+
matchedRules?: string[];
|
|
170
|
+
debounceKind?: string;
|
|
171
|
+
idleHoldMs?: number;
|
|
172
|
+
busyHoldMs?: number;
|
|
165
173
|
}>;
|
|
166
174
|
getLastBusyAt(): number;
|
|
167
175
|
hasIdleHoldPending(): boolean;
|
package/package.json
CHANGED
|
@@ -1568,8 +1568,6 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1568
1568
|
const shortDurationMs = this.generatingStartedAt ? now - this.generatingStartedAt : 0;
|
|
1569
1569
|
LOG.info('CLI', `[${this.type}] suppressed short generating (${shortDurationMs}ms)`);
|
|
1570
1570
|
if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
|
|
1571
|
-
this.generatingDebouncePending = null;
|
|
1572
|
-
this.generatingStartedAt = 0;
|
|
1573
1571
|
// Emit completion for mesh task association even though the UI generating
|
|
1574
1572
|
// started/completed pair is suppressed (too short for visible UI update).
|
|
1575
1573
|
let shortFinalSummary: string | undefined;
|
|
@@ -1580,22 +1578,30 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1580
1578
|
shortEvidenceSource = evidence.source;
|
|
1581
1579
|
shortFinalSummary = extractFinalSummaryFromMessages(evidence.messages as any);
|
|
1582
1580
|
} catch { /* best-effort */ }
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
this.pushEvent({
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
reason: 'short_generating_suppressed',
|
|
1594
|
-
shortDurationMs,
|
|
1595
|
-
finalAssistantEvidenceSource: shortEvidenceSource,
|
|
1596
|
-
},
|
|
1597
|
-
});
|
|
1581
|
+
// If a real response is confirmed, retroactively emit started so the chat
|
|
1582
|
+
// bubble appears even though the debounce suppressed the original event.
|
|
1583
|
+
if (shortFinalSummary) {
|
|
1584
|
+
this.pushEvent({ event: 'agent:generating_started', chatTitle, timestamp: now - shortDurationMs });
|
|
1585
|
+
}
|
|
1586
|
+
this.generatingDebouncePending = null;
|
|
1587
|
+
this.generatingStartedAt = 0;
|
|
1588
|
+
const missingEvidence = ((this.provider as any).requiresFinalAssistantBeforeIdle === true || shortEvidenceSource === 'external-native') && !shortFinalSummary;
|
|
1589
|
+
if (missingEvidence) {
|
|
1590
|
+
LOG.warn('CLI', `[${this.type}] short completion missing final assistant evidence (source=${shortEvidenceSource})`);
|
|
1598
1591
|
}
|
|
1592
|
+
this.pushEvent({
|
|
1593
|
+
event: 'agent:generating_completed',
|
|
1594
|
+
chatTitle,
|
|
1595
|
+
duration: 0,
|
|
1596
|
+
timestamp: now,
|
|
1597
|
+
finalSummary: shortFinalSummary,
|
|
1598
|
+
completionDiagnostic: {
|
|
1599
|
+
reason: 'short_generating_suppressed',
|
|
1600
|
+
shortDurationMs,
|
|
1601
|
+
finalAssistantEvidenceSource: shortEvidenceSource,
|
|
1602
|
+
...(missingEvidence ? { blockReason: 'missing_final_assistant' } : {}),
|
|
1603
|
+
},
|
|
1604
|
+
});
|
|
1599
1605
|
} else {
|
|
1600
1606
|
// Debounce completed, then require the rich transcript path that read_chat
|
|
1601
1607
|
// uses to show an idle turn whose last user-facing message is assistant.
|
|
@@ -218,6 +218,8 @@ export class SpecDriver {
|
|
|
218
218
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
219
219
|
* downshift. */
|
|
220
220
|
private busyExpiryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
221
|
+
/** Set true while reevaluate() is invoked from busyExpiryTimer callback. */
|
|
222
|
+
private busyExpiryFired = false;
|
|
221
223
|
/** Pending idle-commit timer. Armed when the evaluator first returns idle;
|
|
222
224
|
* fires after idle_hold_ms if no non-idle reading has cancelled it. */
|
|
223
225
|
private idleHoldTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -225,7 +227,18 @@ export class SpecDriver {
|
|
|
225
227
|
private pendingIdleState: SpecEvaluation['state'] | null = null;
|
|
226
228
|
private specWatcher: fs.FSWatcher | null = null;
|
|
227
229
|
/** Ring buffer of committed state transitions (max 50). */
|
|
228
|
-
private stateHistory: Array<{
|
|
230
|
+
private stateHistory: Array<{
|
|
231
|
+
stateId: string;
|
|
232
|
+
label: string;
|
|
233
|
+
at: number;
|
|
234
|
+
durationMs: number;
|
|
235
|
+
reason: string;
|
|
236
|
+
matchedStateId?: string;
|
|
237
|
+
matchedRules?: string[];
|
|
238
|
+
debounceKind?: string;
|
|
239
|
+
idleHoldMs?: number;
|
|
240
|
+
busyHoldMs?: number;
|
|
241
|
+
}> = [];
|
|
229
242
|
private prevStateAt = 0;
|
|
230
243
|
|
|
231
244
|
constructor(private readonly opts: SpecDriverOpts) {
|
|
@@ -296,15 +309,44 @@ export class SpecDriver {
|
|
|
296
309
|
this.pendingIdleState = null;
|
|
297
310
|
}
|
|
298
311
|
|
|
299
|
-
private pushHistory(stateId: string, label: string
|
|
312
|
+
private pushHistory(stateId: string, label: string, meta?: {
|
|
313
|
+
reason?: string;
|
|
314
|
+
matchedStateId?: string;
|
|
315
|
+
matchedRules?: string[];
|
|
316
|
+
debounceKind?: string;
|
|
317
|
+
idleHoldMs?: number;
|
|
318
|
+
busyHoldMs?: number;
|
|
319
|
+
}): void {
|
|
300
320
|
const now = Date.now();
|
|
301
321
|
const durationMs = this.prevStateAt > 0 ? now - this.prevStateAt : 0;
|
|
302
322
|
this.prevStateAt = now;
|
|
303
|
-
this.stateHistory.push({
|
|
323
|
+
this.stateHistory.push({
|
|
324
|
+
stateId,
|
|
325
|
+
label,
|
|
326
|
+
at: now,
|
|
327
|
+
durationMs,
|
|
328
|
+
reason: meta?.reason ?? 'eval_match',
|
|
329
|
+
...(meta?.matchedStateId !== undefined ? { matchedStateId: meta.matchedStateId } : {}),
|
|
330
|
+
...(meta?.matchedRules !== undefined ? { matchedRules: meta.matchedRules } : {}),
|
|
331
|
+
...(meta?.debounceKind !== undefined ? { debounceKind: meta.debounceKind } : {}),
|
|
332
|
+
...(meta?.idleHoldMs !== undefined ? { idleHoldMs: meta.idleHoldMs } : {}),
|
|
333
|
+
...(meta?.busyHoldMs !== undefined ? { busyHoldMs: meta.busyHoldMs } : {}),
|
|
334
|
+
});
|
|
304
335
|
if (this.stateHistory.length > 50) this.stateHistory.shift();
|
|
305
336
|
}
|
|
306
337
|
|
|
307
|
-
getStateHistory(): ReadonlyArray<{
|
|
338
|
+
getStateHistory(): ReadonlyArray<{
|
|
339
|
+
stateId: string;
|
|
340
|
+
label: string;
|
|
341
|
+
at: number;
|
|
342
|
+
durationMs: number;
|
|
343
|
+
reason: string;
|
|
344
|
+
matchedStateId?: string;
|
|
345
|
+
matchedRules?: string[];
|
|
346
|
+
debounceKind?: string;
|
|
347
|
+
idleHoldMs?: number;
|
|
348
|
+
busyHoldMs?: number;
|
|
349
|
+
}> {
|
|
308
350
|
return this.stateHistory;
|
|
309
351
|
}
|
|
310
352
|
|
|
@@ -381,7 +423,9 @@ export class SpecDriver {
|
|
|
381
423
|
this.busyExpiryTimer = setTimeout(() => {
|
|
382
424
|
this.busyExpiryTimer = null;
|
|
383
425
|
LOG.debug('SpecDriver', `[${this.opts.specPath.split('/').slice(-3).join('/')}] busyExpiry fired holdMs=${holdMs}`);
|
|
426
|
+
this.busyExpiryFired = true;
|
|
384
427
|
this.reevaluate();
|
|
428
|
+
this.busyExpiryFired = false;
|
|
385
429
|
}, Math.max(holdMs + 50, 100));
|
|
386
430
|
}
|
|
387
431
|
|
|
@@ -477,6 +521,8 @@ export class SpecDriver {
|
|
|
477
521
|
if (isIdleState && idleHoldMs > 0 && this.currentStateId !== evState.id) {
|
|
478
522
|
if (!this.idleHoldTimer) {
|
|
479
523
|
this.pendingIdleState = evState;
|
|
524
|
+
const capturedEv = ev;
|
|
525
|
+
const capturedMatchedRules = extractMatchedRules(ev);
|
|
480
526
|
this.idleHoldTimer = setTimeout(() => {
|
|
481
527
|
this.idleHoldTimer = null;
|
|
482
528
|
const committed = this.pendingIdleState;
|
|
@@ -484,16 +530,22 @@ export class SpecDriver {
|
|
|
484
530
|
if (!committed) return;
|
|
485
531
|
LOG.debug('SpecDriver', `[${this.opts.specPath.split('/').slice(-3).join('/')}] idleHold committed after ${idleHoldMs}ms`);
|
|
486
532
|
this.currentStateId = committed.id;
|
|
487
|
-
this.currentEval =
|
|
488
|
-
this.pushHistory(committed.id, committed.label
|
|
533
|
+
this.currentEval = capturedEv;
|
|
534
|
+
this.pushHistory(committed.id, committed.label, {
|
|
535
|
+
reason: 'idle_hold_committed',
|
|
536
|
+
matchedStateId: capturedEv.state.id,
|
|
537
|
+
matchedRules: capturedMatchedRules,
|
|
538
|
+
debounceKind: 'idle_hold',
|
|
539
|
+
idleHoldMs,
|
|
540
|
+
});
|
|
489
541
|
this.emit({
|
|
490
542
|
kind: 'state_changed',
|
|
491
543
|
state: committed,
|
|
492
544
|
modal: null,
|
|
493
|
-
controls:
|
|
545
|
+
controls: capturedEv.controls.map(c => ({ id: c.id, label: c.label, action_type: c.actionType })),
|
|
494
546
|
});
|
|
495
547
|
this.armOrCancelDelegateTimers(committed.id);
|
|
496
|
-
if (this.opts.emitTrace) this.emit({ kind: 'spec_trace', entries:
|
|
548
|
+
if (this.opts.emitTrace) this.emit({ kind: 'spec_trace', entries: capturedEv.trace });
|
|
497
549
|
}, idleHoldMs);
|
|
498
550
|
}
|
|
499
551
|
// Don't fall through to the normal changed/emit path for idle.
|
|
@@ -538,7 +590,34 @@ export class SpecDriver {
|
|
|
538
590
|
}
|
|
539
591
|
if (changed) {
|
|
540
592
|
this.currentStateId = evState.id;
|
|
541
|
-
|
|
593
|
+
const matchedRules = extractMatchedRules(ev);
|
|
594
|
+
// Determine reason and debounce kind for this transition
|
|
595
|
+
let transitionReason: string;
|
|
596
|
+
let debounceKind: string;
|
|
597
|
+
let transitionBusyHoldMs: number | undefined;
|
|
598
|
+
if (forceEmit) {
|
|
599
|
+
transitionReason = 'forceEmit';
|
|
600
|
+
debounceKind = 'none';
|
|
601
|
+
} else if (evState.id !== ev.state.id) {
|
|
602
|
+
// evState was overridden — completion_idle_after forced idle
|
|
603
|
+
transitionReason = 'completion_idle_after';
|
|
604
|
+
debounceKind = 'completion_idle_after';
|
|
605
|
+
} else if (this.busyExpiryFired) {
|
|
606
|
+
// busyExpiryTimer woke us up and hold has now expired
|
|
607
|
+
transitionReason = 'busy_hold_expired';
|
|
608
|
+
debounceKind = 'busy_hold';
|
|
609
|
+
transitionBusyHoldMs = busyHoldMs;
|
|
610
|
+
} else {
|
|
611
|
+
transitionReason = 'eval_match';
|
|
612
|
+
debounceKind = 'none';
|
|
613
|
+
}
|
|
614
|
+
this.pushHistory(evState.id, evState.label, {
|
|
615
|
+
reason: transitionReason,
|
|
616
|
+
matchedStateId: ev.state.id,
|
|
617
|
+
matchedRules,
|
|
618
|
+
debounceKind,
|
|
619
|
+
...(transitionBusyHoldMs !== undefined ? { busyHoldMs: transitionBusyHoldMs } : {}),
|
|
620
|
+
});
|
|
542
621
|
this.emit({
|
|
543
622
|
kind: 'state_changed',
|
|
544
623
|
state: evState,
|
|
@@ -731,3 +810,11 @@ function guessExt(mime: string): string {
|
|
|
731
810
|
if (/webp/i.test(mime)) return '.webp';
|
|
732
811
|
return '.bin';
|
|
733
812
|
}
|
|
813
|
+
|
|
814
|
+
function extractMatchedRules(ev: SpecEvaluation): string[] {
|
|
815
|
+
if (!Array.isArray(ev.trace)) return [];
|
|
816
|
+
return (ev.trace as any[])
|
|
817
|
+
.filter(t => t.matched === true || t.kind === 'state_match')
|
|
818
|
+
.map(t => t.rule ?? t.stateId ?? t.id ?? String(t))
|
|
819
|
+
.filter(Boolean);
|
|
820
|
+
}
|