@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.cjs CHANGED
@@ -142,39 +142,38 @@ function withTools(model, tools) {
142
142
  }
143
143
  function fromLangchainModel(lcModel) {
144
144
  if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
145
- const adapted = {
146
- invoke: async (messages, options) => {
147
- const normalizeContent = (content) => {
148
- if (content == null) return content;
149
- if (typeof content === "string") return content;
150
- if (Array.isArray(content)) {
151
- return content.map((part) => {
152
- if (!part || typeof part !== "object") return part;
153
- if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
154
- if (part.type === "image_url") {
155
- const img = part.image_url;
156
- if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
157
- if (img && typeof img === "object") {
158
- if ("url" in img) return { type: "image_url", image_url: img };
159
- if ("base64" in img) {
160
- const media = img.media_type || "image/jpeg";
161
- const dataUrl = `data:${media};base64,${img.base64}`;
162
- const detail = img.detail;
163
- return { type: "image_url", image_url: { url: dataUrl, detail } };
164
- }
165
- }
166
- return part;
145
+ const normalizeContent = (content) => {
146
+ if (content == null) return content;
147
+ if (typeof content === "string") return content;
148
+ if (Array.isArray(content)) {
149
+ return content.map((part) => {
150
+ if (!part || typeof part !== "object") return part;
151
+ if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
152
+ if (part.type === "image_url") {
153
+ const img = part.image_url;
154
+ if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
155
+ if (img && typeof img === "object") {
156
+ if ("url" in img) return { type: "image_url", image_url: img };
157
+ if ("base64" in img) {
158
+ const media = img.media_type || "image/jpeg";
159
+ const dataUrl = `data:${media};base64,${img.base64}`;
160
+ const detail = img.detail;
161
+ return { type: "image_url", image_url: { url: dataUrl, detail } };
167
162
  }
168
- return part;
169
- });
163
+ }
164
+ return part;
170
165
  }
171
- return content;
172
- };
173
- const toLC = (m) => {
174
- if (m._getType || m.lc_serializable) return m;
175
- const role = m.role;
176
- return { role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
177
- };
166
+ return part;
167
+ });
168
+ }
169
+ return content;
170
+ };
171
+ const toLC = (m) => {
172
+ if (m._getType || m.lc_serializable) return m;
173
+ return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
174
+ };
175
+ const adapted = {
176
+ invoke: async (messages, options) => {
178
177
  const lcMessages = messages.map(toLC);
179
178
  const response = await lcModel.invoke(lcMessages, options);
180
179
  if (response && typeof response === "object") {
@@ -192,36 +191,6 @@ function fromLangchainModel(lcModel) {
192
191
  },
193
192
  stream: async function* (messages, options) {
194
193
  if (typeof lcModel.stream !== "function") return;
195
- const normalizeContent = (content) => {
196
- if (content == null) return content;
197
- if (typeof content === "string") return content;
198
- if (Array.isArray(content)) {
199
- return content.map((part) => {
200
- if (!part || typeof part !== "object") return part;
201
- if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
202
- if (part.type === "image_url") {
203
- const img = part.image_url;
204
- if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
205
- if (img && typeof img === "object") {
206
- if ("url" in img) return { type: "image_url", image_url: img };
207
- if ("base64" in img) {
208
- const media = img.media_type || "image/jpeg";
209
- const dataUrl = `data:${media};base64,${img.base64}`;
210
- const detail = img.detail;
211
- return { type: "image_url", image_url: { url: dataUrl, detail } };
212
- }
213
- }
214
- return part;
215
- }
216
- return part;
217
- });
218
- }
219
- return content;
220
- };
221
- const toLC = (m) => {
222
- if (m._getType || m.lc_serializable) return m;
223
- return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
224
- };
225
194
  const lcMessages = messages.map(toLC);
226
195
  for await (const chunk of lcModel.stream(lcMessages, options)) {
227
196
  yield chunk;
@@ -1305,7 +1274,18 @@ function createAgentCoreNode(opts) {
1305
1274
  outputSchema: opts.outputSchema,
1306
1275
  tracing: opts.tracing
1307
1276
  };
1308
- const tools = runtime.tools ?? [];
1277
+ const rawTools = runtime.tools ?? [];
1278
+ const seenNames = /* @__PURE__ */ new Map();
1279
+ const tools = [];
1280
+ for (const t of rawTools) {
1281
+ const tName = t.name ?? t.schema?.title;
1282
+ if (tName && seenNames.has(tName)) {
1283
+ tools[seenNames.get(tName)] = t;
1284
+ } else {
1285
+ if (tName) seenNames.set(tName, tools.length);
1286
+ tools.push(t);
1287
+ }
1288
+ }
1309
1289
  const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
1310
1290
  const traceSession = state.ctx?.__traceSession;
1311
1291
  const actorName = runtime.name ?? opts.name ?? "agent";
@@ -1358,26 +1338,6 @@ function createAgentCoreNode(opts) {
1358
1338
  }
1359
1339
  return filteredMsgs;
1360
1340
  };
1361
- const debugToolPairing = (messagesToSend) => {
1362
- try {
1363
- const msgs = Array.isArray(messagesToSend) ? messagesToSend : [];
1364
- const toolUseIds = [];
1365
- const missingResults = [];
1366
- for (let i = 0; i < msgs.length; i++) {
1367
- const m = msgs[i];
1368
- if (m?.role === "assistant") {
1369
- for (const tu of extractToolUses(m)) {
1370
- toolUseIds.push(tu.id);
1371
- const next = msgs[i + 1];
1372
- if (!(next && next.role === "tool" && (next.tool_call_id === tu.id || next.tool_call_id === m?.tool_call_id))) {
1373
- missingResults.push({ id: tu.id, atIndex: i, tool: tu.name });
1374
- }
1375
- }
1376
- }
1377
- }
1378
- } catch (e) {
1379
- }
1380
- };
1381
1341
  const onEvent = state.ctx?.__onEvent;
1382
1342
  const onStream = state.ctx?.__onStream;
1383
1343
  const streamingEnabled = Boolean(state.ctx?.__streaming);
@@ -1396,8 +1356,124 @@ function createAgentCoreNode(opts) {
1396
1356
  };
1397
1357
  let response;
1398
1358
  try {
1399
- const normalizedMessages = normalizeBedrockToolPairing([...state.messages]);
1400
- debugToolPairing(normalizedMessages);
1359
+ const ensureUniqueToolCallIds = (input) => {
1360
+ const msgs = Array.isArray(input) ? input.map((m) => ({ ...m })) : [];
1361
+ const usedIds = /* @__PURE__ */ new Set();
1362
+ let counter = 0;
1363
+ for (let i = 0; i < msgs.length; i++) {
1364
+ const m = msgs[i];
1365
+ if (m?.role !== "assistant") continue;
1366
+ const hasToolCalls = Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
1367
+ const contentArr = Array.isArray(m.content) ? m.content : [];
1368
+ const hasContentToolUse = contentArr.some((c) => c?.type === "tool_use" && c?.id);
1369
+ const hasAdditionalToolCalls = Array.isArray(m.additional_kwargs?.tool_calls) && m.additional_kwargs.tool_calls.length > 0;
1370
+ if (!hasToolCalls && !hasContentToolUse && !hasAdditionalToolCalls) continue;
1371
+ if (hasToolCalls && hasContentToolUse) {
1372
+ const toolCallIds = new Set(
1373
+ m.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1374
+ );
1375
+ const filtered = contentArr.filter((c) => {
1376
+ if (c?.type === "tool_use" && c?.id && toolCallIds.has(c.id)) return false;
1377
+ return true;
1378
+ });
1379
+ if (filtered.length === 0) {
1380
+ m.content = "";
1381
+ } else if (filtered.length !== contentArr.length) {
1382
+ m.content = filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1383
+ }
1384
+ }
1385
+ if (hasAdditionalToolCalls && Array.isArray(m.content)) {
1386
+ const akIds = new Set(
1387
+ m.additional_kwargs.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1388
+ );
1389
+ const arr = m.content;
1390
+ const filtered = arr.filter((c) => {
1391
+ if (c?.type === "tool_use" && c?.id && akIds.has(c.id)) return false;
1392
+ return true;
1393
+ });
1394
+ if (filtered.length !== arr.length) {
1395
+ m.content = filtered.length === 0 ? "" : filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1396
+ }
1397
+ }
1398
+ if (hasToolCalls) {
1399
+ m.tool_calls = m.tool_calls.map((tc) => ({ ...tc }));
1400
+ }
1401
+ if (hasAdditionalToolCalls) {
1402
+ m.additional_kwargs = { ...m.additional_kwargs };
1403
+ m.additional_kwargs.tool_calls = m.additional_kwargs.tool_calls.map((tc) => ({ ...tc }));
1404
+ }
1405
+ const remainingContentArr = Array.isArray(m.content) ? m.content : [];
1406
+ const hasRemainingContentToolUse = remainingContentArr.some((c) => c?.type === "tool_use" && c?.id);
1407
+ if (hasRemainingContentToolUse) {
1408
+ m.content = remainingContentArr.map(
1409
+ (c) => c && typeof c === "object" ? { ...c } : c
1410
+ );
1411
+ }
1412
+ const idsInMessage = /* @__PURE__ */ new Set();
1413
+ if (hasToolCalls) {
1414
+ for (const tc of m.tool_calls) {
1415
+ if (tc?.id) idsInMessage.add(tc.id);
1416
+ }
1417
+ }
1418
+ if (hasRemainingContentToolUse) {
1419
+ for (const c of m.content) {
1420
+ if (c?.type === "tool_use" && c?.id) idsInMessage.add(c.id);
1421
+ }
1422
+ }
1423
+ if (hasAdditionalToolCalls) {
1424
+ for (const tc of m.additional_kwargs.tool_calls) {
1425
+ if (tc?.id) idsInMessage.add(tc.id);
1426
+ }
1427
+ }
1428
+ for (const id of idsInMessage) {
1429
+ if (!usedIds.has(id)) {
1430
+ usedIds.add(id);
1431
+ continue;
1432
+ }
1433
+ const newId = `${id}_${Date.now()}_${counter++}`;
1434
+ if (hasToolCalls) {
1435
+ for (const tc of m.tool_calls) {
1436
+ if (tc.id === id) tc.id = newId;
1437
+ }
1438
+ }
1439
+ if (hasRemainingContentToolUse && Array.isArray(m.content)) {
1440
+ for (const c of m.content) {
1441
+ if (c?.type === "tool_use" && c.id === id) c.id = newId;
1442
+ }
1443
+ }
1444
+ if (hasAdditionalToolCalls) {
1445
+ for (const tc of m.additional_kwargs.tool_calls) {
1446
+ if (tc.id === id) tc.id = newId;
1447
+ }
1448
+ }
1449
+ for (let j = i + 1; j < msgs.length; j++) {
1450
+ const next = msgs[j];
1451
+ if (next?.role === "assistant") break;
1452
+ if (next?.role === "tool" && next.tool_call_id === id) {
1453
+ next.tool_call_id = newId;
1454
+ }
1455
+ if (Array.isArray(next?.content)) {
1456
+ let contentCloned = false;
1457
+ for (let k = 0; k < next.content.length; k++) {
1458
+ const block = next.content[k];
1459
+ if (block?.type === "tool_result" && block.tool_use_id === id) {
1460
+ if (!contentCloned) {
1461
+ next.content = next.content.map((x) => x && typeof x === "object" ? { ...x } : x);
1462
+ contentCloned = true;
1463
+ }
1464
+ next.content[k].tool_use_id = newId;
1465
+ }
1466
+ }
1467
+ }
1468
+ }
1469
+ usedIds.add(newId);
1470
+ }
1471
+ }
1472
+ return msgs;
1473
+ };
1474
+ const normalizedMessages = ensureUniqueToolCallIds(
1475
+ normalizeBedrockToolPairing([...state.messages])
1476
+ );
1401
1477
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1402
1478
  let streamedText = "";
1403
1479
  let streamedMessage;
@@ -2043,7 +2119,9 @@ function resolveToolApprovalState(state, resolution) {
2043
2119
  // src/utils/utilTokens.ts
2044
2120
  function countApproxTokens(text) {
2045
2121
  if (!text) return 0;
2046
- return Math.ceil(text.length / 4);
2122
+ const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2123
+ const asciiCount = text.length - nonAsciiCount;
2124
+ return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2047
2125
  }
2048
2126
 
2049
2127
  // src/agent.ts
@@ -2146,7 +2224,8 @@ function createAgent(opts) {
2146
2224
  let result = false;
2147
2225
  try {
2148
2226
  result = onStateChange(state);
2149
- } catch {
2227
+ } catch (err) {
2228
+ console.warn("[agent-sdk] onStateChange callback error:", err);
2150
2229
  result = false;
2151
2230
  }
2152
2231
  if (!result) return false;
@@ -2324,7 +2403,9 @@ function createAgent(opts) {
2324
2403
  if (toolCallsAfter.length > 0) {
2325
2404
  state = { ...state, ...await toolsNode(state) };
2326
2405
  }
2327
- } catch {
2406
+ } catch (err) {
2407
+ const errMsg = err instanceof Error ? err.message : String(err);
2408
+ emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
2328
2409
  }
2329
2410
  }
2330
2411
  }
@@ -2342,13 +2423,15 @@ function createAgent(opts) {
2342
2423
  const emit = (e) => {
2343
2424
  try {
2344
2425
  onEvent?.(e);
2345
- } catch {
2426
+ } catch (err) {
2427
+ console.warn("[agent-sdk] onEvent callback error:", err);
2346
2428
  }
2347
2429
  };
2348
2430
  const emitProgress = (progress) => {
2349
2431
  try {
2350
2432
  onProgress?.(progress);
2351
- } catch {
2433
+ } catch (err) {
2434
+ console.warn("[agent-sdk] onProgress callback error:", err);
2352
2435
  }
2353
2436
  emit({ type: "progress", ...progress });
2354
2437
  };
@@ -2858,6 +2941,12 @@ async function applyTokenLimits({
2858
2941
  let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
2859
2942
  if (total <= budget) return state;
2860
2943
  const newMessages = [...messages];
2944
+ while (total > budget && newMessages.length > 1) {
2945
+ const idx = newMessages.findIndex((m) => m.role !== "system");
2946
+ if (idx === -1) break;
2947
+ const removed = newMessages.splice(idx, 1)[0];
2948
+ total -= countApproxTokens(String(removed.content || ""));
2949
+ }
2861
2950
  return { ...state, messages: newMessages };
2862
2951
  }
2863
2952
  function createContextTools(stateRef, opts) {
@@ -2959,19 +3048,7 @@ function needsSummarization(state, opts, summarizationEnabled) {
2959
3048
  return "";
2960
3049
  }).join("\n");
2961
3050
  const tokenCount = countApproxTokens(allText);
2962
- const needsSum = tokenCount > maxTok;
2963
- if (process.env.DEBUG_SUMMARIZATION) {
2964
- const msgCount = (state.messages || []).length;
2965
- const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
2966
- const toolDetails = toolMsgs.map((m, i) => {
2967
- const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
2968
- return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
2969
- });
2970
- console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
2971
- toolDetails.forEach((d) => console.log(` ${d}`));
2972
- console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
2973
- }
2974
- return needsSum;
3051
+ return tokenCount > maxTok;
2975
3052
  } catch (e) {
2976
3053
  return false;
2977
3054
  }