@ironflow/node 0.19.3 → 0.20.0

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 (81) hide show
  1. package/README.md +5 -2
  2. package/dist/agent/agent.d.ts +60 -0
  3. package/dist/agent/agent.d.ts.map +1 -0
  4. package/dist/agent/agent.js +133 -0
  5. package/dist/agent/agent.js.map +1 -0
  6. package/dist/agent/approve.d.ts +23 -0
  7. package/dist/agent/approve.d.ts.map +1 -0
  8. package/dist/agent/approve.js +42 -0
  9. package/dist/agent/approve.js.map +1 -0
  10. package/dist/agent/dispatch.d.ts +63 -0
  11. package/dist/agent/dispatch.d.ts.map +1 -0
  12. package/dist/agent/dispatch.js +130 -0
  13. package/dist/agent/dispatch.js.map +1 -0
  14. package/dist/agent/errors.d.ts +90 -0
  15. package/dist/agent/errors.d.ts.map +1 -0
  16. package/dist/agent/errors.js +136 -0
  17. package/dist/agent/errors.js.map +1 -0
  18. package/dist/agent/index.d.ts +30 -0
  19. package/dist/agent/index.d.ts.map +1 -0
  20. package/dist/agent/index.js +27 -0
  21. package/dist/agent/index.js.map +1 -0
  22. package/dist/agent/internal-registry.d.ts +27 -0
  23. package/dist/agent/internal-registry.d.ts.map +1 -0
  24. package/dist/agent/internal-registry.js +36 -0
  25. package/dist/agent/internal-registry.js.map +1 -0
  26. package/dist/agent/internal.d.ts +24 -0
  27. package/dist/agent/internal.d.ts.map +1 -0
  28. package/dist/agent/internal.js +29 -0
  29. package/dist/agent/internal.js.map +1 -0
  30. package/dist/agent/llm.d.ts +39 -0
  31. package/dist/agent/llm.d.ts.map +1 -0
  32. package/dist/agent/llm.js +59 -0
  33. package/dist/agent/llm.js.map +1 -0
  34. package/dist/agent/mcp.d.ts +51 -0
  35. package/dist/agent/mcp.d.ts.map +1 -0
  36. package/dist/agent/mcp.js +155 -0
  37. package/dist/agent/mcp.js.map +1 -0
  38. package/dist/agent/memory.d.ts +74 -0
  39. package/dist/agent/memory.d.ts.map +1 -0
  40. package/dist/agent/memory.js +130 -0
  41. package/dist/agent/memory.js.map +1 -0
  42. package/dist/agent/spawn.d.ts +20 -0
  43. package/dist/agent/spawn.d.ts.map +1 -0
  44. package/dist/agent/spawn.js +29 -0
  45. package/dist/agent/spawn.js.map +1 -0
  46. package/dist/agent/tool.d.ts +39 -0
  47. package/dist/agent/tool.d.ts.map +1 -0
  48. package/dist/agent/tool.js +103 -0
  49. package/dist/agent/tool.js.map +1 -0
  50. package/dist/agent/types.d.ts +363 -0
  51. package/dist/agent/types.d.ts.map +1 -0
  52. package/dist/agent/types.js +9 -0
  53. package/dist/agent/types.js.map +1 -0
  54. package/dist/client.d.ts +21 -3
  55. package/dist/client.d.ts.map +1 -1
  56. package/dist/client.js +75 -44
  57. package/dist/client.js.map +1 -1
  58. package/dist/function.d.ts.map +1 -1
  59. package/dist/function.js +16 -0
  60. package/dist/function.js.map +1 -1
  61. package/dist/internal/assert-defined.d.ts +10 -0
  62. package/dist/internal/assert-defined.d.ts.map +1 -0
  63. package/dist/internal/assert-defined.js +15 -0
  64. package/dist/internal/assert-defined.js.map +1 -0
  65. package/dist/projection-runner.d.ts.map +1 -1
  66. package/dist/projection-runner.js +29 -1
  67. package/dist/projection-runner.js.map +1 -1
  68. package/dist/serve.d.ts.map +1 -1
  69. package/dist/serve.js +26 -0
  70. package/dist/serve.js.map +1 -1
  71. package/dist/test/index.d.ts +2 -2
  72. package/dist/test/index.d.ts.map +1 -1
  73. package/dist/test/index.js.map +1 -1
  74. package/dist/types.d.ts +3 -3
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/version.d.ts +1 -1
  77. package/dist/version.js +1 -1
  78. package/dist/worker.d.ts.map +1 -1
  79. package/dist/worker.js +22 -3
  80. package/dist/worker.js.map +1 -1
  81. package/package.json +15 -9
@@ -0,0 +1,155 @@
1
+ /**
2
+ * exposeMcp() — register agent tools with Ironflow's AgentToolsService
3
+ * and serve dispatched invocations over the existing serve() mount.
4
+ *
5
+ * Boot-time flow:
6
+ *
7
+ * 1. Convert each McpToolDef.input (Zod) → JSON Schema (Draft 2020-12)
8
+ * via z.toJSONSchema().
9
+ * 2. POST `/ironflow.v1.AgentToolsService/RegisterTool` with
10
+ * {agentName, callbackUrl, tools[]}.
11
+ * 3. Stash the returned HMAC secret + def closures in the local
12
+ * registry so serve()'s dispatch route can validate inbound calls.
13
+ *
14
+ * Callback flow lives in `dispatch.ts` (HMAC verify + Zod parse + handler).
15
+ *
16
+ * Authoritative authorization stays server-side: scopes on McpToolDef
17
+ * are advisory hints to MCP clients. The server enforces api_key.tool_scopes
18
+ * superset against ToolDef.required_scopes. RegisterTool requires the
19
+ * `agent:tools:register` action on the calling key.
20
+ */
21
+ import { IronflowError } from "@ironflow/core";
22
+ import { z } from "zod";
23
+ import { registerLocal, unregisterLocal, } from "./internal-registry.js";
24
+ const REGISTER_PATH = "/ironflow.v1.AgentToolsService/RegisterTool";
25
+ const UNREGISTER_PATH = "/ironflow.v1.AgentToolsService/UnregisterTool";
26
+ /**
27
+ * Register the supplied MCP tool definitions with the Ironflow server.
28
+ *
29
+ * Returns an active handle once RegisterTool succeeds. Throws on
30
+ * empty tool lists, missing config (server URL / API key / callback URL),
31
+ * or transport failure.
32
+ */
33
+ export async function exposeMcp(config) {
34
+ if (!config.tools.length) {
35
+ throw new IronflowError("exposeMcp() requires at least one tool", {
36
+ code: "AGENT_MCP_NO_TOOLS",
37
+ retryable: false,
38
+ });
39
+ }
40
+ if (!config.callbackUrl) {
41
+ throw new IronflowError("exposeMcp() requires callbackUrl pointing at your serve() mount", { code: "AGENT_MCP_MISSING_CALLBACK_URL", retryable: false });
42
+ }
43
+ const serverUrl = config.serverUrl ??
44
+ process.env.IRONFLOW_URL ??
45
+ process.env.IRONFLOW_SERVER_URL;
46
+ if (!serverUrl) {
47
+ throw new IronflowError("exposeMcp() requires serverUrl (or IRONFLOW_URL / IRONFLOW_SERVER_URL env)", { code: "AGENT_MCP_MISSING_SERVER_URL", retryable: false });
48
+ }
49
+ const apiKey = config.apiKey ?? process.env.IRONFLOW_API_KEY;
50
+ if (!apiKey) {
51
+ throw new IronflowError("exposeMcp() requires apiKey (or IRONFLOW_API_KEY env) with the agent:tools:register action", { code: "AGENT_MCP_MISSING_API_KEY", retryable: false });
52
+ }
53
+ const seen = new Set();
54
+ const toolPayload = config.tools.map((def) => {
55
+ if (seen.has(def.name)) {
56
+ throw new IronflowError(`exposeMcp() received duplicate tool name "${def.name}"`, { code: "AGENT_MCP_DUPLICATE_TOOL", retryable: false });
57
+ }
58
+ seen.add(def.name);
59
+ return toToolDefJSON(def);
60
+ });
61
+ const requestBody = {
62
+ agentName: config.name,
63
+ callbackUrl: config.callbackUrl,
64
+ tools: toolPayload,
65
+ };
66
+ const resp = await postJSON(serverUrl, REGISTER_PATH, apiKey, requestBody);
67
+ const decoded = (await readJSON(resp));
68
+ if (!decoded.hmacSecret || !decoded.registeredToolNames?.length) {
69
+ throw new IronflowError("RegisterTool response missing hmacSecret or registeredToolNames", { code: "AGENT_MCP_INVALID_RESPONSE", retryable: false });
70
+ }
71
+ const qualifiedNames = decoded.registeredToolNames;
72
+ for (const def of config.tools) {
73
+ const qualifiedName = `${config.name}.${def.name}`;
74
+ const entry = {
75
+ agentName: config.name,
76
+ qualifiedName,
77
+ hmacSecret: decoded.hmacSecret,
78
+ def,
79
+ };
80
+ registerLocal(entry);
81
+ }
82
+ let unregistered = false;
83
+ return {
84
+ name: config.name,
85
+ toolCount: qualifiedNames.length,
86
+ status: "active",
87
+ toolNames: qualifiedNames,
88
+ async unregister() {
89
+ if (unregistered) {
90
+ return;
91
+ }
92
+ unregistered = true;
93
+ // Always clear local entries so handler closures stop receiving
94
+ // dispatches even when the server-side call fails.
95
+ unregisterLocal(config.name);
96
+ try {
97
+ const r = await postJSON(serverUrl, UNREGISTER_PATH, apiKey, {
98
+ agentName: config.name,
99
+ });
100
+ await readJSON(r);
101
+ }
102
+ catch (err) {
103
+ throw new IronflowError(`unregister failed: ${err instanceof Error ? err.message : String(err)}`, { code: "AGENT_MCP_UNREGISTER_FAILED", retryable: false });
104
+ }
105
+ },
106
+ };
107
+ }
108
+ function toToolDefJSON(def) {
109
+ let schemaObj;
110
+ try {
111
+ schemaObj = z.toJSONSchema(def.input);
112
+ }
113
+ catch (err) {
114
+ throw new IronflowError(`exposeMcp() failed to convert input schema for "${def.name}": ${err instanceof Error ? err.message : String(err)}`, { code: "AGENT_MCP_SCHEMA_CONVERSION_FAILED", retryable: false });
115
+ }
116
+ return {
117
+ name: def.name,
118
+ description: def.description,
119
+ inputSchemaJson: JSON.stringify(schemaObj),
120
+ requiredScopes: def.scopes ? [...def.scopes] : [],
121
+ timeoutMs: 0,
122
+ };
123
+ }
124
+ async function postJSON(serverUrl, path, apiKey, body) {
125
+ const url = `${serverUrl.replace(/\/+$/, "")}${path}`;
126
+ const resp = await fetch(url, {
127
+ method: "POST",
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ Authorization: `Bearer ${apiKey}`,
131
+ },
132
+ body: JSON.stringify(body),
133
+ });
134
+ if (!resp.ok) {
135
+ const text = await resp.text().catch(() => "");
136
+ throw new IronflowError(`POST ${path} failed: HTTP ${resp.status}${text ? ` — ${text}` : ""}`, { code: "AGENT_MCP_TRANSPORT_ERROR", retryable: false });
137
+ }
138
+ return resp;
139
+ }
140
+ async function readJSON(resp) {
141
+ const text = await resp.text();
142
+ if (!text) {
143
+ return {};
144
+ }
145
+ try {
146
+ return JSON.parse(text);
147
+ }
148
+ catch {
149
+ throw new IronflowError(`response body is not valid JSON: ${text.slice(0, 256)}`, {
150
+ code: "AGENT_MCP_INVALID_RESPONSE",
151
+ retryable: false,
152
+ });
153
+ }
154
+ }
155
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/agent/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * memory() — durable agent memory backed by an entity stream + projection.
3
+ *
4
+ * Composition:
5
+ * - memory.append(eventName, data) → step.run wraps backend.appendEvent +
6
+ * auto-waitForCatchup so memory.get() inside the same run sees the write.
7
+ * - memory.get() → step.run wraps backend.getProjection. In-run cache
8
+ * short-circuits repeated reads. Cache invalidates on append.
9
+ * - memory.entityStream() — kept as NotImplementedError stub. Lands when a
10
+ * concrete cross-agent peer-memory use case surfaces.
11
+ *
12
+ * Cross-run retry safety: append generates a deterministic idempotencyKey
13
+ * (`${runId}:memory.append:${counter}`) so a replayed handler appends the
14
+ * same logical event and the server dedupes server-side.
15
+ *
16
+ * Anti-scope: raw event replay is not exposed. Consumers must register a
17
+ * projection — see MemoryProjectionRequiredError.
18
+ */
19
+ import type { AppendResult, ProjectionStateResult, StepClient } from "@ironflow/core";
20
+ import type { MemoryClient, MemoryConfig } from "./types.js";
21
+ /**
22
+ * Minimal backend the memory client needs. Wraps the IronflowClient surface
23
+ * so tests can substitute a fake without constructing a real client.
24
+ *
25
+ * Method shapes mirror the underlying client methods:
26
+ * - appendEvent → client.streams.append
27
+ * - getProjection → client.projections.get
28
+ * - waitForCatchup → client.projections.waitForCatchup
29
+ */
30
+ export interface MemoryBackend {
31
+ appendEvent(streamId: string, input: {
32
+ name: string;
33
+ data: Record<string, unknown>;
34
+ entityType: string;
35
+ idempotencyKey: string;
36
+ metadata?: Record<string, unknown>;
37
+ }): Promise<AppendResult>;
38
+ getProjection<TState = unknown>(name: string): Promise<ProjectionStateResult<TState>>;
39
+ waitForCatchup(name: string, opts: {
40
+ minSeq: number;
41
+ partition?: string;
42
+ timeoutMs?: number;
43
+ }): Promise<void>;
44
+ }
45
+ /**
46
+ * Per-run in-memory cache for memory.get() reads.
47
+ *
48
+ * Default cache-on; invalidates on memory.append() within the same run.
49
+ * Lives across step boundaries within a run, replaced fresh on replay.
50
+ */
51
+ export interface MemoryRuntimeCache {
52
+ has: boolean;
53
+ value: unknown;
54
+ }
55
+ export declare function createMemoryRuntimeCache(): MemoryRuntimeCache;
56
+ /**
57
+ * Tracks per-run append counter used for idempotency-key generation.
58
+ *
59
+ * Resets per run; replay on the same run reuses the counter value from
60
+ * the call ordering — same sequence of memory.append calls produces the
61
+ * same sequence of keys, so the server dedupes deterministically.
62
+ */
63
+ export interface MemoryRuntimeCounters {
64
+ appendCount: number;
65
+ }
66
+ export declare function createMemoryRuntimeCounters(): MemoryRuntimeCounters;
67
+ /**
68
+ * Build a MemoryClient backed by the supplied backend.
69
+ *
70
+ * The backend is required when `config` is set. Tests pass a fake backend;
71
+ * agent.ts constructs the default backend from a runtime IronflowClient.
72
+ */
73
+ export declare function makeMemory(step: StepClient, config: MemoryConfig | undefined, runId: string, cache: MemoryRuntimeCache, backend: MemoryBackend | undefined, counters?: MemoryRuntimeCounters): MemoryClient;
74
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,UAAU,EACX,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAEV,YAAY,EACZ,YAAY,EAEb,MAAM,YAAY,CAAC;AAgCpB;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GACA,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB,aAAa,CAAC,MAAM,GAAG,OAAO,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1C,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/D,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,wBAAwB,IAAI,kBAAkB,CAE7D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,2BAA2B,IAAI,qBAAqB,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,QAAQ,GAAE,qBAAqD,GAC9D,YAAY,CA+Dd"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * memory() — durable agent memory backed by an entity stream + projection.
3
+ *
4
+ * Composition:
5
+ * - memory.append(eventName, data) → step.run wraps backend.appendEvent +
6
+ * auto-waitForCatchup so memory.get() inside the same run sees the write.
7
+ * - memory.get() → step.run wraps backend.getProjection. In-run cache
8
+ * short-circuits repeated reads. Cache invalidates on append.
9
+ * - memory.entityStream() — kept as NotImplementedError stub. Lands when a
10
+ * concrete cross-agent peer-memory use case surfaces.
11
+ *
12
+ * Cross-run retry safety: append generates a deterministic idempotencyKey
13
+ * (`${runId}:memory.append:${counter}`) so a replayed handler appends the
14
+ * same logical event and the server dedupes server-side.
15
+ *
16
+ * Anti-scope: raw event replay is not exposed. Consumers must register a
17
+ * projection — see MemoryProjectionRequiredError.
18
+ */
19
+ import { IronflowError } from "@ironflow/core";
20
+ import { MemoryProjectionRequiredError } from "./errors.js";
21
+ /**
22
+ * Wait timeout for the auto-catchup after append. Stream-level minSeq is
23
+ * sufficient for read-your-writes — partition param is intentionally not
24
+ * passed. External projections (cursor at stream level only) reject
25
+ * partition with 400; managed projections accept it but it adds nothing
26
+ * for the typical single-stream agent memory pattern.
27
+ */
28
+ const DEFAULT_WAIT_TIMEOUT_MS = 5000;
29
+ /**
30
+ * Default entity type when MemoryConfig.entityType is omitted. Server-side
31
+ * the field is informational; "agent" matches the typical streamId convention
32
+ * and avoids surprising users who don't know about it.
33
+ */
34
+ const DEFAULT_ENTITY_TYPE = "agent";
35
+ class NotImplementedError extends IronflowError {
36
+ constructor(method) {
37
+ super(`memory.${method}() is not yet implemented — entityStream lands when a concrete cross-agent peer-memory use case surfaces`, {
38
+ code: "AGENT_MEMORY_NOT_IMPLEMENTED",
39
+ retryable: false,
40
+ details: { method },
41
+ });
42
+ this.name = "NotImplementedError";
43
+ }
44
+ }
45
+ export function createMemoryRuntimeCache() {
46
+ return { has: false, value: undefined };
47
+ }
48
+ export function createMemoryRuntimeCounters() {
49
+ return { appendCount: 0 };
50
+ }
51
+ /**
52
+ * Build a MemoryClient backed by the supplied backend.
53
+ *
54
+ * The backend is required when `config` is set. Tests pass a fake backend;
55
+ * agent.ts constructs the default backend from a runtime IronflowClient.
56
+ */
57
+ export function makeMemory(step, config, runId, cache, backend, counters = createMemoryRuntimeCounters()) {
58
+ return {
59
+ async get(options) {
60
+ assertConfigured(config);
61
+ if (!options?.bypassCache && cache.has) {
62
+ return cache.value;
63
+ }
64
+ assertBackend(backend);
65
+ const result = await step.run("memory.get", () => backend.getProjection(config.projection));
66
+ const state = result.state ?? undefined;
67
+ cache.has = true;
68
+ cache.value = state;
69
+ return state;
70
+ },
71
+ async append(eventName, data, options) {
72
+ assertConfigured(config);
73
+ assertBackend(backend);
74
+ const dataRecord = toAppendRecord(data);
75
+ const counterIndex = counters.appendCount;
76
+ counters.appendCount += 1;
77
+ const idempotencyKey = `${runId}:memory.append:${counterIndex}`;
78
+ const appended = await step.run("memory.append", () => backend.appendEvent(config.streamId, {
79
+ name: eventName,
80
+ data: dataRecord,
81
+ entityType: config.entityType ?? DEFAULT_ENTITY_TYPE,
82
+ idempotencyKey,
83
+ metadata: options?.metadata,
84
+ }));
85
+ if (appended.sequence && appended.sequence > 0) {
86
+ await step.run("memory.append.wait", () => backend.waitForCatchup(config.projection, {
87
+ minSeq: appended.sequence,
88
+ timeoutMs: DEFAULT_WAIT_TIMEOUT_MS,
89
+ }));
90
+ }
91
+ cache.has = false;
92
+ cache.value = undefined;
93
+ },
94
+ async entityStream(streamId, projectionName) {
95
+ if (!projectionName) {
96
+ throw new MemoryProjectionRequiredError(streamId);
97
+ }
98
+ throw new NotImplementedError("entityStream");
99
+ },
100
+ };
101
+ }
102
+ function assertConfigured(config) {
103
+ if (!config) {
104
+ throw new IronflowError("memory() requires AgentConfig.memory ({ streamId, projection }) to be set", { code: "AGENT_MEMORY_UNCONFIGURED", retryable: false });
105
+ }
106
+ }
107
+ function assertBackend(backend) {
108
+ if (!backend) {
109
+ throw new IronflowError("memory() requires a runtime backend — set IRONFLOW_URL (or IRONFLOW_SERVER_URL) so the agent can construct an IronflowClient, or inject a backend via test harness", { code: "AGENT_MEMORY_NO_BACKEND", retryable: false });
110
+ }
111
+ }
112
+ /**
113
+ * Convert the user's data argument into the object shape AppendEvent expects.
114
+ *
115
+ * Rejects primitives, arrays, and null with a typed error rather than
116
+ * silently wrapping them in `{ value }`. The wrap-on-write would round-trip
117
+ * cleanly only if every projection reducer knows to unwrap it; rejecting
118
+ * up-front avoids the silent shape mismatch.
119
+ */
120
+ function toAppendRecord(data) {
121
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
122
+ throw new IronflowError("memory.append() requires data to be a plain object — primitives and arrays must be wrapped explicitly so the projection reducer sees a stable shape", {
123
+ code: "AGENT_MEMORY_INVALID_DATA",
124
+ retryable: false,
125
+ details: { receivedType: data === null ? "null" : Array.isArray(data) ? "array" : typeof data },
126
+ });
127
+ }
128
+ return data;
129
+ }
130
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * spawn() — durable sub-agent invocation.
3
+ *
4
+ * Wraps step.invoke (or step.invokeAsync for fire-and-forget) so the
5
+ * sub-agent run is part of the parent's durable plan. Crash-resume
6
+ * applies: re-running the parent after a crash replays the cached
7
+ * sub-agent output without re-invoking.
8
+ *
9
+ * Parent ↔ child run linkage is recorded server-side by the existing
10
+ * invoke implementation. spawn() doesn't add new linkage state.
11
+ */
12
+ import type { StepClient } from "@ironflow/core";
13
+ import type { SpawnFn } from "./types.js";
14
+ /**
15
+ * Build a SpawnFn bound to the given step client.
16
+ *
17
+ * Exported for use by agent.ts; not part of the public API surface.
18
+ */
19
+ export declare function makeSpawn(step: StepClient): SpawnFn;
20
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/agent/spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,YAAY,CAAC;AAErE;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAkBnD"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * spawn() — durable sub-agent invocation.
3
+ *
4
+ * Wraps step.invoke (or step.invokeAsync for fire-and-forget) so the
5
+ * sub-agent run is part of the parent's durable plan. Crash-resume
6
+ * applies: re-running the parent after a crash replays the cached
7
+ * sub-agent output without re-invoking.
8
+ *
9
+ * Parent ↔ child run linkage is recorded server-side by the existing
10
+ * invoke implementation. spawn() doesn't add new linkage state.
11
+ */
12
+ /**
13
+ * Build a SpawnFn bound to the given step client.
14
+ *
15
+ * Exported for use by agent.ts; not part of the public API surface.
16
+ */
17
+ export function makeSpawn(step) {
18
+ return async function spawn(name, options) {
19
+ const stepName = `spawn.${name}`;
20
+ const shouldAwait = options.await !== false;
21
+ if (shouldAwait) {
22
+ const output = await step.invoke(options.functionId, options.input);
23
+ return { output };
24
+ }
25
+ const handle = await step.run(stepName, () => step.invokeAsync(options.functionId, options.input));
26
+ return { runId: handle.runId };
27
+ };
28
+ }
29
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/agent/spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,OAAO,KAAK,UAAU,KAAK,CACzB,IAAY,EACZ,OAA6B;QAE7B,MAAM,QAAQ,GAAG,SAAS,IAAI,EAAE,CAAC;QACjC,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC;QAE5C,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAU,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7E,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAC3C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,CACpD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * tool() — durable, validated, idempotent tool invocation.
3
+ *
4
+ * Wraps step.run with:
5
+ * - Required Zod input validation (per CEO plan, JS-only)
6
+ * - Idempotency strategy: "byCall" (default) or "byArgs"
7
+ * - Per-tool timeout (default 60s)
8
+ *
9
+ * defineTool() is a pass-through factory that preserves the input/output
10
+ * generic types for callers. The runtime call helper is constructed by
11
+ * makeTool() and bound onto AgentContext.tool.
12
+ */
13
+ import type { StepClient } from "@ironflow/core";
14
+ import type { AnyToolDefinition, ToolDefinition, ToolFn } from "./types.js";
15
+ /**
16
+ * Per-run state shared by tool() invocations.
17
+ *
18
+ * byArgs idempotency reuses a single in-flight or resolved promise per
19
+ * (tool name, args hash) pair, so concurrent or repeat calls with the
20
+ * same args fold into one durable step.
21
+ */
22
+ export interface ToolRuntime {
23
+ readonly byArgsCache: Map<string, Promise<unknown>>;
24
+ }
25
+ /**
26
+ * Define a tool. Pass-through factory that preserves the input/output
27
+ * generic types. Tools are typically registered via AgentConfig.tools so
28
+ * the agent can dispatch LLM-requested calls by name.
29
+ */
30
+ export declare function defineTool<TInput, TOutput>(spec: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
31
+ /**
32
+ * Build a ToolFn closure bound to the given step client and tool registry.
33
+ *
34
+ * Exported for use by agent.ts; not part of the public API surface.
35
+ */
36
+ export declare function makeTool(step: StepClient, registry: ReadonlyMap<string, AnyToolDefinition>, runtime: ToolRuntime): ToolFn;
37
+ /** Construct an empty ToolRuntime. Used by agent() per run. */
38
+ export declare function createToolRuntime(): ToolRuntime;
39
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/agent/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAK5E;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;CACrD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EACxC,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAChD,OAAO,EAAE,WAAW,GACnB,MAAM,CASR;AAoFD,+DAA+D;AAC/D,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * tool() — durable, validated, idempotent tool invocation.
3
+ *
4
+ * Wraps step.run with:
5
+ * - Required Zod input validation (per CEO plan, JS-only)
6
+ * - Idempotency strategy: "byCall" (default) or "byArgs"
7
+ * - Per-tool timeout (default 60s)
8
+ *
9
+ * defineTool() is a pass-through factory that preserves the input/output
10
+ * generic types for callers. The runtime call helper is constructed by
11
+ * makeTool() and bound onto AgentContext.tool.
12
+ */
13
+ import { createHash } from "node:crypto";
14
+ import { ToolNotFoundError, ToolValidationError } from "./errors.js";
15
+ import { normalizeDuration } from "./internal.js";
16
+ /** Default per-tool timeout when the definition does not specify one. */
17
+ const DEFAULT_TOOL_TIMEOUT = "60s";
18
+ /**
19
+ * Define a tool. Pass-through factory that preserves the input/output
20
+ * generic types. Tools are typically registered via AgentConfig.tools so
21
+ * the agent can dispatch LLM-requested calls by name.
22
+ */
23
+ export function defineTool(spec) {
24
+ return spec;
25
+ }
26
+ /**
27
+ * Build a ToolFn closure bound to the given step client and tool registry.
28
+ *
29
+ * Exported for use by agent.ts; not part of the public API surface.
30
+ */
31
+ export function makeTool(step, registry, runtime) {
32
+ const tool = (async (defOrName, args) => {
33
+ const def = resolveDefinition(defOrName, registry);
34
+ return invokeTool(step, def, args, runtime);
35
+ });
36
+ return tool;
37
+ }
38
+ function resolveDefinition(defOrName, registry) {
39
+ if (typeof defOrName === "string") {
40
+ const found = registry.get(defOrName);
41
+ if (!found) {
42
+ throw new ToolNotFoundError(defOrName);
43
+ }
44
+ return found;
45
+ }
46
+ return defOrName;
47
+ }
48
+ async function invokeTool(step, def, args, runtime) {
49
+ const validated = validateInput(def, args);
50
+ const timeout = normalizeDuration(def.timeout) ?? DEFAULT_TOOL_TIMEOUT;
51
+ const idempotent = def.idempotent ?? "byCall";
52
+ if (idempotent === "byArgs") {
53
+ const hash = hashArgs(validated);
54
+ const cacheKey = `${def.name}:${hash}`;
55
+ const cached = runtime.byArgsCache.get(cacheKey);
56
+ if (cached) {
57
+ return cached;
58
+ }
59
+ const promise = step.run(`tool.${def.name}.${hash}`, () => def.handler(validated), { timeout });
60
+ runtime.byArgsCache.set(cacheKey, promise);
61
+ return promise;
62
+ }
63
+ return step.run(`tool.${def.name}`, () => def.handler(validated), { timeout });
64
+ }
65
+ function validateInput(def, args) {
66
+ const result = def.input.safeParse(args);
67
+ if (!result.success) {
68
+ throw new ToolValidationError(def.name, result.error.issues);
69
+ }
70
+ return result.data;
71
+ }
72
+ function hashArgs(args) {
73
+ const serialized = stableStringify(args);
74
+ return createHash("sha256").update(serialized).digest("hex").slice(0, 16);
75
+ }
76
+ /**
77
+ * Stable-key JSON stringify so { a: 1, b: 2 } and { b: 2, a: 1 } hash to
78
+ * the same value. Required for byArgs idempotency to behave intuitively
79
+ * across calls that build args from object spreads in different orders.
80
+ *
81
+ * Objects exposing toJSON() (Date, custom serializers) defer to the host
82
+ * JSON.stringify so different Dates do not collapse to the same hash.
83
+ */
84
+ function stableStringify(value) {
85
+ if (value === null || typeof value !== "object") {
86
+ return JSON.stringify(value);
87
+ }
88
+ if (typeof value.toJSON === "function") {
89
+ return JSON.stringify(value);
90
+ }
91
+ if (Array.isArray(value)) {
92
+ return `[${value.map(stableStringify).join(",")}]`;
93
+ }
94
+ const entries = Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
95
+ return `{${entries
96
+ .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)
97
+ .join(",")}}`;
98
+ }
99
+ /** Construct an empty ToolRuntime. Used by agent() per run. */
100
+ export function createToolRuntime() {
101
+ return { byArgsCache: new Map() };
102
+ }
103
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/agent/tool.ts"],"names":[],"mappings":"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"}