@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.
- package/README.md +93 -0
- package/agent-meta/manifest.json +16 -0
- package/agent-meta/skills/agent/SKILL.md +122 -0
- package/build/consts.d.ts +16 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +16 -0
- package/build/consts.js.map +1 -0
- package/build/errors.d.ts +16 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +27 -0
- package/build/errors.js.map +1 -0
- package/build/helpers/compaction.d.ts +46 -0
- package/build/helpers/compaction.d.ts.map +1 -0
- package/build/helpers/compaction.js +119 -0
- package/build/helpers/compaction.js.map +1 -0
- package/build/helpers/index.d.ts +4 -0
- package/build/helpers/index.d.ts.map +1 -0
- package/build/helpers/index.js +4 -0
- package/build/helpers/index.js.map +1 -0
- package/build/helpers/rolling.d.ts +25 -0
- package/build/helpers/rolling.d.ts.map +1 -0
- package/build/helpers/rolling.js +45 -0
- package/build/helpers/rolling.js.map +1 -0
- package/build/helpers/tools.d.ts +29 -0
- package/build/helpers/tools.d.ts.map +1 -0
- package/build/helpers/tools.js +36 -0
- package/build/helpers/tools.js.map +1 -0
- package/build/index.d.ts +12 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -0
- package/build/model.d.ts +15 -0
- package/build/model.d.ts.map +1 -0
- package/build/model.js +208 -0
- package/build/model.js.map +1 -0
- package/build/plugins/export.d.ts +8 -0
- package/build/plugins/export.d.ts.map +1 -0
- package/build/plugins/export.js +4 -0
- package/build/plugins/export.js.map +1 -0
- package/build/plugins/memory-events.d.ts +36 -0
- package/build/plugins/memory-events.d.ts.map +1 -0
- package/build/plugins/memory-events.js +83 -0
- package/build/plugins/memory-events.js.map +1 -0
- package/build/plugins/memory-graph.d.ts +52 -0
- package/build/plugins/memory-graph.d.ts.map +1 -0
- package/build/plugins/memory-graph.js +155 -0
- package/build/plugins/memory-graph.js.map +1 -0
- package/build/plugins/summarize.d.ts +44 -0
- package/build/plugins/summarize.d.ts.map +1 -0
- package/build/plugins/summarize.js +77 -0
- package/build/plugins/summarize.js.map +1 -0
- package/build/runtime/checkpoint.d.ts +37 -0
- package/build/runtime/checkpoint.d.ts.map +1 -0
- package/build/runtime/checkpoint.js +52 -0
- package/build/runtime/checkpoint.js.map +1 -0
- package/build/runtime/provider.d.ts +18 -0
- package/build/runtime/provider.d.ts.map +1 -0
- package/build/runtime/provider.js +30 -0
- package/build/runtime/provider.js.map +1 -0
- package/build/runtime/transport.d.ts +27 -0
- package/build/runtime/transport.d.ts.map +1 -0
- package/build/runtime/transport.js +29 -0
- package/build/runtime/transport.js.map +1 -0
- package/build/service.d.ts +14 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +61 -0
- package/build/service.js.map +1 -0
- package/build/stores/index.d.ts +3 -0
- package/build/stores/index.d.ts.map +1 -0
- package/build/stores/index.js +2 -0
- package/build/stores/index.js.map +1 -0
- package/build/stores/memory.d.ts +6 -0
- package/build/stores/memory.d.ts.map +1 -0
- package/build/stores/memory.js +0 -0
- package/build/stores/memory.js.map +1 -0
- package/build/stores/types.d.ts +38 -0
- package/build/stores/types.d.ts.map +1 -0
- package/build/stores/types.js +2 -0
- package/build/stores/types.js.map +1 -0
- package/build/types.d.ts +130 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +72 -0
- package/src/consts.ts +19 -0
- package/src/errors.ts +33 -0
- package/src/helpers/compaction.ts +172 -0
- package/src/helpers/index.ts +3 -0
- package/src/helpers/rolling.ts +68 -0
- package/src/helpers/tools.ts +46 -0
- package/src/index.ts +11 -0
- package/src/model.ts +269 -0
- package/src/plugins/export.ts +12 -0
- package/src/plugins/memory-events.ts +129 -0
- package/src/plugins/memory-graph.ts +217 -0
- package/src/plugins/summarize.ts +129 -0
- package/src/runtime/checkpoint.ts +89 -0
- package/src/runtime/provider.ts +35 -0
- package/src/runtime/transport.ts +50 -0
- package/src/service.ts +97 -0
- package/src/stores/index.ts +2 -0
- package/src/stores/memory.ts +0 -0
- package/src/stores/types.ts +45 -0
- package/src/types.ts +144 -0
- package/tests/_tools/model.ts +50 -0
- package/tests/agent.spec.ts +218 -0
- package/tests/plugins.spec.ts +257 -0
- package/tests/runtime.spec.ts +140 -0
- package/tests/summary.spec.ts +143 -0
- package/tests/tools.spec.ts +68 -0
- 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"}
|
package/build/consts.js
ADDED
|
@@ -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"}
|
package/build/errors.js
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|