@vimhead.dev/norn-cli 0.1.0-tip.35614826665.1 → 0.1.0-tip.35724621678.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 (119) hide show
  1. package/assets/README.md +41 -13
  2. package/assets/docs/README.md +16 -16
  3. package/assets/docs/agents.md +23 -15
  4. package/assets/docs/cli.md +10 -8
  5. package/assets/docs/composition.md +7 -7
  6. package/assets/docs/persistence.md +22 -22
  7. package/assets/docs/projects.md +6 -6
  8. package/assets/docs/recovery.md +10 -10
  9. package/assets/docs/schemas.md +42 -36
  10. package/assets/docs/workflows.md +39 -32
  11. package/assets/examples/agent-then-analysis/input.json +3 -3
  12. package/assets/examples/agent-then-analysis/norn.project.json +2 -2
  13. package/assets/examples/agent-then-analysis/plugin.ts +37 -9
  14. package/assets/examples/caller-owned-routing/README.md +6 -6
  15. package/assets/examples/caller-owned-routing/assessment.ts +18 -4
  16. package/assets/examples/caller-owned-routing/contracts.ts +7 -2
  17. package/assets/examples/caller-owned-routing/input.json +12 -12
  18. package/assets/examples/caller-owned-routing/norn.project.json +2 -2
  19. package/assets/examples/caller-owned-routing/revision.ts +7 -2
  20. package/assets/examples/caller-owned-routing/router.ts +14 -3
  21. package/assets/examples/caller-selected-continuation/caller.ts +38 -12
  22. package/assets/examples/caller-selected-continuation/input.json +7 -7
  23. package/assets/examples/caller-selected-continuation/norn.project.json +2 -2
  24. package/assets/examples/caller-selected-continuation/producer.ts +10 -3
  25. package/assets/examples/coordinating-multiple-agents/README.md +6 -6
  26. package/assets/examples/coordinating-multiple-agents/input.json +20 -8
  27. package/assets/examples/coordinating-multiple-agents/norn.project.json +2 -2
  28. package/assets/examples/coordinating-multiple-agents/plugin.ts +165 -46
  29. package/assets/examples/coordinating-multiple-agents/queue-tools.ts +30 -7
  30. package/assets/examples/coordinating-multiple-agents/work-queue.ts +211 -63
  31. package/assets/examples/getting-started/norn.project.json +2 -2
  32. package/assets/examples/getting-started/plugin.ts +33 -13
  33. package/assets/examples/minimal-workflow/norn.project.json +2 -2
  34. package/assets/examples/minimal-workflow/plugin.ts +12 -3
  35. package/assets/examples/shared-state/README.md +6 -6
  36. package/assets/examples/shared-state/input.json +1 -1
  37. package/assets/examples/shared-state/norn.project.json +2 -2
  38. package/assets/examples/shared-state/plugin.ts +44 -15
  39. package/assets/examples/shared-state/shared-state.ts +46 -13
  40. package/assets/examples/shared-state/state-tools.ts +68 -16
  41. package/assets/examples/worktree-development-loop/README.md +12 -12
  42. package/assets/examples/worktree-development-loop/norn.project.json +7 -7
  43. package/assets/examples/worktree-development-loop/plugin.ts +7 -1
  44. package/assets/examples/worktree-development-loop/scope.ts +4 -1
  45. package/assets/examples/worktree-development-loop/shared/commands.ts +6 -2
  46. package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -4
  47. package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -1
  48. package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +11 -1
  49. package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +6 -2
  50. package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +40 -10
  51. package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +5 -1
  52. package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
  53. package/assets/examples/worktree-development-loop/workflows/planning/index.ts +5 -1
  54. package/assets/examples/worktree-development-loop/workflows/review/execute.ts +33 -9
  55. package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -8
  56. package/assets/package.json +1 -1
  57. package/assets/packages/cli/src/build-info.ts +4 -1
  58. package/assets/packages/cli/src/bun/cli.ts +21 -5
  59. package/assets/packages/cli/src/cli.ts +585 -170
  60. package/assets/packages/cli/src/client.ts +194 -36
  61. package/assets/packages/cli/src/documentation.ts +135 -30
  62. package/assets/packages/cli/src/generated-build-info.ts +2 -2
  63. package/assets/packages/cli/src/internal/agent-directory.ts +7 -2
  64. package/assets/packages/cli/src/internal/agent-response-tool.ts +57 -15
  65. package/assets/packages/cli/src/internal/agents.ts +248 -60
  66. package/assets/packages/cli/src/internal/commands.ts +82 -20
  67. package/assets/packages/cli/src/internal/documentation-bundle.ts +44 -13
  68. package/assets/packages/cli/src/internal/engine.ts +396 -87
  69. package/assets/packages/cli/src/internal/errors.ts +13 -5
  70. package/assets/packages/cli/src/internal/execution-context.ts +20 -12
  71. package/assets/packages/cli/src/internal/file-coordinator.ts +95 -26
  72. package/assets/packages/cli/src/internal/file-names.ts +4 -1
  73. package/assets/packages/cli/src/internal/launch-request.ts +74 -22
  74. package/assets/packages/cli/src/internal/logs.ts +10 -2
  75. package/assets/packages/cli/src/internal/metrics.ts +247 -61
  76. package/assets/packages/cli/src/internal/pi-assets.ts +105 -23
  77. package/assets/packages/cli/src/internal/run-lease.ts +72 -22
  78. package/assets/packages/cli/src/internal/run-log.ts +49 -20
  79. package/assets/packages/cli/src/internal/run-names.ts +120 -12
  80. package/assets/packages/cli/src/internal/run-state.ts +306 -71
  81. package/assets/packages/cli/src/internal/run-store.ts +216 -53
  82. package/assets/packages/cli/src/internal/usage.ts +23 -7
  83. package/assets/packages/cli/src/internal/worker-directory.ts +9 -3
  84. package/assets/packages/cli/src/internal/workflow-registry.ts +281 -70
  85. package/assets/packages/cli/src/internal/working-directory.ts +2 -1
  86. package/assets/packages/cli/src/workflow-loader.ts +341 -74
  87. package/assets/packages/core/src/atomic-files.ts +13 -4
  88. package/assets/packages/core/src/workflow-transition.ts +13 -2
  89. package/assets/packages/sdk/src/api.ts +248 -80
  90. package/assets/packages/sdk/src/index.ts +6 -1
  91. package/assets/packages/sdk/src/schema.ts +75 -21
  92. package/assets/tests/workflow-ref.test.ts +242 -57
  93. package/bin/norn.mjs +13 -4
  94. package/dist/cli.js +344 -90
  95. package/dist/client.js +100 -22
  96. package/dist/documentation.js +102 -25
  97. package/dist/generated-build-info.d.ts +2 -2
  98. package/dist/generated-build-info.js +2 -2
  99. package/dist/internal/agent-directory.js +3 -1
  100. package/dist/internal/agent-response-tool.js +42 -11
  101. package/dist/internal/agents.js +149 -37
  102. package/dist/internal/commands.js +65 -16
  103. package/dist/internal/documentation-bundle.js +23 -11
  104. package/dist/internal/engine.js +304 -66
  105. package/dist/internal/errors.js +4 -2
  106. package/dist/internal/execution-context.js +2 -1
  107. package/dist/internal/file-coordinator.js +59 -19
  108. package/dist/internal/launch-request.js +32 -14
  109. package/dist/internal/metrics.js +138 -38
  110. package/dist/internal/pi-assets.js +70 -19
  111. package/dist/internal/run-lease.js +29 -9
  112. package/dist/internal/run-log.js +24 -12
  113. package/dist/internal/run-state.js +172 -48
  114. package/dist/internal/run-store.js +157 -45
  115. package/dist/internal/worker-directory.js +6 -2
  116. package/dist/internal/workflow-registry.js +123 -32
  117. package/dist/internal/working-directory.js +2 -1
  118. package/dist/workflow-loader.js +198 -41
  119. package/package.json +2 -2
@@ -1,4 +1,9 @@
1
- import { workflowScope, isWorkflowDeclaration, workflowRefSchema, type NornWorkflowRefSchemaOptions } from "@vimhead.dev/norn";
1
+ import {
2
+ workflowScope,
3
+ isWorkflowDeclaration,
4
+ workflowRefSchema,
5
+ type NornWorkflowRefSchemaOptions,
6
+ } from "@vimhead.dev/norn";
2
7
  import { inspectSchema } from "@vimhead.dev/norn/schema";
3
8
  import assert from "node:assert/strict";
4
9
  import { Type } from "typebox";
@@ -7,45 +12,82 @@ import { expect, expectTypeOf, test } from "vitest";
7
12
 
8
13
  const refs = workflowScope({ name: "refs" });
9
14
  const target = refs.workflow({
10
- name: "finish", entrypoint: false, args: Type.Object({ report: Type.String() }),
11
- execute() { throw new Error("A callable must select a transition, not execute it"); },
15
+ name: "finish",
16
+ entrypoint: false,
17
+ args: Type.Object({ report: Type.String() }),
18
+ execute() {
19
+ throw new Error("A callable must select a transition, not execute it");
20
+ },
12
21
  });
13
22
 
14
23
  test("callable declarations retain metadata and construct transitions without execution", () => {
15
24
  expectTypeOf(target.id).toEqualTypeOf<"refs.finish">();
16
25
  assert.equal(isWorkflowDeclaration(target), true);
17
- assert.equal(isWorkflowDeclaration(() => undefined), false);
26
+ assert.equal(
27
+ isWorkflowDeclaration(() => undefined),
28
+ false,
29
+ );
18
30
  assert.equal(Value.Check(target.args, { report: "done" }), true);
19
- assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id, args: { report: "done" } });
31
+ assert.deepEqual(target({ report: "done" }), {
32
+ type: "next",
33
+ workflowId: target.id,
34
+ args: { report: "done" },
35
+ });
20
36
  });
21
37
 
22
38
  for (const reference of ["refs.finish", target.id]) {
23
39
  test("workflow references decode into contribution-taking transition builders", () => {
24
40
  const contributions = Type.Object({ report: Type.String() });
25
- const options: NornWorkflowRefSchemaOptions<typeof contributions> = { args: contributions };
41
+ const options: NornWorkflowRefSchemaOptions<typeof contributions> = {
42
+ args: contributions,
43
+ };
26
44
  const schema = workflowRefSchema(options);
27
- assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), { type: "next", workflowId: "refs.finish", args: {} });
28
- interface Report { report: string }
45
+ assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), {
46
+ type: "next",
47
+ workflowId: "refs.finish",
48
+ args: {},
49
+ });
50
+ interface Report {
51
+ report: string;
52
+ }
29
53
  const report: Report = { report: "done" };
30
54
  assert.deepEqual(Value.Decode(schema, reference)(report), target(report));
31
- const input = { workflow: reference, forwardArgs: { task: "retain", report: "stale" } };
55
+ const input = {
56
+ workflow: reference,
57
+ forwardArgs: { task: "retain", report: "stale" },
58
+ };
32
59
  const next = Value.Decode(schema, input);
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" });
60
+ assert.deepEqual(next({ report: "done" }), {
61
+ type: "next",
62
+ workflowId: reference,
63
+ args: { task: "retain", report: "done" },
64
+ });
65
+ assert.deepEqual(next({ report: "again" }).args, {
66
+ task: "retain",
67
+ report: "again",
68
+ });
35
69
  assert.deepEqual(input.forwardArgs, { task: "retain", report: "stale" });
36
70
  assert.throws(() => Reflect.apply(next, undefined, [{}]));
37
71
  });
38
72
  }
39
73
 
40
74
  test("nullable reference inputs retain their required property and input union", () => {
41
- const schema = Type.Object({ next: Type.Union([workflowRefSchema(), Type.Null()]) });
75
+ const schema = Type.Object({
76
+ next: Type.Union([workflowRefSchema(), Type.Null()]),
77
+ });
42
78
  const inspected = inspectSchema(schema);
43
79
  assert.deepEqual(inspected.required, ["next"]);
44
80
  assert.equal(Value.Check(schema, {}), false);
45
81
  assert.equal(Value.Check(schema, { next: null }), true);
46
82
  assert.equal(Value.Check(schema, { next: "refs.finish" }), true);
47
- expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.0.type", "string");
48
- expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", ["workflow", "forwardArgs"]);
83
+ expect(inspected).toHaveProperty(
84
+ "properties.next.anyOf.0.anyOf.0.type",
85
+ "string",
86
+ );
87
+ expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", [
88
+ "workflow",
89
+ "forwardArgs",
90
+ ]);
49
91
  });
50
92
 
51
93
  test("optional reference properties remain optional in validation and inspection", () => {
@@ -58,77 +100,198 @@ test("inspection advertises encoded contributions and invocation does not decode
58
100
  let decodes = 0;
59
101
  const contributions = Type.Object({
60
102
  records: Type.String(),
61
- count: Type.Decode(Type.String(), value => { decodes++; return Number(value); }),
103
+ count: Type.Decode(Type.String(), (value) => {
104
+ decodes++;
105
+ return Number(value);
106
+ }),
62
107
  label: Type.Optional(Type.String({ default: "collected" })),
63
108
  });
64
- const reference = Type.With(workflowRefSchema({ args: contributions }), { description: "Continue with the collected records" });
109
+ const reference = Type.With(workflowRefSchema({ args: contributions }), {
110
+ description: "Continue with the collected records",
111
+ });
65
112
  const schema = Type.Object({ next: reference });
66
113
  const inspected = inspectSchema(schema);
67
- expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
68
- expect(inspected).toHaveProperty("properties.next.description", "Continue with the collected records");
69
- expect(inspected).toHaveProperty("properties.next.anyOf", inspectSchema(workflowRefSchema()).anyOf);
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;
114
+ expect(inspected).toHaveProperty(
115
+ "properties.next.x-norn-workflow-ref.contributedArgsSchema",
116
+ inspectSchema(contributions),
117
+ );
118
+ expect(inspected).toHaveProperty(
119
+ "properties.next.description",
120
+ "Continue with the collected records",
121
+ );
122
+ expect(inspected).toHaveProperty(
123
+ "properties.next.anyOf",
124
+ inspectSchema(workflowRefSchema()).anyOf,
125
+ );
126
+ expect(inspected).toHaveProperty(
127
+ "properties.next.anyOf.1.properties.forwardArgs.patternProperties",
128
+ { "^.*$": {} },
129
+ );
130
+ expect(inspected).toHaveProperty(
131
+ "properties.next.x-norn-workflow-ref.contributedArgsSchema.required",
132
+ ["records", "count"],
133
+ );
134
+ expect(inspected).toHaveProperty(
135
+ "properties.next.x-norn-workflow-ref.contributedArgsSchema.properties.count.type",
136
+ "string",
137
+ );
138
+ const forwardArgs = {
139
+ taskId: "task-42",
140
+ nested: { labels: ["one", "two"], enabled: false, absent: null },
141
+ };
142
+ const next = Value.Decode(schema, {
143
+ next: { workflow: "refs.finish", forwardArgs },
144
+ }).next;
75
145
  const contribution = { records: "records.json", count: "2" };
76
- assert.deepEqual(next(contribution).args, { ...forwardArgs, ...contribution });
146
+ assert.deepEqual(next(contribution).args, {
147
+ ...forwardArgs,
148
+ ...contribution,
149
+ });
77
150
  assert.equal(decodes, 0);
78
- assert.throws(() => Reflect.apply(next, undefined, [{ records: "records.json", count: 2 }]));
151
+ assert.throws(() =>
152
+ Reflect.apply(next, undefined, [{ records: "records.json", count: 2 }]),
153
+ );
79
154
  });
80
155
 
81
156
  test("references without contributions advertise an empty object schema", () => {
82
157
  const reference = workflowRefSchema();
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: {} });
158
+ expect(inspectSchema(reference)).toHaveProperty(
159
+ "x-norn-workflow-ref.contributedArgsSchema",
160
+ inspectSchema(Type.Object({})),
161
+ );
162
+ assert.deepEqual(Value.Decode(reference, "refs.finish")({}), {
163
+ type: "next",
164
+ workflowId: "refs.finish",
165
+ args: {},
166
+ });
85
167
  });
86
168
 
87
169
  test("optional, nullable and array references retain annotations and callable codecs", () => {
88
- const reference = workflowRefSchema({ args: Type.Object({ report: Type.String() }) });
89
- const schema = Type.Object({ optional: Type.Optional(reference), nullable: Type.Union([reference, Type.Null()]), many: Type.Array(reference) });
170
+ const reference = workflowRefSchema({
171
+ args: Type.Object({ report: Type.String() }),
172
+ });
173
+ const schema = Type.Object({
174
+ optional: Type.Optional(reference),
175
+ nullable: Type.Union([reference, Type.Null()]),
176
+ many: Type.Array(reference),
177
+ });
90
178
  const inspected = inspectSchema(schema);
91
- for (const path of ["properties.optional", "properties.nullable.anyOf.0", "properties.many.items"]) {
92
- expect(inspected).toHaveProperty(`${path}.x-norn-workflow-ref.contributedArgsSchema`, inspectSchema(Type.Object({ report: Type.String() })));
179
+ for (const path of [
180
+ "properties.optional",
181
+ "properties.nullable.anyOf.0",
182
+ "properties.many.items",
183
+ ]) {
184
+ expect(inspected).toHaveProperty(
185
+ `${path}.x-norn-workflow-ref.contributedArgsSchema`,
186
+ inspectSchema(Type.Object({ report: Type.String() })),
187
+ );
93
188
  }
94
189
  assert.deepEqual(inspected.required, ["nullable", "many"]);
95
- const decoded = Value.Decode(schema, { nullable: null, many: ["refs.finish"], optional: "refs.finish" });
190
+ const decoded = Value.Decode(schema, {
191
+ nullable: null,
192
+ many: ["refs.finish"],
193
+ optional: "refs.finish",
194
+ });
96
195
  assert.equal(decoded.nullable, null);
97
- assert.deepEqual(decoded.many[0]({ report: "array" }), target({ report: "array" }));
98
- assert.deepEqual(decoded.optional!({ report: "optional" }), target({ report: "optional" }));
196
+ assert.deepEqual(
197
+ decoded.many[0]({ report: "array" }),
198
+ target({ report: "array" }),
199
+ );
200
+ assert.deepEqual(
201
+ decoded.optional!({ report: "optional" }),
202
+ target({ report: "optional" }),
203
+ );
99
204
  });
100
205
 
101
206
  test("nested references retain encoded payloads in contributed parameters", () => {
102
207
  const finalContributions = Type.Object({ summary: Type.String() });
103
- const contributions = Type.Object({ records: Type.String(), next: workflowRefSchema({ args: finalContributions }) });
208
+ const contributions = Type.Object({
209
+ records: Type.String(),
210
+ next: workflowRefSchema({ args: finalContributions }),
211
+ });
104
212
  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: "records.json", next: { workflow: "refs.finish", forwardArgs: { batch: 1 } } };
213
+ expect(inspectSchema(reference)).toHaveProperty(
214
+ "x-norn-workflow-ref.contributedArgsSchema.properties.next.x-norn-workflow-ref.contributedArgsSchema",
215
+ inspectSchema(finalContributions),
216
+ );
217
+ const args = {
218
+ records: "records.json",
219
+ next: { workflow: "refs.finish", forwardArgs: { batch: 1 } },
220
+ };
107
221
  const transition = Value.Decode(reference, "intermediate")(args);
108
222
  assert.deepEqual(transition.args, args);
109
- assert.deepEqual(Value.Decode(contributions, transition.args).next({ summary: "done" }).args, { batch: 1, summary: "done" });
223
+ assert.deepEqual(
224
+ Value.Decode(contributions, transition.args).next({ summary: "done" }).args,
225
+ { batch: 1, summary: "done" },
226
+ );
110
227
  });
111
228
 
112
229
  test("recursive contributions use native TypeBox definitions", () => {
113
- const contributions = Type.Cyclic({ Contribution: Type.Object({ label: Type.String(), children: Type.Array(Type.Ref("Contribution")) }) }, "Contribution");
230
+ const contributions = Type.Cyclic(
231
+ {
232
+ Contribution: Type.Object({
233
+ label: Type.String(),
234
+ children: Type.Array(Type.Ref("Contribution")),
235
+ }),
236
+ },
237
+ "Contribution",
238
+ );
114
239
  const reference = workflowRefSchema({ args: contributions });
115
- expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
240
+ expect(inspectSchema(reference)).toHaveProperty(
241
+ "x-norn-workflow-ref.contributedArgsSchema",
242
+ inspectSchema(contributions),
243
+ );
116
244
  const args = { label: "root", children: [{ label: "leaf", children: [] }] };
117
245
  assert.deepEqual(Value.Decode(reference, "refs.finish")(args).args, args);
118
246
  });
119
247
 
120
248
  test("object-valued unions, records, intersections and root codecs can contribute", () => {
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");
130
- for (const value of [null, [], "text", 1]) assert.throws(() => Reflect.apply(unknown, undefined, [value]), /contributions must be objects/);
131
- for (const value of [new Date(), { callback: () => undefined }]) assert.throws(() => Reflect.apply(unknown, undefined, [value]));
249
+ const union = workflowRefSchema({
250
+ args: Type.Union([
251
+ Type.Object({ kind: Type.Literal("ok"), report: Type.String() }),
252
+ Type.Object({ kind: Type.Literal("error"), reason: Type.String() }),
253
+ ]),
254
+ });
255
+ assert.deepEqual(
256
+ Value.Decode(union, "target")({ kind: "error", reason: "failed" }).args,
257
+ { kind: "error", reason: "failed" },
258
+ );
259
+ const record = workflowRefSchema({
260
+ args: Type.Record(Type.String(), Type.Integer()),
261
+ });
262
+ assert.deepEqual(Value.Decode(record, "target")({ first: 1 }).args, {
263
+ first: 1,
264
+ });
265
+ const intersection = workflowRefSchema({
266
+ args: Type.Intersect([
267
+ Type.Object({ left: Type.String() }),
268
+ Type.Object({ right: Type.Integer() }),
269
+ ]),
270
+ });
271
+ assert.deepEqual(
272
+ Value.Decode(intersection, "target")({ left: "x", right: 2 }).args,
273
+ { left: "x", right: 2 },
274
+ );
275
+ const codec = workflowRefSchema({
276
+ args: Type.Decode(
277
+ Type.Object({ text: Type.String() }),
278
+ (value) => value.text,
279
+ ),
280
+ });
281
+ assert.deepEqual(Value.Decode(codec, "target")({ text: "encoded" }).args, {
282
+ text: "encoded",
283
+ });
284
+ const unknown = Value.Decode(
285
+ workflowRefSchema({ args: Type.Unknown() }),
286
+ "target",
287
+ );
288
+ for (const value of [null, [], "text", 1])
289
+ assert.throws(
290
+ () => Reflect.apply(unknown, undefined, [value]),
291
+ /contributions must be objects/,
292
+ );
293
+ for (const value of [new Date(), { callback: () => undefined }])
294
+ assert.throws(() => Reflect.apply(unknown, undefined, [value]));
132
295
  });
133
296
 
134
297
  test("non-JSON contributions fail explicit inspection without preventing construction", () => {
@@ -136,20 +299,42 @@ test("non-JSON contributions fail explicit inspection without preventing constru
136
299
  assert.equal(typeof Value.Decode(reference, "refs.finish"), "function");
137
300
  assert.throws(() => inspectSchema(reference));
138
301
  const nested = workflowRefSchema({ args: Type.Object({ next: reference }) });
139
- assert.throws(() => inspectSchema(Type.Object({ next: Type.Optional(Type.Array(nested)) })));
302
+ assert.throws(() =>
303
+ inspectSchema(Type.Object({ next: Type.Optional(Type.Array(nested)) })),
304
+ );
140
305
  });
141
306
 
142
307
  test("inspection traverses schema positions, not defaults, examples or property-name maps", () => {
143
- const payload = { "x-norn-workflow-ref": { contributedArgsSchema: { type: "bigint" } } };
144
- const schema = Type.Object({ "x-norn-workflow-ref": Type.String() }, { default: payload, examples: [payload] });
308
+ const payload = {
309
+ "x-norn-workflow-ref": { contributedArgsSchema: { type: "bigint" } },
310
+ };
311
+ const schema = Type.Object(
312
+ { "x-norn-workflow-ref": Type.String() },
313
+ { default: payload, examples: [payload] },
314
+ );
145
315
  assert.doesNotThrow(() => inspectSchema(schema));
146
316
  const invalid = workflowRefSchema({ args: Type.BigInt() });
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")]) {
317
+ for (const wrapper of [
318
+ Type.Record(Type.String(), invalid),
319
+ Type.Tuple([invalid]),
320
+ Type.Object({}, { additionalProperties: invalid }),
321
+ Type.Cyclic({ Root: Type.Object({ child: invalid }) }, "Root"),
322
+ ]) {
148
323
  assert.throws(() => inspectSchema(wrapper));
149
324
  }
150
325
  });
151
326
 
152
- for (const input of [undefined, null, "", 3, {}, { workflow: "refs.finish" }, { workflow: "", forwardArgs: {} }, { workflow: "refs.finish", forwardArgs: null }, { workflow: target, forwardArgs: {} }]) {
327
+ for (const input of [
328
+ undefined,
329
+ null,
330
+ "",
331
+ 3,
332
+ {},
333
+ { workflow: "refs.finish" },
334
+ { workflow: "", forwardArgs: {} },
335
+ { workflow: "refs.finish", forwardArgs: null },
336
+ { workflow: target, forwardArgs: {} },
337
+ ]) {
153
338
  test(`reference input schema rejects invalid shape without coercion: ${JSON.stringify(input)}`, () => {
154
339
  assert.equal(Value.Check(workflowRefSchema(), input), false);
155
340
  });
package/bin/norn.mjs CHANGED
@@ -2,9 +2,18 @@
2
2
  import { homedir } from "node:os";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
- process.env.PI_PACKAGE_DIR = fileURLToPath(new URL("..", import.meta.resolve("@earendil-works/pi-coding-agent")));
6
- const { resolveNornAgentDirectory } = await import("../dist/internal/agent-directory.js");
7
- process.env.NORN_AGENT_DIR = resolveNornAgentDirectory({ home: homedir(), environment: process.env });
5
+ process.env.PI_PACKAGE_DIR = fileURLToPath(
6
+ new URL("..", import.meta.resolve("@earendil-works/pi-coding-agent")),
7
+ );
8
+ const { resolveNornAgentDirectory } =
9
+ await import("../dist/internal/agent-directory.js");
10
+ process.env.NORN_AGENT_DIR = resolveNornAgentDirectory({
11
+ home: homedir(),
12
+ environment: process.env,
13
+ });
8
14
  process.env.PI_CODING_AGENT_DIR = process.env.NORN_AGENT_DIR;
9
15
  const cli = await import("../dist/cli.js");
10
- await cli.main(process.argv.slice(2), { kind: "local", root: fileURLToPath(new URL("../assets", import.meta.url)) });
16
+ await cli.main(process.argv.slice(2), {
17
+ kind: "local",
18
+ root: fileURLToPath(new URL("../assets", import.meta.url)),
19
+ });