@agentforge-io/core 2.3.0 → 2.3.1

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.
@@ -379,6 +379,11 @@ class AgentService {
379
379
  const filter = params.overrides?.extraToolsFilter;
380
380
  const fromConnectors = filter && resolvedExtras ? filter(resolvedExtras) : resolvedExtras;
381
381
  const extraTools = mergeExtraTools(params.overrides?.extraTools, fromConnectors);
382
+ // Hoisted accumulators so the post-loop persistence (after the
383
+ // try) can see the final list. Defined here, populated inside
384
+ // the for-await loop below.
385
+ const toolCallStartByUseId = new Map();
386
+ const accumulatedToolCalls = [];
382
387
  try {
383
388
  // Team orchestrators route through OrchestratorService.stream()
384
389
  // so the synthetic `delegate_to_*` tools the orchestrator was
@@ -403,11 +408,56 @@ class AgentService {
403
408
  messageId: 'streaming',
404
409
  agent: { timezone: agent.timezone },
405
410
  }, { ...(params.overrides ?? {}), extraTools });
411
+ // Accumulate tool_use / tool_result chunks during streaming so
412
+ // we can persist them on the assistant message row (line ~700).
413
+ // Without this, `getHistory` returns the assistant's text but
414
+ // loses the tool calls — which means clients that render
415
+ // proposal cards (Recording Assist) or generic tool rows from
416
+ // history have nothing to rehydrate. FIFO matching mirrors the
417
+ // runner's heuristic so the shape stays consistent whether the
418
+ // turn streamed or used `run()`.
406
419
  for await (const chunk of stream) {
407
420
  if (chunk.type === 'text_delta')
408
421
  fullContent += chunk.delta;
409
422
  if (chunk.type === 'usage')
410
423
  finalUsage = chunk.usage;
424
+ if (chunk.type === 'tool_use_start') {
425
+ toolCallStartByUseId.set(chunk.toolUseId, {
426
+ name: chunk.toolName,
427
+ start: Date.now(),
428
+ });
429
+ }
430
+ if (chunk.type === 'tool_result') {
431
+ // Match by toolUseId when present; otherwise FIFO on toolName
432
+ // (matches AgentRunner.run's heuristic). Older runners that
433
+ // don't emit `toolUseId` on result chunks still produce a
434
+ // usable record.
435
+ let entry = null;
436
+ const useId = chunk.toolUseId;
437
+ if (useId && toolCallStartByUseId.has(useId)) {
438
+ const v = toolCallStartByUseId.get(useId);
439
+ entry = { id: useId, name: v.name, start: v.start };
440
+ toolCallStartByUseId.delete(useId);
441
+ }
442
+ else {
443
+ const fifo = Array.from(toolCallStartByUseId.entries()).find(([, v]) => v.name === chunk.toolName);
444
+ if (fifo) {
445
+ entry = { id: fifo[0], name: fifo[1].name, start: fifo[1].start };
446
+ toolCallStartByUseId.delete(fifo[0]);
447
+ }
448
+ }
449
+ if (entry) {
450
+ accumulatedToolCalls.push({
451
+ toolName: entry.name,
452
+ toolUseId: entry.id,
453
+ input: {},
454
+ output: typeof chunk.result === 'string'
455
+ ? chunk.result
456
+ : JSON.stringify(chunk.result),
457
+ durationMs: Date.now() - entry.start,
458
+ });
459
+ }
460
+ }
411
461
  yield chunk;
412
462
  }
413
463
  }
@@ -507,6 +557,12 @@ class AgentService {
507
557
  role: 'assistant',
508
558
  content: fullContent,
509
559
  usage: finalUsage,
560
+ // Persist the accumulated tool calls so `getHistory` can
561
+ // surface them on reload — without this, proposal cards
562
+ // (Recording Assist) and other tool-result-driven UI
563
+ // disappear after refresh. Empty arrays drop to undefined
564
+ // so we don't pollute the column with `[]`.
565
+ toolCalls: accumulatedToolCalls.length > 0 ? accumulatedToolCalls : undefined,
510
566
  });
511
567
  const now = new Date();
512
568
  await this.dispatchUsage({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/core",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Framework-free AI runtime SDK. Owns: agent loop (pluggable LLM provider — Anthropic by default, LangChain-backed providers as drop-ins), conversations, tools, streaming, agent-job queue, SdkHooks. Identity, billing, infra (email/uploads/secrets) live in the host's modules — not here.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",