docks-kit 0.10.1 → 0.11.0
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/AGENTS.md +4 -8
- package/README.md +3 -20
- package/cli/docs/flags.md +0 -20
- package/cli/docs/models.md +0 -27
- package/cli/docs/modifiers.md +0 -33
- package/cli/src/commands/models.ts +3 -7
- package/cli/src/engine-native/claudeSync.ts +1 -2
- package/cli/src/engine-native/codexSync.ts +1 -2
- package/cli/src/engine-native/index.ts +1 -16
- package/cli/src/engine-native/parseArgs.ts +0 -63
- package/cli/src/engine-native/sessionRelayCli.ts +17 -13
- package/cli/src/generated/sotPayload.ts +6 -6
- package/cli/src/main.ts +3 -54
- package/package.json +1 -1
- package/cli/src/engine-native/workflowDeploy.ts +0 -139
- package/cli/src/workflowModels.ts +0 -452
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CLAUDE_EFFORT_LEVELS,
|
|
3
|
-
CODEX_REASONING_EFFORTS
|
|
4
|
-
} from "./efforts"
|
|
5
|
-
import { payloadText } from "./payload"
|
|
6
|
-
|
|
7
|
-
export type WorkflowTool = "claude" | "codex"
|
|
8
|
-
export type WorkflowCompany = "anthropic" | "openai"
|
|
9
|
-
export type WorkflowRoleName = "orchestrator" | "reviewer" | "implementer"
|
|
10
|
-
export type WorkflowBoundName = "minimum_score" | "max_rounds"
|
|
11
|
-
export type WorkflowServiceTier = "fast"
|
|
12
|
-
|
|
13
|
-
export interface WorkflowCandidate {
|
|
14
|
-
readonly company: WorkflowCompany
|
|
15
|
-
readonly tool: WorkflowTool
|
|
16
|
-
readonly model: string
|
|
17
|
-
readonly effort: string
|
|
18
|
-
readonly service_tier?: WorkflowServiceTier
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface WorkflowRole {
|
|
22
|
-
readonly selector: string
|
|
23
|
-
readonly candidates: ReadonlyArray<WorkflowCandidate>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface WorkflowRecordV1 {
|
|
27
|
-
readonly schema: 1
|
|
28
|
-
readonly orchestrator: WorkflowRole
|
|
29
|
-
readonly reviewer: WorkflowRole
|
|
30
|
-
readonly implementer: WorkflowRole
|
|
31
|
-
readonly review: {
|
|
32
|
-
readonly minimum_score: number
|
|
33
|
-
readonly max_rounds: number
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface WorkflowRecordV2 {
|
|
38
|
-
readonly schema: 2
|
|
39
|
-
readonly orchestrator: WorkflowRole
|
|
40
|
-
readonly reviewer: WorkflowRole
|
|
41
|
-
readonly implementer: WorkflowRole
|
|
42
|
-
readonly review: {
|
|
43
|
-
readonly minimum_score: number
|
|
44
|
-
readonly max_rounds: number
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type WorkflowRecord = WorkflowRecordV1 | WorkflowRecordV2
|
|
49
|
-
|
|
50
|
-
export interface WorkflowOverrides {
|
|
51
|
-
readonly orchestrator?: string
|
|
52
|
-
readonly reviewer?: string
|
|
53
|
-
readonly implementer?: string
|
|
54
|
-
readonly minimumScore?: string
|
|
55
|
-
readonly maxRounds?: string
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface WorkflowProfile {
|
|
59
|
-
readonly candidates: ReadonlyArray<WorkflowCandidate>
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface WorkflowRegistryCore {
|
|
63
|
-
readonly schema: 2
|
|
64
|
-
readonly profiles: Readonly<Record<string, WorkflowProfile>>
|
|
65
|
-
readonly defaults: {
|
|
66
|
-
readonly orchestrator: string
|
|
67
|
-
readonly reviewer: string
|
|
68
|
-
readonly implementer: string
|
|
69
|
-
readonly review: {
|
|
70
|
-
readonly minimum_score: number
|
|
71
|
-
readonly max_rounds: number
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
readonly exact_target_grammar: "<tool>:<model>@<effort>[+fast]"
|
|
75
|
-
readonly availability: "checked_when_used"
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface WorkflowRegistryView extends WorkflowRegistryCore {
|
|
79
|
-
readonly tools: Readonly<Record<WorkflowTool, {
|
|
80
|
-
readonly models: ReadonlyArray<string>
|
|
81
|
-
readonly efforts: ReadonlyArray<string>
|
|
82
|
-
}>>
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type UnknownRecord = Record<string, unknown>
|
|
86
|
-
|
|
87
|
-
export const WORKFLOW_RECORD_PREFIX = "Docks-workflow-models: "
|
|
88
|
-
|
|
89
|
-
const TOOL_COMPANIES: Readonly<Record<WorkflowTool, WorkflowCompany>> = {
|
|
90
|
-
claude: "anthropic",
|
|
91
|
-
codex: "openai"
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const isRecord = (value: unknown): value is UnknownRecord =>
|
|
95
|
-
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
96
|
-
|
|
97
|
-
function hasExactKeys(value: UnknownRecord, keys: ReadonlyArray<string>): boolean {
|
|
98
|
-
const actual = Object.keys(value).sort()
|
|
99
|
-
return actual.length === keys.length && keys.every((key, index) => actual[index] === key)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function expectRecord(value: unknown, label: string, keys: ReadonlyArray<string>): UnknownRecord {
|
|
103
|
-
if (!isRecord(value) || !hasExactKeys(value, [...keys].sort())) {
|
|
104
|
-
throw new Error(`${label} must be a closed record`)
|
|
105
|
-
}
|
|
106
|
-
return value
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function parseManifest(): UnknownRecord {
|
|
110
|
-
const parsed = JSON.parse(payloadText("SoT/models.json")) as unknown
|
|
111
|
-
if (!isRecord(parsed)) throw new Error("Embedded model catalog must be an object")
|
|
112
|
-
return parsed
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function toolModels(manifest: UnknownRecord, tool: WorkflowTool): ReadonlyArray<string> {
|
|
116
|
-
const entry = manifest[tool]
|
|
117
|
-
if (!isRecord(entry) || !Array.isArray(entry["models"])) {
|
|
118
|
-
throw new Error(`Embedded ${tool} model catalog is invalid`)
|
|
119
|
-
}
|
|
120
|
-
const models = entry["models"].map((item) => {
|
|
121
|
-
if (!isRecord(item) || typeof item["id"] !== "string") {
|
|
122
|
-
throw new Error(`Embedded ${tool} model entry is invalid`)
|
|
123
|
-
}
|
|
124
|
-
return item["id"]
|
|
125
|
-
})
|
|
126
|
-
return models.filter((model) => model !== "default")
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const toolEfforts = (tool: WorkflowTool): ReadonlyArray<string> =>
|
|
130
|
-
tool === "claude" ? CLAUDE_EFFORT_LEVELS : CODEX_REASONING_EFFORTS
|
|
131
|
-
|
|
132
|
-
function parseCandidate(
|
|
133
|
-
value: unknown,
|
|
134
|
-
label: string,
|
|
135
|
-
tools: WorkflowRegistryView["tools"],
|
|
136
|
-
allowServiceTier: boolean
|
|
137
|
-
): WorkflowCandidate {
|
|
138
|
-
const serviceTier = isRecord(value) ? value["service_tier"] : undefined
|
|
139
|
-
const keys = serviceTier === undefined
|
|
140
|
-
? ["company", "effort", "model", "tool"]
|
|
141
|
-
: ["company", "effort", "model", "service_tier", "tool"]
|
|
142
|
-
const candidate = expectRecord(value, label, keys)
|
|
143
|
-
const tool = candidate["tool"]
|
|
144
|
-
if (tool !== "claude" && tool !== "codex") throw new Error(`${label} has an invalid tool`)
|
|
145
|
-
const company = candidate["company"]
|
|
146
|
-
const expectedCompany = TOOL_COMPANIES[tool]
|
|
147
|
-
if (company !== expectedCompany) throw new Error(`${label} has an invalid company`)
|
|
148
|
-
const model = candidate["model"]
|
|
149
|
-
if (typeof model !== "string" || !tools[tool].models.includes(model)) {
|
|
150
|
-
throw new Error(`${label} has an unverified ${tool} model`)
|
|
151
|
-
}
|
|
152
|
-
const effort = candidate["effort"]
|
|
153
|
-
if (typeof effort !== "string" || !tools[tool].efforts.includes(effort)) {
|
|
154
|
-
throw new Error(`${label} has an unverified ${tool} effort`)
|
|
155
|
-
}
|
|
156
|
-
if (serviceTier !== undefined) {
|
|
157
|
-
if (!allowServiceTier) throw new Error(`${label} requires workflow record schema 2 for a service tier`)
|
|
158
|
-
if (tool !== "codex" || serviceTier !== "fast") throw new Error(`${label} has an invalid service tier`)
|
|
159
|
-
return { company: expectedCompany, tool, model, effort, service_tier: "fast" }
|
|
160
|
-
}
|
|
161
|
-
return { company: expectedCompany, tool, model, effort }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function parseCandidates(
|
|
165
|
-
value: unknown,
|
|
166
|
-
label: string,
|
|
167
|
-
tools: WorkflowRegistryView["tools"],
|
|
168
|
-
allowServiceTier = false
|
|
169
|
-
): ReadonlyArray<WorkflowCandidate> {
|
|
170
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 3) {
|
|
171
|
-
throw new Error(`${label} must contain one to three candidates`)
|
|
172
|
-
}
|
|
173
|
-
return value.map((candidate, index) =>
|
|
174
|
-
parseCandidate(candidate, `${label}[${index}]`, tools, allowServiceTier)
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function parseNumericBound(name: WorkflowBoundName, value: unknown): number {
|
|
179
|
-
const [minimum, maximum] = name === "minimum_score" ? [0, 100] : [1, 10]
|
|
180
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) {
|
|
181
|
-
throw new Error(`${name} must be an integer in ${minimum}..${maximum}`)
|
|
182
|
-
}
|
|
183
|
-
return value
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function resolveSelector(
|
|
187
|
-
selector: string,
|
|
188
|
-
registry: Pick<WorkflowRegistryView, "profiles" | "tools">
|
|
189
|
-
): WorkflowRole {
|
|
190
|
-
const profileMatch = /^profile:([A-Za-z0-9._-]+)$/.exec(selector)
|
|
191
|
-
if (profileMatch !== null) {
|
|
192
|
-
const profileName = profileMatch[1]!
|
|
193
|
-
const profile = registry.profiles[profileName]
|
|
194
|
-
if (profile === undefined) throw new Error(`Unknown workflow profile '${profileName}'`)
|
|
195
|
-
return { selector, candidates: profile.candidates }
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const exactMatch = /^(claude|codex):([A-Za-z0-9._-]+)@([A-Za-z0-9._-]+)(\+fast)?$/.exec(selector)
|
|
199
|
-
if (exactMatch === null) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
`Invalid workflow selector '${selector}' — expected profile:<name> or <tool>:<model>@<effort>[+fast]`
|
|
202
|
-
)
|
|
203
|
-
}
|
|
204
|
-
const tool = exactMatch[1] as WorkflowTool
|
|
205
|
-
const model = exactMatch[2]!
|
|
206
|
-
const effort = exactMatch[3]!
|
|
207
|
-
const fast = exactMatch[4] !== undefined
|
|
208
|
-
if (!registry.tools[tool].models.includes(model)) {
|
|
209
|
-
throw new Error(`Unknown ${tool} workflow model '${model}'`)
|
|
210
|
-
}
|
|
211
|
-
if (!registry.tools[tool].efforts.includes(effort)) {
|
|
212
|
-
throw new Error(`Unknown ${tool} workflow effort '${effort}'`)
|
|
213
|
-
}
|
|
214
|
-
if (fast && tool !== "codex") {
|
|
215
|
-
throw new Error(`Invalid workflow selector '${selector}' — Fast is available only for Codex selectors`)
|
|
216
|
-
}
|
|
217
|
-
const candidate: WorkflowCandidate = { company: TOOL_COMPANIES[tool], tool, model, effort }
|
|
218
|
-
return {
|
|
219
|
-
selector,
|
|
220
|
-
candidates: [fast ? { ...candidate, service_tier: "fast" } : candidate]
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function loadWorkflowRegistry(): WorkflowRegistryView {
|
|
225
|
-
const manifest = parseManifest()
|
|
226
|
-
const tools: WorkflowRegistryView["tools"] = {
|
|
227
|
-
claude: { models: toolModels(manifest, "claude"), efforts: toolEfforts("claude") },
|
|
228
|
-
codex: { models: toolModels(manifest, "codex"), efforts: toolEfforts("codex") }
|
|
229
|
-
}
|
|
230
|
-
const workflow = expectRecord(
|
|
231
|
-
manifest["workflow"],
|
|
232
|
-
"Embedded workflow registry",
|
|
233
|
-
["availability", "defaults", "exact_target_grammar", "profiles", "schema"]
|
|
234
|
-
)
|
|
235
|
-
if (workflow["schema"] !== 2) throw new Error("Embedded workflow registry schema must be 2")
|
|
236
|
-
if (workflow["exact_target_grammar"] !== "<tool>:<model>@<effort>[+fast]") {
|
|
237
|
-
throw new Error("Embedded workflow exact-target grammar is invalid")
|
|
238
|
-
}
|
|
239
|
-
if (workflow["availability"] !== "checked_when_used") {
|
|
240
|
-
throw new Error("Embedded workflow availability contract is invalid")
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const rawProfiles = workflow["profiles"]
|
|
244
|
-
if (!isRecord(rawProfiles) || Object.keys(rawProfiles).length === 0) {
|
|
245
|
-
throw new Error("Embedded workflow profiles must be a nonempty record")
|
|
246
|
-
}
|
|
247
|
-
const profiles: Record<string, WorkflowProfile> = {}
|
|
248
|
-
for (const [name, rawProfile] of Object.entries(rawProfiles)) {
|
|
249
|
-
if (!/^[A-Za-z0-9._-]+$/.test(name)) throw new Error(`Invalid workflow profile name '${name}'`)
|
|
250
|
-
const profile = expectRecord(rawProfile, `Workflow profile '${name}'`, ["candidates"])
|
|
251
|
-
profiles[name] = {
|
|
252
|
-
candidates: parseCandidates(profile["candidates"], `Workflow profile '${name}' candidates`, tools)
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const rawDefaults = expectRecord(
|
|
257
|
-
workflow["defaults"],
|
|
258
|
-
"Embedded workflow defaults",
|
|
259
|
-
["implementer", "orchestrator", "review", "reviewer"]
|
|
260
|
-
)
|
|
261
|
-
const rawReview = expectRecord(
|
|
262
|
-
rawDefaults["review"],
|
|
263
|
-
"Embedded workflow review defaults",
|
|
264
|
-
["max_rounds", "minimum_score"]
|
|
265
|
-
)
|
|
266
|
-
for (const role of ["orchestrator", "reviewer", "implementer"] as const) {
|
|
267
|
-
if (typeof rawDefaults[role] !== "string") throw new Error(`Embedded workflow ${role} default is invalid`)
|
|
268
|
-
}
|
|
269
|
-
const registry: WorkflowRegistryView = {
|
|
270
|
-
schema: 2,
|
|
271
|
-
profiles,
|
|
272
|
-
defaults: {
|
|
273
|
-
orchestrator: rawDefaults["orchestrator"] as string,
|
|
274
|
-
reviewer: rawDefaults["reviewer"] as string,
|
|
275
|
-
implementer: rawDefaults["implementer"] as string,
|
|
276
|
-
review: {
|
|
277
|
-
minimum_score: parseNumericBound("minimum_score", rawReview["minimum_score"]),
|
|
278
|
-
max_rounds: parseNumericBound("max_rounds", rawReview["max_rounds"])
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
tools,
|
|
282
|
-
exact_target_grammar: "<tool>:<model>@<effort>[+fast]",
|
|
283
|
-
availability: "checked_when_used"
|
|
284
|
-
}
|
|
285
|
-
resolveSelector(registry.defaults.orchestrator, registry)
|
|
286
|
-
resolveSelector(registry.defaults.reviewer, registry)
|
|
287
|
-
resolveSelector(registry.defaults.implementer, registry)
|
|
288
|
-
return registry
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function workflowRegistryView(): WorkflowRegistryView {
|
|
292
|
-
return loadWorkflowRegistry()
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export function resolveWorkflowSelector(selector: string): WorkflowRole {
|
|
296
|
-
return resolveSelector(selector, loadWorkflowRegistry())
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export function parseWorkflowBound(name: WorkflowBoundName, value: string): number {
|
|
300
|
-
if (!/^[0-9]+$/.test(value)) {
|
|
301
|
-
const range = name === "minimum_score" ? "0..100" : "1..10"
|
|
302
|
-
throw new Error(`${name} must be a base-10 integer in ${range}`)
|
|
303
|
-
}
|
|
304
|
-
return parseNumericBound(name, Number(value))
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export function defaultWorkflowRecord(): WorkflowRecordV1 {
|
|
308
|
-
const registry = loadWorkflowRegistry()
|
|
309
|
-
return {
|
|
310
|
-
schema: 1,
|
|
311
|
-
orchestrator: resolveSelector(registry.defaults.orchestrator, registry),
|
|
312
|
-
reviewer: resolveSelector(registry.defaults.reviewer, registry),
|
|
313
|
-
implementer: resolveSelector(registry.defaults.implementer, registry),
|
|
314
|
-
review: registry.defaults.review
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function parseRole(
|
|
319
|
-
value: unknown,
|
|
320
|
-
name: WorkflowRoleName,
|
|
321
|
-
registry: WorkflowRegistryView,
|
|
322
|
-
schema: 1 | 2
|
|
323
|
-
): WorkflowRole {
|
|
324
|
-
const role = expectRecord(value, `Workflow ${name} role`, ["candidates", "selector"])
|
|
325
|
-
if (typeof role["selector"] !== "string") throw new Error(`Workflow ${name} selector is invalid`)
|
|
326
|
-
const resolved = resolveSelector(role["selector"], registry)
|
|
327
|
-
const candidates = parseCandidates(
|
|
328
|
-
role["candidates"],
|
|
329
|
-
`Workflow ${name} candidates`,
|
|
330
|
-
registry.tools,
|
|
331
|
-
schema === 2
|
|
332
|
-
)
|
|
333
|
-
if (compactJcs(candidates) !== compactJcs(resolved.candidates)) {
|
|
334
|
-
throw new Error(`Workflow ${name} candidates do not match its selector`)
|
|
335
|
-
}
|
|
336
|
-
return resolved
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function recordUsesFast(roles: ReadonlyArray<WorkflowRole>): boolean {
|
|
340
|
-
return roles.some((role) => role.candidates.some((candidate) => candidate.service_tier === "fast"))
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
export function parseWorkflowRecord(value: unknown): WorkflowRecord {
|
|
344
|
-
const record = expectRecord(
|
|
345
|
-
value,
|
|
346
|
-
"Workflow record",
|
|
347
|
-
["implementer", "orchestrator", "review", "reviewer", "schema"]
|
|
348
|
-
)
|
|
349
|
-
const schema = record["schema"]
|
|
350
|
-
if (schema !== 1 && schema !== 2) throw new Error("Workflow record schema must be 1 or 2")
|
|
351
|
-
const registry = loadWorkflowRegistry()
|
|
352
|
-
const review = expectRecord(record["review"], "Workflow record review", ["max_rounds", "minimum_score"])
|
|
353
|
-
const orchestrator = parseRole(record["orchestrator"], "orchestrator", registry, schema)
|
|
354
|
-
const reviewer = parseRole(record["reviewer"], "reviewer", registry, schema)
|
|
355
|
-
const implementer = parseRole(record["implementer"], "implementer", registry, schema)
|
|
356
|
-
const parsed = {
|
|
357
|
-
orchestrator,
|
|
358
|
-
reviewer,
|
|
359
|
-
implementer,
|
|
360
|
-
review: {
|
|
361
|
-
minimum_score: parseNumericBound("minimum_score", review["minimum_score"]),
|
|
362
|
-
max_rounds: parseNumericBound("max_rounds", review["max_rounds"])
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (schema === 2 && !recordUsesFast([orchestrator, reviewer, implementer])) {
|
|
366
|
-
throw new Error("Workflow record schema 2 requires a Fast service tier")
|
|
367
|
-
}
|
|
368
|
-
return schema === 2 ? { schema: 2, ...parsed } : { schema: 1, ...parsed }
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function buildWorkflowRecord(
|
|
372
|
-
overrides: WorkflowOverrides,
|
|
373
|
-
base: WorkflowRecord = defaultWorkflowRecord()
|
|
374
|
-
): WorkflowRecord {
|
|
375
|
-
const current = parseWorkflowRecord(base)
|
|
376
|
-
const orchestrator = overrides.orchestrator === undefined
|
|
377
|
-
? current.orchestrator
|
|
378
|
-
: resolveWorkflowSelector(overrides.orchestrator)
|
|
379
|
-
const reviewer = overrides.reviewer === undefined
|
|
380
|
-
? current.reviewer
|
|
381
|
-
: resolveWorkflowSelector(overrides.reviewer)
|
|
382
|
-
const implementer = overrides.implementer === undefined
|
|
383
|
-
? current.implementer
|
|
384
|
-
: resolveWorkflowSelector(overrides.implementer)
|
|
385
|
-
const next = {
|
|
386
|
-
orchestrator,
|
|
387
|
-
reviewer,
|
|
388
|
-
implementer,
|
|
389
|
-
review: {
|
|
390
|
-
minimum_score: overrides.minimumScore === undefined
|
|
391
|
-
? current.review.minimum_score
|
|
392
|
-
: parseWorkflowBound("minimum_score", overrides.minimumScore),
|
|
393
|
-
max_rounds: overrides.maxRounds === undefined
|
|
394
|
-
? current.review.max_rounds
|
|
395
|
-
: parseWorkflowBound("max_rounds", overrides.maxRounds)
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return recordUsesFast([orchestrator, reviewer, implementer])
|
|
399
|
-
? { schema: 2, ...next }
|
|
400
|
-
: { schema: 1, ...next }
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function canonicalValue(value: unknown): unknown {
|
|
404
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return value
|
|
405
|
-
if (typeof value === "number") {
|
|
406
|
-
if (!Number.isFinite(value)) throw new Error("JCS cannot encode a non-finite number")
|
|
407
|
-
return value
|
|
408
|
-
}
|
|
409
|
-
if (Array.isArray(value)) return value.map(canonicalValue)
|
|
410
|
-
if (isRecord(value)) {
|
|
411
|
-
return Object.fromEntries(
|
|
412
|
-
Object.entries(value)
|
|
413
|
-
.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
414
|
-
.map(([key, item]) => [key, canonicalValue(item)])
|
|
415
|
-
)
|
|
416
|
-
}
|
|
417
|
-
throw new Error("JCS can encode only JSON values")
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
export function compactJcs(value: unknown): string {
|
|
421
|
-
return JSON.stringify(canonicalValue(value))
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export function renderWorkflowRecordLine(record: WorkflowRecord): string {
|
|
425
|
-
return `${WORKFLOW_RECORD_PREFIX}${compactJcs(parseWorkflowRecord(record))}`
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
export function workflowRegistryJson(): string {
|
|
429
|
-
return JSON.stringify(workflowRegistryView(), null, 2)
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
export function workflowCatalog(): string {
|
|
433
|
-
const registry = workflowRegistryView()
|
|
434
|
-
const profileLines = Object.entries(registry.profiles).flatMap(([name, profile]) => [
|
|
435
|
-
` profile:${name}`,
|
|
436
|
-
...profile.candidates.map(({ tool, model, effort }) => ` ${tool}:${model}@${effort}`)
|
|
437
|
-
])
|
|
438
|
-
return [
|
|
439
|
-
"Workflow model registry:",
|
|
440
|
-
"Profiles:",
|
|
441
|
-
...profileLines,
|
|
442
|
-
"Defaults:",
|
|
443
|
-
` orchestrator ${registry.defaults.orchestrator}`,
|
|
444
|
-
` reviewer ${registry.defaults.reviewer}`,
|
|
445
|
-
` implementer ${registry.defaults.implementer}`,
|
|
446
|
-
` review minimum score ${registry.defaults.review.minimum_score}; maximum rounds ${registry.defaults.review.max_rounds}`,
|
|
447
|
-
`Exact targets: ${registry.exact_target_grammar}`,
|
|
448
|
-
"Fast: append +fast to a Codex exact target. Without +fast, Codex roles use Standard.",
|
|
449
|
-
"Compatibility: +fast requires a Docks consumer that supports workflow record schema 2.",
|
|
450
|
-
"Availability: checked when used by Docks; docks-kit does not probe providers."
|
|
451
|
-
].join("\n")
|
|
452
|
-
}
|