@cognipeer/agent-sdk 0.2.4 → 0.2.6

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.d.cts CHANGED
@@ -75,16 +75,15 @@ interface ToolInterface<TInput = any, TOutput = any, TCallOptions = any> {
75
75
  approvalDefaults?: any;
76
76
  [key: string]: any;
77
77
  }
78
- type RunnableConfig = {
79
- [key: string]: any;
80
- };
78
+ type RunnableConfig = Record<string, unknown>;
81
79
  type BaseMessage = {
82
80
  role: string;
83
81
  name?: string;
84
82
  content: string | ContentPart[];
85
83
  tool_calls?: any;
86
84
  tool_call_id?: string;
87
- [key: string]: any;
85
+ metadata?: Record<string, any>;
86
+ additional_kwargs?: Record<string, any>;
88
87
  };
89
88
  type AIMessage = BaseMessage & {
90
89
  role: 'assistant';
@@ -144,6 +143,7 @@ type AgentLimits = {
144
143
  maxToolCalls?: number;
145
144
  maxParallelTools?: number;
146
145
  };
146
+ /** @deprecated Use AgentLimits instead */
147
147
  type SmartAgentLimits = AgentLimits;
148
148
  type TraceSinkFileConfig = {
149
149
  type: "file";
@@ -176,11 +176,12 @@ type TracingConfig = {
176
176
  mode?: TracingMode;
177
177
  sink?: TraceSinkConfig;
178
178
  };
179
+ /** @deprecated Use TracingConfig instead */
179
180
  type SmartAgentTracingConfig = TracingConfig;
180
181
  type AgentOptions = {
181
182
  name?: string;
182
183
  version?: string;
183
- model: any;
184
+ model: BaseChatModel;
184
185
  tools?: Array<ToolInterface<any, any, any>>;
185
186
  guardrails?: ConversationGuardrail[];
186
187
  handoffs?: HandoffDescriptor[];
@@ -192,7 +193,7 @@ type AgentOptions = {
192
193
  type SmartAgentOptions = {
193
194
  name?: string;
194
195
  version?: string;
195
- model: any;
196
+ model: BaseChatModel;
196
197
  tools?: Array<ToolInterface<any, any, any>>;
197
198
  guardrails?: ConversationGuardrail[];
198
199
  handoffs?: HandoffDescriptor[];
@@ -214,7 +215,7 @@ type SmartAgentOptions = {
214
215
  type AgentRuntimeConfig = {
215
216
  name?: string;
216
217
  version?: string;
217
- model: any;
218
+ model: BaseChatModel;
218
219
  tools: Array<ToolInterface<any, any, any>>;
219
220
  guardrails?: ConversationGuardrail[];
220
221
  systemPrompt?: string;
@@ -747,6 +748,15 @@ declare function applyTokenLimits({ state, limits, }: {
747
748
  summaries?: string[];
748
749
  }>;
749
750
 
751
+ /**
752
+ * Estimates token count from text using a character-based heuristic.
753
+ *
754
+ * This is a rough approximation:
755
+ * - For Latin/ASCII text: ~4 characters per token (GPT-family models)
756
+ * - For CJK/Unicode text: ~1.5 characters per token
757
+ *
758
+ * For production accuracy, consider using a proper tokenizer like tiktoken.
759
+ */
750
760
  declare function countApproxTokens(text: string): number;
751
761
 
752
762
  declare function createContextTools(stateRef: {
package/dist/index.d.ts CHANGED
@@ -75,16 +75,15 @@ interface ToolInterface<TInput = any, TOutput = any, TCallOptions = any> {
75
75
  approvalDefaults?: any;
76
76
  [key: string]: any;
77
77
  }
78
- type RunnableConfig = {
79
- [key: string]: any;
80
- };
78
+ type RunnableConfig = Record<string, unknown>;
81
79
  type BaseMessage = {
82
80
  role: string;
83
81
  name?: string;
84
82
  content: string | ContentPart[];
85
83
  tool_calls?: any;
86
84
  tool_call_id?: string;
87
- [key: string]: any;
85
+ metadata?: Record<string, any>;
86
+ additional_kwargs?: Record<string, any>;
88
87
  };
89
88
  type AIMessage = BaseMessage & {
90
89
  role: 'assistant';
@@ -144,6 +143,7 @@ type AgentLimits = {
144
143
  maxToolCalls?: number;
145
144
  maxParallelTools?: number;
146
145
  };
146
+ /** @deprecated Use AgentLimits instead */
147
147
  type SmartAgentLimits = AgentLimits;
148
148
  type TraceSinkFileConfig = {
149
149
  type: "file";
@@ -176,11 +176,12 @@ type TracingConfig = {
176
176
  mode?: TracingMode;
177
177
  sink?: TraceSinkConfig;
178
178
  };
179
+ /** @deprecated Use TracingConfig instead */
179
180
  type SmartAgentTracingConfig = TracingConfig;
180
181
  type AgentOptions = {
181
182
  name?: string;
182
183
  version?: string;
183
- model: any;
184
+ model: BaseChatModel;
184
185
  tools?: Array<ToolInterface<any, any, any>>;
185
186
  guardrails?: ConversationGuardrail[];
186
187
  handoffs?: HandoffDescriptor[];
@@ -192,7 +193,7 @@ type AgentOptions = {
192
193
  type SmartAgentOptions = {
193
194
  name?: string;
194
195
  version?: string;
195
- model: any;
196
+ model: BaseChatModel;
196
197
  tools?: Array<ToolInterface<any, any, any>>;
197
198
  guardrails?: ConversationGuardrail[];
198
199
  handoffs?: HandoffDescriptor[];
@@ -214,7 +215,7 @@ type SmartAgentOptions = {
214
215
  type AgentRuntimeConfig = {
215
216
  name?: string;
216
217
  version?: string;
217
- model: any;
218
+ model: BaseChatModel;
218
219
  tools: Array<ToolInterface<any, any, any>>;
219
220
  guardrails?: ConversationGuardrail[];
220
221
  systemPrompt?: string;
@@ -747,6 +748,15 @@ declare function applyTokenLimits({ state, limits, }: {
747
748
  summaries?: string[];
748
749
  }>;
749
750
 
751
+ /**
752
+ * Estimates token count from text using a character-based heuristic.
753
+ *
754
+ * This is a rough approximation:
755
+ * - For Latin/ASCII text: ~4 characters per token (GPT-family models)
756
+ * - For CJK/Unicode text: ~1.5 characters per token
757
+ *
758
+ * For production accuracy, consider using a proper tokenizer like tiktoken.
759
+ */
750
760
  declare function countApproxTokens(text: string): number;
751
761
 
752
762
  declare function createContextTools(stateRef: {
package/dist/index.mjs CHANGED
@@ -133,39 +133,38 @@ function withTools(model, tools) {
133
133
  }
134
134
  function fromLangchainModel(lcModel) {
135
135
  if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
136
- const adapted = {
137
- invoke: async (messages, options) => {
138
- const normalizeContent = (content) => {
139
- if (content == null) return content;
140
- if (typeof content === "string") return content;
141
- if (Array.isArray(content)) {
142
- return content.map((part) => {
143
- if (!part || typeof part !== "object") return part;
144
- if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
145
- if (part.type === "image_url") {
146
- const img = part.image_url;
147
- if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
148
- if (img && typeof img === "object") {
149
- if ("url" in img) return { type: "image_url", image_url: img };
150
- if ("base64" in img) {
151
- const media = img.media_type || "image/jpeg";
152
- const dataUrl = `data:${media};base64,${img.base64}`;
153
- const detail = img.detail;
154
- return { type: "image_url", image_url: { url: dataUrl, detail } };
155
- }
156
- }
157
- return part;
136
+ const normalizeContent = (content) => {
137
+ if (content == null) return content;
138
+ if (typeof content === "string") return content;
139
+ if (Array.isArray(content)) {
140
+ return content.map((part) => {
141
+ if (!part || typeof part !== "object") return part;
142
+ if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
143
+ if (part.type === "image_url") {
144
+ const img = part.image_url;
145
+ if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
146
+ if (img && typeof img === "object") {
147
+ if ("url" in img) return { type: "image_url", image_url: img };
148
+ if ("base64" in img) {
149
+ const media = img.media_type || "image/jpeg";
150
+ const dataUrl = `data:${media};base64,${img.base64}`;
151
+ const detail = img.detail;
152
+ return { type: "image_url", image_url: { url: dataUrl, detail } };
158
153
  }
159
- return part;
160
- });
154
+ }
155
+ return part;
161
156
  }
162
- return content;
163
- };
164
- const toLC = (m) => {
165
- if (m._getType || m.lc_serializable) return m;
166
- const role = m.role;
167
- return { role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
168
- };
157
+ return part;
158
+ });
159
+ }
160
+ return content;
161
+ };
162
+ const toLC = (m) => {
163
+ if (m._getType || m.lc_serializable) return m;
164
+ return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
165
+ };
166
+ const adapted = {
167
+ invoke: async (messages, options) => {
169
168
  const lcMessages = messages.map(toLC);
170
169
  const response = await lcModel.invoke(lcMessages, options);
171
170
  if (response && typeof response === "object") {
@@ -183,36 +182,6 @@ function fromLangchainModel(lcModel) {
183
182
  },
184
183
  stream: async function* (messages, options) {
185
184
  if (typeof lcModel.stream !== "function") return;
186
- const normalizeContent = (content) => {
187
- if (content == null) return content;
188
- if (typeof content === "string") return content;
189
- if (Array.isArray(content)) {
190
- return content.map((part) => {
191
- if (!part || typeof part !== "object") return part;
192
- if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
193
- if (part.type === "image_url") {
194
- const img = part.image_url;
195
- if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
196
- if (img && typeof img === "object") {
197
- if ("url" in img) return { type: "image_url", image_url: img };
198
- if ("base64" in img) {
199
- const media = img.media_type || "image/jpeg";
200
- const dataUrl = `data:${media};base64,${img.base64}`;
201
- const detail = img.detail;
202
- return { type: "image_url", image_url: { url: dataUrl, detail } };
203
- }
204
- }
205
- return part;
206
- }
207
- return part;
208
- });
209
- }
210
- return content;
211
- };
212
- const toLC = (m) => {
213
- if (m._getType || m.lc_serializable) return m;
214
- return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
215
- };
216
185
  const lcMessages = messages.map(toLC);
217
186
  for await (const chunk of lcModel.stream(lcMessages, options)) {
218
187
  yield chunk;
@@ -1296,7 +1265,18 @@ function createAgentCoreNode(opts) {
1296
1265
  outputSchema: opts.outputSchema,
1297
1266
  tracing: opts.tracing
1298
1267
  };
1299
- const tools = runtime.tools ?? [];
1268
+ const rawTools = runtime.tools ?? [];
1269
+ const seenNames = /* @__PURE__ */ new Map();
1270
+ const tools = [];
1271
+ for (const t of rawTools) {
1272
+ const tName = t.name ?? t.schema?.title;
1273
+ if (tName && seenNames.has(tName)) {
1274
+ tools[seenNames.get(tName)] = t;
1275
+ } else {
1276
+ if (tName) seenNames.set(tName, tools.length);
1277
+ tools.push(t);
1278
+ }
1279
+ }
1300
1280
  const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
1301
1281
  const traceSession = state.ctx?.__traceSession;
1302
1282
  const actorName = runtime.name ?? opts.name ?? "agent";
@@ -1349,26 +1329,6 @@ function createAgentCoreNode(opts) {
1349
1329
  }
1350
1330
  return filteredMsgs;
1351
1331
  };
1352
- const debugToolPairing = (messagesToSend) => {
1353
- try {
1354
- const msgs = Array.isArray(messagesToSend) ? messagesToSend : [];
1355
- const toolUseIds = [];
1356
- const missingResults = [];
1357
- for (let i = 0; i < msgs.length; i++) {
1358
- const m = msgs[i];
1359
- if (m?.role === "assistant") {
1360
- for (const tu of extractToolUses(m)) {
1361
- toolUseIds.push(tu.id);
1362
- const next = msgs[i + 1];
1363
- if (!(next && next.role === "tool" && (next.tool_call_id === tu.id || next.tool_call_id === m?.tool_call_id))) {
1364
- missingResults.push({ id: tu.id, atIndex: i, tool: tu.name });
1365
- }
1366
- }
1367
- }
1368
- }
1369
- } catch (e) {
1370
- }
1371
- };
1372
1332
  const onEvent = state.ctx?.__onEvent;
1373
1333
  const onStream = state.ctx?.__onStream;
1374
1334
  const streamingEnabled = Boolean(state.ctx?.__streaming);
@@ -1387,8 +1347,124 @@ function createAgentCoreNode(opts) {
1387
1347
  };
1388
1348
  let response;
1389
1349
  try {
1390
- const normalizedMessages = normalizeBedrockToolPairing([...state.messages]);
1391
- debugToolPairing(normalizedMessages);
1350
+ const ensureUniqueToolCallIds = (input) => {
1351
+ const msgs = Array.isArray(input) ? input.map((m) => ({ ...m })) : [];
1352
+ const usedIds = /* @__PURE__ */ new Set();
1353
+ let counter = 0;
1354
+ for (let i = 0; i < msgs.length; i++) {
1355
+ const m = msgs[i];
1356
+ if (m?.role !== "assistant") continue;
1357
+ const hasToolCalls = Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
1358
+ const contentArr = Array.isArray(m.content) ? m.content : [];
1359
+ const hasContentToolUse = contentArr.some((c) => c?.type === "tool_use" && c?.id);
1360
+ const hasAdditionalToolCalls = Array.isArray(m.additional_kwargs?.tool_calls) && m.additional_kwargs.tool_calls.length > 0;
1361
+ if (!hasToolCalls && !hasContentToolUse && !hasAdditionalToolCalls) continue;
1362
+ if (hasToolCalls && hasContentToolUse) {
1363
+ const toolCallIds = new Set(
1364
+ m.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1365
+ );
1366
+ const filtered = contentArr.filter((c) => {
1367
+ if (c?.type === "tool_use" && c?.id && toolCallIds.has(c.id)) return false;
1368
+ return true;
1369
+ });
1370
+ if (filtered.length === 0) {
1371
+ m.content = "";
1372
+ } else if (filtered.length !== contentArr.length) {
1373
+ m.content = filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1374
+ }
1375
+ }
1376
+ if (hasAdditionalToolCalls && Array.isArray(m.content)) {
1377
+ const akIds = new Set(
1378
+ m.additional_kwargs.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1379
+ );
1380
+ const arr = m.content;
1381
+ const filtered = arr.filter((c) => {
1382
+ if (c?.type === "tool_use" && c?.id && akIds.has(c.id)) return false;
1383
+ return true;
1384
+ });
1385
+ if (filtered.length !== arr.length) {
1386
+ m.content = filtered.length === 0 ? "" : filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1387
+ }
1388
+ }
1389
+ if (hasToolCalls) {
1390
+ m.tool_calls = m.tool_calls.map((tc) => ({ ...tc }));
1391
+ }
1392
+ if (hasAdditionalToolCalls) {
1393
+ m.additional_kwargs = { ...m.additional_kwargs };
1394
+ m.additional_kwargs.tool_calls = m.additional_kwargs.tool_calls.map((tc) => ({ ...tc }));
1395
+ }
1396
+ const remainingContentArr = Array.isArray(m.content) ? m.content : [];
1397
+ const hasRemainingContentToolUse = remainingContentArr.some((c) => c?.type === "tool_use" && c?.id);
1398
+ if (hasRemainingContentToolUse) {
1399
+ m.content = remainingContentArr.map(
1400
+ (c) => c && typeof c === "object" ? { ...c } : c
1401
+ );
1402
+ }
1403
+ const idsInMessage = /* @__PURE__ */ new Set();
1404
+ if (hasToolCalls) {
1405
+ for (const tc of m.tool_calls) {
1406
+ if (tc?.id) idsInMessage.add(tc.id);
1407
+ }
1408
+ }
1409
+ if (hasRemainingContentToolUse) {
1410
+ for (const c of m.content) {
1411
+ if (c?.type === "tool_use" && c?.id) idsInMessage.add(c.id);
1412
+ }
1413
+ }
1414
+ if (hasAdditionalToolCalls) {
1415
+ for (const tc of m.additional_kwargs.tool_calls) {
1416
+ if (tc?.id) idsInMessage.add(tc.id);
1417
+ }
1418
+ }
1419
+ for (const id of idsInMessage) {
1420
+ if (!usedIds.has(id)) {
1421
+ usedIds.add(id);
1422
+ continue;
1423
+ }
1424
+ const newId = `${id}_${Date.now()}_${counter++}`;
1425
+ if (hasToolCalls) {
1426
+ for (const tc of m.tool_calls) {
1427
+ if (tc.id === id) tc.id = newId;
1428
+ }
1429
+ }
1430
+ if (hasRemainingContentToolUse && Array.isArray(m.content)) {
1431
+ for (const c of m.content) {
1432
+ if (c?.type === "tool_use" && c.id === id) c.id = newId;
1433
+ }
1434
+ }
1435
+ if (hasAdditionalToolCalls) {
1436
+ for (const tc of m.additional_kwargs.tool_calls) {
1437
+ if (tc.id === id) tc.id = newId;
1438
+ }
1439
+ }
1440
+ for (let j = i + 1; j < msgs.length; j++) {
1441
+ const next = msgs[j];
1442
+ if (next?.role === "assistant") break;
1443
+ if (next?.role === "tool" && next.tool_call_id === id) {
1444
+ next.tool_call_id = newId;
1445
+ }
1446
+ if (Array.isArray(next?.content)) {
1447
+ let contentCloned = false;
1448
+ for (let k = 0; k < next.content.length; k++) {
1449
+ const block = next.content[k];
1450
+ if (block?.type === "tool_result" && block.tool_use_id === id) {
1451
+ if (!contentCloned) {
1452
+ next.content = next.content.map((x) => x && typeof x === "object" ? { ...x } : x);
1453
+ contentCloned = true;
1454
+ }
1455
+ next.content[k].tool_use_id = newId;
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+ usedIds.add(newId);
1461
+ }
1462
+ }
1463
+ return msgs;
1464
+ };
1465
+ const normalizedMessages = ensureUniqueToolCallIds(
1466
+ normalizeBedrockToolPairing([...state.messages])
1467
+ );
1392
1468
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1393
1469
  let streamedText = "";
1394
1470
  let streamedMessage;
@@ -2034,7 +2110,9 @@ function resolveToolApprovalState(state, resolution) {
2034
2110
  // src/utils/utilTokens.ts
2035
2111
  function countApproxTokens(text) {
2036
2112
  if (!text) return 0;
2037
- return Math.ceil(text.length / 4);
2113
+ const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2114
+ const asciiCount = text.length - nonAsciiCount;
2115
+ return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2038
2116
  }
2039
2117
 
2040
2118
  // src/agent.ts
@@ -2137,7 +2215,8 @@ function createAgent(opts) {
2137
2215
  let result = false;
2138
2216
  try {
2139
2217
  result = onStateChange(state);
2140
- } catch {
2218
+ } catch (err) {
2219
+ console.warn("[agent-sdk] onStateChange callback error:", err);
2141
2220
  result = false;
2142
2221
  }
2143
2222
  if (!result) return false;
@@ -2315,7 +2394,9 @@ function createAgent(opts) {
2315
2394
  if (toolCallsAfter.length > 0) {
2316
2395
  state = { ...state, ...await toolsNode(state) };
2317
2396
  }
2318
- } catch {
2397
+ } catch (err) {
2398
+ const errMsg = err instanceof Error ? err.message : String(err);
2399
+ emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
2319
2400
  }
2320
2401
  }
2321
2402
  }
@@ -2333,13 +2414,15 @@ function createAgent(opts) {
2333
2414
  const emit = (e) => {
2334
2415
  try {
2335
2416
  onEvent?.(e);
2336
- } catch {
2417
+ } catch (err) {
2418
+ console.warn("[agent-sdk] onEvent callback error:", err);
2337
2419
  }
2338
2420
  };
2339
2421
  const emitProgress = (progress) => {
2340
2422
  try {
2341
2423
  onProgress?.(progress);
2342
- } catch {
2424
+ } catch (err) {
2425
+ console.warn("[agent-sdk] onProgress callback error:", err);
2343
2426
  }
2344
2427
  emit({ type: "progress", ...progress });
2345
2428
  };
@@ -2849,6 +2932,12 @@ async function applyTokenLimits({
2849
2932
  let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
2850
2933
  if (total <= budget) return state;
2851
2934
  const newMessages = [...messages];
2935
+ while (total > budget && newMessages.length > 1) {
2936
+ const idx = newMessages.findIndex((m) => m.role !== "system");
2937
+ if (idx === -1) break;
2938
+ const removed = newMessages.splice(idx, 1)[0];
2939
+ total -= countApproxTokens(String(removed.content || ""));
2940
+ }
2852
2941
  return { ...state, messages: newMessages };
2853
2942
  }
2854
2943
  function createContextTools(stateRef, opts) {
@@ -2950,19 +3039,7 @@ function needsSummarization(state, opts, summarizationEnabled) {
2950
3039
  return "";
2951
3040
  }).join("\n");
2952
3041
  const tokenCount = countApproxTokens(allText);
2953
- const needsSum = tokenCount > maxTok;
2954
- if (process.env.DEBUG_SUMMARIZATION) {
2955
- const msgCount = (state.messages || []).length;
2956
- const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
2957
- const toolDetails = toolMsgs.map((m, i) => {
2958
- const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
2959
- return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
2960
- });
2961
- console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
2962
- toolDetails.forEach((d) => console.log(` ${d}`));
2963
- console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
2964
- }
2965
- return needsSum;
3042
+ return tokenCount > maxTok;
2966
3043
  } catch (e) {
2967
3044
  return false;
2968
3045
  }