@archmax-ai/harness 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +219 -0
- package/dist/agent.d.ts +174 -0
- package/dist/agent.js +140 -0
- package/dist/agent.js.map +1 -0
- package/dist/assembly/compose.d.ts +170 -0
- package/dist/assembly/compose.js +391 -0
- package/dist/assembly/compose.js.map +1 -0
- package/dist/assembly/delegation-registry.d.ts +5 -0
- package/dist/assembly/delegation-registry.js +118 -0
- package/dist/assembly/delegation-registry.js.map +1 -0
- package/dist/assembly/index.d.ts +192 -0
- package/dist/assembly/index.js +265 -0
- package/dist/assembly/index.js.map +1 -0
- package/dist/assembly/plain.d.ts +7 -0
- package/dist/assembly/plain.js +41 -0
- package/dist/assembly/plain.js.map +1 -0
- package/dist/authoring-skill/archmax-harness/README.md +76 -0
- package/dist/authoring-skill/archmax-harness/SKILL.md +241 -0
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +626 -0
- package/dist/authoring-skill/archmax-harness/references/cases.md +41 -0
- package/dist/authoring-skill/archmax-harness/references/diagnostics.md +51 -0
- package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +1070 -0
- package/dist/authoring-skill/archmax-harness/references/hooks-and-scripts.md +82 -0
- package/dist/authoring-skill/archmax-harness/references/wiring.md +101 -0
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +1631 -0
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +241 -0
- package/dist/behaviour/support.d.ts +232 -0
- package/dist/behaviour/support.js +339 -0
- package/dist/behaviour/support.js.map +1 -0
- package/dist/cli/banner.d.ts +11 -0
- package/dist/cli/banner.js +29 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +33 -0
- package/dist/cli/bootstrap.js +153 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/command.d.ts +39 -0
- package/dist/cli/command.js +97 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/state-flow.d.ts +46 -0
- package/dist/cli/state-flow.js +268 -0
- package/dist/cli/state-flow.js.map +1 -0
- package/dist/cli/style.d.ts +41 -0
- package/dist/cli/style.js +64 -0
- package/dist/cli/style.js.map +1 -0
- package/dist/cli/turn.d.ts +41 -0
- package/dist/cli/turn.js +161 -0
- package/dist/cli/turn.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +426 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/code-description.d.ts +22 -0
- package/dist/core/code-description.js +38 -0
- package/dist/core/code-description.js.map +1 -0
- package/dist/core/deepagents.d.ts +183 -0
- package/dist/core/deepagents.js +128 -0
- package/dist/core/deepagents.js.map +1 -0
- package/dist/core/events.d.ts +350 -0
- package/dist/core/events.js +205 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/frontmatter.d.ts +15 -0
- package/dist/core/frontmatter.js +32 -0
- package/dist/core/frontmatter.js.map +1 -0
- package/dist/core/listing-redact.d.ts +37 -0
- package/dist/core/listing-redact.js +88 -0
- package/dist/core/listing-redact.js.map +1 -0
- package/dist/core/match.d.ts +26 -0
- package/dist/core/match.js +75 -0
- package/dist/core/match.js.map +1 -0
- package/dist/core/message-readers.d.ts +84 -0
- package/dist/core/message-readers.js +159 -0
- package/dist/core/message-readers.js.map +1 -0
- package/dist/core/messages.d.ts +49 -0
- package/dist/core/messages.js +85 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/mount-prompt.d.ts +28 -0
- package/dist/core/mount-prompt.js +55 -0
- package/dist/core/mount-prompt.js.map +1 -0
- package/dist/core/mount-redact.d.ts +28 -0
- package/dist/core/mount-redact.js +32 -0
- package/dist/core/mount-redact.js.map +1 -0
- package/dist/core/mounts.d.ts +88 -0
- package/dist/core/mounts.js +147 -0
- package/dist/core/mounts.js.map +1 -0
- package/dist/core/path-mapping.d.ts +52 -0
- package/dist/core/path-mapping.js +113 -0
- package/dist/core/path-mapping.js.map +1 -0
- package/dist/core/platform-prompt.generated.d.ts +2 -0
- package/dist/core/platform-prompt.generated.js +4 -0
- package/dist/core/platform-prompt.generated.js.map +1 -0
- package/dist/core/prompt.d.ts +42 -0
- package/dist/core/prompt.js +75 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/session-id.d.ts +38 -0
- package/dist/core/session-id.js +61 -0
- package/dist/core/session-id.js.map +1 -0
- package/dist/core/session-store.d.ts +85 -0
- package/dist/core/session-store.js +149 -0
- package/dist/core/session-store.js.map +1 -0
- package/dist/core/session-zone.d.ts +54 -0
- package/dist/core/session-zone.js +82 -0
- package/dist/core/session-zone.js.map +1 -0
- package/dist/core/skill-prompt.d.ts +31 -0
- package/dist/core/skill-prompt.js +54 -0
- package/dist/core/skill-prompt.js.map +1 -0
- package/dist/core/skill-redact.d.ts +15 -0
- package/dist/core/skill-redact.js +27 -0
- package/dist/core/skill-redact.js.map +1 -0
- package/dist/core/skills.d.ts +172 -0
- package/dist/core/skills.js +214 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/tool-mocks.d.ts +23 -0
- package/dist/core/tool-mocks.js +39 -0
- package/dist/core/tool-mocks.js.map +1 -0
- package/dist/core/tool-telemetry.d.ts +16 -0
- package/dist/core/tool-telemetry.js +40 -0
- package/dist/core/tool-telemetry.js.map +1 -0
- package/dist/core/usage.d.ts +114 -0
- package/dist/core/usage.js +226 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/workspace-context.d.ts +136 -0
- package/dist/core/workspace-context.js +178 -0
- package/dist/core/workspace-context.js.map +1 -0
- package/dist/core/workspace-prompt.d.ts +7 -0
- package/dist/core/workspace-prompt.js +102 -0
- package/dist/core/workspace-prompt.js.map +1 -0
- package/dist/core/workspace-router.d.ts +61 -0
- package/dist/core/workspace-router.js +145 -0
- package/dist/core/workspace-router.js.map +1 -0
- package/dist/core/workspace.d.ts +85 -0
- package/dist/core/workspace.js +193 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/core/zones.d.ts +181 -0
- package/dist/core/zones.js +202 -0
- package/dist/core/zones.js.map +1 -0
- package/dist/env.d.ts +99 -0
- package/dist/env.js +271 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +265 -0
- package/dist/index.js +197 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/kernel.d.ts +192 -0
- package/dist/kernel/kernel.js +751 -0
- package/dist/kernel/kernel.js.map +1 -0
- package/dist/lifecycle/hook-shape.d.ts +44 -0
- package/dist/lifecycle/hook-shape.js +66 -0
- package/dist/lifecycle/hook-shape.js.map +1 -0
- package/dist/lifecycle/runner.d.ts +177 -0
- package/dist/lifecycle/runner.js +372 -0
- package/dist/lifecycle/runner.js.map +1 -0
- package/dist/lifecycle/transcript.d.ts +27 -0
- package/dist/lifecycle/transcript.js +43 -0
- package/dist/lifecycle/transcript.js.map +1 -0
- package/dist/machine/allow.d.ts +65 -0
- package/dist/machine/allow.js +107 -0
- package/dist/machine/allow.js.map +1 -0
- package/dist/machine/delegation.d.ts +9 -0
- package/dist/machine/delegation.js +10 -0
- package/dist/machine/delegation.js.map +1 -0
- package/dist/machine/diagnostic.d.ts +14 -0
- package/dist/machine/diagnostic.js +2 -0
- package/dist/machine/diagnostic.js.map +1 -0
- package/dist/machine/lint-spec.d.ts +4 -0
- package/dist/machine/lint-spec.js +266 -0
- package/dist/machine/lint-spec.js.map +1 -0
- package/dist/machine/load-spec.d.ts +90 -0
- package/dist/machine/load-spec.js +120 -0
- package/dist/machine/load-spec.js.map +1 -0
- package/dist/machine/machine.d.ts +273 -0
- package/dist/machine/machine.js +551 -0
- package/dist/machine/machine.js.map +1 -0
- package/dist/machine/mount-grants.d.ts +25 -0
- package/dist/machine/mount-grants.js +24 -0
- package/dist/machine/mount-grants.js.map +1 -0
- package/dist/machine/slug.d.ts +14 -0
- package/dist/machine/slug.js +17 -0
- package/dist/machine/slug.js.map +1 -0
- package/dist/machine/spec-schema.d.ts +662 -0
- package/dist/machine/spec-schema.js +717 -0
- package/dist/machine/spec-schema.js.map +1 -0
- package/dist/machine/tool-names.d.ts +98 -0
- package/dist/machine/tool-names.js +144 -0
- package/dist/machine/tool-names.js.map +1 -0
- package/dist/machine/triggers.d.ts +114 -0
- package/dist/machine/triggers.js +165 -0
- package/dist/machine/triggers.js.map +1 -0
- package/dist/machine/types.d.ts +186 -0
- package/dist/machine/types.js +12 -0
- package/dist/machine/types.js.map +1 -0
- package/dist/machine/validate-spec.d.ts +42 -0
- package/dist/machine/validate-spec.js +29 -0
- package/dist/machine/validate-spec.js.map +1 -0
- package/dist/machine/variables.d.ts +196 -0
- package/dist/machine/variables.js +415 -0
- package/dist/machine/variables.js.map +1 -0
- package/dist/public/cli.d.ts +15 -0
- package/dist/public/cli.js +14 -0
- package/dist/public/cli.js.map +1 -0
- package/dist/public/messages.d.ts +33 -0
- package/dist/public/messages.js +33 -0
- package/dist/public/messages.js.map +1 -0
- package/dist/public/sandbox.d.ts +99 -0
- package/dist/public/sandbox.js +32 -0
- package/dist/public/sandbox.js.map +1 -0
- package/dist/public/spec.d.ts +147 -0
- package/dist/public/spec.js +149 -0
- package/dist/public/spec.js.map +1 -0
- package/dist/public/testing.d.ts +71 -0
- package/dist/public/testing.js +39 -0
- package/dist/public/testing.js.map +1 -0
- package/dist/rubrics/rubrics.d.ts +135 -0
- package/dist/rubrics/rubrics.js +145 -0
- package/dist/rubrics/rubrics.js.map +1 -0
- package/dist/runtime/contract.d.ts +73 -0
- package/dist/runtime/contract.js +96 -0
- package/dist/runtime/contract.js.map +1 -0
- package/dist/sandbox/assets/parts/core.js +22 -0
- package/dist/sandbox/assets/parts/lifecycle-hook.js +47 -0
- package/dist/sandbox/bridge.d.ts +16 -0
- package/dist/sandbox/bridge.js +95 -0
- package/dist/sandbox/bridge.js.map +1 -0
- package/dist/sandbox/executor.d.ts +46 -0
- package/dist/sandbox/executor.js +119 -0
- package/dist/sandbox/executor.js.map +1 -0
- package/dist/sandbox/imports.d.ts +33 -0
- package/dist/sandbox/imports.js +67 -0
- package/dist/sandbox/imports.js.map +1 -0
- package/dist/sandbox/prelude.d.ts +12 -0
- package/dist/sandbox/prelude.js +51 -0
- package/dist/sandbox/prelude.js.map +1 -0
- package/dist/sandbox/ptc-gateway.d.ts +104 -0
- package/dist/sandbox/ptc-gateway.js +167 -0
- package/dist/sandbox/ptc-gateway.js.map +1 -0
- package/dist/sandbox/runtime.d.ts +59 -0
- package/dist/sandbox/runtime.js +51 -0
- package/dist/sandbox/runtime.js.map +1 -0
- package/dist/sandbox/tools.d.ts +34 -0
- package/dist/sandbox/tools.js +147 -0
- package/dist/sandbox/tools.js.map +1 -0
- package/dist/sessions/operations.d.ts +79 -0
- package/dist/sessions/operations.js +121 -0
- package/dist/sessions/operations.js.map +1 -0
- package/dist/sessions/resolve.d.ts +97 -0
- package/dist/sessions/resolve.js +145 -0
- package/dist/sessions/resolve.js.map +1 -0
- package/dist/sessions/resume.d.ts +242 -0
- package/dist/sessions/resume.js +288 -0
- package/dist/sessions/resume.js.map +1 -0
- package/dist/sessions/scope.d.ts +99 -0
- package/dist/sessions/scope.js +145 -0
- package/dist/sessions/scope.js.map +1 -0
- package/dist/sessions/summary.d.ts +65 -0
- package/dist/sessions/summary.js +56 -0
- package/dist/sessions/summary.js.map +1 -0
- package/dist/testing/assertions.d.ts +30 -0
- package/dist/testing/assertions.js +231 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/case-document.d.ts +141 -0
- package/dist/testing/case-document.js +51 -0
- package/dist/testing/case-document.js.map +1 -0
- package/dist/testing/case-schema.d.ts +29 -0
- package/dist/testing/case-schema.js +519 -0
- package/dist/testing/case-schema.js.map +1 -0
- package/dist/testing/discovery.d.ts +10 -0
- package/dist/testing/discovery.js +24 -0
- package/dist/testing/discovery.js.map +1 -0
- package/dist/testing/driver.d.ts +78 -0
- package/dist/testing/driver.js +117 -0
- package/dist/testing/driver.js.map +1 -0
- package/dist/testing/grade.d.ts +116 -0
- package/dist/testing/grade.js +261 -0
- package/dist/testing/grade.js.map +1 -0
- package/dist/testing/interpreter.d.ts +55 -0
- package/dist/testing/interpreter.js +103 -0
- package/dist/testing/interpreter.js.map +1 -0
- package/dist/testing/mock-middleware.d.ts +5 -0
- package/dist/testing/mock-middleware.js +33 -0
- package/dist/testing/mock-middleware.js.map +1 -0
- package/dist/testing/runner.d.ts +205 -0
- package/dist/testing/runner.js +266 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/target.d.ts +27 -0
- package/dist/testing/target.js +21 -0
- package/dist/testing/target.js.map +1 -0
- package/dist/validate/validate.d.ts +41 -0
- package/dist/validate/validate.js +669 -0
- package/dist/validate/validate.js.map +1 -0
- package/dist/workflow/agent-tools.d.ts +10 -0
- package/dist/workflow/agent-tools.js +10 -0
- package/dist/workflow/agent-tools.js.map +1 -0
- package/dist/workflow/checkpointer.d.ts +52 -0
- package/dist/workflow/checkpointer.js +200 -0
- package/dist/workflow/checkpointer.js.map +1 -0
- package/dist/workflow/control-tools.d.ts +174 -0
- package/dist/workflow/control-tools.js +468 -0
- package/dist/workflow/control-tools.js.map +1 -0
- package/dist/workflow/governance.d.ts +104 -0
- package/dist/workflow/governance.js +546 -0
- package/dist/workflow/governance.js.map +1 -0
- package/dist/workflow/middleware.d.ts +85 -0
- package/dist/workflow/middleware.js +283 -0
- package/dist/workflow/middleware.js.map +1 -0
- package/dist/workflow/on-error.d.ts +25 -0
- package/dist/workflow/on-error.js +58 -0
- package/dist/workflow/on-error.js.map +1 -0
- package/dist/workflow/parks.d.ts +46 -0
- package/dist/workflow/parks.js +513 -0
- package/dist/workflow/parks.js.map +1 -0
- package/dist/workflow/paths.d.ts +67 -0
- package/dist/workflow/paths.js +108 -0
- package/dist/workflow/paths.js.map +1 -0
- package/dist/workflow/prompt-cache.d.ts +70 -0
- package/dist/workflow/prompt-cache.js +103 -0
- package/dist/workflow/prompt-cache.js.map +1 -0
- package/dist/workflow/prompt-pruning.d.ts +20 -0
- package/dist/workflow/prompt-pruning.js +83 -0
- package/dist/workflow/prompt-pruning.js.map +1 -0
- package/dist/workflow/render-prompt.d.ts +45 -0
- package/dist/workflow/render-prompt.js +140 -0
- package/dist/workflow/render-prompt.js.map +1 -0
- package/dist/workflow/runtime-config.d.ts +7 -0
- package/dist/workflow/runtime-config.js +9 -0
- package/dist/workflow/runtime-config.js.map +1 -0
- package/dist/workflow/session-artifacts.d.ts +90 -0
- package/dist/workflow/session-artifacts.js +134 -0
- package/dist/workflow/session-artifacts.js.map +1 -0
- package/dist/workflow/session-view.d.ts +31 -0
- package/dist/workflow/session-view.js +94 -0
- package/dist/workflow/session-view.js.map +1 -0
- package/dist/workflow/snapshot.d.ts +14 -0
- package/dist/workflow/snapshot.js +97 -0
- package/dist/workflow/snapshot.js.map +1 -0
- package/dist/workflow/state.d.ts +347 -0
- package/dist/workflow/state.js +322 -0
- package/dist/workflow/state.js.map +1 -0
- package/dist/workflow/sub-workflow.d.ts +174 -0
- package/dist/workflow/sub-workflow.js +392 -0
- package/dist/workflow/sub-workflow.js.map +1 -0
- package/dist/workflow/tool-service.d.ts +48 -0
- package/dist/workflow/tool-service.js +291 -0
- package/dist/workflow/tool-service.js.map +1 -0
- package/dist/workflow/turn-boundary.d.ts +39 -0
- package/dist/workflow/turn-boundary.js +128 -0
- package/dist/workflow/turn-boundary.js.map +1 -0
- package/dist/workflow/turn-runner.d.ts +40 -0
- package/dist/workflow/turn-runner.js +122 -0
- package/dist/workflow/turn-runner.js.map +1 -0
- package/dist/workflow/workflow-tools.d.ts +46 -0
- package/dist/workflow/workflow-tools.js +90 -0
- package/dist/workflow/workflow-tools.js.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { hasUsage } from "../core/usage.js";
|
|
3
|
+
import { isReservedRootName } from "../core/zones.js";
|
|
4
|
+
import { classifyStatus, parkedStateOf, usageSummarySchema, variableEntrySchema, } from "../workflow/state.js";
|
|
5
|
+
import { parentSessionIdOf } from "./scope.js";
|
|
6
|
+
/**
|
|
7
|
+
* Project a session's latest checkpoint into a {@link SessionSummary} using only
|
|
8
|
+
* `getTuple`, so it works for **any** `BaseCheckpointSaver`. Session resolution
|
|
9
|
+
* depends on this answer being real: `null` means "this session has not run yet".
|
|
10
|
+
*/
|
|
11
|
+
export async function summarizeCheckpointedSession(checkpointer, sessionId) {
|
|
12
|
+
const tuple = await checkpointer.getTuple({ configurable: { thread_id: sessionId } });
|
|
13
|
+
if (!tuple)
|
|
14
|
+
return null;
|
|
15
|
+
const values = (tuple.checkpoint.channel_values ?? {});
|
|
16
|
+
const pendingDecision = values.pendingDecision;
|
|
17
|
+
const pendingInput = values.pendingInput;
|
|
18
|
+
const parkedAt = parkedStateOf(pendingDecision) || parkedStateOf(pendingInput);
|
|
19
|
+
const status = typeof values.status === "string" ? values.status : undefined;
|
|
20
|
+
// Parsed rather than cast: an older or malformed checkpoint reads as "none"
|
|
21
|
+
// instead of failing a listing.
|
|
22
|
+
const parsedVariables = z.record(z.string(), variableEntrySchema).safeParse(values.variables);
|
|
23
|
+
const variables = parsedVariables.success ? parsedVariables.data : undefined;
|
|
24
|
+
const parsedUsage = usageSummarySchema.safeParse(values.usage);
|
|
25
|
+
const usage = parsedUsage.success ? parsedUsage.data : undefined;
|
|
26
|
+
const parent = parentSessionIdOf(sessionId);
|
|
27
|
+
return {
|
|
28
|
+
sessionId,
|
|
29
|
+
status,
|
|
30
|
+
classification: classifyStatus(status),
|
|
31
|
+
workflowState: typeof values.workflowState === "string" ? values.workflowState : undefined,
|
|
32
|
+
specHash: typeof values.specHash === "string" ? values.specHash : undefined,
|
|
33
|
+
...(parkedAt ? { state: parkedAt, node: parkedAt } : {}),
|
|
34
|
+
...(typeof pendingInput?.reason === "string" ? { waitReason: pendingInput.reason } : {}),
|
|
35
|
+
...(typeof pendingInput?.resumeAt === "string" ? { resumeAt: pendingInput.resumeAt } : {}),
|
|
36
|
+
...(variables && Object.keys(variables).length > 0 ? { variables } : {}),
|
|
37
|
+
...(usage && hasUsage(usage) ? { usage } : {}),
|
|
38
|
+
...(parent ? { parentSessionId: parent } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Enumerate durable sessions by listing the session store's root (one
|
|
43
|
+
* `<sessionId>/` folder per session) and projecting each latest checkpoint.
|
|
44
|
+
* Runs outside any bound session, so the listing is the raw store root: reserved
|
|
45
|
+
* root names (authored mounts, `_specs/`, session areas) and sessions without a
|
|
46
|
+
* readable checkpoint are skipped.
|
|
47
|
+
*/
|
|
48
|
+
export async function listSessions(workspace, checkpointer) {
|
|
49
|
+
const entries = await workspace.listDir("");
|
|
50
|
+
const sessionIds = entries
|
|
51
|
+
.map((entry) => String(entry.path ?? "").replace(/\/+$/, "").split("/").pop() ?? "")
|
|
52
|
+
.filter((sessionId) => sessionId && !isReservedRootName(sessionId));
|
|
53
|
+
const summaries = await Promise.all(sessionIds.map((id) => summarizeCheckpointedSession(checkpointer, id)));
|
|
54
|
+
return summaries.filter((summary) => summary !== null);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/sessions/summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side evaluation of a case's assertions against a driven turn's
|
|
3
|
+
* {@link SessionView}. Every assertion the case format offers is a hard failure —
|
|
4
|
+
* including `grade`, whose declared `atLeast` is the author's bar, not a note —
|
|
5
|
+
* so a record's `status` is the only thing verdict reduction and the CLI's
|
|
6
|
+
* test view need to read.
|
|
7
|
+
*/
|
|
8
|
+
import type { SessionView } from "../workflow/session-view.js";
|
|
9
|
+
import type { AssertionRecord } from "./runner.js";
|
|
10
|
+
import type { CaseExpectation } from "./case-schema.js";
|
|
11
|
+
import { type JudgeEvidence, type JudgeResult } from "./grade.js";
|
|
12
|
+
/** Scores one closed-QA criterion (wired to the suite's grading model). */
|
|
13
|
+
export type GradeFn = (criteria: string, evidence: JudgeEvidence) => Promise<JudgeResult>;
|
|
14
|
+
/** Whether a failure of this assertion kind halts the rest of the case. */
|
|
15
|
+
export declare function haltsCaseOnFailure(assert: CaseExpectation["assert"]): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Describe an assertion step that never ran. One record per step, never one per
|
|
18
|
+
* token: an un-run `reply` with three tokens would have produced three records,
|
|
19
|
+
* but the engine did not run it and does not invent outcomes it never computed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function notExecutedRecord(expect: CaseExpectation, step: number): AssertionRecord;
|
|
22
|
+
/**
|
|
23
|
+
* Evaluate one turn's expectations. `step` is the index, in the case
|
|
24
|
+
* document's flat `steps` list, of the step these entries came from; every
|
|
25
|
+
* record produced here is stamped with it, so attribution never depends on
|
|
26
|
+
* how many records an expectation happens to emit. The optional `grade`
|
|
27
|
+
* scores `grade` entries; when absent (no `tests.judge` in workflow.yaml), a
|
|
28
|
+
* grade entry records an actionable failure instead of silently passing.
|
|
29
|
+
*/
|
|
30
|
+
export declare function evaluateExpectations(entries: readonly CaseExpectation[], view: SessionView, step: number, grade?: GradeFn): Promise<AssertionRecord[]>;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { deepEqual, partialMatch } from "../core/match.js";
|
|
2
|
+
import { judgeEvidenceFromView } from "./grade.js";
|
|
3
|
+
import { resolvePath } from "../machine/variables.js";
|
|
4
|
+
/**
|
|
5
|
+
* The **structural** assertions: the ones that assert where the session went.
|
|
6
|
+
* Once one of them fails, every later step would drive or grade a session the
|
|
7
|
+
* case no longer describes — a `decide` after a failed `parked` cannot succeed,
|
|
8
|
+
* and a `reply` check after a failed `reachedState` grades the wrong state's
|
|
9
|
+
* output — so a failure here halts the case.
|
|
10
|
+
*
|
|
11
|
+
* The content assertions (`reply`, `calledTool`, `notCalledTool`, `usedNoTools`,
|
|
12
|
+
* `grade`) are deliberately absent: a wrong reply is a defect in the turn, not
|
|
13
|
+
* evidence that the session stopped being the one under test.
|
|
14
|
+
*/
|
|
15
|
+
const STRUCTURAL_ASSERTS = new Set([
|
|
16
|
+
"succeeded",
|
|
17
|
+
"parked",
|
|
18
|
+
"reachedState",
|
|
19
|
+
"trail",
|
|
20
|
+
"noTraversal",
|
|
21
|
+
"triggerArrival",
|
|
22
|
+
"variables",
|
|
23
|
+
]);
|
|
24
|
+
/** Whether a failure of this assertion kind halts the rest of the case. */
|
|
25
|
+
export function haltsCaseOnFailure(assert) {
|
|
26
|
+
return STRUCTURAL_ASSERTS.has(assert);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Describe an assertion step that never ran. One record per step, never one per
|
|
30
|
+
* token: an un-run `reply` with three tokens would have produced three records,
|
|
31
|
+
* but the engine did not run it and does not invent outcomes it never computed.
|
|
32
|
+
*/
|
|
33
|
+
export function notExecutedRecord(expect, step) {
|
|
34
|
+
return {
|
|
35
|
+
kind: expect.assert,
|
|
36
|
+
threshold: expect.assert === "grade" ? expect.atLeast : null,
|
|
37
|
+
pass: false,
|
|
38
|
+
status: "not-executed",
|
|
39
|
+
step,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Every assistant message in the view, newest last. Reply assertions match
|
|
44
|
+
* against this whole transcript rather than only the final reply: a session
|
|
45
|
+
* that parks at a human state often closes with a short "moving on" message
|
|
46
|
+
* while the substantive answer came one message earlier.
|
|
47
|
+
*/
|
|
48
|
+
function assistantTranscript(view) {
|
|
49
|
+
const parts = [];
|
|
50
|
+
for (const e of view.events) {
|
|
51
|
+
if (e.type === "message.completed" && e.data && e.data.message) {
|
|
52
|
+
parts.push(String(e.data.message));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (parts.length === 0 && view.reply)
|
|
56
|
+
parts.push(String(view.reply));
|
|
57
|
+
return parts.join("\n");
|
|
58
|
+
}
|
|
59
|
+
function tokenMatches(token, text) {
|
|
60
|
+
return token.regex ? token.regex.test(text) : text.includes(token.raw);
|
|
61
|
+
}
|
|
62
|
+
function matchesToolCall(calls, name, input, status) {
|
|
63
|
+
return calls.some((c) => c.name === name &&
|
|
64
|
+
(status === undefined || c.status === status) &&
|
|
65
|
+
(input === undefined || partialMatch(input, c.input)));
|
|
66
|
+
}
|
|
67
|
+
function trailOf(view) {
|
|
68
|
+
return Array.isArray(view.auditTrail) ? view.auditTrail : [];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Evaluate one turn's expectations. `step` is the index, in the case
|
|
72
|
+
* document's flat `steps` list, of the step these entries came from; every
|
|
73
|
+
* record produced here is stamped with it, so attribution never depends on
|
|
74
|
+
* how many records an expectation happens to emit. The optional `grade`
|
|
75
|
+
* scores `grade` entries; when absent (no `tests.judge` in workflow.yaml), a
|
|
76
|
+
* grade entry records an actionable failure instead of silently passing.
|
|
77
|
+
*/
|
|
78
|
+
export async function evaluateExpectations(entries, view, step, grade) {
|
|
79
|
+
const records = [];
|
|
80
|
+
const trail = trailOf(view);
|
|
81
|
+
// Closes over `step`, so it is the single place a deterministic record is
|
|
82
|
+
// built and there is no way to build one without its step or its status.
|
|
83
|
+
const gate = (kind, pass, detail) => ({
|
|
84
|
+
kind,
|
|
85
|
+
threshold: null,
|
|
86
|
+
pass,
|
|
87
|
+
status: pass ? "passed" : "failed",
|
|
88
|
+
step,
|
|
89
|
+
...(detail !== undefined ? { detail } : {}),
|
|
90
|
+
});
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
switch (entry.assert) {
|
|
93
|
+
case "succeeded":
|
|
94
|
+
records.push(gate("succeeded", !view.failed && !view.parked));
|
|
95
|
+
break;
|
|
96
|
+
case "parked":
|
|
97
|
+
// A bare `parked` accepts either channel; a pinned one must match the
|
|
98
|
+
// channel — and, when named, the state — the session actually suspended in.
|
|
99
|
+
records.push(gate("parked", view.parked &&
|
|
100
|
+
(entry.channel === undefined || view.parkedChannel === entry.channel) &&
|
|
101
|
+
(entry.node === undefined || view.node === entry.node), [entry.channel, entry.node].filter(Boolean).join(" in ") || undefined));
|
|
102
|
+
break;
|
|
103
|
+
case "reachedState":
|
|
104
|
+
// Committed transitions only (any kind except `trigger`): the arrival
|
|
105
|
+
// step is recorded before the entry state's `before` gate runs, so a
|
|
106
|
+
// vetoed-entry session never "reaches" the state the gate refused.
|
|
107
|
+
records.push(gate("reachedState", trail.some((s) => s.kind !== "trigger" && s.to === entry.state), entry.state));
|
|
108
|
+
break;
|
|
109
|
+
case "reply": {
|
|
110
|
+
const transcript = assistantTranscript(view);
|
|
111
|
+
for (const token of entry.includes) {
|
|
112
|
+
records.push(gate("reply.includes", tokenMatches(token, transcript), token.raw));
|
|
113
|
+
}
|
|
114
|
+
for (const token of entry.excludes) {
|
|
115
|
+
records.push(gate("reply.excludes", !tokenMatches(token, transcript), token.raw));
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case "calledTool":
|
|
120
|
+
records.push(gate("calledTool", matchesToolCall(view.toolCalls, entry.name, entry.input), entry.name));
|
|
121
|
+
break;
|
|
122
|
+
case "notCalledTool":
|
|
123
|
+
records.push(gate("notCalledTool", !matchesToolCall(view.toolCalls, entry.name, entry.input), entry.name));
|
|
124
|
+
break;
|
|
125
|
+
case "blockedTool":
|
|
126
|
+
// Refused by governance — the run view separates a policy rejection
|
|
127
|
+
// from a tool that ran and failed, so a case can assert the guard fired
|
|
128
|
+
// rather than infer it from an absence (which is what `notCalledTool`
|
|
129
|
+
// measures: an agent that never reached for the thing tested nothing).
|
|
130
|
+
records.push(gate("blockedTool", matchesToolCall(view.toolCalls, entry.name, entry.input, "rejected"), entry.name));
|
|
131
|
+
break;
|
|
132
|
+
case "usedNoTools":
|
|
133
|
+
records.push(gate("usedNoTools", view.toolCalls.length === 0));
|
|
134
|
+
break;
|
|
135
|
+
case "ranWorkflow": {
|
|
136
|
+
// Read from the trail, not from tool calls: a script's delegation never
|
|
137
|
+
// appears among the model's calls, and the trail's `sub-workflow` step
|
|
138
|
+
// is the session's whole durable record of the dispatch either way.
|
|
139
|
+
const matching = trail.filter((s) => s.kind === "sub-workflow" &&
|
|
140
|
+
s.workflow === entry.workflow &&
|
|
141
|
+
(s.status ?? "ok") === entry.status);
|
|
142
|
+
const ok = entry.count === undefined ? matching.length > 0 : matching.length === entry.count;
|
|
143
|
+
records.push(gate("ranWorkflow", ok, `${entry.workflow} (${entry.status}${entry.count === undefined ? "" : `, ${matching.length}/${entry.count}`})`));
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case "trail": {
|
|
147
|
+
const matching = trail.filter((s) => (entry.to === undefined || s.to === entry.to) &&
|
|
148
|
+
(entry.stepKind === undefined || s.kind === entry.stepKind) &&
|
|
149
|
+
(entry.reason === undefined || s.reason === entry.reason));
|
|
150
|
+
const wanted = [
|
|
151
|
+
...(entry.to !== undefined ? [`to=${entry.to}`] : []),
|
|
152
|
+
...(entry.stepKind !== undefined ? [`kind=${entry.stepKind}`] : []),
|
|
153
|
+
...(entry.reason !== undefined ? [`reason=${entry.reason}`] : []),
|
|
154
|
+
].join(" ");
|
|
155
|
+
records.push(gate("trail", matching.length === entry.count, `${wanted} expected ${entry.count}, saw ${matching.length}`));
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "noTraversal":
|
|
159
|
+
// A veto at entry leaves nothing but trigger-arrival steps.
|
|
160
|
+
records.push(gate("noTraversal", trail.every((s) => s.kind === "trigger"), "no committed transitions"));
|
|
161
|
+
break;
|
|
162
|
+
case "triggerArrival":
|
|
163
|
+
records.push(gate("triggerArrival", trail.some((s) => s.kind === "trigger" && s.reason === entry.trigger), entry.trigger));
|
|
164
|
+
break;
|
|
165
|
+
case "variables": {
|
|
166
|
+
for (const [name, expected] of Object.entries(entry.expect)) {
|
|
167
|
+
const stored = view.variables?.[name];
|
|
168
|
+
const path = entry.path?.[name];
|
|
169
|
+
const label = path ? `${name}.${path}` : name;
|
|
170
|
+
if (stored === undefined) {
|
|
171
|
+
records.push(gate("variables", false, `${label} (not set)`));
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const actual = path ? resolvePath(stored.value, path.split(".")) : stored.value;
|
|
175
|
+
if (path && actual === undefined) {
|
|
176
|
+
records.push(gate("variables", false, `${label} (path does not resolve)`));
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const matched = deepEqual(actual, expected);
|
|
180
|
+
records.push(gate("variables", matched, matched
|
|
181
|
+
? label
|
|
182
|
+
: `${label}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`));
|
|
183
|
+
const wantLocked = entry.locked?.[name];
|
|
184
|
+
if (wantLocked !== undefined) {
|
|
185
|
+
records.push(gate("variables", stored.locked === wantLocked, `${name} locked=${wantLocked} (actual ${stored.locked})`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case "grade": {
|
|
191
|
+
const record = {
|
|
192
|
+
kind: "grade.closedQA",
|
|
193
|
+
threshold: entry.atLeast,
|
|
194
|
+
pass: false,
|
|
195
|
+
status: "failed",
|
|
196
|
+
score: 0,
|
|
197
|
+
detail: entry.closedQA,
|
|
198
|
+
step,
|
|
199
|
+
};
|
|
200
|
+
if (!grade) {
|
|
201
|
+
record.detail = `${entry.closedQA} (grading model unavailable: declare 'tests: { judge: ... }' in workflow.yaml)`;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
try {
|
|
205
|
+
// The grader scores the turn's evidence — its final reply plus the
|
|
206
|
+
// chronological record of what it did to get there — built from the
|
|
207
|
+
// same view every other assertion on this step reads, so a `grade`
|
|
208
|
+
// and a `calledTool` here can never disagree about what happened.
|
|
209
|
+
const result = await grade(entry.closedQA, judgeEvidenceFromView(view));
|
|
210
|
+
record.score = Math.max(0, Math.min(1, Number(result.score ?? 0)));
|
|
211
|
+
if (result.reason)
|
|
212
|
+
record.detail = `${entry.closedQA}: ${result.reason}`;
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
record.detail = `${entry.closedQA}: ${err.message}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// The case author's declared bar decides, not the grading model's own
|
|
219
|
+
// boolean: `atLeast` is what the verdict compares against, so deciding
|
|
220
|
+
// the record any other way lets a record contradict the verdict shown
|
|
221
|
+
// beside it. A miss fails the case like any other assertion.
|
|
222
|
+
record.status = (record.score ?? 0) >= entry.atLeast ? "passed" : "failed";
|
|
223
|
+
record.pass = record.status === "passed";
|
|
224
|
+
records.push(record);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return records;
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../../src/testing/assertions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The case document model: the parsed shape a case file yields, the error a
|
|
3
|
+
* malformed one raises, and the path conventions that name a case and its
|
|
4
|
+
* fixtures. The grammar that produces it lives in `case-schema.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import type { ToolMockSpec } from "../core/tool-mocks.js";
|
|
7
|
+
/** A validation failure in a case document, addressed by file and location. */
|
|
8
|
+
export declare class CaseSchemaError extends Error {
|
|
9
|
+
readonly file: string;
|
|
10
|
+
constructor(file: string, at: string | null, detail: string);
|
|
11
|
+
}
|
|
12
|
+
export interface CaseTriggerDecl {
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
/** A workspace seed entry: inline content, or content copied from a file. */
|
|
16
|
+
export type CaseWorkspaceEntry = {
|
|
17
|
+
source: "inline";
|
|
18
|
+
content: unknown;
|
|
19
|
+
} | {
|
|
20
|
+
source: "file";
|
|
21
|
+
from: string;
|
|
22
|
+
};
|
|
23
|
+
/** A reply-content token: a plain substring or a compiled `/pattern/flags`. */
|
|
24
|
+
export interface ReplyToken {
|
|
25
|
+
raw: string;
|
|
26
|
+
regex?: RegExp;
|
|
27
|
+
}
|
|
28
|
+
export type CaseExpectation = {
|
|
29
|
+
assert: "succeeded";
|
|
30
|
+
} | {
|
|
31
|
+
assert: "parked";
|
|
32
|
+
channel?: "decision" | "input";
|
|
33
|
+
node?: string;
|
|
34
|
+
} | {
|
|
35
|
+
assert: "reachedState";
|
|
36
|
+
state: string;
|
|
37
|
+
} | {
|
|
38
|
+
assert: "reply";
|
|
39
|
+
includes: ReplyToken[];
|
|
40
|
+
excludes: ReplyToken[];
|
|
41
|
+
} | {
|
|
42
|
+
assert: "calledTool";
|
|
43
|
+
name: string;
|
|
44
|
+
input?: Record<string, unknown>;
|
|
45
|
+
} | {
|
|
46
|
+
assert: "notCalledTool";
|
|
47
|
+
name: string;
|
|
48
|
+
input?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** A call governance **refused** — the agent tried and was blocked. */
|
|
51
|
+
| {
|
|
52
|
+
assert: "blockedTool";
|
|
53
|
+
name: string;
|
|
54
|
+
input?: Record<string, unknown>;
|
|
55
|
+
} | {
|
|
56
|
+
assert: "usedNoTools";
|
|
57
|
+
} | {
|
|
58
|
+
assert: "ranWorkflow";
|
|
59
|
+
workflow: string;
|
|
60
|
+
status: "ok" | "error";
|
|
61
|
+
count?: number;
|
|
62
|
+
} | {
|
|
63
|
+
assert: "trail";
|
|
64
|
+
to?: string;
|
|
65
|
+
stepKind?: string;
|
|
66
|
+
reason?: string;
|
|
67
|
+
count: number;
|
|
68
|
+
} | {
|
|
69
|
+
assert: "noTraversal";
|
|
70
|
+
} | {
|
|
71
|
+
assert: "triggerArrival";
|
|
72
|
+
trigger: string;
|
|
73
|
+
} | {
|
|
74
|
+
assert: "variables";
|
|
75
|
+
expect: Record<string, unknown>;
|
|
76
|
+
path?: Record<string, string>;
|
|
77
|
+
locked?: Record<string, boolean>;
|
|
78
|
+
}
|
|
79
|
+
/** A model-scored assertion: the declared `atLeast` is the bar. */
|
|
80
|
+
| {
|
|
81
|
+
assert: "grade";
|
|
82
|
+
closedQA: string;
|
|
83
|
+
atLeast: number;
|
|
84
|
+
}
|
|
85
|
+
/** @deprecated `judge:` is parsed as `grade`; this member exists so a host's exhaustive switch keeps compiling for one release. It never occurs at runtime. */
|
|
86
|
+
| {
|
|
87
|
+
assert: "judge";
|
|
88
|
+
closedQA: string;
|
|
89
|
+
atLeast: number;
|
|
90
|
+
};
|
|
91
|
+
export type CaseAction = {
|
|
92
|
+
action: "send";
|
|
93
|
+
message: string;
|
|
94
|
+
} | {
|
|
95
|
+
action: "decide";
|
|
96
|
+
to: string;
|
|
97
|
+
comment?: string;
|
|
98
|
+
} | {
|
|
99
|
+
action: "deliver";
|
|
100
|
+
trigger: string;
|
|
101
|
+
variables?: Record<string, unknown>;
|
|
102
|
+
};
|
|
103
|
+
/** One entry of the flat `steps` list: an action, or an assertion bound to the nearest action above it. */
|
|
104
|
+
export type CaseStep = {
|
|
105
|
+
kind: "action";
|
|
106
|
+
action: CaseAction;
|
|
107
|
+
} | {
|
|
108
|
+
kind: "assert";
|
|
109
|
+
expect: CaseExpectation;
|
|
110
|
+
};
|
|
111
|
+
export interface CaseDocument {
|
|
112
|
+
/** Workspace-relative case file path (the case id source). */
|
|
113
|
+
file: string;
|
|
114
|
+
/** The case id: the file path relative to the tests dir, minus the extension. */
|
|
115
|
+
id: string;
|
|
116
|
+
/** Short one-line label for the case — what the CLI prints beside its verdict. */
|
|
117
|
+
title: string;
|
|
118
|
+
/** One or two sentences: the scenario driven and what is asserted. */
|
|
119
|
+
description: string;
|
|
120
|
+
skip?: string;
|
|
121
|
+
trigger?: CaseTriggerDecl;
|
|
122
|
+
/** Variables seeded for every driven turn — the case's own seeds. */
|
|
123
|
+
variables?: Record<string, unknown>;
|
|
124
|
+
workspace: Record<string, CaseWorkspaceEntry>;
|
|
125
|
+
mocks: ToolMockSpec[];
|
|
126
|
+
steps: CaseStep[];
|
|
127
|
+
}
|
|
128
|
+
export declare const TEST_FILE_EXTENSIONS: readonly [".test.yaml", ".test.yml"];
|
|
129
|
+
/** Whether a path names a YAML case file. */
|
|
130
|
+
export declare function isTestFile(path: string): boolean;
|
|
131
|
+
/** Strip the `.test.yaml`/`.test.yml` extension from a case file path. */
|
|
132
|
+
export declare function stripTestExtension(path: string): string;
|
|
133
|
+
/** The case id for a file: its path relative to the tests dir, minus extension. */
|
|
134
|
+
export declare function caseIdForFile(file: string, testsDir: string): string;
|
|
135
|
+
/** Every `from:` reference a case declares, for fixture-existence checks. */
|
|
136
|
+
export declare function collectFileReferences(doc: CaseDocument): string[];
|
|
137
|
+
/**
|
|
138
|
+
* Reject a `from:` path that escapes the tests directory. Returns the
|
|
139
|
+
* normalized tests/-relative path.
|
|
140
|
+
*/
|
|
141
|
+
export declare function normalizeFixturePath(from: string, file: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** A validation failure in a case document, addressed by file and location. */
|
|
2
|
+
export class CaseSchemaError extends Error {
|
|
3
|
+
file;
|
|
4
|
+
constructor(file, at, detail) {
|
|
5
|
+
super(`${file}: ${at ? `${at}: ` : ""}${detail}`);
|
|
6
|
+
this.name = "CaseSchemaError";
|
|
7
|
+
this.file = file;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const TEST_FILE_EXTENSIONS = [".test.yaml", ".test.yml"];
|
|
11
|
+
/** Whether a path names a YAML case file. */
|
|
12
|
+
export function isTestFile(path) {
|
|
13
|
+
return TEST_FILE_EXTENSIONS.some((ext) => path.endsWith(ext));
|
|
14
|
+
}
|
|
15
|
+
/** Strip the `.test.yaml`/`.test.yml` extension from a case file path. */
|
|
16
|
+
export function stripTestExtension(path) {
|
|
17
|
+
for (const ext of TEST_FILE_EXTENSIONS) {
|
|
18
|
+
if (path.endsWith(ext))
|
|
19
|
+
return path.slice(0, -ext.length);
|
|
20
|
+
}
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
/** The case id for a file: its path relative to the tests dir, minus extension. */
|
|
24
|
+
export function caseIdForFile(file, testsDir) {
|
|
25
|
+
const rel = file.startsWith(`${testsDir}/`) ? file.slice(testsDir.length + 1) : file;
|
|
26
|
+
return stripTestExtension(rel);
|
|
27
|
+
}
|
|
28
|
+
/** Every `from:` reference a case declares, for fixture-existence checks. */
|
|
29
|
+
export function collectFileReferences(doc) {
|
|
30
|
+
return Object.values(doc.workspace).flatMap((entry) => entry.source === "file" ? [entry.from] : []);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reject a `from:` path that escapes the tests directory. Returns the
|
|
34
|
+
* normalized tests/-relative path.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeFixturePath(from, file) {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const segment of from.split("/")) {
|
|
39
|
+
if (segment === "" || segment === ".")
|
|
40
|
+
continue;
|
|
41
|
+
if (segment === "..") {
|
|
42
|
+
throw new CaseSchemaError(file, null, `'from: ${from}' escapes the tests directory`);
|
|
43
|
+
}
|
|
44
|
+
out.push(segment);
|
|
45
|
+
}
|
|
46
|
+
if (from.startsWith("/") || out.length === 0) {
|
|
47
|
+
throw new CaseSchemaError(file, null, `'from: ${from}' must be a tests/-relative file path`);
|
|
48
|
+
}
|
|
49
|
+
return out.join("/");
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=case-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-document.js","sourceRoot":"","sources":["../../src/testing/case-document.ts"],"names":[],"mappings":"AAOA,+EAA+E;AAC/E,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,EAAiB,EAAE,MAAc;QACzD,KAAK,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAoED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,YAAY,EAAE,WAAW,CAAU,CAAC;AAEzE,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,KAAK,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAgB;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,qBAAqB,CAAC,GAAiB;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACpD,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAC5C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IAC7D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG;YAAE,SAAS;QAChD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,IAAI,+BAA+B,CAAC,CAAC;QACvF,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,IAAI,uCAAuC,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type CaseDocument, type CaseStep } from "./case-document.js";
|
|
3
|
+
export { CaseSchemaError, TEST_FILE_EXTENSIONS, caseIdForFile, collectFileReferences, isTestFile, normalizeFixturePath, stripTestExtension, } from "./case-document.js";
|
|
4
|
+
export type { CaseAction, CaseDocument, CaseExpectation, CaseStep, CaseTriggerDecl, CaseWorkspaceEntry, ReplyToken, } from "./case-document.js";
|
|
5
|
+
/**
|
|
6
|
+
* Length budgets for the two prose fields. `title` is a label the CLI prints
|
|
7
|
+
* beside a verdict, so it has to fit a terminal line; `description` states the
|
|
8
|
+
* scenario and what is asserted in a sentence or two. Longer rationale belongs
|
|
9
|
+
* in a YAML comment above the document. Measured on whitespace-collapsed text
|
|
10
|
+
* so a folded (`>-`) and a literal (`|`) block of the same prose score alike.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CASE_TITLE_MAX_LENGTH = 60;
|
|
13
|
+
export declare const CASE_DESCRIPTION_MAX_LENGTH = 200;
|
|
14
|
+
/** Every step key the grammar knows, actions first. `judge` is the deprecated alias of `grade`. */
|
|
15
|
+
export declare const STEP_SCHEMAS: Record<string, z.ZodType<CaseStep>>;
|
|
16
|
+
/** Parse and validate one case document. Throws {@link CaseSchemaError} on any structural violation. */
|
|
17
|
+
export declare function parseCaseDocument(file: string, source: string, testsDir: string): CaseDocument;
|
|
18
|
+
/**
|
|
19
|
+
* The YAML of a {@link CaseDocument}: the inverse of {@link parseCaseDocument},
|
|
20
|
+
* in the grammar's own key order (`title`, `description`, `skip`, `trigger`,
|
|
21
|
+
* `variables`, `workspace`, `mocks`, `steps`), so an editor that parses a file,
|
|
22
|
+
* edits the document and writes it back changes only what it edited. `file` and
|
|
23
|
+
* `id` are the file's, not the document's, and are not written. A reply token
|
|
24
|
+
* keeps its `/pattern/flags` spelling; a `judge` step is written as `grade`.
|
|
25
|
+
* Comments are not part of the document and are not preserved — a host that
|
|
26
|
+
* keeps a leading comment re-attaches it. Holds `parse(serialize(doc))` deep-equal
|
|
27
|
+
* to `doc` for every document the grammar accepts.
|
|
28
|
+
*/
|
|
29
|
+
export declare function serializeCaseDocument(doc: CaseDocument): string;
|