@cognipeer/agent-sdk 0.16.0 → 0.17.0

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
@@ -2213,7 +2213,7 @@ function recordTraceEvent(session, event) {
2213
2213
  const baseLabel = event.label?.trim().length ? event.label.trim() : defaultEventLabel(event.type);
2214
2214
  const eventLabel = `${baseLabel} #${sequence}`;
2215
2215
  const durationMs = event.durationMs !== void 0 ? Number(event.durationMs) : void 0;
2216
- if (!Number.isNaN(durationMs ?? NaN) && durationMs !== void 0) {
2216
+ if (!Number.isNaN(durationMs ?? NaN) && durationMs !== void 0 && event.countDuration !== false && !isSubagentTraceScope(session)) {
2217
2217
  session.summary.totalDurationMs += durationMs;
2218
2218
  }
2219
2219
  const inputTokens = event.inputTokens !== void 0 ? Number(event.inputTokens) : void 0;
@@ -2262,10 +2262,13 @@ function recordTraceEvent(session, event) {
2262
2262
  }
2263
2263
  }
2264
2264
  }
2265
+ if (event.alwaysSections?.length) {
2266
+ sections = ensureUniqueSections(id, [...event.alwaysSections, ...sections ?? []]);
2267
+ }
2265
2268
  const resolvedModel = event.model ?? inferModelFromMessages(event.messageList);
2266
2269
  const resolvedProvider = event.provider ?? inferProviderFromMessages(event.messageList);
2267
2270
  const isAiCall = event.type === "ai_call";
2268
- const spanId = generateSpanId();
2271
+ const spanId = event.spanId || generateSpanId();
2269
2272
  const parentSpanId = event.parentSpanId || session.currentIterationSpanId || session.rootSpanId;
2270
2273
  const record = {
2271
2274
  sessionId: session.sessionId,
@@ -2359,6 +2362,58 @@ var DEFERRED_TRACE_EVENTS_KEY = "__deferredTraceEvents";
2359
2362
  function isTraceSessionOpen(session) {
2360
2363
  return Boolean(session) && !session.closed;
2361
2364
  }
2365
+ var SUBAGENT_TRACE_SCOPE = Symbol("subagentTraceScope");
2366
+ function createSubagentTraceScope(parent, info) {
2367
+ const scope = { ...info, iterations: info.iterations ?? 0 };
2368
+ let currentIterationSpanId;
2369
+ return new Proxy(parent, {
2370
+ get(target, prop) {
2371
+ if (prop === SUBAGENT_TRACE_SCOPE) return scope;
2372
+ if (prop === "rootSpanId") return scope.agentSpanId;
2373
+ if (prop === "currentIterationSpanId") return currentIterationSpanId;
2374
+ return Reflect.get(target, prop);
2375
+ },
2376
+ set(target, prop, value) {
2377
+ if (prop === "currentIterationSpanId") {
2378
+ currentIterationSpanId = value;
2379
+ return true;
2380
+ }
2381
+ return Reflect.set(target, prop, value);
2382
+ }
2383
+ });
2384
+ }
2385
+ function subagentTraceScopeOf(session) {
2386
+ if (!session) return void 0;
2387
+ return session[SUBAGENT_TRACE_SCOPE];
2388
+ }
2389
+ function canPauseForHuman(session) {
2390
+ return subagentTraceScopeOf(session)?.hitl !== false;
2391
+ }
2392
+ function isSubagentTraceScope(session) {
2393
+ return subagentTraceScopeOf(session) !== void 0;
2394
+ }
2395
+ function recordSubagentSpan(session, span) {
2396
+ const sections = [];
2397
+ if (session.resolvedConfig.logData) {
2398
+ if (span.task !== void 0) sections.push({ kind: "message", label: "Task", role: "user", content: span.task });
2399
+ if (span.result) sections.push({ kind: "message", label: "Result", role: "assistant", content: span.result });
2400
+ }
2401
+ recordTraceEvent(session, {
2402
+ type: "span",
2403
+ spanId: span.spanId,
2404
+ parentSpanId: span.parentSpanId,
2405
+ label: `Sub-agent ${span.name}`,
2406
+ actor: { scope: "agent", name: span.name, role: "subagent" },
2407
+ status: span.status,
2408
+ durationMs: span.durationMs,
2409
+ // The child's own calls and the delegating tool_call already hold this time.
2410
+ countDuration: false,
2411
+ ...span.error ? { error: { message: span.error } } : {},
2412
+ // The Console reads which sub-agent ran from here, so it survives logData off.
2413
+ alwaysSections: [{ kind: "metadata", label: "Sub-agent", data: sanitizeTracePayload(span.info) }],
2414
+ sections
2415
+ });
2416
+ }
2362
2417
  function recordOrDeferTraceEvent(ctx, event, options) {
2363
2418
  const session = ctx?.__traceSession;
2364
2419
  if (isTraceSessionOpen(session)) {
@@ -4936,6 +4991,9 @@ ${append}`;
4936
4991
 
4937
4992
  // src/utils/traceDecisions.ts
4938
4993
  var TRACED_KEY = "__outcomeTraced";
4994
+ function surfacedFromSubagent(entry) {
4995
+ return Boolean(entry.metadata?.__subagent);
4996
+ }
4939
4997
  function waitedMs(from, to) {
4940
4998
  if (!from || !to) return void 0;
4941
4999
  const ms = Date.parse(to) - Date.parse(from);
@@ -4949,7 +5007,7 @@ function sameJson(a, b) {
4949
5007
  }
4950
5008
  }
4951
5009
  function recordApprovalRequested(session, entry) {
4952
- if (!session) return;
5010
+ if (!session || surfacedFromSubagent(entry) || !canPauseForHuman(session)) return;
4953
5011
  const logData = session.resolvedConfig.logData;
4954
5012
  const sections = [
4955
5013
  {
@@ -4980,7 +5038,7 @@ function recordHumanDecisions(session, state) {
4980
5038
  if (!session) return;
4981
5039
  const logData = session.resolvedConfig.logData;
4982
5040
  for (const entry of state.pendingApprovals ?? []) {
4983
- if (!entry.decidedAt || entry.metadata?.[TRACED_KEY]) continue;
5041
+ if (!entry.decidedAt || entry.metadata?.[TRACED_KEY] || surfacedFromSubagent(entry)) continue;
4984
5042
  if (entry.status !== "approved" && entry.status !== "rejected") continue;
4985
5043
  const approved = entry.status === "approved";
4986
5044
  const edited = approved && entry.approvedArgs !== void 0 && !sameJson(entry.approvedArgs, entry.args);
@@ -5019,7 +5077,7 @@ function recordHumanDecisions(session, state) {
5019
5077
  entry.metadata = { ...entry.metadata || {}, [TRACED_KEY]: true };
5020
5078
  }
5021
5079
  for (const entry of state.pendingUserQuestions ?? []) {
5022
- if (!entry.answeredAt || entry.metadata?.[TRACED_KEY]) continue;
5080
+ if (!entry.answeredAt || entry.metadata?.[TRACED_KEY] || surfacedFromSubagent(entry)) continue;
5023
5081
  if (entry.status !== "answered" && entry.status !== "cancelled") continue;
5024
5082
  const clientTool = readClientTool(entry);
5025
5083
  if (clientTool) {
@@ -5065,7 +5123,7 @@ function recordHumanDecisions(session, state) {
5065
5123
  }
5066
5124
  }
5067
5125
  function recordClientToolRequested(session, entry) {
5068
- if (!session) return;
5126
+ if (!session || surfacedFromSubagent(entry) || !canPauseForHuman(session)) return;
5069
5127
  const clientTool = readClientTool(entry);
5070
5128
  if (!clientTool) return;
5071
5129
  const sections = [
@@ -5721,6 +5779,7 @@ function createToolsNode(initialTools, opts) {
5721
5779
  toolCount += 1;
5722
5780
  return { status: "error" };
5723
5781
  }
5782
+ let traceSpanId;
5724
5783
  try {
5725
5784
  if (cachedHit !== void 0) {
5726
5785
  const executionId2 = nanoid.nanoid();
@@ -5800,7 +5859,8 @@ function createToolsNode(initialTools, opts) {
5800
5859
  onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
5801
5860
  onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
5802
5861
  const anyTool = tool;
5803
- const callOptions = { cancellationToken, signal: abortSignal, toolCallId };
5862
+ traceSpanId = traceSession ? generateSpanId() : void 0;
5863
+ const callOptions = { cancellationToken, signal: abortSignal, toolCallId, ...traceSpanId ? { traceSpanId } : {} };
5804
5864
  const invokeOnce = async () => {
5805
5865
  if (typeof anyTool.func === "function") return anyTool.func(args, callOptions);
5806
5866
  if (typeof anyTool.invoke === "function") return anyTool.invoke(args, callOptions);
@@ -5983,6 +6043,7 @@ function createToolsNode(initialTools, opts) {
5983
6043
  const outputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedOutput) : void 0;
5984
6044
  recordTraceEvent(traceSession, {
5985
6045
  type: "tool_call",
6046
+ spanId: traceSpanId,
5986
6047
  label: `Tool Execution - ${toolName}`,
5987
6048
  actor: { scope: "tool", name: toolName, role: "tool" },
5988
6049
  ...reportedFailure ? { status: "error", error: { message: reportedFailure } } : {},
@@ -6045,6 +6106,7 @@ function createToolsNode(initialTools, opts) {
6045
6106
  const errorOutput = { message, stack: host?.has("postToolUse") ? void 0 : error?.stack };
6046
6107
  recordTraceEvent(traceSession, {
6047
6108
  type: "tool_call",
6109
+ spanId: traceSpanId,
6048
6110
  label: `Tool Error - ${toolName}`,
6049
6111
  actor: { scope: "tool", name: toolName, role: "tool" },
6050
6112
  status: "error",
@@ -7005,7 +7067,7 @@ function createAskUserQuestionTool(stateRef, config) {
7005
7067
  questions: entry.questions,
7006
7068
  allowFreeText
7007
7069
  });
7008
- recordTraceEvent(ctx.__traceSession, {
7070
+ if (canPauseForHuman(ctx.__traceSession)) recordTraceEvent(ctx.__traceSession, {
7009
7071
  type: "user_question",
7010
7072
  label: "Question to user",
7011
7073
  timestamp: entry.requestedAt,
@@ -8288,19 +8350,22 @@ function createAgent(opts) {
8288
8350
  detail: { reason: budget.reason, iteration: iterations }
8289
8351
  });
8290
8352
  }
8353
+ recordHumanDecisions(state.ctx?.__traceSession, state);
8291
8354
  const ctx = { ...state.ctx || {}, __limitBreached: budget.reason };
8292
8355
  state = { ...state, ctx };
8293
8356
  break;
8294
8357
  }
8295
8358
  const traceSession = state.ctx?.__traceSession;
8296
8359
  if (traceSession) {
8297
- const iterationNumber = (traceSession.summary.eventCounts.agent_iteration ?? 0) + 1;
8360
+ const subagentScope = subagentTraceScopeOf(traceSession);
8361
+ const iterationNumber = subagentScope ? ++subagentScope.iterations : (traceSession.summary.eventCounts.agent_iteration ?? 0) - (traceSession.summary.subagentIterations ?? 0) + 1;
8298
8362
  const iterEvent = recordTraceEvent(traceSession, {
8299
8363
  type: "agent_iteration",
8300
8364
  label: `Iteration ${iterationNumber}`,
8301
8365
  actor: { scope: "agent", name: opts.name || "agent", role: "orchestrator" },
8302
8366
  parentSpanId: traceSession.rootSpanId
8303
8367
  });
8368
+ if (subagentScope) traceSession.summary.subagentIterations = (traceSession.summary.subagentIterations ?? 0) + 1;
8304
8369
  if (iterEvent) {
8305
8370
  traceSession.currentIterationSpanId = iterEvent.spanId;
8306
8371
  }
@@ -8610,7 +8675,9 @@ function createAgent(opts) {
8610
8675
  const inheritedSession = config?.__pluginSession;
8611
8676
  ctx.__runId = inheritedSession?.runId ?? traceSession?.sessionId ?? `run_${nanoid.nanoid(10)}`;
8612
8677
  const stateHolder = inheritedSession?.stateHolder ?? { value: void 0 };
8613
- const traceRecorder = createPluginTraceRecorder(() => traceSession);
8678
+ const traceRecorder = createPluginTraceRecorder(
8679
+ () => traceSession ?? (subagentTraceScopeOf(ctx.__traceSession) ? ctx.__traceSession : void 0)
8680
+ );
8614
8681
  const runHost = inheritedSession?.host ?? (pluginHost.hasAny() ? pluginHost.beginRun({
8615
8682
  runId: ctx.__runId,
8616
8683
  agentName: opts.name,
@@ -8853,7 +8920,7 @@ function createAgent(opts) {
8853
8920
  name: toolName,
8854
8921
  description: description || `Delegate task to agent ${opts.name || "Agent"}`,
8855
8922
  schema,
8856
- func: async ({ input }) => {
8923
+ func: async ({ input }, context) => {
8857
8924
  const ref = delegatedTool._stateRef;
8858
8925
  const parentCtx = ref.ctx || {};
8859
8926
  const parentMessages = Array.isArray(ref.messages) ? ref.messages : [];
@@ -8886,6 +8953,33 @@ function createAgent(opts) {
8886
8953
  input
8887
8954
  );
8888
8955
  const childCtx = { __delegationDepth: currentDepth + 1 };
8956
+ const parentSession = parentCtx.__traceSession;
8957
+ const traced = isTraceSessionOpen(parentSession);
8958
+ const agentSpanId = generateSpanId();
8959
+ const parentIterationSpanId = traced ? parentSession.currentIterationSpanId : void 0;
8960
+ if (traced && !withDefaults(opts.tracing).enabled) {
8961
+ childCtx.__traceSession = createSubagentTraceScope(parentSession, {
8962
+ agentSpanId,
8963
+ name: opts.name || "agent",
8964
+ mode: "asTool",
8965
+ hitl: false
8966
+ });
8967
+ }
8968
+ const startedAt = Date.now();
8969
+ const recordSpan = (status, extra) => {
8970
+ if (!traced || !isTraceSessionOpen(parentSession)) return;
8971
+ recordSubagentSpan(parentSession, {
8972
+ spanId: agentSpanId,
8973
+ parentSpanId: context?.traceSpanId ?? parentIterationSpanId,
8974
+ name: opts.name || "agent",
8975
+ status,
8976
+ durationMs: Date.now() - startedAt,
8977
+ error: extra.error,
8978
+ info: { name: opts.name || "agent", mode: "asTool", depth: currentDepth + 1, parentToolCallId: context?.toolCallId, status: status === "success" ? "ok" : "error" },
8979
+ task: input,
8980
+ result: extra.result
8981
+ });
8982
+ };
8889
8983
  const childConfig = {
8890
8984
  onEvent: parentCtx.__onEvent ? (e) => {
8891
8985
  if (e?.type === "finalAnswer" || e?.type === "metadata") return;
@@ -8901,7 +8995,14 @@ function createAgent(opts) {
8901
8995
  stream: Boolean(parentCtx.__streaming),
8902
8996
  cancellationToken: parentCtx.__cancellationToken ?? parentCtx.__abortSignal
8903
8997
  };
8904
- const res = await instance.invoke({ messages: childMessages, ctx: childCtx }, childConfig);
8998
+ let res;
8999
+ try {
9000
+ res = await instance.invoke({ messages: childMessages, ctx: childCtx }, childConfig);
9001
+ } catch (err) {
9002
+ recordSpan("error", { error: err?.message || String(err) });
9003
+ throw err;
9004
+ }
9005
+ recordSpan("success", { result: res.content });
8905
9006
  return {
8906
9007
  content: res.content,
8907
9008
  output: res.output,
@@ -11064,11 +11165,42 @@ function mergeChildUsage(ref, childState, baseline, tag) {
11064
11165
  async function runChild(spec, input, deps, ref, opts) {
11065
11166
  const parentCtx = ref.ctx || {};
11066
11167
  const parentOnEvent = parentCtx.__onEvent;
11067
- const spawnId = `sa_${nanoid.nanoid(6)}`;
11168
+ const spawnId = opts.record?.spawnId ?? `sa_${nanoid.nanoid(6)}`;
11068
11169
  const start = Date.now();
11069
11170
  const usageTag = { name: spec.name, id: spawnId, mode: spec.mode };
11070
11171
  const parentHost = parentCtx.__plugins;
11071
11172
  const childDepth = (Number(parentCtx.__delegationDepth) || 0) + 1;
11173
+ const parentSession = parentCtx.__traceSession;
11174
+ const traced = isTraceSessionOpen(parentSession);
11175
+ const agentSpanId = generateSpanId();
11176
+ const parentIterationSpanId = traced ? parentSession.currentIterationSpanId : void 0;
11177
+ const traceScope = traced ? createSubagentTraceScope(parentSession, { agentSpanId, name: spec.name, spawnId, mode: spec.mode, hitl: opts.allowHITL, iterations: opts.record?.iterations }) : void 0;
11178
+ const recordSegment = (status, info) => {
11179
+ if (!traced || !isTraceSessionOpen(parentSession)) return;
11180
+ recordSubagentSpan(parentSession, {
11181
+ spanId: agentSpanId,
11182
+ // The tool_call span only exists once the tool returns; a paused child
11183
+ // has not returned, so its span hangs off the iteration.
11184
+ parentSpanId: status === "pending" ? parentIterationSpanId : opts.parentSpanId ?? parentIterationSpanId,
11185
+ name: spec.name,
11186
+ status,
11187
+ durationMs: Date.now() - start,
11188
+ error: info.error,
11189
+ info: {
11190
+ id: spawnId,
11191
+ name: spec.name,
11192
+ mode: spec.mode,
11193
+ parallel: opts.parallel,
11194
+ depth: childDepth,
11195
+ parentToolCallId: opts.parentToolCallId,
11196
+ status: info.subagentStatus,
11197
+ ...info.note ? { note: info.note } : {},
11198
+ ...spec.metadata ? { metadata: spec.metadata } : {}
11199
+ },
11200
+ task: info.input,
11201
+ result: info.content
11202
+ });
11203
+ };
11072
11204
  const emitStart = (task) => {
11073
11205
  parentOnEvent?.({ type: "subagent", phase: "start", name: spec.name, id: spawnId, mode: spec.mode, parallel: opts.parallel, parentToolCallId: opts.parentToolCallId, input: task });
11074
11206
  };
@@ -11076,6 +11208,15 @@ async function runChild(spec, input, deps, ref, opts) {
11076
11208
  result2.durationMs ?? (result2.durationMs = Date.now() - start);
11077
11209
  deps.registryRef.results.push(result2);
11078
11210
  const message = result2.error || result2.note || result2.status;
11211
+ const traceStatus = result2.status === "ok" || result2.status === "incomplete" ? "success" : result2.status === "blocked" ? "skipped" : "error";
11212
+ recordSegment(traceStatus, {
11213
+ subagentStatus: result2.status,
11214
+ input,
11215
+ // `message` falls back to the status itself ("ok"): only a failed or refused run has an error to show.
11216
+ error: traceStatus === "success" ? void 0 : message,
11217
+ note: result2.note,
11218
+ content: result2.content
11219
+ });
11079
11220
  parentOnEvent?.({
11080
11221
  type: "subagent",
11081
11222
  phase: FAILED_STATUSES.has(result2.status) ? "error" : "result",
@@ -11174,6 +11315,7 @@ async function runChild(spec, input, deps, ref, opts) {
11174
11315
  ctx: { __delegationDepth: currentDepth + 1 }
11175
11316
  };
11176
11317
  }
11318
+ if (traceScope) childInput = { ...childInput, ctx: { ...childInput.ctx || {}, __traceSession: traceScope } };
11177
11319
  const usageBaseline = childInput.usage?.perRequest?.length ?? 0;
11178
11320
  let res;
11179
11321
  try {
@@ -11214,8 +11356,11 @@ async function runChild(spec, input, deps, ref, opts) {
11214
11356
  input,
11215
11357
  childState: cleanChildState(childState),
11216
11358
  pendingKind: "approval",
11217
- pendingId: childEntry.id
11359
+ pendingId: childEntry.id,
11360
+ spawnId,
11361
+ iterations: traceScope ? subagentTraceScopeOf(traceScope)?.iterations : void 0
11218
11362
  };
11363
+ recordSegment("pending", { subagentStatus: "paused", input });
11219
11364
  parentOnEvent?.({ type: "subagent", phase: "paused", name: spec.name, id: spawnId, mode: spec.mode, parentToolCallId: opts.parentToolCallId });
11220
11365
  return { kind: "approval", toolCallId: surfaced.toolCallId, approvalId: surfaced.id };
11221
11366
  }
@@ -11237,8 +11382,11 @@ async function runChild(spec, input, deps, ref, opts) {
11237
11382
  input,
11238
11383
  childState: cleanChildState(childState),
11239
11384
  pendingKind: "user_question",
11240
- pendingId: childEntry.id
11385
+ pendingId: childEntry.id,
11386
+ spawnId,
11387
+ iterations: traceScope ? subagentTraceScopeOf(traceScope)?.iterations : void 0
11241
11388
  };
11389
+ recordSegment("pending", { subagentStatus: "paused", input });
11242
11390
  parentOnEvent?.({ type: "subagent", phase: "paused", name: spec.name, id: spawnId, mode: spec.mode, parentToolCallId: opts.parentToolCallId });
11243
11391
  return { kind: "user_question", toolCallId: surfaced.toolCallId, id: surfaced.id };
11244
11392
  }
@@ -11342,7 +11490,7 @@ function createDelegateTool(deps) {
11342
11490
  if (!budget.ok) return refusal(budget.reason);
11343
11491
  d.registryRef.spawnedCount += 1;
11344
11492
  }
11345
- return toToolReturn(await runChild(spec, record?.input ?? input, d, ref, { allowHITL: true, parentToolCallId, isResume: !!record, record, reserved: !record }));
11493
+ return toToolReturn(await runChild(spec, record?.input ?? input, d, ref, { allowHITL: true, parentToolCallId, isResume: !!record, record, reserved: !record, parentSpanId: context?.traceSpanId }));
11346
11494
  }
11347
11495
  });
11348
11496
  tool._deps = deps;
@@ -11387,7 +11535,7 @@ function createSpawnTool(deps) {
11387
11535
  if (!budget.ok) return refusal(budget.reason);
11388
11536
  d.registryRef.spawnedCount += 1;
11389
11537
  }
11390
- return toToolReturn(await runChild(spec, record?.input ?? args.input, d, ref, { allowHITL: true, parentToolCallId, isResume: !!record, record, reserved: !record }));
11538
+ return toToolReturn(await runChild(spec, record?.input ?? args.input, d, ref, { allowHITL: true, parentToolCallId, isResume: !!record, record, reserved: !record, parentSpanId: context?.traceSpanId }));
11391
11539
  }
11392
11540
  });
11393
11541
  tool._deps = deps;
@@ -11470,7 +11618,8 @@ function createParallelTool(deps) {
11470
11618
  allowHITL: false,
11471
11619
  parentToolCallId: `${parentToolCallId}#${i}`,
11472
11620
  parallel: true,
11473
- reserved: true
11621
+ reserved: true,
11622
+ parentSpanId: context?.traceSpanId
11474
11623
  });
11475
11624
  results[i] = outcome.kind === "result" ? outcome.result : { name: entry.spec.name, mode: entry.spec.mode, status: "error", input: entry.input, error: "Unexpected pause in parallel child." };
11476
11625
  }
@@ -15765,6 +15914,7 @@ function otelTracing(config) {
15765
15914
  sessionEnd: async ({ status, durationMs, error }, ctx) => {
15766
15915
  const state = ctx.state;
15767
15916
  const session = state.ctx?.__traceSession;
15917
+ if (isSubagentTraceScope(session)) return;
15768
15918
  if (!session) {
15769
15919
  const message = "otelTracing exported nothing: the agent has no trace session. Enable the SDK's tracing runtime with `tracing: { enabled: true }` \u2014 the plugin exports the session the runtime records, it does not record one itself.";
15770
15920
  ctx.logger.warn(message);