@aiwg/cli 2026.10.3 → 2026.10.5
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/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +69 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +286 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +130 -0
- package/dist/src/artifacts/index-refresh.js +591 -0
- package/dist/src/artifacts/index-worker.mjs +11 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +137 -43
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +95 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +252 -12
- package/dist/src/sessions/contracts.js +50 -3
- package/dist/src/sessions/coverage-preflight.js +27 -2
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +145 -26
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +105 -0
- package/dist/src/sessions/repository.js +151 -54
- package/dist/src/sessions/workspace-discovery.js +292 -60
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -64,6 +64,7 @@ export const WorkerAdapterSchema = z.custom().superRefine((value, context) => {
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
const CapabilityEntrySchema = z.object({
|
|
67
|
+
sessionEnvAllowlist: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/)).optional(),
|
|
67
68
|
agent: OutcomeSchema,
|
|
68
69
|
skill: OutcomeSchema,
|
|
69
70
|
globalChild: OutcomeSchema,
|
|
@@ -497,12 +498,68 @@ export function requiredWorkerControls(context) {
|
|
|
497
498
|
}
|
|
498
499
|
/** The same sandbox requirement governs inspection, resolution and rendering. */
|
|
499
500
|
export function requiresWorkerExternalBoundary(adapter, selection) {
|
|
501
|
+
if (hasOperatorWorkerNativeRenderer(adapter) && selection.permissions?.sandbox !== 'external')
|
|
502
|
+
return false;
|
|
500
503
|
const providers = [adapter.id, adapter.provider].map(normalizeProviderDefinitionId);
|
|
501
504
|
return requiredWorkerControls(selection).includes('sandbox')
|
|
502
505
|
&& (providers.some(provider => provider && ['claude', 'muse', 'hermes'].includes(provider))
|
|
503
506
|
|| adapter.invocationProfiles?.[selection.profile]?.controls.sandbox?.support === 'external'
|
|
504
507
|
|| selection.permissions?.sandbox === 'external');
|
|
505
508
|
}
|
|
509
|
+
/** Explicit local operator approval for the direct CLI baseline (decision 17).
|
|
510
|
+
* This is not backend attestation or a live negative-fixture result. */
|
|
511
|
+
export const WORKER_OPERATOR_APPROVAL = 'aiwg doctor --probe-workers --qualify';
|
|
512
|
+
export function isOperatorQualifiedWorkerAdapter(adapter) {
|
|
513
|
+
return adapter.qualification.status === 'qualified'
|
|
514
|
+
&& adapter.qualification.evidenceRef === WORKER_OPERATOR_APPROVAL;
|
|
515
|
+
}
|
|
516
|
+
/** Only this bundled renderer has an independently checked native direct baseline. */
|
|
517
|
+
export function hasOperatorWorkerNativeRenderer(adapter) {
|
|
518
|
+
return isOperatorQualifiedWorkerAdapter(adapter) && adapter.provider === 'codex'
|
|
519
|
+
&& adapter.invocation.kind === 'process';
|
|
520
|
+
}
|
|
521
|
+
/** Check the actual argv before granting the direct renderer exception. */
|
|
522
|
+
export function assertOperatorWorkerNativeArgv(adapter, argv, selected, permissions) {
|
|
523
|
+
if (!hasOperatorWorkerNativeRenderer(adapter))
|
|
524
|
+
return;
|
|
525
|
+
const args = argv.slice(1);
|
|
526
|
+
const requirePair = (flag, value) => {
|
|
527
|
+
if (args.filter(token => token === flag).length !== 1 || args[args.indexOf(flag) + 1] !== value) {
|
|
528
|
+
throw new Error(`Requested native worker control is absent or ambiguous: ${flag}`);
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
const sandbox = permissions?.sandbox ?? 'workspace-write';
|
|
532
|
+
if (!['read-only', 'workspace-write'].includes(sandbox))
|
|
533
|
+
throw new Error('Requested sandbox has no verified native renderer');
|
|
534
|
+
requirePair('-m', selected.model);
|
|
535
|
+
requirePair('-s', sandbox);
|
|
536
|
+
for (const [name, value] of [['model_reasoning_effort', selected.effort.value], ['approval_policy', permissions?.approval ?? 'never']]) {
|
|
537
|
+
const overrides = args.flatMap((token, index) => token === '-c' ? [args[index + 1]] : [])
|
|
538
|
+
.filter(token => token?.split('=')[0] === name);
|
|
539
|
+
if (value === null || overrides.length !== 1 || overrides[0] !== `${name}=${JSON.stringify(value)}`) {
|
|
540
|
+
throw new Error(`Requested native worker control is absent or ambiguous: ${name}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Permit only the bounded native command grammar. Alternate/combined flags,
|
|
544
|
+
// profiles and additional config overrides must not undo the checked pairs.
|
|
545
|
+
if (args[0] !== '--no-daemon' || args[1] !== 'exec' || !args.includes('--ignore-user-config')) {
|
|
546
|
+
throw new Error('Native worker controls could be overridden');
|
|
547
|
+
}
|
|
548
|
+
for (let index = 2; index < args.length; index++) {
|
|
549
|
+
const flag = args[index];
|
|
550
|
+
if (['--ignore-user-config', '--skip-git-repo-check', '--json'].includes(flag))
|
|
551
|
+
continue;
|
|
552
|
+
if (flag === '-' && index === args.length - 1)
|
|
553
|
+
continue;
|
|
554
|
+
if (!['-m', '-s', '-c', '-C', '-o'].includes(flag) || index + 1 >= args.length) {
|
|
555
|
+
throw new Error('Native worker controls could be overridden');
|
|
556
|
+
}
|
|
557
|
+
const value = args[++index];
|
|
558
|
+
if (flag === '-c' && !['model_reasoning_effort', 'approval_policy'].includes(value.split('=')[0])) {
|
|
559
|
+
throw new Error('Native worker controls could be overridden');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
506
563
|
/** Inspect declared controls before installation evidence can be produced. This
|
|
507
564
|
* does not authorize invocation; consumers must use qualifyWorkerInvocationProfile. */
|
|
508
565
|
export function inspectWorkerInvocationProfile(adapter, selection) {
|
|
@@ -517,6 +574,23 @@ export function inspectWorkerInvocationProfile(adapter, selection) {
|
|
|
517
574
|
if (workerVersionRangeMismatch(selection.cliVersion, [adapter.supportedVersions, profile.supportedVersions]) !== null) {
|
|
518
575
|
throw new Error('Worker CLI version is outside qualified profile');
|
|
519
576
|
}
|
|
577
|
+
// The direct CLI baseline trusts the installed driver's bounded renderer.
|
|
578
|
+
// Explicit extra controls still require the existing qualified mappings.
|
|
579
|
+
if (hasOperatorWorkerNativeRenderer(adapter) && profile.transport === 'shell') {
|
|
580
|
+
if (selection.permissions?.sandbox && !['read-only', 'workspace-write'].includes(selection.permissions.sandbox)) {
|
|
581
|
+
throw new Error('Requested sandbox has no verified native renderer');
|
|
582
|
+
}
|
|
583
|
+
if (selection.permissions?.sandbox === 'external' && (!selection.boundary
|
|
584
|
+
|| !['os', 'executor'].includes(selection.boundary.kind) || !selection.boundary.controls.includes('sandbox'))) {
|
|
585
|
+
throw new Error('Requested external sandbox requires an enforcing boundary');
|
|
586
|
+
}
|
|
587
|
+
for (const name of selection.requiredControls ?? []) {
|
|
588
|
+
if (!['native', 'external'].includes(profile.controls[name]?.support)) {
|
|
589
|
+
throw new Error(`Required worker control is unqualified: ${name}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return profile;
|
|
593
|
+
}
|
|
520
594
|
const requiredControls = requiredWorkerControls(selection);
|
|
521
595
|
if (requiresWorkerExternalBoundary(adapter, selection)
|
|
522
596
|
&& (!selection.boundary || !['os', 'executor'].includes(selection.boundary.kind)
|
|
@@ -546,6 +620,10 @@ export function inspectWorkerInvocationProfile(adapter, selection) {
|
|
|
546
620
|
* installation scope, including this boundary, against the recorded digest. */
|
|
547
621
|
export function qualifyWorkerInvocationProfile(adapter, selection) {
|
|
548
622
|
const profile = inspectWorkerInvocationProfile(adapter, selection);
|
|
623
|
+
if (isOperatorQualifiedWorkerAdapter(adapter) && selection.policy?.guards?.denySelfDelegation !== false
|
|
624
|
+
&& !adapter.capabilities.nestedAgentsControllable) {
|
|
625
|
+
throw new Error('Required worker control is unqualified: children');
|
|
626
|
+
}
|
|
549
627
|
if (requiresWorkerExternalBoundary(adapter, selection) && !profile.installationQualification) {
|
|
550
628
|
throw new Error('Required worker control sandbox needs matching installation qualification for its external boundary');
|
|
551
629
|
}
|
|
@@ -817,7 +895,7 @@ export function validateWorkerPolicyRoute(input) {
|
|
|
817
895
|
const guards = input.policy.guards ?? DEFAULT_WORKER_SELECTION.guards;
|
|
818
896
|
const guidance = input.policy.roleGuidance?.[input.role];
|
|
819
897
|
if (observation.qualification.status !== 'qualified' || !observation.qualification.roles.includes(input.role))
|
|
820
|
-
deny('OG_ADAPTER_UNQUALIFIED', '/worker/qualification', 'Role lacks qualified evidence');
|
|
898
|
+
deny('OG_ADAPTER_UNQUALIFIED', '/worker/qualification', 'Role lacks qualified evidence. Run aiwg doctor --probe-workers --qualify.');
|
|
821
899
|
if (!guidance)
|
|
822
900
|
deny('OG_CONFIG_REFERENCE', `/defaults/workerSelection/roleGuidance/${input.role}`, 'Role guidance is unavailable');
|
|
823
901
|
const substitution = input.substitution?.model === input.selectedModel
|
|
@@ -855,13 +933,22 @@ export function validateWorkerPolicyRoute(input) {
|
|
|
855
933
|
else {
|
|
856
934
|
const effort = observation.efforts.find(e => e.id === input.effort);
|
|
857
935
|
if (observation.effortSupport !== 'explicit' || !effort || !(input.effort === 'none' ? guidance?.initialEffort === 'none' && guidance.effortLadder.length === 0 : guidance?.effortLadder.includes(input.effort)) || isWorkerEffortDenied(input.effort, input.policy) || (guards.denySelfDelegation !== false && effort.selfDelegates !== false))
|
|
858
|
-
deny('OG_EFFORT_DENIED', '/worker/efforts', 'Unsupported, denied or self-delegating effort');
|
|
936
|
+
deny('OG_EFFORT_DENIED', '/worker/efforts', 'Unsupported, denied or self-delegating effort. Check catalog effort evidence and models.json defaults.workerSelection.roleGuidance, guards.deniedEfforts and guards.denySelfDelegation.');
|
|
859
937
|
}
|
|
860
938
|
const { data, unknownDataClass } = workerRouteDataPolicy(input.policy, input.dataClass);
|
|
861
939
|
if (unknownDataClass)
|
|
862
940
|
deny('OG_DATA_POLICY', '/defaults/workerSelection/dataClasses', 'Unknown data class');
|
|
863
|
-
if (isWorkerDataUseDenied(observation.dataUse, data))
|
|
864
|
-
|
|
941
|
+
if (isWorkerDataUseDenied(observation.dataUse, data)) {
|
|
942
|
+
const facts = observation.dataUse;
|
|
943
|
+
const keys = [
|
|
944
|
+
...((facts.processing === 'unknown' || facts.productImprovement === null
|
|
945
|
+
|| data.maxRetentionDays !== undefined && facts.retentionDays === null) && data.allowUnknown !== true ? ['allowUnknown'] : []),
|
|
946
|
+
...(facts.processing !== 'unknown' && !data.allowedProcessing?.includes(facts.processing) ? ['allowedProcessing'] : []),
|
|
947
|
+
...(facts.productImprovement === true && data.allowProductImprovement !== true ? ['allowProductImprovement'] : []),
|
|
948
|
+
...(data.maxRetentionDays !== undefined && facts.retentionDays !== null && facts.retentionDays > data.maxRetentionDays ? ['maxRetentionDays'] : []),
|
|
949
|
+
];
|
|
950
|
+
deny('OG_DATA_POLICY', '/worker/dataUse', `Execution path data facts do not satisfy policy. Review models.json ${keys.map(key => `defaults.workerSelection.dataPolicy.${key}`).join(', ')}${input.dataClass ? ` and defaults.workerSelection.dataClasses.${input.dataClass}` : ''}; unknown facts remain unknown.`);
|
|
951
|
+
}
|
|
865
952
|
const now = (input.now ?? new Date()).getTime();
|
|
866
953
|
if (observation.retiredAt != null && Date.parse(observation.retiredAt) <= now
|
|
867
954
|
|| observation.sunsetAt && Date.parse(observation.sunsetAt) <= now) {
|
|
@@ -873,7 +960,14 @@ export function validateWorkerPolicyRoute(input) {
|
|
|
873
960
|
diagnostics.push({ code: 'OG_ADAPTER_UNQUALIFIED', pointer: '/worker/observedAt', blocking: false, message: 'Model observation exceeds the freshness warning threshold' });
|
|
874
961
|
}
|
|
875
962
|
if (input.context && !workerContextHeadroom(input.context, observation).fits) {
|
|
876
|
-
|
|
963
|
+
const missing = [observation.contextTokens === null ? 'contextTokens' : ''].filter(Boolean);
|
|
964
|
+
deny('OG_MODEL_UNAPPROVED', '/worker/contextTokens', missing.length
|
|
965
|
+
? `Bounded context and output cannot be checked: catalog does not report a qualified ${missing.join(', ')} limit. Refresh discovery with reported limits; operator approval does not invent limits.`
|
|
966
|
+
: 'Bounded context and output do not fit qualified limits; reduce the request context or reserved output.');
|
|
967
|
+
}
|
|
968
|
+
if (input.context && observation.contextTokens !== null && observation.maxOutputTokens === null) {
|
|
969
|
+
diagnostics.push({ code: 'OG_MODEL_UNAPPROVED', pointer: '/worker/maxOutputTokens', blocking: false,
|
|
970
|
+
message: 'Output limit is unreported; only the context window was checked.' });
|
|
877
971
|
}
|
|
878
972
|
return diagnostics;
|
|
879
973
|
}
|
|
@@ -883,8 +977,8 @@ export function workerContextHeadroom(estimate, observation) {
|
|
|
883
977
|
const total = terms.reduce((sum, value) => sum + value, 0);
|
|
884
978
|
const fits = Boolean(estimate.estimator.trim()) && terms.every(value => Number.isSafeInteger(value) && value >= 0)
|
|
885
979
|
&& (estimate.safetyMargin === undefined || Number.isSafeInteger(estimate.safetyMargin) && estimate.safetyMargin >= 0)
|
|
886
|
-
&& Number.isSafeInteger(total) && observation.contextTokens !== null
|
|
887
|
-
&& total <= observation.contextTokens && estimate.reservedOutput <= observation.maxOutputTokens;
|
|
980
|
+
&& Number.isSafeInteger(total) && observation.contextTokens !== null
|
|
981
|
+
&& total <= observation.contextTokens && (observation.maxOutputTokens === null || estimate.reservedOutput <= observation.maxOutputTokens);
|
|
888
982
|
return { ...estimate, safetyMargin, total, qualifiedContextTokens: observation.contextTokens,
|
|
889
983
|
qualifiedMaxOutputTokens: observation.maxOutputTokens, fits };
|
|
890
984
|
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ConfigLoader } from './config-loader.js';
|
|
9
9
|
import { WORKER_DISPATCH_ENABLE_COMMAND, WORKER_DISPATCH_READINESS_COMMAND } from './worker-dispatch-offer.js';
|
|
10
|
-
import { resolveWorkerGroupInstallation, resolveParallelism, validateWorkerGroupReferences, validateWorkerBudgetEnforcement, workerActivationDigests, workerConfigDigest, } from '../config/aiwg-config.js';
|
|
11
|
-
import { qualifyWorkerInvocationProfile, validateWorkerPolicyRoute, validateWorkerModelObservation, hasSelectionRelevantConflicts, workerContextHeadroom, workerModelKey, isCanonicalWorkerModelKey, WorkerSelectionPolicySchema, validateWorkerCatalogDiscovery, } from './provider-policy.js';
|
|
10
|
+
import { resolveWorkerGroupInstallation, resolveParallelism, validateWorkerGroupQualifications, validateWorkerGroupReferences, validateWorkerBudgetEnforcement, workerActivationDigests, workerConfigDigest, } from '../config/aiwg-config.js';
|
|
11
|
+
import { qualifyWorkerInvocationProfile, validateWorkerPolicyRoute, validateWorkerModelObservation, hasOperatorWorkerNativeRenderer, hasSelectionRelevantConflicts, workerContextHeadroom, workerModelKey, isCanonicalWorkerModelKey, WorkerSelectionPolicySchema, validateWorkerCatalogDiscovery, } from './provider-policy.js';
|
|
12
12
|
import { MODEL_WRAPPERS } from './provider-models.js';
|
|
13
13
|
import { routeModelTier } from './router.js';
|
|
14
14
|
/**
|
|
@@ -344,8 +344,9 @@ export function resolveWorkerPlan(snapshot, request) {
|
|
|
344
344
|
? observed.qualification.roles.filter(role => policy.roleGuidance?.[role]?.groups.includes(groupId)) : [];
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
plan.diagnostics.push(...validateWorkerGroupQualifications(parallelism, qualifiedRoles).map(diagnostic => ({
|
|
348
|
+
...diagnostic, message: `${diagnostic.message}. Run aiwg doctor --probe-workers --qualify.`,
|
|
349
|
+
})));
|
|
349
350
|
const taskRole = request.taskClass ? policy.taskClasses?.[request.taskClass] : undefined;
|
|
350
351
|
const role = request.role ?? taskRole;
|
|
351
352
|
plan.role = role ?? null;
|
|
@@ -397,7 +398,7 @@ export function resolveWorkerPlan(snapshot, request) {
|
|
|
397
398
|
if (pinMismatch && !substitution)
|
|
398
399
|
deny('OG_PIN_MISMATCH', pointer, 'Exact request or wrapper pin requires explicit substitution');
|
|
399
400
|
if (!observation || !binding || !row?.llmProvider || !row.apiModelId)
|
|
400
|
-
deny('OG_ADAPTER_UNQUALIFIED', pointer, 'Missing exact model, inference provider, observation or current installation evidence');
|
|
401
|
+
deny('OG_ADAPTER_UNQUALIFIED', pointer, 'Missing exact model, inference provider, observation or current installation evidence. Run aiwg doctor --probe-workers --qualify.');
|
|
401
402
|
if (observation && binding && row?.llmProvider && row.apiModelId) {
|
|
402
403
|
const shapeErrors = validateWorkerModelObservation(observation);
|
|
403
404
|
if (shapeErrors.length) {
|
|
@@ -424,12 +425,13 @@ export function resolveWorkerPlan(snapshot, request) {
|
|
|
424
425
|
const configuredServer = parallelism.clis?.[member.cli]?.mcpServerRef;
|
|
425
426
|
if (transport === 'mcp' && configuredServer && configuredServer !== binding.mcp?.serverRef)
|
|
426
427
|
throw new Error('Configured MCP server changed');
|
|
427
|
-
if (adapter.capabilities.modelPin === 'unsupported' || !adapter.capabilities.nativeFallbackControllable
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
if (!hasOperatorWorkerNativeRenderer(adapter) && (adapter.capabilities.modelPin === 'unsupported' || !adapter.capabilities.nativeFallbackControllable))
|
|
429
|
+
throw new Error('Exact model or fallback control is unqualified; qualify an adapter with these controls');
|
|
430
|
+
if (policy.guards?.denySelfDelegation !== false && !adapter.capabilities.nestedAgentsControllable)
|
|
431
|
+
throw new Error('Child-agent control is unqualified. Review models.json defaults.workerSelection.guards.denySelfDelegation; false permits uncontrolled child agents without qualifying their behavior.');
|
|
430
432
|
if (binding.identityEvidence === 'backend-attested' && adapter.capabilities.modelPin !== 'observed')
|
|
431
433
|
throw new Error('Requested-only mapping cannot attest backend identity');
|
|
432
|
-
if (group.execution?.sandbox && !adapter.capabilities.sandboxModes.includes(group.execution.sandbox))
|
|
434
|
+
if (!hasOperatorWorkerNativeRenderer(adapter) && group.execution?.sandbox && !adapter.capabilities.sandboxModes.includes(group.execution.sandbox))
|
|
433
435
|
throw new Error('Requested sandbox mode is unqualified');
|
|
434
436
|
qualifyWorkerInvocationProfile(adapter, { profile: binding.profile, transport, cliVersion: binding.cliVersion,
|
|
435
437
|
policy, permissions, effort, boundary: binding.boundary,
|
|
@@ -460,7 +462,7 @@ export function resolveWorkerPlan(snapshot, request) {
|
|
|
460
462
|
const authority = routeModelTier({ ...request.authority, deterministic: false, routine: false,
|
|
461
463
|
defaultTier: guidance.tier === 'economy' ? 1 : 2, requestedPremium: premium || request.authority?.requestedPremium });
|
|
462
464
|
if (authority.requiresConfirmation)
|
|
463
|
-
deny('OG_MODEL_UNAPPROVED', '/request/authority', authority.rationale.join('; '));
|
|
465
|
+
deny('OG_MODEL_UNAPPROVED', '/request/authority', `${authority.rationale.join('; ')}; agents may pass --authorize-premium only after the human explicitly confirmed the premium escalation (escalation-discipline rule)`);
|
|
464
466
|
const installation = parallelism.clis?.[member.cli];
|
|
465
467
|
const caps = [parallelism.max_parallel_subagents, group.maxParallel, parallelism.admission?.groupCaps?.[groupId],
|
|
466
468
|
parallelism.admission?.memberCaps?.[`${groupId}/${member.id}`], parallelism.admission?.cliCaps?.[member.cli],
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
* is a binding the public config validator also accepts, and vice versa.
|
|
48
48
|
*
|
|
49
49
|
* The shipped direct profiles are declarations, not live qualification: no
|
|
50
|
-
* finding ever calls a profile "qualified".
|
|
51
|
-
*
|
|
50
|
+
* finding ever calls a profile "qualified". Explicit operator approval uses `aiwg doctor --probe-workers --qualify`;
|
|
51
|
+
* a probe alone does not qualify a profile, and an installed, in-range CLI
|
|
52
52
|
* can still be refused by the resolver — the dispatch finding says so
|
|
53
53
|
* plainly with the resolver's own reason.
|
|
54
54
|
*
|
|
@@ -61,7 +61,7 @@ import { isAbsolute, resolve as resolvePath } from 'node:path';
|
|
|
61
61
|
import { boundedVersionProbe, normalizeWorkerVersionBanner, workerVersionProbeDefaults, workerVersionProbeEnvironment, workerVersionRangeMismatch, } from './worker-installation.mjs';
|
|
62
62
|
import { resolveWorkerGroupInstallation } from '../config/aiwg-config.js';
|
|
63
63
|
import { resolveWorkerPlan } from './resolver.js';
|
|
64
|
-
const SMOKE_NOTE = '
|
|
64
|
+
const SMOKE_NOTE = 'A version probe alone leaves profiles unqualified. To record direct CLI operator approval, run aiwg doctor --probe-workers --qualify; a version probe is not an authenticated smoke run (see #3005).';
|
|
65
65
|
function truncateBanner(banner, limit = 200) {
|
|
66
66
|
const singleLine = banner.replace(/[\r\n]+/g, '\\n');
|
|
67
67
|
return singleLine.length > limit ? `${singleLine.slice(0, limit)}…` : singleLine;
|
|
@@ -166,13 +166,13 @@ function describeConfigContext(members) {
|
|
|
166
166
|
* `parallelism.clis` entries plus any cli id referenced only implicitly by a
|
|
167
167
|
* group member (canonical default binding, no explicit installation entry).
|
|
168
168
|
*/
|
|
169
|
-
function allConfiguredCliIds(parallelism) {
|
|
169
|
+
function allConfiguredCliIds(parallelism, cli) {
|
|
170
170
|
const ids = new Set(Object.keys(parallelism.clis ?? {}));
|
|
171
171
|
for (const group of Object.values(parallelism.groups ?? {})) {
|
|
172
172
|
for (const member of group?.members ?? [])
|
|
173
173
|
ids.add(member.cli);
|
|
174
174
|
}
|
|
175
|
-
return [...ids].sort();
|
|
175
|
+
return [...ids].filter(id => cli === undefined || id === cli).sort();
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
178
178
|
* A cli is "required" only when an enabled group member referencing it also
|
|
@@ -202,12 +202,6 @@ function advisoryStatus(status, required) {
|
|
|
202
202
|
return required || status !== 'error' ? status : 'warn';
|
|
203
203
|
}
|
|
204
204
|
const offlineDirectContext = () => ({ estimator: 'doctor-offline-direct-context-v1', briefTokens: 0, inputTokens: 0, toolOutputAllowance: 0, reservedOutput: 0 });
|
|
205
|
-
function formatDiagnostics(diagnostics) {
|
|
206
|
-
const seen = new Map();
|
|
207
|
-
for (const diagnostic of diagnostics)
|
|
208
|
-
seen.set(`${diagnostic.code}:${diagnostic.pointer}:${diagnostic.message}`, diagnostic);
|
|
209
|
-
return [...seen.values()].map(diagnostic => `${diagnostic.code} ${diagnostic.pointer}: ${diagnostic.message}`).join('; ');
|
|
210
|
-
}
|
|
211
205
|
/**
|
|
212
206
|
* Dispatch eligibility from the actual offline dispatch resolver
|
|
213
207
|
* (`resolveWorkerPlan`): called once per role for every group member
|
|
@@ -222,7 +216,8 @@ function resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli,
|
|
|
222
216
|
config: resolverSnapshot.config, models: resolverSnapshot.models, modelDigests: resolverSnapshot.modelDigests,
|
|
223
217
|
catalog: resolverSnapshot.catalog, registry, installations: {},
|
|
224
218
|
};
|
|
225
|
-
const
|
|
219
|
+
const diagnostics = new Map();
|
|
220
|
+
const uncovered = [];
|
|
226
221
|
for (const ref of refs) {
|
|
227
222
|
if (!ref.enabled)
|
|
228
223
|
continue;
|
|
@@ -239,23 +234,48 @@ function resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli,
|
|
|
239
234
|
break;
|
|
240
235
|
}
|
|
241
236
|
const exclusion = plan.exclusions.find(matches);
|
|
242
|
-
|
|
237
|
+
for (const diagnostic of exclusion?.diagnostics ?? plan.diagnostics) {
|
|
238
|
+
// Only an exact member pointer is generalized; other config pointers
|
|
239
|
+
// remain distinct even when their diagnostic text happens to match.
|
|
240
|
+
const memberPointer = `/parallelism/groups/${ref.groupId}/members/${ref.memberId}`;
|
|
241
|
+
const pointer = diagnostic.pointer === memberPointer || diagnostic.pointer.startsWith(`${memberPointer}/`)
|
|
242
|
+
? `/parallelism/groups/${ref.groupId}/members${diagnostic.pointer.slice(memberPointer.length)}`
|
|
243
|
+
: diagnostic.pointer;
|
|
244
|
+
seen.push({ ...diagnostic, pointer, members: [`${ref.groupId}/${ref.memberId}`],
|
|
245
|
+
roles: diagnostic.roles ?? [role] });
|
|
246
|
+
}
|
|
243
247
|
}
|
|
244
248
|
if (eligibleHere) {
|
|
245
249
|
return { eligibility: 'eligible',
|
|
246
250
|
detail: `Dispatch resolver accepts cli '${cli}' for group '${ref.groupId}' member '${ref.memberId}'. `
|
|
247
251
|
+ `Dispatch eligibility: eligible. ${SMOKE_NOTE}` };
|
|
248
252
|
}
|
|
249
|
-
|
|
250
|
-
|
|
253
|
+
if (!seen.length)
|
|
254
|
+
uncovered.push(`${ref.groupId}/${ref.memberId}`);
|
|
255
|
+
for (const diagnostic of seen) {
|
|
256
|
+
const key = JSON.stringify([diagnostic.code, diagnostic.pointer, diagnostic.message,
|
|
257
|
+
diagnostic.blocking, diagnostic.origins]);
|
|
258
|
+
const shared = diagnostics.get(key);
|
|
259
|
+
if (shared) {
|
|
260
|
+
shared.members = [...new Set([...shared.members, ...diagnostic.members])];
|
|
261
|
+
shared.roles = [...new Set([...shared.roles, ...diagnostic.roles])];
|
|
262
|
+
}
|
|
263
|
+
else
|
|
264
|
+
diagnostics.set(key, diagnostic);
|
|
265
|
+
}
|
|
251
266
|
}
|
|
252
|
-
if (!
|
|
267
|
+
if (!diagnostics.size && !uncovered.length) {
|
|
253
268
|
return { eligibility: 'blocked',
|
|
254
269
|
detail: `Dispatch resolver has no enabled group member to evaluate for cli '${cli}'. `
|
|
255
270
|
+ `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
|
|
256
271
|
}
|
|
257
|
-
|
|
258
|
-
|
|
272
|
+
const distinct = [...diagnostics.values()];
|
|
273
|
+
const reasons = distinct.map(diagnostic => `${diagnostic.code} ${diagnostic.pointer}: ${diagnostic.message}`
|
|
274
|
+
+ ` [roles: ${diagnostic.roles.join(', ')}; members: ${diagnostic.members.join(', ')}]`);
|
|
275
|
+
if (uncovered.length)
|
|
276
|
+
reasons.push(`No qualified route [members: ${uncovered.join(', ')}]`);
|
|
277
|
+
return { eligibility: 'blocked', diagnostics: distinct,
|
|
278
|
+
detail: `Dispatch resolver refuses cli '${cli}': ${reasons.join('; ')}. `
|
|
259
279
|
+ `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
|
|
260
280
|
}
|
|
261
281
|
/**
|
|
@@ -277,7 +297,7 @@ function resolveEligibilityFromBindingOnly(parallelism, registry, cli, installRe
|
|
|
277
297
|
const reason = error instanceof Error ? error.message : String(error);
|
|
278
298
|
const readyNote = installReady
|
|
279
299
|
? ' The CLI is installed with an in-range version, but dispatch stays refused for the reason above; '
|
|
280
|
-
+
|
|
300
|
+
+ `an authenticated smoke run (see #3005) alone does not satisfy the resolver gates. ${SMOKE_NOTE}`
|
|
281
301
|
: ` ${SMOKE_NOTE}`;
|
|
282
302
|
return { eligibility: 'blocked',
|
|
283
303
|
detail: `Resolver refuses cli '${cli}': ${reason}. Dispatch eligibility: blocked.${readyNote}` };
|
|
@@ -300,7 +320,7 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
300
320
|
const { parallelism, registry, probe, announce, timeoutMs = workerVersionProbeDefaults.timeoutMs, probeCwd = tmpdir(), configFile = '.aiwg/aiwg.config', projectRoot = process.cwd(), resolverSnapshot } = input;
|
|
301
321
|
const report = {
|
|
302
322
|
enabled: parallelism.worker_dispatch === true,
|
|
303
|
-
workersConfigured: allConfiguredCliIds(parallelism).length > 0,
|
|
323
|
+
workersConfigured: allConfiguredCliIds(parallelism, input.cli).length > 0,
|
|
304
324
|
ready: true,
|
|
305
325
|
findings: [],
|
|
306
326
|
};
|
|
@@ -315,7 +335,7 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
315
335
|
members: [] });
|
|
316
336
|
return report;
|
|
317
337
|
}
|
|
318
|
-
const cliIds = allConfiguredCliIds(parallelism);
|
|
338
|
+
const cliIds = allConfiguredCliIds(parallelism, input.cli);
|
|
319
339
|
if (!cliIds.length) {
|
|
320
340
|
// No workers configured: no findings at all so doctor stays silent.
|
|
321
341
|
return report;
|
|
@@ -325,11 +345,11 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
325
345
|
if (finding.status === 'error' || finding.status === 'warn')
|
|
326
346
|
report.ready = false;
|
|
327
347
|
};
|
|
328
|
-
const pushDispatch = (cli, profile, status, code, eligibility, detail, configKey, members, nextAction) => {
|
|
348
|
+
const pushDispatch = (cli, profile, status, code, eligibility, detail, configKey, members, nextAction, diagnostics) => {
|
|
329
349
|
report.findings.push({ cli, kind: 'dispatch', profile, executable: null, installedVersion: null,
|
|
330
350
|
status, code, message: `${detail} (config ${configFile} key ${configKey}; ${describeConfigContext(members)})`,
|
|
331
351
|
nextAction, configFile, configKey,
|
|
332
|
-
readiness: null, eligibility, eligibilityDetail: detail, members });
|
|
352
|
+
readiness: null, eligibility, eligibilityDetail: detail, members, ...(diagnostics ? { diagnostics } : {}) });
|
|
333
353
|
};
|
|
334
354
|
await Promise.all(cliIds.map(async (cli) => {
|
|
335
355
|
const configured = parallelism.clis?.[cli];
|
|
@@ -344,8 +364,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
344
364
|
message: `Dispatch is disabled for cli '${cli}' (config ${configFile} key ${configKey}/enabled); the version probe was skipped. Installation readiness: skipped. (${describeConfigContext(members)})`,
|
|
345
365
|
nextAction: 'enable-cli-to-onboard',
|
|
346
366
|
configFile, configKey, readiness: 'skipped', members });
|
|
347
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
348
|
-
pushDispatch(cli, configured.invocationProfile ?? null, 'info', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'enable-cli-to-onboard');
|
|
367
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
368
|
+
pushDispatch(cli, configured.invocationProfile ?? null, 'info', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'enable-cli-to-onboard', diagnostics);
|
|
349
369
|
return;
|
|
350
370
|
}
|
|
351
371
|
const resolved = resolveDirectBinding(registry, configured?.adapter, cli);
|
|
@@ -355,8 +375,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
355
375
|
message: `${resolved.error} (config ${configFile} key ${configKey}/adapter); the profile cannot be onboarded. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
356
376
|
nextAction: 'review-worker-configuration',
|
|
357
377
|
configFile, configKey: `${configKey}/adapter`, readiness: 'not-ready', members });
|
|
358
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
359
|
-
pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration');
|
|
378
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
379
|
+
pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration', diagnostics);
|
|
360
380
|
return;
|
|
361
381
|
}
|
|
362
382
|
const { adapter, adapterRef } = resolved;
|
|
@@ -366,8 +386,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
366
386
|
message: `Cli '${cli}' binds '${adapterRef}' (config ${configFile} key ${configKey}/adapter), which uses an HTTP adapter; there is no CLI version probe — check endpoint health instead. Installation readiness: skipped. (${describeConfigContext(members)})`,
|
|
367
387
|
nextAction: 'check-endpoint-health',
|
|
368
388
|
configFile, configKey, readiness: 'skipped', members });
|
|
369
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
370
|
-
pushDispatch(cli, null, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'check-endpoint-health' : 'review-worker-configuration');
|
|
389
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
390
|
+
pushDispatch(cli, null, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'check-endpoint-health' : 'review-worker-configuration', diagnostics);
|
|
371
391
|
return;
|
|
372
392
|
}
|
|
373
393
|
const profileIds = Object.keys(adapter.invocationProfiles ?? {});
|
|
@@ -382,8 +402,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
382
402
|
: `Cli '${cli}' binding '${adapterRef}' has no single direct profile to check (config ${configFile} key ${configKey}); configure invocationProfile explicitly. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
383
403
|
nextAction: 'review-worker-configuration',
|
|
384
404
|
configFile, configKey, readiness: 'not-ready', members });
|
|
385
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
386
|
-
pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration');
|
|
405
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
406
|
+
pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration', diagnostics);
|
|
387
407
|
return;
|
|
388
408
|
}
|
|
389
409
|
const profile = adapter.invocationProfiles[profileRef];
|
|
@@ -394,8 +414,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
394
414
|
message: `Cli '${cli}' binds '${adapterRef}' profile '${profileRef}' (config ${configFile} key ${configKey}); version probe not run — no subprocess was started. Installation readiness: unprobed. Rerun with probes to check install/version. (${describeConfigContext(members)})`,
|
|
395
415
|
nextAction: 'rerun-with-probe-workers',
|
|
396
416
|
configFile, configKey, readiness: 'unprobed', members });
|
|
397
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
398
|
-
pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'rerun-with-probe-workers' : 'review-worker-configuration');
|
|
417
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
418
|
+
pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'rerun-with-probe-workers' : 'review-worker-configuration', diagnostics);
|
|
399
419
|
return;
|
|
400
420
|
}
|
|
401
421
|
announce?.(cli, executable, adapter.invocation.versionArgv);
|
|
@@ -416,8 +436,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
416
436
|
: `Version probe for cli '${cli}' ('${executable}', config ${configFile} key ${configKey}) failed; retry once, then check PATH and execute permission. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
417
437
|
nextAction: notInstalled ? 'install-cli' : 'retry-version-probe',
|
|
418
438
|
configFile, configKey, readiness: 'not-ready', members });
|
|
419
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
420
|
-
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, notInstalled ? 'install-cli' : 'retry-version-probe');
|
|
439
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
440
|
+
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, notInstalled ? 'install-cli' : 'retry-version-probe', diagnostics);
|
|
421
441
|
return;
|
|
422
442
|
}
|
|
423
443
|
const rawBanner = outcome.rawBanner.trim();
|
|
@@ -431,8 +451,8 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
431
451
|
message: `CLI '${executable}' banner ${JSON.stringify(truncateBanner(rawBanner))} does not match the registered version pattern (config ${configFile} key ${configKey}); the install is unexpected — reinstall a supported release. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
432
452
|
nextAction: 'reinstall-supported-cli',
|
|
433
453
|
configFile, configKey, readiness: 'not-ready', members });
|
|
434
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
435
|
-
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'reinstall-supported-cli');
|
|
454
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
455
|
+
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'reinstall-supported-cli', diagnostics);
|
|
436
456
|
return;
|
|
437
457
|
}
|
|
438
458
|
const mismatch = workerVersionRangeMismatch(installedVersion, [profile.supportedVersions, adapter.supportedVersions]);
|
|
@@ -442,17 +462,17 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
442
462
|
message: `CLI '${executable}' version ${installedVersion} is outside supported range '${mismatch}' for cli '${cli}' (profile '${profileRef}', config ${configFile} key ${configKey}); upgrade or pin a supported release. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
443
463
|
nextAction: 'upgrade-or-pin-cli',
|
|
444
464
|
configFile, configKey, readiness: 'not-ready', members });
|
|
445
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
446
|
-
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'upgrade-or-pin-cli');
|
|
465
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
|
|
466
|
+
pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'upgrade-or-pin-cli', diagnostics);
|
|
447
467
|
return;
|
|
448
468
|
}
|
|
449
469
|
pushInstall({ cli, profile: profileRef, executable, installedVersion,
|
|
450
470
|
status: 'ok', code: 'WORKER_DIRECT_READY',
|
|
451
471
|
message: `CLI '${executable}' version ${installedVersion} matches profile '${profileRef}' for cli '${cli}' (config ${configFile} key ${configKey}). Installation readiness: ready. ${SMOKE_NOTE} See the dispatch finding for whether the resolver would dispatch. (${describeConfigContext(members)})`,
|
|
452
|
-
nextAction: '
|
|
472
|
+
nextAction: 'qualify-installed-cli',
|
|
453
473
|
configFile, configKey, readiness: 'ready', members });
|
|
454
|
-
const { eligibility, detail } = checkDispatchEligibility(parallelism, registry, cli, true, resolverSnapshot, refs);
|
|
455
|
-
pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, '
|
|
474
|
+
const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, true, resolverSnapshot, refs);
|
|
475
|
+
pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'qualify-installed-cli', diagnostics);
|
|
456
476
|
}));
|
|
457
477
|
report.findings.sort((a, b) => a.cli.localeCompare(b.cli) || a.kind.localeCompare(b.kind));
|
|
458
478
|
return report;
|
|
@@ -478,7 +498,7 @@ export async function diagnoseDirectWorkerReadiness(input) {
|
|
|
478
498
|
*/
|
|
479
499
|
export async function buildDirectWorkerDoctorSection(input) {
|
|
480
500
|
const { parallelism, registry, probeWorkers } = input;
|
|
481
|
-
const cliIds = allConfiguredCliIds(parallelism ?? {});
|
|
501
|
+
const cliIds = allConfiguredCliIds(parallelism ?? {}, input.cli);
|
|
482
502
|
if (!cliIds.length)
|
|
483
503
|
return { lines: [], probed: false, workersConfigured: false, installFindings: [] };
|
|
484
504
|
const lines = [];
|
|
@@ -493,6 +513,7 @@ export async function buildDirectWorkerDoctorSection(input) {
|
|
|
493
513
|
const report = await diagnoseDirectWorkerReadiness({
|
|
494
514
|
parallelism: parallelism,
|
|
495
515
|
registry,
|
|
516
|
+
cli: input.cli,
|
|
496
517
|
...(probeWorkers ? { probe: input.probe ?? probeDirectWorkerVersion, ...(input.announce ? { announce: input.announce } : {}) } : {}),
|
|
497
518
|
...(input.configFile ? { configFile: input.configFile } : {}),
|
|
498
519
|
...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
|
|
@@ -509,7 +530,7 @@ export async function buildDirectWorkerDoctorSection(input) {
|
|
|
509
530
|
}
|
|
510
531
|
if (probeWorkers && !report.findings.some(finding => finding.code === 'WORKER_DIRECT_READY')) {
|
|
511
532
|
lines.push({ name: 'Direct Worker', status: 'info',
|
|
512
|
-
message:
|
|
533
|
+
message: `no profile is onboarded; ${SMOKE_NOTE}` });
|
|
513
534
|
}
|
|
514
535
|
return {
|
|
515
536
|
lines,
|
|
@@ -32,7 +32,7 @@ import { loadProviderModelCapabilities } from './provider-policy.js';
|
|
|
32
32
|
*/
|
|
33
33
|
export const WORKER_DISPATCH_ENABLE_COMMAND = 'aiwg config set --project parallelism.worker_dispatch true';
|
|
34
34
|
/** Readiness check shared by setup hints and dispatch refusals. */
|
|
35
|
-
export const WORKER_DISPATCH_READINESS_COMMAND = 'aiwg doctor --probe-workers';
|
|
35
|
+
export const WORKER_DISPATCH_READINESS_COMMAND = 'aiwg doctor --probe-workers --qualify';
|
|
36
36
|
/**
|
|
37
37
|
* Dotted config path a decline is recorded at. A setup/onboarding
|
|
38
38
|
* preference, not worker policy — kept out of `parallelism`, which is a
|
|
@@ -117,7 +117,7 @@ export function evaluateWorkerDispatchOffer(parallelism, setup, detected) {
|
|
|
117
117
|
*/
|
|
118
118
|
export function workerDispatchOfferQuestion(detected) {
|
|
119
119
|
return ` Detected worker CLIs (${detected.join(', ')}). Enable direct worker dispatch so AIWG `
|
|
120
|
-
+ `can dispatch tasks to them directly, using their own accounts' quota? [y/N]: `;
|
|
120
|
+
+ `can dispatch tasks to them directly, using their own accounts' quota? After enabling, run aiwg doctor --probe-workers --qualify. [y/N]: `;
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
123
|
* One-line non-interactive / regenerate / refresh / doctor hint. Never
|
|
@@ -127,7 +127,7 @@ export function workerDispatchOfferQuestion(detected) {
|
|
|
127
127
|
*/
|
|
128
128
|
export function workerDispatchOfferHint(detected) {
|
|
129
129
|
return `Direct worker dispatch is off; detected worker CLIs: ${detected.join(', ')}. `
|
|
130
|
-
+ `Enable with \`${WORKER_DISPATCH_ENABLE_COMMAND}\` (then
|
|
130
|
+
+ `Enable with \`${WORKER_DISPATCH_ENABLE_COMMAND}\` (then qualify installed CLIs with \`${WORKER_DISPATCH_READINESS_COMMAND}\`).`;
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Accepted values for the `--worker-dispatch` argument (#3062). Only
|
|
@@ -61,18 +61,31 @@ export function diagnoseWorkerHealthReadiness(snapshot, request) {
|
|
|
61
61
|
message: 'No explicit quota/account correlation scope; shared aliases cannot establish independent upstream capacity.',
|
|
62
62
|
nextAction: 'configure-qualified-quota-scope' });
|
|
63
63
|
}
|
|
64
|
+
const sharedDiagnostics = new Map();
|
|
64
65
|
for (const role of roles) {
|
|
65
66
|
const plan = resolveWorkerPlan(snapshot, { ...request, role: role });
|
|
66
67
|
const isRequired = required.has(role);
|
|
67
68
|
const diagnostics = [...plan.diagnostics, ...plan.exclusions.flatMap(item => item.diagnostics)];
|
|
68
69
|
const seen = new Set();
|
|
69
70
|
for (const diagnostic of diagnostics) {
|
|
70
|
-
|
|
71
|
+
if (diagnostic.roles && !diagnostic.roles.includes(role))
|
|
72
|
+
continue;
|
|
73
|
+
const key = JSON.stringify([diagnostic.code, diagnostic.pointer, diagnostic.message, diagnostic.blocking, diagnostic.origins]);
|
|
71
74
|
if (seen.has(key))
|
|
72
75
|
continue;
|
|
73
76
|
seen.add(key);
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
const finding = { ...diagnostic,
|
|
78
|
+
status: diagnostic.blocking === false ? 'info' : 'warn', role,
|
|
79
|
+
nextAction: repairAction(diagnostic.code, diagnostic.pointer) };
|
|
80
|
+
const shared = sharedDiagnostics.get(key);
|
|
81
|
+
if (shared) {
|
|
82
|
+
shared.roles = [...new Set([...(shared.roles ?? [shared.role]), role])];
|
|
83
|
+
shared.role = null;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
sharedDiagnostics.set(key, finding);
|
|
87
|
+
add(finding);
|
|
88
|
+
}
|
|
76
89
|
}
|
|
77
90
|
if (!plan.selected) {
|
|
78
91
|
if (isRequired)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ProviderModelCapabilityRegistry } from './provider-policy.js';
|
|
2
|
+
import type { WorkerSelectionPolicy } from './types.js';
|
|
3
|
+
import type { WorkerResolutionBinding } from './resolver.js';
|
|
4
|
+
import type { WorkerInvocationDriver } from '../serve/worker-invocation.js';
|
|
5
|
+
export function loadRecordedWorkerInstallations(input: {
|
|
6
|
+
registry: ProviderModelCapabilityRegistry;
|
|
7
|
+
cwd: string;
|
|
8
|
+
policy?: WorkerSelectionPolicy;
|
|
9
|
+
parallelism?: import('../config/aiwg-config.js').ParallelismConfig;
|
|
10
|
+
storePath?: string;
|
|
11
|
+
storeDigest?: string;
|
|
12
|
+
}): Promise<{ registry: ProviderModelCapabilityRegistry; installations: Record<string, WorkerResolutionBinding>; drivers: Record<string, WorkerInvocationDriver>; observations: Record<string, import('./model-discovery.js').ProviderModelDiscovery> }>;
|
|
13
|
+
|
|
14
|
+
export function hasProvider(name: string): boolean;
|
|
15
|
+
|
|
16
|
+
export function compileWorkerVersionPattern(pattern: string): RegExp;
|
|
17
|
+
export function normalizeWorkerVersionBanner(banner: string, pattern?: string): string;
|
|
18
|
+
export const workerVersionProbeEnvAllowlist: readonly string[];
|
|
19
|
+
export function workerVersionProbeEnvironment(source?: Record<string, string | undefined>): Record<string, string>;
|
|
20
|
+
export const workerVersionProbeDefaults: { readonly timeoutMs: number; readonly maxBytes: number };
|
|
21
|
+
export function workerVersionRangeMismatch(version: string, ranges: string[]): string | null;
|
|
22
|
+
export function boundedVersionProbe(
|
|
23
|
+
executable: string,
|
|
24
|
+
argv: string[],
|
|
25
|
+
environment: Record<string, string>,
|
|
26
|
+
cwd: string,
|
|
27
|
+
timeoutMs: number,
|
|
28
|
+
maxBytes: number,
|
|
29
|
+
versionPattern: string | undefined,
|
|
30
|
+
): Promise<{ rawBanner: string; version: string }>;
|
|
31
|
+
|
|
32
|
+
export interface WorkerRecoveryCapabilities {
|
|
33
|
+
canVerifyAttempt?: boolean;
|
|
34
|
+
parseWorkerAttemptFinal?(output: string | Uint8Array, options: Record<string, unknown>): Promise<unknown>;
|
|
35
|
+
}
|
|
36
|
+
export function registerWorkerRecoveryCapabilities<T extends object>(driver: T, capabilities: WorkerRecoveryCapabilities): T;
|
|
37
|
+
export function getWorkerRecoveryCapabilities(driver: object | undefined | null): Readonly<WorkerRecoveryCapabilities> | null;
|
|
38
|
+
|
|
39
|
+
export function prepareWorkerInstallationStore(input: { workerWritableRoots: string[]; apply?: boolean }): Promise<{ path: string; action: string }[]>;
|