@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.
- package/assets/README.md +2 -24
- package/assets/docs/README.md +3 -1
- package/assets/docs/agents.md +3 -3
- package/assets/docs/cli.md +7 -7
- package/assets/docs/composition.md +64 -34
- package/assets/docs/persistence.md +4 -2
- package/assets/docs/projects.md +3 -12
- package/assets/{setup → docs}/providers.md +4 -3
- package/assets/docs/resources.md +7 -7
- package/assets/docs/schemas.md +130 -0
- package/assets/docs/workflows.md +4 -3
- package/assets/examples/agent-then-analysis/README.md +1 -1
- package/assets/examples/agent-then-analysis/plugin.ts +16 -15
- package/assets/examples/caller-selected-continuation/README.md +75 -0
- package/assets/examples/caller-selected-continuation/caller.ts +47 -0
- package/assets/examples/caller-selected-continuation/input.json +9 -0
- package/assets/examples/caller-selected-continuation/norn.project.json +4 -0
- package/assets/examples/caller-selected-continuation/producer.ts +32 -0
- package/assets/examples/coordinating-multiple-agents/README.md +1 -1
- package/assets/examples/coordinating-multiple-agents/plugin.ts +10 -11
- package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +3 -4
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +18 -18
- package/assets/examples/minimal-workflow/plugin.ts +2 -2
- package/assets/examples/shared-state/README.md +1 -1
- package/assets/examples/shared-state/plugin.ts +6 -6
- package/assets/examples/worktree-development-loop/README.md +105 -44
- package/assets/examples/worktree-development-loop/norn.project.json +1 -1
- package/assets/examples/worktree-development-loop/state.ts +6 -6
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +9 -9
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +7 -7
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +7 -7
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/review/schema.ts +11 -11
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +5 -5
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/cli.ts +15 -32
- package/assets/packages/cli/src/client.ts +0 -7
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agent-response-tool.ts +9 -10
- package/assets/packages/cli/src/internal/agents.ts +17 -16
- package/assets/packages/cli/src/internal/engine.ts +20 -18
- package/assets/packages/cli/src/internal/errors.ts +4 -10
- package/assets/packages/cli/src/internal/run-state.ts +4 -0
- package/assets/packages/cli/src/internal/run.ts +4 -7
- package/assets/packages/cli/src/internal/state-store.ts +22 -14
- package/assets/packages/cli/src/internal/workflow-registry.ts +27 -21
- package/assets/packages/cli/src/plugin-loader.ts +33 -45
- package/assets/packages/cli/src/resources.ts +14 -12
- package/assets/packages/core/src/workflow-transition.ts +4 -0
- package/assets/packages/sdk/src/api.ts +87 -131
- package/assets/packages/sdk/src/files.ts +11 -10
- package/assets/packages/sdk/src/index.ts +0 -1
- package/assets/packages/sdk/src/resources.ts +2 -2
- package/assets/packages/sdk/src/schema.ts +53 -29
- package/assets/packages/sdk/src/state-adapter.ts +4 -4
- package/assets/tests/workflow-ref.test.ts +121 -77
- package/dist/cli.js +20 -36
- package/dist/client.d.ts +0 -4
- package/dist/client.js +0 -3
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agent-response-tool.d.ts +2 -2
- package/dist/internal/agent-response-tool.js +7 -7
- package/dist/internal/agents.d.ts +2 -2
- package/dist/internal/agents.js +10 -10
- package/dist/internal/engine.d.ts +1 -1
- package/dist/internal/engine.js +14 -12
- package/dist/internal/errors.d.ts +2 -1
- package/dist/internal/errors.js +5 -8
- package/dist/internal/run-state.js +4 -0
- package/dist/internal/run.d.ts +2 -2
- package/dist/internal/run.js +10 -6
- package/dist/internal/state-store.d.ts +8 -7
- package/dist/internal/state-store.js +15 -9
- package/dist/internal/workflow-registry.d.ts +14 -6
- package/dist/internal/workflow-registry.js +20 -15
- package/dist/plugin-loader.d.ts +15 -21
- package/dist/plugin-loader.js +35 -47
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +15 -13
- package/package.json +3 -4
- package/assets/packages/sdk/src/seer/config.ts +0 -62
- package/assets/packages/sdk/src/seer/index.ts +0 -7
- 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:
|
|
7
|
-
maxIterations:
|
|
8
|
-
currentIteration:
|
|
9
|
-
repositoryPath:
|
|
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:
|
|
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
|
|
17
|
+
return worktreeDevelopmentLoopManifest.workflows.planning({ task: params.task });
|
|
18
18
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Type, type StaticDecode } from "typebox";
|
|
2
2
|
|
|
3
|
-
export const developmentLoopConfigSchema =
|
|
4
|
-
repositoryRoot:
|
|
3
|
+
export const developmentLoopConfigSchema = Type.Object({
|
|
4
|
+
repositoryRoot: Type.String(),
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
export const developmentLoopParamsSchema =
|
|
8
|
-
task:
|
|
9
|
-
baseRef:
|
|
10
|
-
maxIterations:
|
|
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 =
|
|
14
|
-
export type DevelopmentLoopParams =
|
|
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
|
|
31
|
+
return worktreeDevelopmentLoopManifest.workflows.review({
|
|
32
32
|
task: params.task,
|
|
33
33
|
iteration: params.iteration,
|
|
34
34
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Type, type StaticDecode } from "typebox";
|
|
2
2
|
|
|
3
|
-
export const implementationParamsSchema =
|
|
4
|
-
task:
|
|
5
|
-
iteration:
|
|
3
|
+
export const implementationParamsSchema = Type.Object({
|
|
4
|
+
task: Type.String(),
|
|
5
|
+
iteration: Type.Integer({ minimum: 1 }),
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
export const implementationAgentResponseSchema =
|
|
9
|
-
summary:
|
|
8
|
+
export const implementationAgentResponseSchema = Type.Object({
|
|
9
|
+
summary: Type.String(),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
export type ImplementationParams =
|
|
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
|
|
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 {
|
|
1
|
+
import { Type, type StaticDecode } from "typebox";
|
|
2
2
|
|
|
3
|
-
export const planningParamsSchema =
|
|
4
|
-
task:
|
|
3
|
+
export const planningParamsSchema = Type.Object({
|
|
4
|
+
task: Type.String(),
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
export const planningAgentResponseSchema =
|
|
8
|
-
plan:
|
|
9
|
-
summary:
|
|
7
|
+
export const planningAgentResponseSchema = Type.Object({
|
|
8
|
+
plan: Type.String(),
|
|
9
|
+
summary: Type.String(),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
export type PlanningParams =
|
|
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
|
|
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 =
|
|
4
|
+
export const reviewDecisionSchema = Type.Enum(["accept", "revise", "blocked"]);
|
|
5
5
|
|
|
6
|
-
export const reviewParamsSchema =
|
|
7
|
-
task:
|
|
8
|
-
iteration:
|
|
6
|
+
export const reviewParamsSchema = Type.Object({
|
|
7
|
+
task: Type.String(),
|
|
8
|
+
iteration: Type.Integer({ minimum: 1 }),
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
export const reviewAgentResponseSchema =
|
|
11
|
+
export const reviewAgentResponseSchema = Type.Object({
|
|
12
12
|
decision: reviewDecisionSchema,
|
|
13
|
-
summary:
|
|
13
|
+
summary: Type.String(),
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
export const storedReviewSchema =
|
|
16
|
+
export const storedReviewSchema = Type.Object({
|
|
17
17
|
decision: reviewDecisionSchema,
|
|
18
|
-
summary:
|
|
18
|
+
summary: Type.String(),
|
|
19
19
|
reviewArtifact: artifactRefSchema,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
export type ReviewParams =
|
|
23
|
-
export type StoredReview =
|
|
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
|
|
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 =
|
|
6
|
-
iteration:
|
|
5
|
+
export const reviewRouterParamsSchema = Type.Object({
|
|
6
|
+
iteration: Type.Integer({ minimum: 1 }),
|
|
7
7
|
decision: reviewDecisionSchema,
|
|
8
|
-
summary:
|
|
8
|
+
summary: Type.String(),
|
|
9
9
|
automatedReviewArtifact: artifactRefSchema,
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
export type ReviewRouterParams =
|
|
12
|
+
export type ReviewRouterParams = StaticDecode<typeof reviewRouterParamsSchema>;
|
package/assets/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.1.0-tip.
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
6
|
-
"commit": "
|
|
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 {
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
47
|
-
return { label: pending.label, response:
|
|
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(
|
|
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 {
|
|
14
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
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:
|
|
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(
|
|
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 },
|
|
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
|
|
174
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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
|
|
26
|
-
|
|
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
|
}
|