@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,9 +1,24 @@
1
- import type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
2
- import { Type, type StaticDecode, type StaticEncode, type TCodec, type TSchema } from "typebox";
1
+ import type {
2
+ CreateAgentSessionOptions,
3
+ EventBus,
4
+ PromptOptions,
5
+ ToolDefinition,
6
+ } from "@earendil-works/pi-coding-agent";
7
+ import {
8
+ Type,
9
+ type StaticDecode,
10
+ type StaticEncode,
11
+ type TCodec,
12
+ type TSchema,
13
+ } from "typebox";
3
14
  import type { TLocalizedValidationError } from "typebox/error";
4
15
  import { createWorkflowTransition } from "@vimhead.dev/norn-core/workflow-transition";
5
16
  import { Value } from "typebox/value";
6
- import { assertWorkflowMetadata, isPlainObject, jsonValueSchema } from "./schema.ts";
17
+ import {
18
+ assertWorkflowMetadata,
19
+ isPlainObject,
20
+ jsonValueSchema,
21
+ } from "./schema.ts";
7
22
 
8
23
  const WORKFLOW_DECLARATION_KIND = "norn.workflow";
9
24
 
@@ -27,7 +42,8 @@ export type NornWorkflowDeclaration<
27
42
  Id extends string = string,
28
43
  ArgsSchema extends TSchema = TSchema,
29
44
  ConfigSchema extends TSchema | undefined = TSchema | undefined,
30
- Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined,
45
+ Scope extends NornWorkflowScopeInfo | undefined =
46
+ NornWorkflowScopeInfo | undefined,
31
47
  > = {
32
48
  (args: StaticEncode<ArgsSchema>): NornRunNext;
33
49
  readonly kind: typeof WORKFLOW_DECLARATION_KIND;
@@ -35,43 +51,66 @@ export type NornWorkflowDeclaration<
35
51
  readonly entrypoint: NornWorkflowEntrypoint;
36
52
  readonly args: ArgsSchema;
37
53
  readonly gate?: NornWorkflowGate<ArgsSchema> & {
38
- describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
54
+ describe?(
55
+ context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>,
56
+ ): MaybePromise<string>;
39
57
  };
40
58
  readonly config?: ConfigSchema;
41
59
  readonly scope: Scope;
42
- execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
43
- };
44
-
45
- export type NornAnyWorkflowDeclaration = Pick<NornWorkflowDeclaration, keyof NornWorkflowDeclaration> & ((args: never) => NornRunNext);
46
- export type NornWorkflowRefSchemaOptions<ArgsSchema extends TSchema = TSchema> = {
47
- readonly args?: ArgsSchema;
48
- };
49
- export type NornWorkflowRefInput = StaticEncode<typeof workflowReferenceInputSchema>;
50
- export type NornWorkflowRefOutput<ArgsSchema extends TSchema> = (args: StaticEncode<ArgsSchema> & object) => NornRunNext;
51
-
52
- export type NornWorkflowArgsInput<TWorkflow extends NornAnyWorkflowDeclaration> = StaticEncode<TWorkflow["args"]>;
53
- export type NornWorkflowArgs<TWorkflow extends NornAnyWorkflowDeclaration> = StaticDecode<TWorkflow["args"]>;
54
-
55
- export type NornWorkflowGate<ArgsSchema extends TSchema> = unknown extends StaticEncode<ArgsSchema>
56
- ? NornWorkflowAnyGate
57
- : StaticEncode<ArgsSchema> extends Record<string, unknown>
58
- ? {
59
- readonly enabled: true;
60
- readonly fields?: readonly Extract<keyof StaticEncode<ArgsSchema>, string>[];
61
- }
62
- : {
63
- readonly enabled: true;
64
- readonly fields?: never;
65
- };
60
+ execute(
61
+ context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>,
62
+ ): MaybePromise<WorkflowResult>;
63
+ };
64
+
65
+ export type NornAnyWorkflowDeclaration = Pick<
66
+ NornWorkflowDeclaration,
67
+ keyof NornWorkflowDeclaration
68
+ > &
69
+ ((args: never) => NornRunNext);
70
+ export type NornWorkflowRefSchemaOptions<ArgsSchema extends TSchema = TSchema> =
71
+ {
72
+ readonly args?: ArgsSchema;
73
+ };
74
+ export type NornWorkflowRefInput = StaticEncode<
75
+ typeof workflowReferenceInputSchema
76
+ >;
77
+ export type NornWorkflowRefOutput<ArgsSchema extends TSchema> = (
78
+ args: StaticEncode<ArgsSchema> & object,
79
+ ) => NornRunNext;
80
+
81
+ export type NornWorkflowArgsInput<
82
+ TWorkflow extends NornAnyWorkflowDeclaration,
83
+ > = StaticEncode<TWorkflow["args"]>;
84
+ export type NornWorkflowArgs<TWorkflow extends NornAnyWorkflowDeclaration> =
85
+ StaticDecode<TWorkflow["args"]>;
86
+
87
+ export type NornWorkflowGate<ArgsSchema extends TSchema> =
88
+ unknown extends StaticEncode<ArgsSchema>
89
+ ? NornWorkflowAnyGate
90
+ : StaticEncode<ArgsSchema> extends Record<string, unknown>
91
+ ? {
92
+ readonly enabled: true;
93
+ readonly fields?: readonly Extract<
94
+ keyof StaticEncode<ArgsSchema>,
95
+ string
96
+ >[];
97
+ }
98
+ : {
99
+ readonly enabled: true;
100
+ readonly fields?: never;
101
+ };
66
102
 
67
103
  export type NornWorkflowEntrypoint = false | { readonly instructions: string };
68
104
 
69
- type WorkflowConfig<Schema extends TSchema | undefined> = Schema extends TSchema ? StaticDecode<Schema> : undefined;
105
+ type WorkflowConfig<Schema extends TSchema | undefined> = Schema extends TSchema
106
+ ? StaticDecode<Schema>
107
+ : undefined;
70
108
 
71
109
  export type NornWorkflowContext<
72
110
  ArgsSchema extends TSchema = TSchema,
73
111
  ConfigSchema extends TSchema | undefined = TSchema | undefined,
74
- Scope extends NornWorkflowScopeInfo | undefined = NornWorkflowScopeInfo | undefined,
112
+ Scope extends NornWorkflowScopeInfo | undefined =
113
+ NornWorkflowScopeInfo | undefined,
75
114
  > = {
76
115
  readonly args: StaticDecode<ArgsSchema>;
77
116
  readonly config: WorkflowConfig<ConfigSchema>;
@@ -80,9 +119,14 @@ export type NornWorkflowContext<
80
119
  readonly commands: NornCommands;
81
120
  readonly logs: NornLogs;
82
121
  readonly run: NornRun;
83
- } & (Scope extends NornWorkflowScopeInfo ? {
84
- readonly scope: { readonly id: Scope["id"]; readonly config: WorkflowConfig<Scope["config"]> };
85
- } : {});
122
+ } & (Scope extends NornWorkflowScopeInfo
123
+ ? {
124
+ readonly scope: {
125
+ readonly id: Scope["id"];
126
+ readonly config: WorkflowConfig<Scope["config"]>;
127
+ };
128
+ }
129
+ : {});
86
130
 
87
131
  export type NornWorkflowDefinition<
88
132
  Name extends string = string,
@@ -95,9 +139,13 @@ export type NornWorkflowDefinition<
95
139
  readonly args: ArgsSchema;
96
140
  readonly config?: ConfigSchema;
97
141
  readonly gate?: NornWorkflowGate<ArgsSchema> & {
98
- describe?(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<string>;
142
+ describe?(
143
+ context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>,
144
+ ): MaybePromise<string>;
99
145
  };
100
- execute(context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>): MaybePromise<WorkflowResult>;
146
+ execute(
147
+ context: NornWorkflowContext<ArgsSchema, ConfigSchema, Scope>,
148
+ ): MaybePromise<WorkflowResult>;
101
149
  };
102
150
 
103
151
  export type NornWorkflowScopeInfo = {
@@ -105,48 +153,93 @@ export type NornWorkflowScopeInfo = {
105
153
  readonly config?: TSchema;
106
154
  };
107
155
 
108
- export type NornWorkflowScope<Name extends string, ConfigSchema extends TSchema | undefined> = {
156
+ export type NornWorkflowScope<
157
+ Name extends string,
158
+ ConfigSchema extends TSchema | undefined,
159
+ > = {
109
160
  readonly id: Name;
110
161
  readonly config: ConfigSchema;
111
- workflow<const LocalName extends string, ArgsSchema extends TSchema, LocalConfig extends TSchema | undefined = undefined>(
112
- definition: NornWorkflowDefinition<LocalName, ArgsSchema, LocalConfig, { readonly id: Name; readonly config: ConfigSchema }>,
113
- ): NornWorkflowDeclaration<`${Name}.${LocalName}`, ArgsSchema, LocalConfig, { readonly id: Name; readonly config: ConfigSchema }>;
162
+ workflow<
163
+ const LocalName extends string,
164
+ ArgsSchema extends TSchema,
165
+ LocalConfig extends TSchema | undefined = undefined,
166
+ >(
167
+ definition: NornWorkflowDefinition<
168
+ LocalName,
169
+ ArgsSchema,
170
+ LocalConfig,
171
+ { readonly id: Name; readonly config: ConfigSchema }
172
+ >,
173
+ ): NornWorkflowDeclaration<
174
+ `${Name}.${LocalName}`,
175
+ ArgsSchema,
176
+ LocalConfig,
177
+ { readonly id: Name; readonly config: ConfigSchema }
178
+ >;
114
179
  };
115
180
 
116
181
  export function workflow<
117
182
  const Name extends string,
118
183
  ArgsSchema extends TSchema,
119
184
  ConfigSchema extends TSchema | undefined = undefined,
120
- >(definition: NornWorkflowDefinition<Name, ArgsSchema, ConfigSchema>): NornWorkflowDeclaration<Name, ArgsSchema, ConfigSchema, undefined> {
185
+ >(
186
+ definition: NornWorkflowDefinition<Name, ArgsSchema, ConfigSchema>,
187
+ ): NornWorkflowDeclaration<Name, ArgsSchema, ConfigSchema, undefined> {
121
188
  return createWorkflow({ definition, id: definition.name, scope: undefined });
122
189
  }
123
190
 
124
- function createWorkflow<Id extends string, ArgsSchema extends TSchema, ConfigSchema extends TSchema | undefined, Scope extends NornWorkflowScopeInfo | undefined>(
125
- input: { readonly definition: NornWorkflowDefinition<string, ArgsSchema, ConfigSchema, Scope>; readonly id: Id; readonly scope: Scope },
126
- ): NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, Scope> {
127
- const { definition: { name, ...properties }, id, scope } = input;
191
+ function createWorkflow<
192
+ Id extends string,
193
+ ArgsSchema extends TSchema,
194
+ ConfigSchema extends TSchema | undefined,
195
+ Scope extends NornWorkflowScopeInfo | undefined,
196
+ >(input: {
197
+ readonly definition: NornWorkflowDefinition<
198
+ string,
199
+ ArgsSchema,
200
+ ConfigSchema,
201
+ Scope
202
+ >;
203
+ readonly id: Id;
204
+ readonly scope: Scope;
205
+ }): NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, Scope> {
206
+ const {
207
+ definition: { name, ...properties },
208
+ id,
209
+ scope,
210
+ } = input;
128
211
  assertDeclarationName(name);
129
- const callable = (args: StaticEncode<ArgsSchema>): NornRunNext => createWorkflowTransition({ workflowId: id, args });
212
+ const callable = (args: StaticEncode<ArgsSchema>): NornRunNext =>
213
+ createWorkflowTransition({ workflowId: id, args });
130
214
  const result = Object.assign(callable, properties, {
131
215
  kind: WORKFLOW_DECLARATION_KIND,
132
216
  id,
133
217
  scope,
134
218
  }) as NornWorkflowDeclaration<Id, ArgsSchema, ConfigSchema, Scope>;
135
219
  assertWorkflowMetadata(result);
136
- if (!isWorkflowDeclaration(result)) throw new Error(`Invalid workflow definition: ${id}`);
220
+ if (!isWorkflowDeclaration(result))
221
+ throw new Error(`Invalid workflow definition: ${id}`);
137
222
  return result;
138
223
  }
139
224
 
140
- export function workflowScope<const Name extends string, ConfigSchema extends TSchema | undefined = undefined>(
141
- input: { readonly name: Name; readonly config?: ConfigSchema },
142
- ): NornWorkflowScope<Name, ConfigSchema> {
225
+ export function workflowScope<
226
+ const Name extends string,
227
+ ConfigSchema extends TSchema | undefined = undefined,
228
+ >(input: {
229
+ readonly name: Name;
230
+ readonly config?: ConfigSchema;
231
+ }): NornWorkflowScope<Name, ConfigSchema> {
143
232
  assertDeclarationName(input.name);
144
233
  const scope = { id: input.name, config: input.config as ConfigSchema };
145
234
  return {
146
235
  id: scope.id,
147
236
  config: scope.config,
148
237
  workflow(definition) {
149
- return createWorkflow({ definition, id: `${scope.id}.${definition.name}`, scope });
238
+ return createWorkflow({
239
+ definition,
240
+ id: `${scope.id}.${definition.name}`,
241
+ scope,
242
+ });
150
243
  },
151
244
  };
152
245
  }
@@ -237,9 +330,20 @@ export type NornInterruptedRunResult = {
237
330
  readonly interruption: NornRunInterruption;
238
331
  };
239
332
 
240
- export type NornRunResult = NornStartedRunResult | NornCompletedRunResult | NornFailedRunResult | NornStoppedRunResult | NornInterruptedRunResult;
241
-
242
- export type NornRunStatus = "running" | "interrupted" | "stopped" | "pendingResume" | "completed" | "failed";
333
+ export type NornRunResult =
334
+ | NornStartedRunResult
335
+ | NornCompletedRunResult
336
+ | NornFailedRunResult
337
+ | NornStoppedRunResult
338
+ | NornInterruptedRunResult;
339
+
340
+ export type NornRunStatus =
341
+ | "running"
342
+ | "interrupted"
343
+ | "stopped"
344
+ | "pendingResume"
345
+ | "completed"
346
+ | "failed";
243
347
  export type NornRunHealth = "healthy" | "unhealthy";
244
348
 
245
349
  export type NornRunOutcomeInfo = {
@@ -298,21 +402,40 @@ export type NornCommandRunInput = {
298
402
  const emptyWorkflowRefArgsSchema = Type.Object({});
299
403
  const workflowReferenceInputSchema = Type.Union([
300
404
  Type.String({ minLength: 1 }),
301
- Type.Object({ workflow: Type.String({ minLength: 1 }), forwardArgs: Type.Record(Type.String(), Type.Unknown()) }),
405
+ Type.Object({
406
+ workflow: Type.String({ minLength: 1 }),
407
+ forwardArgs: Type.Record(Type.String(), Type.Unknown()),
408
+ }),
302
409
  ]);
303
410
 
304
- export function workflowRefSchema<ArgsSchema extends TSchema = typeof emptyWorkflowRefArgsSchema>(options?: NornWorkflowRefSchemaOptions<ArgsSchema>): TCodec<typeof workflowReferenceInputSchema, NornWorkflowRefOutput<ArgsSchema>> {
411
+ export function workflowRefSchema<
412
+ ArgsSchema extends TSchema = typeof emptyWorkflowRefArgsSchema,
413
+ >(
414
+ options?: NornWorkflowRefSchemaOptions<ArgsSchema>,
415
+ ): TCodec<
416
+ typeof workflowReferenceInputSchema,
417
+ NornWorkflowRefOutput<ArgsSchema>
418
+ > {
305
419
  const contributionSchema = options?.args ?? emptyWorkflowRefArgsSchema;
306
- return Type.With(Type.Decode(workflowReferenceInputSchema, reference => {
307
- const workflowId = typeof reference === "string" ? reference : reference.workflow;
308
- const forwardArgs = typeof reference === "string" ? {} : reference.forwardArgs;
309
- return (args: StaticEncode<ArgsSchema> & object): NornRunNext => {
310
- Value.Assert(contributionSchema, args);
311
- if (!isPlainObject(args)) throw new Error("Workflow reference contributions must be objects");
312
- Value.Assert(jsonValueSchema, args);
313
- return createWorkflowTransition({ workflowId, args: { ...forwardArgs, ...args } });
314
- };
315
- }), { "x-norn-workflow-ref": { contributedArgsSchema: contributionSchema } });
420
+ return Type.With(
421
+ Type.Decode(workflowReferenceInputSchema, (reference) => {
422
+ const workflowId =
423
+ typeof reference === "string" ? reference : reference.workflow;
424
+ const forwardArgs =
425
+ typeof reference === "string" ? {} : reference.forwardArgs;
426
+ return (args: StaticEncode<ArgsSchema> & object): NornRunNext => {
427
+ Value.Assert(contributionSchema, args);
428
+ if (!isPlainObject(args))
429
+ throw new Error("Workflow reference contributions must be objects");
430
+ Value.Assert(jsonValueSchema, args);
431
+ return createWorkflowTransition({
432
+ workflowId,
433
+ args: { ...forwardArgs, ...args },
434
+ });
435
+ };
436
+ }),
437
+ { "x-norn-workflow-ref": { contributedArgsSchema: contributionSchema } },
438
+ );
316
439
  }
317
440
 
318
441
  export type NornLogRef = {
@@ -340,7 +463,9 @@ export type NornAgentCreateSessionInput = {
340
463
  readonly label: string;
341
464
  readonly cwd: string;
342
465
  readonly tools?: string[];
343
- readonly beforeSessionStart?: (context: NornAgentBeforeSessionStartContext) => MaybePromise<void>;
466
+ readonly beforeSessionStart?: (
467
+ context: NornAgentBeforeSessionStartContext,
468
+ ) => MaybePromise<void>;
344
469
  readonly model?: CreateAgentSessionOptions["model"];
345
470
  readonly thinkingLevel?: CreateAgentSessionOptions["thinkingLevel"];
346
471
  readonly systemPrompt?: string;
@@ -354,7 +479,8 @@ export type NornAgentPromptInput<ResponseSchema extends TSchema> = {
354
479
  readonly options?: PromptOptions;
355
480
  };
356
481
 
357
- export type NornAgentSinglePromptInput<ResponseSchema extends TSchema> = NornAgentCreateSessionInput & NornAgentPromptInput<ResponseSchema>;
482
+ export type NornAgentSinglePromptInput<ResponseSchema extends TSchema> =
483
+ NornAgentCreateSessionInput & NornAgentPromptInput<ResponseSchema>;
358
484
 
359
485
  export type NornAgentSessionEvents = EventBus;
360
486
 
@@ -459,7 +585,9 @@ export type NornAgentSession = {
459
585
  readonly label: string;
460
586
  readonly cwd: string;
461
587
  readonly events: NornAgentSessionEvents;
462
- prompt<ResponseSchema extends TSchema>(input: NornAgentPromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
588
+ prompt<ResponseSchema extends TSchema>(
589
+ input: NornAgentPromptInput<ResponseSchema>,
590
+ ): Promise<StaticDecode<ResponseSchema>>;
463
591
  dispose(): Promise<void>;
464
592
  };
465
593
 
@@ -467,7 +595,9 @@ export type NornRun = {
467
595
  id: string;
468
596
  next(workflowId: string, args: unknown): NornRunNext;
469
597
  complete(metadata?: NornRunOutcomeMetadata): NornRunComplete;
470
- fail(metadata: NornRunOutcomeMetadata & { readonly summary: string }): NornRunFail;
598
+ fail(
599
+ metadata: NornRunOutcomeMetadata & { readonly summary: string },
600
+ ): NornRunFail;
471
601
  };
472
602
 
473
603
  export type NornLogs = {
@@ -480,7 +610,9 @@ export type NornCommands = {
480
610
 
481
611
  export type NornAgents = {
482
612
  createSession(input: NornAgentCreateSessionInput): Promise<NornAgentSession>;
483
- prompt<ResponseSchema extends TSchema>(input: NornAgentSinglePromptInput<ResponseSchema>): Promise<StaticDecode<ResponseSchema>>;
613
+ prompt<ResponseSchema extends TSchema>(
614
+ input: NornAgentSinglePromptInput<ResponseSchema>,
615
+ ): Promise<StaticDecode<ResponseSchema>>;
484
616
  };
485
617
 
486
618
  export type NornWorkflowSource = {
@@ -505,7 +637,11 @@ export type NornRegisteredWorkflowInfo = {
505
637
  };
506
638
 
507
639
  export type NornInspectedWorkflowInfo = NornRegisteredWorkflowInfo & {
508
- readonly scope?: { readonly id: string; readonly configKey: string; readonly configSchema: NornJsonSchema | null };
640
+ readonly scope?: {
641
+ readonly id: string;
642
+ readonly configKey: string;
643
+ readonly configSchema: NornJsonSchema | null;
644
+ };
509
645
  readonly argsSchema: NornJsonSchema;
510
646
  readonly configSchema: NornJsonSchema | null;
511
647
  readonly gate: NornWorkflowGateInfo | null;
@@ -557,17 +693,34 @@ export type NornProjectInfo = {
557
693
  };
558
694
 
559
695
  function assertDeclarationName(name: string): void {
560
- if (typeof name !== "string" || name.trim().length === 0 || name.includes(".")) throw new Error(`Invalid workflow or scope name: ${name}; expected a nonempty name without dots`);
696
+ if (
697
+ typeof name !== "string" ||
698
+ name.trim().length === 0 ||
699
+ name.includes(".")
700
+ )
701
+ throw new Error(
702
+ `Invalid workflow or scope name: ${name}; expected a nonempty name without dots`,
703
+ );
561
704
  }
562
705
 
563
- export function isWorkflowDeclaration(value: unknown): value is NornAnyWorkflowDeclaration {
706
+ export function isWorkflowDeclaration(
707
+ value: unknown,
708
+ ): value is NornAnyWorkflowDeclaration {
564
709
  if (typeof value !== "function") return false;
565
- const candidate = value as { kind?: unknown; id?: unknown; entrypoint?: unknown; args?: unknown; execute?: unknown };
710
+ const candidate = value as {
711
+ kind?: unknown;
712
+ id?: unknown;
713
+ entrypoint?: unknown;
714
+ args?: unknown;
715
+ execute?: unknown;
716
+ };
566
717
  return (
567
718
  candidate.kind === WORKFLOW_DECLARATION_KIND &&
568
719
  typeof candidate.id === "string" &&
569
720
  candidate.id.length > 0 &&
570
- (candidate.entrypoint === false || (isPlainObject(candidate.entrypoint) && typeof candidate.entrypoint.instructions === "string")) &&
721
+ (candidate.entrypoint === false ||
722
+ (isPlainObject(candidate.entrypoint) &&
723
+ typeof candidate.entrypoint.instructions === "string")) &&
571
724
  Boolean(candidate.args) &&
572
725
  typeof candidate.execute === "function"
573
726
  );
@@ -575,8 +728,16 @@ export function isWorkflowDeclaration(value: unknown): value is NornAnyWorkflowD
575
728
 
576
729
  export function isWorkflowNext(value: unknown): value is NornRunNext {
577
730
  if (!value || typeof value !== "object") return false;
578
- const candidate = value as { type?: unknown; workflowId?: unknown; args?: unknown };
579
- return candidate.type === "next" && typeof candidate.workflowId === "string" && candidate.workflowId.length > 0;
731
+ const candidate = value as {
732
+ type?: unknown;
733
+ workflowId?: unknown;
734
+ args?: unknown;
735
+ };
736
+ return (
737
+ candidate.type === "next" &&
738
+ typeof candidate.workflowId === "string" &&
739
+ candidate.workflowId.length > 0
740
+ );
580
741
  }
581
742
 
582
743
  export function isWorkflowComplete(value: unknown): value is NornRunComplete {
@@ -587,6 +748,13 @@ export function isWorkflowComplete(value: unknown): value is NornRunComplete {
587
748
 
588
749
  export function isWorkflowFail(value: unknown): value is NornRunFail {
589
750
  if (!value || typeof value !== "object") return false;
590
- const candidate = value as { type?: unknown; metadata?: { summary?: unknown } };
591
- return candidate.type === "fail" && typeof candidate.metadata?.summary === "string" && candidate.metadata.summary.length > 0;
751
+ const candidate = value as {
752
+ type?: unknown;
753
+ metadata?: { summary?: unknown };
754
+ };
755
+ return (
756
+ candidate.type === "fail" &&
757
+ typeof candidate.metadata?.summary === "string" &&
758
+ candidate.metadata.summary.length > 0
759
+ );
592
760
  }
@@ -1,2 +1,7 @@
1
- export type { CreateAgentSessionOptions, EventBus, PromptOptions, ToolDefinition } from "@earendil-works/pi-coding-agent";
1
+ export type {
2
+ CreateAgentSessionOptions,
3
+ EventBus,
4
+ PromptOptions,
5
+ ToolDefinition,
6
+ } from "@earendil-works/pi-coding-agent";
2
7
  export * from "./api.ts";
@@ -3,20 +3,32 @@ import { Meta } from "typebox/schema";
3
3
  import { Value } from "typebox/value";
4
4
  import type { NornAnyWorkflowDeclaration, NornJsonSchema } from "./api.ts";
5
5
 
6
- export const jsonValueSchema = Type.Cyclic({
7
- Json: Type.Union([
8
- Type.Null(), Type.Boolean(), Type.Number(), Type.String(),
9
- Type.Array(Type.Ref("Json")),
10
- Type.Refine(Type.Record(Type.String(), Type.Ref("Json")), value => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null),
11
- ]),
12
- }, "Json");
6
+ export const jsonValueSchema = Type.Cyclic(
7
+ {
8
+ Json: Type.Union([
9
+ Type.Null(),
10
+ Type.Boolean(),
11
+ Type.Number(),
12
+ Type.String(),
13
+ Type.Array(Type.Ref("Json")),
14
+ Type.Refine(
15
+ Type.Record(Type.String(), Type.Ref("Json")),
16
+ (value) =>
17
+ Object.getPrototypeOf(value) === Object.prototype ||
18
+ Object.getPrototypeOf(value) === null,
19
+ ),
20
+ ]),
21
+ },
22
+ "Json",
23
+ );
13
24
 
14
25
  export type NornJsonValue = Static<typeof jsonValueSchema>;
15
26
 
16
27
  export function inspectSchema(schema: TSchema): NornJsonSchema {
17
28
  const inspected: unknown = JSON.parse(JSON.stringify(schema));
18
29
  Value.Assert(Meta["https://json-schema.org/draft/2020-12/schema"], inspected);
19
- if (!isPlainObject(inspected)) throw new Error("Norn inspection requires an object schema");
30
+ if (!isPlainObject(inspected))
31
+ throw new Error("Norn inspection requires an object schema");
20
32
  assertWorkflowReferenceAnnotations(inspected);
21
33
  return inspected;
22
34
  }
@@ -25,35 +37,73 @@ function assertWorkflowReferenceAnnotations(schema: unknown): void {
25
37
  if (!isPlainObject(schema)) return;
26
38
  if (Object.hasOwn(schema, "x-norn-workflow-ref")) {
27
39
  const annotation = schema["x-norn-workflow-ref"];
28
- if (!isPlainObject(annotation) || !Object.hasOwn(annotation, "contributedArgsSchema")) throw new Error("Invalid workflow reference annotation");
29
- Value.Assert(Meta["https://json-schema.org/draft/2020-12/schema"], annotation.contributedArgsSchema);
40
+ if (
41
+ !isPlainObject(annotation) ||
42
+ !Object.hasOwn(annotation, "contributedArgsSchema")
43
+ )
44
+ throw new Error("Invalid workflow reference annotation");
45
+ Value.Assert(
46
+ Meta["https://json-schema.org/draft/2020-12/schema"],
47
+ annotation.contributedArgsSchema,
48
+ );
30
49
  assertWorkflowReferenceAnnotations(annotation.contributedArgsSchema);
31
50
  }
32
- for (const keyword of ["properties", "patternProperties", "$defs", "definitions", "dependentSchemas"]) {
51
+ for (const keyword of [
52
+ "properties",
53
+ "patternProperties",
54
+ "$defs",
55
+ "definitions",
56
+ "dependentSchemas",
57
+ ]) {
33
58
  const schemas = schema[keyword];
34
- if (isPlainObject(schemas)) for (const child of Object.values(schemas)) assertWorkflowReferenceAnnotations(child);
59
+ if (isPlainObject(schemas))
60
+ for (const child of Object.values(schemas))
61
+ assertWorkflowReferenceAnnotations(child);
35
62
  }
36
63
  for (const keyword of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
37
64
  const schemas = schema[keyword];
38
- if (Array.isArray(schemas)) for (const child of schemas) assertWorkflowReferenceAnnotations(child);
65
+ if (Array.isArray(schemas))
66
+ for (const child of schemas) assertWorkflowReferenceAnnotations(child);
39
67
  }
40
- for (const keyword of ["additionalProperties", "unevaluatedProperties", "propertyNames", "items", "contains", "not", "if", "then", "else", "unevaluatedItems", "contentSchema"]) {
68
+ for (const keyword of [
69
+ "additionalProperties",
70
+ "unevaluatedProperties",
71
+ "propertyNames",
72
+ "items",
73
+ "contains",
74
+ "not",
75
+ "if",
76
+ "then",
77
+ "else",
78
+ "unevaluatedItems",
79
+ "contentSchema",
80
+ ]) {
41
81
  assertWorkflowReferenceAnnotations(schema[keyword]);
42
82
  }
43
83
  }
44
84
 
45
- export function assertWorkflowMetadata(workflow: NornAnyWorkflowDeclaration): void {
85
+ export function assertWorkflowMetadata(
86
+ workflow: NornAnyWorkflowDeclaration,
87
+ ): void {
46
88
  const { entrypoint } = workflow;
47
89
  if (entrypoint === false) return;
48
- if (!isPlainObject(entrypoint)) throw new Error(`Workflow entrypoint must be false or an object with instructions: ${workflow.id}`);
49
- if (typeof entrypoint.instructions !== "string" || entrypoint.instructions.trim().length === 0) {
50
- throw new Error(`Workflow entrypoint.instructions must be a nonempty string: ${workflow.id}`);
90
+ if (!isPlainObject(entrypoint))
91
+ throw new Error(
92
+ `Workflow entrypoint must be false or an object with instructions: ${workflow.id}`,
93
+ );
94
+ if (
95
+ typeof entrypoint.instructions !== "string" ||
96
+ entrypoint.instructions.trim().length === 0
97
+ ) {
98
+ throw new Error(
99
+ `Workflow entrypoint.instructions must be a nonempty string: ${workflow.id}`,
100
+ );
51
101
  }
52
102
  }
53
103
 
54
104
  export function unwrapSchema(schema: TSchema): TSchema {
55
105
  if (!Type.IsUnion(schema)) return schema;
56
- const members = schema.anyOf.filter(member => !Type.IsNull(member));
106
+ const members = schema.anyOf.filter((member) => !Type.IsNull(member));
57
107
  return members.length === 1 ? unwrapSchema(members[0]) : schema;
58
108
  }
59
109
 
@@ -64,9 +114,13 @@ export function schemaShape(schema: TSchema): Record<string, TSchema> {
64
114
 
65
115
  export function schemaType(schema: TSchema): string | undefined {
66
116
  const unwrapped = unwrapSchema(schema);
67
- return "type" in unwrapped && typeof unwrapped.type === "string" ? unwrapped.type : undefined;
117
+ return "type" in unwrapped && typeof unwrapped.type === "string"
118
+ ? unwrapped.type
119
+ : undefined;
68
120
  }
69
121
 
70
- export function isPlainObject(value: unknown): value is Record<string, unknown> {
122
+ export function isPlainObject(
123
+ value: unknown,
124
+ ): value is Record<string, unknown> {
71
125
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
72
126
  }