@owlmeans/agent 0.1.18-rc.15 → 0.1.18-rc.17

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 (79) hide show
  1. package/README.md +18 -4
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent/SKILL.md +132 -18
  4. package/build/checkpoint/index.d.ts +2 -0
  5. package/build/checkpoint/index.d.ts.map +1 -0
  6. package/build/checkpoint/index.js +2 -0
  7. package/build/checkpoint/index.js.map +1 -0
  8. package/build/checkpoint/saver.d.ts +7 -0
  9. package/build/checkpoint/saver.d.ts.map +1 -0
  10. package/build/checkpoint/saver.js +135 -0
  11. package/build/checkpoint/saver.js.map +1 -0
  12. package/build/helpers/tools.d.ts +7 -1
  13. package/build/helpers/tools.d.ts.map +1 -1
  14. package/build/helpers/tools.js +10 -1
  15. package/build/helpers/tools.js.map +1 -1
  16. package/build/index.d.ts +2 -1
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js +2 -1
  19. package/build/index.js.map +1 -1
  20. package/build/model.d.ts +11 -6
  21. package/build/model.d.ts.map +1 -1
  22. package/build/model.js +36 -21
  23. package/build/model.js.map +1 -1
  24. package/build/pipeline/index.d.ts +3 -0
  25. package/build/pipeline/index.d.ts.map +1 -0
  26. package/build/pipeline/index.js +2 -0
  27. package/build/pipeline/index.js.map +1 -0
  28. package/build/pipeline/runner.d.ts +22 -0
  29. package/build/pipeline/runner.d.ts.map +1 -0
  30. package/build/pipeline/runner.js +566 -0
  31. package/build/pipeline/runner.js.map +1 -0
  32. package/build/pipeline/types.d.ts +190 -0
  33. package/build/pipeline/types.d.ts.map +1 -0
  34. package/build/pipeline/types.js +2 -0
  35. package/build/pipeline/types.js.map +1 -0
  36. package/build/plugins/export.d.ts +2 -0
  37. package/build/plugins/export.d.ts.map +1 -1
  38. package/build/plugins/export.js +1 -0
  39. package/build/plugins/export.js.map +1 -1
  40. package/build/plugins/inquiry.d.ts +36 -0
  41. package/build/plugins/inquiry.d.ts.map +1 -0
  42. package/build/plugins/inquiry.js +126 -0
  43. package/build/plugins/inquiry.js.map +1 -0
  44. package/build/service.d.ts +1 -1
  45. package/build/service.d.ts.map +1 -1
  46. package/build/service.js +30 -0
  47. package/build/service.js.map +1 -1
  48. package/build/stores/memory.d.ts +3 -2
  49. package/build/stores/memory.d.ts.map +1 -1
  50. package/build/stores/memory.js +0 -0
  51. package/build/stores/memory.js.map +1 -1
  52. package/build/stores/types.d.ts +74 -4
  53. package/build/stores/types.d.ts.map +1 -1
  54. package/build/types.d.ts +42 -1
  55. package/build/types.d.ts.map +1 -1
  56. package/package.json +26 -10
  57. package/src/checkpoint/index.ts +1 -0
  58. package/src/checkpoint/saver.ts +178 -0
  59. package/src/helpers/tools.ts +12 -1
  60. package/src/index.ts +2 -1
  61. package/src/model.ts +40 -20
  62. package/src/pipeline/index.ts +2 -0
  63. package/src/pipeline/runner.ts +724 -0
  64. package/src/pipeline/types.ts +202 -0
  65. package/src/plugins/export.ts +3 -0
  66. package/src/plugins/inquiry.ts +169 -0
  67. package/src/service.ts +31 -1
  68. package/src/stores/memory.ts +0 -0
  69. package/src/stores/types.ts +80 -5
  70. package/src/types.ts +46 -1
  71. package/tests/agent.spec.ts +22 -0
  72. package/tests/checkpoint.spec.ts +158 -0
  73. package/tests/inquiry.spec.ts +121 -0
  74. package/tests/pipeline-inquiry.spec.ts +299 -0
  75. package/tests/pipeline-resume.spec.ts +320 -0
  76. package/tests/pipeline.spec.ts +297 -0
  77. package/tests/runtime.spec.ts +80 -41
  78. package/tests/tools.spec.ts +23 -0
  79. package/src/runtime/checkpoint.ts +0 -89
package/README.md CHANGED
@@ -39,6 +39,7 @@ const agent = context.agents().agent({ exec, tools })
39
39
  | `summarizePlugin` | Compacts each finished run into `summary` + `advice`, and replays the last few on the way in |
40
40
  | `memoryGraphPlugin` | Durable notes filed by subsystem, with links — index injected, content pulled by tool |
41
41
  | `memoryEventsPlugin` | A bounded, ordered record of what happened |
42
+ | `inquiryPlugin` | The `ask_user` tool — offered only when a channel to a person is wired |
42
43
 
43
44
  Both memory plugins also export a plain API (`memoryGraph`, `memoryEvents`) usable with no agent at
44
45
  all, so a pipeline helper writes to the same store an agent reads.
@@ -72,9 +73,22 @@ implements. An unbound port is not an error — the plugin that needs it becomes
72
73
  purpose is streamed to the client, so without it the summary of a run types itself out in the
73
74
  user's view of that run, right after it finished.
74
75
 
75
- **No LangGraph checkpointer.** Recoverability lives in the OwlMeans execution and flow layers, which
76
- already own a serializable state model; `makeAgentExecutionPlugin` is the first real implementation
77
- of `@owlmeans/llm`'s `ExecutionPlugin` seam.
76
+ **An agent run is not resumable; a PIPELINE is.** A turn's tool calls are effects already applied
77
+ to the world, so re-entering one re-applies them — `makeAgentModel` therefore keeps no checkpoint,
78
+ and `makeAgentExecutionPlugin` is the first real implementation of `@owlmeans/llm`'s
79
+ `ExecutionPlugin` seam. What resumes is `makePipeline`, a state machine over named steps whose
80
+ position is written to a run row at every boundary; an agent run belongs inside one of its steps.
81
+ The row is the authority on where a run stands — never the LangGraph checkpoint, which is
82
+ size-guarded and expires. A checkpointer is optional and buys replay, never correctness.
83
+
84
+ **A step asks a person with `ctx.ask(inquiry)`, and a run nobody can answer parks `Waiting`.** An
85
+ answer already in the state comes straight back, so a question is never asked twice; that only
86
+ holds if `Inquiry.id` is DERIVED from the step and the thing being decided rather than minted per
87
+ call. Answers are merged by the runner, in `invoke` and in `resume({ answers })`, never by a
88
+ caller's mapping. An agent that installs `inquiryPlugin` must pass
89
+ `fatal: e => isFatalError(e) != null` into its tool invocation: the tool rethrows
90
+ `InquiryUnavailable` alone — a channel that has gone is terminal, and without the predicate the
91
+ loop spends its whole turn budget on it.
78
92
 
79
93
  <!-- owlmeans:agent-guidance:start -->
80
94
  ## Agent guidance
@@ -84,7 +98,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
84
98
  your project's skill store (`.agents/skills/`):
85
99
 
86
100
  ```sh
87
- npx @owlmeans/agent-skills
101
+ npx @owlmeans/agent-skills@^0.1.18-rc.13
88
102
  ```
89
103
 
90
104
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/agent",
4
- "version": "0.1.18-rc.15",
5
- "generatedAt": "2026-09-01T17:39:40.765Z",
4
+ "version": "0.1.18-rc.17",
5
+ "generatedAt": "2026-09-10T18:54:42.500Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent
3
- description: How to use @owlmeans/agent — context-aware LLM agents and pipelines over the LangGraph functional API, with the AgentPlugin seam, conversation-summarization and memory plugins, storage-independent ports, and the ExecutionPlugin checkpoint implementation. Auto-invoked when importing makeAgentModel, appendAgentsService, an agent plugin, safeInvokeTool, or an agent store.
3
+ description: How to use @owlmeans/agent — context-aware LLM agents over the LangGraph functional API, and resumable PIPELINES over a checkpointed StateGraph, with the AgentPlugin seam, conversation-summarization and memory plugins, and storage-independent ports. Auto-invoked when importing makeAgentModel, makePipeline, makeCheckpointSaver, appendAgentsService, an agent plugin, safeInvokeTool, or an agent store.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/agent
9
9
 
10
10
  **Layer:** Cross-cutting domain
11
- **Install:** `"@owlmeans/agent": "^0.1.18-rc.15"` in `dependencies`, plus the `@langchain/core` and
11
+ **Install:** `"@owlmeans/agent": "^0.1.18-rc.17"` in `dependencies`, plus the `@langchain/core` and
12
12
  `@langchain/langgraph` **peers**
13
13
 
14
14
  The agent runtime. Contracts live in `@owlmeans/agent-common`.
@@ -18,18 +18,58 @@ The agent runtime. Contracts live in `@owlmeans/agent-common`.
18
18
  | Export | Description |
19
19
  |---|---|
20
20
  | `makeAgentModel(options)` | An agent over the LangGraph functional API: `invoke`, `use`, `conversation`. |
21
+ | `makePipeline(spec, options)` | A resumable state machine over a checkpointed LangGraph `StateGraph`: `invoke`, `resume`, `snapshot`, `asStep`. |
22
+ | `makeCheckpointSaver(store)` | A `BaseCheckpointSaver` over the `CheckpointStore` port. |
21
23
  | `makeAgentsService(options?, alias?)` · `appendAgentsService(ctx, options?, alias?)` · `agentServiceApi(options, self)` | The service, and its half without `createService` for composition. |
22
24
  | `summarizePlugin(options?)` | Compacts each finished run into `summary` + `advice`; replays the last few. |
23
25
  | `memoryGraphPlugin(options?)` · `memoryGraph(store, options?)` | Durable notes filed by subsystem, with links. Plugin **and** plain API. |
24
26
  | `memoryEventsPlugin(options?)` · `memoryEvents(store, options?)` | A bounded, ordered record of what happened. |
27
+ | `inquiryPlugin(options?)` · `INQUIRY_PLUGIN` · `ASK_USER_TOOL` | The `ask_user` tool and its one Context paragraph — offered only when a channel is wired. |
25
28
  | `safeInvokeTool`, `toErrorResponse`, `isToolError` | The never-throwing tool contract. |
26
29
  | `composeCompaction`, `composeRollingSummary`, `renderTranscript`, `messageText` | Summary primitives; both composers are total. |
27
- | `makeAgentExecutionPlugin(options)` | The first real implementation of `@owlmeans/llm`'s `ExecutionPlugin`. |
28
- | `makeStaticFlowProvider(flows)` | The server-side `FlowProvider` `@owlmeans/flow` never shipped. |
30
+ | `makeStaticFlowProvider(flows)` | The server-side `FlowProvider` `@owlmeans/flow` does not ship. |
29
31
  | `inProcessTransport()`, `AgentTransport` | The scaling seam; default carries messages by direct call. |
30
- | `createMemory*Store()` | In-memory reference implementations of every port. |
32
+ | `createMemory*Store()` | In-memory reference implementations of every port, including `createMemoryPipelineRunStore` and `createMemoryCheckpointStore`. |
33
+ | `AgentError` · `AgentMissconfiguredError` · `AgentLoopExhaustedError` | The `ResilientError` family. `AgentMissconfiguredError` is a model or tool set the agent was built without; `AgentLoopExhaustedError` is the tool loop hitting its ceiling. |
34
+ | `DEFAULT_MAX_TURNS` (64) · `DEFAULT_PLUGIN_ORDER` · `DEFAULT_ACTION` · `DEFAULT_ENTRYPOINT` | The loop and plugin defaults. |
31
35
 
32
- Subpath exports: `./plugins`, `./helpers`, `./stores`.
36
+ Subpath exports: `./plugins`, `./helpers`, `./stores`, `./pipeline`, `./checkpoint`.
37
+
38
+ ## Pipelines
39
+
40
+ A pipeline is a **state machine over named steps** whose position is persisted at every step
41
+ boundary, so a run killed anywhere continues from the step it stopped at rather than from zero.
42
+
43
+ ```ts
44
+ const spec: PipelineSpec = {
45
+ alias: 'vib:story:design', version: 1,
46
+ steps: [
47
+ { step: 'screens' },
48
+ { step: 'ux', after: ['screens'] },
49
+ { step: 'data', after: ['screens'] }, // runs BESIDE `ux`
50
+ { step: 'persist', after: ['ux', 'data'] }, // joins BOTH
51
+ ],
52
+ }
53
+
54
+ const pipeline = makePipeline<State, Deps>(spec, { steps, runs, checkpointer, fatal })
55
+ await pipeline.invoke(seed, { runId, deps, scope: projectId })
56
+ await pipeline.resume(runId, { deps, from: 'data' })
57
+ ```
58
+
59
+ **`StateGraph`, not the functional API.** A resume must be able to name the step it starts at, and
60
+ only a graph has named nodes; `entrypoint`/`task` identifies a task by its positional call ordinal,
61
+ which renumbers on any edit to the function — under a file watcher that is the common case, not the
62
+ corner case. The functional API stays exactly where it is, inside `makeAgentModel`'s ReAct loop.
63
+
64
+ **Edges are `after: string[]`.** Several steps naming one predecessor fan out; one step naming
65
+ several joins them, and the join waits for ALL of them (the array form of `addEdge`, which is a
66
+ barrier — separate single edges would fire on the first). Branching is `skipWhen`, never a
67
+ conditional edge: a branch expressed as an edge is invisible to a resume, while a branch expressed
68
+ as a guard is the same mechanism that makes a resume correct.
69
+
70
+ **The graph is compiled per run and the steps close over their dependencies.** Passing collaborators
71
+ through the engine's config would make a run depend on which config keys a given LangGraph minor
72
+ propagates into a node body. A closure cannot be lost.
33
73
 
34
74
  ## The plugin seam
35
75
 
@@ -55,6 +95,11 @@ provider will not put a cache breakpoint on — the Anthropic plugin explicitly
55
95
  trailing `Context`. Volatile material anywhere above it invalidates the `Role` + `Skills` prefix
56
96
  that every call sharing a persona pays for.
57
97
 
98
+ **A run that exceeds `maxTurns` throws `AgentLoopExhaustedError`.** The ceiling counts tool rounds
99
+ (`DEFAULT_MAX_TURNS` is 64, `AgentOptions.maxTurns` overrides it), and it is reached only when the
100
+ model keeps calling tools without ever answering — so catch that error by name rather than treating
101
+ every failed run alike: it says the loop ran out of room, not that a tool or the model failed.
102
+
58
103
  **`safeInvokeTool` must never throw.** The loop wraps it in a LangGraph `task`, and a rejected task
59
104
  aborts the whole superstep: every sibling tool call in the same parallel batch dies with AbortError
60
105
  and the run ends on "Multiple errors occurred during superstep 0", discarding work the others had
@@ -65,6 +110,12 @@ the model's own mistake, and the error text already names what was expected.
65
110
  own name, so a map keyed by a local variable silently loses any tool whose two names drifted apart:
66
111
  advertised, callable, permanently "not found".
67
112
 
113
+ **A prompt plugin's cheap side call has to be wired.** `AgentOptions.utility` is what the run hands
114
+ to `PromptComposeParams.utility`, normally `() => executions().utility(exec)`. Nothing resolves one
115
+ by default — the agent holds an execution, not the service that knows its policy — so a plugin that
116
+ would spend one cheap call on a relevance pick silently degrades until this is passed. What such a
117
+ call returns may never land in a cached block; see [[llm-prompt-caching]].
118
+
68
119
  **`compose()` is called with `files: exec.files`.** Without it, a prompt plugin that resolves
69
120
  knowledge from disk is silently inert on agent runs while working fine on plain model calls.
70
121
 
@@ -88,11 +139,11 @@ a prompt is a request. Both composers (`composeCompaction`, `composeRollingSumma
88
139
  no model, a failing model or an empty answer they fall back deterministically, so a caller can record
89
140
  history unconditionally. A failed fold costs detail, never the event.
90
141
 
91
- **Ports, not resources.** `ConversationStore`, `MemoryGraphStore`, `MemoryEventStore` and
92
- `AgentRunStateStore` are narrow interfaces a consumer implements. `Resource.list()` is not uniformly
93
- queryable — `@owlmeans/static-resource` throws on any criteria — so a plugin written against its
94
- query semantics could not be exercised with the monorepo's own in-memory backend. An unbound port is
95
- a no-op, not an error.
142
+ **Ports, not resources.** `ConversationStore`, `MemoryGraphStore`, `MemoryEventStore`,
143
+ `PipelineRunStore` and `CheckpointStore` are narrow interfaces a consumer implements. A port names exactly what the
144
+ plugin needs, which is a far smaller surface than CRUD, and anything can satisfy it — a `Resource`,
145
+ or a file on disk, which is what the project-history equivalent is. An unbound port is a no-op, not
146
+ an error.
96
147
 
97
148
  **Memory writes merge, they do not replace.** Replacing would make every write a potential act of
98
149
  forgetting, which is not a decision one caller has the standing to take. A node that outgrows
@@ -102,13 +153,67 @@ forgetting, which is not a decision one caller has the standing to take. A node
102
153
  the context window on knowledge the run cannot tell apart from what it needs; the agent pulls what it
103
154
  wants by name.
104
155
 
105
- **Checkpoints are size-guarded.** A project-level execution carries the whole project specification;
106
- writing that on every checkpoint is a storage problem that surfaces much later and much worse than a
107
- skipped write.
108
-
109
- **No LangGraph checkpointer, and the `entrypoint` is created inside `invoke()`.** Recoverability
110
- lives in the OwlMeans execution and flow layers, which already own a serializable state model;
111
- adopting a second one would leave two half-truths about where a crashed run stands.
156
+ **The run ROW is the authority on where a run stands — never the LangGraph checkpoint.** The
157
+ checkpoint is size-guarded and expires, so a design that reads a run's position out of it has a
158
+ silent hole exactly where a crashed run needs an answer. Every reader of a position — a resume, a
159
+ reconciler, an operator, a status endpoint — reads the row. With no checkpointer bound at all, a
160
+ resume is still correct; the checkpointer buys replay, never correctness.
161
+
162
+ **Every step is guarded twice.** The runner refuses to re-enter a step its row calls complete, and
163
+ the application's `skipWhen` reads a durable marker the step itself wrote. The first covers a clean
164
+ crash; the second covers a crash BETWEEN the side effect and the row write, which the first cannot
165
+ see. A step that does N undoable things in a loop marks a cursor with `ctx.mark`.
166
+
167
+ **A pipeline state is scalars and KEYS.** Ids, revisions, markers, lists of codes — never an
168
+ artifact. The state is serialized into the row at every step boundary, so a field carrying a
169
+ document writes one copy of it per step. Over `maxStateChars` the step FAILS: refused, never
170
+ truncated, because a resume replaying from a cut-down state replays from a state that never existed.
171
+
172
+ **A step failure is an OUTCOME, not a throw.** `invoke`/`resume` return
173
+ `{ status: Failed, failedAt, error }`, because every caller has a status, a warning or a record to
174
+ write before it decides anything, and a throwing runner puts that in a `catch` where it gets
175
+ forgotten. The one exception is `options.fatal`: those are written to the row Failed FIRST and then
176
+ rethrown, so an exhausted budget cannot become a result a caller carries on from.
177
+
178
+ **No default retry.** `attempts` defaults to 1, because retry budgets in this family MULTIPLY — an
179
+ outer retry around a model's own inner retry is their product. `attempts > 1` on a `nonIdempotent`
180
+ step is refused at build time: an automatic retry is exactly what that flag says must not happen.
181
+
182
+ **`nonIdempotent` marks a step whose side effect cannot be undone** — a wipe, a purge, a claim
183
+ against a rate-limited authority, a model call whose output is already on disk. `resume({ from })`
184
+ refuses to re-enter a completed one without `force`.
185
+
186
+ **A ReAct run is not a resumable unit, and that is why `makeAgentModel` still builds its
187
+ `entrypoint` inside `invoke()`.** Its state is an unbounded message list whose tool results are side
188
+ effects already applied to the world: re-entering a turn re-applies them. What IS resumable is a
189
+ pipeline — and an agent run belongs inside one of its steps.
190
+
191
+ **A step asks with `ctx.ask(inquiry)`, and a run nobody can answer parks `Waiting`.** Three
192
+ outcomes in order: an answer already in the state comes straight back (a question is never asked
193
+ twice); a live `options.inquiry.ask` answer is RECORDED in the state — `stateAnswerOf(capAnswer(…))`
194
+ under `state[INQUIRY_ANSWERS_KEY][id]`, the decision whole and the prose cut — and the FULL answer
195
+ returned to the step; otherwise the run stops `Waiting` with the inquiry on its row, and with no run
196
+ store it throws `PipelineNotResumableError` instead. A throwing channel is not a park: it fails the
197
+ step as an ordinary outcome, because the asking failed rather than the answer being no. `Inquiry.id`
198
+ is the only thing an answer is matched by, so DERIVE it from the step and the thing being decided —
199
+ an id minted per call never matches the state, and the run re-asks and parks forever.
200
+
201
+ **Answers are merged by the RUNNER, in `invoke` and in `resume({ answers })`, never by a mapping.**
202
+ `invoke` builds `{ ...restored, ...seed }`, so a mapping that forwarded the answers map would
203
+ replace the child's own recorded answers wholesale — and write `undefined` over them when the parent
204
+ had none, which re-asks question 1 on every resume. When a composed child parks, `asStep` relays its
205
+ question to the parent's `ctx.ask` and re-invokes the child with the answer (bounded at 8 questions
206
+ per composing step); the parent parks only when its own `ask` parks, and never throws a plain error
207
+ in that path.
208
+
209
+ **An agent that installs `inquiryPlugin` must pass `fatal: e => isFatalError(e) != null`.** The tool
210
+ rethrows `InquiryUnavailable` alone — no channel is an answerable situation, a channel that has GONE
211
+ is terminal — and `safeInvokeTool` contains everything else by default, so without that predicate
212
+ the loop spends its whole turn budget on a dead channel. See [[inquiry]] for the whole primitive.
213
+
214
+ **`safeInvokeTool(tools, call, fatal?)` takes a fatal predicate.** Containment is right for a bad
215
+ argument and wrong for an exhausted budget: a tool may be a whole pipeline behind one call, and
216
+ handing the model a readable "out of tokens" is an invitation to pick another tool and spend again.
112
217
 
113
218
  **`makeStaticFlowProvider` must throw on an unknown flow.** `makeFlowModel` reads a string as a flow
114
219
  name first and only re-reads it as a serialized token once the provider throws — returning null
@@ -120,3 +225,12 @@ Category A (unit, no env, no network). The model is doubled with a small scripte
120
225
  `tests/_tools/model.ts` because `@langchain/core`'s own `FakeStreamingChatModel` always replays its
121
226
  first response and so cannot drive a tool loop. That double stands in for the MODEL, an external
122
227
  boundary — never for an `@owlmeans/*` package.
228
+
229
+ ## Related
230
+
231
+ - [[inquiry]] — the human-in-the-loop primitive `ctx.ask`, `Waiting` and `ask_user` belong to
232
+ - [[agent-common]] — the serializable records and the run lifecycle flow
233
+ - [[llm]] — `Execution`, the model contract and the `advise`-only `ExecutionPlugin`
234
+ - [[agent-checkpoint]] — the durable Mongo implementation of both storage ports (internal)
235
+ - [[llm-prompt-caching]] — which block contributed context lands in, and why
236
+ - [[agent-skills]] — `projectSkillsAgentPlugin` and the `read_skill` tool
@@ -0,0 +1,2 @@
1
+ export * from './saver.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './saver.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { BaseCheckpointSaver } from '@langchain/langgraph';
2
+ import type { SerializerProtocol } from '@langchain/langgraph-checkpoint';
3
+ import type { CheckpointStore } from '../stores/types.js';
4
+ export declare const makeCheckpointSaver: (store: CheckpointStore, options?: {
5
+ serde?: SerializerProtocol;
6
+ }) => BaseCheckpointSaver;
7
+ //# sourceMappingURL=saver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saver.d.ts","sourceRoot":"","sources":["../../src/checkpoint/saver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,KAAK,EAEqB,kBAAkB,EAClD,MAAM,iCAAiC,CAAA;AAExC,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,oBAAoB,CAAA;AAwKhF,eAAO,MAAM,mBAAmB,UACvB,eAAe,YAAW;IAAE,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAAE,KAC9D,mBAAoE,CAAA"}
@@ -0,0 +1,135 @@
1
+ import { BaseCheckpointSaver } from '@langchain/langgraph';
2
+ import { WRITES_IDX_MAP } from '@langchain/langgraph-checkpoint';
3
+ /**
4
+ * Where each half of LangGraph's checkpoint API comes from, and why it is two imports.
5
+ *
6
+ * The CLASS is imported from `@langchain/langgraph`'s root, because a checkpointer crosses a
7
+ * package boundary and a class with protected members is nominally distinct per installed copy —
8
+ * two copies and `entrypoint({ checkpointer })` stops type-checking, or worse, stops recognising
9
+ * what it was handed. The protocol VALUES (`WRITES_IDX_MAP`, and the types beside it) are not in
10
+ * langgraph's root export list at all, so they come from `@langchain/langgraph-checkpoint` — which
11
+ * is a hard dependency of langgraph, so there is exactly one of it either way.
12
+ */
13
+ const encode = (data) => Buffer.from(data).toString('base64');
14
+ const decode = (data) => new Uint8Array(Buffer.from(data, 'base64'));
15
+ const threadOf = (config) => ({
16
+ thread: String(config.configurable?.thread_id ?? ''),
17
+ ns: String(config.configurable?.checkpoint_ns ?? ''),
18
+ id: config.configurable?.checkpoint_id != null
19
+ ? String(config.configurable.checkpoint_id)
20
+ : undefined,
21
+ });
22
+ const configFor = (thread, ns, id) => ({
23
+ configurable: { thread_id: thread, checkpoint_ns: ns, checkpoint_id: id },
24
+ });
25
+ /**
26
+ * A {@link BaseCheckpointSaver} over the package's storage-agnostic {@link CheckpointStore} port.
27
+ *
28
+ * Everything protocol-shaped lives here; everything storage-shaped lives behind the port. That
29
+ * split is what lets an application bind Mongo, Redis or nothing at all without this file — or
30
+ * LangGraph's version of it — being a consideration.
31
+ *
32
+ * Payloads are base64 of whatever the serde produced. Never `JSON.stringify`: the checkpoint holds
33
+ * `BaseMessage` instances, and a plain-object round trip returns something no `instanceof` in a
34
+ * provider adapter recognises again — a resumed run would silently drop its tool calls.
35
+ */
36
+ class PortCheckpointSaver extends BaseCheckpointSaver {
37
+ store;
38
+ constructor(store, serde) {
39
+ super(serde);
40
+ this.store = store;
41
+ }
42
+ async toTuple(thread, ns, record) {
43
+ const checkpoint = await this.serde.loadsTyped(record.type, decode(record.checkpoint));
44
+ const metadata = await this.serde.loadsTyped(record.type, decode(record.metadata));
45
+ const writes = await this.store.writesFor(thread, ns, [record.checkpointId]);
46
+ const pendingWrites = await Promise.all([...writes].sort((a, b) => a.idx - b.idx).map(async (write) => [
47
+ write.taskId, write.channel, await this.serde.loadsTyped(write.type, decode(write.value)),
48
+ ]));
49
+ return {
50
+ config: configFor(thread, ns, record.checkpointId),
51
+ checkpoint,
52
+ metadata,
53
+ ...(record.parentCheckpointId != null
54
+ ? { parentConfig: configFor(thread, ns, record.parentCheckpointId) }
55
+ : {}),
56
+ pendingWrites,
57
+ };
58
+ }
59
+ async getTuple(config) {
60
+ const { thread, ns, id } = threadOf(config);
61
+ if (thread === '') {
62
+ return undefined;
63
+ }
64
+ const record = await this.store.one(thread, ns, id);
65
+ return record != null ? await this.toTuple(thread, ns, record) : undefined;
66
+ }
67
+ async *list(config, options) {
68
+ const { thread, ns } = threadOf(config);
69
+ if (thread === '') {
70
+ return;
71
+ }
72
+ const before = options?.before?.configurable?.checkpoint_id;
73
+ // `limit` is applied by the STORE, not after decoding: a caller asking for the last three
74
+ // checkpoints must not pay for deserializing a thread's whole history to get them.
75
+ const records = await this.store.page({
76
+ threadId: thread,
77
+ ...(config.configurable?.checkpoint_ns != null ? { ns } : {}),
78
+ ...(before != null ? { beforeId: String(before) } : {}),
79
+ ...(options?.limit != null ? { limit: options.limit } : {}),
80
+ });
81
+ for (const record of records) {
82
+ yield await this.toTuple(thread, record.ns, record);
83
+ }
84
+ }
85
+ async put(config, checkpoint, metadata, _newVersions) {
86
+ const { thread, ns, id } = threadOf(config);
87
+ const [type, serialized] = await this.serde.dumpsTyped(checkpoint);
88
+ const [metadataType, serializedMetadata] = await this.serde.dumpsTyped(metadata);
89
+ if (type !== metadataType) {
90
+ throw new Error(`Mismatched checkpoint serialization types: ${type} vs ${metadataType}`);
91
+ }
92
+ await this.store.put({
93
+ threadId: thread,
94
+ ns,
95
+ checkpointId: checkpoint.id,
96
+ ...(id != null ? { parentCheckpointId: id } : {}),
97
+ type,
98
+ checkpoint: encode(serialized),
99
+ metadata: encode(serializedMetadata),
100
+ createdAt: new Date().toISOString(),
101
+ });
102
+ return configFor(thread, ns, checkpoint.id);
103
+ }
104
+ async putWrites(config, writes, taskId) {
105
+ const { thread, ns, id } = threadOf(config);
106
+ if (id == null) {
107
+ return;
108
+ }
109
+ const taskPath = config.configurable?.checkpoint_task_path;
110
+ const records = await Promise.all(writes.map(async ([channel, value], position) => {
111
+ const [type, serialized] = await this.serde.dumpsTyped(value);
112
+ return {
113
+ threadId: thread,
114
+ ns,
115
+ checkpointId: id,
116
+ taskId,
117
+ // A control channel has a well-known NEGATIVE index; a task's own output keeps its
118
+ // position. The sign is the write rule, and the store reads it: positive is first-wins,
119
+ // so a retried task cannot overwrite the answer its first attempt committed; negative is
120
+ // last-wins, because that is what an error or an interrupt is.
121
+ idx: WRITES_IDX_MAP[channel] ?? position,
122
+ channel,
123
+ type,
124
+ value: encode(serialized),
125
+ ...(taskPath != null ? { taskPath: String(taskPath) } : {}),
126
+ };
127
+ }));
128
+ await this.store.putWrites(records);
129
+ }
130
+ async deleteThread(threadId) {
131
+ await this.store.dropThread(threadId);
132
+ }
133
+ }
134
+ export const makeCheckpointSaver = (store, options = {}) => new PortCheckpointSaver(store, options.serde);
135
+ //# sourceMappingURL=saver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saver.js","sourceRoot":"","sources":["../../src/checkpoint/saver.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,12 @@ export declare const isToolError: (result: unknown) => result is ToolErrorRespon
24
24
  * that is what the model calls — a map keyed by a local variable silently loses any tool whose two
25
25
  * names drifted apart, leaving it advertised, callable, and permanently "not found". The key stays
26
26
  * as a fallback so a caller may still address a tool by it.
27
+ *
28
+ * `fatal` is the ONE exception to "never throws", and it exists because containment has a cost the
29
+ * containment itself cannot see. A tool may be a whole pipeline behind a single call; when the
30
+ * thing that stopped it is an exhausted budget or a refusal, handing the model a readable error
31
+ * message is an invitation to pick another tool and spend again. A caller that knows which errors
32
+ * mean "stop" says so here, and those alone travel out.
27
33
  */
28
- export declare const safeInvokeTool: (tools: AgentToolSet, toolCall: ToolCall) => Promise<unknown>;
34
+ export declare const safeInvokeTool: (tools: AgentToolSet, toolCall: ToolCall, fatal?: (e: unknown) => boolean) => Promise<unknown>;
29
35
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/helpers/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAAG,KAAK,EAAE,MAAM,CAAA;CAAE;AAEpD,eAAO,MAAM,eAAe,MAAO,OAAO,KAAG,iBAC2C,CAAA;AAExF,eAAO,MAAM,WAAW,WAAY,OAAO,KAAG,MAAM,IAAI,iBACW,CAAA;AAEnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,cAAc,UAAiB,YAAY,YAAY,QAAQ,KAAG,OAAO,CAAC,OAAO,CAc7F,CAAA"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/helpers/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAAG,KAAK,EAAE,MAAM,CAAA;CAAE;AAEpD,eAAO,MAAM,eAAe,MAAO,OAAO,KAAG,iBAC2C,CAAA;AAExF,eAAO,MAAM,WAAW,WAAY,OAAO,KAAG,MAAM,IAAI,iBACW,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,cAAc,UAClB,YAAY,YAAY,QAAQ,UAAU,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,KACvE,OAAO,CAAC,OAAO,CAiBjB,CAAA"}
@@ -18,8 +18,14 @@ export const isToolError = (result) => typeof result === 'object' && result != n
18
18
  * that is what the model calls — a map keyed by a local variable silently loses any tool whose two
19
19
  * names drifted apart, leaving it advertised, callable, and permanently "not found". The key stays
20
20
  * as a fallback so a caller may still address a tool by it.
21
+ *
22
+ * `fatal` is the ONE exception to "never throws", and it exists because containment has a cost the
23
+ * containment itself cannot see. A tool may be a whole pipeline behind a single call; when the
24
+ * thing that stopped it is an exhausted budget or a refusal, handing the model a readable error
25
+ * message is an invitation to pick another tool and spend again. A caller that knows which errors
26
+ * mean "stop" says so here, and those alone travel out.
21
27
  */
22
- export const safeInvokeTool = async (tools, toolCall) => {
28
+ export const safeInvokeTool = async (tools, toolCall, fatal) => {
23
29
  const tool = tools[toolCall.name]
24
30
  ?? Object.values(tools).find(entry => entry.name === toolCall.name);
25
31
  if (tool == null) {
@@ -29,6 +35,9 @@ export const safeInvokeTool = async (tools, toolCall) => {
29
35
  return await tool.invoke(toolCall.args);
30
36
  }
31
37
  catch (e) {
38
+ if (fatal?.(e) === true) {
39
+ throw e;
40
+ }
32
41
  console.warn(`Error during tool call ${toolCall.name}:`, e);
33
42
  return toErrorResponse(e);
34
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/helpers/tools.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAU,EAAqB,EAAE,CAC/D,CAAC,EAAE,KAAK,EAAE,2BAA2B,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAExF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAe,EAA+B,EAAE,CAC1E,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM,CAAA;AAEnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,KAAmB,EAAE,QAAkB,EAAoB,EAAE;IAChG,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;WAC5B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAA;IAErE,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO,eAAe,CAAC,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,IAAI,YAAY,CAAC,CAAC,CAAA;IACtE,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,0BAA0B,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAA;QAC3D,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/helpers/tools.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAU,EAAqB,EAAE,CAC/D,CAAC,EAAE,KAAK,EAAE,2BAA2B,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAExF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAe,EAA+B,EAAE,CAC1E,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,KAAmB,EAAE,QAAkB,EAAE,KAA+B,EACtD,EAAE;IACpB,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;WAC5B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAA;IAErE,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO,eAAe,CAAC,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,IAAI,YAAY,CAAC,CAAC,CAAA;IACtE,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,CAAC,CAAA;QACT,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,0BAA0B,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAA;QAC3D,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC,CAAA"}
package/build/index.d.ts CHANGED
@@ -5,8 +5,9 @@ export * from './model.js';
5
5
  export * from './service.js';
6
6
  export * from './helpers/index.js';
7
7
  export * from './stores/index.js';
8
+ export * from './pipeline/index.js';
9
+ export * from './checkpoint/index.js';
8
10
  export * from './runtime/provider.js';
9
11
  export * from './runtime/transport.js';
10
- export * from './runtime/checkpoint.js';
11
12
  export * from './plugins/export.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA"}
package/build/index.js CHANGED
@@ -4,8 +4,9 @@ export * from './model.js';
4
4
  export * from './service.js';
5
5
  export * from './helpers/index.js';
6
6
  export * from './stores/index.js';
7
+ export * from './pipeline/index.js';
8
+ export * from './checkpoint/index.js';
7
9
  export * from './runtime/provider.js';
8
10
  export * from './runtime/transport.js';
9
- export * from './runtime/checkpoint.js';
10
11
  export * from './plugins/export.js';
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAE3B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAE3B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA"}
package/build/model.d.ts CHANGED
@@ -3,13 +3,18 @@ import type { AgentModel, AgentOptions } from './types.js';
3
3
  * An OwlMeans agent over the LangGraph functional API.
4
4
  *
5
5
  * The loop is deliberately the plain one: ask the model, run whatever tools it asked for, feed the
6
- * results back, repeat until it stops asking. No `StateGraph`, and no LangGraph checkpointer — this
7
- * family's recoverability lives in the OwlMeans execution and flow layers, which already own a
8
- * serializable state model, and adopting a second one would leave two half-truths about where a
9
- * crashed run stands.
6
+ * results back, repeat until it stops asking.
10
7
  *
11
- * The `entrypoint` is created INSIDE `invoke()`, so nothing survives a call. What continuity a
12
- * conversation has comes from plugins putting it back into the prompt, not from the graph.
8
+ * The `entrypoint` is created INSIDE `invoke()` and takes no checkpointer, and that is not for want
9
+ * of one — `makePipeline` in this same package is a checkpointed `StateGraph`. **A ReAct run is not
10
+ * a resumable unit.** Its state is an unbounded message list whose tool results are side effects
11
+ * already applied to the world: re-entering a turn re-applies them, and the identity a functional
12
+ * replay would key on is the positional call ordinal, which renumbers on any edit to the loop. What
13
+ * IS resumable is a pipeline, whose steps are named, whose state is scalars, and whose side effects
14
+ * are guarded per step — and an agent run belongs INSIDE one of those steps.
15
+ *
16
+ * So nothing survives a call here. What continuity a conversation has comes from plugins putting it
17
+ * back into the prompt, not from the graph.
13
18
  */
14
19
  export declare const makeAgentModel: (options: AgentOptions) => AgentModel;
15
20
  //# sourceMappingURL=model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,UAAU,EAAE,YAAY,EACzB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,YAAa,YAAY,KAAG,UA8OtD,CAAA"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,UAAU,EAAE,YAAY,EACzB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,YAAa,YAAY,KAAG,UA6PtD,CAAA"}