@open-agent-toolkit/cli 0.2.72 → 0.2.73
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 +4 -3
- package/assets/agents/recon-worker.md +42 -8
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/skills/recon.md +114 -23
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
- package/assets/skills/recon/SKILL.md +103 -40
- package/assets/skills/recon/references/packet-contract.md +150 -67
- package/assets/skills/recon/references/profiles.md +38 -16
- package/assets/skills/recon/references/worker-contract.md +39 -8
- package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
- package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
- package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
- package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
- package/assets/skills/recon/scripts/render-packet.mjs +69 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
- package/assets/skills/subagent-orchestration/SKILL.md +18 -9
- package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
- package/package.json +2 -2
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authorityLevels,
|
|
3
|
+
profileRoutingPolicy,
|
|
4
|
+
taskClasses,
|
|
5
|
+
validateV2ProfileTopology,
|
|
6
|
+
waveModes,
|
|
7
|
+
} from './contracts.mjs';
|
|
8
|
+
|
|
9
|
+
const exactTargetFields = [
|
|
10
|
+
'provider',
|
|
11
|
+
'route',
|
|
12
|
+
'role',
|
|
13
|
+
'model',
|
|
14
|
+
'effort',
|
|
15
|
+
'reasoningMode',
|
|
16
|
+
'serviceTier',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const exactTargetFieldSet = new Set(exactTargetFields);
|
|
20
|
+
|
|
21
|
+
const economicalDefaults = Object.freeze({
|
|
22
|
+
map: Object.freeze({
|
|
23
|
+
assignment: 'Enumerate sources, files, or routes from explicit criteria.',
|
|
24
|
+
taskClass: 'mechanical-recon',
|
|
25
|
+
}),
|
|
26
|
+
gather: Object.freeze({
|
|
27
|
+
assignment: 'Extract requested observations with exact citations.',
|
|
28
|
+
taskClass: 'mechanical-recon',
|
|
29
|
+
}),
|
|
30
|
+
'redundant-gather': Object.freeze({
|
|
31
|
+
assignment: 'Independently repeat a bounded evidence extraction.',
|
|
32
|
+
taskClass: 'mechanical-recon',
|
|
33
|
+
}),
|
|
34
|
+
compile: Object.freeze({
|
|
35
|
+
assignment: 'Group and deduplicate dossiers while retaining conflicts.',
|
|
36
|
+
taskClass: 'mechanical-recon',
|
|
37
|
+
}),
|
|
38
|
+
'semantic-verification': Object.freeze({
|
|
39
|
+
assignment: 'Reopen named claims and check their specific source support.',
|
|
40
|
+
taskClass: 'mechanical-recon',
|
|
41
|
+
}),
|
|
42
|
+
'redundant-verification': Object.freeze({
|
|
43
|
+
assignment: 'Independently repeat bounded claim support checks.',
|
|
44
|
+
taskClass: 'mechanical-recon',
|
|
45
|
+
}),
|
|
46
|
+
adversarial: Object.freeze({
|
|
47
|
+
assignment: 'Search for counterexamples to a specified claim.',
|
|
48
|
+
taskClass: 'mechanical-recon',
|
|
49
|
+
}),
|
|
50
|
+
coverage: Object.freeze({
|
|
51
|
+
assignment:
|
|
52
|
+
'Compare evidence with an explicit question or source inventory.',
|
|
53
|
+
taskClass: 'mechanical-recon',
|
|
54
|
+
}),
|
|
55
|
+
reconciliation: Object.freeze({
|
|
56
|
+
assignment:
|
|
57
|
+
'Assemble agreement and disagreement without deciding implications.',
|
|
58
|
+
taskClass: 'mechanical-recon',
|
|
59
|
+
}),
|
|
60
|
+
'contradiction-resolution': Object.freeze({
|
|
61
|
+
assignment: 'Seek discriminating evidence for a named contradiction.',
|
|
62
|
+
taskClass: 'mechanical-recon',
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export class RoutingContractError extends Error {
|
|
67
|
+
constructor(code, message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = 'RoutingContractError';
|
|
70
|
+
this.code = code;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clone(value) {
|
|
75
|
+
return structuredClone(value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function deepFreeze(value) {
|
|
79
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
83
|
+
return Object.freeze(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function routingError(code, message) {
|
|
87
|
+
throw new RoutingContractError(code, message);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function assertObject(value, code, label) {
|
|
91
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
92
|
+
routingError(code, `${label} must be an object`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assertExactTarget(target, label = 'target') {
|
|
97
|
+
assertObject(target, 'INVALID_EXACT_TARGET', label);
|
|
98
|
+
for (const key of Object.keys(target)) {
|
|
99
|
+
if (!exactTargetFieldSet.has(key)) {
|
|
100
|
+
routingError(
|
|
101
|
+
'UNSUPPORTED_TARGET_CONTROL',
|
|
102
|
+
`${label} contains unsupported control ${key}`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const key of ['provider', 'route', 'role', 'model']) {
|
|
107
|
+
if (typeof target[key] !== 'string' || target[key].length === 0) {
|
|
108
|
+
routingError(
|
|
109
|
+
'INVALID_EXACT_TARGET',
|
|
110
|
+
`${label}.${key} must be a non-empty string`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const key of ['effort', 'reasoningMode', 'serviceTier']) {
|
|
115
|
+
if (
|
|
116
|
+
!Object.hasOwn(target, key) ||
|
|
117
|
+
(target[key] !== null &&
|
|
118
|
+
(typeof target[key] !== 'string' || target[key].length === 0))
|
|
119
|
+
) {
|
|
120
|
+
routingError(
|
|
121
|
+
'INVALID_EXACT_TARGET',
|
|
122
|
+
`${label}.${key} must be a non-empty string or null`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function exactTargetEqual(left, right) {
|
|
129
|
+
return exactTargetFields.every((field) => left[field] === right[field]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function assertPositiveInteger(value, label, minimum = 1) {
|
|
133
|
+
if (!Number.isInteger(value) || value < minimum) {
|
|
134
|
+
routingError(
|
|
135
|
+
'INVALID_ROUTING_LIMIT',
|
|
136
|
+
`${label} must be an integer greater than or equal to ${minimum}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function assertNonEmptyString(value, code, label) {
|
|
142
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
143
|
+
routingError(code, `${label} must be a non-empty string`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function assertClosedObject(value, allowedFields, code, label) {
|
|
148
|
+
assertObject(value, code, label);
|
|
149
|
+
for (const key of Object.keys(value)) {
|
|
150
|
+
if (!allowedFields.has(key)) {
|
|
151
|
+
routingError(code, `${label} contains unsupported field ${key}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function assertProposalExecution(manifest) {
|
|
157
|
+
assertObject(manifest, 'INVALID_ROUTING_PROPOSAL', 'manifest');
|
|
158
|
+
if (manifest.schemaVersion !== 2) {
|
|
159
|
+
routingError(
|
|
160
|
+
'UNSUPPORTED_ROUTING_VERSION',
|
|
161
|
+
`Unsupported routing schemaVersion ${manifest.schemaVersion}`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const execution = manifest.execution;
|
|
165
|
+
assertClosedObject(
|
|
166
|
+
execution,
|
|
167
|
+
new Set([
|
|
168
|
+
'target',
|
|
169
|
+
'authority',
|
|
170
|
+
'maxConcurrency',
|
|
171
|
+
'deadlineSeconds',
|
|
172
|
+
'retryLimit',
|
|
173
|
+
'waves',
|
|
174
|
+
'conditions',
|
|
175
|
+
'approval',
|
|
176
|
+
]),
|
|
177
|
+
'INVALID_ROUTING_PROPOSAL',
|
|
178
|
+
'execution',
|
|
179
|
+
);
|
|
180
|
+
const target = execution.target;
|
|
181
|
+
assertExactTarget(target, 'execution target');
|
|
182
|
+
if (!authorityLevels.includes(execution.authority)) {
|
|
183
|
+
routingError(
|
|
184
|
+
'INVALID_ROUTING_AUTHORITY',
|
|
185
|
+
'execution.authority must be provider-enforced or contract-enforced',
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
assertPositiveInteger(execution.maxConcurrency, 'maxConcurrency');
|
|
189
|
+
assertPositiveInteger(execution.deadlineSeconds, 'deadlineSeconds');
|
|
190
|
+
assertPositiveInteger(execution.retryLimit, 'retryLimit', 0);
|
|
191
|
+
if (!Array.isArray(execution.waves) || execution.waves.length === 0) {
|
|
192
|
+
routingError(
|
|
193
|
+
'INVALID_ROUTING_PROPOSAL',
|
|
194
|
+
'execution.waves must contain at least one wave',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const waveIds = new Set();
|
|
198
|
+
const laneIds = new Set();
|
|
199
|
+
const writeRoots = new Set();
|
|
200
|
+
for (const [index, wave] of execution.waves.entries()) {
|
|
201
|
+
assertClosedObject(
|
|
202
|
+
wave,
|
|
203
|
+
new Set([
|
|
204
|
+
'waveId',
|
|
205
|
+
'mode',
|
|
206
|
+
'taskClass',
|
|
207
|
+
'classFloor',
|
|
208
|
+
'selectionReason',
|
|
209
|
+
'target',
|
|
210
|
+
'lanes',
|
|
211
|
+
'conditional',
|
|
212
|
+
]),
|
|
213
|
+
'INVALID_ROUTING_WAVE',
|
|
214
|
+
`waves[${index}]`,
|
|
215
|
+
);
|
|
216
|
+
if (typeof wave.waveId !== 'string' || wave.waveId.length === 0) {
|
|
217
|
+
routingError(
|
|
218
|
+
'INVALID_ROUTING_WAVE',
|
|
219
|
+
`waves[${index}].waveId must be a non-empty string`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (waveIds.has(wave.waveId)) {
|
|
223
|
+
routingError('DUPLICATE_ROUTING_ID', `Duplicate wave ${wave.waveId}`);
|
|
224
|
+
}
|
|
225
|
+
waveIds.add(wave.waveId);
|
|
226
|
+
if (!waveModes.includes(wave.mode)) {
|
|
227
|
+
routingError('INVALID_WAVE_MODE', `Unknown wave mode ${wave.mode}`);
|
|
228
|
+
}
|
|
229
|
+
if (!taskClasses.includes(wave.taskClass)) {
|
|
230
|
+
routingError(
|
|
231
|
+
'INVALID_TASK_CLASS',
|
|
232
|
+
`Unknown task class ${wave.taskClass}`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (!taskClasses.includes(wave.classFloor)) {
|
|
236
|
+
routingError(
|
|
237
|
+
'INVALID_TASK_CLASS',
|
|
238
|
+
`Unknown class floor ${wave.classFloor}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
if (
|
|
242
|
+
taskClasses.indexOf(wave.taskClass) < taskClasses.indexOf(wave.classFloor)
|
|
243
|
+
) {
|
|
244
|
+
routingError(
|
|
245
|
+
'TASK_CLASS_BELOW_FLOOR',
|
|
246
|
+
`Wave ${wave.waveId} task class is below its class floor`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (
|
|
250
|
+
typeof wave.selectionReason !== 'string' ||
|
|
251
|
+
wave.selectionReason.trim().length === 0
|
|
252
|
+
) {
|
|
253
|
+
routingError(
|
|
254
|
+
'MISSING_SELECTION_REASON',
|
|
255
|
+
`Wave ${wave.waveId} requires a substantive selection reason`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (Object.hasOwn(wave, 'target')) {
|
|
259
|
+
assertExactTarget(wave.target, `wave ${wave.waveId} target`);
|
|
260
|
+
}
|
|
261
|
+
if (!Array.isArray(wave.lanes) || wave.lanes.length === 0) {
|
|
262
|
+
routingError(
|
|
263
|
+
'INVALID_ROUTING_WAVE',
|
|
264
|
+
`Wave ${wave.waveId} must contain at least one lane`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (typeof wave.conditional !== 'boolean') {
|
|
268
|
+
routingError(
|
|
269
|
+
'INVALID_ROUTING_WAVE',
|
|
270
|
+
`Wave ${wave.waveId}.conditional must be a boolean`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
for (const lane of wave.lanes) {
|
|
274
|
+
assertClosedObject(
|
|
275
|
+
lane,
|
|
276
|
+
new Set(['laneId', 'scope', 'writeRoot']),
|
|
277
|
+
'INVALID_ROUTING_LANE',
|
|
278
|
+
`Wave ${wave.waveId} lane`,
|
|
279
|
+
);
|
|
280
|
+
assertNonEmptyString(
|
|
281
|
+
lane.laneId,
|
|
282
|
+
'INVALID_ROUTING_LANE',
|
|
283
|
+
`Wave ${wave.waveId} laneId`,
|
|
284
|
+
);
|
|
285
|
+
if (laneIds.has(lane.laneId)) {
|
|
286
|
+
routingError('DUPLICATE_ROUTING_ID', `Duplicate lane ${lane.laneId}`);
|
|
287
|
+
}
|
|
288
|
+
laneIds.add(lane.laneId);
|
|
289
|
+
assertNonEmptyString(
|
|
290
|
+
lane.scope,
|
|
291
|
+
'INVALID_ROUTING_LANE',
|
|
292
|
+
`Lane ${lane.laneId}.scope`,
|
|
293
|
+
);
|
|
294
|
+
assertNonEmptyString(
|
|
295
|
+
lane.writeRoot,
|
|
296
|
+
'INVALID_ROUTING_LANE',
|
|
297
|
+
`Lane ${lane.laneId}.writeRoot`,
|
|
298
|
+
);
|
|
299
|
+
if (
|
|
300
|
+
lane.writeRoot.startsWith('/') ||
|
|
301
|
+
lane.writeRoot.split('/').some((segment) => segment === '..')
|
|
302
|
+
) {
|
|
303
|
+
routingError(
|
|
304
|
+
'INVALID_WRITE_ROOT',
|
|
305
|
+
`Lane ${lane.laneId}.writeRoot must be packet-relative`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (writeRoots.has(lane.writeRoot)) {
|
|
309
|
+
routingError(
|
|
310
|
+
'DUPLICATE_WAVE_OUTPUT',
|
|
311
|
+
`Wave output ${lane.writeRoot} is assigned more than once`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
writeRoots.add(lane.writeRoot);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
assertV2ProposalTopology(manifest, execution);
|
|
319
|
+
return { execution };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function assertV2ProposalTopology(manifest, execution) {
|
|
323
|
+
const [error] = validateV2ProfileTopology(manifest, execution);
|
|
324
|
+
if (error) routingError(error.code, error.message);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function validateApproval(execution) {
|
|
328
|
+
const approval = execution.approval;
|
|
329
|
+
if (
|
|
330
|
+
!approval ||
|
|
331
|
+
approval.type !== 'explicit-user-approval' ||
|
|
332
|
+
typeof approval.approvedAt !== 'string' ||
|
|
333
|
+
!Number.isFinite(Date.parse(approval.approvedAt))
|
|
334
|
+
) {
|
|
335
|
+
routingError(
|
|
336
|
+
'MISSING_APPROVAL_ENVELOPE',
|
|
337
|
+
'Exact target checks require valid explicit approval evidence',
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return approval;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function economicalDefaultForMode(mode) {
|
|
344
|
+
const value = economicalDefaults[mode];
|
|
345
|
+
if (!value) routingError('INVALID_WAVE_MODE', `Unknown wave mode ${mode}`);
|
|
346
|
+
return clone(value);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function economicalRoutingDefaults() {
|
|
350
|
+
return deepFreeze(clone(economicalDefaults));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function resolveEffectiveWaveTarget(execution, wave) {
|
|
354
|
+
const target = wave.target ?? execution.target;
|
|
355
|
+
if (!target) {
|
|
356
|
+
throw new TypeError('Recon routing requires an effective exact target');
|
|
357
|
+
}
|
|
358
|
+
return clone(target);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function normalizeManifestRouting(manifest) {
|
|
362
|
+
if (manifest.schemaVersion !== 2) {
|
|
363
|
+
throw new TypeError(
|
|
364
|
+
`Unsupported recon manifest schemaVersion ${manifest.schemaVersion}`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const execution = manifest.execution;
|
|
368
|
+
const routing = {
|
|
369
|
+
sourceSchemaVersion: 2,
|
|
370
|
+
target: clone(execution.target),
|
|
371
|
+
authority: execution.authority,
|
|
372
|
+
maxConcurrency: execution.maxConcurrency,
|
|
373
|
+
deadlineSeconds: execution.deadlineSeconds,
|
|
374
|
+
retryLimit: execution.retryLimit,
|
|
375
|
+
waves: execution.waves.map((wave) => ({
|
|
376
|
+
...clone(wave),
|
|
377
|
+
target: resolveEffectiveWaveTarget(execution, wave),
|
|
378
|
+
})),
|
|
379
|
+
conditions: clone(execution.conditions),
|
|
380
|
+
approval: clone(execution.approval),
|
|
381
|
+
};
|
|
382
|
+
return deepFreeze(routing);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function createRoutingPreview(manifest) {
|
|
386
|
+
const { execution } = assertProposalExecution(manifest);
|
|
387
|
+
if (execution.approval) validateApproval(execution);
|
|
388
|
+
const waves = execution.waves.map((wave) => {
|
|
389
|
+
const defaultPolicy = economicalDefaultForMode(wave.mode);
|
|
390
|
+
const effectiveTarget = resolveEffectiveWaveTarget(execution, wave);
|
|
391
|
+
return {
|
|
392
|
+
waveId: wave.waveId,
|
|
393
|
+
mode: wave.mode,
|
|
394
|
+
assignment: defaultPolicy.assignment,
|
|
395
|
+
taskClass: wave.taskClass,
|
|
396
|
+
classFloor: wave.classFloor,
|
|
397
|
+
laneCount: wave.lanes.length,
|
|
398
|
+
target: clone(effectiveTarget),
|
|
399
|
+
selectionReason: wave.selectionReason,
|
|
400
|
+
lanes: clone(wave.lanes),
|
|
401
|
+
conditional: wave.conditional === true,
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
const requestedProfile = manifest.run?.requestedProfile ?? null;
|
|
405
|
+
const profilePolicy = profileRoutingPolicy[requestedProfile];
|
|
406
|
+
const laneCount = waves.reduce((count, wave) => count + wave.laneCount, 0);
|
|
407
|
+
const countedAdaptiveLaneCount = waves.reduce(
|
|
408
|
+
(count, wave) =>
|
|
409
|
+
count +
|
|
410
|
+
(profilePolicy.countedLaneModes.includes(wave.mode) ? wave.laneCount : 0),
|
|
411
|
+
0,
|
|
412
|
+
);
|
|
413
|
+
const profileCaps = {
|
|
414
|
+
maxLanes: profilePolicy.lanes,
|
|
415
|
+
maxConcurrency: profilePolicy.concurrency,
|
|
416
|
+
maxConditions: profilePolicy.conditions,
|
|
417
|
+
};
|
|
418
|
+
return deepFreeze({
|
|
419
|
+
schemaVersion: manifest.schemaVersion,
|
|
420
|
+
requestedProfile,
|
|
421
|
+
profileCaps,
|
|
422
|
+
approvalState: execution.approval ? 'recorded' : 'draft',
|
|
423
|
+
authority: execution.authority,
|
|
424
|
+
waves,
|
|
425
|
+
conditions: clone(execution.conditions),
|
|
426
|
+
limits: {
|
|
427
|
+
waveCount: waves.length,
|
|
428
|
+
laneCount,
|
|
429
|
+
countedAdaptiveLaneCount,
|
|
430
|
+
conditionCount: execution.conditions.length,
|
|
431
|
+
maxConcurrency: execution.maxConcurrency,
|
|
432
|
+
deadlineSeconds: execution.deadlineSeconds,
|
|
433
|
+
retryLimit: execution.retryLimit,
|
|
434
|
+
worstCaseLaneAttempts: laneCount * (execution.retryLimit + 1),
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function encodeMarkdownValue(value) {
|
|
440
|
+
return [...String(value)]
|
|
441
|
+
.map((character) =>
|
|
442
|
+
/^[\p{L}\p{N} .:/@%+-]$/u.test(character)
|
|
443
|
+
? character
|
|
444
|
+
: `&#${character.codePointAt(0)};`,
|
|
445
|
+
)
|
|
446
|
+
.join('');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function renderRoutingPreview(preview, format = 'markdown') {
|
|
450
|
+
if (format === 'json') return `${JSON.stringify(preview, null, 2)}\n`;
|
|
451
|
+
if (format !== 'markdown') {
|
|
452
|
+
routingError(
|
|
453
|
+
'UNSUPPORTED_PREVIEW_FORMAT',
|
|
454
|
+
`Unsupported preview format ${format}`,
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const lines = [
|
|
458
|
+
'# Recon routing proposal',
|
|
459
|
+
'',
|
|
460
|
+
`Approval: ${encodeMarkdownValue(preview.approvalState)}`,
|
|
461
|
+
`Authority: ${encodeMarkdownValue(preview.authority)}`,
|
|
462
|
+
`Requested profile: ${encodeMarkdownValue(preview.requestedProfile)}`,
|
|
463
|
+
'',
|
|
464
|
+
'| Wave | Mode | Assignment | Class / floor | Lanes | Exact target | Reason | Conditional |',
|
|
465
|
+
'| --- | --- | --- | --- | ---: | --- | --- | --- |',
|
|
466
|
+
];
|
|
467
|
+
for (const wave of preview.waves) {
|
|
468
|
+
const target = exactTargetFields
|
|
469
|
+
.map(
|
|
470
|
+
(field) =>
|
|
471
|
+
`${field}=${encodeMarkdownValue(wave.target[field] ?? 'null')}`,
|
|
472
|
+
)
|
|
473
|
+
.join(', ');
|
|
474
|
+
lines.push(
|
|
475
|
+
`| ${encodeMarkdownValue(wave.waveId)} | ${encodeMarkdownValue(wave.mode)} | ${encodeMarkdownValue(wave.assignment)} | ${encodeMarkdownValue(wave.taskClass)} / ${encodeMarkdownValue(wave.classFloor)} | ${encodeMarkdownValue(wave.laneCount)} | ${target} | ${encodeMarkdownValue(wave.selectionReason)} | ${encodeMarkdownValue(wave.conditional ? 'yes' : 'no')} |`,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
lines.push(
|
|
479
|
+
'',
|
|
480
|
+
'## Lane topology',
|
|
481
|
+
'',
|
|
482
|
+
'| Wave | Lane | Scope | Write root |',
|
|
483
|
+
'| --- | --- | --- | --- |',
|
|
484
|
+
);
|
|
485
|
+
for (const wave of preview.waves) {
|
|
486
|
+
for (const lane of wave.lanes) {
|
|
487
|
+
lines.push(
|
|
488
|
+
`| ${encodeMarkdownValue(wave.waveId)} | ${encodeMarkdownValue(lane.laneId)} | ${encodeMarkdownValue(lane.scope)} | ${encodeMarkdownValue(lane.writeRoot)} |`,
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
lines.push('', '## Conditional topology', '');
|
|
493
|
+
if (preview.conditions.length === 0) {
|
|
494
|
+
lines.push('None.');
|
|
495
|
+
} else {
|
|
496
|
+
lines.push(
|
|
497
|
+
'| Condition | Destination | After waves | Predicate | Max activations |',
|
|
498
|
+
'| --- | --- | --- | --- | ---: |',
|
|
499
|
+
);
|
|
500
|
+
for (const condition of preview.conditions) {
|
|
501
|
+
const afterWaves = condition.afterWaveIds
|
|
502
|
+
.map((waveId) => encodeMarkdownValue(waveId))
|
|
503
|
+
.join(', ');
|
|
504
|
+
lines.push(
|
|
505
|
+
`| ${encodeMarkdownValue(condition.conditionId)} | ${encodeMarkdownValue(condition.destinationWaveId)} | ${afterWaves} | ${encodeMarkdownValue(condition.predicate)} | ${encodeMarkdownValue(condition.maxActivations)} |`,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
lines.push(
|
|
510
|
+
'',
|
|
511
|
+
'## Worst-case limits',
|
|
512
|
+
'',
|
|
513
|
+
`- Waves: ${encodeMarkdownValue(preview.limits.waveCount)}`,
|
|
514
|
+
`- Lanes: ${encodeMarkdownValue(preview.limits.laneCount)}`,
|
|
515
|
+
`- Conditions: ${encodeMarkdownValue(preview.limits.conditionCount)}`,
|
|
516
|
+
`- Concurrency: ${encodeMarkdownValue(preview.limits.maxConcurrency)}`,
|
|
517
|
+
`- Deadline seconds: ${encodeMarkdownValue(preview.limits.deadlineSeconds)}`,
|
|
518
|
+
`- Retry limit: ${encodeMarkdownValue(preview.limits.retryLimit)}`,
|
|
519
|
+
`- Lane attempts: ${encodeMarkdownValue(preview.limits.worstCaseLaneAttempts)}`,
|
|
520
|
+
);
|
|
521
|
+
if (preview.profileCaps) {
|
|
522
|
+
lines.push(
|
|
523
|
+
`- Profile adaptive-lane cap: ${encodeMarkdownValue(preview.profileCaps.maxLanes)} (counted lanes: ${encodeMarkdownValue(preview.limits.countedAdaptiveLaneCount)} of ${encodeMarkdownValue(preview.limits.laneCount)} total)`,
|
|
524
|
+
`- Profile concurrency cap: ${encodeMarkdownValue(preview.profileCaps.maxConcurrency)}`,
|
|
525
|
+
`- Profile condition cap: ${encodeMarkdownValue(preview.profileCaps.maxConditions)}`,
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
lines.push('');
|
|
529
|
+
return `${lines.join('\n')}\n`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export function checkApprovedWaveTarget(manifest, waveId, candidateTarget) {
|
|
533
|
+
const { execution } = assertProposalExecution(manifest);
|
|
534
|
+
validateApproval(execution);
|
|
535
|
+
if (typeof waveId !== 'string' || waveId.length === 0) {
|
|
536
|
+
routingError('UNKNOWN_APPROVED_WAVE', 'A non-empty wave ID is required');
|
|
537
|
+
}
|
|
538
|
+
const wave = execution.waves.find((item) => item.waveId === waveId);
|
|
539
|
+
if (!wave) {
|
|
540
|
+
routingError('UNKNOWN_APPROVED_WAVE', `Unknown approved wave ${waveId}`);
|
|
541
|
+
}
|
|
542
|
+
assertExactTarget(candidateTarget, 'constructed target');
|
|
543
|
+
const approvedTarget = resolveEffectiveWaveTarget(execution, wave);
|
|
544
|
+
if (!exactTargetEqual(approvedTarget, candidateTarget)) {
|
|
545
|
+
routingError(
|
|
546
|
+
'CONSTRUCTED_TARGET_MISMATCH',
|
|
547
|
+
`Constructed target does not exactly match approved wave ${waveId}`,
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return deepFreeze({
|
|
551
|
+
valid: true,
|
|
552
|
+
waveId,
|
|
553
|
+
target: clone(approvedTarget),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export function controllerEscalationDisposition({
|
|
558
|
+
outcome,
|
|
559
|
+
foreseeable,
|
|
560
|
+
approvedTargetAdequate,
|
|
561
|
+
}) {
|
|
562
|
+
if (outcome !== 'reconciliation-needs-judgment') {
|
|
563
|
+
routingError(
|
|
564
|
+
'INVALID_CONTROLLER_ESCALATION',
|
|
565
|
+
`Unknown controller escalation outcome ${outcome}`,
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
if (foreseeable === true) {
|
|
569
|
+
return deepFreeze({
|
|
570
|
+
action: 'select-before-approval',
|
|
571
|
+
preserveCompletedWork: true,
|
|
572
|
+
launchAllowed: false,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
if (approvedTargetAdequate === true) {
|
|
576
|
+
return deepFreeze({
|
|
577
|
+
action: 'use-approved-terminal',
|
|
578
|
+
preserveCompletedWork: true,
|
|
579
|
+
launchAllowed: true,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
return deepFreeze({
|
|
583
|
+
action: 'renew-approval-or-new-run',
|
|
584
|
+
preserveCompletedWork: true,
|
|
585
|
+
launchAllowed: false,
|
|
586
|
+
gap: 'unresolved-out-of-envelope',
|
|
587
|
+
});
|
|
588
|
+
}
|
|
@@ -27,6 +27,7 @@ export function createValidatedRun({
|
|
|
27
27
|
filesystemIdentities,
|
|
28
28
|
canonicalByteDigests,
|
|
29
29
|
manifest,
|
|
30
|
+
routing,
|
|
30
31
|
ledger,
|
|
31
32
|
artifactsById,
|
|
32
33
|
exactEvidence,
|
|
@@ -44,6 +45,7 @@ export function createValidatedRun({
|
|
|
44
45
|
.map(([path, digest]) => ({ path, digest }))
|
|
45
46
|
.sort((left, right) => left.path.localeCompare(right.path)),
|
|
46
47
|
manifest: clone(manifest),
|
|
48
|
+
routing: clone(routing),
|
|
47
49
|
ledger: clone(ledger),
|
|
48
50
|
artifacts: [...artifactsById.entries()]
|
|
49
51
|
.map(([id, artifact]) => ({
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
checkApprovedWaveTarget,
|
|
8
|
+
createRoutingPreview,
|
|
9
|
+
renderRoutingPreview,
|
|
10
|
+
RoutingContractError,
|
|
11
|
+
} from './lib/routing.mjs';
|
|
12
|
+
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const options = { format: 'markdown' };
|
|
15
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
16
|
+
const argument = argv[index];
|
|
17
|
+
if (
|
|
18
|
+
!['--manifest', '--format', '--wave', '--check-target'].includes(argument)
|
|
19
|
+
) {
|
|
20
|
+
throw new RoutingContractError(
|
|
21
|
+
'INVALID_ARGUMENT',
|
|
22
|
+
`Unknown argument ${argument}`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const value = argv[index + 1];
|
|
26
|
+
if (!value || value.startsWith('--')) {
|
|
27
|
+
throw new RoutingContractError(
|
|
28
|
+
'INVALID_ARGUMENT',
|
|
29
|
+
`${argument} requires a value`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
options[
|
|
33
|
+
{
|
|
34
|
+
'--manifest': 'manifestPath',
|
|
35
|
+
'--format': 'format',
|
|
36
|
+
'--wave': 'waveId',
|
|
37
|
+
'--check-target': 'targetPath',
|
|
38
|
+
}[argument]
|
|
39
|
+
] = value;
|
|
40
|
+
index += 1;
|
|
41
|
+
}
|
|
42
|
+
if (!options.manifestPath) {
|
|
43
|
+
throw new RoutingContractError(
|
|
44
|
+
'INVALID_ARGUMENT',
|
|
45
|
+
'--manifest is required',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if ((options.waveId === undefined) !== (options.targetPath === undefined)) {
|
|
49
|
+
throw new RoutingContractError(
|
|
50
|
+
'INVALID_ARGUMENT',
|
|
51
|
+
'--wave and --check-target must be provided together',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return options;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function readJson(path, label) {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new RoutingContractError(
|
|
62
|
+
'INVALID_JSON',
|
|
63
|
+
`Unable to read ${label}: ${error.message}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
69
|
+
const options = parseArgs(argv);
|
|
70
|
+
const manifest = await readJson(options.manifestPath, 'manifest');
|
|
71
|
+
if (options.waveId !== undefined) {
|
|
72
|
+
const candidate = await readJson(options.targetPath, 'constructed target');
|
|
73
|
+
const result = checkApprovedWaveTarget(manifest, options.waveId, candidate);
|
|
74
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
process.stdout.write(
|
|
78
|
+
renderRoutingPreview(createRoutingPreview(manifest), options.format),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
|
|
83
|
+
main().catch((error) => {
|
|
84
|
+
const code = error instanceof RoutingContractError ? error.code : 'ERROR';
|
|
85
|
+
process.stderr.write(`${code}: ${error.message}\n`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
});
|
|
88
|
+
}
|