@vimhead.dev/norn-cli 0.1.0-tip.35240723931.1 → 0.1.0-tip.35343816255.1

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.
Files changed (88) hide show
  1. package/assets/README.md +2 -24
  2. package/assets/docs/README.md +3 -1
  3. package/assets/docs/agents.md +3 -3
  4. package/assets/docs/cli.md +7 -7
  5. package/assets/docs/composition.md +64 -34
  6. package/assets/docs/persistence.md +4 -2
  7. package/assets/docs/projects.md +3 -12
  8. package/assets/{setup → docs}/providers.md +4 -3
  9. package/assets/docs/resources.md +7 -7
  10. package/assets/docs/schemas.md +130 -0
  11. package/assets/docs/workflows.md +4 -3
  12. package/assets/examples/agent-then-analysis/README.md +1 -1
  13. package/assets/examples/agent-then-analysis/plugin.ts +16 -15
  14. package/assets/examples/caller-selected-continuation/README.md +75 -0
  15. package/assets/examples/caller-selected-continuation/caller.ts +47 -0
  16. package/assets/examples/caller-selected-continuation/input.json +9 -0
  17. package/assets/examples/caller-selected-continuation/norn.project.json +4 -0
  18. package/assets/examples/caller-selected-continuation/producer.ts +32 -0
  19. package/assets/examples/coordinating-multiple-agents/README.md +1 -1
  20. package/assets/examples/coordinating-multiple-agents/plugin.ts +10 -11
  21. package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +3 -4
  22. package/assets/examples/coordinating-multiple-agents/work-queue.ts +18 -18
  23. package/assets/examples/minimal-workflow/plugin.ts +2 -2
  24. package/assets/examples/shared-state/README.md +1 -1
  25. package/assets/examples/shared-state/plugin.ts +6 -6
  26. package/assets/examples/worktree-development-loop/README.md +105 -44
  27. package/assets/examples/worktree-development-loop/norn.project.json +1 -1
  28. package/assets/examples/worktree-development-loop/state.ts +6 -6
  29. package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +1 -1
  30. package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +9 -9
  31. package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +1 -1
  32. package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +7 -7
  33. package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
  34. package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +7 -7
  35. package/assets/examples/worktree-development-loop/workflows/review/execute.ts +1 -1
  36. package/assets/examples/worktree-development-loop/workflows/review/schema.ts +11 -11
  37. package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +1 -1
  38. package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +5 -5
  39. package/assets/package.json +1 -1
  40. package/assets/packages/cli/src/cli.ts +15 -32
  41. package/assets/packages/cli/src/client.ts +0 -7
  42. package/assets/packages/cli/src/generated-build-info.ts +2 -2
  43. package/assets/packages/cli/src/internal/agent-response-tool.ts +9 -10
  44. package/assets/packages/cli/src/internal/agents.ts +17 -16
  45. package/assets/packages/cli/src/internal/engine.ts +20 -18
  46. package/assets/packages/cli/src/internal/errors.ts +4 -10
  47. package/assets/packages/cli/src/internal/run-state.ts +4 -0
  48. package/assets/packages/cli/src/internal/run.ts +4 -7
  49. package/assets/packages/cli/src/internal/state-store.ts +22 -14
  50. package/assets/packages/cli/src/internal/workflow-registry.ts +27 -21
  51. package/assets/packages/cli/src/plugin-loader.ts +33 -45
  52. package/assets/packages/cli/src/resources.ts +14 -12
  53. package/assets/packages/core/src/workflow-transition.ts +4 -0
  54. package/assets/packages/sdk/src/api.ts +87 -131
  55. package/assets/packages/sdk/src/files.ts +11 -10
  56. package/assets/packages/sdk/src/index.ts +0 -1
  57. package/assets/packages/sdk/src/resources.ts +2 -2
  58. package/assets/packages/sdk/src/schema.ts +53 -29
  59. package/assets/packages/sdk/src/state-adapter.ts +4 -4
  60. package/assets/tests/workflow-ref.test.ts +121 -77
  61. package/dist/cli.js +20 -36
  62. package/dist/client.d.ts +0 -4
  63. package/dist/client.js +0 -3
  64. package/dist/generated-build-info.d.ts +2 -2
  65. package/dist/generated-build-info.js +2 -2
  66. package/dist/internal/agent-response-tool.d.ts +2 -2
  67. package/dist/internal/agent-response-tool.js +7 -7
  68. package/dist/internal/agents.d.ts +2 -2
  69. package/dist/internal/agents.js +10 -10
  70. package/dist/internal/engine.d.ts +1 -1
  71. package/dist/internal/engine.js +14 -12
  72. package/dist/internal/errors.d.ts +2 -1
  73. package/dist/internal/errors.js +5 -8
  74. package/dist/internal/run-state.js +4 -0
  75. package/dist/internal/run.d.ts +2 -2
  76. package/dist/internal/run.js +10 -6
  77. package/dist/internal/state-store.d.ts +8 -7
  78. package/dist/internal/state-store.js +15 -9
  79. package/dist/internal/workflow-registry.d.ts +14 -6
  80. package/dist/internal/workflow-registry.js +20 -15
  81. package/dist/plugin-loader.d.ts +15 -21
  82. package/dist/plugin-loader.js +35 -47
  83. package/dist/resources.d.ts +1 -1
  84. package/dist/resources.js +15 -13
  85. package/package.json +3 -4
  86. package/assets/packages/sdk/src/seer/config.ts +0 -62
  87. package/assets/packages/sdk/src/seer/index.ts +0 -7
  88. package/assets/setup/releases.md +0 -76
@@ -1,12 +1,12 @@
1
- import { z } from "zod";
2
1
  import { artifactRefSchema, type NornWorkflowPluginStateTree } from "@vimhead.dev/norn";
2
+ import { Type } from "typebox";
3
3
  import { reviewDecisionSchema } from "./workflows/review/schema.ts";
4
4
 
5
5
  export const developmentLoopState = {
6
- task: z.string(),
7
- maxIterations: z.number().int().min(1).max(10),
8
- currentIteration: z.number().int().min(1),
9
- repositoryPath: z.string(),
6
+ task: Type.String(),
7
+ maxIterations: Type.Integer({ minimum: 1, maximum: 10 }),
8
+ currentIteration: Type.Integer({ minimum: 1 }),
9
+ repositoryPath: Type.String(),
10
10
  } as const satisfies NornWorkflowPluginStateTree;
11
11
 
12
12
  export const planningState = {
@@ -14,7 +14,7 @@ export const planningState = {
14
14
  } as const satisfies NornWorkflowPluginStateTree;
15
15
 
16
16
  export const implementationState = {
17
- implementationSummary: z.string(),
17
+ implementationSummary: Type.String(),
18
18
  } as const satisfies NornWorkflowPluginStateTree;
19
19
 
20
20
  export const reviewState = {
@@ -14,5 +14,5 @@ export async function executeDevelopmentLoopWorkflow(
14
14
  await run.state.set(worktreeDevelopmentLoopManifest.states.developmentLoop.maxIterations, params.maxIterations);
15
15
  await run.state.set(worktreeDevelopmentLoopManifest.states.developmentLoop.currentIteration, 1);
16
16
 
17
- return run.next(worktreeDevelopmentLoopManifest.workflows.planning, { task: params.task });
17
+ return worktreeDevelopmentLoopManifest.workflows.planning({ task: params.task });
18
18
  }
@@ -1,14 +1,14 @@
1
- import { z } from "zod";
1
+ import { Type, type StaticDecode } from "typebox";
2
2
 
3
- export const developmentLoopConfigSchema = z.object({
4
- repositoryRoot: z.string(),
3
+ export const developmentLoopConfigSchema = Type.Object({
4
+ repositoryRoot: Type.String(),
5
5
  });
6
6
 
7
- export const developmentLoopParamsSchema = z.object({
8
- task: z.string(),
9
- baseRef: z.string().default("HEAD"),
10
- maxIterations: z.number().int().min(1).max(10).default(3),
7
+ export const developmentLoopParamsSchema = Type.Object({
8
+ task: Type.String(),
9
+ baseRef: Type.String({ default: "HEAD" }),
10
+ maxIterations: Type.Integer({ minimum: 1, maximum: 10, default: 3 }),
11
11
  });
12
12
 
13
- export type DevelopmentLoopConfig = z.output<typeof developmentLoopConfigSchema>;
14
- export type DevelopmentLoopParams = z.output<typeof developmentLoopParamsSchema>;
13
+ export type DevelopmentLoopConfig = StaticDecode<typeof developmentLoopConfigSchema>;
14
+ export type DevelopmentLoopParams = StaticDecode<typeof developmentLoopParamsSchema>;
@@ -28,7 +28,7 @@ export async function executeImplementationWorkflow(
28
28
  await ensureCommandSucceeded(status);
29
29
  const statusOutput = await run.logs.read(status.stdoutLog);
30
30
  await run.artifacts.write(`implementation/iteration-${params.iteration}-status.txt`, statusOutput);
31
- return run.next(worktreeDevelopmentLoopManifest.workflows.review, {
31
+ return worktreeDevelopmentLoopManifest.workflows.review({
32
32
  task: params.task,
33
33
  iteration: params.iteration,
34
34
  });
@@ -1,12 +1,12 @@
1
- import { z } from "zod";
1
+ import { Type, type StaticDecode } from "typebox";
2
2
 
3
- export const implementationParamsSchema = z.object({
4
- task: z.string(),
5
- iteration: z.number().int().min(1),
3
+ export const implementationParamsSchema = Type.Object({
4
+ task: Type.String(),
5
+ iteration: Type.Integer({ minimum: 1 }),
6
6
  });
7
7
 
8
- export const implementationAgentResponseSchema = z.object({
9
- summary: z.string(),
8
+ export const implementationAgentResponseSchema = Type.Object({
9
+ summary: Type.String(),
10
10
  });
11
11
 
12
- export type ImplementationParams = z.output<typeof implementationParamsSchema>;
12
+ export type ImplementationParams = StaticDecode<typeof implementationParamsSchema>;
@@ -13,7 +13,7 @@ export async function executePlanningWorkflow(run: NornRun, params: PlanningPara
13
13
  });
14
14
  const planArtifact = await run.artifacts.write("planning/plan.md", planning.plan);
15
15
  await run.state.set(worktreeDevelopmentLoopManifest.states.planning.planArtifact, planArtifact);
16
- return run.next(worktreeDevelopmentLoopManifest.workflows.implementation, { task: params.task, iteration: 1 });
16
+ return worktreeDevelopmentLoopManifest.workflows.implementation({ task: params.task, iteration: 1 });
17
17
  }
18
18
 
19
19
  function buildPlanningPrompt(task: string): string {
@@ -1,12 +1,12 @@
1
- import { z } from "zod";
1
+ import { Type, type StaticDecode } from "typebox";
2
2
 
3
- export const planningParamsSchema = z.object({
4
- task: z.string(),
3
+ export const planningParamsSchema = Type.Object({
4
+ task: Type.String(),
5
5
  });
6
6
 
7
- export const planningAgentResponseSchema = z.object({
8
- plan: z.string(),
9
- summary: z.string(),
7
+ export const planningAgentResponseSchema = Type.Object({
8
+ plan: Type.String(),
9
+ summary: Type.String(),
10
10
  });
11
11
 
12
- export type PlanningParams = z.output<typeof planningParamsSchema>;
12
+ export type PlanningParams = StaticDecode<typeof planningParamsSchema>;
@@ -24,7 +24,7 @@ export async function executeReviewWorkflow(run: NornRun, params: ReviewParams):
24
24
  response: reviewAgentResponseSchema,
25
25
  });
26
26
  const automatedReviewArtifact = await run.artifacts.write(`review/iteration-${params.iteration}-automated.json`, JSON.stringify(review, null, 2));
27
- return run.next(worktreeDevelopmentLoopManifest.workflows.reviewRouter, {
27
+ return worktreeDevelopmentLoopManifest.workflows.reviewRouter({
28
28
  iteration: params.iteration,
29
29
  decision: review.decision,
30
30
  summary: review.summary,
@@ -1,23 +1,23 @@
1
- import { z } from "zod";
2
1
  import { artifactRefSchema } from "@vimhead.dev/norn";
2
+ import { Type, type StaticDecode } from "typebox";
3
3
 
4
- export const reviewDecisionSchema = z.enum(["accept", "revise", "blocked"]);
4
+ export const reviewDecisionSchema = Type.Enum(["accept", "revise", "blocked"]);
5
5
 
6
- export const reviewParamsSchema = z.object({
7
- task: z.string(),
8
- iteration: z.number().int().min(1),
6
+ export const reviewParamsSchema = Type.Object({
7
+ task: Type.String(),
8
+ iteration: Type.Integer({ minimum: 1 }),
9
9
  });
10
10
 
11
- export const reviewAgentResponseSchema = z.object({
11
+ export const reviewAgentResponseSchema = Type.Object({
12
12
  decision: reviewDecisionSchema,
13
- summary: z.string(),
13
+ summary: Type.String(),
14
14
  });
15
15
 
16
- export const storedReviewSchema = z.object({
16
+ export const storedReviewSchema = Type.Object({
17
17
  decision: reviewDecisionSchema,
18
- summary: z.string(),
18
+ summary: Type.String(),
19
19
  reviewArtifact: artifactRefSchema,
20
20
  });
21
21
 
22
- export type ReviewParams = z.output<typeof reviewParamsSchema>;
23
- export type StoredReview = z.output<typeof storedReviewSchema>;
22
+ export type ReviewParams = StaticDecode<typeof reviewParamsSchema>;
23
+ export type StoredReview = StaticDecode<typeof storedReviewSchema>;
@@ -47,5 +47,5 @@ export async function executeReviewRouterWorkflow(
47
47
 
48
48
  const nextIteration = currentIteration + 1;
49
49
  await run.state.set(worktreeDevelopmentLoopManifest.states.developmentLoop.currentIteration, nextIteration);
50
- return run.next(worktreeDevelopmentLoopManifest.workflows.implementation, { task, iteration: nextIteration });
50
+ return worktreeDevelopmentLoopManifest.workflows.implementation({ task, iteration: nextIteration });
51
51
  }
@@ -1,12 +1,12 @@
1
- import { z } from "zod";
2
1
  import { artifactRefSchema } from "@vimhead.dev/norn";
2
+ import { Type, type StaticDecode } from "typebox";
3
3
  import { reviewDecisionSchema } from "../review/schema.ts";
4
4
 
5
- export const reviewRouterParamsSchema = z.object({
6
- iteration: z.number().int().min(1),
5
+ export const reviewRouterParamsSchema = Type.Object({
6
+ iteration: Type.Integer({ minimum: 1 }),
7
7
  decision: reviewDecisionSchema,
8
- summary: z.string(),
8
+ summary: Type.String(),
9
9
  automatedReviewArtifact: artifactRefSchema,
10
10
  });
11
11
 
12
- export type ReviewRouterParams = z.output<typeof reviewRouterParamsSchema>;
12
+ export type ReviewRouterParams = StaticDecode<typeof reviewRouterParamsSchema>;
@@ -1 +1 @@
1
- {"version":"0.1.0-tip.35240723931.1"}
1
+ {"version":"0.1.0-tip.35343816255.1"}
@@ -1,24 +1,25 @@
1
- import { spawn } from "node:child_process";
2
1
  import { main as runPi } from "@earendil-works/pi-coding-agent";
2
+ import { type DeletedNornRunInfo, type NornAnyWorkflowDeclaration, type NornRunInfo } from "@vimhead.dev/norn";
3
+ import { isNodeError } from "@vimhead.dev/norn-core/errors";
4
+ import { spawn } from "node:child_process";
3
5
  import { createHash, randomUUID } from "node:crypto";
4
6
  import { chmod, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
5
- import { dirname, isAbsolute, join, resolve } from "node:path";
6
7
  import { homedir } from "node:os";
8
+ import { dirname, isAbsolute, join, resolve } from "node:path";
9
+ import { setTimeout as delay } from "node:timers/promises";
7
10
  import { fileURLToPath } from "node:url";
11
+ import { Value } from "typebox/value";
12
+ import { NORN_BUILD_INFO, type NornBuildInfo, type NornGithubReleaseBinaryBuildInfo } from "./build-info.ts";
8
13
  import { renderNornDocumentationIntro, resolveDocumentationCacheRoot, resolveNornDocumentation, type NornDocumentationSource } from "./documentation.ts";
9
- import { setTimeout as delay } from "node:timers/promises";
10
- import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./plugin-loader.ts";
11
- import { type NornAnyWorkflowDeclaration, type DeletedNornRunInfo, type NornRunInfo } from "@vimhead.dev/norn";
12
14
  import { NornEngine } from "./internal/engine.ts";
13
15
  import { errorMessage, NornProjectLoadError, NornRunStoppedError } from "./internal/errors.ts";
14
- import { isNodeError } from "@vimhead.dev/norn-core/errors";
15
16
  import { clearRunResumeRequest, readRunLaunchRequest, readRunResumeRequest, writeRunLaunchRequest, writeRunResumeRequest, type NornRunResumeRequest } from "./internal/launch-request.ts";
16
- import { generateRunName } from "./internal/run-names.ts";
17
- import { getRunLeaseOwner, NornRunLease } from "./internal/run-lease.ts";
18
- import { NornRunStore } from "./internal/run-store.ts";
19
17
  import { readRunMetrics } from "./internal/metrics.ts";
18
+ import { getRunLeaseOwner, NornRunLease } from "./internal/run-lease.ts";
19
+ import { generateRunName } from "./internal/run-names.ts";
20
20
  import { getRunInfo, listRuns, mergeInterruptedWorkflowParams, resolveRunRoot } from "./internal/run-state.ts";
21
- import { NORN_BUILD_INFO, type NornBuildInfo, type NornGithubReleaseBinaryBuildInfo } from "./build-info.ts";
21
+ import { NornRunStore } from "./internal/run-store.ts";
22
+ import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./plugin-loader.ts";
22
23
 
23
24
  const RUNS_ROOT = join(".norn", "runs");
24
25
  const RUN_WAIT_INTERVAL_MS = 1000;
@@ -109,7 +110,7 @@ const COMMANDS: readonly CliCommand[] = [
109
110
  {
110
111
  id: "project.inspect",
111
112
  path: ["project", "inspect"],
112
- description: "Use when discovering the active Norn project, plugins, workflow sources, and Seer mode.",
113
+ description: "Use when discovering the active Norn project, plugins, and workflow sources.",
113
114
  usage: "norn project inspect",
114
115
  output: "JSON object with project metadata under project, isComplete, and plugin diagnostics. Incomplete discovery does not permit execution.",
115
116
  examples: ["norn project inspect"],
@@ -118,18 +119,6 @@ const COMMANDS: readonly CliCommand[] = [
118
119
  await inspectProject();
119
120
  },
120
121
  },
121
- {
122
- id: "seer.inspect",
123
- path: ["seer", "inspect"],
124
- description: "Use when checking the current project's resolved Seer mode before agent execution.",
125
- usage: "norn seer inspect",
126
- output: "JSON object with resolved Seer mode under seerMode.",
127
- examples: ["norn seer inspect"],
128
- execute: async (args) => {
129
- assertNoExtraArgs("seer inspect", args);
130
- await inspectSeerMode();
131
- },
132
- },
133
122
  {
134
123
  id: "workflows.list",
135
124
  path: ["workflows", "list"],
@@ -385,7 +374,6 @@ const HELP_COMMAND_ORDER = [
385
374
  "runs.delete",
386
375
  "project.init",
387
376
  "project.inspect",
388
- "seer.inspect",
389
377
  "pi",
390
378
  "upgrade",
391
379
  "version",
@@ -413,7 +401,6 @@ const HUMAN_COMMAND_SUMMARIES: Readonly<Record<string, string>> = {
413
401
  "runs.delete": "Delete an inactive run.",
414
402
  "project.init": "Create a Norn project in the current directory.",
415
403
  "project.inspect": "Inspect the Norn project.",
416
- "seer.inspect": "Inspect resolved Seer mode.",
417
404
  pi: "Run bundled Pi for authentication, providers, and model setup.",
418
405
  upgrade: "Upgrade the installed Norn CLI.",
419
406
  version: "Print version/build info.",
@@ -781,18 +768,14 @@ async function inspectProject(): Promise<void> {
781
768
  writeJson({ project, isComplete, diagnostics });
782
769
  }
783
770
 
784
- async function inspectSeerMode(): Promise<void> {
785
- const project = await findNornProject(process.cwd());
786
- writeJson({ seerMode: project.seerMode ?? null });
787
- }
788
-
789
771
  async function startRun(workflowId: string, args: readonly string[]): Promise<void> {
790
772
  assertNoStructuredInputArgs("runs start", args);
791
773
  const project = await loadNornProject(process.cwd());
792
774
  const workflow = project.registry.workflowById(workflowId);
793
775
  if (!workflow) throw new Error(`Unknown workflow: ${workflowId}`);
794
776
  const input = parseStartRunInput(await readStdinJson());
795
- const params = workflow.params.parse(input.params ?? {});
777
+ const params = input.params === undefined ? {} : input.params;
778
+ Value.Decode(workflow.params, params);
796
779
  const configOverride = input.config;
797
780
  const id = randomUUID();
798
781
  const name = generateRunName(new Set((await listRuns(project.projectRoot)).map((run) => run.name)));
@@ -835,7 +818,7 @@ async function parseResumeParams(runInfo: NornRunInfo, params: unknown): Promise
835
818
  const project = await loadNornProject(process.cwd());
836
819
  const workflow = project.registry.workflowById(runInfo.currentWorkflowId);
837
820
  if (!workflow) throw new Error(`Unknown workflow for resumed run: ${runInfo.currentWorkflowId}`);
838
- workflow.params.parse(mergeInterruptedWorkflowParams(runInfo.interruption?.params, params, runInfo.interruption?.fields));
821
+ Value.Decode(workflow.params, mergeInterruptedWorkflowParams(runInfo.interruption?.params, params, runInfo.interruption?.fields));
839
822
  return params;
840
823
  }
841
824
  if (runInfo.status === "pendingResume") {
@@ -15,7 +15,6 @@ import type {
15
15
  import { loadNornProject } from "./plugin-loader.ts";
16
16
  import { NornProjectLoadError } from "./internal/errors.ts";
17
17
  import type { NornRegisteredWorkflow } from "./internal/workflow-registry.ts";
18
- import type { NornResolvedSeerModeConfig } from "@vimhead.dev/norn/seer";
19
18
 
20
19
  export { NornProjectLoadError } from "./internal/errors.ts";
21
20
 
@@ -34,9 +33,6 @@ export type NornClient = {
34
33
  entries(): Promise<readonly NornRegisteredWorkflow[]>;
35
34
  };
36
35
  readonly state: NornWorkflowStateReader;
37
- readonly seer: {
38
- inspect(): Promise<NornResolvedSeerModeConfig | null>;
39
- };
40
36
  readonly runs: {
41
37
  start(input: { readonly workflowId: string; readonly params: unknown; readonly configOverride?: unknown }): Promise<NornRunInfo>;
42
38
  resume(input: { readonly run: string; readonly params?: unknown }): Promise<NornRunInfo>;
@@ -66,9 +62,6 @@ export function createNornClient(input: NornClientInput = {}): NornClient {
66
62
  entries: async () => (await catalog.load()).workflows,
67
63
  },
68
64
  state: catalog.state,
69
- seer: {
70
- inspect: async () => (await processRunner.readJson<{ seerMode: NornResolvedSeerModeConfig | null }>(["seer", "inspect"])).seerMode,
71
- },
72
65
  runs: {
73
66
  start: async (startInput) => (await processRunner.readJson<{ run: NornRunInfo }>(["runs", "start", startInput.workflowId], JSON.stringify(runStartInput(startInput)))).run,
74
67
  resume: async (resumeInput) => (await processRunner.readJson<{ run: NornRunInfo }>(["runs", "resume", resumeInput.run], runResumeStdin(resumeInput))).run,
@@ -2,8 +2,8 @@ import type { NornBuildInfo } from "./build-info.ts";
2
2
 
3
3
  export const NORN_GENERATED_BUILD_INFO = {
4
4
  "kind": "npm-registry",
5
- "version": "0.1.0-tip.35240723931.1",
6
- "commit": "f5872d022f9a38d5c0828bf39fa4ad49ca536caa",
5
+ "version": "0.1.0-tip.35343816255.1",
6
+ "commit": "c38480c32d1c8ee786f12300bf9e21163440d0a7",
7
7
  "packageSpec": "@vimhead.dev/norn-cli@tip",
8
8
  "upgrade": {
9
9
  "supported": false,
@@ -1,9 +1,9 @@
1
1
  import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
- import { Type } from "typebox";
3
- import type { z } from "zod";
4
2
  import type { NornDispose } from "@vimhead.dev/norn";
5
3
  import { AGENT_RESPONSE_TOOL_NAME } from "@vimhead.dev/norn-core/agent-protocol";
6
- import { zodErrorMessage } from "./errors.ts";
4
+ import { Type, type TSchema } from "typebox";
5
+ import { Value } from "typebox/value";
6
+ import { errorMessage } from "./errors.ts";
7
7
 
8
8
  export type NornCapturedAgentResponse = {
9
9
  readonly called: boolean;
@@ -12,7 +12,7 @@ export type NornCapturedAgentResponse = {
12
12
 
13
13
  type PendingAgentResponse = {
14
14
  readonly label: string;
15
- readonly responseSchema: z.ZodType;
15
+ readonly responseSchema: TSchema;
16
16
  called: boolean;
17
17
  response?: unknown;
18
18
  };
@@ -28,7 +28,7 @@ type AgentResponseToolDetails = {
28
28
  export class NornAgentResponseCollector {
29
29
  private readonly pending = new Map<string, PendingAgentResponse>();
30
30
 
31
- begin(runId: string, label: string, responseSchema: z.ZodType): NornDispose {
31
+ begin(runId: string, label: string, responseSchema: TSchema): NornDispose {
32
32
  this.pending.set(runId, { label, responseSchema, called: false });
33
33
  return () => this.pending.delete(runId);
34
34
  }
@@ -40,11 +40,10 @@ export class NornAgentResponseCollector {
40
40
  throw new Error(`Workflow agent response label mismatch: expected ${pending.label}, received ${label}`);
41
41
  }
42
42
  if (pending.called) throw new Error(`Workflow agent response already recorded for ${runId}`);
43
- const parsed = pending.responseSchema.safeParse(response);
44
- if (!parsed.success) throw new Error(zodErrorMessage(parsed.error));
43
+ const decoded = Value.Decode(pending.responseSchema, response);
45
44
  pending.called = true;
46
- pending.response = parsed.data;
47
- return { label: pending.label, response: parsed.data };
45
+ pending.response = decoded;
46
+ return { label: pending.label, response: decoded };
48
47
  }
49
48
 
50
49
  get(runId: string): NornCapturedAgentResponse {
@@ -81,7 +80,7 @@ export class NornAgentResponseToolFactory {
81
80
  terminate: true,
82
81
  };
83
82
  } catch (error) {
84
- return invalidAgentResponseToolResult(error instanceof Error ? error.message : String(error));
83
+ return invalidAgentResponseToolResult(errorMessage(error));
85
84
  }
86
85
  },
87
86
  });
@@ -1,27 +1,28 @@
1
1
  import {
2
2
  SessionManager,
3
- createAgentSessionServices,
4
3
  createAgentSessionFromServices,
4
+ createAgentSessionServices,
5
5
  createEventBus,
6
6
  type AgentSession,
7
7
  type CreateAgentSessionOptions,
8
8
  } from "@earendil-works/pi-coding-agent";
9
+ import type { NornAgentCreateSessionInput, NornAgentPromptInput, NornAgentRunRawAttempt, NornAgentRunResult, NornAgentSession, NornAgentSessionEvents, NornAgentSinglePromptInput, NornAgentUsage } from "@vimhead.dev/norn";
10
+ import { AGENT_RESPONSE_TOOL_NAME } from "@vimhead.dev/norn-core/agent-protocol";
11
+ import { inspectSchema } from "@vimhead.dev/norn/schema";
9
12
  import { randomUUID } from "node:crypto";
10
13
  import { mkdir } from "node:fs/promises";
11
14
  import { homedir } from "node:os";
12
15
  import { isAbsolute, relative, resolve, sep } from "node:path";
13
- import { z } from "zod";
14
- import type { NornAgentCreateSessionInput, NornAgentPromptInput, NornAgentRunRawAttempt, NornAgentRunResult, NornAgentSessionEvents, NornAgentSession, NornAgentSinglePromptInput, NornAgentUsage } from "@vimhead.dev/norn";
15
- import { AGENT_RESPONSE_TOOL_NAME } from "@vimhead.dev/norn-core/agent-protocol";
16
+ import { type StaticDecode, type TSchema } from "typebox";
17
+ import { resolveNornAgentDirectory } from "./agent-directory.ts";
16
18
  import {
17
19
  NornAgentResponseCollector,
18
20
  NornAgentResponseToolFactory,
19
21
  type NornCapturedAgentResponse,
20
22
  } from "./agent-response-tool.ts";
21
- import { resolveNornAgentDirectory } from "./agent-directory.ts";
22
23
  import { errorMessage } from "./errors.ts";
23
- import type { NornRunLogs } from "./logs.ts";
24
24
  import { safeFileName } from "./file-names.ts";
25
+ import type { NornRunLogs } from "./logs.ts";
25
26
  import { NornSessionResourceBindings } from "./resource-bindings.ts";
26
27
  import type { NornRunLogger } from "./run-log.ts";
27
28
  import { agentUsageFromValue, emptyAgentUsage, totalAgentUsage } from "./usage.ts";
@@ -112,7 +113,7 @@ export class NornAgentRunner {
112
113
  }
113
114
  }
114
115
 
115
- async prompt<ResponseSchema extends z.ZodType>(agentInput: NornAgentSinglePromptInput<ResponseSchema>): Promise<z.output<ResponseSchema>> {
116
+ async prompt<ResponseSchema extends TSchema>(agentInput: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>> {
116
117
  const agent = await this.createSession(agentInput);
117
118
  try {
118
119
  return await agent.prompt(agentInput);
@@ -153,11 +154,11 @@ class CreatedNornAgentSession implements NornAgentSession {
153
154
  this.events = input.events;
154
155
  }
155
156
 
156
- async prompt<ResponseSchema extends z.ZodType>(agentInput: NornAgentPromptInput<ResponseSchema>): Promise<z.output<ResponseSchema>> {
157
+ async prompt<ResponseSchema extends TSchema>(agentInput: NornAgentPromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>> {
157
158
  return (await this.promptWithResult(agentInput)).response;
158
159
  }
159
160
 
160
- private async promptWithResult<ResponseSchema extends z.ZodType>(agentInput: NornAgentPromptInput<ResponseSchema>): Promise<NornAgentRunResult<ResponseSchema>> {
161
+ private async promptWithResult<ResponseSchema extends TSchema>(agentInput: NornAgentPromptInput<ResponseSchema>): Promise<NornAgentRunResult<ResponseSchema>> {
161
162
  if (this.isDisposed) throw new Error(`Workflow agent session is disposed: ${this.label}`);
162
163
  const maxAttempts = Math.max(1, Math.floor(agentInput.maxAttempts ?? DEFAULT_AGENT_ATTEMPTS));
163
164
  const attempts: NornAgentRunRawAttempt[] = [];
@@ -174,7 +175,7 @@ class CreatedNornAgentSession implements NornAgentSession {
174
175
 
175
176
  try {
176
177
  if (!raw.responseToolCalled) throw new Error(`Agent did not call ${AGENT_RESPONSE_TOOL_NAME}`);
177
- const response = agentInput.response.parse(raw.toolResponse);
178
+ const response = raw.toolResponse as StaticDecode<ResponseSchema>;
178
179
  const usage = totalAgentUsage(attempts.map((candidate) => candidate.usage));
179
180
  const result: NornAgentRunResult<ResponseSchema> = {
180
181
  label: this.label,
@@ -233,7 +234,7 @@ class CreatedNornAgentSession implements NornAgentSession {
233
234
  await this.input.logger.record({ type: "agent.disposed", label: this.label });
234
235
  }
235
236
 
236
- private async runAttempt<ResponseSchema extends z.ZodType>(
237
+ private async runAttempt<ResponseSchema extends TSchema>(
237
238
  agentInput: NornAgentPromptInput<ResponseSchema>,
238
239
  attempt: number,
239
240
  previousError: string | undefined,
@@ -283,7 +284,7 @@ class CreatedNornAgentSession implements NornAgentSession {
283
284
  };
284
285
  }
285
286
 
286
- private async promptForStructuredResponse<ResponseSchema extends z.ZodType>(
287
+ private async promptForStructuredResponse<ResponseSchema extends TSchema>(
287
288
  agentInput: NornAgentPromptInput<ResponseSchema>,
288
289
  responseRunId: string,
289
290
  attempt: number,
@@ -318,7 +319,7 @@ function usageFromMessage(message: unknown): NornAgentUsage | undefined {
318
319
 
319
320
  function withResponseToolInstruction(
320
321
  prompt: string,
321
- responseSchema: z.ZodType,
322
+ responseSchema: TSchema,
322
323
  label: string,
323
324
  responseRunId: string,
324
325
  attempt: number,
@@ -334,13 +335,13 @@ function withResponseToolInstruction(
334
335
  `Pass label exactly as: ${label}`,
335
336
  "Pass the structured workflow response in the tool's response argument.",
336
337
  "The response argument must match this JSON Schema:",
337
- JSON.stringify(z.toJSONSchema(responseSchema), null, 2),
338
+ JSON.stringify(inspectSchema(responseSchema), null, 2),
338
339
  ...(attempt > 1 && previousError ? ["", `Previous structured response attempt failed: ${previousError}`] : []),
339
340
  ].join("\n");
340
341
  }
341
342
 
342
343
  function withResponseToolFinalizationInstruction(
343
- responseSchema: z.ZodType,
344
+ responseSchema: TSchema,
344
345
  label: string,
345
346
  responseRunId: string,
346
347
  attempt: number,
@@ -354,7 +355,7 @@ function withResponseToolFinalizationInstruction(
354
355
  `Pass label exactly as: ${label}`,
355
356
  "Pass the structured workflow response in the tool's response argument, based on the work you already completed in this session.",
356
357
  "The response argument must match this JSON Schema:",
357
- JSON.stringify(z.toJSONSchema(responseSchema), null, 2),
358
+ JSON.stringify(inspectSchema(responseSchema), null, 2),
358
359
  ...(attempt > 1 && previousError ? ["", `Previous structured response attempt failed: ${previousError}`] : []),
359
360
  ].join("\n");
360
361
  }
@@ -1,34 +1,35 @@
1
- import { randomUUID } from "node:crypto";
2
- import { mkdir } from "node:fs/promises";
3
- import { dirname, join } from "node:path";
4
- import { initializeRunResources } from "./run-resources.ts";
5
- import { createRunFileCoordinator } from "@vimhead.dev/norn/files";
6
1
  import {
7
2
  type NornAnyWorkflowDeclaration,
8
3
  type NornAnyWorkflowPluginManifest,
9
4
  type NornDispose,
10
- type NornRunStartOptions,
11
5
  type NornInterruptedRunResult,
12
- type NornRunResult,
13
6
  type NornRunCheckpoint,
14
7
  type NornRunInfo,
8
+ type NornRunNext,
9
+ type NornRunResult,
10
+ type NornRunStartOptions,
15
11
  type NornWorkflowPlugin,
16
12
  type NornWorkflowPluginImplementation,
17
13
  type NornWorkflowPluginImplementationInput,
18
- type NornRunNext,
19
14
  } from "@vimhead.dev/norn";
15
+ import { createRunFileCoordinator } from "@vimhead.dev/norn/files";
16
+ import { randomUUID } from "node:crypto";
17
+ import { mkdir } from "node:fs/promises";
18
+ import { dirname, join } from "node:path";
19
+ import { Value } from "typebox/value";
20
20
  import { NornAgentResponseCollector } from "./agent-response-tool.ts";
21
21
  import { NornArtifacts } from "./artifacts.ts";
22
22
  import { errorMessage, NornRunStoppedError } from "./errors.ts";
23
- import { NornRunLogs } from "./logs.ts";
24
23
  import { clearRunResumeRequest, matchesRunResumeRequest, readOptionalRunResumeRequest, RESUME_START_GRACE_MS, type NornRunResumeRequest } from "./launch-request.ts";
24
+ import { NornRunLogs } from "./logs.ts";
25
25
  import { NornRunLease } from "./run-lease.ts";
26
26
  import { NornRunLogger } from "./run-log.ts";
27
+ import { initializeRunResources } from "./run-resources.ts";
28
+ import { getRunInfo, mergeInterruptedWorkflowParams, NornRunStateStore, resolveRunRoot, type NornRunState } from "./run-state.ts";
27
29
  import { NornRunStore, runCurrentRoot } from "./run-store.ts";
28
- import { NornRunStateStore, getRunInfo, mergeInterruptedWorkflowParams, resolveRunRoot, type NornRunState } from "./run-state.ts";
29
30
  import { NornRunContext } from "./run.ts";
30
31
  import { NornMemoryWorkflowState } from "./state-store.ts";
31
- import { NornWorkflowRegistry, type NornRegisteredWorkflow, type NornWorkflowStepResult } from "./workflow-registry.ts";
32
+ import { decodePluginConfiguration, NornWorkflowRegistry, type NornRegisteredWorkflow, type NornWorkflowStepResult } from "./workflow-registry.ts";
32
33
 
33
34
  const RUNS_DIR_NAME = ".norn";
34
35
  const MANIFEST_FILE_NAME = "manifest.json";
@@ -79,10 +80,11 @@ export class NornEngine {
79
80
  registerPlugin<TManifest extends NornAnyWorkflowPluginManifest>(plugin: NornWorkflowPlugin<TManifest>): NornDispose {
80
81
  this.disposePlugin(plugin.manifest.id);
81
82
  const implementation = this.resolvePluginImplementation(plugin.implementation);
83
+ const configuration = decodePluginConfiguration({ pluginId: plugin.manifest.id, schema: plugin.manifest.config, value: this.input.config?.[plugin.manifest.id] });
82
84
  const disposers = Object.entries(plugin.manifest.workflows).map(([key, workflow]) => {
83
85
  const workflowImplementation = implementation.workflows[key];
84
86
  if (!workflowImplementation) throw new Error(`Missing implementation for workflow ${plugin.manifest.id}.${key}`);
85
- return this.registry.register(workflow, workflowImplementation, { plugin: { id: plugin.manifest.id }, configSchema: plugin.manifest.config, config: this.input.config?.[plugin.manifest.id] });
87
+ return this.registry.register(workflow, workflowImplementation, { plugin: { id: plugin.manifest.id }, configuration });
86
88
  });
87
89
  this.disposersByPlugin.set(plugin.manifest.id, disposers);
88
90
  return () => {
@@ -170,8 +172,9 @@ export class NornEngine {
170
172
  const workflow = this.registry.workflowById(current.workflowId);
171
173
  if (!workflow) throw new Error(`Unknown workflow for resumed run: ${current.workflowId}`);
172
174
  if (state.status === "interrupted") {
173
- const parsedParams = workflow.params.parse(mergeInterruptedWorkflowParams(current.params, params, current.interruption?.fields));
174
- await session.state.replaceCurrentParams(parsedParams);
175
+ const rawParams = mergeInterruptedWorkflowParams(current.params, params, current.interruption?.fields);
176
+ Value.Decode(workflow.params, rawParams);
177
+ await session.state.replaceCurrentParams(rawParams);
175
178
  await recordRunEvent(session, { type: "run.resumed", workflowId: workflow.id, cwd: session.run.cwd });
176
179
  const resumedCurrent = session.state.currentState().current;
177
180
  if (!resumedCurrent) throw new Error(`Run is not resumable: ${runRoot}`);
@@ -200,13 +203,12 @@ export class NornEngine {
200
203
  throwIfRunAborted(session.activeRun);
201
204
  const stepRuntime = session.run.forWorkflow(currentStep.workflow);
202
205
  if (shouldPauseForGate(currentStep)) {
203
- const parsedParams = currentStep.workflow.params.parse(currentStep.params);
204
- const description = await this.registry.describeGate(currentStep.workflow, stepRuntime, parsedParams, session.state.currentState().configOverride);
206
+ const description = await this.registry.describeGate(currentStep.workflow, stepRuntime, currentStep.params, session.state.currentState().configOverride);
205
207
  const interruption = { description, fields: currentStep.workflow.gate?.fields };
206
- await session.state.interruptCurrent(parsedParams, interruption);
208
+ await session.state.interruptCurrent(currentStep.params, interruption);
207
209
  await recordRunEvent(session, { type: "run.interrupted", workflowId: currentStep.workflow.id });
208
210
  await commitRunBoundary(session, `run interrupted: ${currentStep.workflow.id}`);
209
- return interruptedLaunchResult({ id: stepRuntime.id, name: session.state.currentState().name, workspace: stepRuntime.workspace, cwd: stepRuntime.cwd }, currentStep.workflow, parsedParams, interruption);
211
+ return interruptedLaunchResult({ id: stepRuntime.id, name: session.state.currentState().name, workspace: stepRuntime.workspace, cwd: stepRuntime.cwd }, currentStep.workflow, currentStep.params, interruption);
210
212
  }
211
213
  await session.state.startStep(toRunStateStep(currentStep));
212
214
  const stepResult = await this.executeWorkflowStep(session, currentStep, stepRuntime);
@@ -1,4 +1,5 @@
1
1
  import type { NornPluginDiagnostic } from "@vimhead.dev/norn";
2
+ import { AssertError } from "typebox/value";
2
3
 
3
4
  export class NornProjectLoadError extends Error {
4
5
  readonly code = "NORN_PROJECT_INVALID";
@@ -22,18 +23,11 @@ export class NornRunStoppedError extends Error {
22
23
  }
23
24
  }
24
25
 
25
- export function zodErrorMessage(error: unknown): string {
26
- const issues = (error as { issues?: Array<{ path?: unknown[]; message?: string }> }).issues;
27
- if (!Array.isArray(issues) || issues.length === 0) return errorMessage(error);
28
- return issues
29
- .slice(0, 3)
30
- .map((issue) => {
31
- const path = Array.isArray(issue.path) && issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
32
- return `${path}${issue.message ?? "Invalid workflow response"}`;
33
- })
34
- .join("; ");
26
+ export function schemaErrorMessage(error: AssertError): string {
27
+ return error.cause.errors.slice(0, 3).map(issue => `${issue.instancePath || "/"}: ${issue.message}`).join("; ");
35
28
  }
36
29
 
37
30
  export function errorMessage(error: unknown): string {
31
+ if (error instanceof AssertError) return schemaErrorMessage(error);
38
32
  return error instanceof Error ? error.message : String(error);
39
33
  }