@bendyline/gezel 1.0.1 → 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.
- package/dist/checks/index.js +10 -1
- package/dist/{index-CWMNVAhp.d.ts → index-4Q1N_3oe.d.ts} +1201 -10
- package/dist/index.d.ts +45 -7
- package/dist/index.js +801 -562
- package/dist/markdown/index.d.ts +1 -1
- package/dist/markdown/index.js +48 -3
- package/dist/{report-action-DIz_2QOu.d.ts → report-action-C0KuyeNw.d.ts} +38 -0
- package/dist/schemas/index.d.ts +2 -2
- package/dist/schemas/index.js +684 -559
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -500,13 +500,14 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
|
|
|
500
500
|
label: z3.string().min(1).optional(),
|
|
501
501
|
artifact: z3.boolean().optional()
|
|
502
502
|
}),
|
|
503
|
-
/** `file` must NOT match `pattern` (regex)
|
|
503
|
+
/** `file` must NOT match `pattern` (regex), in the workspace or artifacts drawer. */
|
|
504
504
|
z3.object({
|
|
505
505
|
kind: z3.literal("notContains"),
|
|
506
506
|
file: z3.string().min(1),
|
|
507
507
|
pattern: z3.string().min(1),
|
|
508
508
|
flags: z3.string().optional(),
|
|
509
|
-
label: z3.string().min(1).optional()
|
|
509
|
+
label: z3.string().min(1).optional(),
|
|
510
|
+
artifact: z3.boolean().optional()
|
|
510
511
|
}),
|
|
511
512
|
/**
|
|
512
513
|
* `file` may use high-risk claim wording only when the exact matched
|
|
@@ -649,6 +650,20 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
|
|
|
649
650
|
tools: z3.array(z3.string().min(1)).min(1),
|
|
650
651
|
minSuccessful: z3.number().int().positive().optional()
|
|
651
652
|
}),
|
|
653
|
+
/**
|
|
654
|
+
* A PR-review coverage ledger must name every changed path materialized in
|
|
655
|
+
* a connector corpus. The ledger is workspace JSON (`reviewedFiles` by
|
|
656
|
+
* default); the source records live in the read-only artifacts drawer and
|
|
657
|
+
* carry their authoritative path in frontmatter. This prevents a polished
|
|
658
|
+
* report from passing after only the first context-sized prefix was read.
|
|
659
|
+
*/
|
|
660
|
+
z3.object({
|
|
661
|
+
kind: z3.literal("corpusCoverage"),
|
|
662
|
+
file: z3.string().min(1),
|
|
663
|
+
corpusDir: z3.string().min(1),
|
|
664
|
+
reviewedField: z3.string().min(1).optional(),
|
|
665
|
+
recordField: z3.string().min(1).optional()
|
|
666
|
+
}),
|
|
652
667
|
/**
|
|
653
668
|
* The H1 slide titles in `file` must match the numbered slide headings in
|
|
654
669
|
* `outlineFile`, one-for-one and in order. This makes a locked Markdown
|
|
@@ -921,6 +936,12 @@ var AdvanceWhenSchema = z5.object({
|
|
|
921
936
|
/** Step to activate on the signal. Defaults to `next`; must resolve like `next`. */
|
|
922
937
|
goto: z5.string().optional()
|
|
923
938
|
});
|
|
939
|
+
var CraftbookStepInputSchema = z5.object({
|
|
940
|
+
/** Path relative to the selected drawer's root. */
|
|
941
|
+
file: z5.string().min(1).describe("Path relative to the selected drawer root."),
|
|
942
|
+
/** Read from the artifacts drawer instead of the project workspace. */
|
|
943
|
+
artifact: z5.boolean().optional().describe("True for the artifacts drawer; false/omitted for the project workspace.")
|
|
944
|
+
});
|
|
924
945
|
var ModelTierSchema = z5.enum(MODEL_TIER_ORDER);
|
|
925
946
|
var CraftbookStepSchema = z5.object({
|
|
926
947
|
id: z5.string().min(1),
|
|
@@ -961,6 +982,10 @@ var CraftbookStepSchema = z5.object({
|
|
|
961
982
|
* read the LAST ref's output (legacy routing; prefer gate routing).
|
|
962
983
|
*/
|
|
963
984
|
onExit: ScriptRefListSchema.optional(),
|
|
985
|
+
/** Required file inputs for this step, in the order they should be opened. */
|
|
986
|
+
consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
|
|
987
|
+
"Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
|
|
988
|
+
),
|
|
964
989
|
/** See {@link AdvanceWhenSchema}. */
|
|
965
990
|
advanceWhen: AdvanceWhenSchema.optional(),
|
|
966
991
|
/** The end-of-step decision. See {@link StepGateSchema} (current) / {@link GateSpecSchema} (legacy). */
|
|
@@ -1015,6 +1040,14 @@ function validateCraftbookGraph(cb) {
|
|
|
1015
1040
|
problems.push(`step "${s.id}" advanceWhen.goto "${s.advanceWhen.goto}" missing from steps`);
|
|
1016
1041
|
}
|
|
1017
1042
|
}
|
|
1043
|
+
for (const input of s.consumes ?? []) {
|
|
1044
|
+
if (!input.artifact) continue;
|
|
1045
|
+
if (!/`read_artifact(?:`|\()/.test(s.prompt ?? "")) {
|
|
1046
|
+
problems.push(
|
|
1047
|
+
`step "${s.id}" consumes artifact "${input.file}" but its prompt does not explicitly call \`read_artifact\``
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1018
1051
|
if (s.gate) {
|
|
1019
1052
|
const gate = normalizeStepGate(s.gate);
|
|
1020
1053
|
if (s.terminal && gate.at === "activation") {
|
|
@@ -1307,6 +1340,9 @@ var NewCraftbookStepSchema = z5.object({
|
|
|
1307
1340
|
assignee: TaskAssigneeSchema.optional(),
|
|
1308
1341
|
onEnter: ScriptRefListSchema.optional(),
|
|
1309
1342
|
onExit: ScriptRefListSchema.optional(),
|
|
1343
|
+
consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
|
|
1344
|
+
"Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
|
|
1345
|
+
),
|
|
1310
1346
|
advanceWhen: AdvanceWhenSchema.optional(),
|
|
1311
1347
|
gate: StepGateUnionSchema.optional(),
|
|
1312
1348
|
/** See {@link StepDeliverableSchema} — one field attaches the enforced gate. */
|
|
@@ -1419,6 +1455,7 @@ function resolveSteps(blueprints) {
|
|
|
1419
1455
|
...s.assignee ? { assignee: s.assignee } : {},
|
|
1420
1456
|
...s.onEnter ? { onEnter: s.onEnter } : {},
|
|
1421
1457
|
...s.onExit ? { onExit: s.onExit } : {},
|
|
1458
|
+
...s.consumes && s.consumes.length > 0 ? { consumes: s.consumes } : {},
|
|
1422
1459
|
...s.advanceWhen ? { advanceWhen: s.advanceWhen } : {},
|
|
1423
1460
|
...s.gate ? { gate: s.gate } : {},
|
|
1424
1461
|
...s.next ? { next: s.next } : {},
|
|
@@ -1541,6 +1578,10 @@ function applyStepPatch(step, patch) {
|
|
|
1541
1578
|
delete updated.onExit;
|
|
1542
1579
|
} else updated.onExit = patch.onExit;
|
|
1543
1580
|
}
|
|
1581
|
+
if (patch.consumes !== void 0) {
|
|
1582
|
+
if (patch.consumes === null || patch.consumes.length === 0) delete updated.consumes;
|
|
1583
|
+
else updated.consumes = patch.consumes;
|
|
1584
|
+
}
|
|
1544
1585
|
if (patch.advanceWhen !== void 0) {
|
|
1545
1586
|
if (patch.advanceWhen === null) delete updated.advanceWhen;
|
|
1546
1587
|
else updated.advanceWhen = patch.advanceWhen;
|
|
@@ -1719,7 +1760,7 @@ function craftbookDocFormatFromEnv(value) {
|
|
|
1719
1760
|
}
|
|
1720
1761
|
|
|
1721
1762
|
// src/schemas/craftbook-test.ts
|
|
1722
|
-
import { z as
|
|
1763
|
+
import { z as z20 } from "zod";
|
|
1723
1764
|
|
|
1724
1765
|
// src/schemas/history.ts
|
|
1725
1766
|
import { z as z18 } from "zod";
|
|
@@ -3701,7 +3742,15 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
|
|
|
3701
3742
|
kind: z17.string(),
|
|
3702
3743
|
summary: z17.string(),
|
|
3703
3744
|
at: z17.string(),
|
|
3704
|
-
taskRef: z17.string().optional()
|
|
3745
|
+
taskRef: z17.string().optional(),
|
|
3746
|
+
/**
|
|
3747
|
+
* Gezel responsible for the event, when History recorded one. Kept
|
|
3748
|
+
* separate from the human-readable summary so fixed-presentation
|
|
3749
|
+
* clients (notably the role-name-only CLI) can render the actor using
|
|
3750
|
+
* their own naming mode instead of leaking the friendly name embedded
|
|
3751
|
+
* in the audit prose.
|
|
3752
|
+
*/
|
|
3753
|
+
gezelId: z17.string().optional()
|
|
3705
3754
|
}),
|
|
3706
3755
|
/**
|
|
3707
3756
|
* Emitted when a gezel crosses a growth level threshold and a pending
|
|
@@ -4242,444 +4291,9 @@ var ListHistoryResponseSchema = z18.object({
|
|
|
4242
4291
|
entries: z18.array(HistoryEntrySchema)
|
|
4243
4292
|
});
|
|
4244
4293
|
|
|
4245
|
-
// src/schemas/craftbook-test.ts
|
|
4246
|
-
var CRAFTBOOK_TEST_SCHEMA_VERSION = 1;
|
|
4247
|
-
var CRAFTBOOK_TEST_FILENAME = "test.json";
|
|
4248
|
-
var PrometheusAlertsCheckSchema = z19.object({
|
|
4249
|
-
kind: z19.literal("prometheusAlerts"),
|
|
4250
|
-
file: z19.string().min(1),
|
|
4251
|
-
minRules: z19.number().int().positive().optional(),
|
|
4252
|
-
maxPageAlerts: z19.number().int().nonnegative().optional(),
|
|
4253
|
-
allowedSeverities: z19.array(z19.string().min(1)).optional(),
|
|
4254
|
-
requiredServices: z19.array(z19.string().min(1)).optional(),
|
|
4255
|
-
requiredRunbookUrls: z19.array(z19.string().min(1)).optional()
|
|
4256
|
-
}).strict();
|
|
4257
|
-
var NodeScriptPassesCheckSchema = z19.object({
|
|
4258
|
-
kind: z19.literal("nodeScriptPasses"),
|
|
4259
|
-
script: z19.string().min(1),
|
|
4260
|
-
timeoutMs: z19.number().int().positive().optional(),
|
|
4261
|
-
requiredOutput: z19.array(
|
|
4262
|
-
z19.object({
|
|
4263
|
-
pattern: z19.string().min(1),
|
|
4264
|
-
flags: z19.string().optional(),
|
|
4265
|
-
label: z19.string().optional()
|
|
4266
|
-
}).strict()
|
|
4267
|
-
).optional()
|
|
4268
|
-
}).strict();
|
|
4269
|
-
var BinaryDocumentCheckSchema = z19.object({
|
|
4270
|
-
kind: z19.literal("binaryDocument"),
|
|
4271
|
-
file: z19.string().min(1),
|
|
4272
|
-
/** Look in the artifacts drawer instead of the workspace. */
|
|
4273
|
-
artifact: z19.boolean().optional(),
|
|
4274
|
-
/** Floor on the container's byte length; defaults to 1000. */
|
|
4275
|
-
minBytes: z19.number().int().positive().optional()
|
|
4276
|
-
}).strict();
|
|
4277
|
-
var CraftbookTestCheckSchema = z19.union([
|
|
4278
|
-
GateCheckSchema,
|
|
4279
|
-
PrometheusAlertsCheckSchema,
|
|
4280
|
-
NodeScriptPassesCheckSchema,
|
|
4281
|
-
BinaryDocumentCheckSchema
|
|
4282
|
-
]);
|
|
4283
|
-
var MockServiceIdSchema = z19.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, "mock service ids are lowercase kebab-case");
|
|
4284
|
-
var MockToolsetIdSchema = z19.string().min(1).regex(
|
|
4285
|
-
/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,
|
|
4286
|
-
"mock toolset ids are lowercase catalog ids or scoped npm-style ids"
|
|
4287
|
-
);
|
|
4288
|
-
var MockHttpRouteSchema = z19.object({
|
|
4289
|
-
method: z19.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
4290
|
-
/** Exact path, `:param` segments, or a trailing `*` wildcard. */
|
|
4291
|
-
path: z19.string().min(1),
|
|
4292
|
-
status: z19.number().int().min(100).max(599).default(200),
|
|
4293
|
-
headers: z19.record(z19.string(), z19.string()).optional(),
|
|
4294
|
-
/** String bodies are served verbatim; anything else is JSON-encoded. */
|
|
4295
|
-
body: z19.unknown(),
|
|
4296
|
-
latencyMs: z19.number().int().nonnegative().optional()
|
|
4297
|
-
}).strict();
|
|
4298
|
-
var MockServiceSchema = z19.discriminatedUnion("kind", [
|
|
4299
|
-
z19.object({
|
|
4300
|
-
kind: z19.literal("http"),
|
|
4301
|
-
id: MockServiceIdSchema,
|
|
4302
|
-
description: z19.string().min(1),
|
|
4303
|
-
/**
|
|
4304
|
-
* v1 mock HTTP is reachable ONLY through the `http.authed`
|
|
4305
|
-
* credential rail (anonymous script/browser HTTP hard-rejects
|
|
4306
|
-
* loopback), so a credential is required. The harness seeds it and
|
|
4307
|
-
* grants the mock's exact origin on the trial project.
|
|
4308
|
-
*/
|
|
4309
|
-
credential: z19.object({
|
|
4310
|
-
name: z19.string().regex(/^mock\.[a-z0-9][a-z0-9.-]*$/, "mock credentials are named mock.<service-id>"),
|
|
4311
|
-
authScheme: z19.enum(["bearer", "basic"]).optional()
|
|
4312
|
-
}).strict(),
|
|
4313
|
-
routes: z19.array(MockHttpRouteSchema).min(1)
|
|
4314
|
-
}).strict(),
|
|
4315
|
-
z19.object({
|
|
4316
|
-
kind: z19.literal("webhook"),
|
|
4317
|
-
id: MockServiceIdSchema,
|
|
4318
|
-
description: z19.string().min(1),
|
|
4319
|
-
/** Receiver path; defaults to `/webhook` when omitted. */
|
|
4320
|
-
path: z19.string().min(1).optional()
|
|
4321
|
-
}).strict(),
|
|
4322
|
-
z19.object({
|
|
4323
|
-
kind: z19.literal("cli"),
|
|
4324
|
-
id: MockServiceIdSchema,
|
|
4325
|
-
description: z19.string().min(1),
|
|
4326
|
-
/** Fake-CLI shim seeded as a workspace file (today's dry-run pattern). */
|
|
4327
|
-
shim: z19.object({ path: z19.string().min(1), content: z19.string() }).strict()
|
|
4328
|
-
}).strict(),
|
|
4329
|
-
z19.object({
|
|
4330
|
-
kind: z19.literal("mcp"),
|
|
4331
|
-
id: MockServiceIdSchema,
|
|
4332
|
-
description: z19.string().min(1),
|
|
4333
|
-
/** Override the local-catalog id when the mock replaces a real dependency. */
|
|
4334
|
-
toolsetId: MockToolsetIdSchema.optional(),
|
|
4335
|
-
/**
|
|
4336
|
-
* Served live by the eval mock rail: each declared tool becomes a
|
|
4337
|
-
* real tool on a per-trial Streamable-HTTP MCP endpoint, installed
|
|
4338
|
-
* into the trial via a local-catalog `mock-mcp-<id>` toolset.
|
|
4339
|
-
* `resultTemplate` is JSON-encoded as the tool result text
|
|
4340
|
-
* (default `{"ok":true}` when absent).
|
|
4341
|
-
*/
|
|
4342
|
-
tools: z19.array(
|
|
4343
|
-
z19.object({
|
|
4344
|
-
name: z19.string().min(1),
|
|
4345
|
-
description: z19.string().min(1),
|
|
4346
|
-
resultTemplate: z19.unknown().optional(),
|
|
4347
|
-
/** Deterministic stateful responses, consumed in call order; the last repeats. */
|
|
4348
|
-
resultSequence: z19.array(z19.unknown()).min(1).optional(),
|
|
4349
|
-
/**
|
|
4350
|
-
* Deterministic eval-only file materialization after this
|
|
4351
|
-
* tool call. The fixture must match the container the
|
|
4352
|
-
* deliverable's extension claims — a `minimal-pptx` written
|
|
4353
|
-
* to a `.docx` path now fails the `binaryDocument` check on
|
|
4354
|
-
* content type rather than sliding through a byte floor.
|
|
4355
|
-
*/
|
|
4356
|
-
writeFixture: z19.object({
|
|
4357
|
-
surface: z19.enum(["workspace", "artifact"]),
|
|
4358
|
-
pathArgument: z19.string().min(1),
|
|
4359
|
-
fixture: z19.enum(["minimal-pptx", "minimal-docx", "minimal-pdf", "minimal-png"])
|
|
4360
|
-
}).strict().optional()
|
|
4361
|
-
}).strict()
|
|
4362
|
-
).min(1)
|
|
4363
|
-
}).strict()
|
|
4364
|
-
]);
|
|
4365
|
-
var CraftbookTestFixtureFileSchema = z19.object({
|
|
4366
|
-
path: z19.string().min(1),
|
|
4367
|
-
content: z19.string(),
|
|
4368
|
-
/** Defaults to `workspace`; `harness` never enters the model-visible project. */
|
|
4369
|
-
surface: z19.enum(["workspace", "artifact", "harness"]).optional(),
|
|
4370
|
-
/**
|
|
4371
|
-
* Whether the fixture is presented to the model as source material.
|
|
4372
|
-
* Defaults to true; false still seeds the file for browsers and graders.
|
|
4373
|
-
*/
|
|
4374
|
-
modelInput: z19.boolean().optional()
|
|
4375
|
-
}).strict();
|
|
4376
|
-
var CraftbookTestWorkerSchema = z19.object({
|
|
4377
|
-
name: z19.string().min(1),
|
|
4378
|
-
role: z19.string().min(1),
|
|
4379
|
-
description: z19.string().optional(),
|
|
4380
|
-
about: z19.string().optional()
|
|
4381
|
-
}).strict();
|
|
4382
|
-
var CraftbookTestSetupSchema = z19.object({
|
|
4383
|
-
projectName: z19.string().min(1),
|
|
4384
|
-
about: z19.string().optional(),
|
|
4385
|
-
missionObjectives: z19.string().optional(),
|
|
4386
|
-
files: z19.array(CraftbookTestFixtureFileSchema).default([]),
|
|
4387
|
-
/** Exact values supplied to the catalog craftbook's `paramSchema`. */
|
|
4388
|
-
craftbookParams: z19.record(z19.string(), z19.string()).optional(),
|
|
4389
|
-
/**
|
|
4390
|
-
* Direct execution target. When present the harness seeds this gezel
|
|
4391
|
-
* and sends the kickoff straight to it (measuring whether the book
|
|
4392
|
-
* guides the work); absent → the Meester routes.
|
|
4393
|
-
*/
|
|
4394
|
-
worker: CraftbookTestWorkerSchema.optional()
|
|
4395
|
-
}).strict();
|
|
4396
|
-
var CraftbookTestDeliverableSchema = z19.object({
|
|
4397
|
-
path: z19.string().min(1),
|
|
4398
|
-
kind: DeliverableKindSchema,
|
|
4399
|
-
minBytes: z19.number().int().positive().optional(),
|
|
4400
|
-
checks: z19.array(CraftbookTestCheckSchema).optional()
|
|
4401
|
-
}).strict();
|
|
4402
|
-
var CraftbookTestMockExpectationSchema = z19.object({
|
|
4403
|
-
/** Mock service id from `mocks[]`. */
|
|
4404
|
-
service: MockServiceIdSchema,
|
|
4405
|
-
minRequests: z19.number().int().positive().optional(),
|
|
4406
|
-
/** Regex sources matched against logged request paths. */
|
|
4407
|
-
requiredPaths: z19.array(z19.string().min(1)).optional(),
|
|
4408
|
-
forbiddenPaths: z19.array(z19.string().min(1)).optional(),
|
|
4409
|
-
/**
|
|
4410
|
-
* Exact MCP tool names that must each have been called at least once
|
|
4411
|
-
* on this service (`kind: 'mcp'` only). Exact names, not regexes —
|
|
4412
|
-
* the tool roster is fully declared in the same file, so a pattern
|
|
4413
|
-
* buys nothing and invites drift. Cross-checked against the mock's
|
|
4414
|
-
* declared `tools[]` at parse time.
|
|
4415
|
-
*/
|
|
4416
|
-
requiredTools: z19.array(z19.string().min(1)).optional(),
|
|
4417
|
-
/** Per-MCP-tool call budgets for repeated journeys or retries. */
|
|
4418
|
-
toolCalls: z19.record(
|
|
4419
|
-
z19.string().min(1),
|
|
4420
|
-
z19.object({
|
|
4421
|
-
minCalls: z19.number().int().nonnegative().default(1),
|
|
4422
|
-
maxCalls: z19.number().int().nonnegative().optional()
|
|
4423
|
-
}).strict().refine(
|
|
4424
|
-
(value) => value.maxCalls === void 0 || value.minCalls <= value.maxCalls,
|
|
4425
|
-
"minCalls must be less than or equal to maxCalls"
|
|
4426
|
-
)
|
|
4427
|
-
).optional()
|
|
4428
|
-
}).strict();
|
|
4429
|
-
var CraftbookTestHistoryExpectationSchema = z19.object({
|
|
4430
|
-
kind: HistoryEventKindSchema,
|
|
4431
|
-
minEntries: z19.number().int().nonnegative().default(1),
|
|
4432
|
-
maxEntries: z19.number().int().nonnegative().optional(),
|
|
4433
|
-
summaryPattern: z19.string().min(1).optional(),
|
|
4434
|
-
flags: z19.string().optional(),
|
|
4435
|
-
details: z19.record(z19.string(), z19.union([z19.string(), z19.number(), z19.boolean(), z19.null()])).optional()
|
|
4436
|
-
}).strict();
|
|
4437
|
-
var CraftbookTestSuccessSchema = z19.object({
|
|
4438
|
-
summary: z19.string().min(1),
|
|
4439
|
-
deliverables: z19.array(CraftbookTestDeliverableSchema).optional(),
|
|
4440
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4441
|
-
taskNotes: z19.object({
|
|
4442
|
-
minBytes: z19.number().int().positive().optional(),
|
|
4443
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4444
|
-
requireCraftbookTask: z19.boolean().optional()
|
|
4445
|
-
}).strict().optional(),
|
|
4446
|
-
taskGraph: z19.object({
|
|
4447
|
-
checks: z19.array(CraftbookTestCheckSchema).optional(),
|
|
4448
|
-
requireCraftbookTask: z19.boolean().optional(),
|
|
4449
|
-
/** Require the matching task to reach a terminal step (or complete). */
|
|
4450
|
-
requireTerminalStep: z19.boolean().optional(),
|
|
4451
|
-
requireDraftRef: z19.boolean().optional(),
|
|
4452
|
-
draft: z19.object({
|
|
4453
|
-
status: z19.enum(["draft", "paused", "active", "complete", "canceled"]).optional(),
|
|
4454
|
-
minDescriptionBytes: z19.number().int().positive().optional(),
|
|
4455
|
-
minOutcomes: z19.number().int().positive().optional(),
|
|
4456
|
-
minSteps: z19.number().int().positive().optional(),
|
|
4457
|
-
requireTerminalVerification: z19.boolean().optional(),
|
|
4458
|
-
requireGatedBuildSteps: z19.boolean().optional()
|
|
4459
|
-
}).strict().optional()
|
|
4460
|
-
}).strict().optional(),
|
|
4461
|
-
/** Assertions evaluated against the live mock server's request log. */
|
|
4462
|
-
mocks: z19.array(CraftbookTestMockExpectationSchema).optional(),
|
|
4463
|
-
/** Assertions evaluated against the project's append-only History log. */
|
|
4464
|
-
history: z19.array(CraftbookTestHistoryExpectationSchema).optional(),
|
|
4465
|
-
/** Workspace fixtures whose final content must equal the seeded bytes exactly. */
|
|
4466
|
-
unchangedFixtures: z19.array(z19.string().min(1)).optional()
|
|
4467
|
-
}).strict();
|
|
4468
|
-
var CraftbookTestRubricSchema = z19.object({
|
|
4469
|
-
artifact: z19.object({
|
|
4470
|
-
/** Workspace or artifact path the judge reads (adapter derives the basename). */
|
|
4471
|
-
path: z19.string().min(1),
|
|
4472
|
-
kind: z19.enum(["html", "markdown", "yaml", "typescript", "json", "text"])
|
|
4473
|
-
}).strict(),
|
|
4474
|
-
axes: z19.array(z19.object({ name: z19.string().min(1), description: z19.string().min(1) }).strict()).min(1),
|
|
4475
|
-
contextNote: z19.string().optional()
|
|
4476
|
-
}).strict();
|
|
4477
|
-
var CraftbookTestSpecSchema = z19.object({
|
|
4478
|
-
schemaVersion: z19.literal(CRAFTBOOK_TEST_SCHEMA_VERSION),
|
|
4479
|
-
title: z19.string().min(1),
|
|
4480
|
-
objective: z19.string().min(1),
|
|
4481
|
-
/**
|
|
4482
|
-
* Task-class taxonomy tags (e.g. `html-game`, `corpus`, `external`).
|
|
4483
|
-
* The single declared source for harness selection and batch
|
|
4484
|
-
* planning — replaces the old regex classifiers.
|
|
4485
|
-
*/
|
|
4486
|
-
tags: z19.array(z19.string().min(1)).default([]),
|
|
4487
|
-
/** Kickoff chat message the harness sends. Required — every book runs. */
|
|
4488
|
-
prompt: z19.string().min(1),
|
|
4489
|
-
setup: CraftbookTestSetupSchema,
|
|
4490
|
-
mocks: z19.array(MockServiceSchema).default([]),
|
|
4491
|
-
success: CraftbookTestSuccessSchema,
|
|
4492
|
-
rubric: CraftbookTestRubricSchema,
|
|
4493
|
-
qualityFocus: z19.array(z19.string().min(1)).default([]),
|
|
4494
|
-
/**
|
|
4495
|
-
* Sanctioned escape hatch for experiments — carried opaquely, never
|
|
4496
|
-
* interpreted by CI. Promote a field out of here before relying on it.
|
|
4497
|
-
*/
|
|
4498
|
-
extensions: z19.record(z19.string(), z19.unknown()).optional()
|
|
4499
|
-
}).strict().superRefine((spec, ctx) => {
|
|
4500
|
-
const mockIds = new Set(spec.mocks.map((m) => m.id));
|
|
4501
|
-
const mockById = new Map(spec.mocks.map((m) => [m.id, m]));
|
|
4502
|
-
for (const [i, expectation] of (spec.success.mocks ?? []).entries()) {
|
|
4503
|
-
if (!mockIds.has(expectation.service)) {
|
|
4504
|
-
ctx.addIssue({
|
|
4505
|
-
code: z19.ZodIssueCode.custom,
|
|
4506
|
-
path: ["success", "mocks", i, "service"],
|
|
4507
|
-
message: `success.mocks[${i}] references unknown mock service "${expectation.service}"`
|
|
4508
|
-
});
|
|
4509
|
-
}
|
|
4510
|
-
if (expectation.requiredTools && expectation.requiredTools.length > 0) {
|
|
4511
|
-
const target = mockById.get(expectation.service);
|
|
4512
|
-
if (target && target.kind !== "mcp") {
|
|
4513
|
-
ctx.addIssue({
|
|
4514
|
-
code: z19.ZodIssueCode.custom,
|
|
4515
|
-
path: ["success", "mocks", i, "requiredTools"],
|
|
4516
|
-
message: `success.mocks[${i}].requiredTools requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4517
|
-
});
|
|
4518
|
-
} else if (target?.kind === "mcp") {
|
|
4519
|
-
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4520
|
-
for (const name of expectation.requiredTools) {
|
|
4521
|
-
if (!declared.has(name)) {
|
|
4522
|
-
ctx.addIssue({
|
|
4523
|
-
code: z19.ZodIssueCode.custom,
|
|
4524
|
-
path: ["success", "mocks", i, "requiredTools"],
|
|
4525
|
-
message: `success.mocks[${i}].requiredTools names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4526
|
-
});
|
|
4527
|
-
}
|
|
4528
|
-
}
|
|
4529
|
-
}
|
|
4530
|
-
}
|
|
4531
|
-
if (expectation.toolCalls && Object.keys(expectation.toolCalls).length > 0) {
|
|
4532
|
-
const target = mockById.get(expectation.service);
|
|
4533
|
-
if (target && target.kind !== "mcp") {
|
|
4534
|
-
ctx.addIssue({
|
|
4535
|
-
code: z19.ZodIssueCode.custom,
|
|
4536
|
-
path: ["success", "mocks", i, "toolCalls"],
|
|
4537
|
-
message: `success.mocks[${i}].toolCalls requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4538
|
-
});
|
|
4539
|
-
} else if (target?.kind === "mcp") {
|
|
4540
|
-
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4541
|
-
for (const name of Object.keys(expectation.toolCalls)) {
|
|
4542
|
-
if (!declared.has(name)) {
|
|
4543
|
-
ctx.addIssue({
|
|
4544
|
-
code: z19.ZodIssueCode.custom,
|
|
4545
|
-
path: ["success", "mocks", i, "toolCalls", name],
|
|
4546
|
-
message: `success.mocks[${i}].toolCalls names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4547
|
-
});
|
|
4548
|
-
}
|
|
4549
|
-
}
|
|
4550
|
-
}
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4553
|
-
for (const [i, mock] of spec.mocks.entries()) {
|
|
4554
|
-
if (mock.kind === "http" && mock.credential.name !== `mock.${mock.id}`) {
|
|
4555
|
-
ctx.addIssue({
|
|
4556
|
-
code: z19.ZodIssueCode.custom,
|
|
4557
|
-
path: ["mocks", i, "credential", "name"],
|
|
4558
|
-
message: `http mock "${mock.id}" must use credential name "mock.${mock.id}"`
|
|
4559
|
-
});
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
const workspaceFixtures = new Set(
|
|
4563
|
-
spec.setup.files.filter((file) => file.surface === void 0 || file.surface === "workspace").map((file) => file.path)
|
|
4564
|
-
);
|
|
4565
|
-
for (const [i, path] of (spec.success.unchangedFixtures ?? []).entries()) {
|
|
4566
|
-
if (!workspaceFixtures.has(path)) {
|
|
4567
|
-
ctx.addIssue({
|
|
4568
|
-
code: z19.ZodIssueCode.custom,
|
|
4569
|
-
path: ["success", "unchangedFixtures", i],
|
|
4570
|
-
message: `unchanged fixture "${path}" is not a seeded workspace file`
|
|
4571
|
-
});
|
|
4572
|
-
}
|
|
4573
|
-
}
|
|
4574
|
-
for (const [i, expectation] of (spec.success.history ?? []).entries()) {
|
|
4575
|
-
if (expectation.maxEntries !== void 0 && expectation.minEntries > expectation.maxEntries) {
|
|
4576
|
-
ctx.addIssue({
|
|
4577
|
-
code: z19.ZodIssueCode.custom,
|
|
4578
|
-
path: ["success", "history", i],
|
|
4579
|
-
message: "minEntries must be less than or equal to maxEntries"
|
|
4580
|
-
});
|
|
4581
|
-
}
|
|
4582
|
-
}
|
|
4583
|
-
});
|
|
4584
|
-
function parseCraftbookTestSpec(raw, opts) {
|
|
4585
|
-
const mode = opts?.mode ?? "strict";
|
|
4586
|
-
const candidate = mode === "tolerant" ? deepStripUnknown(raw) : raw;
|
|
4587
|
-
const parsed = CraftbookTestSpecSchema.safeParse(candidate);
|
|
4588
|
-
if (parsed.success) return { ok: true, spec: parsed.data };
|
|
4589
|
-
return {
|
|
4590
|
-
ok: false,
|
|
4591
|
-
errors: parsed.error.issues.map((issue) => {
|
|
4592
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
|
|
4593
|
-
return `${path}: ${issue.message}`;
|
|
4594
|
-
})
|
|
4595
|
-
};
|
|
4596
|
-
}
|
|
4597
|
-
function deepStripUnknown(raw) {
|
|
4598
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
4599
|
-
const record = structuredClone(raw);
|
|
4600
|
-
const version = record.schemaVersion;
|
|
4601
|
-
if (typeof version === "number" && Number.isInteger(version) && version > CRAFTBOOK_TEST_SCHEMA_VERSION) {
|
|
4602
|
-
record.schemaVersion = CRAFTBOOK_TEST_SCHEMA_VERSION;
|
|
4603
|
-
}
|
|
4604
|
-
for (let pass = 0; pass < 16; pass++) {
|
|
4605
|
-
const attempt = CraftbookTestSpecSchema.safeParse(record);
|
|
4606
|
-
if (attempt.success) return record;
|
|
4607
|
-
const unknownKeyIssues = attempt.error.issues.filter(
|
|
4608
|
-
(issue) => issue.code === z19.ZodIssueCode.unrecognized_keys
|
|
4609
|
-
);
|
|
4610
|
-
if (unknownKeyIssues.length === 0) return record;
|
|
4611
|
-
for (const issue of unknownKeyIssues) {
|
|
4612
|
-
const target = resolvePath(record, issue.path);
|
|
4613
|
-
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
4614
|
-
for (const key of issue.keys) delete target[key];
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
}
|
|
4618
|
-
return record;
|
|
4619
|
-
}
|
|
4620
|
-
function resolvePath(root, path) {
|
|
4621
|
-
let node = root;
|
|
4622
|
-
for (const segment of path) {
|
|
4623
|
-
if (node === null || typeof node !== "object") return void 0;
|
|
4624
|
-
node = node[segment];
|
|
4625
|
-
}
|
|
4626
|
-
return node;
|
|
4627
|
-
}
|
|
4628
|
-
|
|
4629
|
-
// src/schemas/codex-setup.ts
|
|
4630
|
-
import { z as z20 } from "zod";
|
|
4631
|
-
var CodexSetupModelOptionSchema = z20.object({
|
|
4632
|
-
id: z20.string().min(1),
|
|
4633
|
-
label: z20.string().min(1),
|
|
4634
|
-
description: z20.string().optional(),
|
|
4635
|
-
kind: z20.enum(["gezel", "model"]).default("model"),
|
|
4636
|
-
provider: z20.string().min(1),
|
|
4637
|
-
/** Stable gezel id for persona-backed entries. Absent on raw-model entries. */
|
|
4638
|
-
gezelId: z20.string().min(1).optional(),
|
|
4639
|
-
role: z20.string().min(1).optional(),
|
|
4640
|
-
/** Human-readable name of the effective inference model behind a gezel. */
|
|
4641
|
-
modelLabel: z20.string().min(1).optional(),
|
|
4642
|
-
contextWindow: z20.number().int().positive().optional(),
|
|
4643
|
-
supportsReasoning: z20.boolean().optional(),
|
|
4644
|
-
supportsTools: z20.boolean().optional()
|
|
4645
|
-
});
|
|
4646
|
-
var CodexSetupStateSchema = z20.enum([
|
|
4647
|
-
"not-configured",
|
|
4648
|
-
"configured",
|
|
4649
|
-
"update-needed",
|
|
4650
|
-
"conflict",
|
|
4651
|
-
"unavailable"
|
|
4652
|
-
]);
|
|
4653
|
-
var CodexSetupStatusResponseSchema = z20.object({
|
|
4654
|
-
state: CodexSetupStateSchema,
|
|
4655
|
-
models: z20.array(CodexSetupModelOptionSchema),
|
|
4656
|
-
configuredModel: z20.string().optional(),
|
|
4657
|
-
recommendedModel: z20.string().optional(),
|
|
4658
|
-
reasons: z20.array(z20.string()),
|
|
4659
|
-
message: z20.string().optional(),
|
|
4660
|
-
codexInstalled: z20.boolean(),
|
|
4661
|
-
codexVersion: z20.string().optional(),
|
|
4662
|
-
codexPath: z20.string().optional(),
|
|
4663
|
-
endpointsEnabled: z20.boolean(),
|
|
4664
|
-
profileName: z20.string().min(1),
|
|
4665
|
-
profilePath: z20.string().min(1),
|
|
4666
|
-
launchCommand: z20.string().min(1),
|
|
4667
|
-
bridge: z20.object({
|
|
4668
|
-
baseUrl: z20.string().url(),
|
|
4669
|
-
listening: z20.boolean(),
|
|
4670
|
-
port: z20.number().int().nonnegative()
|
|
4671
|
-
}),
|
|
4672
|
-
canConfigure: z20.boolean(),
|
|
4673
|
-
/** Whether Gezel-owned credential/state material exists and can be safely removed. */
|
|
4674
|
-
canRemove: z20.boolean()
|
|
4675
|
-
});
|
|
4676
|
-
var ConfigureCodexRequestSchema = z20.object({
|
|
4677
|
-
model: z20.string().min(1)
|
|
4678
|
-
});
|
|
4679
|
-
|
|
4680
4294
|
// src/schemas/project.ts
|
|
4681
|
-
import { z as
|
|
4682
|
-
var HttpsOriginSchema =
|
|
4295
|
+
import { z as z19 } from "zod";
|
|
4296
|
+
var HttpsOriginSchema = z19.string().url().refine(
|
|
4683
4297
|
(value) => {
|
|
4684
4298
|
try {
|
|
4685
4299
|
const url = new URL(value);
|
|
@@ -4690,7 +4304,7 @@ var HttpsOriginSchema = z21.string().url().refine(
|
|
|
4690
4304
|
},
|
|
4691
4305
|
{ message: "must be an exact HTTPS origin (for example https://api.example.com)" }
|
|
4692
4306
|
);
|
|
4693
|
-
var ProjectGitHubSchema =
|
|
4307
|
+
var ProjectGitHubSchema = z19.object({
|
|
4694
4308
|
// Accept any non-empty string. Git URLs come in many forms beyond
|
|
4695
4309
|
// `https://`: ssh shorthand (`git@github.com:owner/repo`), local
|
|
4696
4310
|
// filesystem paths (`/path/to/bare.git`), other-host references.
|
|
@@ -4698,89 +4312,89 @@ var ProjectGitHubSchema = z21.object({
|
|
|
4698
4312
|
// and broke Phase-3 worktree tests that use a local bare repo as
|
|
4699
4313
|
// the upstream. Higher layers (the github sync code) parse the URL
|
|
4700
4314
|
// and reject anything that doesn't shape up as a clonable ref.
|
|
4701
|
-
url:
|
|
4702
|
-
branch:
|
|
4315
|
+
url: z19.string().min(1),
|
|
4316
|
+
branch: z19.string().optional(),
|
|
4703
4317
|
/** Resolved absolute path to the working tree. Managed by the service. */
|
|
4704
|
-
checkoutDir:
|
|
4705
|
-
lastSyncedAt:
|
|
4318
|
+
checkoutDir: z19.string().optional(),
|
|
4319
|
+
lastSyncedAt: z19.string().optional(),
|
|
4706
4320
|
/** Repo default branch (e.g. "main"), detected lazily and cached. Managed by the service. */
|
|
4707
|
-
defaultBranch:
|
|
4321
|
+
defaultBranch: z19.string().optional()
|
|
4708
4322
|
});
|
|
4709
|
-
var ProjectConnectorBindingSchema =
|
|
4323
|
+
var ProjectConnectorBindingSchema = z19.object({
|
|
4710
4324
|
/** Stable binding id; also the SecretStore `fieldId` + corpus-slug seed. */
|
|
4711
|
-
id:
|
|
4325
|
+
id: z19.string().min(1),
|
|
4712
4326
|
/** The connector-type catalog id, e.g. `mail-gmail`, `linear-issues`. */
|
|
4713
|
-
type:
|
|
4327
|
+
type: z19.string().min(1),
|
|
4714
4328
|
/** Catalog source the type resolved from (provenance/pin). */
|
|
4715
|
-
sourceId:
|
|
4329
|
+
sourceId: z19.string().optional(),
|
|
4716
4330
|
/** Pinned connector-type version. */
|
|
4717
|
-
version:
|
|
4718
|
-
displayName:
|
|
4331
|
+
version: z19.string().optional(),
|
|
4332
|
+
displayName: z19.string().optional(),
|
|
4719
4333
|
/**
|
|
4720
4334
|
* Artifact-relative corpus root (`data/<corpusName>`), resolved once at bind
|
|
4721
4335
|
* time and never recomputed — renaming a binding must not strand its corpus.
|
|
4722
4336
|
*/
|
|
4723
|
-
corpusDir:
|
|
4337
|
+
corpusDir: z19.string().optional(),
|
|
4724
4338
|
/** Per-binding config, validated at bind time against the type's `configSchema`. */
|
|
4725
|
-
config:
|
|
4339
|
+
config: z19.record(z19.string(), z19.unknown()).default({}),
|
|
4726
4340
|
/** Opaque, adapter-shaped incremental-sync cursor. Persisted so resync resumes. */
|
|
4727
|
-
cursor:
|
|
4341
|
+
cursor: z19.unknown().optional(),
|
|
4728
4342
|
/** Pause syncing without unbinding. */
|
|
4729
|
-
disabled:
|
|
4730
|
-
lastSyncedAt:
|
|
4343
|
+
disabled: z19.boolean().optional(),
|
|
4344
|
+
lastSyncedAt: z19.string().optional(),
|
|
4731
4345
|
/** Last sync error, surfaced in the UI; cleared on the next success. */
|
|
4732
|
-
lastError:
|
|
4346
|
+
lastError: z19.string().optional()
|
|
4733
4347
|
});
|
|
4734
|
-
var ProjectNudgeConfigSchema =
|
|
4735
|
-
enabled:
|
|
4736
|
-
rapidIntervalMs:
|
|
4737
|
-
slowIntervalMs:
|
|
4738
|
-
recentActivityWindowMs:
|
|
4739
|
-
rapidAttemptsBeforeBackoff:
|
|
4348
|
+
var ProjectNudgeConfigSchema = z19.object({
|
|
4349
|
+
enabled: z19.boolean().optional(),
|
|
4350
|
+
rapidIntervalMs: z19.number().int().positive().optional(),
|
|
4351
|
+
slowIntervalMs: z19.number().int().positive().optional(),
|
|
4352
|
+
recentActivityWindowMs: z19.number().int().positive().optional(),
|
|
4353
|
+
rapidAttemptsBeforeBackoff: z19.number().int().positive().optional(),
|
|
4740
4354
|
/**
|
|
4741
4355
|
* Grace period applied to the very first nudge a project ever
|
|
4742
4356
|
* receives, measured from `project.createdAt`. Default per tempo;
|
|
4743
4357
|
* setting `0` opts out (legacy behavior — first nudge fires as
|
|
4744
4358
|
* soon as the rapid interval allows).
|
|
4745
4359
|
*/
|
|
4746
|
-
firstNudgeGraceMs:
|
|
4360
|
+
firstNudgeGraceMs: z19.number().int().nonnegative().optional()
|
|
4747
4361
|
});
|
|
4748
|
-
var ProjectNudgeStateSchema =
|
|
4749
|
-
lastNudgedAt:
|
|
4750
|
-
consecutiveRapidNudges:
|
|
4362
|
+
var ProjectNudgeStateSchema = z19.object({
|
|
4363
|
+
lastNudgedAt: z19.string().optional(),
|
|
4364
|
+
consecutiveRapidNudges: z19.number().int().nonnegative().optional()
|
|
4751
4365
|
});
|
|
4752
|
-
var ProjectTabVisibilitySchema =
|
|
4753
|
-
overview:
|
|
4754
|
-
tasks:
|
|
4755
|
-
approvals:
|
|
4756
|
-
workspace:
|
|
4757
|
-
artifacts:
|
|
4758
|
-
map:
|
|
4366
|
+
var ProjectTabVisibilitySchema = z19.object({
|
|
4367
|
+
overview: z19.boolean().optional(),
|
|
4368
|
+
tasks: z19.boolean().optional(),
|
|
4369
|
+
approvals: z19.boolean().optional(),
|
|
4370
|
+
workspace: z19.boolean().optional(),
|
|
4371
|
+
artifacts: z19.boolean().optional(),
|
|
4372
|
+
map: z19.boolean().optional()
|
|
4759
4373
|
}).strict();
|
|
4760
|
-
var ProjectManagedWorkspaceWritePolicySchema =
|
|
4761
|
-
var ProjectTypeProvenanceSchema =
|
|
4374
|
+
var ProjectManagedWorkspaceWritePolicySchema = z19.enum(["auto", "allow", "deny"]);
|
|
4375
|
+
var ProjectTypeProvenanceSchema = z19.object({
|
|
4762
4376
|
/** Catalog id of the applied project type. */
|
|
4763
|
-
id:
|
|
4377
|
+
id: z19.string(),
|
|
4764
4378
|
/** Type version installed at adoption. */
|
|
4765
|
-
version:
|
|
4379
|
+
version: z19.string(),
|
|
4766
4380
|
/** Catalog source the type resolved from (`bundled` | `local` | `community` | …). */
|
|
4767
|
-
source:
|
|
4381
|
+
source: z19.string(),
|
|
4768
4382
|
/** Param values collected at adoption, substituted into templates + seed files. */
|
|
4769
|
-
params:
|
|
4383
|
+
params: z19.record(z19.string(), z19.unknown()).optional(),
|
|
4770
4384
|
/** ISO timestamp of adoption. */
|
|
4771
|
-
appliedAt:
|
|
4385
|
+
appliedAt: z19.string()
|
|
4772
4386
|
});
|
|
4773
|
-
var ProjectSchema =
|
|
4387
|
+
var ProjectSchema = z19.object({
|
|
4774
4388
|
id: EntityIdSchema,
|
|
4775
|
-
name:
|
|
4776
|
-
description:
|
|
4777
|
-
workingDir:
|
|
4389
|
+
name: z19.string(),
|
|
4390
|
+
description: z19.string().optional(),
|
|
4391
|
+
workingDir: z19.string().optional(),
|
|
4778
4392
|
/** Optional gezel that acts as the project's voorman (foreman). Surfaces in
|
|
4779
4393
|
* the project detail pane, flows into the system prompt when a session is
|
|
4780
4394
|
* scoped here. For solo projects (`mode === 'solo'`) this same field
|
|
4781
4395
|
* holds the project's ambachtsman — the data is unchanged, only the
|
|
4782
4396
|
* label flips. */
|
|
4783
|
-
voormanGezelId:
|
|
4397
|
+
voormanGezelId: z19.string().optional(),
|
|
4784
4398
|
/**
|
|
4785
4399
|
* Internal marker: ISO timestamp of the one-time automatic voorman
|
|
4786
4400
|
* assignment. The indexer ensures every project ends up with a voorman
|
|
@@ -4790,7 +4404,7 @@ var ProjectSchema = z21.object({
|
|
|
4790
4404
|
* silently re-populated on the next scan. Not user-editable; absent on
|
|
4791
4405
|
* projects last written before this field existed.
|
|
4792
4406
|
*/
|
|
4793
|
-
voormanAutoAssignedAt:
|
|
4407
|
+
voormanAutoAssignedAt: z19.string().optional(),
|
|
4794
4408
|
/**
|
|
4795
4409
|
* Roster — gezels that have been pulled into this project. Populated
|
|
4796
4410
|
* automatically the first time a gezel is set as voorman, opens a
|
|
@@ -4805,7 +4419,7 @@ var ProjectSchema = z21.object({
|
|
|
4805
4419
|
* (back-compat with every project written before this field
|
|
4806
4420
|
* existed).
|
|
4807
4421
|
*/
|
|
4808
|
-
gezelIds:
|
|
4422
|
+
gezelIds: z19.array(z19.string()).optional(),
|
|
4809
4423
|
/**
|
|
4810
4424
|
* Suggested-work keys the user has dismissed ("don't offer this again
|
|
4811
4425
|
* here"). Advisory UI state, same spirit as `gezelIds`: enabling a
|
|
@@ -4815,7 +4429,7 @@ var ProjectSchema = z21.object({
|
|
|
4815
4429
|
* `project-type:<typeId>:<scheduleKey>`). Deliberately not exposed
|
|
4816
4430
|
* through the model-facing `update_project` MCP tool.
|
|
4817
4431
|
*/
|
|
4818
|
-
suggestedWorkDismissed:
|
|
4432
|
+
suggestedWorkDismissed: z19.array(z19.string()).optional(),
|
|
4819
4433
|
/**
|
|
4820
4434
|
* Shared per-project configuration values ("project properties") that
|
|
4821
4435
|
* craftbook params and features draw from — e.g. `content.language`,
|
|
@@ -4824,7 +4438,7 @@ var ProjectSchema = z21.object({
|
|
|
4824
4438
|
* ids are allowed (the registry improves display, it doesn't gate).
|
|
4825
4439
|
* Values are plain strings; empty string is treated as unset.
|
|
4826
4440
|
*/
|
|
4827
|
-
properties:
|
|
4441
|
+
properties: z19.record(z19.string(), z19.string()).optional(),
|
|
4828
4442
|
/**
|
|
4829
4443
|
* Project shape. `crew` (the default) is the original behavior — the
|
|
4830
4444
|
* voorman recruits and coordinates a team of specialists. `solo` is a
|
|
@@ -4835,7 +4449,7 @@ var ProjectSchema = z21.object({
|
|
|
4835
4449
|
* for back-compat with every project on disk before this field
|
|
4836
4450
|
* existed.
|
|
4837
4451
|
*/
|
|
4838
|
-
mode:
|
|
4452
|
+
mode: z19.enum(["crew", "solo"]).optional(),
|
|
4839
4453
|
/**
|
|
4840
4454
|
* Optional custom label for this project's lead gezel, overriding the
|
|
4841
4455
|
* mode-based default ("Voorman" / "Ambachtsman") everywhere the UI
|
|
@@ -4843,7 +4457,7 @@ var ProjectSchema = z21.object({
|
|
|
4843
4457
|
* "Opponent"); absent → the mode default. The data field stays
|
|
4844
4458
|
* `voormanGezelId` — only the label changes.
|
|
4845
4459
|
*/
|
|
4846
|
-
leadLabel:
|
|
4460
|
+
leadLabel: z19.string().optional(),
|
|
4847
4461
|
/**
|
|
4848
4462
|
* Lean-agent profile (set by a project type at adoption, e.g. checkers).
|
|
4849
4463
|
* When true, sessions here get a minimal tool surface (the type's script
|
|
@@ -4851,7 +4465,7 @@ var ProjectSchema = z21.object({
|
|
|
4851
4465
|
* scaffolding). Keeps small local models from being overwhelmed on a
|
|
4852
4466
|
* focused single-purpose task. Absent → the full agent profile.
|
|
4853
4467
|
*/
|
|
4854
|
-
leanProfile:
|
|
4468
|
+
leanProfile: z19.boolean().optional(),
|
|
4855
4469
|
/**
|
|
4856
4470
|
* Per-project workspace-indexing switch. Missing/true preserves the
|
|
4857
4471
|
* historical behavior: structural discovery plus the content-index refresh
|
|
@@ -4864,9 +4478,9 @@ var ProjectSchema = z21.object({
|
|
|
4864
4478
|
* document index. Project-type manifests may seed the value at adoption and
|
|
4865
4479
|
* the user can override it later in Project Settings.
|
|
4866
4480
|
*/
|
|
4867
|
-
indexingEnabled:
|
|
4481
|
+
indexingEnabled: z19.boolean().optional(),
|
|
4868
4482
|
github: ProjectGitHubSchema.optional(),
|
|
4869
|
-
connectors:
|
|
4483
|
+
connectors: z19.array(ProjectConnectorBindingSchema).optional(),
|
|
4870
4484
|
nudgeConfig: ProjectNudgeConfigSchema.optional(),
|
|
4871
4485
|
nudgeState: ProjectNudgeStateSchema.optional(),
|
|
4872
4486
|
/**
|
|
@@ -4889,7 +4503,7 @@ var ProjectSchema = z21.object({
|
|
|
4889
4503
|
* @deprecated Use `managedWorkspaceWritePolicy` and the centralized
|
|
4890
4504
|
* `projectManagedWorkspaceWritable` resolver.
|
|
4891
4505
|
*/
|
|
4892
|
-
allowGezelWrites:
|
|
4506
|
+
allowGezelWrites: z19.boolean().optional(),
|
|
4893
4507
|
/**
|
|
4894
4508
|
* Per-project Codex execution posture selected from the project status bar.
|
|
4895
4509
|
* It overrides per-gezel/install Codex defaults so the visible control is
|
|
@@ -4926,7 +4540,7 @@ var ProjectSchema = z21.object({
|
|
|
4926
4540
|
* weak local model having to take an explicit action. Reversible and
|
|
4927
4541
|
* non-destructive; chat and direct tool calls keep working.
|
|
4928
4542
|
*/
|
|
4929
|
-
status:
|
|
4543
|
+
status: z19.enum(["active", "readonly", "inactive", "stable"]).optional(),
|
|
4930
4544
|
/**
|
|
4931
4545
|
* Bury this project in the navigation without deleting it. Archived
|
|
4932
4546
|
* projects remain available from the dedicated section in the full
|
|
@@ -4936,13 +4550,13 @@ var ProjectSchema = z21.object({
|
|
|
4936
4550
|
* Missing/false means visible in the ordinary project UX, preserving
|
|
4937
4551
|
* compatibility with projects written before archiving existed.
|
|
4938
4552
|
*/
|
|
4939
|
-
archived:
|
|
4553
|
+
archived: z19.boolean().optional(),
|
|
4940
4554
|
/**
|
|
4941
4555
|
* Per-project override of the `run_nodejs_script` wall-clock
|
|
4942
4556
|
* timeout. Clamped between 30 seconds and 30 minutes. Missing →
|
|
4943
4557
|
* the service-side default (5 min) applies.
|
|
4944
4558
|
*/
|
|
4945
|
-
workspaceScriptTimeoutMs:
|
|
4559
|
+
workspaceScriptTimeoutMs: z19.number().int().min(3e4).max(30 * 6e4).optional(),
|
|
4946
4560
|
/**
|
|
4947
4561
|
* Named credentials this project is explicitly allowed to use.
|
|
4948
4562
|
* Credentials are stored once globally in `SecretStore`; a grant
|
|
@@ -4951,76 +4565,515 @@ var ProjectSchema = z21.object({
|
|
|
4951
4565
|
* Missing → no credentials granted. See `scripts/dispatcher.ts`
|
|
4952
4566
|
* and `secrets/registry.ts` for resolution.
|
|
4953
4567
|
*/
|
|
4954
|
-
grantedCredentials:
|
|
4568
|
+
grantedCredentials: z19.array(z19.string()).optional(),
|
|
4569
|
+
/**
|
|
4570
|
+
* Advanced exact-origin bindings for toolset credentials. Built-in provider
|
|
4571
|
+
* credentials are service-pinned and webhook credentials follow the
|
|
4572
|
+
* configured webhook URL, so entries for those names are ignored.
|
|
4573
|
+
*/
|
|
4574
|
+
credentialAllowedOrigins: z19.record(z19.string(), z19.array(HttpsOriginSchema)).optional(),
|
|
4575
|
+
/**
|
|
4576
|
+
* Explicit user override of the project's type (an id from the bundled
|
|
4577
|
+
* project-type taxonomy, see `project-types/taxonomy.ts`). When set, it
|
|
4578
|
+
* wins over `detectedProjectType` for craftbook suggestions. Cleared
|
|
4579
|
+
* (unset) → fall back to auto-detection. Missing on every project written
|
|
4580
|
+
* before this field existed.
|
|
4581
|
+
*/
|
|
4582
|
+
projectTypeId: z19.string().optional(),
|
|
4583
|
+
/**
|
|
4584
|
+
* Auto-detected project type, recomputed on each content-index scan from
|
|
4585
|
+
* the workspace file mix + about/mission text. Not user-editable — the
|
|
4586
|
+
* user expresses an override via `projectTypeId`. Absent until the first
|
|
4587
|
+
* scan classifies the project (or when nothing scores above the floor).
|
|
4588
|
+
*/
|
|
4589
|
+
detectedProjectType: z19.object({
|
|
4590
|
+
id: z19.string(),
|
|
4591
|
+
score: z19.number(),
|
|
4592
|
+
scannedAt: z19.string()
|
|
4593
|
+
}).optional(),
|
|
4594
|
+
/**
|
|
4595
|
+
* Provenance of an applied custom project type (see docs/project-types.md).
|
|
4596
|
+
* Distinct from `projectTypeId`, which is the taxonomy id used for craftbook
|
|
4597
|
+
* suggestion: a custom type stamps this on adoption and, when it `extends` a
|
|
4598
|
+
* taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
|
|
4599
|
+
* inherits. Absent on projects created without a custom type.
|
|
4600
|
+
*/
|
|
4601
|
+
projectType: ProjectTypeProvenanceSchema.optional(),
|
|
4602
|
+
/**
|
|
4603
|
+
* Filesystem ownership boundary. Missing/`user` is ordinary per-account
|
|
4604
|
+
* state. `machine-shared` is a grandfathered project mounted from the
|
|
4605
|
+
* installer-managed shared root and operated on by this user's daemon.
|
|
4606
|
+
* The engine broker never opens the project or receives its paths.
|
|
4607
|
+
*/
|
|
4608
|
+
storageScope: z19.enum(["user", "machine-shared"]).optional(),
|
|
4609
|
+
createdAt: z19.string(),
|
|
4610
|
+
updatedAt: z19.string()
|
|
4611
|
+
});
|
|
4612
|
+
function resolveProjectTypeId(project) {
|
|
4613
|
+
return project.projectTypeId ?? project.detectedProjectType?.id;
|
|
4614
|
+
}
|
|
4615
|
+
function projectAllowsAmbientWork(project) {
|
|
4616
|
+
const status = project.status ?? "active";
|
|
4617
|
+
return status === "active";
|
|
4618
|
+
}
|
|
4619
|
+
var InstalledPackageSchema = z19.object({
|
|
4620
|
+
name: z19.string(),
|
|
4621
|
+
version: z19.string()
|
|
4622
|
+
});
|
|
4623
|
+
var ProjectDetailSchema = ProjectSchema.extend({
|
|
4624
|
+
packages: z19.array(InstalledPackageSchema),
|
|
4625
|
+
/** Contents of `documents/about.md` inside the project, if present. */
|
|
4626
|
+
about: z19.string().optional(),
|
|
4627
|
+
/** Contents of `documents/missionObjectives.md`, if present. */
|
|
4628
|
+
missionObjectives: z19.string().optional()
|
|
4629
|
+
});
|
|
4630
|
+
var ProjectFileEntrySchema = z19.object({
|
|
4631
|
+
name: z19.string(),
|
|
4632
|
+
path: z19.string(),
|
|
4633
|
+
isDirectory: z19.boolean(),
|
|
4634
|
+
/** File mtime (ms epoch). Populated only when the caller opts into stats. */
|
|
4635
|
+
mtimeMs: z19.number().optional()
|
|
4636
|
+
});
|
|
4637
|
+
var ProjectGithubSchema = ProjectGitHubSchema;
|
|
4638
|
+
|
|
4639
|
+
// src/schemas/craftbook-test.ts
|
|
4640
|
+
var CRAFTBOOK_TEST_SCHEMA_VERSION = 1;
|
|
4641
|
+
var CRAFTBOOK_TEST_FILENAME = "test.json";
|
|
4642
|
+
var PrometheusAlertsCheckSchema = z20.object({
|
|
4643
|
+
kind: z20.literal("prometheusAlerts"),
|
|
4644
|
+
file: z20.string().min(1),
|
|
4645
|
+
minRules: z20.number().int().positive().optional(),
|
|
4646
|
+
maxPageAlerts: z20.number().int().nonnegative().optional(),
|
|
4647
|
+
allowedSeverities: z20.array(z20.string().min(1)).optional(),
|
|
4648
|
+
requiredServices: z20.array(z20.string().min(1)).optional(),
|
|
4649
|
+
requiredRunbookUrls: z20.array(z20.string().min(1)).optional()
|
|
4650
|
+
}).strict();
|
|
4651
|
+
var NodeScriptPassesCheckSchema = z20.object({
|
|
4652
|
+
kind: z20.literal("nodeScriptPasses"),
|
|
4653
|
+
script: z20.string().min(1),
|
|
4654
|
+
timeoutMs: z20.number().int().positive().optional(),
|
|
4655
|
+
requiredOutput: z20.array(
|
|
4656
|
+
z20.object({
|
|
4657
|
+
pattern: z20.string().min(1),
|
|
4658
|
+
flags: z20.string().optional(),
|
|
4659
|
+
label: z20.string().optional()
|
|
4660
|
+
}).strict()
|
|
4661
|
+
).optional()
|
|
4662
|
+
}).strict();
|
|
4663
|
+
var BinaryDocumentCheckSchema = z20.object({
|
|
4664
|
+
kind: z20.literal("binaryDocument"),
|
|
4665
|
+
file: z20.string().min(1),
|
|
4666
|
+
/** Look in the artifacts drawer instead of the workspace. */
|
|
4667
|
+
artifact: z20.boolean().optional(),
|
|
4668
|
+
/** Floor on the container's byte length; defaults to 1000. */
|
|
4669
|
+
minBytes: z20.number().int().positive().optional()
|
|
4670
|
+
}).strict();
|
|
4671
|
+
var CraftbookTestCheckSchema = z20.union([
|
|
4672
|
+
GateCheckSchema,
|
|
4673
|
+
PrometheusAlertsCheckSchema,
|
|
4674
|
+
NodeScriptPassesCheckSchema,
|
|
4675
|
+
BinaryDocumentCheckSchema
|
|
4676
|
+
]);
|
|
4677
|
+
var MockServiceIdSchema = z20.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, "mock service ids are lowercase kebab-case");
|
|
4678
|
+
var MockToolsetIdSchema = z20.string().min(1).regex(
|
|
4679
|
+
/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,
|
|
4680
|
+
"mock toolset ids are lowercase catalog ids or scoped npm-style ids"
|
|
4681
|
+
);
|
|
4682
|
+
var MockHttpRouteSchema = z20.object({
|
|
4683
|
+
method: z20.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
4684
|
+
/** Exact path, `:param` segments, or a trailing `*` wildcard. */
|
|
4685
|
+
path: z20.string().min(1),
|
|
4686
|
+
status: z20.number().int().min(100).max(599).default(200),
|
|
4687
|
+
headers: z20.record(z20.string(), z20.string()).optional(),
|
|
4688
|
+
/** String bodies are served verbatim; anything else is JSON-encoded. */
|
|
4689
|
+
body: z20.unknown(),
|
|
4690
|
+
latencyMs: z20.number().int().nonnegative().optional()
|
|
4691
|
+
}).strict();
|
|
4692
|
+
var MockServiceSchema = z20.discriminatedUnion("kind", [
|
|
4693
|
+
z20.object({
|
|
4694
|
+
kind: z20.literal("http"),
|
|
4695
|
+
id: MockServiceIdSchema,
|
|
4696
|
+
description: z20.string().min(1),
|
|
4697
|
+
/**
|
|
4698
|
+
* v1 mock HTTP is reachable ONLY through the `http.authed`
|
|
4699
|
+
* credential rail (anonymous script/browser HTTP hard-rejects
|
|
4700
|
+
* loopback), so a credential is required. The harness seeds it and
|
|
4701
|
+
* grants the mock's exact origin on the trial project.
|
|
4702
|
+
*/
|
|
4703
|
+
credential: z20.object({
|
|
4704
|
+
name: z20.string().regex(/^mock\.[a-z0-9][a-z0-9.-]*$/, "mock credentials are named mock.<service-id>"),
|
|
4705
|
+
authScheme: z20.enum(["bearer", "basic"]).optional()
|
|
4706
|
+
}).strict(),
|
|
4707
|
+
routes: z20.array(MockHttpRouteSchema).min(1)
|
|
4708
|
+
}).strict(),
|
|
4709
|
+
z20.object({
|
|
4710
|
+
kind: z20.literal("webhook"),
|
|
4711
|
+
id: MockServiceIdSchema,
|
|
4712
|
+
description: z20.string().min(1),
|
|
4713
|
+
/** Receiver path; defaults to `/webhook` when omitted. */
|
|
4714
|
+
path: z20.string().min(1).optional()
|
|
4715
|
+
}).strict(),
|
|
4716
|
+
z20.object({
|
|
4717
|
+
kind: z20.literal("cli"),
|
|
4718
|
+
id: MockServiceIdSchema,
|
|
4719
|
+
description: z20.string().min(1),
|
|
4720
|
+
/** Fake-CLI shim seeded as a workspace file (today's dry-run pattern). */
|
|
4721
|
+
shim: z20.object({ path: z20.string().min(1), content: z20.string() }).strict()
|
|
4722
|
+
}).strict(),
|
|
4723
|
+
z20.object({
|
|
4724
|
+
kind: z20.literal("mcp"),
|
|
4725
|
+
id: MockServiceIdSchema,
|
|
4726
|
+
description: z20.string().min(1),
|
|
4727
|
+
/** Override the local-catalog id when the mock replaces a real dependency. */
|
|
4728
|
+
toolsetId: MockToolsetIdSchema.optional(),
|
|
4729
|
+
/**
|
|
4730
|
+
* Served live by the eval mock rail: each declared tool becomes a
|
|
4731
|
+
* real tool on a per-trial Streamable-HTTP MCP endpoint, installed
|
|
4732
|
+
* into the trial via a local-catalog `mock-mcp-<id>` toolset.
|
|
4733
|
+
* `resultTemplate` is JSON-encoded as the tool result text
|
|
4734
|
+
* (default `{"ok":true}` when absent).
|
|
4735
|
+
*/
|
|
4736
|
+
tools: z20.array(
|
|
4737
|
+
z20.object({
|
|
4738
|
+
name: z20.string().min(1),
|
|
4739
|
+
description: z20.string().min(1),
|
|
4740
|
+
resultTemplate: z20.unknown().optional(),
|
|
4741
|
+
/** Deterministic stateful responses, consumed in call order; the last repeats. */
|
|
4742
|
+
resultSequence: z20.array(z20.unknown()).min(1).optional(),
|
|
4743
|
+
/**
|
|
4744
|
+
* Deterministic eval-only file materialization after this
|
|
4745
|
+
* tool call. The fixture must match the container the
|
|
4746
|
+
* deliverable's extension claims — a `minimal-pptx` written
|
|
4747
|
+
* to a `.docx` path now fails the `binaryDocument` check on
|
|
4748
|
+
* content type rather than sliding through a byte floor.
|
|
4749
|
+
*/
|
|
4750
|
+
writeFixture: z20.object({
|
|
4751
|
+
surface: z20.enum(["workspace", "artifact"]),
|
|
4752
|
+
pathArgument: z20.string().min(1),
|
|
4753
|
+
fixture: z20.enum(["minimal-pptx", "minimal-docx", "minimal-pdf", "minimal-png"])
|
|
4754
|
+
}).strict().optional()
|
|
4755
|
+
}).strict()
|
|
4756
|
+
).min(1)
|
|
4757
|
+
}).strict()
|
|
4758
|
+
]);
|
|
4759
|
+
var CraftbookTestFixtureFileSchema = z20.object({
|
|
4760
|
+
path: z20.string().min(1),
|
|
4761
|
+
content: z20.string(),
|
|
4762
|
+
/** Defaults to `workspace`; `harness` never enters the model-visible project. */
|
|
4763
|
+
surface: z20.enum(["workspace", "artifact", "harness"]).optional(),
|
|
4955
4764
|
/**
|
|
4956
|
-
*
|
|
4957
|
-
*
|
|
4958
|
-
* configured webhook URL, so entries for those names are ignored.
|
|
4765
|
+
* Whether the fixture is presented to the model as source material.
|
|
4766
|
+
* Defaults to true; false still seeds the file for browsers and graders.
|
|
4959
4767
|
*/
|
|
4960
|
-
|
|
4768
|
+
modelInput: z20.boolean().optional()
|
|
4769
|
+
}).strict();
|
|
4770
|
+
var CraftbookTestWorkerSchema = z20.object({
|
|
4771
|
+
name: z20.string().min(1),
|
|
4772
|
+
role: z20.string().min(1),
|
|
4773
|
+
description: z20.string().optional(),
|
|
4774
|
+
about: z20.string().optional()
|
|
4775
|
+
}).strict();
|
|
4776
|
+
var CraftbookTestSetupSchema = z20.object({
|
|
4777
|
+
projectName: z20.string().min(1),
|
|
4778
|
+
about: z20.string().optional(),
|
|
4779
|
+
missionObjectives: z20.string().optional(),
|
|
4780
|
+
/** Reproduce project write posture before the craftbook task starts. */
|
|
4781
|
+
managedWorkspaceWritePolicy: ProjectManagedWorkspaceWritePolicySchema.optional(),
|
|
4782
|
+
files: z20.array(CraftbookTestFixtureFileSchema).default([]),
|
|
4783
|
+
/** Exact values supplied to the catalog craftbook's `paramSchema`. */
|
|
4784
|
+
craftbookParams: z20.record(z20.string(), z20.string()).optional(),
|
|
4961
4785
|
/**
|
|
4962
|
-
*
|
|
4963
|
-
*
|
|
4964
|
-
*
|
|
4965
|
-
* (unset) → fall back to auto-detection. Missing on every project written
|
|
4966
|
-
* before this field existed.
|
|
4786
|
+
* Direct execution target. When present the harness seeds this gezel
|
|
4787
|
+
* and sends the kickoff straight to it (measuring whether the book
|
|
4788
|
+
* guides the work); absent → the Meester routes.
|
|
4967
4789
|
*/
|
|
4968
|
-
|
|
4790
|
+
worker: CraftbookTestWorkerSchema.optional()
|
|
4791
|
+
}).strict();
|
|
4792
|
+
var CraftbookTestDeliverableSchema = z20.object({
|
|
4793
|
+
path: z20.string().min(1),
|
|
4794
|
+
kind: DeliverableKindSchema,
|
|
4795
|
+
/** Grade the path in the project's artifacts drawer, not its workspace. */
|
|
4796
|
+
artifact: z20.boolean().optional(),
|
|
4797
|
+
minBytes: z20.number().int().positive().optional(),
|
|
4798
|
+
checks: z20.array(CraftbookTestCheckSchema).optional()
|
|
4799
|
+
}).strict();
|
|
4800
|
+
var CraftbookTestMockExpectationSchema = z20.object({
|
|
4801
|
+
/** Mock service id from `mocks[]`. */
|
|
4802
|
+
service: MockServiceIdSchema,
|
|
4803
|
+
minRequests: z20.number().int().positive().optional(),
|
|
4804
|
+
/** Regex sources matched against logged request paths. */
|
|
4805
|
+
requiredPaths: z20.array(z20.string().min(1)).optional(),
|
|
4806
|
+
forbiddenPaths: z20.array(z20.string().min(1)).optional(),
|
|
4969
4807
|
/**
|
|
4970
|
-
*
|
|
4971
|
-
*
|
|
4972
|
-
*
|
|
4973
|
-
*
|
|
4808
|
+
* Exact MCP tool names that must each have been called at least once
|
|
4809
|
+
* on this service (`kind: 'mcp'` only). Exact names, not regexes —
|
|
4810
|
+
* the tool roster is fully declared in the same file, so a pattern
|
|
4811
|
+
* buys nothing and invites drift. Cross-checked against the mock's
|
|
4812
|
+
* declared `tools[]` at parse time.
|
|
4974
4813
|
*/
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4814
|
+
requiredTools: z20.array(z20.string().min(1)).optional(),
|
|
4815
|
+
/** Per-MCP-tool call budgets for repeated journeys or retries. */
|
|
4816
|
+
toolCalls: z20.record(
|
|
4817
|
+
z20.string().min(1),
|
|
4818
|
+
z20.object({
|
|
4819
|
+
minCalls: z20.number().int().nonnegative().default(1),
|
|
4820
|
+
maxCalls: z20.number().int().nonnegative().optional()
|
|
4821
|
+
}).strict().refine(
|
|
4822
|
+
(value) => value.maxCalls === void 0 || value.minCalls <= value.maxCalls,
|
|
4823
|
+
"minCalls must be less than or equal to maxCalls"
|
|
4824
|
+
)
|
|
4825
|
+
).optional()
|
|
4826
|
+
}).strict();
|
|
4827
|
+
var CraftbookTestHistoryExpectationSchema = z20.object({
|
|
4828
|
+
kind: HistoryEventKindSchema,
|
|
4829
|
+
minEntries: z20.number().int().nonnegative().default(1),
|
|
4830
|
+
maxEntries: z20.number().int().nonnegative().optional(),
|
|
4831
|
+
summaryPattern: z20.string().min(1).optional(),
|
|
4832
|
+
flags: z20.string().optional(),
|
|
4833
|
+
details: z20.record(z20.string(), z20.union([z20.string(), z20.number(), z20.boolean(), z20.null()])).optional()
|
|
4834
|
+
}).strict();
|
|
4835
|
+
var CraftbookTestSuccessSchema = z20.object({
|
|
4836
|
+
summary: z20.string().min(1),
|
|
4837
|
+
deliverables: z20.array(CraftbookTestDeliverableSchema).optional(),
|
|
4838
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4839
|
+
taskNotes: z20.object({
|
|
4840
|
+
minBytes: z20.number().int().positive().optional(),
|
|
4841
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4842
|
+
requireCraftbookTask: z20.boolean().optional()
|
|
4843
|
+
}).strict().optional(),
|
|
4844
|
+
taskGraph: z20.object({
|
|
4845
|
+
checks: z20.array(CraftbookTestCheckSchema).optional(),
|
|
4846
|
+
requireCraftbookTask: z20.boolean().optional(),
|
|
4847
|
+
/** Require the matching task to reach a terminal step (or complete). */
|
|
4848
|
+
requireTerminalStep: z20.boolean().optional(),
|
|
4849
|
+
requireDraftRef: z20.boolean().optional(),
|
|
4850
|
+
draft: z20.object({
|
|
4851
|
+
status: z20.enum(["draft", "paused", "active", "complete", "canceled"]).optional(),
|
|
4852
|
+
minDescriptionBytes: z20.number().int().positive().optional(),
|
|
4853
|
+
minOutcomes: z20.number().int().positive().optional(),
|
|
4854
|
+
minSteps: z20.number().int().positive().optional(),
|
|
4855
|
+
requireTerminalVerification: z20.boolean().optional(),
|
|
4856
|
+
requireGatedBuildSteps: z20.boolean().optional()
|
|
4857
|
+
}).strict().optional()
|
|
4858
|
+
}).strict().optional(),
|
|
4859
|
+
/** Assertions evaluated against the live mock server's request log. */
|
|
4860
|
+
mocks: z20.array(CraftbookTestMockExpectationSchema).optional(),
|
|
4861
|
+
/** Assertions evaluated against the project's append-only History log. */
|
|
4862
|
+
history: z20.array(CraftbookTestHistoryExpectationSchema).optional(),
|
|
4863
|
+
/** Workspace fixtures whose final content must equal the seeded bytes exactly. */
|
|
4864
|
+
unchangedFixtures: z20.array(z20.string().min(1)).optional()
|
|
4865
|
+
}).strict();
|
|
4866
|
+
var CraftbookTestRubricSchema = z20.object({
|
|
4867
|
+
artifact: z20.object({
|
|
4868
|
+
/** Workspace or artifact path the judge reads (adapter derives the basename). */
|
|
4869
|
+
path: z20.string().min(1),
|
|
4870
|
+
kind: z20.enum(["html", "markdown", "yaml", "typescript", "json", "text"])
|
|
4871
|
+
}).strict(),
|
|
4872
|
+
axes: z20.array(z20.object({ name: z20.string().min(1), description: z20.string().min(1) }).strict()).min(1),
|
|
4873
|
+
contextNote: z20.string().optional()
|
|
4874
|
+
}).strict();
|
|
4875
|
+
var CraftbookTestSpecSchema = z20.object({
|
|
4876
|
+
schemaVersion: z20.literal(CRAFTBOOK_TEST_SCHEMA_VERSION),
|
|
4877
|
+
title: z20.string().min(1),
|
|
4878
|
+
objective: z20.string().min(1),
|
|
4980
4879
|
/**
|
|
4981
|
-
*
|
|
4982
|
-
*
|
|
4983
|
-
*
|
|
4984
|
-
* taxonomy id, ALSO sets `projectTypeId` so detection-based suggestion
|
|
4985
|
-
* inherits. Absent on projects created without a custom type.
|
|
4880
|
+
* Task-class taxonomy tags (e.g. `html-game`, `corpus`, `external`).
|
|
4881
|
+
* The single declared source for harness selection and batch
|
|
4882
|
+
* planning — replaces the old regex classifiers.
|
|
4986
4883
|
*/
|
|
4987
|
-
|
|
4884
|
+
tags: z20.array(z20.string().min(1)).default([]),
|
|
4885
|
+
/** Kickoff chat message the harness sends. Required — every book runs. */
|
|
4886
|
+
prompt: z20.string().min(1),
|
|
4887
|
+
setup: CraftbookTestSetupSchema,
|
|
4888
|
+
mocks: z20.array(MockServiceSchema).default([]),
|
|
4889
|
+
success: CraftbookTestSuccessSchema,
|
|
4890
|
+
rubric: CraftbookTestRubricSchema,
|
|
4891
|
+
qualityFocus: z20.array(z20.string().min(1)).default([]),
|
|
4988
4892
|
/**
|
|
4989
|
-
*
|
|
4990
|
-
*
|
|
4991
|
-
* installer-managed shared root and operated on by this user's daemon.
|
|
4992
|
-
* The engine broker never opens the project or receives its paths.
|
|
4893
|
+
* Sanctioned escape hatch for experiments — carried opaquely, never
|
|
4894
|
+
* interpreted by CI. Promote a field out of here before relying on it.
|
|
4993
4895
|
*/
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4896
|
+
extensions: z20.record(z20.string(), z20.unknown()).optional()
|
|
4897
|
+
}).strict().superRefine((spec, ctx) => {
|
|
4898
|
+
const mockIds = new Set(spec.mocks.map((m) => m.id));
|
|
4899
|
+
const mockById = new Map(spec.mocks.map((m) => [m.id, m]));
|
|
4900
|
+
for (const [i, expectation] of (spec.success.mocks ?? []).entries()) {
|
|
4901
|
+
if (!mockIds.has(expectation.service)) {
|
|
4902
|
+
ctx.addIssue({
|
|
4903
|
+
code: z20.ZodIssueCode.custom,
|
|
4904
|
+
path: ["success", "mocks", i, "service"],
|
|
4905
|
+
message: `success.mocks[${i}] references unknown mock service "${expectation.service}"`
|
|
4906
|
+
});
|
|
4907
|
+
}
|
|
4908
|
+
if (expectation.requiredTools && expectation.requiredTools.length > 0) {
|
|
4909
|
+
const target = mockById.get(expectation.service);
|
|
4910
|
+
if (target && target.kind !== "mcp") {
|
|
4911
|
+
ctx.addIssue({
|
|
4912
|
+
code: z20.ZodIssueCode.custom,
|
|
4913
|
+
path: ["success", "mocks", i, "requiredTools"],
|
|
4914
|
+
message: `success.mocks[${i}].requiredTools requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4915
|
+
});
|
|
4916
|
+
} else if (target?.kind === "mcp") {
|
|
4917
|
+
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4918
|
+
for (const name of expectation.requiredTools) {
|
|
4919
|
+
if (!declared.has(name)) {
|
|
4920
|
+
ctx.addIssue({
|
|
4921
|
+
code: z20.ZodIssueCode.custom,
|
|
4922
|
+
path: ["success", "mocks", i, "requiredTools"],
|
|
4923
|
+
message: `success.mocks[${i}].requiredTools names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4924
|
+
});
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
if (expectation.toolCalls && Object.keys(expectation.toolCalls).length > 0) {
|
|
4930
|
+
const target = mockById.get(expectation.service);
|
|
4931
|
+
if (target && target.kind !== "mcp") {
|
|
4932
|
+
ctx.addIssue({
|
|
4933
|
+
code: z20.ZodIssueCode.custom,
|
|
4934
|
+
path: ["success", "mocks", i, "toolCalls"],
|
|
4935
|
+
message: `success.mocks[${i}].toolCalls requires an mcp service; "${expectation.service}" is kind "${target.kind}"`
|
|
4936
|
+
});
|
|
4937
|
+
} else if (target?.kind === "mcp") {
|
|
4938
|
+
const declared = new Set(target.tools.map((tool) => tool.name));
|
|
4939
|
+
for (const name of Object.keys(expectation.toolCalls)) {
|
|
4940
|
+
if (!declared.has(name)) {
|
|
4941
|
+
ctx.addIssue({
|
|
4942
|
+
code: z20.ZodIssueCode.custom,
|
|
4943
|
+
path: ["success", "mocks", i, "toolCalls", name],
|
|
4944
|
+
message: `success.mocks[${i}].toolCalls names undeclared tool "${name}" on mcp service "${expectation.service}"`
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4951
|
+
for (const [i, mock] of spec.mocks.entries()) {
|
|
4952
|
+
if (mock.kind === "http" && mock.credential.name !== `mock.${mock.id}`) {
|
|
4953
|
+
ctx.addIssue({
|
|
4954
|
+
code: z20.ZodIssueCode.custom,
|
|
4955
|
+
path: ["mocks", i, "credential", "name"],
|
|
4956
|
+
message: `http mock "${mock.id}" must use credential name "mock.${mock.id}"`
|
|
4957
|
+
});
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
const workspaceFixtures = new Set(
|
|
4961
|
+
spec.setup.files.filter((file) => file.surface === void 0 || file.surface === "workspace").map((file) => file.path)
|
|
4962
|
+
);
|
|
4963
|
+
for (const [i, path] of (spec.success.unchangedFixtures ?? []).entries()) {
|
|
4964
|
+
if (!workspaceFixtures.has(path)) {
|
|
4965
|
+
ctx.addIssue({
|
|
4966
|
+
code: z20.ZodIssueCode.custom,
|
|
4967
|
+
path: ["success", "unchangedFixtures", i],
|
|
4968
|
+
message: `unchanged fixture "${path}" is not a seeded workspace file`
|
|
4969
|
+
});
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
for (const [i, expectation] of (spec.success.history ?? []).entries()) {
|
|
4973
|
+
if (expectation.maxEntries !== void 0 && expectation.minEntries > expectation.maxEntries) {
|
|
4974
|
+
ctx.addIssue({
|
|
4975
|
+
code: z20.ZodIssueCode.custom,
|
|
4976
|
+
path: ["success", "history", i],
|
|
4977
|
+
message: "minEntries must be less than or equal to maxEntries"
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4997
4981
|
});
|
|
4998
|
-
function
|
|
4999
|
-
|
|
4982
|
+
function parseCraftbookTestSpec(raw, opts) {
|
|
4983
|
+
const mode = opts?.mode ?? "strict";
|
|
4984
|
+
const candidate = mode === "tolerant" ? deepStripUnknown(raw) : raw;
|
|
4985
|
+
const parsed = CraftbookTestSpecSchema.safeParse(candidate);
|
|
4986
|
+
if (parsed.success) return { ok: true, spec: parsed.data };
|
|
4987
|
+
return {
|
|
4988
|
+
ok: false,
|
|
4989
|
+
errors: parsed.error.issues.map((issue) => {
|
|
4990
|
+
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
|
|
4991
|
+
return `${path}: ${issue.message}`;
|
|
4992
|
+
})
|
|
4993
|
+
};
|
|
5000
4994
|
}
|
|
5001
|
-
function
|
|
5002
|
-
|
|
5003
|
-
|
|
4995
|
+
function deepStripUnknown(raw) {
|
|
4996
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
4997
|
+
const record = structuredClone(raw);
|
|
4998
|
+
const version = record.schemaVersion;
|
|
4999
|
+
if (typeof version === "number" && Number.isInteger(version) && version > CRAFTBOOK_TEST_SCHEMA_VERSION) {
|
|
5000
|
+
record.schemaVersion = CRAFTBOOK_TEST_SCHEMA_VERSION;
|
|
5001
|
+
}
|
|
5002
|
+
for (let pass = 0; pass < 16; pass++) {
|
|
5003
|
+
const attempt = CraftbookTestSpecSchema.safeParse(record);
|
|
5004
|
+
if (attempt.success) return record;
|
|
5005
|
+
const unknownKeyIssues = attempt.error.issues.filter(
|
|
5006
|
+
(issue) => issue.code === z20.ZodIssueCode.unrecognized_keys
|
|
5007
|
+
);
|
|
5008
|
+
if (unknownKeyIssues.length === 0) return record;
|
|
5009
|
+
for (const issue of unknownKeyIssues) {
|
|
5010
|
+
const target = resolvePath(record, issue.path);
|
|
5011
|
+
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
5012
|
+
for (const key of issue.keys) delete target[key];
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
return record;
|
|
5017
|
+
}
|
|
5018
|
+
function resolvePath(root, path) {
|
|
5019
|
+
let node = root;
|
|
5020
|
+
for (const segment of path) {
|
|
5021
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
5022
|
+
node = node[segment];
|
|
5023
|
+
}
|
|
5024
|
+
return node;
|
|
5004
5025
|
}
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5026
|
+
|
|
5027
|
+
// src/schemas/codex-setup.ts
|
|
5028
|
+
import { z as z21 } from "zod";
|
|
5029
|
+
var CodexSetupModelOptionSchema = z21.object({
|
|
5030
|
+
id: z21.string().min(1),
|
|
5031
|
+
label: z21.string().min(1),
|
|
5032
|
+
description: z21.string().optional(),
|
|
5033
|
+
kind: z21.enum(["gezel", "model"]).default("model"),
|
|
5034
|
+
provider: z21.string().min(1),
|
|
5035
|
+
/** Stable gezel id for persona-backed entries. Absent on raw-model entries. */
|
|
5036
|
+
gezelId: z21.string().min(1).optional(),
|
|
5037
|
+
role: z21.string().min(1).optional(),
|
|
5038
|
+
/** Human-readable name of the effective inference model behind a gezel. */
|
|
5039
|
+
modelLabel: z21.string().min(1).optional(),
|
|
5040
|
+
contextWindow: z21.number().int().positive().optional(),
|
|
5041
|
+
supportsReasoning: z21.boolean().optional(),
|
|
5042
|
+
supportsTools: z21.boolean().optional()
|
|
5008
5043
|
});
|
|
5009
|
-
var
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5044
|
+
var CodexSetupStateSchema = z21.enum([
|
|
5045
|
+
"not-configured",
|
|
5046
|
+
"configured",
|
|
5047
|
+
"update-needed",
|
|
5048
|
+
"conflict",
|
|
5049
|
+
"unavailable"
|
|
5050
|
+
]);
|
|
5051
|
+
var CodexSetupStatusResponseSchema = z21.object({
|
|
5052
|
+
state: CodexSetupStateSchema,
|
|
5053
|
+
models: z21.array(CodexSetupModelOptionSchema),
|
|
5054
|
+
configuredModel: z21.string().optional(),
|
|
5055
|
+
recommendedModel: z21.string().optional(),
|
|
5056
|
+
reasons: z21.array(z21.string()),
|
|
5057
|
+
message: z21.string().optional(),
|
|
5058
|
+
codexInstalled: z21.boolean(),
|
|
5059
|
+
codexVersion: z21.string().optional(),
|
|
5060
|
+
codexPath: z21.string().optional(),
|
|
5061
|
+
endpointsEnabled: z21.boolean(),
|
|
5062
|
+
profileName: z21.string().min(1),
|
|
5063
|
+
profilePath: z21.string().min(1),
|
|
5064
|
+
launchCommand: z21.string().min(1),
|
|
5065
|
+
bridge: z21.object({
|
|
5066
|
+
baseUrl: z21.string().url(),
|
|
5067
|
+
listening: z21.boolean(),
|
|
5068
|
+
port: z21.number().int().nonnegative()
|
|
5069
|
+
}),
|
|
5070
|
+
canConfigure: z21.boolean(),
|
|
5071
|
+
/** Whether Gezel-owned credential/state material exists and can be safely removed. */
|
|
5072
|
+
canRemove: z21.boolean()
|
|
5015
5073
|
});
|
|
5016
|
-
var
|
|
5017
|
-
|
|
5018
|
-
path: z21.string(),
|
|
5019
|
-
isDirectory: z21.boolean(),
|
|
5020
|
-
/** File mtime (ms epoch). Populated only when the caller opts into stats. */
|
|
5021
|
-
mtimeMs: z21.number().optional()
|
|
5074
|
+
var ConfigureCodexRequestSchema = z21.object({
|
|
5075
|
+
model: z21.string().min(1)
|
|
5022
5076
|
});
|
|
5023
|
-
var ProjectGithubSchema = ProjectGitHubSchema;
|
|
5024
5077
|
|
|
5025
5078
|
// src/schemas/project-local.ts
|
|
5026
5079
|
import { z as z22 } from "zod";
|
|
@@ -5532,6 +5585,8 @@ var UpdateTaskStepRequestSchema = z23.object({
|
|
|
5532
5585
|
/** Step automation hooks (single ref or ordered list). `null` detaches. */
|
|
5533
5586
|
onEnter: ScriptRefListSchema.nullable().optional(),
|
|
5534
5587
|
onExit: ScriptRefListSchema.nullable().optional(),
|
|
5588
|
+
/** Required file inputs for the step. `null` clears the declaration. */
|
|
5589
|
+
consumes: z23.array(CraftbookStepInputSchema).min(1).nullable().optional(),
|
|
5535
5590
|
/** Auto-advance contract. `null` clears it. */
|
|
5536
5591
|
advanceWhen: AdvanceWhenSchema.nullable().optional(),
|
|
5537
5592
|
/** The end-of-step gate (current or legacy shape). `null` clears it. */
|
|
@@ -6461,6 +6516,13 @@ var ConnectorActionSchema = z26.object({
|
|
|
6461
6516
|
/** Consent gate this action clears at commit time (e.g. `recipient-allowlist`). */
|
|
6462
6517
|
consentScope: z26.string()
|
|
6463
6518
|
});
|
|
6519
|
+
var ConnectorSetupInstructionsSchema = z26.object({
|
|
6520
|
+
title: z26.string().min(1),
|
|
6521
|
+
description: z26.string().min(1).optional(),
|
|
6522
|
+
steps: z26.array(z26.string().min(1)).optional(),
|
|
6523
|
+
url: z26.string().url().optional(),
|
|
6524
|
+
urlLabel: z26.string().min(1).optional()
|
|
6525
|
+
});
|
|
6464
6526
|
var ConnectorTypeCompositionShape = {
|
|
6465
6527
|
/** Which driver executes the fetch. */
|
|
6466
6528
|
driver: ConnectorDriverSchema,
|
|
@@ -6468,6 +6530,8 @@ var ConnectorTypeCompositionShape = {
|
|
|
6468
6530
|
configSchema: z26.record(z26.string(), z26.unknown()).optional(),
|
|
6469
6531
|
/** Shape of the credential the binding stores in the SecretStore. */
|
|
6470
6532
|
secretShape: z26.record(z26.string(), z26.unknown()).optional(),
|
|
6533
|
+
/** Concise setup guidance rendered above the binding form. */
|
|
6534
|
+
setupInstructions: ConnectorSetupInstructionsSchema.optional(),
|
|
6471
6535
|
/**
|
|
6472
6536
|
* Driver-specific fetch config: `{adapterId}` (native) | `{server,list,fetch}`
|
|
6473
6537
|
* (mcp) | `{fetch|cli}` (script) | `{component,action,...}` (spectral).
|
|
@@ -6777,6 +6841,15 @@ var ChatModelMlxSourceSchema = z26.object({
|
|
|
6777
6841
|
});
|
|
6778
6842
|
var ChatModelIdentitySchema = IdentityCommonSchema.extend({
|
|
6779
6843
|
kind: z26.literal("chat-model"),
|
|
6844
|
+
/**
|
|
6845
|
+
* Organization that created the core model weights when it differs from
|
|
6846
|
+
* the catalog maintainer (for example, a quant maintained by its converter).
|
|
6847
|
+
* Omit when `maintainer` already names the maker.
|
|
6848
|
+
*/
|
|
6849
|
+
maker: z26.object({
|
|
6850
|
+
name: z26.string().min(1),
|
|
6851
|
+
url: z26.string().url().optional()
|
|
6852
|
+
}).optional(),
|
|
6780
6853
|
parameterSize: z26.string(),
|
|
6781
6854
|
supportsTools: z26.boolean(),
|
|
6782
6855
|
contextWindow: z26.number().int().positive().optional(),
|
|
@@ -6874,6 +6947,11 @@ var ChatModelManifestSchema = z26.object({
|
|
|
6874
6947
|
name: z26.string(),
|
|
6875
6948
|
url: z26.string().url().optional()
|
|
6876
6949
|
}),
|
|
6950
|
+
/** Core-model maker; present when it differs from `maintainer`. */
|
|
6951
|
+
maker: z26.object({
|
|
6952
|
+
name: z26.string().min(1),
|
|
6953
|
+
url: z26.string().url().optional()
|
|
6954
|
+
}).optional(),
|
|
6877
6955
|
logo: z26.string().optional(),
|
|
6878
6956
|
license: z26.string().optional(),
|
|
6879
6957
|
...LicenseMetaShape,
|
|
@@ -7685,6 +7763,18 @@ var ChatSessionSchema = z28.object({
|
|
|
7685
7763
|
}),
|
|
7686
7764
|
/** Snapshot of the gezel's about.md at session creation, for drift warnings. */
|
|
7687
7765
|
aboutSnapshot: z28.string().optional(),
|
|
7766
|
+
/**
|
|
7767
|
+
* Per-session override of `config.roleBasedNameOnlyMode` ("boring mode").
|
|
7768
|
+
* Stamped at creation when the creating client has a fixed presentation
|
|
7769
|
+
* mode — the TUI always renders role-based labels, so it pins the
|
|
7770
|
+
* sessions it creates to `true`. This keeps the prompt (what the model
|
|
7771
|
+
* is told about other gezels) consistent with what that client shows;
|
|
7772
|
+
* without it the TUI displayed `reviewer:` while the system prompt said
|
|
7773
|
+
* "the voorman is Tomas", and the model naturally leaked names into
|
|
7774
|
+
* prose. Unset = follow the live config flag, which is what desktop
|
|
7775
|
+
* sessions do.
|
|
7776
|
+
*/
|
|
7777
|
+
roleBasedNameOnlyMode: z28.boolean().optional(),
|
|
7688
7778
|
/** Set when the last attempted resume failed — UI surfaces a banner. */
|
|
7689
7779
|
resumeFailed: z28.boolean().optional(),
|
|
7690
7780
|
/**
|
|
@@ -7913,7 +8003,14 @@ var CreateChatSessionRequestSchema = z28.object({
|
|
|
7913
8003
|
projectId: z28.string().optional(),
|
|
7914
8004
|
taskRef: z28.string().optional(),
|
|
7915
8005
|
stepId: z28.string().optional(),
|
|
7916
|
-
craftbookRef: z28.string().optional()
|
|
8006
|
+
craftbookRef: z28.string().optional(),
|
|
8007
|
+
/**
|
|
8008
|
+
* Pin the session's name-rendering mode instead of following
|
|
8009
|
+
* `config.roleBasedNameOnlyMode`. Passed by clients whose presentation
|
|
8010
|
+
* mode is fixed (the TUI) so prompt-side name rendering matches their
|
|
8011
|
+
* labels. See `ChatSessionSchema.roleBasedNameOnlyMode`.
|
|
8012
|
+
*/
|
|
8013
|
+
roleBasedNameOnlyMode: z28.boolean().optional()
|
|
7917
8014
|
});
|
|
7918
8015
|
var ListChatSessionsResponseSchema = z28.object({
|
|
7919
8016
|
sessions: z28.array(ChatSessionSummarySchema)
|
|
@@ -8539,13 +8636,26 @@ var GitHubPullFileSchema = z33.object({
|
|
|
8539
8636
|
additions: z33.number().int(),
|
|
8540
8637
|
deletions: z33.number().int(),
|
|
8541
8638
|
changes: z33.number().int(),
|
|
8542
|
-
/**
|
|
8639
|
+
/** Unified diff hunk, if requested and returned by GitHub. */
|
|
8543
8640
|
patch: z33.string().optional(),
|
|
8641
|
+
/** Character count before Gezel's local patch budget was applied. */
|
|
8642
|
+
patchChars: z33.number().int().nonnegative().optional(),
|
|
8643
|
+
/** True when Gezel clipped `patch`; callers must request the file/diff directly. */
|
|
8644
|
+
patchTruncated: z33.boolean().optional(),
|
|
8544
8645
|
/** Prior path when `status === 'renamed'`. */
|
|
8545
8646
|
previousFilename: z33.string().optional()
|
|
8546
8647
|
});
|
|
8547
8648
|
var ListGitHubPullFilesResponseSchema = z33.object({
|
|
8548
|
-
files: z33.array(GitHubPullFileSchema)
|
|
8649
|
+
files: z33.array(GitHubPullFileSchema),
|
|
8650
|
+
/** Total files in the PR before an optional path filter. */
|
|
8651
|
+
allFiles: z33.number().int().nonnegative().optional(),
|
|
8652
|
+
/** Total files selected by the optional path filter. */
|
|
8653
|
+
totalFiles: z33.number().int().nonnegative().optional(),
|
|
8654
|
+
offset: z33.number().int().nonnegative().optional(),
|
|
8655
|
+
limit: z33.number().int().positive().optional(),
|
|
8656
|
+
hasMore: z33.boolean().optional(),
|
|
8657
|
+
nextOffset: z33.number().int().nonnegative().optional(),
|
|
8658
|
+
includesPatch: z33.boolean().optional()
|
|
8549
8659
|
});
|
|
8550
8660
|
var GitHubPullCommentSchema = z33.object({
|
|
8551
8661
|
id: z33.number(),
|
|
@@ -8562,7 +8672,14 @@ var ListGitHubPullCommentsResponseSchema = z33.object({
|
|
|
8562
8672
|
});
|
|
8563
8673
|
var GitHubPullDiffResponseSchema = z33.object({
|
|
8564
8674
|
number: z33.number().int(),
|
|
8565
|
-
diff: z33.string()
|
|
8675
|
+
diff: z33.string(),
|
|
8676
|
+
/** Exact changed path when this is a file-scoped diff. */
|
|
8677
|
+
path: z33.string().optional(),
|
|
8678
|
+
offset: z33.number().int().nonnegative().optional(),
|
|
8679
|
+
returnedChars: z33.number().int().nonnegative().optional(),
|
|
8680
|
+
totalChars: z33.number().int().nonnegative().optional(),
|
|
8681
|
+
truncated: z33.boolean().optional(),
|
|
8682
|
+
nextOffset: z33.number().int().nonnegative().optional()
|
|
8566
8683
|
});
|
|
8567
8684
|
var GitHubCreateCommentRequestSchema = z33.object({
|
|
8568
8685
|
body: z33.string().min(1)
|
|
@@ -10850,6 +10967,13 @@ var GezelConfigSchema = z37.object({
|
|
|
10850
10967
|
* Advanced.
|
|
10851
10968
|
*/
|
|
10852
10969
|
showAdvancedFeatures: z37.boolean().optional(),
|
|
10970
|
+
/**
|
|
10971
|
+
* When `true`, very early work-in-progress surfaces are revealed in the
|
|
10972
|
+
* UI and CLI. This is a discoverability preference, not a security or
|
|
10973
|
+
* service-layer capability boundary. Defaults on in development builds
|
|
10974
|
+
* and off in releases; an explicit user choice always wins.
|
|
10975
|
+
*/
|
|
10976
|
+
showWorkInProgressFeatures: z37.boolean().optional(),
|
|
10853
10977
|
/**
|
|
10854
10978
|
* Debug-only opt-in: when `true`, the service rewrites every
|
|
10855
10979
|
* template-derived gezel's `about.md` back to the prose its catalog
|
|
@@ -17460,6 +17584,7 @@ var STEP_FENCE_KEYS = [
|
|
|
17460
17584
|
"deliverable",
|
|
17461
17585
|
"onEnter",
|
|
17462
17586
|
"onExit",
|
|
17587
|
+
"consumes",
|
|
17463
17588
|
"advanceWhen",
|
|
17464
17589
|
"gate",
|
|
17465
17590
|
"next",
|
|
@@ -18757,6 +18882,68 @@ function isMoEFromTags(tags) {
|
|
|
18757
18882
|
});
|
|
18758
18883
|
}
|
|
18759
18884
|
|
|
18885
|
+
// src/model-attribution.ts
|
|
18886
|
+
function modelAttribution(manifest) {
|
|
18887
|
+
const sourceOwners = [
|
|
18888
|
+
manifest.llamaCpp?.huggingfaceRepo,
|
|
18889
|
+
manifest.mlx?.huggingfaceRepo,
|
|
18890
|
+
manifest.ds4?.huggingfaceRepo
|
|
18891
|
+
].map((repo) => repo ? huggingFaceOwnerFromRepo(repo) : null).filter((owner) => owner !== null);
|
|
18892
|
+
const explicitMakerOwner = manifest.maker?.url ? huggingFaceOwnerFromUrl(manifest.maker.url) : null;
|
|
18893
|
+
const maintainerOwner = manifest.maintainer.url ? huggingFaceOwnerFromUrl(manifest.maintainer.url) : null;
|
|
18894
|
+
const upstreamOwner = manifest.upstream ? huggingFaceOwnerFromUrl(manifest.upstream) : null;
|
|
18895
|
+
const normalizedSources = new Set(sourceOwners.map(normalizeOrganization));
|
|
18896
|
+
const maintainerIsSource = [manifest.maintainer.name, maintainerOwner].filter((name) => name !== null).some((name) => normalizedSources.has(normalizeOrganization(name)));
|
|
18897
|
+
const upstreamIsSource = upstreamOwner ? normalizedSources.has(normalizeOrganization(upstreamOwner)) : false;
|
|
18898
|
+
const makerFromUpstream = !manifest.maker && maintainerIsSource && upstreamOwner && !upstreamIsSource;
|
|
18899
|
+
const maker = manifest.maker?.name ?? (makerFromUpstream ? displayOrganizationName(upstreamOwner) : manifest.maintainer.name);
|
|
18900
|
+
const makerAliases = /* @__PURE__ */ new Set([normalizeOrganization(maker)]);
|
|
18901
|
+
if (explicitMakerOwner) makerAliases.add(normalizeOrganization(explicitMakerOwner));
|
|
18902
|
+
if (upstreamOwner) makerAliases.add(normalizeOrganization(upstreamOwner));
|
|
18903
|
+
if (!manifest.maker && !makerFromUpstream) {
|
|
18904
|
+
makerAliases.add(normalizeOrganization(manifest.maintainer.name));
|
|
18905
|
+
if (maintainerOwner) makerAliases.add(normalizeOrganization(maintainerOwner));
|
|
18906
|
+
}
|
|
18907
|
+
const customizers = [];
|
|
18908
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18909
|
+
const customizerCandidates = manifest.maker ? [manifest.maintainer.name, ...sourceOwners] : sourceOwners;
|
|
18910
|
+
for (const owner of customizerCandidates) {
|
|
18911
|
+
const normalized = normalizeOrganization(owner);
|
|
18912
|
+
if (!normalized || makerAliases.has(normalized) || seen.has(normalized)) continue;
|
|
18913
|
+
seen.add(normalized);
|
|
18914
|
+
customizers.push(owner);
|
|
18915
|
+
}
|
|
18916
|
+
return { maker, customizers };
|
|
18917
|
+
}
|
|
18918
|
+
function displayOrganizationName(owner) {
|
|
18919
|
+
const knownNames = {
|
|
18920
|
+
"deepseek-ai": "DeepSeek",
|
|
18921
|
+
"zai-org": "Z.ai"
|
|
18922
|
+
};
|
|
18923
|
+
return knownNames[owner.toLowerCase()] ?? owner;
|
|
18924
|
+
}
|
|
18925
|
+
function formatModelAttribution(manifest) {
|
|
18926
|
+
const attribution = modelAttribution(manifest);
|
|
18927
|
+
return attribution.customizers.length > 0 ? `${attribution.maker}, customized by ${attribution.customizers.join(", ")}` : attribution.maker;
|
|
18928
|
+
}
|
|
18929
|
+
function huggingFaceOwnerFromRepo(repo) {
|
|
18930
|
+
const slash = repo.indexOf("/");
|
|
18931
|
+
return slash > 0 ? repo.slice(0, slash) : null;
|
|
18932
|
+
}
|
|
18933
|
+
function huggingFaceOwnerFromUrl(value) {
|
|
18934
|
+
try {
|
|
18935
|
+
const url = new URL(value);
|
|
18936
|
+
if (url.hostname.toLowerCase() !== "huggingface.co") return null;
|
|
18937
|
+
const owner = url.pathname.split("/").filter(Boolean)[0];
|
|
18938
|
+
return owner ?? null;
|
|
18939
|
+
} catch {
|
|
18940
|
+
return null;
|
|
18941
|
+
}
|
|
18942
|
+
}
|
|
18943
|
+
function normalizeOrganization(value) {
|
|
18944
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
18945
|
+
}
|
|
18946
|
+
|
|
18760
18947
|
// src/fitness-badge.ts
|
|
18761
18948
|
var CHECK_LABELS = [
|
|
18762
18949
|
{ key: "spawn", label: "did not start" },
|
|
@@ -25379,6 +25566,11 @@ function collapseCraftbookForTier(book, opts) {
|
|
|
25379
25566
|
0,
|
|
25380
25567
|
...members.map((m) => m.gate ? normalizeStepGate(m.gate).maxAttempts ?? 0 : 0)
|
|
25381
25568
|
);
|
|
25569
|
+
const consumes = members.flatMap((member) => member.consumes ?? []).filter(
|
|
25570
|
+
(input, index, all) => all.findIndex(
|
|
25571
|
+
(candidate) => candidate.file === input.file && Boolean(candidate.artifact) === Boolean(input.artifact)
|
|
25572
|
+
) === index
|
|
25573
|
+
);
|
|
25382
25574
|
const isLast = groupIdx === groups.length - 1;
|
|
25383
25575
|
const nextGroupAnchor = groups[groupIdx + 1];
|
|
25384
25576
|
const nextId = nextGroupAnchor ? nextGroupAnchor.filter((m) => hasCompletionGate(m)).slice(-1)[0] ?? nextGroupAnchor[nextGroupAnchor.length - 1] : void 0;
|
|
@@ -25394,6 +25586,7 @@ function collapseCraftbookForTier(book, opts) {
|
|
|
25394
25586
|
const step = {
|
|
25395
25587
|
...anchor,
|
|
25396
25588
|
prompt: collapsedPrompt({ anchor, mergedNames, checks }),
|
|
25589
|
+
...consumes.length > 0 ? { consumes } : { consumes: void 0 },
|
|
25397
25590
|
gate,
|
|
25398
25591
|
// Terminal + advanceWhen is an illegal combination; the terminal
|
|
25399
25592
|
// group keeps only its completion gate.
|
|
@@ -25403,7 +25596,7 @@ function collapseCraftbookForTier(book, opts) {
|
|
|
25403
25596
|
advanceWhen: anchor.advanceWhen ? { ...anchor.advanceWhen, goto: void 0 } : void 0
|
|
25404
25597
|
}
|
|
25405
25598
|
};
|
|
25406
|
-
for (const key of ["terminal", "advanceWhen", "next"]) {
|
|
25599
|
+
for (const key of ["terminal", "advanceWhen", "next", "consumes"]) {
|
|
25407
25600
|
if (step[key] === void 0) delete step[key];
|
|
25408
25601
|
}
|
|
25409
25602
|
if (step.advanceWhen && step.advanceWhen.goto === void 0) {
|
|
@@ -25542,6 +25735,16 @@ function docFromCraftbook(book) {
|
|
|
25542
25735
|
};
|
|
25543
25736
|
}
|
|
25544
25737
|
function augmentGraphProblem(problem, stepIds) {
|
|
25738
|
+
const artifactInput = /^step "([^"]+)" consumes artifact "([^"]+)" but its prompt does not explicitly call `read_artifact`$/.exec(
|
|
25739
|
+
problem
|
|
25740
|
+
);
|
|
25741
|
+
if (artifactInput) {
|
|
25742
|
+
return {
|
|
25743
|
+
where: `steps (id "${artifactInput[1]}") \u2192 prompt`,
|
|
25744
|
+
message: `${problem}.`,
|
|
25745
|
+
fix: `start the procedure with \`read_artifact({ path: ${JSON.stringify(artifactInput[2])} })\`; artifact paths are not workspace paths`
|
|
25746
|
+
};
|
|
25747
|
+
}
|
|
25545
25748
|
const missing = /"([^"]+)" missing from steps/.exec(problem);
|
|
25546
25749
|
if (missing) {
|
|
25547
25750
|
const near = nearestMatch(missing[1], stepIds);
|
|
@@ -26861,9 +27064,38 @@ function deriveThreadTitleFromMessages(messages, options = {}) {
|
|
|
26861
27064
|
return starter ? deriveThreadTitle(starter.content) : null;
|
|
26862
27065
|
}
|
|
26863
27066
|
|
|
27067
|
+
// src/catalog-work-in-progress.ts
|
|
27068
|
+
var CONNECTOR_PROJECT_TYPE_BASES = /* @__PURE__ */ new Set(["email", "social-media"]);
|
|
27069
|
+
function resolveShowWorkInProgressFeatures(configured, buildVersion) {
|
|
27070
|
+
return configured ?? buildVersion === "0.0.0";
|
|
27071
|
+
}
|
|
27072
|
+
function catalogItemUsesConnectors(item, connectorCraftbookIds2 = /* @__PURE__ */ new Set()) {
|
|
27073
|
+
const manifest = item.manifest;
|
|
27074
|
+
if (manifest.kind === "craftbook-template") {
|
|
27075
|
+
return (manifest.connectors?.length ?? 0) > 0;
|
|
27076
|
+
}
|
|
27077
|
+
if (manifest.kind !== "project-type") return false;
|
|
27078
|
+
if (manifest.extends && CONNECTOR_PROJECT_TYPE_BASES.has(manifest.extends)) return true;
|
|
27079
|
+
if ((manifest.craftbooks ?? []).some((id) => connectorCraftbookIds2.has(id))) return true;
|
|
27080
|
+
return (manifest.schedules ?? []).some(
|
|
27081
|
+
(schedule) => connectorCraftbookIds2.has(schedule.craftbook)
|
|
27082
|
+
);
|
|
27083
|
+
}
|
|
27084
|
+
function connectorCraftbookIds(items) {
|
|
27085
|
+
return new Set(
|
|
27086
|
+
items.flatMap(
|
|
27087
|
+
(item) => item.manifest.kind === "craftbook-template" && catalogItemUsesConnectors(item) ? [item.manifest.id] : []
|
|
27088
|
+
)
|
|
27089
|
+
);
|
|
27090
|
+
}
|
|
27091
|
+
function visibleCatalogItems(items, showWorkInProgressFeatures, connectorIds = connectorCraftbookIds(items)) {
|
|
27092
|
+
if (showWorkInProgressFeatures) return [...items];
|
|
27093
|
+
return items.filter((item) => !catalogItemUsesConnectors(item, connectorIds));
|
|
27094
|
+
}
|
|
27095
|
+
|
|
26864
27096
|
// src/index.ts
|
|
26865
|
-
var GEZEL_VERSION = "1.0.
|
|
26866
|
-
var GEZEL_CONTENT_COMPAT = "1.
|
|
27097
|
+
var GEZEL_VERSION = "1.0.3";
|
|
27098
|
+
var GEZEL_CONTENT_COMPAT = "1.26226";
|
|
26867
27099
|
function nowIso() {
|
|
26868
27100
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
26869
27101
|
}
|
|
@@ -26990,6 +27222,7 @@ export {
|
|
|
26990
27222
|
CraftbookSchema,
|
|
26991
27223
|
CraftbookScriptsSchema,
|
|
26992
27224
|
CraftbookSpawnSchema,
|
|
27225
|
+
CraftbookStepInputSchema,
|
|
26993
27226
|
CraftbookStepSchema,
|
|
26994
27227
|
CraftbookSuggestionSchema,
|
|
26995
27228
|
CraftbookSummarySchema,
|
|
@@ -27895,6 +28128,7 @@ export {
|
|
|
27895
28128
|
blockSize,
|
|
27896
28129
|
buildIssueUrl,
|
|
27897
28130
|
buildSuiteScoreboard,
|
|
28131
|
+
catalogItemUsesConnectors,
|
|
27898
28132
|
cellAttributableTrials,
|
|
27899
28133
|
classifySecurityLevel,
|
|
27900
28134
|
coerceDeliverableKind,
|
|
@@ -27906,6 +28140,7 @@ export {
|
|
|
27906
28140
|
composeFileContext,
|
|
27907
28141
|
composeFitnessBadge,
|
|
27908
28142
|
computeModelFit,
|
|
28143
|
+
connectorCraftbookIds,
|
|
27909
28144
|
craftbookDocFormatFromEnv,
|
|
27910
28145
|
craftbookFromDoc,
|
|
27911
28146
|
craftbookRequirementsMet,
|
|
@@ -27948,6 +28183,7 @@ export {
|
|
|
27948
28183
|
formatCraftbookDocErrors,
|
|
27949
28184
|
formatErrorReport,
|
|
27950
28185
|
formatJsonSchemaViolations,
|
|
28186
|
+
formatModelAttribution,
|
|
27951
28187
|
formatNpmRegistrySpec,
|
|
27952
28188
|
formatPassClaim,
|
|
27953
28189
|
formatReviewProvenance,
|
|
@@ -28024,6 +28260,7 @@ export {
|
|
|
28024
28260
|
meetsCapabilityFloor,
|
|
28025
28261
|
mergeStreets,
|
|
28026
28262
|
metaToFormValue,
|
|
28263
|
+
modelAttribution,
|
|
28027
28264
|
modelFitnessKey,
|
|
28028
28265
|
nearestFreeRect,
|
|
28029
28266
|
nearestMatch,
|
|
@@ -28098,6 +28335,7 @@ export {
|
|
|
28098
28335
|
resolveRoleId,
|
|
28099
28336
|
resolveSandboxCopilot,
|
|
28100
28337
|
resolveSecurityPolicy,
|
|
28338
|
+
resolveShowWorkInProgressFeatures,
|
|
28101
28339
|
resolveSteps,
|
|
28102
28340
|
retryTransient,
|
|
28103
28341
|
rewriteGezelHrefs,
|
|
@@ -28154,6 +28392,7 @@ export {
|
|
|
28154
28392
|
validateScriptInput,
|
|
28155
28393
|
verifyBinaryDocumentBytes,
|
|
28156
28394
|
videoMemoryBudgetBytes,
|
|
28395
|
+
visibleCatalogItems,
|
|
28157
28396
|
workshopTempoDefaults,
|
|
28158
28397
|
writeProcessOutput,
|
|
28159
28398
|
xpForLevel,
|