@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.
- package/assets/README.md +41 -13
- package/assets/docs/README.md +16 -16
- package/assets/docs/agents.md +23 -15
- package/assets/docs/cli.md +10 -8
- package/assets/docs/composition.md +7 -7
- package/assets/docs/persistence.md +22 -22
- package/assets/docs/projects.md +6 -6
- package/assets/docs/recovery.md +10 -10
- package/assets/docs/schemas.md +42 -36
- package/assets/docs/workflows.md +39 -32
- package/assets/examples/agent-then-analysis/input.json +3 -3
- package/assets/examples/agent-then-analysis/norn.project.json +2 -2
- package/assets/examples/agent-then-analysis/plugin.ts +37 -9
- package/assets/examples/caller-owned-routing/README.md +6 -6
- package/assets/examples/caller-owned-routing/assessment.ts +18 -4
- package/assets/examples/caller-owned-routing/contracts.ts +7 -2
- package/assets/examples/caller-owned-routing/input.json +12 -12
- package/assets/examples/caller-owned-routing/norn.project.json +2 -2
- package/assets/examples/caller-owned-routing/revision.ts +7 -2
- package/assets/examples/caller-owned-routing/router.ts +14 -3
- package/assets/examples/caller-selected-continuation/caller.ts +38 -12
- package/assets/examples/caller-selected-continuation/input.json +7 -7
- package/assets/examples/caller-selected-continuation/norn.project.json +2 -2
- package/assets/examples/caller-selected-continuation/producer.ts +10 -3
- package/assets/examples/coordinating-multiple-agents/README.md +6 -6
- package/assets/examples/coordinating-multiple-agents/input.json +20 -8
- package/assets/examples/coordinating-multiple-agents/norn.project.json +2 -2
- package/assets/examples/coordinating-multiple-agents/plugin.ts +165 -46
- package/assets/examples/coordinating-multiple-agents/queue-tools.ts +30 -7
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +211 -63
- package/assets/examples/getting-started/norn.project.json +2 -2
- package/assets/examples/getting-started/plugin.ts +33 -13
- package/assets/examples/minimal-workflow/norn.project.json +2 -2
- package/assets/examples/minimal-workflow/plugin.ts +12 -3
- package/assets/examples/shared-state/README.md +6 -6
- package/assets/examples/shared-state/input.json +1 -1
- package/assets/examples/shared-state/norn.project.json +2 -2
- package/assets/examples/shared-state/plugin.ts +44 -15
- package/assets/examples/shared-state/shared-state.ts +46 -13
- package/assets/examples/shared-state/state-tools.ts +68 -16
- package/assets/examples/worktree-development-loop/README.md +12 -12
- package/assets/examples/worktree-development-loop/norn.project.json +7 -7
- package/assets/examples/worktree-development-loop/plugin.ts +7 -1
- package/assets/examples/worktree-development-loop/scope.ts +4 -1
- package/assets/examples/worktree-development-loop/shared/commands.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -4
- package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +11 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +40 -10
- package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/planning/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +33 -9
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -8
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/build-info.ts +4 -1
- package/assets/packages/cli/src/bun/cli.ts +21 -5
- package/assets/packages/cli/src/cli.ts +585 -170
- package/assets/packages/cli/src/client.ts +194 -36
- package/assets/packages/cli/src/documentation.ts +135 -30
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agent-directory.ts +7 -2
- package/assets/packages/cli/src/internal/agent-response-tool.ts +57 -15
- package/assets/packages/cli/src/internal/agents.ts +248 -60
- package/assets/packages/cli/src/internal/commands.ts +82 -20
- package/assets/packages/cli/src/internal/documentation-bundle.ts +44 -13
- package/assets/packages/cli/src/internal/engine.ts +396 -87
- package/assets/packages/cli/src/internal/errors.ts +13 -5
- package/assets/packages/cli/src/internal/execution-context.ts +20 -12
- package/assets/packages/cli/src/internal/file-coordinator.ts +95 -26
- package/assets/packages/cli/src/internal/file-names.ts +4 -1
- package/assets/packages/cli/src/internal/launch-request.ts +74 -22
- package/assets/packages/cli/src/internal/logs.ts +10 -2
- package/assets/packages/cli/src/internal/metrics.ts +247 -61
- package/assets/packages/cli/src/internal/pi-assets.ts +105 -23
- package/assets/packages/cli/src/internal/run-lease.ts +72 -22
- package/assets/packages/cli/src/internal/run-log.ts +49 -20
- package/assets/packages/cli/src/internal/run-names.ts +120 -12
- package/assets/packages/cli/src/internal/run-state.ts +306 -71
- package/assets/packages/cli/src/internal/run-store.ts +216 -53
- package/assets/packages/cli/src/internal/usage.ts +23 -7
- package/assets/packages/cli/src/internal/worker-directory.ts +9 -3
- package/assets/packages/cli/src/internal/workflow-registry.ts +281 -70
- package/assets/packages/cli/src/internal/working-directory.ts +2 -1
- package/assets/packages/cli/src/workflow-loader.ts +341 -74
- package/assets/packages/core/src/atomic-files.ts +13 -4
- package/assets/packages/core/src/workflow-transition.ts +13 -2
- package/assets/packages/sdk/src/api.ts +248 -80
- package/assets/packages/sdk/src/index.ts +6 -1
- package/assets/packages/sdk/src/schema.ts +75 -21
- package/assets/tests/workflow-ref.test.ts +242 -57
- package/bin/norn.mjs +13 -4
- package/dist/cli.js +344 -90
- package/dist/client.js +100 -22
- package/dist/documentation.js +102 -25
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agent-directory.js +3 -1
- package/dist/internal/agent-response-tool.js +42 -11
- package/dist/internal/agents.js +149 -37
- package/dist/internal/commands.js +65 -16
- package/dist/internal/documentation-bundle.js +23 -11
- package/dist/internal/engine.js +304 -66
- package/dist/internal/errors.js +4 -2
- package/dist/internal/execution-context.js +2 -1
- package/dist/internal/file-coordinator.js +59 -19
- package/dist/internal/launch-request.js +32 -14
- package/dist/internal/metrics.js +138 -38
- package/dist/internal/pi-assets.js +70 -19
- package/dist/internal/run-lease.js +29 -9
- package/dist/internal/run-log.js +24 -12
- package/dist/internal/run-state.js +172 -48
- package/dist/internal/run-store.js +157 -45
- package/dist/internal/worker-directory.js +6 -2
- package/dist/internal/workflow-registry.js +123 -32
- package/dist/internal/working-directory.js +2 -1
- package/dist/workflow-loader.js +198 -41
- package/package.json +2 -2
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
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",
|
|
11
|
-
|
|
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(
|
|
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" }), {
|
|
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> = {
|
|
41
|
+
const options: NornWorkflowRefSchemaOptions<typeof contributions> = {
|
|
42
|
+
args: contributions,
|
|
43
|
+
};
|
|
26
44
|
const schema = workflowRefSchema(options);
|
|
27
|
-
assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), {
|
|
28
|
-
|
|
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 = {
|
|
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" }), {
|
|
34
|
-
|
|
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({
|
|
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(
|
|
48
|
-
|
|
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 => {
|
|
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 }), {
|
|
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(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
expect(inspected).toHaveProperty(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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, {
|
|
146
|
+
assert.deepEqual(next(contribution).args, {
|
|
147
|
+
...forwardArgs,
|
|
148
|
+
...contribution,
|
|
149
|
+
});
|
|
77
150
|
assert.equal(decodes, 0);
|
|
78
|
-
assert.throws(() =>
|
|
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(
|
|
84
|
-
|
|
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({
|
|
89
|
-
|
|
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 [
|
|
92
|
-
|
|
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, {
|
|
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(
|
|
98
|
-
|
|
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({
|
|
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(
|
|
106
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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(() =>
|
|
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 = {
|
|
144
|
-
|
|
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 [
|
|
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 [
|
|
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(
|
|
6
|
-
|
|
7
|
-
|
|
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), {
|
|
16
|
+
await cli.main(process.argv.slice(2), {
|
|
17
|
+
kind: "local",
|
|
18
|
+
root: fileURLToPath(new URL("../assets", import.meta.url)),
|
|
19
|
+
});
|