@owlmeans/agent 0.1.18-rc.52 → 0.1.18-rc.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ import { makeAgentModel } from '@owlmeans/agent'
14
14
  const agent = makeAgentModel({
15
15
  exec, // an @owlmeans/llm Execution — its prompt policy is the persona
16
16
  tools, // { [name]: StructuredToolInterface }
17
- context: [projectBrief], // volatile context; lands in PromptBlock.Context
17
+ context: [projectBrief], // run context; lands in PromptBlock.Context, the project message
18
18
  })
19
19
 
20
20
  const result = await agent.invoke('rename the dashboard header')
@@ -45,9 +45,11 @@ with no agent at all, so a pipeline helper writes to the same store an agent rea
45
45
 
46
46
  ## Things worth knowing before you change it
47
47
 
48
- **Contributed context goes to `PromptBlock.Context` and nowhere else.** That is the only block a
49
- provider will not put a cache breakpoint on. Volatile material anywhere above it invalidates the
50
- prefix that every call sharing a persona pays for.
48
+ **Contributed context goes to `PromptBlock.Context` and nowhere else.** It travels in the project
49
+ message after the system, so the system region — role, skills, packages — stays byte-identical for
50
+ every run of a persona. Run material anywhere in the system invalidates the prefix every call
51
+ sharing that persona pays for. Each round caches the opening (`loopCache` of the model's provider
52
+ plugin) and lets the growing conversation be read back from the round before.
51
53
 
52
54
  **`toolHelper.safeInvokeTool` must never throw.** A rejected LangGraph task aborts the whole superstep: every
53
55
  sibling tool call in the same parallel batch dies with it, discarding work they had already
@@ -84,7 +86,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
84
86
  your project's skill store (`.agents/skills/`):
85
87
 
86
88
  ```sh
87
- npx @owlmeans/agent-skills@^0.1.18-rc.54
89
+ npx @owlmeans/agent-skills@^0.1.18-rc.57
88
90
  ```
89
91
 
90
92
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 2,
2
+ "schemaVersion": 3,
3
3
  "package": "@owlmeans/agent",
4
- "version": "0.1.18-rc.52",
5
- "generatedAt": "2026-10-15T10:20:01.303Z",
4
+ "version": "0.1.18-rc.54",
5
+ "generatedAt": "2026-10-20T23:19:58.631Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -10,7 +10,62 @@
10
10
  "name": "agent",
11
11
  "category": "package-specific",
12
12
  "file": "skills/agent/SKILL.md",
13
- "canonicalPath": ".agents/skills/agent/SKILL.md"
13
+ "canonicalPath": ".agents/skills/agent/SKILL.md",
14
+ "parts": [
15
+ {
16
+ "name": "cumulative-results",
17
+ "file": "skills/agent/parts/cumulative-results.md",
18
+ "description": "Cumulative pipeline results — deterministic extractors, the ledger and its rebuild, how a step's view is cut, refresh, model summaries, composed runs and seeds.",
19
+ "purposes": [
20
+ "agent",
21
+ "job"
22
+ ],
23
+ "mentions": [
24
+ "cumulativeResultsPlugin",
25
+ "CUMULATIVE_RESULTS_PLUGIN",
26
+ "CumulativeResultStore",
27
+ "CumulativeResultFact",
28
+ "DEFAULT_RESULT_SUMMARY_SCHEMA",
29
+ "ctx.results",
30
+ "withResults"
31
+ ]
32
+ },
33
+ {
34
+ "name": "pipelines",
35
+ "file": "skills/agent/parts/pipelines.md",
36
+ "description": "Resumable pipelines over a checkpointed StateGraph — steps and edges, the PipelinePlugin seam, and the rules for run rows, step guards, state size, failures and retries.",
37
+ "purposes": [
38
+ "agent",
39
+ "job",
40
+ "state"
41
+ ],
42
+ "mentions": [
43
+ "makePipeline",
44
+ "PipelineSpec",
45
+ "PipelinePlugin",
46
+ "asStep",
47
+ "skipWhen",
48
+ "nonIdempotent",
49
+ "makeCheckpointSaver",
50
+ "PipelineRunStore",
51
+ "CheckpointStore",
52
+ "maxStateChars",
53
+ "ctx.mark",
54
+ "@owlmeans/agent/pipeline"
55
+ ]
56
+ },
57
+ {
58
+ "name": "testing",
59
+ "file": "skills/agent/parts/testing.md",
60
+ "description": "Unit tests of agents and pipelines with a scripted model double and files kept in deps.",
61
+ "purposes": [
62
+ "test"
63
+ ],
64
+ "mentions": [
65
+ "FakeStreamingChatModel"
66
+ ]
67
+ }
68
+ ]
14
69
  }
15
70
  ]
16
71
  }
@@ -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.52"` in `dependencies`, plus the `@langchain/core` and
11
+ **Install:** `"@owlmeans/agent": "^0.1.18-rc.54"` 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`.
@@ -19,7 +19,7 @@ The agent runtime. Contracts live in `@owlmeans/agent-common`.
19
19
  |---|---|
20
20
  | `makeAgentModel(options)` | An agent over the LangGraph functional API: `invoke`, `use`, `conversation`. |
21
21
  | `makePipeline(spec, options)` | A resumable state machine over a checkpointed LangGraph `StateGraph`: `invoke`, `resume`, `snapshot`, `asStep`. `options.plugins` seats `PipelinePlugin`s. |
22
- | `cumulativeResultsPlugin(options)` · `CUMULATIVE_RESULTS_PLUGIN` · `DEFAULT_RESULT_SUMMARY_SCHEMA` | CUMULATIVE PIPELINE RESULTS — tells every step what its predecessors produced; see below. |
22
+ | `cumulativeResultsPlugin(options)` · `CUMULATIVE_RESULTS_PLUGIN` · `DEFAULT_RESULT_SUMMARY_SCHEMA` | CUMULATIVE PIPELINE RESULTS — tells every step what its predecessors produced; see the `cumulative-results` part. |
23
23
  | `PipelinePlugin` and its events, `StepResults`, `PipelineParentRef` | The pipeline plugin seam (types, from `./pipeline`). |
24
24
  | `makeCheckpointSaver(store)` | A `BaseCheckpointSaver` over the `CheckpointStore` port. |
25
25
  | `makeAgentsService(options?, alias?)` · `appendAgentsService(ctx, options?, alias?)` · `agentServiceApi(options, self)` | The service, and its half without `createService` for composition. |
@@ -36,124 +36,6 @@ The agent runtime. Contracts live in `@owlmeans/agent-common`.
36
36
 
37
37
  Subpath exports: `./plugins`, `./helpers`, `./stores`, `./pipeline`, `./checkpoint`.
38
38
 
39
- ## Pipelines
40
-
41
- A pipeline is a **state machine over named steps** whose position is persisted at every step
42
- boundary, so a run killed anywhere continues from the step it stopped at rather than from zero.
43
-
44
- ```ts
45
- const spec: PipelineSpec = {
46
- alias: 'vib:story:design', version: 1,
47
- steps: [
48
- { step: 'screens' },
49
- { step: 'ux', after: ['screens'] },
50
- { step: 'data', after: ['screens'] }, // runs BESIDE `ux`
51
- { step: 'persist', after: ['ux', 'data'] }, // joins BOTH
52
- ],
53
- }
54
-
55
- const pipeline = makePipeline<State, Deps>(spec, { steps, runs, checkpointer, fatal })
56
- await pipeline.invoke(seed, { runId, deps, scope: projectId })
57
- await pipeline.resume(runId, { deps, from: 'data' })
58
- ```
59
-
60
- **`StateGraph`, not the functional API.** A resume must be able to name the step it starts at, and
61
- only a graph has named nodes; `entrypoint`/`task` identifies a task by its positional call ordinal,
62
- which renumbers on any edit to the function — under a file watcher that is the common case, not the
63
- corner case. The functional API stays exactly where it is, inside `makeAgentModel`'s ReAct loop.
64
-
65
- **Edges are `after: string[]`.** Several steps naming one predecessor fan out; one step naming
66
- several joins them, and the join waits for ALL of them (the array form of `addEdge`, which is a
67
- barrier — separate single edges would fire on the first). Branching is `skipWhen`, never a
68
- conditional edge: a branch expressed as an edge is invisible to a resume, while a branch expressed
69
- as a guard is the same mechanism that makes a resume correct.
70
-
71
- **The graph is compiled per run and the steps close over their dependencies.** Passing collaborators
72
- through the engine's config would make a run depend on which config keys a given LangGraph minor
73
- propagates into a node body. A closure cannot be lost.
74
-
75
- ## Pipeline plugins
76
-
77
- ```ts
78
- interface PipelinePlugin<S, C> {
79
- alias: string; order?: number // seated by alias, run by ascending order
80
- enter?: (event) => void // once per invoke/resume: mode fresh|continue|restart, inherited steps, parent
81
- beforeStep?: (event) => { results?: StepResults } | void // the step WILL run; may hand it ctx.results
82
- afterStep?: (event) => void // patch merged into state, BEFORE the row commit marks the step done
83
- passStep?: (event) => void // reason: 'skipped' (guard) | 'failed' (optional step, run goes on)
84
- exit?: (event) => void // once, with the result the caller is about to receive
85
- }
86
- ```
87
-
88
- **A plugin watches a run; it never writes the state and never decides where the run stands.**
89
- Whatever it keeps, it keeps in a store of its own. `afterStep` runs before the commit so a plugin's
90
- own write lands before the step is durably done — a crash between the two re-runs the step rather
91
- than leaving a done step with nothing recorded about it.
92
-
93
- **A plugin that throws costs a warning, never the run** — unless `options.fatal` calls the error
94
- fatal: then it takes the exact path a fatal step error takes, the row written `Failed` first and the
95
- error rethrown (from `enter`, `passStep` and `exit` too). The runner's stop signal passes through.
96
-
97
- **Seating nothing changes nothing.** Every hook site is guarded, so a pipeline without plugins awaits
98
- nothing it did not await before — its parallel steps interleave identically and its step context
99
- has no `results` key. `tests/pipeline-compat.spec.ts` pins this with golden rows, trace lines and
100
- progress events recorded from the runner before the seam existed; it must stay green unedited.
101
-
102
- `asStep` passes the composing step to the child as `PipelineInvokeArgs.parent` (read-only:
103
- pipeline, run id, step, and the parent step's `results`); the child's plugins see it on `enter`.
104
-
105
- ## Cumulative pipeline results
106
-
107
- After each step, code reads what the step produced into FACTS (`CumulativeResultFact`: a type's
108
- shape and import specifier, an endpoint's method/path/guard, a resource's table, a file) and every
109
- later step is told them — so it uses the authoritative names instead of re-deriving them.
110
-
111
- ```ts
112
- const pipeline = makePipeline<State, Deps>(spec, {
113
- steps, runs,
114
- plugins: [cumulativeResultsPlugin<State, Deps>({
115
- spec: { steps: { types: { extractors: ['ts-types'], full: ['endpoints'] } } }, // or run => spec | null
116
- extractors: { 'ts-types': { scope: ({ deps }) => deps.glob('src/types/**'), extract: readTypes } },
117
- store, // CumulativeResultStore; share it with composed pipelines
118
- })],
119
- })
120
- // inside a step:
121
- const exec = executions().withResults(taskExec, ctx.results?.view) // every model built from it composes the view
122
- ctx.results?.record({ files: written }) // what the step touched
123
- ctx.results?.facts({ kind: 'type', name: 'User' }) // typed query for code
124
- ```
125
-
126
- - **Extractors are deterministic code, never a model call.** A rebuild after a crash must find
127
- exactly what the first pass found. `scope` recomputes the files an extractor answers for from
128
- DURABLE inputs (state keys, the file tree), never from what a step reported — it is what a rebuild
129
- reads. Declarations name extractors; the plugin holds the code.
130
- - **The ledger is an optimization, never the authority.** A fresh start or restart clears the run's
131
- own entries (and those of runs composed under it); a continuation loads them and silently
132
- REBUILDS any missing entry of a finished step from its durable scope. Resume correctness never
133
- depends on the store; a failing store is a warning. A step's `record({ facts })`-only facts with
134
- no extractor cannot be rebuilt.
135
- - **A view is cut once per step**: predecessors only (siblings may not have run), full within
136
- `window` edges (default 2) or when the producer names the step in `full` (`'*'` for every step),
137
- names only beyond, never shown when named in `omit`. Over `maxChars` the OLDEST entries not naming
138
- the step go to names only, then out (listed in `view.omitted`); named ones last; an entry's own
139
- text is never cut. `facts()` answers from every visible entry regardless of the prompt budget.
140
- - **A fact belongs to the first entry that produced it** — the step's predecessors and the runs
141
- composed under the step itself. A later re-extraction never duplicates it.
142
- - **A later step that rewrites an earlier entry's files refreshes it**: re-extracted from its own
143
- sources, revision bumped, facts about untouched files kept (`refresh: false` opts out). Entries of
144
- other pipelines are never refreshed — their extractors live elsewhere.
145
- - **A skipped step** keeps a stored entry, or gets one rebuilt from its files; **a failed optional
146
- step** gets a `partial` entry from what it left behind.
147
- - **Model summaries are OFF unless a step declares `summary`** and the plugin has `summarize`.
148
- The request carries a ready prompt and schema; the answer is rendered deterministically, capped
149
- in code, always labelled "not verified", and a failure is a warning without the summary.
150
- - **A composed run shares its parent's ledger** (parent's, else the root run id); its steps see the
151
- parent's view for the composing step, and the parent's later steps see the child's entries,
152
- labelled `<composingStep>/<childStep>` and ordered inside the composing step. Both pipelines must
153
- be given the SAME store.
154
- - **Seeds** are facts known before any step ran, visible to every step. A declaration resolved to
155
- `null`/`false` leaves that run without `ctx.results` at all.
156
-
157
39
  ## The agent plugin seam
158
40
 
159
41
  ```ts
@@ -173,10 +55,27 @@ itself does not know those features exist.
173
55
 
174
56
  ## Rules
175
57
 
176
- **Contributed context goes to `PromptBlock.Context` and nowhere else.** It is the only block a
177
- provider will not put a cache breakpoint on — the Anthropic plugin explicitly refuses to mark a
178
- trailing `Context`. Volatile material anywhere above it invalidates the `Role` + `Skills` prefix
179
- that every call sharing a persona pays for.
58
+ **Contributed context goes to `PromptBlock.Context` and nowhere else.** `AgentOptions.context`,
59
+ `invoke`'s `args.context` and every plugin's `context()` land there — in the project message, the
60
+ user turn right after the system — below the system's cached `Role` + `Skills` and `Packages`
61
+ prefix that every run of a persona shares. The project message is stable for one run and is cached
62
+ for its rounds; volatile material anywhere above it would cost every run sharing the persona a
63
+ cache write.
64
+
65
+ **The loop's prompt is composed once and marked per round.** The run sends
66
+ `[system, project message, opening, …rounds]`: the system as real text in a `SystemMessage` (never a
67
+ `JSON.stringify`d block array), and the composed Tail on a COPY of the opening, so the transcript
68
+ keeps the caller's message. Each round hands the wire to the provider plugin's
69
+ `loopCache(wire, agentModel, hints)` — it strips and re-marks copies, the opening at its boundaries
70
+ and the growing conversation read back round after round — and streams with the call options it
71
+ returns. The plugin is `AgentOptions.provider ?? llmPluginRegistry.resolveFor(agentModel)`, resolved
72
+ on the unbound model. `cacheSystem: false` on the persona sends no markers. Detail:
73
+ [[llm-prompt-caching]].
74
+
75
+ **`spectate` gets the turn and the answering model.** `AgentSpectateHook(input, message, action,
76
+ retries, startedAt?, model?)` is called once per round with the turn number as `retries` and the
77
+ agent model's id as `model`, after `cacheHelper.withCacheTiers` has carried a 1-hour cache write
78
+ into the usage.
180
79
 
181
80
  **A run that exceeds `maxTurns` throws `AgentLoopExhaustedError`.** The ceiling counts tool rounds
182
81
  (`DEFAULT_MAX_TURNS` is 64, `AgentOptions.maxTurns` overrides it), and it is reached only when the
@@ -237,41 +136,6 @@ forgetting, which is not a decision one caller has the standing to take. A node
237
136
  the context window on knowledge the run cannot tell apart from what it needs; the agent pulls what it
238
137
  wants by name.
239
138
 
240
- **The run ROW is the authority on where a run stands — never the LangGraph checkpoint.** The
241
- checkpoint is size-guarded and expires, so a design that reads a run's position out of it has a
242
- silent hole exactly where a crashed run needs an answer. Every reader of a position — a resume, a
243
- reconciler, an operator, a status endpoint — reads the row. With no checkpointer bound at all, a
244
- resume is still correct; the checkpointer buys replay, never correctness.
245
-
246
- **Every step is guarded twice.** The runner refuses to re-enter a step its row calls complete, and
247
- the application's `skipWhen` reads a durable marker the step itself wrote. The first covers a clean
248
- crash; the second covers a crash BETWEEN the side effect and the row write, which the first cannot
249
- see. A step that does N undoable things in a loop marks a cursor with `ctx.mark`.
250
-
251
- **A pipeline state is scalars and KEYS.** Ids, revisions, markers, lists of codes — never an
252
- artifact. The state is serialized into the row at every step boundary, so a field carrying a
253
- document writes one copy of it per step. Over `maxStateChars` the step FAILS: refused, never
254
- truncated, because a resume replaying from a cut-down state replays from a state that never existed.
255
-
256
- **A step failure is an OUTCOME, not a throw.** `invoke`/`resume` return
257
- `{ status: Failed, failedAt, error }`, because every caller has a status, a warning or a record to
258
- write before it decides anything, and a throwing runner puts that in a `catch` where it gets
259
- forgotten. The one exception is `options.fatal`: those are written to the row Failed FIRST and then
260
- rethrown, so an exhausted budget cannot become a result a caller carries on from.
261
-
262
- **No default retry.** `attempts` defaults to 1, because retry budgets in this family MULTIPLY — an
263
- outer retry around a model's own inner retry is their product. `attempts > 1` on a `nonIdempotent`
264
- step is refused at build time: an automatic retry is exactly what that flag says must not happen.
265
-
266
- **`nonIdempotent` marks a step whose side effect cannot be undone** — a wipe, a purge, a claim
267
- against a rate-limited authority, a model call whose output is already on disk. `resume({ from })`
268
- refuses to re-enter a completed one without `force`.
269
-
270
- **A ReAct run is not a resumable unit, and that is why `makeAgentModel` still builds its
271
- `entrypoint` inside `invoke()`.** Its state is an unbounded message list whose tool results are side
272
- effects already applied to the world: re-entering a turn re-applies them. What IS resumable is a
273
- pipeline — and an agent run belongs inside one of its steps.
274
-
275
139
  **`toolHelper.safeInvokeTool(tools, call, fatal?)` takes a fatal predicate.** Containment is right for a bad
276
140
  argument and wrong for an exhausted budget: a tool may be a whole pipeline behind one call, and
277
141
  handing the model a readable "out of tokens" is an invitation to pick another tool and spend again.
@@ -280,19 +144,18 @@ handing the model a readable "out of tokens" is an invitation to pick another to
280
144
  name first and only re-reads it as a serialized token once the provider throws — returning null
281
145
  would break every restore.
282
146
 
283
- ## Testing
284
-
285
- Category A (unit, no env, no network). The model is doubled with a small scripted object in
286
- `tests/_tools/model.ts` because `@langchain/core`'s own `FakeStreamingChatModel` always replays its
287
- first response and so cannot drive a tool loop. That double stands in for the MODEL, an external
288
- boundary — never for an `@owlmeans/*` package. The cumulative-results specs keep the project as a
289
- `Map` of files in `deps` — the durable input extractors read — so a "new process" is a new plugin
290
- over the same files.
291
-
292
147
  ## Related
293
148
 
294
149
  - [[agent-common]] — the serializable records and the run lifecycle flow
295
150
  - [[llm]] — `Execution`, the model contract and the `advise`-only `ExecutionPlugin`
296
151
  - [[agent-checkpoint]] — the durable Mongo implementation of both storage ports (internal)
297
152
  - [[llm-prompt-caching]] — which block contributed context lands in, and why
298
- - [[agent-skills]] — `projectSkillsAgentPlugin` and the `read_skill` tool
153
+ - [[agent-skills]] — `owlmeansPackagesPlugin`, the prompt plugin that loads package skills on agent runs too
154
+
155
+ ## Parts
156
+
157
+ | Part | Load it when |
158
+ |---|---|
159
+ | [pipelines](parts/pipelines.md) | declaring or running a pipeline, writing a pipeline plugin, or resuming a run |
160
+ | [cumulative-results](parts/cumulative-results.md) | telling a pipeline's later steps what earlier steps produced, or writing an extractor |
161
+ | [testing](parts/testing.md) | writing tests that drive an agent loop or a pipeline |
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: cumulative-results
3
+ description: Cumulative pipeline results — deterministic extractors, the ledger and its rebuild, how a step's view is cut, refresh, model summaries, composed runs and seeds.
4
+ metadata:
5
+ purposes: agent, job
6
+ mentions: cumulativeResultsPlugin, CUMULATIVE_RESULTS_PLUGIN, CumulativeResultStore, CumulativeResultFact, DEFAULT_RESULT_SUMMARY_SCHEMA, ctx.results, withResults
7
+ ---
8
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
9
+
10
+ # @owlmeans/agent — Cumulative pipeline results
11
+
12
+ ## Cumulative pipeline results
13
+
14
+ After each step, code reads what the step produced into FACTS (`CumulativeResultFact`: a type's
15
+ shape and import specifier, an endpoint's method/path/guard, a resource's table, a file) and every
16
+ later step is told them — so it uses the authoritative names instead of re-deriving them.
17
+
18
+ ```ts
19
+ const pipeline = makePipeline<State, Deps>(spec, {
20
+ steps, runs,
21
+ plugins: [cumulativeResultsPlugin<State, Deps>({
22
+ spec: { steps: { types: { extractors: ['ts-types'], full: ['endpoints'] } } }, // or run => spec | null
23
+ extractors: { 'ts-types': { scope: ({ deps }) => deps.glob('src/types/**'), extract: readTypes } },
24
+ store, // CumulativeResultStore; share it with composed pipelines
25
+ })],
26
+ })
27
+ // inside a step:
28
+ const exec = executions().withResults(taskExec, ctx.results?.view) // every model built from it composes the view
29
+ ctx.results?.record({ files: written }) // what the step touched
30
+ ctx.results?.facts({ kind: 'type', name: 'User' }) // typed query for code
31
+ ```
32
+
33
+ - **Extractors are deterministic code, never a model call.** A rebuild after a crash must find
34
+ exactly what the first pass found. `scope` recomputes the files an extractor answers for from
35
+ DURABLE inputs (state keys, the file tree), never from what a step reported — it is what a rebuild
36
+ reads. Declarations name extractors; the plugin holds the code.
37
+ - **The ledger is an optimization, never the authority.** A fresh start or restart clears the run's
38
+ own entries (and those of runs composed under it); a continuation loads them and silently
39
+ REBUILDS any missing entry of a finished step from its durable scope. Resume correctness never
40
+ depends on the store; a failing store is a warning. A step's `record({ facts })`-only facts with
41
+ no extractor cannot be rebuilt.
42
+ - **A view is cut once per step**: predecessors only (siblings may not have run), full within
43
+ `window` edges (default 2) or when the producer names the step in `full` (`'*'` for every step),
44
+ names only beyond, never shown when named in `omit`. Over `maxChars` the OLDEST entries not naming
45
+ the step go to names only, then out (listed in `view.omitted`); named ones last; an entry's own
46
+ text is never cut. `facts()` answers from every visible entry regardless of the prompt budget.
47
+ - **A fact belongs to the first entry that produced it** — the step's predecessors and the runs
48
+ composed under the step itself. A later re-extraction never duplicates it.
49
+ - **A later step that rewrites an earlier entry's files refreshes it**: re-extracted from its own
50
+ sources, revision bumped, facts about untouched files kept (`refresh: false` opts out). Entries of
51
+ other pipelines are never refreshed — their extractors live elsewhere.
52
+ - **A skipped step** keeps a stored entry, or gets one rebuilt from its files; **a failed optional
53
+ step** gets a `partial` entry from what it left behind.
54
+ - **Model summaries are OFF unless a step declares `summary`** and the plugin has `summarize`.
55
+ The request carries a ready prompt and schema; the answer is rendered deterministically, capped
56
+ in code, always labelled "not verified", and a failure is a warning without the summary.
57
+ - **A composed run shares its parent's ledger** (parent's, else the root run id); its steps see the
58
+ parent's view for the composing step, and the parent's later steps see the child's entries,
59
+ labelled `<composingStep>/<childStep>` and ordered inside the composing step. Both pipelines must
60
+ be given the SAME store.
61
+ - **Seeds** are facts known before any step ran, visible to every step. A declaration resolved to
62
+ `null`/`false` leaves that run without `ctx.results` at all.
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: pipelines
3
+ description: Resumable pipelines over a checkpointed StateGraph — steps and edges, the PipelinePlugin seam, and the rules for run rows, step guards, state size, failures and retries.
4
+ metadata:
5
+ purposes: agent, job, state
6
+ mentions: makePipeline, PipelineSpec, PipelinePlugin, asStep, skipWhen, nonIdempotent, makeCheckpointSaver, PipelineRunStore, CheckpointStore, maxStateChars, ctx.mark, @owlmeans/agent/pipeline
7
+ ---
8
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
9
+
10
+ # @owlmeans/agent — Pipelines
11
+
12
+ ## Pipelines
13
+
14
+ A pipeline is a **state machine over named steps** whose position is persisted at every step
15
+ boundary, so a run killed anywhere continues from the step it stopped at rather than from zero.
16
+
17
+ ```ts
18
+ const spec: PipelineSpec = {
19
+ alias: 'vib:story:design', version: 1,
20
+ steps: [
21
+ { step: 'screens' },
22
+ { step: 'ux', after: ['screens'] },
23
+ { step: 'data', after: ['screens'] }, // runs BESIDE `ux`
24
+ { step: 'persist', after: ['ux', 'data'] }, // joins BOTH
25
+ ],
26
+ }
27
+
28
+ const pipeline = makePipeline<State, Deps>(spec, { steps, runs, checkpointer, fatal })
29
+ await pipeline.invoke(seed, { runId, deps, scope: projectId })
30
+ await pipeline.resume(runId, { deps, from: 'data' })
31
+ ```
32
+
33
+ **`StateGraph`, not the functional API.** A resume must be able to name the step it starts at, and
34
+ only a graph has named nodes; `entrypoint`/`task` identifies a task by its positional call ordinal,
35
+ which renumbers on any edit to the function — under a file watcher that is the common case, not the
36
+ corner case. The functional API stays exactly where it is, inside `makeAgentModel`'s ReAct loop.
37
+
38
+ **Edges are `after: string[]`.** Several steps naming one predecessor fan out; one step naming
39
+ several joins them, and the join waits for ALL of them (the array form of `addEdge`, which is a
40
+ barrier — separate single edges would fire on the first). Branching is `skipWhen`, never a
41
+ conditional edge: a branch expressed as an edge is invisible to a resume, while a branch expressed
42
+ as a guard is the same mechanism that makes a resume correct.
43
+
44
+ **The graph is compiled per run and the steps close over their dependencies.** Passing collaborators
45
+ through the engine's config would make a run depend on which config keys a given LangGraph minor
46
+ propagates into a node body. A closure cannot be lost.
47
+
48
+ ## Pipeline plugins
49
+
50
+ ```ts
51
+ interface PipelinePlugin<S, C> {
52
+ alias: string; order?: number // seated by alias, run by ascending order
53
+ enter?: (event) => void // once per invoke/resume: mode fresh|continue|restart, inherited steps, parent
54
+ beforeStep?: (event) => { results?: StepResults } | void // the step WILL run; may hand it ctx.results
55
+ afterStep?: (event) => void // patch merged into state, BEFORE the row commit marks the step done
56
+ passStep?: (event) => void // reason: 'skipped' (guard) | 'failed' (optional step, run goes on)
57
+ exit?: (event) => void // once, with the result the caller is about to receive
58
+ }
59
+ ```
60
+
61
+ **A plugin watches a run; it never writes the state and never decides where the run stands.**
62
+ Whatever it keeps, it keeps in a store of its own. `afterStep` runs before the commit so a plugin's
63
+ own write lands before the step is durably done — a crash between the two re-runs the step rather
64
+ than leaving a done step with nothing recorded about it.
65
+
66
+ **A plugin that throws costs a warning, never the run** — unless `options.fatal` calls the error
67
+ fatal: then it takes the exact path a fatal step error takes, the row written `Failed` first and the
68
+ error rethrown (from `enter`, `passStep` and `exit` too). The runner's stop signal passes through.
69
+
70
+ **Seating nothing changes nothing.** Every hook site is guarded, so a pipeline without plugins awaits
71
+ nothing it did not await before — its parallel steps interleave identically and its step context
72
+ has no `results` key. `tests/pipeline-compat.spec.ts` pins this with golden rows, trace lines and
73
+ progress events recorded from the runner before the seam existed; it must stay green unedited.
74
+
75
+ `asStep` passes the composing step to the child as `PipelineInvokeArgs.parent` (read-only:
76
+ pipeline, run id, step, and the parent step's `results`); the child's plugins see it on `enter`.
77
+
78
+ ## Rules
79
+
80
+ **The run ROW is the authority on where a run stands — never the LangGraph checkpoint.** The
81
+ checkpoint is size-guarded and expires, so a design that reads a run's position out of it has a
82
+ silent hole exactly where a crashed run needs an answer. Every reader of a position — a resume, a
83
+ reconciler, an operator, a status endpoint — reads the row. With no checkpointer bound at all, a
84
+ resume is still correct; the checkpointer buys replay, never correctness.
85
+
86
+ **Every step is guarded twice.** The runner refuses to re-enter a step its row calls complete, and
87
+ the application's `skipWhen` reads a durable marker the step itself wrote. The first covers a clean
88
+ crash; the second covers a crash BETWEEN the side effect and the row write, which the first cannot
89
+ see. A step that does N undoable things in a loop marks a cursor with `ctx.mark`.
90
+
91
+ **A pipeline state is scalars and KEYS.** Ids, revisions, markers, lists of codes — never an
92
+ artifact. The state is serialized into the row at every step boundary, so a field carrying a
93
+ document writes one copy of it per step. Over `maxStateChars` the step FAILS: refused, never
94
+ truncated, because a resume replaying from a cut-down state replays from a state that never existed.
95
+
96
+ **A step failure is an OUTCOME, not a throw.** `invoke`/`resume` return
97
+ `{ status: Failed, failedAt, error }`, because every caller has a status, a warning or a record to
98
+ write before it decides anything, and a throwing runner puts that in a `catch` where it gets
99
+ forgotten. The one exception is `options.fatal`: those are written to the row Failed FIRST and then
100
+ rethrown, so an exhausted budget cannot become a result a caller carries on from.
101
+
102
+ **No default retry.** `attempts` defaults to 1, because retry budgets in this family MULTIPLY — an
103
+ outer retry around a model's own inner retry is their product. `attempts > 1` on a `nonIdempotent`
104
+ step is refused at build time: an automatic retry is exactly what that flag says must not happen.
105
+
106
+ **`nonIdempotent` marks a step whose side effect cannot be undone** — a wipe, a purge, a claim
107
+ against a rate-limited authority, a model call whose output is already on disk. `resume({ from })`
108
+ refuses to re-enter a completed one without `force`.
109
+
110
+ **A ReAct run is not a resumable unit, and that is why `makeAgentModel` still builds its
111
+ `entrypoint` inside `invoke()`.** Its state is an unbounded message list whose tool results are side
112
+ effects already applied to the world: re-entering a turn re-applies them. What IS resumable is a
113
+ pipeline — and an agent run belongs inside one of its steps.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: testing
3
+ description: Unit tests of agents and pipelines with a scripted model double and files kept in deps.
4
+ metadata:
5
+ purposes: test
6
+ mentions: FakeStreamingChatModel
7
+ ---
8
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
9
+
10
+ # @owlmeans/agent — Testing
11
+
12
+ ## Testing
13
+
14
+ Category A (unit, no env, no network). The model is doubled with a small scripted object in
15
+ `tests/_tools/model.ts` because `@langchain/core`'s own `FakeStreamingChatModel` always replays its
16
+ first response and so cannot drive a tool loop. That double stands in for the MODEL, an external
17
+ boundary — never for an `@owlmeans/*` package. The cumulative-results specs keep the project as a
18
+ `Map` of files in `deps` — the durable input extractors read — so a "new process" is a new plugin
19
+ over the same files.
@@ -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;AAKnB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,YAAa,YAAY,KAAG,UAgQtD,CAAA"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,UAAU,EAAE,YAAY,EACzB,MAAM,YAAY,CAAA;AA6BnB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,YAAa,YAAY,KAAG,UAmRtD,CAAA"}
package/build/model.js CHANGED
@@ -2,13 +2,35 @@ import { AIMessage, HumanMessage, SystemMessage, ToolMessage } from '@langchain/
2
2
  import { addMessages, entrypoint, task } from '@langchain/langgraph';
3
3
  import { createIdOfLength } from '@owlmeans/basic-ids';
4
4
  import { makeFlowModel } from '@owlmeans/flow';
5
- import { llmPluginRegistry } from '@owlmeans/llm';
5
+ import { PromptRegion } from '@owlmeans/llm-common';
6
+ import { cacheHelper, llmPluginRegistry } from '@owlmeans/llm';
6
7
  import { logger } from '@owlmeans/log';
7
8
  import { AgentRunStatus, AgentRunTransition, agentRunFlow, conversationFor, } from '@owlmeans/agent-common';
8
9
  import { DEFAULT_ACTION, DEFAULT_ENTRYPOINT, DEFAULT_MAX_TURNS, DEFAULT_PLUGIN_ORDER } from './consts.js';
9
10
  import { AgentLoopExhaustedError, AgentMissconfiguredError } from './errors.js';
10
11
  import { toolHelper } from './helpers/tools.js';
11
12
  const log = logger('agent');
13
+ /**
14
+ * A copy of the opening with the per-call tail after its payload — a string stays a string. The
15
+ * opening itself stays in the transcript untouched; only the wire carries the tail.
16
+ */
17
+ const withTail = (opening, tail) => {
18
+ if (opening.getType() !== 'human') {
19
+ return [opening, new HumanMessage({ content: tail })];
20
+ }
21
+ const content = typeof opening.content === 'string'
22
+ ? `${opening.content}\n\n${tail}`
23
+ : [...opening.content, { type: 'text', text: tail }];
24
+ return [new HumanMessage({
25
+ content, id: opening.id, name: opening.name, additional_kwargs: opening.additional_kwargs,
26
+ })];
27
+ };
28
+ /** The provider's id of the model a run talks to, for billing — the config's, else the client's own. */
29
+ const modelIdOf = (model) => {
30
+ const typed = model;
31
+ const id = typed.metadata?.config?.model ?? typed.model ?? typed.modelName;
32
+ return typeof id === 'string' ? id : undefined;
33
+ };
12
34
  /**
13
35
  * An OwlMeans agent over the LangGraph functional API.
14
36
  *
@@ -34,7 +56,8 @@ export const makeAgentModel = (options) => {
34
56
  }
35
57
  const conversation = options.conversation ?? conversationFor(exec.purpose);
36
58
  const prompts = options.prompts ?? exec.prompts;
37
- const provider = options.provider ?? llmPluginRegistry.pluginFor(agentModel);
59
+ const provider = options.provider ?? llmPluginRegistry.resolveFor(agentModel);
60
+ const modelId = modelIdOf(agentModel);
38
61
  const purpose = exec.purpose;
39
62
  const registry = [];
40
63
  const ordered = () => [...registry].sort((a, b) => (a.order ?? DEFAULT_PLUGIN_ORDER) - (b.order ?? DEFAULT_PLUGIN_ORDER));
@@ -107,26 +130,45 @@ export const makeAgentModel = (options) => {
107
130
  results: exec.results,
108
131
  })
109
132
  : null;
110
- const system = composed?.system?.content
111
- ?? (context.length > 0 ? context.join('\n\n') : '');
112
- const systemMessage = new SystemMessage(typeof system === 'string' ? system : JSON.stringify(system));
133
+ // The opening of every round: the system (a persona, stable across runs), the project
134
+ // message (this run's facts), and the caller's opening carrying the per-call tail. The system
135
+ // goes out as real text — its cache blocks are the provider plugin's, never a stringified array.
136
+ const system = composed != null ? composed.system ?? '' : context.join('\n\n');
137
+ const head = [
138
+ ...(system !== '' ? [new SystemMessage(system)] : []),
139
+ ...(composed?.project != null ? [new HumanMessage(composed.project)] : []),
140
+ ];
141
+ const hints = {
142
+ system: composed?.blocks.filter(block => block.region === PromptRegion.System),
143
+ ...(composed?.project != null ? { project: head.length - 1 } : {}),
144
+ ...(composed != null ? { ttl: composed.cache.ttl } : {}),
145
+ };
146
+ const cached = composed?.cache.enabled !== false;
113
147
  const tooled = agentModel.bindTools?.(Object.values(toolSet)) ?? agentModel;
114
- const ask = task('call-llm', async (messages) => {
148
+ const ask = task('call-llm', async ({ messages, turn }) => {
115
149
  const startedAt = Date.now();
116
- const stream = await tooled.stream([systemMessage, ...messages], { runName: action, metadata: { purpose } });
150
+ const [first, ...rest] = messages;
151
+ const opening = composed?.tail != null && first != null ? withTail(first, composed.tail) : [first];
152
+ const wire = [...head, ...opening, ...rest].filter(message => message != null);
153
+ // Each round strips and re-marks copies: the system and project message once, and the
154
+ // growing conversation read back from the round before.
155
+ const patch = cached
156
+ ? provider?.loopCache?.(wire, agentModel, hints)
157
+ : undefined;
158
+ const stream = await tooled.stream((patch?.messages ?? wire), { ...patch?.options, runName: action, metadata: { purpose } });
117
159
  let final;
118
160
  for await (const chunk of stream) {
119
161
  final = final != null ? final.concat(chunk) : chunk;
120
162
  }
121
- const result = new AIMessage(final);
122
- await spectate?.(messages, result, action, 0, startedAt);
163
+ const result = new AIMessage(cacheHelper.withCacheTiers(final));
164
+ await spectate?.(messages, result, action, turn, startedAt, modelId);
123
165
  return result;
124
166
  });
125
167
  // A rejected task aborts the whole superstep, killing every sibling call in the same batch —
126
168
  // `safeInvokeTool` is what keeps a bad argument from costing the work the others finished.
127
169
  const call = task('call-tool', async (toolCall) => toolHelper.safeInvokeTool(toolSet, toolCall, options.fatal));
128
170
  const agent = entrypoint(entrypointName, async (messages) => {
129
- let response = await ask(messages);
171
+ let response = await ask({ messages, turn: 0 });
130
172
  let turn = 0;
131
173
  while (true) {
132
174
  messages = addMessages(messages, [response]);
@@ -163,7 +205,7 @@ export const makeAgentModel = (options) => {
163
205
  });
164
206
  }));
165
207
  messages = addMessages(messages, results);
166
- response = await ask(messages);
208
+ response = await ask({ messages, turn });
167
209
  }
168
210
  return messages;
169
211
  });
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE9F,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EACL,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,eAAe,GAClE,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACzG,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAI/E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE/C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;AAE3B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAqB,EAAc,EAAE;IAClE,MAAM,EACJ,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,GAAG,kBAAkB,EAC5D,QAAQ,GAAG,iBAAiB,EAAE,UAAU,GAAG,IAAI,EAAE,QAAQ,GAC1D,GAAG,OAAO,CAAA;IAEX,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAK,IAAwB,CAAC,KAAK,CAAA;IACxE,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,wBAAwB,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAA;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;IAE5B,MAAM,QAAQ,GAAkB,EAAE,CAAA;IAClC,MAAM,OAAO,GAAG,GAAkB,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC/D,CAAC,CAAC,CAAC,KAAK,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,oBAAoB,CAAC,CAAC,CAAA;IAExE;;;;;;;;OAQG;IACH,MAAM,IAAI,GAAG,CAAC,MAAmB,EAAQ,EAAE;QACzC,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAA;QACpE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,EAAE,CAAC,GAAG,MAAM,CAAA;QACvB,CAAC;IACH,CAAC,CAAA;IAED,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,CAAA;IACd,CAAC;IAED,MAAM,KAAK,GAAe;QACxB,YAAY,EAAE,GAAG,EAAE,CAAC,YAAY;QAEhC,GAAG,EAAE,IAAI;QAET,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,cAAc,CAAA;YAC5C,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;YACxF,MAAM,UAAU,GAAG,OAAO,KAAK,KAAK,QAAQ;gBAC1C,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;YAE9D,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;YACvB,MAAM,IAAI,GAAc,MAAM,aAAa,CAAC,YAAY,CAAC,CAAA;YACzD,MAAM,GAAG,GAAa;gBACpB,EAAE,EAAE,gBAAgB,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;aAC/E,CAAA;YACD,IAAI,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;YAE9C,4FAA4F;YAC5F,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YAE9C,MAAM,WAAW,GAAa,EAAE,CAAA;YAChC,IAAI,OAAO,GAAiB,EAAE,GAAG,KAAK,EAAE,CAAA;YACxC,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;oBAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;wBACjC,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;4BACxB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;wBAChC,CAAC;oBACH,CAAC;oBACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAA;oBACjC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;wBAClB,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,EAAE,CAAA;oBACpC,CAAC;gBACH,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,wFAAwF;oBACxF,+EAA+E;oBAC/E,GAAG,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;gBACnF,CAAC;YACH,CAAC;YAED,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,WAAW,CAAC,CAAA;YAErF,qFAAqF;YACrF,qFAAqF;YACrF,+EAA+E;YAC/E,MAAM,QAAQ,GAAG,OAAO,IAAI,IAAI;gBAC9B,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,OAAO,CACvB,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,EAC3B,EAAE,EACF;oBACE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM;oBACzF,OAAO,EAAE,IAAI,CAAC,OAAO;iBACtB,CACF;gBACD,CAAC,CAAC,IAAI,CAAA;YACR,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,EAAE,OAAO;mBACnC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YACrD,MAAM,aAAa,GAAG,IAAI,aAAa,CACrC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAC7D,CAAA;YAED,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,UAAU,CAAA;YAE3E,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,QAA2B,EAAE,EAAE;gBACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;gBAE5B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAChC,CAAC,aAAa,EAAE,GAAG,QAAQ,CAAC,EAC5B,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,CAC3C,CAAA;gBAED,IAAI,KAAiC,CAAA;gBACrC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBACjC,KAAK,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;gBACrD,CAAC;gBAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,KAAM,CAAC,CAAA;gBACpC,MAAM,QAAQ,EAAE,CAAC,QAA4B,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,CAAC,CAAA;gBAE5E,OAAO,MAAM,CAAA;YACf,CAAC,CAAC,CAAA;YAEF,6FAA6F;YAC7F,2FAA2F;YAC3F,MAAM,IAAI,GAAG,IAAI,CACf,WAAW,EACX,KAAK,EAAE,QAAkB,EAAE,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAC1F,CAAA;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE,KAAK,EAAE,QAA2B,EAAE,EAAE;gBAC7E,IAAI,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAA;gBAClC,IAAI,IAAI,GAAG,CAAC,CAAA;gBAEZ,OAAO,IAAI,EAAE,CAAC;oBACZ,QAAQ,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;oBAE5C,IAAI,CAAC;wBACH,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAC,MAAM,EAAC,EAAE,CACzC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,QAAyB,CAAC,CAAC,CAAC,CAAA;oBACrD,CAAC;oBAAC,OAAO,CAAC,EAAE,CAAC;wBACX,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAA;oBAC5C,CAAC;oBAED,IAAI,QAAQ,CAAC,UAAU,IAAI,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACpE,MAAK;oBACP,CAAC;oBAED,IAAI,EAAE,IAAI,GAAG,QAAQ,EAAE,CAAC;wBACtB,MAAM,IAAI,uBAAuB,CAAC,GAAG,QAAQ,EAAE,CAAC,CAAA;oBAClD,CAAC;oBACD,IAAI,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;oBAE3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAC,QAAQ,EAAC,EAAE;wBACzE,IAAI,MAAM,GAAY,IAAI,CAAA;wBAC1B,IAAI,KAAK,GAAkB,IAAI,CAAA;wBAC/B,IAAI,CAAC;4BACH,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA;4BAC7B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;gCACtE,KAAK,GAAG,MAAM,CAAE,MAA6B,CAAC,KAAK,CAAC,CAAA;4BACtD,CAAC;wBACH,CAAC;wBAAC,OAAO,CAAC,EAAE,CAAC;4BACX,KAAK,GAAG,2BAA4B,CAAW,CAAC,OAAO,EAAE,CAAA;wBAC3D,CAAC;wBAED,OAAO,IAAI,WAAW,CAAC;4BACrB,YAAY,EAAE,QAAQ,CAAC,EAAG;4BAC1B,IAAI,EAAE,QAAQ,CAAC,IAAI;4BACnB,OAAO,EAAE,KAAK,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;4BAChF,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;yBAC5C,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAC,CAAA;oBAEH,QAAQ,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;oBACzC,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAA;gBAChC,CAAC;gBAED,OAAO,QAAQ,CAAA;YACjB,CAAC,CAAC,CAAA;YAEF,4FAA4F;YAC5F,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAE3C,IAAI,UAAU,GAAkB,CAAC,OAAO,CAAC,CAAA;YACzC,IAAI,MAAmB,CAAA;YACvB,IAAI,QAAQ,GAAG,KAAK,CAAA;YAEpB,MAAM,MAAM,GAAG,KAAK,EAAE,OAAwB,EAAiB,EAAE;gBAC/D,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAM;gBACR,CAAC;gBACD,QAAQ,GAAG,IAAI,CAAA;gBAEf,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;oBAC3B,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;oBAC/C,CAAC;oBAAC,OAAO,CAAC,EAAE,CAAC;wBACX,uFAAuF;wBACvF,qFAAqF;wBACrF,iDAAiD;wBACjD,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;oBAC/E,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,OAAO,CACV,OAAO,CAAC,MAAM,KAAK,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAC1F,OAAO,CAAC,MAAM,KAAK,cAAc,CAAC,EAAE,EACpC,OAAO,CAAC,KAAK,EAAE,OAAO,IAAI,OAAO,CAAC,IAAI,CACvC,CAAA;YACH,CAAC,CAAA;YAED,IAAI,CAAC;gBACH,IAAI,IAAI,GAA4B,EAAE,CAAA;gBACtC,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;oBACvD,IAAI,GAAG,IAA+B,CAAA;gBACxC,CAAC;gBAED,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAA8B,CAAA;gBAClE,UAAU,GAAG,QAAQ,IAAI,CAAC,OAAO,CAAC,CAAA;gBAClC,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,YAAY,SAAS,CAAc;uBACzF,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;gBAEnC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;gBAE/C,MAAM,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,CAAA;gBAErF,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC,CAAA;gBAC7C,CAAC;gBAED,OAAO,MAAM,CAAA;YACf,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,KAAK,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;gBAC3D,MAAM,GAAG;oBACP,OAAO,EAAE,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;oBACvC,QAAQ,EAAE,UAAU;oBACpB,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;iBAC1C,CAAA;gBAED,2FAA2F;gBAC3F,2FAA2F;gBAC3F,uFAAuF;gBACvF,0FAA0F;gBAC1F,6DAA6D;gBAC7D,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;gBAEtD,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;KACF,CAAA;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import type { SummarizeOptions } from './types.js';
10
10
  *
11
11
  * What it contributes is rendered as clearly delimited, explicitly untrusted material. These are
12
12
  * model words derived from user input, stored and replayed into a later prompt: they belong in the
13
- * volatile context block, described as a record of what happened, never as instructions.
13
+ * context block of the project message, described as a record of what happened, never as instructions.
14
14
  */
15
15
  export declare const summarizePlugin: (options?: SummarizeOptions) => AgentPlugin;
16
16
  //# sourceMappingURL=summarize.d.ts.map
@@ -12,7 +12,7 @@ import { compactionHelper } from '../helpers/compaction.js';
12
12
  *
13
13
  * What it contributes is rendered as clearly delimited, explicitly untrusted material. These are
14
14
  * model words derived from user input, stored and replayed into a later prompt: they belong in the
15
- * volatile context block, described as a record of what happened, never as instructions.
15
+ * context block of the project message, described as a record of what happened, never as instructions.
16
16
  */
17
17
  export const summarizePlugin = (options = {}) => {
18
18
  const { store, model, maxSummaryChars = DEFAULT_SUMMARY_CHARS, maxAdviceChars = DEFAULT_ADVICE_CHARS, window = DEFAULT_EVENT_WINDOW, action = 'agent-compaction', onEvent, } = options;
package/build/types.d.ts CHANGED
@@ -17,10 +17,11 @@ export interface AgentToolSet {
17
17
  * What the caller gets told about each model call.
18
18
  *
19
19
  * Shaped to match `spectate(spectator, callType)` from `@owlmeans/llm` exactly, so an application
20
- * that already has a spectator passes the curried function straight in.
20
+ * that already has a spectator passes the curried function straight in. `retries` carries the
21
+ * loop's turn number, and `model` the id of the model that answered.
21
22
  */
22
23
  export interface AgentSpectateHook {
23
- (input: ModelInputItem[], message: AIMessage, action: string, retries: number, startedAt?: number): Promise<unknown>;
24
+ (input: ModelInputItem[], message: AIMessage, action: string, retries: number, startedAt?: number, model?: string): Promise<unknown>;
24
25
  }
25
26
  export interface AgentOptions {
26
27
  /** The execution the run belongs to. Its `prompt` policy is the agent's persona. */
@@ -28,7 +29,10 @@ export interface AgentOptions {
28
29
  /** Overrides the model resolved from the execution. */
29
30
  agentModel?: BaseChatModel;
30
31
  tools: AgentToolSet;
31
- /** Static volatile context. Lands in `PromptBlock.Context`, never in the cached prefix. */
32
+ /**
33
+ * Static context. Lands in `PromptBlock.Context` — the project message, behind the cached
34
+ * system region — never in the system message other runs share.
35
+ */
32
36
  context?: string[];
33
37
  conversation?: ConversationRef;
34
38
  /** LangGraph entrypoint name; shows up in traces. */
@@ -41,7 +45,10 @@ export interface AgentOptions {
41
45
  * by default: the agent holds an execution, not the service that knows its policy.
42
46
  */
43
47
  utility?: () => BaseChatModel | undefined;
44
- /** Provider plugin used for cache placement. Resolved from the model when omitted. */
48
+ /**
49
+ * Provider plugin used for cache placement. Resolved from the model when omitted
50
+ * (`llmPluginRegistry.resolveFor`, so a model the service built gets its own provider's).
51
+ */
45
52
  provider?: LlmPlugin;
46
53
  maxTurns?: number;
47
54
  /**
@@ -65,7 +72,7 @@ export interface AgentOptions {
65
72
  export interface AgentInvokeArgs {
66
73
  /** LangChain `runName` for the model calls of this run. */
67
74
  action?: string;
68
- /** Extra volatile context for this call only. */
75
+ /** Extra context for this run only — it joins the project message. */
69
76
  context?: string[];
70
77
  }
71
78
  export interface AgentRunOutcome {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/agent",
3
- "version": "0.1.18-rc.52",
3
+ "version": "0.1.18-rc.54",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -68,14 +68,14 @@
68
68
  "@langchain/langgraph-checkpoint": "^1.1.5"
69
69
  },
70
70
  "dependencies": {
71
- "@owlmeans/agent-common": "^0.1.18-rc.49",
72
- "@owlmeans/basic-ids": "^0.1.18-rc.40",
73
- "@owlmeans/context": "^0.1.18-rc.34",
74
- "@owlmeans/error": "^0.1.18-rc.38",
75
- "@owlmeans/flow": "^0.1.18-rc.45",
76
- "@owlmeans/llm": "^0.1.18-rc.50",
77
- "@owlmeans/llm-common": "^0.1.18-rc.39",
78
- "@owlmeans/log": "^0.1.18-rc.3",
71
+ "@owlmeans/agent-common": "^0.1.18-rc.50",
72
+ "@owlmeans/basic-ids": "^0.1.18-rc.41",
73
+ "@owlmeans/context": "^0.1.18-rc.35",
74
+ "@owlmeans/error": "^0.1.18-rc.39",
75
+ "@owlmeans/flow": "^0.1.18-rc.46",
76
+ "@owlmeans/llm": "^0.1.18-rc.52",
77
+ "@owlmeans/llm-common": "^0.1.18-rc.40",
78
+ "@owlmeans/log": "^0.1.18-rc.4",
79
79
  "ajv": "^8.20.0"
80
80
  },
81
81
  "publishConfig": {
package/src/model.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { AIMessage, HumanMessage, SystemMessage, ToolMessage } from '@langchain/core/messages'
2
- import type { AIMessageChunk, BaseMessage, BaseMessageLike, ToolCall } from '@langchain/core/messages'
2
+ import type { AIMessageChunk, BaseMessage, BaseMessageLike, MessageFieldWithRole, ToolCall } from '@langchain/core/messages'
3
3
  import { addMessages, entrypoint, task } from '@langchain/langgraph'
4
4
  import { createIdOfLength } from '@owlmeans/basic-ids'
5
5
  import { makeFlowModel } from '@owlmeans/flow'
6
6
  import type { FlowModel } from '@owlmeans/flow'
7
- import { llmPluginRegistry } from '@owlmeans/llm'
8
- import type { HelperExecution, ModelInputItem } from '@owlmeans/llm'
7
+ import { PromptRegion } from '@owlmeans/llm-common'
8
+ import { cacheHelper, llmPluginRegistry } from '@owlmeans/llm'
9
+ import type { HelperExecution, LlmLoopCacheHints, ModelInputItem } from '@owlmeans/llm'
9
10
  import { logger } from '@owlmeans/log'
10
11
  import {
11
12
  AgentRunStatus, AgentRunTransition, agentRunFlow, conversationFor,
@@ -19,6 +20,30 @@ import { toolHelper } from './helpers/tools.js'
19
20
 
20
21
  const log = logger('agent')
21
22
 
23
+ /**
24
+ * A copy of the opening with the per-call tail after its payload — a string stays a string. The
25
+ * opening itself stays in the transcript untouched; only the wire carries the tail.
26
+ */
27
+ const withTail = (opening: BaseMessage, tail: string): BaseMessage[] => {
28
+ if (opening.getType() !== 'human') {
29
+ return [opening, new HumanMessage({ content: tail })]
30
+ }
31
+ const content = typeof opening.content === 'string'
32
+ ? `${opening.content}\n\n${tail}`
33
+ : [...opening.content, { type: 'text', text: tail }]
34
+
35
+ return [new HumanMessage({
36
+ content, id: opening.id, name: opening.name, additional_kwargs: opening.additional_kwargs,
37
+ } as ConstructorParameters<typeof HumanMessage>[0])]
38
+ }
39
+
40
+ /** The provider's id of the model a run talks to, for billing — the config's, else the client's own. */
41
+ const modelIdOf = (model: unknown): string | undefined => {
42
+ const typed = model as { metadata?: { config?: { model?: string } }, model?: unknown, modelName?: unknown }
43
+ const id = typed.metadata?.config?.model ?? typed.model ?? typed.modelName
44
+ return typeof id === 'string' ? id : undefined
45
+ }
46
+
22
47
  /**
23
48
  * An OwlMeans agent over the LangGraph functional API.
24
49
  *
@@ -49,7 +74,8 @@ export const makeAgentModel = (options: AgentOptions): AgentModel => {
49
74
 
50
75
  const conversation = options.conversation ?? conversationFor(exec.purpose)
51
76
  const prompts = options.prompts ?? exec.prompts
52
- const provider = options.provider ?? llmPluginRegistry.pluginFor(agentModel)
77
+ const provider = options.provider ?? llmPluginRegistry.resolveFor(agentModel)
78
+ const modelId = modelIdOf(agentModel)
53
79
  const purpose = exec.purpose
54
80
 
55
81
  const registry: AgentPlugin[] = []
@@ -136,20 +162,38 @@ export const makeAgentModel = (options: AgentOptions): AgentModel => {
136
162
  },
137
163
  )
138
164
  : null
139
- const system = composed?.system?.content
140
- ?? (context.length > 0 ? context.join('\n\n') : '')
141
- const systemMessage = new SystemMessage(
142
- typeof system === 'string' ? system : JSON.stringify(system),
143
- )
165
+ // The opening of every round: the system (a persona, stable across runs), the project
166
+ // message (this run's facts), and the caller's opening carrying the per-call tail. The system
167
+ // goes out as real text — its cache blocks are the provider plugin's, never a stringified array.
168
+ const system = composed != null ? composed.system ?? '' : context.join('\n\n')
169
+ const head: BaseMessage[] = [
170
+ ...(system !== '' ? [new SystemMessage(system)] : []),
171
+ ...(composed?.project != null ? [new HumanMessage(composed.project)] : []),
172
+ ]
173
+ const hints: LlmLoopCacheHints = {
174
+ system: composed?.blocks.filter(block => block.region === PromptRegion.System),
175
+ ...(composed?.project != null ? { project: head.length - 1 } : {}),
176
+ ...(composed != null ? { ttl: composed.cache.ttl } : {}),
177
+ }
178
+ const cached = composed?.cache.enabled !== false
144
179
 
145
180
  const tooled = agentModel.bindTools?.(Object.values(toolSet)) ?? agentModel
146
181
 
147
- const ask = task('call-llm', async (messages: BaseMessageLike[]) => {
182
+ const ask = task('call-llm', async ({ messages, turn }: { messages: BaseMessageLike[], turn: number }) => {
148
183
  const startedAt = Date.now()
149
184
 
185
+ const [first, ...rest] = messages as BaseMessage[]
186
+ const opening = composed?.tail != null && first != null ? withTail(first, composed.tail) : [first]
187
+ const wire = [...head, ...opening, ...rest].filter(message => message != null)
188
+ // Each round strips and re-marks copies: the system and project message once, and the
189
+ // growing conversation read back from the round before.
190
+ const patch = cached
191
+ ? provider?.loopCache?.(wire as unknown as MessageFieldWithRole[], agentModel, hints)
192
+ : undefined
193
+
150
194
  const stream = await tooled.stream(
151
- [systemMessage, ...messages],
152
- { runName: action, metadata: { purpose } },
195
+ (patch?.messages ?? wire) as BaseMessageLike[],
196
+ { ...patch?.options, runName: action, metadata: { purpose } } as Parameters<typeof tooled.stream>[1],
153
197
  )
154
198
 
155
199
  let final: AIMessageChunk | undefined
@@ -157,8 +201,8 @@ export const makeAgentModel = (options: AgentOptions): AgentModel => {
157
201
  final = final != null ? final.concat(chunk) : chunk
158
202
  }
159
203
 
160
- const result = new AIMessage(final!)
161
- await spectate?.(messages as ModelInputItem[], result, action, 0, startedAt)
204
+ const result = new AIMessage(cacheHelper.withCacheTiers(final!))
205
+ await spectate?.(messages as ModelInputItem[], result, action, turn, startedAt, modelId)
162
206
 
163
207
  return result
164
208
  })
@@ -171,7 +215,7 @@ export const makeAgentModel = (options: AgentOptions): AgentModel => {
171
215
  )
172
216
 
173
217
  const agent = entrypoint(entrypointName, async (messages: BaseMessageLike[]) => {
174
- let response = await ask(messages)
218
+ let response = await ask({ messages, turn: 0 })
175
219
  let turn = 0
176
220
 
177
221
  while (true) {
@@ -214,7 +258,7 @@ export const makeAgentModel = (options: AgentOptions): AgentModel => {
214
258
  }))
215
259
 
216
260
  messages = addMessages(messages, results)
217
- response = await ask(messages)
261
+ response = await ask({ messages, turn })
218
262
  }
219
263
 
220
264
  return messages
@@ -18,7 +18,7 @@ import { compactionHelper } from '../helpers/compaction.js'
18
18
  *
19
19
  * What it contributes is rendered as clearly delimited, explicitly untrusted material. These are
20
20
  * model words derived from user input, stored and replayed into a later prompt: they belong in the
21
- * volatile context block, described as a record of what happened, never as instructions.
21
+ * context block of the project message, described as a record of what happened, never as instructions.
22
22
  */
23
23
  export const summarizePlugin = (options: SummarizeOptions = {}): AgentPlugin => {
24
24
  const {
package/src/types.ts CHANGED
@@ -17,11 +17,13 @@ export interface AgentToolSet { [key: string]: StructuredToolInterface }
17
17
  * What the caller gets told about each model call.
18
18
  *
19
19
  * Shaped to match `spectate(spectator, callType)` from `@owlmeans/llm` exactly, so an application
20
- * that already has a spectator passes the curried function straight in.
20
+ * that already has a spectator passes the curried function straight in. `retries` carries the
21
+ * loop's turn number, and `model` the id of the model that answered.
21
22
  */
22
23
  export interface AgentSpectateHook {
23
24
  (
24
25
  input: ModelInputItem[], message: AIMessage, action: string, retries: number, startedAt?: number,
26
+ model?: string,
25
27
  ): Promise<unknown>
26
28
  }
27
29
 
@@ -31,7 +33,10 @@ export interface AgentOptions {
31
33
  /** Overrides the model resolved from the execution. */
32
34
  agentModel?: BaseChatModel
33
35
  tools: AgentToolSet
34
- /** Static volatile context. Lands in `PromptBlock.Context`, never in the cached prefix. */
36
+ /**
37
+ * Static context. Lands in `PromptBlock.Context` — the project message, behind the cached
38
+ * system region — never in the system message other runs share.
39
+ */
35
40
  context?: string[]
36
41
  conversation?: ConversationRef
37
42
  /** LangGraph entrypoint name; shows up in traces. */
@@ -44,7 +49,10 @@ export interface AgentOptions {
44
49
  * by default: the agent holds an execution, not the service that knows its policy.
45
50
  */
46
51
  utility?: () => BaseChatModel | undefined
47
- /** Provider plugin used for cache placement. Resolved from the model when omitted. */
52
+ /**
53
+ * Provider plugin used for cache placement. Resolved from the model when omitted
54
+ * (`llmPluginRegistry.resolveFor`, so a model the service built gets its own provider's).
55
+ */
48
56
  provider?: LlmPlugin
49
57
  maxTurns?: number
50
58
  /**
@@ -69,7 +77,7 @@ export interface AgentOptions {
69
77
  export interface AgentInvokeArgs {
70
78
  /** LangChain `runName` for the model calls of this run. */
71
79
  action?: string
72
- /** Extra volatile context for this call only. */
80
+ /** Extra context for this run only — it joins the project message. */
73
81
  context?: string[]
74
82
  }
75
83
 
@@ -22,16 +22,25 @@ export interface ScriptedModel {
22
22
  turns: () => number
23
23
  /** The message lists it was asked with, in order. */
24
24
  asked: () => unknown[][]
25
+ /** The call options of each ask, in order. */
26
+ options: () => Array<Record<string, unknown>>
25
27
  }
26
28
 
27
- export const scriptedModel = (script: ScriptedTurn[]): ScriptedModel => {
29
+ /**
30
+ * `extra` puts identity on the double — a `modelName`, the `metadata.config` a service-built model
31
+ * carries — so a provider plugin can recognise and size it.
32
+ */
33
+ export const scriptedModel = (script: ScriptedTurn[], extra: Record<string, unknown> = {}): ScriptedModel => {
28
34
  let at = 0
29
35
  const asked: unknown[][] = []
36
+ const options: Array<Record<string, unknown>> = []
30
37
 
31
38
  const model = {
39
+ ...extra,
32
40
  bindTools: () => model,
33
- stream: async (messages: unknown[]) => {
41
+ stream: async (messages: unknown[], callOptions: Record<string, unknown> = {}) => {
34
42
  asked.push(messages)
43
+ options.push(callOptions)
35
44
  const turn = script[Math.min(at, script.length - 1)]
36
45
  at += 1
37
46
 
@@ -46,5 +55,5 @@ export const scriptedModel = (script: ScriptedTurn[]): ScriptedModel => {
46
55
  },
47
56
  }
48
57
 
49
- return { model: model as unknown as BaseChatModel, turns: () => at, asked: () => asked }
58
+ return { model: model as unknown as BaseChatModel, turns: () => at, asked: () => asked, options: () => options }
50
59
  }
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
  import { tool } from '@langchain/core/tools'
3
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models'
3
4
  import * as z from 'zod'
4
- import { ExecutionLevel, PromptBlock } from '@owlmeans/llm-common'
5
- import { makePromptService } from '@owlmeans/llm'
6
- import type { Execution, PromptService } from '@owlmeans/llm'
5
+ import { ExecutionLevel, ModelProvider, PromptBlock } from '@owlmeans/llm-common'
6
+ import { anthropicPlugin, makePromptService, openAiPlugin } from '@owlmeans/llm'
7
+ import type { Execution, ModelConfig, PromptService } from '@owlmeans/llm'
7
8
  import { AgentRunStatus } from '@owlmeans/agent-common'
8
9
  import { makeAgentModel } from '../src/index.js'
9
10
  import type { AgentPlugin, AgentToolSet } from '../src/index.js'
@@ -115,9 +116,9 @@ describe('agent — plugins', () => {
115
116
  expect((finished[0] as { status: string }).status).toBe(AgentRunStatus.Ok)
116
117
  })
117
118
 
118
- test('contributed context lands in the volatile block, never in the cached prefix', async () => {
119
- // The Context block is the only one a provider will not put a cache breakpoint on. Volatile
120
- // material anywhere above it invalidates the prefix every call that shares the persona pays for.
119
+ test('contributed context lands in the project message, never in the cached system', async () => {
120
+ // The system region is what every run of the persona shares. Run-specific material anywhere in
121
+ // it invalidates the prefix every call that shares the persona pays for.
121
122
  const prompts = promptService()
122
123
  const composed = await prompts.compose(
123
124
  { role: 'You are a test agent.', context: ['REMEMBERED CONTEXT'] },
@@ -233,8 +234,85 @@ describe('agent — finalization', () => {
233
234
 
234
235
  await agent.invoke('go')
235
236
 
236
- const system = String(scripted.asked()[0][0].content)
237
- expect(system).toContain('the agent version')
238
- expect(system).not.toContain('the service version')
237
+ // The project message, right after the system, carries what the plugins contributed.
238
+ const project = String((scripted.asked()[0][1] as { content: unknown }).content)
239
+ expect(project).toContain('the agent version')
240
+ expect(project).not.toContain('the service version')
241
+ })
242
+ })
243
+
244
+ describe('agent — the prompt on the wire', () => {
245
+ const content = (message: unknown): unknown => (message as { content: unknown }).content
246
+ const marks = (message: unknown, key: string): number => Array.isArray(content(message))
247
+ ? (content(message) as Array<Record<string, unknown>>).filter(block => block[key] != null).length
248
+ : 0
249
+ const long = 'x'.repeat(400)
250
+ const anthropicExec = (prompts: () => PromptService): Execution => ({
251
+ ...execution(prompts), prompt: { role: `You are a test agent. ${long}`, skills: [] },
252
+ } as unknown as Execution)
253
+
254
+ // The system was stringified once — a JSON dump of content blocks sent as the persona.
255
+ test('the system goes out as text blocks, the project message second, the tail on a copy of the opening', async () => {
256
+ const scripted = scriptedModel([
257
+ { toolCalls: [{ name: 'echo', args: { value: 'hi' } }] },
258
+ { content: 'done' },
259
+ ], { modelName: 'claude-haiku-4-5', metadata: { config: { model: 'claude-haiku-4-5', cacheMinTokens: 1 } } })
260
+ const prompts = promptService()
261
+ prompts.register({ alias: 'percall', body: 'per-call skill' })
262
+ const agent = makeAgentModel({
263
+ exec: anthropicExec(() => prompts), agentModel: scripted.model, tools: { echo }, provider: anthropicPlugin,
264
+ context: ['RUN FACTS'],
265
+ })
266
+
267
+ const result = await agent.invoke('use the tool')
268
+
269
+ const [first, second] = scripted.asked()
270
+ expect(content(first![0])).toEqual([
271
+ { type: 'text', text: `You are a test agent. ${long}`, cache_control: { type: 'ephemeral' } },
272
+ ])
273
+ expect(JSON.stringify(content(first![1]))).toContain('RUN FACTS')
274
+ expect(marks(first![1], 'cache_control')).toBe(1)
275
+ // Three explicit markers at most, never on the final message; the automatic one moves forward.
276
+ for (const round of [first!, second!]) {
277
+ const explicit = round.reduce<number>((sum, message) => sum + marks(message, 'cache_control'), 0)
278
+ expect(explicit).toBeLessThanOrEqual(3)
279
+ expect(marks(round[round.length - 1], 'cache_control')).toBe(0)
280
+ }
281
+ expect(scripted.options()[1]!.cache_control).toEqual({ type: 'ephemeral' })
282
+ expect(second!.length).toBe(first!.length + 2)
283
+ // The transcript keeps the opening as the caller sent it.
284
+ expect(result.messages[0]!.content).toBe('use the tool')
285
+ })
286
+
287
+ test('the tail rides a copy of the opening, after the payload', async () => {
288
+ const scripted = scriptedModel([{ content: 'ok' }])
289
+ const prompts = makePromptService({}, `agent-spec-prompts-${++seq}`)
290
+ prompts.use({ alias: 'late', compose: ctx => ctx.add(PromptBlock.Tail, 'LATE PACKAGE') })
291
+ const agent = makeAgentModel({ exec: execution(() => prompts), agentModel: scripted.model, tools: {} })
292
+
293
+ const result = await agent.invoke('go')
294
+
295
+ const asked = scripted.asked()[0]!
296
+ expect(content(asked[asked.length - 1])).toBe('go\n\nLATE PACKAGE')
297
+ expect(result.messages[0]!.content).toBe('go')
298
+ })
299
+
300
+ // By instance alone every ChatOpenAI is the compatible plugin's, which caches nothing; the agent
301
+ // resolves the provider the service built the model with.
302
+ test('a service-built ChatOpenAI is cached through the OpenAI plugin', async () => {
303
+ const scripted = scriptedModel([{ content: 'ok' }])
304
+ const chat = openAiPlugin.build({
305
+ alias: 'spec', secret: 'sk-test', callbacks: [],
306
+ config: { alias: 'spec', provider: ModelProvider.OpenAI, model: 'gpt-6-sol', cacheMinTokens: 1 } as ModelConfig,
307
+ })
308
+ ;(chat as unknown as { bindTools: () => BaseChatModel }).bindTools = () => scripted.model
309
+ const agent = makeAgentModel({
310
+ exec: anthropicExec(promptService), agentModel: chat, tools: {}, context: ['RUN FACTS'],
311
+ })
312
+
313
+ await agent.invoke('go')
314
+
315
+ expect(JSON.stringify(scripted.asked()[0])).toContain('prompt_cache_breakpoint')
316
+ expect(scripted.options()[0]!.promptCacheOptions).toEqual({ mode: 'explicit' })
239
317
  })
240
318
  })