@cognipeer/agent-sdk 0.2.5 → 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);
@@ -1399,25 +1359,54 @@ function createAgentCoreNode(opts) {
1399
1359
  const hasContentToolUse = contentArr.some((c) => c?.type === "tool_use" && c?.id);
1400
1360
  const hasAdditionalToolCalls = Array.isArray(m.additional_kwargs?.tool_calls) && m.additional_kwargs.tool_calls.length > 0;
1401
1361
  if (!hasToolCalls && !hasContentToolUse && !hasAdditionalToolCalls) continue;
1402
- if (hasToolCalls) {
1403
- m.tool_calls = m.tool_calls.map((tc) => ({ ...tc }));
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
+ }
1404
1375
  }
1405
- if (hasContentToolUse) {
1406
- m.content = contentArr.map(
1407
- (c) => c && typeof c === "object" ? { ...c } : c
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)
1408
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 }));
1409
1391
  }
1410
1392
  if (hasAdditionalToolCalls) {
1411
1393
  m.additional_kwargs = { ...m.additional_kwargs };
1412
1394
  m.additional_kwargs.tool_calls = m.additional_kwargs.tool_calls.map((tc) => ({ ...tc }));
1413
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
+ }
1414
1403
  const idsInMessage = /* @__PURE__ */ new Set();
1415
1404
  if (hasToolCalls) {
1416
1405
  for (const tc of m.tool_calls) {
1417
1406
  if (tc?.id) idsInMessage.add(tc.id);
1418
1407
  }
1419
1408
  }
1420
- if (hasContentToolUse) {
1409
+ if (hasRemainingContentToolUse) {
1421
1410
  for (const c of m.content) {
1422
1411
  if (c?.type === "tool_use" && c?.id) idsInMessage.add(c.id);
1423
1412
  }
@@ -1438,7 +1427,7 @@ function createAgentCoreNode(opts) {
1438
1427
  if (tc.id === id) tc.id = newId;
1439
1428
  }
1440
1429
  }
1441
- if (hasContentToolUse && Array.isArray(m.content)) {
1430
+ if (hasRemainingContentToolUse && Array.isArray(m.content)) {
1442
1431
  for (const c of m.content) {
1443
1432
  if (c?.type === "tool_use" && c.id === id) c.id = newId;
1444
1433
  }
@@ -1476,7 +1465,6 @@ function createAgentCoreNode(opts) {
1476
1465
  const normalizedMessages = ensureUniqueToolCallIds(
1477
1466
  normalizeBedrockToolPairing([...state.messages])
1478
1467
  );
1479
- debugToolPairing(normalizedMessages);
1480
1468
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1481
1469
  let streamedText = "";
1482
1470
  let streamedMessage;
@@ -2122,7 +2110,9 @@ function resolveToolApprovalState(state, resolution) {
2122
2110
  // src/utils/utilTokens.ts
2123
2111
  function countApproxTokens(text) {
2124
2112
  if (!text) return 0;
2125
- 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);
2126
2116
  }
2127
2117
 
2128
2118
  // src/agent.ts
@@ -2225,7 +2215,8 @@ function createAgent(opts) {
2225
2215
  let result = false;
2226
2216
  try {
2227
2217
  result = onStateChange(state);
2228
- } catch {
2218
+ } catch (err) {
2219
+ console.warn("[agent-sdk] onStateChange callback error:", err);
2229
2220
  result = false;
2230
2221
  }
2231
2222
  if (!result) return false;
@@ -2403,7 +2394,9 @@ function createAgent(opts) {
2403
2394
  if (toolCallsAfter.length > 0) {
2404
2395
  state = { ...state, ...await toolsNode(state) };
2405
2396
  }
2406
- } 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}` });
2407
2400
  }
2408
2401
  }
2409
2402
  }
@@ -2421,13 +2414,15 @@ function createAgent(opts) {
2421
2414
  const emit = (e) => {
2422
2415
  try {
2423
2416
  onEvent?.(e);
2424
- } catch {
2417
+ } catch (err) {
2418
+ console.warn("[agent-sdk] onEvent callback error:", err);
2425
2419
  }
2426
2420
  };
2427
2421
  const emitProgress = (progress) => {
2428
2422
  try {
2429
2423
  onProgress?.(progress);
2430
- } catch {
2424
+ } catch (err) {
2425
+ console.warn("[agent-sdk] onProgress callback error:", err);
2431
2426
  }
2432
2427
  emit({ type: "progress", ...progress });
2433
2428
  };
@@ -2937,6 +2932,12 @@ async function applyTokenLimits({
2937
2932
  let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
2938
2933
  if (total <= budget) return state;
2939
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
+ }
2940
2941
  return { ...state, messages: newMessages };
2941
2942
  }
2942
2943
  function createContextTools(stateRef, opts) {
@@ -3038,19 +3039,7 @@ function needsSummarization(state, opts, summarizationEnabled) {
3038
3039
  return "";
3039
3040
  }).join("\n");
3040
3041
  const tokenCount = countApproxTokens(allText);
3041
- const needsSum = tokenCount > maxTok;
3042
- if (process.env.DEBUG_SUMMARIZATION) {
3043
- const msgCount = (state.messages || []).length;
3044
- const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
3045
- const toolDetails = toolMsgs.map((m, i) => {
3046
- const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
3047
- return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
3048
- });
3049
- console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
3050
- toolDetails.forEach((d) => console.log(` ${d}`));
3051
- console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
3052
- }
3053
- return needsSum;
3042
+ return tokenCount > maxTok;
3054
3043
  } catch (e) {
3055
3044
  return false;
3056
3045
  }