@codemation/node-example 0.0.39 → 0.0.41

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @codemation/node-example
2
2
 
3
+ ## 0.0.41
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`c1b081f`](https://github.com/MadeRelevant/codemation/commit/c1b081ffc8b66b0c4593c94f1d57a1cdf5c41140), [`be520d2`](https://github.com/MadeRelevant/codemation/commit/be520d2755144a3709ecc109019b84e2c502337e)]:
8
+ - @codemation/core@0.13.0
9
+
10
+ ## 0.0.40
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`3044474`](https://github.com/MadeRelevant/codemation/commit/3044474495525490735510ff74500b53761284b6)]:
15
+ - @codemation/core@0.12.0
16
+
3
17
  ## 0.0.39
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { ReadableStream } from "node:stream/web";
2
1
  import { ZodType } from "zod";
3
2
  import { InjectionToken as TypeToken } from "tsyringe";
3
+ import { ReadableStream } from "node:stream/web";
4
4
 
5
5
  //#region ../core/src/contracts/baseTypes.d.ts
6
6
  /**
@@ -13,6 +13,137 @@ type NodeId = string;
13
13
  type OutputPortKey = string;
14
14
  type InputPortKey = string;
15
15
  //#endregion
16
+ //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
17
+ type CostTrackingComponent = "chat" | "ocr" | "rag";
18
+ interface CostTrackingUsageRecord {
19
+ readonly component: CostTrackingComponent;
20
+ readonly provider: string;
21
+ readonly operation: string;
22
+ readonly pricingKey: string;
23
+ readonly usageUnit: string;
24
+ readonly quantity: number;
25
+ readonly modelName?: string;
26
+ readonly attributes?: TelemetryAttributes;
27
+ }
28
+ interface CostTrackingPriceQuote {
29
+ readonly currency: string;
30
+ readonly currencyScale: number;
31
+ readonly estimatedAmountMinor: number;
32
+ readonly estimateKind: "catalog";
33
+ }
34
+ interface CostTrackingTelemetry {
35
+ captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
36
+ forScope(scope: TelemetryScope): CostTrackingTelemetry;
37
+ }
38
+ //#endregion
39
+ //#region ../core/src/contracts/telemetryTypes.d.ts
40
+ type TelemetryAttributePrimitive = string | number | boolean | null;
41
+ interface TelemetryAttributes {
42
+ readonly [key: string]: TelemetryAttributePrimitive | undefined;
43
+ }
44
+ interface TelemetryMetricRecord {
45
+ readonly name: string;
46
+ readonly value: number;
47
+ readonly unit?: string;
48
+ readonly attributes?: TelemetryAttributes;
49
+ }
50
+ interface TelemetrySpanEventRecord {
51
+ readonly name: string;
52
+ readonly occurredAt?: Date;
53
+ readonly attributes?: TelemetryAttributes;
54
+ }
55
+ interface TelemetryArtifactAttachment {
56
+ readonly kind: string;
57
+ readonly contentType: string;
58
+ readonly previewText?: string;
59
+ readonly previewJson?: JsonValue;
60
+ readonly payloadText?: string;
61
+ readonly payloadJson?: JsonValue;
62
+ readonly bytes?: number;
63
+ readonly truncated?: boolean;
64
+ readonly expiresAt?: Date;
65
+ }
66
+ interface TelemetryArtifactReference {
67
+ readonly artifactId: string;
68
+ readonly traceId?: string;
69
+ readonly spanId?: string;
70
+ }
71
+ interface TelemetrySpanEnd {
72
+ readonly status?: "ok" | "error";
73
+ readonly statusMessage?: string;
74
+ readonly endedAt?: Date;
75
+ readonly attributes?: TelemetryAttributes;
76
+ }
77
+ interface TelemetryChildSpanStart {
78
+ readonly name: string;
79
+ readonly kind?: "internal" | "client";
80
+ readonly startedAt?: Date;
81
+ readonly attributes?: TelemetryAttributes;
82
+ }
83
+ interface TelemetryScope {
84
+ readonly traceId?: string;
85
+ readonly spanId?: string;
86
+ readonly costTracking?: CostTrackingTelemetry;
87
+ addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
88
+ recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
89
+ attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
90
+ }
91
+ interface TelemetrySpanScope extends TelemetryScope {
92
+ readonly traceId: string;
93
+ readonly spanId: string;
94
+ end(args?: TelemetrySpanEnd): Promise<void> | void;
95
+ /**
96
+ * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
97
+ * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
98
+ *
99
+ * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
100
+ * span instead of the orchestrator's node-level span.
101
+ */
102
+ asNodeTelemetry(args: Readonly<{
103
+ nodeId: NodeId;
104
+ activationId: NodeActivationId;
105
+ }>): NodeExecutionTelemetry;
106
+ }
107
+ interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
108
+ startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
109
+ }
110
+ interface ExecutionTelemetry extends TelemetryScope {
111
+ readonly traceId: string;
112
+ readonly spanId: string;
113
+ forNode(args: Readonly<{
114
+ nodeId: NodeId;
115
+ activationId: NodeActivationId;
116
+ }>): NodeExecutionTelemetry;
117
+ }
118
+ //#endregion
119
+ //#region ../core/src/contracts/retryPolicySpec.types.d.ts
120
+ /**
121
+ * In-process retry policy for runnable nodes. Serialized configs use the same
122
+ * `kind` discriminator (`JSON.stringify` / persisted workflows).
123
+ *
124
+ * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
125
+ */
126
+ type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
127
+ interface NoneRetryPolicySpec {
128
+ readonly kind: "none";
129
+ }
130
+ interface FixedRetryPolicySpec {
131
+ readonly kind: "fixed";
132
+ /** Total attempts including the first execution. Must be >= 1. */
133
+ readonly maxAttempts: number;
134
+ readonly delayMs: number;
135
+ }
136
+ interface ExponentialRetryPolicySpec {
137
+ readonly kind: "exponential";
138
+ /** Total attempts including the first execution. Must be >= 1. */
139
+ readonly maxAttempts: number;
140
+ readonly initialDelayMs: number;
141
+ readonly multiplier: number;
142
+ readonly maxDelayMs?: number;
143
+ /** When true, each delay is multiplied by a random factor in [1, 1.2). */
144
+ readonly jitter?: boolean;
145
+ }
146
+ //#endregion
16
147
  //#region ../core/src/contracts/credentialTypes.d.ts
17
148
  type CredentialTypeId = string;
18
149
  type CredentialRequirement = Readonly<{
@@ -24,6 +155,71 @@ type CredentialRequirement = Readonly<{
24
155
  helpUrl?: string;
25
156
  }>;
26
157
  //#endregion
158
+ //#region ../core/src/contracts/collectionTypes.d.ts
159
+ /**
160
+ * Represents a typed store for a single collection.
161
+ * All rows include auto-managed id, created_at, and updated_at fields.
162
+ */
163
+ interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
164
+ /**
165
+ * Insert a new row. id, created_at, and updated_at are auto-populated.
166
+ */
167
+ insert(row: TRow): Promise<TRow & {
168
+ id: string;
169
+ created_at: Date;
170
+ updated_at: Date;
171
+ }>;
172
+ /**
173
+ * Get a single row by id.
174
+ */
175
+ get(id: string): Promise<(TRow & {
176
+ id: string;
177
+ created_at: Date;
178
+ updated_at: Date;
179
+ }) | null>;
180
+ /**
181
+ * Find a single row matching the provided filter.
182
+ */
183
+ findOne(filter: Partial<TRow>): Promise<(TRow & {
184
+ id: string;
185
+ created_at: Date;
186
+ updated_at: Date;
187
+ }) | null>;
188
+ /**
189
+ * List rows with optional pagination and filtering.
190
+ */
191
+ list(opts?: {
192
+ limit?: number;
193
+ offset?: number;
194
+ where?: Partial<TRow>;
195
+ }): Promise<{
196
+ rows: ReadonlyArray<TRow & {
197
+ id: string;
198
+ created_at: Date;
199
+ updated_at: Date;
200
+ }>;
201
+ total: number;
202
+ }>;
203
+ /**
204
+ * Update a row by id with partial data.
205
+ */
206
+ update(id: string, patch: Partial<TRow>): Promise<TRow & {
207
+ id: string;
208
+ created_at: Date;
209
+ updated_at: Date;
210
+ }>;
211
+ /**
212
+ * Delete a row by id. Hard delete only (no soft delete).
213
+ */
214
+ delete(id: string): Promise<{
215
+ deleted: boolean;
216
+ }>;
217
+ }
218
+ /**
219
+ * Runtime collections context: keyed by collection name.
220
+ */
221
+ type CollectionsContext = Readonly<Record<string, CollectionStore>>;
222
+ //#endregion
27
223
  //#region ../core/src/contracts/runTypes.d.ts
28
224
  /**
29
225
  * Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
@@ -42,7 +238,7 @@ interface RunTestContext {
42
238
  readonly testCaseLabel?: string;
43
239
  }
44
240
  type NodeInputsByPort = Readonly<Record<InputPortKey, Items>>;
45
- type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped";
241
+ type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped" | "hitl-approved" | "hitl-rejected" | "hitl-timeout" | "hitl-auto-accepted" | "hitl-cancelled";
46
242
  interface NodeExecutionError {
47
243
  message: string;
48
244
  name?: string;
@@ -71,34 +267,6 @@ type ConnectionInvocationAppendArgs = Readonly<{
71
267
  parentInvocationId?: ConnectionInvocationId;
72
268
  }>;
73
269
  //#endregion
74
- //#region ../core/src/contracts/retryPolicySpec.types.d.ts
75
- /**
76
- * In-process retry policy for runnable nodes. Serialized configs use the same
77
- * `kind` discriminator (`JSON.stringify` / persisted workflows).
78
- *
79
- * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
80
- */
81
- type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
82
- interface NoneRetryPolicySpec {
83
- readonly kind: "none";
84
- }
85
- interface FixedRetryPolicySpec {
86
- readonly kind: "fixed";
87
- /** Total attempts including the first execution. Must be >= 1. */
88
- readonly maxAttempts: number;
89
- readonly delayMs: number;
90
- }
91
- interface ExponentialRetryPolicySpec {
92
- readonly kind: "exponential";
93
- /** Total attempts including the first execution. Must be >= 1. */
94
- readonly maxAttempts: number;
95
- readonly initialDelayMs: number;
96
- readonly multiplier: number;
97
- readonly maxDelayMs?: number;
98
- /** When true, each delay is multiplied by a random factor in [1, 1.2). */
99
- readonly jitter?: boolean;
100
- }
101
- //#endregion
102
270
  //#region ../core/src/contracts/workflowTypes.d.ts
103
271
  type NodeIdRef<TJson = unknown> = NodeId & Readonly<{
104
272
  __codemationNodeJson?: TJson;
@@ -260,175 +428,53 @@ interface NodeErrorHandler {
260
428
  }
261
429
  type NodeErrorHandlerSpec = TypeToken<NodeErrorHandler> | NodeErrorHandler;
262
430
  //#endregion
263
- //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
264
- type CostTrackingComponent = "chat" | "ocr" | "rag";
265
- interface CostTrackingUsageRecord {
266
- readonly component: CostTrackingComponent;
267
- readonly provider: string;
268
- readonly operation: string;
269
- readonly pricingKey: string;
270
- readonly usageUnit: string;
271
- readonly quantity: number;
272
- readonly modelName?: string;
273
- readonly attributes?: TelemetryAttributes;
274
- }
275
- interface CostTrackingPriceQuote {
276
- readonly currency: string;
277
- readonly currencyScale: number;
278
- readonly estimatedAmountMinor: number;
279
- readonly estimateKind: "catalog";
280
- }
281
- interface CostTrackingTelemetry {
282
- captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
283
- forScope(scope: TelemetryScope): CostTrackingTelemetry;
284
- }
285
- //#endregion
286
- //#region ../core/src/contracts/telemetryTypes.d.ts
287
- type TelemetryAttributePrimitive = string | number | boolean | null;
288
- interface TelemetryAttributes {
289
- readonly [key: string]: TelemetryAttributePrimitive | undefined;
290
- }
291
- interface TelemetryMetricRecord {
292
- readonly name: string;
293
- readonly value: number;
294
- readonly unit?: string;
295
- readonly attributes?: TelemetryAttributes;
296
- }
297
- interface TelemetrySpanEventRecord {
298
- readonly name: string;
299
- readonly occurredAt?: Date;
300
- readonly attributes?: TelemetryAttributes;
301
- }
302
- interface TelemetryArtifactAttachment {
303
- readonly kind: string;
304
- readonly contentType: string;
305
- readonly previewText?: string;
306
- readonly previewJson?: JsonValue;
307
- readonly payloadText?: string;
308
- readonly payloadJson?: JsonValue;
309
- readonly bytes?: number;
310
- readonly truncated?: boolean;
311
- readonly expiresAt?: Date;
312
- }
313
- interface TelemetryArtifactReference {
314
- readonly artifactId: string;
315
- readonly traceId?: string;
316
- readonly spanId?: string;
317
- }
318
- interface TelemetrySpanEnd {
319
- readonly status?: "ok" | "error";
320
- readonly statusMessage?: string;
321
- readonly endedAt?: Date;
322
- readonly attributes?: TelemetryAttributes;
323
- }
324
- interface TelemetryChildSpanStart {
325
- readonly name: string;
326
- readonly kind?: "internal" | "client";
327
- readonly startedAt?: Date;
328
- readonly attributes?: TelemetryAttributes;
329
- }
330
- interface TelemetryScope {
331
- readonly traceId?: string;
332
- readonly spanId?: string;
333
- readonly costTracking?: CostTrackingTelemetry;
334
- addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
335
- recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
336
- attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
337
- }
338
- interface TelemetrySpanScope extends TelemetryScope {
339
- readonly traceId: string;
340
- readonly spanId: string;
341
- end(args?: TelemetrySpanEnd): Promise<void> | void;
431
+ //#region ../core/src/contracts/runtimeTypes.d.ts
432
+ /** Opaque unique identifier for a single HumanTask instance. */
433
+ type HumanTaskId = string;
434
+ /**
435
+ * Minimal handle handed to the `deliver` callback so it can route to the correct
436
+ * inbox channel.
437
+ */
438
+ interface HumanTaskHandle {
439
+ readonly taskId: HumanTaskId;
440
+ readonly runId: string;
441
+ readonly nodeId: string;
442
+ readonly expiresAt: Date;
443
+ /** TODO: real signed URL; placeholder empty string for now. */
444
+ readonly resumeUrl: string;
342
445
  /**
343
- * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
344
- * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
345
- *
346
- * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
347
- * span instead of the orchestrator's node-level span.
446
+ * Arbitrary JSON metadata copied from `SuspensionRequest.request.metadata` at suspension time.
447
+ * Used by the agent runtime to round-trip the `agentCheckpoint` back to the
448
+ * resumed node via `ctx.resumeContext.task.metadata`.
348
449
  */
349
- asNodeTelemetry(args: Readonly<{
350
- nodeId: NodeId;
351
- activationId: NodeActivationId;
352
- }>): NodeExecutionTelemetry;
353
- }
354
- interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
355
- startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
450
+ readonly metadata?: Readonly<Record<string, JsonValue>>;
356
451
  }
357
- interface ExecutionTelemetry extends TelemetryScope {
358
- readonly traceId: string;
359
- readonly spanId: string;
360
- forNode(args: Readonly<{
361
- nodeId: NodeId;
362
- activationId: NodeActivationId;
363
- }>): NodeExecutionTelemetry;
452
+ /** Identity of the person who made a decision on the task. */
453
+ interface HumanTaskActor {
454
+ readonly actorId: string;
455
+ readonly displayName?: string;
364
456
  }
365
- //#endregion
366
- //#region ../core/src/contracts/collectionTypes.d.ts
367
457
  /**
368
- * Represents a typed store for a single collection.
369
- * All rows include auto-managed id, created_at, and updated_at fields.
458
+ * Resume context injected into `NodeExecutionContext` when the engine re-activates
459
+ * a previously suspended node. `defineHumanApprovalNode` wraps this with parsed
460
+ * `TDecision`; at the engine layer `decision.value` is `unknown`.
370
461
  */
371
- interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
372
- /**
373
- * Insert a new row. id, created_at, and updated_at are auto-populated.
374
- */
375
- insert(row: TRow): Promise<TRow & {
376
- id: string;
377
- created_at: Date;
378
- updated_at: Date;
379
- }>;
380
- /**
381
- * Get a single row by id.
382
- */
383
- get(id: string): Promise<(TRow & {
384
- id: string;
385
- created_at: Date;
386
- updated_at: Date;
387
- }) | null>;
388
- /**
389
- * Find a single row matching the provided filter.
390
- */
391
- findOne(filter: Partial<TRow>): Promise<(TRow & {
392
- id: string;
393
- created_at: Date;
394
- updated_at: Date;
395
- }) | null>;
396
- /**
397
- * List rows with optional pagination and filtering.
398
- */
399
- list(opts?: {
400
- limit?: number;
401
- offset?: number;
402
- where?: Partial<TRow>;
403
- }): Promise<{
404
- rows: ReadonlyArray<TRow & {
405
- id: string;
406
- created_at: Date;
407
- updated_at: Date;
408
- }>;
409
- total: number;
410
- }>;
411
- /**
412
- * Update a row by id with partial data.
413
- */
414
- update(id: string, patch: Partial<TRow>): Promise<TRow & {
415
- id: string;
416
- created_at: Date;
417
- updated_at: Date;
418
- }>;
419
- /**
420
- * Delete a row by id. Hard delete only (no soft delete).
421
- */
422
- delete(id: string): Promise<{
423
- deleted: boolean;
462
+ interface ResumeContext {
463
+ readonly decision: Readonly<{
464
+ kind: "decided";
465
+ value: unknown;
466
+ actor: HumanTaskActor;
467
+ decidedAt: Date;
468
+ }> | Readonly<{
469
+ kind: "timed_out";
470
+ at: Date;
471
+ }> | Readonly<{
472
+ kind: "auto_accepted";
473
+ at: Date;
424
474
  }>;
475
+ readonly delivery: JsonValue;
476
+ readonly task: HumanTaskHandle;
425
477
  }
426
- /**
427
- * Runtime collections context: keyed by collection name.
428
- */
429
- type CollectionsContext = Readonly<Record<string, CollectionStore>>;
430
- //#endregion
431
- //#region ../core/src/contracts/runtimeTypes.d.ts
432
478
  interface NodeExecutionStatePublisher {
433
479
  markQueued(args: {
434
480
  nodeId: NodeId;
@@ -485,6 +531,22 @@ interface ExecutionBinaryService {
485
531
  activationId: NodeActivationId;
486
532
  }): NodeBinaryAttachmentService;
487
533
  openReadStream(attachment: BinaryAttachment): Promise<BinaryStorageReadResult | undefined>;
534
+ /**
535
+ * Reads all bytes from the attachment into a contiguous `Uint8Array`.
536
+ * Checks `attachment.size` against `maxBytes` *before* any allocation; throws a bounded-read
537
+ * error when exceeded (no OOM). Throws if the stream is unavailable or the byte count mismatches.
538
+ */
539
+ getBytes(attachment: BinaryAttachment, maxBytes?: number): Promise<Uint8Array>;
540
+ /**
541
+ * Reads the attachment and decodes the bytes as UTF-8 text.
542
+ * Subject to the same bounded-read safety as `getBytes`.
543
+ */
544
+ getText(attachment: BinaryAttachment, maxBytes?: number): Promise<string>;
545
+ /**
546
+ * Reads the attachment, decodes as UTF-8 text, and parses as JSON.
547
+ * Throws a clear error on invalid JSON. Subject to the same bounded-read safety.
548
+ */
549
+ getJson<T = unknown>(attachment: BinaryAttachment, maxBytes?: number): Promise<T>;
488
550
  }
489
551
  interface ExecutionContext {
490
552
  runId: RunId;
@@ -517,6 +579,14 @@ interface ExecutionContext {
517
579
  * Collections registered in the codemation config, keyed by collection name.
518
580
  */
519
581
  readonly collections?: CollectionsContext;
582
+ /**
583
+ * Resolve a DI token from the host container.
584
+ * Allows nodes to reach host-side services (e.g. `InboxChannelResolverToken`)
585
+ * without importing host code. Wired by `DefaultExecutionContextFactory`; throws
586
+ * a clear error when no resolver is configured (e.g. in unit tests that don't
587
+ * set up the full container).
588
+ */
589
+ resolve<T>(token: TypeToken<T>): T;
520
590
  }
521
591
  interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase> extends ExecutionContext {
522
592
  nodeId: NodeId;
@@ -524,6 +594,11 @@ interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase>
524
594
  config: TConfig;
525
595
  telemetry: NodeExecutionTelemetry;
526
596
  binary: NodeBinaryAttachmentService;
597
+ /**
598
+ * Present when this node activation is a HITL resume.
599
+ * The node checks `ctx.resumeContext !== undefined` and takes the resume branch.
600
+ */
601
+ resumeContext?: ResumeContext;
527
602
  }
528
603
  /**
529
604
  * Per-item runnable node: return JSON, an array to fan-out on `main`, an explicit `Item`, or {@link emitPorts}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { ReadableStream } from "node:stream/web";
2
1
  import { ZodType } from "zod";
3
2
  import { InjectionToken as TypeToken } from "tsyringe";
3
+ import { ReadableStream } from "node:stream/web";
4
4
 
5
5
  //#region ../core/src/contracts/baseTypes.d.ts
6
6
  /**
@@ -13,6 +13,137 @@ type NodeId = string;
13
13
  type OutputPortKey = string;
14
14
  type InputPortKey = string;
15
15
  //#endregion
16
+ //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
17
+ type CostTrackingComponent = "chat" | "ocr" | "rag";
18
+ interface CostTrackingUsageRecord {
19
+ readonly component: CostTrackingComponent;
20
+ readonly provider: string;
21
+ readonly operation: string;
22
+ readonly pricingKey: string;
23
+ readonly usageUnit: string;
24
+ readonly quantity: number;
25
+ readonly modelName?: string;
26
+ readonly attributes?: TelemetryAttributes;
27
+ }
28
+ interface CostTrackingPriceQuote {
29
+ readonly currency: string;
30
+ readonly currencyScale: number;
31
+ readonly estimatedAmountMinor: number;
32
+ readonly estimateKind: "catalog";
33
+ }
34
+ interface CostTrackingTelemetry {
35
+ captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
36
+ forScope(scope: TelemetryScope): CostTrackingTelemetry;
37
+ }
38
+ //#endregion
39
+ //#region ../core/src/contracts/telemetryTypes.d.ts
40
+ type TelemetryAttributePrimitive = string | number | boolean | null;
41
+ interface TelemetryAttributes {
42
+ readonly [key: string]: TelemetryAttributePrimitive | undefined;
43
+ }
44
+ interface TelemetryMetricRecord {
45
+ readonly name: string;
46
+ readonly value: number;
47
+ readonly unit?: string;
48
+ readonly attributes?: TelemetryAttributes;
49
+ }
50
+ interface TelemetrySpanEventRecord {
51
+ readonly name: string;
52
+ readonly occurredAt?: Date;
53
+ readonly attributes?: TelemetryAttributes;
54
+ }
55
+ interface TelemetryArtifactAttachment {
56
+ readonly kind: string;
57
+ readonly contentType: string;
58
+ readonly previewText?: string;
59
+ readonly previewJson?: JsonValue;
60
+ readonly payloadText?: string;
61
+ readonly payloadJson?: JsonValue;
62
+ readonly bytes?: number;
63
+ readonly truncated?: boolean;
64
+ readonly expiresAt?: Date;
65
+ }
66
+ interface TelemetryArtifactReference {
67
+ readonly artifactId: string;
68
+ readonly traceId?: string;
69
+ readonly spanId?: string;
70
+ }
71
+ interface TelemetrySpanEnd {
72
+ readonly status?: "ok" | "error";
73
+ readonly statusMessage?: string;
74
+ readonly endedAt?: Date;
75
+ readonly attributes?: TelemetryAttributes;
76
+ }
77
+ interface TelemetryChildSpanStart {
78
+ readonly name: string;
79
+ readonly kind?: "internal" | "client";
80
+ readonly startedAt?: Date;
81
+ readonly attributes?: TelemetryAttributes;
82
+ }
83
+ interface TelemetryScope {
84
+ readonly traceId?: string;
85
+ readonly spanId?: string;
86
+ readonly costTracking?: CostTrackingTelemetry;
87
+ addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
88
+ recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
89
+ attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
90
+ }
91
+ interface TelemetrySpanScope extends TelemetryScope {
92
+ readonly traceId: string;
93
+ readonly spanId: string;
94
+ end(args?: TelemetrySpanEnd): Promise<void> | void;
95
+ /**
96
+ * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
97
+ * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
98
+ *
99
+ * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
100
+ * span instead of the orchestrator's node-level span.
101
+ */
102
+ asNodeTelemetry(args: Readonly<{
103
+ nodeId: NodeId;
104
+ activationId: NodeActivationId;
105
+ }>): NodeExecutionTelemetry;
106
+ }
107
+ interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
108
+ startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
109
+ }
110
+ interface ExecutionTelemetry extends TelemetryScope {
111
+ readonly traceId: string;
112
+ readonly spanId: string;
113
+ forNode(args: Readonly<{
114
+ nodeId: NodeId;
115
+ activationId: NodeActivationId;
116
+ }>): NodeExecutionTelemetry;
117
+ }
118
+ //#endregion
119
+ //#region ../core/src/contracts/retryPolicySpec.types.d.ts
120
+ /**
121
+ * In-process retry policy for runnable nodes. Serialized configs use the same
122
+ * `kind` discriminator (`JSON.stringify` / persisted workflows).
123
+ *
124
+ * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
125
+ */
126
+ type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
127
+ interface NoneRetryPolicySpec {
128
+ readonly kind: "none";
129
+ }
130
+ interface FixedRetryPolicySpec {
131
+ readonly kind: "fixed";
132
+ /** Total attempts including the first execution. Must be >= 1. */
133
+ readonly maxAttempts: number;
134
+ readonly delayMs: number;
135
+ }
136
+ interface ExponentialRetryPolicySpec {
137
+ readonly kind: "exponential";
138
+ /** Total attempts including the first execution. Must be >= 1. */
139
+ readonly maxAttempts: number;
140
+ readonly initialDelayMs: number;
141
+ readonly multiplier: number;
142
+ readonly maxDelayMs?: number;
143
+ /** When true, each delay is multiplied by a random factor in [1, 1.2). */
144
+ readonly jitter?: boolean;
145
+ }
146
+ //#endregion
16
147
  //#region ../core/src/contracts/credentialTypes.d.ts
17
148
  type CredentialTypeId = string;
18
149
  type CredentialRequirement = Readonly<{
@@ -24,6 +155,71 @@ type CredentialRequirement = Readonly<{
24
155
  helpUrl?: string;
25
156
  }>;
26
157
  //#endregion
158
+ //#region ../core/src/contracts/collectionTypes.d.ts
159
+ /**
160
+ * Represents a typed store for a single collection.
161
+ * All rows include auto-managed id, created_at, and updated_at fields.
162
+ */
163
+ interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
164
+ /**
165
+ * Insert a new row. id, created_at, and updated_at are auto-populated.
166
+ */
167
+ insert(row: TRow): Promise<TRow & {
168
+ id: string;
169
+ created_at: Date;
170
+ updated_at: Date;
171
+ }>;
172
+ /**
173
+ * Get a single row by id.
174
+ */
175
+ get(id: string): Promise<(TRow & {
176
+ id: string;
177
+ created_at: Date;
178
+ updated_at: Date;
179
+ }) | null>;
180
+ /**
181
+ * Find a single row matching the provided filter.
182
+ */
183
+ findOne(filter: Partial<TRow>): Promise<(TRow & {
184
+ id: string;
185
+ created_at: Date;
186
+ updated_at: Date;
187
+ }) | null>;
188
+ /**
189
+ * List rows with optional pagination and filtering.
190
+ */
191
+ list(opts?: {
192
+ limit?: number;
193
+ offset?: number;
194
+ where?: Partial<TRow>;
195
+ }): Promise<{
196
+ rows: ReadonlyArray<TRow & {
197
+ id: string;
198
+ created_at: Date;
199
+ updated_at: Date;
200
+ }>;
201
+ total: number;
202
+ }>;
203
+ /**
204
+ * Update a row by id with partial data.
205
+ */
206
+ update(id: string, patch: Partial<TRow>): Promise<TRow & {
207
+ id: string;
208
+ created_at: Date;
209
+ updated_at: Date;
210
+ }>;
211
+ /**
212
+ * Delete a row by id. Hard delete only (no soft delete).
213
+ */
214
+ delete(id: string): Promise<{
215
+ deleted: boolean;
216
+ }>;
217
+ }
218
+ /**
219
+ * Runtime collections context: keyed by collection name.
220
+ */
221
+ type CollectionsContext = Readonly<Record<string, CollectionStore>>;
222
+ //#endregion
27
223
  //#region ../core/src/contracts/runTypes.d.ts
28
224
  /**
29
225
  * Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
@@ -42,7 +238,7 @@ interface RunTestContext {
42
238
  readonly testCaseLabel?: string;
43
239
  }
44
240
  type NodeInputsByPort = Readonly<Record<InputPortKey, Items>>;
45
- type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped";
241
+ type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped" | "hitl-approved" | "hitl-rejected" | "hitl-timeout" | "hitl-auto-accepted" | "hitl-cancelled";
46
242
  interface NodeExecutionError {
47
243
  message: string;
48
244
  name?: string;
@@ -71,34 +267,6 @@ type ConnectionInvocationAppendArgs = Readonly<{
71
267
  parentInvocationId?: ConnectionInvocationId;
72
268
  }>;
73
269
  //#endregion
74
- //#region ../core/src/contracts/retryPolicySpec.types.d.ts
75
- /**
76
- * In-process retry policy for runnable nodes. Serialized configs use the same
77
- * `kind` discriminator (`JSON.stringify` / persisted workflows).
78
- *
79
- * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
80
- */
81
- type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
82
- interface NoneRetryPolicySpec {
83
- readonly kind: "none";
84
- }
85
- interface FixedRetryPolicySpec {
86
- readonly kind: "fixed";
87
- /** Total attempts including the first execution. Must be >= 1. */
88
- readonly maxAttempts: number;
89
- readonly delayMs: number;
90
- }
91
- interface ExponentialRetryPolicySpec {
92
- readonly kind: "exponential";
93
- /** Total attempts including the first execution. Must be >= 1. */
94
- readonly maxAttempts: number;
95
- readonly initialDelayMs: number;
96
- readonly multiplier: number;
97
- readonly maxDelayMs?: number;
98
- /** When true, each delay is multiplied by a random factor in [1, 1.2). */
99
- readonly jitter?: boolean;
100
- }
101
- //#endregion
102
270
  //#region ../core/src/contracts/workflowTypes.d.ts
103
271
  type NodeIdRef<TJson = unknown> = NodeId & Readonly<{
104
272
  __codemationNodeJson?: TJson;
@@ -260,175 +428,53 @@ interface NodeErrorHandler {
260
428
  }
261
429
  type NodeErrorHandlerSpec = TypeToken<NodeErrorHandler> | NodeErrorHandler;
262
430
  //#endregion
263
- //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
264
- type CostTrackingComponent = "chat" | "ocr" | "rag";
265
- interface CostTrackingUsageRecord {
266
- readonly component: CostTrackingComponent;
267
- readonly provider: string;
268
- readonly operation: string;
269
- readonly pricingKey: string;
270
- readonly usageUnit: string;
271
- readonly quantity: number;
272
- readonly modelName?: string;
273
- readonly attributes?: TelemetryAttributes;
274
- }
275
- interface CostTrackingPriceQuote {
276
- readonly currency: string;
277
- readonly currencyScale: number;
278
- readonly estimatedAmountMinor: number;
279
- readonly estimateKind: "catalog";
280
- }
281
- interface CostTrackingTelemetry {
282
- captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
283
- forScope(scope: TelemetryScope): CostTrackingTelemetry;
284
- }
285
- //#endregion
286
- //#region ../core/src/contracts/telemetryTypes.d.ts
287
- type TelemetryAttributePrimitive = string | number | boolean | null;
288
- interface TelemetryAttributes {
289
- readonly [key: string]: TelemetryAttributePrimitive | undefined;
290
- }
291
- interface TelemetryMetricRecord {
292
- readonly name: string;
293
- readonly value: number;
294
- readonly unit?: string;
295
- readonly attributes?: TelemetryAttributes;
296
- }
297
- interface TelemetrySpanEventRecord {
298
- readonly name: string;
299
- readonly occurredAt?: Date;
300
- readonly attributes?: TelemetryAttributes;
301
- }
302
- interface TelemetryArtifactAttachment {
303
- readonly kind: string;
304
- readonly contentType: string;
305
- readonly previewText?: string;
306
- readonly previewJson?: JsonValue;
307
- readonly payloadText?: string;
308
- readonly payloadJson?: JsonValue;
309
- readonly bytes?: number;
310
- readonly truncated?: boolean;
311
- readonly expiresAt?: Date;
312
- }
313
- interface TelemetryArtifactReference {
314
- readonly artifactId: string;
315
- readonly traceId?: string;
316
- readonly spanId?: string;
317
- }
318
- interface TelemetrySpanEnd {
319
- readonly status?: "ok" | "error";
320
- readonly statusMessage?: string;
321
- readonly endedAt?: Date;
322
- readonly attributes?: TelemetryAttributes;
323
- }
324
- interface TelemetryChildSpanStart {
325
- readonly name: string;
326
- readonly kind?: "internal" | "client";
327
- readonly startedAt?: Date;
328
- readonly attributes?: TelemetryAttributes;
329
- }
330
- interface TelemetryScope {
331
- readonly traceId?: string;
332
- readonly spanId?: string;
333
- readonly costTracking?: CostTrackingTelemetry;
334
- addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
335
- recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
336
- attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
337
- }
338
- interface TelemetrySpanScope extends TelemetryScope {
339
- readonly traceId: string;
340
- readonly spanId: string;
341
- end(args?: TelemetrySpanEnd): Promise<void> | void;
431
+ //#region ../core/src/contracts/runtimeTypes.d.ts
432
+ /** Opaque unique identifier for a single HumanTask instance. */
433
+ type HumanTaskId = string;
434
+ /**
435
+ * Minimal handle handed to the `deliver` callback so it can route to the correct
436
+ * inbox channel.
437
+ */
438
+ interface HumanTaskHandle {
439
+ readonly taskId: HumanTaskId;
440
+ readonly runId: string;
441
+ readonly nodeId: string;
442
+ readonly expiresAt: Date;
443
+ /** TODO: real signed URL; placeholder empty string for now. */
444
+ readonly resumeUrl: string;
342
445
  /**
343
- * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
344
- * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
345
- *
346
- * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
347
- * span instead of the orchestrator's node-level span.
446
+ * Arbitrary JSON metadata copied from `SuspensionRequest.request.metadata` at suspension time.
447
+ * Used by the agent runtime to round-trip the `agentCheckpoint` back to the
448
+ * resumed node via `ctx.resumeContext.task.metadata`.
348
449
  */
349
- asNodeTelemetry(args: Readonly<{
350
- nodeId: NodeId;
351
- activationId: NodeActivationId;
352
- }>): NodeExecutionTelemetry;
353
- }
354
- interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
355
- startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
450
+ readonly metadata?: Readonly<Record<string, JsonValue>>;
356
451
  }
357
- interface ExecutionTelemetry extends TelemetryScope {
358
- readonly traceId: string;
359
- readonly spanId: string;
360
- forNode(args: Readonly<{
361
- nodeId: NodeId;
362
- activationId: NodeActivationId;
363
- }>): NodeExecutionTelemetry;
452
+ /** Identity of the person who made a decision on the task. */
453
+ interface HumanTaskActor {
454
+ readonly actorId: string;
455
+ readonly displayName?: string;
364
456
  }
365
- //#endregion
366
- //#region ../core/src/contracts/collectionTypes.d.ts
367
457
  /**
368
- * Represents a typed store for a single collection.
369
- * All rows include auto-managed id, created_at, and updated_at fields.
458
+ * Resume context injected into `NodeExecutionContext` when the engine re-activates
459
+ * a previously suspended node. `defineHumanApprovalNode` wraps this with parsed
460
+ * `TDecision`; at the engine layer `decision.value` is `unknown`.
370
461
  */
371
- interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
372
- /**
373
- * Insert a new row. id, created_at, and updated_at are auto-populated.
374
- */
375
- insert(row: TRow): Promise<TRow & {
376
- id: string;
377
- created_at: Date;
378
- updated_at: Date;
379
- }>;
380
- /**
381
- * Get a single row by id.
382
- */
383
- get(id: string): Promise<(TRow & {
384
- id: string;
385
- created_at: Date;
386
- updated_at: Date;
387
- }) | null>;
388
- /**
389
- * Find a single row matching the provided filter.
390
- */
391
- findOne(filter: Partial<TRow>): Promise<(TRow & {
392
- id: string;
393
- created_at: Date;
394
- updated_at: Date;
395
- }) | null>;
396
- /**
397
- * List rows with optional pagination and filtering.
398
- */
399
- list(opts?: {
400
- limit?: number;
401
- offset?: number;
402
- where?: Partial<TRow>;
403
- }): Promise<{
404
- rows: ReadonlyArray<TRow & {
405
- id: string;
406
- created_at: Date;
407
- updated_at: Date;
408
- }>;
409
- total: number;
410
- }>;
411
- /**
412
- * Update a row by id with partial data.
413
- */
414
- update(id: string, patch: Partial<TRow>): Promise<TRow & {
415
- id: string;
416
- created_at: Date;
417
- updated_at: Date;
418
- }>;
419
- /**
420
- * Delete a row by id. Hard delete only (no soft delete).
421
- */
422
- delete(id: string): Promise<{
423
- deleted: boolean;
462
+ interface ResumeContext {
463
+ readonly decision: Readonly<{
464
+ kind: "decided";
465
+ value: unknown;
466
+ actor: HumanTaskActor;
467
+ decidedAt: Date;
468
+ }> | Readonly<{
469
+ kind: "timed_out";
470
+ at: Date;
471
+ }> | Readonly<{
472
+ kind: "auto_accepted";
473
+ at: Date;
424
474
  }>;
475
+ readonly delivery: JsonValue;
476
+ readonly task: HumanTaskHandle;
425
477
  }
426
- /**
427
- * Runtime collections context: keyed by collection name.
428
- */
429
- type CollectionsContext = Readonly<Record<string, CollectionStore>>;
430
- //#endregion
431
- //#region ../core/src/contracts/runtimeTypes.d.ts
432
478
  interface NodeExecutionStatePublisher {
433
479
  markQueued(args: {
434
480
  nodeId: NodeId;
@@ -485,6 +531,22 @@ interface ExecutionBinaryService {
485
531
  activationId: NodeActivationId;
486
532
  }): NodeBinaryAttachmentService;
487
533
  openReadStream(attachment: BinaryAttachment): Promise<BinaryStorageReadResult | undefined>;
534
+ /**
535
+ * Reads all bytes from the attachment into a contiguous `Uint8Array`.
536
+ * Checks `attachment.size` against `maxBytes` *before* any allocation; throws a bounded-read
537
+ * error when exceeded (no OOM). Throws if the stream is unavailable or the byte count mismatches.
538
+ */
539
+ getBytes(attachment: BinaryAttachment, maxBytes?: number): Promise<Uint8Array>;
540
+ /**
541
+ * Reads the attachment and decodes the bytes as UTF-8 text.
542
+ * Subject to the same bounded-read safety as `getBytes`.
543
+ */
544
+ getText(attachment: BinaryAttachment, maxBytes?: number): Promise<string>;
545
+ /**
546
+ * Reads the attachment, decodes as UTF-8 text, and parses as JSON.
547
+ * Throws a clear error on invalid JSON. Subject to the same bounded-read safety.
548
+ */
549
+ getJson<T = unknown>(attachment: BinaryAttachment, maxBytes?: number): Promise<T>;
488
550
  }
489
551
  interface ExecutionContext {
490
552
  runId: RunId;
@@ -517,6 +579,14 @@ interface ExecutionContext {
517
579
  * Collections registered in the codemation config, keyed by collection name.
518
580
  */
519
581
  readonly collections?: CollectionsContext;
582
+ /**
583
+ * Resolve a DI token from the host container.
584
+ * Allows nodes to reach host-side services (e.g. `InboxChannelResolverToken`)
585
+ * without importing host code. Wired by `DefaultExecutionContextFactory`; throws
586
+ * a clear error when no resolver is configured (e.g. in unit tests that don't
587
+ * set up the full container).
588
+ */
589
+ resolve<T>(token: TypeToken<T>): T;
520
590
  }
521
591
  interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase> extends ExecutionContext {
522
592
  nodeId: NodeId;
@@ -524,6 +594,11 @@ interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase>
524
594
  config: TConfig;
525
595
  telemetry: NodeExecutionTelemetry;
526
596
  binary: NodeBinaryAttachmentService;
597
+ /**
598
+ * Present when this node activation is a HITL resume.
599
+ * The node checks `ctx.resumeContext !== undefined` and takes the resume branch.
600
+ */
601
+ resumeContext?: ResumeContext;
527
602
  }
528
603
  /**
529
604
  * Per-item runnable node: return JSON, an array to fan-out on `main`, an explicit `Item`, or {@link emitPorts}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemation/node-example",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@codemation/core": "0.11.1"
31
+ "@codemation/core": "0.13.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.3.5",