@adhdev/daemon-core 0.9.82-rc.188 → 0.9.82-rc.189
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 +58 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -5
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/adapter.d.ts +4 -0
- package/dist/providers/spec/driver.d.ts +4 -0
- package/dist/providers/spec/evaluator.d.ts +9 -1
- package/dist/providers/spec/schema.gen.d.ts +16 -0
- package/dist/providers/spec/types.d.ts +15 -0
- package/package.json +1 -1
- package/src/mesh/mesh-events.ts +7 -0
- package/src/providers/spec/adapter.ts +8 -0
- package/src/providers/spec/driver.ts +6 -1
- package/src/providers/spec/evaluator.ts +39 -3
- package/src/providers/spec/schema.gen.ts +16 -0
- package/src/providers/spec/schema.json +5 -1
- package/src/providers/spec/types.ts +15 -0
|
@@ -49,6 +49,10 @@ export declare class TerminalAdapter {
|
|
|
49
49
|
send_keys(s: string): void;
|
|
50
50
|
resize(cols: number, rows: number): void;
|
|
51
51
|
snapshot(): string;
|
|
52
|
+
getCursorPosition(): {
|
|
53
|
+
row: number;
|
|
54
|
+
col: number;
|
|
55
|
+
};
|
|
52
56
|
kill(): void;
|
|
53
57
|
private onChunk;
|
|
54
58
|
private computeScreen;
|
|
@@ -139,6 +139,10 @@ export declare class SpecDriver {
|
|
|
139
139
|
start(): void;
|
|
140
140
|
dispatch(cmd: DashboardCommand): void;
|
|
141
141
|
snapshot(): string;
|
|
142
|
+
getCursorPosition(): {
|
|
143
|
+
row: number;
|
|
144
|
+
col: number;
|
|
145
|
+
};
|
|
142
146
|
shutdown(): void;
|
|
143
147
|
private loadSpecOrThrow;
|
|
144
148
|
private buildAdapterOpts;
|
|
@@ -44,4 +44,12 @@ export interface SpecEvaluation {
|
|
|
44
44
|
sections: ResolvedSection[];
|
|
45
45
|
trace: TraceEntry[];
|
|
46
46
|
}
|
|
47
|
-
export declare function evaluate(spec: CliSpec, screenText: string
|
|
47
|
+
export declare function evaluate(spec: CliSpec, screenText: string,
|
|
48
|
+
/** Optional cursor position (0-based row and col). When supplied, states
|
|
49
|
+
* with cursor_row_min/max or cursor_col_min/max predicates are filtered.
|
|
50
|
+
* When omitted, cursor predicates are ignored and evaluation is text-only
|
|
51
|
+
* (backward-compatible with all existing specs and call sites). */
|
|
52
|
+
cursor?: {
|
|
53
|
+
row: number;
|
|
54
|
+
col: number;
|
|
55
|
+
}): SpecEvaluation;
|
|
@@ -203,6 +203,22 @@ export declare const SCHEMA: {
|
|
|
203
203
|
readonly type: "string";
|
|
204
204
|
readonly default: "i";
|
|
205
205
|
};
|
|
206
|
+
readonly cursor_row_min: {
|
|
207
|
+
readonly type: "integer";
|
|
208
|
+
readonly minimum: 0;
|
|
209
|
+
};
|
|
210
|
+
readonly cursor_row_max: {
|
|
211
|
+
readonly type: "integer";
|
|
212
|
+
readonly minimum: 0;
|
|
213
|
+
};
|
|
214
|
+
readonly cursor_col_min: {
|
|
215
|
+
readonly type: "integer";
|
|
216
|
+
readonly minimum: 0;
|
|
217
|
+
};
|
|
218
|
+
readonly cursor_col_max: {
|
|
219
|
+
readonly type: "integer";
|
|
220
|
+
readonly minimum: 0;
|
|
221
|
+
};
|
|
206
222
|
};
|
|
207
223
|
};
|
|
208
224
|
readonly sectionPattern: {
|
|
@@ -11,6 +11,21 @@ export interface SectionRegex {
|
|
|
11
11
|
section?: string;
|
|
12
12
|
regex: string;
|
|
13
13
|
flags?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional cursor-position guards. When present, the state is only
|
|
16
|
+
* considered matched if the terminal cursor row/column satisfies the
|
|
17
|
+
* bounds (0-based, inclusive). Missing or undefined means "no constraint".
|
|
18
|
+
*
|
|
19
|
+
* Use case: distinguish modal zone from body zone for TUIs that use
|
|
20
|
+
* cursor position rather than distinct text to locate the active prompt
|
|
21
|
+
* (e.g. Antigravity cursor lands in modal_zone rows 8-31 when approval
|
|
22
|
+
* is visible, never in body rows 0-7). Without this guard, body text
|
|
23
|
+
* containing "Do you want to proceed?" could false-positive a modal match.
|
|
24
|
+
*/
|
|
25
|
+
cursor_row_min?: number;
|
|
26
|
+
cursor_row_max?: number;
|
|
27
|
+
cursor_col_min?: number;
|
|
28
|
+
cursor_col_max?: number;
|
|
14
29
|
}
|
|
15
30
|
export interface SectionPattern {
|
|
16
31
|
section?: string;
|
package/package.json
CHANGED
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -1962,6 +1962,11 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
1962
1962
|
const nodeId = readNonEmptyString(payload.nodeId);
|
|
1963
1963
|
const workspace = readNonEmptyString(payload.workspace);
|
|
1964
1964
|
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : 'Remote agent';
|
|
1965
|
+
const relayModalMessage = readNonEmptyString(payload.modalMessage);
|
|
1966
|
+
const relayModalButtons = Array.isArray(payload.modalButtons)
|
|
1967
|
+
? (payload.modalButtons as unknown[]).filter((b): b is string => typeof b === 'string' && b.trim().length > 0)
|
|
1968
|
+
: null;
|
|
1969
|
+
|
|
1965
1970
|
return injectMeshSystemMessage(components, {
|
|
1966
1971
|
meshId,
|
|
1967
1972
|
nodeId,
|
|
@@ -1979,6 +1984,8 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
1979
1984
|
startedAt: readNonEmptyString(payload.startedAt),
|
|
1980
1985
|
completedAt: readNonEmptyString(payload.completedAt),
|
|
1981
1986
|
retryOfJobId: readNonEmptyString(payload.retryOfJobId),
|
|
1987
|
+
...(relayModalMessage ? { modalMessage: relayModalMessage } : {}),
|
|
1988
|
+
...(relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {}),
|
|
1982
1989
|
...(payload.result && typeof payload.result === 'object' && !Array.isArray(payload.result) ? { result: payload.result } : {}),
|
|
1983
1990
|
...(payload.completionDiagnostic && typeof payload.completionDiagnostic === 'object' && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {}),
|
|
1984
1991
|
...(payload.workerResult && typeof payload.workerResult === 'object' && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {}),
|
|
@@ -128,6 +128,14 @@ export class TerminalAdapter {
|
|
|
128
128
|
return this.lastScreen || this.computeScreen();
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
getCursorPosition(): { row: number; col: number } {
|
|
132
|
+
const buf = this.term.buffer.active;
|
|
133
|
+
return {
|
|
134
|
+
row: Math.max(0, (buf as any).cursorY ?? 0),
|
|
135
|
+
col: Math.max(0, (buf as any).cursorX ?? 0),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
kill(): void {
|
|
132
140
|
this.stopTimers();
|
|
133
141
|
try { this.pty?.kill(); } catch { /* ignore */ }
|
|
@@ -247,6 +247,10 @@ export class SpecDriver {
|
|
|
247
247
|
return this.adapter.snapshot();
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
getCursorPosition(): { row: number; col: number } {
|
|
251
|
+
return this.adapter.getCursorPosition();
|
|
252
|
+
}
|
|
253
|
+
|
|
250
254
|
shutdown(): void {
|
|
251
255
|
for (const t of this.delegateTimers.values()) clearTimeout(t);
|
|
252
256
|
this.delegateTimers.clear();
|
|
@@ -316,7 +320,8 @@ export class SpecDriver {
|
|
|
316
320
|
|
|
317
321
|
private reevaluate(forceEmit = false): void {
|
|
318
322
|
const screen = this.adapter.snapshot();
|
|
319
|
-
const
|
|
323
|
+
const cursor = this.adapter.getCursorPosition();
|
|
324
|
+
const ev = evaluate(this.spec, screen, cursor);
|
|
320
325
|
|
|
321
326
|
// Busy hold: many TUIs flicker between busy and idle every frame
|
|
322
327
|
// (claude in particular — its token counter appears and disappears
|
|
@@ -139,6 +139,7 @@ function matchState(
|
|
|
139
139
|
sections: ResolvedSection[],
|
|
140
140
|
fullScreen: string,
|
|
141
141
|
trace: TraceEntry[],
|
|
142
|
+
cursor?: { row: number; col: number },
|
|
142
143
|
): { matched: boolean; title: string | null } {
|
|
143
144
|
const haystack = sectionText(sections, state.when.section, fullScreen);
|
|
144
145
|
const re = compileRegex(state.when);
|
|
@@ -146,7 +147,31 @@ function matchState(
|
|
|
146
147
|
trace.push({ kind: 'state_skip', text: `state[${state.id}] when ${state.when.section ?? '*'}~/${state.when.regex}/ no match` });
|
|
147
148
|
return { matched: false, title: null };
|
|
148
149
|
}
|
|
149
|
-
|
|
150
|
+
|
|
151
|
+
// Cursor-position guards: check row/col bounds when the state declares them.
|
|
152
|
+
// Guards are skipped entirely when the caller did not supply a cursor position
|
|
153
|
+
// (cursor === undefined) so existing pure-text evaluation is unaffected.
|
|
154
|
+
if (cursor !== undefined) {
|
|
155
|
+
const w = state.when;
|
|
156
|
+
if (w.cursor_row_min !== undefined && cursor.row < w.cursor_row_min) {
|
|
157
|
+
trace.push({ kind: 'state_skip', text: `state[${state.id}] cursor row ${cursor.row} < cursor_row_min ${w.cursor_row_min}` });
|
|
158
|
+
return { matched: false, title: null };
|
|
159
|
+
}
|
|
160
|
+
if (w.cursor_row_max !== undefined && cursor.row > w.cursor_row_max) {
|
|
161
|
+
trace.push({ kind: 'state_skip', text: `state[${state.id}] cursor row ${cursor.row} > cursor_row_max ${w.cursor_row_max}` });
|
|
162
|
+
return { matched: false, title: null };
|
|
163
|
+
}
|
|
164
|
+
if (w.cursor_col_min !== undefined && cursor.col < w.cursor_col_min) {
|
|
165
|
+
trace.push({ kind: 'state_skip', text: `state[${state.id}] cursor col ${cursor.col} < cursor_col_min ${w.cursor_col_min}` });
|
|
166
|
+
return { matched: false, title: null };
|
|
167
|
+
}
|
|
168
|
+
if (w.cursor_col_max !== undefined && cursor.col > w.cursor_col_max) {
|
|
169
|
+
trace.push({ kind: 'state_skip', text: `state[${state.id}] cursor col ${cursor.col} > cursor_col_max ${w.cursor_col_max}` });
|
|
170
|
+
return { matched: false, title: null };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
trace.push({ kind: 'state_match', text: `state[${state.id}] matched via ${state.when.section ?? '*'}~/${state.when.regex}/${cursor !== undefined ? ` cursor=(${cursor.row},${cursor.col})` : ''}` });
|
|
150
175
|
|
|
151
176
|
let title: string | null = null;
|
|
152
177
|
if (state.extract_title) {
|
|
@@ -217,19 +242,30 @@ function extractModal(
|
|
|
217
242
|
// Public evaluator
|
|
218
243
|
// ────────────────────────────────────────────────────────────────────────────
|
|
219
244
|
|
|
220
|
-
export function evaluate(
|
|
245
|
+
export function evaluate(
|
|
246
|
+
spec: CliSpec,
|
|
247
|
+
screenText: string,
|
|
248
|
+
/** Optional cursor position (0-based row and col). When supplied, states
|
|
249
|
+
* with cursor_row_min/max or cursor_col_min/max predicates are filtered.
|
|
250
|
+
* When omitted, cursor predicates are ignored and evaluation is text-only
|
|
251
|
+
* (backward-compatible with all existing specs and call sites). */
|
|
252
|
+
cursor?: { row: number; col: number },
|
|
253
|
+
): SpecEvaluation {
|
|
221
254
|
const trace: TraceEntry[] = [];
|
|
222
255
|
const lines = screenText.split('\n');
|
|
223
256
|
const sections = resolveSections(spec, lines);
|
|
224
257
|
for (const s of sections) {
|
|
225
258
|
trace.push({ kind: 'section', text: `section[${s.id}] lines [${s.fromLine}, ${s.toLine}) (${s.toLine - s.fromLine} lines)` });
|
|
226
259
|
}
|
|
260
|
+
if (cursor !== undefined) {
|
|
261
|
+
trace.push({ kind: 'section', text: `cursor (${cursor.row}, ${cursor.col})` });
|
|
262
|
+
}
|
|
227
263
|
|
|
228
264
|
let activeState: { id: string; label: string; title: string | null } | null = null;
|
|
229
265
|
let modal: ModalSnapshot | null = null;
|
|
230
266
|
|
|
231
267
|
for (const st of spec.states) {
|
|
232
|
-
const { matched, title } = matchState(st, sections, screenText, trace);
|
|
268
|
+
const { matched, title } = matchState(st, sections, screenText, trace, cursor);
|
|
233
269
|
if (!matched) continue;
|
|
234
270
|
const extractedModal = extractModal(st, sections, screenText, title, trace);
|
|
235
271
|
// If the state declares modal_buttons but extraction failed (button
|
|
@@ -207,6 +207,22 @@ export const SCHEMA = {
|
|
|
207
207
|
"flags": {
|
|
208
208
|
"type": "string",
|
|
209
209
|
"default": "i"
|
|
210
|
+
},
|
|
211
|
+
"cursor_row_min": {
|
|
212
|
+
"type": "integer",
|
|
213
|
+
"minimum": 0
|
|
214
|
+
},
|
|
215
|
+
"cursor_row_max": {
|
|
216
|
+
"type": "integer",
|
|
217
|
+
"minimum": 0
|
|
218
|
+
},
|
|
219
|
+
"cursor_col_min": {
|
|
220
|
+
"type": "integer",
|
|
221
|
+
"minimum": 0
|
|
222
|
+
},
|
|
223
|
+
"cursor_col_max": {
|
|
224
|
+
"type": "integer",
|
|
225
|
+
"minimum": 0
|
|
210
226
|
}
|
|
211
227
|
}
|
|
212
228
|
},
|
|
@@ -111,7 +111,11 @@
|
|
|
111
111
|
"properties": {
|
|
112
112
|
"section": { "type": "string" },
|
|
113
113
|
"regex": { "type": "string", "minLength": 1 },
|
|
114
|
-
"flags": { "type": "string", "default": "i" }
|
|
114
|
+
"flags": { "type": "string", "default": "i" },
|
|
115
|
+
"cursor_row_min": { "type": "integer", "minimum": 0 },
|
|
116
|
+
"cursor_row_max": { "type": "integer", "minimum": 0 },
|
|
117
|
+
"cursor_col_min": { "type": "integer", "minimum": 0 },
|
|
118
|
+
"cursor_col_max": { "type": "integer", "minimum": 0 }
|
|
115
119
|
}
|
|
116
120
|
},
|
|
117
121
|
"sectionPattern": {
|
|
@@ -19,6 +19,21 @@ export interface SectionRegex {
|
|
|
19
19
|
section?: string;
|
|
20
20
|
regex: string;
|
|
21
21
|
flags?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional cursor-position guards. When present, the state is only
|
|
24
|
+
* considered matched if the terminal cursor row/column satisfies the
|
|
25
|
+
* bounds (0-based, inclusive). Missing or undefined means "no constraint".
|
|
26
|
+
*
|
|
27
|
+
* Use case: distinguish modal zone from body zone for TUIs that use
|
|
28
|
+
* cursor position rather than distinct text to locate the active prompt
|
|
29
|
+
* (e.g. Antigravity cursor lands in modal_zone rows 8-31 when approval
|
|
30
|
+
* is visible, never in body rows 0-7). Without this guard, body text
|
|
31
|
+
* containing "Do you want to proceed?" could false-positive a modal match.
|
|
32
|
+
*/
|
|
33
|
+
cursor_row_min?: number;
|
|
34
|
+
cursor_row_max?: number;
|
|
35
|
+
cursor_col_min?: number;
|
|
36
|
+
cursor_col_max?: number;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
export interface SectionPattern {
|