@aiwg/cli 2026.10.10 → 2026.10.12

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 (156) hide show
  1. package/agentic/code/providers/model-capabilities.v1.json +78 -14
  2. package/agentic/code/providers/model-catalog.v1.json +62 -12
  3. package/dist/src/artifacts/binding-target-identity.js +51 -0
  4. package/dist/src/artifacts/checksum-manifest.js +12 -0
  5. package/dist/src/artifacts/index-builder.js +11 -13
  6. package/dist/src/artifacts/query-engine.js +5 -11
  7. package/dist/src/cli/handlers/doctor-help.js +32 -0
  8. package/dist/src/cli/handlers/gates.js +19 -18
  9. package/dist/src/cli/handlers/models-help.js +7 -1
  10. package/dist/src/cli/handlers/models.js +3 -3
  11. package/dist/src/cli/handlers/script-runner.js +8 -5
  12. package/dist/src/cli/handlers/sessions-intake.js +104 -0
  13. package/dist/src/cli/handlers/sessions-tail.js +172 -0
  14. package/dist/src/cli/handlers/sessions.js +287 -31
  15. package/dist/src/cli/handlers/skill-lint.js +10 -3
  16. package/dist/src/cli/handlers/steward-format.js +100 -0
  17. package/dist/src/cli/handlers/steward-models.js +167 -0
  18. package/dist/src/cli/handlers/steward.js +14 -248
  19. package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
  20. package/dist/src/cli/handlers/use.js +18 -28
  21. package/dist/src/cli/handlers/utilities.js +1 -24
  22. package/dist/src/config/aiwg-config.js +7 -0
  23. package/dist/src/config/authorization-cli.js +89 -0
  24. package/dist/src/config/cli-help.js +1 -0
  25. package/dist/src/config/cli.js +10 -1
  26. package/dist/src/config/worker-context.js +337 -0
  27. package/dist/src/decision/context-pruning.js +3 -2
  28. package/dist/src/decision/ensemble/contract.js +15 -4
  29. package/dist/src/decision/ensemble-study/artifacts.js +5 -0
  30. package/dist/src/decision/ensemble-study/score.js +4 -3
  31. package/dist/src/decision/feature-export.js +3 -9
  32. package/dist/src/decision/heldout/collector.js +2 -2
  33. package/dist/src/decision/issue-triage/runtime.js +3 -9
  34. package/dist/src/decision/qualification/gates.js +2 -1
  35. package/dist/src/decision/qualification/release.js +2 -3
  36. package/dist/src/decision/routing/report.js +2 -0
  37. package/dist/src/decision/sdlc-screening.js +3 -5
  38. package/dist/src/decision/sensitivity/comparative.js +5 -3
  39. package/dist/src/gates/driver.js +6 -77
  40. package/dist/src/gates/evaluate.js +4 -11
  41. package/dist/src/gates/floors.js +4 -4
  42. package/dist/src/gates/index.js +3 -3
  43. package/dist/src/gates/lattice.js +26 -0
  44. package/dist/src/gates/registry.js +2 -1
  45. package/dist/src/gates/report.js +19 -7
  46. package/dist/src/issues/audit-cli.js +120 -0
  47. package/dist/src/issues/audit-contract.js +64 -0
  48. package/dist/src/issues/audit-local.js +82 -0
  49. package/dist/src/issues/audit.js +220 -0
  50. package/dist/src/lint/rule-activation.js +15 -1
  51. package/dist/src/mcp/tools/worker-invoke.mjs +108 -10
  52. package/dist/src/mission-protocol/canonical-codecs.js +270 -0
  53. package/dist/src/mission-protocol/codecs.js +22 -550
  54. package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
  55. package/dist/src/mission-protocol/health-trace-types.js +43 -0
  56. package/dist/src/mission-protocol/types.js +2 -42
  57. package/dist/src/mission-protocol/worker-contract-json.js +10 -0
  58. package/dist/src/mission-protocol/worker-review-gate.js +100 -0
  59. package/dist/src/mission-protocol/worker-review-types.js +2 -0
  60. package/dist/src/models/catalog-policy.js +280 -0
  61. package/dist/src/models/index.js +1 -1
  62. package/dist/src/models/model-capabilities.v1.json +78 -14
  63. package/dist/src/models/model-catalog.v1.json +62 -12
  64. package/dist/src/models/policy-resources.js +28 -0
  65. package/dist/src/models/provider-policy.js +10 -539
  66. package/dist/src/models/resolver.js +26 -8
  67. package/dist/src/models/router.js +15 -2
  68. package/dist/src/models/tiers.js +13 -0
  69. package/dist/src/models/types.js +0 -6
  70. package/dist/src/models/worker-ceilings.js +59 -0
  71. package/dist/src/models/worker-invocation-policy.js +243 -0
  72. package/dist/src/models/worker-plan.js +16 -20
  73. package/dist/src/models/worker-review-lineage.js +55 -0
  74. package/dist/src/models/worker-selection-policy.js +1 -1
  75. package/dist/src/models/wrapper-route.js +6 -3
  76. package/dist/src/policy/authorization.js +30 -1
  77. package/dist/src/policy/hitl-config.js +63 -0
  78. package/dist/src/policy/hitl-receipt.js +57 -0
  79. package/dist/src/policy/hitl-types.js +4 -0
  80. package/dist/src/policy/hitl-validation.js +39 -0
  81. package/dist/src/policy/hitl.js +117 -0
  82. package/dist/src/process/aiwg-probe-command.d.mts +15 -0
  83. package/dist/src/process/aiwg-probe-command.mjs +37 -0
  84. package/dist/src/rules/activation.d.mts +1 -1
  85. package/dist/src/rules/activation.mjs +0 -2
  86. package/dist/src/rules/gate-coverage.js +51 -0
  87. package/dist/src/rules/gate-references.js +16 -0
  88. package/dist/src/rules/rule-frontmatter.schema.json +7 -1
  89. package/dist/src/serve/worker-command-request.js +21 -4
  90. package/dist/src/serve/worker-invocation-preparation.js +3 -1
  91. package/dist/src/serve/worker-invocation.js +21 -10
  92. package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
  93. package/dist/src/sessions/adapters/grok-build-native.js +269 -0
  94. package/dist/src/sessions/adapters/grok-build.js +57 -15
  95. package/dist/src/sessions/adapters/grokbot.js +7 -0
  96. package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
  97. package/dist/src/sessions/adapters/hermes.js +42 -3
  98. package/dist/src/sessions/adapters/warp.js +1 -1
  99. package/dist/src/sessions/batch-import.js +44 -6
  100. package/dist/src/sessions/contracts.js +20 -1
  101. package/dist/src/sessions/coverage-preflight.js +7 -1
  102. package/dist/src/sessions/discovery-capability.js +38 -0
  103. package/dist/src/sessions/fortemi-export-mapping.js +33 -3
  104. package/dist/src/sessions/host-catalog.js +157 -0
  105. package/dist/src/sessions/image-blobs.js +141 -0
  106. package/dist/src/sessions/importer.js +9 -232
  107. package/dist/src/sessions/index.js +6 -0
  108. package/dist/src/sessions/intake-dir.js +71 -0
  109. package/dist/src/sessions/normalization.js +251 -0
  110. package/dist/src/sessions/refresh.js +25 -2
  111. package/dist/src/sessions/repository.js +164 -22
  112. package/dist/src/sessions/tail.js +91 -0
  113. package/dist/src/sessions/workspace-discovery.js +88 -9
  114. package/dist/src/skills/agent-skill-sidecar.js +28 -0
  115. package/dist/src/skills/agent-skills.js +4 -23
  116. package/dist/src/skills/claude-surface-projection.js +25 -0
  117. package/dist/src/skills/deployer.js +8 -288
  118. package/dist/src/skills/deployment-policy.js +114 -0
  119. package/dist/src/skills/deployment-projection.js +152 -0
  120. package/dist/src/skills/deployment-types.js +20 -0
  121. package/dist/src/skills/exporter.js +8 -31
  122. package/dist/src/skills/provider-extensions.js +41 -0
  123. package/dist/src/skills/run.js +6 -0
  124. package/dist/src/skills/types.js +0 -8
  125. package/dist/src/skills/validator-resources.js +68 -0
  126. package/dist/src/skills/validator.js +6 -55
  127. package/dist/src/storage/cli-migrate-options.js +125 -0
  128. package/dist/src/storage/cli-migrate.js +111 -0
  129. package/dist/src/storage/cli.js +4 -279
  130. package/dist/src/storage/migration-adapter.js +79 -0
  131. package/dist/src/storage/migration-copy.js +108 -0
  132. package/dist/src/storage/migration-protocol.js +14 -197
  133. package/dist/src/storage/migration-records.js +186 -0
  134. package/dist/src/storage/migration-types.js +12 -0
  135. package/dist/src/tracker/forge-http.js +2 -1
  136. package/dist/src/tracker/issue-pages.js +110 -0
  137. package/package.json +7 -1
  138. package/schemas/mission-v1.schema.json +335 -0
  139. package/schemas/models/model-catalog.v1.schema.json +19 -1
  140. package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
  141. package/schemas/models/model-policy.v1.schema.json +1 -1
  142. package/tools/agents/providers/provider-paths-staging.mjs +35 -0
  143. package/tools/agents/providers/rule-binding-targets.mjs +146 -0
  144. package/tools/agents/providers/rule-delivery.mjs +7 -126
  145. package/tools/ralph-external/lib/muse-adapter.mjs +10 -0
  146. package/tools/ralph-external/lib/worker-context.mjs +237 -0
  147. package/tools/ralph-external/lib/worker-invocation-ancestry.mjs +132 -0
  148. package/tools/ralph-external/lib/worker-invocation-processes.mjs +123 -0
  149. package/tools/ralph-external/lib/worker-isolation-status.mjs +48 -0
  150. package/tools/ralph-external/lib/worker-isolation.mjs +17 -29
  151. package/tools/ralph-external/lib/worker-launch-cli.mjs +3 -1
  152. package/tools/ralph-external/lib/worker-overflow.mjs +48 -13
  153. package/tools/ralph-external/lib/worker-retry-evidence.mjs +103 -0
  154. package/tools/ralph-external/lib/worker-termination.mjs +82 -0
  155. package/tools/ralph-external/session-launcher.mjs +51 -96
  156. package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
@@ -5,6 +5,8 @@
5
5
  * @implements @.aiwg/architecture/ADR-015-enhanced-model-selection.md
6
6
  * @architecture @.aiwg/architecture/enhanced-model-selection-design.md
7
7
  */
8
+ import { normalizeModelTier, TIER_ORDER, flagshipElected, isFlagshipModel } from './tiers.js';
9
+ import { compileModelPolicy } from './provider-policy.js';
8
10
  import { ConfigLoader } from './config-loader.js';
9
11
  import { resolveWorkerPlan } from './worker-plan.js';
10
12
  export * from './worker-plan.js';
@@ -39,23 +41,39 @@ export class ModelResolver {
39
41
  const config = await this.getConfig();
40
42
  // Determine effective tier and role
41
43
  const { tier, role, source } = this.determineEffectiveTierAndRole(agentName, agentRole, agentTier, config);
44
+ if (normalizeModelTier(tier) === 'flagship' && !flagshipElected({ ...this.options, agentName })) {
45
+ throw new Error('Flagship models are user-elected only');
46
+ }
42
47
  // Check for direct model override (bypasses tier system)
43
48
  const override = config.agentOverrides[agentName];
44
49
  if (override?.['model-override']) {
50
+ if (isFlagshipModel(override['model-override']) && !flagshipElected({ ...this.options, agentName }))
51
+ throw new Error('Flagship models are user-elected only');
45
52
  return {
46
53
  modelId: override['model-override'],
47
54
  provider: this.options.provider || config.defaults.provider,
48
- tier,
55
+ tier: normalizeModelTier(tier),
49
56
  role,
50
57
  source: 'agent',
51
58
  };
52
59
  }
53
- // Resolve tier + role to concrete model
54
- const modelId = this.resolveModelId(tier, role, config);
60
+ const provider = this.options.provider || config.defaults.provider;
61
+ const modelId = normalizeModelTier(tier) === 'flagship'
62
+ ? compileModelPolicy({
63
+ provider: provider === 'openai' ? 'codex' : provider,
64
+ artifact: 'agent', policy: { tier: 'flagship', role },
65
+ election: this.options.election, flagshipAllowlist: this.options.flagshipAllowlist, agentName,
66
+ }).effectiveModel
67
+ : this.resolveModelId(tier, role, config);
68
+ if (!modelId)
69
+ throw new Error(`No supported model binding for ${provider}/${tier}`);
70
+ if (isFlagshipModel(modelId) && !flagshipElected({ ...this.options, agentName })) {
71
+ throw new Error('Flagship models are user-elected only');
72
+ }
55
73
  return {
56
74
  modelId,
57
75
  provider: this.options.provider || config.defaults.provider,
58
- tier,
76
+ tier: normalizeModelTier(tier),
59
77
  role,
60
78
  source,
61
79
  };
@@ -132,9 +150,9 @@ export class ModelResolver {
132
150
  * Return the higher of two tiers
133
151
  */
134
152
  maxTier(tier1, tier2) {
135
- const tierOrder = ['economy', 'standard', 'premium', 'max-quality'];
136
- const index1 = tierOrder.indexOf(tier1);
137
- const index2 = tierOrder.indexOf(tier2);
153
+ const tierOrder = TIER_ORDER;
154
+ const index1 = tierOrder.indexOf(normalizeModelTier(tier1));
155
+ const index2 = tierOrder.indexOf(normalizeModelTier(tier2));
138
156
  return tierOrder[Math.max(index1, index2)];
139
157
  }
140
158
  /**
@@ -227,7 +245,7 @@ export class ModelResolver {
227
245
  async listTiers() {
228
246
  const config = await this.getConfig();
229
247
  return Object.entries(config.tiers).map(([tierName, tierDef]) => ({
230
- tier: tierName,
248
+ tier: normalizeModelTier(tierName),
231
249
  description: tierDef.description,
232
250
  costMultiplier: tierDef.costMultiplier,
233
251
  }));
@@ -8,14 +8,16 @@
8
8
  *
9
9
  * @implements #1185
10
10
  */
11
+ import { flagshipElected, normalizeModelTier } from './tiers.js';
11
12
  const RUNTIME_TO_MODEL_TIER = {
12
13
  0: null,
13
14
  1: 'economy',
14
15
  2: 'standard',
15
16
  3: 'premium',
17
+ 4: 'flagship',
16
18
  };
17
19
  function clampRuntimeTier(tier, fallback) {
18
- return tier === 0 || tier === 1 || tier === 2 || tier === 3 ? tier : fallback;
20
+ return tier === 0 || tier === 1 || tier === 2 || tier === 3 || tier === 4 ? tier : fallback;
19
21
  }
20
22
  export function routeModelTier(request = {}) {
21
23
  const defaultTier = clampRuntimeTier(request.defaultTier, 1);
@@ -28,6 +30,11 @@ export function routeModelTier(request = {}) {
28
30
  source = 'deterministic';
29
31
  rationale.push('deterministic tool/cache/existing answer is sufficient');
30
32
  }
33
+ else if (request.requestedTier) {
34
+ tier = { economy: 1, standard: 2, premium: 3, flagship: 4 }[normalizeModelTier(request.requestedTier)];
35
+ source = 'request';
36
+ rationale.push('explicit tier request');
37
+ }
31
38
  else if (request.requestedPremium) {
32
39
  tier = 3;
33
40
  source = 'request';
@@ -51,7 +58,13 @@ export function routeModelTier(request = {}) {
51
58
  else {
52
59
  rationale.push('no escalation signal present');
53
60
  }
54
- const requiresConfirmation = (tier === 3 && !request.premiumAuthorized) || tier > maxAutoTier;
61
+ const elected = flagshipElected(request);
62
+ if (tier === 4 && !elected) {
63
+ tier = 3;
64
+ source = 'policy';
65
+ rationale.push('flagship is user-elected only; capped at premium');
66
+ }
67
+ const requiresConfirmation = tier === 4 ? false : (tier === 3 && !request.premiumAuthorized) || tier > maxAutoTier;
55
68
  if (requiresConfirmation) {
56
69
  rationale.push(tier === 3
57
70
  ? 'Tier 3 requires explicit human confirmation'
@@ -0,0 +1,13 @@
1
+ export const MODEL_TIERS = ['economy', 'standard', 'premium', 'flagship', 'max-quality'];
2
+ export const TIER_ORDER = ['economy', 'standard', 'premium', 'flagship'];
3
+ export function normalizeModelTier(tier) {
4
+ return tier === 'max-quality' ? 'flagship' : tier;
5
+ }
6
+ export function flagshipElected(request) {
7
+ return request.election === 'user'
8
+ || Boolean(request.agentName && request.flagshipAllowlist?.includes(request.agentName));
9
+ }
10
+ export function isFlagshipModel(id) {
11
+ return /(?:^|[/:._-])(?:fable|astra)(?:$|[/:._-]|\d)/i.test(id);
12
+ }
13
+ //# sourceMappingURL=tiers.js.map
@@ -1,8 +1,2 @@
1
- /**
2
- * TypeScript types for Enhanced Model Selection System (Phase 1)
3
- *
4
- * @implements @.aiwg/architecture/ADR-015-enhanced-model-selection.md
5
- * @architecture @.aiwg/architecture/enhanced-model-selection-design.md
6
- */
7
1
  export {};
8
2
  //# sourceMappingURL=types.js.map
@@ -0,0 +1,59 @@
1
+ import { workerConfigDigest } from '../config/worker-config-digest.js';
2
+ export const WORKER_CEILING_KEYS = ['tokens', 'costUsd', 'wallSeconds', 'maxAttempts'];
3
+ /** Parent/brief restrictions intersect; they never replace a stricter restriction. */
4
+ export function intersectWorkerCeilings(...layers) {
5
+ const result = {};
6
+ for (const key of WORKER_CEILING_KEYS) {
7
+ const values = layers.map(layer => layer?.[key]).filter((value) => value !== undefined);
8
+ if (values.length)
9
+ result[key] = Math.min(...values);
10
+ }
11
+ return result;
12
+ }
13
+ export function resolveWorkerCeilings(parallelism, constraints, request, defaults = {}) {
14
+ const layers = [
15
+ { values: defaults, pointer: 'defaults/invocation', source: 'builtin', authority: 'builtin' },
16
+ { values: parallelism.budgets?.perRun ?? {}, pointer: '/parallelism/budgets/perRun', source: 'config', authority: 'project' },
17
+ { values: parallelism.budgets?.perTask ?? {}, pointer: '/parallelism/budgets/perTask', source: 'config', authority: 'project' },
18
+ { values: { maxAttempts: parallelism.retry?.maxAttempts ?? 3 },
19
+ pointer: parallelism.retry?.maxAttempts === undefined ? 'defaults/parallelism/retry' : '/parallelism/retry',
20
+ source: parallelism.retry?.maxAttempts === undefined ? 'builtin' : 'config',
21
+ authority: parallelism.retry?.maxAttempts === undefined ? 'builtin' : 'project' },
22
+ { values: constraints ?? {}, pointer: '/constraints/ceilings', source: 'constraints', authority: 'project' },
23
+ ];
24
+ const ceilings = {};
25
+ const origins = {};
26
+ const diagnostics = [];
27
+ for (const key of WORKER_CEILING_KEYS) {
28
+ const entries = layers.map(layer => ({ ...layer, value: layer.values[key], pointer: `${layer.pointer}/${key}` }));
29
+ if (key === 'wallSeconds' && parallelism.health?.hardTimeoutSeconds !== undefined)
30
+ entries.push({
31
+ values: { wallSeconds: parallelism.health.hardTimeoutSeconds }, value: parallelism.health.hardTimeoutSeconds,
32
+ pointer: '/parallelism/health/hardTimeoutSeconds', source: 'config', authority: 'project',
33
+ });
34
+ const caller = request?.[key];
35
+ const values = [...entries.map(entry => entry.value), caller].filter((value) => value !== undefined);
36
+ const pointer = `/request/ceilings/${key}`;
37
+ if (values.some(value => !Number.isFinite(value) || value < 0 || key !== 'costUsd' && !Number.isSafeInteger(value))) {
38
+ diagnostics.push({ code: 'OG_CONFIG_REFERENCE', pointer,
39
+ message: 'Ceilings must be finite nonnegative values; counts and seconds must be integers' });
40
+ continue;
41
+ }
42
+ const resolved = Math.min(...entries.map(entry => entry.value ?? Infinity));
43
+ if (caller !== undefined && caller > resolved)
44
+ diagnostics.push({ code: 'OG_POLICY_RELAXATION', pointer,
45
+ message: `Requested ${key} ceiling ${caller} exceeds resolved policy ceiling ${resolved}.` });
46
+ if (!values.length)
47
+ continue;
48
+ ceilings[key] = Math.min(...values);
49
+ if (caller !== undefined)
50
+ entries.push({ values: request, value: caller, pointer,
51
+ source: 'request', authority: 'project' });
52
+ origins[`/ceilings/${key}`] = entries.filter(entry => entry.value === ceilings[key]).map(entry => ({
53
+ source: entry.source, authority: entry.authority, pointer: entry.pointer,
54
+ digest: workerConfigDigest(entry.values), contribution: entry.value,
55
+ }));
56
+ }
57
+ return { ceilings, origins, diagnostics };
58
+ }
59
+ //# sourceMappingURL=worker-ceilings.js.map
@@ -0,0 +1,243 @@
1
+ // Checks invocation controls, native sandbox rendering and scoped worker health.
2
+ import { WorkerAdapterSchema, WorkerPolicyError } from './provider-policy.js';
3
+ import { normalizeProviderDefinitionId } from '../providers/provider-definitions.js';
4
+ import { workerVersionRangeMismatch } from './worker-installation.mjs';
5
+ import { workerModelKey } from './worker-selection-policy.js';
6
+ import { qualificationDigest } from './qualification-digest.mjs';
7
+ const WORKER_CONTROLS = ['model', 'effort', 'sandbox', 'approval', 'workdir',
8
+ 'finalOutput', 'stdin', 'structuredOutput', 'stepCap', 'trust', 'children', 'fallback'];
9
+ /** Caller controls only add restrictions. Absent resolved authority, require all controls. */
10
+ export function requiredWorkerControls(context) {
11
+ if (context.requiredControls !== undefined && (!Array.isArray(context.requiredControls)
12
+ || context.requiredControls.some(name => typeof name !== 'string' || !name))) {
13
+ throw new Error('Worker required controls must be an array of control names');
14
+ }
15
+ const controls = new Set(WORKER_CONTROLS);
16
+ if (context.policy && context.permissions) {
17
+ if (context.effort === null)
18
+ controls.delete('effort');
19
+ // Step limits are observed unless explicitly required by the trusted route.
20
+ controls.delete('stepCap');
21
+ if (context.policy.guards?.denySelfDelegation === false)
22
+ controls.delete('children');
23
+ }
24
+ for (const name of context.requiredControls ?? [])
25
+ controls.add(name);
26
+ return [...controls];
27
+ }
28
+ /** The same sandbox requirement governs inspection, resolution and rendering. */
29
+ export function requiresWorkerExternalBoundary(adapter, selection) {
30
+ if (hasOperatorWorkerNativeRenderer(adapter)
31
+ && (selection.permissions?.sandbox !== 'external' || selection.transport === 'shell'))
32
+ return false;
33
+ const providers = [adapter.id, adapter.provider].map(normalizeProviderDefinitionId);
34
+ return requiredWorkerControls(selection).includes('sandbox')
35
+ && (providers.some(provider => provider && ['claude', 'muse', 'hermes'].includes(provider))
36
+ || adapter.invocationProfiles?.[selection.profile]?.controls.sandbox?.support === 'external'
37
+ || selection.permissions?.sandbox === 'external');
38
+ }
39
+ /** Explicit local operator approval for the direct CLI baseline (decision 17).
40
+ * This is not backend attestation or a live negative-fixture result. */
41
+ export const WORKER_OPERATOR_APPROVAL = 'aiwg doctor --probe-workers --qualify';
42
+ export function isOperatorQualifiedWorkerAdapter(adapter) {
43
+ return adapter.qualification.status === 'qualified'
44
+ && adapter.qualification.evidenceRef === WORKER_OPERATOR_APPROVAL;
45
+ }
46
+ /** Bundled renderers with an independently checked native direct baseline. */
47
+ export function hasOperatorWorkerNativeRenderer(adapter) {
48
+ return isOperatorQualifiedWorkerAdapter(adapter) && ['codex', 'muse'].includes(adapter.provider)
49
+ && adapter.invocation.kind === 'process';
50
+ }
51
+ /** The process-group launch boundary requires the independently verified native renderer. */
52
+ export function assertWorkerLaunchMode(adapter, permissions = {}) {
53
+ const mode = permissions.launchMode ?? 'systemd-unit';
54
+ if (!['systemd-unit', 'native-sandbox'].includes(mode)) {
55
+ throw new WorkerPolicyError('OG_CONFIG_REFERENCE', 'Unknown worker launch mode');
56
+ }
57
+ if (mode === 'native-sandbox' && (!hasOperatorWorkerNativeRenderer(adapter)
58
+ || !['read-only', 'workspace-write'].includes(permissions.sandbox ?? '')
59
+ || (permissions.transport ?? 'shell') !== 'shell')) {
60
+ throw new WorkerPolicyError('OG_POLICY_RELAXATION', 'native-sandbox requires a verified native read-only or workspace-write shell renderer');
61
+ }
62
+ }
63
+ /** Check the actual argv before granting the direct renderer exception. */
64
+ export function assertOperatorWorkerNativeArgv(adapter, argv, selected, permissions) {
65
+ if (!hasOperatorWorkerNativeRenderer(adapter))
66
+ return;
67
+ const args = argv.slice(1);
68
+ const requirePair = (flag, value) => {
69
+ if (args.filter(token => token === flag).length !== 1 || args[args.indexOf(flag) + 1] !== value) {
70
+ throw new Error(`Requested native worker control is absent or ambiguous: ${flag}`);
71
+ }
72
+ };
73
+ const sandbox = permissions?.sandbox ?? 'workspace-write';
74
+ if (!['read-only', 'workspace-write', 'external'].includes(sandbox))
75
+ throw new Error('Requested sandbox has no verified native renderer');
76
+ if (adapter.provider === 'muse') {
77
+ requirePair('--provider', 'meta');
78
+ requirePair('--model', selected.model);
79
+ if (selected.effort.value === null)
80
+ throw new Error('Muse requires explicit effort');
81
+ requirePair('--reasoning-effort', selected.effort.value);
82
+ if ((permissions?.approval ?? 'never') !== 'never')
83
+ throw new Error('Muse direct worker approval requires never');
84
+ requirePair('--approval-mode', 'never');
85
+ requirePair('--approval-judge', 'off');
86
+ // #3685: Tier 0 Muse workers run in conductor-prepared worker trees, so
87
+ // project context must load. An argv without exactly one
88
+ // --trust-workspace would run with project_sources=0 (no project
89
+ // rules/skills/hooks) and is refused rather than silently degraded.
90
+ if (args.filter(token => token === '--trust-workspace').length !== 1) {
91
+ throw new Error('Requested native worker control is absent or ambiguous: --trust-workspace');
92
+ }
93
+ if (args.includes('--disable-sandbox') !== (sandbox === 'external')) {
94
+ throw new Error('Requested native worker sandbox control does not match');
95
+ }
96
+ if (sandbox === 'read-only' && (!args.includes('--disable-write') || !args.includes('--disable-shell'))) {
97
+ throw new Error('Requested native worker read-only control is absent');
98
+ }
99
+ if (args[0] !== 'exec')
100
+ throw new Error('Native worker controls could be overridden');
101
+ const seen = new Set();
102
+ for (let index = 1; index < args.length; index++) {
103
+ const flag = args[index];
104
+ if (seen.has(flag))
105
+ throw new Error('Native worker controls could be overridden');
106
+ seen.add(flag);
107
+ if (['--json', '--trust-workspace', '--disable-write', '--disable-shell', '--disable-sandbox'].includes(flag))
108
+ continue;
109
+ if (!['--provider', '--model', '--reasoning-effort', '--workspace', '--approval-mode', '--approval-judge',
110
+ '--max-model-steps', '--output-schema', '--prompt-file'].includes(flag) || index + 1 >= args.length) {
111
+ throw new Error('Native worker controls could be overridden');
112
+ }
113
+ index++;
114
+ }
115
+ return;
116
+ }
117
+ requirePair('-m', selected.model);
118
+ requirePair('-s', sandbox === 'external' ? 'danger-full-access' : sandbox);
119
+ if ((permissions?.approval ?? 'never') !== 'never')
120
+ throw new Error('Codex direct worker approval requires never');
121
+ for (const [name, value] of [['model_reasoning_effort', selected.effort.value], ['approval_policy', permissions?.approval ?? 'never']]) {
122
+ const overrides = args.flatMap((token, index) => token === '-c' ? [args[index + 1]] : [])
123
+ .filter(token => token?.split('=')[0] === name);
124
+ if (value === null || overrides.length !== 1 || overrides[0] !== `${name}=${JSON.stringify(value)}`) {
125
+ throw new Error(`Requested native worker control is absent or ambiguous: ${name}`);
126
+ }
127
+ }
128
+ // Permit only the bounded native command grammar. Alternate/combined flags,
129
+ // profiles and additional config overrides must not undo the checked pairs.
130
+ if (args[0] !== '--no-daemon' || args[1] !== 'exec' || !args.includes('--ignore-user-config')) {
131
+ throw new Error('Native worker controls could be overridden');
132
+ }
133
+ for (let index = 2; index < args.length; index++) {
134
+ const flag = args[index];
135
+ if (['--ignore-user-config', '--skip-git-repo-check', '--json'].includes(flag))
136
+ continue;
137
+ if (flag === '-' && index === args.length - 1)
138
+ continue;
139
+ if (!['-m', '-s', '-c', '-C', '-o'].includes(flag) || index + 1 >= args.length) {
140
+ throw new Error('Native worker controls could be overridden');
141
+ }
142
+ const value = args[++index];
143
+ if (flag === '-c' && !['model_reasoning_effort', 'approval_policy'].includes(value.split('=')[0])) {
144
+ throw new Error('Native worker controls could be overridden');
145
+ }
146
+ }
147
+ }
148
+ /** Inspect declared controls before installation evidence can be produced. This
149
+ * does not authorize invocation; consumers must use qualifyWorkerInvocationProfile. */
150
+ export function inspectWorkerInvocationProfile(adapter, selection) {
151
+ WorkerAdapterSchema.parse(adapter);
152
+ const profile = adapter.invocationProfiles?.[selection.profile];
153
+ if (adapter.qualification.status !== 'qualified' || !profile || profile.transport !== selection.transport) {
154
+ throw new Error('Worker transport/profile is unqualified');
155
+ }
156
+ // Shared range predicate (worker-installation.mjs): read-only surfaces
157
+ // (worker-direct-readiness.ts) reuse the same helper instead of
158
+ // re-deriving this inspector's range logic.
159
+ if (workerVersionRangeMismatch(selection.cliVersion, [adapter.supportedVersions, profile.supportedVersions]) !== null) {
160
+ throw new Error('Worker CLI version is outside qualified profile');
161
+ }
162
+ // The direct CLI baseline trusts the installed driver's bounded renderer.
163
+ // Explicit extra controls still require the existing qualified mappings.
164
+ if (hasOperatorWorkerNativeRenderer(adapter) && profile.transport === 'shell') {
165
+ if (selection.permissions?.sandbox && !['read-only', 'workspace-write', 'external'].includes(selection.permissions.sandbox)) {
166
+ throw new Error('Requested sandbox has no verified native renderer');
167
+ }
168
+ for (const name of selection.requiredControls ?? []) {
169
+ if (!['native', 'external'].includes(profile.controls[name]?.support)) {
170
+ throw new Error(`Required worker control is unqualified: ${name}`);
171
+ }
172
+ }
173
+ return profile;
174
+ }
175
+ const requiredControls = requiredWorkerControls(selection);
176
+ if (requiresWorkerExternalBoundary(adapter, selection)
177
+ && (!selection.boundary || !['os', 'executor'].includes(selection.boundary.kind)
178
+ || !selection.boundary.identity || !/^sha256:[a-f0-9]{64}$/.test(selection.boundary.digest)
179
+ || !selection.boundary.controls?.includes('sandbox'))) {
180
+ throw new Error('Required worker control sandbox needs a qualified external enforcing boundary');
181
+ }
182
+ for (const name of requiredControls) {
183
+ if ((name === 'children' && !adapter.capabilities.nestedAgentsControllable)
184
+ || (name === 'fallback' && !adapter.capabilities.nativeFallbackControllable)) {
185
+ throw new Error(`Required worker control is unqualified: ${name}`);
186
+ }
187
+ const control = profile.controls[name];
188
+ if (!control || !['native', 'external'].includes(control.support) || !control.mapping || !control.evidence) {
189
+ throw new Error(`Required worker control is unqualified: ${name}`);
190
+ }
191
+ }
192
+ if (selection.transport === 'mcp' && (!selection.mcp || !profile.mcp
193
+ || Object.keys(profile.mcp).some(key => profile.mcp[key]
194
+ !== selection.mcp[key]))) {
195
+ throw new Error('MCP server/tool/schema/session qualification changed');
196
+ }
197
+ return profile;
198
+ }
199
+ /** Qualification is transport/version scoped; declaration or boundary claims
200
+ * alone cannot qualify an external sandbox. The invocation owner rechecks the
201
+ * installation scope, including this boundary, against the recorded digest. */
202
+ export function qualifyWorkerInvocationProfile(adapter, selection) {
203
+ const profile = inspectWorkerInvocationProfile(adapter, selection);
204
+ if (isOperatorQualifiedWorkerAdapter(adapter) && selection.policy?.guards?.denySelfDelegation !== false
205
+ && !adapter.capabilities.nestedAgentsControllable) {
206
+ throw new Error('Required worker control is unqualified: children');
207
+ }
208
+ if (requiresWorkerExternalBoundary(adapter, selection) && !profile.installationQualification) {
209
+ throw new Error('Required worker control sandbox needs matching installation qualification for its external boundary');
210
+ }
211
+ return profile;
212
+ }
213
+ /** Health timing evidence belongs to the exact execution profile and tuple.
214
+ * Fixture qualification does not become authenticated/live qualification. */
215
+ export function workerHealthScopeDigest(profile, scope) {
216
+ const { installationQualification: _installation, health, ...executionProfile } = profile;
217
+ const { qualification: _qualification, ...signals } = health ?? {};
218
+ return qualificationDigest({ profile: executionProfile, health: signals,
219
+ profileRef: scope.profileRef, cliVersion: scope.cliVersion, accountScope: scope.accountScope,
220
+ modelKey: workerModelKey(scope.inferenceProvider, scope.model), effort: scope.effort });
221
+ }
222
+ /** Reused by observation and invocation owners before using qualified health.
223
+ * Older profiles have no qualification record and retain degraded observation. */
224
+ export function assertWorkerHealthQualification(profile, scope) {
225
+ const evidence = profile.health?.qualification;
226
+ if (!evidence)
227
+ return;
228
+ if (evidence.status === 'unqualified') {
229
+ if (profile.health?.coverage === 'full' || profile.health?.handshake === 'semantic') {
230
+ throw new Error('Unqualified worker health cannot assert full or semantic coverage');
231
+ }
232
+ return;
233
+ }
234
+ const modelKey = workerModelKey(scope.inferenceProvider, scope.model);
235
+ if (evidence.cliVersion !== scope.cliVersion || evidence.accountScope !== scope.accountScope
236
+ || evidence.profileRef !== scope.profileRef || evidence.modelKey !== modelKey || evidence.effort !== scope.effort
237
+ || !profile.health?.qualifiedAt || !Number.isFinite(Date.parse(profile.health.qualifiedAt))
238
+ || evidence.scopeDigest !== workerHealthScopeDigest(profile, scope)
239
+ || (evidence.status === 'live-qualified' && evidence.authorizedLive !== true)) {
240
+ throw new Error('Worker health qualification scope changed or lacks live authorization');
241
+ }
242
+ }
243
+ //# sourceMappingURL=worker-invocation-policy.js.map
@@ -1,9 +1,12 @@
1
+ import { workerReviewLineageDiagnostics } from './worker-review-lineage.js';
1
2
  // Resolves eligible worker tuples against current config, catalog and installation snapshots.
3
+ import { isFlagshipModel, flagshipElected, normalizeModelTier } from './tiers.js';
2
4
  import { ownWorkerRoleGuidance } from './worker-selection-policy.js';
5
+ import { resolveWorkerCeilings } from './worker-ceilings.js';
3
6
  import { mergeGroupWorkerPolicy } from '../config/worker-group-templates.js';
4
7
  import { WORKER_DISPATCH_ENABLE_COMMAND, WORKER_DISPATCH_READINESS_COMMAND } from './worker-dispatch-offer.js';
5
8
  import { resolveWorkerGroupInstallation, resolveParallelism, validateWorkerGroupQualifications, validateWorkerGroupReferences, validateWorkerBudgetEnforcement, workerActivationDigests, workerConfigDigest, } from '../config/aiwg-config.js';
6
- import { qualifyWorkerInvocationProfile, validateWorkerPolicyRoute, validateWorkerModelObservation, assertWorkerLaunchMode, hasOperatorWorkerNativeRenderer, hasSelectionRelevantConflicts, workerContextHeadroom, workerModelKey, isCanonicalWorkerModelKey, WorkerSelectionPolicySchema, validateWorkerCatalogDiscovery, } from './provider-policy.js';
9
+ import { qualifyWorkerInvocationProfile, validateWorkerPolicyRoute, validateWorkerModelObservation, assertWorkerLaunchMode, hasOperatorWorkerNativeRenderer, workerContextHeadroom, workerModelKey, isCanonicalWorkerModelKey, WorkerSelectionPolicySchema, validateWorkerCatalogDiscovery, } from './provider-policy.js';
7
10
  import { MODEL_WRAPPERS } from './provider-models.js';
8
11
  import { routeModelTier } from './router.js';
9
12
  const matchesChoice = (tuple, choice) => tuple.group === choice.group && tuple.member === choice.member && tuple.model === choice.model && tuple.effort === choice.effort;
@@ -21,7 +24,7 @@ function workerCatalogDigest(catalog) {
21
24
  }
22
25
  /** Pure S1 resolution. No probing, launch, activation, admission, or writes.
23
26
  * Recheck against freshly loaded snapshots before preparing an invocation. */
24
- export function resolveWorkerPlan(snapshot, request) {
27
+ export function resolveWorkerPlan(snapshot, request, ceilingDefaults = {}) {
25
28
  let parallelism = snapshot.config.parallelism ?? {};
26
29
  const policy = snapshot.models.defaults.workerSelection;
27
30
  const digests = {
@@ -85,16 +88,10 @@ export function resolveWorkerPlan(snapshot, request) {
85
88
  if (plan.diagnostics.length)
86
89
  return plan;
87
90
  parallelism = resolveParallelism(parallelism, snapshot.config.providers?.[0]);
88
- const ceilingLayers = [parallelism.budgets?.perRun, parallelism.budgets?.perTask,
89
- { maxAttempts: parallelism.retry?.maxAttempts ?? 3 }, snapshot.constraints?.ceilings, request.ceilings];
90
- for (const key of ['tokens', 'costUsd', 'wallSeconds', 'maxAttempts']) {
91
- const values = ceilingLayers.map(layer => layer?.[key]).filter((value) => value !== undefined);
92
- if (values.some(value => !Number.isFinite(value) || value < 0 || key !== 'costUsd' && !Number.isSafeInteger(value))) {
93
- block('OG_CONFIG_REFERENCE', `/request/ceilings/${key}`, 'Ceilings must be finite nonnegative values; counts must be integers');
94
- }
95
- else if (values.length)
96
- plan.ceilings[key] = Math.min(...values);
97
- }
91
+ const resolvedCeilings = resolveWorkerCeilings(parallelism, snapshot.constraints?.ceilings, request.ceilings, ceilingDefaults);
92
+ plan.ceilings = resolvedCeilings.ceilings;
93
+ plan.origins = { ...plan.origins, ...resolvedCeilings.origins };
94
+ plan.diagnostics.push(...resolvedCeilings.diagnostics);
98
95
  if (plan.ceilings.maxAttempts === 0)
99
96
  block('OG_BUDGET_UNMEASURABLE', '/request/ceilings/maxAttempts', 'No task attempts remain');
100
97
  const qualifiedRoles = {};
@@ -125,10 +122,7 @@ export function resolveWorkerPlan(snapshot, request) {
125
122
  block('OG_CONFIG_REFERENCE', '/request/group', 'Group is outside role guidance');
126
123
  if ([request.maxParallel, snapshot.constraints?.maxParallel].some(value => value !== undefined && (!Number.isSafeInteger(value) || value < 0)))
127
124
  block('OG_CONFIG_REFERENCE', '/request/maxParallel', 'Capacity ceilings must be nonnegative integers');
128
- if (request.requireDifferentFamily && (!request.reviewOf || validateWorkerModelObservation(request.reviewOf).length
129
- || hasSelectionRelevantConflicts(request.reviewOf) || !request.reviewOf.family || request.reviewOf.family === 'unknown')) {
130
- block('OG_MODEL_UNAPPROVED', '/request/reviewOf', 'Required review needs unambiguous observed source lineage');
131
- }
125
+ plan.diagnostics.push(...workerReviewLineageDiagnostics(request));
132
126
  const candidateGroups = request.group ? [request.group] : guidance?.groups ?? [];
133
127
  const unqualifiedGroups = new Set(qualificationGaps.map(gapGroup));
134
128
  const qualifiedGroups = candidateGroups.filter(groupId => !unqualifiedGroups.has(groupId));
@@ -213,8 +207,7 @@ export function resolveWorkerPlan(snapshot, request) {
213
207
  reasons.push(...validateWorkerPolicyRoute({ policy: groupPolicy, role, inferenceProvider: row.llmProvider, apiModelId: row.apiModelId,
214
208
  selectedModel: member.model, effort, identityEvidence: binding.identityEvidence, observation,
215
209
  wrapperPin, ...(substitution ? { substitution: request.substitution } : {}), dataClass: request.dataClass, now, context: request.context }));
216
- if (request.requireDifferentFamily && (observation.family === 'unknown' || observation.family === request.reviewOf.family))
217
- deny('OG_MODEL_UNAPPROVED', '/worker/family', 'Required review must use a different observed family');
210
+ reasons.push(...workerReviewLineageDiagnostics({ ...request, reviewer: observation }));
218
211
  try {
219
212
  const { adapter } = resolveWorkerGroupInstallation(parallelism, member.cli, snapshot.registry);
220
213
  const configuredProfile = parallelism.clis?.[member.cli]?.invocationProfile;
@@ -266,9 +259,12 @@ export function resolveWorkerPlan(snapshot, request) {
266
259
  deny('OG_MODEL_UNAPPROVED', '/constraints/revokedModels', 'Current model revocation');
267
260
  if (snapshot.constraints?.unavailableMembers?.includes(`${groupId}/${member.id}`))
268
261
  deny('OG_ADAPTER_UNQUALIFIED', pointer, 'Member is currently unavailable');
269
- const premium = ['premium', 'max-quality'].includes(guidance.tier) || observation.pricing.costClass === 'premium';
262
+ const flagship = normalizeModelTier(guidance.tier) === 'flagship' || isFlagshipModel(row.apiModelId);
263
+ if (flagship && !flagshipElected(request.authority ?? {}))
264
+ deny('OG_MODEL_UNAPPROVED', '/request/authority', 'Flagship models are user-elected only');
265
+ const premium = ['premium', 'flagship', 'max-quality'].includes(guidance.tier) || observation.pricing.costClass === 'premium';
270
266
  // Worker candidates always invoke a model; deterministic/routine hints must not bypass premium authority.
271
- const authority = routeModelTier({ ...request.authority, deterministic: false, routine: false,
267
+ const authority = routeModelTier({ ...request.authority, deterministic: false, routine: false, requestedTier: flagship ? 'flagship' : undefined,
272
268
  defaultTier: guidance.tier === 'economy' ? 1 : 2, requestedPremium: premium || request.authority?.requestedPremium });
273
269
  if (authority.requiresConfirmation)
274
270
  deny('OG_MODEL_UNAPPROVED', '/request/authority', `${authority.rationale.join('; ')}; agents may pass --authorize-premium only after the human explicitly confirmed the premium escalation (escalation-discipline rule)`);
@@ -0,0 +1,55 @@
1
+ // Shared observed-lineage validation for worker resolution and final review.
2
+ import Ajv2020 from 'ajv/dist/2020.js';
3
+ import addFormats from 'ajv-formats';
4
+ import { existsSync, readFileSync } from 'node:fs';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ let catalogValidators;
8
+ export function getCatalogValidators() {
9
+ if (!catalogValidators) {
10
+ const here = dirname(fileURLToPath(import.meta.url));
11
+ const file = [resolve(here, '../../schemas/models/model-catalog.v1.schema.json'), resolve(here, '../../../schemas/models/model-catalog.v1.schema.json')].find(existsSync);
12
+ if (!file)
13
+ throw new Error('Model catalog schema is unavailable');
14
+ const schema = JSON.parse(readFileSync(file, 'utf8'));
15
+ const ajv = addFormats(new Ajv2020({ strict: false, allErrors: true }));
16
+ ajv.addSchema(schema);
17
+ catalogValidators = { catalog: ajv.getSchema(schema.$id), observation: ajv.compile({ $ref: `${schema.$id}#/$defs/workerObservation` }), discovery: ajv.compile({ $ref: `${schema.$id}#/properties/discovery` }) };
18
+ }
19
+ return catalogValidators;
20
+ }
21
+ export function validateWorkerModelObservation(input) {
22
+ const validate = getCatalogValidators().observation;
23
+ return validate(input) ? [] : (validate.errors ?? []).map(error => `${error.instancePath}: ${error.message}`);
24
+ }
25
+ /** Optional catalog facts do not veto otherwise qualified selection. */
26
+ export function hasSelectionRelevantConflicts(observation) {
27
+ const relevant = /^\/(?:lifecycle|availability|visibility|hidden|efforts|effortSupport|reasoningEfforts|sunsetAt|retiredAt|replacementModelId|supersededBy|dataUse|id|apiModelId|llmProvider|cliId|family|runtimeVersion|accountScope|endpointFingerprint|identityEvidence|contextTokens|maxOutputTokens|qualification)(?:\/|$)/;
28
+ return observation.conflicts?.some(conflict => relevant.test(conflict.field)) ?? false;
29
+ }
30
+ /** Same lineage rule for resolve and acceptance; CLI names never prove diversity. */
31
+ export function workerReviewLineageDiagnostics(input) {
32
+ if (!input.requireDifferentFamily)
33
+ return [];
34
+ const diagnostics = [];
35
+ const deny = (pointer, message) => {
36
+ diagnostics.push({ code: 'OG_MODEL_UNAPPROVED', pointer, message });
37
+ };
38
+ const unambiguous = (observation) => !!observation && validateWorkerModelObservation(observation).length === 0
39
+ && !hasSelectionRelevantConflicts(observation) && !!observation.family && observation.family !== 'unknown';
40
+ if (!unambiguous(input.reviewOf)) {
41
+ deny('/request/reviewOf', 'Required review needs unambiguous observed source lineage');
42
+ }
43
+ // Omission here is intentional during resolver preflight. Acceptance explicitly
44
+ // requires a reviewer before calling this shared predicate.
45
+ if (input.reviewer !== undefined) {
46
+ if (!unambiguous(input.reviewer)) {
47
+ deny('/worker/family', 'Required review needs unambiguous observed reviewer lineage');
48
+ }
49
+ else if (input.reviewer.family === input.reviewOf?.family) {
50
+ deny('/worker/family', 'Required review must use a different observed family');
51
+ }
52
+ }
53
+ return diagnostics;
54
+ }
55
+ //# sourceMappingURL=worker-review-lineage.js.map
@@ -14,7 +14,7 @@ const baseRoles = ['efficiency', 'coding', 'reasoning'];
14
14
  const WorkerRoleSchema = z.strictObject({
15
15
  baseRole: z.enum(baseRoles).optional(),
16
16
  groups: uniqueStrings.refine(values => values.length > 0),
17
- tier: z.enum(['economy', 'standard', 'premium', 'max-quality']),
17
+ tier: z.enum(['economy', 'standard', 'premium', 'flagship', 'max-quality']),
18
18
  initialEffort: z.string().min(1).nullable(),
19
19
  effortLadder: uniqueStrings,
20
20
  escalateAfter: integer().positive().optional(),
@@ -5,7 +5,7 @@ import { resolveWorkerPlan } from './resolver.js';
5
5
  function roleForTier(tier) {
6
6
  if (tier === 'economy')
7
7
  return 'efficiency';
8
- if (tier === 'premium' || tier === 'max-quality')
8
+ if (tier === 'premium' || tier === 'flagship' || tier === 'max-quality')
9
9
  return 'reasoning';
10
10
  if (tier === 'standard')
11
11
  return 'coding';
@@ -14,7 +14,7 @@ function roleForTier(tier) {
14
14
  export function buildWrapperRouteEnvelope(request) {
15
15
  if (!request.assignment.trim())
16
16
  throw new Error('Wrapper routing requires a bounded assignment.');
17
- const decision = routeModelTier(request);
17
+ const decision = routeModelTier({ ...request, agentName: request.agentName ?? request.capability.name });
18
18
  if (request.worker?.snapshot.config.parallelism?.worker_dispatch && !request.deterministic) {
19
19
  const worker = resolveWorkerPlan(request.worker.snapshot, {
20
20
  ...request.worker.request,
@@ -39,7 +39,10 @@ export function buildWrapperRouteEnvelope(request) {
39
39
  ? compileModelPolicy({
40
40
  provider: request.provider,
41
41
  artifact: 'agent',
42
- policy: { role, tier: tier === 'max-quality' ? 'premium' : tier },
42
+ policy: { role, tier },
43
+ election: request.election,
44
+ flagshipAllowlist: request.flagshipAllowlist,
45
+ agentName: request.agentName ?? request.capability.name,
43
46
  ...(request.catalog ? { catalog: request.catalog } : {}),
44
47
  })
45
48
  : null;