@cognipeer/agent-sdk 0.1.1 → 0.2.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.
package/dist/index.js DELETED
@@ -1,1212 +0,0 @@
1
- // src/model.ts
2
- function isSmartModel(m) {
3
- return !!m && typeof m === "object" && typeof m.invoke === "function";
4
- }
5
- function withTools(model, tools) {
6
- if (model?.bindTools) return model.bindTools(tools);
7
- return model;
8
- }
9
- function fromLangchainModel(lcModel) {
10
- if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
11
- const adapted = {
12
- invoke: async (messages) => {
13
- const normalizeContent = (content) => {
14
- if (content == null) return content;
15
- if (typeof content === "string") return content;
16
- if (Array.isArray(content)) {
17
- return content.map((part) => {
18
- if (!part || typeof part !== "object") return part;
19
- if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
20
- if (part.type === "image_url") {
21
- const img = part.image_url;
22
- if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
23
- if (img && typeof img === "object") {
24
- if ("url" in img) return { type: "image_url", image_url: img };
25
- if ("base64" in img) {
26
- const media = img.media_type || "image/jpeg";
27
- const dataUrl = `data:${media};base64,${img.base64}`;
28
- const detail = img.detail;
29
- return { type: "image_url", image_url: { url: dataUrl, detail } };
30
- }
31
- }
32
- return part;
33
- }
34
- return part;
35
- });
36
- }
37
- return content;
38
- };
39
- const toLC = (m) => {
40
- if (m._getType || m.lc_serializable) return m;
41
- const role = m.role;
42
- return { role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
43
- };
44
- const lcMessages = messages.map(toLC);
45
- const response = await lcModel.invoke(lcMessages);
46
- if (response && typeof response === "object") {
47
- const content = response.content ?? response.text ?? "";
48
- return {
49
- role: response.role || "assistant",
50
- content,
51
- tool_calls: response.tool_calls,
52
- usage: response.usage,
53
- response_metadata: response.response_metadata,
54
- ...response
55
- };
56
- }
57
- return { role: "assistant", content: String(response ?? "") };
58
- },
59
- bindTools: (tools) => {
60
- if (typeof lcModel.bindTools === "function") {
61
- const bound = lcModel.bindTools(tools);
62
- return fromLangchainModel(bound);
63
- }
64
- return adapted;
65
- },
66
- modelName: lcModel.modelName || lcModel._modelId || lcModel._llmType || lcModel.name,
67
- _lc: lcModel
68
- };
69
- return adapted;
70
- }
71
-
72
- // src/tool.ts
73
- import { tool } from "@langchain/core/tools";
74
- function createSmartTool({
75
- name,
76
- description,
77
- schema,
78
- func
79
- }) {
80
- return tool(async (input) => {
81
- return func(input);
82
- }, {
83
- name,
84
- description,
85
- schema
86
- });
87
- }
88
- function createTool({
89
- name,
90
- description,
91
- schema,
92
- func
93
- }) {
94
- return createSmartTool({ name, description, schema, func });
95
- }
96
-
97
- // src/prompts.ts
98
- function buildSystemPrompt(extra, planning, name = "Agent") {
99
- const extraTrimmed = extra?.trim();
100
- const agentHeader = `Agent Name: ${name}`;
101
- const planningBlock = planning ? `<planning>
102
- PLANNING IS MANDATORY.
103
-
104
- Rules:
105
- 1) Your FIRST action in every task is a single call to the tool "manage_todo_list" with operation="write" and a full, ordered plan (1+ items). Even for trivial tasks, write a one-item plan.
106
- 2) After every non-planning tool call, you MUST immediately call "manage_todo_list" with operation="write" (or re-write) to update statuses and append one-line evidence on the affected item only.
107
- 3) Keep exactly ONE item "in-progress" at any time; all others are "not-started" or "completed".
108
- 4) Never reveal the plan text. Never summarize the plan in the assistant messages.
109
- 5) If you ever produce an assistant message without having written a plan in this session, STOP and first write the plan via "manage_todo_list".
110
- </planning>` : "";
111
- return [
112
- agentHeader,
113
- "You are an advanced AI agent that is concise, accurate, and helpful.",
114
- "Follow these rules:",
115
- "- Use tools only when they add value; avoid redundant calls.",
116
- "- Never fabricate tool results; if unavailable, say so briefly.",
117
- "- Prefer short, structured answers; use bullet points when helpful.",
118
- "- Keep privacy and safety: do not reveal secrets or sensitive data.",
119
- "- If inputs are ambiguous or missing, ask one concise clarifying question.",
120
- "- Reuse prior tool results already present in the conversation when sufficient.",
121
- planningBlock,
122
- extraTrimmed ? `Extra instructions: ${extraTrimmed}` : ""
123
- ].filter(Boolean).join("\n");
124
- }
125
-
126
- // src/agent.ts
127
- import { z } from "zod";
128
-
129
- // src/nodes/resolver.ts
130
- function createResolverNode() {
131
- return async (state) => {
132
- const messages = Array.isArray(state.messages) ? state.messages : [];
133
- return {
134
- ...state,
135
- messages,
136
- summaries: state.summaries || [],
137
- toolCallCount: state.toolCallCount || 0,
138
- toolCache: state.toolCache || {},
139
- toolHistory: state.toolHistory || [],
140
- toolHistoryArchived: state.toolHistoryArchived || [],
141
- plan: state.plan || null,
142
- planVersion: state.planVersion || 0
143
- };
144
- };
145
- }
146
-
147
- // src/utils/usage.ts
148
- function normalizeUsage(raw) {
149
- if (!raw) return void 0;
150
- const prompt = num(raw.prompt_tokens, raw.input_tokens, raw.promptTokens, raw.total_prompt_tokens);
151
- const completion = num(raw.completion_tokens, raw.output_tokens, raw.completionTokens, raw.total_completion_tokens);
152
- const total = num(
153
- raw.total_tokens,
154
- raw.totalTokens,
155
- (typeof prompt === "number" ? prompt : 0) + (typeof completion === "number" ? completion : 0)
156
- );
157
- const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || {};
158
- const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || {};
159
- const normalized = {
160
- prompt_tokens: safe(prompt),
161
- completion_tokens: safe(completion),
162
- total_tokens: safe(total || (prompt || 0) + (completion || 0)),
163
- prompt_tokens_details: {
164
- cached_tokens: safe(
165
- promptDetailsSrc.cached_tokens || promptDetailsSrc.cached || raw.cached_input_tokens || raw.cached_prompt_tokens
166
- ),
167
- audio_tokens: safe(promptDetailsSrc.audio_tokens)
168
- },
169
- completion_tokens_details: {
170
- reasoning_tokens: safe(completionDetailsSrc.reasoning_tokens),
171
- audio_tokens: safe(completionDetailsSrc.audio_tokens),
172
- accepted_prediction_tokens: safe(completionDetailsSrc.accepted_prediction_tokens),
173
- rejected_prediction_tokens: safe(completionDetailsSrc.rejected_prediction_tokens)
174
- },
175
- raw
176
- };
177
- return normalized;
178
- }
179
- function num(...vals) {
180
- for (const v of vals) {
181
- if (v === 0) return 0;
182
- if (v == null) continue;
183
- const n = Number(v);
184
- if (!Number.isNaN(n)) return n;
185
- }
186
- return void 0;
187
- }
188
- function safe(v) {
189
- const n = Number(v);
190
- return Number.isFinite(n) && n >= 0 ? n : 0;
191
- }
192
-
193
- // src/utils/debugLogger.ts
194
- import fs from "fs";
195
- import path from "path";
196
- function ensureDir(p) {
197
- if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
198
- }
199
- function resolveLogsBaseDir(customPath) {
200
- const root = process.cwd();
201
- const base = customPath && customPath.trim().length > 0 ? customPath : path.join(root, "logs");
202
- ensureDir(base);
203
- return base;
204
- }
205
- function createDebugSession(opts) {
206
- const dbg = opts.debug;
207
- if (!dbg?.enabled) return void 0;
208
- const baseDir = resolveLogsBaseDir(dbg.path);
209
- const sessionId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
210
- const sessionDir = path.join(baseDir, sessionId);
211
- if (!dbg.callback) ensureDir(sessionDir);
212
- return { sessionId, baseDir, sessionDir, stepIndex: 0, callback: dbg.callback };
213
- }
214
- function bumpStep(session) {
215
- session.stepIndex += 1;
216
- return session.stepIndex - 1;
217
- }
218
- function getModelName(model) {
219
- return model?.model ?? model?.modelName ?? model?.options?.model ?? model?.lc_alias ?? model?.constructor?.name ?? void 0;
220
- }
221
- function serializeAgentTools(tools) {
222
- return (tools || []).map((t) => {
223
- const name = t.name ?? t.lc_alias ?? t.constructor?.name ?? "tool";
224
- const description = t.description ?? t.lc_description ?? void 0;
225
- const params = t.schema ?? t.lc_kwargs?.schema ?? t.argsSchema ?? void 0;
226
- return {
227
- function: {
228
- name,
229
- description,
230
- parameters: params
231
- },
232
- type: "function"
233
- };
234
- });
235
- }
236
- function formatTools(tools) {
237
- try {
238
- return JSON.stringify(
239
- tools.map((t) => ({
240
- function: t?.function ? {
241
- name: t.function.name,
242
- description: t.function.description,
243
- parameters: t.function.parameters
244
- } : void 0,
245
- type: t?.type
246
- })),
247
- null,
248
- 4
249
- );
250
- } catch {
251
- return "[]";
252
- }
253
- }
254
- function formatMessages(messages) {
255
- const lines = [];
256
- const push = (s = "") => lines.push(s);
257
- for (const m of messages) {
258
- const anyM = m;
259
- const role = anyM.role || (typeof anyM.getType === "function" ? anyM.getType() : void 0);
260
- if (role === "system") {
261
- push("### System\n");
262
- push(String(anyM.content));
263
- push("");
264
- } else if (role === "user" || role === "human") {
265
- push("### User\n");
266
- push(typeof anyM.content === "string" ? anyM.content : JSON.stringify(anyM.content, null, 2));
267
- push("");
268
- } else if (role === "assistant" || role === "ai") {
269
- push("### AI\n");
270
- const toolCalls = Array.isArray(anyM.tool_calls) ? anyM.tool_calls : [];
271
- if (toolCalls.length) {
272
- push("Tool Calls:");
273
- for (const tc of toolCalls) {
274
- push("```");
275
- push(JSON.stringify(tc, null, 2));
276
- push("```");
277
- }
278
- }
279
- push(typeof anyM.content === "string" ? anyM.content : JSON.stringify(anyM.content, null, 2));
280
- push("");
281
- } else if (role === "tool") {
282
- push("### ToolResponse\n");
283
- const hdr = { name: anyM.name, tool_call_id: anyM.tool_call_id, executionId: anyM.executionId };
284
- push("```");
285
- push(JSON.stringify(hdr, null, 2));
286
- push("```");
287
- push(typeof anyM.content === "string" ? anyM.content : JSON.stringify(anyM.content, null, 2));
288
- push("");
289
- }
290
- }
291
- return lines.join("\n");
292
- }
293
- function formatMarkdown(params) {
294
- const { modelName, date, limits, usage, tools = [], messages } = params;
295
- const sections = [];
296
- const add = (s = "") => sections.push(s);
297
- add(`# modelName`);
298
- add(String(modelName ?? "unknown"));
299
- add("");
300
- add(`# date`);
301
- add(date);
302
- add("");
303
- add(`# limitler`);
304
- add("```");
305
- add(JSON.stringify(limits ?? {}, null, 2));
306
- add("```");
307
- add("");
308
- add(`# usage`);
309
- if (usage) {
310
- add("```");
311
- add(JSON.stringify(usage));
312
- add("```");
313
- } else {
314
- add("(none)");
315
- }
316
- add("");
317
- add(`# tools`);
318
- add("```");
319
- add(formatTools(tools));
320
- add("```");
321
- add("");
322
- add(`# Messages`);
323
- add(formatMessages(messages));
324
- return sections.join("\n");
325
- }
326
- async function writeStepMarkdown(session, fileName, markdown, payload) {
327
- if (session.callback) {
328
- session.callback({
329
- sessionId: session.sessionId,
330
- stepIndex: session.stepIndex,
331
- fileName,
332
- markdown,
333
- messages: payload?.messages ?? [],
334
- usage: payload?.usage,
335
- modelName: payload?.modelName,
336
- limits: payload?.limits,
337
- tools: payload?.tools
338
- });
339
- return;
340
- }
341
- const fp = path.join(session.sessionDir, fileName);
342
- await fs.promises.writeFile(fp, markdown, "utf8");
343
- }
344
-
345
- // src/nodes/agentCore.ts
346
- function createAgentCoreNode(opts) {
347
- return async (state) => {
348
- const runtime = state.agent || {
349
- name: opts.name,
350
- model: opts.model,
351
- tools: opts.tools || [],
352
- systemPrompt: void 0,
353
- limits: opts.limits,
354
- useTodoList: void 0,
355
- outputSchema: opts.outputSchema
356
- };
357
- const tools = runtime.tools ?? [];
358
- const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
359
- const response = await modelWithTools.invoke([...state.messages]);
360
- const messagesWithResponse = [
361
- ...state.messages,
362
- response
363
- ];
364
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
365
- const normalized = normalizeUsage(rawUsage);
366
- const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
367
- if (normalized) {
368
- const usageState = state.usage || { perRequest: [], totals: {} };
369
- const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
370
- const turn = usageState.perRequest.length + 1;
371
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
372
- const cachedInputTok = normalized.prompt_tokens_details?.cached_tokens;
373
- usageState.perRequest.push({ id, modelName, usage: normalized, timestamp, turn, cachedInput: cachedInputTok });
374
- const inputTok = normalized.prompt_tokens;
375
- const outputTok = normalized.completion_tokens;
376
- const totalTok = normalized.total_tokens;
377
- const key = modelName;
378
- const agg = usageState.totals[key] || { input: 0, output: 0, total: 0, cachedInput: 0 };
379
- usageState.totals[key] = {
380
- input: agg.input + (Number(inputTok) || 0),
381
- output: agg.output + (Number(outputTok) || 0),
382
- total: agg.total + (Number(totalTok) || 0),
383
- cachedInput: agg.cachedInput + (Number(cachedInputTok) || 0)
384
- };
385
- state.usage = usageState;
386
- }
387
- return { messages: messagesWithResponse, usage: state.usage };
388
- };
389
- }
390
-
391
- // src/nodes/tools.ts
392
- import { nanoid } from "nanoid";
393
- import { ToolMessage } from "@langchain/core/messages";
394
- function createToolsNode(initialTools, opts) {
395
- const baseToolByName = /* @__PURE__ */ new Map();
396
- for (const t of initialTools) baseToolByName.set(t.name, t);
397
- return async (state) => {
398
- const runtime = state.agent || {
399
- name: opts?.name,
400
- model: opts?.model,
401
- tools: initialTools,
402
- limits: opts?.limits,
403
- systemPrompt: opts?.systemPrompt,
404
- useTodoList: opts?.useTodoList,
405
- outputSchema: opts?.outputSchema
406
- };
407
- const activeTools = runtime.tools;
408
- const toolByName = /* @__PURE__ */ new Map();
409
- for (const t of activeTools) toolByName.set(t.name, t);
410
- const limits = {
411
- maxToolCalls: runtime.limits?.maxToolCalls ?? 10,
412
- maxParallelTools: Math.max(1, runtime.limits?.maxParallelTools ?? 1)
413
- };
414
- const appended = [];
415
- const onEvent = state.ctx?.__onEvent;
416
- for (const t of toolByName.values()) {
417
- const anyT = t;
418
- if (anyT._stateRef && typeof anyT._stateRef === "object") {
419
- anyT._stateRef.toolHistory = state.toolHistory;
420
- anyT._stateRef.toolHistoryArchived = state.toolHistoryArchived;
421
- anyT._stateRef.__onEvent = onEvent;
422
- }
423
- }
424
- const last = state.messages[state.messages.length - 1];
425
- let toolCount = state.toolCallCount || 0;
426
- const toolCalls = Array.isArray(
427
- last?.tool_calls
428
- ) ? last.tool_calls : [];
429
- const toolHistory = state.toolHistory || [];
430
- const remaining = Math.max(0, limits.maxToolCalls - toolCount);
431
- const planned = toolCalls.slice(0, remaining);
432
- const skipped = toolCalls.slice(remaining);
433
- for (const tc of skipped) {
434
- onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
435
- appended.push(new ToolMessage({
436
- content: `Skipped tool due to tool-call limit: ${tc.name}`,
437
- tool_call_id: tc.id || `${tc.name}_${appended.length}`,
438
- name: tc.name
439
- }));
440
- }
441
- const runOne = async (tc) => {
442
- const t = toolByName.get(tc.name);
443
- if (!t) {
444
- onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
445
- appended.push(
446
- new ToolMessage({
447
- content: `Tool not found: ${tc.name}`,
448
- tool_call_id: tc.id || `${tc.name}_${appended.length}`,
449
- name: tc.name
450
- })
451
- );
452
- toolCount += 1;
453
- return;
454
- }
455
- let args = tc.args;
456
- if (typeof args === "string") {
457
- try {
458
- args = JSON.parse(args);
459
- } catch (_) {
460
- }
461
- }
462
- try {
463
- const start = Date.now();
464
- onEvent?.({ type: "tool_call", phase: "start", name: t.name, id: tc.id, args });
465
- let output;
466
- const anyTool = t;
467
- if (typeof anyTool.func === "function") output = await anyTool.func(args);
468
- else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args);
469
- else if (typeof anyTool.call === "function") output = await anyTool.call(args);
470
- else if (typeof anyTool._call === "function") output = await anyTool._call(args);
471
- else if (typeof anyTool.run === "function") output = await anyTool.run(args);
472
- else throw new Error("Tool is not invokable");
473
- if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
474
- const newRuntime = output.__handoff.runtime;
475
- const executionId2 = nanoid();
476
- toolHistory.push({ executionId: executionId2, toolName: t.name, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
477
- appended.push(new ToolMessage({ content: "ok", tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name }));
478
- onEvent?.({ type: "handoff", from: runtime.name, to: newRuntime.name, toolName: t.name });
479
- state.agent = newRuntime;
480
- onEvent?.({ type: "tool_call", phase: "success", name: t.name, id: tc.id, args, result: "handoff", durationMs: Date.now() - start });
481
- toolCount += 1;
482
- return;
483
- }
484
- const content = typeof output === "string" ? output : JSON.stringify(output);
485
- if (output && typeof output === "object" && output.__finalStructuredOutput) {
486
- if (!state.ctx) state.ctx = {};
487
- state.ctx.__structuredOutputParsed = output.data;
488
- state.ctx.__finalizedDueToStructuredOutput = true;
489
- }
490
- const executionId = nanoid();
491
- toolHistory.push({ executionId, toolName: t.name, args, output, rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
492
- appended.push(new ToolMessage({ content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name }));
493
- onEvent?.({ type: "tool_call", phase: "success", name: t.name, id: tc.id, args, result: output, durationMs: Date.now() - start });
494
- toolCount += 1;
495
- } catch (e) {
496
- const executionId = nanoid();
497
- toolHistory.push({ executionId, toolName: t.name, args, output: `Error executing tool: ${e?.message || String(e)}`, rawOutput: null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
498
- appended.push(new ToolMessage({ content: `Error executing tool: ${e?.message || String(e)}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name }));
499
- onEvent?.({ type: "tool_call", phase: "error", name: t.name, id: tc.id, args, error: { message: e?.message || String(e) } });
500
- toolCount += 1;
501
- }
502
- };
503
- const concurrency = Math.max(1, limits.maxParallelTools);
504
- for (let i = 0; i < planned.length; i += concurrency) {
505
- const batch = planned.slice(i, i + concurrency);
506
- await Promise.all(batch.map(runOne));
507
- }
508
- return { messages: [...state.messages, ...appended], toolCallCount: toolCount, toolHistory, agent: state.agent };
509
- };
510
- }
511
-
512
- // src/nodes/toolLimitFinalize.ts
513
- function createToolLimitFinalizeNode(_opts) {
514
- return async (state) => {
515
- const notice = { role: "system", content: "Tool-call limit reached. Produce the best possible final answer using the available context and prior tool outputs. Do not call any more tools." };
516
- const ctx = { ...state.ctx || {}, __finalizedDueToToolLimit: true };
517
- return { messages: [...state.messages, notice], ctx };
518
- };
519
- }
520
-
521
- // src/agent.ts
522
- function createAgent(opts) {
523
- const resolver = createResolverNode();
524
- const agentCore = createAgentCoreNode(opts);
525
- const toolsBase = [...opts.tools ?? []];
526
- if (opts.outputSchema) {
527
- const responseTool = createSmartTool({
528
- name: "response",
529
- description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
530
- schema: opts.outputSchema,
531
- func: async (data) => ({ __finalStructuredOutput: true, data })
532
- });
533
- toolsBase.push(responseTool);
534
- }
535
- const toolsNode = createToolsNode(toolsBase, opts);
536
- const finalizeNode = createToolLimitFinalizeNode(opts);
537
- async function runLoop(initial, config) {
538
- let state = await resolver(initial);
539
- const maxToolCalls = opts.limits?.maxToolCalls ?? 10;
540
- const iterationLimit = Math.max(maxToolCalls * 3 + 10, 40);
541
- let iterations = 0;
542
- while (iterations < iterationLimit) {
543
- iterations++;
544
- state = { ...state, ...await agentCore(state) };
545
- const lastMsg = state.messages[state.messages.length - 1];
546
- const toolCalls = Array.isArray(lastMsg?.tool_calls) ? lastMsg.tool_calls : [];
547
- const toolCallCount = state.toolCallCount || 0;
548
- if (state.ctx?.__finalizedDueToToolLimit) {
549
- break;
550
- }
551
- if (toolCallCount >= maxToolCalls && toolCalls.length > 0) {
552
- state = { ...state, ...await finalizeNode(state) };
553
- continue;
554
- }
555
- if (toolCalls.length === 0) break;
556
- state = { ...state, ...await toolsNode(state) };
557
- if (state.ctx?.__finalizedDueToStructuredOutput) break;
558
- }
559
- return state;
560
- }
561
- const runtime = {
562
- name: opts.name,
563
- model: opts.model,
564
- tools: toolsBase,
565
- systemPrompt: void 0,
566
- limits: opts.limits,
567
- useTodoList: void 0,
568
- outputSchema: opts.outputSchema
569
- };
570
- const instance = {
571
- invoke: async (input, config) => {
572
- const onEvent = config?.onEvent;
573
- const emit = (e) => {
574
- try {
575
- onEvent?.(e);
576
- } catch {
577
- }
578
- };
579
- const initial = {
580
- messages: input.messages || [],
581
- summaries: input.summaries || [],
582
- toolCallCount: input.toolCallCount || 0,
583
- toolCache: input.toolCache || {},
584
- toolHistory: input.toolHistory || [],
585
- toolHistoryArchived: input.toolHistoryArchived || [],
586
- metadata: input.metadata,
587
- ctx: { ...input.ctx || {}, __onEvent: onEvent },
588
- plan: input.plan || null,
589
- planVersion: input.planVersion || 0,
590
- agent: input.agent || runtime,
591
- usage: input.usage || { perRequest: [], totals: {} }
592
- };
593
- const res = await runLoop(initial, config);
594
- const finalMsg = res.messages[res.messages.length - 1];
595
- let content = "";
596
- if (typeof finalMsg?.content === "string") content = finalMsg.content;
597
- else if (Array.isArray(finalMsg?.content)) content = finalMsg.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("");
598
- let parsed = void 0;
599
- const schema = opts.outputSchema;
600
- if (schema && res.ctx?.__structuredOutputParsed) {
601
- parsed = res.ctx.__structuredOutputParsed;
602
- } else if (schema && content) {
603
- let jsonText = null;
604
- const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
605
- if (fenced && fenced[1]) jsonText = fenced[1].trim();
606
- else {
607
- const braceIdx = content.indexOf("{");
608
- const bracketIdx = content.indexOf("[");
609
- const start = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b)[0];
610
- if (start !== void 0) jsonText = content.slice(start).trim();
611
- }
612
- try {
613
- const raw = JSON.parse(jsonText ?? content);
614
- parsed = schema.parse(raw);
615
- } catch {
616
- }
617
- }
618
- emit({ type: "finalAnswer", content: typeof finalMsg?.content === "string" ? finalMsg.content : content });
619
- return {
620
- content,
621
- output: parsed,
622
- metadata: { usage: res.usage },
623
- messages: res.messages,
624
- state: res
625
- };
626
- },
627
- asTool: ({ toolName, description, inputDescription }) => {
628
- const schema = z.object({ input: z.string().describe(inputDescription || "Input for delegated agent") });
629
- return createSmartTool({
630
- name: toolName,
631
- description: description || `Delegate task to agent ${opts.name || "Agent"}`,
632
- schema,
633
- func: async ({ input }) => {
634
- const res = await instance.invoke({ messages: [{ role: "user", content: input }] });
635
- return { content: res.content };
636
- }
637
- });
638
- },
639
- asHandoff: ({ toolName, description, schema }) => {
640
- const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
641
- const zschema = schema || z.object({ reason: z.string().describe("Reason for handoff") });
642
- const tool3 = createSmartTool({
643
- name: finalName,
644
- description: description || `Handoff control to agent ${runtime.name || "Agent"}`,
645
- schema: zschema,
646
- func: async (_args) => ({ __handoff: { runtime } })
647
- });
648
- return { type: "handoff", toolName: finalName, description: description || "", schema: zschema, target: instance };
649
- },
650
- __runtime: runtime
651
- };
652
- if (opts.handoffs && Array.isArray(opts.handoffs)) {
653
- const handoffTools = opts.handoffs.map((h) => {
654
- const schema = h.schema || z.object({ reason: z.string().describe("Reason for handoff") });
655
- return createSmartTool({
656
- name: h.toolName,
657
- description: h.description || `Handoff to ${h.target.__runtime.name || "agent"}`,
658
- schema,
659
- func: async (_args) => ({ __handoff: { runtime: h.target.__runtime } })
660
- });
661
- });
662
- runtime.tools = [...runtime.tools, ...handoffTools];
663
- }
664
- return instance;
665
- }
666
-
667
- // src/nodes/agent.ts
668
- function createAgentNode(opts) {
669
- return async (state) => {
670
- const runtime = state.agent || {
671
- name: opts.name,
672
- model: opts.model,
673
- tools: opts.tools || [],
674
- systemPrompt: opts.systemPrompt,
675
- limits: opts.limits,
676
- useTodoList: opts.useTodoList,
677
- outputSchema: opts.outputSchema
678
- };
679
- const tools = runtime.tools ?? [];
680
- const limits = {
681
- maxToolCalls: runtime.limits?.maxToolCalls ?? 10,
682
- toolOutputTokenLimit: runtime.limits?.toolOutputTokenLimit ?? 5e3,
683
- contextTokenLimit: runtime.limits?.contextTokenLimit ?? 6e4,
684
- summaryTokenLimit: runtime.limits?.summaryTokenLimit ?? 5e4
685
- };
686
- const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
687
- const structuredOutputHint = runtime.outputSchema ? [
688
- "A structured output schema is active.",
689
- "Do NOT output the final JSON directly as an assistant message.",
690
- "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
691
- "Call it exactly once then STOP producing further assistant messages."
692
- ].join("\n") : "";
693
- const systemMsg = {
694
- role: "system",
695
- content: buildSystemPrompt(
696
- [runtime.systemPrompt, structuredOutputHint].filter(Boolean).join("\n"),
697
- runtime.useTodoList === true,
698
- runtime.name || "Agent"
699
- )
700
- };
701
- const messages = [systemMsg, ...state.messages];
702
- const debugSession = state.ctx?.__debugSession;
703
- const response = await modelWithTools.invoke([systemMsg, ...state.messages]);
704
- const messagesWithResponse = [
705
- ...messages,
706
- response
707
- ];
708
- const messagesWithSystem = [
709
- systemMsg,
710
- ...messagesWithResponse
711
- ];
712
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
713
- const normalized = normalizeUsage(rawUsage);
714
- const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
715
- if (normalized) {
716
- const usageState = state.usage || { perRequest: [], totals: {} };
717
- const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
718
- const turn = usageState.perRequest.length + 1;
719
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
720
- const cachedInputTok = normalized.prompt_tokens_details.cached_tokens;
721
- usageState.perRequest.push({ id, modelName, usage: normalized, timestamp, turn, cachedInput: cachedInputTok });
722
- const inputTok = normalized.prompt_tokens;
723
- const outputTok = normalized.completion_tokens;
724
- const totalTok = normalized.total_tokens;
725
- const key = modelName;
726
- const agg = usageState.totals[key] || { input: 0, output: 0, total: 0, cachedInput: 0 };
727
- usageState.totals[key] = {
728
- input: agg.input + (Number(inputTok) || 0),
729
- output: agg.output + (Number(outputTok) || 0),
730
- total: agg.total + (Number(totalTok) || 0),
731
- cachedInput: agg.cachedInput + (Number(cachedInputTok) || 0)
732
- };
733
- state.usage = usageState;
734
- }
735
- if (debugSession) {
736
- const idx = bumpStep(debugSession);
737
- const fileName = `${String(idx).padStart(2, "0")}.md`;
738
- const markdown = formatMarkdown({
739
- modelName: getModelName(runtime.model || opts.model),
740
- date: (/* @__PURE__ */ new Date()).toISOString(),
741
- limits: runtime.limits,
742
- // IMPORTANT: log only per-request normalized usage
743
- usage: normalized,
744
- tools: serializeAgentTools(tools),
745
- messages: messagesWithSystem
746
- });
747
- await writeStepMarkdown(debugSession, fileName, markdown, {
748
- messages: messagesWithSystem,
749
- usage: normalized,
750
- modelName: getModelName(runtime.model || opts.model),
751
- limits: runtime.limits,
752
- tools: serializeAgentTools(tools)
753
- });
754
- }
755
- return { messages: messagesWithResponse, usage: state.usage };
756
- };
757
- }
758
-
759
- // src/nodes/contextSummarize.ts
760
- import { nanoid as nanoid2 } from "nanoid";
761
-
762
- // src/utils/utilTokens.ts
763
- function countApproxTokens(text) {
764
- if (!text) return 0;
765
- return Math.ceil(text.length / 4);
766
- }
767
-
768
- // src/nodes/contextSummarize.ts
769
- var systemMessage = (content) => ({ role: "system", content });
770
- var humanMessage = (content) => ({ role: "user", content });
771
- var toolMessage = (name, content, tool_call_id) => ({ role: "tool", name, content, tool_call_id });
772
- function createContextSummarizeNode(opts) {
773
- return async (state) => {
774
- const messages = state.messages || [];
775
- const summaryTokenLimit = opts.limits?.summaryTokenLimit ?? opts.limits?.summary_token_limit ?? 1e5;
776
- const model = opts.model;
777
- const archived = state.toolHistoryArchived ? [...state.toolHistoryArchived] : [];
778
- const live = state.toolHistory ? [...state.toolHistory] : [];
779
- const byCallId = /* @__PURE__ */ new Map();
780
- for (const item of archived) {
781
- if (item.tool_call_id) byCallId.set(item.tool_call_id, item);
782
- }
783
- for (const item of live) {
784
- if (item.tool_call_id) byCallId.set(item.tool_call_id, item);
785
- }
786
- for (const item of live) {
787
- if (!item.executionId) item.executionId = nanoid2();
788
- }
789
- const getText = (m) => {
790
- if (!m) return "";
791
- const c = m.content;
792
- if (typeof c === "string") return c;
793
- if (Array.isArray(c)) return c.map((p) => typeof p === "string" ? p : p?.text ?? p?.content ?? "").join("");
794
- return String(c ?? "");
795
- };
796
- const sysText = "You are a concise summarization assistant for a tool-using agent. Summarize prior tool executions into a compact brief that preserves key facts, decisions, and outputs. Reference executionId values where helpful. Avoid repeating raw data; prefer synthesis.";
797
- const sys = systemMessage(sysText);
798
- let summaryText = "";
799
- try {
800
- const limit = Math.max(1e3, Number(summaryTokenLimit) || 1e5);
801
- const safetyBuffer = Math.floor(Math.min(4e3, limit * 0.05));
802
- const perCallBudget = Math.max(500, limit - safetyBuffer);
803
- const safeToolPair = opts?.safeToolPair !== false;
804
- const chunks = [];
805
- let current = [];
806
- let currentTokens = countApproxTokens(sysText);
807
- let pendingToolCalls = /* @__PURE__ */ new Set();
808
- const extractToolCallIds = (msg) => {
809
- const tc = msg?.additional_kwargs?.tool_calls;
810
- if (!Array.isArray(tc) || tc.length === 0) return [];
811
- return tc.map((c) => c?.id).filter((id) => typeof id === "string");
812
- };
813
- for (let i = 0; i < messages.length; i++) {
814
- const m = messages[i];
815
- const text = getText(m);
816
- const tks = countApproxTokens(text);
817
- const isAIWithToolCalls = safeToolPair && m?.role === "assistant" && extractToolCallIds(m).length > 0;
818
- const isToolMsg = safeToolPair && m?.role === "tool";
819
- const toolCallId = isToolMsg ? m.tool_call_id : void 0;
820
- const wouldExceed = currentTokens + tks > perCallBudget && current.length > 0;
821
- if (wouldExceed) {
822
- let forceAddToCurrent = false;
823
- if (safeToolPair) {
824
- if (isToolMsg && toolCallId && pendingToolCalls.has(toolCallId)) {
825
- forceAddToCurrent = true;
826
- } else if (isAIWithToolCalls) {
827
- } else if (pendingToolCalls.size > 0) {
828
- }
829
- }
830
- if (!forceAddToCurrent) {
831
- if (current.length > 0) {
832
- chunks.push(current);
833
- }
834
- current = [];
835
- currentTokens = countApproxTokens(sysText);
836
- pendingToolCalls = /* @__PURE__ */ new Set();
837
- }
838
- }
839
- current.push(m);
840
- currentTokens += tks;
841
- if (isAIWithToolCalls) {
842
- for (const id of extractToolCallIds(m)) pendingToolCalls.add(id);
843
- }
844
- if (isToolMsg && toolCallId && pendingToolCalls.has(toolCallId)) {
845
- pendingToolCalls.delete(toolCallId);
846
- }
847
- if (currentTokens >= perCallBudget && pendingToolCalls.size === 0) {
848
- chunks.push(current);
849
- current = [];
850
- currentTokens = countApproxTokens(sysText);
851
- pendingToolCalls = /* @__PURE__ */ new Set();
852
- }
853
- }
854
- if (current.length > 0) {
855
- chunks.push(current);
856
- }
857
- const partials = [];
858
- for (const chunk of chunks) {
859
- let resp = null;
860
- try {
861
- resp = await (model?.invoke ? model.invoke([sys, ...chunk]) : null);
862
- const c = resp?.content;
863
- const text = typeof c === "string" ? c : Array.isArray(c) ? c.map((p) => typeof p === "string" ? p : p?.text ?? p?.content ?? "").join("") : "";
864
- partials.push((text || "").trim());
865
- } catch (e) {
866
- console.error("Summarization chunk failed:", e);
867
- partials.push("[summary unavailable for one chunk]");
868
- }
869
- }
870
- const mergeSysText = "You merge partial summaries into a single cohesive, non-redundant summary that preserves key facts and decisions. Be concise.";
871
- const mergeSys = systemMessage(mergeSysText);
872
- const mergeIteratively = async (texts) => {
873
- if (texts.length === 0) return "";
874
- if (texts.length === 1) return texts[0];
875
- const groups = [];
876
- let group = [];
877
- let toks = countApproxTokens(mergeSysText);
878
- for (const t of texts) {
879
- const tt = `- ${t}`;
880
- const tk = countApproxTokens(tt);
881
- if (toks + tk > perCallBudget && group.length > 0) {
882
- groups.push(group);
883
- group = [];
884
- toks = countApproxTokens(mergeSysText);
885
- }
886
- group.push(tt);
887
- toks += tk;
888
- }
889
- if (group.length > 0) groups.push(group);
890
- const next = [];
891
- for (const g of groups) {
892
- const mergePrompt = humanMessage(`Merge the following partial summaries into a single concise brief. Avoid duplication, keep key facts and decisions.
893
-
894
- ${g.join("\n")}
895
-
896
- Merged summary:`);
897
- try {
898
- const r = await (model?.invoke ? model.invoke([mergeSys, mergePrompt]) : null);
899
- const cc = r?.content;
900
- const txt = typeof cc === "string" ? cc : Array.isArray(cc) ? cc.map((p) => typeof p === "string" ? p : p?.text ?? p?.content ?? "").join("") : "";
901
- next.push((txt || "").trim());
902
- } catch (e) {
903
- console.error("Merge summarization failed:", e);
904
- next.push(g.join(" "));
905
- }
906
- }
907
- return mergeIteratively(next);
908
- };
909
- summaryText = await mergeIteratively(partials);
910
- if (!summaryText) summaryText = "Context summarized.";
911
- } catch (e) {
912
- console.error("Error during context summarization:", e);
913
- summaryText = "Context summarized.";
914
- }
915
- const rewritten = [];
916
- const alreadySummarized = (content) => {
917
- const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => typeof p === "string" ? p : p?.text ?? p?.content ?? "").join("") : String(content ?? "");
918
- return /\bSUMMARIZED\b/.test(text);
919
- };
920
- for (const m of messages) {
921
- if (m.getType() === "tool") {
922
- const toolMessageObj = m;
923
- if (alreadySummarized(toolMessageObj.content) || toolMessageObj.name === "context_summarize" || toolMessageObj.name === "manage_todo_list") {
924
- rewritten.push(m);
925
- continue;
926
- }
927
- const callId = toolMessageObj.tool_call_id;
928
- const safeId = callId || `summarized_${nanoid2(6)}`;
929
- let hist = callId ? byCallId.get(callId) : void 0;
930
- if (!hist) {
931
- hist = {
932
- executionId: nanoid2(),
933
- toolName: toolMessageObj.name || "unknown_tool",
934
- args: null,
935
- output: typeof toolMessageObj.content === "string" ? toolMessageObj.content : Array.isArray(toolMessageObj.content) ? toolMessageObj.content.map((p) => typeof p === "string" ? p : p?.text ?? p?.content ?? "").join("") : String(toolMessageObj.content ?? ""),
936
- rawOutput: void 0,
937
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
938
- summarized: true,
939
- messageId: void 0,
940
- tool_call_id: callId,
941
- fromCache: false
942
- };
943
- if (callId) byCallId.set(callId, hist);
944
- archived.push({ ...hist });
945
- }
946
- if (!hist.executionId) hist.executionId = nanoid2();
947
- hist.summarized = true;
948
- rewritten.push({
949
- role: "tool",
950
- content: `SUMMARIZED executionId:'${hist.executionId}'`,
951
- tool_call_id: safeId,
952
- name: toolMessage.name
953
- });
954
- continue;
955
- }
956
- rewritten.push(m);
957
- }
958
- const summarizeCallId = `summarize_${nanoid2(6)}`;
959
- const summarizeArgs = { reason: "Exceeded token limit: summarize prior tool outputs for context." };
960
- const syntheticAssistant = {
961
- role: "assistant",
962
- content: "",
963
- additional_kwargs: {
964
- tool_calls: [
965
- { id: summarizeCallId, type: "function", function: { name: "context_summarize", arguments: JSON.stringify(summarizeArgs) } }
966
- ]
967
- }
968
- };
969
- const summaryExecId = nanoid2();
970
- const syntheticToolResp = {
971
- role: "tool",
972
- content: `${summaryText}`,
973
- tool_call_id: summarizeCallId,
974
- name: "context_summarize"
975
- };
976
- for (const item of live) item.summarized = true;
977
- const merged = [...archived, ...live];
978
- const dedupMap = /* @__PURE__ */ new Map();
979
- for (const it of merged) {
980
- const key = it.tool_call_id || `${it.toolName}:${it.timestamp}`;
981
- dedupMap.set(key, { ...it });
982
- }
983
- const newArchived = Array.from(dedupMap.values());
984
- const onEvent = state.ctx?.__onEvent;
985
- onEvent?.({ type: "summarization", summary: summaryText, archivedCount: newArchived.length });
986
- return {
987
- messages: [...rewritten, syntheticAssistant, syntheticToolResp],
988
- toolHistoryArchived: newArchived,
989
- toolHistory: [],
990
- ctx: { ...state.ctx || {}, __contextSummarized: true }
991
- };
992
- };
993
- }
994
-
995
- // src/utils/tokenManager.ts
996
- async function applyTokenLimits({
997
- state,
998
- model,
999
- limits
1000
- }) {
1001
- const messages = state.messages || [];
1002
- const budget = limits.contextTokenLimit;
1003
- let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
1004
- if (total <= budget) return state;
1005
- const newMessages = [...messages];
1006
- return { ...state, messages: newMessages };
1007
- }
1008
-
1009
- // src/contextTools.ts
1010
- import { tool as tool2 } from "@langchain/core/tools";
1011
- import { z as z2 } from "zod";
1012
- function createContextTools(stateRef, opts) {
1013
- const tools = [];
1014
- if (opts?.planningEnabled) {
1015
- const manageTodo = tool2(
1016
- async ({ operation, todoList }) => {
1017
- const onEvent = manageTodo._stateRef?.__onEvent;
1018
- if (operation === "read") {
1019
- const list = stateRef.todoList || [];
1020
- onEvent?.({ type: "plan", source: "manage_todo_list", operation: "read", todoList: list });
1021
- return list;
1022
- }
1023
- if (Array.isArray(todoList)) {
1024
- stateRef.todoList = todoList;
1025
- }
1026
- const payload = {
1027
- status: "ok",
1028
- operation,
1029
- count: Array.isArray(todoList) ? todoList.length : void 0
1030
- };
1031
- onEvent?.({ type: "plan", source: "manage_todo_list", operation: "write", todoList });
1032
- return payload;
1033
- },
1034
- {
1035
- name: "manage_todo_list",
1036
- description: "Manage a structured todo list to track progress and plan tasks throughout your coding session. Use this tool VERY frequently to ensure task visibility and proper planning.\n\nWhen to use this tool:\n- Complex multi-step work requiring planning and tracking\n- When user provides multiple tasks or requests (numbered/comma-separated)\n- After receiving new instructions that require multiple steps\n- BEFORE starting work on any todo (mark as in-progress)\n- IMMEDIATELY after completing each todo (mark completed individually)\n- When breaking down larger tasks into smaller actionable steps\n- To give users visibility into your progress and planning\n\nWhen NOT to use:\n- Single, trivial tasks that can be completed in one step\n- Purely conversational/informational requests\n- When just reading files or performing simple searches\n\nCRITICAL workflow:\n1. Plan tasks by writing todo list with specific, actionable items\n2. Complete the work for that specific todo\n3. Mark that todo as completed IMMEDIATELY\n4. Move to next todo and repeat\n\nTodo states:\n- not-started: Todo not yet begun\n- in-progress: Currently working (limit ONE at a time)\n- completed: Finished successfully\n\nIMPORTANT: Mark todos completed as soon as they are done. Do not batch completions.",
1037
- schema: z2.object({
1038
- operation: z2.enum(["write", "read"]),
1039
- todoList: z2.array(z2.object({
1040
- id: z2.number().int().positive().describe("Sequential id starting from 1"),
1041
- title: z2.string().min(1),
1042
- description: z2.string().min(1),
1043
- status: z2.enum(["not-started", "in-progress", "completed"]),
1044
- evidence: z2.string().max(200).optional()
1045
- })).optional()
1046
- })
1047
- }
1048
- );
1049
- manageTodo._stateRef = stateRef;
1050
- tools.push(manageTodo);
1051
- }
1052
- const getTool = tool2(
1053
- async ({ executionId }) => {
1054
- let execution = stateRef.toolHistory?.find((t) => t.executionId === executionId);
1055
- if (!execution) {
1056
- execution = stateRef.toolHistoryArchived?.find((t) => t.executionId === executionId);
1057
- }
1058
- if (execution) {
1059
- return execution.rawOutput || execution.output;
1060
- }
1061
- return "Execution not found. Please check the executionId.";
1062
- },
1063
- {
1064
- name: "get_tool_response",
1065
- description: "RETRIEVE tool execution response: Use this to access the full output of a tool execution that shows as 'SUMMARIZED' in the tool history.",
1066
- schema: z2.object({ executionId: z2.string().describe("Tool execution id") })
1067
- }
1068
- );
1069
- getTool._stateRef = stateRef;
1070
- tools.push(getTool);
1071
- if (opts?.outputSchema) {
1072
- const responseTool = tool2(
1073
- async (data) => {
1074
- try {
1075
- const validated = opts.outputSchema.parse ? opts.outputSchema.parse(data) : data;
1076
- return { __finalStructuredOutput: true, data: validated };
1077
- } catch (e) {
1078
- return { error: "Schema validation failed", details: e?.message };
1079
- }
1080
- },
1081
- {
1082
- name: "response",
1083
- description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
1084
- // Use schema directly; if not Zod, fall back to any-object
1085
- schema: opts.outputSchema.shape ? opts.outputSchema : z2.any()
1086
- }
1087
- );
1088
- responseTool._stateRef = stateRef;
1089
- tools.push(responseTool);
1090
- }
1091
- return tools;
1092
- }
1093
-
1094
- // src/graph/decisions.ts
1095
- function needsSummarization(state, opts, summarizationEnabled) {
1096
- const maxTok = opts.limits?.maxToken;
1097
- if (!maxTok || !summarizationEnabled) return false;
1098
- try {
1099
- const allText = (state.messages || []).map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("") : "").join("\n");
1100
- const tokenCount = countApproxTokens(allText);
1101
- return tokenCount > maxTok;
1102
- } catch {
1103
- return false;
1104
- }
1105
- }
1106
- function resolverDecisionFactory(opts, summarizationEnabled) {
1107
- return function resolverDecision(state) {
1108
- return needsSummarization(state, opts, summarizationEnabled) ? "contextSummarize" : "agent";
1109
- };
1110
- }
1111
- function toolsDecisionFactory(opts, summarizationEnabled) {
1112
- return function toolsDecision(state) {
1113
- const max = opts.limits?.maxToolCalls ?? 10;
1114
- const count = state.toolCallCount || 0;
1115
- if (count >= max) return "toolLimitFinalize";
1116
- return needsSummarization(state, opts, summarizationEnabled) ? "contextSummarize" : "agent";
1117
- };
1118
- }
1119
-
1120
- // src/smart/index.ts
1121
- function createSmartAgent(opts) {
1122
- const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0 };
1123
- const planningEnabled = opts.useTodoList === true;
1124
- const contextTools = createContextTools(stateRef, { planningEnabled, outputSchema: void 0 });
1125
- const mergedTools = [...opts.tools ?? [], ...contextTools];
1126
- const base = createAgent({ ...opts, tools: mergedTools });
1127
- const summarizationEnabled = opts.summarization !== false;
1128
- const summarizer = summarizationEnabled ? createContextSummarizeNode(opts) : void 0;
1129
- const decideBefore = resolverDecisionFactory(opts, summarizationEnabled);
1130
- const decideAfter = toolsDecisionFactory(opts, summarizationEnabled);
1131
- const structuredOutputHint = opts.outputSchema ? [
1132
- "A structured output schema is active.",
1133
- "Do NOT output the final JSON directly as an assistant message.",
1134
- "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
1135
- "Call it exactly once then STOP producing further assistant messages."
1136
- ].join("\n") : "";
1137
- function systemMessage2() {
1138
- const sys = buildSystemPrompt(
1139
- [opts.systemPrompt, structuredOutputHint].filter(Boolean).join("\n"),
1140
- opts.useTodoList === true,
1141
- opts.name || "Agent"
1142
- );
1143
- return { role: "system", content: sys };
1144
- }
1145
- const instance = {
1146
- invoke: async (input, config) => {
1147
- stateRef.toolHistory = input.toolHistory;
1148
- stateRef.toolHistoryArchived = input.toolHistoryArchived;
1149
- const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
1150
- const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
1151
- let state = { ...input, messages: seedMessages };
1152
- let lastResult = null;
1153
- const iterationLimit = Math.max((opts.limits?.maxToolCalls ?? 10) * 3 + 5, 30);
1154
- for (let i = 0; i < iterationLimit; i++) {
1155
- const next = summarizationEnabled ? decideBefore(state) : "agent";
1156
- if (next === "contextSummarize" && summarizer) {
1157
- const delta = await summarizer(state);
1158
- state = { ...state, ...delta };
1159
- continue;
1160
- }
1161
- const res = await base.invoke(state, config);
1162
- lastResult = res;
1163
- state = res.state || { ...state, messages: res.messages };
1164
- if (state.ctx?.__finalizedDueToStructuredOutput) break;
1165
- if (summarizationEnabled) {
1166
- const after = decideAfter(state);
1167
- if (after === "contextSummarize" && summarizer) {
1168
- const delta = await summarizer(state);
1169
- state = { ...state, ...delta };
1170
- continue;
1171
- }
1172
- }
1173
- break;
1174
- }
1175
- if (!lastResult) {
1176
- const res = await base.invoke(state, config);
1177
- lastResult = res;
1178
- }
1179
- return lastResult;
1180
- },
1181
- asTool: base.asTool,
1182
- asHandoff: base.asHandoff,
1183
- __runtime: base.__runtime
1184
- };
1185
- return instance;
1186
- }
1187
- export {
1188
- applyTokenLimits,
1189
- buildSystemPrompt,
1190
- bumpStep,
1191
- countApproxTokens,
1192
- createAgent,
1193
- createAgentNode,
1194
- createContextSummarizeNode,
1195
- createContextTools,
1196
- createDebugSession,
1197
- createResolverNode,
1198
- createSmartAgent,
1199
- createSmartTool,
1200
- createTool,
1201
- createToolLimitFinalizeNode,
1202
- createToolsNode,
1203
- formatMarkdown,
1204
- fromLangchainModel,
1205
- getModelName,
1206
- isSmartModel,
1207
- resolveLogsBaseDir,
1208
- serializeAgentTools,
1209
- withTools,
1210
- writeStepMarkdown
1211
- };
1212
- //# sourceMappingURL=index.js.map