@nifrajs/coding-agent 3.2.0

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 (211) hide show
  1. package/EXTENSIONS.md +25 -0
  2. package/PERFORMANCE.md +19 -0
  3. package/README.md +69 -0
  4. package/REGISTRY.md +15 -0
  5. package/SECURITY.md +32 -0
  6. package/dist/approvals.d.ts +70 -0
  7. package/dist/approvals.d.ts.map +1 -0
  8. package/dist/approvals.js +174 -0
  9. package/dist/approvals.js.map +1 -0
  10. package/dist/capabilities.d.ts +20 -0
  11. package/dist/capabilities.d.ts.map +1 -0
  12. package/dist/capabilities.js +49 -0
  13. package/dist/capabilities.js.map +1 -0
  14. package/dist/cli.d.ts +24 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +380 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/context.d.ts +17 -0
  19. package/dist/context.d.ts.map +1 -0
  20. package/dist/context.js +66 -0
  21. package/dist/context.js.map +1 -0
  22. package/dist/deployment-adapters.d.ts +40 -0
  23. package/dist/deployment-adapters.d.ts.map +1 -0
  24. package/dist/deployment-adapters.js +147 -0
  25. package/dist/deployment-adapters.js.map +1 -0
  26. package/dist/diff.d.ts +17 -0
  27. package/dist/diff.d.ts.map +1 -0
  28. package/dist/diff.js +61 -0
  29. package/dist/diff.js.map +1 -0
  30. package/dist/errors.d.ts +9 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +35 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/extensions.d.ts +104 -0
  35. package/dist/extensions.d.ts.map +1 -0
  36. package/dist/extensions.js +284 -0
  37. package/dist/extensions.js.map +1 -0
  38. package/dist/handoffs.d.ts +97 -0
  39. package/dist/handoffs.d.ts.map +1 -0
  40. package/dist/handoffs.js +199 -0
  41. package/dist/handoffs.js.map +1 -0
  42. package/dist/healing.d.ts +56 -0
  43. package/dist/healing.d.ts.map +1 -0
  44. package/dist/healing.js +54 -0
  45. package/dist/healing.js.map +1 -0
  46. package/dist/host.d.ts +61 -0
  47. package/dist/host.d.ts.map +1 -0
  48. package/dist/host.js +369 -0
  49. package/dist/host.js.map +1 -0
  50. package/dist/index.d.ts +26 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +26 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/isolated-worker.d.ts +2 -0
  55. package/dist/isolated-worker.d.ts.map +1 -0
  56. package/dist/isolated-worker.js +155 -0
  57. package/dist/isolated-worker.js.map +1 -0
  58. package/dist/isolated.d.ts +52 -0
  59. package/dist/isolated.d.ts.map +1 -0
  60. package/dist/isolated.js +293 -0
  61. package/dist/isolated.js.map +1 -0
  62. package/dist/native.d.ts +92 -0
  63. package/dist/native.d.ts.map +1 -0
  64. package/dist/native.js +339 -0
  65. package/dist/native.js.map +1 -0
  66. package/dist/orchestration/artifact-port.d.ts +30 -0
  67. package/dist/orchestration/artifact-port.d.ts.map +1 -0
  68. package/dist/orchestration/artifact-port.js +59 -0
  69. package/dist/orchestration/artifact-port.js.map +1 -0
  70. package/dist/orchestration/catalog.d.ts +71 -0
  71. package/dist/orchestration/catalog.d.ts.map +1 -0
  72. package/dist/orchestration/catalog.js +46 -0
  73. package/dist/orchestration/catalog.js.map +1 -0
  74. package/dist/orchestration/compile.d.ts +59 -0
  75. package/dist/orchestration/compile.d.ts.map +1 -0
  76. package/dist/orchestration/compile.js +268 -0
  77. package/dist/orchestration/compile.js.map +1 -0
  78. package/dist/orchestration/dispatch.d.ts +115 -0
  79. package/dist/orchestration/dispatch.d.ts.map +1 -0
  80. package/dist/orchestration/dispatch.js +220 -0
  81. package/dist/orchestration/dispatch.js.map +1 -0
  82. package/dist/orchestration/durable-jobs.d.ts +35 -0
  83. package/dist/orchestration/durable-jobs.d.ts.map +1 -0
  84. package/dist/orchestration/durable-jobs.js +273 -0
  85. package/dist/orchestration/durable-jobs.js.map +1 -0
  86. package/dist/orchestration/effect-key.d.ts +23 -0
  87. package/dist/orchestration/effect-key.d.ts.map +1 -0
  88. package/dist/orchestration/effect-key.js +24 -0
  89. package/dist/orchestration/effect-key.js.map +1 -0
  90. package/dist/orchestration/evidence-store.d.ts +75 -0
  91. package/dist/orchestration/evidence-store.d.ts.map +1 -0
  92. package/dist/orchestration/evidence-store.js +183 -0
  93. package/dist/orchestration/evidence-store.js.map +1 -0
  94. package/dist/orchestration/hash.d.ts +12 -0
  95. package/dist/orchestration/hash.d.ts.map +1 -0
  96. package/dist/orchestration/hash.js +43 -0
  97. package/dist/orchestration/hash.js.map +1 -0
  98. package/dist/orchestration/host.d.ts +121 -0
  99. package/dist/orchestration/host.d.ts.map +1 -0
  100. package/dist/orchestration/host.js +334 -0
  101. package/dist/orchestration/host.js.map +1 -0
  102. package/dist/orchestration/index.d.ts +20 -0
  103. package/dist/orchestration/index.d.ts.map +1 -0
  104. package/dist/orchestration/index.js +19 -0
  105. package/dist/orchestration/index.js.map +1 -0
  106. package/dist/orchestration/policy.d.ts +63 -0
  107. package/dist/orchestration/policy.d.ts.map +1 -0
  108. package/dist/orchestration/policy.js +80 -0
  109. package/dist/orchestration/policy.js.map +1 -0
  110. package/dist/orchestration/recovery.d.ts +87 -0
  111. package/dist/orchestration/recovery.d.ts.map +1 -0
  112. package/dist/orchestration/recovery.js +197 -0
  113. package/dist/orchestration/recovery.js.map +1 -0
  114. package/dist/orchestration/tracer.d.ts +31 -0
  115. package/dist/orchestration/tracer.d.ts.map +1 -0
  116. package/dist/orchestration/tracer.js +141 -0
  117. package/dist/orchestration/tracer.js.map +1 -0
  118. package/dist/plans.d.ts +35 -0
  119. package/dist/plans.d.ts.map +1 -0
  120. package/dist/plans.js +63 -0
  121. package/dist/plans.js.map +1 -0
  122. package/dist/presets.d.ts +10 -0
  123. package/dist/presets.d.ts.map +1 -0
  124. package/dist/presets.js +70 -0
  125. package/dist/presets.js.map +1 -0
  126. package/dist/process.d.ts +6 -0
  127. package/dist/process.d.ts.map +1 -0
  128. package/dist/process.js +42 -0
  129. package/dist/process.js.map +1 -0
  130. package/dist/registry.d.ts +37 -0
  131. package/dist/registry.d.ts.map +1 -0
  132. package/dist/registry.js +51 -0
  133. package/dist/registry.js.map +1 -0
  134. package/dist/replay.d.ts +24 -0
  135. package/dist/replay.d.ts.map +1 -0
  136. package/dist/replay.js +154 -0
  137. package/dist/replay.js.map +1 -0
  138. package/dist/rpc.d.ts +51 -0
  139. package/dist/rpc.d.ts.map +1 -0
  140. package/dist/rpc.js +412 -0
  141. package/dist/rpc.js.map +1 -0
  142. package/dist/session-migration.d.ts +59 -0
  143. package/dist/session-migration.d.ts.map +1 -0
  144. package/dist/session-migration.js +315 -0
  145. package/dist/session-migration.js.map +1 -0
  146. package/dist/sessions.d.ts +74 -0
  147. package/dist/sessions.d.ts.map +1 -0
  148. package/dist/sessions.js +222 -0
  149. package/dist/sessions.js.map +1 -0
  150. package/dist/subagents.d.ts +51 -0
  151. package/dist/subagents.d.ts.map +1 -0
  152. package/dist/subagents.js +141 -0
  153. package/dist/subagents.js.map +1 -0
  154. package/dist/tools.d.ts +13 -0
  155. package/dist/tools.d.ts.map +1 -0
  156. package/dist/tools.js +37 -0
  157. package/dist/tools.js.map +1 -0
  158. package/dist/ui.d.ts +42 -0
  159. package/dist/ui.d.ts.map +1 -0
  160. package/dist/ui.js +93 -0
  161. package/dist/ui.js.map +1 -0
  162. package/dist/verification.d.ts +34 -0
  163. package/dist/verification.d.ts.map +1 -0
  164. package/dist/verification.js +91 -0
  165. package/dist/verification.js.map +1 -0
  166. package/dist/workflows.d.ts +98 -0
  167. package/dist/workflows.d.ts.map +1 -0
  168. package/dist/workflows.js +174 -0
  169. package/dist/workflows.js.map +1 -0
  170. package/package.json +84 -0
  171. package/src/approvals.ts +249 -0
  172. package/src/capabilities.ts +74 -0
  173. package/src/cli.ts +405 -0
  174. package/src/context.ts +81 -0
  175. package/src/deployment-adapters.ts +181 -0
  176. package/src/diff.ts +80 -0
  177. package/src/errors.ts +46 -0
  178. package/src/extensions.ts +402 -0
  179. package/src/handoffs.ts +299 -0
  180. package/src/healing.ts +93 -0
  181. package/src/host.ts +465 -0
  182. package/src/index.ts +165 -0
  183. package/src/isolated-worker.ts +177 -0
  184. package/src/isolated.ts +344 -0
  185. package/src/native.ts +478 -0
  186. package/src/orchestration/artifact-port.ts +75 -0
  187. package/src/orchestration/catalog.ts +102 -0
  188. package/src/orchestration/compile.ts +373 -0
  189. package/src/orchestration/dispatch.ts +383 -0
  190. package/src/orchestration/durable-jobs.ts +374 -0
  191. package/src/orchestration/effect-key.ts +35 -0
  192. package/src/orchestration/evidence-store.ts +233 -0
  193. package/src/orchestration/hash.ts +43 -0
  194. package/src/orchestration/host.ts +445 -0
  195. package/src/orchestration/index.ts +131 -0
  196. package/src/orchestration/policy.ts +116 -0
  197. package/src/orchestration/recovery.ts +271 -0
  198. package/src/orchestration/tracer.ts +177 -0
  199. package/src/plans.ts +105 -0
  200. package/src/presets.ts +90 -0
  201. package/src/process.ts +47 -0
  202. package/src/registry.ts +80 -0
  203. package/src/replay.ts +190 -0
  204. package/src/rpc.ts +598 -0
  205. package/src/session-migration.ts +401 -0
  206. package/src/sessions.ts +303 -0
  207. package/src/subagents.ts +210 -0
  208. package/src/tools.ts +52 -0
  209. package/src/ui.ts +137 -0
  210. package/src/verification.ts +133 -0
  211. package/src/workflows.ts +279 -0
@@ -0,0 +1,279 @@
1
+ import { publicErrorDetails } from "./errors.ts"
2
+
3
+ export interface WorkflowContext {
4
+ readonly signal: AbortSignal
5
+ readonly values: ReadonlyMap<string, unknown>
6
+ set(name: string, value: unknown): void
7
+ run(step: WorkflowStep): Promise<unknown>
8
+ }
9
+
10
+ export type WorkflowStep =
11
+ | {
12
+ readonly type: "task"
13
+ readonly id: string
14
+ readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>
15
+ }
16
+ | { readonly type: "sequence"; readonly steps: readonly WorkflowStep[] }
17
+ | {
18
+ readonly type: "parallel"
19
+ readonly steps: readonly WorkflowStep[]
20
+ readonly maxConcurrency?: number
21
+ }
22
+ | {
23
+ readonly type: "verify"
24
+ readonly id: string
25
+ readonly run: (
26
+ context: WorkflowContext,
27
+ ) => unknown | PromiseLike<boolean | { readonly ok: boolean; readonly report?: unknown }>
28
+ }
29
+ | {
30
+ readonly type: "approve"
31
+ readonly id: string
32
+ readonly reason: string
33
+ readonly run: (context: WorkflowContext) => unknown | PromiseLike<boolean>
34
+ }
35
+ | {
36
+ readonly type: "retry"
37
+ readonly step: WorkflowStep
38
+ readonly attempts?: number
39
+ readonly backoffMs?: number
40
+ }
41
+ | {
42
+ readonly type: "branch"
43
+ readonly id: string
44
+ readonly when: (context: WorkflowContext) => boolean | PromiseLike<boolean>
45
+ readonly then: WorkflowStep
46
+ readonly otherwise?: WorkflowStep
47
+ }
48
+ | {
49
+ readonly type: "checkpoint"
50
+ readonly id: string
51
+ readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>
52
+ }
53
+ | {
54
+ readonly type: "handoff"
55
+ readonly id: string
56
+ readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>
57
+ }
58
+
59
+ export interface WorkflowRunnerOptions {
60
+ readonly signal?: AbortSignal
61
+ readonly maxSteps?: number
62
+ readonly maxDepth?: number
63
+ readonly onEvent?: (event: WorkflowEvent) => void | PromiseLike<void>
64
+ readonly exposeErrorStacks?: boolean
65
+ }
66
+
67
+ export type WorkflowEvent =
68
+ | { readonly type: "step.started"; readonly id: string }
69
+ | { readonly type: "step.completed"; readonly id: string; readonly output?: unknown }
70
+ | {
71
+ readonly type: "step.failed"
72
+ readonly id: string
73
+ readonly error: string
74
+ readonly stack?: string
75
+ }
76
+ | { readonly type: "approval.required"; readonly id: string; readonly reason: string }
77
+ | { readonly type: "checkpoint.created"; readonly id: string }
78
+
79
+ export interface WorkflowResult {
80
+ readonly ok: boolean
81
+ readonly values: ReadonlyMap<string, unknown>
82
+ readonly completed: readonly string[]
83
+ readonly error?: string
84
+ readonly stack?: string
85
+ }
86
+
87
+ /** Bounded orchestration primitives. The kernel knows no provider, UI, or framework package. */
88
+ export class WorkflowRunner {
89
+ private readonly options: Required<Pick<WorkflowRunnerOptions, "maxSteps" | "maxDepth">> &
90
+ WorkflowRunnerOptions
91
+ private readonly values = new Map<string, unknown>()
92
+ private readonly completed: string[] = []
93
+ private steps = 0
94
+
95
+ constructor(options: WorkflowRunnerOptions = {}) {
96
+ this.options = {
97
+ ...options,
98
+ maxSteps: options.maxSteps ?? 128,
99
+ maxDepth: options.maxDepth ?? 8,
100
+ }
101
+ if (!Number.isSafeInteger(this.options.maxSteps) || this.options.maxSteps < 1)
102
+ throw new RangeError("workflow: maxSteps must be positive")
103
+ if (!Number.isSafeInteger(this.options.maxDepth) || this.options.maxDepth < 1)
104
+ throw new RangeError("workflow: maxDepth must be positive")
105
+ }
106
+
107
+ async run(step: WorkflowStep): Promise<WorkflowResult> {
108
+ try {
109
+ await this.execute(step, 0)
110
+ return {
111
+ ok: true,
112
+ values: new Map(this.values),
113
+ completed: Object.freeze([...this.completed]),
114
+ }
115
+ } catch (error) {
116
+ const details = publicErrorDetails(
117
+ error,
118
+ "workflow failed",
119
+ this.options.exposeErrorStacks === true,
120
+ )
121
+ return {
122
+ ok: false,
123
+ values: new Map(this.values),
124
+ completed: Object.freeze([...this.completed]),
125
+ error: details.message,
126
+ ...(details.stack === undefined ? {} : { stack: details.stack }),
127
+ }
128
+ }
129
+ }
130
+
131
+ private context(): WorkflowContext {
132
+ return {
133
+ signal: this.options.signal ?? new AbortController().signal,
134
+ values: this.values,
135
+ set: (name, value) => this.values.set(name, value),
136
+ run: (step) => this.execute(step, 1),
137
+ }
138
+ }
139
+
140
+ private async execute(step: WorkflowStep, depth: number): Promise<unknown> {
141
+ if (depth > this.options.maxDepth) throw new Error("workflow max depth exceeded")
142
+ if (++this.steps > this.options.maxSteps) throw new Error("workflow max steps exceeded")
143
+ if (this.options.signal?.aborted) throw new Error("workflow cancelled")
144
+ switch (step.type) {
145
+ case "task":
146
+ return this.single(step.id, () => step.run(this.context()))
147
+ case "verify": {
148
+ const result = await this.single(step.id, () => step.run(this.context()))
149
+ const ok = typeof result === "boolean" ? result : isVerificationResult(result) && result.ok
150
+ if (!ok) throw new Error(`verification failed: ${step.id}`)
151
+ return result
152
+ }
153
+ case "approve": {
154
+ await this.emit({ type: "approval.required", id: step.id, reason: step.reason })
155
+ const approved = await this.single(step.id, () => step.run(this.context()))
156
+ if (approved !== true) throw new Error(`approval denied: ${step.id}`)
157
+ return approved
158
+ }
159
+ case "checkpoint": {
160
+ const result = await this.single(step.id, () => step.run(this.context()))
161
+ await this.emit({ type: "checkpoint.created", id: step.id })
162
+ return result
163
+ }
164
+ case "handoff":
165
+ return this.single(step.id, () => step.run(this.context()))
166
+ case "sequence": {
167
+ const results: unknown[] = []
168
+ for (const child of step.steps) results.push(await this.execute(child, depth + 1))
169
+ return results
170
+ }
171
+ case "parallel":
172
+ return this.parallel(step.steps, step.maxConcurrency ?? step.steps.length, depth)
173
+ case "retry": {
174
+ const attempts = step.attempts ?? 3
175
+ if (!Number.isSafeInteger(attempts) || attempts < 1 || attempts > 16)
176
+ throw new RangeError("workflow: attempts must be between 1 and 16")
177
+ let last: unknown
178
+ for (let attempt = 0; attempt < attempts; attempt++) {
179
+ try {
180
+ return await this.execute(step.step, depth + 1)
181
+ } catch (error) {
182
+ last = error
183
+ if (attempt + 1 < attempts && (step.backoffMs ?? 0) > 0)
184
+ await delay(Math.min(step.backoffMs! * (attempt + 1), 10_000), this.options.signal)
185
+ }
186
+ }
187
+ throw last instanceof Error ? last : new Error(String(last))
188
+ }
189
+ case "branch": {
190
+ const selected = await this.single(step.id, () => step.when(this.context()))
191
+ return selected === true
192
+ ? this.execute(step.then, depth + 1)
193
+ : step.otherwise === undefined
194
+ ? undefined
195
+ : this.execute(step.otherwise, depth + 1)
196
+ }
197
+ }
198
+ }
199
+
200
+ private async single(id: string, run: () => unknown | PromiseLike<unknown>): Promise<unknown> {
201
+ await this.emit({ type: "step.started", id })
202
+ try {
203
+ const output = await run()
204
+ this.completed.push(id)
205
+ await this.emit({ type: "step.completed", id, ...(output === undefined ? {} : { output }) })
206
+ return output
207
+ } catch (error) {
208
+ const details = publicErrorDetails(
209
+ error,
210
+ "workflow step failed",
211
+ this.options.exposeErrorStacks === true,
212
+ )
213
+ await this.emit({
214
+ type: "step.failed",
215
+ id,
216
+ error: details.message,
217
+ ...(details.stack === undefined ? {} : { stack: details.stack }),
218
+ })
219
+ throw error
220
+ }
221
+ }
222
+
223
+ private async parallel(
224
+ steps: readonly WorkflowStep[],
225
+ maxConcurrency: number,
226
+ depth: number,
227
+ ): Promise<readonly unknown[]> {
228
+ if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1)
229
+ throw new RangeError("workflow: maxConcurrency must be positive")
230
+ const outputs: unknown[] = new Array(steps.length)
231
+ let next = 0
232
+ const worker = async (): Promise<void> => {
233
+ for (;;) {
234
+ const index = next++
235
+ if (index >= steps.length) return
236
+ outputs[index] = await this.execute(steps[index]!, depth + 1)
237
+ }
238
+ }
239
+ await Promise.all(
240
+ Array.from({ length: Math.min(maxConcurrency, steps.length) }, () => worker()),
241
+ )
242
+ return outputs
243
+ }
244
+
245
+ private async emit(event: WorkflowEvent): Promise<void> {
246
+ await this.options.onEvent?.(event)
247
+ }
248
+ }
249
+
250
+ async function delay(ms: number, signal: AbortSignal | undefined): Promise<void> {
251
+ await new Promise<void>((resolve, reject) => {
252
+ const timer = setTimeout(resolve, ms)
253
+ if (signal === undefined) return
254
+ if (signal.aborted) {
255
+ clearTimeout(timer)
256
+ reject(new Error("workflow cancelled"))
257
+ return
258
+ }
259
+ signal.addEventListener(
260
+ "abort",
261
+ () => {
262
+ clearTimeout(timer)
263
+ reject(new Error("workflow cancelled"))
264
+ },
265
+ { once: true },
266
+ )
267
+ })
268
+ }
269
+
270
+ function isVerificationResult(
271
+ value: unknown,
272
+ ): value is { readonly ok: boolean; readonly report?: unknown } {
273
+ return (
274
+ value !== null &&
275
+ typeof value === "object" &&
276
+ !Array.isArray(value) &&
277
+ typeof (value as { readonly ok?: unknown }).ok === "boolean"
278
+ )
279
+ }