@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,
|
|
@@ -14,6 +14,8 @@ import { createRequire } from 'node:module';
|
|
|
14
14
|
import { compileWorkerVersionPattern, workerVersionRangeMismatch } from './worker-installation.mjs';
|
|
15
15
|
import { normalizeProviderDefinitionId } from '../providers/provider-definitions.js';
|
|
16
16
|
import { qualificationDigest } from './qualification-digest.mjs';
|
|
17
|
+
import { DEFAULT_WORKER_SELECTION, WorkerSelectionPolicySchema, workerModelKey, ownWorkerRoleGuidance } from './worker-selection-policy.js';
|
|
18
|
+
export { DEFAULT_WORKER_SELECTION, WorkerSelectionPolicySchema, mergeWorkerSelection, workerModelKey, isCanonicalWorkerModelKey } from './worker-selection-policy.js';
|
|
17
19
|
const require = createRequire(import.meta.url);
|
|
18
20
|
function requireModelResource(filename) {
|
|
19
21
|
const candidates = [
|
|
@@ -183,156 +185,6 @@ const PolicySchema = z.strictObject({
|
|
|
183
185
|
effort: z.enum(['minimal', 'low', 'medium', 'high', 'xhigh']).optional(),
|
|
184
186
|
override: z.string().min(1).optional(),
|
|
185
187
|
});
|
|
186
|
-
const uniqueStrings = z.array(z.string().min(1)).refine(values => new Set(values).size === values.length, 'Duplicate entries');
|
|
187
|
-
const WorkerDataPolicySchema = z.strictObject({
|
|
188
|
-
allowedProcessing: z.array(z.enum(['local', 'remote'])).refine(values => new Set(values).size === values.length).optional(),
|
|
189
|
-
allowProductImprovement: z.boolean().optional(),
|
|
190
|
-
allowUnknown: z.boolean().optional(),
|
|
191
|
-
maxRetentionDays: integer().nonnegative().optional(),
|
|
192
|
-
});
|
|
193
|
-
const WorkerRoleSchema = z.strictObject({
|
|
194
|
-
groups: uniqueStrings.refine(values => values.length > 0),
|
|
195
|
-
tier: z.enum(['economy', 'standard', 'premium', 'max-quality']),
|
|
196
|
-
initialEffort: z.string().min(1).nullable(),
|
|
197
|
-
effortLadder: uniqueStrings,
|
|
198
|
-
escalateAfter: integer().positive().optional(),
|
|
199
|
-
maxEscalations: integer().nonnegative().optional(),
|
|
200
|
-
deescalateAfter: integer().positive().optional(),
|
|
201
|
-
allowNotApplicableEffort: z.boolean().optional(),
|
|
202
|
-
});
|
|
203
|
-
export const WorkerSelectionPolicySchema = z.strictObject({
|
|
204
|
-
roleGuidance: z.record(z.string().regex(/^[a-z][a-z0-9-]{0,62}$/), WorkerRoleSchema).optional(),
|
|
205
|
-
guards: z.strictObject({
|
|
206
|
-
deniedEfforts: uniqueStrings.optional(),
|
|
207
|
-
deniedModels: uniqueStrings.optional(),
|
|
208
|
-
denySelfDelegation: z.boolean().optional(),
|
|
209
|
-
requireExactModel: z.boolean().optional(),
|
|
210
|
-
allowedVisibility: z.array(z.enum(['listed', 'hidden'])).refine(v => new Set(v).size === v.length).optional(),
|
|
211
|
-
allowedLifecycle: z.array(z.enum(['active', 'deprecated'])).refine(v => new Set(v).size === v.length).optional(),
|
|
212
|
-
minimumIdentityEvidence: z.enum(['runtime-configured', 'backend-attested']).optional(),
|
|
213
|
-
}).optional(),
|
|
214
|
-
dataPolicy: WorkerDataPolicySchema.optional(),
|
|
215
|
-
dataClasses: z.record(z.string().regex(/^[a-z][a-z0-9-]{0,62}$/), WorkerDataPolicySchema).optional(),
|
|
216
|
-
freshness: z.strictObject({
|
|
217
|
-
warnHours: integer().positive().optional(),
|
|
218
|
-
blockHours: integer().positive().optional(),
|
|
219
|
-
policyDays: integer().positive().optional(),
|
|
220
|
-
}).optional(),
|
|
221
|
-
taskClasses: z.record(z.string().regex(/^[a-z][a-z0-9-]{0,62}$/), z.string().min(1)).optional(),
|
|
222
|
-
}).superRefine((policy, context) => {
|
|
223
|
-
for (const [role, guidance] of Object.entries(policy.roleGuidance ?? {})) {
|
|
224
|
-
if (!['efficiency', 'coding', 'reasoning'].includes(role))
|
|
225
|
-
context.addIssue({ code: 'custom', path: ['roleGuidance', role], message: 'Unknown worker role' });
|
|
226
|
-
if (guidance.effortLadder.includes('none') || (guidance.initialEffort === 'none' ? guidance.effortLadder.length !== 0 : guidance.initialEffort !== null && !guidance.effortLadder.includes(guidance.initialEffort))) {
|
|
227
|
-
context.addIssue({ code: 'custom', path: ['roleGuidance', role], message: 'Initial effort must belong to the ladder; real none requires an empty ladder' });
|
|
228
|
-
}
|
|
229
|
-
if (guidance.initialEffort === null && (guidance.allowNotApplicableEffort !== true || guidance.effortLadder.length !== 0)) {
|
|
230
|
-
context.addIssue({ code: 'custom', path: ['roleGuidance', role], message: 'Null effort requires an empty ladder and allowNotApplicableEffort=true' });
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
for (const key of policy.guards?.deniedModels ?? []) {
|
|
234
|
-
if (!isCanonicalWorkerModelKey(key))
|
|
235
|
-
context.addIssue({ code: 'custom', path: ['guards', 'deniedModels'], message: 'Noncanonical inference model key' });
|
|
236
|
-
}
|
|
237
|
-
if (policy.freshness?.warnHours !== undefined && policy.freshness.blockHours !== undefined && policy.freshness.warnHours > policy.freshness.blockHours) {
|
|
238
|
-
context.addIssue({ code: 'custom', path: ['freshness'], message: 'warnHours must not exceed blockHours' });
|
|
239
|
-
}
|
|
240
|
-
for (const role of Object.values(policy.taskClasses ?? {})) {
|
|
241
|
-
if (!['coding', 'reasoning', 'efficiency'].includes(role))
|
|
242
|
-
context.addIssue({ code: 'custom', path: ['taskClasses'], message: 'Unknown worker role' });
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
/** Canonical RFC3986 identity; installation and account never enter the key. */
|
|
246
|
-
export function workerModelKey(inferenceProvider, apiModelId) {
|
|
247
|
-
if (!/^[a-z][a-z0-9-]{0,62}$/.test(inferenceProvider) || !apiModelId)
|
|
248
|
-
throw new Error('OG_CONFIG_REFERENCE: invalid inference model identity');
|
|
249
|
-
return `${inferenceProvider}/${encodeURIComponent(apiModelId).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`;
|
|
250
|
-
}
|
|
251
|
-
export function isCanonicalWorkerModelKey(key) {
|
|
252
|
-
const slash = key.indexOf('/');
|
|
253
|
-
try {
|
|
254
|
-
return slash > 0 && workerModelKey(key.slice(0, slash), decodeURIComponent(key.slice(slash + 1))) === key;
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
export const DEFAULT_WORKER_SELECTION = {
|
|
261
|
-
guards: { deniedEfforts: ['ultra'], deniedModels: [], denySelfDelegation: true, requireExactModel: true, allowedVisibility: ['listed'], allowedLifecycle: ['active'], minimumIdentityEvidence: 'runtime-configured' },
|
|
262
|
-
dataPolicy: { allowedProcessing: ['local', 'remote'], allowProductImprovement: false, allowUnknown: false },
|
|
263
|
-
freshness: { warnHours: 24, blockHours: 168, policyDays: 30 },
|
|
264
|
-
taskClasses: { inventory: 'efficiency', implementation: 'coding', architecture: 'reasoning', review: 'reasoning' },
|
|
265
|
-
};
|
|
266
|
-
/** Merge leaves, preserving pre-merge explicit origins and attempted relaxations. */
|
|
267
|
-
export function mergeWorkerSelection(layers) {
|
|
268
|
-
const policy = {};
|
|
269
|
-
const origins = {};
|
|
270
|
-
const diagnostics = [];
|
|
271
|
-
const visit = (target, input, parts, layer) => {
|
|
272
|
-
for (const [key, value] of Object.entries(input)) {
|
|
273
|
-
const path = [...parts, key];
|
|
274
|
-
const pointer = `/defaults/workerSelection/${path.map(p => p.replace(/~/g, '~0').replace(/\//g, '~1')).join('/')}`;
|
|
275
|
-
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
276
|
-
target[key] ??= {};
|
|
277
|
-
visit(target[key], value, path, layer);
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
const origin = { source: layer.source, authority: layer.authority, digest: layer.digest, pointer, contribution: structuredClone(value) };
|
|
281
|
-
const prior = origins[pointer] ?? [];
|
|
282
|
-
const explicit = prior.filter(o => o.authority !== 'builtin');
|
|
283
|
-
let effective = structuredClone(value);
|
|
284
|
-
let relaxation = false;
|
|
285
|
-
if (layer.authority !== 'builtin' && explicit.length > 0) {
|
|
286
|
-
const previous = target[key];
|
|
287
|
-
if (['deniedEfforts', 'deniedModels'].includes(key))
|
|
288
|
-
effective = [...new Set([...previous, ...value])];
|
|
289
|
-
else if (['allowedVisibility', 'allowedLifecycle', 'allowedProcessing'].includes(key)) {
|
|
290
|
-
relaxation = value.some((v) => !previous.includes(v));
|
|
291
|
-
effective = previous.filter((v) => value.includes(v));
|
|
292
|
-
}
|
|
293
|
-
else if (['denySelfDelegation', 'requireExactModel'].includes(key)) {
|
|
294
|
-
relaxation = previous === true && value === false;
|
|
295
|
-
effective = previous || value;
|
|
296
|
-
}
|
|
297
|
-
else if (['allowUnknown', 'allowProductImprovement'].includes(key)) {
|
|
298
|
-
relaxation = previous === false && value === true;
|
|
299
|
-
effective = previous && value;
|
|
300
|
-
}
|
|
301
|
-
else if (['maxRetentionDays', 'warnHours', 'blockHours', 'policyDays'].includes(key)) {
|
|
302
|
-
relaxation = value > previous;
|
|
303
|
-
effective = Math.min(previous, value);
|
|
304
|
-
}
|
|
305
|
-
else if (key === 'minimumIdentityEvidence') {
|
|
306
|
-
relaxation = previous === 'backend-attested' && value !== previous;
|
|
307
|
-
effective = previous === 'backend-attested' ? previous : value;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
if (relaxation)
|
|
311
|
-
diagnostics.push({ code: 'OG_POLICY_RELAXATION', pointer, message: 'Explicit restriction cannot relax an inherited restriction', origins: [...explicit, origin] });
|
|
312
|
-
target[key] = effective;
|
|
313
|
-
origins[pointer] = [...prior, origin];
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
for (const layer of layers)
|
|
317
|
-
if (layer.policy)
|
|
318
|
-
visit(policy, layer.policy, [], layer);
|
|
319
|
-
const validation = WorkerSelectionPolicySchema.safeParse(policy);
|
|
320
|
-
if (!validation.success)
|
|
321
|
-
for (const issue of validation.error.issues)
|
|
322
|
-
diagnostics.push({ code: 'OG_CONFIG_REFERENCE', pointer: `/defaults/workerSelection/${issue.path.join('/')}`, message: issue.message });
|
|
323
|
-
// Data classes are applied as further restrictions to the base path.
|
|
324
|
-
for (const dataClass of Object.values(policy.dataClasses ?? {})) {
|
|
325
|
-
const base = policy.dataPolicy ?? {};
|
|
326
|
-
if (dataClass.allowedProcessing)
|
|
327
|
-
dataClass.allowedProcessing = dataClass.allowedProcessing.filter(p => !base.allowedProcessing || base.allowedProcessing.includes(p));
|
|
328
|
-
for (const key of ['allowUnknown', 'allowProductImprovement'])
|
|
329
|
-
if (base[key] === false)
|
|
330
|
-
dataClass[key] = false;
|
|
331
|
-
if (base.maxRetentionDays !== undefined)
|
|
332
|
-
dataClass.maxRetentionDays = Math.min(dataClass.maxRetentionDays ?? base.maxRetentionDays, base.maxRetentionDays);
|
|
333
|
-
}
|
|
334
|
-
return { policy, origins, diagnostics };
|
|
335
|
-
}
|
|
336
188
|
const UserProjectModelConfigSchema = z.looseObject({
|
|
337
189
|
defaults: z.strictObject({
|
|
338
190
|
tier: z.enum(['economy', 'standard', 'premium', 'max-quality']).optional(),
|
|
@@ -435,7 +287,7 @@ export class WorkerRoleEffortError extends WorkerPolicyError {
|
|
|
435
287
|
/** Validate policy input before entering the sanitized adapter boundary. Null
|
|
436
288
|
* remains valid for models whose effort is explicitly not applicable. */
|
|
437
289
|
export function assertWorkerRoleEffort(policy, role, requiresExplicit = false) {
|
|
438
|
-
const effort = policy
|
|
290
|
+
const effort = ownWorkerRoleGuidance(policy, role)?.initialEffort;
|
|
439
291
|
if (effort === undefined || requiresExplicit && (effort === null || !effort.trim()))
|
|
440
292
|
throw new WorkerRoleEffortError(role);
|
|
441
293
|
}
|
|
@@ -459,7 +311,7 @@ export function assertWorkerSelectionAllowed(selected, context) {
|
|
|
459
311
|
const observation = context.modelObservation ?? context.defaultObservation;
|
|
460
312
|
if (!observation)
|
|
461
313
|
throw new WorkerPolicyError('OG_MODEL_UNAPPROVED', 'Worker invocation requires model or default observation evidence');
|
|
462
|
-
const guidance = context.role ? policy
|
|
314
|
+
const guidance = context.role ? ownWorkerRoleGuidance(policy, context.role) : undefined;
|
|
463
315
|
if (isWorkerNullEffortDenied(selected.effort.value, observation, guidance)) {
|
|
464
316
|
throw new WorkerPolicyError('OG_EFFORT_DENIED', 'Worker invocation denied effort: null effort requires qualified absence of an effort control');
|
|
465
317
|
}
|
|
@@ -907,7 +759,7 @@ function isWorkerNullEffortDenied(effort, observation, guidance) {
|
|
|
907
759
|
}
|
|
908
760
|
function workerRouteDataPolicy(policy, dataClass) {
|
|
909
761
|
const data = { ...DEFAULT_WORKER_SELECTION.dataPolicy, ...policy.dataPolicy };
|
|
910
|
-
const narrowed = dataClass ? policy.dataClasses
|
|
762
|
+
const narrowed = dataClass && policy.dataClasses && Object.hasOwn(policy.dataClasses, dataClass) ? policy.dataClasses[dataClass] : undefined;
|
|
911
763
|
if (narrowed) {
|
|
912
764
|
if (narrowed.allowedProcessing)
|
|
913
765
|
data.allowedProcessing = data.allowedProcessing?.filter(p => narrowed.allowedProcessing.includes(p));
|
|
@@ -956,8 +808,8 @@ export function validateWorkerPolicyRoute(input) {
|
|
|
956
808
|
message: `Conflicting optional model evidence remains unknown: ${observation.conflicts.map(conflict => conflict.field).join(', ')}`,
|
|
957
809
|
});
|
|
958
810
|
const guards = input.policy.guards ?? DEFAULT_WORKER_SELECTION.guards;
|
|
959
|
-
const guidance = input.policy
|
|
960
|
-
if (observation.qualification.status !== 'qualified' || !observation.qualification.roles.includes(input.role))
|
|
811
|
+
const guidance = ownWorkerRoleGuidance(input.policy, input.role);
|
|
812
|
+
if (observation.qualification.status !== 'qualified' || !observation.qualification.roles.includes(guidance?.baseRole ?? input.role))
|
|
961
813
|
deny('OG_ADAPTER_UNQUALIFIED', '/worker/qualification', 'Role lacks qualified evidence. Run aiwg doctor --probe-workers --qualify.');
|
|
962
814
|
if (!guidance)
|
|
963
815
|
deny('OG_CONFIG_REFERENCE', `/defaults/workerSelection/roleGuidance/${input.role}`, 'Role guidance is unavailable');
|