@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
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AgentRunStateStore, ConversationStore, MemoryEventStore, MemoryGraphStore } from './types.js';
|
|
2
|
+
export declare const createMemoryConversationStore: () => ConversationStore;
|
|
3
|
+
export declare const createMemoryGraphStore: () => MemoryGraphStore;
|
|
4
|
+
export declare const createMemoryEventStore: () => MemoryEventStore;
|
|
5
|
+
export declare const createMemoryRunStateStore: () => AgentRunStateStore;
|
|
6
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAC1E,MAAM,YAAY,CAAA;AAanB,eAAO,MAAM,6BAA6B,QAAO,iBAsBhD,CAAA;AAED,eAAO,MAAM,sBAAsB,QAAO,gBAqBzC,CAAA;AAED,eAAO,MAAM,sBAAsB,QAAO,gBA8BzC,CAAA;AAED,eAAO,MAAM,yBAAyB,QAAO,kBAO5C,CAAA"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentRunState, ConversationEvent, ConversationEventInput, ConversationRef, MemoryEvent, MemoryEventInput, MemoryNode } from '@owlmeans/agent-common';
|
|
2
|
+
/**
|
|
3
|
+
* Storage, as this package needs it.
|
|
4
|
+
*
|
|
5
|
+
* These are PORTS, not resources. The package could have taken `Resource<T>` and let a consumer
|
|
6
|
+
* register a backend under an alias — but `Resource.list()` is not uniformly queryable
|
|
7
|
+
* (`@owlmeans/static-resource` throws on any criteria), so a plugin written against its query
|
|
8
|
+
* semantics could not be exercised with the monorepo's own in-memory backend. A port names what
|
|
9
|
+
* the plugin actually needs, which is a much smaller surface than CRUD, and any backend can
|
|
10
|
+
* satisfy it — including a file on disk, which is what the project-history equivalent is.
|
|
11
|
+
*
|
|
12
|
+
* Every port is optional to bind. A plugin whose port is missing degrades to a no-op rather than
|
|
13
|
+
* throwing, exactly as `ExecutionService.checkpoint` does with no plugin registered: memory is an
|
|
14
|
+
* enhancement, and an application that has not wired storage yet must still be able to run agents.
|
|
15
|
+
*/
|
|
16
|
+
export interface ConversationStore {
|
|
17
|
+
/** The most recent `limit` events, NEWEST FIRST. */
|
|
18
|
+
last: (ref: ConversationRef, limit: number) => Promise<ConversationEvent[]>;
|
|
19
|
+
/** Append one event, allocating its `seq`. */
|
|
20
|
+
append: (event: ConversationEventInput) => Promise<ConversationEvent>;
|
|
21
|
+
}
|
|
22
|
+
export interface MemoryGraphStore {
|
|
23
|
+
/** Every node of a scope, without its content — names and links only. */
|
|
24
|
+
index: (scope: string) => Promise<Array<Pick<MemoryNode, 'subsystem' | 'links' | 'updatedAt'>>>;
|
|
25
|
+
read: (scope: string, subsystem: string) => Promise<MemoryNode | null>;
|
|
26
|
+
write: (node: Omit<MemoryNode, 'id' | 'updatedAt'>) => Promise<MemoryNode>;
|
|
27
|
+
}
|
|
28
|
+
export interface MemoryEventStore {
|
|
29
|
+
/** The most recent `limit` events of a scope, NEWEST FIRST. */
|
|
30
|
+
read: (scope: string, limit: number) => Promise<MemoryEvent[]>;
|
|
31
|
+
/** Append one event, allocating its `seq`, and prune the scope to `limit` if given. */
|
|
32
|
+
append: (event: MemoryEventInput, limit?: number) => Promise<MemoryEvent>;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentRunStateStore {
|
|
35
|
+
load: (runId: string) => Promise<AgentRunState | null>;
|
|
36
|
+
save: (state: AgentRunState) => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/stores/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,eAAe,EACzE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAC1C,MAAM,wBAAwB,CAAA;AAE/B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,IAAI,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAC3E,8CAA8C;IAC9C,MAAM,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAC/F,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACtE,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CAC3E;AAED,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IAC9D,uFAAuF;IACvF,MAAM,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;CAC1E;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IACtD,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/stores/types.ts"],"names":[],"mappings":""}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
|
|
3
|
+
import type { StructuredToolInterface } from '@langchain/core/tools';
|
|
4
|
+
import type { BasicConfig, BasicContext, InitializedService } from '@owlmeans/context';
|
|
5
|
+
import type { FlowModel, FlowProvider, ShallowFlow } from '@owlmeans/flow';
|
|
6
|
+
import type { Execution, LlmPlugin, ModelInputItem, PromptService } from '@owlmeans/llm';
|
|
7
|
+
import type { AgentRunStatus, ConversationEvent, ConversationRef } from '@owlmeans/agent-common';
|
|
8
|
+
import type { AgentTransport } from './runtime/transport.js';
|
|
9
|
+
import type { ConversationStore } from './stores/types.js';
|
|
10
|
+
/** Tools an agent may call, keyed however the caller likes — resolution is by `tool.name`. */
|
|
11
|
+
export interface AgentToolSet {
|
|
12
|
+
[key: string]: StructuredToolInterface;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* What the caller gets told about each model call.
|
|
16
|
+
*
|
|
17
|
+
* Shaped to match `spectate(spectator, callType)` from `@owlmeans/llm` exactly, so an application
|
|
18
|
+
* that already has a spectator passes the curried function straight in.
|
|
19
|
+
*/
|
|
20
|
+
export interface AgentSpectateHook {
|
|
21
|
+
(input: ModelInputItem[], message: AIMessage, action: string, retries: number, startedAt?: number): Promise<unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface AgentOptions {
|
|
24
|
+
/** The execution the run belongs to. Its `prompt` policy is the agent's persona. */
|
|
25
|
+
exec: Execution;
|
|
26
|
+
/** Overrides the model resolved from the execution. */
|
|
27
|
+
agentModel?: BaseChatModel;
|
|
28
|
+
tools: AgentToolSet;
|
|
29
|
+
/** Static volatile context. Lands in `PromptBlock.Context`, never in the cached prefix. */
|
|
30
|
+
context?: string[];
|
|
31
|
+
conversation?: ConversationRef;
|
|
32
|
+
/** LangGraph entrypoint name; shows up in traces. */
|
|
33
|
+
entrypoint?: string;
|
|
34
|
+
spectate?: AgentSpectateHook;
|
|
35
|
+
prompts?: () => PromptService;
|
|
36
|
+
/** Provider plugin used for cache placement. Resolved from the model when omitted. */
|
|
37
|
+
provider?: LlmPlugin;
|
|
38
|
+
maxTurns?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Whether `invoke()` finalizes the run itself.
|
|
41
|
+
*
|
|
42
|
+
* Leave it on for a caller whose work ends when the model stops talking. Turn it OFF when
|
|
43
|
+
* something runs AFTER the agent that changes the outcome — a validation pass, a build — because
|
|
44
|
+
* a compaction written before that step describes a state that did not survive it, and the
|
|
45
|
+
* "what to do next" it produces is then advice about a world that no longer exists.
|
|
46
|
+
*/
|
|
47
|
+
autoFinish?: boolean;
|
|
48
|
+
plugins?: AgentPlugin[];
|
|
49
|
+
}
|
|
50
|
+
export interface AgentInvokeArgs {
|
|
51
|
+
/** LangChain `runName` for the model calls of this run. */
|
|
52
|
+
action?: string;
|
|
53
|
+
/** Extra volatile context for this call only. */
|
|
54
|
+
context?: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface AgentRunOutcome {
|
|
57
|
+
status: AgentRunStatus;
|
|
58
|
+
/** What happened after the loop — a fixer verdict, a build result. Reaches the compaction. */
|
|
59
|
+
note?: string;
|
|
60
|
+
error?: Error;
|
|
61
|
+
}
|
|
62
|
+
/** What a plugin sees. Deliberately carries no service: a model built standalone has none. */
|
|
63
|
+
export interface AgentRun {
|
|
64
|
+
id: string;
|
|
65
|
+
conversation: ConversationRef;
|
|
66
|
+
exec: Execution;
|
|
67
|
+
flow: FlowModel;
|
|
68
|
+
/** The ask that opened the run. */
|
|
69
|
+
prompt: string;
|
|
70
|
+
action: string;
|
|
71
|
+
}
|
|
72
|
+
export interface AgentRunHandle {
|
|
73
|
+
id: string;
|
|
74
|
+
conversation: ConversationRef;
|
|
75
|
+
/** Fires `onFinish` on every plugin. Idempotent — a second call is a no-op, never a second event. */
|
|
76
|
+
finish: (outcome: AgentRunOutcome) => Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
export interface AgentResult {
|
|
79
|
+
message: AIMessage;
|
|
80
|
+
/** The whole transcript of the run, the opening human message included. */
|
|
81
|
+
messages: BaseMessage[];
|
|
82
|
+
run: AgentRunHandle;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The package's own optional-capability seam.
|
|
86
|
+
*
|
|
87
|
+
* A plugin may contribute what an agent knows (`context`), what it can do (`tools`), watch it work
|
|
88
|
+
* (`onTurn`), and act when it stops (`onFinish`). Everything memory- and summary-related in this
|
|
89
|
+
* family is one of these; nothing in the loop itself knows those features exist.
|
|
90
|
+
*/
|
|
91
|
+
export interface AgentPlugin {
|
|
92
|
+
alias: string;
|
|
93
|
+
/** Lower runs first. Defaults to 50. */
|
|
94
|
+
order?: number;
|
|
95
|
+
context?: (run: AgentRun) => Promise<string[]>;
|
|
96
|
+
tools?: (run: AgentRun) => AgentToolSet;
|
|
97
|
+
onTurn?: (run: AgentRun, messages: readonly BaseMessage[]) => Promise<void>;
|
|
98
|
+
onFinish?: (run: AgentRun, result: AgentResult, outcome: AgentRunOutcome) => Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
export interface AgentModel {
|
|
101
|
+
use: (plugin: AgentPlugin) => void;
|
|
102
|
+
invoke: (input: string | HumanMessage, args?: AgentInvokeArgs) => Promise<AgentResult>;
|
|
103
|
+
conversation: () => ConversationRef;
|
|
104
|
+
}
|
|
105
|
+
export interface ConversationApi {
|
|
106
|
+
last: (limit?: number) => Promise<ConversationEvent[]>;
|
|
107
|
+
append: (event: Omit<ConversationEvent, 'id' | 'seq' | 'createdAt'>) => Promise<ConversationEvent>;
|
|
108
|
+
}
|
|
109
|
+
export interface AgentServiceOptions {
|
|
110
|
+
/** Extra flows the provider should serve. The run lifecycle flow is always included. */
|
|
111
|
+
flows?: ShallowFlow[];
|
|
112
|
+
transport?: AgentTransport;
|
|
113
|
+
plugins?: AgentPlugin[];
|
|
114
|
+
conversations?: ConversationStore;
|
|
115
|
+
}
|
|
116
|
+
export interface AgentService extends InitializedService {
|
|
117
|
+
/** Build an agent with the service's plugins already attached. */
|
|
118
|
+
agent: (options: AgentOptions) => AgentModel;
|
|
119
|
+
use: (plugin: AgentPlugin) => void;
|
|
120
|
+
plugins: () => AgentPlugin[];
|
|
121
|
+
flow: FlowProvider;
|
|
122
|
+
transport: () => AgentTransport;
|
|
123
|
+
/** Conversation access for callers that want it outside a run. No store bound → empty results. */
|
|
124
|
+
conversation: (ref: ConversationRef) => ConversationApi;
|
|
125
|
+
}
|
|
126
|
+
export interface WithAgentsService {
|
|
127
|
+
agents: () => AgentService;
|
|
128
|
+
}
|
|
129
|
+
export type AgentContext<C extends BasicConfig = BasicConfig> = BasicContext<C> & WithAgentsService;
|
|
130
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/agent",
|
|
3
|
+
"version": "0.1.18-rc.7",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -b",
|
|
8
|
+
"dev": "sleep 178 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
9
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty",
|
|
10
|
+
"test": "bun test ./tests"
|
|
11
|
+
},
|
|
12
|
+
"main": "build/index.js",
|
|
13
|
+
"module": "build/index.js",
|
|
14
|
+
"types": "build/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./build/index.js",
|
|
18
|
+
"require": "./build/index.js",
|
|
19
|
+
"default": "./build/index.js",
|
|
20
|
+
"module": "./build/index.js",
|
|
21
|
+
"types": "./build/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./plugins": {
|
|
24
|
+
"import": "./build/plugins/export.js",
|
|
25
|
+
"require": "./build/plugins/export.js",
|
|
26
|
+
"default": "./build/plugins/export.js",
|
|
27
|
+
"module": "./build/plugins/export.js",
|
|
28
|
+
"types": "./build/plugins/export.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./helpers": {
|
|
31
|
+
"import": "./build/helpers/index.js",
|
|
32
|
+
"require": "./build/helpers/index.js",
|
|
33
|
+
"default": "./build/helpers/index.js",
|
|
34
|
+
"module": "./build/helpers/index.js",
|
|
35
|
+
"types": "./build/helpers/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./stores": {
|
|
38
|
+
"import": "./build/stores/index.js",
|
|
39
|
+
"require": "./build/stores/index.js",
|
|
40
|
+
"default": "./build/stores/index.js",
|
|
41
|
+
"module": "./build/stores/index.js",
|
|
42
|
+
"types": "./build/stores/index.d.ts"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@langchain/core": "^1.1.39",
|
|
47
|
+
"@langchain/langgraph": "^1.2.8",
|
|
48
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
49
|
+
"@types/bun": "^1.3.14",
|
|
50
|
+
"@types/node": "^26.1.0",
|
|
51
|
+
"nodemon": "^3.1.14",
|
|
52
|
+
"typescript": "^7.0.2",
|
|
53
|
+
"zod": "^4.1.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@owlmeans/agent-common": "^0.1.18-rc.7",
|
|
57
|
+
"@owlmeans/basic-ids": "^0.1.18-rc.6",
|
|
58
|
+
"@owlmeans/context": "^0.1.18-rc.6",
|
|
59
|
+
"@owlmeans/error": "^0.1.18-rc.6",
|
|
60
|
+
"@owlmeans/flow": "^0.1.18-rc.6",
|
|
61
|
+
"@owlmeans/llm": "^0.1.18-rc.6",
|
|
62
|
+
"@owlmeans/llm-common": "^0.1.18-rc.6",
|
|
63
|
+
"ajv": "^8.17.1"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@langchain/core": "^1.1.39",
|
|
70
|
+
"@langchain/langgraph": "^1.2.8"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { AGENTS_SERVICE } from '@owlmeans/agent-common'
|
|
2
|
+
|
|
3
|
+
/** Default LangGraph entrypoint name. Shows up in traces, so it is worth overriding per agent. */
|
|
4
|
+
export const DEFAULT_ENTRYPOINT = 'owlmeans-agent'
|
|
5
|
+
|
|
6
|
+
/** Default action label for a model call, used as the LangChain `runName`. */
|
|
7
|
+
export const DEFAULT_ACTION = 'agent-ask'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* How many tool rounds one run may take before it is stopped.
|
|
11
|
+
*
|
|
12
|
+
* A model that keeps calling tools without ever answering is not rare — it is the ordinary failure
|
|
13
|
+
* mode of a loop whose tool results do not satisfy it. Without a ceiling the run consumes the
|
|
14
|
+
* caller's budget until something else kills it, which reads as a hang rather than a refusal.
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_MAX_TURNS = 64
|
|
17
|
+
|
|
18
|
+
/** Ordering weight of a plugin that declares none. */
|
|
19
|
+
export const DEFAULT_PLUGIN_ORDER = 50
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error'
|
|
2
|
+
|
|
3
|
+
export class AgentError extends ResilientError {
|
|
4
|
+
public static override typeName: string = `AgentRuntime${ResilientError.typeName}`
|
|
5
|
+
|
|
6
|
+
constructor(message: string = 'error') {
|
|
7
|
+
super(AgentError.typeName, `agent-runtime:${message}`)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** The agent was built without something it cannot work around — a model, or a tool set. */
|
|
12
|
+
export class AgentMissconfiguredError extends AgentError {
|
|
13
|
+
public static override typeName: string = `Missconfigured${AgentError.typeName}`
|
|
14
|
+
|
|
15
|
+
constructor(message: string = 'error') {
|
|
16
|
+
super(`missconfigured:${message}`)
|
|
17
|
+
this.type = AgentMissconfiguredError.typeName
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The tool loop hit its turn ceiling without the model ever answering. */
|
|
22
|
+
export class AgentLoopExhaustedError extends AgentError {
|
|
23
|
+
public static override typeName: string = `LoopExhausted${AgentError.typeName}`
|
|
24
|
+
|
|
25
|
+
constructor(message: string = 'error') {
|
|
26
|
+
super(`loop-exhausted:${message}`)
|
|
27
|
+
this.type = AgentLoopExhaustedError.typeName
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ResilientError.registerErrorClass(AgentError)
|
|
32
|
+
ResilientError.registerErrorClass(AgentMissconfiguredError)
|
|
33
|
+
ResilientError.registerErrorClass(AgentLoopExhaustedError)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages'
|
|
2
|
+
import type { JSONSchemaType } from 'ajv'
|
|
3
|
+
import type { LlmModel } from '@owlmeans/llm'
|
|
4
|
+
import { AgentRunStatus, DEFAULT_ADVICE_CHARS, DEFAULT_SUMMARY_CHARS, truncateAt } from '@owlmeans/agent-common'
|
|
5
|
+
|
|
6
|
+
export interface Compaction {
|
|
7
|
+
summary: string
|
|
8
|
+
advice?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CompactionInput {
|
|
12
|
+
/** Omit to skip the model entirely and take the deterministic path. */
|
|
13
|
+
model?: LlmModel
|
|
14
|
+
/** The ask that opened the run. */
|
|
15
|
+
prompt: string
|
|
16
|
+
messages: readonly BaseMessage[]
|
|
17
|
+
status: AgentRunStatus
|
|
18
|
+
/** What happened after the loop — a validation verdict, a build result. */
|
|
19
|
+
note?: string
|
|
20
|
+
maxSummaryChars?: number
|
|
21
|
+
maxAdviceChars?: number
|
|
22
|
+
/** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
|
|
23
|
+
action?: string
|
|
24
|
+
/** How much of the transcript to show the model. */
|
|
25
|
+
maxTranscriptChars?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DEFAULT_TRANSCRIPT_CHARS = 24_000
|
|
29
|
+
|
|
30
|
+
/** The text of a message, whatever content shape it arrived in. */
|
|
31
|
+
export const messageText = (message: BaseMessage): string => {
|
|
32
|
+
const content = message.content
|
|
33
|
+
if (typeof content === 'string') {
|
|
34
|
+
return content
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(content)) {
|
|
37
|
+
return content
|
|
38
|
+
.map(part => typeof part === 'string' ? part : (part as { text?: string }).text ?? '')
|
|
39
|
+
.filter(text => text !== '')
|
|
40
|
+
.join('\n')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ''
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A transcript the model can read, newest-biased.
|
|
48
|
+
*
|
|
49
|
+
* The tail is what matters to a compaction — how the run ENDED decides what to do next — so when
|
|
50
|
+
* the budget binds it is the head that goes.
|
|
51
|
+
*/
|
|
52
|
+
export const renderTranscript = (
|
|
53
|
+
messages: readonly BaseMessage[], maxChars = DEFAULT_TRANSCRIPT_CHARS,
|
|
54
|
+
): string => {
|
|
55
|
+
const lines: string[] = []
|
|
56
|
+
let used = 0
|
|
57
|
+
|
|
58
|
+
for (let i = messages.length - 1; i >= 0; --i) {
|
|
59
|
+
const message = messages[i]
|
|
60
|
+
const text = messageText(message).trim()
|
|
61
|
+
const calls = (message as { tool_calls?: Array<{ name: string }> }).tool_calls
|
|
62
|
+
const body = text !== ''
|
|
63
|
+
? text
|
|
64
|
+
: calls != null && calls.length > 0
|
|
65
|
+
? `(called ${calls.map(call => call.name).join(', ')})`
|
|
66
|
+
: ''
|
|
67
|
+
if (body === '') {
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const line = `${message.getType()}: ${body}`
|
|
72
|
+
if (used + line.length > maxChars) {
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
lines.unshift(line)
|
|
76
|
+
used += line.length
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return lines.join('\n\n')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const COMPACTION_SCHEMA: JSONSchemaType<{ summary: string, advice: string }> = {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
summary: { type: 'string' },
|
|
86
|
+
advice: { type: 'string' },
|
|
87
|
+
},
|
|
88
|
+
required: ['summary', 'advice'],
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Compact a finished run into what the next one needs.
|
|
94
|
+
*
|
|
95
|
+
* Two parts, deliberately. A summary alone leaves the next run to re-derive the plan from the
|
|
96
|
+
* outcome, which is where it invents a different one; the advice is the half that carries intent
|
|
97
|
+
* across the gap.
|
|
98
|
+
*
|
|
99
|
+
* **Never throws, and never trusts the model's arithmetic.** The character caps are applied after
|
|
100
|
+
* the answer comes back, because a cap in a prompt is a request. When the model is absent or fails
|
|
101
|
+
* — an exhausted budget is the common case, and asking again would fail the same way — the
|
|
102
|
+
* deterministic fallback still produces a usable event: what was asked, and how it ended.
|
|
103
|
+
*/
|
|
104
|
+
export const composeCompaction = async (input: CompactionInput): Promise<Compaction> => {
|
|
105
|
+
const {
|
|
106
|
+
model, prompt, messages, status, note,
|
|
107
|
+
maxSummaryChars = DEFAULT_SUMMARY_CHARS,
|
|
108
|
+
maxAdviceChars = DEFAULT_ADVICE_CHARS,
|
|
109
|
+
action = 'agent-compaction',
|
|
110
|
+
maxTranscriptChars,
|
|
111
|
+
} = input
|
|
112
|
+
|
|
113
|
+
const fallback = (): Compaction => {
|
|
114
|
+
const last = [...messages].reverse().find(message => messageText(message).trim() !== '')
|
|
115
|
+
const tail = last != null ? messageText(last).trim() : ''
|
|
116
|
+
const head = `Asked: ${prompt.trim()}`
|
|
117
|
+
const ended = status === AgentRunStatus.Ok ? 'Finished.' : 'Did not finish.'
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
summary: truncateAt(
|
|
121
|
+
[head, ended, note?.trim(), tail].filter(part => part != null && part !== '').join(' '),
|
|
122
|
+
maxSummaryChars,
|
|
123
|
+
),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (model == null) {
|
|
128
|
+
return fallback()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const result = await model.invoke<{ summary: string, advice: string }>(
|
|
133
|
+
`
|
|
134
|
+
Compact the conversation below into a handover for the next session working on the same subject.
|
|
135
|
+
|
|
136
|
+
Write two things:
|
|
137
|
+
|
|
138
|
+
- summary: what was asked, what was actually done, and how it ended. Facts only — name the files,
|
|
139
|
+
decisions and failures that occurred. At most ${maxSummaryChars} characters.
|
|
140
|
+
- advice: what the next session should do first, and what it should not repeat. If the work
|
|
141
|
+
finished cleanly, say what remains or say that nothing does. At most ${maxAdviceChars} characters.
|
|
142
|
+
|
|
143
|
+
Write for a reader who cannot see this conversation and will act on your words alone. Do not
|
|
144
|
+
address the reader, do not describe the conversation as a conversation, and do not speculate about
|
|
145
|
+
anything not shown.
|
|
146
|
+
|
|
147
|
+
# The ask that opened the session
|
|
148
|
+
${prompt}
|
|
149
|
+
|
|
150
|
+
# How it ended
|
|
151
|
+
${status === AgentRunStatus.Ok ? 'Completed' : 'Failed'}${note != null && note !== '' ? ` — ${note}` : ''}
|
|
152
|
+
|
|
153
|
+
# Conversation
|
|
154
|
+
${renderTranscript(messages, maxTranscriptChars)}
|
|
155
|
+
`,
|
|
156
|
+
COMPACTION_SCHEMA,
|
|
157
|
+
{ action },
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const summary = truncateAt(result.summary ?? '', maxSummaryChars)
|
|
161
|
+
const advice = truncateAt(result.advice ?? '', maxAdviceChars)
|
|
162
|
+
|
|
163
|
+
// An empty summary is a non-answer, not a short one — take the deterministic path rather than
|
|
164
|
+
// storing a blank event that the next run will read as "nothing happened".
|
|
165
|
+
return summary === ''
|
|
166
|
+
? fallback()
|
|
167
|
+
: { summary, ...(advice !== '' ? { advice } : {}) }
|
|
168
|
+
} catch (e) {
|
|
169
|
+
console.warn('Agent compaction failed, falling back to a deterministic summary:', e)
|
|
170
|
+
return fallback()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { LlmModel } from '@owlmeans/llm'
|
|
2
|
+
import { truncateAt } from '@owlmeans/agent-common'
|
|
3
|
+
|
|
4
|
+
export interface RollingSummaryInput {
|
|
5
|
+
/** Omit to skip the model and take the deterministic path. */
|
|
6
|
+
model?: LlmModel
|
|
7
|
+
/** The prose account so far. Empty on the first fold. */
|
|
8
|
+
previous: string
|
|
9
|
+
/** What just happened, as one line. */
|
|
10
|
+
event: string
|
|
11
|
+
/** Anything the fold may use but that need not survive into the summary. */
|
|
12
|
+
details?: string
|
|
13
|
+
/** Hard ceiling on the returned prose, in characters. */
|
|
14
|
+
maxChars: number
|
|
15
|
+
/** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
|
|
16
|
+
action?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fold one event into a running account of a subject, under a hard character ceiling.
|
|
21
|
+
*
|
|
22
|
+
* **Never throws.** When the model is unavailable or refuses, the previous prose is kept and
|
|
23
|
+
* head-truncated to make room rather than being replaced by an error or dropped: the caller's own
|
|
24
|
+
* verbatim record of the event is what preserves the fact, so a failed fold costs detail, never
|
|
25
|
+
* the event itself. That is the property that lets a caller record history unconditionally.
|
|
26
|
+
*/
|
|
27
|
+
export const composeRollingSummary = async (input: RollingSummaryInput): Promise<string> => {
|
|
28
|
+
const { model, previous, event, details, maxChars, action = 'agent-rolling-summary' } = input
|
|
29
|
+
|
|
30
|
+
const trimmedPrevious = previous.trim()
|
|
31
|
+
const fallback = (): string => trimmedPrevious === ''
|
|
32
|
+
? truncateAt(event, maxChars)
|
|
33
|
+
: truncateAt(trimmedPrevious, maxChars)
|
|
34
|
+
|
|
35
|
+
if (model == null) {
|
|
36
|
+
return fallback()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const result = await model.ask(
|
|
41
|
+
`
|
|
42
|
+
Update the running account of a project with the event below.
|
|
43
|
+
|
|
44
|
+
Write ONE account that covers the project's whole life so far, at most ${maxChars} characters. Keep
|
|
45
|
+
what still matters — what the project is, the decisions taken, what has been built, what failed and
|
|
46
|
+
was not repaired. Drop detail that later events made irrelevant. Prefer losing old detail to losing
|
|
47
|
+
recent facts.
|
|
48
|
+
|
|
49
|
+
Facts only. No preamble, no headings, no addressing the reader, no speculation about what happens
|
|
50
|
+
next. Plain prose paragraphs.
|
|
51
|
+
|
|
52
|
+
# The account so far
|
|
53
|
+
${trimmedPrevious === '' ? '(nothing recorded yet)' : trimmedPrevious}
|
|
54
|
+
|
|
55
|
+
# What just happened
|
|
56
|
+
${event}${details != null && details !== '' ? `\n\n# Detail\n${details}` : ''}
|
|
57
|
+
`,
|
|
58
|
+
{ action },
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const summary = truncateAt(result ?? '', maxChars)
|
|
62
|
+
|
|
63
|
+
return summary === '' ? fallback() : summary
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.warn('Rolling summary fold failed, keeping the previous account:', e)
|
|
66
|
+
return fallback()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ToolCall } from '@langchain/core/messages'
|
|
2
|
+
import type { AgentToolSet } from '../types.js'
|
|
3
|
+
|
|
4
|
+
/** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
|
|
5
|
+
export interface ToolErrorResponse { error: string }
|
|
6
|
+
|
|
7
|
+
export const toErrorResponse = (e: unknown): ToolErrorResponse =>
|
|
8
|
+
({ error: `Error during tool call: ${(e instanceof Error) ? e.message : String(e)}` })
|
|
9
|
+
|
|
10
|
+
export const isToolError = (result: unknown): result is ToolErrorResponse =>
|
|
11
|
+
typeof result === 'object' && result != null && 'error' in result
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Run one model-requested tool, contained.
|
|
15
|
+
*
|
|
16
|
+
* **Never throws.** The caller wraps this in a LangGraph task, and a rejected task aborts the whole
|
|
17
|
+
* superstep: every sibling tool call in the same parallel batch dies with AbortError and the run
|
|
18
|
+
* ends on "Multiple errors occurred during superstep 0", discarding work the other calls had
|
|
19
|
+
* already finished. A tool failure has to come back as something the model can read and correct
|
|
20
|
+
* instead — most of them are the model's own mistake (an argument outside an enum, a hallucinated
|
|
21
|
+
* tool name), and the error text already names what was expected.
|
|
22
|
+
*
|
|
23
|
+
* Awaiting `invoke` is what makes the catch reachable: the tool's schema is validated inside it, so
|
|
24
|
+
* a bad argument rejects asynchronously and returning the promise unawaited would carry the
|
|
25
|
+
* rejection straight past this handler.
|
|
26
|
+
*
|
|
27
|
+
* Resolution is by the tool's OWN name, not by the map key. `bindTools` advertises `tool.name`, so
|
|
28
|
+
* that is what the model calls — a map keyed by a local variable silently loses any tool whose two
|
|
29
|
+
* names drifted apart, leaving it advertised, callable, and permanently "not found". The key stays
|
|
30
|
+
* as a fallback so a caller may still address a tool by it.
|
|
31
|
+
*/
|
|
32
|
+
export const safeInvokeTool = async (tools: AgentToolSet, toolCall: ToolCall): Promise<unknown> => {
|
|
33
|
+
const tool = tools[toolCall.name]
|
|
34
|
+
?? Object.values(tools).find(entry => entry.name === toolCall.name)
|
|
35
|
+
|
|
36
|
+
if (tool == null) {
|
|
37
|
+
return toErrorResponse(new Error(`Tool ${toolCall.name} not found`))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
return await tool.invoke(toolCall.args)
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.warn(`Error during tool call ${toolCall.name}:`, e)
|
|
44
|
+
return toErrorResponse(e)
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './consts.js'
|
|
2
|
+
export * from './errors.js'
|
|
3
|
+
export type * from './types.js'
|
|
4
|
+
export * from './model.js'
|
|
5
|
+
export * from './service.js'
|
|
6
|
+
export * from './helpers/index.js'
|
|
7
|
+
export * from './stores/index.js'
|
|
8
|
+
export * from './runtime/provider.js'
|
|
9
|
+
export * from './runtime/transport.js'
|
|
10
|
+
export * from './runtime/checkpoint.js'
|
|
11
|
+
export * from './plugins/export.js'
|