@open-agent-toolkit/cli 0.1.43 → 0.1.46
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/assets/agents/oat-reviewer.md +2 -2
- package/assets/config/dispatch-matrix-recommendation.json +23 -0
- package/assets/docs/cli-utilities/config-and-local-state.md +7 -0
- package/assets/docs/cli-utilities/configuration.md +104 -25
- package/assets/docs/cli-utilities/workflow-gates.md +79 -16
- package/assets/docs/contributing/code.md +10 -4
- package/assets/docs/contributing/hooks-and-safety.md +23 -0
- package/assets/docs/provider-sync/config.md +5 -1
- package/assets/docs/provider-sync/manifest-and-drift.md +6 -1
- package/assets/docs/provider-sync/providers.md +6 -3
- package/assets/docs/provider-sync/scope-and-surface.md +2 -1
- package/assets/docs/reference/oat-directory-structure.md +2 -2
- package/assets/docs/workflows/projects/dispatch-ceiling.md +110 -27
- package/assets/docs/workflows/projects/implementation-execution.md +20 -13
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-implement/SKILL.md +155 -58
- package/assets/skills/oat-project-plan/SKILL.md +22 -3
- package/assets/skills/oat-project-quick-start/SKILL.md +54 -23
- package/assets/templates/state.md +5 -0
- package/dist/commands/config/index.d.ts +6 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +429 -20
- package/dist/commands/doctor/index.d.ts +6 -1
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +209 -19
- package/dist/commands/gate/index.d.ts +39 -2
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +343 -30
- package/dist/commands/internal/cursor-current-target.d.ts +37 -0
- package/dist/commands/internal/cursor-current-target.d.ts.map +1 -0
- package/dist/commands/internal/cursor-current-target.js +228 -0
- package/dist/commands/internal/index.d.ts.map +1 -1
- package/dist/commands/internal/index.js +2 -0
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +487 -17
- package/dist/commands/providers/codex/index.d.ts +4 -0
- package/dist/commands/providers/codex/index.d.ts.map +1 -0
- package/dist/commands/providers/codex/index.js +7 -0
- package/dist/commands/providers/codex/materialize.d.ts +5 -0
- package/dist/commands/providers/codex/materialize.d.ts.map +1 -0
- package/dist/commands/providers/codex/materialize.js +152 -0
- package/dist/commands/providers/index.d.ts +2 -2
- package/dist/commands/providers/index.d.ts.map +1 -1
- package/dist/commands/providers/index.js +4 -2
- package/dist/commands/providers/providers.types.d.ts +23 -0
- package/dist/commands/providers/providers.types.d.ts.map +1 -1
- package/dist/commands/shared/codex-strays.d.ts.map +1 -1
- package/dist/commands/shared/codex-strays.js +11 -1
- package/dist/commands/status/index.d.ts +4 -1
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +1 -1
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +1 -1
- package/dist/commands/sync/sync.types.d.ts +4 -1
- package/dist/commands/sync/sync.types.d.ts.map +1 -1
- package/dist/config/dispatch-ceiling-preset.d.ts +4 -0
- package/dist/config/dispatch-ceiling-preset.d.ts.map +1 -1
- package/dist/config/dispatch-ceiling-preset.js +3 -0
- package/dist/config/dispatch-policy-options.d.ts +20 -0
- package/dist/config/dispatch-policy-options.d.ts.map +1 -0
- package/dist/config/dispatch-policy-options.js +96 -0
- package/dist/config/oat-config.d.ts +21 -5
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +118 -10
- package/dist/config/resolve.js +12 -0
- package/dist/manifest/manifest.types.d.ts +12 -12
- package/dist/providers/ceiling/registry.d.ts +7 -5
- package/dist/providers/ceiling/registry.d.ts.map +1 -1
- package/dist/providers/ceiling/registry.js +32 -5
- package/dist/providers/codex/codec/config-merge.d.ts +6 -0
- package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
- package/dist/providers/codex/codec/config-merge.js +7 -0
- package/dist/providers/codex/codec/materialize.d.ts +16 -0
- package/dist/providers/codex/codec/materialize.d.ts.map +1 -0
- package/dist/providers/codex/codec/materialize.js +57 -0
- package/dist/providers/codex/codec/shared.d.ts +1 -0
- package/dist/providers/codex/codec/shared.d.ts.map +1 -1
- package/dist/providers/codex/codec/shared.js +9 -1
- package/dist/providers/codex/codec/sync-extension.d.ts +7 -1
- package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/codex/codec/sync-extension.js +183 -46
- package/dist/providers/identity/availability.d.ts +45 -0
- package/dist/providers/identity/availability.d.ts.map +1 -0
- package/dist/providers/identity/availability.js +353 -0
- package/dist/providers/identity/family.d.ts +13 -0
- package/dist/providers/identity/family.d.ts.map +1 -0
- package/dist/providers/identity/family.js +32 -0
- package/dist/providers/identity/provenance.d.ts +21 -0
- package/dist/providers/identity/provenance.d.ts.map +1 -0
- package/dist/providers/identity/provenance.js +65 -0
- package/dist/providers/identity/stamp.d.ts +35 -0
- package/dist/providers/identity/stamp.d.ts.map +1 -0
- package/dist/providers/identity/stamp.js +171 -0
- package/package.json +2 -2
|
@@ -4,11 +4,13 @@ import { buildCommandContext, } from '../../../app/command-context.js';
|
|
|
4
4
|
import { getFrontmatterBlock } from '../../shared/frontmatter.js';
|
|
5
5
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
6
6
|
import { compileDispatchPolicyPreset, } from '../../../config/dispatch-ceiling-preset.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getDispatchPolicyChoices, managedDispatchPolicyValueList, renderDispatchPolicyChoicesMarkdown, } from '../../../config/dispatch-policy-options.js';
|
|
8
|
+
import { resolveActiveProject, VALID_CLAUDE_DISPATCH_CEILINGS, VALID_CODEX_DISPATCH_CEILINGS, VALID_DISPATCH_MATRIX_TIERS, VALID_MANAGED_DISPATCH_POLICIES, validateDispatchRouteTarget, } from '../../../config/oat-config.js';
|
|
8
9
|
import { resolveEffectiveConfig, } from '../../../config/resolve.js';
|
|
9
10
|
import { resolveProjectRoot } from '../../../fs/paths.js';
|
|
10
11
|
import TOML from '@iarna/toml';
|
|
11
12
|
import { getCeilingAdapter, } from '../../../providers/ceiling/registry.js';
|
|
13
|
+
import { classifyModelFamily, } from '../../../providers/identity/family.js';
|
|
12
14
|
import { Command } from 'commander';
|
|
13
15
|
import YAML from 'yaml';
|
|
14
16
|
const CODEX_VALUES = [
|
|
@@ -54,7 +56,7 @@ function isValidProviderValue(provider, value) {
|
|
|
54
56
|
return (typeof value === 'string' &&
|
|
55
57
|
CLAUDE_VALUES.includes(value));
|
|
56
58
|
}
|
|
57
|
-
return
|
|
59
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
58
60
|
}
|
|
59
61
|
function configSourceToCeilingSource(source) {
|
|
60
62
|
if (source === 'local') {
|
|
@@ -105,7 +107,7 @@ function isValidManagedPolicy(value) {
|
|
|
105
107
|
VALID_MANAGED_DISPATCH_POLICIES.includes(value));
|
|
106
108
|
}
|
|
107
109
|
function validManagedPolicyList() {
|
|
108
|
-
return
|
|
110
|
+
return managedDispatchPolicyValueList(', ');
|
|
109
111
|
}
|
|
110
112
|
function validProviderValueList(provider) {
|
|
111
113
|
return providerValueOrder(provider)?.join(', ') ?? 'none';
|
|
@@ -121,6 +123,253 @@ function invalidProjectPolicyMessage(value) {
|
|
|
121
123
|
const actual = typeof value === 'string' ? value : String(value);
|
|
122
124
|
return `Invalid project dispatch policy "${actual}". Valid managed policies: ${validManagedPolicyList()}. Use mode "inherit" for host defaults.`;
|
|
123
125
|
}
|
|
126
|
+
function normalizeProjectMatrixBareValue(provider, value) {
|
|
127
|
+
if (typeof value !== 'string') {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const trimmed = value.trim();
|
|
131
|
+
if (!trimmed) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const order = providerValueOrder(provider);
|
|
135
|
+
if (order && !isValidProviderValue(provider, trimmed)) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
return trimmed;
|
|
139
|
+
}
|
|
140
|
+
function normalizeProjectMatrixRouteTarget(value) {
|
|
141
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
const record = value;
|
|
145
|
+
const target = {};
|
|
146
|
+
for (const key of ['harness', 'model', 'effort']) {
|
|
147
|
+
const rawValue = record[key];
|
|
148
|
+
if (typeof rawValue === 'string' && rawValue.trim()) {
|
|
149
|
+
target[key] = rawValue.trim();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return Object.keys(target).length > 0 ? target : undefined;
|
|
153
|
+
}
|
|
154
|
+
function normalizeProjectMatrixCell(provider, value) {
|
|
155
|
+
const bareValue = normalizeProjectMatrixBareValue(provider, value);
|
|
156
|
+
if (bareValue !== undefined) {
|
|
157
|
+
return bareValue;
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
const route = [];
|
|
163
|
+
for (const entry of value) {
|
|
164
|
+
const bareEntry = normalizeProjectMatrixBareValue(provider, entry);
|
|
165
|
+
if (bareEntry !== undefined) {
|
|
166
|
+
route.push(bareEntry);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const target = normalizeProjectMatrixRouteTarget(entry);
|
|
170
|
+
if (target !== undefined) {
|
|
171
|
+
route.push(target);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return route.length > 0 ? route : undefined;
|
|
175
|
+
}
|
|
176
|
+
function normalizeProjectMatrixProviderValue(provider, value) {
|
|
177
|
+
const bareValue = normalizeProjectMatrixBareValue(provider, value);
|
|
178
|
+
if (bareValue !== undefined) {
|
|
179
|
+
return bareValue;
|
|
180
|
+
}
|
|
181
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const tierMap = {};
|
|
185
|
+
for (const [tier, rawCell] of Object.entries(value)) {
|
|
186
|
+
if (!VALID_DISPATCH_MATRIX_TIERS.includes(tier)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const normalized = normalizeProjectMatrixCell(provider, rawCell);
|
|
190
|
+
if (normalized !== undefined) {
|
|
191
|
+
tierMap[tier] = normalized;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return Object.keys(tierMap).length > 0 ? tierMap : undefined;
|
|
195
|
+
}
|
|
196
|
+
function readProjectDispatchMatrix(value) {
|
|
197
|
+
if (value === undefined || value === null) {
|
|
198
|
+
return { matrix: null, warnings: [] };
|
|
199
|
+
}
|
|
200
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
201
|
+
return {
|
|
202
|
+
matrix: null,
|
|
203
|
+
warnings: [
|
|
204
|
+
'Ignoring malformed oat_dispatch_policy.matrix in project state.',
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const matrix = {};
|
|
209
|
+
for (const [provider, rawProviderValue] of Object.entries(value)) {
|
|
210
|
+
if (!provider.trim()) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const normalized = normalizeProjectMatrixProviderValue(provider, rawProviderValue);
|
|
214
|
+
if (normalized !== undefined) {
|
|
215
|
+
matrix[provider] = normalized;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (Object.keys(matrix).length === 0) {
|
|
219
|
+
return {
|
|
220
|
+
matrix: null,
|
|
221
|
+
warnings: [
|
|
222
|
+
'Ignoring malformed oat_dispatch_policy.matrix in project state.',
|
|
223
|
+
],
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return { matrix, warnings: [] };
|
|
227
|
+
}
|
|
228
|
+
function policyTier(policy) {
|
|
229
|
+
return typeof policy === 'string' &&
|
|
230
|
+
VALID_DISPATCH_MATRIX_TIERS.includes(policy)
|
|
231
|
+
? policy
|
|
232
|
+
: null;
|
|
233
|
+
}
|
|
234
|
+
function uncappedPreferredTier(provider, role, preferredValue) {
|
|
235
|
+
if (provider !== 'codex' || role === 'reviewer' || preferredValue === null) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
if (VALID_DISPATCH_MATRIX_TIERS.includes(preferredValue)) {
|
|
239
|
+
return preferredValue;
|
|
240
|
+
}
|
|
241
|
+
let matchedTier = null;
|
|
242
|
+
for (const tier of VALID_DISPATCH_MATRIX_TIERS) {
|
|
243
|
+
const compiledValue = compiledPolicyValueForProvider(provider, compileDispatchPolicyPreset(tier));
|
|
244
|
+
if (compiledValue === preferredValue) {
|
|
245
|
+
matchedTier = tier;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return matchedTier;
|
|
249
|
+
}
|
|
250
|
+
function dispatchValueFromRouteTarget(target) {
|
|
251
|
+
const adapter = getCeilingAdapter(target.harness);
|
|
252
|
+
if (adapter.mechanism === 'pinned-variant') {
|
|
253
|
+
return target.effort ?? null;
|
|
254
|
+
}
|
|
255
|
+
if (adapter.mechanism === 'model-arg') {
|
|
256
|
+
return target.model ?? null;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
function routeTargetFromBareValue(provider, value, routeIndex, routeLength) {
|
|
261
|
+
const adapter = getCeilingAdapter(provider);
|
|
262
|
+
const target = {
|
|
263
|
+
harness: provider,
|
|
264
|
+
crossHarness: false,
|
|
265
|
+
routeIndex,
|
|
266
|
+
routeLength,
|
|
267
|
+
};
|
|
268
|
+
if (adapter.mechanism === 'pinned-variant') {
|
|
269
|
+
target.effort = value;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
target.model = value;
|
|
273
|
+
}
|
|
274
|
+
return target;
|
|
275
|
+
}
|
|
276
|
+
function routeTargetFromObject(provider, target, routeIndex, routeLength) {
|
|
277
|
+
const harness = target.harness ?? provider;
|
|
278
|
+
return {
|
|
279
|
+
harness,
|
|
280
|
+
...(target.model ? { model: target.model } : {}),
|
|
281
|
+
...(target.effort ? { effort: target.effort } : {}),
|
|
282
|
+
crossHarness: harness !== provider,
|
|
283
|
+
routeIndex,
|
|
284
|
+
routeLength,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function resolveRouteMatrixCell(provider, route, escalationLevel, cellSource) {
|
|
288
|
+
const routeIndex = Math.min(escalationLevel, route.length - 1);
|
|
289
|
+
const entry = route[routeIndex];
|
|
290
|
+
if (entry === undefined) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const target = typeof entry === 'string'
|
|
294
|
+
? routeTargetFromBareValue(provider, entry, routeIndex, route.length)
|
|
295
|
+
: routeTargetFromObject(provider, entry, routeIndex, route.length);
|
|
296
|
+
const targetValidation = typeof entry === 'string'
|
|
297
|
+
? { valid: true }
|
|
298
|
+
: validateDispatchRouteTarget(provider, entry);
|
|
299
|
+
const value = targetValidation.valid
|
|
300
|
+
? typeof entry === 'string'
|
|
301
|
+
? entry
|
|
302
|
+
: dispatchValueFromRouteTarget(target)
|
|
303
|
+
: null;
|
|
304
|
+
const warnings = targetValidation.valid
|
|
305
|
+
? []
|
|
306
|
+
: [
|
|
307
|
+
`Ignoring incomplete Codex dispatch target at route index ${routeIndex}: ${targetValidation.reason}`,
|
|
308
|
+
];
|
|
309
|
+
return {
|
|
310
|
+
value,
|
|
311
|
+
cellSource,
|
|
312
|
+
target,
|
|
313
|
+
selectionBranch: routeIndex > 0 ? 'escalation-target' : 'matrix-pinned',
|
|
314
|
+
warnings,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function resolveProviderCellFromValue(provider, providerValue, tier, cellSource, escalationLevel) {
|
|
318
|
+
if (providerValue === undefined) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
if (typeof providerValue === 'string') {
|
|
322
|
+
return {
|
|
323
|
+
value: providerValue,
|
|
324
|
+
cellSource,
|
|
325
|
+
target: null,
|
|
326
|
+
selectionBranch: 'prompt-persisted',
|
|
327
|
+
warnings: [],
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (tier === null) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const cell = providerValue[tier];
|
|
334
|
+
if (cell === undefined) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
if (typeof cell === 'string') {
|
|
338
|
+
return {
|
|
339
|
+
value: cell,
|
|
340
|
+
cellSource,
|
|
341
|
+
target: null,
|
|
342
|
+
selectionBranch: 'matrix-pinned',
|
|
343
|
+
warnings: [],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return resolveRouteMatrixCell(provider, cell, escalationLevel, cellSource);
|
|
347
|
+
}
|
|
348
|
+
function resolveProviderMatrixCell(provider, tier, resolvedConfig, projectMatrix, escalationLevel) {
|
|
349
|
+
let selected = null;
|
|
350
|
+
const layers = [
|
|
351
|
+
{
|
|
352
|
+
source: 'user-config',
|
|
353
|
+
providers: resolvedConfig.user.workflow?.dispatchCeiling?.providers,
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
source: 'repo-config',
|
|
357
|
+
providers: resolvedConfig.shared.workflow?.dispatchCeiling?.providers,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
source: 'local-config',
|
|
361
|
+
providers: resolvedConfig.local.workflow?.dispatchCeiling?.providers,
|
|
362
|
+
},
|
|
363
|
+
{ source: 'project-state', providers: projectMatrix },
|
|
364
|
+
];
|
|
365
|
+
for (const layer of layers) {
|
|
366
|
+
const resolved = resolveProviderCellFromValue(provider, layer.providers?.[provider], tier, layer.source, escalationLevel);
|
|
367
|
+
if (resolved) {
|
|
368
|
+
selected = resolved;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return selected;
|
|
372
|
+
}
|
|
124
373
|
function readProjectDispatchPolicy(provider, content) {
|
|
125
374
|
const frontmatter = getFrontmatterBlock(content);
|
|
126
375
|
if (!frontmatter) {
|
|
@@ -135,6 +384,7 @@ function readProjectDispatchPolicy(provider, content) {
|
|
|
135
384
|
return null;
|
|
136
385
|
}
|
|
137
386
|
const policyRecord = policy;
|
|
387
|
+
const parsedMatrix = readProjectDispatchMatrix(policyRecord['matrix']);
|
|
138
388
|
const mode = policyRecord['mode'];
|
|
139
389
|
if (mode === 'inherit') {
|
|
140
390
|
return {
|
|
@@ -143,6 +393,11 @@ function readProjectDispatchPolicy(provider, content) {
|
|
|
143
393
|
value: null,
|
|
144
394
|
source: 'project-state',
|
|
145
395
|
preset: null,
|
|
396
|
+
matrix: parsedMatrix.matrix,
|
|
397
|
+
cellSource: null,
|
|
398
|
+
target: null,
|
|
399
|
+
selectionBranch: 'inherit',
|
|
400
|
+
warnings: parsedMatrix.warnings,
|
|
146
401
|
};
|
|
147
402
|
}
|
|
148
403
|
if (mode !== 'managed') {
|
|
@@ -161,6 +416,11 @@ function readProjectDispatchPolicy(provider, content) {
|
|
|
161
416
|
value: null,
|
|
162
417
|
source: 'project-state',
|
|
163
418
|
preset: policyValue,
|
|
419
|
+
matrix: parsedMatrix.matrix,
|
|
420
|
+
cellSource: null,
|
|
421
|
+
target: null,
|
|
422
|
+
selectionBranch: 'prompt-persisted',
|
|
423
|
+
warnings: parsedMatrix.warnings,
|
|
164
424
|
};
|
|
165
425
|
}
|
|
166
426
|
const providers = policyRecord['providers'];
|
|
@@ -183,6 +443,11 @@ function readProjectDispatchPolicy(provider, content) {
|
|
|
183
443
|
value,
|
|
184
444
|
source: 'project-state',
|
|
185
445
|
preset: policyValue,
|
|
446
|
+
matrix: parsedMatrix.matrix,
|
|
447
|
+
cellSource: 'project-state',
|
|
448
|
+
target: null,
|
|
449
|
+
selectionBranch: 'prompt-persisted',
|
|
450
|
+
warnings: parsedMatrix.warnings,
|
|
186
451
|
};
|
|
187
452
|
}
|
|
188
453
|
function readLegacyProjectDispatchCeiling(provider, parsed) {
|
|
@@ -208,6 +473,11 @@ function readLegacyProjectDispatchCeiling(provider, parsed) {
|
|
|
208
473
|
value,
|
|
209
474
|
source: 'project-state',
|
|
210
475
|
preset: typeof presetValue === 'string' ? presetValue : null,
|
|
476
|
+
matrix: null,
|
|
477
|
+
cellSource: 'project-state',
|
|
478
|
+
target: null,
|
|
479
|
+
selectionBranch: 'prompt-persisted',
|
|
480
|
+
warnings: [],
|
|
211
481
|
};
|
|
212
482
|
}
|
|
213
483
|
function readProjectDispatchCeiling(provider, content) {
|
|
@@ -274,6 +544,11 @@ function stripConfigCandidateSource(candidate) {
|
|
|
274
544
|
value: candidate.value,
|
|
275
545
|
source: candidate.source,
|
|
276
546
|
preset: candidate.preset,
|
|
547
|
+
matrix: candidate.matrix,
|
|
548
|
+
cellSource: candidate.cellSource,
|
|
549
|
+
target: candidate.target,
|
|
550
|
+
selectionBranch: candidate.selectionBranch,
|
|
551
|
+
warnings: candidate.warnings,
|
|
277
552
|
};
|
|
278
553
|
}
|
|
279
554
|
function readResolvedConfigPolicyCandidate(provider, resolvedConfig) {
|
|
@@ -291,6 +566,11 @@ function readResolvedConfigPolicyCandidate(provider, resolvedConfig) {
|
|
|
291
566
|
value: null,
|
|
292
567
|
source,
|
|
293
568
|
preset: null,
|
|
569
|
+
matrix: null,
|
|
570
|
+
cellSource: null,
|
|
571
|
+
target: null,
|
|
572
|
+
selectionBranch: 'inherit',
|
|
573
|
+
warnings: [],
|
|
294
574
|
configSource: modeEntry.source,
|
|
295
575
|
};
|
|
296
576
|
}
|
|
@@ -311,6 +591,11 @@ function readResolvedConfigPolicyCandidate(provider, resolvedConfig) {
|
|
|
311
591
|
value: compiledPolicyValueForProvider(provider, compiled),
|
|
312
592
|
source,
|
|
313
593
|
preset: policy,
|
|
594
|
+
matrix: null,
|
|
595
|
+
cellSource: null,
|
|
596
|
+
target: null,
|
|
597
|
+
selectionBranch: 'prompt-persisted',
|
|
598
|
+
warnings: [],
|
|
314
599
|
configSource,
|
|
315
600
|
};
|
|
316
601
|
}
|
|
@@ -336,6 +621,11 @@ function readResolvedLegacyConfigCeilingCandidate(provider, resolvedConfig) {
|
|
|
336
621
|
value: entry.value,
|
|
337
622
|
source,
|
|
338
623
|
preset: null,
|
|
624
|
+
matrix: null,
|
|
625
|
+
cellSource: source,
|
|
626
|
+
target: null,
|
|
627
|
+
selectionBranch: 'prompt-persisted',
|
|
628
|
+
warnings: [],
|
|
339
629
|
configSource: entry.source,
|
|
340
630
|
};
|
|
341
631
|
}
|
|
@@ -384,11 +674,28 @@ function normalizePreferredValue(provider, value) {
|
|
|
384
674
|
}
|
|
385
675
|
return normalized;
|
|
386
676
|
}
|
|
677
|
+
function normalizeEscalationLevel(value) {
|
|
678
|
+
if (value === undefined) {
|
|
679
|
+
return 0;
|
|
680
|
+
}
|
|
681
|
+
const normalized = value.trim();
|
|
682
|
+
if (!/^\d+$/.test(normalized)) {
|
|
683
|
+
throw new Error(`Invalid escalation level "${value}". Use a non-negative integer.`);
|
|
684
|
+
}
|
|
685
|
+
return Number.parseInt(normalized, 10);
|
|
686
|
+
}
|
|
687
|
+
function selectionFamily(provider, value, target) {
|
|
688
|
+
return value
|
|
689
|
+
? classifyModelFamily({ value, providerId: target?.harness ?? provider })
|
|
690
|
+
: 'unknown';
|
|
691
|
+
}
|
|
387
692
|
function selectDispatchValue(provider, role, policy, preferredValue) {
|
|
388
693
|
const baseSelection = {
|
|
389
694
|
role,
|
|
390
695
|
policyMode: policy.mode,
|
|
391
696
|
policy: policy.policy,
|
|
697
|
+
cellSource: policy.cellSource,
|
|
698
|
+
target: policy.target,
|
|
392
699
|
};
|
|
393
700
|
if (policy.mode === 'inherit') {
|
|
394
701
|
return {
|
|
@@ -397,15 +704,30 @@ function selectDispatchValue(provider, role, policy, preferredValue) {
|
|
|
397
704
|
selectedValue: null,
|
|
398
705
|
capped: false,
|
|
399
706
|
selectionMode: 'inherit-default',
|
|
707
|
+
selectionBranch: 'inherit',
|
|
708
|
+
family: 'unknown',
|
|
709
|
+
target: null,
|
|
400
710
|
};
|
|
401
711
|
}
|
|
402
712
|
if (policy.policy === 'uncapped') {
|
|
713
|
+
const targetValue = policy.target
|
|
714
|
+
? dispatchValueFromRouteTarget(policy.target)
|
|
715
|
+
: null;
|
|
716
|
+
const selectedValue = role === 'reviewer' ? null : (targetValue ?? preferredValue);
|
|
717
|
+
const target = role === 'reviewer' || !targetValue ? null : policy.target;
|
|
403
718
|
return {
|
|
404
719
|
...baseSelection,
|
|
405
720
|
preferredValue: role === 'reviewer' ? null : preferredValue,
|
|
406
|
-
selectedValue
|
|
721
|
+
selectedValue,
|
|
407
722
|
capped: false,
|
|
408
723
|
selectionMode: role === 'reviewer' ? 'no-review-target' : 'uncapped',
|
|
724
|
+
selectionBranch: target
|
|
725
|
+
? policy.selectionBranch
|
|
726
|
+
: selectedValue
|
|
727
|
+
? 'prompt-persisted'
|
|
728
|
+
: 'unresolved',
|
|
729
|
+
family: selectionFamily(provider, selectedValue, target),
|
|
730
|
+
target,
|
|
409
731
|
};
|
|
410
732
|
}
|
|
411
733
|
if (policy.value === null) {
|
|
@@ -414,7 +736,10 @@ function selectDispatchValue(provider, role, policy, preferredValue) {
|
|
|
414
736
|
preferredValue: null,
|
|
415
737
|
selectedValue: null,
|
|
416
738
|
capped: false,
|
|
417
|
-
selectionMode: 'capped',
|
|
739
|
+
selectionMode: policy.target ? 'unresolved' : 'capped',
|
|
740
|
+
selectionBranch: policy.selectionBranch,
|
|
741
|
+
family: 'unknown',
|
|
742
|
+
target: policy.target,
|
|
418
743
|
};
|
|
419
744
|
}
|
|
420
745
|
if (role === 'reviewer' || preferredValue === null) {
|
|
@@ -424,6 +749,8 @@ function selectDispatchValue(provider, role, policy, preferredValue) {
|
|
|
424
749
|
selectedValue: policy.value,
|
|
425
750
|
capped: false,
|
|
426
751
|
selectionMode: role === 'reviewer' ? 'review-target' : 'capped',
|
|
752
|
+
selectionBranch: policy.selectionBranch,
|
|
753
|
+
family: selectionFamily(provider, policy.value, policy.target),
|
|
427
754
|
};
|
|
428
755
|
}
|
|
429
756
|
const order = providerValueOrder(provider);
|
|
@@ -436,17 +763,57 @@ function selectDispatchValue(provider, role, policy, preferredValue) {
|
|
|
436
763
|
selectedValue: policy.value,
|
|
437
764
|
capped: false,
|
|
438
765
|
selectionMode: 'capped',
|
|
766
|
+
selectionBranch: policy.selectionBranch,
|
|
767
|
+
family: selectionFamily(provider, policy.value, policy.target),
|
|
439
768
|
};
|
|
440
769
|
}
|
|
441
770
|
const selectedIndex = Math.min(preferredIndex, ceilingIndex);
|
|
771
|
+
const selectedValue = order[selectedIndex];
|
|
442
772
|
return {
|
|
443
773
|
...baseSelection,
|
|
444
774
|
preferredValue,
|
|
445
|
-
selectedValue
|
|
775
|
+
selectedValue,
|
|
446
776
|
capped: preferredIndex > ceilingIndex,
|
|
447
777
|
selectionMode: 'capped',
|
|
778
|
+
selectionBranch: policy.selectionBranch,
|
|
779
|
+
family: selectionFamily(provider, selectedValue, selectedValue === policy.value ? policy.target : null),
|
|
780
|
+
target: selectedValue === policy.value ? policy.target : null,
|
|
448
781
|
};
|
|
449
782
|
}
|
|
783
|
+
function hasCodexVariantDispatchArgs(dispatchArgs) {
|
|
784
|
+
return (dispatchArgs !== null &&
|
|
785
|
+
'variant' in dispatchArgs &&
|
|
786
|
+
typeof dispatchArgs.variant === 'string' &&
|
|
787
|
+
dispatchArgs.variant.length > 0);
|
|
788
|
+
}
|
|
789
|
+
function hasModelDispatchArgs(dispatchArgs) {
|
|
790
|
+
return (dispatchArgs !== null &&
|
|
791
|
+
'model' in dispatchArgs &&
|
|
792
|
+
typeof dispatchArgs.model === 'string' &&
|
|
793
|
+
dispatchArgs.model.length > 0);
|
|
794
|
+
}
|
|
795
|
+
function modelAxis(selection, dispatchArgs) {
|
|
796
|
+
if (selection.target?.model && dispatchArgs) {
|
|
797
|
+
return `selected:${selection.target.model}`;
|
|
798
|
+
}
|
|
799
|
+
if (hasModelDispatchArgs(dispatchArgs)) {
|
|
800
|
+
return `selected:${dispatchArgs.model}`;
|
|
801
|
+
}
|
|
802
|
+
if (selection.selectionMode === 'inherit-default') {
|
|
803
|
+
return 'inherited';
|
|
804
|
+
}
|
|
805
|
+
return 'unresolved';
|
|
806
|
+
}
|
|
807
|
+
function codexEffortAxis(selection, dispatchArgs) {
|
|
808
|
+
if (hasCodexVariantDispatchArgs(dispatchArgs) && selection.target?.effort) {
|
|
809
|
+
return `selected:${selection.target.effort}`;
|
|
810
|
+
}
|
|
811
|
+
if (selection.selectionMode === 'inherit-default' ||
|
|
812
|
+
selection.selectionMode === 'no-review-target') {
|
|
813
|
+
return 'provider-default';
|
|
814
|
+
}
|
|
815
|
+
return 'unresolved';
|
|
816
|
+
}
|
|
450
817
|
/**
|
|
451
818
|
* Join a resolved ceiling value with the active provider's adapter to compute
|
|
452
819
|
* the enforcement mode, mechanism, dispatch args, and verify-on-upgrade flag.
|
|
@@ -460,13 +827,21 @@ function buildProviderResolution(provider, policy, role, orchestratorTier, prefe
|
|
|
460
827
|
mode: adapter.supportsCeiling ? 'advisory' : 'unsupported',
|
|
461
828
|
mechanism: adapter.mechanism,
|
|
462
829
|
dispatchArgs: null,
|
|
830
|
+
modelAxis: 'unresolved',
|
|
831
|
+
effortAxis: provider === 'codex' ? 'provider-default' : 'not-applicable',
|
|
463
832
|
verifyOnDispatch: false,
|
|
833
|
+
cellSource: null,
|
|
834
|
+
target: null,
|
|
464
835
|
selection: {
|
|
465
836
|
role,
|
|
466
837
|
preferredValue,
|
|
467
838
|
selectedValue: null,
|
|
468
839
|
capped: false,
|
|
469
840
|
selectionMode: 'unresolved',
|
|
841
|
+
selectionBranch: 'unresolved',
|
|
842
|
+
family: 'unknown',
|
|
843
|
+
target: null,
|
|
844
|
+
cellSource: null,
|
|
470
845
|
policyMode: null,
|
|
471
846
|
policy: null,
|
|
472
847
|
},
|
|
@@ -474,9 +849,11 @@ function buildProviderResolution(provider, policy, role, orchestratorTier, prefe
|
|
|
474
849
|
}
|
|
475
850
|
const selection = selectDispatchValue(provider, role, policy, preferredValue);
|
|
476
851
|
const dispatchValue = selection.selectedValue;
|
|
477
|
-
const
|
|
852
|
+
const isCrossHarness = selection.target?.crossHarness === true;
|
|
853
|
+
const dispatchArgs = dispatchValue && !isCrossHarness
|
|
478
854
|
? adapter.compileToDispatchArgs(dispatchValue, role, {
|
|
479
855
|
orchestratorTier,
|
|
856
|
+
target: selection.target,
|
|
480
857
|
})
|
|
481
858
|
: null;
|
|
482
859
|
let mode;
|
|
@@ -494,11 +871,18 @@ function buildProviderResolution(provider, policy, role, orchestratorTier, prefe
|
|
|
494
871
|
mode,
|
|
495
872
|
mechanism: adapter.mechanism,
|
|
496
873
|
dispatchArgs,
|
|
497
|
-
|
|
874
|
+
modelAxis: modelAxis(selection, dispatchArgs),
|
|
875
|
+
effortAxis: provider === 'codex'
|
|
876
|
+
? codexEffortAxis(selection, dispatchArgs)
|
|
877
|
+
: 'not-applicable',
|
|
878
|
+
verifyOnDispatch: dispatchValue && !isCrossHarness
|
|
498
879
|
? adapter.verifyOnDispatch(dispatchValue, {
|
|
499
880
|
orchestratorTier,
|
|
881
|
+
target: selection.target,
|
|
500
882
|
})
|
|
501
883
|
: false,
|
|
884
|
+
cellSource: policy.cellSource,
|
|
885
|
+
target: selection.target,
|
|
502
886
|
selection,
|
|
503
887
|
};
|
|
504
888
|
}
|
|
@@ -550,7 +934,11 @@ async function resolveDispatchCeiling(context, dependencies, options) {
|
|
|
550
934
|
? await resolveCodexProviderDefaultEffort(repoRoot, context, dependencies)
|
|
551
935
|
: 'not-applicable';
|
|
552
936
|
const preferredValue = normalizePreferredValue(provider, options.preferred);
|
|
553
|
-
const
|
|
937
|
+
const escalationLevel = normalizeEscalationLevel(options.escalationLevel);
|
|
938
|
+
const resolvedValue = await resolveCeilingValue(provider, resolvedConfig, projectPath, dependencies, escalationLevel, role, preferredValue);
|
|
939
|
+
for (const warning of resolvedValue?.warnings ?? []) {
|
|
940
|
+
context.logger.warn(warning);
|
|
941
|
+
}
|
|
554
942
|
const providerResolution = buildProviderResolution(provider, resolvedValue, role, orchestratorTier, preferredValue);
|
|
555
943
|
const providers = {
|
|
556
944
|
[provider]: providerResolution,
|
|
@@ -567,6 +955,7 @@ async function resolveDispatchCeiling(context, dependencies, options) {
|
|
|
567
955
|
unresolved: false,
|
|
568
956
|
projectPath,
|
|
569
957
|
providerDefaultEffort,
|
|
958
|
+
matrix: resolvedValue.matrix,
|
|
570
959
|
providers,
|
|
571
960
|
};
|
|
572
961
|
}
|
|
@@ -585,6 +974,7 @@ async function resolveDispatchCeiling(context, dependencies, options) {
|
|
|
585
974
|
unresolved: true,
|
|
586
975
|
projectPath,
|
|
587
976
|
providerDefaultEffort,
|
|
977
|
+
matrix: null,
|
|
588
978
|
providers,
|
|
589
979
|
message,
|
|
590
980
|
};
|
|
@@ -595,16 +985,50 @@ async function resolveDispatchCeiling(context, dependencies, options) {
|
|
|
595
985
|
* Never reads the preset label for dispatch — the preset is surfaced as
|
|
596
986
|
* provenance only.
|
|
597
987
|
*/
|
|
598
|
-
async function resolveCeilingValue(provider, resolvedConfig, projectPath, dependencies) {
|
|
988
|
+
async function resolveCeilingValue(provider, resolvedConfig, projectPath, dependencies, escalationLevel, role, preferredValue) {
|
|
599
989
|
const configCeiling = readResolvedConfigCeiling(provider, resolvedConfig);
|
|
600
|
-
if (configCeiling) {
|
|
601
|
-
return configCeiling;
|
|
602
|
-
}
|
|
603
990
|
const projectCeiling = await resolveProjectStateCeiling(provider, projectPath, dependencies);
|
|
604
|
-
|
|
605
|
-
|
|
991
|
+
const baseCeiling = configCeiling ?? projectCeiling;
|
|
992
|
+
if (!baseCeiling) {
|
|
993
|
+
return null;
|
|
606
994
|
}
|
|
607
|
-
|
|
995
|
+
if (baseCeiling.mode === 'inherit') {
|
|
996
|
+
return baseCeiling;
|
|
997
|
+
}
|
|
998
|
+
if (baseCeiling.policy === 'uncapped') {
|
|
999
|
+
const preferredTier = uncappedPreferredTier(provider, role, preferredValue);
|
|
1000
|
+
if (preferredTier) {
|
|
1001
|
+
const matrixCell = resolveProviderMatrixCell(provider, preferredTier, resolvedConfig, projectCeiling?.matrix ?? null, escalationLevel);
|
|
1002
|
+
if (matrixCell) {
|
|
1003
|
+
return {
|
|
1004
|
+
...baseCeiling,
|
|
1005
|
+
cellSource: matrixCell.cellSource,
|
|
1006
|
+
target: matrixCell.target,
|
|
1007
|
+
selectionBranch: matrixCell.selectionBranch,
|
|
1008
|
+
warnings: [...baseCeiling.warnings, ...matrixCell.warnings],
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return baseCeiling;
|
|
1013
|
+
}
|
|
1014
|
+
const tier = policyTier(baseCeiling.policy);
|
|
1015
|
+
const matrixCell = resolveProviderMatrixCell(provider, tier, resolvedConfig, projectCeiling?.matrix ?? null, escalationLevel);
|
|
1016
|
+
if (matrixCell) {
|
|
1017
|
+
return {
|
|
1018
|
+
...baseCeiling,
|
|
1019
|
+
value: matrixCell.value,
|
|
1020
|
+
cellSource: matrixCell.cellSource,
|
|
1021
|
+
target: matrixCell.target,
|
|
1022
|
+
selectionBranch: matrixCell.selectionBranch,
|
|
1023
|
+
warnings: [...baseCeiling.warnings, ...matrixCell.warnings],
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
if (baseCeiling.policy !== 'legacy-ceiling' &&
|
|
1027
|
+
baseCeiling.value === null &&
|
|
1028
|
+
providerValueOrder(provider) === null) {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
return baseCeiling;
|
|
608
1032
|
}
|
|
609
1033
|
function policyLabel(policy) {
|
|
610
1034
|
if (policy === 'legacy-ceiling') {
|
|
@@ -625,6 +1049,23 @@ function writeHumanResolution(context, resolution) {
|
|
|
625
1049
|
if (providerResolution) {
|
|
626
1050
|
context.logger.info(`Mode: ${providerResolution.mode} (${providerResolution.mechanism})`);
|
|
627
1051
|
context.logger.info(`Selection: ${providerResolution.selection.selectionMode}`);
|
|
1052
|
+
if (providerResolution.target) {
|
|
1053
|
+
const details = [
|
|
1054
|
+
`harness=${providerResolution.target.harness}`,
|
|
1055
|
+
providerResolution.target.model
|
|
1056
|
+
? `model=${providerResolution.target.model}`
|
|
1057
|
+
: null,
|
|
1058
|
+
providerResolution.target.effort
|
|
1059
|
+
? `effort=${providerResolution.target.effort}`
|
|
1060
|
+
: null,
|
|
1061
|
+
]
|
|
1062
|
+
.filter(Boolean)
|
|
1063
|
+
.join(' ');
|
|
1064
|
+
const suffix = providerResolution.target.crossHarness
|
|
1065
|
+
? ' (cross-harness target; native dispatch args deferred)'
|
|
1066
|
+
: '';
|
|
1067
|
+
context.logger.info(`Dispatch target: ${details}${suffix}`);
|
|
1068
|
+
}
|
|
628
1069
|
}
|
|
629
1070
|
if (resolution.provider === 'codex') {
|
|
630
1071
|
context.logger.info(`Codex provider default effort: ${resolution.providerDefaultEffort}`);
|
|
@@ -681,18 +1122,47 @@ async function runDispatchCeilingResolve(context, dependencies, options) {
|
|
|
681
1122
|
process.exitCode = 1;
|
|
682
1123
|
}
|
|
683
1124
|
}
|
|
1125
|
+
async function runDispatchCeilingChoices(context, options) {
|
|
1126
|
+
const choices = getDispatchPolicyChoices();
|
|
1127
|
+
const format = (options.format ?? 'markdown').trim().toLowerCase();
|
|
1128
|
+
if (context.json || options.json) {
|
|
1129
|
+
context.logger.json({ status: 'ok', choices });
|
|
1130
|
+
process.exitCode = 0;
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
if (format !== 'markdown') {
|
|
1134
|
+
context.logger.error(`Invalid choices format "${options.format}". Valid formats: markdown.`);
|
|
1135
|
+
process.exitCode = 1;
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
context.logger.info(renderDispatchPolicyChoicesMarkdown(choices));
|
|
1139
|
+
process.exitCode = 0;
|
|
1140
|
+
}
|
|
684
1141
|
export function createProjectDispatchCeilingCommand(overrides = {}) {
|
|
685
1142
|
const dependencies = {
|
|
686
1143
|
...DEFAULT_DEPENDENCIES,
|
|
687
1144
|
...overrides,
|
|
688
1145
|
};
|
|
689
1146
|
const command = new Command('dispatch-ceiling').description('Resolve OAT project dispatch ceiling metadata');
|
|
1147
|
+
command.addCommand(new Command('choices')
|
|
1148
|
+
.description('Print canonical dispatch policy choices')
|
|
1149
|
+
.option('--format <format>', 'Output format: markdown', 'markdown')
|
|
1150
|
+
.option('--json', 'Output machine-readable JSON')
|
|
1151
|
+
.action(async (options, cmd) => {
|
|
1152
|
+
const globalOptions = readGlobalOptions(cmd);
|
|
1153
|
+
const context = dependencies.buildCommandContext({
|
|
1154
|
+
...globalOptions,
|
|
1155
|
+
json: globalOptions.json === true || options.json === true,
|
|
1156
|
+
});
|
|
1157
|
+
await runDispatchCeilingChoices(context, options);
|
|
1158
|
+
}));
|
|
690
1159
|
command.addCommand(new Command('resolve')
|
|
691
1160
|
.description('Resolve dispatch policy for a provider')
|
|
692
|
-
.requiredOption('--provider <provider>', 'Provider name: codex or
|
|
1161
|
+
.requiredOption('--provider <provider>', 'Provider name: codex, claude, or cursor are enforced; unregistered providers resolve as unsupported advisory')
|
|
693
1162
|
.option('--role <role>', 'Dispatch role for variant compilation: implementer (default) or reviewer')
|
|
694
1163
|
.option('--orchestrator-tier <tier>', 'Orchestrator tier, used to flag verify-on-upgrade for above-orchestrator requests')
|
|
695
1164
|
.option('--preferred <value>', 'Preferred implementer/fix dispatch value before applying the resolved policy')
|
|
1165
|
+
.option('--escalation-level <level>', 'Ordered route entry to select; 0 is the floor and higher values advance through escalation targets')
|
|
696
1166
|
.option('--project-path <path>', 'Read project-state policy from an explicit project path')
|
|
697
1167
|
.option('--preflight', 'Treat unresolved non-interactive resolution as an implementation block')
|
|
698
1168
|
.option('--non-interactive', 'Force non-interactive block behavior when the ceiling is unresolved')
|