@adhdev/daemon-core 0.9.82-rc.211 → 0.9.82-rc.212
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 +32 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -7
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/driver.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +8 -0
- package/dist/providers/spec/types.d.ts +6 -0
- package/package.json +1 -1
- package/src/mesh/mesh-events-utils.ts +6 -1
- package/src/providers/cli-provider-instance.ts +4 -3
- package/src/providers/spec/driver.ts +35 -4
- package/src/providers/spec/loader.ts +1 -0
- package/src/providers/spec/schema.gen.ts +1 -0
- package/src/providers/spec/schema.json +1 -0
- package/src/providers/spec/types.ts +6 -0
|
@@ -146,6 +146,10 @@ export declare class SpecDriver {
|
|
|
146
146
|
private completionIdleKey;
|
|
147
147
|
/** Previous screen lines — passed to evaluate() for `changed` condition detection. */
|
|
148
148
|
private prevScreenLines;
|
|
149
|
+
/** Timestamp of the last PTY frame that changed the screen content.
|
|
150
|
+
* Used by screen_active_hold_ms to suppress idle downshifts while
|
|
151
|
+
* the terminal is still actively updating. */
|
|
152
|
+
private lastScreenChangedAt;
|
|
149
153
|
/** Timer that re-runs evaluate() once the hold window expires. Needed
|
|
150
154
|
* because the PTY stops emitting once the agent finishes; without an
|
|
151
155
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
@@ -755,6 +755,10 @@ export declare const SCHEMA_V3: {
|
|
|
755
755
|
readonly type: "integer";
|
|
756
756
|
readonly minimum: 0;
|
|
757
757
|
};
|
|
758
|
+
readonly screen_active_hold_ms: {
|
|
759
|
+
readonly type: "integer";
|
|
760
|
+
readonly minimum: 0;
|
|
761
|
+
};
|
|
758
762
|
readonly completion_marker: {
|
|
759
763
|
readonly type: "object";
|
|
760
764
|
readonly additionalProperties: false;
|
|
@@ -1451,6 +1455,10 @@ export declare const SCHEMA: {
|
|
|
1451
1455
|
readonly type: "integer";
|
|
1452
1456
|
readonly minimum: 0;
|
|
1453
1457
|
};
|
|
1458
|
+
readonly screen_active_hold_ms: {
|
|
1459
|
+
readonly type: "integer";
|
|
1460
|
+
readonly minimum: 0;
|
|
1461
|
+
};
|
|
1454
1462
|
readonly completion_marker: {
|
|
1455
1463
|
readonly type: "object";
|
|
1456
1464
|
readonly additionalProperties: false;
|
|
@@ -225,6 +225,11 @@ export interface CliSpec {
|
|
|
225
225
|
busy_hold_ms?: number;
|
|
226
226
|
idle_hold_ms?: number;
|
|
227
227
|
startup_grace_ms?: number;
|
|
228
|
+
/** Suppress idle downshift while the screen is actively changing.
|
|
229
|
+
* When set, any PTY frame that changes the screen resets a timer;
|
|
230
|
+
* idle transitions (busy_hold expiry and completion_idle_after) are
|
|
231
|
+
* blocked until the screen has been stable for this many ms. */
|
|
232
|
+
screen_active_hold_ms?: number;
|
|
228
233
|
completion_marker?: {
|
|
229
234
|
section?: string;
|
|
230
235
|
matches: string;
|
|
@@ -243,6 +248,7 @@ export interface CliSpec {
|
|
|
243
248
|
busy_hold_ms?: number;
|
|
244
249
|
idle_hold_ms?: number;
|
|
245
250
|
startup_grace_ms?: number;
|
|
251
|
+
screen_active_hold_ms?: number;
|
|
246
252
|
/**
|
|
247
253
|
* Legacy field name for completion_marker. Populated by the loader
|
|
248
254
|
* from `timing.completion_marker` for backward compat.
|
package/package.json
CHANGED
|
@@ -38,12 +38,14 @@ function formatCompletionMetadata(event: Record<string, unknown>): string {
|
|
|
38
38
|
const finalAssistantPresent = typeof completionDiagnostic?.finalAssistantPresent === 'boolean'
|
|
39
39
|
? String(completionDiagnostic.finalAssistantPresent)
|
|
40
40
|
: '';
|
|
41
|
+
const evidenceLevel = readNonEmptyString(event.evidenceLevel);
|
|
41
42
|
const parts = [
|
|
42
43
|
readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : '',
|
|
43
44
|
readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : '',
|
|
44
45
|
readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : '',
|
|
45
46
|
diagnosticReason ? `completion_diagnostic=${diagnosticReason}` : '',
|
|
46
47
|
finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : '',
|
|
48
|
+
evidenceLevel && evidenceLevel !== 'sufficient' ? `evidence_level=${evidenceLevel}` : '',
|
|
47
49
|
].filter(Boolean);
|
|
48
50
|
return parts.length > 0 ? ` (${parts.join('; ')})` : '';
|
|
49
51
|
}
|
|
@@ -59,7 +61,10 @@ export function buildMeshSystemMessage(args: {
|
|
|
59
61
|
if (args.metadataEvent.source === 'long_generating_reconciliation') {
|
|
60
62
|
return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The long-generating monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
|
|
61
63
|
}
|
|
62
|
-
|
|
64
|
+
const reviewNote = args.metadataEvent.reviewRecommended === true
|
|
65
|
+
? ' Completion evidence is insufficient — verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly.'
|
|
66
|
+
: ' Use mesh_read_chat once to review its final progress, but do not poll repeatedly.';
|
|
67
|
+
return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
|
|
63
68
|
}
|
|
64
69
|
if (args.event === 'agent:waiting_approval') {
|
|
65
70
|
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
@@ -1544,9 +1544,10 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1544
1544
|
message: typeof modal?.message === 'string' ? modal.message.trim() : '',
|
|
1545
1545
|
buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button: unknown) => String(button).trim()) : [],
|
|
1546
1546
|
});
|
|
1547
|
-
// PTY redraws
|
|
1548
|
-
//
|
|
1549
|
-
|
|
1547
|
+
// PTY redraws repeat the same modal content; fingerprint dedup prevents duplicate events.
|
|
1548
|
+
// Do NOT also gate on lastStatus: consecutive approvals can arrive waiting_approval→waiting_approval
|
|
1549
|
+
// (e.g. antigravity-cli resolves one prompt and immediately shows the next) and would be silently dropped.
|
|
1550
|
+
if (approvalFingerprint !== this.lastApprovalEventFingerprint) {
|
|
1550
1551
|
this.lastApprovalEventFingerprint = approvalFingerprint;
|
|
1551
1552
|
this.appendRuntimeSystemMessage(
|
|
1552
1553
|
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
@@ -230,6 +230,10 @@ export class SpecDriver {
|
|
|
230
230
|
private completionIdleKey = '';
|
|
231
231
|
/** Previous screen lines — passed to evaluate() for `changed` condition detection. */
|
|
232
232
|
private prevScreenLines: string[] = [];
|
|
233
|
+
/** Timestamp of the last PTY frame that changed the screen content.
|
|
234
|
+
* Used by screen_active_hold_ms to suppress idle downshifts while
|
|
235
|
+
* the terminal is still actively updating. */
|
|
236
|
+
private lastScreenChangedAt = 0;
|
|
233
237
|
/** Timer that re-runs evaluate() once the hold window expires. Needed
|
|
234
238
|
* because the PTY stops emitting once the agent finishes; without an
|
|
235
239
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
@@ -470,9 +474,14 @@ export class SpecDriver {
|
|
|
470
474
|
private reevaluate(forceEmit = false): void {
|
|
471
475
|
const screen = this.adapter.snapshot();
|
|
472
476
|
const cursor = this.adapter.getCursorPosition();
|
|
477
|
+
const currentLines = screen.split('\n').map(l => l.endsWith('\r') ? l.slice(0, -1) : l);
|
|
473
478
|
const ev = evaluate(this.spec, screen, cursor, this.prevScreenLines.length > 0 ? this.prevScreenLines : undefined);
|
|
479
|
+
// Track when the screen last changed for screen_active_hold_ms.
|
|
480
|
+
if (this.prevScreenLines.length > 0 && currentLines.join('\n') !== this.prevScreenLines.join('\n')) {
|
|
481
|
+
this.lastScreenChangedAt = Date.now();
|
|
482
|
+
}
|
|
474
483
|
// Update prevScreenLines for next evaluation's `changed` condition detection.
|
|
475
|
-
this.prevScreenLines =
|
|
484
|
+
this.prevScreenLines = currentLines;
|
|
476
485
|
|
|
477
486
|
// Busy hold: many TUIs flicker between busy and idle every frame
|
|
478
487
|
// (claude in particular — its token counter appears and disappears
|
|
@@ -513,6 +522,7 @@ export class SpecDriver {
|
|
|
513
522
|
}
|
|
514
523
|
}
|
|
515
524
|
const completionIdleRule = this.spec.debounce?.completion_idle_after;
|
|
525
|
+
const screenActiveHoldMs = this.spec.debounce?.screen_active_hold_ms;
|
|
516
526
|
let busyWakeMs = busyHoldMs;
|
|
517
527
|
// Don't fire completion_idle_after while in a modal state (approval,
|
|
518
528
|
// picker, etc.) or within a grace period after leaving one. Two cases:
|
|
@@ -525,7 +535,18 @@ export class SpecDriver {
|
|
|
525
535
|
const now = Date.now();
|
|
526
536
|
const recentlyInModal = isModalState(this.currentStateId) || (this.lastModalAt > 0 && now - this.lastModalAt < postModalGraceMs);
|
|
527
537
|
const recentlyLeftModal = !recentlyInModal && this.lastModalExitAt > 0 && now - this.lastModalExitAt < postModalGraceMs;
|
|
528
|
-
|
|
538
|
+
// screen_active_hold_ms: suppress idle downshift while the screen is
|
|
539
|
+
// actively changing. Any PTY frame that mutates screen content resets
|
|
540
|
+
// lastScreenChangedAt; we hold off until it has been stable for the
|
|
541
|
+
// configured duration. Applies to both completion_idle_after and the
|
|
542
|
+
// busy_hold expiry path below.
|
|
543
|
+
const screenActiveMs = screenActiveHoldMs ?? 0;
|
|
544
|
+
const screenStableMs = this.lastScreenChangedAt > 0 ? now - this.lastScreenChangedAt : Infinity;
|
|
545
|
+
const screenIsActive = screenActiveMs > 0 && screenStableMs < screenActiveMs;
|
|
546
|
+
if (screenIsActive) {
|
|
547
|
+
busyWakeMs = Math.min(busyWakeMs, screenActiveMs - screenStableMs + 50);
|
|
548
|
+
}
|
|
549
|
+
if (evState.id === 'busy' && completionIdleRule && !recentlyInModal && !recentlyLeftModal && !screenIsActive) {
|
|
529
550
|
const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
|
|
530
551
|
if (completionKey) {
|
|
531
552
|
const now = Date.now();
|
|
@@ -560,8 +581,18 @@ export class SpecDriver {
|
|
|
560
581
|
this.completionIdleFirstSeenAt = 0;
|
|
561
582
|
}
|
|
562
583
|
} else if (evState.id !== 'busy') {
|
|
563
|
-
|
|
564
|
-
|
|
584
|
+
if (!screenIsActive) {
|
|
585
|
+
this.completionIdleKey = '';
|
|
586
|
+
this.completionIdleFirstSeenAt = 0;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// screen_active_hold_ms: if the screen is still changing and the
|
|
591
|
+
// evaluator wants to downshift from busy to idle, pin to busy instead.
|
|
592
|
+
// This catches paths not covered by the completion_idle_after gate above
|
|
593
|
+
// (e.g. direct idle via busy_hold expiry or default-state fallback).
|
|
594
|
+
if (screenIsActive && this.currentStateId === 'busy' && evState.id === (this.spec.default_state ?? 'idle')) {
|
|
595
|
+
evState = this.lastBusyState ?? evState;
|
|
565
596
|
}
|
|
566
597
|
|
|
567
598
|
if (evState.id === 'busy') {
|
|
@@ -79,6 +79,7 @@ function attachDebounceAlias(spec: CliSpec): void {
|
|
|
79
79
|
...(t.busy_hold_ms !== undefined ? { busy_hold_ms: t.busy_hold_ms } : {}),
|
|
80
80
|
...(t.idle_hold_ms !== undefined ? { idle_hold_ms: t.idle_hold_ms } : {}),
|
|
81
81
|
...(t.startup_grace_ms !== undefined ? { startup_grace_ms: t.startup_grace_ms } : {}),
|
|
82
|
+
...(t.screen_active_hold_ms !== undefined ? { screen_active_hold_ms: t.screen_active_hold_ms } : {}),
|
|
82
83
|
...(cm ? {
|
|
83
84
|
completion_idle_after: {
|
|
84
85
|
...(cm.section ? { section: cm.section } : {}),
|
|
@@ -384,6 +384,7 @@ export const SCHEMA_V3 = {
|
|
|
384
384
|
"busy_hold_ms": { "type": "integer", "minimum": 0 },
|
|
385
385
|
"idle_hold_ms": { "type": "integer", "minimum": 0 },
|
|
386
386
|
"startup_grace_ms": { "type": "integer", "minimum": 0 },
|
|
387
|
+
"screen_active_hold_ms": { "type": "integer", "minimum": 0 },
|
|
387
388
|
"completion_marker": {
|
|
388
389
|
"type": "object",
|
|
389
390
|
"additionalProperties": false,
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"busy_hold_ms": { "type": "integer", "minimum": 0 },
|
|
47
47
|
"idle_hold_ms": { "type": "integer", "minimum": 0 },
|
|
48
48
|
"startup_grace_ms": { "type": "integer", "minimum": 0 },
|
|
49
|
+
"screen_active_hold_ms": { "type": "integer", "minimum": 0 },
|
|
49
50
|
"completion_marker": {
|
|
50
51
|
"type": "object",
|
|
51
52
|
"additionalProperties": false,
|
|
@@ -273,6 +273,11 @@ export interface CliSpec {
|
|
|
273
273
|
busy_hold_ms?: number;
|
|
274
274
|
idle_hold_ms?: number;
|
|
275
275
|
startup_grace_ms?: number;
|
|
276
|
+
/** Suppress idle downshift while the screen is actively changing.
|
|
277
|
+
* When set, any PTY frame that changes the screen resets a timer;
|
|
278
|
+
* idle transitions (busy_hold expiry and completion_idle_after) are
|
|
279
|
+
* blocked until the screen has been stable for this many ms. */
|
|
280
|
+
screen_active_hold_ms?: number;
|
|
276
281
|
completion_marker?: {
|
|
277
282
|
section?: string;
|
|
278
283
|
matches: string;
|
|
@@ -291,6 +296,7 @@ export interface CliSpec {
|
|
|
291
296
|
busy_hold_ms?: number;
|
|
292
297
|
idle_hold_ms?: number;
|
|
293
298
|
startup_grace_ms?: number;
|
|
299
|
+
screen_active_hold_ms?: number;
|
|
294
300
|
/**
|
|
295
301
|
* Legacy field name for completion_marker. Populated by the loader
|
|
296
302
|
* from `timing.completion_marker` for backward compat.
|