@bendyline/gezel 1.0.2 → 1.0.3

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.
@@ -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). E.g. exclude internal-only release noise. */
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
@@ -915,6 +930,12 @@ var AdvanceWhenSchema = z5.object({
915
930
  /** Step to activate on the signal. Defaults to `next`; must resolve like `next`. */
916
931
  goto: z5.string().optional()
917
932
  });
933
+ var CraftbookStepInputSchema = z5.object({
934
+ /** Path relative to the selected drawer's root. */
935
+ file: z5.string().min(1).describe("Path relative to the selected drawer root."),
936
+ /** Read from the artifacts drawer instead of the project workspace. */
937
+ artifact: z5.boolean().optional().describe("True for the artifacts drawer; false/omitted for the project workspace.")
938
+ });
918
939
  var ModelTierSchema = z5.enum(MODEL_TIER_ORDER);
919
940
  var CraftbookStepSchema = z5.object({
920
941
  id: z5.string().min(1),
@@ -955,6 +976,10 @@ var CraftbookStepSchema = z5.object({
955
976
  * read the LAST ref's output (legacy routing; prefer gate routing).
956
977
  */
957
978
  onExit: ScriptRefListSchema.optional(),
979
+ /** Required file inputs for this step, in the order they should be opened. */
980
+ consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
981
+ "Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
982
+ ),
958
983
  /** See {@link AdvanceWhenSchema}. */
959
984
  advanceWhen: AdvanceWhenSchema.optional(),
960
985
  /** The end-of-step decision. See {@link StepGateSchema} (current) / {@link GateSpecSchema} (legacy). */
@@ -1009,6 +1034,14 @@ function validateCraftbookGraph(cb) {
1009
1034
  problems.push(`step "${s.id}" advanceWhen.goto "${s.advanceWhen.goto}" missing from steps`);
1010
1035
  }
1011
1036
  }
1037
+ for (const input of s.consumes ?? []) {
1038
+ if (!input.artifact) continue;
1039
+ if (!/`read_artifact(?:`|\()/.test(s.prompt ?? "")) {
1040
+ problems.push(
1041
+ `step "${s.id}" consumes artifact "${input.file}" but its prompt does not explicitly call \`read_artifact\``
1042
+ );
1043
+ }
1044
+ }
1012
1045
  if (s.gate) {
1013
1046
  const gate = normalizeStepGate(s.gate);
1014
1047
  if (s.terminal && gate.at === "activation") {
@@ -1301,6 +1334,9 @@ var NewCraftbookStepSchema = z5.object({
1301
1334
  assignee: TaskAssigneeSchema.optional(),
1302
1335
  onEnter: ScriptRefListSchema.optional(),
1303
1336
  onExit: ScriptRefListSchema.optional(),
1337
+ consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
1338
+ "Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
1339
+ ),
1304
1340
  advanceWhen: AdvanceWhenSchema.optional(),
1305
1341
  gate: StepGateUnionSchema.optional(),
1306
1342
  /** See {@link StepDeliverableSchema} — one field attaches the enforced gate. */
@@ -1413,6 +1449,7 @@ function resolveSteps(blueprints) {
1413
1449
  ...s.assignee ? { assignee: s.assignee } : {},
1414
1450
  ...s.onEnter ? { onEnter: s.onEnter } : {},
1415
1451
  ...s.onExit ? { onExit: s.onExit } : {},
1452
+ ...s.consumes && s.consumes.length > 0 ? { consumes: s.consumes } : {},
1416
1453
  ...s.advanceWhen ? { advanceWhen: s.advanceWhen } : {},
1417
1454
  ...s.gate ? { gate: s.gate } : {},
1418
1455
  ...s.next ? { next: s.next } : {},
@@ -1535,6 +1572,10 @@ function applyStepPatch(step, patch) {
1535
1572
  delete updated.onExit;
1536
1573
  } else updated.onExit = patch.onExit;
1537
1574
  }
1575
+ if (patch.consumes !== void 0) {
1576
+ if (patch.consumes === null || patch.consumes.length === 0) delete updated.consumes;
1577
+ else updated.consumes = patch.consumes;
1578
+ }
1538
1579
  if (patch.advanceWhen !== void 0) {
1539
1580
  if (patch.advanceWhen === null) delete updated.advanceWhen;
1540
1581
  else updated.advanceWhen = patch.advanceWhen;
@@ -1713,7 +1754,7 @@ function craftbookDocFormatFromEnv(value) {
1713
1754
  }
1714
1755
 
1715
1756
  // src/schemas/craftbook-test.ts
1716
- import { z as z19 } from "zod";
1757
+ import { z as z20 } from "zod";
1717
1758
 
1718
1759
  // src/schemas/history.ts
1719
1760
  import { z as z18 } from "zod";
@@ -3534,7 +3575,15 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3534
3575
  kind: z17.string(),
3535
3576
  summary: z17.string(),
3536
3577
  at: z17.string(),
3537
- taskRef: z17.string().optional()
3578
+ taskRef: z17.string().optional(),
3579
+ /**
3580
+ * Gezel responsible for the event, when History recorded one. Kept
3581
+ * separate from the human-readable summary so fixed-presentation
3582
+ * clients (notably the role-name-only CLI) can render the actor using
3583
+ * their own naming mode instead of leaking the friendly name embedded
3584
+ * in the audit prose.
3585
+ */
3586
+ gezelId: z17.string().optional()
3538
3587
  }),
3539
3588
  /**
3540
3589
  * Emitted when a gezel crosses a growth level threshold and a pending
@@ -4075,444 +4124,9 @@ var ListHistoryResponseSchema = z18.object({
4075
4124
  entries: z18.array(HistoryEntrySchema)
4076
4125
  });
4077
4126
 
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
4127
  // src/schemas/project.ts
4514
- import { z as z21 } from "zod";
4515
- var HttpsOriginSchema = z21.string().url().refine(
4128
+ import { z as z19 } from "zod";
4129
+ var HttpsOriginSchema = z19.string().url().refine(
4516
4130
  (value) => {
4517
4131
  try {
4518
4132
  const url = new URL(value);
@@ -4523,7 +4137,7 @@ var HttpsOriginSchema = z21.string().url().refine(
4523
4137
  },
4524
4138
  { message: "must be an exact HTTPS origin (for example https://api.example.com)" }
4525
4139
  );
4526
- var ProjectGitHubSchema = z21.object({
4140
+ var ProjectGitHubSchema = z19.object({
4527
4141
  // Accept any non-empty string. Git URLs come in many forms beyond
4528
4142
  // `https://`: ssh shorthand (`git@github.com:owner/repo`), local
4529
4143
  // filesystem paths (`/path/to/bare.git`), other-host references.
@@ -4531,89 +4145,89 @@ var ProjectGitHubSchema = z21.object({
4531
4145
  // and broke Phase-3 worktree tests that use a local bare repo as
4532
4146
  // the upstream. Higher layers (the github sync code) parse the URL
4533
4147
  // and reject anything that doesn't shape up as a clonable ref.
4534
- url: z21.string().min(1),
4535
- branch: z21.string().optional(),
4148
+ url: z19.string().min(1),
4149
+ branch: z19.string().optional(),
4536
4150
  /** Resolved absolute path to the working tree. Managed by the service. */
4537
- checkoutDir: z21.string().optional(),
4538
- lastSyncedAt: z21.string().optional(),
4151
+ checkoutDir: z19.string().optional(),
4152
+ lastSyncedAt: z19.string().optional(),
4539
4153
  /** Repo default branch (e.g. "main"), detected lazily and cached. Managed by the service. */
4540
- defaultBranch: z21.string().optional()
4154
+ defaultBranch: z19.string().optional()
4541
4155
  });
4542
- var ProjectConnectorBindingSchema = z21.object({
4156
+ var ProjectConnectorBindingSchema = z19.object({
4543
4157
  /** Stable binding id; also the SecretStore `fieldId` + corpus-slug seed. */
4544
- id: z21.string().min(1),
4158
+ id: z19.string().min(1),
4545
4159
  /** The connector-type catalog id, e.g. `mail-gmail`, `linear-issues`. */
4546
- type: z21.string().min(1),
4160
+ type: z19.string().min(1),
4547
4161
  /** Catalog source the type resolved from (provenance/pin). */
4548
- sourceId: z21.string().optional(),
4162
+ sourceId: z19.string().optional(),
4549
4163
  /** Pinned connector-type version. */
4550
- version: z21.string().optional(),
4551
- displayName: z21.string().optional(),
4164
+ version: z19.string().optional(),
4165
+ displayName: z19.string().optional(),
4552
4166
  /**
4553
4167
  * Artifact-relative corpus root (`data/<corpusName>`), resolved once at bind
4554
4168
  * time and never recomputed — renaming a binding must not strand its corpus.
4555
4169
  */
4556
- corpusDir: z21.string().optional(),
4170
+ corpusDir: z19.string().optional(),
4557
4171
  /** Per-binding config, validated at bind time against the type's `configSchema`. */
4558
- config: z21.record(z21.string(), z21.unknown()).default({}),
4172
+ config: z19.record(z19.string(), z19.unknown()).default({}),
4559
4173
  /** Opaque, adapter-shaped incremental-sync cursor. Persisted so resync resumes. */
4560
- cursor: z21.unknown().optional(),
4174
+ cursor: z19.unknown().optional(),
4561
4175
  /** Pause syncing without unbinding. */
4562
- disabled: z21.boolean().optional(),
4563
- lastSyncedAt: z21.string().optional(),
4176
+ disabled: z19.boolean().optional(),
4177
+ lastSyncedAt: z19.string().optional(),
4564
4178
  /** Last sync error, surfaced in the UI; cleared on the next success. */
4565
- lastError: z21.string().optional()
4179
+ lastError: z19.string().optional()
4566
4180
  });
4567
- var ProjectNudgeConfigSchema = z21.object({
4568
- enabled: z21.boolean().optional(),
4569
- rapidIntervalMs: z21.number().int().positive().optional(),
4570
- slowIntervalMs: z21.number().int().positive().optional(),
4571
- recentActivityWindowMs: z21.number().int().positive().optional(),
4572
- rapidAttemptsBeforeBackoff: z21.number().int().positive().optional(),
4181
+ var ProjectNudgeConfigSchema = z19.object({
4182
+ enabled: z19.boolean().optional(),
4183
+ rapidIntervalMs: z19.number().int().positive().optional(),
4184
+ slowIntervalMs: z19.number().int().positive().optional(),
4185
+ recentActivityWindowMs: z19.number().int().positive().optional(),
4186
+ rapidAttemptsBeforeBackoff: z19.number().int().positive().optional(),
4573
4187
  /**
4574
4188
  * Grace period applied to the very first nudge a project ever
4575
4189
  * receives, measured from `project.createdAt`. Default per tempo;
4576
4190
  * setting `0` opts out (legacy behavior — first nudge fires as
4577
4191
  * soon as the rapid interval allows).
4578
4192
  */
4579
- firstNudgeGraceMs: z21.number().int().nonnegative().optional()
4193
+ firstNudgeGraceMs: z19.number().int().nonnegative().optional()
4580
4194
  });
4581
- var ProjectNudgeStateSchema = z21.object({
4582
- lastNudgedAt: z21.string().optional(),
4583
- consecutiveRapidNudges: z21.number().int().nonnegative().optional()
4195
+ var ProjectNudgeStateSchema = z19.object({
4196
+ lastNudgedAt: z19.string().optional(),
4197
+ consecutiveRapidNudges: z19.number().int().nonnegative().optional()
4584
4198
  });
4585
- var ProjectTabVisibilitySchema = z21.object({
4586
- overview: z21.boolean().optional(),
4587
- tasks: z21.boolean().optional(),
4588
- approvals: z21.boolean().optional(),
4589
- workspace: z21.boolean().optional(),
4590
- artifacts: z21.boolean().optional(),
4591
- map: z21.boolean().optional()
4199
+ var ProjectTabVisibilitySchema = z19.object({
4200
+ overview: z19.boolean().optional(),
4201
+ tasks: z19.boolean().optional(),
4202
+ approvals: z19.boolean().optional(),
4203
+ workspace: z19.boolean().optional(),
4204
+ artifacts: z19.boolean().optional(),
4205
+ map: z19.boolean().optional()
4592
4206
  }).strict();
4593
- var ProjectManagedWorkspaceWritePolicySchema = z21.enum(["auto", "allow", "deny"]);
4594
- var ProjectTypeProvenanceSchema = z21.object({
4207
+ var ProjectManagedWorkspaceWritePolicySchema = z19.enum(["auto", "allow", "deny"]);
4208
+ var ProjectTypeProvenanceSchema = z19.object({
4595
4209
  /** Catalog id of the applied project type. */
4596
- id: z21.string(),
4210
+ id: z19.string(),
4597
4211
  /** Type version installed at adoption. */
4598
- version: z21.string(),
4212
+ version: z19.string(),
4599
4213
  /** Catalog source the type resolved from (`bundled` | `local` | `community` | …). */
4600
- source: z21.string(),
4214
+ source: z19.string(),
4601
4215
  /** Param values collected at adoption, substituted into templates + seed files. */
4602
- params: z21.record(z21.string(), z21.unknown()).optional(),
4216
+ params: z19.record(z19.string(), z19.unknown()).optional(),
4603
4217
  /** ISO timestamp of adoption. */
4604
- appliedAt: z21.string()
4218
+ appliedAt: z19.string()
4605
4219
  });
4606
- var ProjectSchema = z21.object({
4220
+ var ProjectSchema = z19.object({
4607
4221
  id: EntityIdSchema,
4608
- name: z21.string(),
4609
- description: z21.string().optional(),
4610
- workingDir: z21.string().optional(),
4222
+ name: z19.string(),
4223
+ description: z19.string().optional(),
4224
+ workingDir: z19.string().optional(),
4611
4225
  /** Optional gezel that acts as the project's voorman (foreman). Surfaces in
4612
4226
  * the project detail pane, flows into the system prompt when a session is
4613
4227
  * scoped here. For solo projects (`mode === 'solo'`) this same field
4614
4228
  * holds the project's ambachtsman — the data is unchanged, only the
4615
4229
  * label flips. */
4616
- voormanGezelId: z21.string().optional(),
4230
+ voormanGezelId: z19.string().optional(),
4617
4231
  /**
4618
4232
  * Internal marker: ISO timestamp of the one-time automatic voorman
4619
4233
  * assignment. The indexer ensures every project ends up with a voorman
@@ -4623,7 +4237,7 @@ var ProjectSchema = z21.object({
4623
4237
  * silently re-populated on the next scan. Not user-editable; absent on
4624
4238
  * projects last written before this field existed.
4625
4239
  */
4626
- voormanAutoAssignedAt: z21.string().optional(),
4240
+ voormanAutoAssignedAt: z19.string().optional(),
4627
4241
  /**
4628
4242
  * Roster — gezels that have been pulled into this project. Populated
4629
4243
  * automatically the first time a gezel is set as voorman, opens a
@@ -4638,7 +4252,7 @@ var ProjectSchema = z21.object({
4638
4252
  * (back-compat with every project written before this field
4639
4253
  * existed).
4640
4254
  */
4641
- gezelIds: z21.array(z21.string()).optional(),
4255
+ gezelIds: z19.array(z19.string()).optional(),
4642
4256
  /**
4643
4257
  * Suggested-work keys the user has dismissed ("don't offer this again
4644
4258
  * here"). Advisory UI state, same spirit as `gezelIds`: enabling a
@@ -4648,7 +4262,7 @@ var ProjectSchema = z21.object({
4648
4262
  * `project-type:<typeId>:<scheduleKey>`). Deliberately not exposed
4649
4263
  * through the model-facing `update_project` MCP tool.
4650
4264
  */
4651
- suggestedWorkDismissed: z21.array(z21.string()).optional(),
4265
+ suggestedWorkDismissed: z19.array(z19.string()).optional(),
4652
4266
  /**
4653
4267
  * Shared per-project configuration values ("project properties") that
4654
4268
  * craftbook params and features draw from — e.g. `content.language`,
@@ -4657,7 +4271,7 @@ var ProjectSchema = z21.object({
4657
4271
  * ids are allowed (the registry improves display, it doesn't gate).
4658
4272
  * Values are plain strings; empty string is treated as unset.
4659
4273
  */
4660
- properties: z21.record(z21.string(), z21.string()).optional(),
4274
+ properties: z19.record(z19.string(), z19.string()).optional(),
4661
4275
  /**
4662
4276
  * Project shape. `crew` (the default) is the original behavior — the
4663
4277
  * voorman recruits and coordinates a team of specialists. `solo` is a
@@ -4668,7 +4282,7 @@ var ProjectSchema = z21.object({
4668
4282
  * for back-compat with every project on disk before this field
4669
4283
  * existed.
4670
4284
  */
4671
- mode: z21.enum(["crew", "solo"]).optional(),
4285
+ mode: z19.enum(["crew", "solo"]).optional(),
4672
4286
  /**
4673
4287
  * Optional custom label for this project's lead gezel, overriding the
4674
4288
  * mode-based default ("Voorman" / "Ambachtsman") everywhere the UI
@@ -4676,7 +4290,7 @@ var ProjectSchema = z21.object({
4676
4290
  * "Opponent"); absent → the mode default. The data field stays
4677
4291
  * `voormanGezelId` — only the label changes.
4678
4292
  */
4679
- leadLabel: z21.string().optional(),
4293
+ leadLabel: z19.string().optional(),
4680
4294
  /**
4681
4295
  * Lean-agent profile (set by a project type at adoption, e.g. checkers).
4682
4296
  * When true, sessions here get a minimal tool surface (the type's script
@@ -4684,7 +4298,7 @@ var ProjectSchema = z21.object({
4684
4298
  * scaffolding). Keeps small local models from being overwhelmed on a
4685
4299
  * focused single-purpose task. Absent → the full agent profile.
4686
4300
  */
4687
- leanProfile: z21.boolean().optional(),
4301
+ leanProfile: z19.boolean().optional(),
4688
4302
  /**
4689
4303
  * Per-project workspace-indexing switch. Missing/true preserves the
4690
4304
  * historical behavior: structural discovery plus the content-index refresh
@@ -4697,9 +4311,9 @@ var ProjectSchema = z21.object({
4697
4311
  * document index. Project-type manifests may seed the value at adoption and
4698
4312
  * the user can override it later in Project Settings.
4699
4313
  */
4700
- indexingEnabled: z21.boolean().optional(),
4314
+ indexingEnabled: z19.boolean().optional(),
4701
4315
  github: ProjectGitHubSchema.optional(),
4702
- connectors: z21.array(ProjectConnectorBindingSchema).optional(),
4316
+ connectors: z19.array(ProjectConnectorBindingSchema).optional(),
4703
4317
  nudgeConfig: ProjectNudgeConfigSchema.optional(),
4704
4318
  nudgeState: ProjectNudgeStateSchema.optional(),
4705
4319
  /**
@@ -4722,7 +4336,7 @@ var ProjectSchema = z21.object({
4722
4336
  * @deprecated Use `managedWorkspaceWritePolicy` and the centralized
4723
4337
  * `projectManagedWorkspaceWritable` resolver.
4724
4338
  */
4725
- allowGezelWrites: z21.boolean().optional(),
4339
+ allowGezelWrites: z19.boolean().optional(),
4726
4340
  /**
4727
4341
  * Per-project Codex execution posture selected from the project status bar.
4728
4342
  * It overrides per-gezel/install Codex defaults so the visible control is
@@ -4759,7 +4373,7 @@ var ProjectSchema = z21.object({
4759
4373
  * weak local model having to take an explicit action. Reversible and
4760
4374
  * non-destructive; chat and direct tool calls keep working.
4761
4375
  */
4762
- status: z21.enum(["active", "readonly", "inactive", "stable"]).optional(),
4376
+ status: z19.enum(["active", "readonly", "inactive", "stable"]).optional(),
4763
4377
  /**
4764
4378
  * Bury this project in the navigation without deleting it. Archived
4765
4379
  * projects remain available from the dedicated section in the full
@@ -4769,13 +4383,13 @@ var ProjectSchema = z21.object({
4769
4383
  * Missing/false means visible in the ordinary project UX, preserving
4770
4384
  * compatibility with projects written before archiving existed.
4771
4385
  */
4772
- archived: z21.boolean().optional(),
4386
+ archived: z19.boolean().optional(),
4773
4387
  /**
4774
4388
  * Per-project override of the `run_nodejs_script` wall-clock
4775
4389
  * timeout. Clamped between 30 seconds and 30 minutes. Missing →
4776
4390
  * the service-side default (5 min) applies.
4777
4391
  */
4778
- workspaceScriptTimeoutMs: z21.number().int().min(3e4).max(30 * 6e4).optional(),
4392
+ workspaceScriptTimeoutMs: z19.number().int().min(3e4).max(30 * 6e4).optional(),
4779
4393
  /**
4780
4394
  * Named credentials this project is explicitly allowed to use.
4781
4395
  * Credentials are stored once globally in `SecretStore`; a grant
@@ -4784,76 +4398,515 @@ var ProjectSchema = z21.object({
4784
4398
  * Missing → no credentials granted. See `scripts/dispatcher.ts`
4785
4399
  * and `secrets/registry.ts` for resolution.
4786
4400
  */
4787
- grantedCredentials: z21.array(z21.string()).optional(),
4401
+ grantedCredentials: z19.array(z19.string()).optional(),
4402
+ /**
4403
+ * Advanced exact-origin bindings for toolset credentials. Built-in provider
4404
+ * credentials are service-pinned and webhook credentials follow the
4405
+ * configured webhook URL, so entries for those names are ignored.
4406
+ */
4407
+ credentialAllowedOrigins: z19.record(z19.string(), z19.array(HttpsOriginSchema)).optional(),
4408
+ /**
4409
+ * Explicit user override of the project's type (an id from the bundled
4410
+ * project-type taxonomy, see `project-types/taxonomy.ts`). When set, it
4411
+ * wins over `detectedProjectType` for craftbook suggestions. Cleared
4412
+ * (unset) → fall back to auto-detection. Missing on every project written
4413
+ * before this field existed.
4414
+ */
4415
+ projectTypeId: z19.string().optional(),
4416
+ /**
4417
+ * Auto-detected project type, recomputed on each content-index scan from
4418
+ * the workspace file mix + about/mission text. Not user-editable — the
4419
+ * user expresses an override via `projectTypeId`. Absent until the first
4420
+ * scan classifies the project (or when nothing scores above the floor).
4421
+ */
4422
+ detectedProjectType: z19.object({
4423
+ id: z19.string(),
4424
+ score: z19.number(),
4425
+ scannedAt: z19.string()
4426
+ }).optional(),
4427
+ /**
4428
+ * Provenance of an applied custom project type (see docs/project-types.md).
4429
+ * Distinct from `projectTypeId`, which is the taxonomy id used for craftbook
4430
+ * suggestion: a custom type stamps this on adoption and, when it `extends` a
4431
+ * taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
4432
+ * inherits. Absent on projects created without a custom type.
4433
+ */
4434
+ projectType: ProjectTypeProvenanceSchema.optional(),
4435
+ /**
4436
+ * Filesystem ownership boundary. Missing/`user` is ordinary per-account
4437
+ * state. `machine-shared` is a grandfathered project mounted from the
4438
+ * installer-managed shared root and operated on by this user's daemon.
4439
+ * The engine broker never opens the project or receives its paths.
4440
+ */
4441
+ storageScope: z19.enum(["user", "machine-shared"]).optional(),
4442
+ createdAt: z19.string(),
4443
+ updatedAt: z19.string()
4444
+ });
4445
+ function resolveProjectTypeId(project) {
4446
+ return project.projectTypeId ?? project.detectedProjectType?.id;
4447
+ }
4448
+ function projectAllowsAmbientWork(project) {
4449
+ const status = project.status ?? "active";
4450
+ return status === "active";
4451
+ }
4452
+ var InstalledPackageSchema = z19.object({
4453
+ name: z19.string(),
4454
+ version: z19.string()
4455
+ });
4456
+ var ProjectDetailSchema = ProjectSchema.extend({
4457
+ packages: z19.array(InstalledPackageSchema),
4458
+ /** Contents of `documents/about.md` inside the project, if present. */
4459
+ about: z19.string().optional(),
4460
+ /** Contents of `documents/missionObjectives.md`, if present. */
4461
+ missionObjectives: z19.string().optional()
4462
+ });
4463
+ var ProjectFileEntrySchema = z19.object({
4464
+ name: z19.string(),
4465
+ path: z19.string(),
4466
+ isDirectory: z19.boolean(),
4467
+ /** File mtime (ms epoch). Populated only when the caller opts into stats. */
4468
+ mtimeMs: z19.number().optional()
4469
+ });
4470
+ var ProjectGithubSchema = ProjectGitHubSchema;
4471
+
4472
+ // src/schemas/craftbook-test.ts
4473
+ var CRAFTBOOK_TEST_SCHEMA_VERSION = 1;
4474
+ var CRAFTBOOK_TEST_FILENAME = "test.json";
4475
+ var PrometheusAlertsCheckSchema = z20.object({
4476
+ kind: z20.literal("prometheusAlerts"),
4477
+ file: z20.string().min(1),
4478
+ minRules: z20.number().int().positive().optional(),
4479
+ maxPageAlerts: z20.number().int().nonnegative().optional(),
4480
+ allowedSeverities: z20.array(z20.string().min(1)).optional(),
4481
+ requiredServices: z20.array(z20.string().min(1)).optional(),
4482
+ requiredRunbookUrls: z20.array(z20.string().min(1)).optional()
4483
+ }).strict();
4484
+ var NodeScriptPassesCheckSchema = z20.object({
4485
+ kind: z20.literal("nodeScriptPasses"),
4486
+ script: z20.string().min(1),
4487
+ timeoutMs: z20.number().int().positive().optional(),
4488
+ requiredOutput: z20.array(
4489
+ z20.object({
4490
+ pattern: z20.string().min(1),
4491
+ flags: z20.string().optional(),
4492
+ label: z20.string().optional()
4493
+ }).strict()
4494
+ ).optional()
4495
+ }).strict();
4496
+ var BinaryDocumentCheckSchema = z20.object({
4497
+ kind: z20.literal("binaryDocument"),
4498
+ file: z20.string().min(1),
4499
+ /** Look in the artifacts drawer instead of the workspace. */
4500
+ artifact: z20.boolean().optional(),
4501
+ /** Floor on the container's byte length; defaults to 1000. */
4502
+ minBytes: z20.number().int().positive().optional()
4503
+ }).strict();
4504
+ var CraftbookTestCheckSchema = z20.union([
4505
+ GateCheckSchema,
4506
+ PrometheusAlertsCheckSchema,
4507
+ NodeScriptPassesCheckSchema,
4508
+ BinaryDocumentCheckSchema
4509
+ ]);
4510
+ var MockServiceIdSchema = z20.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, "mock service ids are lowercase kebab-case");
4511
+ var MockToolsetIdSchema = z20.string().min(1).regex(
4512
+ /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,
4513
+ "mock toolset ids are lowercase catalog ids or scoped npm-style ids"
4514
+ );
4515
+ var MockHttpRouteSchema = z20.object({
4516
+ method: z20.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
4517
+ /** Exact path, `:param` segments, or a trailing `*` wildcard. */
4518
+ path: z20.string().min(1),
4519
+ status: z20.number().int().min(100).max(599).default(200),
4520
+ headers: z20.record(z20.string(), z20.string()).optional(),
4521
+ /** String bodies are served verbatim; anything else is JSON-encoded. */
4522
+ body: z20.unknown(),
4523
+ latencyMs: z20.number().int().nonnegative().optional()
4524
+ }).strict();
4525
+ var MockServiceSchema = z20.discriminatedUnion("kind", [
4526
+ z20.object({
4527
+ kind: z20.literal("http"),
4528
+ id: MockServiceIdSchema,
4529
+ description: z20.string().min(1),
4530
+ /**
4531
+ * v1 mock HTTP is reachable ONLY through the `http.authed`
4532
+ * credential rail (anonymous script/browser HTTP hard-rejects
4533
+ * loopback), so a credential is required. The harness seeds it and
4534
+ * grants the mock's exact origin on the trial project.
4535
+ */
4536
+ credential: z20.object({
4537
+ name: z20.string().regex(/^mock\.[a-z0-9][a-z0-9.-]*$/, "mock credentials are named mock.<service-id>"),
4538
+ authScheme: z20.enum(["bearer", "basic"]).optional()
4539
+ }).strict(),
4540
+ routes: z20.array(MockHttpRouteSchema).min(1)
4541
+ }).strict(),
4542
+ z20.object({
4543
+ kind: z20.literal("webhook"),
4544
+ id: MockServiceIdSchema,
4545
+ description: z20.string().min(1),
4546
+ /** Receiver path; defaults to `/webhook` when omitted. */
4547
+ path: z20.string().min(1).optional()
4548
+ }).strict(),
4549
+ z20.object({
4550
+ kind: z20.literal("cli"),
4551
+ id: MockServiceIdSchema,
4552
+ description: z20.string().min(1),
4553
+ /** Fake-CLI shim seeded as a workspace file (today's dry-run pattern). */
4554
+ shim: z20.object({ path: z20.string().min(1), content: z20.string() }).strict()
4555
+ }).strict(),
4556
+ z20.object({
4557
+ kind: z20.literal("mcp"),
4558
+ id: MockServiceIdSchema,
4559
+ description: z20.string().min(1),
4560
+ /** Override the local-catalog id when the mock replaces a real dependency. */
4561
+ toolsetId: MockToolsetIdSchema.optional(),
4562
+ /**
4563
+ * Served live by the eval mock rail: each declared tool becomes a
4564
+ * real tool on a per-trial Streamable-HTTP MCP endpoint, installed
4565
+ * into the trial via a local-catalog `mock-mcp-<id>` toolset.
4566
+ * `resultTemplate` is JSON-encoded as the tool result text
4567
+ * (default `{"ok":true}` when absent).
4568
+ */
4569
+ tools: z20.array(
4570
+ z20.object({
4571
+ name: z20.string().min(1),
4572
+ description: z20.string().min(1),
4573
+ resultTemplate: z20.unknown().optional(),
4574
+ /** Deterministic stateful responses, consumed in call order; the last repeats. */
4575
+ resultSequence: z20.array(z20.unknown()).min(1).optional(),
4576
+ /**
4577
+ * Deterministic eval-only file materialization after this
4578
+ * tool call. The fixture must match the container the
4579
+ * deliverable's extension claims — a `minimal-pptx` written
4580
+ * to a `.docx` path now fails the `binaryDocument` check on
4581
+ * content type rather than sliding through a byte floor.
4582
+ */
4583
+ writeFixture: z20.object({
4584
+ surface: z20.enum(["workspace", "artifact"]),
4585
+ pathArgument: z20.string().min(1),
4586
+ fixture: z20.enum(["minimal-pptx", "minimal-docx", "minimal-pdf", "minimal-png"])
4587
+ }).strict().optional()
4588
+ }).strict()
4589
+ ).min(1)
4590
+ }).strict()
4591
+ ]);
4592
+ var CraftbookTestFixtureFileSchema = z20.object({
4593
+ path: z20.string().min(1),
4594
+ content: z20.string(),
4595
+ /** Defaults to `workspace`; `harness` never enters the model-visible project. */
4596
+ surface: z20.enum(["workspace", "artifact", "harness"]).optional(),
4788
4597
  /**
4789
- * Advanced exact-origin bindings for toolset credentials. Built-in provider
4790
- * credentials are service-pinned and webhook credentials follow the
4791
- * configured webhook URL, so entries for those names are ignored.
4598
+ * Whether the fixture is presented to the model as source material.
4599
+ * Defaults to true; false still seeds the file for browsers and graders.
4792
4600
  */
4793
- credentialAllowedOrigins: z21.record(z21.string(), z21.array(HttpsOriginSchema)).optional(),
4601
+ modelInput: z20.boolean().optional()
4602
+ }).strict();
4603
+ var CraftbookTestWorkerSchema = z20.object({
4604
+ name: z20.string().min(1),
4605
+ role: z20.string().min(1),
4606
+ description: z20.string().optional(),
4607
+ about: z20.string().optional()
4608
+ }).strict();
4609
+ var CraftbookTestSetupSchema = z20.object({
4610
+ projectName: z20.string().min(1),
4611
+ about: z20.string().optional(),
4612
+ missionObjectives: z20.string().optional(),
4613
+ /** Reproduce project write posture before the craftbook task starts. */
4614
+ managedWorkspaceWritePolicy: ProjectManagedWorkspaceWritePolicySchema.optional(),
4615
+ files: z20.array(CraftbookTestFixtureFileSchema).default([]),
4616
+ /** Exact values supplied to the catalog craftbook's `paramSchema`. */
4617
+ craftbookParams: z20.record(z20.string(), z20.string()).optional(),
4794
4618
  /**
4795
- * Explicit user override of the project's type (an id from the bundled
4796
- * project-type taxonomy, see `project-types/taxonomy.ts`). When set, it
4797
- * wins over `detectedProjectType` for craftbook suggestions. Cleared
4798
- * (unset) → fall back to auto-detection. Missing on every project written
4799
- * before this field existed.
4619
+ * Direct execution target. When present the harness seeds this gezel
4620
+ * and sends the kickoff straight to it (measuring whether the book
4621
+ * guides the work); absent the Meester routes.
4800
4622
  */
4801
- projectTypeId: z21.string().optional(),
4623
+ worker: CraftbookTestWorkerSchema.optional()
4624
+ }).strict();
4625
+ var CraftbookTestDeliverableSchema = z20.object({
4626
+ path: z20.string().min(1),
4627
+ kind: DeliverableKindSchema,
4628
+ /** Grade the path in the project's artifacts drawer, not its workspace. */
4629
+ artifact: z20.boolean().optional(),
4630
+ minBytes: z20.number().int().positive().optional(),
4631
+ checks: z20.array(CraftbookTestCheckSchema).optional()
4632
+ }).strict();
4633
+ var CraftbookTestMockExpectationSchema = z20.object({
4634
+ /** Mock service id from `mocks[]`. */
4635
+ service: MockServiceIdSchema,
4636
+ minRequests: z20.number().int().positive().optional(),
4637
+ /** Regex sources matched against logged request paths. */
4638
+ requiredPaths: z20.array(z20.string().min(1)).optional(),
4639
+ forbiddenPaths: z20.array(z20.string().min(1)).optional(),
4802
4640
  /**
4803
- * Auto-detected project type, recomputed on each content-index scan from
4804
- * the workspace file mix + about/mission text. Not user-editable the
4805
- * user expresses an override via `projectTypeId`. Absent until the first
4806
- * scan classifies the project (or when nothing scores above the floor).
4641
+ * Exact MCP tool names that must each have been called at least once
4642
+ * on this service (`kind: 'mcp'` only). Exact names, not regexes
4643
+ * the tool roster is fully declared in the same file, so a pattern
4644
+ * buys nothing and invites drift. Cross-checked against the mock's
4645
+ * declared `tools[]` at parse time.
4807
4646
  */
4808
- detectedProjectType: z21.object({
4809
- id: z21.string(),
4810
- score: z21.number(),
4811
- scannedAt: z21.string()
4812
- }).optional(),
4647
+ requiredTools: z20.array(z20.string().min(1)).optional(),
4648
+ /** Per-MCP-tool call budgets for repeated journeys or retries. */
4649
+ toolCalls: z20.record(
4650
+ z20.string().min(1),
4651
+ z20.object({
4652
+ minCalls: z20.number().int().nonnegative().default(1),
4653
+ maxCalls: z20.number().int().nonnegative().optional()
4654
+ }).strict().refine(
4655
+ (value) => value.maxCalls === void 0 || value.minCalls <= value.maxCalls,
4656
+ "minCalls must be less than or equal to maxCalls"
4657
+ )
4658
+ ).optional()
4659
+ }).strict();
4660
+ var CraftbookTestHistoryExpectationSchema = z20.object({
4661
+ kind: HistoryEventKindSchema,
4662
+ minEntries: z20.number().int().nonnegative().default(1),
4663
+ maxEntries: z20.number().int().nonnegative().optional(),
4664
+ summaryPattern: z20.string().min(1).optional(),
4665
+ flags: z20.string().optional(),
4666
+ details: z20.record(z20.string(), z20.union([z20.string(), z20.number(), z20.boolean(), z20.null()])).optional()
4667
+ }).strict();
4668
+ var CraftbookTestSuccessSchema = z20.object({
4669
+ summary: z20.string().min(1),
4670
+ deliverables: z20.array(CraftbookTestDeliverableSchema).optional(),
4671
+ checks: z20.array(CraftbookTestCheckSchema).optional(),
4672
+ taskNotes: z20.object({
4673
+ minBytes: z20.number().int().positive().optional(),
4674
+ checks: z20.array(CraftbookTestCheckSchema).optional(),
4675
+ requireCraftbookTask: z20.boolean().optional()
4676
+ }).strict().optional(),
4677
+ taskGraph: z20.object({
4678
+ checks: z20.array(CraftbookTestCheckSchema).optional(),
4679
+ requireCraftbookTask: z20.boolean().optional(),
4680
+ /** Require the matching task to reach a terminal step (or complete). */
4681
+ requireTerminalStep: z20.boolean().optional(),
4682
+ requireDraftRef: z20.boolean().optional(),
4683
+ draft: z20.object({
4684
+ status: z20.enum(["draft", "paused", "active", "complete", "canceled"]).optional(),
4685
+ minDescriptionBytes: z20.number().int().positive().optional(),
4686
+ minOutcomes: z20.number().int().positive().optional(),
4687
+ minSteps: z20.number().int().positive().optional(),
4688
+ requireTerminalVerification: z20.boolean().optional(),
4689
+ requireGatedBuildSteps: z20.boolean().optional()
4690
+ }).strict().optional()
4691
+ }).strict().optional(),
4692
+ /** Assertions evaluated against the live mock server's request log. */
4693
+ mocks: z20.array(CraftbookTestMockExpectationSchema).optional(),
4694
+ /** Assertions evaluated against the project's append-only History log. */
4695
+ history: z20.array(CraftbookTestHistoryExpectationSchema).optional(),
4696
+ /** Workspace fixtures whose final content must equal the seeded bytes exactly. */
4697
+ unchangedFixtures: z20.array(z20.string().min(1)).optional()
4698
+ }).strict();
4699
+ var CraftbookTestRubricSchema = z20.object({
4700
+ artifact: z20.object({
4701
+ /** Workspace or artifact path the judge reads (adapter derives the basename). */
4702
+ path: z20.string().min(1),
4703
+ kind: z20.enum(["html", "markdown", "yaml", "typescript", "json", "text"])
4704
+ }).strict(),
4705
+ axes: z20.array(z20.object({ name: z20.string().min(1), description: z20.string().min(1) }).strict()).min(1),
4706
+ contextNote: z20.string().optional()
4707
+ }).strict();
4708
+ var CraftbookTestSpecSchema = z20.object({
4709
+ schemaVersion: z20.literal(CRAFTBOOK_TEST_SCHEMA_VERSION),
4710
+ title: z20.string().min(1),
4711
+ objective: z20.string().min(1),
4813
4712
  /**
4814
- * Provenance of an applied custom project type (see docs/project-types.md).
4815
- * Distinct from `projectTypeId`, which is the taxonomy id used for craftbook
4816
- * suggestion: a custom type stamps this on adoption and, when it `extends` a
4817
- * taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
4818
- * inherits. Absent on projects created without a custom type.
4713
+ * Task-class taxonomy tags (e.g. `html-game`, `corpus`, `external`).
4714
+ * The single declared source for harness selection and batch
4715
+ * planning replaces the old regex classifiers.
4819
4716
  */
4820
- projectType: ProjectTypeProvenanceSchema.optional(),
4717
+ tags: z20.array(z20.string().min(1)).default([]),
4718
+ /** Kickoff chat message the harness sends. Required — every book runs. */
4719
+ prompt: z20.string().min(1),
4720
+ setup: CraftbookTestSetupSchema,
4721
+ mocks: z20.array(MockServiceSchema).default([]),
4722
+ success: CraftbookTestSuccessSchema,
4723
+ rubric: CraftbookTestRubricSchema,
4724
+ qualityFocus: z20.array(z20.string().min(1)).default([]),
4821
4725
  /**
4822
- * Filesystem ownership boundary. Missing/`user` is ordinary per-account
4823
- * state. `machine-shared` is a grandfathered project mounted from the
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.
4726
+ * Sanctioned escape hatch for experiments carried opaquely, never
4727
+ * interpreted by CI. Promote a field out of here before relying on it.
4826
4728
  */
4827
- storageScope: z21.enum(["user", "machine-shared"]).optional(),
4828
- createdAt: z21.string(),
4829
- updatedAt: z21.string()
4729
+ extensions: z20.record(z20.string(), z20.unknown()).optional()
4730
+ }).strict().superRefine((spec, ctx) => {
4731
+ const mockIds = new Set(spec.mocks.map((m) => m.id));
4732
+ const mockById = new Map(spec.mocks.map((m) => [m.id, m]));
4733
+ for (const [i, expectation] of (spec.success.mocks ?? []).entries()) {
4734
+ if (!mockIds.has(expectation.service)) {
4735
+ ctx.addIssue({
4736
+ code: z20.ZodIssueCode.custom,
4737
+ path: ["success", "mocks", i, "service"],
4738
+ message: `success.mocks[${i}] references unknown mock service "${expectation.service}"`
4739
+ });
4740
+ }
4741
+ if (expectation.requiredTools && expectation.requiredTools.length > 0) {
4742
+ const target = mockById.get(expectation.service);
4743
+ if (target && target.kind !== "mcp") {
4744
+ ctx.addIssue({
4745
+ code: z20.ZodIssueCode.custom,
4746
+ path: ["success", "mocks", i, "requiredTools"],
4747
+ message: `success.mocks[${i}].requiredTools requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
4748
+ });
4749
+ } else if (target?.kind === "mcp") {
4750
+ const declared = new Set(target.tools.map((tool) => tool.name));
4751
+ for (const name of expectation.requiredTools) {
4752
+ if (!declared.has(name)) {
4753
+ ctx.addIssue({
4754
+ code: z20.ZodIssueCode.custom,
4755
+ path: ["success", "mocks", i, "requiredTools"],
4756
+ message: `success.mocks[${i}].requiredTools names undeclared tool "${name}" on mcp service "${expectation.service}"`
4757
+ });
4758
+ }
4759
+ }
4760
+ }
4761
+ }
4762
+ if (expectation.toolCalls && Object.keys(expectation.toolCalls).length > 0) {
4763
+ const target = mockById.get(expectation.service);
4764
+ if (target && target.kind !== "mcp") {
4765
+ ctx.addIssue({
4766
+ code: z20.ZodIssueCode.custom,
4767
+ path: ["success", "mocks", i, "toolCalls"],
4768
+ message: `success.mocks[${i}].toolCalls requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
4769
+ });
4770
+ } else if (target?.kind === "mcp") {
4771
+ const declared = new Set(target.tools.map((tool) => tool.name));
4772
+ for (const name of Object.keys(expectation.toolCalls)) {
4773
+ if (!declared.has(name)) {
4774
+ ctx.addIssue({
4775
+ code: z20.ZodIssueCode.custom,
4776
+ path: ["success", "mocks", i, "toolCalls", name],
4777
+ message: `success.mocks[${i}].toolCalls names undeclared tool "${name}" on mcp service "${expectation.service}"`
4778
+ });
4779
+ }
4780
+ }
4781
+ }
4782
+ }
4783
+ }
4784
+ for (const [i, mock] of spec.mocks.entries()) {
4785
+ if (mock.kind === "http" && mock.credential.name !== `mock.${mock.id}`) {
4786
+ ctx.addIssue({
4787
+ code: z20.ZodIssueCode.custom,
4788
+ path: ["mocks", i, "credential", "name"],
4789
+ message: `http mock "${mock.id}" must use credential name "mock.${mock.id}"`
4790
+ });
4791
+ }
4792
+ }
4793
+ const workspaceFixtures = new Set(
4794
+ spec.setup.files.filter((file) => file.surface === void 0 || file.surface === "workspace").map((file) => file.path)
4795
+ );
4796
+ for (const [i, path] of (spec.success.unchangedFixtures ?? []).entries()) {
4797
+ if (!workspaceFixtures.has(path)) {
4798
+ ctx.addIssue({
4799
+ code: z20.ZodIssueCode.custom,
4800
+ path: ["success", "unchangedFixtures", i],
4801
+ message: `unchanged fixture "${path}" is not a seeded workspace file`
4802
+ });
4803
+ }
4804
+ }
4805
+ for (const [i, expectation] of (spec.success.history ?? []).entries()) {
4806
+ if (expectation.maxEntries !== void 0 && expectation.minEntries > expectation.maxEntries) {
4807
+ ctx.addIssue({
4808
+ code: z20.ZodIssueCode.custom,
4809
+ path: ["success", "history", i],
4810
+ message: "minEntries must be less than or equal to maxEntries"
4811
+ });
4812
+ }
4813
+ }
4830
4814
  });
4831
- function resolveProjectTypeId(project) {
4832
- return project.projectTypeId ?? project.detectedProjectType?.id;
4815
+ function parseCraftbookTestSpec(raw, opts) {
4816
+ const mode = opts?.mode ?? "strict";
4817
+ const candidate = mode === "tolerant" ? deepStripUnknown(raw) : raw;
4818
+ const parsed = CraftbookTestSpecSchema.safeParse(candidate);
4819
+ if (parsed.success) return { ok: true, spec: parsed.data };
4820
+ return {
4821
+ ok: false,
4822
+ errors: parsed.error.issues.map((issue) => {
4823
+ const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
4824
+ return `${path}: ${issue.message}`;
4825
+ })
4826
+ };
4833
4827
  }
4834
- function projectAllowsAmbientWork(project) {
4835
- const status = project.status ?? "active";
4836
- return status === "active";
4828
+ function deepStripUnknown(raw) {
4829
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
4830
+ const record = structuredClone(raw);
4831
+ const version = record.schemaVersion;
4832
+ if (typeof version === "number" && Number.isInteger(version) && version > CRAFTBOOK_TEST_SCHEMA_VERSION) {
4833
+ record.schemaVersion = CRAFTBOOK_TEST_SCHEMA_VERSION;
4834
+ }
4835
+ for (let pass = 0; pass < 16; pass++) {
4836
+ const attempt = CraftbookTestSpecSchema.safeParse(record);
4837
+ if (attempt.success) return record;
4838
+ const unknownKeyIssues = attempt.error.issues.filter(
4839
+ (issue) => issue.code === z20.ZodIssueCode.unrecognized_keys
4840
+ );
4841
+ if (unknownKeyIssues.length === 0) return record;
4842
+ for (const issue of unknownKeyIssues) {
4843
+ const target = resolvePath(record, issue.path);
4844
+ if (target && typeof target === "object" && !Array.isArray(target)) {
4845
+ for (const key of issue.keys) delete target[key];
4846
+ }
4847
+ }
4848
+ }
4849
+ return record;
4850
+ }
4851
+ function resolvePath(root, path) {
4852
+ let node = root;
4853
+ for (const segment of path) {
4854
+ if (node === null || typeof node !== "object") return void 0;
4855
+ node = node[segment];
4856
+ }
4857
+ return node;
4837
4858
  }
4838
- var InstalledPackageSchema = z21.object({
4839
- name: z21.string(),
4840
- version: z21.string()
4859
+
4860
+ // src/schemas/codex-setup.ts
4861
+ import { z as z21 } from "zod";
4862
+ var CodexSetupModelOptionSchema = z21.object({
4863
+ id: z21.string().min(1),
4864
+ label: z21.string().min(1),
4865
+ description: z21.string().optional(),
4866
+ kind: z21.enum(["gezel", "model"]).default("model"),
4867
+ provider: z21.string().min(1),
4868
+ /** Stable gezel id for persona-backed entries. Absent on raw-model entries. */
4869
+ gezelId: z21.string().min(1).optional(),
4870
+ role: z21.string().min(1).optional(),
4871
+ /** Human-readable name of the effective inference model behind a gezel. */
4872
+ modelLabel: z21.string().min(1).optional(),
4873
+ contextWindow: z21.number().int().positive().optional(),
4874
+ supportsReasoning: z21.boolean().optional(),
4875
+ supportsTools: z21.boolean().optional()
4841
4876
  });
4842
- var ProjectDetailSchema = ProjectSchema.extend({
4843
- packages: z21.array(InstalledPackageSchema),
4844
- /** Contents of `documents/about.md` inside the project, if present. */
4845
- about: z21.string().optional(),
4846
- /** Contents of `documents/missionObjectives.md`, if present. */
4847
- missionObjectives: z21.string().optional()
4877
+ var CodexSetupStateSchema = z21.enum([
4878
+ "not-configured",
4879
+ "configured",
4880
+ "update-needed",
4881
+ "conflict",
4882
+ "unavailable"
4883
+ ]);
4884
+ var CodexSetupStatusResponseSchema = z21.object({
4885
+ state: CodexSetupStateSchema,
4886
+ models: z21.array(CodexSetupModelOptionSchema),
4887
+ configuredModel: z21.string().optional(),
4888
+ recommendedModel: z21.string().optional(),
4889
+ reasons: z21.array(z21.string()),
4890
+ message: z21.string().optional(),
4891
+ codexInstalled: z21.boolean(),
4892
+ codexVersion: z21.string().optional(),
4893
+ codexPath: z21.string().optional(),
4894
+ endpointsEnabled: z21.boolean(),
4895
+ profileName: z21.string().min(1),
4896
+ profilePath: z21.string().min(1),
4897
+ launchCommand: z21.string().min(1),
4898
+ bridge: z21.object({
4899
+ baseUrl: z21.string().url(),
4900
+ listening: z21.boolean(),
4901
+ port: z21.number().int().nonnegative()
4902
+ }),
4903
+ canConfigure: z21.boolean(),
4904
+ /** Whether Gezel-owned credential/state material exists and can be safely removed. */
4905
+ canRemove: z21.boolean()
4848
4906
  });
4849
- var ProjectFileEntrySchema = z21.object({
4850
- name: z21.string(),
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()
4907
+ var ConfigureCodexRequestSchema = z21.object({
4908
+ model: z21.string().min(1)
4855
4909
  });
4856
- var ProjectGithubSchema = ProjectGitHubSchema;
4857
4910
 
4858
4911
  // src/schemas/project-local.ts
4859
4912
  import { z as z22 } from "zod";
@@ -5365,6 +5418,8 @@ var UpdateTaskStepRequestSchema = z23.object({
5365
5418
  /** Step automation hooks (single ref or ordered list). `null` detaches. */
5366
5419
  onEnter: ScriptRefListSchema.nullable().optional(),
5367
5420
  onExit: ScriptRefListSchema.nullable().optional(),
5421
+ /** Required file inputs for the step. `null` clears the declaration. */
5422
+ consumes: z23.array(CraftbookStepInputSchema).min(1).nullable().optional(),
5368
5423
  /** Auto-advance contract. `null` clears it. */
5369
5424
  advanceWhen: AdvanceWhenSchema.nullable().optional(),
5370
5425
  /** The end-of-step gate (current or legacy shape). `null` clears it. */
@@ -6294,6 +6349,13 @@ var ConnectorActionSchema = z26.object({
6294
6349
  /** Consent gate this action clears at commit time (e.g. `recipient-allowlist`). */
6295
6350
  consentScope: z26.string()
6296
6351
  });
6352
+ var ConnectorSetupInstructionsSchema = z26.object({
6353
+ title: z26.string().min(1),
6354
+ description: z26.string().min(1).optional(),
6355
+ steps: z26.array(z26.string().min(1)).optional(),
6356
+ url: z26.string().url().optional(),
6357
+ urlLabel: z26.string().min(1).optional()
6358
+ });
6297
6359
  var ConnectorTypeCompositionShape = {
6298
6360
  /** Which driver executes the fetch. */
6299
6361
  driver: ConnectorDriverSchema,
@@ -6301,6 +6363,8 @@ var ConnectorTypeCompositionShape = {
6301
6363
  configSchema: z26.record(z26.string(), z26.unknown()).optional(),
6302
6364
  /** Shape of the credential the binding stores in the SecretStore. */
6303
6365
  secretShape: z26.record(z26.string(), z26.unknown()).optional(),
6366
+ /** Concise setup guidance rendered above the binding form. */
6367
+ setupInstructions: ConnectorSetupInstructionsSchema.optional(),
6304
6368
  /**
6305
6369
  * Driver-specific fetch config: `{adapterId}` (native) | `{server,list,fetch}`
6306
6370
  * (mcp) | `{fetch|cli}` (script) | `{component,action,...}` (spectral).
@@ -6610,6 +6674,15 @@ var ChatModelMlxSourceSchema = z26.object({
6610
6674
  });
6611
6675
  var ChatModelIdentitySchema = IdentityCommonSchema.extend({
6612
6676
  kind: z26.literal("chat-model"),
6677
+ /**
6678
+ * Organization that created the core model weights when it differs from
6679
+ * the catalog maintainer (for example, a quant maintained by its converter).
6680
+ * Omit when `maintainer` already names the maker.
6681
+ */
6682
+ maker: z26.object({
6683
+ name: z26.string().min(1),
6684
+ url: z26.string().url().optional()
6685
+ }).optional(),
6613
6686
  parameterSize: z26.string(),
6614
6687
  supportsTools: z26.boolean(),
6615
6688
  contextWindow: z26.number().int().positive().optional(),
@@ -6707,6 +6780,11 @@ var ChatModelManifestSchema = z26.object({
6707
6780
  name: z26.string(),
6708
6781
  url: z26.string().url().optional()
6709
6782
  }),
6783
+ /** Core-model maker; present when it differs from `maintainer`. */
6784
+ maker: z26.object({
6785
+ name: z26.string().min(1),
6786
+ url: z26.string().url().optional()
6787
+ }).optional(),
6710
6788
  logo: z26.string().optional(),
6711
6789
  license: z26.string().optional(),
6712
6790
  ...LicenseMetaShape,
@@ -7518,6 +7596,18 @@ var ChatSessionSchema = z28.object({
7518
7596
  }),
7519
7597
  /** Snapshot of the gezel's about.md at session creation, for drift warnings. */
7520
7598
  aboutSnapshot: z28.string().optional(),
7599
+ /**
7600
+ * Per-session override of `config.roleBasedNameOnlyMode` ("boring mode").
7601
+ * Stamped at creation when the creating client has a fixed presentation
7602
+ * mode — the TUI always renders role-based labels, so it pins the
7603
+ * sessions it creates to `true`. This keeps the prompt (what the model
7604
+ * is told about other gezels) consistent with what that client shows;
7605
+ * without it the TUI displayed `reviewer:` while the system prompt said
7606
+ * "the voorman is Tomas", and the model naturally leaked names into
7607
+ * prose. Unset = follow the live config flag, which is what desktop
7608
+ * sessions do.
7609
+ */
7610
+ roleBasedNameOnlyMode: z28.boolean().optional(),
7521
7611
  /** Set when the last attempted resume failed — UI surfaces a banner. */
7522
7612
  resumeFailed: z28.boolean().optional(),
7523
7613
  /**
@@ -7746,7 +7836,14 @@ var CreateChatSessionRequestSchema = z28.object({
7746
7836
  projectId: z28.string().optional(),
7747
7837
  taskRef: z28.string().optional(),
7748
7838
  stepId: z28.string().optional(),
7749
- craftbookRef: z28.string().optional()
7839
+ craftbookRef: z28.string().optional(),
7840
+ /**
7841
+ * Pin the session's name-rendering mode instead of following
7842
+ * `config.roleBasedNameOnlyMode`. Passed by clients whose presentation
7843
+ * mode is fixed (the TUI) so prompt-side name rendering matches their
7844
+ * labels. See `ChatSessionSchema.roleBasedNameOnlyMode`.
7845
+ */
7846
+ roleBasedNameOnlyMode: z28.boolean().optional()
7750
7847
  });
7751
7848
  var ListChatSessionsResponseSchema = z28.object({
7752
7849
  sessions: z28.array(ChatSessionSummarySchema)
@@ -8372,13 +8469,26 @@ var GitHubPullFileSchema = z33.object({
8372
8469
  additions: z33.number().int(),
8373
8470
  deletions: z33.number().int(),
8374
8471
  changes: z33.number().int(),
8375
- /** Truncated unified diff hunk, if returned by GitHub. */
8472
+ /** Unified diff hunk, if requested and returned by GitHub. */
8376
8473
  patch: z33.string().optional(),
8474
+ /** Character count before Gezel's local patch budget was applied. */
8475
+ patchChars: z33.number().int().nonnegative().optional(),
8476
+ /** True when Gezel clipped `patch`; callers must request the file/diff directly. */
8477
+ patchTruncated: z33.boolean().optional(),
8377
8478
  /** Prior path when `status === 'renamed'`. */
8378
8479
  previousFilename: z33.string().optional()
8379
8480
  });
8380
8481
  var ListGitHubPullFilesResponseSchema = z33.object({
8381
- files: z33.array(GitHubPullFileSchema)
8482
+ files: z33.array(GitHubPullFileSchema),
8483
+ /** Total files in the PR before an optional path filter. */
8484
+ allFiles: z33.number().int().nonnegative().optional(),
8485
+ /** Total files selected by the optional path filter. */
8486
+ totalFiles: z33.number().int().nonnegative().optional(),
8487
+ offset: z33.number().int().nonnegative().optional(),
8488
+ limit: z33.number().int().positive().optional(),
8489
+ hasMore: z33.boolean().optional(),
8490
+ nextOffset: z33.number().int().nonnegative().optional(),
8491
+ includesPatch: z33.boolean().optional()
8382
8492
  });
8383
8493
  var GitHubPullCommentSchema = z33.object({
8384
8494
  id: z33.number(),
@@ -8395,7 +8505,14 @@ var ListGitHubPullCommentsResponseSchema = z33.object({
8395
8505
  });
8396
8506
  var GitHubPullDiffResponseSchema = z33.object({
8397
8507
  number: z33.number().int(),
8398
- diff: z33.string()
8508
+ diff: z33.string(),
8509
+ /** Exact changed path when this is a file-scoped diff. */
8510
+ path: z33.string().optional(),
8511
+ offset: z33.number().int().nonnegative().optional(),
8512
+ returnedChars: z33.number().int().nonnegative().optional(),
8513
+ totalChars: z33.number().int().nonnegative().optional(),
8514
+ truncated: z33.boolean().optional(),
8515
+ nextOffset: z33.number().int().nonnegative().optional()
8399
8516
  });
8400
8517
  var GitHubCreateCommentRequestSchema = z33.object({
8401
8518
  body: z33.string().min(1)
@@ -10683,6 +10800,13 @@ var GezelConfigSchema = z37.object({
10683
10800
  * Advanced.
10684
10801
  */
10685
10802
  showAdvancedFeatures: z37.boolean().optional(),
10803
+ /**
10804
+ * When `true`, very early work-in-progress surfaces are revealed in the
10805
+ * UI and CLI. This is a discoverability preference, not a security or
10806
+ * service-layer capability boundary. Defaults on in development builds
10807
+ * and off in releases; an explicit user choice always wins.
10808
+ */
10809
+ showWorkInProgressFeatures: z37.boolean().optional(),
10686
10810
  /**
10687
10811
  * Debug-only opt-in: when `true`, the service rewrites every
10688
10812
  * template-derived gezel's `about.md` back to the prose its catalog
@@ -15039,6 +15163,7 @@ export {
15039
15163
  CraftbookSchema,
15040
15164
  CraftbookScriptsSchema,
15041
15165
  CraftbookSpawnSchema,
15166
+ CraftbookStepInputSchema,
15042
15167
  CraftbookStepSchema,
15043
15168
  CraftbookSuggestionSchema,
15044
15169
  CraftbookSummarySchema,