@aiwg/cli 2026.10.9 → 2026.10.10
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/model-capabilities.v1.json +197 -3
- package/bin/aiwg.mjs +8 -0
- package/dist/src/cli/handlers/mc-admission.js +39 -0
- package/dist/src/cli/handlers/mc.js +2 -31
- package/dist/src/cli/handlers/models-help.js +52 -0
- package/dist/src/cli/handlers/models.js +11 -81
- package/dist/src/cli/handlers/use-addon-profile.js +72 -0
- package/dist/src/cli/handlers/use.js +191 -230
- package/dist/src/cli/ui.js +1 -0
- package/dist/src/config/aiwg-config.js +38 -184
- package/dist/src/config/cli-help.js +54 -0
- package/dist/src/config/cli.js +5 -69
- package/dist/src/config/config-normalization.js +10 -0
- package/dist/src/config/worker-activation-gate.js +26 -0
- package/dist/src/config/worker-adoption-cli.js +102 -0
- package/dist/src/config/worker-adoption.js +368 -0
- package/dist/src/config/worker-config-digest.js +14 -0
- package/dist/src/config/worker-group-templates.js +150 -0
- package/dist/src/config/worker-guidance-warning.js +28 -0
- package/dist/src/config/worker-owner-lock.js +152 -0
- package/dist/src/config/worker-parallelism.js +154 -0
- package/dist/src/config/worker-write.js +75 -0
- package/dist/src/decision/telemetry/exporter.js +2 -1
- package/dist/src/decision/telemetry/index.js +1 -0
- package/dist/src/decision/telemetry/mapping.js +43 -0
- package/dist/src/decision/telemetry/otlp-http.js +5 -2
- package/dist/src/decision/telemetry/redaction.js +6 -1
- package/dist/src/decision/telemetry/types.js +3 -0
- package/dist/src/decision/telemetry/worker.js +69 -0
- package/dist/src/lint/rule-activation.js +15 -7
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/server.mjs +2 -11
- package/dist/src/mcp/stdio-lifecycle.mjs +27 -0
- package/dist/src/mcp/tools/worker-failure-diagnostics.mjs +9 -0
- package/dist/src/mcp/tools/worker-invoke.mjs +6 -6
- package/dist/src/mission-protocol/codecs.js +8 -38
- package/dist/src/mission-protocol/worker-contract-diagnostics.js +62 -0
- package/dist/src/mission-protocol/worker-invocation-types.js +2 -0
- package/dist/src/models/conductor-data-policy.d.mts +8 -0
- package/dist/src/models/conductor-data-policy.mjs +190 -0
- package/dist/src/models/config-loader.js +19 -3
- package/dist/src/models/config-writer.js +27 -0
- package/dist/src/models/model-capabilities.v1.json +197 -3
- package/dist/src/models/provider-policy.js +7 -155
- package/dist/src/models/resolver.js +2 -293
- package/dist/src/models/worker-artifact-binding.mjs +12 -0
- package/dist/src/models/worker-direct-readiness-policy.js +155 -0
- package/dist/src/models/worker-direct-readiness.js +9 -218
- package/dist/src/models/worker-health-readiness.js +105 -26
- package/dist/src/models/worker-installation.mjs +12 -14
- package/dist/src/models/worker-member-references.js +64 -0
- package/dist/src/models/worker-plan.js +317 -0
- package/dist/src/models/worker-selection-policy.js +217 -0
- package/dist/src/models/wrapper-deployment.js +1 -1
- package/dist/src/serve/shared-host-admission-checks.js +116 -0
- package/dist/src/serve/shared-host-scheduler.js +25 -107
- package/dist/src/serve/shared-host-starvation.js +73 -0
- package/dist/src/serve/worker-artifacts.d.mts +4 -0
- package/dist/src/serve/worker-artifacts.mjs +56 -0
- package/dist/src/serve/worker-command-request.js +68 -0
- package/dist/src/serve/worker-invocation-paths.js +144 -0
- package/dist/src/serve/worker-invocation-preparation.js +169 -0
- package/dist/src/serve/worker-invocation.js +24 -291
- package/dist/src/serve/worker-stdin-frame.d.mts +2 -2
- package/dist/src/serve/worker-stdin-frame.mjs +6 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +19 -7
- package/package.json +9 -1
- package/schemas/mission-v1.schema.json +20 -19
- package/schemas/models/model-config-compatibility.v1.schema.json +16 -0
- package/tools/agents/providers/claude-post-deploy.mjs +35 -0
- package/tools/agents/providers/claude.mjs +3 -30
- package/tools/agents/providers/codex-post-deploy.mjs +13 -0
- package/tools/agents/providers/codex.mjs +2 -8
- package/tools/agents/providers/conductor-data-guard.mjs +61 -0
- package/tools/agents/providers/rule-delivery.mjs +3 -16
- package/tools/agents/providers/rule-presentation.mjs +38 -0
- package/tools/plugin/package-plugins.mjs +1 -1
- package/tools/ralph-external/lib/claude-adapter.mjs +2 -2
- package/tools/ralph-external/lib/codex-adapter.mjs +3 -3
- package/tools/ralph-external/lib/managed-worker-attempt.mjs +86 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +11 -260
- package/tools/ralph-external/lib/session-attempt.mjs +85 -0
- package/tools/ralph-external/lib/strict-worker-json.mjs +190 -0
- package/tools/ralph-external/lib/worker-artifacts.mjs +209 -0
- package/tools/ralph-external/lib/worker-final.mjs +143 -0
- package/tools/ralph-external/lib/worker-inputs.mjs +55 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +2 -44
- package/tools/ralph-external/lib/worker-launch-cli.mjs +47 -0
- package/tools/ralph-external/lib/worker-native-sandbox.mjs +83 -0
- package/tools/ralph-external/session-launcher.mjs +73 -217
- package/vscode-extension/schemas/aiwg.config.v1.json +161 -2
|
@@ -510,7 +510,7 @@
|
|
|
510
510
|
"approval_policy=\"${approval}\"",
|
|
511
511
|
"--skip-git-repo-check",
|
|
512
512
|
"-o",
|
|
513
|
-
"${final}",
|
|
513
|
+
"${final}.last-message",
|
|
514
514
|
"--json",
|
|
515
515
|
"-"
|
|
516
516
|
],
|
|
@@ -575,7 +575,7 @@
|
|
|
575
575
|
},
|
|
576
576
|
"finalOutput": {
|
|
577
577
|
"support": "unqualified",
|
|
578
|
-
"mapping": "[-o/--output-last-message F] last-message
|
|
578
|
+
"mapping": "[-o/--output-last-message F.last-message] launcher-owned last-message capture, separate from worker-written workerClaims JSON at F (brief output.resultPath / --final-output); --json carries the JSONL thread/turn/item/error event stream on stdout",
|
|
579
579
|
"evidence": "H: codex-cli 0.161.0 exec --help documents -o/--output-last-message and --json; unqualified until live evidence"
|
|
580
580
|
},
|
|
581
581
|
"stdin": {
|
|
@@ -1606,7 +1606,7 @@
|
|
|
1606
1606
|
"Unknown model, effort, permission or required step mappings block dispatch.",
|
|
1607
1607
|
"Native MCP/client, MSP/ACP, gateway and conversation capabilities do not establish worker authority.",
|
|
1608
1608
|
"There is NO `oz` symlink on the evidenced host: the real executable is `warp-terminal`, with `agent run` etc. as its subcommands; `runtimeDriver`/`executable` are pinned to that, not to a bare `oz`.",
|
|
1609
|
-
"
|
|
1609
|
+
"Brief delivery is wired through buildWorkerArtifacts and the shared session launcher: one bounded workspace-relative .agents/skills/aiwg-brief-<invocationId>/SKILL.md file, exclusive creation, no symlink components, collision preservation and cleanup on failure, completion or stop. The same invocation ID binds --skill and the artifact path; brief text never rides argv. Real skill search relative to --cwd and missing-skill behavior remain UNVERIFIED behind authentication; synthetic file-delivery tests do not qualify the profile.",
|
|
1610
1610
|
"`--environment`/`-e` (cloud environment/runner) and `--harness claude|codex` (third-party harness mode) are documented but NEVER emitted by this profile; the cloud subcommand is also never emitted. Execution location is unverified until an authenticated run.",
|
|
1611
1611
|
"`--no-snapshot` is always emitted: `agent run --help` documents it as disabling the end-of-run workspace snapshot upload. This flag does not establish execution location or absence of other uploads.",
|
|
1612
1612
|
"No live success/error output was observed (no login on the evidenced host): `finalOutput`/`structuredOutput` support is grade H/R only, never upgraded past `unqualified`."
|
|
@@ -3326,6 +3326,200 @@
|
|
|
3326
3326
|
}
|
|
3327
3327
|
}
|
|
3328
3328
|
},
|
|
3329
|
+
"workerAdapters": {
|
|
3330
|
+
"local-http": {
|
|
3331
|
+
"version": 1,
|
|
3332
|
+
"id": "local-http",
|
|
3333
|
+
"provider": "ollama",
|
|
3334
|
+
"adapterVersion": "1",
|
|
3335
|
+
"supportedVersions": "8.18.0",
|
|
3336
|
+
"runtimeDriver": "installed:local-http",
|
|
3337
|
+
"catalogAdapter": {
|
|
3338
|
+
"kind": "http",
|
|
3339
|
+
"locator": "/api/tags",
|
|
3340
|
+
"parser": "ollama-model-list-v1",
|
|
3341
|
+
"timeoutSeconds": 10,
|
|
3342
|
+
"maxBytes": 65536
|
|
3343
|
+
},
|
|
3344
|
+
"invocation": {
|
|
3345
|
+
"kind": "process",
|
|
3346
|
+
"executable": "curl",
|
|
3347
|
+
"argv": [
|
|
3348
|
+
"-q",
|
|
3349
|
+
"-sS",
|
|
3350
|
+
"--max-redirs",
|
|
3351
|
+
"0",
|
|
3352
|
+
"--proto",
|
|
3353
|
+
"=http",
|
|
3354
|
+
"--noproxy",
|
|
3355
|
+
"*",
|
|
3356
|
+
"--connect-timeout",
|
|
3357
|
+
"10",
|
|
3358
|
+
"--max-time",
|
|
3359
|
+
"300",
|
|
3360
|
+
"--max-filesize",
|
|
3361
|
+
"10485760",
|
|
3362
|
+
"--fail-with-body",
|
|
3363
|
+
"-H",
|
|
3364
|
+
"Content-Type: application/json",
|
|
3365
|
+
"-X",
|
|
3366
|
+
"POST",
|
|
3367
|
+
"http://${endpoint}/api/generate",
|
|
3368
|
+
"--data-binary",
|
|
3369
|
+
"@-"
|
|
3370
|
+
],
|
|
3371
|
+
"stdin": "adapter-frame",
|
|
3372
|
+
"cwd": "${workspace}",
|
|
3373
|
+
"envAllowlist": [
|
|
3374
|
+
"OLLAMA_HOST"
|
|
3375
|
+
],
|
|
3376
|
+
"versionArgv": [
|
|
3377
|
+
"--version"
|
|
3378
|
+
],
|
|
3379
|
+
"versionPattern": "^curl ([0-9A-Za-z.+-]{1,128}) [\\s\\S]{1,4096}$",
|
|
3380
|
+
"maxOutputBytes": 10485760
|
|
3381
|
+
},
|
|
3382
|
+
"capabilities": {
|
|
3383
|
+
"output": "validated-file",
|
|
3384
|
+
"modelPin": "requested",
|
|
3385
|
+
"effort": "not-applicable",
|
|
3386
|
+
"cancel": "process-group",
|
|
3387
|
+
"resume": false,
|
|
3388
|
+
"sandboxModes": [
|
|
3389
|
+
"external"
|
|
3390
|
+
],
|
|
3391
|
+
"nestedAgentsControllable": true,
|
|
3392
|
+
"nativeFallbackControllable": true,
|
|
3393
|
+
"hardBudget": []
|
|
3394
|
+
},
|
|
3395
|
+
"qualification": {
|
|
3396
|
+
"status": "unqualified"
|
|
3397
|
+
},
|
|
3398
|
+
"invocationProfiles": {
|
|
3399
|
+
"direct-http": {
|
|
3400
|
+
"transport": "shell",
|
|
3401
|
+
"supportedVersions": "8.18.0",
|
|
3402
|
+
"controls": {
|
|
3403
|
+
"model": {
|
|
3404
|
+
"support": "native",
|
|
3405
|
+
"mapping": "[\"--data-binary\",\"@-\"]",
|
|
3406
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3407
|
+
},
|
|
3408
|
+
"effort": {
|
|
3409
|
+
"support": "not-applicable",
|
|
3410
|
+
"mapping": "unavailable: explicit effort is refused; null effort and empty effort lists only",
|
|
3411
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3412
|
+
},
|
|
3413
|
+
"sandbox": {
|
|
3414
|
+
"support": "external",
|
|
3415
|
+
"mapping": "unavailable natively: requires a host OS/executor boundary; local endpoint does not prove local inference",
|
|
3416
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3417
|
+
},
|
|
3418
|
+
"approval": {
|
|
3419
|
+
"support": "external",
|
|
3420
|
+
"mapping": "unavailable natively: curl has no approval or permission system; the host authorizes the single text-only POST",
|
|
3421
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3422
|
+
},
|
|
3423
|
+
"workdir": {
|
|
3424
|
+
"support": "external",
|
|
3425
|
+
"mapping": "host spawns curl in the resolved absolute workspace; no server-side workspace control",
|
|
3426
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3427
|
+
},
|
|
3428
|
+
"finalOutput": {
|
|
3429
|
+
"support": "external",
|
|
3430
|
+
"mapping": "host captures stdout and validates the original envelope and extracted worker claims; no server output-file control",
|
|
3431
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3432
|
+
},
|
|
3433
|
+
"stdin": {
|
|
3434
|
+
"support": "native",
|
|
3435
|
+
"mapping": "[\"--data-binary\",\"@-\"]",
|
|
3436
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3437
|
+
},
|
|
3438
|
+
"structuredOutput": {
|
|
3439
|
+
"support": "external",
|
|
3440
|
+
"mapping": "unavailable server schema control; host strict claims parser validates the extracted response",
|
|
3441
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3442
|
+
},
|
|
3443
|
+
"stepCap": {
|
|
3444
|
+
"support": "unqualified",
|
|
3445
|
+
"mapping": "unavailable: no model step cap; curl time and size bounds are not a step cap",
|
|
3446
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3447
|
+
},
|
|
3448
|
+
"trust": {
|
|
3449
|
+
"support": "native",
|
|
3450
|
+
"mapping": "-q",
|
|
3451
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3452
|
+
},
|
|
3453
|
+
"children": {
|
|
3454
|
+
"support": "native",
|
|
3455
|
+
"mapping": "[\"--data-binary\",\"@-\"]",
|
|
3456
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3457
|
+
},
|
|
3458
|
+
"fallback": {
|
|
3459
|
+
"support": "native",
|
|
3460
|
+
"mapping": "[\"--max-redirs\",\"0\"]",
|
|
3461
|
+
"evidence": "curl 8.18.0 installed help; tools/ralph-external/lib/ollama-adapter.mjs and offline Ollama fixtures; server 0.33.3 API assumptions provisional"
|
|
3462
|
+
}
|
|
3463
|
+
},
|
|
3464
|
+
"envAllowlist": [
|
|
3465
|
+
"OLLAMA_HOST"
|
|
3466
|
+
],
|
|
3467
|
+
"pitfalls": [
|
|
3468
|
+
"One text-only POST /api/generate, stream:false, explicit model and no tools, child-agent API or fallback request. Coding and explicit effort are refused.",
|
|
3469
|
+
"The model control binds the resolved model in the adapter-frame JSON body; argv contains no model or brief.",
|
|
3470
|
+
"No Ollama CLI, server start or weight-download command is invoked, including for disabled members. Server-side downloads, inference locality and backend identity remain unqualified.",
|
|
3471
|
+
"curl 8.18.0 is the executable version; Ollama 0.33.3 response fields and done_reason=stop remain provisional pending the conductor smoke.",
|
|
3472
|
+
"No native sandbox or approval system exists; external workspace/capture/validation controls require an enforcing host boundary.",
|
|
3473
|
+
"No streaming health grammar, progress, session log, server cancellation, resume or model step cap is available. Owned curl exit is not proof of remote server stop."
|
|
3474
|
+
],
|
|
3475
|
+
"health": {
|
|
3476
|
+
"version": 1,
|
|
3477
|
+
"handshake": "process-only",
|
|
3478
|
+
"outputFraming": "final-json",
|
|
3479
|
+
"signalParser": "unavailable",
|
|
3480
|
+
"sessionLogLocator": null,
|
|
3481
|
+
"progressEvidence": [],
|
|
3482
|
+
"stopEvidence": "owned curl process identity and observed exit only; remote server cancellation unavailable",
|
|
3483
|
+
"fixtureRef": "test/fixtures/providers/local-http/health/manifest.json",
|
|
3484
|
+
"qualifiedAt": null,
|
|
3485
|
+
"statusOperation": null,
|
|
3486
|
+
"cancelOperation": null,
|
|
3487
|
+
"coverage": "degraded",
|
|
3488
|
+
"qualificationTarget": "unobserved",
|
|
3489
|
+
"evidenceGrades": [
|
|
3490
|
+
"H",
|
|
3491
|
+
"R"
|
|
3492
|
+
],
|
|
3493
|
+
"provenance": [
|
|
3494
|
+
{
|
|
3495
|
+
"kind": "help",
|
|
3496
|
+
"ref": "docs/providers/ollama-direct.md",
|
|
3497
|
+
"observedAt": "2026-10-09T00:00:00Z",
|
|
3498
|
+
"cliVersion": "8.18.0"
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
"kind": "repository",
|
|
3502
|
+
"ref": "tools/ralph-external/lib/ollama-adapter.mjs",
|
|
3503
|
+
"observedAt": "2026-10-26T00:00:00Z",
|
|
3504
|
+
"cliVersion": null
|
|
3505
|
+
}
|
|
3506
|
+
],
|
|
3507
|
+
"qualificationOwner": "B-OG-09",
|
|
3508
|
+
"qualification": {
|
|
3509
|
+
"status": "unqualified",
|
|
3510
|
+
"scopeDigest": null,
|
|
3511
|
+
"cliVersion": null,
|
|
3512
|
+
"accountScope": null,
|
|
3513
|
+
"profileRef": null,
|
|
3514
|
+
"modelKey": null,
|
|
3515
|
+
"effort": null,
|
|
3516
|
+
"authorizedLive": false
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
},
|
|
3329
3523
|
"workerHealthAdapters": {
|
|
3330
3524
|
"local-http": {
|
|
3331
3525
|
"version": 1,
|
package/bin/aiwg.mjs
CHANGED
|
@@ -27,6 +27,14 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
27
27
|
import os from 'os';
|
|
28
28
|
import { randomUUID } from 'crypto';
|
|
29
29
|
|
|
30
|
+
// Every exit path calls process.exit() right after writing. On a pipe, Node
|
|
31
|
+
// queues writes the reader has not consumed yet and process.exit() drops them,
|
|
32
|
+
// so a slow reader saw JSON reports cut off at the 64 KiB pipe buffer. Blocking
|
|
33
|
+
// pipe writes finish before the next statement runs, so nothing is lost.
|
|
34
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
35
|
+
if (!stream.isTTY) stream._handle?.setBlocking?.(true);
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
const __filename = fileURLToPath(import.meta.url);
|
|
31
39
|
const __dirname = path.dirname(__filename);
|
|
32
40
|
const packageRoot = path.resolve(__dirname, '..');
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Builds the existing MC admission owner with a live worker-activation gate.
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { assertWorkerActivationCurrent } from '../../config/worker-activation-gate.js';
|
|
4
|
+
import * as ui from '../ui.js';
|
|
5
|
+
const MC_ROOT = '.aiwg/ralph-external/mc';
|
|
6
|
+
/** Shared binding used by MC and the managed Mission/Flow/Ralph caller paths. */
|
|
7
|
+
export async function createMcAdmissionScheduler(projectRoot, cfg, hostRegistry) {
|
|
8
|
+
const { resolveParallelism, workerActivationDigests, workerConfigDigest } = await import('../../config/aiwg-config.js');
|
|
9
|
+
const { FileAdmissionStore, SharedHostScheduler } = await import('../../serve/shared-host-scheduler.js');
|
|
10
|
+
const provider = cfg?.providers[0] ?? 'unknown';
|
|
11
|
+
const parallelism = resolveParallelism(cfg?.parallelism, provider);
|
|
12
|
+
if (parallelism.worker_mode === 'managed') {
|
|
13
|
+
const { managedProjectAdmissionPolicy, projectAdmissionAuthority } = await import('../../serve/shared-host-scheduler.js');
|
|
14
|
+
const activation = parallelism.activation;
|
|
15
|
+
if (!cfg || !activation || activation.configDigest !== workerActivationDigests(cfg).configDigest) {
|
|
16
|
+
throw new Error('managed admission requires a current activated policy');
|
|
17
|
+
}
|
|
18
|
+
const authority = projectAdmissionAuthority(projectRoot);
|
|
19
|
+
const authorityRef = parallelism.admission?.authorityRef ?? 'project';
|
|
20
|
+
if (authorityRef.startsWith('host:') && process.env['AIWG_EXECUTOR_IDENTITY_STORE']) {
|
|
21
|
+
ui.warn('AIWG_EXECUTOR_IDENTITY_STORE is ignored for host admission authority lookup.');
|
|
22
|
+
}
|
|
23
|
+
const policy = managedProjectAdmissionPolicy(projectRoot, { ...parallelism,
|
|
24
|
+
admission: { ...parallelism.admission, authorityRef: 'project' },
|
|
25
|
+
}, workerConfigDigest(activation), parallelism.max_parallel_subagents);
|
|
26
|
+
const binding = authorityRef === 'project' ? { store: new FileAdmissionStore(authority.path), policy }
|
|
27
|
+
: (hostRegistry ?? (await import('../../serve/executor-registry.js')).executorRegistry).resolveHostAdmissionAuthority(authorityRef, policy);
|
|
28
|
+
const check = () => assertWorkerActivationCurrent(projectRoot, activation);
|
|
29
|
+
check();
|
|
30
|
+
const scheduler = new SharedHostScheduler(binding.store, binding.policy, Date.now, check);
|
|
31
|
+
scheduler.reconcileNow();
|
|
32
|
+
return scheduler;
|
|
33
|
+
}
|
|
34
|
+
const maxConcurrent = parallelism.max_parallel_mc_missions;
|
|
35
|
+
return new SharedHostScheduler(new FileAdmissionStore(join(projectRoot, MC_ROOT, 'admission.json')), {
|
|
36
|
+
maxConcurrent, leaseTtlMs: 5 * 60_000, agingIntervalMs: 30_000, allowPreemption: false, defaultHostQuota: maxConcurrent,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=mc-admission.js.map
|
|
@@ -36,37 +36,8 @@ async function readAdmissionConfig(projectRoot) {
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
export
|
|
41
|
-
const { resolveParallelism, workerActivationDigests, workerConfigDigest } = await import('../../config/aiwg-config.js');
|
|
42
|
-
const { FileAdmissionStore, SharedHostScheduler } = await import('../../serve/shared-host-scheduler.js');
|
|
43
|
-
const provider = cfg?.providers[0] ?? 'unknown';
|
|
44
|
-
const parallelism = resolveParallelism(cfg?.parallelism, provider);
|
|
45
|
-
if (parallelism.worker_mode === 'managed') {
|
|
46
|
-
const { managedProjectAdmissionPolicy, projectAdmissionAuthority } = await import('../../serve/shared-host-scheduler.js');
|
|
47
|
-
const activation = parallelism.activation;
|
|
48
|
-
if (!cfg || !activation || activation.configDigest !== workerActivationDigests(cfg).configDigest) {
|
|
49
|
-
throw new Error('managed admission requires a current activated policy');
|
|
50
|
-
}
|
|
51
|
-
const authority = projectAdmissionAuthority(projectRoot);
|
|
52
|
-
const authorityRef = parallelism.admission?.authorityRef ?? 'project';
|
|
53
|
-
if (authorityRef.startsWith('host:') && process.env['AIWG_EXECUTOR_IDENTITY_STORE']) {
|
|
54
|
-
ui.warn('AIWG_EXECUTOR_IDENTITY_STORE is ignored for host admission authority lookup.');
|
|
55
|
-
}
|
|
56
|
-
const policy = managedProjectAdmissionPolicy(projectRoot, { ...parallelism,
|
|
57
|
-
admission: { ...parallelism.admission, authorityRef: 'project' },
|
|
58
|
-
}, workerConfigDigest(activation), parallelism.max_parallel_subagents);
|
|
59
|
-
const binding = authorityRef === 'project' ? { store: new FileAdmissionStore(authority.path), policy }
|
|
60
|
-
: (hostRegistry ?? (await import('../../serve/executor-registry.js')).executorRegistry).resolveHostAdmissionAuthority(authorityRef, policy);
|
|
61
|
-
const scheduler = new SharedHostScheduler(binding.store, binding.policy);
|
|
62
|
-
scheduler.reconcileNow();
|
|
63
|
-
return scheduler;
|
|
64
|
-
}
|
|
65
|
-
const maxConcurrent = parallelism.max_parallel_mc_missions;
|
|
66
|
-
return new SharedHostScheduler(new FileAdmissionStore(join(projectRoot, MC_ROOT, 'admission.json')), {
|
|
67
|
-
maxConcurrent, leaseTtlMs: 5 * 60_000, agingIntervalMs: 30_000, allowPreemption: false, defaultHostQuota: maxConcurrent,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
39
|
+
import { createMcAdmissionScheduler } from './mc-admission.js';
|
|
40
|
+
export { createMcAdmissionScheduler } from './mc-admission.js';
|
|
70
41
|
const mcWorkerSupport = () => workerConsumerSupport([...WORKER_BASELINE_FEATURES, 'worker-health.v1']);
|
|
71
42
|
function workerDispatch(mission) {
|
|
72
43
|
const bindings = new Set();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Documents the existing model maintenance and resolution commands.
|
|
2
|
+
export const MODELS_HELP = `Usage: aiwg models <subcommand> [options]
|
|
3
|
+
|
|
4
|
+
Subcommands:
|
|
5
|
+
list List discovered agent and skill model policies
|
|
6
|
+
audit Compile selected policies and report diagnostics
|
|
7
|
+
resolve Resolve artifact policies, or a direct worker invocation
|
|
8
|
+
validate Validate selected artifact policies (not worker configuration)
|
|
9
|
+
sources Show the effective model catalog without network refresh
|
|
10
|
+
refresh Refresh model discovery (may access the network)
|
|
11
|
+
set-default <TIER> Write defaults.tier to models.json
|
|
12
|
+
set --tier <TIER> Update selected agent/skill frontmatter; requires a selector or --all
|
|
13
|
+
migrate Add canonical role/tier hints to legacy skills
|
|
14
|
+
|
|
15
|
+
Artifact selectors and options:
|
|
16
|
+
--agent <name> --skill <name> --glob <pattern> --role <role>
|
|
17
|
+
--tier <tier> --framework <name> --all
|
|
18
|
+
--provider <provider> Provider for artifact compilation (default: claude)
|
|
19
|
+
--model <id> Explicit Grok Build model for artifact resolve
|
|
20
|
+
--target <directory> Artifact/project root (default: current directory)
|
|
21
|
+
--propose <file> Review pair edits (--parallelism/--worker-selection JSON files)
|
|
22
|
+
--apply <file> Apply with --authorize DIGEST or --standing-authorization FILE
|
|
23
|
+
--recover Restore retained pair with the original authorization
|
|
24
|
+
--scope project|user set-default destination (default: project)
|
|
25
|
+
--dry-run Preview mutations without writing
|
|
26
|
+
--json Emit JSON
|
|
27
|
+
--drift Include catalog drift for sources/refresh
|
|
28
|
+
--url <url> Remote catalog URL for refresh
|
|
29
|
+
--help, -h Show this reference
|
|
30
|
+
TIER: economy | standard | premium | max-quality
|
|
31
|
+
|
|
32
|
+
Direct worker resolution (plans an invocation; does not launch it):
|
|
33
|
+
aiwg models resolve --role <role> --task <class> --group <group>
|
|
34
|
+
--brief <file> --workdir <tree> --final-output <file>
|
|
35
|
+
[--transport shell|mcp] [--format json|shell] [--authorize-premium]
|
|
36
|
+
--role efficiency | coding | reasoning | configured project alias
|
|
37
|
+
--task Task class from defaults.workerSelection.taskClasses
|
|
38
|
+
Built-ins: inventory, implementation, architecture, review
|
|
39
|
+
--group Group from parallelism.groups and the role's guidance
|
|
40
|
+
--brief Absolute path to the task brief
|
|
41
|
+
--workdir Absolute path to the worker tree
|
|
42
|
+
--final-output Absolute path for the final result
|
|
43
|
+
--transport Must match group execution transport (default: shell)
|
|
44
|
+
--format JSON plan or POSIX shell command (default: json)
|
|
45
|
+
--json Alias for --format json
|
|
46
|
+
--authorize-premium Authorize premium for this resolution only
|
|
47
|
+
Agents require explicit human confirmation of escalation
|
|
48
|
+
|
|
49
|
+
Worker groups live in .aiwg/aiwg.config; role guidance lives in project-root models.json.
|
|
50
|
+
Configure both, then run aiwg doctor --probe-workers --qualify.
|
|
51
|
+
See docs/getting-started/worker-dispatch.md for a complete example.`;
|
|
52
|
+
//# sourceMappingURL=models-help.js.map
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { MODELS_HELP } from './models-help.js';
|
|
2
|
+
import { updateJson } from '../../models/config-writer.js';
|
|
3
|
+
import { atomicConfigWrite as atomicWrite } from '../../config/worker-write.js';
|
|
4
|
+
import { handleWorkerAdoptionArgs } from '../../config/worker-adoption-cli.js';
|
|
5
|
+
import { resolveParallelism } from '../../config/aiwg-config.js';
|
|
1
6
|
/**
|
|
2
7
|
* Safe model-policy audit, resolution, and mutation commands.
|
|
3
8
|
* @implements #1804
|
|
@@ -8,7 +13,7 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
8
13
|
import path from 'node:path';
|
|
9
14
|
import { glob } from 'glob';
|
|
10
15
|
import { load as loadYaml } from 'js-yaml';
|
|
11
|
-
import { compileModelPolicy, validateCanonicalModelPolicy,
|
|
16
|
+
import { compileModelPolicy, validateCanonicalModelPolicy, loadProviderModelCapabilities, WorkerPolicyError, assertWorkerRoleEffort, } from '../../models/provider-policy.js';
|
|
12
17
|
import { resolveGrokBuildRoleModel } from '../../models/grok-build-models.js';
|
|
13
18
|
import { readAiwgConfig, emptyConfig } from '../../config/aiwg-config.js';
|
|
14
19
|
import { ConfigLoader } from '../../models/config-loader.js';
|
|
@@ -34,22 +39,21 @@ export async function loadWorkerInputs(ctx) {
|
|
|
34
39
|
const models = await loader.load(config.parallelism);
|
|
35
40
|
const catalog = await effectiveCatalog(ctx, ctx.cwd, false, parseArgs(['resolve']));
|
|
36
41
|
const registry = loadProviderModelCapabilities();
|
|
37
|
-
// Let the resolver explain the dispatch gate before requiring host qualification (#3070).
|
|
38
42
|
if (!config.parallelism?.worker_dispatch) {
|
|
39
43
|
return { snapshot: { config, models, modelDigests: loader.getInputDigests(), catalog,
|
|
40
44
|
registry, installations: {} }, drivers: {} };
|
|
41
45
|
}
|
|
42
46
|
// Explain missing policy before a recorded approval's digest check can
|
|
43
47
|
// mask it. The host owner first preserves its missing-installation errors.
|
|
44
|
-
const parallelism = config.parallelism;
|
|
48
|
+
const parallelism = resolveParallelism(config.parallelism, config.providers[0]);
|
|
45
49
|
const precheckPolicy = () => {
|
|
46
50
|
if (!ctx.args.some(arg => arg === '--role' || arg.startsWith('--role=')))
|
|
47
51
|
return;
|
|
48
52
|
const request = parseWorkerCommandArgs(ctx.args.slice(1));
|
|
49
53
|
const members = Object.entries(parallelism.groups ?? {})
|
|
50
54
|
.filter(([id]) => !request.group || request.group === id)
|
|
51
|
-
.flatMap(([, group]) => group.members).filter(member => member.enabled !== false
|
|
52
|
-
&& parallelism.clis?.[member.cli]?.enabled !== false && member.roles.includes(request.role));
|
|
55
|
+
.flatMap(([, group]) => group.members ?? []).filter(member => member.enabled !== false
|
|
56
|
+
&& parallelism.clis?.[member.cli]?.enabled !== false && member.roles.includes((models.defaults.workerSelection?.roleGuidance?.[request.role]?.baseRole ?? request.role)));
|
|
53
57
|
const requiresExplicit = members.length > 0 && members.every(member => {
|
|
54
58
|
// Health metadata can identify the provider before an operator has
|
|
55
59
|
// qualified its dispatch adapter. It grants no dispatch authority.
|
|
@@ -269,35 +273,6 @@ function print(value, json) {
|
|
|
269
273
|
else
|
|
270
274
|
console.log(JSON.stringify(value, null, 2));
|
|
271
275
|
}
|
|
272
|
-
async function atomicWrite(file, content) {
|
|
273
|
-
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
274
|
-
const temporary = `${file}.tmp-${process.pid}`;
|
|
275
|
-
let mode = 0o600;
|
|
276
|
-
try {
|
|
277
|
-
mode = (await fs.stat(file)).mode;
|
|
278
|
-
}
|
|
279
|
-
catch { /* new file */ }
|
|
280
|
-
await fs.writeFile(temporary, content, { encoding: 'utf8', mode });
|
|
281
|
-
await fs.rename(temporary, file);
|
|
282
|
-
}
|
|
283
|
-
async function updateJson(file, mutate, dryRun) {
|
|
284
|
-
let value = {};
|
|
285
|
-
try {
|
|
286
|
-
value = JSON.parse(await fs.readFile(file, 'utf8'));
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
if (error?.code !== 'ENOENT')
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
292
|
-
mutate(value);
|
|
293
|
-
const validation = validateUserProjectModelConfig(value);
|
|
294
|
-
if (!validation.valid)
|
|
295
|
-
throw new Error(validation.diagnostics.map(item => item.message).join('; '));
|
|
296
|
-
const content = `${JSON.stringify(value, null, 2)}\n`;
|
|
297
|
-
if (!dryRun)
|
|
298
|
-
await atomicWrite(file, content);
|
|
299
|
-
return { file, beforeWrite: value, dryRun };
|
|
300
|
-
}
|
|
301
276
|
async function effectiveCatalog(ctx, target, allowNetwork, parsed, provider) {
|
|
302
277
|
const { collectProviderInventory } = await import('../../providers/provider-inventory.js');
|
|
303
278
|
const { resolveDynamicModelCatalog } = await import('../../models/model-discovery.js');
|
|
@@ -350,53 +325,6 @@ function replaceFrontmatter(raw, item, tier) {
|
|
|
350
325
|
}
|
|
351
326
|
return `---\n${frontmatter}\n---\n${match[2]}`;
|
|
352
327
|
}
|
|
353
|
-
const MODELS_HELP = `Usage: aiwg models <subcommand> [options]
|
|
354
|
-
|
|
355
|
-
Subcommands:
|
|
356
|
-
list List discovered agent and skill model policies
|
|
357
|
-
audit Compile selected policies and report diagnostics
|
|
358
|
-
resolve Resolve artifact policies, or a direct worker invocation
|
|
359
|
-
validate Validate selected artifact policies (not worker configuration)
|
|
360
|
-
sources Show the effective model catalog without network refresh
|
|
361
|
-
refresh Refresh model discovery (may access the network)
|
|
362
|
-
set-default <TIER> Write defaults.tier to models.json
|
|
363
|
-
set --tier <TIER> Update selected agent/skill frontmatter; requires a selector or --all
|
|
364
|
-
migrate Add canonical role/tier hints to legacy skills
|
|
365
|
-
|
|
366
|
-
Artifact selectors and options:
|
|
367
|
-
--agent <name> --skill <name> --glob <pattern> --role <role>
|
|
368
|
-
--tier <tier> --framework <name> --all
|
|
369
|
-
--provider <provider> Provider for artifact compilation (default: claude)
|
|
370
|
-
--model <id> Explicit Grok Build model for artifact resolve
|
|
371
|
-
--target <directory> Artifact/project root (default: current directory)
|
|
372
|
-
--scope project|user set-default destination (default: project)
|
|
373
|
-
--dry-run Preview mutations without writing
|
|
374
|
-
--json Emit JSON
|
|
375
|
-
--drift Include catalog drift for sources/refresh
|
|
376
|
-
--url <url> Remote catalog URL for refresh
|
|
377
|
-
--help, -h Show this reference
|
|
378
|
-
TIER: economy | standard | premium | max-quality
|
|
379
|
-
|
|
380
|
-
Direct worker resolution (plans an invocation; does not launch it):
|
|
381
|
-
aiwg models resolve --role <role> --task <class> --group <group>
|
|
382
|
-
--brief <file> --workdir <tree> --final-output <file>
|
|
383
|
-
[--transport shell|mcp] [--format json|shell] [--authorize-premium]
|
|
384
|
-
--role efficiency | coding | reasoning
|
|
385
|
-
--task Task class from defaults.workerSelection.taskClasses
|
|
386
|
-
Built-ins: inventory, implementation, architecture, review
|
|
387
|
-
--group Group from parallelism.groups and the role's guidance
|
|
388
|
-
--brief Absolute path to the task brief
|
|
389
|
-
--workdir Absolute path to the worker tree
|
|
390
|
-
--final-output Absolute path for the final result
|
|
391
|
-
--transport Must match group execution transport (default: shell)
|
|
392
|
-
--format JSON plan or POSIX shell command (default: json)
|
|
393
|
-
--json Alias for --format json
|
|
394
|
-
--authorize-premium Authorize premium for this resolution only
|
|
395
|
-
Agents require explicit human confirmation of escalation
|
|
396
|
-
|
|
397
|
-
Worker groups live in .aiwg/aiwg.config; role guidance lives in project-root models.json.
|
|
398
|
-
Configure both, then run aiwg doctor --probe-workers --qualify.
|
|
399
|
-
See docs/getting-started/worker-dispatch.md for a complete example.`;
|
|
400
328
|
async function execute(ctx, services = { load: loadWorkerInputs }) {
|
|
401
329
|
if (ctx.args.length === 0 || ctx.args[0] === 'help' || ctx.args.includes('--help') || ctx.args.includes('-h')) {
|
|
402
330
|
print(MODELS_HELP, false);
|
|
@@ -406,6 +334,8 @@ async function execute(ctx, services = { load: loadWorkerInputs }) {
|
|
|
406
334
|
if (ctx.args[0] === 'resolve' && ctx.args.slice(1).some(arg => /^--(?:task|group|brief|workdir|final-output|transport|format|authorize-premium)(?:=|$)/.test(arg))) {
|
|
407
335
|
return resolveDirect(ctx, services);
|
|
408
336
|
}
|
|
337
|
+
if (await handleWorkerAdoptionArgs(ctx.args, ctx.cwd, 'models', ctx.signal, ctx.dryRun))
|
|
338
|
+
return { exitCode: 0 };
|
|
409
339
|
const parsed = parseArgs(ctx.args);
|
|
410
340
|
if (parsed.subcommand === 'resolve') {
|
|
411
341
|
const supported = new Set(['agent', 'skill', 'glob', 'role', 'tier', 'framework', 'all', 'target', 'provider', 'model', 'json', 'help', 'dry-run']);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Select and persist an optional bundled addon memory profile.
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import * as ui from '../ui.js';
|
|
5
|
+
export async function selectAddonProfile(options) {
|
|
6
|
+
const { addonSource, framework, target, args, dryRunAddon, bulkIntent, signal } = options;
|
|
7
|
+
try {
|
|
8
|
+
const profileManifestPath = path.join(addonSource, 'manifest.json');
|
|
9
|
+
const profileManifestContent = await fs.readFile(profileManifestPath, 'utf-8');
|
|
10
|
+
const profileManifest = JSON.parse(profileManifestContent);
|
|
11
|
+
const topology = profileManifest.memory?.topology;
|
|
12
|
+
const templates = profileManifest.templates;
|
|
13
|
+
if (topology && templates && templates.length > 1) {
|
|
14
|
+
const profileIdx = args.findIndex(a => a === '--profile');
|
|
15
|
+
let selectedProfile;
|
|
16
|
+
if (profileIdx >= 0 && args[profileIdx + 1]) {
|
|
17
|
+
// Explicit --profile flag
|
|
18
|
+
selectedProfile = args[profileIdx + 1];
|
|
19
|
+
const templateFile = templates.find((t) => t.replace('.md', '') === selectedProfile || t === selectedProfile);
|
|
20
|
+
if (!templateFile) {
|
|
21
|
+
ui.warn(`Unknown profile "${selectedProfile}". Available: ${templates.map((t) => t.replace('.md', '')).join(', ')}`);
|
|
22
|
+
selectedProfile = undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (bulkIntent || !process.stdin.isTTY) {
|
|
26
|
+
// Bulk/automation or non-TTY: silently pick 'generic' default.
|
|
27
|
+
// The profile picker is annoying during `aiwg use all`.
|
|
28
|
+
selectedProfile = 'generic';
|
|
29
|
+
}
|
|
30
|
+
else if (process.stdin.isTTY) {
|
|
31
|
+
// Interactive profile selection via the shared `listSelect` helper
|
|
32
|
+
// (POC for spike #926). One call renders the option list, handles
|
|
33
|
+
// number-or-name matching, threads `signal` for Ctrl-C
|
|
34
|
+
// cancellation, and resolves to the fallback on timeout or empty
|
|
35
|
+
// input. The hand-rolled parse-and-branch that used to live here
|
|
36
|
+
// is now a one-liner.
|
|
37
|
+
const { createPromptInterface, listSelect } = await import('../prompt-utils.js');
|
|
38
|
+
ui.blank();
|
|
39
|
+
ui.header(' Select a topology profile:');
|
|
40
|
+
const templateNames = templates.map((t) => t.replace('.md', ''));
|
|
41
|
+
const options = templateNames.map((name) => ({
|
|
42
|
+
label: name === 'generic' ? `${name} (default)` : name,
|
|
43
|
+
value: name,
|
|
44
|
+
}));
|
|
45
|
+
const rl = createPromptInterface();
|
|
46
|
+
try {
|
|
47
|
+
selectedProfile = await listSelect(rl, ' Enter number or name [generic]: ', options, 'generic', signal);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
rl.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Write profile config to project namespace
|
|
54
|
+
if (selectedProfile && !dryRunAddon) {
|
|
55
|
+
const namespace = topology.namespace || `.aiwg/${framework}`;
|
|
56
|
+
const configDir = path.join(target, namespace);
|
|
57
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
58
|
+
const profileConfig = {
|
|
59
|
+
profile: selectedProfile,
|
|
60
|
+
pageTemplate: `templates/${selectedProfile}.md`,
|
|
61
|
+
selectedAt: new Date().toISOString(),
|
|
62
|
+
};
|
|
63
|
+
await fs.writeFile(path.join(configDir, 'config.json'), JSON.stringify(profileConfig, null, 2) + '\n');
|
|
64
|
+
ui.success(`Profile "${selectedProfile}" selected → ${namespace}/config.json`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Profile selection is optional — don't fail deployment
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=use-addon-profile.js.map
|