@vimhead.dev/norn-cli 0.1.0-tip.35359392805.1 → 0.1.0-tip.35390859630.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 (111) hide show
  1. package/assets/README.md +17 -26
  2. package/assets/docs/README.md +3 -3
  3. package/assets/docs/agents.md +12 -1
  4. package/assets/docs/cli.md +5 -5
  5. package/assets/docs/composition.md +21 -21
  6. package/assets/docs/persistence.md +7 -10
  7. package/assets/docs/projects.md +30 -18
  8. package/assets/docs/providers.md +1 -1
  9. package/assets/docs/recovery.md +13 -9
  10. package/assets/docs/resources.md +4 -19
  11. package/assets/docs/schemas.md +1 -1
  12. package/assets/docs/workflows.md +95 -7
  13. package/assets/examples/agent-then-analysis/README.md +4 -4
  14. package/assets/examples/agent-then-analysis/input.json +1 -1
  15. package/assets/examples/agent-then-analysis/norn.project.json +1 -1
  16. package/assets/examples/agent-then-analysis/plugin.ts +55 -68
  17. package/assets/examples/caller-selected-continuation/README.md +5 -5
  18. package/assets/examples/caller-selected-continuation/caller.ts +35 -38
  19. package/assets/examples/caller-selected-continuation/input.json +2 -2
  20. package/assets/examples/caller-selected-continuation/norn.project.json +1 -1
  21. package/assets/examples/caller-selected-continuation/producer.ts +15 -23
  22. package/assets/examples/coordinating-multiple-agents/README.md +2 -2
  23. package/assets/examples/coordinating-multiple-agents/input.json +1 -1
  24. package/assets/examples/coordinating-multiple-agents/norn.project.json +1 -1
  25. package/assets/examples/coordinating-multiple-agents/plugin.ts +52 -56
  26. package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +4 -4
  27. package/assets/examples/getting-started/norn.project.json +1 -1
  28. package/assets/examples/getting-started/plugin.ts +16 -26
  29. package/assets/examples/minimal-workflow/README.md +8 -8
  30. package/assets/examples/minimal-workflow/norn.project.json +1 -1
  31. package/assets/examples/minimal-workflow/plugin.ts +11 -25
  32. package/assets/examples/shared-state/README.md +10 -2
  33. package/assets/examples/shared-state/input.json +1 -1
  34. package/assets/examples/shared-state/norn.project.json +1 -1
  35. package/assets/examples/shared-state/plugin.ts +36 -41
  36. package/assets/examples/shared-state/shared-state.ts +75 -0
  37. package/assets/{packages/sdk/src → examples/shared-state}/state-adapter.ts +17 -16
  38. package/assets/examples/worktree-development-loop/README.md +6 -7
  39. package/assets/examples/worktree-development-loop/norn.project.json +1 -1
  40. package/assets/examples/worktree-development-loop/plugin.ts +6 -26
  41. package/assets/examples/worktree-development-loop/scope.ts +4 -0
  42. package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +14 -15
  43. package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +3 -4
  44. package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +2 -2
  45. package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +34 -33
  46. package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +2 -3
  47. package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +7 -3
  48. package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +23 -16
  49. package/assets/examples/worktree-development-loop/workflows/planning/index.ts +2 -3
  50. package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +4 -2
  51. package/assets/examples/worktree-development-loop/workflows/review/execute.ts +40 -31
  52. package/assets/examples/worktree-development-loop/workflows/review/index.ts +3 -4
  53. package/assets/examples/worktree-development-loop/workflows/review/schema.ts +5 -4
  54. package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +49 -44
  55. package/assets/examples/worktree-development-loop/workflows/review-router/index.ts +2 -3
  56. package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +4 -3
  57. package/assets/package.json +1 -1
  58. package/assets/packages/cli/src/cli.ts +35 -34
  59. package/assets/packages/cli/src/client.ts +9 -16
  60. package/assets/packages/cli/src/documentation-intro.ts +1 -1
  61. package/assets/packages/cli/src/generated-build-info.ts +2 -2
  62. package/assets/packages/cli/src/internal/agent-response-tool.ts +3 -3
  63. package/assets/packages/cli/src/internal/engine.ts +38 -62
  64. package/assets/packages/cli/src/internal/errors.ts +4 -4
  65. package/assets/packages/cli/src/internal/launch-request.ts +6 -6
  66. package/assets/packages/cli/src/internal/run-state.ts +31 -21
  67. package/assets/packages/cli/src/internal/run.ts +2 -5
  68. package/assets/packages/cli/src/internal/workflow-registry.ts +114 -111
  69. package/assets/packages/cli/src/workflow-loader.ts +214 -0
  70. package/assets/packages/core/src/workflow-transition.ts +2 -2
  71. package/assets/packages/sdk/src/api.ts +133 -289
  72. package/assets/packages/sdk/src/index.ts +1 -1
  73. package/assets/packages/sdk/src/schema.ts +3 -3
  74. package/assets/tests/workflow-ref.test.ts +50 -51
  75. package/dist/cli.js +33 -32
  76. package/dist/client.d.ts +3 -4
  77. package/dist/client.js +3 -8
  78. package/dist/documentation-intro.js +1 -1
  79. package/dist/generated-build-info.d.ts +2 -2
  80. package/dist/generated-build-info.js +2 -2
  81. package/dist/internal/agent-response-tool.js +3 -3
  82. package/dist/internal/engine.d.ts +4 -8
  83. package/dist/internal/engine.js +36 -49
  84. package/dist/internal/errors.d.ts +3 -3
  85. package/dist/internal/errors.js +1 -1
  86. package/dist/internal/launch-request.d.ts +4 -4
  87. package/dist/internal/launch-request.js +2 -2
  88. package/dist/internal/run-state.d.ts +9 -8
  89. package/dist/internal/run-state.js +29 -20
  90. package/dist/internal/run.d.ts +1 -3
  91. package/dist/internal/run.js +3 -5
  92. package/dist/internal/workflow-registry.d.ts +25 -17
  93. package/dist/internal/workflow-registry.js +107 -72
  94. package/dist/{plugin-loader.d.ts → workflow-loader.d.ts} +7 -8
  95. package/dist/workflow-loader.js +235 -0
  96. package/package.json +2 -2
  97. package/assets/examples/worktree-development-loop/manifest.ts +0 -26
  98. package/assets/examples/worktree-development-loop/state.ts +0 -23
  99. package/assets/examples/worktree-development-loop/workflows/development-loop/declaration.ts +0 -8
  100. package/assets/examples/worktree-development-loop/workflows/implementation/declaration.ts +0 -8
  101. package/assets/examples/worktree-development-loop/workflows/planning/declaration.ts +0 -8
  102. package/assets/examples/worktree-development-loop/workflows/review/declaration.ts +0 -8
  103. package/assets/examples/worktree-development-loop/workflows/review-router/declaration.ts +0 -12
  104. package/assets/packages/cli/src/internal/run-resources.ts +0 -23
  105. package/assets/packages/cli/src/internal/state-store.ts +0 -83
  106. package/assets/packages/cli/src/plugin-loader.ts +0 -400
  107. package/dist/internal/run-resources.d.ts +0 -6
  108. package/dist/internal/run-resources.js +0 -26
  109. package/dist/internal/state-store.d.ts +0 -23
  110. package/dist/internal/state-store.js +0 -103
  111. package/dist/plugin-loader.js +0 -341
@@ -1,39 +1,38 @@
1
- import { artifactRefSchema, definePluginManifest, isWorkflowDeclaration, isWorkflowPluginManifest, workflowRefSchema, type NornWorkflowRefSchemaOptions } from "@vimhead.dev/norn";
1
+ import { artifactRefSchema, workflow, isWorkflowDeclaration, workflowRefSchema, type NornWorkflowRefSchemaOptions } from "@vimhead.dev/norn";
2
2
  import { inspectSchema } from "@vimhead.dev/norn/schema";
3
3
  import assert from "node:assert/strict";
4
4
  import { Type } from "typebox";
5
5
  import { Value } from "typebox/value";
6
6
  import { expect, expectTypeOf, test } from "vitest";
7
7
 
8
- const manifest = definePluginManifest({
9
- id: "refs", workflows: { finish: { isEntrypoint: false, params: Type.Object({ report: Type.String() }) } },
8
+ const target = workflow({
9
+ id: "refs.finish", isEntrypoint: false, args: Type.Object({ report: Type.String() }),
10
+ execute() { throw new Error("A callable must select a transition, not execute it"); },
10
11
  });
11
- const target = manifest.workflows.finish;
12
12
 
13
13
  test("callable declarations retain metadata and construct transitions without execution", () => {
14
14
  expectTypeOf(target.id).toEqualTypeOf<"refs.finish">();
15
15
  assert.equal(isWorkflowDeclaration(target), true);
16
- assert.equal(isWorkflowPluginManifest(manifest), true);
17
16
  assert.equal(isWorkflowDeclaration(() => undefined), false);
18
- assert.equal(Value.Check(target.params, { report: "done" }), true);
17
+ assert.equal(Value.Check(target.args, { report: "done" }), true);
19
18
  assert.deepEqual(target.isolation, { mode: "runWorkspace" });
20
- assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id, params: { report: "done" } });
19
+ assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id, args: { report: "done" } });
21
20
  });
22
21
 
23
22
  for (const reference of ["refs.finish", target.id]) {
24
23
  test("workflow references decode into contribution-taking transition builders", () => {
25
24
  const contributions = Type.Object({ report: Type.String() });
26
- const options: NornWorkflowRefSchemaOptions<typeof contributions> = { params: contributions };
25
+ const options: NornWorkflowRefSchemaOptions<typeof contributions> = { args: contributions };
27
26
  const schema = workflowRefSchema(options);
28
- assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), { type: "next", workflowId: "refs.finish", params: {} });
27
+ assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), { type: "next", workflowId: "refs.finish", args: {} });
29
28
  interface Report { report: string }
30
29
  const report: Report = { report: "done" };
31
30
  assert.deepEqual(Value.Decode(schema, reference)(report), target(report));
32
- const input = { workflow: reference, forwardParams: { task: "retain", report: "stale" } };
31
+ const input = { workflow: reference, forwardArgs: { task: "retain", report: "stale" } };
33
32
  const next = Value.Decode(schema, input);
34
- assert.deepEqual(next({ report: "done" }), { type: "next", workflowId: reference, params: { task: "retain", report: "done" } });
35
- assert.deepEqual(next({ report: "again" }).params, { task: "retain", report: "again" });
36
- assert.deepEqual(input.forwardParams, { task: "retain", report: "stale" });
33
+ assert.deepEqual(next({ report: "done" }), { type: "next", workflowId: reference, args: { task: "retain", report: "done" } });
34
+ assert.deepEqual(next({ report: "again" }).args, { task: "retain", report: "again" });
35
+ assert.deepEqual(input.forwardArgs, { task: "retain", report: "stale" });
37
36
  assert.throws(() => Reflect.apply(next, undefined, [{}]));
38
37
  });
39
38
  }
@@ -46,7 +45,7 @@ test("nullable reference inputs retain their required property and input union",
46
45
  assert.equal(Value.Check(schema, { next: null }), true);
47
46
  assert.equal(Value.Check(schema, { next: "refs.finish" }), true);
48
47
  expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.0.type", "string");
49
- expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", ["workflow", "forwardParams"]);
48
+ expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", ["workflow", "forwardArgs"]);
50
49
  });
51
50
 
52
51
  test("optional reference properties remain optional in validation and inspection", () => {
@@ -62,35 +61,35 @@ test("inspection advertises encoded contributions and invocation does not decode
62
61
  count: Type.Decode(Type.String(), value => { decodes++; return Number(value); }),
63
62
  label: Type.Optional(Type.String({ default: "collected" })),
64
63
  });
65
- const reference = Type.With(workflowRefSchema({ params: contributions }), { description: "Continue with the collected records" });
64
+ const reference = Type.With(workflowRefSchema({ args: contributions }), { description: "Continue with the collected records" });
66
65
  const schema = Type.Object({ next: reference });
67
66
  const inspected = inspectSchema(schema);
68
- expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedParamsSchema", inspectSchema(contributions));
67
+ expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
69
68
  expect(inspected).toHaveProperty("properties.next.description", "Continue with the collected records");
70
69
  expect(inspected).toHaveProperty("properties.next.anyOf", inspectSchema(workflowRefSchema()).anyOf);
71
- expect(inspected).toHaveProperty("properties.next.anyOf.1.properties.forwardParams.patternProperties", { "^.*$": {} });
72
- expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedParamsSchema.required", ["records", "count"]);
73
- expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedParamsSchema.properties.count.type", "string");
74
- const forwardParams = { taskId: "task-42", nested: { labels: ["one", "two"], enabled: false, absent: null } };
75
- const next = Value.Decode(schema, { next: { workflow: "refs.finish", forwardParams } }).next;
70
+ expect(inspected).toHaveProperty("properties.next.anyOf.1.properties.forwardArgs.patternProperties", { "^.*$": {} });
71
+ expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema.required", ["records", "count"]);
72
+ expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema.properties.count.type", "string");
73
+ const forwardArgs = { taskId: "task-42", nested: { labels: ["one", "two"], enabled: false, absent: null } };
74
+ const next = Value.Decode(schema, { next: { workflow: "refs.finish", forwardArgs } }).next;
76
75
  const contribution = { records: { path: "records.json" }, count: "2" };
77
- assert.deepEqual(next(contribution).params, { ...forwardParams, ...contribution });
76
+ assert.deepEqual(next(contribution).args, { ...forwardArgs, ...contribution });
78
77
  assert.equal(decodes, 0);
79
78
  assert.throws(() => Reflect.apply(next, undefined, [{ records: { path: "records.json" }, count: 2 }]));
80
79
  });
81
80
 
82
81
  test("references without contributions advertise an empty object schema", () => {
83
82
  const reference = workflowRefSchema();
84
- expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedParamsSchema", inspectSchema(Type.Object({})));
85
- assert.deepEqual(Value.Decode(reference, "refs.finish")({}), { type: "next", workflowId: "refs.finish", params: {} });
83
+ expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema", inspectSchema(Type.Object({})));
84
+ assert.deepEqual(Value.Decode(reference, "refs.finish")({}), { type: "next", workflowId: "refs.finish", args: {} });
86
85
  });
87
86
 
88
87
  test("optional, nullable and array references retain annotations and callable codecs", () => {
89
- const reference = workflowRefSchema({ params: Type.Object({ report: Type.String() }) });
88
+ const reference = workflowRefSchema({ args: Type.Object({ report: Type.String() }) });
90
89
  const schema = Type.Object({ optional: Type.Optional(reference), nullable: Type.Union([reference, Type.Null()]), many: Type.Array(reference) });
91
90
  const inspected = inspectSchema(schema);
92
91
  for (const path of ["properties.optional", "properties.nullable.anyOf.0", "properties.many.items"]) {
93
- expect(inspected).toHaveProperty(`${path}.x-norn-workflow-ref.contributedParamsSchema`, inspectSchema(Type.Object({ report: Type.String() })));
92
+ expect(inspected).toHaveProperty(`${path}.x-norn-workflow-ref.contributedArgsSchema`, inspectSchema(Type.Object({ report: Type.String() })));
94
93
  }
95
94
  assert.deepEqual(inspected.required, ["nullable", "many"]);
96
95
  const decoded = Value.Decode(schema, { nullable: null, many: ["refs.finish"], optional: "refs.finish" });
@@ -101,56 +100,56 @@ test("optional, nullable and array references retain annotations and callable co
101
100
 
102
101
  test("nested references retain encoded payloads in contributed parameters", () => {
103
102
  const finalContributions = Type.Object({ summary: Type.String() });
104
- const contributions = Type.Object({ records: artifactRefSchema, next: workflowRefSchema({ params: finalContributions }) });
105
- const reference = workflowRefSchema({ params: contributions });
106
- expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedParamsSchema.properties.next.x-norn-workflow-ref.contributedParamsSchema", inspectSchema(finalContributions));
107
- const params = { records: { path: "records.json" }, next: { workflow: "refs.finish", forwardParams: { batch: 1 } } };
108
- const transition = Value.Decode(reference, "intermediate")(params);
109
- assert.deepEqual(transition.params, params);
110
- assert.deepEqual(Value.Decode(contributions, transition.params).next({ summary: "done" }).params, { batch: 1, summary: "done" });
103
+ const contributions = Type.Object({ records: artifactRefSchema, next: workflowRefSchema({ args: finalContributions }) });
104
+ const reference = workflowRefSchema({ args: contributions });
105
+ expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema.properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(finalContributions));
106
+ const args = { records: { path: "records.json" }, next: { workflow: "refs.finish", forwardArgs: { batch: 1 } } };
107
+ const transition = Value.Decode(reference, "intermediate")(args);
108
+ assert.deepEqual(transition.args, args);
109
+ assert.deepEqual(Value.Decode(contributions, transition.args).next({ summary: "done" }).args, { batch: 1, summary: "done" });
111
110
  });
112
111
 
113
112
  test("recursive contributions use native TypeBox definitions", () => {
114
113
  const contributions = Type.Cyclic({ Contribution: Type.Object({ label: Type.String(), children: Type.Array(Type.Ref("Contribution")) }) }, "Contribution");
115
- const reference = workflowRefSchema({ params: contributions });
116
- expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedParamsSchema", inspectSchema(contributions));
117
- const params = { label: "root", children: [{ label: "leaf", children: [] }] };
118
- assert.deepEqual(Value.Decode(reference, "refs.finish")(params).params, params);
114
+ const reference = workflowRefSchema({ args: contributions });
115
+ expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
116
+ const args = { label: "root", children: [{ label: "leaf", children: [] }] };
117
+ assert.deepEqual(Value.Decode(reference, "refs.finish")(args).args, args);
119
118
  });
120
119
 
121
120
  test("object-valued unions, records, intersections and root codecs can contribute", () => {
122
- const union = workflowRefSchema({ params: Type.Union([Type.Object({ kind: Type.Literal("ok"), report: Type.String() }), Type.Object({ kind: Type.Literal("error"), reason: Type.String() })]) });
123
- assert.deepEqual(Value.Decode(union, "target")({ kind: "error", reason: "failed" }).params, { kind: "error", reason: "failed" });
124
- const record = workflowRefSchema({ params: Type.Record(Type.String(), Type.Integer()) });
125
- assert.deepEqual(Value.Decode(record, "target")({ first: 1 }).params, { first: 1 });
126
- const intersection = workflowRefSchema({ params: Type.Intersect([Type.Object({ left: Type.String() }), Type.Object({ right: Type.Integer() })]) });
127
- assert.deepEqual(Value.Decode(intersection, "target")({ left: "x", right: 2 }).params, { left: "x", right: 2 });
128
- const codec = workflowRefSchema({ params: Type.Decode(Type.Object({ text: Type.String() }), value => value.text) });
129
- assert.deepEqual(Value.Decode(codec, "target")({ text: "encoded" }).params, { text: "encoded" });
130
- const unknown = Value.Decode(workflowRefSchema({ params: Type.Unknown() }), "target");
121
+ const union = workflowRefSchema({ args: Type.Union([Type.Object({ kind: Type.Literal("ok"), report: Type.String() }), Type.Object({ kind: Type.Literal("error"), reason: Type.String() })]) });
122
+ assert.deepEqual(Value.Decode(union, "target")({ kind: "error", reason: "failed" }).args, { kind: "error", reason: "failed" });
123
+ const record = workflowRefSchema({ args: Type.Record(Type.String(), Type.Integer()) });
124
+ assert.deepEqual(Value.Decode(record, "target")({ first: 1 }).args, { first: 1 });
125
+ const intersection = workflowRefSchema({ args: Type.Intersect([Type.Object({ left: Type.String() }), Type.Object({ right: Type.Integer() })]) });
126
+ assert.deepEqual(Value.Decode(intersection, "target")({ left: "x", right: 2 }).args, { left: "x", right: 2 });
127
+ const codec = workflowRefSchema({ args: Type.Decode(Type.Object({ text: Type.String() }), value => value.text) });
128
+ assert.deepEqual(Value.Decode(codec, "target")({ text: "encoded" }).args, { text: "encoded" });
129
+ const unknown = Value.Decode(workflowRefSchema({ args: Type.Unknown() }), "target");
131
130
  for (const value of [null, [], "text", 1]) assert.throws(() => Reflect.apply(unknown, undefined, [value]), /contributions must be objects/);
132
131
  for (const value of [new Date(), { callback: () => undefined }]) assert.throws(() => Reflect.apply(unknown, undefined, [value]));
133
132
  });
134
133
 
135
134
  test("non-JSON contributions fail explicit inspection without preventing construction", () => {
136
- const reference = workflowRefSchema({ params: Type.BigInt() });
135
+ const reference = workflowRefSchema({ args: Type.BigInt() });
137
136
  assert.equal(typeof Value.Decode(reference, "refs.finish"), "function");
138
137
  assert.throws(() => inspectSchema(reference));
139
- const nested = workflowRefSchema({ params: Type.Object({ next: reference }) });
138
+ const nested = workflowRefSchema({ args: Type.Object({ next: reference }) });
140
139
  assert.throws(() => inspectSchema(Type.Object({ next: Type.Optional(Type.Array(nested)) })));
141
140
  });
142
141
 
143
142
  test("inspection traverses schema positions, not defaults, examples or property-name maps", () => {
144
- const payload = { "x-norn-workflow-ref": { contributedParamsSchema: { type: "bigint" } } };
143
+ const payload = { "x-norn-workflow-ref": { contributedArgsSchema: { type: "bigint" } } };
145
144
  const schema = Type.Object({ "x-norn-workflow-ref": Type.String() }, { default: payload, examples: [payload] });
146
145
  assert.doesNotThrow(() => inspectSchema(schema));
147
- const invalid = workflowRefSchema({ params: Type.BigInt() });
146
+ const invalid = workflowRefSchema({ args: Type.BigInt() });
148
147
  for (const wrapper of [Type.Record(Type.String(), invalid), Type.Tuple([invalid]), Type.Object({}, { additionalProperties: invalid }), Type.Cyclic({ Root: Type.Object({ child: invalid }) }, "Root")]) {
149
148
  assert.throws(() => inspectSchema(wrapper));
150
149
  }
151
150
  });
152
151
 
153
- for (const input of [undefined, null, "", 3, {}, { workflow: "refs.finish" }, { workflow: "", forwardParams: {} }, { workflow: "refs.finish", forwardParams: null }, { workflow: target, forwardParams: {} }]) {
152
+ for (const input of [undefined, null, "", 3, {}, { workflow: "refs.finish" }, { workflow: "", forwardArgs: {} }, { workflow: "refs.finish", forwardArgs: null }, { workflow: target, forwardArgs: {} }]) {
154
153
  test(`reference input schema rejects invalid shape without coercion: ${JSON.stringify(input)}`, () => {
155
154
  assert.equal(Value.Check(workflowRefSchema(), input), false);
156
155
  });
package/dist/cli.js CHANGED
@@ -23,9 +23,9 @@ import { clearRunResumeRequest, readRunLaunchRequest, readRunResumeRequest, writ
23
23
  import { readRunMetrics } from "./internal/metrics.js";
24
24
  import { getRunLeaseOwner, NornRunLease } from "./internal/run-lease.js";
25
25
  import { generateRunName } from "./internal/run-names.js";
26
- import { getRunInfo, listRuns, mergeInterruptedWorkflowParams, resolveRunRoot } from "./internal/run-state.js";
26
+ import { assertRunVersion, getRunInfo, listRuns, mergeInterruptedWorkflowArgs, resolveRunRoot } from "./internal/run-state.js";
27
27
  import { NornRunStore } from "./internal/run-store.js";
28
- import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./plugin-loader.js";
28
+ import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./workflow-loader.js";
29
29
  var RUNS_ROOT = join(".norn", "runs");
30
30
  var RUN_WAIT_INTERVAL_MS = 1e3;
31
31
  var CLI_DESCRIPTION = "Norn is a harness-agnostic runtime for agent-driven and code-driven workflows, built primarily for agents. Build workflows with the Norn SDK; use this JSON-native CLI to discover workflows, start or resume runs, inspect evidence, and manage the installed runtime.";
@@ -112,9 +112,9 @@ var COMMANDS = [
112
112
  {
113
113
  id: "project.inspect",
114
114
  path: ["project", "inspect"],
115
- description: "Use when discovering the active Norn project, plugins, and workflow sources.",
115
+ description: "Use when discovering the active Norn project, configuration, and workflow sources.",
116
116
  usage: "norn project inspect",
117
- output: "JSON object with project metadata under project, isComplete, and plugin diagnostics. Incomplete discovery does not permit execution.",
117
+ output: "JSON object with project metadata under project, isComplete, and registration diagnostics. Incomplete discovery does not permit execution.",
118
118
  examples: ["norn project inspect"],
119
119
  execute: async (args) => {
120
120
  assertNoExtraArgs("project inspect", args);
@@ -127,17 +127,17 @@ var COMMANDS = [
127
127
  description: "Use when selecting a Norn workflow for a user task; defaults to entrypoint workflows.",
128
128
  usage: "norn workflows list [--entrypoints|--all]",
129
129
  options: ["--entrypoints: list entrypoint workflows", "--all: include internal workflow steps"],
130
- output: "JSON object with workflow summaries under workflows, isComplete, and plugin diagnostics. Successful discovery can be incomplete; start/resume remain strict.",
130
+ output: "JSON object with workflow summaries under workflows, isComplete, and registration diagnostics. Successful discovery can be incomplete; start/resume remain strict.",
131
131
  examples: ["norn workflows list", "norn workflows list --all"],
132
132
  execute: listWorkflows
133
133
  },
134
134
  {
135
135
  id: "workflows.inspect",
136
136
  path: ["workflows", "inspect"],
137
- description: "Use when reading a workflow's instructions, params schema, gate contract, and source plugin before starting or editing it.",
137
+ description: "Use when reading a workflow's instructions, args schema, gate contract, and registration source before starting or editing it.",
138
138
  usage: "norn workflows inspect <workflow-id>",
139
139
  arguments: ["workflow-id: fully qualified workflow id"],
140
- output: "JSON object with workflow details, isComplete, and plugin diagnostics. workflow is null if unavailable in an incomplete catalog or its schema cannot be inspected; an unknown id in a complete catalog is an error.",
140
+ output: "JSON object with workflow details, isComplete, and registration diagnostics. workflow is null if unavailable in an incomplete catalog or its schema cannot be inspected; an unknown id in a complete catalog is an error.",
141
141
  examples: ["norn workflows inspect example.plan"],
142
142
  execute: async (args) => {
143
143
  const workflowId = requiredArg("workflows inspect", args, 0, "workflow id");
@@ -148,12 +148,12 @@ var COMMANDS = [
148
148
  {
149
149
  id: "runs.start",
150
150
  path: ["runs", "start"],
151
- description: "Use when starting a Norn workflow run after the workflow id and params are known.",
151
+ description: "Use when starting a Norn workflow run after the workflow id and args are known.",
152
152
  usage: "norn runs start <workflow-id>",
153
153
  arguments: ["workflow-id: fully qualified workflow id to start"],
154
- stdin: 'Optional JSON object: {"params":{...},"config":{"pluginId":{...}}}.',
154
+ stdin: 'Optional JSON object: {"args":{...},"config":{"workflowOrScopeId":{...}}}.',
155
155
  output: "JSON object with started run info under run.",
156
- examples: [`printf '{"params":{"task":"Add tests"}}' | norn runs start example.plan`],
156
+ examples: [`printf '{"args":{"task":"Add tests"}}' | norn runs start example.plan`],
157
157
  execute: async (args) => {
158
158
  const workflowId = requiredArg("runs start", args, 0, "workflow id");
159
159
  await startRun(workflowId, args.slice(1));
@@ -165,9 +165,9 @@ var COMMANDS = [
165
165
  description: "Use when resuming an interrupted gate or a checkpoint restored for retry.",
166
166
  usage: "norn runs resume <run>",
167
167
  arguments: ["run: run id, generated name, or run path"],
168
- stdin: 'Optional JSON object: {"params":{...}}. Interrupted runs require a patch containing only editable gate fields; pending-resume runs do not accept params.',
168
+ stdin: 'Optional JSON object: {"args":{...}}. Interrupted runs require a patch containing only editable gate fields; pending-resume runs do not accept args.',
169
169
  output: "JSON object with resumed run info under run.",
170
- examples: [`printf '{"params":{"decision":"accept"}}' | norn runs resume quiet-river-lantern`],
170
+ examples: [`printf '{"args":{"decision":"accept"}}' | norn runs resume quiet-river-lantern`],
171
171
  execute: async (args) => {
172
172
  const run = requiredArg("runs resume", args, 0, "run");
173
173
  await resumeRun(run, args.slice(1));
@@ -686,7 +686,7 @@ async function initProject() {
686
686
  const projectPath = resolve(projectRoot, NORN_PROJECT_FILE_NAME);
687
687
  if (await isFile(projectPath)) throw new Error(`Norn project already exists: ${projectPath}`);
688
688
  await mkdir(resolve(projectRoot, RUNS_ROOT), { recursive: true });
689
- await writeFile(projectPath, `${JSON.stringify({ version: 1, plugins: [], includes: [], config: {} }, null, 2)}
689
+ await writeFile(projectPath, `${JSON.stringify({ version: 1, workflows: [], includes: [], config: {} }, null, 2)}
690
690
  `, "utf8");
691
691
  await ensureGitignoreExcludesRunState(projectRoot);
692
692
  writeJson({ project: { path: projectPath, root: projectRoot, runsRoot: resolve(projectRoot, RUNS_ROOT) } });
@@ -699,21 +699,21 @@ async function inspectProject() {
699
699
  const { project, isComplete, diagnostics } = await discoverNornProject(process.cwd());
700
700
  writeJson({ project, isComplete, diagnostics });
701
701
  }
702
- async function startRun(workflowId, args) {
703
- assertNoStructuredInputArgs("runs start", args);
702
+ async function startRun(workflowId, commandArgs) {
703
+ assertNoStructuredInputArgs("runs start", commandArgs);
704
704
  const project = await loadNornProject(process.cwd());
705
705
  const workflow = project.registry.workflowById(workflowId);
706
706
  if (!workflow) throw new Error(`Unknown workflow: ${workflowId}`);
707
707
  const input = parseStartRunInput(await readStdinJson());
708
- const params = input.params === void 0 ? {} : input.params;
709
- Value.Decode(workflow.params, params);
708
+ const args = input.args === void 0 ? {} : input.args;
709
+ Value.Decode(workflow.args, args);
710
710
  const configOverride = input.config;
711
711
  const id = randomUUID();
712
712
  const name = generateRunName(new Set((await listRuns(project.projectRoot)).map((run) => run.name)));
713
713
  const runRoot = resolve(project.projectRoot, RUNS_ROOT, id);
714
714
  await mkdir(runRoot, { recursive: true });
715
715
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
716
- await writeRunLaunchRequest(runRoot, { version: 1, type: "run", id, name, workflowId, params, configOverride, createdAt });
716
+ await writeRunLaunchRequest(runRoot, { version: 2, type: "run", id, name, workflowId, args, configOverride, createdAt });
717
717
  await startDetachedExecuteRun(id, project.projectRoot);
718
718
  writeJson({ run: startedRunInfo({ id, name, workflow, runRoot, createdAt }) });
719
719
  }
@@ -726,8 +726,9 @@ async function resumeRun(run, args) {
726
726
  let request;
727
727
  try {
728
728
  const runInfo = await getRunInfo(runRoot);
729
- const params = await parseResumeParams(runInfo, input.params);
730
- request = { version: 1, type: "resume", id: runInfo.id, requestId: randomUUID(), params, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
729
+ assertRunVersion(runInfo.version);
730
+ const args2 = await parseResumeArgs(runInfo, input.args);
731
+ request = { version: 2, type: "resume", id: runInfo.id, requestId: randomUUID(), args: args2, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
731
732
  await writeRunResumeRequest(runRoot, request);
732
733
  } finally {
733
734
  await lease.release();
@@ -740,18 +741,18 @@ async function resumeRun(run, args) {
740
741
  }
741
742
  writeJson({ run: await getRunInfo(runRoot) });
742
743
  }
743
- async function parseResumeParams(runInfo, params) {
744
+ async function parseResumeArgs(runInfo, args) {
744
745
  if (runInfo.status === "interrupted") {
745
- if (params === void 0) throw new Error(`Interrupted workflow resume requires params: ${runInfo.name}`);
746
+ if (args === void 0) throw new Error(`Interrupted workflow resume requires args: ${runInfo.name}`);
746
747
  if (!runInfo.currentWorkflowId) throw new Error(`Run has no current workflow: ${runInfo.name}`);
747
748
  const project = await loadNornProject(process.cwd());
748
749
  const workflow = project.registry.workflowById(runInfo.currentWorkflowId);
749
750
  if (!workflow) throw new Error(`Unknown workflow for resumed run: ${runInfo.currentWorkflowId}`);
750
- Value.Decode(workflow.params, mergeInterruptedWorkflowParams(runInfo.interruption?.params, params, runInfo.interruption?.fields));
751
- return params;
751
+ Value.Decode(workflow.args, mergeInterruptedWorkflowArgs(runInfo.interruption?.args, args, runInfo.interruption?.fields));
752
+ return args;
752
753
  }
753
754
  if (runInfo.status === "pendingResume") {
754
- if (params !== void 0) throw new Error(`Pending-resume workflows do not accept params: ${runInfo.name}`);
755
+ if (args !== void 0) throw new Error(`Pending-resume workflows do not accept args: ${runInfo.name}`);
755
756
  return void 0;
756
757
  }
757
758
  throw new Error(`Run must be rolled back before resuming: ${runInfo.name}`);
@@ -823,11 +824,11 @@ async function executeRun(runId) {
823
824
  try {
824
825
  const project = await loadNornProject(process.cwd());
825
826
  const engine = new NornEngine({ cwd: project.projectRoot, signal: abortController.signal, gateMode: "pause", config: project.projectConfig });
826
- for (const plugin of project.plugins) engine.registerPlugin(plugin);
827
+ engine.registerWorkflows(project.definitions);
827
828
  if (request.type === "run") {
828
829
  const workflow = project.registry.workflowById(request.workflowId);
829
830
  if (!workflow) throw new Error(`Unknown workflow: ${request.workflowId}`);
830
- await engine.runWorkflow(workflow, request.params, { id: request.id, name: request.name, configOverride: request.configOverride });
831
+ await engine.runWorkflow(workflow, request.args, { id: request.id, name: request.name, configOverride: request.configOverride });
831
832
  } else {
832
833
  await engine.resumeRequestedWorkflow({ runRoot, request });
833
834
  }
@@ -913,7 +914,7 @@ function detachedExecuteRunArgs(runId) {
913
914
  }
914
915
  function startedRunInfo(input) {
915
916
  return {
916
- version: 1,
917
+ version: 2,
917
918
  id: input.id,
918
919
  name: input.name,
919
920
  path: input.runRoot,
@@ -963,14 +964,14 @@ async function readStdin() {
963
964
  function parseStartRunInput(value) {
964
965
  if (value === void 0) return {};
965
966
  const input = parseStructuredInputObject("runs start", value);
966
- assertStructuredInputKeys("runs start", input, ["params", "config"]);
967
- return { params: input.params, config: input.config };
967
+ assertStructuredInputKeys("runs start", input, ["args", "config"]);
968
+ return { args: input.args, config: input.config };
968
969
  }
969
970
  function parseResumeRunInput(value) {
970
971
  if (value === void 0) return {};
971
972
  const input = parseStructuredInputObject("runs resume", value);
972
- assertStructuredInputKeys("runs resume", input, ["params"]);
973
- return { params: input.params };
973
+ assertStructuredInputKeys("runs resume", input, ["args"]);
974
+ return { args: input.args };
974
975
  }
975
976
  function parseStructuredInputObject(command, value) {
976
977
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${command} reads a JSON object from stdin`);
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DeletedNornRunInfo, NornProjectInspection, NornWorkflowCatalogInfo, NornWorkflowInspection, NornRunCheckpoint, NornRunInfo, NornRunMetrics, NornWorkflowStateReader } from "@vimhead.dev/norn";
1
+ import type { DeletedNornRunInfo, NornProjectInspection, NornWorkflowCatalogInfo, NornWorkflowInspection, NornRunCheckpoint, NornRunInfo, NornRunMetrics } from "@vimhead.dev/norn";
2
2
  import type { NornRegisteredWorkflow } from "./internal/workflow-registry.ts";
3
3
  export { NornProjectLoadError } from "./internal/errors.ts";
4
4
  export type NornClientInput = {
@@ -16,16 +16,15 @@ export type NornClient = {
16
16
  inspect(workflowId: string): Promise<NornWorkflowInspection>;
17
17
  entries(): Promise<readonly NornRegisteredWorkflow[]>;
18
18
  };
19
- readonly state: NornWorkflowStateReader;
20
19
  readonly runs: {
21
20
  start(input: {
22
21
  readonly workflowId: string;
23
- readonly params: unknown;
22
+ readonly args: unknown;
24
23
  readonly configOverride?: unknown;
25
24
  }): Promise<NornRunInfo>;
26
25
  resume(input: {
27
26
  readonly run: string;
28
- readonly params?: unknown;
27
+ readonly args?: unknown;
29
28
  }): Promise<NornRunInfo>;
30
29
  wait(run: string): Promise<NornRunInfo>;
31
30
  list(): Promise<NornRunInfo[]>;
package/dist/client.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { spawn } from "node:child_process";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { createInterface } from "node:readline";
5
- import { loadNornProject } from "./plugin-loader.js";
5
+ import { loadNornProject } from "./workflow-loader.js";
6
6
  import { NornProjectLoadError } from "./internal/errors.js";
7
7
  import { NornProjectLoadError as NornProjectLoadError2 } from "./internal/errors.js";
8
8
  function createNornClient(input = {}) {
@@ -17,7 +17,6 @@ function createNornClient(input = {}) {
17
17
  inspect: async (workflowId) => processRunner.readJson(["workflows", "inspect", workflowId]),
18
18
  entries: async () => (await catalog.load()).workflows
19
19
  },
20
- state: catalog.state,
21
20
  runs: {
22
21
  start: async (startInput) => (await processRunner.readJson(["runs", "start", startInput.workflowId], JSON.stringify(runStartInput(startInput)))).run,
23
22
  resume: async (resumeInput) => (await processRunner.readJson(["runs", "resume", resumeInput.run], runResumeStdin(resumeInput))).run,
@@ -41,10 +40,6 @@ var NornWorkflowCatalog = class {
41
40
  }
42
41
  cwd;
43
42
  loaded;
44
- state = {
45
- get: async (state) => (await this.load()).state.get(state),
46
- getOptional: async (state) => (await this.load()).state.getOptional(state)
47
- };
48
43
  load() {
49
44
  this.loaded ??= loadNornProject(this.cwd).catch((error) => {
50
45
  this.loaded = void 0;
@@ -104,10 +99,10 @@ var NornProcessRunner = class {
104
99
  }
105
100
  };
106
101
  function runStartInput(input) {
107
- return input.configOverride === void 0 ? { params: input.params } : { params: input.params, config: input.configOverride };
102
+ return input.configOverride === void 0 ? { args: input.args } : { args: input.args, config: input.configOverride };
108
103
  }
109
104
  function runResumeStdin(input) {
110
- return input.params === void 0 ? void 0 : JSON.stringify({ params: input.params });
105
+ return input.args === void 0 ? void 0 : JSON.stringify({ args: input.args });
111
106
  }
112
107
  export {
113
108
  NornProjectLoadError2 as NornProjectLoadError,
@@ -20,7 +20,7 @@ function renderNornDocumentationIntro(input) {
20
20
  "",
21
21
  "Live CLI contracts (argument arrays appended to the runtime argv above):",
22
22
  '- ["workflows", "list"] \u2014 currently registered entrypoints.',
23
- '- ["workflows", "inspect", "<workflow-id>"] \u2014 current instructions, params schema, and source.',
23
+ '- ["workflows", "inspect", "<workflow-id>"] \u2014 current instructions, args schema, and source.',
24
24
  '- ["help"] \u2014 command usage.'
25
25
  ].join("\n");
26
26
  }
@@ -1,7 +1,7 @@
1
1
  export declare const NORN_GENERATED_BUILD_INFO: {
2
2
  readonly kind: "npm-registry";
3
- readonly version: "0.1.0-tip.35359392805.1";
4
- readonly commit: "f958006ca0ae3752060727c5b76b2ac1a008b103";
3
+ readonly version: "0.1.0-tip.35390859630.1";
4
+ readonly commit: "7a7976170d62c342c29c6479444394d542c3f22b";
5
5
  readonly packageSpec: "@vimhead.dev/norn-cli@tip";
6
6
  readonly upgrade: {
7
7
  readonly supported: false;
@@ -1,8 +1,8 @@
1
1
  // src/generated-build-info.ts
2
2
  var NORN_GENERATED_BUILD_INFO = {
3
3
  "kind": "npm-registry",
4
- "version": "0.1.0-tip.35359392805.1",
5
- "commit": "f958006ca0ae3752060727c5b76b2ac1a008b103",
4
+ "version": "0.1.0-tip.35390859630.1",
5
+ "commit": "7a7976170d62c342c29c6479444394d542c3f22b",
6
6
  "packageSpec": "@vimhead.dev/norn-cli@tip",
7
7
  "upgrade": {
8
8
  "supported": false,
@@ -52,12 +52,12 @@ var NornAgentResponseToolFactory = class {
52
52
  label: Type.String({ description: "The exact workflow agent label provided in the prompt." }),
53
53
  response: Type.Unknown({ description: "The structured response object requested by the workflow." })
54
54
  }),
55
- async execute(_toolCallId, params) {
55
+ async execute(_toolCallId, args) {
56
56
  try {
57
- const captured = responseCollector.capture(params.runId, params.label, params.response);
57
+ const captured = responseCollector.capture(args.runId, args.label, args.response);
58
58
  return {
59
59
  content: [{ type: "text", text: `Recorded workflow agent response for ${captured.label}.` }],
60
- details: { valid: true, runId: params.runId, label: captured.label, response: captured.response },
60
+ details: { valid: true, runId: args.runId, label: captured.label, response: captured.response },
61
61
  terminate: true
62
62
  };
63
63
  } catch (error) {
@@ -1,4 +1,4 @@
1
- import { type NornAnyWorkflowDeclaration, type NornAnyWorkflowPluginManifest, type NornDispose, type NornRunCheckpoint, type NornRunInfo, type NornRunResult, type NornRunStartOptions, type NornWorkflowPlugin } from "@vimhead.dev/norn";
1
+ import { type NornAnyWorkflowDeclaration, type NornDispose, type NornRunCheckpoint, type NornRunInfo, type NornRunResult, type NornRunStartOptions } from "@vimhead.dev/norn";
2
2
  import { NornAgentResponseCollector } from "./agent-response-tool.ts";
3
3
  import { type NornRunResumeRequest } from "./launch-request.ts";
4
4
  import { type NornRegisteredWorkflow } from "./workflow-registry.ts";
@@ -13,19 +13,16 @@ export type NornEngineInput = {
13
13
  export declare class NornEngine {
14
14
  private readonly input;
15
15
  private readonly registry;
16
- private readonly registrarState;
17
- private readonly disposersByPlugin;
18
16
  private readonly responseCollector;
19
17
  private readonly activeRuns;
20
18
  constructor(input: NornEngineInput);
21
- registerPlugin<TManifest extends NornAnyWorkflowPluginManifest>(plugin: NornWorkflowPlugin<TManifest>): NornDispose;
19
+ registerWorkflows(workflows: readonly NornAnyWorkflowDeclaration[]): NornDispose;
22
20
  listWorkflows(): import("@vimhead.dev/norn").NornRegisteredWorkflowInfo[];
23
21
  visibleWorkflowEntries(): NornRegisteredWorkflow[];
24
- private resolvePluginImplementation;
25
- runWorkflow<TWorkflow extends NornAnyWorkflowDeclaration>(workflow: TWorkflow, params: unknown, options: NornRunStartOptions | undefined): Promise<NornRunResult>;
22
+ runWorkflow<TWorkflow extends NornAnyWorkflowDeclaration>(workflow: TWorkflow, args: unknown, options: NornRunStartOptions | undefined): Promise<NornRunResult>;
26
23
  listRunCheckpoints(path: string): Promise<NornRunCheckpoint[]>;
27
24
  rollbackRun(path: string, checkpointId: string): Promise<NornRunInfo>;
28
- resumeWorkflow(path: string, params?: unknown): Promise<NornRunResult>;
25
+ resumeWorkflow(path: string, args?: unknown): Promise<NornRunResult>;
29
26
  resumeRequestedWorkflow(input: {
30
27
  readonly runRoot: string;
31
28
  readonly request: NornRunResumeRequest;
@@ -40,5 +37,4 @@ export declare class NornEngine {
40
37
  private startActiveRun;
41
38
  private finishActiveRun;
42
39
  private failActiveRun;
43
- private disposePlugin;
44
40
  }