@codemation/core 0.8.1 → 0.10.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 (100) hide show
  1. package/CHANGELOG.md +386 -0
  2. package/dist/{EngineRuntimeRegistration.types-BP6tsaNP.d.ts → EngineRuntimeRegistration.types-D1fyApMI.d.ts} +2 -2
  3. package/dist/{EngineWorkflowRunnerService-DzOCa1BW.d.cts → EngineRuntimeRegistration.types-pB3FnzqR.d.cts} +17 -17
  4. package/dist/{InMemoryRunDataFactory-1iz7_SnO.d.cts → InMemoryRunDataFactory-Xw7v4-sj.d.cts} +31 -29
  5. package/dist/InMemoryRunEventBusRegistry-VM3OWnHo.cjs +47 -0
  6. package/dist/InMemoryRunEventBusRegistry-VM3OWnHo.cjs.map +1 -0
  7. package/dist/InMemoryRunEventBusRegistry-sM4z4n_i.js +41 -0
  8. package/dist/InMemoryRunEventBusRegistry-sM4z4n_i.js.map +1 -0
  9. package/dist/{RunIntentService-BqhmdoA1.d.ts → RunIntentService-BE9CAkbf.d.ts} +966 -471
  10. package/dist/{RunIntentService-S-1lW-gS.d.cts → RunIntentService-siBSjaaY.d.cts} +859 -493
  11. package/dist/bootstrap/index.cjs +5 -2
  12. package/dist/bootstrap/index.d.cts +212 -135
  13. package/dist/bootstrap/index.d.ts +4 -4
  14. package/dist/bootstrap/index.js +3 -3
  15. package/dist/{bootstrap-Bx1u4cbS.cjs → bootstrap-Cm5ruQxx.cjs} +253 -2
  16. package/dist/bootstrap-Cm5ruQxx.cjs.map +1 -0
  17. package/dist/{bootstrap-BoknFKnw.js → bootstrap-D3r505ko.js} +236 -3
  18. package/dist/bootstrap-D3r505ko.js.map +1 -0
  19. package/dist/{index-CVs9rVhl.d.ts → index-DeLl1Tne.d.ts} +632 -230
  20. package/dist/index.cjs +323 -176
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +544 -91
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +299 -166
  25. package/dist/index.js.map +1 -1
  26. package/dist/{runtime-DUW6tIJ1.js → runtime-BGNbRnqs.js} +934 -75
  27. package/dist/runtime-BGNbRnqs.js.map +1 -0
  28. package/dist/{runtime-Dvo2ru5A.cjs → runtime-DKXJwTNv.cjs} +1028 -73
  29. package/dist/runtime-DKXJwTNv.cjs.map +1 -0
  30. package/dist/testing.cjs +4 -4
  31. package/dist/testing.cjs.map +1 -1
  32. package/dist/testing.d.cts +2 -2
  33. package/dist/testing.d.ts +2 -2
  34. package/dist/testing.js +3 -3
  35. package/package.json +7 -2
  36. package/src/ai/AiHost.ts +42 -14
  37. package/src/authoring/DefinedCollectionRegistry.ts +17 -0
  38. package/src/authoring/defineCollection.types.ts +181 -0
  39. package/src/authoring/definePollingTrigger.types.ts +396 -0
  40. package/src/authoring/definePollingTriggerInternals.ts +74 -0
  41. package/src/authoring/index.ts +19 -0
  42. package/src/bootstrap/index.ts +9 -0
  43. package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +8 -0
  44. package/src/browser.ts +1 -0
  45. package/src/contracts/CodemationTelemetryAttributeNames.ts +6 -0
  46. package/src/contracts/NoOpNodeExecutionTelemetry.ts +2 -11
  47. package/src/contracts/NoOpTelemetrySpanScope.ts +46 -10
  48. package/src/contracts/assertionTypes.ts +63 -0
  49. package/src/contracts/baseTypes.ts +12 -0
  50. package/src/contracts/collectionTypes.ts +44 -0
  51. package/src/contracts/credentialTypes.ts +23 -1
  52. package/src/contracts/executionPersistenceContracts.ts +30 -0
  53. package/src/contracts/index.ts +4 -0
  54. package/src/contracts/runTypes.ts +37 -1
  55. package/src/contracts/runtimeTypes.ts +42 -0
  56. package/src/contracts/telemetryTypes.ts +8 -0
  57. package/src/contracts/testTriggerTypes.ts +66 -0
  58. package/src/contracts/workflowTypes.ts +36 -7
  59. package/src/contracts.ts +59 -0
  60. package/src/events/ConnectionInvocationEventPublisher.ts +46 -0
  61. package/src/events/index.ts +1 -0
  62. package/src/events/runEvents.ts +74 -0
  63. package/src/execution/ChildExecutionScopeFactory.ts +55 -0
  64. package/src/execution/DefaultExecutionContextFactory.ts +6 -0
  65. package/src/execution/ExecutionTelemetryCostTrackingDecoratorFactory.ts +18 -0
  66. package/src/execution/NodeExecutor.ts +10 -2
  67. package/src/execution/NodeInstanceFactory.ts +13 -1
  68. package/src/execution/NodeInstantiationError.ts +16 -0
  69. package/src/execution/NodeRunStateWriter.ts +7 -0
  70. package/src/execution/NodeRunStateWriterFactory.ts +7 -0
  71. package/src/execution/WorkflowRunExecutionContextFactory.ts +3 -0
  72. package/src/execution/index.ts +2 -0
  73. package/src/index.ts +8 -0
  74. package/src/orchestration/AbortControllerFactory.ts +9 -0
  75. package/src/orchestration/NodeExecutionRequestHandlerService.ts +1 -0
  76. package/src/orchestration/RunContinuationService.ts +3 -0
  77. package/src/orchestration/RunStartService.ts +122 -3
  78. package/src/orchestration/TestSuiteOrchestrator.ts +350 -0
  79. package/src/orchestration/TestSuiteRunIdFactory.ts +11 -0
  80. package/src/orchestration/TriggerRuntimeService.ts +34 -7
  81. package/src/orchestration/index.ts +9 -0
  82. package/src/runtime/EngineFactory.ts +12 -0
  83. package/src/triggers/polling/PollingTriggerDedupWindow.ts +23 -0
  84. package/src/triggers/polling/PollingTriggerLogger.ts +18 -0
  85. package/src/triggers/polling/PollingTriggerRuntime.ts +122 -0
  86. package/src/triggers/polling/index.ts +5 -0
  87. package/src/types/index.ts +12 -9
  88. package/src/workflow/definition/NodeIterationIdFactory.ts +26 -0
  89. package/src/workflow/dsl/NodeIdSlugifier.ts +18 -0
  90. package/src/workflow/dsl/WorkflowBuilder.ts +71 -3
  91. package/src/workflow/dsl/WorkflowDefinitionError.ts +15 -0
  92. package/src/workflow/index.ts +3 -0
  93. package/dist/InMemoryRunEventBusRegistry-B0_C4OnP.cjs +0 -262
  94. package/dist/InMemoryRunEventBusRegistry-B0_C4OnP.cjs.map +0 -1
  95. package/dist/InMemoryRunEventBusRegistry-C2U83Hmv.js +0 -238
  96. package/dist/InMemoryRunEventBusRegistry-C2U83Hmv.js.map +0 -1
  97. package/dist/bootstrap-BoknFKnw.js.map +0 -1
  98. package/dist/bootstrap-Bx1u4cbS.cjs.map +0 -1
  99. package/dist/runtime-DUW6tIJ1.js.map +0 -1
  100. package/dist/runtime-Dvo2ru5A.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,391 @@
1
1
  # @codemation/core
2
2
 
3
+ ## 0.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#127](https://github.com/MadeRelevant/codemation/pull/127) [`1f10121`](https://github.com/MadeRelevant/codemation/commit/1f10121a093ef0612a33c873419b032709c9964d) Thanks [@cblokland90](https://github.com/cblokland90)! - Add regression test suite confirming `item.binary` slots survive SubWorkflow boundaries in both directions (parent→child and child→parent), including stream readback of bytes across run boundaries. Document the shared-BinaryStorage pattern required for tests that call `ctx.binary.attach`.
8
+
9
+ ## 0.10.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#119](https://github.com/MadeRelevant/codemation/pull/119) [`847deb4`](https://github.com/MadeRelevant/codemation/commit/847deb4c42801632bfb970cdb2625cd0755241cb) Thanks [@cblokland90](https://github.com/cblokland90)! - Reset source version line back to 0.x. Earlier releases prematurely jumped these packages to 1.x and 2.x via silent `major` changesets buried under unrelated work; the framework is still in beta. The npm versions 1.x and 2.0.0 are deprecated upstream — consume the 0.x line going forward.
14
+ - `@codemation/core` 2.0.0 → 0.9.0 (continues from 0.8.1)
15
+ - `@codemation/core-nodes` 1.1.0 → 0.5.0 (continues from 0.4.3)
16
+ - `@codemation/host` 1.1.0 → 0.4.0 (continues from 0.3.1)
17
+
18
+ `@codemation/agent-skills`, `create-codemation`, `@codemation/cli`, and `@codemation/core-nodes-msgraph` already track 0.x and are unaffected.
19
+
20
+ `create-codemation` template dependency ranges updated from `1.x` to `0.x` to track the corrected line.
21
+
22
+ ## 2.0.0
23
+
24
+ ### Major Changes
25
+
26
+ - [#102](https://github.com/MadeRelevant/codemation/pull/102) [`6566d55`](https://github.com/MadeRelevant/codemation/commit/6566d55c829f6631357ac95052b0852e86092ac5) Thanks [@cblokland90](https://github.com/cblokland90)! - **Breaking change:** Default node ids in `WorkflowBuilder` now derive from a slug of the node's label (`config.name`) instead of a sequential counter (`${tokenName}:${seq}`).
27
+
28
+ Previously, adding or reordering nodes changed their auto-assigned ids, silently orphaning credential bindings stored in the database (keyed by `workflowId + nodeId + slotKey`). The new scheme makes ids stable across reorders and inserts.
29
+
30
+ **Migration required:** Any existing credential bindings keyed by the old `${tokenName}:${seq}` format will appear unbound after this change. Users must re-bind credentials manually in the workflow editor. To avoid disruption, add an explicit `id:` field to node configs before upgrading — explicit ids are unaffected by this change and take priority over the label slug.
31
+
32
+ **Validation added:** `WorkflowBuilder.build()` now throws `WorkflowDefinitionError` if any node has an empty effective id (blank label + no explicit id) or if two nodes share the same effective id. Fix: provide a unique `id:` on the offending node configs.
33
+
34
+ ### Minor Changes
35
+
36
+ - [#111](https://github.com/MadeRelevant/codemation/pull/111) [`a77505f`](https://github.com/MadeRelevant/codemation/commit/a77505f331d7d3892f3c1c8f19dc37952b4d96bd) Thanks [@cblokland90](https://github.com/cblokland90)! - Add `definePollingTrigger` helper for declarative polling trigger authoring.
37
+
38
+ Plugin authors can now define polling triggers with a single `definePollingTrigger({...})` call instead of manually wiring `PollingTriggerRuntime` + `RunnableNodeConfig` + `@node` class pairs. The helper synthesises both the trigger config class and the runtime adapter, handles internal dedup-key bookkeeping, and exposes a `poll()` test seam for unit testing without spinning up the runtime.
39
+
40
+ - [#101](https://github.com/MadeRelevant/codemation/pull/101) [`2c0723f`](https://github.com/MadeRelevant/codemation/commit/2c0723fb1670e842c272939b5db73d4b95b25535) Thanks [@cblokland90](https://github.com/cblokland90)! - Add collections: declare typed Postgres/SQLite-backed data tables in the codemation config via `defineCollection({...})`. Schema sync runs at runtime startup behind an advisory lock (Postgres) or in-process mutex (SQLite).
41
+
42
+ Workflow access:
43
+ - `ctx.collections.<name>.crud(...)` from inside custom node code
44
+ - Six new canvas nodes: `CollectionInsert`, `CollectionGet`, `CollectionFindOne`, `CollectionList`, `CollectionUpdate`, `CollectionDelete`
45
+
46
+ Operator surfaces:
47
+ - HTTP API at `/collections/*`
48
+ - CLI: `codemation collections list|show|rows|get|insert|update|delete|sync`
49
+ - UI at `/collections`
50
+
51
+ Destructive schema changes (column drops, type changes) require `CODEMATION_COLLECTIONS_ALLOW_DESTRUCTIVE=1`.
52
+
53
+ Out of scope (separate PRs):
54
+ - Real leader election (advisory lock at boot is sufficient for sync; trigger double-firing during container swap is unaddressed)
55
+ - Admin-role gating on the UI
56
+ - Runtime user-defined schemas (Airtable-style)
57
+ - Joins, aggregates, query DSL beyond indexed-field equality
58
+
59
+ - [#109](https://github.com/MadeRelevant/codemation/pull/109) [`fb9f7fe`](https://github.com/MadeRelevant/codemation/commit/fb9f7fed9bf5a3d6b0c5f78a30027be3ab7bcaca) Thanks [@cblokland90](https://github.com/cblokland90)! - OAuth2 plugin authors can now declare `authorizeUrl` / `tokenUrl` (with `{publicFieldKey}` template substitution) directly on a credential type's `auth` definition — no core change required to add a new provider. Migrated `@codemation/core-nodes-msgraph` to use this for Microsoft tenant-templated URLs (fixes "Unsupported OAuth2 provider id: microsoft" on connect).
60
+
61
+ Removed dead `@codemation/core-nodes-gmail` devDep from `@codemation/host` and the matching `serverExternalPackages` entry from `@codemation/next-host` so plugin-author `pnpm dev` no longer rebuilds gmail when working on an unrelated plugin.
62
+
63
+ Softened the credentials UI's "Not set in host env: …" message: it's now an informational tip with neutral styling (was destructive/error styling), since the field works perfectly fine when filled in manually.
64
+
65
+ - [#101](https://github.com/MadeRelevant/codemation/pull/101) [`2c0723f`](https://github.com/MadeRelevant/codemation/commit/2c0723fb1670e842c272939b5db73d4b95b25535) Thanks [@cblokland90](https://github.com/cblokland90)! - Add collections authoring API: `defineCollection()` for declaring typed data tables, `DefinedCollectionRegistry` for registration, and `CollectionStore` contract for runtime access. Includes column DSL (`c.text()`, `c.int()`, etc.) with validation for field/index names and reserved fields.
66
+
67
+ - [#108](https://github.com/MadeRelevant/codemation/pull/108) [`781c146`](https://github.com/MadeRelevant/codemation/commit/781c146eb9d8bb8bdbc1963ea2a4b9abe4b7bfbf) Thanks [@cblokland90](https://github.com/cblokland90)! - Extract generic polling-trigger machinery from gmail into core and expose it via setup context.
68
+
69
+ **`@codemation/core`** — new polling-trigger API
70
+ - New `PollingTriggerRuntime` class: owns the set-interval loop, overlap guard, and state persistence via `TriggerSetupStateRepository`. Plugin authors no longer need to implement these themselves.
71
+ - New `PollingTriggerDedupWindow` class: merges processed-ID sets with a configurable cap (default 2000). Prevents unbounded memory growth across polling cycles.
72
+ - New `PollingTriggerHandle` interface exposed on `TriggerSetupContext.polling`: pre-binds trigger id, emit, and registerCleanup so plugin code only supplies `intervalMs` and `runCycle`. The handle also carries a `.dedup` reference for message-level deduplication.
73
+ - `EngineDeps.pollingTriggerLogger` optional field: hosts may wire a real logger; defaults to a no-op.
74
+ - `PollingTriggerRuntime`, `PollingTriggerDedupWindow`, and `NoOpPollingTriggerLogger` are exported from the main `@codemation/core` barrel.
75
+ - ESLint `allowedConstructorNames` extended to include `AbortController` (a global built-in, not a DI-managed class).
76
+
77
+ **`@codemation/core-nodes-gmail`** — internal refactor, no external API change
78
+ - `GmailPollingTriggerRuntime` deleted; loop/overlap-guard/persistence now come from the core runtime.
79
+ - `GmailPollingService.poll` renamed to `runCycle`; repo injection and `persist()` method removed; dedup delegated to `PollingTriggerDedupWindow`.
80
+ - `OnNewGmailTriggerNode.setup` now calls `ctx.polling.start(...)` instead of `gmailPollingTriggerRuntime.ensureStarted(...)`.
81
+ - `GmailNodeTokens.RuntimeLogger` token removed (no longer needed).
82
+
83
+ - [#100](https://github.com/MadeRelevant/codemation/pull/100) [`11616ae`](https://github.com/MadeRelevant/codemation/commit/11616aefb91d4b96b7eb9af4b935eec055a8a7bb) Thanks [@cblokland90](https://github.com/cblokland90)! - Foundation for first-class **workflow testing**: a TestTrigger node, an IsTestRun branching node, an Assertion node, a `TestSuiteOrchestrator` service that fans one workflow run per yielded fixture item, host-side persistence (Prisma `TestSuiteRun` + `TestAssertion` tables, repositories, `TestRunnerService`), and a per-suite event tracker that records assertions and node coverage. HTTP routes and the canvas Tests tab (next-host) ship in follow-up slices.
84
+
85
+ **What this slice adds**
86
+ - **`@codemation/core` — additive contract changes**
87
+ - `RunExecutionOptions.testContext?: { testSuiteRunId; testCaseIndex }` — set by the orchestrator on each test-case run; threaded through `ExecutionContext` so nodes can read it as `ctx.testContext`. Propagates to subworkflow runs via `ParentExecutionRef.testContext` + `EngineExecutionLimitsPolicy.mergeExecutionOptionsForNewRun`, so assertions emitted by subworkflows land under the correct parent test case.
88
+ - `TriggerNodeConfig.triggerKind?: "live" | "test"` — `"test"` triggers are skipped by `TriggerRuntimeService` (live activation, webhooks, polling) and are only invoked by the orchestrator.
89
+ - `NodeConfigBase.emitsAssertions?: true` — marker the host-side `TestAssertionPersister` (next slice) keys off when subscribing to `nodeCompleted`.
90
+ - New `AssertionResult` type (`pass | fail | error`, plus `score`, `expected`, `actual`, `message`, `details`) — the stable shape every assertion node emits on `main`.
91
+ - New `TestTriggerNodeConfig` + `TestTriggerSetupContext` — author callback signature returns `AsyncIterable<Item>` and exposes credential resolution + an `AbortSignal`.
92
+ - New `RunEvent` kinds: `testSuiteStarted`, `testCaseStarted`, `testCaseCompleted`, `testSuiteFinished` (with terminal status `succeeded | failed | partial | cancelled | errored`).
93
+ - New `TestSuiteOrchestrator` service in `orchestration/` — drives the iterator, applies a per-suite concurrency semaphore (default 4), dispatches one `engine.runWorkflow(...)` per item with `executionOptions.testContext` set, awaits terminal status, and publishes lifecycle events on the existing `RunEventBus`. No persistence, no HTTP — pure engine logic so tests can drive it via in-memory deps.
94
+ - `TestSuiteRunIdFactory`, `AbortControllerFactory` — DI-friendly minters used by the orchestrator.
95
+ - **`@codemation/core-nodes` — three new nodes**
96
+ - **`TestTrigger`** / `TestTriggerNode`: drop on the canvas alongside live triggers. `setup` is a no-op; `execute` is a passthrough. The author's `generateItems` is consumed by the orchestrator.
97
+ - **`IsTestRun`** / `IsTestRunNode`: per-item router with `true` / `false` ports. Routes to `true` iff `ctx.testContext` is set — lets workflows skip real side-effects in test runs (e.g. don't actually send the reply).
98
+ - **`Assertion`** / `AssertionNode`: generic callback-style assertion node. Author returns `Promise<AssertionResult[]>` per item; the node emits one workflow `Item` per result. Sets `emitsAssertions: true` so the host persister can identify it.
99
+ - Declarative shorthands (`StringEqualsAssertionNode`, `JudgeByAgentAssertionNode`) intentionally deferred — the generic callback node covers Phase 1 and the declarative variants compose on top.
100
+ - **`@codemation/host` — persistence + orchestration + HTTP**
101
+ - **Prisma schema**: new `TestSuiteRun` and `TestAssertion` tables in both Postgres and SQLite mirrors. Adds `Run.testSuiteRunId` (FK with `ON DELETE SET NULL`) and `Run.testCaseIndex` (indexed for join + ordering). Workflow definition itself is **not** FK'd — workflows live in code; `TestSuiteRun.triggerNodeName` is snapshotted at creation so historical viewing survives node renames/deletions.
102
+ - **`TestSuiteRunRepository`** + **`TestAssertionRepository`** domain interfaces with Prisma + in-memory adapters.
103
+ - **`TestRunnerService`** (host application layer) — single facade for "start a test suite": creates the persistence row, drives the orchestrator, awaits, finalizes counts + coverage. Subscribes to `RunEventBus.subscribeToWorkflow` only for the lifetime of one suite (no global subscriber, no shared mutable state across concurrent suites).
104
+ - **`TestSuiteRunTracker`** + **`TestSuiteRunTrackerFactory`** — per-suite event accumulator. Two-stage event buffering tolerates inline runners that emit `nodeCompleted` synchronously inside `runWorkflow` (before the orchestrator publishes `testCaseStarted`); without it, fast/in-memory engines drop assertions silently.
105
+ - **`AssertionResultGuard`** — type-guard the tracker uses to skip junk output if a misconfigured `emitsAssertions: true` node emits non-assertion items (defensive, not crash-on-bad-input).
106
+ - **HTTP routes** (Hono, all behind the existing session-verifier middleware):
107
+ - `POST /api/workflows/:workflowId/test-suite-runs` body `{ triggerNodeId, concurrency? }` → 201 with `{ testSuiteRunId, status, totalCases, passedCases, failedCases }`
108
+ - `GET /api/workflows/:workflowId/test-suite-runs` → list summaries
109
+ - `GET /api/test-suite-runs/:id` → detail (including `concurrency`, `nodeCoverage`, `errorMessage`)
110
+ - `GET /api/test-suite-runs/:id/assertions` → all assertions across the suite's child runs
111
+ - `GET /api/runs/:runId/assertions` → assertions for one child run
112
+ - Paths exposed through `ApiPaths.workflowTestSuiteRuns/testSuiteRun/testSuiteRunAssertions/runAssertions` so the next-host React Query layer can call them by helper instead of string literals.
113
+ - **DI bootstrap** in `AppContainerFactory`: registers all new singletons (factories, mappers, guard, repository selector, route handler + registrar) and wires Prisma vs in-memory `TestSuiteRunRepository` / `TestAssertionRepository` based on `appConfig.persistence.kind` (mirroring the existing `WorkflowRunRepository` selection). `TestSuiteOrchestrator` itself is registered via a tsyringe factory that injects `Engine` + the engine-side `RunEventBus` + a fresh `CredentialResolverFactory(CredentialSessionService)`.
114
+ - **DTOs** in `application/contracts/TestingContracts.ts`: `StartTestSuiteRunRequest/Response`, `TestSuiteRunSummaryDto`, `TestSuiteRunDetailDto`, `TestAssertionDto`. Mappers (`TestSuiteRunSummaryMapper`, `TestAssertionMapper`) translate persistence records → wire shape.
115
+ - **WebSocket / event narrowing** — `WorkflowWebsocketServer` and one integration test reader updated to type-narrow on the new test-suite event kinds (which carry `testSuiteRunId` rather than `runId`).
116
+
117
+ **Tests**
118
+ - `TestSuiteOrchestrator` unit suite (6 tests): per-item dispatch with `testContext`, partial-pass aggregation, lifecycle event emission, concurrency cap, `errored` status when `generateItems` throws, rejection of non-test triggers.
119
+ - Node unit suite (6 tests): TestTrigger passthrough + `triggerKind === "test"`, IsTestRun routing on both branches, AssertionNode emitting one item per result, `emitsAssertions === true`.
120
+ - `TestRunnerService` integration suite (2 tests): creates the persistence row, finalizes counts + coverage, persists 3 `TestAssertion` rows from a 2-case suite (one passing, one failing); rejects non-test triggers without leaving a phantom row.
121
+ - **`@codemation/next-host` — Tests tab UI**
122
+ - **Third canvas tab** ("Tests") next to Live workflow / Executions, mutually exclusive with both. Local React state for now (Phase 1) — promotion to the URL codec is a Phase 2 cleanup once the UX is settled.
123
+ - **`TestsPanel`** — top-level container with a trigger picker (shadcn `Select` populated from workflow nodes whose `triggerKind === "test"`), a "Run tests" CTA wired through `useStartTestSuiteRunMutation`, a left list of past suite runs, and a right detail panel.
124
+ - **`TestSuitePassRateChart`** — recharts line chart of pass rate over time across this workflow's suite runs. Carries an explicit `rolling-input` label so authors don't read trends as agent regressions when the underlying fixtures drift (Phase 2 ships snapshots).
125
+ - **`TestSuiteRunsList`** + **`TestSuiteRunStatusBadge`** — list rows + colored status badges (`running` / `succeeded` / `partial` / `failed` / `cancelled` / `errored`).
126
+ - **`TestSuiteRunDetailPanel`** — header with pass-rate + counts + concurrency + nodes-covered + (when set) an `errorMessage` callout; the body is a per-run grouped assertions list.
127
+ - **`TestAssertionsList`** + **`TestAssertionRow`** — each assertion shows status badge, optional score, optional `expected`/`actual` JSON viewers side-by-side.
128
+ - **React Query hooks** (`testSuiteHooks.ts`) cover all four GET endpoints plus the start mutation, with cache invalidation on `workflowTestSuiteRunsQueryKey` after a successful run.
129
+ - **WorkflowNodeDto** + **mapper additions** (host + next-host's `PersistedWorkflowSnapshotMapper`) propagate `triggerKind` to the wire shape so the Tests panel can identify test triggers without server round-trips. Both mappers default omitted values to `"live"` to keep the wire DTO consistent.
130
+
131
+ **Not in this slice (planned follow-ups)**
132
+ - Test-input snapshots (Phase 2 — Phase 1 inputs are always live; UI carries a "rolling-input" label so charts aren't read as agent regressions).
133
+ - Declarative assertion family (StringEquals, JsonPath, JudgeByAgent helpers — generic callback `Assertion` covers Phase 1).
134
+ - Cancellation endpoint (`POST /api/test-suite-runs/:id/cancel`) — orchestrator already supports `AbortSignal` cancellation; the HTTP surface for it is deferred until the UI surfaces it.
135
+ - Realtime updates on the Tests panel — currently the suite list refetches on mutation success; live `testSuite*` events arrive via the existing realtime bridge but the Tests panel doesn't subscribe yet.
136
+ - URL codec entry for `pane=tests` so suite drilldowns are deep-linkable (currently in-memory React state).
137
+ - Coverage heatmap overlay on the canvas itself.
138
+
139
+ The contract additions are **strictly additive**; no existing API surface changed shape.
140
+
141
+ ### Patch Changes
142
+
143
+ - [#110](https://github.com/MadeRelevant/codemation/pull/110) [`4902978`](https://github.com/MadeRelevant/codemation/commit/49029782243ece59ab6aa5bb46396db445cad47c) Thanks [@cblokland90](https://github.com/cblokland90)! - Add per-package `test:unit` scripts so Turbo can address each package individually for affected-only filtering. No runtime changes — dev-tooling only.
144
+
145
+ - [#100](https://github.com/MadeRelevant/codemation/pull/100) [`11616ae`](https://github.com/MadeRelevant/codemation/commit/11616aefb91d4b96b7eb9af4b935eec055a8a7bb) Thanks [@cblokland90](https://github.com/cblokland90)! - Major dev-server startup-time and bundle-size improvements, plus dev-CLI hardening.
146
+
147
+ **Why this matters**
148
+
149
+ Before this work, opening the workflow detail page on a 4-cpu / 8-GB WSL box would
150
+ OOM-kill `next-server` mid-compile (~5 GB peak RSS). After: the page cold-compiles in
151
+ **5.5 s** with peak **1.8 GB** and the dev server stays comfortably alive. The dev CLI
152
+ also boots significantly faster and survives consumer-source errors without tearing
153
+ the whole session down.
154
+
155
+ **Hard numbers**
156
+ - Workflow page Turbopack RSS peak: **5.0 GB → 1.8 GB** (-64%)
157
+ - Workflow page cold compile time: **~14 s → ~5.5 s**
158
+ - Lucide-react files in workflow page bundle: **1,713 → 74** (-95.7%)
159
+ - Host package typecheck: **17.5 s / 4,093 files / 2.1 GB → 8.8 s / 2,806 files / 1.9 GB**
160
+ - Host source tree: **-112,492 lines** of generated Prisma `.d.ts`
161
+ - Host circular dep cycles: **92 → 21**
162
+ - Core circular dep cycles: **53 → 50**
163
+
164
+ **`@codemation/next-host`**
165
+ - New `WorkflowCanvasLucideIconRegistry` — curated 18-icon set used by core node plugins.
166
+ Replaces `lucide-react/dynamic` (which forced bundling of all 1,713 icons because it
167
+ loads them by string at runtime). Workflows using `icon: "lucide:<unknown>"` now fall
168
+ back to the `Boxes` icon and emit a one-time `console.warn`. **Plugin authors needing
169
+ custom icons must ship SVG via `builtin:` / `si:` / URL tokens.**
170
+ - New slim subpath exports on `@codemation/host`: **`@codemation/host/dto`**,
171
+ **`@codemation/host/mapping`**, plus extensions to **`@codemation/host/client`**.
172
+ All 65 deep `@codemation/host-src/*` imports replaced; `@codemation/host-src/*`
173
+ tsconfig path removed. Prevents the UI from dragging the heavy host runtime graph
174
+ through Turbopack on every UI route compile.
175
+ - 42 lucide-react named imports rewritten to per-icon deep imports
176
+ (`lucide-react/dist/esm/icons/<kebab>`).
177
+ - Workflow detail page lazy-loads `WorkflowDetailScreenTestsView` and the
178
+ Monaco-backed `WorkflowJsonEditorDialog`.
179
+ - Removed `@codemation/core` and `@codemation/host` from `transpilePackages` and
180
+ dropped the corresponding root-barrel tsconfig paths so Next loads them from
181
+ compiled `dist/` instead of TypeScript source.
182
+ - Dev: `EdgeSessionVerifier` resolves `/api/auth/session` via
183
+ `x-forwarded-host` (the dev gateway) instead of `request.nextUrl.origin` (Next's
184
+ loopback). Previously the auth-check fetch looped back into Next, forcing
185
+ Turbopack to compile the catch-all `/api/[[...path]]` route on every page load.
186
+
187
+ **`@codemation/host`**
188
+ - Generated Prisma clients (`prisma-client`, `prisma-postgresql-client`,
189
+ `prisma-sqlite-client`) moved out of `src/infrastructure/persistence/generated/`
190
+ to `prisma-generated/` (sibling of `src/`). They're still typechecked and bundled
191
+ by the host build, but no longer pollute the public source surface that downstream
192
+ packages walk.
193
+ - New **`@codemation/host/dto`**, **`@codemation/host/mapping`** subpath exports
194
+ re-exposing only the contract DTO types and presentation factories the UI needs.
195
+ The existing **`@codemation/host/client`** subpath gained `ApiPaths`,
196
+ `BrowserLoggerFactory`, `logLevelPolicyFactory`, `InAppCallbackUrlPolicy`, and
197
+ `Logger` so the UI no longer needs deep imports.
198
+
199
+ **`@codemation/core`**
200
+ - New **`@codemation/core/contracts`** subpath — re-exports only pure-type contracts
201
+ (`assertionTypes`, `runTypes`, `workflowTypes`, etc.) using `export type *`. Type-only
202
+ consumers can import from here to avoid dragging the workflow DSL runtime into their
203
+ compile graph. Existing `@codemation/core` (root barrel) is unchanged for backwards
204
+ compatibility.
205
+ - Extracted `core/src/contracts/baseTypes.ts` (six fundamental id types) to break a
206
+ long-standing `credentialTypes ↔ workflowTypes` cycle.
207
+
208
+ **`@codemation/cli` — dev-CLI hardening**
209
+ - **`DevHttpProbe`**: TCP-listener probe replaces the HTTP-response probe, so a slow
210
+ Next dev cold compile no longer SIGTERMs the dev tree.
211
+ - **Single-runtime swap** in `runQueuedRebuild`: stops the old in-process runtime
212
+ before creating the new one, freeing ~1.5 GB during dev source-changes. Consumer
213
+ errors are now non-fatal — the gateway returns 503 and the dev session stays up
214
+ until the next save fixes the build.
215
+ - **Workspace-plugin watch is now opt-in** via `CODEMATION_DEV_WATCH_PLUGINS=true`.
216
+ By default `pnpm dev` no longer spawns `tsdown --watch` for each workspace plugin
217
+ (saves ~500 MB baseline + the rebuild-loop pressure). Plugins still load from
218
+ their existing `dist/` output; opt in only when actively editing a plugin's source.
219
+ - **`DevSourceWatcher`**: 75 ms → 750 ms debounce so a single `tsdown` rebuild collapses
220
+ into one runtime swap. Defense-in-depth ignore re-check at the event handler (chokidar
221
+ doesn't always re-evaluate `ignored` for files created post-start). 20 s startup grace
222
+ period to drop initial-build noise.
223
+ - **Workspace plugin watch root** narrowed from `dist/` to the plugin's entry file —
224
+ tsdown rewrites the entry once per real build, so one watch event per build instead of
225
+ a dozen.
226
+ - Removed `--conditions=development` from the Next-host's `NODE_OPTIONS`. Previously
227
+ this resolved `@codemation/{core,host}` to TypeScript source; combined with
228
+ `transpilePackages` it forced Turbopack to walk the full source tree on every
229
+ UI route compile.
230
+
231
+ **Architectural guard rails (no behavior change, prevent regressions)**
232
+ - ESLint `no-restricted-imports` blocks `@codemation/host-src/*` and root
233
+ `@codemation/host` from `next-host` UI; blocks `prisma-generated/*` outside host's
234
+ persistence layer.
235
+ - New **`dependency-cruiser`** config + `pnpm depcruise` script.
236
+ - New **`knip`** config + `pnpm lint:knip` script.
237
+ - New `tooling/scripts/check-circular-deps.mjs` + `pnpm lint:circular` wired into
238
+ `pnpm lint` with frozen baselines (core: 50, host: 21, core-nodes: 73).
239
+ - **`@next/bundle-analyzer`** wired up; `pnpm analyze` available for on-demand
240
+ inspection (uses `next experimental-analyze` for Turbopack-mode introspection).
241
+ - New `AGENTS.md` "Cross-package imports" section documenting the slim-subpath
242
+ discipline and the rationale for it.
243
+
244
+ The contract additions are strictly additive; no existing API surface changed shape.
245
+
246
+ - [#112](https://github.com/MadeRelevant/codemation/pull/112) [`6fc7d3f`](https://github.com/MadeRelevant/codemation/commit/6fc7d3fe95f8d88386c16971fffa8dd3faa7704f) Thanks [@cblokland90](https://github.com/cblokland90)! - Surface workflow-planning errors in the node inspector instead of swallowing them as `[codemation-http] unhandled route error`. When `NodeInstanceFactory` fails to instantiate a node (e.g. tsyringe `TypeInfo not known` for a constructor param), the offending `nodeId` is preserved via a new `NodeInstantiationError`, and `RunStartService` now persists a failed run with the error attached to that node — same shape execution errors already use, so the UI shows `name`/`message`/`stack` in the node "output" panel.
247
+
248
+ - [#100](https://github.com/MadeRelevant/codemation/pull/100) [`11616ae`](https://github.com/MadeRelevant/codemation/commit/11616aefb91d4b96b7eb9af4b935eec055a8a7bb) Thanks [@cblokland90](https://github.com/cblokland90)! - Workflow Testing UI polish and end-to-end correctness fixes.
249
+
250
+ **`@codemation/next-host`** — Tests UI
251
+ - Fix `Maximum update depth exceeded` on the Tests panel. The trends chart was receiving a
252
+ fresh `[]` reference per render (`?? []` inline) which made recharts' internal effects loop;
253
+ every `?? EMPTY_*` fallback the chart consumes is now a module-scoped stable reference.
254
+ - Fix the same loop class on the canvas-play-dropdown → Tests path. The auto-start `useEffect`
255
+ had `startMutation` (a react-query mutation result, unstable per render) in its deps array,
256
+ which re-fired the mutation on every render. Now uses a ref keyed on `autoStartTriggerNodeId`
257
+ with explicit reset when the prop clears.
258
+ - Fix the canvas inspector showing `{ "json": {...} }` for historical / test-suite child runs.
259
+ `WorkflowDetailPresenter.jsonValueToMainItems` was wrapping every array entry as
260
+ `{ json: <entry> }`, but trigger outputs are persisted **already-Item-shaped**, producing
261
+ `{json: {json: {...}}}`. Detects already-Item entries and passes them through.
262
+ - Surface assertion-rollup-corrected status on the executions list. New `RunSummary.testCaseStatus`
263
+ is preferred over engine `status` so a test-case run whose assertions failed shows as
264
+ **failed** instead of "completed" (engine status is unchanged — only the UI display).
265
+ - Tabs no longer overlap the test-cases detail panel — moved from absolute positioning to a flow
266
+ header in the Tests view.
267
+ - Filter strip above the case tree-table: All / Passing / Failing / Errored / In flight, with
268
+ live counts. Empty buckets are disabled so users can't filter into a confusing empty state.
269
+ - Collapse all / Expand all controls on the case tree-table; expansion state lifted from
270
+ per-row `useState` to the table so broadcasts work. Auto-open-on-failure heuristic still fires
271
+ per-row but only the first time each run id appears, so a row the user explicitly collapsed
272
+ stays collapsed when realtime updates stream in.
273
+ - Trend chart x-axis is now numeric `idx` with subsampled ticks (~5 evenly-spaced labels) and
274
+ time-aware formatting (`HH:MM` when all runs share a day, `M/D HH:MM` across days).
275
+ - Status icon expanded to cover the full case-status union (`succeeded` / `failed` / `errored` /
276
+ `cancelled` / `running` / `queued`) with distinct icons and colors.
277
+
278
+ **`@codemation/host`** — Testing framework correctness
279
+ - Fix `TestSuiteRunTracker` race that left the last test case stuck on `testCaseStatus="running"`
280
+ and the suite counters off by one. The bus dispatched events fire-and-forget; `finalize` ran
281
+ before in-flight handlers wrote their `updateTestCaseStatus` calls. Tracker now serializes
282
+ events through a `processingTail` chain and `finalize` awaits it before reading
283
+ `listChildRuns`.
284
+ - Initialize `Run.testCaseStatus` to `"running"` at row creation when `executionOptions.testContext`
285
+ is present. Previously the tracker's `persistCaseStarted` raced the engine inserting the row
286
+ and silently swallowed P2025 — the suite-detail page never showed a "running" transition.
287
+ - `TestSuiteChildRunDto` exposes the new `testCaseStatus?: TestCaseRunStatus` field; mapper
288
+ narrows the persistence string through a known-statuses guard.
289
+ - `PrismaWorkflowRunRepository.listRuns` threads `testCaseStatus` into `RunSummary` so the
290
+ executions list can render the corrected outcome.
291
+
292
+ **`@codemation/core`**
293
+ - `RunSummary` gains an optional `testCaseStatus?: TestCaseRunStatus`. Additive, non-breaking.
294
+
295
+ **Dev experience**
296
+ - `pnpm dev` (root) now runs `tsdown --watch` for `@codemation/host` alongside `test-dev` under
297
+ `concurrently`, so host source edits rebuild `dist/` automatically. Without this, host changes
298
+ were invisible to the running Next dev server (which deliberately resolves host from `dist/`
299
+ to keep Turbopack memory bounded on 8 GB WSL boxes), forcing a manual
300
+ `pnpm --filter @codemation/host build` after every host edit.
301
+
302
+ **Documentation**
303
+ - Top-level `docs/workflow-testing.md` and the `codemation-workflow-dsl` skill reference
304
+ rewritten for the score-based assertion model (`score: 0..1` + `passThreshold?` + `errored?`),
305
+ with examples for boolean assertions, continuous metrics, and judge-by-agent assertions.
306
+
307
+ **Tests**
308
+ - New HTTP-driven e2e suite (`packages/host/test/e2e/testSuiteRunHttpFlow.e2e.test.ts`) drives
309
+ the full real-orchestrator + real-Prisma + real-engine lifecycle through `POST` →
310
+ `GET /api/test-suite-runs/:id` → child runs → assertions, asserting the partial-suite
311
+ outcome with assertion-rollup downgrade.
312
+ - New unit tests cover the case-status filter engine, the historical-run double-wrap regression,
313
+ and the chart prop-stability regression class.
314
+
315
+ ## 1.0.1
316
+
317
+ ### Patch Changes
318
+
319
+ - [`ed75183`](https://github.com/MadeRelevant/codemation/commit/ed75183f51ae71b06aa2e57ae4fc48ce9db2e4ce) - Establish "per Item per Call" identity end-to-end so the workflow run inspector reports, visualizes, and dashboards multi-item AI agents correctly.
320
+
321
+ Previously, an orchestrator agent that processed N items emitted one flat list of LLM rounds and tool calls — the bottom execution tree, the right-panel agent timeline, cost dashboards, and the realtime event stream all collapsed iterations into one bucket, making sub-agent fan-outs (and parallel item processing in general) unreadable.
322
+
323
+ **What changed**
324
+ - **Engine** (`@codemation/core`): `NodeExecutor` mints a `NodeIterationId` per item inside per-item runnable activations and stamps it (with `itemIndex`) onto `NodeExecutionContext`. Connection invocations, telemetry spans (`gen_ai.chat.completion`, `agent.tool.call`), metric points (`codemation.cost.estimated`, `codemation.agent.turns`, `codemation.agent.tool_calls`), and run events all carry the per-item identity. New `ChildExecutionScopeFactory` re-roots `NodeExecutionContext` for sub-agents so credentials and iteration ids resolve correctly across the orchestrator → tool → sub-agent boundary.
325
+ - **Sub-agent credentials** (`@codemation/core-nodes`): `NodeBackedToolRuntime.resolveNodeCtx` no longer re-wraps `args.ctx.nodeId` with `ConnectionNodeIdFactory.toolConnectionNodeId` — the caller already pre-wraps it. The previous double-nesting produced exponentially deep node ids (`AIAgentNode:2__conn__tool__conn__searchInMail__conn__tool__conn__searchInMail__conn__llm`) that didn't match user-bound credential slots. Sub-agent OpenAI / API-key slots resolve again.
326
+ - **Realtime events**: new `connectionInvocationStarted` / `connectionInvocationCompleted` / `connectionInvocationFailed` events carry the full `ConnectionInvocationRecord` (incl. `iterationId`, `itemIndex`, `parentInvocationId`) and surgical reducers update the run cache without waiting for a coarse `runSaved` snapshot. Run-query polling dropped from 250 ms → 5 s now that WebSocket events drive most updates.
327
+ - **Persistence** (`@codemation/host`): Prisma `ExecutionInstance` model gains `iteration_id`, `item_index`, `parent_invocation_id` columns + index (sqlite + postgres migrations); `PrismaWorkflowRunRepository` round-trips them on read/save and via `ExecutionInstanceDto`. Without this the cold reload of a finished run silently flattens the per-item tree because `runSaved` events stream through Prisma. Telemetry tables already carried these columns from Phase 4; both sides now agree.
328
+ - **Iteration projection / cost queries** (`@codemation/host`): new `RunIterationProjectionFactory` projects `RunIterationRecord`s from connection invocations + iteration cost metrics and `GetIterationCostQueryHandler` serves per-iteration cost rollups for dashboards.
329
+ - **Inspector view model** (`@codemation/next-host`): `NodeInspectorTelemetryPresenter` groups LLM and tool spans by `iterationId` into "Item N" accordion entries (single-item agents fall back to flat layout). New `FocusedInvocationModelFactory` powers item-level prev/next navigation when a specific invocation is selected — the breadcrumb shows "Item X of Y" and nav targets the first invocation of adjacent items. Tool spans now interleave chronologically with LLM rounds (request → tools → response) instead of LLM rounds first then orphan tools at the bottom.
330
+ - **Bottom execution tree** (`@codemation/next-host`): new `ExecutionTreeItemGroupInjector` injects synthetic "Item N" parent rows between an agent and its connection invocations when the agent processed 2+ items. Single-item activations are left untouched; sub-agent invocations whose `parentInvocationId` already points at a tool-call row stay nested under the orchestrator's specific tool call.
331
+ - **Sub-agent credential boundary**: `ChildExecutionScopeFactory.forSubAgent` ensures sub-agent `NodeExecutionContext` keeps the parent invocation id and span context intact so trace nesting and credential resolution agree on the connection-node id.
332
+ - **Tests**: new unit + UI suites for each layer (sub-agent scope, item-group injector, focused invocation model, agent timeline per-item grouping, chronological ordering, Prisma iterationId round trip, item-aware properties panel, connection-invocation event publisher) and a runnable `apps/test-dev` sample (`agentSubAgentToolFanout`) that exercises the orchestrator → sub-agent fan-out across 2 items end-to-end.
333
+
334
+ ## 1.0.0
335
+
336
+ ### Major Changes
337
+
338
+ - [#93](https://github.com/MadeRelevant/codemation/pull/93) [`640e303`](https://github.com/MadeRelevant/codemation/commit/640e3032b1386568df725980a27761b6e230302c) Thanks [@cblokland90](https://github.com/cblokland90)! - Replace LangChain with the Vercel AI SDK for all AIAgent flows.
339
+
340
+ Codemation no longer depends on `@langchain/core` or `@langchain/openai`. Chat model providers, the turn loop, structured output, and tool calls now run on top of the Vercel **AI SDK** (`ai`, `@ai-sdk/openai`, `@ai-sdk/provider`). Custom Codemation behaviors that LangChain did not cover — the **tool-args repair loop**, the **structured-output repair loop**, **connection-invocation tracking**, and our **telemetry / cost-tracking spans** — are preserved and built on top of the new primitives.
341
+
342
+ ### Dependency changes
343
+ - **Removed**: `@langchain/core`, `@langchain/openai` (from `@codemation/core-nodes`).
344
+ - **Added**: `ai` `^6.0.168`, `@ai-sdk/openai` `^3.0.53`, `@ai-sdk/provider` `^3.0.8` (to `@codemation/core-nodes`). `@codemation/host` picks up `ai` + `@ai-sdk/provider` for its test harness only.
345
+
346
+ ### Public API renames (`@codemation/core`)
347
+
348
+ | Before | After |
349
+ | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
350
+ | `LangChainChatModelLike` | `ChatLanguageModel` |
351
+ | `LangChainStructuredOutputModelLike` | _(removed — replaced by `StructuredOutputOptions` + `generateText({ experimental_output: Output.object(...) })`)_ |
352
+ | `ChatModelFactory.create` → `LangChainChatModelLike` | `ChatModelFactory.create` → `ChatLanguageModel` (thin wrapper around an AI SDK `LanguageModelV2`) |
353
+
354
+ `ChatLanguageModel` exposes the underlying AI SDK `LanguageModel` via `languageModel` plus `modelName`, `provider`, and optional `defaultCallOptions` (`maxOutputTokens`, `temperature`, `providerOptions`). `StructuredOutputOptions` mirrors `generateText({ output: Output.object(...) })` and carries an optional `schemaName` plus `strict` flag.
355
+
356
+ ### Custom behavior preserved (not delegated to the AI SDK)
357
+ - **Tool dispatch + tool-args repair**: tools are passed to `generateText` **without `execute`** so tool calls surface back to Codemation; `AgentToolExecutionCoordinator` still drives parallel execution, per-tool Zod-input validation, repair prompts, and retry accounting via `repairAttemptsByToolName`.
358
+ - **Structured output repair**: `AgentStructuredOutputRunner` still runs the `OpenAiStrictJsonSchemaFactory` + `AgentStructuredOutputRepairPromptFactory` loop; AI SDK's `Output.object(...)` is used only for the **first** structured attempt when the provider supports it.
359
+ - **Connection-invocation tracking**: `ConnectionInvocationIdFactory` + synthetic `LanguageModelConnectionNode` / tool connection node states (`queued` / `running` / `completed` / `failed`) are still emitted per turn and per tool call.
360
+ - **Telemetry span names (intentional, short-term)**: LLM calls stay on `gen_ai.chat.completion`, tool calls on `agent.tool.call`, metrics on `codemation.ai.turns` / `codemation.ai.tool_calls` / `codemation.cost.estimated`. We disable AI SDK's built-in telemetry (`experimental_telemetry`) for this cut so host-side telemetry aggregations keep working unchanged. Migrating to AI SDK native span names is intentionally deferred.
361
+ - **Engine-level retry control**: every `generateText` call uses `maxRetries: 0` so Codemation's own retry / repair policy is the single source of truth.
362
+
363
+ ### New test utilities
364
+
365
+ Tests that previously scripted `LangChainChatModelLike` now script AI SDK `LanguageModelV3` via `MockLanguageModelV3` from `ai/test`. `@codemation/core-nodes` and `@codemation/host` test files ship small adapters (`ScriptedResponseConverter`, `ScriptedDoGenerateFactory`, `TelemetryResponseConverter`) that translate Codemation's legacy `{ content, tool_calls, usage_metadata }` fixtures into `LanguageModelV3GenerateResult`.
366
+
367
+ ### Migration notes for consumers
368
+ - If you implemented a **custom `ChatModelFactory`**, return a `ChatLanguageModel` (wrap an AI SDK `LanguageModelV2`) instead of a LangChain-shaped chat model. The `name` / `modelName` / `provider` on your config still drive cost tracking.
369
+ - If you imported the type `LangChainChatModelLike` (or `LangChainStructuredOutputModelLike`) from `@codemation/core`, switch to `ChatLanguageModel` (and drop structured-output-method imports — `generateText({ experimental_output })` covers it).
370
+ - `OpenAIChatModelFactory` now builds an AI SDK OpenAI provider under the hood; behavior for end users (model presets, credential resolution, token accounting, structured output against strict mode) is unchanged.
371
+ - Telemetry dashboards, trace views, and cost-tracking queries continue to work against the existing Codemation span / metric names.
372
+
373
+ ### Patch Changes
374
+
375
+ - [#93](https://github.com/MadeRelevant/codemation/pull/93) [`640e303`](https://github.com/MadeRelevant/codemation/commit/640e3032b1386568df725980a27761b6e230302c) Thanks [@cblokland90](https://github.com/cblokland90)! - Fix `Unique constraint failed on the fields: (instance_id)` crash when rerunning a workflow that contains an AI agent.
376
+
377
+ Reproduction: build `Manual trigger → AI agent → node → node`, click play on the agent, then click play on the next node (sometimes twice). The second run would fail at `PrismaWorkflowRunRepository.saveOnce` with a Postgres PK violation on the `ExecutionInstance` table.
378
+
379
+ Root cause: `RunStartService.createRunCurrentState` was deep-copying the prior run's `connectionInvocations` verbatim into the new run's initial state. Each record kept its original globally-unique `invocationId`, which is the primary key in `ExecutionInstance`. `saveOnce`'s existing-row lookup is scoped to the current `runId`, so the collision against the prior run's rows was only detected by Postgres when the insert fired.
380
+
381
+ Beyond the crash, the old behavior was also a data-model lie for compliance / OTEL: a `ConnectionInvocationRecord` represents a single auditable LLM / tool call and must belong to exactly one run. Copying it into another run made the same event appear to have happened twice.
382
+
383
+ Fix (domain + defense-in-depth):
384
+ - `@codemation/core` — `RunStartService.createRunCurrentState` now starts new runs with an empty invocation ledger. The prior run's invocations remain queryable on that run's persisted state (their true owner).
385
+ - `@codemation/host` — `PrismaWorkflowRunRepository.buildExecutionInstances` skips any invocation whose `runId` differs from the run being saved, so a stray carry-over from any other code path self-heals instead of crashing the save.
386
+
387
+ UI impact: none for the historical-run view (it reads invocations directly from the selected run). The client-side debugger overlay continues to surface the prior run's invocations locally during a rerun, and inspector telemetry already fetches against each invocation's original `runId`.
388
+
3
389
  ## 0.8.1
4
390
 
5
391
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { Ar as WebhookTriggerRoutingDiagnostics, Or as WebhookTriggerMatcher, Pa as EngineExecutionLimitsPolicyConfig, ha as DependencyContainer, hn as WorkflowPolicyRuntimeDefaults, pr as TriggerRuntimeDiagnostics } from "./RunIntentService-BqhmdoA1.js";
1
+ import { Mr as WebhookTriggerMatcher, Pr as WebhookTriggerRoutingDiagnostics, Ta as DependencyContainer, Wa as EngineExecutionLimitsPolicyConfig, _r as TriggerRuntimeDiagnostics, vn as WorkflowPolicyRuntimeDefaults } from "./RunIntentService-BE9CAkbf.js";
2
2
 
3
3
  //#region src/bootstrap/runtime/EngineRuntimeRegistration.types.d.ts
4
4
 
@@ -41,4 +41,4 @@ interface EngineRuntimeRegistrationOptions {
41
41
  }
42
42
  //#endregion
43
43
  export { TriggerRuntimeDiagnosticsProvider as n, WebhookTriggerMatcherProvider as r, EngineRuntimeRegistrationOptions as t };
44
- //# sourceMappingURL=EngineRuntimeRegistration.types-BP6tsaNP.d.ts.map
44
+ //# sourceMappingURL=EngineRuntimeRegistration.types-D1fyApMI.d.ts.map
@@ -1,4 +1,4 @@
1
- import { E as NodeId, F as ParentExecutionRef, Ln as PersistedWorkflowSnapshot, Rn as PersistedWorkflowSnapshotNode, Si as EngineExecutionLimitsPolicyConfig, Y as WorkflowDefinition, b as NodeConfigBase, f as Items, gn as WebhookTriggerMatcher, qn as RunResult, r as Engine, ri as DependencyContainer, rt as WorkflowPolicyRuntimeDefaults, sn as WorkflowRepository, tn as TriggerRuntimeDiagnostics, tt as WorkflowId, vn as WebhookTriggerRoutingDiagnostics, zn as PersistedWorkflowTokenRegistryLike } from "./RunIntentService-S-1lW-gS.cjs";
1
+ import { Br as PersistedWorkflowTokenRegistryLike, Fi as NodeId, G as WorkflowDefinition, Jr as RunResult, M as ParentExecutionRef, Q as WorkflowPolicyRuntimeDefaults, Ri as WorkflowId, Rr as PersistedWorkflowSnapshot, Zt as TriggerRuntimeDiagnostics, ai as DependencyContainer, d as Items, fn as WebhookTriggerMatcher, mn as WebhookTriggerRoutingDiagnostics, r as Engine, rn as WorkflowRepository, wi as EngineExecutionLimitsPolicyConfig, y as NodeConfigBase, zr as PersistedWorkflowSnapshotNode } from "./RunIntentService-siBSjaaY.cjs";
2
2
 
3
3
  //#region src/workflowSnapshots/WorkflowSnapshotCodec.d.ts
4
4
  declare class WorkflowSnapshotCodec {
@@ -18,6 +18,20 @@ declare class WorkflowSnapshotCodec {
18
18
  private asRecord;
19
19
  }
20
20
  //#endregion
21
+ //#region src/runtime/EngineWorkflowRunnerService.d.ts
22
+ declare class EngineWorkflowRunnerService {
23
+ private readonly engine;
24
+ private readonly workflowRepository;
25
+ constructor(engine: Engine, workflowRepository: WorkflowRepository);
26
+ runById(args: {
27
+ workflowId: WorkflowId;
28
+ startAt?: NodeId;
29
+ items: Items;
30
+ parent?: ParentExecutionRef;
31
+ }): Promise<RunResult>;
32
+ private findDefaultStartNodeId;
33
+ }
34
+ //#endregion
21
35
  //#region src/bootstrap/runtime/EngineRuntimeRegistration.types.d.ts
22
36
  /**
23
37
  * Creates the webhook route matcher used by {@link import("../api/Engine").Engine}.
@@ -57,19 +71,5 @@ interface EngineRuntimeRegistrationOptions {
57
71
  workflowPolicyRuntimeDefaults?: WorkflowPolicyRuntimeDefaults;
58
72
  }
59
73
  //#endregion
60
- //#region src/runtime/EngineWorkflowRunnerService.d.ts
61
- declare class EngineWorkflowRunnerService {
62
- private readonly engine;
63
- private readonly workflowRepository;
64
- constructor(engine: Engine, workflowRepository: WorkflowRepository);
65
- runById(args: {
66
- workflowId: WorkflowId;
67
- startAt?: NodeId;
68
- items: Items;
69
- parent?: ParentExecutionRef;
70
- }): Promise<RunResult>;
71
- private findDefaultStartNodeId;
72
- }
73
- //#endregion
74
- export { WorkflowSnapshotCodec as a, WebhookTriggerMatcherProvider as i, EngineRuntimeRegistrationOptions as n, TriggerRuntimeDiagnosticsProvider as r, EngineWorkflowRunnerService as t };
75
- //# sourceMappingURL=EngineWorkflowRunnerService-DzOCa1BW.d.cts.map
74
+ export { WorkflowSnapshotCodec as a, EngineWorkflowRunnerService as i, TriggerRuntimeDiagnosticsProvider as n, WebhookTriggerMatcherProvider as r, EngineRuntimeRegistrationOptions as t };
75
+ //# sourceMappingURL=EngineRuntimeRegistration.types-pB3FnzqR.d.cts.map
@@ -1,4 +1,4 @@
1
- import { A as NodeOutputs, At as ExecutionContext, B as RunId, Bt as NodeBinaryAttachmentService, Ct as BinaryStorageStatResult, Dr as CostTrackingTelemetryFactory, E as NodeId, F as ParentExecutionRef, Gt as NodeExecutionStatePublisher, H as RunnableNodeConfig, I as PersistedRunPolicySnapshot, Jr as CredentialSessionService, R as RunDataFactory, St as BinaryStorageReadResult, Tt as BinaryStorageWriteResult, Vt as NodeExecutionContext, a as BinaryAttachment, bt as BinaryBody, jt as ExecutionContextFactory, kr as CostTrackingUsageRecord, kt as ExecutionBinaryService, rr as ExecutionTelemetryFactory, tt as WorkflowId, u as Item, v as MutableRunData, vt as RetryPolicySpec, xt as BinaryStorage, y as NodeActivationId, z as RunDataSnapshot } from "./RunIntentService-S-1lW-gS.cjs";
1
+ import { $r as RunTestContext, Bt as NodeExecutionStatePublisher, Et as ExecutionContextFactory, F as RunDataSnapshot, Fi as NodeId, Ft as NodeBinaryAttachmentService, Hn as CostTrackingTelemetryFactory, I as RunId, It as NodeExecutionContext, Kn as CollectionsContext, M as ParentExecutionRef, N as PersistedRunPolicySnapshot, O as NodeOutputs, P as RunDataFactory, R as RunnableNodeConfig, Ri as WorkflowId, Tt as ExecutionContext, Wn as CostTrackingUsageRecord, _ as MutableRunData, _t as BinaryStorageReadResult, a as BinaryAttachment, bt as BinaryStorageWriteResult, dr as CredentialSessionService, gt as BinaryStorage, ht as BinaryBody, l as Item, pt as RetryPolicySpec, v as NodeActivationId, vn as ExecutionTelemetryFactory, vt as BinaryStorageStatResult, wt as ExecutionBinaryService } from "./RunIntentService-siBSjaaY.cjs";
2
2
 
3
3
  //#region src/contracts/CostCatalogContract.d.ts
4
4
  interface CostCatalogEntry {
@@ -22,6 +22,31 @@ declare class CredentialResolverFactory {
22
22
  create(workflowId: WorkflowId, nodeId: NodeId, config?: NodeExecutionContext["config"]): NodeExecutionContext["getCredential"];
23
23
  }
24
24
  //#endregion
25
+ //#region src/binaries/UnavailableBinaryStorage.d.ts
26
+ declare class UnavailableBinaryStorage implements BinaryStorage {
27
+ readonly driverName = "unavailable";
28
+ write(): Promise<never>;
29
+ openReadStream(): Promise<undefined>;
30
+ stat(): Promise<{
31
+ exists: false;
32
+ }>;
33
+ delete(): Promise<void>;
34
+ }
35
+ //#endregion
36
+ //#region src/binaries/DefaultExecutionBinaryServiceFactory.d.ts
37
+ declare class DefaultExecutionBinaryService implements ExecutionBinaryService {
38
+ private readonly storage;
39
+ private readonly workflowId;
40
+ private readonly runId;
41
+ private readonly now;
42
+ constructor(storage: BinaryStorage, workflowId: WorkflowId, runId: RunId, now: () => Date);
43
+ forNode(args: {
44
+ nodeId: NodeId;
45
+ activationId: NodeActivationId;
46
+ }): NodeBinaryAttachmentService;
47
+ openReadStream(attachment: BinaryAttachment): Promise<BinaryStorageReadResult | undefined>;
48
+ }
49
+ //#endregion
25
50
  //#region src/execution/asyncSleeper.types.d.ts
26
51
  interface AsyncSleeper {
27
52
  sleep(ms: number): Promise<void>;
@@ -38,8 +63,9 @@ declare class DefaultExecutionContextFactory implements ExecutionContextFactory
38
63
  private readonly telemetryFactory;
39
64
  private readonly costTrackingFactory;
40
65
  private readonly currentDate;
66
+ private readonly collections?;
41
67
  private readonly telemetryDecoratorFactory;
42
- constructor(binaryStorage?: BinaryStorage, telemetryFactory?: ExecutionTelemetryFactory, costTrackingFactory?: CostTrackingTelemetryFactory, currentDate?: () => Date);
68
+ constructor(binaryStorage?: BinaryStorage, telemetryFactory?: ExecutionTelemetryFactory, costTrackingFactory?: CostTrackingTelemetryFactory, currentDate?: () => Date, collections?: CollectionsContext | undefined);
43
69
  create(args: {
44
70
  runId: RunId;
45
71
  workflowId: WorkflowId;
@@ -52,6 +78,7 @@ declare class DefaultExecutionContextFactory implements ExecutionContextFactory
52
78
  nodeState?: NodeExecutionStatePublisher;
53
79
  telemetry?: ExecutionContext["telemetry"];
54
80
  getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
81
+ testContext?: RunTestContext;
55
82
  }): ExecutionContext;
56
83
  }
57
84
  //#endregion
@@ -84,31 +111,6 @@ declare class RunnableOutputBehaviorResolver {
84
111
  private isKeepBinariesEnabled;
85
112
  }
86
113
  //#endregion
87
- //#region src/binaries/UnavailableBinaryStorage.d.ts
88
- declare class UnavailableBinaryStorage implements BinaryStorage {
89
- readonly driverName = "unavailable";
90
- write(): Promise<never>;
91
- openReadStream(): Promise<undefined>;
92
- stat(): Promise<{
93
- exists: false;
94
- }>;
95
- delete(): Promise<void>;
96
- }
97
- //#endregion
98
- //#region src/binaries/DefaultExecutionBinaryServiceFactory.d.ts
99
- declare class DefaultExecutionBinaryService implements ExecutionBinaryService {
100
- private readonly storage;
101
- private readonly workflowId;
102
- private readonly runId;
103
- private readonly now;
104
- constructor(storage: BinaryStorage, workflowId: WorkflowId, runId: RunId, now: () => Date);
105
- forNode(args: {
106
- nodeId: NodeId;
107
- activationId: NodeActivationId;
108
- }): NodeBinaryAttachmentService;
109
- openReadStream(attachment: BinaryAttachment): Promise<BinaryStorageReadResult | undefined>;
110
- }
111
- //#endregion
112
114
  //#region src/runStorage/InMemoryBinaryStorageRegistry.d.ts
113
115
  declare class InMemoryBinaryStorage implements BinaryStorage {
114
116
  readonly driverName = "memory";
@@ -127,5 +129,5 @@ declare class InMemoryRunDataFactory implements RunDataFactory {
127
129
  create(initial?: Record<NodeId, NodeOutputs>): MutableRunData;
128
130
  }
129
131
  //#endregion
130
- export { RunnableOutputBehavior as a, InProcessRetryRunner as c, AsyncSleeper as d, CredentialResolverFactory as f, UnavailableBinaryStorage as i, DefaultExecutionContextFactory as l, CostCatalogEntry as m, InMemoryBinaryStorage as n, RunnableOutputBehaviorResolver as o, CostCatalog as p, DefaultExecutionBinaryService as r, ItemExprResolver as s, InMemoryRunDataFactory as t, DefaultAsyncSleeper as u };
131
- //# sourceMappingURL=InMemoryRunDataFactory-1iz7_SnO.d.cts.map
132
+ export { ItemExprResolver as a, DefaultAsyncSleeper as c, UnavailableBinaryStorage as d, CredentialResolverFactory as f, RunnableOutputBehaviorResolver as i, AsyncSleeper as l, CostCatalogEntry as m, InMemoryBinaryStorage as n, InProcessRetryRunner as o, CostCatalog as p, RunnableOutputBehavior as r, DefaultExecutionContextFactory as s, InMemoryRunDataFactory as t, DefaultExecutionBinaryService as u };
133
+ //# sourceMappingURL=InMemoryRunDataFactory-Xw7v4-sj.d.cts.map
@@ -0,0 +1,47 @@
1
+
2
+ //#region src/events/InMemoryRunEventSubscription.ts
3
+ var InMemoryRunEventSubscription = class {
4
+ constructor(onClose) {
5
+ this.onClose = onClose;
6
+ }
7
+ async close() {
8
+ this.onClose();
9
+ }
10
+ };
11
+
12
+ //#endregion
13
+ //#region src/events/InMemoryRunEventBusRegistry.ts
14
+ var InMemoryRunEventBus = class {
15
+ globalListeners = /* @__PURE__ */ new Set();
16
+ listenersByWorkflowId = /* @__PURE__ */ new Map();
17
+ async publish(event) {
18
+ for (const listener of this.globalListeners) listener(event);
19
+ for (const listener of this.listenersByWorkflowId.get(event.workflowId) ?? []) listener(event);
20
+ }
21
+ async subscribe(onEvent) {
22
+ this.globalListeners.add(onEvent);
23
+ return new InMemoryRunEventSubscription(() => {
24
+ this.globalListeners.delete(onEvent);
25
+ });
26
+ }
27
+ async subscribeToWorkflow(workflowId, onEvent) {
28
+ const existing = this.listenersByWorkflowId.get(workflowId) ?? /* @__PURE__ */ new Set();
29
+ existing.add(onEvent);
30
+ this.listenersByWorkflowId.set(workflowId, existing);
31
+ return new InMemoryRunEventSubscription(() => {
32
+ const listeners = this.listenersByWorkflowId.get(workflowId);
33
+ if (!listeners) return;
34
+ listeners.delete(onEvent);
35
+ if (listeners.size === 0) this.listenersByWorkflowId.delete(workflowId);
36
+ });
37
+ }
38
+ };
39
+
40
+ //#endregion
41
+ Object.defineProperty(exports, 'InMemoryRunEventBus', {
42
+ enumerable: true,
43
+ get: function () {
44
+ return InMemoryRunEventBus;
45
+ }
46
+ });
47
+ //# sourceMappingURL=InMemoryRunEventBusRegistry-VM3OWnHo.cjs.map