@owlmeans/agent 0.1.18-rc.7

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 (111) hide show
  1. package/README.md +93 -0
  2. package/agent-meta/manifest.json +16 -0
  3. package/agent-meta/skills/agent/SKILL.md +122 -0
  4. package/build/consts.d.ts +16 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +16 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/errors.d.ts +16 -0
  9. package/build/errors.d.ts.map +1 -0
  10. package/build/errors.js +27 -0
  11. package/build/errors.js.map +1 -0
  12. package/build/helpers/compaction.d.ts +46 -0
  13. package/build/helpers/compaction.d.ts.map +1 -0
  14. package/build/helpers/compaction.js +119 -0
  15. package/build/helpers/compaction.js.map +1 -0
  16. package/build/helpers/index.d.ts +4 -0
  17. package/build/helpers/index.d.ts.map +1 -0
  18. package/build/helpers/index.js +4 -0
  19. package/build/helpers/index.js.map +1 -0
  20. package/build/helpers/rolling.d.ts +25 -0
  21. package/build/helpers/rolling.d.ts.map +1 -0
  22. package/build/helpers/rolling.js +45 -0
  23. package/build/helpers/rolling.js.map +1 -0
  24. package/build/helpers/tools.d.ts +29 -0
  25. package/build/helpers/tools.d.ts.map +1 -0
  26. package/build/helpers/tools.js +36 -0
  27. package/build/helpers/tools.js.map +1 -0
  28. package/build/index.d.ts +12 -0
  29. package/build/index.d.ts.map +1 -0
  30. package/build/index.js +11 -0
  31. package/build/index.js.map +1 -0
  32. package/build/model.d.ts +15 -0
  33. package/build/model.d.ts.map +1 -0
  34. package/build/model.js +208 -0
  35. package/build/model.js.map +1 -0
  36. package/build/plugins/export.d.ts +8 -0
  37. package/build/plugins/export.d.ts.map +1 -0
  38. package/build/plugins/export.js +4 -0
  39. package/build/plugins/export.js.map +1 -0
  40. package/build/plugins/memory-events.d.ts +36 -0
  41. package/build/plugins/memory-events.d.ts.map +1 -0
  42. package/build/plugins/memory-events.js +83 -0
  43. package/build/plugins/memory-events.js.map +1 -0
  44. package/build/plugins/memory-graph.d.ts +52 -0
  45. package/build/plugins/memory-graph.d.ts.map +1 -0
  46. package/build/plugins/memory-graph.js +155 -0
  47. package/build/plugins/memory-graph.js.map +1 -0
  48. package/build/plugins/summarize.d.ts +44 -0
  49. package/build/plugins/summarize.d.ts.map +1 -0
  50. package/build/plugins/summarize.js +77 -0
  51. package/build/plugins/summarize.js.map +1 -0
  52. package/build/runtime/checkpoint.d.ts +37 -0
  53. package/build/runtime/checkpoint.d.ts.map +1 -0
  54. package/build/runtime/checkpoint.js +52 -0
  55. package/build/runtime/checkpoint.js.map +1 -0
  56. package/build/runtime/provider.d.ts +18 -0
  57. package/build/runtime/provider.d.ts.map +1 -0
  58. package/build/runtime/provider.js +30 -0
  59. package/build/runtime/provider.js.map +1 -0
  60. package/build/runtime/transport.d.ts +27 -0
  61. package/build/runtime/transport.d.ts.map +1 -0
  62. package/build/runtime/transport.js +29 -0
  63. package/build/runtime/transport.js.map +1 -0
  64. package/build/service.d.ts +14 -0
  65. package/build/service.d.ts.map +1 -0
  66. package/build/service.js +61 -0
  67. package/build/service.js.map +1 -0
  68. package/build/stores/index.d.ts +3 -0
  69. package/build/stores/index.d.ts.map +1 -0
  70. package/build/stores/index.js +2 -0
  71. package/build/stores/index.js.map +1 -0
  72. package/build/stores/memory.d.ts +6 -0
  73. package/build/stores/memory.d.ts.map +1 -0
  74. package/build/stores/memory.js +0 -0
  75. package/build/stores/memory.js.map +1 -0
  76. package/build/stores/types.d.ts +38 -0
  77. package/build/stores/types.d.ts.map +1 -0
  78. package/build/stores/types.js +2 -0
  79. package/build/stores/types.js.map +1 -0
  80. package/build/types.d.ts +130 -0
  81. package/build/types.d.ts.map +1 -0
  82. package/build/types.js +2 -0
  83. package/build/types.js.map +1 -0
  84. package/package.json +72 -0
  85. package/src/consts.ts +19 -0
  86. package/src/errors.ts +33 -0
  87. package/src/helpers/compaction.ts +172 -0
  88. package/src/helpers/index.ts +3 -0
  89. package/src/helpers/rolling.ts +68 -0
  90. package/src/helpers/tools.ts +46 -0
  91. package/src/index.ts +11 -0
  92. package/src/model.ts +269 -0
  93. package/src/plugins/export.ts +12 -0
  94. package/src/plugins/memory-events.ts +129 -0
  95. package/src/plugins/memory-graph.ts +217 -0
  96. package/src/plugins/summarize.ts +129 -0
  97. package/src/runtime/checkpoint.ts +89 -0
  98. package/src/runtime/provider.ts +35 -0
  99. package/src/runtime/transport.ts +50 -0
  100. package/src/service.ts +97 -0
  101. package/src/stores/index.ts +2 -0
  102. package/src/stores/memory.ts +0 -0
  103. package/src/stores/types.ts +45 -0
  104. package/src/types.ts +144 -0
  105. package/tests/_tools/model.ts +50 -0
  106. package/tests/agent.spec.ts +218 -0
  107. package/tests/plugins.spec.ts +257 -0
  108. package/tests/runtime.spec.ts +140 -0
  109. package/tests/summary.spec.ts +143 -0
  110. package/tests/tools.spec.ts +68 -0
  111. package/tsconfig.json +19 -0
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @owlmeans/agent
2
+
3
+ Context-aware LLM agents and pipelines over the LangGraph functional API, with pluggable memory and
4
+ storage-independent persistence.
5
+
6
+ Contracts live in [`@owlmeans/agent-common`](../agent-common); LangGraph and LangChain core are
7
+ **peer** dependencies.
8
+
9
+ ## Building an agent
10
+
11
+ ```ts
12
+ import { makeAgentModel } from '@owlmeans/agent'
13
+
14
+ const agent = makeAgentModel({
15
+ exec, // an @owlmeans/llm Execution — its prompt policy is the persona
16
+ tools, // { [name]: StructuredToolInterface }
17
+ context: [projectBrief], // volatile context; lands in PromptBlock.Context
18
+ })
19
+
20
+ const result = await agent.invoke('rename the dashboard header')
21
+ ```
22
+
23
+ Or through the context service, which pre-attaches the plugins registered on it:
24
+
25
+ ```ts
26
+ import { appendAgentsService } from '@owlmeans/agent'
27
+
28
+ appendAgentsService(context, { conversations, plugins: [summarizePlugin({ store })] })
29
+ const agent = context.agents().agent({ exec, tools })
30
+ ```
31
+
32
+ ## Plugins
33
+
34
+ `AgentPlugin` is the optional-capability seam: a plugin may contribute what the agent knows
35
+ (`context`), what it can do (`tools`), watch it work (`onTurn`), and act when it stops (`onFinish`).
36
+
37
+ | Plugin | What it adds |
38
+ |---|---|
39
+ | `summarizePlugin` | Compacts each finished run into `summary` + `advice`, and replays the last few on the way in |
40
+ | `memoryGraphPlugin` | Durable notes filed by subsystem, with links — index injected, content pulled by tool |
41
+ | `memoryEventsPlugin` | A bounded, ordered record of what happened |
42
+
43
+ Both memory plugins also export a plain API (`memoryGraph`, `memoryEvents`) usable with no agent at
44
+ all, so a pipeline helper writes to the same store an agent reads.
45
+
46
+ ## Things worth knowing before you change it
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.
51
+
52
+ **`safeInvokeTool` must never throw.** A rejected LangGraph task aborts the whole superstep: every
53
+ sibling tool call in the same parallel batch dies with it, discarding work they had already
54
+ finished. A tool failure comes back as `{ error }` the model can read and correct.
55
+
56
+ **Resolution is by `tool.name`, not by map key.** `bindTools` advertises the tool's own name, so a
57
+ map keyed by a local variable silently loses any tool whose two names drifted apart — advertised,
58
+ callable, and permanently "not found".
59
+
60
+ **`autoFinish: false` when something runs after the agent.** A compaction written before a
61
+ validation or build pass describes a state that did not survive it, so its "what to do next" is
62
+ advice about a world that no longer exists. Call `result.run.finish(outcome)` once the real outcome
63
+ is known; it is idempotent.
64
+
65
+ **Plugin failures never fail a run.** Contributing, per-turn work and finalization are all
66
+ swallowed with a warning. Memory is an enhancement; losing it costs context, throwing costs the work.
67
+
68
+ **Ports, not resources.** `ConversationStore` and friends are narrow interfaces a consumer
69
+ implements. An unbound port is not an error — the plugin that needs it becomes a no-op.
70
+
71
+ **Give the compaction call a `runName` your application filters.** Every model call carrying a
72
+ purpose is streamed to the client, so without it the summary of a run types itself out in the
73
+ user's view of that run, right after it finished.
74
+
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.
78
+
79
+ <!-- owlmeans:agent-guidance:start -->
80
+ ## Agent guidance
81
+
82
+ This package ships embedded agent skills under `agent-meta/`. After installing your
83
+ `@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
84
+ your project's skill store (`.agents/skills/`):
85
+
86
+ ```sh
87
+ npx @owlmeans/agent-skills
88
+ ```
89
+
90
+ The embedded files are version-matched to this package release. Do not edit them
91
+ directly — they are regenerated on each publish. To contribute guidance edits,
92
+ open a PR against the source monorepo.
93
+ <!-- owlmeans:agent-guidance:end -->
@@ -0,0 +1,16 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "package": "@owlmeans/agent",
4
+ "version": "0.1.18-rc.7",
5
+ "generatedAt": "2026-08-27T21:09:58.916Z",
6
+ "canonicalRepo": "https://github.com/owlmeans/common",
7
+ "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "agent",
11
+ "category": "package-specific",
12
+ "file": "skills/agent/SKILL.md",
13
+ "canonicalPath": ".agents/skills/agent/SKILL.md"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,122 @@
1
+ ---
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.
4
+ user-invocable: false
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # @owlmeans/agent
9
+
10
+ **Layer:** Cross-cutting domain
11
+ **Install:** `"@owlmeans/agent": "^0.1.18-rc.7"` in `dependencies`, plus the `@langchain/core` and
12
+ `@langchain/langgraph` **peers**
13
+
14
+ The agent runtime. Contracts live in `@owlmeans/agent-common`.
15
+
16
+ ## Key exports
17
+
18
+ | Export | Description |
19
+ |---|---|
20
+ | `makeAgentModel(options)` | An agent over the LangGraph functional API: `invoke`, `use`, `conversation`. |
21
+ | `makeAgentsService(options?, alias?)` · `appendAgentsService(ctx, options?, alias?)` · `agentServiceApi(options, self)` | The service, and its half without `createService` for composition. |
22
+ | `summarizePlugin(options?)` | Compacts each finished run into `summary` + `advice`; replays the last few. |
23
+ | `memoryGraphPlugin(options?)` · `memoryGraph(store, options?)` | Durable notes filed by subsystem, with links. Plugin **and** plain API. |
24
+ | `memoryEventsPlugin(options?)` · `memoryEvents(store, options?)` | A bounded, ordered record of what happened. |
25
+ | `safeInvokeTool`, `toErrorResponse`, `isToolError` | The never-throwing tool contract. |
26
+ | `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. |
29
+ | `inProcessTransport()`, `AgentTransport` | The scaling seam; default carries messages by direct call. |
30
+ | `createMemory*Store()` | In-memory reference implementations of every port. |
31
+
32
+ Subpath exports: `./plugins`, `./helpers`, `./stores`.
33
+
34
+ ## The plugin seam
35
+
36
+ ```ts
37
+ interface AgentPlugin {
38
+ alias: string
39
+ order?: number // lower first, default 50
40
+ context?: (run) => Promise<string[]> // what the agent knows
41
+ tools?: (run) => AgentToolSet // what it can do
42
+ onTurn?: (run, messages) => Promise<void> // watch it work
43
+ onFinish?: (run, result, outcome) => Promise<void> // act when it stops
44
+ }
45
+ ```
46
+
47
+ Registered with `agent.use(plugin)` or `service.use(plugin)`; seated **by alias**, so re-registering
48
+ replaces rather than duplicating. Everything memory- and summary-related is one of these; the loop
49
+ itself does not know those features exist.
50
+
51
+ ## Rules
52
+
53
+ **Contributed context goes to `PromptBlock.Context` and nowhere else.** It is the only block a
54
+ provider will not put a cache breakpoint on — the Anthropic plugin explicitly refuses to mark a
55
+ trailing `Context`. Volatile material anywhere above it invalidates the `Role` + `Skills` prefix
56
+ that every call sharing a persona pays for.
57
+
58
+ **`safeInvokeTool` must never throw.** The loop wraps it in a LangGraph `task`, and a rejected task
59
+ aborts the whole superstep: every sibling tool call in the same parallel batch dies with AbortError
60
+ and the run ends on "Multiple errors occurred during superstep 0", discarding work the others had
61
+ already finished. A tool failure comes back as `{ error }` the model can read and correct — most are
62
+ the model's own mistake, and the error text already names what was expected.
63
+
64
+ **Tools resolve by `tool.name`, with the map key as a fallback.** `bindTools` advertises the tool's
65
+ own name, so a map keyed by a local variable silently loses any tool whose two names drifted apart:
66
+ advertised, callable, permanently "not found".
67
+
68
+ **`compose()` is called with `files: exec.files`.** Without it, a prompt plugin that resolves
69
+ knowledge from disk is silently inert on agent runs while working fine on plain model calls.
70
+
71
+ **Use `autoFinish: false` whenever something runs after the agent.** A compaction written before a
72
+ validation or build pass describes a state that did not survive it, so its "what to do next" is
73
+ advice about a world that no longer exists. Call `result.run.finish(outcome)` once the real outcome
74
+ is known — it is idempotent, and a second call is a no-op rather than a second event.
75
+
76
+ **A failed run is still reported to `onFinish` before the error is rethrown.** A run that vanishes
77
+ from the history is one the next session repeats verbatim.
78
+
79
+ **Plugin failures never fail a run.** Contributing, per-turn work and finalization are each
80
+ swallowed with a warning. Memory is an enhancement: losing it costs context, throwing costs the work.
81
+
82
+ **Give the compaction call a `runName` the application filters.** Every model call carrying a purpose
83
+ is streamed to the client, so without it the summary of a run types itself out in the user's view of
84
+ that run, immediately after it finished.
85
+
86
+ **Character caps are applied after the model answers, never asked for in the prompt alone.** A cap in
87
+ a prompt is a request. Both composers (`composeCompaction`, `composeRollingSummary`) are total: with
88
+ no model, a failing model or an empty answer they fall back deterministically, so a caller can record
89
+ history unconditionally. A failed fold costs detail, never the event.
90
+
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.
96
+
97
+ **Memory writes merge, they do not replace.** Replacing would make every write a potential act of
98
+ forgetting, which is not a decision one caller has the standing to take. A node that outgrows
99
+ `maxNodeChars` is compacted through the cheap model, or head-truncated when there is none.
100
+
101
+ **Only the memory INDEX is injected — names and links, never content.** Bulk-injecting notes spends
102
+ the context window on knowledge the run cannot tell apart from what it needs; the agent pulls what it
103
+ wants by name.
104
+
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.
112
+
113
+ **`makeStaticFlowProvider` must throw on an unknown flow.** `makeFlowModel` reads a string as a flow
114
+ name first and only re-reads it as a serialized token once the provider throws — returning null
115
+ would break every restore.
116
+
117
+ ## Testing
118
+
119
+ Category A (unit, no env, no network). The model is doubled with a small scripted object in
120
+ `tests/_tools/model.ts` because `@langchain/core`'s own `FakeStreamingChatModel` always replays its
121
+ first response and so cannot drive a tool loop. That double stands in for the MODEL, an external
122
+ boundary — never for an `@owlmeans/*` package.
@@ -0,0 +1,16 @@
1
+ export { AGENTS_SERVICE } from '@owlmeans/agent-common';
2
+ /** Default LangGraph entrypoint name. Shows up in traces, so it is worth overriding per agent. */
3
+ export declare const DEFAULT_ENTRYPOINT = "owlmeans-agent";
4
+ /** Default action label for a model call, used as the LangChain `runName`. */
5
+ export declare const DEFAULT_ACTION = "agent-ask";
6
+ /**
7
+ * How many tool rounds one run may take before it is stopped.
8
+ *
9
+ * A model that keeps calling tools without ever answering is not rare — it is the ordinary failure
10
+ * mode of a loop whose tool results do not satisfy it. Without a ceiling the run consumes the
11
+ * caller's budget until something else kills it, which reads as a hang rather than a refusal.
12
+ */
13
+ export declare const DEFAULT_MAX_TURNS = 64;
14
+ /** Ordering weight of a plugin that declares none. */
15
+ export declare const DEFAULT_PLUGIN_ORDER = 50;
16
+ //# sourceMappingURL=consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEvD,kGAAkG;AAClG,eAAO,MAAM,kBAAkB,mBAAmB,CAAA;AAElD,8EAA8E;AAC9E,eAAO,MAAM,cAAc,cAAc,CAAA;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC,sDAAsD;AACtD,eAAO,MAAM,oBAAoB,KAAK,CAAA"}
@@ -0,0 +1,16 @@
1
+ export { AGENTS_SERVICE } from '@owlmeans/agent-common';
2
+ /** Default LangGraph entrypoint name. Shows up in traces, so it is worth overriding per agent. */
3
+ export const DEFAULT_ENTRYPOINT = 'owlmeans-agent';
4
+ /** Default action label for a model call, used as the LangChain `runName`. */
5
+ export const DEFAULT_ACTION = 'agent-ask';
6
+ /**
7
+ * How many tool rounds one run may take before it is stopped.
8
+ *
9
+ * A model that keeps calling tools without ever answering is not rare — it is the ordinary failure
10
+ * mode of a loop whose tool results do not satisfy it. Without a ceiling the run consumes the
11
+ * caller's budget until something else kills it, which reads as a hang rather than a refusal.
12
+ */
13
+ export const DEFAULT_MAX_TURNS = 64;
14
+ /** Ordering weight of a plugin that declares none. */
15
+ export const DEFAULT_PLUGIN_ORDER = 50;
16
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEvD,kGAAkG;AAClG,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAA;AAElD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAA;AAEzC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAEnC,sDAAsD;AACtD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { ResilientError } from '@owlmeans/error';
2
+ export declare class AgentError extends ResilientError {
3
+ static typeName: string;
4
+ constructor(message?: string);
5
+ }
6
+ /** The agent was built without something it cannot work around — a model, or a tool set. */
7
+ export declare class AgentMissconfiguredError extends AgentError {
8
+ static typeName: string;
9
+ constructor(message?: string);
10
+ }
11
+ /** The tool loop hit its turn ceiling without the model ever answering. */
12
+ export declare class AgentLoopExhaustedError extends AgentError {
13
+ static typeName: string;
14
+ constructor(message?: string);
15
+ }
16
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,UAAW,SAAQ,cAAc;IAC5C,OAAuB,QAAQ,EAAE,MAAM,CAA2C;IAElF,YAAY,OAAO,GAAE,MAAgB,EAEpC;CACF;AAED,4FAA4F;AAC5F,qBAAa,wBAAyB,SAAQ,UAAU;IACtD,OAAuB,QAAQ,EAAE,MAAM,CAAyC;IAEhF,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF;AAED,2EAA2E;AAC3E,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,OAAuB,QAAQ,EAAE,MAAM,CAAwC;IAE/E,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF"}
@@ -0,0 +1,27 @@
1
+ import { ResilientError } from '@owlmeans/error';
2
+ export class AgentError extends ResilientError {
3
+ static typeName = `AgentRuntime${ResilientError.typeName}`;
4
+ constructor(message = 'error') {
5
+ super(AgentError.typeName, `agent-runtime:${message}`);
6
+ }
7
+ }
8
+ /** The agent was built without something it cannot work around — a model, or a tool set. */
9
+ export class AgentMissconfiguredError extends AgentError {
10
+ static typeName = `Missconfigured${AgentError.typeName}`;
11
+ constructor(message = 'error') {
12
+ super(`missconfigured:${message}`);
13
+ this.type = AgentMissconfiguredError.typeName;
14
+ }
15
+ }
16
+ /** The tool loop hit its turn ceiling without the model ever answering. */
17
+ export class AgentLoopExhaustedError extends AgentError {
18
+ static typeName = `LoopExhausted${AgentError.typeName}`;
19
+ constructor(message = 'error') {
20
+ super(`loop-exhausted:${message}`);
21
+ this.type = AgentLoopExhaustedError.typeName;
22
+ }
23
+ }
24
+ ResilientError.registerErrorClass(AgentError);
25
+ ResilientError.registerErrorClass(AgentMissconfiguredError);
26
+ ResilientError.registerErrorClass(AgentLoopExhaustedError);
27
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAO,UAAW,SAAQ,cAAc;IACrC,MAAM,CAAU,QAAQ,GAAW,eAAe,cAAc,CAAC,QAAQ,EAAE,CAAA;IAElF,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,iBAAiB,OAAO,EAAE,CAAC,CAAA;IACxD,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,OAAO,wBAAyB,SAAQ,UAAU;IAC/C,MAAM,CAAU,QAAQ,GAAW,iBAAiB,UAAU,CAAC,QAAQ,EAAE,CAAA;IAEhF,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC,QAAQ,CAAA;IAC/C,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IAC9C,MAAM,CAAU,QAAQ,GAAW,gBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAA;IAE/E,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC,QAAQ,CAAA;IAC9C,CAAC;CACF;AAED,cAAc,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAA;AAC7C,cAAc,CAAC,kBAAkB,CAAC,wBAAwB,CAAC,CAAA;AAC3D,cAAc,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,CAAA"}
@@ -0,0 +1,46 @@
1
+ import type { BaseMessage } from '@langchain/core/messages';
2
+ import type { LlmModel } from '@owlmeans/llm';
3
+ import { AgentRunStatus } from '@owlmeans/agent-common';
4
+ export interface Compaction {
5
+ summary: string;
6
+ advice?: string;
7
+ }
8
+ export interface CompactionInput {
9
+ /** Omit to skip the model entirely and take the deterministic path. */
10
+ model?: LlmModel;
11
+ /** The ask that opened the run. */
12
+ prompt: string;
13
+ messages: readonly BaseMessage[];
14
+ status: AgentRunStatus;
15
+ /** What happened after the loop — a validation verdict, a build result. */
16
+ note?: string;
17
+ maxSummaryChars?: number;
18
+ maxAdviceChars?: number;
19
+ /** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
20
+ action?: string;
21
+ /** How much of the transcript to show the model. */
22
+ maxTranscriptChars?: number;
23
+ }
24
+ /** The text of a message, whatever content shape it arrived in. */
25
+ export declare const messageText: (message: BaseMessage) => string;
26
+ /**
27
+ * A transcript the model can read, newest-biased.
28
+ *
29
+ * The tail is what matters to a compaction — how the run ENDED decides what to do next — so when
30
+ * the budget binds it is the head that goes.
31
+ */
32
+ export declare const renderTranscript: (messages: readonly BaseMessage[], maxChars?: number) => string;
33
+ /**
34
+ * Compact a finished run into what the next one needs.
35
+ *
36
+ * Two parts, deliberately. A summary alone leaves the next run to re-derive the plan from the
37
+ * outcome, which is where it invents a different one; the advice is the half that carries intent
38
+ * across the gap.
39
+ *
40
+ * **Never throws, and never trusts the model's arithmetic.** The character caps are applied after
41
+ * the answer comes back, because a cap in a prompt is a request. When the model is absent or fails
42
+ * — an exhausted budget is the common case, and asking again would fail the same way — the
43
+ * deterministic fallback still produces a usable event: what was asked, and how it ended.
44
+ */
45
+ export declare const composeCompaction: (input: CompactionInput) => Promise<Compaction>;
46
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/helpers/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,cAAc,EAA2D,MAAM,wBAAwB,CAAA;AAEhH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sIAAsI;IACtI,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAID,mEAAmE;AACnE,eAAO,MAAM,WAAW,YAAa,WAAW,KAAG,MAalD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,aACjB,SAAS,WAAW,EAAE,wBAC/B,MA0BF,CAAA;AAYD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,UAAiB,eAAe,KAAG,OAAO,CAAC,UAAU,CAoElF,CAAA"}
@@ -0,0 +1,119 @@
1
+ import { AgentRunStatus, DEFAULT_ADVICE_CHARS, DEFAULT_SUMMARY_CHARS, truncateAt } from '@owlmeans/agent-common';
2
+ const DEFAULT_TRANSCRIPT_CHARS = 24_000;
3
+ /** The text of a message, whatever content shape it arrived in. */
4
+ export const messageText = (message) => {
5
+ const content = message.content;
6
+ if (typeof content === 'string') {
7
+ return content;
8
+ }
9
+ if (Array.isArray(content)) {
10
+ return content
11
+ .map(part => typeof part === 'string' ? part : part.text ?? '')
12
+ .filter(text => text !== '')
13
+ .join('\n');
14
+ }
15
+ return '';
16
+ };
17
+ /**
18
+ * A transcript the model can read, newest-biased.
19
+ *
20
+ * The tail is what matters to a compaction — how the run ENDED decides what to do next — so when
21
+ * the budget binds it is the head that goes.
22
+ */
23
+ export const renderTranscript = (messages, maxChars = DEFAULT_TRANSCRIPT_CHARS) => {
24
+ const lines = [];
25
+ let used = 0;
26
+ for (let i = messages.length - 1; i >= 0; --i) {
27
+ const message = messages[i];
28
+ const text = messageText(message).trim();
29
+ const calls = message.tool_calls;
30
+ const body = text !== ''
31
+ ? text
32
+ : calls != null && calls.length > 0
33
+ ? `(called ${calls.map(call => call.name).join(', ')})`
34
+ : '';
35
+ if (body === '') {
36
+ continue;
37
+ }
38
+ const line = `${message.getType()}: ${body}`;
39
+ if (used + line.length > maxChars) {
40
+ break;
41
+ }
42
+ lines.unshift(line);
43
+ used += line.length;
44
+ }
45
+ return lines.join('\n\n');
46
+ };
47
+ const COMPACTION_SCHEMA = {
48
+ type: 'object',
49
+ properties: {
50
+ summary: { type: 'string' },
51
+ advice: { type: 'string' },
52
+ },
53
+ required: ['summary', 'advice'],
54
+ additionalProperties: false,
55
+ };
56
+ /**
57
+ * Compact a finished run into what the next one needs.
58
+ *
59
+ * Two parts, deliberately. A summary alone leaves the next run to re-derive the plan from the
60
+ * outcome, which is where it invents a different one; the advice is the half that carries intent
61
+ * across the gap.
62
+ *
63
+ * **Never throws, and never trusts the model's arithmetic.** The character caps are applied after
64
+ * the answer comes back, because a cap in a prompt is a request. When the model is absent or fails
65
+ * — an exhausted budget is the common case, and asking again would fail the same way — the
66
+ * deterministic fallback still produces a usable event: what was asked, and how it ended.
67
+ */
68
+ export const composeCompaction = async (input) => {
69
+ const { model, prompt, messages, status, note, maxSummaryChars = DEFAULT_SUMMARY_CHARS, maxAdviceChars = DEFAULT_ADVICE_CHARS, action = 'agent-compaction', maxTranscriptChars, } = input;
70
+ const fallback = () => {
71
+ const last = [...messages].reverse().find(message => messageText(message).trim() !== '');
72
+ const tail = last != null ? messageText(last).trim() : '';
73
+ const head = `Asked: ${prompt.trim()}`;
74
+ const ended = status === AgentRunStatus.Ok ? 'Finished.' : 'Did not finish.';
75
+ return {
76
+ summary: truncateAt([head, ended, note?.trim(), tail].filter(part => part != null && part !== '').join(' '), maxSummaryChars),
77
+ };
78
+ };
79
+ if (model == null) {
80
+ return fallback();
81
+ }
82
+ try {
83
+ const result = await model.invoke(`
84
+ Compact the conversation below into a handover for the next session working on the same subject.
85
+
86
+ Write two things:
87
+
88
+ - summary: what was asked, what was actually done, and how it ended. Facts only — name the files,
89
+ decisions and failures that occurred. At most ${maxSummaryChars} characters.
90
+ - advice: what the next session should do first, and what it should not repeat. If the work
91
+ finished cleanly, say what remains or say that nothing does. At most ${maxAdviceChars} characters.
92
+
93
+ Write for a reader who cannot see this conversation and will act on your words alone. Do not
94
+ address the reader, do not describe the conversation as a conversation, and do not speculate about
95
+ anything not shown.
96
+
97
+ # The ask that opened the session
98
+ ${prompt}
99
+
100
+ # How it ended
101
+ ${status === AgentRunStatus.Ok ? 'Completed' : 'Failed'}${note != null && note !== '' ? ` — ${note}` : ''}
102
+
103
+ # Conversation
104
+ ${renderTranscript(messages, maxTranscriptChars)}
105
+ `, COMPACTION_SCHEMA, { action });
106
+ const summary = truncateAt(result.summary ?? '', maxSummaryChars);
107
+ const advice = truncateAt(result.advice ?? '', maxAdviceChars);
108
+ // An empty summary is a non-answer, not a short one — take the deterministic path rather than
109
+ // storing a blank event that the next run will read as "nothing happened".
110
+ return summary === ''
111
+ ? fallback()
112
+ : { summary, ...(advice !== '' ? { advice } : {}) };
113
+ }
114
+ catch (e) {
115
+ console.warn('Agent compaction failed, falling back to a deterministic summary:', e);
116
+ return fallback();
117
+ }
118
+ };
119
+ //# sourceMappingURL=compaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/helpers/compaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './tools.js';
2
+ export * from './compaction.js';
3
+ export * from './rolling.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './tools.js';
2
+ export * from './compaction.js';
3
+ export * from './rolling.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,25 @@
1
+ import type { LlmModel } from '@owlmeans/llm';
2
+ export interface RollingSummaryInput {
3
+ /** Omit to skip the model and take the deterministic path. */
4
+ model?: LlmModel;
5
+ /** The prose account so far. Empty on the first fold. */
6
+ previous: string;
7
+ /** What just happened, as one line. */
8
+ event: string;
9
+ /** Anything the fold may use but that need not survive into the summary. */
10
+ details?: string;
11
+ /** Hard ceiling on the returned prose, in characters. */
12
+ maxChars: number;
13
+ /** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
14
+ action?: string;
15
+ }
16
+ /**
17
+ * Fold one event into a running account of a subject, under a hard character ceiling.
18
+ *
19
+ * **Never throws.** When the model is unavailable or refuses, the previous prose is kept and
20
+ * head-truncated to make room rather than being replaced by an error or dropped: the caller's own
21
+ * verbatim record of the event is what preserves the fact, so a failed fold costs detail, never
22
+ * the event itself. That is the property that lets a caller record history unconditionally.
23
+ */
24
+ export declare const composeRollingSummary: (input: RollingSummaryInput) => Promise<string>;
25
+ //# sourceMappingURL=rolling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rolling.d.ts","sourceRoot":"","sources":["../../src/helpers/rolling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAG7C,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,UAAiB,mBAAmB,KAAG,OAAO,CAAC,MAAM,CAyCtF,CAAA"}
@@ -0,0 +1,45 @@
1
+ import { truncateAt } from '@owlmeans/agent-common';
2
+ /**
3
+ * Fold one event into a running account of a subject, under a hard character ceiling.
4
+ *
5
+ * **Never throws.** When the model is unavailable or refuses, the previous prose is kept and
6
+ * head-truncated to make room rather than being replaced by an error or dropped: the caller's own
7
+ * verbatim record of the event is what preserves the fact, so a failed fold costs detail, never
8
+ * the event itself. That is the property that lets a caller record history unconditionally.
9
+ */
10
+ export const composeRollingSummary = async (input) => {
11
+ const { model, previous, event, details, maxChars, action = 'agent-rolling-summary' } = input;
12
+ const trimmedPrevious = previous.trim();
13
+ const fallback = () => trimmedPrevious === ''
14
+ ? truncateAt(event, maxChars)
15
+ : truncateAt(trimmedPrevious, maxChars);
16
+ if (model == null) {
17
+ return fallback();
18
+ }
19
+ try {
20
+ const result = await model.ask(`
21
+ Update the running account of a project with the event below.
22
+
23
+ Write ONE account that covers the project's whole life so far, at most ${maxChars} characters. Keep
24
+ what still matters — what the project is, the decisions taken, what has been built, what failed and
25
+ was not repaired. Drop detail that later events made irrelevant. Prefer losing old detail to losing
26
+ recent facts.
27
+
28
+ Facts only. No preamble, no headings, no addressing the reader, no speculation about what happens
29
+ next. Plain prose paragraphs.
30
+
31
+ # The account so far
32
+ ${trimmedPrevious === '' ? '(nothing recorded yet)' : trimmedPrevious}
33
+
34
+ # What just happened
35
+ ${event}${details != null && details !== '' ? `\n\n# Detail\n${details}` : ''}
36
+ `, { action });
37
+ const summary = truncateAt(result ?? '', maxChars);
38
+ return summary === '' ? fallback() : summary;
39
+ }
40
+ catch (e) {
41
+ console.warn('Rolling summary fold failed, keeping the previous account:', e);
42
+ return fallback();
43
+ }
44
+ };
45
+ //# sourceMappingURL=rolling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rolling.js","sourceRoot":"","sources":["../../src/helpers/rolling.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAiBnD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,KAA0B,EAAmB,EAAE;IACzF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,uBAAuB,EAAE,GAAG,KAAK,CAAA;IAE7F,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;IACvC,MAAM,QAAQ,GAAG,GAAW,EAAE,CAAC,eAAe,KAAK,EAAE;QACnD,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC7B,CAAC,CAAC,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;IAEzC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAC5B;;;yEAGmE,QAAQ;;;;;;;;;EAS/E,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,eAAe;;;EAGnE,KAAK,GAAG,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;OACtE,EACD,EAAE,MAAM,EAAE,CACX,CAAA;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAA;QAElD,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;IAC9C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,CAAC,CAAC,CAAA;QAC7E,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,29 @@
1
+ import type { ToolCall } from '@langchain/core/messages';
2
+ import type { AgentToolSet } from '../types.js';
3
+ /** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
4
+ export interface ToolErrorResponse {
5
+ error: string;
6
+ }
7
+ export declare const toErrorResponse: (e: unknown) => ToolErrorResponse;
8
+ export declare const isToolError: (result: unknown) => result is ToolErrorResponse;
9
+ /**
10
+ * Run one model-requested tool, contained.
11
+ *
12
+ * **Never throws.** The caller wraps this in a LangGraph task, and a rejected task aborts the whole
13
+ * superstep: every sibling tool call in the same parallel batch dies with AbortError and the run
14
+ * ends on "Multiple errors occurred during superstep 0", discarding work the other calls had
15
+ * already finished. A tool failure has to come back as something the model can read and correct
16
+ * instead — most of them are the model's own mistake (an argument outside an enum, a hallucinated
17
+ * tool name), and the error text already names what was expected.
18
+ *
19
+ * Awaiting `invoke` is what makes the catch reachable: the tool's schema is validated inside it, so
20
+ * a bad argument rejects asynchronously and returning the promise unawaited would carry the
21
+ * rejection straight past this handler.
22
+ *
23
+ * Resolution is by the tool's OWN name, not by the map key. `bindTools` advertises `tool.name`, so
24
+ * that is what the model calls — a map keyed by a local variable silently loses any tool whose two
25
+ * names drifted apart, leaving it advertised, callable, and permanently "not found". The key stays
26
+ * as a fallback so a caller may still address a tool by it.
27
+ */
28
+ export declare const safeInvokeTool: (tools: AgentToolSet, toolCall: ToolCall) => Promise<unknown>;
29
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +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"}