@adia-ai/agent 0.8.29 → 0.8.31

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 (58) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +384 -11
  3. package/agent.d.ts +44 -3
  4. package/agent.d.ts.map +1 -1
  5. package/agent.js +172 -8
  6. package/agent.js.map +1 -1
  7. package/events.d.ts +46 -2
  8. package/events.d.ts.map +1 -1
  9. package/events.js +30 -1
  10. package/events.js.map +1 -1
  11. package/guardrail.d.ts +68 -0
  12. package/guardrail.d.ts.map +1 -0
  13. package/guardrail.js +79 -0
  14. package/guardrail.js.map +1 -0
  15. package/index.d.ts +15 -7
  16. package/index.d.ts.map +1 -1
  17. package/index.js +7 -3
  18. package/index.js.map +1 -1
  19. package/integrations.d.ts +19 -2
  20. package/integrations.d.ts.map +1 -1
  21. package/integrations.js +6 -1
  22. package/integrations.js.map +1 -1
  23. package/loop.d.ts +19 -8
  24. package/loop.d.ts.map +1 -1
  25. package/loop.js +176 -9
  26. package/loop.js.map +1 -1
  27. package/mcp-transport.d.ts +128 -0
  28. package/mcp-transport.d.ts.map +1 -0
  29. package/mcp-transport.js +361 -0
  30. package/mcp-transport.js.map +1 -0
  31. package/mcp.d.ts +120 -0
  32. package/mcp.d.ts.map +1 -0
  33. package/mcp.js +264 -0
  34. package/mcp.js.map +1 -0
  35. package/package.json +1 -1
  36. package/resource.d.ts +28 -3
  37. package/resource.d.ts.map +1 -1
  38. package/resource.js +43 -5
  39. package/resource.js.map +1 -1
  40. package/session.d.ts +18 -0
  41. package/session.d.ts.map +1 -1
  42. package/session.js +40 -0
  43. package/session.js.map +1 -1
  44. package/stub.d.ts +8 -0
  45. package/stub.d.ts.map +1 -1
  46. package/stub.js +13 -0
  47. package/stub.js.map +1 -1
  48. package/tools.d.ts +6 -0
  49. package/tools.d.ts.map +1 -1
  50. package/tools.js.map +1 -1
  51. package/trace.d.ts +50 -0
  52. package/trace.d.ts.map +1 -0
  53. package/trace.js +60 -0
  54. package/trace.js.map +1 -0
  55. package/workflow.d.ts +113 -11
  56. package/workflow.d.ts.map +1 -1
  57. package/workflow.js +93 -15
  58. package/workflow.js.map +1 -1
package/mcp.js ADDED
@@ -0,0 +1,264 @@
1
+ /**
2
+ * MCP-client mode — a server becomes a tool/resource source.
3
+ *
4
+ * `mcpServer({ url })` declares a source; the agent connects it and every
5
+ * tool the server lists arrives as a normal `ToolDef` in the loop. Nothing
6
+ * downstream knows the difference: guardrails see the calls, the tracer
7
+ * records the executions, `checkInput` runs at the boundary, a failure comes
8
+ * back as an `isError` tool_result. That sameness is the point — MCP is a
9
+ * SOURCE of tools, not a second kind of tool.
10
+ *
11
+ * How it composes with the tier-2 registry (integrations.ts):
12
+ * - `register` — used, and it is the fail-fast the lead cares about: two
13
+ * servers exposing `search`, or a server tool colliding with a declared
14
+ * one, throws at connect instead of silently shadowing.
15
+ * - `buildToolDispatch` — used: the ONE place a manifest becomes a ToolDef
16
+ * (law 8's shared executor seam) stays the only place.
17
+ * - `resolveIntegrations` — deliberately NOT used. Its two jobs are
18
+ * persona-enablement intersection and dropping manifests whose env key is
19
+ * unprovisioned; an MCP server has neither a persona list nor an env key,
20
+ * and its `MAX_INTEGRATIONS` cap SILENTLY drops the overflow. A server
21
+ * exposing more tools than the cap is a real thing (the in-repo a2ui
22
+ * server has ~20), and quietly serving two thirds of them is the failure
23
+ * mode this whole feature is supposed to prevent. So the cap here is
24
+ * explicit and LOUD: `maxTools` (default `MAX_INTEGRATIONS`) throws when
25
+ * exceeded, and `tools` narrows the list on purpose.
26
+ *
27
+ * Schemas arrive as JSON Schema and are passed to the model VERBATIM. They
28
+ * are not trimmed to `checkInput`'s allowlist: the MCP server is the
29
+ * authoritative validator (its rejection comes back as an error result the
30
+ * model can read), so a faithful copy of a contract someone else owns beats
31
+ * a locally-enforceable subset that describes the tool less accurately. The
32
+ * manifest says so with `validation: 'remote'` rather than leaving it
33
+ * implied.
34
+ *
35
+ * RESOURCES map onto the two existing modes and no new one. `tool` is the
36
+ * default — the harness cannot know which of a remote server's resources an
37
+ * app wants in every prompt, and attaching all of them would grow the system
38
+ * prompt without bound — and `attach: [name|uri]` promotes the ones the APP
39
+ * decides belong there every turn. That is the whole application-controlled
40
+ * vs. model-controlled distinction MCP itself draws, expressed with the
41
+ * modes the harness already has. gh#671's third mode is NOT needed here; see
42
+ * the README's ruling.
43
+ *
44
+ * Everything a server returns — tool descriptions, resource text, error
45
+ * strings — is DATA. A description that reads like an instruction is still a
46
+ * description; it lands inside the tool spec or a `<resource>` envelope and
47
+ * decides nothing about what the harness does.
48
+ */
49
+ import { buildToolDispatch, createIntegrationRegistry, MAX_INTEGRATIONS, } from './integrations.js';
50
+ import { defineResource } from './resource.js';
51
+ import { connectStreamableHttp, McpCallError, McpConnectionError, } from './mcp-transport.js';
52
+ /** Declare an MCP server as a source. Pure — no connection happens here, so
53
+ * this is safe to call at module scope; the agent connects on first use (or
54
+ * eagerly via `agent.connect()`). */
55
+ export function mcpServer(options) {
56
+ if (!options?.url)
57
+ throw new Error('mcpServer: url is required');
58
+ return { kind: 'mcp', url: options.url, options };
59
+ }
60
+ function sanitize(name) {
61
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_');
62
+ }
63
+ /** Pull every page of a cursor-paginated list method. The page bound is a
64
+ * guard against a server that keeps handing back a cursor, not a limit on
65
+ * legitimate size. */
66
+ async function listAll(connection, method, key, signal) {
67
+ const items = [];
68
+ let cursor;
69
+ for (let page = 0; page < 50; page++) {
70
+ const result = await connection.request(method, cursor ? { cursor } : {}, signal ? { signal } : undefined);
71
+ const batch = result?.[key];
72
+ if (Array.isArray(batch))
73
+ items.push(...batch);
74
+ const next = result?.['nextCursor'];
75
+ if (typeof next !== 'string' || !next)
76
+ return items;
77
+ cursor = next;
78
+ }
79
+ throw new McpConnectionError(`${method} paginated past 50 pages`, connection.url);
80
+ }
81
+ /** MCP content blocks → the one string a tool_result carries. Non-text
82
+ * blocks are named rather than dropped: a model told "[image/png, 4.2 kB
83
+ * omitted]" can ask for something else; a model handed silence cannot. */
84
+ function contentToText(blocks) {
85
+ if (!Array.isArray(blocks))
86
+ return typeof blocks === 'string' ? blocks : JSON.stringify(blocks ?? null);
87
+ return blocks
88
+ .map((raw) => {
89
+ const block = (raw ?? {});
90
+ if (typeof block.text === 'string')
91
+ return block.text;
92
+ if (typeof block.blob === 'string') {
93
+ return `[${block.mimeType ?? 'binary'}, ${block.blob.length} base64 chars omitted]`;
94
+ }
95
+ return JSON.stringify(block);
96
+ })
97
+ .join('\n');
98
+ }
99
+ function toolManifest(connection, listing, prefix) {
100
+ const wireName = sanitize(`${prefix}${listing.name}`);
101
+ return {
102
+ id: `mcp:${connection.url}:${listing.name}`,
103
+ tool: {
104
+ name: wireName,
105
+ ...(listing.description ? { description: listing.description } : {}),
106
+ inputSchema: listing.inputSchema ?? { type: 'object', properties: {} },
107
+ },
108
+ label: listing.title ?? listing.name,
109
+ validation: 'remote',
110
+ auth: { kind: 'none' },
111
+ execute: async (input, ctx) => {
112
+ // The turn's signal reaches the wire: a user who aborts mid-answer
113
+ // cancels the server call instead of leaving it running and ignored.
114
+ const result = await connection.request('tools/call', { name: listing.name, arguments: input }, ctx?.signal ? { signal: ctx.signal } : undefined);
115
+ const text = contentToText(result?.content);
116
+ // A tool the SERVER rejected must reach the model as an error, not as
117
+ // prose that reads like a successful answer. `executeTool` turns a
118
+ // throw into an isError tool_result, so throwing is how a ToolDef says
119
+ // "this failed" without a second return shape.
120
+ if (result?.isError)
121
+ throw new McpCallError(text || 'tool reported an error', connection.url);
122
+ if (text)
123
+ return text;
124
+ return result?.structuredContent ?? '';
125
+ },
126
+ };
127
+ }
128
+ function resourceDef(connection, listing, prefix, mode) {
129
+ const name = sanitize(`${prefix}${listing.name ?? listing.uri}`);
130
+ return defineResource({
131
+ name,
132
+ description: listing.description ?? `MCP resource ${listing.uri}`,
133
+ mode,
134
+ get: async (_input, ctx) => {
135
+ const result = await connection.request('resources/read', { uri: listing.uri }, ctx?.signal ? { signal: ctx.signal } : undefined);
136
+ return contentToText(result?.contents);
137
+ },
138
+ });
139
+ }
140
+ /** Connect one declared source: handshake, list, register, build. Any
141
+ * failure along the way throws `McpConnectionError` — including a failure
142
+ * the REGISTRY raises, which is a plain Error about a manifest and would
143
+ * otherwise escape as a different error class than the one the failure
144
+ * table promises. The caller never receives a half-built source or an empty
145
+ * tool list that looks like a server with nothing to offer.
146
+ *
147
+ * `signal` cancels the connect in flight (the turn that triggered it was
148
+ * aborted); it does NOT stay attached to the resulting connection, whose
149
+ * later calls carry their own turn's signal. */
150
+ export async function connectMcpSource(source, opts = {}) {
151
+ const options = source.options;
152
+ const signal = opts.signal;
153
+ const connection = await connectStreamableHttp(options, signal ? { signal } : {});
154
+ try {
155
+ return await describeSource(source, connection, signal);
156
+ }
157
+ catch (err) {
158
+ // The handshake succeeded and something after it did not: the session on
159
+ // the server is ours and nobody else holds a reference to it, so it gets
160
+ // closed here rather than left to a timeout.
161
+ await connection.close().catch(() => { });
162
+ throw err;
163
+ }
164
+ }
165
+ async function describeSource(source, connection, signal) {
166
+ const options = source.options;
167
+ const prefix = options.prefix ?? '';
168
+ let listings;
169
+ try {
170
+ listings = await listAll(connection, 'tools/list', 'tools', signal);
171
+ }
172
+ catch (err) {
173
+ throw new McpConnectionError(`tools/list failed: ${err.message}`, source.url, { cause: err });
174
+ }
175
+ const allow = options.tools ? new Set(options.tools) : null;
176
+ const selected = allow ? listings.filter(t => allow.has(t.name)) : listings;
177
+ if (allow) {
178
+ const missing = [...allow].filter(name => !listings.some(t => t.name === name));
179
+ if (missing.length) {
180
+ throw new McpConnectionError(`requested tool(s) ${missing.join(', ')} are not exposed by this server ` +
181
+ `(it lists: ${listings.map(t => t.name).join(', ') || 'nothing'})`, source.url);
182
+ }
183
+ }
184
+ const maxTools = options.maxTools ?? MAX_INTEGRATIONS;
185
+ if (selected.length > maxTools) {
186
+ throw new McpConnectionError(`server exposes ${selected.length} tools, over the ${maxTools} cap — ` +
187
+ 'raise maxTools or narrow the list with tools: [...]', source.url);
188
+ }
189
+ // Registration and ToolDef construction raise plain Errors about a
190
+ // manifest — a duplicate wire name after sanitizing, an empty name, an
191
+ // inputSchema that is not an object. They are connect-time faults like any
192
+ // other, so they wear the connect-time class (cause preserved) instead of
193
+ // escaping as something a caller catching McpConnectionError would miss.
194
+ let toolDefs;
195
+ try {
196
+ const registry = createIntegrationRegistry();
197
+ for (const listing of selected)
198
+ registry.register(toolManifest(connection, listing, prefix));
199
+ const dispatch = buildToolDispatch(registry.list(), {});
200
+ toolDefs = 'tools' in dispatch ? dispatch.tools : [];
201
+ }
202
+ catch (err) {
203
+ if (err instanceof McpConnectionError)
204
+ throw err;
205
+ throw new McpConnectionError(`cannot register this server's tools: ${err.message}`, source.url, { cause: err });
206
+ }
207
+ let resourceDefs = [];
208
+ let resourceReport = [];
209
+ const serverDoesResources = !!connection.capabilities['resources'];
210
+ if (options.resources !== false && serverDoesResources) {
211
+ let resourceListings;
212
+ try {
213
+ resourceListings = await listAll(connection, 'resources/list', 'resources', signal);
214
+ }
215
+ catch (err) {
216
+ throw new McpConnectionError(`resources/list failed: ${err.message}`, source.url, { cause: err });
217
+ }
218
+ const attachSet = new Set(options.attach ?? []);
219
+ const unknownAttach = [...attachSet].filter(key => !resourceListings.some(r => r.name === key || r.uri === key));
220
+ if (unknownAttach.length) {
221
+ throw new McpConnectionError(`attach names ${unknownAttach.join(', ')} match no listed resource ` +
222
+ `(it lists: ${resourceListings.map(r => r.name ?? r.uri).join(', ') || 'nothing'})`, source.url);
223
+ }
224
+ try {
225
+ resourceDefs = resourceListings.map((listing) => {
226
+ const mode = attachSet.has(listing.name ?? '') || attachSet.has(listing.uri) ? 'attach' : 'tool';
227
+ return resourceDef(connection, listing, prefix, mode);
228
+ });
229
+ }
230
+ catch (err) {
231
+ throw new McpConnectionError(`cannot register this server's resources: ${err.message}`, source.url, { cause: err });
232
+ }
233
+ resourceReport = resourceListings.map((listing, index) => ({
234
+ name: resourceDefs[index].name,
235
+ uri: listing.uri,
236
+ mode: resourceDefs[index].mode,
237
+ }));
238
+ }
239
+ return {
240
+ url: source.url,
241
+ serverInfo: connection.serverInfo,
242
+ protocolVersion: connection.protocolVersion,
243
+ tools: toolDefs.map(t => t.name),
244
+ resources: resourceReport,
245
+ connection,
246
+ toolDefs,
247
+ resourceDefs,
248
+ };
249
+ }
250
+ /** The whole assembled tool set must have unique names — the loop dispatches
251
+ * by name, so a duplicate is a tool that silently never runs. Per-server
252
+ * registration cannot see across sources or into the declared tools, so the
253
+ * check lands here, once, over everything. */
254
+ export function assertUniqueToolNames(tools) {
255
+ const seen = new Set();
256
+ for (const tool of tools) {
257
+ if (seen.has(tool.name)) {
258
+ throw new McpConnectionError(`tool name "${tool.name}" is contributed twice (an MCP server collides with a declared tool ` +
259
+ 'or another server) — set a prefix on one of the sources');
260
+ }
261
+ seen.add(tool.name);
262
+ }
263
+ }
264
+ //# sourceMappingURL=mcp.js.map
package/mcp.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["src/mcp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/agent",
3
- "version": "0.8.29",
3
+ "version": "0.8.31",
4
4
  "description": "Composable chat-agent harness — assemble an agent from four declared parts (prompt layers, tools, workflows, resources) on top of @adia-ai/llm. Owns the tool-call loop, the session model, and the one shared event reducer. Works in browser and Node.",
5
5
  "type": "module",
6
6
  "exports": {
package/resource.d.ts CHANGED
@@ -8,20 +8,45 @@
8
8
  * never asks; the data is just there.
9
9
  * - 'tool' — model-controlled: the resource becomes a read-only tool
10
10
  * (`read_<name>`) the model calls when it decides it needs the data.
11
+ *
12
+ * Attach mode inlines somebody else's bytes into the system prompt, so the
13
+ * envelope has to be forgeable-proof: see `attachLayers` for the escaping
14
+ * and why it is not optional.
11
15
  */
12
16
  import type { PromptLayer } from './prompt.js';
13
17
  import { type ToolDef } from './tools.js';
18
+ /** What a `get` may USE — the turn's cancellation, today. Passed as the
19
+ * SECOND argument, so every existing one-argument `get` is unaffected. */
20
+ export interface ResourceContext {
21
+ signal?: AbortSignal;
22
+ }
14
23
  export interface ResourceDef {
15
24
  name: string;
16
25
  description?: string;
17
26
  mode: 'attach' | 'tool';
18
- get: (input?: Record<string, unknown>) => string | Promise<string>;
27
+ /** Tool-mode only: the arguments the model may pass. Without it a
28
+ * tool-mode resource is a zero-argument read, which can't express a
29
+ * parameterized corpus read (`search_chunks(query, kind, limit)`).
30
+ * Attach-mode resources take no input and ignore this. */
31
+ inputSchema?: Record<string, unknown>;
32
+ /** Non-string returns are JSON-serialized — a corpus record is an object,
33
+ * and making every call site stringify was the same tax the tool
34
+ * executor already pays for `execute`. */
35
+ get: (input?: Record<string, unknown>, ctx?: ResourceContext) => unknown | Promise<unknown>;
19
36
  }
20
37
  export declare function defineResource(def: ResourceDef): ResourceDef;
21
38
  /** Attach-mode resources → dynamic prompt layers. Resolution happens at
22
39
  * compose time (async), so the layer text is a plain string by the time
23
- * it reaches composeSystem. */
24
- export declare function attachLayers(resources: ResourceDef[]): Promise<PromptLayer[]>;
40
+ * it reaches composeSystem.
41
+ *
42
+ * The content is DATA, and the envelope is what says so. A resource body
43
+ * that contains `</resource>` would otherwise close its own envelope and
44
+ * everything after it would read as prompt — a server, a file, or a
45
+ * database row could then write instructions into the system prompt. That
46
+ * is a containment bug in the harness, not a hazard the resource's author
47
+ * is responsible for, so the escaping lives here and covers EVERY attach
48
+ * resource, MCP or hand-declared. */
49
+ export declare function attachLayers(resources: ResourceDef[], ctx?: ResourceContext): Promise<PromptLayer[]>;
25
50
  /** Tool-mode resources → read-only tools the model can call. */
26
51
  export declare function resourceTools(resources: ResourceDef[]): ToolDef[];
27
52
  //# sourceMappingURL=resource.d.ts.map
package/resource.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["src/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAS5D;AAED;;gCAEgC;AAChC,wBAAsB,YAAY,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAOnF;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CASjE"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["src/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEtD;2EAC2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB;;;+DAG2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC;;+CAE2C;IAC3C,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,eAAe,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7F;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAY5D;AA0BD;;;;;;;;;;sCAUsC;AACtC,wBAAsB,YAAY,CAChC,SAAS,EAAE,WAAW,EAAE,EACxB,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CASxB;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CASjE"}
package/resource.js CHANGED
@@ -8,6 +8,10 @@
8
8
  * never asks; the data is just there.
9
9
  * - 'tool' — model-controlled: the resource becomes a read-only tool
10
10
  * (`read_<name>`) the model calls when it decides it needs the data.
11
+ *
12
+ * Attach mode inlines somebody else's bytes into the system prompt, so the
13
+ * envelope has to be forgeable-proof: see `attachLayers` for the escaping
14
+ * and why it is not optional.
11
15
  */
12
16
  import { defineTool } from './tools.js';
13
17
  export function defineResource(def) {
@@ -19,16 +23,50 @@ export function defineResource(def) {
19
23
  if (typeof def.get !== 'function') {
20
24
  throw new Error(`defineResource "${def.name}": get() is required`);
21
25
  }
26
+ if (def.inputSchema && def.mode !== 'tool') {
27
+ throw new Error(`defineResource "${def.name}": inputSchema is tool-mode only`);
28
+ }
22
29
  return def;
23
30
  }
31
+ function asText(value) {
32
+ return typeof value === 'string' ? value : JSON.stringify(value ?? null);
33
+ }
34
+ /** Attribute values: a `"` would end the attribute early and let the rest of
35
+ * a name or description pose as further attributes. */
36
+ function escapeAttribute(value) {
37
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
38
+ }
39
+ /** Bodies get the narrowest escape that still makes the envelope
40
+ * unforgeable: only the two STRUCTURAL sequences — an opening `<resource`
41
+ * and a closing `</resource` — have their `<` escaped.
42
+ *
43
+ * Escaping every `<` was the alternative and it is worse: attached bodies
44
+ * here are catalog JSON, component markup, docs. Mangling every tag in them
45
+ * to close one hole degrades the data the model reads on every turn, for a
46
+ * containment property those two sequences already give. A body can no
47
+ * longer end its own envelope or open a nested one that claims a different
48
+ * name; everything else it contains arrives byte-for-byte. */
49
+ function fenceBody(text) {
50
+ return text.replace(/<(\/?resource\b)/gi, '&lt;$1');
51
+ }
24
52
  /** Attach-mode resources → dynamic prompt layers. Resolution happens at
25
53
  * compose time (async), so the layer text is a plain string by the time
26
- * it reaches composeSystem. */
27
- export async function attachLayers(resources) {
54
+ * it reaches composeSystem.
55
+ *
56
+ * The content is DATA, and the envelope is what says so. A resource body
57
+ * that contains `</resource>` would otherwise close its own envelope and
58
+ * everything after it would read as prompt — a server, a file, or a
59
+ * database row could then write instructions into the system prompt. That
60
+ * is a containment bug in the harness, not a hazard the resource's author
61
+ * is responsible for, so the escaping lives here and covers EVERY attach
62
+ * resource, MCP or hand-declared. */
63
+ export async function attachLayers(resources, ctx) {
28
64
  const attach = resources.filter(r => r.mode === 'attach');
29
65
  const resolved = await Promise.all(attach.map(async (r) => ({
30
66
  name: `resource:${r.name}`,
31
- text: `<resource name="${r.name}"${r.description ? ` description="${r.description}"` : ''}>\n${await r.get()}\n</resource>`,
67
+ text: `<resource name="${escapeAttribute(r.name)}"` +
68
+ `${r.description ? ` description="${escapeAttribute(r.description)}"` : ''}>\n` +
69
+ `${fenceBody(asText(await r.get(undefined, ctx)))}\n</resource>`,
32
70
  })));
33
71
  return resolved.filter(l => l.text);
34
72
  }
@@ -39,8 +77,8 @@ export function resourceTools(resources) {
39
77
  .map(r => defineTool({
40
78
  name: `read_${r.name.replace(/[^a-zA-Z0-9_-]/g, '_')}`,
41
79
  description: r.description ?? `Read the "${r.name}" resource`,
42
- inputSchema: { type: 'object', properties: {} },
43
- execute: (input) => r.get(input),
80
+ inputSchema: r.inputSchema ?? { type: 'object', properties: {} },
81
+ execute: (input, toolCtx) => r.get(input, toolCtx.signal ? { signal: toolCtx.signal } : {}),
44
82
  }));
45
83
  }
46
84
  //# sourceMappingURL=resource.js.map
package/resource.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["src/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAStD,MAAM,UAAU,cAAc,CAAC,GAAgB;IAC7C,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACnE,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,IAAI,8BAA8B,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,IAAI,sBAAsB,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;gCAEgC;AAChC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAwB;IACzD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAC,CAAC,EAAC,EAAE,CAAC,CAAC;QACxD,IAAI,EAAE,YAAY,CAAC,CAAC,IAAI,EAAE;QAC1B,IAAI,EAAE,mBAAmB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,MAAM,CAAC,CAAC,GAAG,EAAE,eAAe;KAC5H,CAAC,CAAC,CAAC,CAAC;IACL,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,SAAwB;IACpD,OAAO,SAAS;SACb,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC;QACnB,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,EAAE;QACtD,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,aAAa,CAAC,CAAC,IAAI,YAAY;QAC7D,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;QAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;KACjC,CAAC,CAAC,CAAC;AACR,CAAC"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["src/resource.ts"],"names":[],"mappings":"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"}
package/session.d.ts CHANGED
@@ -9,6 +9,13 @@
9
9
  * `draft` is reduce()'s in-flight assistant turn (text snapshot + parts
10
10
  * accumulated between a round's first chunk and its close). It is part of
11
11
  * the serialized shape on purpose — resuming mid-turn drops nothing.
12
+ *
13
+ * `memory` is the per-session key/value slot (gh#665). It is JSON-safe by
14
+ * contract and written ONLY through `reduce()` — same single-writer rule
15
+ * the messages obey, so a memory write is an event in the same stream and
16
+ * cannot happen behind a consumer's back. Cross-session persistence is a
17
+ * SEAM, not an implementation: a Session serializes with its memory, and
18
+ * where that JSON goes (localStorage, a DB row) stays the caller's.
12
19
  */
13
20
  import type { Part } from '@adia-ai/llm';
14
21
  export interface Msg {
@@ -19,11 +26,22 @@ export interface Draft {
19
26
  text: string;
20
27
  parts: Part[];
21
28
  }
29
+ /** The values a memory slot may hold — the JSON-safe closure. */
30
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
31
+ [key: string]: JsonValue;
32
+ };
22
33
  export interface Session {
23
34
  id: string;
24
35
  messages: Msg[];
25
36
  draft?: Draft;
37
+ /** Per-session memory. Present only once something has been remembered. */
38
+ memory?: Record<string, JsonValue>;
26
39
  }
40
+ /** Throws on anything a Session could not survive `JSON.stringify` →
41
+ * `JSON.parse` unchanged (functions, undefined, Date, Map, cycles). Loud
42
+ * at the write, because the alternative is a session that silently loses
43
+ * a field the next time it round-trips through storage. */
44
+ export declare function assertJsonSafe(value: unknown, path?: string): void;
27
45
  export declare function createSession(id?: string): Session;
28
46
  export declare function toJSON(session: Session): string;
29
47
  export declare function fromJSON(json: string): Session;
package/session.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAID,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAKlD;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM9C"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,iEAAiE;AACjE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtG,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACpC;AAED;;;4DAG4D;AAC5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAW,GAAG,IAAI,CA2BpE;AAID,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAKlD;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM9C"}
package/session.js CHANGED
@@ -9,7 +9,47 @@
9
9
  * `draft` is reduce()'s in-flight assistant turn (text snapshot + parts
10
10
  * accumulated between a round's first chunk and its close). It is part of
11
11
  * the serialized shape on purpose — resuming mid-turn drops nothing.
12
+ *
13
+ * `memory` is the per-session key/value slot (gh#665). It is JSON-safe by
14
+ * contract and written ONLY through `reduce()` — same single-writer rule
15
+ * the messages obey, so a memory write is an event in the same stream and
16
+ * cannot happen behind a consumer's back. Cross-session persistence is a
17
+ * SEAM, not an implementation: a Session serializes with its memory, and
18
+ * where that JSON goes (localStorage, a DB row) stays the caller's.
12
19
  */
20
+ /** Throws on anything a Session could not survive `JSON.stringify` →
21
+ * `JSON.parse` unchanged (functions, undefined, Date, Map, cycles). Loud
22
+ * at the write, because the alternative is a session that silently loses
23
+ * a field the next time it round-trips through storage. */
24
+ export function assertJsonSafe(value, path = 'memory') {
25
+ if (value === null)
26
+ return;
27
+ switch (typeof value) {
28
+ case 'string':
29
+ case 'number':
30
+ case 'boolean':
31
+ if (typeof value === 'number' && !Number.isFinite(value)) {
32
+ throw new Error(`${path}: ${value} is not JSON-safe`);
33
+ }
34
+ return;
35
+ case 'object': {
36
+ if (Array.isArray(value)) {
37
+ value.forEach((item, i) => assertJsonSafe(item, `${path}[${i}]`));
38
+ return;
39
+ }
40
+ const proto = Object.getPrototypeOf(value);
41
+ if (proto !== Object.prototype && proto !== null) {
42
+ throw new Error(`${path}: ${value.constructor?.name ?? 'object'} is not JSON-safe (plain objects only)`);
43
+ }
44
+ for (const [key, item] of Object.entries(value)) {
45
+ assertJsonSafe(item, `${path}.${key}`);
46
+ }
47
+ return;
48
+ }
49
+ default:
50
+ throw new Error(`${path}: ${typeof value} is not JSON-safe`);
51
+ }
52
+ }
13
53
  let sessionCounter = 0;
14
54
  export function createSession(id) {
15
55
  return {
package/session.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoBH,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,MAAM,UAAU,aAAa,CAAC,EAAW;IACvC,OAAO;QACL,EAAE,EAAE,EAAE,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE;QAC5D,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAgB;IACrC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IAC3C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAyBH;;;4DAG4D;AAC5D,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAI,GAAG,QAAQ;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO;IAC3B,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,KAAK,mBAAmB,CAAC,CAAC;YACxD,CAAC;YACD,OAAO;QACT,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAClE,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAM,KAAgB,CAAC,WAAW,EAAE,IAAI,IAAI,QAAQ,wCAAwC,CAAC,CAAC;YACvH,CAAC;YACD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;gBAC3E,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,OAAO;QACT,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,KAAK,mBAAmB,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,MAAM,UAAU,aAAa,CAAC,EAAW;IACvC,OAAO;QACL,EAAE,EAAE,EAAE,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE;QAC5D,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAgB;IACrC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IAC3C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/stub.d.ts CHANGED
@@ -11,6 +11,14 @@ import type { ChatOpts, LLMClient, ToolUse } from '@adia-ai/llm';
11
11
  export interface ScriptedTurn {
12
12
  text?: string;
13
13
  toolUse?: ToolUse[];
14
+ /** Argument fragments streamed BEFORE the complete `toolUse` chunks, the
15
+ * way Anthropic/OpenAI stream partial tool input. Scripted explicitly:
16
+ * a stub that invented its own split would be testing the stub. */
17
+ toolUseDeltas?: Array<{
18
+ id: string;
19
+ name: string;
20
+ partial: string;
21
+ }>;
14
22
  stopReason?: string;
15
23
  }
16
24
  export interface ScriptClient extends LLMClient {
package/stub.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"stub.d.ts","sourceRoot":"","sources":["src/stub.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAc,SAAS,EAAe,OAAO,EAAE,MAAM,cAAc,CAAC;AAE1F,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C;mDAC+C;IAC/C,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,CA0ChE"}
1
+ {"version":3,"file":"stub.d.ts","sourceRoot":"","sources":["src/stub.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAc,SAAS,EAAe,OAAO,EAAE,MAAM,cAAc,CAAC;AAE1F,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB;;wEAEoE;IACpE,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C;mDAC+C;IAC/C,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,CAuDhE"}
package/stub.js CHANGED
@@ -37,6 +37,19 @@ export function scriptClient(turns) {
37
37
  snapshot = turn.text;
38
38
  yield { type: 'text', text: turn.text, snapshot };
39
39
  }
40
+ const snapshots = new Map();
41
+ for (const delta of turn.toolUseDeltas ?? []) {
42
+ const accumulated = (snapshots.get(delta.id) ?? '') + delta.partial;
43
+ snapshots.set(delta.id, accumulated);
44
+ yield {
45
+ type: 'tool_use_delta',
46
+ id: delta.id,
47
+ name: delta.name,
48
+ index: 0,
49
+ partial: delta.partial,
50
+ snapshot: accumulated,
51
+ };
52
+ }
40
53
  for (const call of turn.toolUse ?? []) {
41
54
  yield { type: 'tool_use', id: call.id, name: call.name, input: call.input };
42
55
  }
package/stub.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"stub.js","sourceRoot":"","sources":["src/stub.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,MAAM,UAAU,YAAY,CAAC,KAAqB;IAChD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAA6B,EAAE,CAAC;IAE3C,SAAS,IAAI;QACX,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,GAAG,CAAC,aAAa,KAAK,CAAC,MAAM,iBAAiB,CAAC,CAAC;QACvG,MAAM,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,IAAuB;YAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;YACpB,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;gBACrB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;gBAC9B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;gBAC1E,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3D,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,CAAC,MAAM,CAAC,IAAuB;YACnC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;YACpB,IAAI,QAAQ,GAAG,EAAE,CAAC;YAClB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;gBACrB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;YACpD,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBACtC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9E,CAAC;YACD,MAAM;gBACJ,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;gBAC9B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;aAC3E,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"stub.js","sourceRoot":"","sources":["src/stub.ts"],"names":[],"mappings":"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"}
package/tools.d.ts CHANGED
@@ -12,9 +12,15 @@
12
12
  * can validate inside their own `execute`.
13
13
  */
14
14
  import type { ToolSpec } from '@adia-ai/llm';
15
+ import type { JsonValue } from './session.js';
15
16
  export interface ToolContext {
16
17
  sessionId: string;
17
18
  signal?: AbortSignal;
19
+ /** Ask for a session-memory write. This does NOT write: the loop turns
20
+ * the patch into a `memory` event, and `reduce()` remains the only
21
+ * writer. Absent when a tool runs outside the loop (a direct
22
+ * `executeTool` call). A null value deletes the key. */
23
+ remember?: (patch: Record<string, JsonValue | null>) => void;
18
24
  }
19
25
  export interface ToolDef {
20
26
  name: string;
package/tools.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CAC/E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAWhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAMpD;AAED;gDACgD;AAChD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAmBzG;AAeD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;oEAEoE;AACpE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,WAAW,CAAC,CA4BtB"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;6DAGyD;IACzD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CAC/E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAWhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAMpD;AAED;gDACgD;AAChD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAmBzG;AAeD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;oEAEoE;AACpE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,WAAW,CAAC,CA4BtB"}