@cognipeer/agent-sdk 0.2.5 → 0.2.7

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.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,38 +182,10 @@ 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
- for await (const chunk of lcModel.stream(lcMessages, options)) {
186
+ const streamResult = lcModel.stream(lcMessages, options);
187
+ const stream = typeof streamResult?.then === "function" ? await streamResult : streamResult;
188
+ for await (const chunk of stream) {
218
189
  yield chunk;
219
190
  }
220
191
  },
@@ -336,17 +307,17 @@ function normalizeUsage(raw) {
336
307
  raw.totalTokens,
337
308
  (typeof prompt === "number" ? prompt : 0) + (typeof completion === "number" ? completion : 0)
338
309
  );
339
- const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || {};
340
- const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || {};
310
+ const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || {};
311
+ const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || {};
341
312
  const normalized = {
342
313
  prompt_tokens: safe(prompt),
343
314
  completion_tokens: safe(completion),
344
315
  total_tokens: safe(total || (prompt || 0) + (completion || 0)),
345
316
  prompt_tokens_details: {
346
317
  cached_tokens: safe(
347
- promptDetailsSrc.cached_tokens || promptDetailsSrc.cached || raw.cached_input_tokens || raw.cached_prompt_tokens
318
+ promptDetailsSrc.cached_tokens || promptDetailsSrc.cached || promptDetailsSrc.cache_read || raw.cached_input_tokens || raw.cached_prompt_tokens || raw.cache_read_input_tokens
348
319
  ),
349
- audio_tokens: safe(promptDetailsSrc.audio_tokens)
320
+ audio_tokens: safe(promptDetailsSrc.audio_tokens || promptDetailsSrc.audio)
350
321
  },
351
322
  completion_tokens_details: {
352
323
  reasoning_tokens: safe(completionDetailsSrc.reasoning_tokens),
@@ -1032,8 +1003,12 @@ function recordTraceEvent(session, event) {
1032
1003
  session.summary.eventCounts[event.type] = (session.summary.eventCounts[event.type] || 0) + 1;
1033
1004
  let sections;
1034
1005
  if (session.resolvedConfig.logData) {
1035
- const converted = convertMessageListToSections(event.messageList);
1036
- sections = ensureUniqueSections(id, converted);
1006
+ if (event.sections && event.sections.length > 0) {
1007
+ sections = ensureUniqueSections(id, event.sections);
1008
+ } else {
1009
+ const converted = convertMessageListToSections(event.messageList);
1010
+ sections = ensureUniqueSections(id, converted);
1011
+ }
1037
1012
  }
1038
1013
  const resolvedModel = event.model ?? inferModelFromMessages(event.messageList);
1039
1014
  const resolvedProvider = event.provider ?? inferProviderFromMessages(event.messageList);
@@ -1296,7 +1271,18 @@ function createAgentCoreNode(opts) {
1296
1271
  outputSchema: opts.outputSchema,
1297
1272
  tracing: opts.tracing
1298
1273
  };
1299
- const tools = runtime.tools ?? [];
1274
+ const rawTools = runtime.tools ?? [];
1275
+ const seenNames = /* @__PURE__ */ new Map();
1276
+ const tools = [];
1277
+ for (const t of rawTools) {
1278
+ const tName = t.name ?? t.schema?.title;
1279
+ if (tName && seenNames.has(tName)) {
1280
+ tools[seenNames.get(tName)] = t;
1281
+ } else {
1282
+ if (tName) seenNames.set(tName, tools.length);
1283
+ tools.push(t);
1284
+ }
1285
+ }
1300
1286
  const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
1301
1287
  const traceSession = state.ctx?.__traceSession;
1302
1288
  const actorName = runtime.name ?? opts.name ?? "agent";
@@ -1349,26 +1335,6 @@ function createAgentCoreNode(opts) {
1349
1335
  }
1350
1336
  return filteredMsgs;
1351
1337
  };
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
1338
  const onEvent = state.ctx?.__onEvent;
1373
1339
  const onStream = state.ctx?.__onStream;
1374
1340
  const streamingEnabled = Boolean(state.ctx?.__streaming);
@@ -1399,25 +1365,54 @@ function createAgentCoreNode(opts) {
1399
1365
  const hasContentToolUse = contentArr.some((c) => c?.type === "tool_use" && c?.id);
1400
1366
  const hasAdditionalToolCalls = Array.isArray(m.additional_kwargs?.tool_calls) && m.additional_kwargs.tool_calls.length > 0;
1401
1367
  if (!hasToolCalls && !hasContentToolUse && !hasAdditionalToolCalls) continue;
1402
- if (hasToolCalls) {
1403
- m.tool_calls = m.tool_calls.map((tc) => ({ ...tc }));
1368
+ if (hasToolCalls && hasContentToolUse) {
1369
+ const toolCallIds = new Set(
1370
+ m.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1371
+ );
1372
+ const filtered = contentArr.filter((c) => {
1373
+ if (c?.type === "tool_use" && c?.id && toolCallIds.has(c.id)) return false;
1374
+ return true;
1375
+ });
1376
+ if (filtered.length === 0) {
1377
+ m.content = "";
1378
+ } else if (filtered.length !== contentArr.length) {
1379
+ m.content = filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1380
+ }
1404
1381
  }
1405
- if (hasContentToolUse) {
1406
- m.content = contentArr.map(
1407
- (c) => c && typeof c === "object" ? { ...c } : c
1382
+ if (hasAdditionalToolCalls && Array.isArray(m.content)) {
1383
+ const akIds = new Set(
1384
+ m.additional_kwargs.tool_calls.filter((tc) => tc?.id).map((tc) => tc.id)
1408
1385
  );
1386
+ const arr = m.content;
1387
+ const filtered = arr.filter((c) => {
1388
+ if (c?.type === "tool_use" && c?.id && akIds.has(c.id)) return false;
1389
+ return true;
1390
+ });
1391
+ if (filtered.length !== arr.length) {
1392
+ m.content = filtered.length === 0 ? "" : filtered.length === 1 && filtered[0]?.type === "text" ? filtered[0].text ?? "" : filtered;
1393
+ }
1394
+ }
1395
+ if (hasToolCalls) {
1396
+ m.tool_calls = m.tool_calls.map((tc) => ({ ...tc }));
1409
1397
  }
1410
1398
  if (hasAdditionalToolCalls) {
1411
1399
  m.additional_kwargs = { ...m.additional_kwargs };
1412
1400
  m.additional_kwargs.tool_calls = m.additional_kwargs.tool_calls.map((tc) => ({ ...tc }));
1413
1401
  }
1402
+ const remainingContentArr = Array.isArray(m.content) ? m.content : [];
1403
+ const hasRemainingContentToolUse = remainingContentArr.some((c) => c?.type === "tool_use" && c?.id);
1404
+ if (hasRemainingContentToolUse) {
1405
+ m.content = remainingContentArr.map(
1406
+ (c) => c && typeof c === "object" ? { ...c } : c
1407
+ );
1408
+ }
1414
1409
  const idsInMessage = /* @__PURE__ */ new Set();
1415
1410
  if (hasToolCalls) {
1416
1411
  for (const tc of m.tool_calls) {
1417
1412
  if (tc?.id) idsInMessage.add(tc.id);
1418
1413
  }
1419
1414
  }
1420
- if (hasContentToolUse) {
1415
+ if (hasRemainingContentToolUse) {
1421
1416
  for (const c of m.content) {
1422
1417
  if (c?.type === "tool_use" && c?.id) idsInMessage.add(c.id);
1423
1418
  }
@@ -1438,7 +1433,7 @@ function createAgentCoreNode(opts) {
1438
1433
  if (tc.id === id) tc.id = newId;
1439
1434
  }
1440
1435
  }
1441
- if (hasContentToolUse && Array.isArray(m.content)) {
1436
+ if (hasRemainingContentToolUse && Array.isArray(m.content)) {
1442
1437
  for (const c of m.content) {
1443
1438
  if (c?.type === "tool_use" && c.id === id) c.id = newId;
1444
1439
  }
@@ -1476,11 +1471,12 @@ function createAgentCoreNode(opts) {
1476
1471
  const normalizedMessages = ensureUniqueToolCallIds(
1477
1472
  normalizeBedrockToolPairing([...state.messages])
1478
1473
  );
1479
- debugToolPairing(normalizedMessages);
1480
1474
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1481
1475
  let streamedText = "";
1482
1476
  let streamedMessage;
1483
- for await (const chunk of modelWithTools.stream(normalizedMessages, { signal: abortSignal, cancellationToken })) {
1477
+ const streamResult = modelWithTools.stream(normalizedMessages, { signal: abortSignal, cancellationToken });
1478
+ const stream = typeof streamResult?.then === "function" ? await streamResult : streamResult;
1479
+ for await (const chunk of stream) {
1484
1480
  if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
1485
1481
  break;
1486
1482
  }
@@ -1525,7 +1521,7 @@ function createAgentCoreNode(opts) {
1525
1521
  ...state.messages,
1526
1522
  response
1527
1523
  ];
1528
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
1524
+ const rawUsage = response?.usage_metadata || response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
1529
1525
  const normalized = normalizeUsage(rawUsage);
1530
1526
  const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
1531
1527
  const durationMs = Date.now() - start;
@@ -1616,41 +1612,6 @@ function createToolsNode(initialTools, opts) {
1616
1612
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
1617
1613
  const planned = toolCalls.slice(0, remaining);
1618
1614
  const skipped = toolCalls.slice(remaining);
1619
- for (const tc of skipped) {
1620
- onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
1621
- const sanitizedArgs = sanitizeTracePayload(tc.args);
1622
- const messageList = [
1623
- {
1624
- role: "assistant",
1625
- name: tc.name,
1626
- content: `Skipped tool due to tool-call limit: ${tc.name}`,
1627
- tool_calls: [
1628
- {
1629
- id: tc.id,
1630
- type: "function",
1631
- function: {
1632
- name: tc.name,
1633
- arguments: sanitizedArgs
1634
- }
1635
- }
1636
- ]
1637
- }
1638
- ];
1639
- recordTraceEvent(traceSession, {
1640
- type: "tool_call",
1641
- label: `Tool Skipped - ${tc.name}`,
1642
- actor: { scope: "tool", name: tc.name, role: "tool" },
1643
- status: "skipped",
1644
- toolExecutionId: tc.id,
1645
- messageList
1646
- });
1647
- appended.push({
1648
- role: "tool",
1649
- content: `Skipped tool due to tool-call limit: ${tc.name}`,
1650
- tool_call_id: tc.id || `${tc.name}_${appended.length}`,
1651
- name: tc.name
1652
- });
1653
- }
1654
1615
  const isCancelled = () => {
1655
1616
  if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
1656
1617
  if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
@@ -1891,6 +1852,42 @@ function createToolsNode(initialTools, opts) {
1891
1852
  break;
1892
1853
  }
1893
1854
  }
1855
+ for (const tc of skipped) {
1856
+ onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
1857
+ const sanitizedArgs = sanitizeTracePayload(tc.args);
1858
+ const messageList = [
1859
+ {
1860
+ role: "assistant",
1861
+ name: tc.name,
1862
+ content: `Skipped tool due to tool-call limit: ${tc.name}`,
1863
+ tool_calls: [
1864
+ {
1865
+ id: tc.id,
1866
+ type: "function",
1867
+ function: {
1868
+ name: tc.name,
1869
+ arguments: sanitizedArgs
1870
+ }
1871
+ }
1872
+ ]
1873
+ }
1874
+ ];
1875
+ recordTraceEvent(traceSession, {
1876
+ type: "tool_call",
1877
+ label: `Tool Skipped - ${tc.name}`,
1878
+ actor: { scope: "tool", name: tc.name, role: "tool" },
1879
+ status: "skipped",
1880
+ toolExecutionId: tc.id,
1881
+ messageList
1882
+ });
1883
+ appended.push({
1884
+ role: "tool",
1885
+ content: `Skipped tool due to tool-call limit: ${tc.name}`,
1886
+ tool_call_id: tc.id || `${tc.name}_${appended.length}`,
1887
+ name: tc.name
1888
+ });
1889
+ toolCount += 1;
1890
+ }
1894
1891
  if (!awaitingApproval && state.ctx?.__awaitingApproval) {
1895
1892
  const ctx = { ...state.ctx };
1896
1893
  delete ctx.__awaitingApproval;
@@ -2122,7 +2119,62 @@ function resolveToolApprovalState(state, resolution) {
2122
2119
  // src/utils/utilTokens.ts
2123
2120
  function countApproxTokens(text) {
2124
2121
  if (!text) return 0;
2125
- 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);
2125
+ }
2126
+ function extractMessageText(m) {
2127
+ if (!m) return "";
2128
+ const parts = [];
2129
+ parts.push(m.role || "");
2130
+ if (m.name) parts.push(m.name);
2131
+ if (typeof m.content === "string") {
2132
+ parts.push(m.content);
2133
+ } else if (Array.isArray(m.content)) {
2134
+ for (const c of m.content) {
2135
+ if (typeof c === "string") parts.push(c);
2136
+ else if (c?.text) parts.push(c.text);
2137
+ else if (c?.content) parts.push(c.content);
2138
+ else if (c?.type === "tool_use" && c?.input) {
2139
+ parts.push(c.name || "");
2140
+ parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
2141
+ } else if (c != null && typeof c === "object") {
2142
+ parts.push(JSON.stringify(c));
2143
+ }
2144
+ }
2145
+ } else if (m.content != null && typeof m.content === "object") {
2146
+ parts.push(JSON.stringify(m.content));
2147
+ }
2148
+ if (Array.isArray(m.tool_calls)) {
2149
+ for (const tc of m.tool_calls) {
2150
+ if (tc?.function?.name) parts.push(tc.function.name);
2151
+ if (tc?.function?.arguments) {
2152
+ parts.push(
2153
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2154
+ );
2155
+ }
2156
+ if (tc?.name && !tc?.function?.name) parts.push(tc.name);
2157
+ if (tc?.args && !tc?.function?.arguments) {
2158
+ parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
2159
+ }
2160
+ }
2161
+ }
2162
+ if (Array.isArray(m.additional_kwargs?.tool_calls)) {
2163
+ for (const tc of m.additional_kwargs.tool_calls) {
2164
+ if (tc?.function?.name) parts.push(tc.function.name);
2165
+ if (tc?.function?.arguments) {
2166
+ parts.push(
2167
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2168
+ );
2169
+ }
2170
+ }
2171
+ }
2172
+ return parts.join("\n");
2173
+ }
2174
+ function countMessagesTokens(messages) {
2175
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
2176
+ const allText = messages.map(extractMessageText).join("\n");
2177
+ return countApproxTokens(allText) + messages.length * 4;
2126
2178
  }
2127
2179
 
2128
2180
  // src/agent.ts
@@ -2225,7 +2277,8 @@ function createAgent(opts) {
2225
2277
  let result = false;
2226
2278
  try {
2227
2279
  result = onStateChange(state);
2228
- } catch {
2280
+ } catch (err) {
2281
+ console.warn("[agent-sdk] onStateChange callback error:", err);
2229
2282
  result = false;
2230
2283
  }
2231
2284
  if (!result) return false;
@@ -2292,8 +2345,7 @@ function createAgent(opts) {
2292
2345
  maxTok = 5e4;
2293
2346
  }
2294
2347
  if (maxTok) {
2295
- 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");
2296
- const tokenCount = countApproxTokens(allText);
2348
+ const tokenCount = countMessagesTokens(state.messages || []);
2297
2349
  if (tokenCount > maxTok) {
2298
2350
  const ctx = { ...state.ctx || {}, __needsSummarization: true };
2299
2351
  state = { ...state, ctx };
@@ -2403,7 +2455,9 @@ function createAgent(opts) {
2403
2455
  if (toolCallsAfter.length > 0) {
2404
2456
  state = { ...state, ...await toolsNode(state) };
2405
2457
  }
2406
- } catch {
2458
+ } catch (err) {
2459
+ const errMsg = err instanceof Error ? err.message : String(err);
2460
+ emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
2407
2461
  }
2408
2462
  }
2409
2463
  }
@@ -2421,13 +2475,15 @@ function createAgent(opts) {
2421
2475
  const emit = (e) => {
2422
2476
  try {
2423
2477
  onEvent?.(e);
2424
- } catch {
2478
+ } catch (err) {
2479
+ console.warn("[agent-sdk] onEvent callback error:", err);
2425
2480
  }
2426
2481
  };
2427
2482
  const emitProgress = (progress) => {
2428
2483
  try {
2429
2484
  onProgress?.(progress);
2430
- } catch {
2485
+ } catch (err) {
2486
+ console.warn("[agent-sdk] onProgress callback error:", err);
2431
2487
  }
2432
2488
  emit({ type: "progress", ...progress });
2433
2489
  };
@@ -2670,6 +2726,83 @@ function createAgentNode(opts) {
2670
2726
  };
2671
2727
  }
2672
2728
 
2729
+ // src/utils/traceSections.ts
2730
+ function formatMessageContent(content) {
2731
+ if (content == null) return "";
2732
+ if (typeof content === "string") return content;
2733
+ if (Array.isArray(content)) {
2734
+ return content.map((item) => {
2735
+ if (typeof item === "string") return item;
2736
+ if (item && typeof item === "object") {
2737
+ if (typeof item.text === "string") return item.text;
2738
+ if (typeof item.content === "string") return item.content;
2739
+ try {
2740
+ return JSON.stringify(item);
2741
+ } catch {
2742
+ return String(item);
2743
+ }
2744
+ }
2745
+ try {
2746
+ return JSON.stringify(item);
2747
+ } catch {
2748
+ return String(item);
2749
+ }
2750
+ }).filter(Boolean).join("\n");
2751
+ }
2752
+ if (typeof content === "object") {
2753
+ if (typeof content.text === "string") return content.text;
2754
+ if (typeof content.content === "string") return content.content;
2755
+ try {
2756
+ return JSON.stringify(content, null, 2);
2757
+ } catch {
2758
+ return String(content);
2759
+ }
2760
+ }
2761
+ return String(content);
2762
+ }
2763
+ function buildSummarizationSections(opts) {
2764
+ const sections = [];
2765
+ sections.push({
2766
+ kind: "summary",
2767
+ label: "Context Summary",
2768
+ content: opts.summaryText
2769
+ });
2770
+ const tokensSaved = opts.tokenCountBefore - opts.tokenCountAfter;
2771
+ const reductionPct = opts.tokenCountBefore > 0 ? Math.round(tokensSaved / opts.tokenCountBefore * 100) : 0;
2772
+ const metaData = {
2773
+ messagesCompressed: opts.messagesCompressed,
2774
+ tokenCountBefore: opts.tokenCountBefore,
2775
+ tokenCountAfter: opts.tokenCountAfter,
2776
+ tokensSaved,
2777
+ reductionPercent: reductionPct
2778
+ };
2779
+ if (opts.inputTokens !== void 0) metaData.inputTokens = opts.inputTokens;
2780
+ if (opts.outputTokens !== void 0) metaData.outputTokens = opts.outputTokens;
2781
+ if (opts.cachedInputTokens !== void 0) metaData.cachedInputTokens = opts.cachedInputTokens;
2782
+ if (opts.durationMs !== void 0) metaData.durationMs = opts.durationMs;
2783
+ if (opts.previousSummary) metaData.previousSummaryLength = opts.previousSummary.length;
2784
+ sections.push({
2785
+ kind: "metadata",
2786
+ label: "Summarization Stats",
2787
+ data: metaData
2788
+ });
2789
+ if (Array.isArray(opts.promptMessages) && opts.promptMessages.length > 0) {
2790
+ for (const msg of opts.promptMessages) {
2791
+ const role = typeof msg?.role === "string" ? msg.role : "system";
2792
+ const content = formatMessageContent(msg?.content ?? "");
2793
+ if (content) {
2794
+ sections.push({
2795
+ kind: "message",
2796
+ label: `Summarization Prompt (${role})`,
2797
+ role,
2798
+ content
2799
+ });
2800
+ }
2801
+ }
2802
+ }
2803
+ return sections;
2804
+ }
2805
+
2673
2806
  // src/nodes/contextSummarize.ts
2674
2807
  var systemMessage = (content) => ({ role: "system", content });
2675
2808
  var humanMessage = (content) => ({ role: "user", content });
@@ -2728,8 +2861,7 @@ function createContextSummarizeNode(opts) {
2728
2861
  if (compressableMessages.length === 0) {
2729
2862
  return {};
2730
2863
  }
2731
- const allTextBefore = 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");
2732
- const tokenCountBefore = countApproxTokens(allTextBefore);
2864
+ const tokenCountBefore = countMessagesTokens(messages);
2733
2865
  const summaryPromptMaxTokens = config.summaryPromptMaxTokens;
2734
2866
  const availableTokensForConversation = summaryPromptMaxTokens - 500;
2735
2867
  const messageTexts = [];
@@ -2792,7 +2924,7 @@ Summary:`;
2792
2924
  durationMs = Date.now() - startTime;
2793
2925
  summaryText = typeof response.content === "string" ? response.content : Array.isArray(response.content) ? response.content.map((c) => c.text || "").join("") : JSON.stringify(response.content);
2794
2926
  outputTokensEstimate = countApproxTokens(summaryText);
2795
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
2927
+ const rawUsage = response?.usage_metadata || response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
2796
2928
  const normalized = normalizeUsage(rawUsage);
2797
2929
  if (normalized) {
2798
2930
  inputTokensActual = normalized.prompt_tokens;
@@ -2824,7 +2956,16 @@ Summary:`;
2824
2956
  inputTokens: inputTokensEstimate,
2825
2957
  outputTokens: 0,
2826
2958
  error: { message: err?.message || String(err), stack: err?.stack },
2827
- messageList: sanitizeTracePayload(summaryPrompt)
2959
+ sections: buildSummarizationSections({
2960
+ summaryText: "",
2961
+ messagesCompressed: 0,
2962
+ tokenCountBefore,
2963
+ tokenCountAfter: tokenCountBefore,
2964
+ inputTokens: inputTokensEstimate,
2965
+ outputTokens: 0,
2966
+ durationMs,
2967
+ promptMessages: summaryPrompt
2968
+ })
2828
2969
  });
2829
2970
  return {};
2830
2971
  }
@@ -2878,8 +3019,7 @@ Summary:`;
2878
3019
  };
2879
3020
  const summaries = Array.isArray(state.summaries) ? [...state.summaries, summaryText] : [summaryText];
2880
3021
  const finalMessages = [...validatedMessages, assistantSummaryCall, toolSummaryResponse];
2881
- const allTextAfter = finalMessages.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");
2882
- const tokenCountAfter = countApproxTokens(allTextAfter);
3022
+ const tokenCountAfter = countMessagesTokens(finalMessages);
2883
3023
  const summarizationEvent = {
2884
3024
  type: "summarization",
2885
3025
  summary: summaryText,
@@ -2906,10 +3046,21 @@ Summary:`;
2906
3046
  outputTokens: outputTokensActual ?? outputTokensEstimate,
2907
3047
  cachedInputTokens,
2908
3048
  totalTokens: totalTokensActual,
2909
- messageList: sanitizeTracePayload([
2910
- ...summaryPrompt,
2911
- { role: "assistant", content: summaryText, name: "summarization_result" }
2912
- ]),
3049
+ sections: buildSummarizationSections({
3050
+ summaryText,
3051
+ previousSummary: previousSummary || void 0,
3052
+ messagesCompressed: compressableMessages.length,
3053
+ tokenCountBefore,
3054
+ tokenCountAfter,
3055
+ inputTokens: inputTokensActual ?? inputTokensEstimate,
3056
+ outputTokens: outputTokensActual ?? outputTokensEstimate,
3057
+ cachedInputTokens,
3058
+ durationMs,
3059
+ promptMessages: sanitizeTracePayload([
3060
+ ...summaryPrompt,
3061
+ { role: "assistant", content: summaryText, name: "summarization_result" }
3062
+ ])
3063
+ }),
2913
3064
  debug: {
2914
3065
  messagesCompressed: compressableMessages.length,
2915
3066
  tokenCountBefore,
@@ -2937,6 +3088,12 @@ async function applyTokenLimits({
2937
3088
  let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
2938
3089
  if (total <= budget) return state;
2939
3090
  const newMessages = [...messages];
3091
+ while (total > budget && newMessages.length > 1) {
3092
+ const idx = newMessages.findIndex((m) => m.role !== "system");
3093
+ if (idx === -1) break;
3094
+ const removed = newMessages.splice(idx, 1)[0];
3095
+ total -= countApproxTokens(String(removed.content || ""));
3096
+ }
2940
3097
  return { ...state, messages: newMessages };
2941
3098
  }
2942
3099
  function createContextTools(stateRef, opts) {
@@ -3027,30 +3184,8 @@ function needsSummarization(state, opts, summarizationEnabled) {
3027
3184
  maxTok = 5e4;
3028
3185
  }
3029
3186
  try {
3030
- const allText = (state.messages || []).map((m) => {
3031
- if (typeof m.content === "string") return m.content;
3032
- if (Array.isArray(m.content)) {
3033
- return m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? JSON.stringify(c)).join("");
3034
- }
3035
- if (m.content && typeof m.content === "object") {
3036
- return JSON.stringify(m.content);
3037
- }
3038
- return "";
3039
- }).join("\n");
3040
- 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;
3187
+ const tokenCount = countMessagesTokens(state.messages || []);
3188
+ return tokenCount > maxTok;
3054
3189
  } catch (e) {
3055
3190
  return false;
3056
3191
  }
@@ -3448,6 +3583,6 @@ function createCodeGuardrail(options) {
3448
3583
  });
3449
3584
  }
3450
3585
 
3451
- export { GuardrailPhase, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, evaluateGuardrails, fileSink, fromLangchainModel, fromLangchainTools, httpSink, isSmartModel, jsonSchemaRule, regexRule, resolveToolApprovalState, restoreSnapshot, startStreamingSession, withTools };
3586
+ export { GuardrailPhase, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, evaluateGuardrails, fileSink, fromLangchainModel, fromLangchainTools, httpSink, isSmartModel, jsonSchemaRule, regexRule, resolveToolApprovalState, restoreSnapshot, startStreamingSession, withTools };
3452
3587
  //# sourceMappingURL=index.mjs.map
3453
3588
  //# sourceMappingURL=index.mjs.map