@open-agent-toolkit/cli 0.1.48 → 0.1.51

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 (53) hide show
  1. package/assets/agents/oat-phase-implementer.md +16 -5
  2. package/assets/docs/cli-utilities/configuration.md +41 -0
  3. package/assets/docs/cli-utilities/workflow-gates.md +18 -1
  4. package/assets/docs/provider-sync/commands.md +29 -1
  5. package/assets/docs/provider-sync/providers.md +18 -5
  6. package/assets/docs/provider-sync/scope-and-surface.md +13 -0
  7. package/assets/docs/workflows/projects/dispatch-ceiling.md +59 -7
  8. package/assets/docs/workflows/projects/implementation-execution.md +63 -3
  9. package/assets/docs/workflows/projects/reviews.md +18 -1
  10. package/assets/public-package-versions.json +4 -4
  11. package/assets/skills/oat-agent-instructions-analyze/references/docs/provider-reference.md +8 -2
  12. package/assets/skills/oat-agent-instructions-apply/references/docs/provider-reference.md +8 -2
  13. package/assets/skills/oat-project-implement/SKILL.md +141 -59
  14. package/assets/skills/oat-project-review-provide/SKILL.md +44 -10
  15. package/assets/skills/oat-project-review-provide-remote/SKILL.md +28 -1
  16. package/dist/commands/config/index.d.ts +3 -0
  17. package/dist/commands/config/index.d.ts.map +1 -1
  18. package/dist/commands/config/index.js +70 -128
  19. package/dist/commands/doctor/index.d.ts +3 -0
  20. package/dist/commands/doctor/index.d.ts.map +1 -1
  21. package/dist/commands/doctor/index.js +205 -195
  22. package/dist/commands/gate/index.d.ts.map +1 -1
  23. package/dist/commands/gate/index.js +72 -1
  24. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  25. package/dist/commands/project/dispatch-ceiling/index.js +224 -112
  26. package/dist/commands/providers/codex/materialize.d.ts.map +1 -1
  27. package/dist/commands/providers/codex/materialize.js +31 -15
  28. package/dist/config/dispatch-matrix.d.ts +59 -0
  29. package/dist/config/dispatch-matrix.d.ts.map +1 -0
  30. package/dist/config/dispatch-matrix.js +264 -0
  31. package/dist/config/oat-config.d.ts +2 -28
  32. package/dist/config/oat-config.d.ts.map +1 -1
  33. package/dist/config/oat-config.js +8 -144
  34. package/dist/config/resolve.d.ts.map +1 -1
  35. package/dist/config/resolve.js +2 -1
  36. package/dist/providers/codex/codec/config-merge.d.ts +8 -2
  37. package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
  38. package/dist/providers/codex/codec/config-merge.js +17 -2
  39. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  40. package/dist/providers/codex/codec/sync-extension.js +14 -4
  41. package/dist/providers/identity/availability.d.ts +12 -0
  42. package/dist/providers/identity/availability.d.ts.map +1 -1
  43. package/dist/providers/identity/availability.js +72 -29
  44. package/dist/providers/identity/dispatch-report.d.ts +124 -0
  45. package/dist/providers/identity/dispatch-report.d.ts.map +1 -0
  46. package/dist/providers/identity/dispatch-report.js +285 -0
  47. package/dist/providers/identity/dispatch-validation.d.ts +28 -0
  48. package/dist/providers/identity/dispatch-validation.d.ts.map +1 -0
  49. package/dist/providers/identity/dispatch-validation.js +149 -0
  50. package/dist/providers/identity/stamp.d.ts +2 -0
  51. package/dist/providers/identity/stamp.d.ts.map +1 -1
  52. package/dist/providers/identity/stamp.js +3 -1
  53. package/package.json +2 -2
@@ -10,7 +10,7 @@ import { validateRealPathWithinScope } from '../../../fs/paths.js';
10
10
  import TOML from '@iarna/toml';
11
11
  import YAML from 'yaml';
12
12
  import { SUPPORTED_CODEX_BASE_ROLES, SUPPORTED_CODEX_ROLE_TARGETS, } from './catalog.js';
13
- import { mergeCodexConfig } from './config-merge.js';
13
+ import { mergeCodexConfig, readCodexMaxDepth, } from './config-merge.js';
14
14
  import { exportCanonicalAgentToCodexRole } from './export-to-codex.js';
15
15
  import { materializeCodexRole } from './materialize.js';
16
16
  import { isOatManagedCodexRoleFile, readOatManagedCodexRoleOwner, withOatManagedCodexRoleOwner, } from './shared.js';
@@ -273,6 +273,14 @@ function isUserCodexScope(scopeRoot, options) {
273
273
  }
274
274
  return resolve(scopeRoot) === resolve(options.userConfigDir, '..');
275
275
  }
276
+ async function readInheritedCodexMaxDepth(scopeRoot, options) {
277
+ if (!options.userConfigDir || isUserCodexScope(scopeRoot, options)) {
278
+ return undefined;
279
+ }
280
+ const userScopeRoot = resolve(options.userConfigDir, '..');
281
+ const userConfigContent = await readOptionalFile(configPath(userScopeRoot));
282
+ return readCodexMaxDepth(userConfigContent) ?? undefined;
283
+ }
276
284
  async function readCodexMaterializationTargets(scopeRoot, options = {}) {
277
285
  const targets = new Map();
278
286
  const effectiveConfig = await resolveEffectiveConfig(scopeRoot, options.userConfigDir ?? join(scopeRoot, '.oat', '__no-user-config__'), options.env);
@@ -395,9 +403,6 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
395
403
  const desiredRoleNames = new Set(desiredRoles.map((role) => role.roleName));
396
404
  const existingConfigPath = configPath(scopeRoot);
397
405
  const existingConfigContent = await readOptionalFile(existingConfigPath);
398
- const staleRoles = isPartialSync
399
- ? []
400
- : await collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames, isUserCodexScope(scopeRoot, options) ? 'user-config' : 'project-config', !isUserCodexScope(scopeRoot, options));
401
406
  if (isPartialSync && desiredRoles.length === 0) {
402
407
  return {
403
408
  operations: [],
@@ -405,6 +410,10 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
405
410
  aggregateConfigHash: hashContent(existingConfigContent ?? ''),
406
411
  };
407
412
  }
413
+ const inheritedMaxDepth = await readInheritedCodexMaxDepth(scopeRoot, options);
414
+ const staleRoles = isPartialSync
415
+ ? []
416
+ : await collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames, isUserCodexScope(scopeRoot, options) ? 'user-config' : 'project-config', !isUserCodexScope(scopeRoot, options));
408
417
  const operations = [];
409
418
  for (const role of desiredRoles) {
410
419
  const existingRoleContent = await readOptionalFile(role.rolePath);
@@ -456,6 +465,7 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
456
465
  existingContent: existingConfigContent,
457
466
  desiredRoles: normalizeManagedRolesConfig(desiredRoles),
458
467
  staleManagedRoles: staleRoles,
468
+ inheritedMaxDepth,
459
469
  });
460
470
  operations.push({
461
471
  action: existingConfigContent === null
@@ -39,7 +39,19 @@ export interface ValidateMatrixCellOptions {
39
39
  effort?: string;
40
40
  } | null;
41
41
  }
42
+ export interface CursorTaskProbeResult extends MatrixCellAvailabilityResult {
43
+ decisive: boolean;
44
+ evidence: 'task-probe' | 'subagent-allow-list' | 'none';
45
+ }
46
+ export interface CursorCatalogResult {
47
+ status: 'resolved' | 'unavailable' | 'failed';
48
+ candidates: string[];
49
+ sourceCommand: 'models' | 'list-models' | null;
50
+ diagnostic: string | null;
51
+ }
42
52
  export declare function normalizeMatrixCellAvailability(result: MatrixCellAvailabilityResponse): MatrixCellAvailabilityResult;
53
+ export declare function probeCursorSubagentModel(value: string, options: ValidateMatrixCellOptions): Promise<CursorTaskProbeResult>;
54
+ export declare function resolveCursorModelCatalog(options: ValidateMatrixCellOptions): Promise<CursorCatalogResult>;
43
55
  export declare function validateCursorSubagentModel(value: string, options: ValidateMatrixCellOptions): Promise<MatrixCellAvailabilityResult>;
44
56
  export declare function validateMatrixCell(provider: string, value: string, options: ValidateMatrixCellOptions): Promise<MatrixCellAvailabilityResponse>;
45
57
  //# sourceMappingURL=availability.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/availability.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,CAAC;AAE/E,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,sBAAsB,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,8BAA8B,GACtC,sBAAsB,GACtB,4BAA4B,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,cAAc,EAAE,CACd,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,eAAe,KACrB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC;CACV;AAWD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,8BAA8B,GACrC,4BAA4B,CAE9B;AA+VD,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,4BAA4B,CAAC,CAsEvC;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,8BAA8B,CAAC,CA2CzC"}
1
+ {"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/availability.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,CAAC;AAE/E,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,sBAAsB,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,8BAA8B,GACtC,sBAAsB,GACtB,4BAA4B,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,cAAc,EAAE,CACd,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,eAAe,KACrB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,qBAAsB,SAAQ,4BAA4B;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,YAAY,GAAG,qBAAqB,GAAG,MAAM,CAAC;CACzD;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,GAAG,aAAa,GAAG,QAAQ,CAAC;IAC9C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,QAAQ,GAAG,aAAa,GAAG,IAAI,CAAC;IAC/C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAWD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,8BAA8B,GACrC,4BAA4B,CAE9B;AAwWD,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAwChC;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAkD9B;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,4BAA4B,CAAC,CAqBvC;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,8BAA8B,CAAC,CAuCzC"}
@@ -78,15 +78,6 @@ function parseCursorCatalog(stdout) {
78
78
  }
79
79
  return entries;
80
80
  }
81
- function availabilityFromCursorCatalog(value, stdout) {
82
- const entries = parseCursorCatalog(stdout);
83
- if (entries.length === 0) {
84
- return null;
85
- }
86
- return entries.some((entry) => entry.slug === value)
87
- ? 'valid'
88
- : 'unknown-value';
89
- }
90
81
  const CURSOR_SUBAGENT_PROBE_SENTINEL = 'OAT_CURSOR_SUBAGENT_MODEL_VALID';
91
82
  function hasCursorSubagentProbeSentinel(stdout) {
92
83
  return stdout
@@ -150,6 +141,20 @@ function cursorCatalogContextMessage(value, availability) {
150
141
  }
151
142
  return undefined;
152
143
  }
144
+ function availabilityDependencies(options) {
145
+ return {
146
+ ...DEFAULT_DEPENDENCIES,
147
+ env: options.env ?? DEFAULT_DEPENDENCIES.env,
148
+ ...options.dependencies,
149
+ };
150
+ }
151
+ function cursorCatalogDiagnostic(results) {
152
+ const messages = unique(results
153
+ .flatMap((result) => [result.stderr, result.stdout])
154
+ .map((message) => message.trim())
155
+ .filter((message) => message.length > 0));
156
+ return messages.length > 0 ? messages.join('\n') : null;
157
+ }
153
158
  function isRecord(value) {
154
159
  return typeof value === 'object' && value !== null && !Array.isArray(value);
155
160
  }
@@ -271,18 +276,18 @@ async function validateCodexCell(value, cwd, dependencies, target) {
271
276
  }
272
277
  return { availability: 'valid' };
273
278
  }
274
- export async function validateCursorSubagentModel(value, options) {
275
- const dependencies = {
276
- ...DEFAULT_DEPENDENCIES,
277
- env: options.env ?? DEFAULT_DEPENDENCIES.env,
278
- ...options.dependencies,
279
- };
279
+ export async function probeCursorSubagentModel(value, options) {
280
+ const dependencies = availabilityDependencies(options);
280
281
  const env = dependencies.env ?? process.env;
281
282
  const runOptions = { cwd: options.cwd, env };
282
283
  const probeResult = await dependencies.runCursorAgent(cursorSubagentProbeArgs(value, env), runOptions);
283
284
  const probeOutput = `${probeResult.stdout}\n${probeResult.stderr}`;
284
285
  if (probeResult.ok && hasCursorSubagentProbeSentinel(probeResult.stdout)) {
285
- return { availability: 'valid' };
286
+ return {
287
+ availability: 'valid',
288
+ decisive: true,
289
+ evidence: 'task-probe',
290
+ };
286
291
  }
287
292
  const allowedValues = parseCursorAllowedSubagentModels(probeOutput);
288
293
  if (allowedValues.length > 0) {
@@ -295,28 +300,70 @@ export async function validateCursorSubagentModel(value, options) {
295
300
  message: availability === 'valid'
296
301
  ? cursorAllowedModelsMessage(allowedValues)
297
302
  : cursorRejectedMessage(allowedValues),
303
+ decisive: true,
304
+ evidence: 'subagent-allow-list',
298
305
  };
299
306
  }
307
+ return {
308
+ availability: 'unvalidated',
309
+ decisive: false,
310
+ evidence: 'none',
311
+ };
312
+ }
313
+ export async function resolveCursorModelCatalog(options) {
314
+ const dependencies = availabilityDependencies(options);
315
+ const env = dependencies.env ?? process.env;
316
+ const runOptions = { cwd: options.cwd, env };
317
+ const attempts = [];
300
318
  const modelsResult = await dependencies.runCursorAgent([...apiKeyArgs(env), 'models'], runOptions);
319
+ attempts.push(modelsResult);
301
320
  if (modelsResult.ok) {
302
- const availability = availabilityFromCursorCatalog(value, modelsResult.stdout);
303
- if (availability !== null) {
321
+ const candidates = parseCursorCatalog(modelsResult.stdout).map((entry) => entry.slug);
322
+ if (candidates.length > 0) {
304
323
  return {
305
- availability: availability === 'valid' ? 'unvalidated' : 'unknown-value',
306
- message: cursorCatalogContextMessage(value, availability),
324
+ status: 'resolved',
325
+ candidates,
326
+ sourceCommand: 'models',
327
+ diagnostic: null,
307
328
  };
308
329
  }
309
330
  }
310
331
  const listResult = await dependencies.runCursorAgent([...apiKeyArgs(env), '--list-models'], runOptions);
332
+ attempts.push(listResult);
311
333
  if (listResult.ok) {
312
- const availability = availabilityFromCursorCatalog(value, listResult.stdout);
313
- if (availability !== null) {
334
+ const candidates = parseCursorCatalog(listResult.stdout).map((entry) => entry.slug);
335
+ if (candidates.length > 0) {
314
336
  return {
315
- availability: availability === 'valid' ? 'unvalidated' : 'unknown-value',
316
- message: cursorCatalogContextMessage(value, availability),
337
+ status: 'resolved',
338
+ candidates,
339
+ sourceCommand: 'list-models',
340
+ diagnostic: null,
317
341
  };
318
342
  }
319
343
  }
344
+ return {
345
+ status: attempts.every((result) => !result.ok) ? 'unavailable' : 'failed',
346
+ candidates: [],
347
+ sourceCommand: null,
348
+ diagnostic: cursorCatalogDiagnostic(attempts),
349
+ };
350
+ }
351
+ export async function validateCursorSubagentModel(value, options) {
352
+ const taskProbe = await probeCursorSubagentModel(value, options);
353
+ if (taskProbe.decisive) {
354
+ const { decisive: _decisive, evidence: _evidence, ...result } = taskProbe;
355
+ return result;
356
+ }
357
+ const catalog = await resolveCursorModelCatalog(options);
358
+ if (catalog.status === 'resolved') {
359
+ const availability = catalog.candidates.includes(value)
360
+ ? 'valid'
361
+ : 'unknown-value';
362
+ return {
363
+ availability: availability === 'valid' ? 'unvalidated' : 'unknown-value',
364
+ message: cursorCatalogContextMessage(value, availability),
365
+ };
366
+ }
320
367
  return { availability: 'unvalidated' };
321
368
  }
322
369
  export async function validateMatrixCell(provider, value, options) {
@@ -327,11 +374,7 @@ export async function validateMatrixCell(provider, value, options) {
327
374
  ? { availability: 'unknown-value' }
328
375
  : 'unknown-value';
329
376
  }
330
- const dependencies = {
331
- ...DEFAULT_DEPENDENCIES,
332
- env: options.env ?? DEFAULT_DEPENDENCIES.env,
333
- ...options.dependencies,
334
- };
377
+ const dependencies = availabilityDependencies(options);
335
378
  if (normalizedProvider === 'claude') {
336
379
  const availability = VALID_CLAUDE_DISPATCH_CEILINGS.includes(normalizedValue)
337
380
  ? 'valid'
@@ -0,0 +1,124 @@
1
+ import type { WorkflowDispatchMatrixTier } from '../../config/dispatch-matrix.js';
2
+ import type { IdentityProvenance } from './provenance.js';
3
+ import type { DispatchAction, DispatchRole, DispatchStampRecord } from './stamp.js';
4
+ export type DispatchReportPolicyStatus = 'resolved' | 'unresolved' | 'blocked';
5
+ export type DispatchReportPolicyMode = 'managed' | 'inherit' | null;
6
+ export interface ResolvedDispatchTargetReport {
7
+ harness: string;
8
+ model?: string;
9
+ effort?: string;
10
+ crossHarness: boolean;
11
+ routeIndex: number;
12
+ routeLength: number;
13
+ }
14
+ export interface DispatchControlRequest {
15
+ value: string | null;
16
+ mechanism: 'task-model-argument' | 'materialized-role' | 'base-role' | 'provider-default' | 'host-inherited' | 'not-applicable';
17
+ reason: string;
18
+ }
19
+ export interface DispatchRequestedControls {
20
+ model: DispatchControlRequest;
21
+ effort: DispatchControlRequest;
22
+ }
23
+ export interface DispatchConfiguredDefaults {
24
+ model: string | null;
25
+ modelSource: string | null;
26
+ effort: string | null;
27
+ effortSource: string | null;
28
+ }
29
+ export interface DispatchGateInvocation {
30
+ readonly runId: string;
31
+ readonly targetId: string;
32
+ readonly runtime: string;
33
+ readonly model: string;
34
+ readonly reasoningEffort: string;
35
+ readonly source: 'exec-target-config' | 'unknown';
36
+ }
37
+ export interface DispatchRuntimeIdentity {
38
+ producer: string | null;
39
+ model: string | null;
40
+ effort: string | null;
41
+ provenance: IdentityProvenance;
42
+ confidence: string;
43
+ }
44
+ export interface DispatchReportSelectionInput {
45
+ role: DispatchRole;
46
+ requestedCandidate: {
47
+ model: string;
48
+ effort?: string;
49
+ } | null;
50
+ candidateTier: WorkflowDispatchMatrixTier | null;
51
+ candidateIndex: number | null;
52
+ ceilingTier: WorkflowDispatchMatrixTier | null;
53
+ ceilingTarget: ResolvedDispatchTargetReport | null;
54
+ selectedValue: string | null;
55
+ selectionMode: string;
56
+ selectionBranch: string;
57
+ target: ResolvedDispatchTargetReport | null;
58
+ cellSource: string | null;
59
+ }
60
+ export interface DispatchReportProviderResolution {
61
+ dispatchArgs: {
62
+ variant?: string;
63
+ model?: string;
64
+ } | null;
65
+ selection: DispatchReportSelectionInput;
66
+ }
67
+ export interface DispatchReportResolution {
68
+ status: DispatchReportPolicyStatus;
69
+ provider: string;
70
+ value: string | null;
71
+ policyMode: DispatchReportPolicyMode;
72
+ policy: string | null;
73
+ source: string | null;
74
+ providers: Record<string, DispatchReportProviderResolution>;
75
+ }
76
+ export interface DispatchReportV1 {
77
+ schemaVersion: 1;
78
+ route: {
79
+ scope: string;
80
+ action: DispatchAction;
81
+ role: DispatchRole;
82
+ target: string;
83
+ };
84
+ policy: {
85
+ status: DispatchReportPolicyStatus;
86
+ mode: DispatchReportPolicyMode;
87
+ name: string | null;
88
+ source: string | null;
89
+ };
90
+ selection: {
91
+ requestedCandidate: {
92
+ model: string;
93
+ effort?: string;
94
+ } | null;
95
+ candidateTier: WorkflowDispatchMatrixTier | null;
96
+ candidateIndex: number | null;
97
+ ceilingTier: WorkflowDispatchMatrixTier | null;
98
+ ceilingTarget: ResolvedDispatchTargetReport | null;
99
+ selectedValue: string | null;
100
+ exactSelectedTarget: ResolvedDispatchTargetReport | null;
101
+ selectionMode: string;
102
+ selectionBranch: string;
103
+ cellSource: string | null;
104
+ };
105
+ requestedControls: DispatchRequestedControls;
106
+ configuredDefaults: DispatchConfiguredDefaults;
107
+ gateInvocation: Readonly<DispatchGateInvocation> | null;
108
+ runtimeIdentity: DispatchRuntimeIdentity;
109
+ }
110
+ export interface DispatchReportInput {
111
+ scope: string;
112
+ action: DispatchAction;
113
+ role: DispatchRole;
114
+ resolution: DispatchReportResolution;
115
+ requestedControls: DispatchRequestedControls;
116
+ configuredDefaults: DispatchConfiguredDefaults;
117
+ gateInvocation?: DispatchGateInvocation | null;
118
+ runtimeIdentity?: DispatchRuntimeIdentity;
119
+ }
120
+ export declare function buildDispatchReport(input: DispatchReportInput): DispatchReportV1;
121
+ export declare function serializeDispatchReport(report: DispatchReportV1): string;
122
+ export declare function formatDispatchReport(report: DispatchReportV1): string;
123
+ export declare function toDispatchStampRecord(report: DispatchReportV1): DispatchStampRecord;
124
+ //# sourceMappingURL=dispatch-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-report.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/dispatch-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,285 @@
1
+ const ROLE_BY_ACTION = {
2
+ implementation: 'implementer',
3
+ fix: 'fix',
4
+ review: 'reviewer',
5
+ };
6
+ const NOT_REPORTED_RUNTIME_IDENTITY = {
7
+ producer: null,
8
+ model: null,
9
+ effort: null,
10
+ provenance: 'unknown',
11
+ confidence: 'not-reported',
12
+ };
13
+ function cloneTarget(target) {
14
+ return target ? { ...target } : null;
15
+ }
16
+ function invocationTarget(provider) {
17
+ return (provider.dispatchArgs?.variant ?? provider.dispatchArgs?.model ?? 'unknown');
18
+ }
19
+ function assertActionRole(action, role) {
20
+ if (ROLE_BY_ACTION[action] !== role) {
21
+ throw new Error(`Invalid dispatch report action/role pair: ${action}/${role}`);
22
+ }
23
+ }
24
+ function orderedTarget(target) {
25
+ if (!target) {
26
+ return null;
27
+ }
28
+ return {
29
+ harness: target.harness,
30
+ ...(target.model === undefined ? {} : { model: target.model }),
31
+ ...(target.effort === undefined ? {} : { effort: target.effort }),
32
+ crossHarness: target.crossHarness,
33
+ routeIndex: target.routeIndex,
34
+ routeLength: target.routeLength,
35
+ };
36
+ }
37
+ function orderedReport(report) {
38
+ return {
39
+ schemaVersion: 1,
40
+ route: {
41
+ scope: report.route.scope,
42
+ action: report.route.action,
43
+ role: report.route.role,
44
+ target: report.route.target,
45
+ },
46
+ policy: {
47
+ status: report.policy.status,
48
+ mode: report.policy.mode,
49
+ name: report.policy.name,
50
+ source: report.policy.source,
51
+ },
52
+ selection: {
53
+ requestedCandidate: report.selection.requestedCandidate
54
+ ? {
55
+ model: report.selection.requestedCandidate.model,
56
+ ...(report.selection.requestedCandidate.effort === undefined
57
+ ? {}
58
+ : { effort: report.selection.requestedCandidate.effort }),
59
+ }
60
+ : null,
61
+ candidateTier: report.selection.candidateTier,
62
+ candidateIndex: report.selection.candidateIndex,
63
+ ceilingTier: report.selection.ceilingTier,
64
+ ceilingTarget: orderedTarget(report.selection.ceilingTarget),
65
+ selectedValue: report.selection.selectedValue,
66
+ exactSelectedTarget: orderedTarget(report.selection.exactSelectedTarget),
67
+ selectionMode: report.selection.selectionMode,
68
+ selectionBranch: report.selection.selectionBranch,
69
+ cellSource: report.selection.cellSource,
70
+ },
71
+ requestedControls: {
72
+ model: {
73
+ value: report.requestedControls.model.value,
74
+ mechanism: report.requestedControls.model.mechanism,
75
+ reason: report.requestedControls.model.reason,
76
+ },
77
+ effort: {
78
+ value: report.requestedControls.effort.value,
79
+ mechanism: report.requestedControls.effort.mechanism,
80
+ reason: report.requestedControls.effort.reason,
81
+ },
82
+ },
83
+ configuredDefaults: {
84
+ model: report.configuredDefaults.model,
85
+ modelSource: report.configuredDefaults.modelSource,
86
+ effort: report.configuredDefaults.effort,
87
+ effortSource: report.configuredDefaults.effortSource,
88
+ },
89
+ gateInvocation: report.gateInvocation
90
+ ? {
91
+ runId: report.gateInvocation.runId,
92
+ targetId: report.gateInvocation.targetId,
93
+ runtime: report.gateInvocation.runtime,
94
+ model: report.gateInvocation.model,
95
+ reasoningEffort: report.gateInvocation.reasoningEffort,
96
+ source: report.gateInvocation.source,
97
+ }
98
+ : null,
99
+ runtimeIdentity: {
100
+ producer: report.runtimeIdentity.producer,
101
+ model: report.runtimeIdentity.model,
102
+ effort: report.runtimeIdentity.effort,
103
+ provenance: report.runtimeIdentity.provenance,
104
+ confidence: report.runtimeIdentity.confidence,
105
+ },
106
+ };
107
+ }
108
+ function display(value) {
109
+ return value === null ? 'none' : String(value);
110
+ }
111
+ function formatTarget(target) {
112
+ if (!target) {
113
+ return 'none';
114
+ }
115
+ return [
116
+ `harness=${target.harness}`,
117
+ target.model === undefined ? null : `model=${target.model}`,
118
+ target.effort === undefined ? null : `effort=${target.effort}`,
119
+ `crossHarness=${String(target.crossHarness)}`,
120
+ `routeIndex=${target.routeIndex}`,
121
+ `routeLength=${target.routeLength}`,
122
+ ]
123
+ .filter((part) => part !== null)
124
+ .join(' ');
125
+ }
126
+ function formatCandidate(candidate) {
127
+ if (!candidate) {
128
+ return 'none';
129
+ }
130
+ return [
131
+ `model=${candidate.model}`,
132
+ candidate.effort === undefined ? null : `effort=${candidate.effort}`,
133
+ ]
134
+ .filter((part) => part !== null)
135
+ .join(' ');
136
+ }
137
+ function formatControl(name, control) {
138
+ return ` ${name}: ${display(control.value)} (${control.mechanism}) — ${control.reason}`;
139
+ }
140
+ function controlAxis(control) {
141
+ if (control.mechanism === 'task-model-argument' ||
142
+ control.mechanism === 'materialized-role') {
143
+ return control.value ? `selected:${control.value}` : 'unknown';
144
+ }
145
+ if (control.mechanism === 'host-inherited') {
146
+ return 'inherited';
147
+ }
148
+ if (control.mechanism === 'base-role' ||
149
+ control.mechanism === 'provider-default') {
150
+ return 'provider-default';
151
+ }
152
+ return 'not-applicable';
153
+ }
154
+ function dispatchPolicy(report) {
155
+ if (report.policy.name) {
156
+ return report.policy.name;
157
+ }
158
+ return report.policy.mode === 'inherit' ? 'inherit-host-defaults' : 'unknown';
159
+ }
160
+ function dispatchCeiling(report) {
161
+ return (report.selection.ceilingTarget?.effort ??
162
+ report.selection.ceilingTarget?.model ??
163
+ 'none');
164
+ }
165
+ export function buildDispatchReport(input) {
166
+ assertActionRole(input.action, input.role);
167
+ const provider = input.resolution.providers[input.resolution.provider];
168
+ if (!provider) {
169
+ throw new Error(`Dispatch report resolution is missing provider data for "${input.resolution.provider}".`);
170
+ }
171
+ const { selection } = provider;
172
+ const gateInvocation = input.gateInvocation
173
+ ? Object.freeze({ ...input.gateInvocation })
174
+ : null;
175
+ return {
176
+ schemaVersion: 1,
177
+ route: {
178
+ scope: input.scope,
179
+ action: input.action,
180
+ role: input.role,
181
+ target: invocationTarget(provider),
182
+ },
183
+ policy: {
184
+ status: input.resolution.status,
185
+ mode: input.resolution.policyMode,
186
+ name: input.resolution.policy,
187
+ source: input.resolution.source,
188
+ },
189
+ selection: {
190
+ requestedCandidate: selection.requestedCandidate
191
+ ? { ...selection.requestedCandidate }
192
+ : null,
193
+ candidateTier: selection.candidateTier,
194
+ candidateIndex: selection.candidateIndex,
195
+ ceilingTier: selection.ceilingTier,
196
+ ceilingTarget: cloneTarget(selection.ceilingTarget),
197
+ selectedValue: selection.selectedValue,
198
+ exactSelectedTarget: cloneTarget(selection.target),
199
+ selectionMode: selection.selectionMode,
200
+ selectionBranch: selection.selectionBranch,
201
+ cellSource: selection.cellSource,
202
+ },
203
+ requestedControls: {
204
+ model: { ...input.requestedControls.model },
205
+ effort: { ...input.requestedControls.effort },
206
+ },
207
+ configuredDefaults: { ...input.configuredDefaults },
208
+ gateInvocation,
209
+ runtimeIdentity: input.runtimeIdentity
210
+ ? { ...input.runtimeIdentity }
211
+ : { ...NOT_REPORTED_RUNTIME_IDENTITY },
212
+ };
213
+ }
214
+ export function serializeDispatchReport(report) {
215
+ return JSON.stringify(orderedReport(report), null, 2);
216
+ }
217
+ export function formatDispatchReport(report) {
218
+ const runtimeNotReported = report.runtimeIdentity.producer === null &&
219
+ report.runtimeIdentity.model === null &&
220
+ report.runtimeIdentity.effort === null &&
221
+ report.runtimeIdentity.confidence === 'not-reported';
222
+ const runtimeValue = (value) => value === null ? 'not reported' : value;
223
+ const gateLines = report.gateInvocation
224
+ ? [
225
+ ` Run ID: ${report.gateInvocation.runId}`,
226
+ ` Target ID: ${report.gateInvocation.targetId}`,
227
+ ` Runtime: ${report.gateInvocation.runtime}`,
228
+ ` Model: ${report.gateInvocation.model}`,
229
+ ` Reasoning effort: ${report.gateInvocation.reasoningEffort}`,
230
+ ` Source: ${report.gateInvocation.source}`,
231
+ ]
232
+ : [' Not configured'];
233
+ return [
234
+ 'Dispatch Report V1',
235
+ 'Route',
236
+ ` Scope: ${report.route.scope}`,
237
+ ` Action / role: ${report.route.action} / ${report.route.role}`,
238
+ ` Invocation target: ${report.route.target}`,
239
+ 'Policy',
240
+ ` Status: ${report.policy.status}`,
241
+ ` Mode / name: ${display(report.policy.mode)} / ${display(report.policy.name)}`,
242
+ ` Source: ${display(report.policy.source)}`,
243
+ 'Selection',
244
+ ` Requested candidate: ${formatCandidate(report.selection.requestedCandidate)}`,
245
+ ` Candidate tier / index: ${display(report.selection.candidateTier)} / ${display(report.selection.candidateIndex)}`,
246
+ ` Ceiling tier: ${display(report.selection.ceilingTier)}`,
247
+ ` Ceiling target: ${formatTarget(report.selection.ceilingTarget)}`,
248
+ ` Selected value: ${display(report.selection.selectedValue)}`,
249
+ ` Exact selected target: ${formatTarget(report.selection.exactSelectedTarget)}`,
250
+ ` Mode / branch: ${report.selection.selectionMode} / ${report.selection.selectionBranch}`,
251
+ ` Cell source: ${display(report.selection.cellSource)}`,
252
+ 'Requested controls',
253
+ formatControl('Model', report.requestedControls.model),
254
+ formatControl('Effort', report.requestedControls.effort),
255
+ 'Configured defaults (not runtime observations)',
256
+ ` Model: ${display(report.configuredDefaults.model)}`,
257
+ ` Model source: ${display(report.configuredDefaults.modelSource)}`,
258
+ ` Effort: ${display(report.configuredDefaults.effort)}`,
259
+ ` Effort source: ${display(report.configuredDefaults.effortSource)}`,
260
+ 'Gate invocation (configured, immutable)',
261
+ ...gateLines,
262
+ 'Runtime identity (observed/reported separately)',
263
+ ...(runtimeNotReported ? [' Runtime identity was not reported.'] : []),
264
+ ` Producer: ${runtimeValue(report.runtimeIdentity.producer)}`,
265
+ ` Model: ${runtimeValue(report.runtimeIdentity.model)}`,
266
+ ` Effort: ${runtimeValue(report.runtimeIdentity.effort)}`,
267
+ ` Provenance: ${report.runtimeIdentity.provenance}`,
268
+ ` Confidence: ${report.runtimeIdentity.confidence}`,
269
+ ].join('\n');
270
+ }
271
+ export function toDispatchStampRecord(report) {
272
+ const runtimeReported = report.runtimeIdentity.producer !== null;
273
+ return {
274
+ scope: report.route.scope,
275
+ action: report.route.action,
276
+ role: report.route.role,
277
+ producer: report.runtimeIdentity.producer ?? 'unknown',
278
+ provenance: runtimeReported ? report.runtimeIdentity.provenance : 'unknown',
279
+ modelAxis: controlAxis(report.requestedControls.model),
280
+ effortAxis: controlAxis(report.requestedControls.effort),
281
+ dispatchPolicy: dispatchPolicy(report),
282
+ dispatchCeiling: dispatchCeiling(report),
283
+ target: report.route.target,
284
+ };
285
+ }