@bendyline/gezel 1.0.2 → 1.0.4
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/dist/checks/index.d.ts +1 -1
- package/dist/checks/index.js +2 -2
- package/dist/{index-CWMNVAhp.d.ts → index-Cj_G8Y-S.d.ts} +1369 -11
- package/dist/index.d.ts +46 -8
- package/dist/index.js +2247 -1053
- package/dist/markdown/index.d.ts +1 -1
- package/dist/markdown/index.js +55 -6
- package/dist/{report-action-DIz_2QOu.d.ts → report-action-UJ-pJpZO.d.ts} +43 -0
- package/dist/schemas/index.d.ts +2 -2
- package/dist/schemas/index.js +730 -571
- package/package.json +1 -1
package/dist/schemas/index.js
CHANGED
|
@@ -494,13 +494,14 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
|
|
|
494
494
|
label: z3.string().min(1).optional(),
|
|
495
495
|
artifact: z3.boolean().optional()
|
|
496
496
|
}),
|
|
497
|
-
/** `file` must NOT match `pattern` (regex)
|
|
497
|
+
/** `file` must NOT match `pattern` (regex), in the workspace or artifacts drawer. */
|
|
498
498
|
z3.object({
|
|
499
499
|
kind: z3.literal("notContains"),
|
|
500
500
|
file: z3.string().min(1),
|
|
501
501
|
pattern: z3.string().min(1),
|
|
502
502
|
flags: z3.string().optional(),
|
|
503
|
-
label: z3.string().min(1).optional()
|
|
503
|
+
label: z3.string().min(1).optional(),
|
|
504
|
+
artifact: z3.boolean().optional()
|
|
504
505
|
}),
|
|
505
506
|
/**
|
|
506
507
|
* `file` may use high-risk claim wording only when the exact matched
|
|
@@ -643,6 +644,20 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
|
|
|
643
644
|
tools: z3.array(z3.string().min(1)).min(1),
|
|
644
645
|
minSuccessful: z3.number().int().positive().optional()
|
|
645
646
|
}),
|
|
647
|
+
/**
|
|
648
|
+
* A PR-review coverage ledger must name every changed path materialized in
|
|
649
|
+
* a connector corpus. The ledger is workspace JSON (`reviewedFiles` by
|
|
650
|
+
* default); the source records live in the read-only artifacts drawer and
|
|
651
|
+
* carry their authoritative path in frontmatter. This prevents a polished
|
|
652
|
+
* report from passing after only the first context-sized prefix was read.
|
|
653
|
+
*/
|
|
654
|
+
z3.object({
|
|
655
|
+
kind: z3.literal("corpusCoverage"),
|
|
656
|
+
file: z3.string().min(1),
|
|
657
|
+
corpusDir: z3.string().min(1),
|
|
658
|
+
reviewedField: z3.string().min(1).optional(),
|
|
659
|
+
recordField: z3.string().min(1).optional()
|
|
660
|
+
}),
|
|
646
661
|
/**
|
|
647
662
|
* The H1 slide titles in `file` must match the numbered slide headings in
|
|
648
663
|
* `outlineFile`, one-for-one and in order. This makes a locked Markdown
|
|
@@ -652,7 +667,9 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
|
|
|
652
667
|
z3.object({
|
|
653
668
|
kind: z3.literal("markdownHeadingsMatch"),
|
|
654
669
|
file: z3.string().min(1),
|
|
655
|
-
outlineFile: z3.string().min(1)
|
|
670
|
+
outlineFile: z3.string().min(1),
|
|
671
|
+
/** Read outlineFile from artifacts while file uses the check's primary surface. */
|
|
672
|
+
outlineArtifact: z3.boolean().optional()
|
|
656
673
|
}),
|
|
657
674
|
/**
|
|
658
675
|
* Named facts in `file` must come from authorized sources: each fact
|
|
@@ -915,6 +932,12 @@ var AdvanceWhenSchema = z5.object({
|
|
|
915
932
|
/** Step to activate on the signal. Defaults to `next`; must resolve like `next`. */
|
|
916
933
|
goto: z5.string().optional()
|
|
917
934
|
});
|
|
935
|
+
var CraftbookStepInputSchema = z5.object({
|
|
936
|
+
/** Path relative to the selected drawer's root. */
|
|
937
|
+
file: z5.string().min(1).describe("Path relative to the selected drawer root."),
|
|
938
|
+
/** Read from the artifacts drawer instead of the project workspace. */
|
|
939
|
+
artifact: z5.boolean().optional().describe("True for the artifacts drawer; false/omitted for the project workspace.")
|
|
940
|
+
});
|
|
918
941
|
var ModelTierSchema = z5.enum(MODEL_TIER_ORDER);
|
|
919
942
|
var CraftbookStepSchema = z5.object({
|
|
920
943
|
id: z5.string().min(1),
|
|
@@ -955,6 +978,10 @@ var CraftbookStepSchema = z5.object({
|
|
|
955
978
|
* read the LAST ref's output (legacy routing; prefer gate routing).
|
|
956
979
|
*/
|
|
957
980
|
onExit: ScriptRefListSchema.optional(),
|
|
981
|
+
/** Required file inputs for this step, in the order they should be opened. */
|
|
982
|
+
consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
|
|
983
|
+
"Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
|
|
984
|
+
),
|
|
958
985
|
/** See {@link AdvanceWhenSchema}. */
|
|
959
986
|
advanceWhen: AdvanceWhenSchema.optional(),
|
|
960
987
|
/** The end-of-step decision. See {@link StepGateSchema} (current) / {@link GateSpecSchema} (legacy). */
|
|
@@ -966,14 +993,16 @@ var CraftbookStepSchema = z5.object({
|
|
|
966
993
|
* Marks the parent step that triggers a declarative per-item fanout
|
|
967
994
|
* (see {@link CraftbookSpawnSchema}). When this step activates on a
|
|
968
995
|
* spawn-host task, the runtime reads the craftbook's `spawn.overFile`
|
|
969
|
-
*
|
|
996
|
+
* JSON array on its declared surface and spawns one child task per item — no model
|
|
970
997
|
* tool call. Inert unless the craftbook also declares `spawn`.
|
|
971
998
|
*/
|
|
972
999
|
spawnFanout: z5.boolean().optional()
|
|
973
1000
|
});
|
|
974
1001
|
var CraftbookSpawnSchema = z5.object({
|
|
975
|
-
/**
|
|
1002
|
+
/** Surface-relative JSON file the parent produces; its array drives the fanout. */
|
|
976
1003
|
overFile: z5.string().min(1),
|
|
1004
|
+
/** Read overFile from the artifacts drawer instead of the project workspace. */
|
|
1005
|
+
overArtifact: z5.boolean().optional(),
|
|
977
1006
|
/** Dotted path to the array inside `overFile`. Absent → the file itself is the array. */
|
|
978
1007
|
itemsPath: z5.string().optional(),
|
|
979
1008
|
/** Entry step id of the child template. Defaults to the first `steps` entry. */
|
|
@@ -1009,6 +1038,14 @@ function validateCraftbookGraph(cb) {
|
|
|
1009
1038
|
problems.push(`step "${s.id}" advanceWhen.goto "${s.advanceWhen.goto}" missing from steps`);
|
|
1010
1039
|
}
|
|
1011
1040
|
}
|
|
1041
|
+
for (const input of s.consumes ?? []) {
|
|
1042
|
+
if (!input.artifact) continue;
|
|
1043
|
+
if (!/`read_artifact(?:`|\()/.test(s.prompt ?? "")) {
|
|
1044
|
+
problems.push(
|
|
1045
|
+
`step "${s.id}" consumes artifact "${input.file}" but its prompt does not explicitly call \`read_artifact\``
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1012
1049
|
if (s.gate) {
|
|
1013
1050
|
const gate = normalizeStepGate(s.gate);
|
|
1014
1051
|
if (s.terminal && gate.at === "activation") {
|
|
@@ -1301,6 +1338,9 @@ var NewCraftbookStepSchema = z5.object({
|
|
|
1301
1338
|
assignee: TaskAssigneeSchema.optional(),
|
|
1302
1339
|
onEnter: ScriptRefListSchema.optional(),
|
|
1303
1340
|
onExit: ScriptRefListSchema.optional(),
|
|
1341
|
+
consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
|
|
1342
|
+
"Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
|
|
1343
|
+
),
|
|
1304
1344
|
advanceWhen: AdvanceWhenSchema.optional(),
|
|
1305
1345
|
gate: StepGateUnionSchema.optional(),
|
|
1306
1346
|
/** See {@link StepDeliverableSchema} — one field attaches the enforced gate. */
|
|
@@ -1413,6 +1453,7 @@ function resolveSteps(blueprints) {
|
|
|
1413
1453
|
...s.assignee ? { assignee: s.assignee } : {},
|
|
1414
1454
|
...s.onEnter ? { onEnter: s.onEnter } : {},
|
|
1415
1455
|
...s.onExit ? { onExit: s.onExit } : {},
|
|
1456
|
+
...s.consumes && s.consumes.length > 0 ? { consumes: s.consumes } : {},
|
|
1416
1457
|
...s.advanceWhen ? { advanceWhen: s.advanceWhen } : {},
|
|
1417
1458
|
...s.gate ? { gate: s.gate } : {},
|
|
1418
1459
|
...s.next ? { next: s.next } : {},
|
|
@@ -1535,6 +1576,10 @@ function applyStepPatch(step, patch) {
|
|
|
1535
1576
|
delete updated.onExit;
|
|
1536
1577
|
} else updated.onExit = patch.onExit;
|
|
1537
1578
|
}
|
|
1579
|
+
if (patch.consumes !== void 0) {
|
|
1580
|
+
if (patch.consumes === null || patch.consumes.length === 0) delete updated.consumes;
|
|
1581
|
+
else updated.consumes = patch.consumes;
|
|
1582
|
+
}
|
|
1538
1583
|
if (patch.advanceWhen !== void 0) {
|
|
1539
1584
|
if (patch.advanceWhen === null) delete updated.advanceWhen;
|
|
1540
1585
|
else updated.advanceWhen = patch.advanceWhen;
|
|
@@ -1713,7 +1758,7 @@ function craftbookDocFormatFromEnv(value) {
|
|
|
1713
1758
|
}
|
|
1714
1759
|
|
|
1715
1760
|
// src/schemas/craftbook-test.ts
|
|
1716
|
-
import { z as
|
|
1761
|
+
import { z as z20 } from "zod";
|
|
1717
1762
|
|
|
1718
1763
|
// src/schemas/history.ts
|
|
1719
1764
|
import { z as z18 } from "zod";
|
|
@@ -3534,7 +3579,15 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
|
|
|
3534
3579
|
kind: z17.string(),
|
|
3535
3580
|
summary: z17.string(),
|
|
3536
3581
|
at: z17.string(),
|
|
3537
|
-
taskRef: z17.string().optional()
|
|
3582
|
+
taskRef: z17.string().optional(),
|
|
3583
|
+
/**
|
|
3584
|
+
* Gezel responsible for the event, when History recorded one. Kept
|
|
3585
|
+
* separate from the human-readable summary so fixed-presentation
|
|
3586
|
+
* clients (notably the role-name-only CLI) can render the actor using
|
|
3587
|
+
* their own naming mode instead of leaking the friendly name embedded
|
|
3588
|
+
* in the audit prose.
|
|
3589
|
+
*/
|
|
3590
|
+
gezelId: z17.string().optional()
|
|
3538
3591
|
}),
|
|
3539
3592
|
/**
|
|
3540
3593
|
* Emitted when a gezel crosses a growth level threshold and a pending
|
|
@@ -4075,444 +4128,9 @@ var ListHistoryResponseSchema = z18.object({
|
|
|
4075
4128
|
entries: z18.array(HistoryEntrySchema)
|
|
4076
4129
|
});
|
|
4077
4130
|
|
|
4078
|
-
// src/schemas/craftbook-test.ts
|
|
4079
|
-
var CRAFTBOOK_TEST_SCHEMA_VERSION = 1;
|
|
4080
|
-
var CRAFTBOOK_TEST_FILENAME = "test.json";
|
|
4081
|
-
var PrometheusAlertsCheckSchema = z19.object({
|
|
4082
|
-
kind: z19.literal("prometheusAlerts"),
|
|
4083
|
-
file: z19.string().min(1),
|
|
4084
|
-
minRules: z19.number().int().positive().optional(),
|
|
4085
|
-
maxPageAlerts: z19.number().int().nonnegative().optional(),
|
|
4086
|
-
allowedSeverities: z19.array(z19.string().min(1)).optional(),
|
|
4087
|
-
requiredServices: z19.array(z19.string().min(1)).optional(),
|
|
4088
|
-
requiredRunbookUrls: z19.array(z19.string().min(1)).optional()
|
|
4089
|
-
}).strict();
|
|
4090
|
-
var NodeScriptPassesCheckSchema = z19.object({
|
|
4091
|
-
kind: z19.literal("nodeScriptPasses"),
|
|
4092
|
-
script: z19.string().min(1),
|
|
4093
|
-
timeoutMs: z19.number().int().positive().optional(),
|
|
4094
|
-
requiredOutput: z19.array(
|
|
4095
|
-
z19.object({
|
|
4096
|
-
pattern: z19.string().min(1),
|
|
4097
|
-
flags: z19.string().optional(),
|
|
4098
|
-
label: z19.string().optional()
|
|
4099
|
-
}).strict()
|
|
4100
|
-
).optional()
|
|
4101
|
-
}).strict();
|
|
4102
|
-
var BinaryDocumentCheckSchema = z19.object({
|
|
4103
|
-
kind: z19.literal("binaryDocument"),
|
|
4104
|
-
file: z19.string().min(1),
|
|
4105
|
-
/** Look in the artifacts drawer instead of the workspace. */
|
|
4106
|
-
artifact: z19.boolean().optional(),
|
|
4107
|
-
/** Floor on the container's byte length; defaults to 1000. */
|
|
4108
|
-
minBytes: z19.number().int().positive().optional()
|
|
4109
|
-
}).strict();
|
|
4110
|
-
var CraftbookTestCheckSchema = z19.union([
|
|
4111
|
-
GateCheckSchema,
|
|
4112
|
-
PrometheusAlertsCheckSchema,
|
|
4113
|
-
NodeScriptPassesCheckSchema,
|
|
4114
|
-
BinaryDocumentCheckSchema
|
|
4115
|
-
]);
|
|
4116
|
-
var MockServiceIdSchema = z19.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, "mock service ids are lowercase kebab-case");
|
|
4117
|
-
var MockToolsetIdSchema = z19.string().min(1).regex(
|
|
4118
|
-
/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,
|
|
4119
|
-
"mock toolset ids are lowercase catalog ids or scoped npm-style ids"
|
|
4120
|
-
);
|
|
4121
|
-
var MockHttpRouteSchema = z19.object({
|
|
4122
|
-
method: z19.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
4123
|
-
/** Exact path, `:param` segments, or a trailing `*` wildcard. */
|
|
4124
|
-
path: z19.string().min(1),
|
|
4125
|
-
status: z19.number().int().min(100).max(599).default(200),
|
|
4126
|
-
headers: z19.record(z19.string(), z19.string()).optional(),
|
|
4127
|
-
/** String bodies are served verbatim; anything else is JSON-encoded. */
|
|
4128
|
-
body: z19.unknown(),
|
|
4129
|
-
latencyMs: z19.number().int().nonnegative().optional()
|
|
4130
|
-
}).strict();
|
|
4131
|
-
var MockServiceSchema = z19.discriminatedUnion("kind", [
|
|
4132
|
-
z19.object({
|
|
4133
|
-
kind: z19.literal("http"),
|
|
4134
|
-
id: MockServiceIdSchema,
|
|
4135
|
-
description: z19.string().min(1),
|
|
4136
|
-
/**
|
|
4137
|
-
* v1 mock HTTP is reachable ONLY through the `http.authed`
|
|
4138
|
-
* credential rail (anonymous script/browser HTTP hard-rejects
|
|
4139
|
-
* loopback), so a credential is required. The harness seeds it and
|
|
4140
|
-
* grants the mock's exact origin on the trial project.
|
|
4141
|
-
*/
|
|
4142
|
-
credential: z19.object({
|
|
4143
|
-
name: z19.string().regex(/^mock\.[a-z0-9][a-z0-9.-]*$/, "mock credentials are named mock.<service-id>"),
|
|
4144
|
-
authScheme: z19.enum(["bearer", "basic"]).optional()
|
|
4145
|
-
}).strict(),
|
|
4146
|
-
routes: z19.array(MockHttpRouteSchema).min(1)
|
|
4147
|
-
}).strict(),
|
|
4148
|
-
z19.object({
|
|
4149
|
-
kind: z19.literal("webhook"),
|
|
4150
|
-
id: MockServiceIdSchema,
|
|
4151
|
-
description: z19.string().min(1),
|
|
4152
|
-
/** Receiver path; defaults to `/webhook` when omitted. */
|
|
4153
|
-
path: z19.string().min(1).optional()
|
|
4154
|
-
}).strict(),
|
|
4155
|
-
z19.object({
|
|
4156
|
-
kind: z19.literal("cli"),
|
|
4157
|
-
id: MockServiceIdSchema,
|
|
4158
|
-
description: z19.string().min(1),
|
|
4159
|
-
/** Fake-CLI shim seeded as a workspace file (today's dry-run pattern). */
|
|
4160
|
-
shim: z19.object({ path: z19.string().min(1), content: z19.string() }).strict()
|
|
4161
|
-
}).strict(),
|
|
4162
|
-
z19.object({
|
|
4163
|
-
kind: z19.literal("mcp"),
|
|
4164
|
-
id: MockServiceIdSchema,
|
|
4165
|
-
description: z19.string().min(1),
|
|
4166
|
-
/** Override the local-catalog id when the mock replaces a real dependency. */
|
|
4167
|
-
toolsetId: MockToolsetIdSchema.optional(),
|
|
4168
|
-
/**
|
|
4169
|
-
* Served live by the eval mock rail: each declared tool becomes a
|
|
4170
|
-
* real tool on a per-trial Streamable-HTTP MCP endpoint, installed
|
|
4171
|
-
* into the trial via a local-catalog `mock-mcp-<id>` toolset.
|
|
4172
|
-
* `resultTemplate` is JSON-encoded as the tool result text
|
|
4173
|
-
* (default `{"ok":true}` when absent).
|
|
4174
|
-
*/
|
|
4175
|
-
tools: z19.array(
|
|
4176
|
-
z19.object({
|
|
4177
|
-
name: z19.string().min(1),
|
|
4178
|
-
description: z19.string().min(1),
|
|
4179
|
-
resultTemplate: z19.unknown().optional(),
|
|
4180
|
-
/** Deterministic stateful responses, consumed in call order; the last repeats. */
|
|
4181
|
-
resultSequence: z19.array(z19.unknown()).min(1).optional(),
|
|
4182
|
-
/**
|
|
4183
|
-
* Deterministic eval-only file materialization after this
|
|
4184
|
-
* tool call. The fixture must match the container the
|
|
4185
|
-
* deliverable's extension claims — a `minimal-pptx` written
|
|
4186
|
-
* to a `.docx` path now fails the `binaryDocument` check on
|
|
4187
|
-
* content type rather than sliding through a byte floor.
|
|
4188
|
-
*/
|
|
4189
|
-
writeFixture: z19.object({
|
|
4190
|
-
surface: z19.enum(["workspace", "artifact"]),
|
|
4191
|
-
pathArgument: z19.string().min(1),
|
|
4192
|
-
fixture: z19.enum(["minimal-pptx", "minimal-docx", "minimal-pdf", "minimal-png"])
|
|
4193
|
-
}).strict().optional()
|
|
4194
|
-
}).strict()
|
|
4195
|
-
).min(1)
|
|
4196
|
-
}).strict()
|
|
4197
|
-
]);
|
|
4198
|
-
var CraftbookTestFixtureFileSchema = z19.object({
|
|
4199
|
-
path: z19.string().min(1),
|
|
4200
|
-
content: z19.string(),
|
|
4201
|
-
/** Defaults to `workspace`; `harness` never enters the model-visible project. */
|
|
4202
|
-
surface: z19.enum(["workspace", "artifact", "harness"]).optional(),
|
|
4203
|
-
/**
|
|
4204
|
-
* Whether the fixture is presented to the model as source material.
|
|
4205
|
-
* Defaults to true; false still seeds the file for browsers and graders.
|
|
4206
|
-
*/
|
|
4207
|
-
modelInput: z19.boolean().optional()
|
|
4208
|
-
}).strict();
|
|
4209
|
-
var CraftbookTestWorkerSchema = z19.object({
|
|
4210
|
-
name: z19.string().min(1),
|
|
4211
|
-
role: z19.string().min(1),
|
|
4212
|
-
description: z19.string().optional(),
|
|
4213
|
-
about: z19.string().optional()
|
|
4214
|
-
}).strict();
|
|
4215
|
-
var CraftbookTestSetupSchema = z19.object({
|
|
4216
|
-
projectName: z19.string().min(1),
|
|
4217
|
-
about: z19.string().optional(),
|
|
4218
|
-
missionObjectives: z19.string().optional(),
|
|
4219
|
-
files: z19.array(CraftbookTestFixtureFileSchema).default([]),
|
|
4220
|
-
/** Exact values supplied to the catalog craftbook's `paramSchema`. */
|
|
4221
|
-
craftbookParams: z19.record(z19.string(), z19.string()).optional(),
|
|
4222
|
-
/**
|
|
4223
|
-
* Direct execution target. When present the harness seeds this gezel
|
|
4224
|
-
* and sends the kickoff straight to it (measuring whether the book
|
|
4225
|
-
* guides the work); absent → the Meester routes.
|
|
4226
|
-
*/
|
|
4227
|
-
worker: CraftbookTestWorkerSchema.optional()
|
|
4228
|
-
}).strict();
|
|
4229
|
-
var CraftbookTestDeliverableSchema = z19.object({
|
|
4230
|
-
path: z19.string().min(1),
|
|
4231
|
-
kind: DeliverableKindSchema,
|
|
4232
|
-
minBytes: z19.number().int().positive().optional(),
|
|
4233
|
-
checks: z19.array(CraftbookTestCheckSchema).optional()
|
|
4234
|
-
}).strict();
|
|
4235
|
-
var CraftbookTestMockExpectationSchema = z19.object({
|
|
4236
|
-
/** Mock service id from `mocks[]`. */
|
|
4237
|
-
service: MockServiceIdSchema,
|
|
4238
|
-
minRequests: z19.number().int().positive().optional(),
|
|
4239
|
-
/** Regex sources matched against logged request paths. */
|
|
4240
|
-
requiredPaths: z19.array(z19.string().min(1)).optional(),
|
|
4241
|
-
forbiddenPaths: z19.array(z19.string().min(1)).optional(),
|
|
4242
|
-
/**
|
|
4243
|
-
* Exact MCP tool names that must each have been called at least once
|
|
4244
|
-
* on this service (`kind: 'mcp'` only). Exact names, not regexes —
|
|
4245
|
-
* the tool roster is fully declared in the same file, so a pattern
|
|
4246
|
-
* buys nothing and invites drift. Cross-checked against the mock's
|
|
4247
|
-
* declared `tools[]` at parse time.
|
|
4248
|
-
*/
|
|
4249
|
-
requiredTools: z19.array(z19.string().min(1)).optional(),
|
|
4250
|
-
/** Per-MCP-tool call budgets for repeated journeys or retries. */
|
|
4251
|
-
toolCalls: z19.record(
|
|
4252
|
-
z19.string().min(1),
|
|
4253
|
-
z19.object({
|
|
4254
|
-
minCalls: z19.number().int().nonnegative().default(1),
|
|
4255
|
-
maxCalls: z19.number().int().nonnegative().optional()
|
|
4256
|
-
}).strict().refine(
|
|
4257
|
-
(value) => value.maxCalls === void 0 || value.minCalls <= value.maxCalls,
|
|
4258
|
-
"minCalls must be less than or equal to maxCalls"
|
|
4259
|
-
)
|
|
4260
|
-
).optional()
|
|
4261
|
-
}).strict();
|
|
4262
|
-
var CraftbookTestHistoryExpectationSchema = z19.object({
|
|
4263
|
-
kind: HistoryEventKindSchema,
|
|
4264
|
-
minEntries: z19.number().int().nonnegative().default(1),
|
|
4265
|
-
maxEntries: z19.number().int().nonnegative().optional(),
|
|
4266
|
-
summaryPattern: z19.string().min(1).optional(),
|
|
4267
|
-
flags: z19.string().optional(),
|
|
4268
|
-
details: z19.record(z19.string(), z19.union([z19.string(), z19.number(), z19.boolean(), z19.null()])).optional()
|
|
4269
|
-
}).strict();
|
|
4270
|
-
var CraftbookTestSuccessSchema = z19.object({
|
|
4271
|
-
summary: z19.string().min(1),
|
|
4272
|
-
deliverables: z19.array(CraftbookTestDeliverableSchema).optional(),
|
|
4273
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4274
|
-
taskNotes: z19.object({
|
|
4275
|
-
minBytes: z19.number().int().positive().optional(),
|
|
4276
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4277
|
-
requireCraftbookTask: z19.boolean().optional()
|
|
4278
|
-
}).strict().optional(),
|
|
4279
|
-
taskGraph: z19.object({
|
|
4280
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4281
|
-
requireCraftbookTask: z19.boolean().optional(),
|
|
4282
|
-
/** Require the matching task to reach a terminal step (or complete). */
|
|
4283
|
-
requireTerminalStep: z19.boolean().optional(),
|
|
4284
|
-
requireDraftRef: z19.boolean().optional(),
|
|
4285
|
-
draft: z19.object({
|
|
4286
|
-
status: z19.enum(["draft", "paused", "active", "complete", "canceled"]).optional(),
|
|
4287
|
-
minDescriptionBytes: z19.number().int().positive().optional(),
|
|
4288
|
-
minOutcomes: z19.number().int().positive().optional(),
|
|
4289
|
-
minSteps: z19.number().int().positive().optional(),
|
|
4290
|
-
requireTerminalVerification: z19.boolean().optional(),
|
|
4291
|
-
requireGatedBuildSteps: z19.boolean().optional()
|
|
4292
|
-
}).strict().optional()
|
|
4293
|
-
}).strict().optional(),
|
|
4294
|
-
/** Assertions evaluated against the live mock server's request log. */
|
|
4295
|
-
mocks: z19.array(CraftbookTestMockExpectationSchema).optional(),
|
|
4296
|
-
/** Assertions evaluated against the project's append-only History log. */
|
|
4297
|
-
history: z19.array(CraftbookTestHistoryExpectationSchema).optional(),
|
|
4298
|
-
/** Workspace fixtures whose final content must equal the seeded bytes exactly. */
|
|
4299
|
-
unchangedFixtures: z19.array(z19.string().min(1)).optional()
|
|
4300
|
-
}).strict();
|
|
4301
|
-
var CraftbookTestRubricSchema = z19.object({
|
|
4302
|
-
artifact: z19.object({
|
|
4303
|
-
/** Workspace or artifact path the judge reads (adapter derives the basename). */
|
|
4304
|
-
path: z19.string().min(1),
|
|
4305
|
-
kind: z19.enum(["html", "markdown", "yaml", "typescript", "json", "text"])
|
|
4306
|
-
}).strict(),
|
|
4307
|
-
axes: z19.array(z19.object({ name: z19.string().min(1), description: z19.string().min(1) }).strict()).min(1),
|
|
4308
|
-
contextNote: z19.string().optional()
|
|
4309
|
-
}).strict();
|
|
4310
|
-
var CraftbookTestSpecSchema = z19.object({
|
|
4311
|
-
schemaVersion: z19.literal(CRAFTBOOK_TEST_SCHEMA_VERSION),
|
|
4312
|
-
title: z19.string().min(1),
|
|
4313
|
-
objective: z19.string().min(1),
|
|
4314
|
-
/**
|
|
4315
|
-
* Task-class taxonomy tags (e.g. `html-game`, `corpus`, `external`).
|
|
4316
|
-
* The single declared source for harness selection and batch
|
|
4317
|
-
* planning — replaces the old regex classifiers.
|
|
4318
|
-
*/
|
|
4319
|
-
tags: z19.array(z19.string().min(1)).default([]),
|
|
4320
|
-
/** Kickoff chat message the harness sends. Required — every book runs. */
|
|
4321
|
-
prompt: z19.string().min(1),
|
|
4322
|
-
setup: CraftbookTestSetupSchema,
|
|
4323
|
-
mocks: z19.array(MockServiceSchema).default([]),
|
|
4324
|
-
success: CraftbookTestSuccessSchema,
|
|
4325
|
-
rubric: CraftbookTestRubricSchema,
|
|
4326
|
-
qualityFocus: z19.array(z19.string().min(1)).default([]),
|
|
4327
|
-
/**
|
|
4328
|
-
* Sanctioned escape hatch for experiments — carried opaquely, never
|
|
4329
|
-
* interpreted by CI. Promote a field out of here before relying on it.
|
|
4330
|
-
*/
|
|
4331
|
-
extensions: z19.record(z19.string(), z19.unknown()).optional()
|
|
4332
|
-
}).strict().superRefine((spec, ctx) => {
|
|
4333
|
-
const mockIds = new Set(spec.mocks.map((m) => m.id));
|
|
4334
|
-
const mockById = new Map(spec.mocks.map((m) => [m.id, m]));
|
|
4335
|
-
for (const [i, expectation] of (spec.success.mocks ?? []).entries()) {
|
|
4336
|
-
if (!mockIds.has(expectation.service)) {
|
|
4337
|
-
ctx.addIssue({
|
|
4338
|
-
code: z19.ZodIssueCode.custom,
|
|
4339
|
-
path: ["success", "mocks", i, "service"],
|
|
4340
|
-
message: `success.mocks[${i}] references unknown mock service "${expectation.service}"`
|
|
4341
|
-
});
|
|
4342
|
-
}
|
|
4343
|
-
if (expectation.requiredTools && expectation.requiredTools.length > 0) {
|
|
4344
|
-
const target = mockById.get(expectation.service);
|
|
4345
|
-
if (target && target.kind !== "mcp") {
|
|
4346
|
-
ctx.addIssue({
|
|
4347
|
-
code: z19.ZodIssueCode.custom,
|
|
4348
|
-
path: ["success", "mocks", i, "requiredTools"],
|
|
4349
|
-
message: `success.mocks[${i}].requiredTools requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4350
|
-
});
|
|
4351
|
-
} else if (target?.kind === "mcp") {
|
|
4352
|
-
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4353
|
-
for (const name of expectation.requiredTools) {
|
|
4354
|
-
if (!declared.has(name)) {
|
|
4355
|
-
ctx.addIssue({
|
|
4356
|
-
code: z19.ZodIssueCode.custom,
|
|
4357
|
-
path: ["success", "mocks", i, "requiredTools"],
|
|
4358
|
-
message: `success.mocks[${i}].requiredTools names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4359
|
-
});
|
|
4360
|
-
}
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
}
|
|
4364
|
-
if (expectation.toolCalls && Object.keys(expectation.toolCalls).length > 0) {
|
|
4365
|
-
const target = mockById.get(expectation.service);
|
|
4366
|
-
if (target && target.kind !== "mcp") {
|
|
4367
|
-
ctx.addIssue({
|
|
4368
|
-
code: z19.ZodIssueCode.custom,
|
|
4369
|
-
path: ["success", "mocks", i, "toolCalls"],
|
|
4370
|
-
message: `success.mocks[${i}].toolCalls requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4371
|
-
});
|
|
4372
|
-
} else if (target?.kind === "mcp") {
|
|
4373
|
-
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4374
|
-
for (const name of Object.keys(expectation.toolCalls)) {
|
|
4375
|
-
if (!declared.has(name)) {
|
|
4376
|
-
ctx.addIssue({
|
|
4377
|
-
code: z19.ZodIssueCode.custom,
|
|
4378
|
-
path: ["success", "mocks", i, "toolCalls", name],
|
|
4379
|
-
message: `success.mocks[${i}].toolCalls names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4380
|
-
});
|
|
4381
|
-
}
|
|
4382
|
-
}
|
|
4383
|
-
}
|
|
4384
|
-
}
|
|
4385
|
-
}
|
|
4386
|
-
for (const [i, mock] of spec.mocks.entries()) {
|
|
4387
|
-
if (mock.kind === "http" && mock.credential.name !== `mock.${mock.id}`) {
|
|
4388
|
-
ctx.addIssue({
|
|
4389
|
-
code: z19.ZodIssueCode.custom,
|
|
4390
|
-
path: ["mocks", i, "credential", "name"],
|
|
4391
|
-
message: `http mock "${mock.id}" must use credential name "mock.${mock.id}"`
|
|
4392
|
-
});
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
const workspaceFixtures = new Set(
|
|
4396
|
-
spec.setup.files.filter((file) => file.surface === void 0 || file.surface === "workspace").map((file) => file.path)
|
|
4397
|
-
);
|
|
4398
|
-
for (const [i, path] of (spec.success.unchangedFixtures ?? []).entries()) {
|
|
4399
|
-
if (!workspaceFixtures.has(path)) {
|
|
4400
|
-
ctx.addIssue({
|
|
4401
|
-
code: z19.ZodIssueCode.custom,
|
|
4402
|
-
path: ["success", "unchangedFixtures", i],
|
|
4403
|
-
message: `unchanged fixture "${path}" is not a seeded workspace file`
|
|
4404
|
-
});
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
for (const [i, expectation] of (spec.success.history ?? []).entries()) {
|
|
4408
|
-
if (expectation.maxEntries !== void 0 && expectation.minEntries > expectation.maxEntries) {
|
|
4409
|
-
ctx.addIssue({
|
|
4410
|
-
code: z19.ZodIssueCode.custom,
|
|
4411
|
-
path: ["success", "history", i],
|
|
4412
|
-
message: "minEntries must be less than or equal to maxEntries"
|
|
4413
|
-
});
|
|
4414
|
-
}
|
|
4415
|
-
}
|
|
4416
|
-
});
|
|
4417
|
-
function parseCraftbookTestSpec(raw, opts) {
|
|
4418
|
-
const mode = opts?.mode ?? "strict";
|
|
4419
|
-
const candidate = mode === "tolerant" ? deepStripUnknown(raw) : raw;
|
|
4420
|
-
const parsed = CraftbookTestSpecSchema.safeParse(candidate);
|
|
4421
|
-
if (parsed.success) return { ok: true, spec: parsed.data };
|
|
4422
|
-
return {
|
|
4423
|
-
ok: false,
|
|
4424
|
-
errors: parsed.error.issues.map((issue) => {
|
|
4425
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
|
|
4426
|
-
return `${path}: ${issue.message}`;
|
|
4427
|
-
})
|
|
4428
|
-
};
|
|
4429
|
-
}
|
|
4430
|
-
function deepStripUnknown(raw) {
|
|
4431
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
4432
|
-
const record = structuredClone(raw);
|
|
4433
|
-
const version = record.schemaVersion;
|
|
4434
|
-
if (typeof version === "number" && Number.isInteger(version) && version > CRAFTBOOK_TEST_SCHEMA_VERSION) {
|
|
4435
|
-
record.schemaVersion = CRAFTBOOK_TEST_SCHEMA_VERSION;
|
|
4436
|
-
}
|
|
4437
|
-
for (let pass = 0; pass < 16; pass++) {
|
|
4438
|
-
const attempt = CraftbookTestSpecSchema.safeParse(record);
|
|
4439
|
-
if (attempt.success) return record;
|
|
4440
|
-
const unknownKeyIssues = attempt.error.issues.filter(
|
|
4441
|
-
(issue) => issue.code === z19.ZodIssueCode.unrecognized_keys
|
|
4442
|
-
);
|
|
4443
|
-
if (unknownKeyIssues.length === 0) return record;
|
|
4444
|
-
for (const issue of unknownKeyIssues) {
|
|
4445
|
-
const target = resolvePath(record, issue.path);
|
|
4446
|
-
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
4447
|
-
for (const key of issue.keys) delete target[key];
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
}
|
|
4451
|
-
return record;
|
|
4452
|
-
}
|
|
4453
|
-
function resolvePath(root, path) {
|
|
4454
|
-
let node = root;
|
|
4455
|
-
for (const segment of path) {
|
|
4456
|
-
if (node === null || typeof node !== "object") return void 0;
|
|
4457
|
-
node = node[segment];
|
|
4458
|
-
}
|
|
4459
|
-
return node;
|
|
4460
|
-
}
|
|
4461
|
-
|
|
4462
|
-
// src/schemas/codex-setup.ts
|
|
4463
|
-
import { z as z20 } from "zod";
|
|
4464
|
-
var CodexSetupModelOptionSchema = z20.object({
|
|
4465
|
-
id: z20.string().min(1),
|
|
4466
|
-
label: z20.string().min(1),
|
|
4467
|
-
description: z20.string().optional(),
|
|
4468
|
-
kind: z20.enum(["gezel", "model"]).default("model"),
|
|
4469
|
-
provider: z20.string().min(1),
|
|
4470
|
-
/** Stable gezel id for persona-backed entries. Absent on raw-model entries. */
|
|
4471
|
-
gezelId: z20.string().min(1).optional(),
|
|
4472
|
-
role: z20.string().min(1).optional(),
|
|
4473
|
-
/** Human-readable name of the effective inference model behind a gezel. */
|
|
4474
|
-
modelLabel: z20.string().min(1).optional(),
|
|
4475
|
-
contextWindow: z20.number().int().positive().optional(),
|
|
4476
|
-
supportsReasoning: z20.boolean().optional(),
|
|
4477
|
-
supportsTools: z20.boolean().optional()
|
|
4478
|
-
});
|
|
4479
|
-
var CodexSetupStateSchema = z20.enum([
|
|
4480
|
-
"not-configured",
|
|
4481
|
-
"configured",
|
|
4482
|
-
"update-needed",
|
|
4483
|
-
"conflict",
|
|
4484
|
-
"unavailable"
|
|
4485
|
-
]);
|
|
4486
|
-
var CodexSetupStatusResponseSchema = z20.object({
|
|
4487
|
-
state: CodexSetupStateSchema,
|
|
4488
|
-
models: z20.array(CodexSetupModelOptionSchema),
|
|
4489
|
-
configuredModel: z20.string().optional(),
|
|
4490
|
-
recommendedModel: z20.string().optional(),
|
|
4491
|
-
reasons: z20.array(z20.string()),
|
|
4492
|
-
message: z20.string().optional(),
|
|
4493
|
-
codexInstalled: z20.boolean(),
|
|
4494
|
-
codexVersion: z20.string().optional(),
|
|
4495
|
-
codexPath: z20.string().optional(),
|
|
4496
|
-
endpointsEnabled: z20.boolean(),
|
|
4497
|
-
profileName: z20.string().min(1),
|
|
4498
|
-
profilePath: z20.string().min(1),
|
|
4499
|
-
launchCommand: z20.string().min(1),
|
|
4500
|
-
bridge: z20.object({
|
|
4501
|
-
baseUrl: z20.string().url(),
|
|
4502
|
-
listening: z20.boolean(),
|
|
4503
|
-
port: z20.number().int().nonnegative()
|
|
4504
|
-
}),
|
|
4505
|
-
canConfigure: z20.boolean(),
|
|
4506
|
-
/** Whether Gezel-owned credential/state material exists and can be safely removed. */
|
|
4507
|
-
canRemove: z20.boolean()
|
|
4508
|
-
});
|
|
4509
|
-
var ConfigureCodexRequestSchema = z20.object({
|
|
4510
|
-
model: z20.string().min(1)
|
|
4511
|
-
});
|
|
4512
|
-
|
|
4513
4131
|
// src/schemas/project.ts
|
|
4514
|
-
import { z as
|
|
4515
|
-
var HttpsOriginSchema =
|
|
4132
|
+
import { z as z19 } from "zod";
|
|
4133
|
+
var HttpsOriginSchema = z19.string().url().refine(
|
|
4516
4134
|
(value) => {
|
|
4517
4135
|
try {
|
|
4518
4136
|
const url = new URL(value);
|
|
@@ -4523,7 +4141,7 @@ var HttpsOriginSchema = z21.string().url().refine(
|
|
|
4523
4141
|
},
|
|
4524
4142
|
{ message: "must be an exact HTTPS origin (for example https://api.example.com)" }
|
|
4525
4143
|
);
|
|
4526
|
-
var ProjectGitHubSchema =
|
|
4144
|
+
var ProjectGitHubSchema = z19.object({
|
|
4527
4145
|
// Accept any non-empty string. Git URLs come in many forms beyond
|
|
4528
4146
|
// `https://`: ssh shorthand (`git@github.com:owner/repo`), local
|
|
4529
4147
|
// filesystem paths (`/path/to/bare.git`), other-host references.
|
|
@@ -4531,89 +4149,89 @@ var ProjectGitHubSchema = z21.object({
|
|
|
4531
4149
|
// and broke Phase-3 worktree tests that use a local bare repo as
|
|
4532
4150
|
// the upstream. Higher layers (the github sync code) parse the URL
|
|
4533
4151
|
// and reject anything that doesn't shape up as a clonable ref.
|
|
4534
|
-
url:
|
|
4535
|
-
branch:
|
|
4152
|
+
url: z19.string().min(1),
|
|
4153
|
+
branch: z19.string().optional(),
|
|
4536
4154
|
/** Resolved absolute path to the working tree. Managed by the service. */
|
|
4537
|
-
checkoutDir:
|
|
4538
|
-
lastSyncedAt:
|
|
4155
|
+
checkoutDir: z19.string().optional(),
|
|
4156
|
+
lastSyncedAt: z19.string().optional(),
|
|
4539
4157
|
/** Repo default branch (e.g. "main"), detected lazily and cached. Managed by the service. */
|
|
4540
|
-
defaultBranch:
|
|
4158
|
+
defaultBranch: z19.string().optional()
|
|
4541
4159
|
});
|
|
4542
|
-
var ProjectConnectorBindingSchema =
|
|
4160
|
+
var ProjectConnectorBindingSchema = z19.object({
|
|
4543
4161
|
/** Stable binding id; also the SecretStore `fieldId` + corpus-slug seed. */
|
|
4544
|
-
id:
|
|
4162
|
+
id: z19.string().min(1),
|
|
4545
4163
|
/** The connector-type catalog id, e.g. `mail-gmail`, `linear-issues`. */
|
|
4546
|
-
type:
|
|
4164
|
+
type: z19.string().min(1),
|
|
4547
4165
|
/** Catalog source the type resolved from (provenance/pin). */
|
|
4548
|
-
sourceId:
|
|
4166
|
+
sourceId: z19.string().optional(),
|
|
4549
4167
|
/** Pinned connector-type version. */
|
|
4550
|
-
version:
|
|
4551
|
-
displayName:
|
|
4168
|
+
version: z19.string().optional(),
|
|
4169
|
+
displayName: z19.string().optional(),
|
|
4552
4170
|
/**
|
|
4553
4171
|
* Artifact-relative corpus root (`data/<corpusName>`), resolved once at bind
|
|
4554
4172
|
* time and never recomputed — renaming a binding must not strand its corpus.
|
|
4555
4173
|
*/
|
|
4556
|
-
corpusDir:
|
|
4174
|
+
corpusDir: z19.string().optional(),
|
|
4557
4175
|
/** Per-binding config, validated at bind time against the type's `configSchema`. */
|
|
4558
|
-
config:
|
|
4176
|
+
config: z19.record(z19.string(), z19.unknown()).default({}),
|
|
4559
4177
|
/** Opaque, adapter-shaped incremental-sync cursor. Persisted so resync resumes. */
|
|
4560
|
-
cursor:
|
|
4178
|
+
cursor: z19.unknown().optional(),
|
|
4561
4179
|
/** Pause syncing without unbinding. */
|
|
4562
|
-
disabled:
|
|
4563
|
-
lastSyncedAt:
|
|
4180
|
+
disabled: z19.boolean().optional(),
|
|
4181
|
+
lastSyncedAt: z19.string().optional(),
|
|
4564
4182
|
/** Last sync error, surfaced in the UI; cleared on the next success. */
|
|
4565
|
-
lastError:
|
|
4183
|
+
lastError: z19.string().optional()
|
|
4566
4184
|
});
|
|
4567
|
-
var ProjectNudgeConfigSchema =
|
|
4568
|
-
enabled:
|
|
4569
|
-
rapidIntervalMs:
|
|
4570
|
-
slowIntervalMs:
|
|
4571
|
-
recentActivityWindowMs:
|
|
4572
|
-
rapidAttemptsBeforeBackoff:
|
|
4185
|
+
var ProjectNudgeConfigSchema = z19.object({
|
|
4186
|
+
enabled: z19.boolean().optional(),
|
|
4187
|
+
rapidIntervalMs: z19.number().int().positive().optional(),
|
|
4188
|
+
slowIntervalMs: z19.number().int().positive().optional(),
|
|
4189
|
+
recentActivityWindowMs: z19.number().int().positive().optional(),
|
|
4190
|
+
rapidAttemptsBeforeBackoff: z19.number().int().positive().optional(),
|
|
4573
4191
|
/**
|
|
4574
4192
|
* Grace period applied to the very first nudge a project ever
|
|
4575
4193
|
* receives, measured from `project.createdAt`. Default per tempo;
|
|
4576
4194
|
* setting `0` opts out (legacy behavior — first nudge fires as
|
|
4577
4195
|
* soon as the rapid interval allows).
|
|
4578
4196
|
*/
|
|
4579
|
-
firstNudgeGraceMs:
|
|
4197
|
+
firstNudgeGraceMs: z19.number().int().nonnegative().optional()
|
|
4580
4198
|
});
|
|
4581
|
-
var ProjectNudgeStateSchema =
|
|
4582
|
-
lastNudgedAt:
|
|
4583
|
-
consecutiveRapidNudges:
|
|
4199
|
+
var ProjectNudgeStateSchema = z19.object({
|
|
4200
|
+
lastNudgedAt: z19.string().optional(),
|
|
4201
|
+
consecutiveRapidNudges: z19.number().int().nonnegative().optional()
|
|
4584
4202
|
});
|
|
4585
|
-
var ProjectTabVisibilitySchema =
|
|
4586
|
-
overview:
|
|
4587
|
-
tasks:
|
|
4588
|
-
approvals:
|
|
4589
|
-
workspace:
|
|
4590
|
-
artifacts:
|
|
4591
|
-
map:
|
|
4203
|
+
var ProjectTabVisibilitySchema = z19.object({
|
|
4204
|
+
overview: z19.boolean().optional(),
|
|
4205
|
+
tasks: z19.boolean().optional(),
|
|
4206
|
+
approvals: z19.boolean().optional(),
|
|
4207
|
+
workspace: z19.boolean().optional(),
|
|
4208
|
+
artifacts: z19.boolean().optional(),
|
|
4209
|
+
map: z19.boolean().optional()
|
|
4592
4210
|
}).strict();
|
|
4593
|
-
var ProjectManagedWorkspaceWritePolicySchema =
|
|
4594
|
-
var ProjectTypeProvenanceSchema =
|
|
4211
|
+
var ProjectManagedWorkspaceWritePolicySchema = z19.enum(["auto", "allow", "deny"]);
|
|
4212
|
+
var ProjectTypeProvenanceSchema = z19.object({
|
|
4595
4213
|
/** Catalog id of the applied project type. */
|
|
4596
|
-
id:
|
|
4214
|
+
id: z19.string(),
|
|
4597
4215
|
/** Type version installed at adoption. */
|
|
4598
|
-
version:
|
|
4216
|
+
version: z19.string(),
|
|
4599
4217
|
/** Catalog source the type resolved from (`bundled` | `local` | `community` | …). */
|
|
4600
|
-
source:
|
|
4218
|
+
source: z19.string(),
|
|
4601
4219
|
/** Param values collected at adoption, substituted into templates + seed files. */
|
|
4602
|
-
params:
|
|
4220
|
+
params: z19.record(z19.string(), z19.unknown()).optional(),
|
|
4603
4221
|
/** ISO timestamp of adoption. */
|
|
4604
|
-
appliedAt:
|
|
4222
|
+
appliedAt: z19.string()
|
|
4605
4223
|
});
|
|
4606
|
-
var ProjectSchema =
|
|
4224
|
+
var ProjectSchema = z19.object({
|
|
4607
4225
|
id: EntityIdSchema,
|
|
4608
|
-
name:
|
|
4609
|
-
description:
|
|
4610
|
-
workingDir:
|
|
4226
|
+
name: z19.string(),
|
|
4227
|
+
description: z19.string().optional(),
|
|
4228
|
+
workingDir: z19.string().optional(),
|
|
4611
4229
|
/** Optional gezel that acts as the project's voorman (foreman). Surfaces in
|
|
4612
4230
|
* the project detail pane, flows into the system prompt when a session is
|
|
4613
4231
|
* scoped here. For solo projects (`mode === 'solo'`) this same field
|
|
4614
|
-
* holds the project's
|
|
4232
|
+
* holds the project's Builder — the data is unchanged, only the
|
|
4615
4233
|
* label flips. */
|
|
4616
|
-
voormanGezelId:
|
|
4234
|
+
voormanGezelId: z19.string().optional(),
|
|
4617
4235
|
/**
|
|
4618
4236
|
* Internal marker: ISO timestamp of the one-time automatic voorman
|
|
4619
4237
|
* assignment. The indexer ensures every project ends up with a voorman
|
|
@@ -4623,7 +4241,7 @@ var ProjectSchema = z21.object({
|
|
|
4623
4241
|
* silently re-populated on the next scan. Not user-editable; absent on
|
|
4624
4242
|
* projects last written before this field existed.
|
|
4625
4243
|
*/
|
|
4626
|
-
voormanAutoAssignedAt:
|
|
4244
|
+
voormanAutoAssignedAt: z19.string().optional(),
|
|
4627
4245
|
/**
|
|
4628
4246
|
* Roster — gezels that have been pulled into this project. Populated
|
|
4629
4247
|
* automatically the first time a gezel is set as voorman, opens a
|
|
@@ -4638,7 +4256,7 @@ var ProjectSchema = z21.object({
|
|
|
4638
4256
|
* (back-compat with every project written before this field
|
|
4639
4257
|
* existed).
|
|
4640
4258
|
*/
|
|
4641
|
-
gezelIds:
|
|
4259
|
+
gezelIds: z19.array(z19.string()).optional(),
|
|
4642
4260
|
/**
|
|
4643
4261
|
* Suggested-work keys the user has dismissed ("don't offer this again
|
|
4644
4262
|
* here"). Advisory UI state, same spirit as `gezelIds`: enabling a
|
|
@@ -4648,7 +4266,7 @@ var ProjectSchema = z21.object({
|
|
|
4648
4266
|
* `project-type:<typeId>:<scheduleKey>`). Deliberately not exposed
|
|
4649
4267
|
* through the model-facing `update_project` MCP tool.
|
|
4650
4268
|
*/
|
|
4651
|
-
suggestedWorkDismissed:
|
|
4269
|
+
suggestedWorkDismissed: z19.array(z19.string()).optional(),
|
|
4652
4270
|
/**
|
|
4653
4271
|
* Shared per-project configuration values ("project properties") that
|
|
4654
4272
|
* craftbook params and features draw from — e.g. `content.language`,
|
|
@@ -4657,26 +4275,26 @@ var ProjectSchema = z21.object({
|
|
|
4657
4275
|
* ids are allowed (the registry improves display, it doesn't gate).
|
|
4658
4276
|
* Values are plain strings; empty string is treated as unset.
|
|
4659
4277
|
*/
|
|
4660
|
-
properties:
|
|
4278
|
+
properties: z19.record(z19.string(), z19.string()).optional(),
|
|
4661
4279
|
/**
|
|
4662
4280
|
* Project shape. `crew` (the default) is the original behavior — the
|
|
4663
4281
|
* voorman recruits and coordinates a team of specialists. `solo` is a
|
|
4664
|
-
* "job" — a single
|
|
4282
|
+
* "job" — a single Builder (stored in
|
|
4665
4283
|
* `voormanGezelId`) handles the whole project themselves; team-
|
|
4666
4284
|
* management MCP tools are stripped from their session, and the
|
|
4667
4285
|
* Meester is instructed not to nominate other gezels. Missing → `crew`
|
|
4668
4286
|
* for back-compat with every project on disk before this field
|
|
4669
4287
|
* existed.
|
|
4670
4288
|
*/
|
|
4671
|
-
mode:
|
|
4289
|
+
mode: z19.enum(["crew", "solo"]).optional(),
|
|
4672
4290
|
/**
|
|
4673
4291
|
* Optional custom label for this project's lead gezel, overriding the
|
|
4674
|
-
* mode-based default ("Voorman" / "
|
|
4292
|
+
* mode-based default ("Voorman" / "Builder") everywhere the UI
|
|
4675
4293
|
* renders it. Set by a project type at adoption (e.g. checkers →
|
|
4676
4294
|
* "Opponent"); absent → the mode default. The data field stays
|
|
4677
4295
|
* `voormanGezelId` — only the label changes.
|
|
4678
4296
|
*/
|
|
4679
|
-
leadLabel:
|
|
4297
|
+
leadLabel: z19.string().optional(),
|
|
4680
4298
|
/**
|
|
4681
4299
|
* Lean-agent profile (set by a project type at adoption, e.g. checkers).
|
|
4682
4300
|
* When true, sessions here get a minimal tool surface (the type's script
|
|
@@ -4684,7 +4302,7 @@ var ProjectSchema = z21.object({
|
|
|
4684
4302
|
* scaffolding). Keeps small local models from being overwhelmed on a
|
|
4685
4303
|
* focused single-purpose task. Absent → the full agent profile.
|
|
4686
4304
|
*/
|
|
4687
|
-
leanProfile:
|
|
4305
|
+
leanProfile: z19.boolean().optional(),
|
|
4688
4306
|
/**
|
|
4689
4307
|
* Per-project workspace-indexing switch. Missing/true preserves the
|
|
4690
4308
|
* historical behavior: structural discovery plus the content-index refresh
|
|
@@ -4697,9 +4315,9 @@ var ProjectSchema = z21.object({
|
|
|
4697
4315
|
* document index. Project-type manifests may seed the value at adoption and
|
|
4698
4316
|
* the user can override it later in Project Settings.
|
|
4699
4317
|
*/
|
|
4700
|
-
indexingEnabled:
|
|
4318
|
+
indexingEnabled: z19.boolean().optional(),
|
|
4701
4319
|
github: ProjectGitHubSchema.optional(),
|
|
4702
|
-
connectors:
|
|
4320
|
+
connectors: z19.array(ProjectConnectorBindingSchema).optional(),
|
|
4703
4321
|
nudgeConfig: ProjectNudgeConfigSchema.optional(),
|
|
4704
4322
|
nudgeState: ProjectNudgeStateSchema.optional(),
|
|
4705
4323
|
/**
|
|
@@ -4722,7 +4340,7 @@ var ProjectSchema = z21.object({
|
|
|
4722
4340
|
* @deprecated Use `managedWorkspaceWritePolicy` and the centralized
|
|
4723
4341
|
* `projectManagedWorkspaceWritable` resolver.
|
|
4724
4342
|
*/
|
|
4725
|
-
allowGezelWrites:
|
|
4343
|
+
allowGezelWrites: z19.boolean().optional(),
|
|
4726
4344
|
/**
|
|
4727
4345
|
* Per-project Codex execution posture selected from the project status bar.
|
|
4728
4346
|
* It overrides per-gezel/install Codex defaults so the visible control is
|
|
@@ -4759,7 +4377,7 @@ var ProjectSchema = z21.object({
|
|
|
4759
4377
|
* weak local model having to take an explicit action. Reversible and
|
|
4760
4378
|
* non-destructive; chat and direct tool calls keep working.
|
|
4761
4379
|
*/
|
|
4762
|
-
status:
|
|
4380
|
+
status: z19.enum(["active", "readonly", "inactive", "stable"]).optional(),
|
|
4763
4381
|
/**
|
|
4764
4382
|
* Bury this project in the navigation without deleting it. Archived
|
|
4765
4383
|
* projects remain available from the dedicated section in the full
|
|
@@ -4769,13 +4387,13 @@ var ProjectSchema = z21.object({
|
|
|
4769
4387
|
* Missing/false means visible in the ordinary project UX, preserving
|
|
4770
4388
|
* compatibility with projects written before archiving existed.
|
|
4771
4389
|
*/
|
|
4772
|
-
archived:
|
|
4390
|
+
archived: z19.boolean().optional(),
|
|
4773
4391
|
/**
|
|
4774
4392
|
* Per-project override of the `run_nodejs_script` wall-clock
|
|
4775
4393
|
* timeout. Clamped between 30 seconds and 30 minutes. Missing →
|
|
4776
4394
|
* the service-side default (5 min) applies.
|
|
4777
4395
|
*/
|
|
4778
|
-
workspaceScriptTimeoutMs:
|
|
4396
|
+
workspaceScriptTimeoutMs: z19.number().int().min(3e4).max(30 * 6e4).optional(),
|
|
4779
4397
|
/**
|
|
4780
4398
|
* Named credentials this project is explicitly allowed to use.
|
|
4781
4399
|
* Credentials are stored once globally in `SecretStore`; a grant
|
|
@@ -4784,76 +4402,515 @@ var ProjectSchema = z21.object({
|
|
|
4784
4402
|
* Missing → no credentials granted. See `scripts/dispatcher.ts`
|
|
4785
4403
|
* and `secrets/registry.ts` for resolution.
|
|
4786
4404
|
*/
|
|
4787
|
-
grantedCredentials:
|
|
4405
|
+
grantedCredentials: z19.array(z19.string()).optional(),
|
|
4406
|
+
/**
|
|
4407
|
+
* Advanced exact-origin bindings for toolset credentials. Built-in provider
|
|
4408
|
+
* credentials are service-pinned and webhook credentials follow the
|
|
4409
|
+
* configured webhook URL, so entries for those names are ignored.
|
|
4410
|
+
*/
|
|
4411
|
+
credentialAllowedOrigins: z19.record(z19.string(), z19.array(HttpsOriginSchema)).optional(),
|
|
4412
|
+
/**
|
|
4413
|
+
* Explicit user override of the project's type (an id from the bundled
|
|
4414
|
+
* project-type taxonomy, see `project-types/taxonomy.ts`). When set, it
|
|
4415
|
+
* wins over `detectedProjectType` for craftbook suggestions. Cleared
|
|
4416
|
+
* (unset) → fall back to auto-detection. Missing on every project written
|
|
4417
|
+
* before this field existed.
|
|
4418
|
+
*/
|
|
4419
|
+
projectTypeId: z19.string().optional(),
|
|
4420
|
+
/**
|
|
4421
|
+
* Auto-detected project type, recomputed on each content-index scan from
|
|
4422
|
+
* the workspace file mix + about/mission text. Not user-editable — the
|
|
4423
|
+
* user expresses an override via `projectTypeId`. Absent until the first
|
|
4424
|
+
* scan classifies the project (or when nothing scores above the floor).
|
|
4425
|
+
*/
|
|
4426
|
+
detectedProjectType: z19.object({
|
|
4427
|
+
id: z19.string(),
|
|
4428
|
+
score: z19.number(),
|
|
4429
|
+
scannedAt: z19.string()
|
|
4430
|
+
}).optional(),
|
|
4431
|
+
/**
|
|
4432
|
+
* Provenance of an applied custom project type (see docs/project-types.md).
|
|
4433
|
+
* Distinct from `projectTypeId`, which is the taxonomy id used for craftbook
|
|
4434
|
+
* suggestion: a custom type stamps this on adoption and, when it `extends` a
|
|
4435
|
+
* taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
|
|
4436
|
+
* inherits. Absent on projects created without a custom type.
|
|
4437
|
+
*/
|
|
4438
|
+
projectType: ProjectTypeProvenanceSchema.optional(),
|
|
4439
|
+
/**
|
|
4440
|
+
* Filesystem ownership boundary. Missing/`user` is ordinary per-account
|
|
4441
|
+
* state. `machine-shared` is a grandfathered project mounted from the
|
|
4442
|
+
* installer-managed shared root and operated on by this user's daemon.
|
|
4443
|
+
* The engine broker never opens the project or receives its paths.
|
|
4444
|
+
*/
|
|
4445
|
+
storageScope: z19.enum(["user", "machine-shared"]).optional(),
|
|
4446
|
+
createdAt: z19.string(),
|
|
4447
|
+
updatedAt: z19.string()
|
|
4448
|
+
});
|
|
4449
|
+
function resolveProjectTypeId(project) {
|
|
4450
|
+
return project.projectTypeId ?? project.detectedProjectType?.id;
|
|
4451
|
+
}
|
|
4452
|
+
function projectAllowsAmbientWork(project) {
|
|
4453
|
+
const status = project.status ?? "active";
|
|
4454
|
+
return status === "active";
|
|
4455
|
+
}
|
|
4456
|
+
var InstalledPackageSchema = z19.object({
|
|
4457
|
+
name: z19.string(),
|
|
4458
|
+
version: z19.string()
|
|
4459
|
+
});
|
|
4460
|
+
var ProjectDetailSchema = ProjectSchema.extend({
|
|
4461
|
+
packages: z19.array(InstalledPackageSchema),
|
|
4462
|
+
/** Contents of `documents/about.md` inside the project, if present. */
|
|
4463
|
+
about: z19.string().optional(),
|
|
4464
|
+
/** Contents of `documents/missionObjectives.md`, if present. */
|
|
4465
|
+
missionObjectives: z19.string().optional()
|
|
4466
|
+
});
|
|
4467
|
+
var ProjectFileEntrySchema = z19.object({
|
|
4468
|
+
name: z19.string(),
|
|
4469
|
+
path: z19.string(),
|
|
4470
|
+
isDirectory: z19.boolean(),
|
|
4471
|
+
/** File mtime (ms epoch). Populated only when the caller opts into stats. */
|
|
4472
|
+
mtimeMs: z19.number().optional()
|
|
4473
|
+
});
|
|
4474
|
+
var ProjectGithubSchema = ProjectGitHubSchema;
|
|
4475
|
+
|
|
4476
|
+
// src/schemas/craftbook-test.ts
|
|
4477
|
+
var CRAFTBOOK_TEST_SCHEMA_VERSION = 1;
|
|
4478
|
+
var CRAFTBOOK_TEST_FILENAME = "test.json";
|
|
4479
|
+
var PrometheusAlertsCheckSchema = z20.object({
|
|
4480
|
+
kind: z20.literal("prometheusAlerts"),
|
|
4481
|
+
file: z20.string().min(1),
|
|
4482
|
+
minRules: z20.number().int().positive().optional(),
|
|
4483
|
+
maxPageAlerts: z20.number().int().nonnegative().optional(),
|
|
4484
|
+
allowedSeverities: z20.array(z20.string().min(1)).optional(),
|
|
4485
|
+
requiredServices: z20.array(z20.string().min(1)).optional(),
|
|
4486
|
+
requiredRunbookUrls: z20.array(z20.string().min(1)).optional()
|
|
4487
|
+
}).strict();
|
|
4488
|
+
var NodeScriptPassesCheckSchema = z20.object({
|
|
4489
|
+
kind: z20.literal("nodeScriptPasses"),
|
|
4490
|
+
script: z20.string().min(1),
|
|
4491
|
+
timeoutMs: z20.number().int().positive().optional(),
|
|
4492
|
+
requiredOutput: z20.array(
|
|
4493
|
+
z20.object({
|
|
4494
|
+
pattern: z20.string().min(1),
|
|
4495
|
+
flags: z20.string().optional(),
|
|
4496
|
+
label: z20.string().optional()
|
|
4497
|
+
}).strict()
|
|
4498
|
+
).optional()
|
|
4499
|
+
}).strict();
|
|
4500
|
+
var BinaryDocumentCheckSchema = z20.object({
|
|
4501
|
+
kind: z20.literal("binaryDocument"),
|
|
4502
|
+
file: z20.string().min(1),
|
|
4503
|
+
/** Look in the artifacts drawer instead of the workspace. */
|
|
4504
|
+
artifact: z20.boolean().optional(),
|
|
4505
|
+
/** Floor on the container's byte length; defaults to 1000. */
|
|
4506
|
+
minBytes: z20.number().int().positive().optional()
|
|
4507
|
+
}).strict();
|
|
4508
|
+
var CraftbookTestCheckSchema = z20.union([
|
|
4509
|
+
GateCheckSchema,
|
|
4510
|
+
PrometheusAlertsCheckSchema,
|
|
4511
|
+
NodeScriptPassesCheckSchema,
|
|
4512
|
+
BinaryDocumentCheckSchema
|
|
4513
|
+
]);
|
|
4514
|
+
var MockServiceIdSchema = z20.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, "mock service ids are lowercase kebab-case");
|
|
4515
|
+
var MockToolsetIdSchema = z20.string().min(1).regex(
|
|
4516
|
+
/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,
|
|
4517
|
+
"mock toolset ids are lowercase catalog ids or scoped npm-style ids"
|
|
4518
|
+
);
|
|
4519
|
+
var MockHttpRouteSchema = z20.object({
|
|
4520
|
+
method: z20.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
4521
|
+
/** Exact path, `:param` segments, or a trailing `*` wildcard. */
|
|
4522
|
+
path: z20.string().min(1),
|
|
4523
|
+
status: z20.number().int().min(100).max(599).default(200),
|
|
4524
|
+
headers: z20.record(z20.string(), z20.string()).optional(),
|
|
4525
|
+
/** String bodies are served verbatim; anything else is JSON-encoded. */
|
|
4526
|
+
body: z20.unknown(),
|
|
4527
|
+
latencyMs: z20.number().int().nonnegative().optional()
|
|
4528
|
+
}).strict();
|
|
4529
|
+
var MockServiceSchema = z20.discriminatedUnion("kind", [
|
|
4530
|
+
z20.object({
|
|
4531
|
+
kind: z20.literal("http"),
|
|
4532
|
+
id: MockServiceIdSchema,
|
|
4533
|
+
description: z20.string().min(1),
|
|
4534
|
+
/**
|
|
4535
|
+
* v1 mock HTTP is reachable ONLY through the `http.authed`
|
|
4536
|
+
* credential rail (anonymous script/browser HTTP hard-rejects
|
|
4537
|
+
* loopback), so a credential is required. The harness seeds it and
|
|
4538
|
+
* grants the mock's exact origin on the trial project.
|
|
4539
|
+
*/
|
|
4540
|
+
credential: z20.object({
|
|
4541
|
+
name: z20.string().regex(/^mock\.[a-z0-9][a-z0-9.-]*$/, "mock credentials are named mock.<service-id>"),
|
|
4542
|
+
authScheme: z20.enum(["bearer", "basic"]).optional()
|
|
4543
|
+
}).strict(),
|
|
4544
|
+
routes: z20.array(MockHttpRouteSchema).min(1)
|
|
4545
|
+
}).strict(),
|
|
4546
|
+
z20.object({
|
|
4547
|
+
kind: z20.literal("webhook"),
|
|
4548
|
+
id: MockServiceIdSchema,
|
|
4549
|
+
description: z20.string().min(1),
|
|
4550
|
+
/** Receiver path; defaults to `/webhook` when omitted. */
|
|
4551
|
+
path: z20.string().min(1).optional()
|
|
4552
|
+
}).strict(),
|
|
4553
|
+
z20.object({
|
|
4554
|
+
kind: z20.literal("cli"),
|
|
4555
|
+
id: MockServiceIdSchema,
|
|
4556
|
+
description: z20.string().min(1),
|
|
4557
|
+
/** Fake-CLI shim seeded as a workspace file (today's dry-run pattern). */
|
|
4558
|
+
shim: z20.object({ path: z20.string().min(1), content: z20.string() }).strict()
|
|
4559
|
+
}).strict(),
|
|
4560
|
+
z20.object({
|
|
4561
|
+
kind: z20.literal("mcp"),
|
|
4562
|
+
id: MockServiceIdSchema,
|
|
4563
|
+
description: z20.string().min(1),
|
|
4564
|
+
/** Override the local-catalog id when the mock replaces a real dependency. */
|
|
4565
|
+
toolsetId: MockToolsetIdSchema.optional(),
|
|
4566
|
+
/**
|
|
4567
|
+
* Served live by the eval mock rail: each declared tool becomes a
|
|
4568
|
+
* real tool on a per-trial Streamable-HTTP MCP endpoint, installed
|
|
4569
|
+
* into the trial via a local-catalog `mock-mcp-<id>` toolset.
|
|
4570
|
+
* `resultTemplate` is JSON-encoded as the tool result text
|
|
4571
|
+
* (default `{"ok":true}` when absent).
|
|
4572
|
+
*/
|
|
4573
|
+
tools: z20.array(
|
|
4574
|
+
z20.object({
|
|
4575
|
+
name: z20.string().min(1),
|
|
4576
|
+
description: z20.string().min(1),
|
|
4577
|
+
resultTemplate: z20.unknown().optional(),
|
|
4578
|
+
/** Deterministic stateful responses, consumed in call order; the last repeats. */
|
|
4579
|
+
resultSequence: z20.array(z20.unknown()).min(1).optional(),
|
|
4580
|
+
/**
|
|
4581
|
+
* Deterministic eval-only file materialization after this
|
|
4582
|
+
* tool call. The fixture must match the container the
|
|
4583
|
+
* deliverable's extension claims — a `minimal-pptx` written
|
|
4584
|
+
* to a `.docx` path now fails the `binaryDocument` check on
|
|
4585
|
+
* content type rather than sliding through a byte floor.
|
|
4586
|
+
*/
|
|
4587
|
+
writeFixture: z20.object({
|
|
4588
|
+
surface: z20.enum(["workspace", "artifact"]),
|
|
4589
|
+
pathArgument: z20.string().min(1),
|
|
4590
|
+
fixture: z20.enum(["minimal-pptx", "minimal-docx", "minimal-pdf", "minimal-png"])
|
|
4591
|
+
}).strict().optional()
|
|
4592
|
+
}).strict()
|
|
4593
|
+
).min(1)
|
|
4594
|
+
}).strict()
|
|
4595
|
+
]);
|
|
4596
|
+
var CraftbookTestFixtureFileSchema = z20.object({
|
|
4597
|
+
path: z20.string().min(1),
|
|
4598
|
+
content: z20.string(),
|
|
4599
|
+
/** Defaults to `workspace`; `harness` never enters the model-visible project. */
|
|
4600
|
+
surface: z20.enum(["workspace", "artifact", "harness"]).optional(),
|
|
4788
4601
|
/**
|
|
4789
|
-
*
|
|
4790
|
-
*
|
|
4791
|
-
* configured webhook URL, so entries for those names are ignored.
|
|
4602
|
+
* Whether the fixture is presented to the model as source material.
|
|
4603
|
+
* Defaults to true; false still seeds the file for browsers and graders.
|
|
4792
4604
|
*/
|
|
4793
|
-
|
|
4605
|
+
modelInput: z20.boolean().optional()
|
|
4606
|
+
}).strict();
|
|
4607
|
+
var CraftbookTestWorkerSchema = z20.object({
|
|
4608
|
+
name: z20.string().min(1),
|
|
4609
|
+
role: z20.string().min(1),
|
|
4610
|
+
description: z20.string().optional(),
|
|
4611
|
+
about: z20.string().optional()
|
|
4612
|
+
}).strict();
|
|
4613
|
+
var CraftbookTestSetupSchema = z20.object({
|
|
4614
|
+
projectName: z20.string().min(1),
|
|
4615
|
+
about: z20.string().optional(),
|
|
4616
|
+
missionObjectives: z20.string().optional(),
|
|
4617
|
+
/** Reproduce project write posture before the craftbook task starts. */
|
|
4618
|
+
managedWorkspaceWritePolicy: ProjectManagedWorkspaceWritePolicySchema.optional(),
|
|
4619
|
+
files: z20.array(CraftbookTestFixtureFileSchema).default([]),
|
|
4620
|
+
/** Exact values supplied to the catalog craftbook's `paramSchema`. */
|
|
4621
|
+
craftbookParams: z20.record(z20.string(), z20.string()).optional(),
|
|
4794
4622
|
/**
|
|
4795
|
-
*
|
|
4796
|
-
*
|
|
4797
|
-
*
|
|
4798
|
-
* (unset) → fall back to auto-detection. Missing on every project written
|
|
4799
|
-
* before this field existed.
|
|
4623
|
+
* Direct execution target. When present the harness seeds this gezel
|
|
4624
|
+
* and sends the kickoff straight to it (measuring whether the book
|
|
4625
|
+
* guides the work); absent → the Meester routes.
|
|
4800
4626
|
*/
|
|
4801
|
-
|
|
4627
|
+
worker: CraftbookTestWorkerSchema.optional()
|
|
4628
|
+
}).strict();
|
|
4629
|
+
var CraftbookTestDeliverableSchema = z20.object({
|
|
4630
|
+
path: z20.string().min(1),
|
|
4631
|
+
kind: DeliverableKindSchema,
|
|
4632
|
+
/** Grade the path in the project's artifacts drawer, not its workspace. */
|
|
4633
|
+
artifact: z20.boolean().optional(),
|
|
4634
|
+
minBytes: z20.number().int().positive().optional(),
|
|
4635
|
+
checks: z20.array(CraftbookTestCheckSchema).optional()
|
|
4636
|
+
}).strict();
|
|
4637
|
+
var CraftbookTestMockExpectationSchema = z20.object({
|
|
4638
|
+
/** Mock service id from `mocks[]`. */
|
|
4639
|
+
service: MockServiceIdSchema,
|
|
4640
|
+
minRequests: z20.number().int().positive().optional(),
|
|
4641
|
+
/** Regex sources matched against logged request paths. */
|
|
4642
|
+
requiredPaths: z20.array(z20.string().min(1)).optional(),
|
|
4643
|
+
forbiddenPaths: z20.array(z20.string().min(1)).optional(),
|
|
4802
4644
|
/**
|
|
4803
|
-
*
|
|
4804
|
-
*
|
|
4805
|
-
*
|
|
4806
|
-
*
|
|
4645
|
+
* Exact MCP tool names that must each have been called at least once
|
|
4646
|
+
* on this service (`kind: 'mcp'` only). Exact names, not regexes —
|
|
4647
|
+
* the tool roster is fully declared in the same file, so a pattern
|
|
4648
|
+
* buys nothing and invites drift. Cross-checked against the mock's
|
|
4649
|
+
* declared `tools[]` at parse time.
|
|
4807
4650
|
*/
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4651
|
+
requiredTools: z20.array(z20.string().min(1)).optional(),
|
|
4652
|
+
/** Per-MCP-tool call budgets for repeated journeys or retries. */
|
|
4653
|
+
toolCalls: z20.record(
|
|
4654
|
+
z20.string().min(1),
|
|
4655
|
+
z20.object({
|
|
4656
|
+
minCalls: z20.number().int().nonnegative().default(1),
|
|
4657
|
+
maxCalls: z20.number().int().nonnegative().optional()
|
|
4658
|
+
}).strict().refine(
|
|
4659
|
+
(value) => value.maxCalls === void 0 || value.minCalls <= value.maxCalls,
|
|
4660
|
+
"minCalls must be less than or equal to maxCalls"
|
|
4661
|
+
)
|
|
4662
|
+
).optional()
|
|
4663
|
+
}).strict();
|
|
4664
|
+
var CraftbookTestHistoryExpectationSchema = z20.object({
|
|
4665
|
+
kind: HistoryEventKindSchema,
|
|
4666
|
+
minEntries: z20.number().int().nonnegative().default(1),
|
|
4667
|
+
maxEntries: z20.number().int().nonnegative().optional(),
|
|
4668
|
+
summaryPattern: z20.string().min(1).optional(),
|
|
4669
|
+
flags: z20.string().optional(),
|
|
4670
|
+
details: z20.record(z20.string(), z20.union([z20.string(), z20.number(), z20.boolean(), z20.null()])).optional()
|
|
4671
|
+
}).strict();
|
|
4672
|
+
var CraftbookTestSuccessSchema = z20.object({
|
|
4673
|
+
summary: z20.string().min(1),
|
|
4674
|
+
deliverables: z20.array(CraftbookTestDeliverableSchema).optional(),
|
|
4675
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4676
|
+
taskNotes: z20.object({
|
|
4677
|
+
minBytes: z20.number().int().positive().optional(),
|
|
4678
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4679
|
+
requireCraftbookTask: z20.boolean().optional()
|
|
4680
|
+
}).strict().optional(),
|
|
4681
|
+
taskGraph: z20.object({
|
|
4682
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4683
|
+
requireCraftbookTask: z20.boolean().optional(),
|
|
4684
|
+
/** Require the matching task to reach a terminal step (or complete). */
|
|
4685
|
+
requireTerminalStep: z20.boolean().optional(),
|
|
4686
|
+
requireDraftRef: z20.boolean().optional(),
|
|
4687
|
+
draft: z20.object({
|
|
4688
|
+
status: z20.enum(["draft", "paused", "active", "complete", "canceled"]).optional(),
|
|
4689
|
+
minDescriptionBytes: z20.number().int().positive().optional(),
|
|
4690
|
+
minOutcomes: z20.number().int().positive().optional(),
|
|
4691
|
+
minSteps: z20.number().int().positive().optional(),
|
|
4692
|
+
requireTerminalVerification: z20.boolean().optional(),
|
|
4693
|
+
requireGatedBuildSteps: z20.boolean().optional()
|
|
4694
|
+
}).strict().optional()
|
|
4695
|
+
}).strict().optional(),
|
|
4696
|
+
/** Assertions evaluated against the live mock server's request log. */
|
|
4697
|
+
mocks: z20.array(CraftbookTestMockExpectationSchema).optional(),
|
|
4698
|
+
/** Assertions evaluated against the project's append-only History log. */
|
|
4699
|
+
history: z20.array(CraftbookTestHistoryExpectationSchema).optional(),
|
|
4700
|
+
/** Workspace fixtures whose final content must equal the seeded bytes exactly. */
|
|
4701
|
+
unchangedFixtures: z20.array(z20.string().min(1)).optional()
|
|
4702
|
+
}).strict();
|
|
4703
|
+
var CraftbookTestRubricSchema = z20.object({
|
|
4704
|
+
artifact: z20.object({
|
|
4705
|
+
/** Workspace or artifact path the judge reads (adapter derives the basename). */
|
|
4706
|
+
path: z20.string().min(1),
|
|
4707
|
+
kind: z20.enum(["html", "markdown", "yaml", "typescript", "json", "text"])
|
|
4708
|
+
}).strict(),
|
|
4709
|
+
axes: z20.array(z20.object({ name: z20.string().min(1), description: z20.string().min(1) }).strict()).min(1),
|
|
4710
|
+
contextNote: z20.string().optional()
|
|
4711
|
+
}).strict();
|
|
4712
|
+
var CraftbookTestSpecSchema = z20.object({
|
|
4713
|
+
schemaVersion: z20.literal(CRAFTBOOK_TEST_SCHEMA_VERSION),
|
|
4714
|
+
title: z20.string().min(1),
|
|
4715
|
+
objective: z20.string().min(1),
|
|
4813
4716
|
/**
|
|
4814
|
-
*
|
|
4815
|
-
*
|
|
4816
|
-
*
|
|
4817
|
-
* taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
|
|
4818
|
-
* inherits. Absent on projects created without a custom type.
|
|
4717
|
+
* Task-class taxonomy tags (e.g. `html-game`, `corpus`, `external`).
|
|
4718
|
+
* The single declared source for harness selection and batch
|
|
4719
|
+
* planning — replaces the old regex classifiers.
|
|
4819
4720
|
*/
|
|
4820
|
-
|
|
4721
|
+
tags: z20.array(z20.string().min(1)).default([]),
|
|
4722
|
+
/** Kickoff chat message the harness sends. Required — every book runs. */
|
|
4723
|
+
prompt: z20.string().min(1),
|
|
4724
|
+
setup: CraftbookTestSetupSchema,
|
|
4725
|
+
mocks: z20.array(MockServiceSchema).default([]),
|
|
4726
|
+
success: CraftbookTestSuccessSchema,
|
|
4727
|
+
rubric: CraftbookTestRubricSchema,
|
|
4728
|
+
qualityFocus: z20.array(z20.string().min(1)).default([]),
|
|
4821
4729
|
/**
|
|
4822
|
-
*
|
|
4823
|
-
*
|
|
4824
|
-
* installer-managed shared root and operated on by this user's daemon.
|
|
4825
|
-
* The engine broker never opens the project or receives its paths.
|
|
4730
|
+
* Sanctioned escape hatch for experiments — carried opaquely, never
|
|
4731
|
+
* interpreted by CI. Promote a field out of here before relying on it.
|
|
4826
4732
|
*/
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4733
|
+
extensions: z20.record(z20.string(), z20.unknown()).optional()
|
|
4734
|
+
}).strict().superRefine((spec, ctx) => {
|
|
4735
|
+
const mockIds = new Set(spec.mocks.map((m) => m.id));
|
|
4736
|
+
const mockById = new Map(spec.mocks.map((m) => [m.id, m]));
|
|
4737
|
+
for (const [i, expectation] of (spec.success.mocks ?? []).entries()) {
|
|
4738
|
+
if (!mockIds.has(expectation.service)) {
|
|
4739
|
+
ctx.addIssue({
|
|
4740
|
+
code: z20.ZodIssueCode.custom,
|
|
4741
|
+
path: ["success", "mocks", i, "service"],
|
|
4742
|
+
message: `success.mocks[${i}] references unknown mock service "${expectation.service}"`
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
if (expectation.requiredTools && expectation.requiredTools.length > 0) {
|
|
4746
|
+
const target = mockById.get(expectation.service);
|
|
4747
|
+
if (target && target.kind !== "mcp") {
|
|
4748
|
+
ctx.addIssue({
|
|
4749
|
+
code: z20.ZodIssueCode.custom,
|
|
4750
|
+
path: ["success", "mocks", i, "requiredTools"],
|
|
4751
|
+
message: `success.mocks[${i}].requiredTools requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4752
|
+
});
|
|
4753
|
+
} else if (target?.kind === "mcp") {
|
|
4754
|
+
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4755
|
+
for (const name of expectation.requiredTools) {
|
|
4756
|
+
if (!declared.has(name)) {
|
|
4757
|
+
ctx.addIssue({
|
|
4758
|
+
code: z20.ZodIssueCode.custom,
|
|
4759
|
+
path: ["success", "mocks", i, "requiredTools"],
|
|
4760
|
+
message: `success.mocks[${i}].requiredTools names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4761
|
+
});
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
if (expectation.toolCalls && Object.keys(expectation.toolCalls).length > 0) {
|
|
4767
|
+
const target = mockById.get(expectation.service);
|
|
4768
|
+
if (target && target.kind !== "mcp") {
|
|
4769
|
+
ctx.addIssue({
|
|
4770
|
+
code: z20.ZodIssueCode.custom,
|
|
4771
|
+
path: ["success", "mocks", i, "toolCalls"],
|
|
4772
|
+
message: `success.mocks[${i}].toolCalls requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4773
|
+
});
|
|
4774
|
+
} else if (target?.kind === "mcp") {
|
|
4775
|
+
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4776
|
+
for (const name of Object.keys(expectation.toolCalls)) {
|
|
4777
|
+
if (!declared.has(name)) {
|
|
4778
|
+
ctx.addIssue({
|
|
4779
|
+
code: z20.ZodIssueCode.custom,
|
|
4780
|
+
path: ["success", "mocks", i, "toolCalls", name],
|
|
4781
|
+
message: `success.mocks[${i}].toolCalls names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4782
|
+
});
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
for (const [i, mock] of spec.mocks.entries()) {
|
|
4789
|
+
if (mock.kind === "http" && mock.credential.name !== `mock.${mock.id}`) {
|
|
4790
|
+
ctx.addIssue({
|
|
4791
|
+
code: z20.ZodIssueCode.custom,
|
|
4792
|
+
path: ["mocks", i, "credential", "name"],
|
|
4793
|
+
message: `http mock "${mock.id}" must use credential name "mock.${mock.id}"`
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
const workspaceFixtures = new Set(
|
|
4798
|
+
spec.setup.files.filter((file) => file.surface === void 0 || file.surface === "workspace").map((file) => file.path)
|
|
4799
|
+
);
|
|
4800
|
+
for (const [i, path] of (spec.success.unchangedFixtures ?? []).entries()) {
|
|
4801
|
+
if (!workspaceFixtures.has(path)) {
|
|
4802
|
+
ctx.addIssue({
|
|
4803
|
+
code: z20.ZodIssueCode.custom,
|
|
4804
|
+
path: ["success", "unchangedFixtures", i],
|
|
4805
|
+
message: `unchanged fixture "${path}" is not a seeded workspace file`
|
|
4806
|
+
});
|
|
4807
|
+
}
|
|
4808
|
+
}
|
|
4809
|
+
for (const [i, expectation] of (spec.success.history ?? []).entries()) {
|
|
4810
|
+
if (expectation.maxEntries !== void 0 && expectation.minEntries > expectation.maxEntries) {
|
|
4811
|
+
ctx.addIssue({
|
|
4812
|
+
code: z20.ZodIssueCode.custom,
|
|
4813
|
+
path: ["success", "history", i],
|
|
4814
|
+
message: "minEntries must be less than or equal to maxEntries"
|
|
4815
|
+
});
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4830
4818
|
});
|
|
4831
|
-
function
|
|
4832
|
-
|
|
4819
|
+
function parseCraftbookTestSpec(raw, opts) {
|
|
4820
|
+
const mode = opts?.mode ?? "strict";
|
|
4821
|
+
const candidate = mode === "tolerant" ? deepStripUnknown(raw) : raw;
|
|
4822
|
+
const parsed = CraftbookTestSpecSchema.safeParse(candidate);
|
|
4823
|
+
if (parsed.success) return { ok: true, spec: parsed.data };
|
|
4824
|
+
return {
|
|
4825
|
+
ok: false,
|
|
4826
|
+
errors: parsed.error.issues.map((issue) => {
|
|
4827
|
+
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
|
|
4828
|
+
return `${path}: ${issue.message}`;
|
|
4829
|
+
})
|
|
4830
|
+
};
|
|
4833
4831
|
}
|
|
4834
|
-
function
|
|
4835
|
-
|
|
4836
|
-
|
|
4832
|
+
function deepStripUnknown(raw) {
|
|
4833
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
4834
|
+
const record = structuredClone(raw);
|
|
4835
|
+
const version = record.schemaVersion;
|
|
4836
|
+
if (typeof version === "number" && Number.isInteger(version) && version > CRAFTBOOK_TEST_SCHEMA_VERSION) {
|
|
4837
|
+
record.schemaVersion = CRAFTBOOK_TEST_SCHEMA_VERSION;
|
|
4838
|
+
}
|
|
4839
|
+
for (let pass = 0; pass < 16; pass++) {
|
|
4840
|
+
const attempt = CraftbookTestSpecSchema.safeParse(record);
|
|
4841
|
+
if (attempt.success) return record;
|
|
4842
|
+
const unknownKeyIssues = attempt.error.issues.filter(
|
|
4843
|
+
(issue) => issue.code === z20.ZodIssueCode.unrecognized_keys
|
|
4844
|
+
);
|
|
4845
|
+
if (unknownKeyIssues.length === 0) return record;
|
|
4846
|
+
for (const issue of unknownKeyIssues) {
|
|
4847
|
+
const target = resolvePath(record, issue.path);
|
|
4848
|
+
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
4849
|
+
for (const key of issue.keys) delete target[key];
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
return record;
|
|
4854
|
+
}
|
|
4855
|
+
function resolvePath(root, path) {
|
|
4856
|
+
let node = root;
|
|
4857
|
+
for (const segment of path) {
|
|
4858
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
4859
|
+
node = node[segment];
|
|
4860
|
+
}
|
|
4861
|
+
return node;
|
|
4837
4862
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4863
|
+
|
|
4864
|
+
// src/schemas/codex-setup.ts
|
|
4865
|
+
import { z as z21 } from "zod";
|
|
4866
|
+
var CodexSetupModelOptionSchema = z21.object({
|
|
4867
|
+
id: z21.string().min(1),
|
|
4868
|
+
label: z21.string().min(1),
|
|
4869
|
+
description: z21.string().optional(),
|
|
4870
|
+
kind: z21.enum(["gezel", "model"]).default("model"),
|
|
4871
|
+
provider: z21.string().min(1),
|
|
4872
|
+
/** Stable gezel id for persona-backed entries. Absent on raw-model entries. */
|
|
4873
|
+
gezelId: z21.string().min(1).optional(),
|
|
4874
|
+
role: z21.string().min(1).optional(),
|
|
4875
|
+
/** Human-readable name of the effective inference model behind a gezel. */
|
|
4876
|
+
modelLabel: z21.string().min(1).optional(),
|
|
4877
|
+
contextWindow: z21.number().int().positive().optional(),
|
|
4878
|
+
supportsReasoning: z21.boolean().optional(),
|
|
4879
|
+
supportsTools: z21.boolean().optional()
|
|
4841
4880
|
});
|
|
4842
|
-
var
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4881
|
+
var CodexSetupStateSchema = z21.enum([
|
|
4882
|
+
"not-configured",
|
|
4883
|
+
"configured",
|
|
4884
|
+
"update-needed",
|
|
4885
|
+
"conflict",
|
|
4886
|
+
"unavailable"
|
|
4887
|
+
]);
|
|
4888
|
+
var CodexSetupStatusResponseSchema = z21.object({
|
|
4889
|
+
state: CodexSetupStateSchema,
|
|
4890
|
+
models: z21.array(CodexSetupModelOptionSchema),
|
|
4891
|
+
configuredModel: z21.string().optional(),
|
|
4892
|
+
recommendedModel: z21.string().optional(),
|
|
4893
|
+
reasons: z21.array(z21.string()),
|
|
4894
|
+
message: z21.string().optional(),
|
|
4895
|
+
codexInstalled: z21.boolean(),
|
|
4896
|
+
codexVersion: z21.string().optional(),
|
|
4897
|
+
codexPath: z21.string().optional(),
|
|
4898
|
+
endpointsEnabled: z21.boolean(),
|
|
4899
|
+
profileName: z21.string().min(1),
|
|
4900
|
+
profilePath: z21.string().min(1),
|
|
4901
|
+
launchCommand: z21.string().min(1),
|
|
4902
|
+
bridge: z21.object({
|
|
4903
|
+
baseUrl: z21.string().url(),
|
|
4904
|
+
listening: z21.boolean(),
|
|
4905
|
+
port: z21.number().int().nonnegative()
|
|
4906
|
+
}),
|
|
4907
|
+
canConfigure: z21.boolean(),
|
|
4908
|
+
/** Whether Gezel-owned credential/state material exists and can be safely removed. */
|
|
4909
|
+
canRemove: z21.boolean()
|
|
4848
4910
|
});
|
|
4849
|
-
var
|
|
4850
|
-
|
|
4851
|
-
path: z21.string(),
|
|
4852
|
-
isDirectory: z21.boolean(),
|
|
4853
|
-
/** File mtime (ms epoch). Populated only when the caller opts into stats. */
|
|
4854
|
-
mtimeMs: z21.number().optional()
|
|
4911
|
+
var ConfigureCodexRequestSchema = z21.object({
|
|
4912
|
+
model: z21.string().min(1)
|
|
4855
4913
|
});
|
|
4856
|
-
var ProjectGithubSchema = ProjectGitHubSchema;
|
|
4857
4914
|
|
|
4858
4915
|
// src/schemas/project-local.ts
|
|
4859
4916
|
import { z as z22 } from "zod";
|
|
@@ -5169,6 +5226,15 @@ var TaskSchema = z23.object({
|
|
|
5169
5226
|
issueRef: z23.string().regex(/^BW-[1-9]\d*$/),
|
|
5170
5227
|
path: z23.string().min(1)
|
|
5171
5228
|
}),
|
|
5229
|
+
z23.object({
|
|
5230
|
+
/**
|
|
5231
|
+
* An invoke_craftbook call keyed to one persisted root chat turn.
|
|
5232
|
+
* The HTTP task boundary uses this opaque digest to return the first
|
|
5233
|
+
* still-active task when a provider continuation repeats the call.
|
|
5234
|
+
*/
|
|
5235
|
+
kind: z23.literal("craftbook-invocation"),
|
|
5236
|
+
key: z23.string().regex(/^craftbook-root-v1:[a-f0-9]{64}$/)
|
|
5237
|
+
}),
|
|
5172
5238
|
z23.object({
|
|
5173
5239
|
/**
|
|
5174
5240
|
* A host materialized from a gezel template's `suggestedCraftbooks`
|
|
@@ -5198,6 +5264,13 @@ var TaskSchema = z23.object({
|
|
|
5198
5264
|
params: z23.record(z23.string(), z23.unknown()).optional(),
|
|
5199
5265
|
at: z23.string()
|
|
5200
5266
|
}).optional(),
|
|
5267
|
+
/**
|
|
5268
|
+
* Naming presentation inherited from the session that launched this
|
|
5269
|
+
* workflow. Task handoffs create fresh sessions (and may be rehydrated
|
|
5270
|
+
* after restart), so they cannot rely on the launcher's session record
|
|
5271
|
+
* still being available when the next step starts.
|
|
5272
|
+
*/
|
|
5273
|
+
roleBasedNameOnlyMode: z23.boolean().optional(),
|
|
5201
5274
|
createdAt: z23.string(),
|
|
5202
5275
|
updatedAt: z23.string(),
|
|
5203
5276
|
createdBy: TaskAssigneeSchema
|
|
@@ -5252,6 +5325,8 @@ var CreateTaskRequestSchema = z23.object({
|
|
|
5252
5325
|
}).optional(),
|
|
5253
5326
|
fanout: NewTaskFanoutSchema.optional(),
|
|
5254
5327
|
createdBy: TaskAssigneeSchema.optional(),
|
|
5328
|
+
/** Preserve the launcher's naming presentation across task handoffs. */
|
|
5329
|
+
roleBasedNameOnlyMode: z23.boolean().optional(),
|
|
5255
5330
|
/**
|
|
5256
5331
|
* Enqueue the entry-step handoff immediately after create — the
|
|
5257
5332
|
* single-channel kickoff (there is no "tell a gezel about work"
|
|
@@ -5261,7 +5336,13 @@ var CreateTaskRequestSchema = z23.object({
|
|
|
5261
5336
|
* on cron/fanout hosts (their children dispatch via their own
|
|
5262
5337
|
* activation hooks — flag-dispatching the host would double-engage).
|
|
5263
5338
|
*/
|
|
5264
|
-
dispatchEntry: z23.boolean().optional()
|
|
5339
|
+
dispatchEntry: z23.boolean().optional(),
|
|
5340
|
+
/**
|
|
5341
|
+
* Internal invoke_craftbook idempotency digest. The task route converts
|
|
5342
|
+
* it into service-owned Task.origin provenance; ordinary create_task
|
|
5343
|
+
* callers omit it.
|
|
5344
|
+
*/
|
|
5345
|
+
craftbookInvocationKey: z23.string().regex(/^craftbook-root-v1:[a-f0-9]{64}$/).optional()
|
|
5265
5346
|
}).refine((v) => !!v.craftbookId !== !!(v.steps && v.steps.length > 0), {
|
|
5266
5347
|
message: "exactly one of craftbookId or steps must be provided for the main craftbook",
|
|
5267
5348
|
path: ["craftbookId"]
|
|
@@ -5365,6 +5446,8 @@ var UpdateTaskStepRequestSchema = z23.object({
|
|
|
5365
5446
|
/** Step automation hooks (single ref or ordered list). `null` detaches. */
|
|
5366
5447
|
onEnter: ScriptRefListSchema.nullable().optional(),
|
|
5367
5448
|
onExit: ScriptRefListSchema.nullable().optional(),
|
|
5449
|
+
/** Required file inputs for the step. `null` clears the declaration. */
|
|
5450
|
+
consumes: z23.array(CraftbookStepInputSchema).min(1).nullable().optional(),
|
|
5368
5451
|
/** Auto-advance contract. `null` clears it. */
|
|
5369
5452
|
advanceWhen: AdvanceWhenSchema.nullable().optional(),
|
|
5370
5453
|
/** The end-of-step gate (current or legacy shape). `null` clears it. */
|
|
@@ -6175,7 +6258,7 @@ var ProjectTypeCompositionShape = {
|
|
|
6175
6258
|
tabVisibility: ProjectTabVisibilitySchema.optional(),
|
|
6176
6259
|
/**
|
|
6177
6260
|
* Project shape for instances of this type. `solo` marks a single-gezel
|
|
6178
|
-
* "
|
|
6261
|
+
* "Builder" experience (games, the chat room): the one roster gezel
|
|
6179
6262
|
* does everything, no separate overseer voorman is recruited, and the UI
|
|
6180
6263
|
* collapses the crew picker to that lead. Maps onto the project's `mode`
|
|
6181
6264
|
* at adoption. Omit for the default crew shape.
|
|
@@ -6183,9 +6266,9 @@ var ProjectTypeCompositionShape = {
|
|
|
6183
6266
|
mode: z26.enum(["crew", "solo"]).optional(),
|
|
6184
6267
|
/**
|
|
6185
6268
|
* Custom label for this type's project lead, shown wherever the generic
|
|
6186
|
-
* "Voorman" / "
|
|
6269
|
+
* "Voorman" / "Builder" would appear (the chat chip, project
|
|
6187
6270
|
* settings) — e.g. checkers uses "Opponent". Aimed at solo types that
|
|
6188
|
-
* want a domain word instead of "
|
|
6271
|
+
* want a domain word instead of "Builder". The underlying data field
|
|
6189
6272
|
* stays `voormanGezelId`; only the rendered label changes.
|
|
6190
6273
|
*/
|
|
6191
6274
|
leadLabel: z26.string().min(1).optional(),
|
|
@@ -6294,6 +6377,13 @@ var ConnectorActionSchema = z26.object({
|
|
|
6294
6377
|
/** Consent gate this action clears at commit time (e.g. `recipient-allowlist`). */
|
|
6295
6378
|
consentScope: z26.string()
|
|
6296
6379
|
});
|
|
6380
|
+
var ConnectorSetupInstructionsSchema = z26.object({
|
|
6381
|
+
title: z26.string().min(1),
|
|
6382
|
+
description: z26.string().min(1).optional(),
|
|
6383
|
+
steps: z26.array(z26.string().min(1)).optional(),
|
|
6384
|
+
url: z26.string().url().optional(),
|
|
6385
|
+
urlLabel: z26.string().min(1).optional()
|
|
6386
|
+
});
|
|
6297
6387
|
var ConnectorTypeCompositionShape = {
|
|
6298
6388
|
/** Which driver executes the fetch. */
|
|
6299
6389
|
driver: ConnectorDriverSchema,
|
|
@@ -6301,6 +6391,8 @@ var ConnectorTypeCompositionShape = {
|
|
|
6301
6391
|
configSchema: z26.record(z26.string(), z26.unknown()).optional(),
|
|
6302
6392
|
/** Shape of the credential the binding stores in the SecretStore. */
|
|
6303
6393
|
secretShape: z26.record(z26.string(), z26.unknown()).optional(),
|
|
6394
|
+
/** Concise setup guidance rendered above the binding form. */
|
|
6395
|
+
setupInstructions: ConnectorSetupInstructionsSchema.optional(),
|
|
6304
6396
|
/**
|
|
6305
6397
|
* Driver-specific fetch config: `{adapterId}` (native) | `{server,list,fetch}`
|
|
6306
6398
|
* (mcp) | `{fetch|cli}` (script) | `{component,action,...}` (spectral).
|
|
@@ -6610,6 +6702,15 @@ var ChatModelMlxSourceSchema = z26.object({
|
|
|
6610
6702
|
});
|
|
6611
6703
|
var ChatModelIdentitySchema = IdentityCommonSchema.extend({
|
|
6612
6704
|
kind: z26.literal("chat-model"),
|
|
6705
|
+
/**
|
|
6706
|
+
* Organization that created the core model weights when it differs from
|
|
6707
|
+
* the catalog maintainer (for example, a quant maintained by its converter).
|
|
6708
|
+
* Omit when `maintainer` already names the maker.
|
|
6709
|
+
*/
|
|
6710
|
+
maker: z26.object({
|
|
6711
|
+
name: z26.string().min(1),
|
|
6712
|
+
url: z26.string().url().optional()
|
|
6713
|
+
}).optional(),
|
|
6613
6714
|
parameterSize: z26.string(),
|
|
6614
6715
|
supportsTools: z26.boolean(),
|
|
6615
6716
|
contextWindow: z26.number().int().positive().optional(),
|
|
@@ -6707,6 +6808,11 @@ var ChatModelManifestSchema = z26.object({
|
|
|
6707
6808
|
name: z26.string(),
|
|
6708
6809
|
url: z26.string().url().optional()
|
|
6709
6810
|
}),
|
|
6811
|
+
/** Core-model maker; present when it differs from `maintainer`. */
|
|
6812
|
+
maker: z26.object({
|
|
6813
|
+
name: z26.string().min(1),
|
|
6814
|
+
url: z26.string().url().optional()
|
|
6815
|
+
}).optional(),
|
|
6710
6816
|
logo: z26.string().optional(),
|
|
6711
6817
|
license: z26.string().optional(),
|
|
6712
6818
|
...LicenseMetaShape,
|
|
@@ -7518,6 +7624,18 @@ var ChatSessionSchema = z28.object({
|
|
|
7518
7624
|
}),
|
|
7519
7625
|
/** Snapshot of the gezel's about.md at session creation, for drift warnings. */
|
|
7520
7626
|
aboutSnapshot: z28.string().optional(),
|
|
7627
|
+
/**
|
|
7628
|
+
* Per-session override of `config.roleBasedNameOnlyMode` ("boring mode").
|
|
7629
|
+
* Stamped at creation when the creating client has a fixed presentation
|
|
7630
|
+
* mode — the TUI always renders role-based labels, so it pins the
|
|
7631
|
+
* sessions it creates to `true`. This keeps the prompt (what the model
|
|
7632
|
+
* is told about other gezels) consistent with what that client shows;
|
|
7633
|
+
* without it the TUI displayed `reviewer:` while the system prompt said
|
|
7634
|
+
* "the voorman is Tomas", and the model naturally leaked names into
|
|
7635
|
+
* prose. Unset = follow the live config flag, which is what desktop
|
|
7636
|
+
* sessions do.
|
|
7637
|
+
*/
|
|
7638
|
+
roleBasedNameOnlyMode: z28.boolean().optional(),
|
|
7521
7639
|
/** Set when the last attempted resume failed — UI surfaces a banner. */
|
|
7522
7640
|
resumeFailed: z28.boolean().optional(),
|
|
7523
7641
|
/**
|
|
@@ -7746,7 +7864,14 @@ var CreateChatSessionRequestSchema = z28.object({
|
|
|
7746
7864
|
projectId: z28.string().optional(),
|
|
7747
7865
|
taskRef: z28.string().optional(),
|
|
7748
7866
|
stepId: z28.string().optional(),
|
|
7749
|
-
craftbookRef: z28.string().optional()
|
|
7867
|
+
craftbookRef: z28.string().optional(),
|
|
7868
|
+
/**
|
|
7869
|
+
* Pin the session's name-rendering mode instead of following
|
|
7870
|
+
* `config.roleBasedNameOnlyMode`. Passed by clients whose presentation
|
|
7871
|
+
* mode is fixed (the TUI) so prompt-side name rendering matches their
|
|
7872
|
+
* labels. See `ChatSessionSchema.roleBasedNameOnlyMode`.
|
|
7873
|
+
*/
|
|
7874
|
+
roleBasedNameOnlyMode: z28.boolean().optional()
|
|
7750
7875
|
});
|
|
7751
7876
|
var ListChatSessionsResponseSchema = z28.object({
|
|
7752
7877
|
sessions: z28.array(ChatSessionSummarySchema)
|
|
@@ -8372,13 +8497,26 @@ var GitHubPullFileSchema = z33.object({
|
|
|
8372
8497
|
additions: z33.number().int(),
|
|
8373
8498
|
deletions: z33.number().int(),
|
|
8374
8499
|
changes: z33.number().int(),
|
|
8375
|
-
/**
|
|
8500
|
+
/** Unified diff hunk, if requested and returned by GitHub. */
|
|
8376
8501
|
patch: z33.string().optional(),
|
|
8502
|
+
/** Character count before Gezel's local patch budget was applied. */
|
|
8503
|
+
patchChars: z33.number().int().nonnegative().optional(),
|
|
8504
|
+
/** True when Gezel clipped `patch`; callers must request the file/diff directly. */
|
|
8505
|
+
patchTruncated: z33.boolean().optional(),
|
|
8377
8506
|
/** Prior path when `status === 'renamed'`. */
|
|
8378
8507
|
previousFilename: z33.string().optional()
|
|
8379
8508
|
});
|
|
8380
8509
|
var ListGitHubPullFilesResponseSchema = z33.object({
|
|
8381
|
-
files: z33.array(GitHubPullFileSchema)
|
|
8510
|
+
files: z33.array(GitHubPullFileSchema),
|
|
8511
|
+
/** Total files in the PR before an optional path filter. */
|
|
8512
|
+
allFiles: z33.number().int().nonnegative().optional(),
|
|
8513
|
+
/** Total files selected by the optional path filter. */
|
|
8514
|
+
totalFiles: z33.number().int().nonnegative().optional(),
|
|
8515
|
+
offset: z33.number().int().nonnegative().optional(),
|
|
8516
|
+
limit: z33.number().int().positive().optional(),
|
|
8517
|
+
hasMore: z33.boolean().optional(),
|
|
8518
|
+
nextOffset: z33.number().int().nonnegative().optional(),
|
|
8519
|
+
includesPatch: z33.boolean().optional()
|
|
8382
8520
|
});
|
|
8383
8521
|
var GitHubPullCommentSchema = z33.object({
|
|
8384
8522
|
id: z33.number(),
|
|
@@ -8395,7 +8533,14 @@ var ListGitHubPullCommentsResponseSchema = z33.object({
|
|
|
8395
8533
|
});
|
|
8396
8534
|
var GitHubPullDiffResponseSchema = z33.object({
|
|
8397
8535
|
number: z33.number().int(),
|
|
8398
|
-
diff: z33.string()
|
|
8536
|
+
diff: z33.string(),
|
|
8537
|
+
/** Exact changed path when this is a file-scoped diff. */
|
|
8538
|
+
path: z33.string().optional(),
|
|
8539
|
+
offset: z33.number().int().nonnegative().optional(),
|
|
8540
|
+
returnedChars: z33.number().int().nonnegative().optional(),
|
|
8541
|
+
totalChars: z33.number().int().nonnegative().optional(),
|
|
8542
|
+
truncated: z33.boolean().optional(),
|
|
8543
|
+
nextOffset: z33.number().int().nonnegative().optional()
|
|
8399
8544
|
});
|
|
8400
8545
|
var GitHubCreateCommentRequestSchema = z33.object({
|
|
8401
8546
|
body: z33.string().min(1)
|
|
@@ -10683,6 +10828,13 @@ var GezelConfigSchema = z37.object({
|
|
|
10683
10828
|
* Advanced.
|
|
10684
10829
|
*/
|
|
10685
10830
|
showAdvancedFeatures: z37.boolean().optional(),
|
|
10831
|
+
/**
|
|
10832
|
+
* When `true`, very early work-in-progress surfaces are revealed in the
|
|
10833
|
+
* UI and CLI. This is a discoverability preference, not a security or
|
|
10834
|
+
* service-layer capability boundary. Defaults on in development builds
|
|
10835
|
+
* and off in releases; an explicit user choice always wins.
|
|
10836
|
+
*/
|
|
10837
|
+
showWorkInProgressFeatures: z37.boolean().optional(),
|
|
10686
10838
|
/**
|
|
10687
10839
|
* Debug-only opt-in: when `true`, the service rewrites every
|
|
10688
10840
|
* template-derived gezel's `about.md` back to the prose its catalog
|
|
@@ -10959,7 +11111,7 @@ var GezelConfigSchema = z37.object({
|
|
|
10959
11111
|
* crew with granular per-step craftbooks. Right for raw-completion
|
|
10960
11112
|
* local models, where gezel's loop IS the agent.
|
|
10961
11113
|
* - `flat`: route concrete asks to a single generalist (`start_job` →
|
|
10962
|
-
* solo
|
|
11114
|
+
* solo Builder, which already collapses the craftbook onto the
|
|
10963
11115
|
* one specialist). Right for self-orchestrating providers (codex-cli,
|
|
10964
11116
|
* anthropic-cli, copilot) that bring their own agent loop — the crew
|
|
10965
11117
|
* + granular steps are mostly redundant overhead for them (eval data:
|
|
@@ -11863,6 +12015,12 @@ var ListProjectsResponseSchema = z37.object({
|
|
|
11863
12015
|
var CreateProjectRequestSchema = z37.object({
|
|
11864
12016
|
name: z37.string().min(1),
|
|
11865
12017
|
description: z37.string().optional(),
|
|
12018
|
+
/**
|
|
12019
|
+
* Existing local folder to use as the project workspace. Folder-backed
|
|
12020
|
+
* projects are created with ambient Meester progress check-ins disabled;
|
|
12021
|
+
* the user can opt back in from Project Settings.
|
|
12022
|
+
*/
|
|
12023
|
+
workingDir: z37.string().min(1).optional(),
|
|
11866
12024
|
// about/missionObjectives are *encouraged, not required* at the wire level.
|
|
11867
12025
|
// The New Project dialog still enforces the 60/40 richness minimums for the
|
|
11868
12026
|
// blank/GitHub flows (where the user is authoring context from scratch), but
|
|
@@ -11875,7 +12033,7 @@ var CreateProjectRequestSchema = z37.object({
|
|
|
11875
12033
|
missionObjectives: z37.string().optional().describe('Concrete success criteria \u2014 usually a bullet list. What does "done" look like?'),
|
|
11876
12034
|
/**
|
|
11877
12035
|
* Project shape. `crew` (default) → traditional voorman-coordinates-
|
|
11878
|
-
* specialists. `solo` → a "job": one
|
|
12036
|
+
* specialists. `solo` → a "job": one Builder does
|
|
11879
12037
|
* everything themselves; team-management tools are filtered out for
|
|
11880
12038
|
* sessions on this project.
|
|
11881
12039
|
*/
|
|
@@ -15039,6 +15197,7 @@ export {
|
|
|
15039
15197
|
CraftbookSchema,
|
|
15040
15198
|
CraftbookScriptsSchema,
|
|
15041
15199
|
CraftbookSpawnSchema,
|
|
15200
|
+
CraftbookStepInputSchema,
|
|
15042
15201
|
CraftbookStepSchema,
|
|
15043
15202
|
CraftbookSuggestionSchema,
|
|
15044
15203
|
CraftbookSummarySchema,
|