@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.
Files changed (91) hide show
  1. package/agentic/code/providers/model-capabilities.v1.json +197 -3
  2. package/bin/aiwg.mjs +8 -0
  3. package/dist/src/cli/handlers/mc-admission.js +39 -0
  4. package/dist/src/cli/handlers/mc.js +2 -31
  5. package/dist/src/cli/handlers/models-help.js +52 -0
  6. package/dist/src/cli/handlers/models.js +11 -81
  7. package/dist/src/cli/handlers/use-addon-profile.js +72 -0
  8. package/dist/src/cli/handlers/use.js +191 -230
  9. package/dist/src/cli/ui.js +1 -0
  10. package/dist/src/config/aiwg-config.js +38 -184
  11. package/dist/src/config/cli-help.js +54 -0
  12. package/dist/src/config/cli.js +5 -69
  13. package/dist/src/config/config-normalization.js +10 -0
  14. package/dist/src/config/worker-activation-gate.js +26 -0
  15. package/dist/src/config/worker-adoption-cli.js +102 -0
  16. package/dist/src/config/worker-adoption.js +368 -0
  17. package/dist/src/config/worker-config-digest.js +14 -0
  18. package/dist/src/config/worker-group-templates.js +150 -0
  19. package/dist/src/config/worker-guidance-warning.js +28 -0
  20. package/dist/src/config/worker-owner-lock.js +152 -0
  21. package/dist/src/config/worker-parallelism.js +154 -0
  22. package/dist/src/config/worker-write.js +75 -0
  23. package/dist/src/decision/telemetry/exporter.js +2 -1
  24. package/dist/src/decision/telemetry/index.js +1 -0
  25. package/dist/src/decision/telemetry/mapping.js +43 -0
  26. package/dist/src/decision/telemetry/otlp-http.js +5 -2
  27. package/dist/src/decision/telemetry/redaction.js +6 -1
  28. package/dist/src/decision/telemetry/types.js +3 -0
  29. package/dist/src/decision/telemetry/worker.js +69 -0
  30. package/dist/src/lint/rule-activation.js +15 -7
  31. package/dist/src/lint/runner.js +2 -2
  32. package/dist/src/mcp/server.mjs +2 -11
  33. package/dist/src/mcp/stdio-lifecycle.mjs +27 -0
  34. package/dist/src/mcp/tools/worker-failure-diagnostics.mjs +9 -0
  35. package/dist/src/mcp/tools/worker-invoke.mjs +6 -6
  36. package/dist/src/mission-protocol/codecs.js +8 -38
  37. package/dist/src/mission-protocol/worker-contract-diagnostics.js +62 -0
  38. package/dist/src/mission-protocol/worker-invocation-types.js +2 -0
  39. package/dist/src/models/conductor-data-policy.d.mts +8 -0
  40. package/dist/src/models/conductor-data-policy.mjs +190 -0
  41. package/dist/src/models/config-loader.js +19 -3
  42. package/dist/src/models/config-writer.js +27 -0
  43. package/dist/src/models/model-capabilities.v1.json +197 -3
  44. package/dist/src/models/provider-policy.js +7 -155
  45. package/dist/src/models/resolver.js +2 -293
  46. package/dist/src/models/worker-artifact-binding.mjs +12 -0
  47. package/dist/src/models/worker-direct-readiness-policy.js +155 -0
  48. package/dist/src/models/worker-direct-readiness.js +9 -218
  49. package/dist/src/models/worker-health-readiness.js +105 -26
  50. package/dist/src/models/worker-installation.mjs +12 -14
  51. package/dist/src/models/worker-member-references.js +64 -0
  52. package/dist/src/models/worker-plan.js +317 -0
  53. package/dist/src/models/worker-selection-policy.js +217 -0
  54. package/dist/src/models/wrapper-deployment.js +1 -1
  55. package/dist/src/serve/shared-host-admission-checks.js +116 -0
  56. package/dist/src/serve/shared-host-scheduler.js +25 -107
  57. package/dist/src/serve/shared-host-starvation.js +73 -0
  58. package/dist/src/serve/worker-artifacts.d.mts +4 -0
  59. package/dist/src/serve/worker-artifacts.mjs +56 -0
  60. package/dist/src/serve/worker-command-request.js +68 -0
  61. package/dist/src/serve/worker-invocation-paths.js +144 -0
  62. package/dist/src/serve/worker-invocation-preparation.js +169 -0
  63. package/dist/src/serve/worker-invocation.js +24 -291
  64. package/dist/src/serve/worker-stdin-frame.d.mts +2 -2
  65. package/dist/src/serve/worker-stdin-frame.mjs +6 -6
  66. package/dist/src/smiths/context-pipeline/parallelism-section.js +19 -7
  67. package/package.json +9 -1
  68. package/schemas/mission-v1.schema.json +20 -19
  69. package/schemas/models/model-config-compatibility.v1.schema.json +16 -0
  70. package/tools/agents/providers/claude-post-deploy.mjs +35 -0
  71. package/tools/agents/providers/claude.mjs +3 -30
  72. package/tools/agents/providers/codex-post-deploy.mjs +13 -0
  73. package/tools/agents/providers/codex.mjs +2 -8
  74. package/tools/agents/providers/conductor-data-guard.mjs +61 -0
  75. package/tools/agents/providers/rule-delivery.mjs +3 -16
  76. package/tools/agents/providers/rule-presentation.mjs +38 -0
  77. package/tools/plugin/package-plugins.mjs +1 -1
  78. package/tools/ralph-external/lib/claude-adapter.mjs +2 -2
  79. package/tools/ralph-external/lib/codex-adapter.mjs +3 -3
  80. package/tools/ralph-external/lib/managed-worker-attempt.mjs +86 -0
  81. package/tools/ralph-external/lib/provider-adapter.mjs +11 -260
  82. package/tools/ralph-external/lib/session-attempt.mjs +85 -0
  83. package/tools/ralph-external/lib/strict-worker-json.mjs +190 -0
  84. package/tools/ralph-external/lib/worker-artifacts.mjs +209 -0
  85. package/tools/ralph-external/lib/worker-final.mjs +143 -0
  86. package/tools/ralph-external/lib/worker-inputs.mjs +55 -0
  87. package/tools/ralph-external/lib/worker-isolation.mjs +2 -44
  88. package/tools/ralph-external/lib/worker-launch-cli.mjs +47 -0
  89. package/tools/ralph-external/lib/worker-native-sandbox.mjs +83 -0
  90. package/tools/ralph-external/session-launcher.mjs +73 -217
  91. 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 file; --json carries the JSONL thread/turn/item/error event stream on stdout",
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
- "BRIEF TRANSPORT IS BLOCKED PENDING A SHARED CHANGE: `--prompt` places the brief on argv (never emitted by this profile); `-f/--file` is NOT a member of the required `<--prompt|--saved-prompt|--task-id|--skill>` group (live probe, 2026-10-11) and its own config-file schema is undocumented, so it cannot be assumed to carry the brief; `--skill` is the only remaining required-group member that can carry free text (`agent run --help`: 'Use a skill as the base prompt for the agent' when given without `--prompt`), but its search root relative to `--cwd` and behavior when the skill is missing are UNVERIFIED behind the login gate. The pure adapter derives a distinct `aiwg-brief-<invocationId>` path without writes and refuses unsafe IDs. `buildWarpBriefSkillFile()` (tools/ralph-external/lib/warp-adapter.mjs) exports the exact path/content an execution-time file-generation primitive would need to write before this argv runs; that primitive does not exist in the shared session-launcher today (the same class of gap as #2998 for Antigravity's stdin frame). Needed shared change: an execution-time generated-artifact primitive the session-launcher can call with the adapter-exported `{ relativePath, content }` immediately before spawning the worker process, with invocation identity supplied to the adapter, refusal to overwrite an existing path, cleanup afterward, and no commits of generated skills (pending #3002). Brief transport is NOT available today.",
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
- /** Shared binding used by MC and the managed Mission/Flow/Ralph caller paths. */
40
- export async function createMcAdmissionScheduler(projectRoot, cfg, hostRegistry) {
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, validateUserProjectModelConfig, loadProviderModelCapabilities, WorkerPolicyError, assertWorkerRoleEffort, } from '../../models/provider-policy.js';
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