@adhdev/daemon-core 1.0.22 → 1.0.23-rc.1
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 +42 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +42 -11
- package/dist/index.mjs.map +1 -1
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +19 -7
- package/package.json +3 -3
- package/src/commands/high-family/mesh-coordinator-launch.ts +31 -2
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +28 -8
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +15 -5
- package/src/providers/types/interactive-prompt.ts +8 -1
|
@@ -76,17 +76,29 @@ declare function scopeText(input: CliStatusInput, scope: SpinnerSpec['scope'] |
|
|
|
76
76
|
declare function spinnerMatches(spec: SpinnerSpec, input: CliStatusInput): boolean;
|
|
77
77
|
declare function settledPromptMatches(spec: SettledPromptSpec, input: CliStatusInput): boolean;
|
|
78
78
|
/**
|
|
79
|
-
* APPROVAL-PICKER-MISROUTE (mission f1d25e11) defense-in-depth: an
|
|
79
|
+
* APPROVAL-PICKER-MISROUTE (mission f1d25e11 / fb2a7053) defense-in-depth: an
|
|
80
80
|
* AskUserQuestion multi-choice picker is NOT an approval modal. Its option rows
|
|
81
81
|
* ("❯ 1. label") can otherwise satisfy the approval button cue and get
|
|
82
82
|
* mis-classified as `waiting_approval`, so the worker's question is surfaced to
|
|
83
83
|
* the coordinator as a task_approval_needed (→ mesh_approve, which cannot answer
|
|
84
|
-
* it)
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
84
|
+
* it), with no promptId for mesh_answer_question.
|
|
85
|
+
*
|
|
86
|
+
* The picker's distinguishing signature is its footer: the claude TUI select
|
|
87
|
+
* hints "Enter to select" AND "Esc to cancel". A genuine tool-consent approval
|
|
88
|
+
* modal (Yes/No/Allow/Deny) NEVER renders that footer pair — it uses the ordinary
|
|
89
|
+
* "? for shortcuts" prompt line instead — so the footer alone reliably separates
|
|
90
|
+
* the two even though BOTH draw numbered option rows.
|
|
91
|
+
*
|
|
92
|
+
* The original guard additionally required the freeform escape hatch ("Type
|
|
93
|
+
* something" / "Chat about this"). That made the guard collapse whenever the
|
|
94
|
+
* escape-hatch rows were absent or scrolled out of the captured frame: the
|
|
95
|
+
* picker then fell through to the approval matchers → waiting_approval. The
|
|
96
|
+
* escape hatch is therefore DOWNGRADED from a required signal to an optional
|
|
97
|
+
* supporting one: the guard now fires on the select footer plus at least one
|
|
98
|
+
* numbered option row (which every picker draws), and the escape hatch merely
|
|
99
|
+
* reinforces an already-matching footer. The numbered-row requirement keeps a
|
|
100
|
+
* bare "Enter to select" string (e.g. inside prose) from being mistaken for a
|
|
101
|
+
* picker.
|
|
90
102
|
*/
|
|
91
103
|
export declare function isAskUserQuestionPickerSignature(text: string): boolean;
|
|
92
104
|
declare function modalMatches(spec: ModalSpec, input: CliStatusInput): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23-rc.1",
|
|
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,8 +47,8 @@
|
|
|
47
47
|
"author": "vilmire",
|
|
48
48
|
"license": "AGPL-3.0-or-later",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@adhdev/mesh-shared": "
|
|
51
|
-
"@adhdev/session-host-core": "
|
|
50
|
+
"@adhdev/mesh-shared": "1.0.23-rc.1",
|
|
51
|
+
"@adhdev/session-host-core": "1.0.23-rc.1",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
|
@@ -17,6 +17,7 @@ import { registerMeshCoordinator } from '../../mesh/coordinator-registry.js';
|
|
|
17
17
|
import { partitionSessionHostRecords } from '../../session-host/runtime-surface.js';
|
|
18
18
|
import { createHermesManualMeshCoordinatorSetup, resolveMeshCoordinatorSetup } from '../mesh-coordinator.js';
|
|
19
19
|
import { normalizeMeshNodeId } from '@adhdev/mesh-shared';
|
|
20
|
+
import { delegatedWorkerAutoApproveSettings } from '../../repo-mesh-types.js';
|
|
20
21
|
import {
|
|
21
22
|
readProviderPriorityFromPolicy,
|
|
22
23
|
resolveProviderTypeFromPriority,
|
|
@@ -429,7 +430,25 @@ export const meshCoordinatorLaunchHandlers: Record<string, HighFamilyHandler> =
|
|
|
429
430
|
dir: workspace,
|
|
430
431
|
cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : undefined,
|
|
431
432
|
env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : undefined,
|
|
432
|
-
settings: {
|
|
433
|
+
settings: {
|
|
434
|
+
meshCoordinatorFor: meshId,
|
|
435
|
+
// AUTOAPPROVE-COORD: the coordinator is a mesh session too, so it
|
|
436
|
+
// must inherit the workspace's declarative auto-approve MODE
|
|
437
|
+
// (.adhdev/mesh.json providerDefaults.autoApproveModes) exactly like
|
|
438
|
+
// a delegated worker does at dispatch. Reuse the already-loaded repo
|
|
439
|
+
// config so the launch args carry --permission-mode. NB: we deliberately
|
|
440
|
+
// do NOT stamp launchedByCoordinator here — that flag is a WORKER-only
|
|
441
|
+
// dangerous-mode downgrade signal (auto-approve-modes.ts); the coordinator
|
|
442
|
+
// is the owner, not a worker. The dangerous gate is already applied inside
|
|
443
|
+
// delegatedWorkerAutoApproveSettings via mesh/node policy.
|
|
444
|
+
...delegatedWorkerAutoApproveSettings(
|
|
445
|
+
effectiveMesh?.policy,
|
|
446
|
+
coordinatorNode?.policy,
|
|
447
|
+
providerMeta,
|
|
448
|
+
repoMeshConfigLoad.config,
|
|
449
|
+
cliType,
|
|
450
|
+
),
|
|
451
|
+
},
|
|
433
452
|
...(initialModel ? { initialModel } : {}),
|
|
434
453
|
...(initialThinkingLevel ? { initialThinkingLevel } : {}),
|
|
435
454
|
});
|
|
@@ -650,7 +669,17 @@ export const meshCoordinatorLaunchHandlers: Record<string, HighFamilyHandler> =
|
|
|
650
669
|
cliArgs: cliArgs.length > 0 ? cliArgs : undefined,
|
|
651
670
|
env: Object.keys(launchEnv).length > 0 ? launchEnv : undefined,
|
|
652
671
|
settings: {
|
|
653
|
-
meshCoordinatorFor: meshId
|
|
672
|
+
meshCoordinatorFor: meshId,
|
|
673
|
+
// AUTOAPPROVE-COORD: inherit the workspace declarative auto-approve MODE
|
|
674
|
+
// for the coordinator session (see the cli_command branch for the full
|
|
675
|
+
// rationale). No launchedByCoordinator stamp — the coordinator is the owner.
|
|
676
|
+
...delegatedWorkerAutoApproveSettings(
|
|
677
|
+
effectiveMesh?.policy,
|
|
678
|
+
coordinatorNode?.policy,
|
|
679
|
+
providerMeta,
|
|
680
|
+
repoMeshConfigLoad.config,
|
|
681
|
+
cliType,
|
|
682
|
+
),
|
|
654
683
|
},
|
|
655
684
|
...(initialModel ? { initialModel } : {}),
|
|
656
685
|
...(initialThinkingLevel ? { initialThinkingLevel } : {}),
|
|
@@ -204,30 +204,50 @@ function extractButtonLabels(spec: ModalSpec, text: string): string[] {
|
|
|
204
204
|
* an assistant enumeration lacks that pair and does not fire the cue.
|
|
205
205
|
*/
|
|
206
206
|
function buttonBlockApprovalCue(spec: ModalSpec, text: string): boolean {
|
|
207
|
+
// Defense-in-depth (mission fb2a7053): even if some caller reaches here on a
|
|
208
|
+
// question picker, its "Enter to select"/"Esc to cancel" footer marks it as a
|
|
209
|
+
// choice, not an approval — never derive an approval cue from picker rows.
|
|
210
|
+
if (/Enter to select/i.test(text) && /Esc to cancel/i.test(text)) return false;
|
|
207
211
|
const labels = extractButtonLabels(spec, text);
|
|
208
212
|
if (labels.length < 2) return false;
|
|
209
213
|
if (pickApprovalButton(labels).index < 0) return false;
|
|
210
214
|
return hasNegativeApprovalOption(labels);
|
|
211
215
|
}
|
|
212
216
|
|
|
217
|
+
// A claude-cli AskUserQuestion picker option row: an optional cursor / checkbox
|
|
218
|
+
// followed by a "N." number marker. The picker draws AT LEAST one such row.
|
|
219
|
+
const PICKER_OPTION_ROW = /^\s*(?:[❯›>]\s*)?(?:\[[ xX]\]|[☐☒◻◼]\s*)?\d+[.)]\s+\S/m;
|
|
220
|
+
|
|
213
221
|
/**
|
|
214
|
-
* APPROVAL-PICKER-MISROUTE (mission f1d25e11) defense-in-depth: an
|
|
222
|
+
* APPROVAL-PICKER-MISROUTE (mission f1d25e11 / fb2a7053) defense-in-depth: an
|
|
215
223
|
* AskUserQuestion multi-choice picker is NOT an approval modal. Its option rows
|
|
216
224
|
* ("❯ 1. label") can otherwise satisfy the approval button cue and get
|
|
217
225
|
* mis-classified as `waiting_approval`, so the worker's question is surfaced to
|
|
218
226
|
* the coordinator as a task_approval_needed (→ mesh_approve, which cannot answer
|
|
219
|
-
* it)
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
227
|
+
* it), with no promptId for mesh_answer_question.
|
|
228
|
+
*
|
|
229
|
+
* The picker's distinguishing signature is its footer: the claude TUI select
|
|
230
|
+
* hints "Enter to select" AND "Esc to cancel". A genuine tool-consent approval
|
|
231
|
+
* modal (Yes/No/Allow/Deny) NEVER renders that footer pair — it uses the ordinary
|
|
232
|
+
* "? for shortcuts" prompt line instead — so the footer alone reliably separates
|
|
233
|
+
* the two even though BOTH draw numbered option rows.
|
|
234
|
+
*
|
|
235
|
+
* The original guard additionally required the freeform escape hatch ("Type
|
|
236
|
+
* something" / "Chat about this"). That made the guard collapse whenever the
|
|
237
|
+
* escape-hatch rows were absent or scrolled out of the captured frame: the
|
|
238
|
+
* picker then fell through to the approval matchers → waiting_approval. The
|
|
239
|
+
* escape hatch is therefore DOWNGRADED from a required signal to an optional
|
|
240
|
+
* supporting one: the guard now fires on the select footer plus at least one
|
|
241
|
+
* numbered option row (which every picker draws), and the escape hatch merely
|
|
242
|
+
* reinforces an already-matching footer. The numbered-row requirement keeps a
|
|
243
|
+
* bare "Enter to select" string (e.g. inside prose) from being mistaken for a
|
|
244
|
+
* picker.
|
|
225
245
|
*/
|
|
226
246
|
export function isAskUserQuestionPickerSignature(text: string): boolean {
|
|
227
247
|
if (!text) return false;
|
|
228
248
|
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
229
249
|
if (!hasSelectFooter) return false;
|
|
230
|
-
return
|
|
250
|
+
return PICKER_OPTION_ROW.test(text);
|
|
231
251
|
}
|
|
232
252
|
|
|
233
253
|
function modalMatches(spec: ModalSpec, input: CliStatusInput): boolean {
|
|
@@ -78,20 +78,30 @@ function compile(re: string, flags?: string): RegExp {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// See detect-status.PICKER_OPTION_ROW — kept in lockstep so both layers gate on
|
|
82
|
+
// the identical picker signature.
|
|
83
|
+
const PICKER_OPTION_ROW = /^\s*(?:[❯›>]\s*)?(?:\[[ xX]\]|[☐☒◻◼]\s*)?\d+[.)]\s+\S/m;
|
|
84
|
+
|
|
81
85
|
/**
|
|
82
|
-
* APPROVAL-PICKER-MISROUTE (mission f1d25e11) defense-in-depth: mirror
|
|
86
|
+
* APPROVAL-PICKER-MISROUTE (mission f1d25e11 / fb2a7053) defense-in-depth: mirror
|
|
83
87
|
* detect-status.isAskUserQuestionPickerSignature at the button-parse layer. A
|
|
84
88
|
* multi-choice AskUserQuestion picker is not an approval modal; its numbered
|
|
85
89
|
* option rows can otherwise be extracted as approval buttons and produce a
|
|
86
|
-
* spurious approval modal.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
90
|
+
* spurious approval modal.
|
|
91
|
+
*
|
|
92
|
+
* The picker signature is the claude TUI select footer ("Enter to select" +
|
|
93
|
+
* "Esc to cancel") plus at least one numbered option row. A genuine approval
|
|
94
|
+
* modal never renders that footer pair, so it is safe even though approval modals
|
|
95
|
+
* also draw numbered rows. The freeform escape hatch ("Type something" / "Chat
|
|
96
|
+
* about this") used to be REQUIRED here too, which broke the guard whenever the
|
|
97
|
+
* hatch rows were absent or scrolled out of frame; it is now downgraded to an
|
|
98
|
+
* optional supporting signal (mirrors detect-status).
|
|
89
99
|
*/
|
|
90
100
|
function isAskUserQuestionPickerSignature(text: string): boolean {
|
|
91
101
|
if (!text) return false;
|
|
92
102
|
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
93
103
|
if (!hasSelectFooter) return false;
|
|
94
|
-
return
|
|
104
|
+
return PICKER_OPTION_ROW.test(text);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
function findQuestionLineIndex(
|
|
@@ -303,8 +303,15 @@ function readClaudeOptionDescription(lines: string[], optionLineIndex: number):
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
function parseClaudeHeaderlessInteractiveTuiQuestion(page: ClaudeInteractiveTuiPage, index: number): InteractiveQuestion | null {
|
|
306
|
+
// The claude TUI select footer ("Enter to select" + "Esc to cancel") is the
|
|
307
|
+
// picker's defining signature. The freeform escape hatch ("Type something" /
|
|
308
|
+
// "Chat about this") used to be REQUIRED here, but those option rows can be
|
|
309
|
+
// absent or scrolled out of the captured frame — in which case the picker
|
|
310
|
+
// failed to parse, activeInteractivePrompt stayed null, and detect-status
|
|
311
|
+
// then mis-classified the screen as waiting_approval (mission fb2a7053).
|
|
312
|
+
// Requiring only the footer plus (below) a real option block is sufficient;
|
|
313
|
+
// the escape hatch, when present, is still picked up as allowFreeform.
|
|
306
314
|
if (!isClaudeTuiSelectFooter(page.screenText)) return null;
|
|
307
|
-
if (!/Type something\.?|Chat about this/i.test(page.screenText)) return null;
|
|
308
315
|
|
|
309
316
|
const lines = page.screenText.split(/\r?\n/);
|
|
310
317
|
let footerIndex = -1;
|