@tangleai/agents 0.21.1

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/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@tangleai/agents",
3
+ "version": "0.21.1",
4
+ "description": "Validated tools, bounded agents, action programs, recursive execution and guarded refinement.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./src/index.js",
8
+ "types": "./src/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.d.ts",
12
+ "import": "./src/index.js",
13
+ "default": "./src/index.js"
14
+ },
15
+ "./toolbox": {
16
+ "types": "./src/toolbox.d.ts",
17
+ "import": "./src/toolbox.js",
18
+ "default": "./src/toolbox.js"
19
+ },
20
+ "./agent": {
21
+ "types": "./src/agent.d.ts",
22
+ "import": "./src/agent.js",
23
+ "default": "./src/agent.js"
24
+ },
25
+ "./program": {
26
+ "types": "./src/program.d.ts",
27
+ "import": "./src/program.js",
28
+ "default": "./src/program.js"
29
+ },
30
+ "./recursive": {
31
+ "types": "./src/recursive.d.ts",
32
+ "import": "./src/recursive.js",
33
+ "default": "./src/recursive.js"
34
+ },
35
+ "./refine": {
36
+ "types": "./src/refine.d.ts",
37
+ "import": "./src/refine.js",
38
+ "default": "./src/refine.js"
39
+ },
40
+ "./schemas/program": {
41
+ "types": "./src/schemas/program.d.ts",
42
+ "import": "./src/schemas/program.js",
43
+ "default": "./src/schemas/program.js"
44
+ },
45
+ "./program-result": {
46
+ "types": "./src/program-result.d.ts",
47
+ "import": "./src/program-result.js",
48
+ "default": "./src/program-result.js"
49
+ },
50
+ "./program-session": {
51
+ "types": "./src/program-session.d.ts",
52
+ "import": "./src/program-session.js",
53
+ "default": "./src/program-session.js"
54
+ },
55
+ "./package.json": "./package.json"
56
+ },
57
+ "engines": {
58
+ "node": ">=24"
59
+ },
60
+ "sideEffects": false,
61
+ "dependencies": {
62
+ "@jarenjs/core": "0.84.3",
63
+ "@jarenjs/validate": "0.84.3",
64
+ "@tangleai/models": "^0.21.1",
65
+ "@tangleai/context": "^0.21.1",
66
+ "@jarenjs/contract": "0.84.3"
67
+ },
68
+ "private": false,
69
+ "files": [
70
+ "src/**/*.js",
71
+ "src/**/*.d.ts",
72
+ "README.md",
73
+ "LICENSE",
74
+ "CHANGELOG.md"
75
+ ],
76
+ "publishConfig": {
77
+ "access": "public",
78
+ "registry": "https://registry.npmjs.org/"
79
+ },
80
+ "repository": {
81
+ "type": "git",
82
+ "url": "git+https://github.com/jklarenbeek/tangleai.git",
83
+ "directory": "packages/agents"
84
+ }
85
+ }
package/src/agent.d.ts ADDED
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @typedef {Object} AgentHooks
3
+ * @property {(text: string) => void} [onDelta] - streamed reply text
4
+ * @property {(text: string) => void} [onReasoning] - streamed thinking
5
+ * @property {(call: { name: string, arguments: string }) => void} [onToolCall]
6
+ * @property {(step: { name: string, result: any }) => void} [onToolResult]
7
+ * @property {AbortSignal} [signal]
8
+ */
9
+ /**
10
+ * @param {{ client: { complete: (request: any) => Promise<any> },
11
+ * toolbox?: { toFunctionTools: () => any[], execute: (name: string, args: any) => any } | null,
12
+ * system?: string, maxToolRounds?: number, maxToolResultChars?: number,
13
+ * historyBudget?: number, ledger?: any,
14
+ * budget?: { turns?: number, tokens?: number, ms?: number,
15
+ * spent?: { turns?: number, tokens?: number, ms?: number } },
16
+ * retrieval?: { memories?: { tags?: string[], where?: any, limit?: number },
17
+ * skills?: { tags?: string[], where?: any, limit?: number } },
18
+ * now?: () => number,
19
+ * environment?: any, transcript?: { slot?: string, window?: number },
20
+ * compaction?: (droppedRounds: any[][], addresses?: any[]) => string }} options
21
+ * - `historyBudget` caps the request history in CHARACTERS (tokens
22
+ * are provider-private; characters are deterministic). When a
23
+ * request would exceed it, the middle of the conversation is
24
+ * replaced by one synopsis message; the system prompt, the first
25
+ * user message and the largest tail that fits always survive, and
26
+ * cuts happen only at tool-round boundaries so `tool_calls`/`tool`
27
+ * pairing stays wire-legal. `compaction` replaces the built-in
28
+ * synopsis writer (it receives the dropped rounds, each an array of
29
+ * wire messages, and — with a ledger — the address of each). The
30
+ * returned transcript is always the FULL, uncompacted history.
31
+ * - `ledger` (anything with `putSlot`/`getSlot`/`readSlot`, normally
32
+ * `createLedger()`) makes compaction RECOVERABLE: each dropped round
33
+ * is archived to a content-addressed slot before the synopsis is
34
+ * written, every synopsis line carries its address, and a `recall`
35
+ * tool is registered so the model can fetch one back. Nothing leaves
36
+ * the request without a copy that can be named. With no ledger the
37
+ * original lossy path runs unchanged. A ledger also supplies the
38
+ * active goal composed into every request, and the memories and
39
+ * skills `retrieval` asks for.
40
+ * - `budget` is a hard stop, not a warning: `turns` (one turn is one
41
+ * MODEL CALL — the unit that costs money and the unit a resumed
42
+ * session keeps counting), `tokens` and `ms` each end the run with a
43
+ * named `stopReason` (`budget-turns`, `budget-tokens`, `budget-ms`)
44
+ * and a message saying what remains. Each is optional; `spent` seeds
45
+ * the counters so a budget survives a reload. The `ms` clock starts at
46
+ * the first model call and is wall-clock from there.
47
+ * - `retrieval` composes ledger memories and skills into the system
48
+ * prompt of every request, with the ledger's own `recall` query shape.
49
+ * Absent, nothing is retrieved — the goal is unconditional, but what
50
+ * else is worth carrying is the host's call.
51
+ * - `now` returns milliseconds (`Date.now` by default), injected so a
52
+ * time budget is testable.
53
+ * - `environment` (from `createEnvironment`) registers the corpus
54
+ * operations as tools — `env_digest`, `env_peek`, `env_grep`,
55
+ * `env_chunk`, `env_stat`, `env_read` — beside the host's, skipping any
56
+ * name the host already registered. Content never enters a request
57
+ * unasked: every one of them answers with metadata and addresses
58
+ * except `env_read`, which makes the model state a character budget.
59
+ * - `transcript` (needs `environment`) makes the CONVERSATION one of
60
+ * those slots: it is written whole before every call and the request
61
+ * keeps `window` round units plus the address of the rest. That is the
62
+ * alternative to `historyBudget` rather than a tuning of it — there is
63
+ * no budget to exceed when the history is addressed instead of resent.
64
+ * Both together is legal and redundant; neither changes the other's
65
+ * behaviour.
66
+ * @returns {{ send: (history: any[], hooks?: AgentHooks) => Promise<{
67
+ * message: any, messages: any[], steps: any[], stopReason: string }>,
68
+ * resume: (history?: any[], hooks?: AgentHooks) => Promise<{
69
+ * message: any, messages: any[], steps: any[], stopReason: string }>,
70
+ * spend: () => { turns: number, tokens: number, ms: number } }}
71
+ */
72
+ export function createAgent(options: {
73
+ client: {
74
+ complete: (request: any) => Promise<any>;
75
+ };
76
+ toolbox?: {
77
+ toFunctionTools: () => any[];
78
+ execute: (name: string, args: any) => any;
79
+ } | null;
80
+ system?: string;
81
+ maxToolRounds?: number;
82
+ maxToolResultChars?: number;
83
+ historyBudget?: number;
84
+ ledger?: any;
85
+ budget?: {
86
+ turns?: number;
87
+ tokens?: number;
88
+ ms?: number;
89
+ spent?: {
90
+ turns?: number;
91
+ tokens?: number;
92
+ ms?: number;
93
+ };
94
+ };
95
+ retrieval?: {
96
+ memories?: {
97
+ tags?: string[];
98
+ where?: any;
99
+ limit?: number;
100
+ };
101
+ skills?: {
102
+ tags?: string[];
103
+ where?: any;
104
+ limit?: number;
105
+ };
106
+ };
107
+ now?: () => number;
108
+ environment?: any;
109
+ transcript?: {
110
+ slot?: string;
111
+ window?: number;
112
+ };
113
+ compaction?: (droppedRounds: any[][], addresses?: any[]) => string;
114
+ }): {
115
+ send: (history: any[], hooks?: AgentHooks) => Promise<{
116
+ message: any;
117
+ messages: any[];
118
+ steps: any[];
119
+ stopReason: string;
120
+ }>;
121
+ resume: (history?: any[], hooks?: AgentHooks) => Promise<{
122
+ message: any;
123
+ messages: any[];
124
+ steps: any[];
125
+ stopReason: string;
126
+ }>;
127
+ spend: () => {
128
+ turns: number;
129
+ tokens: number;
130
+ ms: number;
131
+ };
132
+ };
133
+ /**
134
+ * The conversation as text a `grep` can answer from: one header line per
135
+ * message, then its content. Line-oriented on purpose — `grep` reports
136
+ * the line that matched, so a tool result written as one JSON line comes
137
+ * back as one legible hit with its address beside it.
138
+ * @param {any[]} messages
139
+ * @returns {string}
140
+ */
141
+ export function transcriptText(messages: any[]): string;
142
+ export type AgentHooks = {
143
+ /**
144
+ * - streamed reply text
145
+ */
146
+ onDelta?: (text: string) => void;
147
+ /**
148
+ * - streamed thinking
149
+ */
150
+ onReasoning?: (text: string) => void;
151
+ onToolCall?: (call: {
152
+ name: string;
153
+ arguments: string;
154
+ }) => void;
155
+ onToolResult?: (step: {
156
+ name: string;
157
+ result: any;
158
+ }) => void;
159
+ signal?: AbortSignal;
160
+ };