@aiwg/cli 2026.10.0 → 2026.10.3
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/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3091 -24
- package/bin/aiwg.mjs +86 -21
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +105 -15
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/channel/manager.mjs +59 -10
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/decision.js +297 -2
- package/dist/src/cli/handlers/diagnose.js +9 -2
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/init.js +245 -30
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +69 -6
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +16 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/runtime-info.js +24 -9
- package/dist/src/cli/handlers/script-runner.js +16 -6
- package/dist/src/cli/handlers/session.js +396 -62
- package/dist/src/cli/handlers/sessions.js +146 -40
- package/dist/src/cli/handlers/setup-manifest.js +439 -39
- package/dist/src/cli/handlers/setup.js +139 -39
- package/dist/src/cli/handlers/subcommands.js +3 -1
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/use.js +13 -0
- package/dist/src/cli/handlers/utilities.js +106 -23
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/config/aiwg-config.js +268 -4
- package/dist/src/config/cli.js +146 -19
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/user-config.js +36 -10
- package/dist/src/decision/adapters/jev.js +80 -28
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/driver.js +4 -0
- package/dist/src/decision/entry.js +38 -6
- package/dist/src/decision/heldout/collector.js +10 -1
- package/dist/src/decision/index.js +3 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/ledger.js +839 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +3 -1
- package/dist/src/installation/manager-command.mjs +1 -1
- package/dist/src/installation/manager.mjs +17 -2
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/adapters/codex-runtime.js +360 -56
- package/dist/src/mcp/cli.mjs +587 -77
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.mjs +16 -26
- package/dist/src/mcp/helpers.mjs +106 -2
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +264 -57
- package/dist/src/mcp/registry.mjs +198 -45
- package/dist/src/mcp/server.mjs +403 -20
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +58 -0
- package/dist/src/mcp/tools/orchestration.mjs +38 -0
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +639 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +70 -13
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3091 -24
- package/dist/src/models/model-discovery.js +862 -63
- package/dist/src/models/provider-policy.js +625 -11
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +259 -0
- package/dist/src/models/worker-direct-readiness.js +529 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +131 -0
- package/dist/src/models/worker-installation.mjs +879 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.mjs +387 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/grok-build-headless.js +43 -21
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +9 -0
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +538 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +45 -0
- package/dist/src/sessions/adapters/generic.js +37 -3
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +423 -24
- package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
- package/dist/src/sessions/adapters/muse.js +68 -29
- package/dist/src/sessions/adapters/pi.js +1 -0
- package/dist/src/sessions/batch-contracts.js +13 -1
- package/dist/src/sessions/batch-import.js +23 -42
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +35 -0
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/importer.js +24 -8
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/repository.js +54 -13
- package/dist/src/sessions/workspace-discovery.js +99 -37
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +18 -1
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
- package/tools/_resolve-impl.mjs +39 -0
- package/tools/agents/deploy-agents.mjs +15 -2
- package/tools/agents/providers/base.mjs +63 -43
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +6 -3
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/providers/deepseek-harness-transport.mjs +358 -0
- package/tools/providers/omp-transport.mjs +192 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct-worker onboarding/readiness (issue #2894, S1 Tier 0 direct handoff).
|
|
3
|
+
*
|
|
4
|
+
* For each configured worker this reports two separate facts that must never
|
|
5
|
+
* be merged:
|
|
6
|
+
* - "installation readiness": is the CLI installed, and is the installed
|
|
7
|
+
* version in the registered range;
|
|
8
|
+
* - "dispatch eligibility": what the resolver itself decides for this cli
|
|
9
|
+
* (worker_dispatch, per-cli enable, adapter binding, group/member
|
|
10
|
+
* qualification). When the caller supplies a `resolverSnapshot`, this calls
|
|
11
|
+
* the actual offline dispatch resolver (`resolveWorkerPlan` in resolver.ts)
|
|
12
|
+
* for every group member referencing the cli and reports its real
|
|
13
|
+
* member-specific refusals (missing installation/model evidence, policy
|
|
14
|
+
* denials, etc.) — never a re-derived approximation. Without a
|
|
15
|
+
* `resolverSnapshot` (for callers that have not resolved the wider model
|
|
16
|
+
* config/catalog), this falls back to a binding-only check through
|
|
17
|
+
* `resolveWorkerGroupInstallation` and says plainly that the result is
|
|
18
|
+
* binding-level only, not a dispatch verdict.
|
|
19
|
+
*
|
|
20
|
+
* Probing is opt-in. `diagnoseDirectWorkerReadiness` starts no process unless
|
|
21
|
+
* the caller supplies a `probe`; surfaces such as `aiwg doctor` report
|
|
22
|
+
* configuration from config and the registry by default and only probe behind
|
|
23
|
+
* an explicit flag. Immediately before each probe, the resolved executable
|
|
24
|
+
* and its version argv are announced through the caller's `announce`
|
|
25
|
+
* callback (synchronous, never buffered) so operators see exactly what is
|
|
26
|
+
* about to run before anything is spawned. The single probe implementation
|
|
27
|
+
* lives in the shared installation owner (`boundedVersionProbe` in
|
|
28
|
+
* worker-installation.mjs) with its credential-free environment allowlist and
|
|
29
|
+
* bounds; this module only maps its outcome onto readiness findings.
|
|
30
|
+
*
|
|
31
|
+
* Trust boundary: a version probe runs the executable named by project
|
|
32
|
+
* configuration (or the registry default) with only its registered
|
|
33
|
+
* `versionArgv`, a filtered credential-free environment, and an explicit
|
|
34
|
+
* cwd. That configuration is an execution trust boundary, not an executable
|
|
35
|
+
* allowlist — project configuration can select an arbitrary binary, and a
|
|
36
|
+
* `versionArgv` label cannot prove the selected program is side-effect free.
|
|
37
|
+
* Opting into `--probe-workers` trusts that executable (and, for bare names,
|
|
38
|
+
* PATH resolution) to the same extent any other locally invoked tool is
|
|
39
|
+
* trusted.
|
|
40
|
+
*
|
|
41
|
+
* The configured `adapter` binding is always resolved through the same
|
|
42
|
+
* public resolver used by installation and dispatch (`resolveWorkerGroupInstallation`
|
|
43
|
+
* / `resolveWorkerAdapter`): exact, case-sensitive namespace:id references,
|
|
44
|
+
* with implicit canonical `providers:<cli>` bindings for cli ids that are
|
|
45
|
+
* only referenced by a group member and have no explicit `parallelism.clis`
|
|
46
|
+
* entry. No alias normalization happens here — a binding this module accepts
|
|
47
|
+
* is a binding the public config validator also accepts, and vice versa.
|
|
48
|
+
*
|
|
49
|
+
* The shipped direct profiles are declarations, not live qualification: no
|
|
50
|
+
* finding ever calls a profile "qualified". Profiles stay unqualified until
|
|
51
|
+
* an authenticated smoke run (tracker #3005), and an installed, in-range CLI
|
|
52
|
+
* can still be refused by the resolver — the dispatch finding says so
|
|
53
|
+
* plainly with the resolver's own reason.
|
|
54
|
+
*
|
|
55
|
+
* Decision 17 (baseline simplification) dropped provider qualification
|
|
56
|
+
* runners, attestations and prescribed evidence files; this module therefore
|
|
57
|
+
* performs no live-qualification recording and requires no evidence files.
|
|
58
|
+
*/
|
|
59
|
+
import { tmpdir } from 'node:os';
|
|
60
|
+
import { isAbsolute, resolve as resolvePath } from 'node:path';
|
|
61
|
+
import { boundedVersionProbe, normalizeWorkerVersionBanner, workerVersionProbeDefaults, workerVersionProbeEnvironment, workerVersionRangeMismatch, } from './worker-installation.mjs';
|
|
62
|
+
import { resolveWorkerGroupInstallation } from '../config/aiwg-config.js';
|
|
63
|
+
import { resolveWorkerPlan } from './resolver.js';
|
|
64
|
+
const SMOKE_NOTE = 'Profiles are unqualified until an authenticated smoke run (see #3005).';
|
|
65
|
+
function truncateBanner(banner, limit = 200) {
|
|
66
|
+
const singleLine = banner.replace(/[\r\n]+/g, '\\n');
|
|
67
|
+
return singleLine.length > limit ? `${singleLine.slice(0, limit)}…` : singleLine;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve a configured executable against the project root before any probe
|
|
71
|
+
* changes its working directory. Bare command names (no path separator) are
|
|
72
|
+
* left untouched so PATH resolution still applies; already-absolute paths
|
|
73
|
+
* are left untouched; relative paths containing a separator are resolved
|
|
74
|
+
* against `projectRoot` so a project-relative executable is never looked up
|
|
75
|
+
* relative to the probe's tmpdir cwd.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveDirectWorkerExecutablePath(executable, projectRoot) {
|
|
78
|
+
if (isAbsolute(executable))
|
|
79
|
+
return executable;
|
|
80
|
+
if (!executable.includes('/') && !executable.includes('\\'))
|
|
81
|
+
return executable;
|
|
82
|
+
return resolvePath(projectRoot, executable);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Default live prober: the shared bounded probe with a credential-free
|
|
86
|
+
* environment and explicit cwd. ENOENT means not installed; every other
|
|
87
|
+
* spawn, timeout or output failure is a failed probe. Never launches a
|
|
88
|
+
* worker. Callers opt in by passing this (or their own prober); without a
|
|
89
|
+
* prober no process is started.
|
|
90
|
+
*/
|
|
91
|
+
export async function probeDirectWorkerVersion(executable, versionArgv, options = {}) {
|
|
92
|
+
const timeoutMs = options.timeoutMs ?? workerVersionProbeDefaults.timeoutMs;
|
|
93
|
+
const maxBytes = options.maxBytes ?? workerVersionProbeDefaults.maxBytes;
|
|
94
|
+
const cwd = options.cwd ?? tmpdir();
|
|
95
|
+
try {
|
|
96
|
+
const { rawBanner } = await boundedVersionProbe(executable, versionArgv, workerVersionProbeEnvironment(), cwd, timeoutMs, maxBytes, undefined);
|
|
97
|
+
return { ok: true, rawBanner };
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return { ok: false, reason: error?.code === 'ENOENT' ? 'not-installed' : 'probe-failed' };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const ADAPTER_REFERENCE = /^(providers|workerAdapters):(.+)$/;
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the configured binding by exact, case-sensitive namespace:id
|
|
106
|
+
* lookup — the same shape `resolveWorkerAdapter` (provider-policy.ts) uses
|
|
107
|
+
* for dispatch and `validateParallelismConfig` uses for config validation.
|
|
108
|
+
* No alias normalization happens for either namespace, so a binding this
|
|
109
|
+
* accepts is also a binding the public config validator accepts, and a
|
|
110
|
+
* custom `workerAdapters:<id>` reference resolves against
|
|
111
|
+
* `registry.workerAdapters`, never against the provider-alias registry.
|
|
112
|
+
*
|
|
113
|
+
* For the `providers` namespace this falls back from `workerAdapter` to
|
|
114
|
+
* `workerHealthAdapter` when no direct-dispatch binding is pinned yet — the
|
|
115
|
+
* install/version metadata is identical either way, and that is exactly
|
|
116
|
+
* what lets install readiness and dispatch eligibility disagree honestly
|
|
117
|
+
* (installed and in range, but the resolver still has no workerAdapter to
|
|
118
|
+
* dispatch through).
|
|
119
|
+
*/
|
|
120
|
+
function resolveDirectBinding(registry, configuredAdapter, cli) {
|
|
121
|
+
let namespace;
|
|
122
|
+
let id;
|
|
123
|
+
if (configuredAdapter !== undefined) {
|
|
124
|
+
const match = ADAPTER_REFERENCE.exec(configuredAdapter.trim());
|
|
125
|
+
if (!match)
|
|
126
|
+
return { error: `Invalid worker adapter reference '${configuredAdapter}'` };
|
|
127
|
+
[, namespace, id] = match;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
namespace = 'providers';
|
|
131
|
+
id = cli;
|
|
132
|
+
}
|
|
133
|
+
if (namespace === 'providers') {
|
|
134
|
+
const entry = registry.providers[id];
|
|
135
|
+
const adapter = entry?.workerAdapter ?? entry?.workerHealthAdapter;
|
|
136
|
+
if (!adapter)
|
|
137
|
+
return { error: `No direct worker profile is registered for binding 'providers:${id}'` };
|
|
138
|
+
return { adapter, adapterRef: `providers:${id}` };
|
|
139
|
+
}
|
|
140
|
+
const adapter = registry.workerAdapters?.[id];
|
|
141
|
+
if (!adapter)
|
|
142
|
+
return { error: `Unknown worker adapter reference 'workerAdapters:${id}'` };
|
|
143
|
+
return { adapter, adapterRef: `workerAdapters:${id}` };
|
|
144
|
+
}
|
|
145
|
+
/** Every group member referencing this cli, including disabled ones (for context). */
|
|
146
|
+
function memberReferences(parallelism, cli) {
|
|
147
|
+
const refs = [];
|
|
148
|
+
for (const [groupId, group] of Object.entries(parallelism.groups ?? {})) {
|
|
149
|
+
for (const member of group?.members ?? []) {
|
|
150
|
+
if (member.cli !== cli)
|
|
151
|
+
continue;
|
|
152
|
+
refs.push({ groupId, memberId: member.id, member,
|
|
153
|
+
enabled: member.enabled !== false && parallelism.clis?.[cli]?.enabled !== false });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return refs.sort((a, b) => `${a.groupId}/${a.memberId}`.localeCompare(`${b.groupId}/${b.memberId}`));
|
|
157
|
+
}
|
|
158
|
+
function memberLabels(refs) {
|
|
159
|
+
return refs.map(ref => `${ref.groupId}/${ref.memberId}${ref.enabled ? '' : ' (disabled)'}`);
|
|
160
|
+
}
|
|
161
|
+
function describeConfigContext(members) {
|
|
162
|
+
return members.length ? `members: ${members.join(', ')}` : 'members: no group member references this cli';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Union of every cli id a readiness pass must consider: explicit
|
|
166
|
+
* `parallelism.clis` entries plus any cli id referenced only implicitly by a
|
|
167
|
+
* group member (canonical default binding, no explicit installation entry).
|
|
168
|
+
*/
|
|
169
|
+
function allConfiguredCliIds(parallelism) {
|
|
170
|
+
const ids = new Set(Object.keys(parallelism.clis ?? {}));
|
|
171
|
+
for (const group of Object.values(parallelism.groups ?? {})) {
|
|
172
|
+
for (const member of group?.members ?? [])
|
|
173
|
+
ids.add(member.cli);
|
|
174
|
+
}
|
|
175
|
+
return [...ids].sort();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A cli is "required" only when an enabled group member referencing it also
|
|
179
|
+
* covers a role its group lists in `requiredRoles`. Every other configured
|
|
180
|
+
* cli is optional: installation failures stay advisory (never 'error', so
|
|
181
|
+
* they never flip doctor's exit code) until something actually requires it.
|
|
182
|
+
*/
|
|
183
|
+
function isCliRequired(parallelism, cli) {
|
|
184
|
+
for (const group of Object.values(parallelism.groups ?? {})) {
|
|
185
|
+
const required = new Set(group?.requiredRoles ?? []);
|
|
186
|
+
if (!required.size)
|
|
187
|
+
continue;
|
|
188
|
+
for (const member of group?.members ?? []) {
|
|
189
|
+
if (member.cli !== cli || member.enabled === false)
|
|
190
|
+
continue;
|
|
191
|
+
if (parallelism.clis?.[cli]?.enabled === false)
|
|
192
|
+
continue;
|
|
193
|
+
if (member.roles.some(role => required.has(role)))
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
/** Cap a status at 'warn' for optional (non-required) clis so an unreferenced
|
|
200
|
+
* or not-yet-wired profile never flips doctor's exit code. */
|
|
201
|
+
function advisoryStatus(status, required) {
|
|
202
|
+
return required || status !== 'error' ? status : 'warn';
|
|
203
|
+
}
|
|
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
|
+
/**
|
|
212
|
+
* Dispatch eligibility from the actual offline dispatch resolver
|
|
213
|
+
* (`resolveWorkerPlan`): called once per role for every group member
|
|
214
|
+
* referencing this cli, never re-derived. A resolvable binding alone is not
|
|
215
|
+
* reported as eligible — eligibility requires the resolver to actually place
|
|
216
|
+
* at least one referencing member in its eligible/alternatives set. Refusals
|
|
217
|
+
* are member-specific and name the resolver's own missing-evidence or policy
|
|
218
|
+
* diagnostics.
|
|
219
|
+
*/
|
|
220
|
+
function resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli, refs) {
|
|
221
|
+
const snapshot = {
|
|
222
|
+
config: resolverSnapshot.config, models: resolverSnapshot.models, modelDigests: resolverSnapshot.modelDigests,
|
|
223
|
+
catalog: resolverSnapshot.catalog, registry, installations: {},
|
|
224
|
+
};
|
|
225
|
+
const refusals = [];
|
|
226
|
+
for (const ref of refs) {
|
|
227
|
+
if (!ref.enabled)
|
|
228
|
+
continue;
|
|
229
|
+
const matches = (tuple) => tuple.group === ref.groupId && tuple.member === ref.memberId;
|
|
230
|
+
const seen = [];
|
|
231
|
+
let eligibleHere = false;
|
|
232
|
+
for (const role of ref.member.roles) {
|
|
233
|
+
const plan = resolveWorkerPlan(snapshot, {
|
|
234
|
+
role: role, group: ref.groupId, member: ref.memberId,
|
|
235
|
+
now: resolverSnapshot.now, context: offlineDirectContext(),
|
|
236
|
+
});
|
|
237
|
+
if ([plan.selected, ...plan.alternatives].some(tuple => tuple && matches(tuple))) {
|
|
238
|
+
eligibleHere = true;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
const exclusion = plan.exclusions.find(matches);
|
|
242
|
+
seen.push(...(exclusion?.diagnostics ?? plan.diagnostics));
|
|
243
|
+
}
|
|
244
|
+
if (eligibleHere) {
|
|
245
|
+
return { eligibility: 'eligible',
|
|
246
|
+
detail: `Dispatch resolver accepts cli '${cli}' for group '${ref.groupId}' member '${ref.memberId}'. `
|
|
247
|
+
+ `Dispatch eligibility: eligible. ${SMOKE_NOTE}` };
|
|
248
|
+
}
|
|
249
|
+
const reasons = formatDiagnostics(seen);
|
|
250
|
+
refusals.push(`group '${ref.groupId}' member '${ref.memberId}'${reasons ? `: ${reasons}` : ' (no qualified route)'}`);
|
|
251
|
+
}
|
|
252
|
+
if (!refusals.length) {
|
|
253
|
+
return { eligibility: 'blocked',
|
|
254
|
+
detail: `Dispatch resolver has no enabled group member to evaluate for cli '${cli}'. `
|
|
255
|
+
+ `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
|
|
256
|
+
}
|
|
257
|
+
return { eligibility: 'blocked',
|
|
258
|
+
detail: `Dispatch resolver refuses cli '${cli}': ${refusals.join('; ')}. `
|
|
259
|
+
+ `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Binding-only fallback used only when the caller has not supplied a
|
|
263
|
+
* `resolverSnapshot` (for example a direct unit-level call that never
|
|
264
|
+
* resolved the wider model config/catalog). States plainly that this is a
|
|
265
|
+
* binding-level result, not the dispatch resolver's verdict.
|
|
266
|
+
*/
|
|
267
|
+
function resolveEligibilityFromBindingOnly(parallelism, registry, cli, installReady) {
|
|
268
|
+
try {
|
|
269
|
+
const resolved = resolveWorkerGroupInstallation(parallelism, cli, registry);
|
|
270
|
+
return { eligibility: 'eligible',
|
|
271
|
+
detail: `Resolver accepts cli '${cli}' (binding '${resolved.installation.adapter}'). Dispatch eligibility: eligible at binding level only — `
|
|
272
|
+
+ `no resolver snapshot was supplied, so group/member qualification was not evaluated. `
|
|
273
|
+
+ `Launch additionally requires current installation evidence and model observation under the dispatch resolver's gates. `
|
|
274
|
+
+ `${SMOKE_NOTE}` };
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
278
|
+
const readyNote = installReady
|
|
279
|
+
? ' The CLI is installed with an in-range version, but dispatch stays refused for the reason above; '
|
|
280
|
+
+ 'an authenticated smoke run (see #3005) alone does not satisfy the resolver gates.'
|
|
281
|
+
: ` ${SMOKE_NOTE}`;
|
|
282
|
+
return { eligibility: 'blocked',
|
|
283
|
+
detail: `Resolver refuses cli '${cli}': ${reason}. Dispatch eligibility: blocked.${readyNote}` };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function checkDispatchEligibility(parallelism, registry, cli, installReady, resolverSnapshot, refs) {
|
|
287
|
+
if (resolverSnapshot && refs.length)
|
|
288
|
+
return resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli, refs);
|
|
289
|
+
return resolveEligibilityFromBindingOnly(parallelism, registry, cli, installReady);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Read-only readiness for every configured worker. Starts no process unless
|
|
293
|
+
* `probe` is supplied; without a prober each cli gets a config-only install
|
|
294
|
+
* finding plus its resolver eligibility. Never claims a profile is
|
|
295
|
+
* "qualified". Considers every explicit `parallelism.clis` entry plus any
|
|
296
|
+
* cli id a group member references implicitly (no explicit installation
|
|
297
|
+
* entry, canonical default binding).
|
|
298
|
+
*/
|
|
299
|
+
export async function diagnoseDirectWorkerReadiness(input) {
|
|
300
|
+
const { parallelism, registry, probe, announce, timeoutMs = workerVersionProbeDefaults.timeoutMs, probeCwd = tmpdir(), configFile = '.aiwg/aiwg.config', projectRoot = process.cwd(), resolverSnapshot } = input;
|
|
301
|
+
const report = {
|
|
302
|
+
enabled: parallelism.worker_dispatch === true,
|
|
303
|
+
workersConfigured: allConfiguredCliIds(parallelism).length > 0,
|
|
304
|
+
ready: true,
|
|
305
|
+
findings: [],
|
|
306
|
+
};
|
|
307
|
+
if (!report.enabled) {
|
|
308
|
+
report.findings.push({ cli: '*', kind: 'dispatch', profile: null, executable: null, installedVersion: null,
|
|
309
|
+
status: 'info', code: 'WORKER_DIRECT_DISABLED',
|
|
310
|
+
message: `Group dispatch is disabled (config ${configFile} key /parallelism/worker_dispatch); install/version checks do not apply and onboarding cannot proceed.`,
|
|
311
|
+
nextAction: 'enable-worker-dispatch-to-onboard',
|
|
312
|
+
configFile, configKey: '/parallelism/worker_dispatch',
|
|
313
|
+
readiness: null, eligibility: 'blocked',
|
|
314
|
+
eligibilityDetail: 'Dispatch eligibility: blocked — group dispatch is disabled.',
|
|
315
|
+
members: [] });
|
|
316
|
+
return report;
|
|
317
|
+
}
|
|
318
|
+
const cliIds = allConfiguredCliIds(parallelism);
|
|
319
|
+
if (!cliIds.length) {
|
|
320
|
+
// No workers configured: no findings at all so doctor stays silent.
|
|
321
|
+
return report;
|
|
322
|
+
}
|
|
323
|
+
const pushInstall = (finding) => {
|
|
324
|
+
report.findings.push({ ...finding, kind: 'install', eligibility: null, eligibilityDetail: null });
|
|
325
|
+
if (finding.status === 'error' || finding.status === 'warn')
|
|
326
|
+
report.ready = false;
|
|
327
|
+
};
|
|
328
|
+
const pushDispatch = (cli, profile, status, code, eligibility, detail, configKey, members, nextAction) => {
|
|
329
|
+
report.findings.push({ cli, kind: 'dispatch', profile, executable: null, installedVersion: null,
|
|
330
|
+
status, code, message: `${detail} (config ${configFile} key ${configKey}; ${describeConfigContext(members)})`,
|
|
331
|
+
nextAction, configFile, configKey,
|
|
332
|
+
readiness: null, eligibility, eligibilityDetail: detail, members });
|
|
333
|
+
};
|
|
334
|
+
await Promise.all(cliIds.map(async (cli) => {
|
|
335
|
+
const configured = parallelism.clis?.[cli];
|
|
336
|
+
const refs = memberReferences(parallelism, cli);
|
|
337
|
+
const members = memberLabels(refs);
|
|
338
|
+
const required = isCliRequired(parallelism, cli);
|
|
339
|
+
const configKey = `/parallelism/clis/${cli}`;
|
|
340
|
+
if (configured?.enabled === false) {
|
|
341
|
+
pushInstall({ cli, profile: configured.invocationProfile ?? null,
|
|
342
|
+
executable: configured.executable ?? null, installedVersion: null,
|
|
343
|
+
status: 'info', code: 'WORKER_DIRECT_CLI_DISABLED',
|
|
344
|
+
message: `Dispatch is disabled for cli '${cli}' (config ${configFile} key ${configKey}/enabled); the version probe was skipped. Installation readiness: skipped. (${describeConfigContext(members)})`,
|
|
345
|
+
nextAction: 'enable-cli-to-onboard',
|
|
346
|
+
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');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const resolved = resolveDirectBinding(registry, configured?.adapter, cli);
|
|
352
|
+
if ('error' in resolved) {
|
|
353
|
+
pushInstall({ cli, profile: configured?.invocationProfile ?? null, executable: null, installedVersion: null,
|
|
354
|
+
status: advisoryStatus(probe ? 'error' : 'warn', required), code: 'WORKER_DIRECT_NO_PROFILE',
|
|
355
|
+
message: `${resolved.error} (config ${configFile} key ${configKey}/adapter); the profile cannot be onboarded. Installation readiness: not-ready. (${describeConfigContext(members)})`,
|
|
356
|
+
nextAction: 'review-worker-configuration',
|
|
357
|
+
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');
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const { adapter, adapterRef } = resolved;
|
|
363
|
+
if (adapter.invocation.kind !== 'process') {
|
|
364
|
+
pushInstall({ cli, profile: null, executable: null, installedVersion: null,
|
|
365
|
+
status: 'info', code: 'WORKER_DIRECT_HTTP',
|
|
366
|
+
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
|
+
nextAction: 'check-endpoint-health',
|
|
368
|
+
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');
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const profileIds = Object.keys(adapter.invocationProfiles ?? {});
|
|
374
|
+
const profileRef = configured?.invocationProfile
|
|
375
|
+
?? (profileIds.length === 1 ? profileIds[0] : undefined);
|
|
376
|
+
if (!profileRef || !adapter.invocationProfiles?.[profileRef]) {
|
|
377
|
+
pushInstall({ cli, profile: configured?.invocationProfile ?? null,
|
|
378
|
+
executable: null, installedVersion: null,
|
|
379
|
+
status: advisoryStatus('error', required), code: 'WORKER_DIRECT_PROFILE_UNKNOWN',
|
|
380
|
+
message: configured?.invocationProfile
|
|
381
|
+
? `Configured invocation profile '${configured.invocationProfile}' is not registered for binding '${adapterRef}' (config ${configFile} key ${configKey}/invocationProfile). Installation readiness: not-ready. (${describeConfigContext(members)})`
|
|
382
|
+
: `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
|
+
nextAction: 'review-worker-configuration',
|
|
384
|
+
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');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const profile = adapter.invocationProfiles[profileRef];
|
|
390
|
+
const executable = resolveDirectWorkerExecutablePath(configured?.executable ?? adapter.invocation.executable, projectRoot);
|
|
391
|
+
if (!probe) {
|
|
392
|
+
pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
|
|
393
|
+
status: 'info', code: 'WORKER_DIRECT_UNPROBED',
|
|
394
|
+
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
|
+
nextAction: 'rerun-with-probe-workers',
|
|
396
|
+
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');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
announce?.(cli, executable, adapter.invocation.versionArgv);
|
|
402
|
+
let outcome;
|
|
403
|
+
try {
|
|
404
|
+
outcome = await probe(executable, adapter.invocation.versionArgv, { timeoutMs, cwd: probeCwd });
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
outcome = { ok: false, reason: 'probe-failed' };
|
|
408
|
+
}
|
|
409
|
+
if (!outcome.ok) {
|
|
410
|
+
const notInstalled = outcome.reason === 'not-installed';
|
|
411
|
+
pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
|
|
412
|
+
status: advisoryStatus(notInstalled ? 'error' : 'warn', required),
|
|
413
|
+
code: notInstalled ? 'WORKER_DIRECT_NOT_INSTALLED' : 'WORKER_DIRECT_PROBE_FAILED',
|
|
414
|
+
message: notInstalled
|
|
415
|
+
? `CLI '${executable}' is not installed (version probe failed to start; config ${configFile} key ${configKey}/executable); install it before onboarding cli '${cli}'. Installation readiness: not-ready. (${describeConfigContext(members)})`
|
|
416
|
+
: `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
|
+
nextAction: notInstalled ? 'install-cli' : 'retry-version-probe',
|
|
418
|
+
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');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const rawBanner = outcome.rawBanner.trim();
|
|
424
|
+
let installedVersion;
|
|
425
|
+
try {
|
|
426
|
+
installedVersion = normalizeWorkerVersionBanner(rawBanner, adapter.invocation.versionPattern);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
|
|
430
|
+
status: advisoryStatus('error', required), code: 'WORKER_DIRECT_BANNER_UNMATCHED',
|
|
431
|
+
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
|
+
nextAction: 'reinstall-supported-cli',
|
|
433
|
+
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');
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const mismatch = workerVersionRangeMismatch(installedVersion, [profile.supportedVersions, adapter.supportedVersions]);
|
|
439
|
+
if (mismatch) {
|
|
440
|
+
pushInstall({ cli, profile: profileRef, executable, installedVersion,
|
|
441
|
+
status: advisoryStatus('warn', required), code: 'WORKER_DIRECT_VERSION_OUT_OF_RANGE',
|
|
442
|
+
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
|
+
nextAction: 'upgrade-or-pin-cli',
|
|
444
|
+
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');
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
pushInstall({ cli, profile: profileRef, executable, installedVersion,
|
|
450
|
+
status: 'ok', code: 'WORKER_DIRECT_READY',
|
|
451
|
+
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: 'run-authenticated-smoke-run',
|
|
453
|
+
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, 'run-authenticated-smoke-run');
|
|
456
|
+
}));
|
|
457
|
+
report.findings.sort((a, b) => a.cli.localeCompare(b.cli) || a.kind.localeCompare(b.kind));
|
|
458
|
+
return report;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Doctor wiring for the direct-worker section. With `probeWorkers` false this
|
|
462
|
+
* is config-only and starts no process; with true it runs the shared bounded
|
|
463
|
+
* probes, announcing each resolved executable through `announce` (if
|
|
464
|
+
* supplied) immediately before it is spawned — never a worker launch, model
|
|
465
|
+
* call or mutation, and never buffered behind the rest of the report. No
|
|
466
|
+
* workers configured (explicitly or implicitly through a group member) means
|
|
467
|
+
* no lines at all, so doctor output and exit code are unchanged there.
|
|
468
|
+
*
|
|
469
|
+
* `installFindings` carries the structured per-cli installation findings
|
|
470
|
+
* (`kind === 'install'`, one per configured cli, untruncated) backing
|
|
471
|
+
* `lines`, so machine-readable surfaces (`aiwg doctor --probe-workers
|
|
472
|
+
* --json`) can render one record per CLI without re-running the probes.
|
|
473
|
+
*
|
|
474
|
+
* Trust boundary: with `probeWorkers: true`, every probed executable is
|
|
475
|
+
* exactly the one named by project configuration (or the registry default)
|
|
476
|
+
* run with only its registered version arguments — opting in trusts that
|
|
477
|
+
* executable; a version probe cannot prove the program has no side effects.
|
|
478
|
+
*/
|
|
479
|
+
export async function buildDirectWorkerDoctorSection(input) {
|
|
480
|
+
const { parallelism, registry, probeWorkers } = input;
|
|
481
|
+
const cliIds = allConfiguredCliIds(parallelism ?? {});
|
|
482
|
+
if (!cliIds.length)
|
|
483
|
+
return { lines: [], probed: false, workersConfigured: false, installFindings: [] };
|
|
484
|
+
const lines = [];
|
|
485
|
+
if (probeWorkers) {
|
|
486
|
+
lines.push({ name: 'Direct Worker Probes', status: 'info',
|
|
487
|
+
message: 'probe execution is opt-in (--probe-workers): running bounded `<executable> ...versionArgv` probes with a cleared environment and explicit cwd (no worker launch, no model call, no mutation). Trust boundary: each probe runs exactly the executable named by project configuration with only its version arguments — a version probe cannot prove that executable is side-effect free.' });
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
lines.push({ name: 'Direct Worker', status: 'info',
|
|
491
|
+
message: 'direct-worker configuration only: no subprocess was started. Rerun with --probe-workers to run the bounded version probes' });
|
|
492
|
+
}
|
|
493
|
+
const report = await diagnoseDirectWorkerReadiness({
|
|
494
|
+
parallelism: parallelism,
|
|
495
|
+
registry,
|
|
496
|
+
...(probeWorkers ? { probe: input.probe ?? probeDirectWorkerVersion, ...(input.announce ? { announce: input.announce } : {}) } : {}),
|
|
497
|
+
...(input.configFile ? { configFile: input.configFile } : {}),
|
|
498
|
+
...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
|
|
499
|
+
...(input.resolverSnapshot ? { resolverSnapshot: input.resolverSnapshot } : {}),
|
|
500
|
+
});
|
|
501
|
+
for (const finding of report.findings.slice(0, 15)) {
|
|
502
|
+
const name = finding.cli === '*' ? 'Direct Worker'
|
|
503
|
+
: finding.kind === 'dispatch' ? `Direct Worker Dispatch (${finding.cli})` : `Direct Worker (${finding.cli})`;
|
|
504
|
+
lines.push({ name, status: finding.status, message: `${finding.code}: ${finding.message} [next: ${finding.nextAction}]` });
|
|
505
|
+
}
|
|
506
|
+
if (report.findings.length > 15) {
|
|
507
|
+
lines.push({ name: 'Direct Worker', status: 'warn',
|
|
508
|
+
message: `${report.findings.length - 15} additional findings; narrow the cli set in parallelism.clis` });
|
|
509
|
+
}
|
|
510
|
+
if (probeWorkers && !report.findings.some(finding => finding.code === 'WORKER_DIRECT_READY')) {
|
|
511
|
+
lines.push({ name: 'Direct Worker', status: 'info',
|
|
512
|
+
message: 'no profile is onboarded; profiles are unqualified until an authenticated smoke run (see #3005)' });
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
lines,
|
|
516
|
+
probed: probeWorkers,
|
|
517
|
+
workersConfigured: true,
|
|
518
|
+
// Disabled dispatch skips probes but still needs one record per configured
|
|
519
|
+
// CLI in machine-readable output. Keep the existing text findings intact.
|
|
520
|
+
installFindings: report.enabled
|
|
521
|
+
? report.findings.filter(finding => finding.kind === 'install')
|
|
522
|
+
: cliIds.map(cli => ({
|
|
523
|
+
...report.findings[0], cli, kind: 'install',
|
|
524
|
+
configKey: `/parallelism/clis/${cli}`, readiness: 'skipped',
|
|
525
|
+
eligibility: null, eligibilityDetail: null,
|
|
526
|
+
})),
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
//# sourceMappingURL=worker-direct-readiness.js.map
|