@adhdev/daemon-core 1.0.18-rc.20 → 1.0.18-rc.4
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/cli-adapters/provider-cli-shared.d.ts +0 -33
- package/dist/commands/cli-manager.d.ts +0 -9
- package/dist/commands/upgrade-helper.d.ts +0 -1
- package/dist/commands/windows-atomic-upgrade.d.ts +0 -5
- package/dist/config/mesh-json-config.d.ts +0 -62
- package/dist/index.d.ts +2 -4
- package/dist/index.js +3998 -6112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3855 -5966
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +0 -76
- package/dist/mesh/mesh-active-work.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -28
- package/dist/mesh/mesh-node-identity.d.ts +0 -23
- package/dist/mesh/mesh-refine-gates.d.ts +0 -89
- package/dist/mesh/mesh-remote-event-pull.d.ts +0 -3
- package/dist/mesh/mesh-runtime-store.d.ts +0 -11
- package/dist/mesh/mesh-work-queue.d.ts +1 -24
- package/dist/providers/chat-message-normalization.d.ts +0 -22
- package/dist/providers/cli-provider-instance-types.d.ts +0 -4
- package/dist/providers/cli-provider-instance.d.ts +0 -78
- package/dist/providers/contracts.d.ts +0 -17
- package/dist/providers/provider-schema.d.ts +0 -1
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +0 -14
- package/dist/providers/types/interactive-prompt.d.ts +0 -18
- package/dist/repo-mesh-types.d.ts +6 -38
- package/dist/shared-types.d.ts +2 -4
- package/package.json +3 -3
- package/src/boot/daemon-lifecycle.ts +0 -10
- package/src/cli-adapters/cli-state-engine.ts +1 -17
- package/src/cli-adapters/provider-cli-adapter.ts +6 -2
- package/src/cli-adapters/provider-cli-shared.ts +0 -48
- package/src/commands/cli-manager.ts +8 -72
- package/src/commands/high-family/mesh-coordinator-launch.ts +2 -17
- package/src/commands/high-family/mesh-events.ts +2 -13
- package/src/commands/med-family/mesh-crud.ts +0 -155
- package/src/commands/med-family/mesh-queue.ts +1 -74
- package/src/commands/router-refine.ts +4 -71
- package/src/commands/router.ts +0 -8
- package/src/commands/upgrade-helper.ts +15 -78
- package/src/commands/windows-atomic-upgrade.ts +40 -194
- package/src/config/mesh-json-config.ts +0 -111
- package/src/index.ts +1 -21
- package/src/mesh/coordinator-prompt.ts +11 -258
- package/src/mesh/mesh-active-work.ts +1 -11
- package/src/mesh/mesh-completion-synthesis.ts +1 -12
- package/src/mesh/mesh-event-classify.ts +0 -19
- package/src/mesh/mesh-event-forwarding.ts +6 -64
- package/src/mesh/mesh-events-utils.ts +0 -42
- package/src/mesh/mesh-ledger.ts +0 -77
- package/src/mesh/mesh-node-identity.ts +0 -49
- package/src/mesh/mesh-queue-assignment.ts +11 -210
- package/src/mesh/mesh-reconcile-loop.ts +3 -306
- package/src/mesh/mesh-refine-gates.ts +0 -300
- package/src/mesh/mesh-remote-event-pull.ts +47 -68
- package/src/mesh/mesh-runtime-store.ts +0 -21
- package/src/mesh/mesh-work-queue.ts +2 -85
- package/src/providers/chat-message-normalization.ts +0 -53
- package/src/providers/cli-provider-instance-types.ts +0 -53
- package/src/providers/cli-provider-instance.ts +15 -497
- package/src/providers/contracts.ts +1 -25
- package/src/providers/provider-schema.ts +0 -83
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +0 -23
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +0 -20
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +0 -44
- package/src/providers/types/interactive-prompt.ts +0 -77
- package/src/repo-mesh-types.ts +12 -112
- package/src/shared-types.ts +1 -9
- package/src/status/reporter.ts +0 -1
- package/src/status/snapshot.ts +0 -2
- package/dist/mesh/mesh-disk-retention.d.ts +0 -105
- package/dist/providers/auto-approve-modes.d.ts +0 -14
- package/src/mesh/mesh-disk-retention.ts +0 -370
- package/src/providers/auto-approve-modes.ts +0 -103
|
@@ -488,28 +488,6 @@ export interface ProviderCompatibilityEntry {
|
|
|
488
488
|
scriptDir: string;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
export type AutoApproveModeStrategy =
|
|
492
|
-
| 'pty-parse-default'
|
|
493
|
-
| 'launch-args'
|
|
494
|
-
| 'post-boot-command';
|
|
495
|
-
|
|
496
|
-
export type AutoApproveModeRisk = 'safe' | 'caution' | 'dangerous';
|
|
497
|
-
|
|
498
|
-
export interface AutoApproveMode {
|
|
499
|
-
id: string;
|
|
500
|
-
label: string;
|
|
501
|
-
strategy: AutoApproveModeStrategy;
|
|
502
|
-
risk: AutoApproveModeRisk;
|
|
503
|
-
warning?: string;
|
|
504
|
-
launchArgs?: string[];
|
|
505
|
-
removeArgs?: string[];
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
export interface AutoApproveModesConfig {
|
|
509
|
-
default: string;
|
|
510
|
-
modes: AutoApproveMode[];
|
|
511
|
-
}
|
|
512
|
-
|
|
513
491
|
export interface ProviderModule {
|
|
514
492
|
/** Unique identifier (e.g. 'cline', 'cursor', 'gemini-cli') */
|
|
515
493
|
type: string;
|
|
@@ -541,9 +519,7 @@ export interface ProviderModule {
|
|
|
541
519
|
status?: string;
|
|
542
520
|
/** Inventory/support detail string maintained in adhdev-providers */
|
|
543
521
|
details?: string;
|
|
544
|
-
|
|
545
|
-
autoApproveModes?: AutoApproveModesConfig;
|
|
546
|
-
/** Install instructions (shown when command is missing) */
|
|
522
|
+
/** Install instructions (shown when command is missing) */
|
|
547
523
|
install?: string;
|
|
548
524
|
/** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
|
|
549
525
|
versionCommand?: ProviderVersionCommand;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ProviderControlDef, ProviderControlType, ProviderModule } from './contracts.js'
|
|
2
|
-
import { deriveAutoApproveModeRisk } from './auto-approve-modes.js'
|
|
3
2
|
import { providerHasOpenPanelSupport } from './open-panel-support.js'
|
|
4
3
|
|
|
5
4
|
const VALID_CAPABILITY_MEDIA_TYPES = new Set(['text', 'image', 'audio', 'video', 'resource'])
|
|
@@ -67,7 +66,6 @@ const KNOWN_PROVIDER_FIELDS = new Set<string>([
|
|
|
67
66
|
'providerVersion',
|
|
68
67
|
'status',
|
|
69
68
|
'details',
|
|
70
|
-
'autoApproveModes',
|
|
71
69
|
'modelLaunchArgs',
|
|
72
70
|
'modelOptions',
|
|
73
71
|
'thinkingLaunchArgs',
|
|
@@ -146,7 +144,6 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
146
144
|
validateCapabilities(provider as unknown as ProviderModule, controls, errors)
|
|
147
145
|
validateNativeHistory(provider.nativeHistory, errors)
|
|
148
146
|
validateMeshCoordinator(provider.meshCoordinator, errors)
|
|
149
|
-
validateAutoApproveModes(provider.autoApproveModes, errors)
|
|
150
147
|
|
|
151
148
|
for (const control of controls) {
|
|
152
149
|
validateControl(control as ProviderControlDef, errors)
|
|
@@ -163,86 +160,6 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
163
160
|
return { errors, warnings }
|
|
164
161
|
}
|
|
165
162
|
|
|
166
|
-
export function validateAutoApproveModes(raw: unknown, errors: string[]): void {
|
|
167
|
-
if (raw === undefined) return
|
|
168
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
169
|
-
errors.push('autoApproveModes must be an object')
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const config = raw as Record<string, unknown>
|
|
174
|
-
const defaultModeId = typeof config.default === 'string' ? config.default.trim() : ''
|
|
175
|
-
if (!defaultModeId) {
|
|
176
|
-
errors.push('autoApproveModes.default must be a non-empty string')
|
|
177
|
-
} else {
|
|
178
|
-
config.default = defaultModeId
|
|
179
|
-
}
|
|
180
|
-
if (!Array.isArray(config.modes) || config.modes.length === 0) {
|
|
181
|
-
errors.push('autoApproveModes.modes must be a non-empty array')
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const ids = new Set<string>()
|
|
186
|
-
for (const [index, rawMode] of config.modes.entries()) {
|
|
187
|
-
const prefix = `autoApproveModes.modes[${index}]`
|
|
188
|
-
if (!rawMode || typeof rawMode !== 'object' || Array.isArray(rawMode)) {
|
|
189
|
-
errors.push(`${prefix} must be an object`)
|
|
190
|
-
continue
|
|
191
|
-
}
|
|
192
|
-
const mode = rawMode as Record<string, unknown>
|
|
193
|
-
const id = typeof mode.id === 'string' ? mode.id.trim() : ''
|
|
194
|
-
if (!id) {
|
|
195
|
-
errors.push(`${prefix}.id must be a non-empty string`)
|
|
196
|
-
} else if (ids.has(id)) {
|
|
197
|
-
errors.push(`${prefix}.id must be unique (duplicate: ${id})`)
|
|
198
|
-
} else {
|
|
199
|
-
ids.add(id)
|
|
200
|
-
mode.id = id
|
|
201
|
-
}
|
|
202
|
-
if (typeof mode.label !== 'string' || !mode.label.trim()) {
|
|
203
|
-
errors.push(`${prefix}.label must be a non-empty string`)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const strategy = mode.strategy
|
|
207
|
-
if (!['pty-parse-default', 'launch-args', 'post-boot-command'].includes(String(strategy))) {
|
|
208
|
-
errors.push(`${prefix}.strategy must be one of: pty-parse-default, launch-args, post-boot-command`)
|
|
209
|
-
} else if (strategy === 'post-boot-command') {
|
|
210
|
-
errors.push(`${prefix}.strategy post-boot-command is reserved and unsupported in v1`)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const risk = mode.risk
|
|
214
|
-
if (!['safe', 'caution', 'dangerous'].includes(String(risk))) {
|
|
215
|
-
errors.push(`${prefix}.risk must be one of: safe, caution, dangerous`)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
for (const field of ['launchArgs', 'removeArgs'] as const) {
|
|
219
|
-
const value = mode[field]
|
|
220
|
-
if (value !== undefined && (!Array.isArray(value) || value.some((arg) => typeof arg !== 'string' || !arg.trim()))) {
|
|
221
|
-
errors.push(`${prefix}.${field} must be an array of non-empty strings when provided`)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
if (strategy === 'launch-args' && (!Array.isArray(mode.launchArgs) || mode.launchArgs.length === 0)) {
|
|
225
|
-
errors.push(`${prefix}.launchArgs must be a non-empty array for launch-args strategy`)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Mutate the validated provider object so downstream runtime/UI consumers see
|
|
229
|
-
// the effective risk. The runtime resolver repeats this derivation as defense-in-depth.
|
|
230
|
-
if (['safe', 'caution', 'dangerous'].includes(String(risk))
|
|
231
|
-
&& deriveAutoApproveModeRisk(mode as any) === 'dangerous') {
|
|
232
|
-
mode.risk = 'dangerous'
|
|
233
|
-
}
|
|
234
|
-
if (mode.risk === 'dangerous' && (typeof mode.warning !== 'string' || !mode.warning.trim())) {
|
|
235
|
-
errors.push(`${prefix}.warning is required for dangerous modes`)
|
|
236
|
-
} else if (mode.warning !== undefined && (typeof mode.warning !== 'string' || !mode.warning.trim())) {
|
|
237
|
-
errors.push(`${prefix}.warning must be a non-empty string when provided`)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (defaultModeId && !ids.has(defaultModeId)) {
|
|
242
|
-
errors.push(`autoApproveModes.default must reference an existing mode id: ${defaultModeId}`)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
163
|
function validateCapabilities(provider: ProviderModule, controls: ProviderControlDef[], errors: string[]): void {
|
|
247
164
|
const capabilities = provider.capabilities
|
|
248
165
|
if (provider.contractVersion === 2) {
|
|
@@ -210,33 +210,10 @@ function buttonBlockApprovalCue(spec: ModalSpec, text: string): boolean {
|
|
|
210
210
|
return hasNegativeApprovalOption(labels);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
/**
|
|
214
|
-
* APPROVAL-PICKER-MISROUTE (mission f1d25e11) defense-in-depth: an
|
|
215
|
-
* AskUserQuestion multi-choice picker is NOT an approval modal. Its option rows
|
|
216
|
-
* ("❯ 1. label") can otherwise satisfy the approval button cue and get
|
|
217
|
-
* mis-classified as `waiting_approval`, so the worker's question is surfaced to
|
|
218
|
-
* the coordinator as a task_approval_needed (→ mesh_approve, which cannot answer
|
|
219
|
-
* it). The picker carries a distinctive signature the approval FSM never does:
|
|
220
|
-
* the claude TUI select footer ("Enter to select … Esc to cancel") together with
|
|
221
|
-
* the freeform escape hatch ("Type something" / "Chat about this"). When both are
|
|
222
|
-
* present the screen is a question picker — surfaced separately as
|
|
223
|
-
* waiting_choice — so the approval matchers must yield. Mirrors the legacy
|
|
224
|
-
* looksLikeSelectionPicker guard (cli-provider-instance.ts) ported to SDK-v1.
|
|
225
|
-
*/
|
|
226
|
-
export function isAskUserQuestionPickerSignature(text: string): boolean {
|
|
227
|
-
if (!text) return false;
|
|
228
|
-
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
229
|
-
if (!hasSelectFooter) return false;
|
|
230
|
-
return /Type something\.?|Chat about this/i.test(text);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
213
|
function modalMatches(spec: ModalSpec, input: CliStatusInput): boolean {
|
|
234
214
|
// Status-level modal detection is cue-only — does the question appear at all?
|
|
235
215
|
// Button extraction lives in buildParseApprovalFromTui.
|
|
236
216
|
const text = input.screenText ?? '';
|
|
237
|
-
// A question picker (AskUserQuestion) is never an approval — bail before any
|
|
238
|
-
// approval cue can match its numbered option rows (mission f1d25e11).
|
|
239
|
-
if (isAskUserQuestionPickerSignature(text)) return false;
|
|
240
217
|
const question = compile(spec.questionPattern, spec.questionFlags ?? 'i');
|
|
241
218
|
if (question.test(text)) return true;
|
|
242
219
|
for (const variant of spec.questionVariants ?? []) {
|
|
@@ -78,22 +78,6 @@ function compile(re: string, flags?: string): RegExp {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
/**
|
|
82
|
-
* APPROVAL-PICKER-MISROUTE (mission f1d25e11) defense-in-depth: mirror
|
|
83
|
-
* detect-status.isAskUserQuestionPickerSignature at the button-parse layer. A
|
|
84
|
-
* multi-choice AskUserQuestion picker is not an approval modal; its numbered
|
|
85
|
-
* option rows can otherwise be extracted as approval buttons and produce a
|
|
86
|
-
* spurious approval modal. When the picker signature (claude TUI select footer +
|
|
87
|
-
* "Type something"/"Chat about this" freeform escape hatch) is present, return
|
|
88
|
-
* null early so the screen is surfaced as waiting_choice, never approval.
|
|
89
|
-
*/
|
|
90
|
-
function isAskUserQuestionPickerSignature(text: string): boolean {
|
|
91
|
-
if (!text) return false;
|
|
92
|
-
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
93
|
-
if (!hasSelectFooter) return false;
|
|
94
|
-
return /Type something\.?|Chat about this/i.test(text);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
81
|
function findQuestionLineIndex(
|
|
98
82
|
spec: ModalTuiSpec,
|
|
99
83
|
lines: string[],
|
|
@@ -284,10 +268,6 @@ export function buildParseApprovalFromTui(
|
|
|
284
268
|
return function parseApproval(input: CliApprovalInput): CliApprovalModal | null {
|
|
285
269
|
const rawText = input.screenText ?? input.buffer ?? '';
|
|
286
270
|
if (!rawText) return null;
|
|
287
|
-
// An AskUserQuestion picker is not an approval — never extract its option
|
|
288
|
-
// rows as approval buttons (mission f1d25e11). Check the raw text before any
|
|
289
|
-
// visible-region scoping trims the footer that identifies the picker.
|
|
290
|
-
if (isAskUserQuestionPickerSignature(rawText)) return null;
|
|
291
271
|
const text = visibleRegion ? applyVisibleRegion(visibleRegion, rawText) : rawText;
|
|
292
272
|
const lines = text.split('\n');
|
|
293
273
|
const question = findQuestionLineIndex(spec, lines);
|
|
@@ -119,20 +119,6 @@
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
|
-
"autoApproveModes": {
|
|
123
|
-
"type": "object",
|
|
124
|
-
"description": "Provider-specific auto-approve choices and their launch/runtime strategy.",
|
|
125
|
-
"required": ["default", "modes"],
|
|
126
|
-
"additionalProperties": false,
|
|
127
|
-
"properties": {
|
|
128
|
-
"default": { "type": "string", "minLength": 1 },
|
|
129
|
-
"modes": {
|
|
130
|
-
"type": "array",
|
|
131
|
-
"minItems": 1,
|
|
132
|
-
"items": { "$ref": "#/$defs/autoApproveMode" }
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
122
|
"sendDelayMs": {
|
|
137
123
|
"type": "number",
|
|
138
124
|
"minimum": 0,
|
|
@@ -490,36 +476,6 @@
|
|
|
490
476
|
}
|
|
491
477
|
},
|
|
492
478
|
"$defs": {
|
|
493
|
-
"autoApproveMode": {
|
|
494
|
-
"type": "object",
|
|
495
|
-
"required": ["id", "label", "strategy", "risk"],
|
|
496
|
-
"additionalProperties": false,
|
|
497
|
-
"properties": {
|
|
498
|
-
"id": { "type": "string", "minLength": 1 },
|
|
499
|
-
"label": { "type": "string", "minLength": 1 },
|
|
500
|
-
"strategy": { "enum": ["pty-parse-default", "launch-args", "post-boot-command"] },
|
|
501
|
-
"risk": { "enum": ["safe", "caution", "dangerous"] },
|
|
502
|
-
"warning": { "type": "string", "minLength": 1 },
|
|
503
|
-
"launchArgs": {
|
|
504
|
-
"type": "array",
|
|
505
|
-
"items": { "type": "string", "minLength": 1 }
|
|
506
|
-
},
|
|
507
|
-
"removeArgs": {
|
|
508
|
-
"type": "array",
|
|
509
|
-
"items": { "type": "string", "minLength": 1 }
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
"allOf": [
|
|
513
|
-
{
|
|
514
|
-
"if": { "properties": { "strategy": { "const": "launch-args" } }, "required": ["strategy"] },
|
|
515
|
-
"then": { "required": ["launchArgs"], "properties": { "launchArgs": { "minItems": 1 } } }
|
|
516
|
-
},
|
|
517
|
-
{
|
|
518
|
-
"if": { "properties": { "risk": { "const": "dangerous" } }, "required": ["risk"] },
|
|
519
|
-
"then": { "required": ["warning"] }
|
|
520
|
-
}
|
|
521
|
-
]
|
|
522
|
-
},
|
|
523
479
|
"patternList": {
|
|
524
480
|
"type": "array",
|
|
525
481
|
"items": {
|
|
@@ -117,83 +117,6 @@ export function normalizeInteractivePromptResponse(raw: unknown): InteractivePro
|
|
|
117
117
|
return { promptId, answers };
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
121
|
-
* Resolve a coordinator-friendly answer form into the strict, questionId-keyed
|
|
122
|
-
* InteractivePromptResponse the TUI/answer machinery consumes (mission f1d25e11).
|
|
123
|
-
*
|
|
124
|
-
* mesh_answer_question lets the coordinator answer against the option LABELS or
|
|
125
|
-
* 1-based INDEXES it saw in the agent:waiting_choice event, without having to
|
|
126
|
-
* reconstruct the exact questionId → selectedLabels map. This resolves that
|
|
127
|
-
* ergonomic form against the AUTHORITATIVE active prompt (the daemon holds it),
|
|
128
|
-
* so index/label resolution and question ordering are correct by construction.
|
|
129
|
-
*
|
|
130
|
-
* Accepted `raw` shapes:
|
|
131
|
-
* - The strict form ({ promptId, answers: { [questionId]: { selectedLabels } } })
|
|
132
|
-
* — passed straight to normalizeInteractivePromptResponse (back-compat).
|
|
133
|
-
* - The friendly form ({ promptId, answers: [ { questionId?, select?, freeform? } ] })
|
|
134
|
-
* — entries map to questions by questionId, else by array position. `select`
|
|
135
|
-
* is a label (string) / 1-based index (number) / array of either.
|
|
136
|
-
*/
|
|
137
|
-
export function resolveInteractivePromptResponse(
|
|
138
|
-
prompt: InteractivePrompt,
|
|
139
|
-
raw: unknown,
|
|
140
|
-
): InteractivePromptResponse {
|
|
141
|
-
if (!raw || typeof raw !== 'object') throw new Error('Interactive prompt response must be an object');
|
|
142
|
-
const record = raw as Record<string, unknown>;
|
|
143
|
-
const promptId = readString(record.promptId);
|
|
144
|
-
if (!promptId) throw new Error('promptId must be a non-empty string');
|
|
145
|
-
if (promptId !== prompt.promptId) throw new Error('Interactive prompt response does not match active prompt');
|
|
146
|
-
// Strict (keyed-object) form → existing normalizer.
|
|
147
|
-
if (record.answers && typeof record.answers === 'object' && !Array.isArray(record.answers)) {
|
|
148
|
-
return normalizeInteractivePromptResponse(record);
|
|
149
|
-
}
|
|
150
|
-
if (!Array.isArray(record.answers)) throw new Error('answers must be an array or a questionId-keyed object');
|
|
151
|
-
|
|
152
|
-
const resolveOneLabel = (question: InteractiveQuestion, sel: unknown): string => {
|
|
153
|
-
if (typeof sel === 'number' && Number.isFinite(sel)) {
|
|
154
|
-
const idx = Math.trunc(sel) - 1; // 1-based
|
|
155
|
-
const option = question.options[idx];
|
|
156
|
-
if (!option) throw new Error(`Option index ${sel} out of range for ${question.questionId}`);
|
|
157
|
-
return option.label;
|
|
158
|
-
}
|
|
159
|
-
const label = readString(sel);
|
|
160
|
-
if (!label) throw new Error(`Empty selection for ${question.questionId}`);
|
|
161
|
-
// Exact label match first; fall back to a numeric string index.
|
|
162
|
-
const exact = question.options.find((o) => o.label === label);
|
|
163
|
-
if (exact) return exact.label;
|
|
164
|
-
const asIndex = Number(label);
|
|
165
|
-
if (Number.isInteger(asIndex)) {
|
|
166
|
-
const option = question.options[asIndex - 1];
|
|
167
|
-
if (option) return option.label;
|
|
168
|
-
}
|
|
169
|
-
throw new Error(`Unknown option for ${question.questionId}: ${label}`);
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const answers: Record<string, InteractiveAnswer> = {};
|
|
173
|
-
const entries = record.answers as unknown[];
|
|
174
|
-
entries.forEach((entryRaw, index) => {
|
|
175
|
-
if (!entryRaw || typeof entryRaw !== 'object' || Array.isArray(entryRaw)) return;
|
|
176
|
-
const entry = entryRaw as Record<string, unknown>;
|
|
177
|
-
const questionId = readString(entry.questionId);
|
|
178
|
-
const question = (questionId ? prompt.questions.find((q) => q.questionId === questionId) : undefined)
|
|
179
|
-
?? prompt.questions[index];
|
|
180
|
-
if (!question) throw new Error(`No matching question for answer entry ${index}`);
|
|
181
|
-
const freeformText = readString(entry.freeform) ?? readString(entry.freeformText);
|
|
182
|
-
const selectedLabels: string[] = [];
|
|
183
|
-
const select = entry.select;
|
|
184
|
-
if (Array.isArray(select)) {
|
|
185
|
-
for (const sel of select) selectedLabels.push(resolveOneLabel(question, sel));
|
|
186
|
-
} else if (select !== undefined && select !== null) {
|
|
187
|
-
selectedLabels.push(resolveOneLabel(question, select));
|
|
188
|
-
}
|
|
189
|
-
answers[question.questionId] = {
|
|
190
|
-
selectedLabels,
|
|
191
|
-
...(freeformText ? { freeformText } : {}),
|
|
192
|
-
};
|
|
193
|
-
});
|
|
194
|
-
return { promptId, answers };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
120
|
export function buildClaudeInteractiveToolResult(response: InteractivePromptResponse): string {
|
|
198
121
|
return JSON.stringify({
|
|
199
122
|
type: 'user',
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -15,11 +15,6 @@ import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
|
15
15
|
import type { MeshMissionSummary, MeshMissionSlimSummary } from './mesh/mesh-missions.js';
|
|
16
16
|
import type { MeshMagiActivitySummary } from './mesh/mesh-magi-status.js';
|
|
17
17
|
import type { MagiKindPanelMap, DifficultyBrainMap, NodeCapabilitySlot } from '@adhdev/mesh-shared';
|
|
18
|
-
import type { ProviderModule } from './providers/contracts.js';
|
|
19
|
-
import { deriveAutoApproveModeRisk } from './providers/auto-approve-modes.js';
|
|
20
|
-
// Type-only import (no runtime cycle) — mesh-json-config imports types from this
|
|
21
|
-
// module, and this direction is `import type` so tsc erases it at emit.
|
|
22
|
-
import type { RepoMeshDeclarativeConfig } from './config/mesh-json-config.js';
|
|
23
18
|
|
|
24
19
|
// ─── Core Mesh Types ────────────────────────────
|
|
25
20
|
|
|
@@ -355,8 +350,6 @@ export interface RepoMeshPolicy {
|
|
|
355
350
|
* A node policy may override this per-node (RepoMeshNodePolicy.delegatedWorkerAutoApprove).
|
|
356
351
|
*/
|
|
357
352
|
delegatedWorkerAutoApprove?: boolean;
|
|
358
|
-
/** Explicit opt-in required before delegated workers may use a dangerous provider mode. */
|
|
359
|
-
delegatedWorkerDangerousModeAllow?: boolean;
|
|
360
353
|
/**
|
|
361
354
|
* MESH-SEND-KEYS (feature 3): opt-in to allow the coordinator to inject
|
|
362
355
|
* DESTRUCTIVE keys (CTRL_C / ESC) into a worker PTY via mesh_send_keys. These
|
|
@@ -512,8 +505,6 @@ export interface RepoMeshNodePolicy {
|
|
|
512
505
|
* precedence over the mesh-level policy for worker sessions launched onto this node.
|
|
513
506
|
*/
|
|
514
507
|
delegatedWorkerAutoApprove?: boolean;
|
|
515
|
-
/** Per-node override for dangerous delegated worker mode authorization. */
|
|
516
|
-
delegatedWorkerDangerousModeAllow?: boolean;
|
|
517
508
|
/**
|
|
518
509
|
* MESH-SEND-KEYS (feature 3): per-node override for
|
|
519
510
|
* RepoMeshPolicy.allowSendKeysDestructive.
|
|
@@ -559,7 +550,6 @@ export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
|
559
550
|
// any specific session manually; that override is preserved per-device.
|
|
560
551
|
spawnedSessionVisibility: 'hidden',
|
|
561
552
|
delegatedWorkerAutoApprove: true,
|
|
562
|
-
delegatedWorkerDangerousModeAllow: false,
|
|
563
553
|
sessionCleanupOnNodeRemove: 'preserve',
|
|
564
554
|
// MAGI auto-launches a worker session per pinned replica target with no idle
|
|
565
555
|
// session; those stay idle-LIVE after their turn. Default ON (stop_and_delete)
|
|
@@ -786,13 +776,6 @@ export function mergeAndNormalizePolicy(
|
|
|
786
776
|
} else {
|
|
787
777
|
delete policy.autoConvergeCodeChange;
|
|
788
778
|
}
|
|
789
|
-
// Dangerous delegated-worker provider modes are fail-closed and only persist
|
|
790
|
-
// when the mesh owner has explicitly opted in.
|
|
791
|
-
if (policy.delegatedWorkerDangerousModeAllow === true) {
|
|
792
|
-
policy.delegatedWorkerDangerousModeAllow = true;
|
|
793
|
-
} else {
|
|
794
|
-
delete policy.delegatedWorkerDangerousModeAllow;
|
|
795
|
-
}
|
|
796
779
|
// Coordinator idle-push policy: strict opt-in. Only persist the explicit
|
|
797
780
|
// 'auto_silent_on_dispatch' value; any other/invalid value normalizes to the
|
|
798
781
|
// 'always' default and is dropped so existing meshes.json stays byte-for-byte
|
|
@@ -806,106 +789,23 @@ export function mergeAndNormalizePolicy(
|
|
|
806
789
|
}
|
|
807
790
|
|
|
808
791
|
/**
|
|
809
|
-
* Resolve delegated worker
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
* invariant:
|
|
815
|
-
*
|
|
816
|
-
* ① ENABLE gate (machine-local policy only): node boolean > mesh boolean.
|
|
817
|
-
* `enabled=false` returns `false` IMMEDIATELY, BEFORE any mode selection.
|
|
818
|
-
* The repo `mesh.json` providerDefaults has ZERO influence here — a
|
|
819
|
-
* node/mesh opt-out is never overridden by a repo-declared requested mode.
|
|
820
|
-
*
|
|
821
|
-
* ② MODE selection (only when enabled === true):
|
|
822
|
-
* task override [FUTURE — param reserved below, not yet wired] >
|
|
823
|
-
* repo mesh.json providerDefaults.autoApproveModes[providerType] >
|
|
824
|
-
* provider spec autoApproveModes.default.
|
|
825
|
-
* A repo-requested mode ID is adopted ONLY when it exists in the provider's
|
|
826
|
-
* own `autoApproveModes.modes`; an unknown/stale/typo'd ID is IGNORED and we
|
|
827
|
-
* fall back to the provider default (fail-closed: never coerce into a
|
|
828
|
-
* dangerous mode via a bad ID).
|
|
829
|
-
*
|
|
830
|
-
* ③ DANGEROUS gate: whichever mode stage ② picked, if it is dangerous and the
|
|
831
|
-
* machine-local delegatedWorkerDangerousModeAllow is not set, downgrade to a
|
|
832
|
-
* non-dangerous PTY-parse mode (or `false` if none exists).
|
|
792
|
+
* Resolve whether a delegated worker session launched onto `nodePolicy` (within a mesh
|
|
793
|
+
* governed by `meshPolicy`) should auto-approve. Precedence: node override → mesh policy
|
|
794
|
+
* → default true. The result is stamped into the worker launch settings envelope as
|
|
795
|
+
* `autoApprove`; it wins over the global per-provider-type autoApprove config because the
|
|
796
|
+
* launch path merges the envelope as a settingsOverride on top of the provider defaults.
|
|
833
797
|
*/
|
|
834
798
|
export function resolveDelegatedWorkerAutoApprove(
|
|
835
|
-
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove'
|
|
836
|
-
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove'
|
|
837
|
-
|
|
838
|
-
repoConfig?: RepoMeshDeclarativeConfig | null,
|
|
839
|
-
// providerType is needed to look up the repo-declared requested mode; it is
|
|
840
|
-
// separate from `provider` because the caller resolves the spec independently.
|
|
841
|
-
providerType?: string | null,
|
|
842
|
-
): boolean | string {
|
|
843
|
-
// ── ① ENABLE gate — machine-local only. false short-circuits before mode. ──
|
|
844
|
-
let enabled = true;
|
|
799
|
+
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove'> | null,
|
|
800
|
+
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove'> | null,
|
|
801
|
+
): boolean {
|
|
845
802
|
if (typeof nodePolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
846
|
-
|
|
847
|
-
} else if (typeof meshPolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
848
|
-
enabled = meshPolicy.delegatedWorkerAutoApprove;
|
|
803
|
+
return nodePolicy.delegatedWorkerAutoApprove;
|
|
849
804
|
}
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
const modes = provider?.autoApproveModes;
|
|
853
|
-
if (!modes) return true;
|
|
854
|
-
|
|
855
|
-
// ── ② MODE selection (enabled only). Repo providerDefaults MAY override the
|
|
856
|
-
// provider spec default here — but only with a mode ID the spec knows. ──
|
|
857
|
-
// Future: a per-task override would slot in ahead of the repo default; the
|
|
858
|
-
// signature reserves that precedence but no task override is wired yet.
|
|
859
|
-
const requestedModeRaw = typeof providerType === 'string'
|
|
860
|
-
? repoConfig?.providerDefaults?.autoApproveModes?.[providerType.trim()]
|
|
861
|
-
: undefined;
|
|
862
|
-
const requestedModeId = typeof requestedModeRaw === 'string' && requestedModeRaw.trim()
|
|
863
|
-
? requestedModeRaw.trim()
|
|
864
|
-
: '';
|
|
865
|
-
const requestedMode = requestedModeId
|
|
866
|
-
? modes.modes.find((mode) => mode.id === requestedModeId)
|
|
867
|
-
: undefined;
|
|
868
|
-
const selectedMode = requestedMode
|
|
869
|
-
?? modes.modes.find((mode) => mode.id === modes.default);
|
|
870
|
-
if (!selectedMode || selectedMode.strategy === 'post-boot-command') return false;
|
|
871
|
-
|
|
872
|
-
// ── ③ DANGEROUS gate — downgrade a dangerous selection without machine opt-in. ──
|
|
873
|
-
const dangerousAllowed = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
874
|
-
if (deriveAutoApproveModeRisk(selectedMode) === 'dangerous' && !dangerousAllowed) {
|
|
875
|
-
const ptyFallback = modes.modes.find((mode) =>
|
|
876
|
-
mode.strategy === 'pty-parse-default' && deriveAutoApproveModeRisk(mode) !== 'dangerous');
|
|
877
|
-
return ptyFallback?.id || false;
|
|
805
|
+
if (typeof meshPolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
806
|
+
return meshPolicy.delegatedWorkerAutoApprove;
|
|
878
807
|
}
|
|
879
|
-
return
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
export function resolveDelegatedWorkerDangerousModeAllow(
|
|
883
|
-
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerDangerousModeAllow'> | null,
|
|
884
|
-
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerDangerousModeAllow'> | null,
|
|
885
|
-
): boolean {
|
|
886
|
-
if (typeof nodePolicy?.delegatedWorkerDangerousModeAllow === 'boolean') {
|
|
887
|
-
return nodePolicy.delegatedWorkerDangerousModeAllow;
|
|
888
|
-
}
|
|
889
|
-
return meshPolicy?.delegatedWorkerDangerousModeAllow === true;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
/** Shape a boolean-or-mode resolution for the settings precedence contract. */
|
|
893
|
-
export function delegatedWorkerAutoApproveSettings(
|
|
894
|
-
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
895
|
-
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
896
|
-
provider?: Pick<ProviderModule, 'autoApproveModes'> | null,
|
|
897
|
-
repoConfig?: RepoMeshDeclarativeConfig | null,
|
|
898
|
-
providerType?: string | null,
|
|
899
|
-
): {
|
|
900
|
-
autoApprove: boolean | undefined;
|
|
901
|
-
autoApproveMode: string | undefined;
|
|
902
|
-
delegatedWorkerDangerousModeAllow: boolean;
|
|
903
|
-
} {
|
|
904
|
-
const resolved = resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType);
|
|
905
|
-
const delegatedWorkerDangerousModeAllow = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
906
|
-
return typeof resolved === 'string'
|
|
907
|
-
? { autoApprove: undefined, autoApproveMode: resolved, delegatedWorkerDangerousModeAllow }
|
|
908
|
-
: { autoApprove: resolved, autoApproveMode: undefined, delegatedWorkerDangerousModeAllow };
|
|
808
|
+
return true;
|
|
909
809
|
}
|
|
910
810
|
|
|
911
811
|
/**
|
package/src/shared-types.ts
CHANGED
|
@@ -52,7 +52,7 @@ export type { ProviderErrorReason } from './providers/provider-instance.js';
|
|
|
52
52
|
// Local import for use in Managed*Entry types below
|
|
53
53
|
import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
|
|
54
54
|
import type { WorkspaceEntry } from './config/workspaces.js';
|
|
55
|
-
import type {
|
|
55
|
+
import type { ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
|
|
56
56
|
import type {
|
|
57
57
|
GitCompactSummary,
|
|
58
58
|
GitDiffSummary,
|
|
@@ -563,8 +563,6 @@ export interface AvailableProviderInfo {
|
|
|
563
563
|
lastVerification?: MachineProviderCheckResult;
|
|
564
564
|
/** Provider-declared Repo Mesh coordinator/MCP behavior. */
|
|
565
565
|
meshCoordinator?: ProviderMeshCoordinatorConfig;
|
|
566
|
-
/** Provider-declared auto-approve choices shown by session launch UIs. */
|
|
567
|
-
autoApproveModes?: AutoApproveModesConfig;
|
|
568
566
|
/** BRAIN-ROUTING: suggested model values for the new-session model dropdown. */
|
|
569
567
|
modelOptions?: string[];
|
|
570
568
|
/** BRAIN-ROUTING: reasoning-effort values for the new-session thinking dropdown. */
|
|
@@ -777,12 +775,6 @@ export interface DashboardBootstrapDaemonEntry extends Partial<CloudDaemonSummar
|
|
|
777
775
|
export type DaemonStatusEventName =
|
|
778
776
|
| 'agent:generating_started'
|
|
779
777
|
| 'agent:waiting_approval'
|
|
780
|
-
// A question picker (AskUserQuestion / InteractivePrompt) parks the agent
|
|
781
|
-
// awaiting a human decision — distinct from an approval modal, but equally a
|
|
782
|
-
// state the user must answer before work continues. Relayed to the server so
|
|
783
|
-
// push notifications fire (owner requirement: coordinator sessions must be
|
|
784
|
-
// pinged for pending questions).
|
|
785
|
-
| 'agent:waiting_choice'
|
|
786
778
|
| 'agent:generating_completed'
|
|
787
779
|
| 'agent:stopped'
|
|
788
780
|
| 'monitor:no_progress'
|
package/src/status/reporter.ts
CHANGED
|
@@ -118,7 +118,6 @@ export class DaemonStatusReporter {
|
|
|
118
118
|
switch (value) {
|
|
119
119
|
case 'agent:generating_started':
|
|
120
120
|
case 'agent:waiting_approval':
|
|
121
|
-
case 'agent:waiting_choice':
|
|
122
121
|
case 'agent:generating_completed':
|
|
123
122
|
case 'agent:stopped':
|
|
124
123
|
case 'monitor:no_progress':
|
package/src/status/snapshot.ts
CHANGED
|
@@ -145,7 +145,6 @@ export function buildAvailableProviders(
|
|
|
145
145
|
lastDetection?: AvailableProviderInfo['lastDetection'];
|
|
146
146
|
lastVerification?: AvailableProviderInfo['lastVerification'];
|
|
147
147
|
meshCoordinator?: AvailableProviderInfo['meshCoordinator'];
|
|
148
|
-
autoApproveModes?: AvailableProviderInfo['autoApproveModes'];
|
|
149
148
|
_sourceTrust?: AvailableProviderInfo['trust'];
|
|
150
149
|
_sourceLayer?: AvailableProviderInfo['sourceLayer'];
|
|
151
150
|
_sourceName?: string | null;
|
|
@@ -184,7 +183,6 @@ export function buildAvailableProviders(
|
|
|
184
183
|
...(provider.lastDetection !== undefined ? { lastDetection: provider.lastDetection } : {}),
|
|
185
184
|
...(provider.lastVerification !== undefined ? { lastVerification: provider.lastVerification } : {}),
|
|
186
185
|
...(provider.meshCoordinator !== undefined ? { meshCoordinator: provider.meshCoordinator } : {}),
|
|
187
|
-
...(provider.autoApproveModes !== undefined ? { autoApproveModes: provider.autoApproveModes } : {}),
|
|
188
186
|
...(trust ? {
|
|
189
187
|
trust,
|
|
190
188
|
trustDescription: describeTrust(trust),
|