@lunora/agent 0.0.0 → 1.0.0-alpha.10

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 (53) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +38 -31
  3. package/dist/channels.d.mts +74 -0
  4. package/dist/channels.d.ts +74 -0
  5. package/dist/channels.mjs +181 -0
  6. package/dist/component.d.mts +104 -0
  7. package/dist/component.d.ts +104 -0
  8. package/dist/component.mjs +418 -0
  9. package/dist/inbound.d.mts +34 -0
  10. package/dist/inbound.d.ts +34 -0
  11. package/dist/inbound.mjs +32 -0
  12. package/dist/index.d.mts +832 -0
  13. package/dist/index.d.ts +832 -0
  14. package/dist/index.mjs +18 -0
  15. package/dist/naming.d.mts +30 -0
  16. package/dist/naming.d.ts +30 -0
  17. package/dist/naming.mjs +8 -0
  18. package/dist/packem_shared/AGENT_MODULE-Dnt_-AAT.mjs +24 -0
  19. package/dist/packem_shared/VoiceSessionDO-BdwlLaXC.mjs +297 -0
  20. package/dist/packem_shared/adaptMcpResult-wtNMvLoP.mjs +65 -0
  21. package/dist/packem_shared/agentAsTool-CUHlWsmt.mjs +98 -0
  22. package/dist/packem_shared/base64-BVwtgRJV.mjs +18 -0
  23. package/dist/packem_shared/braintrustTelemetry-TP7Kwuuj.mjs +47 -0
  24. package/dist/packem_shared/buildModelMessages-BWFigaoo.mjs +69 -0
  25. package/dist/packem_shared/codeTool-CjgJOC9t.mjs +122 -0
  26. package/dist/packem_shared/collectAgenticMemoryTools-QrzpV-WX.mjs +97 -0
  27. package/dist/packem_shared/combineTelemetry-DCyaaWAI.mjs +43 -0
  28. package/dist/packem_shared/common-DQXayow6.mjs +89 -0
  29. package/dist/packem_shared/compileAgentWorkflow-DYFFyx7i.mjs +78 -0
  30. package/dist/packem_shared/consoleTelemetry--3sWfu1R.mjs +93 -0
  31. package/dist/packem_shared/createAgentContext-4xJGXNR4.mjs +50 -0
  32. package/dist/packem_shared/createAgentGenerate-DO7Z96zX.mjs +192 -0
  33. package/dist/packem_shared/createDispatchRunner-DSbp_dph-ZHTtxy3f.mjs +69 -0
  34. package/dist/packem_shared/defineAgent-DAwAZC9P.mjs +148 -0
  35. package/dist/packem_shared/defineSkill-Ctf_S-rz.mjs +22 -0
  36. package/dist/packem_shared/functionTool-D6lCa2jB.mjs +20 -0
  37. package/dist/packem_shared/graph-component-Bbaxxymp.mjs +217 -0
  38. package/dist/packem_shared/memory-D4FPcBsX.mjs +12 -0
  39. package/dist/packem_shared/normalizeEntityName-BouctxLC.mjs +3 -0
  40. package/dist/packem_shared/otlpTelemetry-DU5ZmoEV.mjs +177 -0
  41. package/dist/packem_shared/runAgentLoop-M8PKbtWT.mjs +493 -0
  42. package/dist/packem_shared/runVoiceTurn-LnqLvCRR.mjs +211 -0
  43. package/dist/packem_shared/sandboxComponent-DR3pTwBL.mjs +194 -0
  44. package/dist/packem_shared/sentryTelemetry-A4F5ndh9.mjs +36 -0
  45. package/dist/packem_shared/types.d-boAM2Yi1.d.mts +1114 -0
  46. package/dist/packem_shared/types.d-boAM2Yi1.d.ts +1114 -0
  47. package/dist/sandbox.d.mts +204 -0
  48. package/dist/sandbox.d.ts +204 -0
  49. package/dist/sandbox.mjs +113 -0
  50. package/dist/telemetry/index.d.mts +254 -0
  51. package/dist/telemetry/index.d.ts +254 -0
  52. package/dist/telemetry/index.mjs +5 -0
  53. package/package.json +88 -7
@@ -0,0 +1,3 @@
1
+ import '@lunora/server';
2
+ import '@lunora/values';
3
+ export { b as graphComponent, g as graphTables, n as normalizeEntityName } from './graph-component-Bbaxxymp.mjs';
@@ -0,0 +1,177 @@
1
+ import { s as summarizeUsage, r as readField, e as summarizeGatewayTelemetry, c as contentText, t as toolInputOf, a as toolNameOf } from './common-DQXayow6.mjs';
2
+
3
+ const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
4
+ const otlpRandomHex = (bytes) => {
5
+ const buffer = new Uint8Array(bytes);
6
+ crypto.getRandomValues(buffer);
7
+ let hex = "";
8
+ for (const byte of buffer) {
9
+ hex += byte.toString(16).padStart(2, "0");
10
+ }
11
+ return hex;
12
+ };
13
+ const encodeAttribute = (key, value) => {
14
+ if (typeof value === "boolean") {
15
+ return { key, value: { boolValue: value } };
16
+ }
17
+ if (typeof value === "number") {
18
+ if (!Number.isFinite(value)) {
19
+ return { key, value: { stringValue: String(value) } };
20
+ }
21
+ return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
22
+ }
23
+ return { key, value: { stringValue: value } };
24
+ };
25
+ const mergeHeaders = (defaults, overrides, token) => {
26
+ const merged = {};
27
+ const seen = /* @__PURE__ */ new Map();
28
+ const put = (name, value) => {
29
+ const lower = name.toLowerCase();
30
+ const existing = seen.get(lower);
31
+ if (existing === void 0) {
32
+ seen.set(lower, name);
33
+ merged[name] = value;
34
+ } else {
35
+ merged[existing] = value;
36
+ }
37
+ };
38
+ for (const [name, value] of Object.entries(defaults)) {
39
+ put(name, value);
40
+ }
41
+ for (const [name, value] of Object.entries(overrides ?? {})) {
42
+ put(name, value);
43
+ }
44
+ if (token !== void 0 && token.length > 0) {
45
+ put("authorization", `Bearer ${token}`);
46
+ }
47
+ return merged;
48
+ };
49
+ const buildResourceAttributes = (serviceName, extra) => {
50
+ const merged = { "service.name": serviceName };
51
+ for (const [key, value] of Object.entries({})) {
52
+ merged[key] = value;
53
+ }
54
+ return Object.entries(merged).map(([key, value]) => encodeAttribute(key, value));
55
+ };
56
+ const wrapResourceSpans = (span, scopeName, serviceName, resourceAttributes) => {
57
+ return {
58
+ resourceSpans: [
59
+ {
60
+ resource: { attributes: buildResourceAttributes(serviceName) },
61
+ scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
62
+ }
63
+ ]
64
+ };
65
+ };
66
+
67
+ const pushAttribute = (attributes, key, value) => {
68
+ if (value === void 0 || value === null) {
69
+ return;
70
+ }
71
+ if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
72
+ attributes.push(encodeAttribute(key, value));
73
+ return;
74
+ }
75
+ attributes.push(encodeAttribute(key, JSON.stringify(value)));
76
+ };
77
+ const observeSettled = (promise, onSettle) => {
78
+ const run = async () => {
79
+ let result;
80
+ try {
81
+ result = await promise;
82
+ } catch (error) {
83
+ onSettle(false, error instanceof Error ? error.message : String(error), void 0);
84
+ return;
85
+ }
86
+ onSettle(true, void 0, result);
87
+ };
88
+ run().catch(() => void 0);
89
+ };
90
+ const otlpTelemetry = (options) => {
91
+ const { conversationId, endpoint, headers, recordInputs = false, recordOutputs = false, token, traceId: fixedTraceId, waitUntil } = options;
92
+ const serviceName = options.serviceName ?? "lunora";
93
+ let base = endpoint;
94
+ while (base.endsWith("/")) {
95
+ base = base.slice(0, -1);
96
+ }
97
+ const tracesUrl = `${base}/v1/traces`;
98
+ const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers, token);
99
+ const emitSpan = (name, startTs, ok, message, attributes) => {
100
+ const span = {
101
+ attributes,
102
+ endTimeUnixNano: otlpUnixNano(Date.now()),
103
+ // SPAN_KIND_INTERNAL — matches the runtime's `ctx.trace` spans.
104
+ kind: 1,
105
+ name,
106
+ spanId: otlpRandomHex(8),
107
+ startTimeUnixNano: otlpUnixNano(startTs),
108
+ // STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
109
+ status: ok ? { code: 1 } : { code: 2, message: message ?? "" },
110
+ traceId: fixedTraceId ?? otlpRandomHex(16)
111
+ };
112
+ try {
113
+ const sent = fetch(tracesUrl, {
114
+ body: JSON.stringify(wrapResourceSpans(span, "@lunora/agent", serviceName)),
115
+ headers: mergedHeaders,
116
+ method: "POST"
117
+ }).catch(() => {
118
+ });
119
+ waitUntil?.(sent);
120
+ } catch {
121
+ }
122
+ };
123
+ return {
124
+ executeLanguageModelCall: (options_) => {
125
+ const startTs = Date.now();
126
+ const modelId = readField(options_, "modelId");
127
+ const promise = options_.execute();
128
+ const emit = (ok, message, result) => {
129
+ const attributes = [];
130
+ pushAttribute(attributes, "gen_ai.operation.name", "chat");
131
+ pushAttribute(attributes, "gen_ai.request.model", modelId);
132
+ pushAttribute(attributes, "gen_ai.system", readField(options_, "provider"));
133
+ pushAttribute(attributes, "gen_ai.conversation.id", conversationId);
134
+ const usage = summarizeUsage(readField(result, "usage"));
135
+ if (usage) {
136
+ pushAttribute(attributes, "gen_ai.usage.input_tokens", usage.inputTokens);
137
+ pushAttribute(attributes, "gen_ai.usage.output_tokens", usage.outputTokens);
138
+ pushAttribute(attributes, "gen_ai.usage.total_tokens", usage.totalTokens);
139
+ }
140
+ const gateway = summarizeGatewayTelemetry(result);
141
+ if (gateway) {
142
+ pushAttribute(attributes, "gen_ai.usage.cost", gateway.cost);
143
+ pushAttribute(attributes, "gen_ai.response.cached", gateway.cached);
144
+ pushAttribute(attributes, "cf.aig.log_id", gateway.logId);
145
+ }
146
+ if (recordInputs) {
147
+ pushAttribute(attributes, "gen_ai.prompt", readField(options_, "messages"));
148
+ }
149
+ if (recordOutputs) {
150
+ pushAttribute(attributes, "gen_ai.completion", contentText(readField(result, "content")));
151
+ }
152
+ emitSpan(typeof modelId === "string" ? `chat ${modelId}` : "language_model_call", startTs, ok, message, attributes);
153
+ };
154
+ observeSettled(promise, emit);
155
+ return promise;
156
+ },
157
+ executeTool: (options_) => {
158
+ const startTs = Date.now();
159
+ const toolName = toolNameOf(options_);
160
+ const promise = options_.execute();
161
+ const emit = (ok, message) => {
162
+ const attributes = [];
163
+ pushAttribute(attributes, "gen_ai.operation.name", "execute_tool");
164
+ pushAttribute(attributes, "gen_ai.tool.name", toolName);
165
+ pushAttribute(attributes, "gen_ai.tool.call.id", readField(options_, "toolCallId"));
166
+ if (recordInputs) {
167
+ pushAttribute(attributes, "gen_ai.tool.input", toolInputOf(options_));
168
+ }
169
+ emitSpan(typeof toolName === "string" ? `execute_tool ${toolName}` : "execute_tool", startTs, ok, message, attributes);
170
+ };
171
+ observeSettled(promise, emit);
172
+ return promise;
173
+ }
174
+ };
175
+ };
176
+
177
+ export { otlpTelemetry };
@@ -0,0 +1,493 @@
1
+ import { resolveAgentModel } from './createAgentGenerate-DO7Z96zX.mjs';
2
+ import { f as firstGraphSource, m as memoryStepName, a as firstEpisodicSource, r as resolveInjectedSources } from './memory-D4FPcBsX.mjs';
3
+ import { buildModelMessages } from './buildModelMessages-BWFigaoo.mjs';
4
+ import { agentBindingName } from '../naming.mjs';
5
+ import { toFunctionReference } from './AGENT_MODULE-Dnt_-AAT.mjs';
6
+
7
+ const DEFAULT_MAX_TURNS = 8;
8
+ const stringifyOutput = (output) => output === void 0 ? "null" : JSON.stringify(output);
9
+ const normalizeStopWhen = (stopWhen) => {
10
+ if (stopWhen === void 0) {
11
+ return [];
12
+ }
13
+ if (Array.isArray(stopWhen)) {
14
+ return stopWhen;
15
+ }
16
+ return [stopWhen];
17
+ };
18
+ const isStopConditionMet = async (conditions, steps) => {
19
+ const results = await Promise.all(conditions.map(async (condition) => condition({ steps })));
20
+ return results.some(Boolean);
21
+ };
22
+ const sumTokens = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
23
+ const addUsage = (base, next) => {
24
+ if (next === void 0) {
25
+ return base;
26
+ }
27
+ if (base === void 0) {
28
+ return { ...next };
29
+ }
30
+ const result = {};
31
+ const inputTokens = sumTokens(base.inputTokens, next.inputTokens);
32
+ const outputTokens = sumTokens(base.outputTokens, next.outputTokens);
33
+ const totalTokens = sumTokens(base.totalTokens, next.totalTokens);
34
+ if (inputTokens !== void 0) {
35
+ result.inputTokens = inputTokens;
36
+ }
37
+ if (outputTokens !== void 0) {
38
+ result.outputTokens = outputTokens;
39
+ }
40
+ if (totalTokens !== void 0) {
41
+ result.totalTokens = totalTokens;
42
+ }
43
+ return result;
44
+ };
45
+ const resolveNeedsApproval = async (tool, input, context) => {
46
+ const { needsApproval } = tool;
47
+ if (needsApproval === void 0 || needsApproval === false) {
48
+ return false;
49
+ }
50
+ if (needsApproval === true) {
51
+ return true;
52
+ }
53
+ return needsApproval(input, context);
54
+ };
55
+ const awaitApproval = async (turnContext, call) => {
56
+ const { instanceId, patchThread, persist, step } = turnContext;
57
+ await persist({
58
+ content: `Awaiting approval to run tool "${call.name}".`,
59
+ messageKey: `${instanceId}:approval:${call.id}`,
60
+ role: "tool",
61
+ status: "awaiting_approval",
62
+ toolCallId: call.id,
63
+ toolName: call.name
64
+ });
65
+ await patchThread({ status: "awaiting_input" });
66
+ const event = await step.waitForEvent(`approval:${call.id}`, { type: `agent-approval:${call.id}` });
67
+ await patchThread({ status: "running" });
68
+ return { decision: event.payload.decision, ...event.payload.note === void 0 ? {} : { note: event.payload.note } };
69
+ };
70
+ const runToolCall = async (turnContext, call) => {
71
+ const { env, getState, instanceId, onTokenDelta, persist, run, setState, step, threadKey, tools } = turnContext;
72
+ const stepName = `tool:${call.name}:${call.id}`;
73
+ const tool = tools[call.name];
74
+ const messageKey = `${instanceId}:tool:${call.id}`;
75
+ if (!tool) {
76
+ await persist({ content: `Error: unknown tool "${call.name}"`, messageKey, role: "tool", stepName, toolCallId: call.id, toolName: call.name });
77
+ return;
78
+ }
79
+ const reportProgress = (data) => {
80
+ onTokenDelta?.({ data, kind: "progress", threadKey, toolCallId: call.id });
81
+ };
82
+ const toolContext = { env, getState, idempotencyKey: stepName, reportProgress, run, setState, threadKey, toolCallId: call.id };
83
+ let status;
84
+ if (await resolveNeedsApproval(tool, call.input, toolContext)) {
85
+ const { decision, note } = await awaitApproval(turnContext, call);
86
+ if (decision === "reject") {
87
+ const reason = note === void 0 ? "" : ` Reason: ${note}`;
88
+ await persist({
89
+ content: `Tool "${call.name}" was rejected by the user and not run.${reason}`,
90
+ messageKey,
91
+ role: "tool",
92
+ status: "rejected",
93
+ stepName,
94
+ toolCallId: call.id,
95
+ toolName: call.name
96
+ });
97
+ return;
98
+ }
99
+ status = "approved";
100
+ }
101
+ const output = await step.do(stepName, () => Promise.resolve(tool.execute(call.input, toolContext)));
102
+ await persist({
103
+ content: typeof output === "string" ? output : stringifyOutput(output),
104
+ messageKey,
105
+ role: "tool",
106
+ stepName,
107
+ ...status === void 0 ? {} : { status },
108
+ toolCallId: call.id,
109
+ toolName: call.name
110
+ });
111
+ };
112
+ const applyPrepareStepResult = (base, overrides, env) => {
113
+ if (!overrides) {
114
+ return base;
115
+ }
116
+ let { messages } = base;
117
+ if (overrides.messages !== void 0) {
118
+ messages = overrides.messages;
119
+ }
120
+ if (overrides.system !== void 0) {
121
+ messages = [{ content: overrides.system, role: "system" }, ...messages];
122
+ }
123
+ return {
124
+ activeTools: overrides.activeTools ?? base.activeTools,
125
+ messages,
126
+ model: overrides.model === void 0 ? base.model : resolveAgentModel(overrides.model, env),
127
+ toolChoice: overrides.toolChoice ?? base.toolChoice
128
+ };
129
+ };
130
+ const splitForCompaction = (history, compaction) => {
131
+ if (compaction === void 0 || history.length <= compaction.maxMessages) {
132
+ return void 0;
133
+ }
134
+ const keepRecent = Math.max(1, compaction.keepRecent ?? Math.ceil(compaction.maxMessages / 2));
135
+ if (history.length <= keepRecent) {
136
+ return void 0;
137
+ }
138
+ let cut = history.length - keepRecent;
139
+ while (cut > 0 && history[cut]?.role === "tool") {
140
+ cut -= 1;
141
+ }
142
+ if (cut <= 0) {
143
+ return void 0;
144
+ }
145
+ return { older: history.slice(0, cut), recent: history.slice(cut) };
146
+ };
147
+ const compactHistory = async (turnContext, history) => {
148
+ const { agent, compact, env } = turnContext;
149
+ const split = splitForCompaction(history, agent.compaction);
150
+ if (split === void 0 || compact === void 0) {
151
+ return { history, summary: void 0 };
152
+ }
153
+ try {
154
+ const summary = await compact({ env, messages: buildModelMessages({ history: split.older }), model: agent.compaction?.model ?? agent.model });
155
+ return summary.length > 0 ? { history: split.recent, summary } : { history, summary: void 0 };
156
+ } catch {
157
+ return { history, summary: void 0 };
158
+ }
159
+ };
160
+ const generateTurn = async (turnContext, turn, steps) => {
161
+ const { agent, env, generate, instructions, listMessages, memoryContext, onTokenDelta, run, step, streamGenerate, threadKey } = turnContext;
162
+ return step.do(`llm:turn:${String(turn)}`, async () => {
163
+ const rawHistory = await run(listMessages, { key: threadKey });
164
+ const { history, summary } = await compactHistory(turnContext, rawHistory);
165
+ let prepared = {
166
+ activeTools: agent.activeTools,
167
+ messages: buildModelMessages({ history, instructions, memoryContext, summary }),
168
+ model: void 0,
169
+ toolChoice: agent.toolChoice
170
+ };
171
+ if (agent.prepareStep) {
172
+ prepared = applyPrepareStepResult(prepared, await agent.prepareStep({ messages: prepared.messages, stepNumber: turn, steps }), env);
173
+ }
174
+ const request = {
175
+ messages: prepared.messages,
176
+ ...prepared.activeTools === void 0 ? {} : { activeTools: prepared.activeTools },
177
+ ...prepared.model === void 0 ? {} : { model: prepared.model },
178
+ ...prepared.toolChoice === void 0 ? {} : { toolChoice: prepared.toolChoice }
179
+ };
180
+ if (streamGenerate && onTokenDelta) {
181
+ return streamGenerate(request, (text) => {
182
+ onTokenDelta({ text, threadKey, turn });
183
+ });
184
+ }
185
+ return generate(request);
186
+ });
187
+ };
188
+ const notifyStepFinish = async (turnContext, info) => {
189
+ const { agent, step } = turnContext;
190
+ if (!agent.onStepFinish) {
191
+ return;
192
+ }
193
+ await step.do(`agent:step-finish:${String(info.turn)}`, async () => {
194
+ await agent.onStepFinish?.(info);
195
+ return true;
196
+ });
197
+ };
198
+ const toStepInfo = (decision) => {
199
+ return {
200
+ text: decision.text,
201
+ toolCalls: decision.toolCalls.map((call) => {
202
+ return { input: call.input, toolCallId: call.id, toolName: call.name };
203
+ }),
204
+ ...decision.usage === void 0 ? {} : { usage: decision.usage }
205
+ };
206
+ };
207
+ const readRetrievedContext = (retrieved) => {
208
+ const context = retrieved?.context;
209
+ return typeof context === "string" && context.length > 0 ? context : void 0;
210
+ };
211
+ const graphTraverseBounds = (graph) => {
212
+ const bounds = {};
213
+ if (graph?.depth !== void 0) {
214
+ bounds.depth = graph.depth;
215
+ }
216
+ if (graph?.fanOut !== void 0) {
217
+ bounds.fanOut = graph.fanOut;
218
+ }
219
+ if (graph?.maxNodes !== void 0) {
220
+ bounds.maxNodes = graph.maxNodes;
221
+ }
222
+ if (graph?.maxSeeds !== void 0) {
223
+ bounds.maxSeeds = graph.maxSeeds;
224
+ }
225
+ return bounds;
226
+ };
227
+ const dispatchSemanticMemory = async (source, input, step, run) => {
228
+ if (source.source === void 0) {
229
+ return void 0;
230
+ }
231
+ const memorySource = toFunctionReference(source.source);
232
+ const { topK } = source;
233
+ const stepName = memoryStepName("memory:retrieve", source.key);
234
+ const retrieved = await step.do(stepName, async () => run(memorySource, { query: input, ...topK === void 0 ? {} : { topK } }));
235
+ return readRetrievedContext(retrieved);
236
+ };
237
+ const dispatchGraphMemory = async (source, input, owner, graphTraverse, step, run) => {
238
+ if (owner === void 0) {
239
+ return void 0;
240
+ }
241
+ const stepName = memoryStepName("memory:traverse", source.key);
242
+ const retrieved = await step.do(stepName, async () => run(graphTraverse, { owner, query: input, ...graphTraverseBounds(source.graph) }));
243
+ return readRetrievedContext(retrieved);
244
+ };
245
+ const dispatchEpisodicMemory = async (source, owner, episodeRecall, step, run) => {
246
+ if (owner === void 0) {
247
+ return void 0;
248
+ }
249
+ const stepName = memoryStepName("memory:recall", source.key);
250
+ const recallArgs = { owner, ...source.episodic?.recall === void 0 ? {} : { limit: source.episodic.recall } };
251
+ const retrieved = await step.do(stepName, async () => run(episodeRecall, recallArgs));
252
+ return readRetrievedContext(retrieved);
253
+ };
254
+ const dispatchInjectedSource = async (source, deps) => {
255
+ if (source.kind === "episodic") {
256
+ return dispatchEpisodicMemory(source, deps.owner, deps.episodeRecall, deps.step, deps.run);
257
+ }
258
+ if (source.kind === "graph") {
259
+ return dispatchGraphMemory(source, deps.input, deps.owner, deps.graphTraverse, deps.step, deps.run);
260
+ }
261
+ return dispatchSemanticMemory(source, deps.input, deps.step, deps.run);
262
+ };
263
+ const dedupeEpisodicSources = (sources) => {
264
+ let seenEpisodic = false;
265
+ return sources.filter((source) => {
266
+ if (source.kind !== "episodic") {
267
+ return true;
268
+ }
269
+ if (seenEpisodic) {
270
+ return false;
271
+ }
272
+ seenEpisodic = true;
273
+ return true;
274
+ });
275
+ };
276
+ const retrieveMemoryContext = async (agent, input, owner, paths, step, run) => {
277
+ const sources = dedupeEpisodicSources(resolveInjectedSources(agent));
278
+ if (sources.length === 0) {
279
+ return void 0;
280
+ }
281
+ const graphTraverse = toFunctionReference(paths.graphTraverse);
282
+ const episodeRecall = toFunctionReference(paths.episodeRecall);
283
+ const contexts = [];
284
+ for (const source of sources) {
285
+ const context = await dispatchInjectedSource(source, { episodeRecall, graphTraverse, input, owner, run, step });
286
+ if (context !== void 0) {
287
+ contexts.push(context);
288
+ }
289
+ }
290
+ return contexts.length > 0 ? contexts.join("\n\n") : void 0;
291
+ };
292
+ const extractGraphMemoryAtRunEnd = async (options) => {
293
+ const { agent, env, extractGraph, finalText, input, instanceId, owner, paths, run, step } = options;
294
+ if (extractGraph === void 0 || owner === void 0 || finalText === void 0 || finalText.length === 0) {
295
+ return;
296
+ }
297
+ const source = firstGraphSource(agent);
298
+ if (source === void 0) {
299
+ return;
300
+ }
301
+ try {
302
+ const graphInput = { assistantText: finalText, env, model: source.graph?.extractionModel ?? agent.model, userInput: input };
303
+ const extracted = await step.do(memoryStepName("memory:extract", source.key), async () => extractGraph(graphInput));
304
+ await run(toFunctionReference(paths.graphUpsert), { ...extracted, messageKey: `${instanceId}:${memoryStepName("extract", source.key)}`, owner });
305
+ } catch {
306
+ }
307
+ };
308
+ const extractEpisodeAtRunEnd = async (options) => {
309
+ const { agent, env, extractEpisode, finalText, input, instanceId, owner, paths, run, step, threadKey } = options;
310
+ if (extractEpisode === void 0 || owner === void 0 || finalText === void 0 || finalText.length === 0) {
311
+ return;
312
+ }
313
+ const source = firstEpisodicSource(agent);
314
+ if (source === void 0) {
315
+ return;
316
+ }
317
+ try {
318
+ const episodeInput = { assistantText: finalText, env, model: source.episodic?.extractionModel ?? agent.model, userInput: input };
319
+ const { summary } = await step.do(memoryStepName("memory:episode", source.key), async () => extractEpisode(episodeInput));
320
+ await run(toFunctionReference(paths.episodeUpsert), {
321
+ messageKey: `${instanceId}:${memoryStepName("episode", source.key)}`,
322
+ owner,
323
+ summary,
324
+ threadKey
325
+ });
326
+ } catch {
327
+ }
328
+ };
329
+ const terminatePriorInstance = async (env, exportName, priorInstanceId) => {
330
+ const binding = env[agentBindingName(exportName)];
331
+ if (!binding || typeof binding.get !== "function") {
332
+ return;
333
+ }
334
+ try {
335
+ const instance = await binding.get(priorInstanceId);
336
+ await instance.terminate();
337
+ } catch {
338
+ }
339
+ };
340
+ const handleTurn = async (turnContext, turn, decision, steps, stopConditions) => {
341
+ const { instanceId, persist } = turnContext;
342
+ const stepUsage = decision.usage === void 0 ? {} : { usage: decision.usage };
343
+ if (decision.toolCalls.length === 0) {
344
+ const content = decision.output !== void 0 && decision.text.length === 0 ? stringifyOutput(decision.output) : decision.text;
345
+ await persist({ content, messageKey: `${instanceId}:assistant:${String(turn)}`, role: "assistant" });
346
+ await notifyStepFinish(turnContext, { text: decision.text, toolCalls: [], turn, ...stepUsage });
347
+ return {
348
+ final: {
349
+ stopped: "final",
350
+ text: decision.text,
351
+ turns: turn + 1,
352
+ ...decision.output === void 0 ? {} : { output: decision.output }
353
+ }
354
+ };
355
+ }
356
+ await persist({ content: decision.text, messageKey: `${instanceId}:assistant:${String(turn)}`, role: "assistant", toolCalls: decision.toolCalls });
357
+ for (const call of decision.toolCalls) {
358
+ await runToolCall(turnContext, call);
359
+ }
360
+ await notifyStepFinish(turnContext, { text: decision.text, toolCalls: decision.toolCalls, turn, ...stepUsage });
361
+ steps.push(toStepInfo(decision));
362
+ if (stopConditions.length > 0 && await isStopConditionMet(stopConditions, steps)) {
363
+ return "stopCondition";
364
+ }
365
+ return void 0;
366
+ };
367
+ const runTurns = async (turnContext, maxTurns, stopConditions, usageBox) => {
368
+ const steps = [];
369
+ let final;
370
+ let stoppedByCondition = false;
371
+ let turnsRun = 0;
372
+ for (let turn = 0; turn < maxTurns; turn += 1) {
373
+ const decision = await generateTurn(turnContext, turn, steps);
374
+ usageBox.value = addUsage(usageBox.value, decision.usage);
375
+ turnsRun = turn + 1;
376
+ const outcome = await handleTurn(turnContext, turn, decision, steps, stopConditions);
377
+ if (outcome === "stopCondition") {
378
+ stoppedByCondition = true;
379
+ break;
380
+ }
381
+ if (outcome !== void 0) {
382
+ final = outcome.final;
383
+ break;
384
+ }
385
+ }
386
+ return { final, stoppedByCondition, turnsRun };
387
+ };
388
+ const runAgentLoop = async (options) => {
389
+ const { agent, compact, env, exportName, extractEpisode, extractGraph, generate, instanceId, onTokenDelta, params, paths, run, step, streamGenerate } = options;
390
+ const maxTurns = agent.maxTurns ?? DEFAULT_MAX_TURNS;
391
+ const stopConditions = normalizeStopWhen(agent.stopWhen);
392
+ const appendMessage = toFunctionReference(paths.appendMessage);
393
+ const ensureThread = toFunctionReference(paths.ensureThread);
394
+ const listMessages = toFunctionReference(paths.listMessages);
395
+ const patchThread = toFunctionReference(paths.patchThread);
396
+ const setStateRef = toFunctionReference(paths.setState);
397
+ const stateRef = toFunctionReference(paths.state);
398
+ const persist = async (message) => {
399
+ await run(appendMessage, { threadKey: params.threadKey, ...message });
400
+ };
401
+ const patchThreadByKey = async (patch) => {
402
+ await run(patchThread, { key: params.threadKey, ...patch });
403
+ };
404
+ const getState = async () => await run(stateRef, { key: params.threadKey });
405
+ const setState = async (state) => {
406
+ await run(setStateRef, { key: params.threadKey, state });
407
+ };
408
+ const bootstrap = await run(ensureThread, {
409
+ agent: exportName,
410
+ instanceId,
411
+ key: params.threadKey,
412
+ ...agent.initialState === void 0 ? {} : { initialState: agent.initialState },
413
+ ...agent.onConcurrentRun === void 0 ? {} : { onConcurrentRun: agent.onConcurrentRun },
414
+ ...params.owner === void 0 ? {} : { owner: params.owner },
415
+ ...params.title === void 0 ? {} : { title: params.title }
416
+ });
417
+ if (bootstrap?.replaced && bootstrap.priorInstanceId !== void 0) {
418
+ await terminatePriorInstance(env, exportName, bootstrap.priorInstanceId);
419
+ }
420
+ await persist({ content: params.input, messageKey: `${instanceId}:user`, role: "user" });
421
+ const memoryContext = await retrieveMemoryContext(agent, params.input, params.owner, paths, step, run);
422
+ const instructions = typeof agent.instructions === "function" ? agent.instructions({ env, input: params.input, threadKey: params.threadKey }) : agent.instructions;
423
+ const turnContext = {
424
+ agent,
425
+ compact,
426
+ env,
427
+ generate,
428
+ getState,
429
+ instanceId,
430
+ instructions,
431
+ listMessages,
432
+ memoryContext,
433
+ onTokenDelta,
434
+ patchThread: patchThreadByKey,
435
+ persist,
436
+ run,
437
+ setState,
438
+ step,
439
+ streamGenerate,
440
+ threadKey: params.threadKey,
441
+ tools: agent.tools ?? {}
442
+ };
443
+ const usageBox = { value: void 0 };
444
+ try {
445
+ const { final, stoppedByCondition, turnsRun } = await runTurns(turnContext, maxTurns, stopConditions, usageBox);
446
+ const usagePatch = usageBox.value === void 0 ? {} : { usage: usageBox.value };
447
+ await extractGraphMemoryAtRunEnd({
448
+ agent,
449
+ env,
450
+ extractGraph,
451
+ finalText: final?.text,
452
+ input: params.input,
453
+ instanceId,
454
+ owner: params.owner,
455
+ paths,
456
+ run,
457
+ step
458
+ });
459
+ await extractEpisodeAtRunEnd({
460
+ agent,
461
+ env,
462
+ extractEpisode,
463
+ finalText: final?.text,
464
+ input: params.input,
465
+ instanceId,
466
+ owner: params.owner,
467
+ paths,
468
+ run,
469
+ step,
470
+ threadKey: params.threadKey
471
+ });
472
+ if (final) {
473
+ await run(patchThread, { key: params.threadKey, status: "idle", ...usagePatch });
474
+ return { ...final, ...usagePatch };
475
+ }
476
+ if (stoppedByCondition) {
477
+ await run(patchThread, { key: params.threadKey, status: "idle", ...usagePatch });
478
+ return { stopped: "stopCondition", turns: turnsRun, ...usagePatch };
479
+ }
480
+ await run(patchThread, { error: `agent stopped: maxTurns (${String(maxTurns)}) reached`, key: params.threadKey, status: "error", ...usagePatch });
481
+ return { stopped: "maxTurns", turns: maxTurns, ...usagePatch };
482
+ } catch (error) {
483
+ await run(patchThread, {
484
+ error: error instanceof Error ? error.message : String(error),
485
+ key: params.threadKey,
486
+ status: "error",
487
+ ...usageBox.value === void 0 ? {} : { usage: usageBox.value }
488
+ });
489
+ throw error;
490
+ }
491
+ };
492
+
493
+ export { runAgentLoop, splitForCompaction };