@open-agent-toolkit/cli 0.1.43 → 0.1.45

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 (47) hide show
  1. package/assets/config/dispatch-matrix-recommendation.json +23 -0
  2. package/assets/docs/cli-utilities/configuration.md +59 -20
  3. package/assets/docs/cli-utilities/workflow-gates.md +79 -16
  4. package/assets/docs/contributing/code.md +10 -4
  5. package/assets/docs/contributing/hooks-and-safety.md +23 -0
  6. package/assets/docs/workflows/projects/dispatch-ceiling.md +80 -10
  7. package/assets/public-package-versions.json +4 -4
  8. package/assets/skills/oat-project-implement/SKILL.md +53 -11
  9. package/assets/skills/oat-project-plan/SKILL.md +22 -3
  10. package/assets/skills/oat-project-quick-start/SKILL.md +22 -3
  11. package/assets/templates/state.md +5 -0
  12. package/dist/commands/config/index.d.ts +6 -0
  13. package/dist/commands/config/index.d.ts.map +1 -1
  14. package/dist/commands/config/index.js +366 -8
  15. package/dist/commands/doctor/index.d.ts +6 -1
  16. package/dist/commands/doctor/index.d.ts.map +1 -1
  17. package/dist/commands/doctor/index.js +138 -4
  18. package/dist/commands/gate/index.d.ts +39 -2
  19. package/dist/commands/gate/index.d.ts.map +1 -1
  20. package/dist/commands/gate/index.js +343 -30
  21. package/dist/commands/internal/cursor-current-target.d.ts +37 -0
  22. package/dist/commands/internal/cursor-current-target.d.ts.map +1 -0
  23. package/dist/commands/internal/cursor-current-target.js +228 -0
  24. package/dist/commands/internal/index.d.ts.map +1 -1
  25. package/dist/commands/internal/index.js +2 -0
  26. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  27. package/dist/commands/project/dispatch-ceiling/index.js +359 -16
  28. package/dist/config/oat-config.d.ts +15 -5
  29. package/dist/config/oat-config.d.ts.map +1 -1
  30. package/dist/config/oat-config.js +103 -10
  31. package/dist/config/resolve.js +12 -0
  32. package/dist/manifest/manifest.types.d.ts +12 -12
  33. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  34. package/dist/providers/ceiling/registry.js +16 -0
  35. package/dist/providers/identity/availability.d.ts +22 -0
  36. package/dist/providers/identity/availability.d.ts.map +1 -0
  37. package/dist/providers/identity/availability.js +119 -0
  38. package/dist/providers/identity/family.d.ts +13 -0
  39. package/dist/providers/identity/family.d.ts.map +1 -0
  40. package/dist/providers/identity/family.js +32 -0
  41. package/dist/providers/identity/provenance.d.ts +21 -0
  42. package/dist/providers/identity/provenance.d.ts.map +1 -0
  43. package/dist/providers/identity/provenance.js +65 -0
  44. package/dist/providers/identity/stamp.d.ts +35 -0
  45. package/dist/providers/identity/stamp.d.ts.map +1 -0
  46. package/dist/providers/identity/stamp.js +171 -0
  47. package/package.json +2 -2
@@ -21,6 +21,7 @@ export const VALID_CLAUDE_DISPATCH_CEILINGS = ['haiku', 'sonnet', 'opus', 'fable
21
21
  export const VALID_DISPATCH_CEILING_PRESETS = ['balanced', 'maximum', 'cost-conscious'];
22
22
  export const VALID_DISPATCH_POLICY_MODES = ['managed', 'inherit'];
23
23
  export const VALID_MANAGED_DISPATCH_POLICIES = ['economy', 'balanced', 'high', 'frontier', 'uncapped'];
24
+ export const VALID_DISPATCH_MATRIX_TIERS = ['economy', 'balanced', 'high', 'frontier'];
24
25
  const VALID_GATE_ON_FAILURES = [
25
26
  'block',
26
27
  'prompt',
@@ -49,7 +50,11 @@ export const BUILTIN_EXEC_TARGETS = {
49
50
  runtime: 'cursor',
50
51
  baseCommand: ['cursor-agent', '-p'],
51
52
  hostDetectionCommand: ['sh', '-c', 'test -n "$CURSOR_AGENT"'],
52
- availabilityCommand: ['cursor-agent', '--version'],
53
+ availabilityCommand: [
54
+ 'sh',
55
+ '-c',
56
+ 'command -v cursor-agent || command -v agent',
57
+ ],
53
58
  priority: 70,
54
59
  },
55
60
  };
@@ -72,6 +77,14 @@ function normalizeArgv(value) {
72
77
  }
73
78
  return [...value];
74
79
  }
80
+ function normalizeStringList(value) {
81
+ if (!Array.isArray(value) ||
82
+ value.length === 0 ||
83
+ !value.every((item) => typeof item === 'string' && item.trim().length > 0)) {
84
+ return undefined;
85
+ }
86
+ return value.map((item) => item.trim());
87
+ }
75
88
  function normalizeGateConfig(value) {
76
89
  if (value === null) {
77
90
  return null;
@@ -96,6 +109,82 @@ function normalizeGateConfig(value) {
96
109
  }
97
110
  return gate;
98
111
  }
112
+ function normalizeProviderBareValue(providerKey, value) {
113
+ const trimmed = trimNonEmptyString(value);
114
+ if (trimmed === undefined) {
115
+ return undefined;
116
+ }
117
+ if (providerKey === 'codex' &&
118
+ !VALID_CODEX_DISPATCH_CEILINGS.includes(trimmed)) {
119
+ return undefined;
120
+ }
121
+ if (providerKey === 'claude' &&
122
+ !VALID_CLAUDE_DISPATCH_CEILINGS.includes(trimmed)) {
123
+ return undefined;
124
+ }
125
+ return trimmed;
126
+ }
127
+ function normalizeDispatchRouteTarget(value) {
128
+ if (!isRecord(value)) {
129
+ return undefined;
130
+ }
131
+ const target = {};
132
+ const harness = trimNonEmptyString(value.harness);
133
+ if (harness !== undefined) {
134
+ target.harness = harness;
135
+ }
136
+ const model = trimNonEmptyString(value.model);
137
+ if (model !== undefined) {
138
+ target.model = model;
139
+ }
140
+ const effort = trimNonEmptyString(value.effort);
141
+ if (effort !== undefined) {
142
+ target.effort = effort;
143
+ }
144
+ return Object.keys(target).length > 0 ? target : undefined;
145
+ }
146
+ function normalizeDispatchMatrixCell(providerKey, value) {
147
+ const bareValue = normalizeProviderBareValue(providerKey, value);
148
+ if (bareValue !== undefined) {
149
+ return bareValue;
150
+ }
151
+ if (!Array.isArray(value) || value.length === 0) {
152
+ return undefined;
153
+ }
154
+ const route = [];
155
+ for (const entry of value) {
156
+ const bareEntry = normalizeProviderBareValue(providerKey, entry);
157
+ if (bareEntry !== undefined) {
158
+ route.push(bareEntry);
159
+ continue;
160
+ }
161
+ const target = normalizeDispatchRouteTarget(entry);
162
+ if (target !== undefined) {
163
+ route.push(target);
164
+ }
165
+ }
166
+ return route.length > 0 ? route : undefined;
167
+ }
168
+ function normalizeDispatchProviderValue(providerKey, value) {
169
+ const bareValue = normalizeProviderBareValue(providerKey, value);
170
+ if (bareValue !== undefined) {
171
+ return bareValue;
172
+ }
173
+ if (!isRecord(value)) {
174
+ return undefined;
175
+ }
176
+ const tierMap = {};
177
+ for (const [tier, rawCell] of Object.entries(value)) {
178
+ if (!VALID_DISPATCH_MATRIX_TIERS.includes(tier)) {
179
+ continue;
180
+ }
181
+ const normalized = normalizeDispatchMatrixCell(providerKey, rawCell);
182
+ if (normalized !== undefined) {
183
+ tierMap[tier] = normalized;
184
+ }
185
+ }
186
+ return Object.keys(tierMap).length > 0 ? tierMap : undefined;
187
+ }
99
188
  function normalizeExecTarget(value) {
100
189
  if (value === null) {
101
190
  return null;
@@ -112,6 +201,10 @@ function normalizeExecTarget(value) {
112
201
  if (baseCommand !== undefined) {
113
202
  target.baseCommand = baseCommand;
114
203
  }
204
+ const models = normalizeStringList(value.models);
205
+ if (models !== undefined) {
206
+ target.models = models;
207
+ }
115
208
  if ('priority' in value) {
116
209
  if (typeof value.priority === 'number' && Number.isFinite(value.priority)) {
117
210
  target.priority = value.priority;
@@ -215,17 +308,17 @@ function normalizeWorkflowConfig(parsed) {
215
308
  dispatchCeiling.preset = parsed.dispatchCeiling
216
309
  .preset;
217
310
  }
311
+ const recommendationVersion = trimNonEmptyString(parsed.dispatchCeiling.recommendationVersion);
312
+ if (recommendationVersion !== undefined) {
313
+ dispatchCeiling.recommendationVersion = recommendationVersion;
314
+ }
218
315
  if (isRecord(parsed.dispatchCeiling.providers)) {
219
316
  const providers = {};
220
- if (typeof parsed.dispatchCeiling.providers.codex === 'string' &&
221
- VALID_CODEX_DISPATCH_CEILINGS.includes(parsed.dispatchCeiling.providers.codex)) {
222
- providers.codex = parsed.dispatchCeiling.providers
223
- .codex;
224
- }
225
- if (typeof parsed.dispatchCeiling.providers.claude === 'string' &&
226
- VALID_CLAUDE_DISPATCH_CEILINGS.includes(parsed.dispatchCeiling.providers.claude)) {
227
- providers.claude = parsed.dispatchCeiling.providers
228
- .claude;
317
+ for (const [providerKey, rawProviderValue] of Object.entries(parsed.dispatchCeiling.providers)) {
318
+ const normalized = normalizeDispatchProviderValue(providerKey, rawProviderValue);
319
+ if (normalized !== undefined) {
320
+ providers[providerKey] = normalized;
321
+ }
229
322
  }
230
323
  if (Object.keys(providers).length > 0) {
231
324
  dispatchCeiling.providers = providers;
@@ -181,6 +181,7 @@ function mergeExecTargetLayer(targets, layer) {
181
181
  targets[id] = cloneExecTarget({
182
182
  runtime: override.runtime ?? existing.runtime,
183
183
  baseCommand: override.baseCommand ?? existing.baseCommand,
184
+ models: override.models ?? existing.models,
184
185
  hostDetectionCommand: override.hostDetectionCommand ?? existing.hostDetectionCommand,
185
186
  availabilityCommand: override.availabilityCommand ?? existing.availabilityCommand,
186
187
  priority: override.priority ?? existing.priority,
@@ -211,6 +212,9 @@ function cloneExecTarget(target) {
211
212
  if (target.availabilityCommand) {
212
213
  next.availabilityCommand = [...target.availabilityCommand];
213
214
  }
215
+ if (target.models) {
216
+ next.models = [...target.models];
217
+ }
214
218
  return next;
215
219
  }
216
220
  function toCompleteExecTarget(target) {
@@ -231,8 +235,16 @@ function toCompleteExecTarget(target) {
231
235
  if (isValidArgv(target.availabilityCommand)) {
232
236
  completeTarget.availabilityCommand = [...target.availabilityCommand];
233
237
  }
238
+ if (isValidStringList(target.models)) {
239
+ completeTarget.models = target.models.map((model) => model.trim());
240
+ }
234
241
  return completeTarget;
235
242
  }
243
+ function isValidStringList(value) {
244
+ return (Array.isArray(value) &&
245
+ value.length > 0 &&
246
+ value.every((part) => typeof part === 'string' && part.trim().length > 0));
247
+ }
236
248
  function isValidArgv(value) {
237
249
  if (!Array.isArray(value) ||
238
250
  value.length === 0 ||
@@ -9,36 +9,36 @@ export declare const ManifestEntrySchema: z.ZodEffects<z.ZodObject<{
9
9
  isFile: z.ZodDefault<z.ZodBoolean>;
10
10
  lastSynced: z.ZodString;
11
11
  }, "strip", z.ZodTypeAny, {
12
+ provider: string;
12
13
  canonicalPath: string;
13
14
  providerPath: string;
14
- provider: string;
15
15
  contentType: "skill" | "agent" | "rule";
16
16
  strategy: "symlink" | "copy";
17
17
  contentHash: string | null;
18
18
  isFile: boolean;
19
19
  lastSynced: string;
20
20
  }, {
21
+ provider: string;
21
22
  canonicalPath: string;
22
23
  providerPath: string;
23
- provider: string;
24
24
  contentType: "skill" | "agent" | "rule";
25
25
  strategy: "symlink" | "copy";
26
26
  contentHash: string | null;
27
27
  lastSynced: string;
28
28
  isFile?: boolean | undefined;
29
29
  }>, {
30
+ provider: string;
30
31
  canonicalPath: string;
31
32
  providerPath: string;
32
- provider: string;
33
33
  contentType: "skill" | "agent" | "rule";
34
34
  strategy: "symlink" | "copy";
35
35
  contentHash: string | null;
36
36
  isFile: boolean;
37
37
  lastSynced: string;
38
38
  }, {
39
+ provider: string;
39
40
  canonicalPath: string;
40
41
  providerPath: string;
41
- provider: string;
42
42
  contentType: "skill" | "agent" | "rule";
43
43
  strategy: "symlink" | "copy";
44
44
  contentHash: string | null;
@@ -58,36 +58,36 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
58
58
  isFile: z.ZodDefault<z.ZodBoolean>;
59
59
  lastSynced: z.ZodString;
60
60
  }, "strip", z.ZodTypeAny, {
61
+ provider: string;
61
62
  canonicalPath: string;
62
63
  providerPath: string;
63
- provider: string;
64
64
  contentType: "skill" | "agent" | "rule";
65
65
  strategy: "symlink" | "copy";
66
66
  contentHash: string | null;
67
67
  isFile: boolean;
68
68
  lastSynced: string;
69
69
  }, {
70
+ provider: string;
70
71
  canonicalPath: string;
71
72
  providerPath: string;
72
- provider: string;
73
73
  contentType: "skill" | "agent" | "rule";
74
74
  strategy: "symlink" | "copy";
75
75
  contentHash: string | null;
76
76
  lastSynced: string;
77
77
  isFile?: boolean | undefined;
78
78
  }>, {
79
+ provider: string;
79
80
  canonicalPath: string;
80
81
  providerPath: string;
81
- provider: string;
82
82
  contentType: "skill" | "agent" | "rule";
83
83
  strategy: "symlink" | "copy";
84
84
  contentHash: string | null;
85
85
  isFile: boolean;
86
86
  lastSynced: string;
87
87
  }, {
88
+ provider: string;
88
89
  canonicalPath: string;
89
90
  providerPath: string;
90
- provider: string;
91
91
  contentType: "skill" | "agent" | "rule";
92
92
  strategy: "symlink" | "copy";
93
93
  contentHash: string | null;
@@ -97,9 +97,9 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
97
97
  lastUpdated: z.ZodString;
98
98
  }, "strip", z.ZodTypeAny, {
99
99
  entries: {
100
+ provider: string;
100
101
  canonicalPath: string;
101
102
  providerPath: string;
102
- provider: string;
103
103
  contentType: "skill" | "agent" | "rule";
104
104
  strategy: "symlink" | "copy";
105
105
  contentHash: string | null;
@@ -111,9 +111,9 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
111
111
  lastUpdated: string;
112
112
  }, {
113
113
  entries: {
114
+ provider: string;
114
115
  canonicalPath: string;
115
116
  providerPath: string;
116
- provider: string;
117
117
  contentType: "skill" | "agent" | "rule";
118
118
  strategy: "symlink" | "copy";
119
119
  contentHash: string | null;
@@ -125,9 +125,9 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
125
125
  lastUpdated: string;
126
126
  }>, {
127
127
  entries: {
128
+ provider: string;
128
129
  canonicalPath: string;
129
130
  providerPath: string;
130
- provider: string;
131
131
  contentType: "skill" | "agent" | "rule";
132
132
  strategy: "symlink" | "copy";
133
133
  contentHash: string | null;
@@ -139,9 +139,9 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
139
139
  lastUpdated: string;
140
140
  }, {
141
141
  entries: {
142
+ provider: string;
142
143
  canonicalPath: string;
143
144
  providerPath: string;
144
- provider: string;
145
145
  contentType: "skill" | "agent" | "rule";
146
146
  strategy: "symlink" | "copy";
147
147
  contentHash: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/providers/ceiling/registry.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,WAAW,GAAG,MAAM,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB,IAAI,CAAC;AAET,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC;;;OAGG;IACH,qBAAqB,CACnB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,qBAAqB,GACzB,mBAAmB,CAAC;IACvB;;;;OAIG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC;CACtE;AAMD,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAK9C,CAAC;AA0EF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,sBAAsB,CAE1E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/providers/ceiling/registry.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,WAAW,GAAG,MAAM,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB,IAAI,CAAC;AAET,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC;;;OAGG;IACH,qBAAqB,CACnB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,qBAAqB,GACzB,mBAAmB,CAAC;IACvB;;;;OAIG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC;CACtE;AAMD,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAK9C,CAAC;AA2FF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,sBAAsB,CAE1E"}
@@ -54,6 +54,21 @@ const claudeAdapter = {
54
54
  return isAboveOrchestrator(value, ctx.orchestratorTier);
55
55
  },
56
56
  };
57
+ const cursorAdapter = {
58
+ provider: 'cursor',
59
+ supportsCeiling: true,
60
+ validValues: [],
61
+ mechanism: 'model-arg',
62
+ compileToDispatchArgs(value) {
63
+ const model = value.trim();
64
+ return model ? { model } : null;
65
+ },
66
+ // Cursor model slugs do not share a total order, so upgrade verification is
67
+ // not meaningful here; availability is checked by the identity oracle layer.
68
+ verifyOnDispatch() {
69
+ return false;
70
+ },
71
+ };
57
72
  function advisoryAdapter(provider) {
58
73
  return {
59
74
  provider,
@@ -71,6 +86,7 @@ function advisoryAdapter(provider) {
71
86
  const REGISTERED_ADAPTERS = {
72
87
  codex: codexAdapter,
73
88
  claude: claudeAdapter,
89
+ cursor: cursorAdapter,
74
90
  };
75
91
  /**
76
92
  * Look up the ceiling adapter for a provider. Unknown providers fall back to an
@@ -0,0 +1,22 @@
1
+ export type MatrixCellAvailability = 'valid' | 'unknown-value' | 'unvalidated';
2
+ export interface CursorAgentRunOptions {
3
+ cwd: string;
4
+ env: NodeJS.ProcessEnv;
5
+ }
6
+ export interface CursorAgentRunResult {
7
+ ok: boolean;
8
+ stdout: string;
9
+ stderr: string;
10
+ }
11
+ export interface AvailabilityOracleDependencies {
12
+ pathExists: (path: string) => Promise<boolean>;
13
+ runCursorAgent: (args: string[], options: CursorAgentRunOptions) => Promise<CursorAgentRunResult>;
14
+ env?: NodeJS.ProcessEnv;
15
+ }
16
+ export interface ValidateMatrixCellOptions {
17
+ cwd: string;
18
+ env?: NodeJS.ProcessEnv;
19
+ dependencies?: Partial<AvailabilityOracleDependencies>;
20
+ }
21
+ export declare function validateMatrixCell(provider: string, value: string, options: ValidateMatrixCellOptions): Promise<MatrixCellAvailability>;
22
+ //# sourceMappingURL=availability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/availability.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,CAAC;AAE/E,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,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,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACxD;AAwID,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,sBAAsB,CAAC,CA8BjC"}
@@ -0,0 +1,119 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { join } from 'node:path';
3
+ import { promisify } from 'node:util';
4
+ import { VALID_CLAUDE_DISPATCH_CEILINGS, VALID_CODEX_DISPATCH_CEILINGS, } from '../../config/oat-config.js';
5
+ import { fileExists } from '../../fs/io.js';
6
+ const execFileAsync = promisify(execFile);
7
+ async function runCursorAgent(args, options) {
8
+ try {
9
+ const { stdout, stderr } = await execFileAsync('cursor-agent', args, {
10
+ cwd: options.cwd,
11
+ env: options.env,
12
+ maxBuffer: 1024 * 1024,
13
+ timeout: 10_000,
14
+ });
15
+ return {
16
+ ok: true,
17
+ stdout: String(stdout),
18
+ stderr: String(stderr),
19
+ };
20
+ }
21
+ catch (error) {
22
+ const failure = error;
23
+ return {
24
+ ok: false,
25
+ stdout: String(failure.stdout ?? ''),
26
+ stderr: String(failure.stderr ?? failure.message),
27
+ };
28
+ }
29
+ }
30
+ const DEFAULT_DEPENDENCIES = {
31
+ pathExists: fileExists,
32
+ runCursorAgent,
33
+ env: process.env,
34
+ };
35
+ function apiKeyArgs(env) {
36
+ const apiKey = env.CURSOR_API_KEY;
37
+ return typeof apiKey === 'string' && apiKey.length > 0
38
+ ? ['--api-key', apiKey]
39
+ : [];
40
+ }
41
+ function parseCursorCatalog(stdout) {
42
+ const entries = [];
43
+ for (const line of stdout.split(/\r?\n/)) {
44
+ const match = line.match(/^(\S+)\s+-\s+(.+)$/);
45
+ if (!match) {
46
+ continue;
47
+ }
48
+ const [, slug] = match;
49
+ if (slug) {
50
+ entries.push({ slug });
51
+ }
52
+ }
53
+ return entries;
54
+ }
55
+ function availabilityFromCursorCatalog(value, stdout) {
56
+ const entries = parseCursorCatalog(stdout);
57
+ if (entries.length === 0) {
58
+ return null;
59
+ }
60
+ return entries.some((entry) => entry.slug === value)
61
+ ? 'valid'
62
+ : 'unknown-value';
63
+ }
64
+ async function validateCodexCell(value, cwd, dependencies) {
65
+ if (!VALID_CODEX_DISPATCH_CEILINGS.includes(value)) {
66
+ return 'unknown-value';
67
+ }
68
+ try {
69
+ const implementerExists = await dependencies.pathExists(join(cwd, '.codex', 'agents', `oat-phase-implementer-${value}.toml`));
70
+ const reviewerExists = await dependencies.pathExists(join(cwd, '.codex', 'agents', `oat-reviewer-${value}.toml`));
71
+ return implementerExists && reviewerExists ? 'valid' : 'unknown-value';
72
+ }
73
+ catch {
74
+ return 'unvalidated';
75
+ }
76
+ }
77
+ async function validateCursorCell(value, cwd, dependencies) {
78
+ const env = dependencies.env ?? process.env;
79
+ const runOptions = { cwd, env };
80
+ const modelsResult = await dependencies.runCursorAgent([...apiKeyArgs(env), 'models'], runOptions);
81
+ if (modelsResult.ok) {
82
+ const availability = availabilityFromCursorCatalog(value, modelsResult.stdout);
83
+ if (availability !== null) {
84
+ return availability;
85
+ }
86
+ }
87
+ const listResult = await dependencies.runCursorAgent([...apiKeyArgs(env), '--list-models'], runOptions);
88
+ if (listResult.ok) {
89
+ const availability = availabilityFromCursorCatalog(value, listResult.stdout);
90
+ if (availability !== null) {
91
+ return availability;
92
+ }
93
+ }
94
+ return 'unvalidated';
95
+ }
96
+ export async function validateMatrixCell(provider, value, options) {
97
+ const normalizedProvider = provider.trim().toLowerCase();
98
+ const normalizedValue = value.trim();
99
+ if (!normalizedProvider || !normalizedValue) {
100
+ return 'unknown-value';
101
+ }
102
+ const dependencies = {
103
+ ...DEFAULT_DEPENDENCIES,
104
+ env: options.env ?? DEFAULT_DEPENDENCIES.env,
105
+ ...options.dependencies,
106
+ };
107
+ if (normalizedProvider === 'claude') {
108
+ return VALID_CLAUDE_DISPATCH_CEILINGS.includes(normalizedValue)
109
+ ? 'valid'
110
+ : 'unknown-value';
111
+ }
112
+ if (normalizedProvider === 'codex') {
113
+ return validateCodexCell(normalizedValue, options.cwd, dependencies);
114
+ }
115
+ if (normalizedProvider === 'cursor') {
116
+ return validateCursorCell(normalizedValue, options.cwd, dependencies);
117
+ }
118
+ return 'unvalidated';
119
+ }
@@ -0,0 +1,13 @@
1
+ export type ModelFamily = 'claude' | 'openai' | 'composer' | 'glm' | 'unknown';
2
+ interface ClassifyModelFamilyInput {
3
+ value: string;
4
+ /**
5
+ * Structured model-provider identifier, when a harness exposes one. This is
6
+ * not the orchestration/runtime harness id; e.g. Cursor can run several model
7
+ * families and must be classified from the concrete model value.
8
+ */
9
+ providerId?: string;
10
+ }
11
+ export declare function classifyModelFamily(input: ClassifyModelFamilyInput): ModelFamily;
12
+ export {};
13
+ //# sourceMappingURL=family.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"family.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/family.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/E,UAAU,wBAAwB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgCD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,GAC9B,WAAW,CASb"}
@@ -0,0 +1,32 @@
1
+ const PROVIDER_ID_FAMILIES = [
2
+ [/^(anthropic|claude)$/i, 'claude'],
3
+ [/^(openai|codex)$/i, 'openai'],
4
+ [/^composer$/i, 'composer'],
5
+ [/^(glm|zai|zhipu)$/i, 'glm'],
6
+ ];
7
+ const VALUE_FAMILIES = [
8
+ [/\b(claude|sonnet|opus|haiku|fable)\b/i, 'claude'],
9
+ [/\b(gpt|openai|codex|o[1-9])[-\w.]*\b/i, 'openai'],
10
+ [/\bcomposer\b/i, 'composer'],
11
+ [/\bglm\b/i, 'glm'],
12
+ ];
13
+ function normalize(value) {
14
+ return typeof value === 'string' ? value.trim() : '';
15
+ }
16
+ function firstMatchingFamily(value, patterns) {
17
+ for (const [pattern, family] of patterns) {
18
+ if (pattern.test(value)) {
19
+ return family;
20
+ }
21
+ }
22
+ return undefined;
23
+ }
24
+ export function classifyModelFamily(input) {
25
+ const providerId = normalize(input?.providerId);
26
+ const providerFamily = firstMatchingFamily(providerId, PROVIDER_ID_FAMILIES);
27
+ if (providerFamily) {
28
+ return providerFamily;
29
+ }
30
+ const value = normalize(input?.value);
31
+ return firstMatchingFamily(value, VALUE_FAMILIES) ?? 'unknown';
32
+ }
@@ -0,0 +1,21 @@
1
+ export type IdentityProvenance = 'declared' | 'observed' | 'inferred' | 'unknown';
2
+ export type IdentityConfidence = 'high' | 'medium' | 'low' | 'unknown';
3
+ export interface IdentityRecord {
4
+ value: string;
5
+ provenance: IdentityProvenance;
6
+ /**
7
+ * True when the harness has been proven to reject invalid model selections
8
+ * instead of silently falling back to another model.
9
+ */
10
+ rejectOnInvalid?: boolean;
11
+ }
12
+ export interface ResolvedIdentity {
13
+ value: string;
14
+ provenance: IdentityProvenance;
15
+ confidence: IdentityConfidence;
16
+ mismatch: boolean;
17
+ diversityClaimable: boolean;
18
+ records: IdentityRecord[];
19
+ }
20
+ export declare function resolveIdentityConfidence(records: IdentityRecord[]): ResolvedIdentity;
21
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/provenance.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,UAAU,GACV,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,kBAAkB,CAAC;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,kBAAkB,CAAC;IAC/B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AA+DD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,cAAc,EAAE,GACxB,gBAAgB,CAmClB"}
@@ -0,0 +1,65 @@
1
+ const PROVENANCE_PRIORITY = {
2
+ declared: 4,
3
+ observed: 3,
4
+ inferred: 2,
5
+ unknown: 1,
6
+ };
7
+ function normalizedValue(value) {
8
+ return typeof value === 'string' && value.trim().length > 0
9
+ ? value.trim()
10
+ : 'unknown';
11
+ }
12
+ function normalizeRecord(record) {
13
+ return {
14
+ ...record,
15
+ value: normalizedValue(record.value),
16
+ };
17
+ }
18
+ function strongestRecord(records, provenance) {
19
+ return records.find((record) => record.provenance === provenance && record.value !== 'unknown');
20
+ }
21
+ function strongestKnownRecord(records) {
22
+ return records
23
+ .filter((record) => record.value !== 'unknown')
24
+ .sort((left, right) => PROVENANCE_PRIORITY[right.provenance] -
25
+ PROVENANCE_PRIORITY[left.provenance])[0];
26
+ }
27
+ function resolved(record, confidence, mismatch, records) {
28
+ const value = record.value;
29
+ return {
30
+ value,
31
+ provenance: record.provenance,
32
+ confidence,
33
+ mismatch,
34
+ diversityClaimable: value !== 'unknown' &&
35
+ record.provenance !== 'unknown' &&
36
+ confidence !== 'unknown',
37
+ records,
38
+ };
39
+ }
40
+ export function resolveIdentityConfidence(records) {
41
+ const normalizedRecords = records.map(normalizeRecord);
42
+ const declared = strongestRecord(normalizedRecords, 'declared');
43
+ const observed = strongestRecord(normalizedRecords, 'observed');
44
+ if (declared && observed) {
45
+ if (declared.value === observed.value) {
46
+ return resolved(declared, 'high', false, normalizedRecords);
47
+ }
48
+ return resolved(observed, 'low', true, normalizedRecords);
49
+ }
50
+ if (declared) {
51
+ return resolved(declared, declared.rejectOnInvalid === true ? 'high' : 'medium', false, normalizedRecords);
52
+ }
53
+ if (observed) {
54
+ return resolved(observed, 'low', false, normalizedRecords);
55
+ }
56
+ const inferred = strongestRecord(normalizedRecords, 'inferred');
57
+ if (inferred) {
58
+ return resolved(inferred, 'low', false, normalizedRecords);
59
+ }
60
+ const fallback = strongestKnownRecord(normalizedRecords) ?? {
61
+ value: 'unknown',
62
+ provenance: 'unknown',
63
+ };
64
+ return resolved(fallback, 'unknown', false, normalizedRecords);
65
+ }
@@ -0,0 +1,35 @@
1
+ import type { IdentityProvenance } from './provenance.js';
2
+ export type DispatchAction = 'implementation' | 'fix' | 'review';
3
+ export type DispatchRole = 'implementer' | 'fix' | 'reviewer';
4
+ export interface DispatchStampRecord {
5
+ scope: string;
6
+ action: DispatchAction;
7
+ role: DispatchRole;
8
+ producer: string;
9
+ provenance: IdentityProvenance;
10
+ modelAxis: string;
11
+ effortAxis: string;
12
+ dispatchPolicy: string;
13
+ dispatchCeiling: string;
14
+ target: string;
15
+ }
16
+ export interface DispatchStamp extends DispatchStampRecord {
17
+ line: string;
18
+ lineNumber: number;
19
+ legacy: boolean;
20
+ }
21
+ export interface ProducerIdentity {
22
+ scope: string;
23
+ action: DispatchAction;
24
+ role: DispatchRole;
25
+ producer: string;
26
+ provenance: IdentityProvenance;
27
+ target: string;
28
+ }
29
+ export interface ParseDispatchStampsOptions {
30
+ onWarning?: (warning: string) => void;
31
+ }
32
+ export declare function formatDispatchStamp(record: DispatchStampRecord): string;
33
+ export declare function parseDispatchStamps(markdown: string, options?: ParseDispatchStampsOptions): DispatchStamp[];
34
+ export declare function getProducerIdentitiesByScope(markdown: string, options?: ParseDispatchStampsOptions): Record<string, ProducerIdentity[]>;
35
+ //# sourceMappingURL=stamp.d.ts.map