@agent-inspect/langchain 6.7.5 → 6.8.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
@@ -323,6 +323,247 @@ function streamMetadataFromState(state, options) {
323
323
  }
324
324
  return out;
325
325
  }
326
+
327
+ // packages/langchain/src/tool-identity.ts
328
+ function serializedLabel(s) {
329
+ if (typeof s.name === "string" && s.name.trim()) return s.name.trim();
330
+ if (Array.isArray(s.id) && s.id.length > 0) {
331
+ const last = s.id[s.id.length - 1];
332
+ if (typeof last === "string" && last.trim()) return last.trim();
333
+ }
334
+ return typeof s.type === "string" && s.type.trim() ? s.type.trim() : void 0;
335
+ }
336
+ function nonEmpty(value) {
337
+ if (typeof value !== "string") return void 0;
338
+ const trimmed = value.trim();
339
+ return trimmed.length > 0 ? trimmed : void 0;
340
+ }
341
+ function resolveToolIdentity(tool, runName, metadata, toolCallId) {
342
+ const frameworkRunName = nonEmpty(runName);
343
+ const metaToolName = nonEmpty(metadata?.toolName);
344
+ const metaTool = nonEmpty(metadata?.tool);
345
+ const classLabel = serializedLabel(tool);
346
+ const callId = nonEmpty(toolCallId) ?? nonEmpty(metadata?.toolCallId) ?? nonEmpty(metadata?.tool_call_id);
347
+ const displayName = frameworkRunName ?? metaToolName ?? metaTool ?? classLabel ?? "unknown-tool";
348
+ return {
349
+ displayName,
350
+ toolName: displayName,
351
+ ...classLabel && classLabel !== displayName ? { toolClass: classLabel } : {},
352
+ ...callId ? { toolCallId: callId } : {},
353
+ ...frameworkRunName ? { frameworkRunName } : {}
354
+ };
355
+ }
356
+ function applyToolIdentityAttributes(attrs, identity) {
357
+ attrs.tool = identity.displayName;
358
+ attrs.toolName = identity.toolName;
359
+ if (identity.toolClass) attrs.toolClass = identity.toolClass;
360
+ if (identity.toolCallId) attrs.toolCallId = identity.toolCallId;
361
+ if (identity.frameworkRunName) attrs.frameworkRunName = identity.frameworkRunName;
362
+ }
363
+
364
+ // packages/langchain/src/persist-intent.ts
365
+ function resolvePersistIntent(input) {
366
+ const hasTraceDir = typeof input.traceDir === "string" && input.traceDir.trim().length > 0;
367
+ const explicit = input.persist;
368
+ if (explicit === false) {
369
+ return { persist: false, contradictory: hasTraceDir };
370
+ }
371
+ if (explicit === true) {
372
+ return { persist: true, contradictory: false };
373
+ }
374
+ return { persist: hasTraceDir, contradictory: false };
375
+ }
376
+
377
+ // packages/langchain/src/invocation-state.ts
378
+ function createInvocationState(envelopeRunId) {
379
+ return {
380
+ envelopeRunId,
381
+ activeRuns: /* @__PURE__ */ new Map(),
382
+ endedRuns: /* @__PURE__ */ new Set(),
383
+ knownRelationships: /* @__PURE__ */ new Map(),
384
+ pendingRelationships: [],
385
+ completionGeneration: 0,
386
+ envelopeStarted: false,
387
+ finalized: false
388
+ };
389
+ }
390
+ function isObservedCallbackRun(state, parentLcRunId) {
391
+ return state.activeRuns.has(parentLcRunId) || state.endedRuns.has(parentLcRunId);
392
+ }
393
+ function beginCallbackRun(state, input) {
394
+ state.completionGeneration += 1;
395
+ const run = {
396
+ lcRunId: input.lcRunId,
397
+ ...input.parentLcRunId !== void 0 ? { parentLcRunId: input.parentLcRunId } : {},
398
+ startedAt: input.startedAt,
399
+ ...input.kind !== void 0 ? { kind: input.kind } : {},
400
+ ...input.stepId !== void 0 ? { stepId: input.stepId } : {}
401
+ };
402
+ state.activeRuns.set(input.lcRunId, run);
403
+ if (input.parentLcRunId !== void 0) {
404
+ if (isObservedCallbackRun(state, input.parentLcRunId)) {
405
+ state.knownRelationships.set(input.lcRunId, input.parentLcRunId);
406
+ } else {
407
+ state.pendingRelationships.push({
408
+ childLcRunId: input.lcRunId,
409
+ parentLcRunId: input.parentLcRunId,
410
+ reason: "unobserved-parent"
411
+ });
412
+ }
413
+ }
414
+ return run;
415
+ }
416
+ function endCallbackRun(state, lcRunId) {
417
+ const active = state.activeRuns.get(lcRunId);
418
+ if (active === void 0) {
419
+ if (state.endedRuns.has(lcRunId) || state.finalized) {
420
+ return { ended: false, late: true };
421
+ }
422
+ return { ended: false, late: true };
423
+ }
424
+ state.completionGeneration += 1;
425
+ state.activeRuns.delete(lcRunId);
426
+ state.endedRuns.add(lcRunId);
427
+ return { ended: true, late: false };
428
+ }
429
+ function markEnvelopeStarted(state, startTime) {
430
+ if (state.envelopeStarted) return false;
431
+ state.envelopeStarted = true;
432
+ state.runStartTime = startTime;
433
+ return true;
434
+ }
435
+ function markFinalized(state) {
436
+ if (state.finalized) return false;
437
+ if (!state.envelopeStarted) return false;
438
+ state.finalized = true;
439
+ return true;
440
+ }
441
+ function noteTerminalError(state, message) {
442
+ state.terminalError = { message };
443
+ }
444
+ function canScheduleFinalize(state) {
445
+ return state.envelopeStarted && !state.finalized && state.activeRuns.size === 0;
446
+ }
447
+ function bumpCompletionGeneration(state) {
448
+ state.completionGeneration += 1;
449
+ return state.completionGeneration;
450
+ }
451
+ function resetInvocationState(state, nextEnvelopeRunId) {
452
+ state.activeRuns.clear();
453
+ state.endedRuns.clear();
454
+ state.knownRelationships.clear();
455
+ state.pendingRelationships.length = 0;
456
+ state.terminalError = void 0;
457
+ state.completionGeneration += 1;
458
+ state.envelopeStarted = false;
459
+ state.finalized = false;
460
+ state.runStartTime = void 0;
461
+ if (nextEnvelopeRunId !== void 0) {
462
+ state.envelopeRunId = nextEnvelopeRunId;
463
+ }
464
+ }
465
+
466
+ // packages/langchain/src/parent-reconciliation.ts
467
+ var WELL_KNOWN_SEMANTIC_PARENTS = /* @__PURE__ */ new Set([
468
+ "LangGraph",
469
+ "__start__",
470
+ "__end__"
471
+ ]);
472
+ function isSemanticParentLabel(parentLcRunId) {
473
+ if (WELL_KNOWN_SEMANTIC_PARENTS.has(parentLcRunId)) return true;
474
+ return parentLcRunId.startsWith("__") && parentLcRunId.endsWith("__");
475
+ }
476
+ var LANGGRAPH_PARENT_KEYS = [
477
+ "handoffFrom",
478
+ "taskId",
479
+ "nodeId",
480
+ "nodeName",
481
+ "checkpointNamespace"
482
+ ];
483
+ function langGraphRecord(attributes) {
484
+ const lg = attributes?.langGraph;
485
+ if (typeof lg !== "object" || lg === null || Array.isArray(lg)) return void 0;
486
+ return lg;
487
+ }
488
+ function resolveParentRelationship(input, lookup) {
489
+ const parentLcRunId = input.parentLcRunId;
490
+ if (parentLcRunId) {
491
+ const exact = lookup.exactStepByLcRunId(parentLcRunId);
492
+ if (exact) {
493
+ return {
494
+ parentStepId: exact,
495
+ confidence: "explicit",
496
+ parentMapping: "exact"
497
+ };
498
+ }
499
+ }
500
+ const lg = langGraphRecord(input.attributes);
501
+ if (lg) {
502
+ for (const key of LANGGRAPH_PARENT_KEYS) {
503
+ if (key !== "handoffFrom" && key !== "checkpointNamespace") continue;
504
+ const raw = lg[key];
505
+ if (typeof raw !== "string" || !raw.trim()) continue;
506
+ const stepId = lookup.uniqueStepByLangGraphKey(key === "handoffFrom" ? "taskId" : key, raw);
507
+ const matched = stepId ?? (key === "handoffFrom" ? lookup.uniqueStepByLangGraphKey("nodeName", raw) ?? lookup.uniqueStepByLangGraphKey("nodeId", raw) : void 0);
508
+ if (matched) {
509
+ return {
510
+ parentStepId: matched,
511
+ confidence: "correlated",
512
+ parentMapping: "langgraph-metadata",
513
+ correlatedVia: key
514
+ };
515
+ }
516
+ }
517
+ }
518
+ if (parentLcRunId && isSemanticParentLabel(parentLcRunId)) {
519
+ const semantic = lookup.uniqueStepBySemanticLabel(parentLcRunId);
520
+ if (semantic) {
521
+ return {
522
+ parentStepId: semantic,
523
+ confidence: "correlated",
524
+ parentMapping: "semantic-name",
525
+ semanticParentLabel: parentLcRunId
526
+ };
527
+ }
528
+ return {
529
+ confidence: "unresolved",
530
+ parentMapping: "unresolved",
531
+ semanticParentLabel: parentLcRunId,
532
+ unresolvedParentRunId: parentLcRunId
533
+ };
534
+ }
535
+ if (parentLcRunId) {
536
+ return {
537
+ confidence: "unresolved",
538
+ parentMapping: "unresolved",
539
+ unresolvedParentRunId: parentLcRunId
540
+ };
541
+ }
542
+ return {
543
+ confidence: "explicit",
544
+ parentMapping: "exact"
545
+ };
546
+ }
547
+ function applyParentResolutionMetadata(metadata, resolution) {
548
+ const hasParentSignal = Boolean(resolution.parentStepId) || Boolean(resolution.unresolvedParentRunId) || Boolean(resolution.semanticParentLabel) || Boolean(resolution.correlatedVia);
549
+ if (!hasParentSignal) return;
550
+ metadata.parentMapping = resolution.parentMapping;
551
+ metadata.parentConfidence = resolution.confidence;
552
+ if (resolution.semanticParentLabel) {
553
+ metadata.semanticParentLabel = resolution.semanticParentLabel;
554
+ }
555
+ if (resolution.unresolvedParentRunId) {
556
+ metadata.unresolvedParentRunId = resolution.unresolvedParentRunId;
557
+ }
558
+ if (resolution.correlatedVia) {
559
+ metadata.parentCorrelatedVia = resolution.correlatedVia;
560
+ }
561
+ if (resolution.parentMapping === "synthetic-group") {
562
+ metadata.synthetic = true;
563
+ }
564
+ }
565
+
566
+ // packages/langchain/src/trace-persistence.ts
326
567
  function kindToStepType(kind) {
327
568
  switch (kind) {
328
569
  case "LLM":
@@ -352,27 +593,30 @@ var LangChainTracePersistence = class {
352
593
  #standalone;
353
594
  #silent;
354
595
  #safety;
355
- #runStarted = false;
356
- #runCompleted = false;
357
- #runStartTime;
358
- #rootLcRunId;
359
- /** Live LangChain callback runIds for standalone envelope finalization. */
360
- #activeLcRunIds = /* @__PURE__ */ new Set();
361
- #sawError = false;
362
- #lastErrorMessage;
363
- #finalizationToken = 0;
596
+ #lifecycle;
364
597
  #lcToStepId = /* @__PURE__ */ new Map();
598
+ /** `${field}\0${value}` → stepId, or null when ambiguous. */
599
+ #langGraphIndex = /* @__PURE__ */ new Map();
600
+ /** Semantic / display label → stepId, or null when ambiguous. */
601
+ #semanticLabelIndex = /* @__PURE__ */ new Map();
602
+ /** Semantic label → synthetic step id (created once ≥2 siblings share the label). */
603
+ #syntheticByLabel = /* @__PURE__ */ new Map();
604
+ /** Count of unresolved semantic-parent children seen per label (this invocation). */
605
+ #semanticParentCounts = /* @__PURE__ */ new Map();
606
+ #lateEventCount = 0;
365
607
  constructor(options = {}) {
366
608
  const inContext = advanced.hasActiveContext();
367
609
  this.#standalone = !inContext;
368
610
  this.#silent = options.silent ?? false;
369
611
  this.#traceDir = inContext ? advanced.getTraceDirFromContext() ?? advanced.resolveTraceDir({ dir: options.traceDir }) : advanced.resolveTraceDir({ dir: options.traceDir });
370
- this.#runId = (inContext ? advanced.getCurrentRunId() : void 0) ?? options.runId ?? advanced.createRunId();
612
+ const contextRunId = inContext ? advanced.getCurrentRunId() : void 0;
613
+ this.#runId = contextRunId ?? options.runId ?? advanced.createRunId();
371
614
  this.#runName = options.runName ?? "langchain-agent";
372
615
  this.#safety = advanced.resolveTraceSafetyOptions({
373
616
  redact: options.redact ? { rules: options.redact } : true,
374
617
  maxPreviewLength: options.maxPreviewChars
375
618
  });
619
+ this.#lifecycle = createInvocationState(this.#runId);
376
620
  }
377
621
  get runId() {
378
622
  return this.#runId;
@@ -380,49 +624,217 @@ var LangChainTracePersistence = class {
380
624
  get traceDir() {
381
625
  return this.#traceDir;
382
626
  }
627
+ /** @internal Test / diagnostics access to per-invocation lifecycle. */
628
+ get lifecycle() {
629
+ return this.#lifecycle;
630
+ }
631
+ /** Count of end/start events ignored after finalize (diagnostics). */
632
+ get lateEventCount() {
633
+ return this.#lateEventCount;
634
+ }
635
+ /** Bounded adapter diagnostics for CLI/MCP summaries (no filesystem paths). */
636
+ getDiagnostics() {
637
+ return {
638
+ lateEventCount: this.#lateEventCount,
639
+ activeRunCount: this.#lifecycle.activeRuns.size,
640
+ endedRunCount: this.#lifecycle.endedRuns.size,
641
+ pendingRelationshipCount: this.#lifecycle.pendingRelationships.length,
642
+ knownRelationshipCount: this.#lifecycle.knownRelationships.size,
643
+ syntheticGroupCount: this.#syntheticByLabel.size,
644
+ envelopeStarted: this.#lifecycle.envelopeStarted,
645
+ finalized: this.#lifecycle.finalized,
646
+ completionGeneration: this.#lifecycle.completionGeneration,
647
+ hasTerminalError: Boolean(this.#lifecycle.terminalError)
648
+ };
649
+ }
650
+ /**
651
+ * Start a fresh envelope after a prior invocation finalized (callback reuse).
652
+ * Allocates a new run id unless still nested in an inspectRun context.
653
+ */
654
+ beginNewInvocation() {
655
+ if (advanced.hasActiveContext()) {
656
+ const ctxId = advanced.getCurrentRunId();
657
+ if (ctxId) {
658
+ this.#runId = ctxId;
659
+ resetInvocationState(this.#lifecycle, this.#runId);
660
+ this.#clearStepIndexes();
661
+ this.#lateEventCount = 0;
662
+ return;
663
+ }
664
+ }
665
+ this.#runId = advanced.createRunId();
666
+ resetInvocationState(this.#lifecycle, this.#runId);
667
+ this.#clearStepIndexes();
668
+ this.#lateEventCount = 0;
669
+ }
383
670
  reset() {
384
- this.#finalizationToken += 1;
385
- this.#runStarted = false;
386
- this.#runCompleted = false;
387
- this.#runStartTime = void 0;
388
- this.#rootLcRunId = void 0;
389
- this.#activeLcRunIds.clear();
390
- this.#sawError = false;
391
- this.#lastErrorMessage = void 0;
671
+ resetInvocationState(this.#lifecycle);
672
+ this.#clearStepIndexes();
673
+ this.#lateEventCount = 0;
674
+ }
675
+ #clearStepIndexes() {
392
676
  this.#lcToStepId.clear();
677
+ this.#langGraphIndex.clear();
678
+ this.#semanticLabelIndex.clear();
679
+ this.#syntheticByLabel.clear();
680
+ this.#semanticParentCounts.clear();
681
+ }
682
+ #registerUnique(index, key, stepId) {
683
+ if (!index.has(key)) {
684
+ index.set(key, stepId);
685
+ return;
686
+ }
687
+ if (index.get(key) !== stepId) {
688
+ index.set(key, null);
689
+ }
690
+ }
691
+ #langGraphIndexKey(field, value) {
692
+ return `${field}\0${value}`;
693
+ }
694
+ #registerStepIndexes(stepId, name, attributes) {
695
+ const labels = /* @__PURE__ */ new Set([name]);
696
+ const stripped = name.replace(/^(chain|tool|llm|retriever|agent):/, "");
697
+ if (stripped) labels.add(stripped);
698
+ for (const label of labels) {
699
+ this.#registerUnique(this.#semanticLabelIndex, label, stepId);
700
+ }
701
+ const lg = attributes.langGraph;
702
+ if (typeof lg === "object" && lg !== null && !Array.isArray(lg)) {
703
+ const record = lg;
704
+ for (const field of [
705
+ "taskId",
706
+ "nodeId",
707
+ "nodeName",
708
+ "checkpointNamespace"
709
+ ]) {
710
+ const raw = record[field];
711
+ if (typeof raw === "string" && raw.trim()) {
712
+ this.#registerUnique(
713
+ this.#langGraphIndex,
714
+ this.#langGraphIndexKey(field, raw),
715
+ stepId
716
+ );
717
+ }
718
+ }
719
+ }
720
+ }
721
+ #resolveParent(parentLcRunId, attributes) {
722
+ return resolveParentRelationship(
723
+ { parentLcRunId, attributes },
724
+ {
725
+ exactStepByLcRunId: (lcRunId) => this.#lcToStepId.get(lcRunId),
726
+ uniqueStepByLangGraphKey: (key, value) => {
727
+ const hit = this.#langGraphIndex.get(this.#langGraphIndexKey(key, value));
728
+ return hit === null || hit === void 0 ? void 0 : hit;
729
+ },
730
+ uniqueStepBySemanticLabel: (label) => {
731
+ const hit = this.#semanticLabelIndex.get(label);
732
+ return hit === null || hit === void 0 ? void 0 : hit;
733
+ }
734
+ }
735
+ );
736
+ }
737
+ /**
738
+ * When ≥2 steps share the same unresolved semantic parent label, emit one
739
+ * synthetic grouping node and attach this (and later) siblings under it.
740
+ * The first sibling remains unresolved (append-only JSONL cannot rewrite it).
741
+ */
742
+ async #maybeAttachSyntheticGroup(resolution, startTime) {
743
+ const label = resolution.semanticParentLabel;
744
+ if (resolution.parentMapping !== "unresolved" || !label || resolution.parentStepId) {
745
+ return resolution;
746
+ }
747
+ const existing = this.#syntheticByLabel.get(label);
748
+ if (existing) {
749
+ return {
750
+ parentStepId: existing,
751
+ confidence: "synthetic",
752
+ parentMapping: "synthetic-group",
753
+ semanticParentLabel: label,
754
+ unresolvedParentRunId: label
755
+ };
756
+ }
757
+ const nextCount = (this.#semanticParentCounts.get(label) ?? 0) + 1;
758
+ this.#semanticParentCounts.set(label, nextCount);
759
+ if (nextCount < 2) {
760
+ return resolution;
761
+ }
762
+ const syntheticStepId = advanced.createStepId();
763
+ this.#syntheticByLabel.set(label, syntheticStepId);
764
+ const metadata = {
765
+ adapter: "langchain",
766
+ confidence: "synthetic",
767
+ synthetic: true,
768
+ parentMapping: "synthetic-group",
769
+ parentConfidence: "synthetic",
770
+ semanticParentLabel: label,
771
+ unresolvedParentRunId: label
772
+ };
773
+ const event = {
774
+ schemaVersion: "0.1",
775
+ event: "step_started",
776
+ timestamp: startTime,
777
+ runId: this.#runId,
778
+ stepId: syntheticStepId,
779
+ name: `synthetic:${label}`,
780
+ type: "logic",
781
+ startTime,
782
+ metadata
783
+ };
784
+ await this.#write(event);
785
+ return {
786
+ parentStepId: syntheticStepId,
787
+ confidence: "synthetic",
788
+ parentMapping: "synthetic-group",
789
+ semanticParentLabel: label,
790
+ unresolvedParentRunId: label
791
+ };
393
792
  }
394
- noteRoot(lcRunId, parentRunId) {
395
- if (!parentRunId && !this.#rootLcRunId) {
396
- this.#rootLcRunId = lcRunId;
793
+ /** Rotate when a prior standalone invocation already finalized. */
794
+ #prepareForStart() {
795
+ if (this.#standalone && this.#lifecycle.finalized) {
796
+ this.beginNewInvocation();
397
797
  }
398
798
  }
799
+ /**
800
+ * @deprecated Root-ID heuristics are no longer used for envelope completion.
801
+ * Retained as a no-op for call-site compatibility during the v6.8 train.
802
+ */
803
+ noteRoot(_lcRunId, _parentRunId) {
804
+ }
399
805
  resolveParentId(lcParentRunId) {
400
806
  if (!lcParentRunId) return void 0;
401
807
  return this.#lcToStepId.get(lcParentRunId);
402
808
  }
403
809
  async onStepStart(params) {
404
810
  try {
405
- this.#finalizationToken += 1;
406
- this.noteRoot(params.lcRunId, params.lcParentRunId);
407
- this.#activeLcRunIds.add(params.lcRunId);
408
- if (this.#standalone && !this.#runStarted) {
409
- await this.#ensureRunStarted(params.startTime, params.attributes);
410
- }
811
+ this.#prepareForStart();
411
812
  const stepId = advanced.createStepId();
412
813
  this.#lcToStepId.set(params.lcRunId, stepId);
413
- const parentId = this.resolveParentId(params.lcParentRunId);
414
- const metadata = toStepMetadata(params.attributes);
415
- if (params.lcParentRunId && !parentId) {
416
- metadata.parentMapping = "unresolved";
417
- metadata.unresolvedParentRunId = params.lcParentRunId;
814
+ this.#registerStepIndexes(stepId, params.name, params.attributes);
815
+ beginCallbackRun(this.#lifecycle, {
816
+ lcRunId: params.lcRunId,
817
+ parentLcRunId: params.lcParentRunId,
818
+ startedAt: params.startTime,
819
+ kind: params.kind,
820
+ stepId
821
+ });
822
+ if (this.#standalone && !this.#lifecycle.envelopeStarted) {
823
+ await this.#ensureRunStarted(params.startTime, params.attributes);
418
824
  }
825
+ const resolution = await this.#maybeAttachSyntheticGroup(
826
+ this.#resolveParent(params.lcParentRunId, params.attributes),
827
+ params.startTime
828
+ );
829
+ const metadata = toStepMetadata(params.attributes);
830
+ applyParentResolutionMetadata(metadata, resolution);
419
831
  const event = {
420
832
  schemaVersion: "0.1",
421
833
  event: "step_started",
422
834
  timestamp: params.startTime,
423
835
  runId: this.#runId,
424
836
  stepId,
425
- ...parentId ? { parentId } : {},
837
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
426
838
  name: params.name,
427
839
  type: kindToStepType(params.kind),
428
840
  startTime: params.startTime,
@@ -435,35 +847,58 @@ var LangChainTracePersistence = class {
435
847
  }
436
848
  async onStepEnd(params) {
437
849
  try {
850
+ if (this.#lifecycle.finalized && !this.#lifecycle.activeRuns.has(params.lcRunId) && !this.#lcToStepId.has(params.lcRunId)) {
851
+ this.#lateEventCount += 1;
852
+ return;
853
+ }
438
854
  let stepId = this.#lcToStepId.get(params.lcRunId);
439
855
  if (!stepId && params.completionAttributes) {
856
+ if (this.#lifecycle.finalized) {
857
+ this.#lateEventCount += 1;
858
+ return;
859
+ }
440
860
  stepId = advanced.createStepId();
441
861
  this.#lcToStepId.set(params.lcRunId, stepId);
442
- const parentId = this.resolveParentId(params.lcParentRunId);
443
- const metadata = toStepMetadata(params.completionAttributes);
444
- if (params.lcParentRunId && !parentId) {
445
- metadata.parentMapping = "unresolved";
446
- metadata.unresolvedParentRunId = params.lcParentRunId;
447
- }
862
+ const synthName = String(params.completionAttributes.name ?? "llm:llm");
863
+ this.#registerStepIndexes(stepId, synthName, params.completionAttributes);
864
+ beginCallbackRun(this.#lifecycle, {
865
+ lcRunId: params.lcRunId,
866
+ parentLcRunId: params.lcParentRunId,
867
+ startedAt: params.endTime - (params.durationMs ?? 0),
868
+ kind: params.completionAttributes.kind ?? "LLM",
869
+ stepId
870
+ });
448
871
  const startTime = params.endTime - (params.durationMs ?? 0);
872
+ const resolution = await this.#maybeAttachSyntheticGroup(
873
+ this.#resolveParent(params.lcParentRunId, params.completionAttributes),
874
+ startTime
875
+ );
876
+ const metadata = toStepMetadata(params.completionAttributes);
877
+ applyParentResolutionMetadata(metadata, resolution);
449
878
  const started = {
450
879
  schemaVersion: "0.1",
451
880
  event: "step_started",
452
881
  timestamp: startTime,
453
882
  runId: this.#runId,
454
883
  stepId,
455
- ...parentId ? { parentId } : {},
456
- name: String(params.completionAttributes.name ?? "llm:llm"),
884
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
885
+ name: synthName,
457
886
  type: kindToStepType(
458
887
  params.completionAttributes.kind ?? "LLM"
459
888
  ),
460
889
  startTime,
461
890
  metadata
462
891
  };
892
+ if (this.#standalone && !this.#lifecycle.envelopeStarted) {
893
+ await this.#ensureRunStarted(startTime, params.completionAttributes);
894
+ }
463
895
  await this.#write(started);
464
896
  }
465
897
  if (!stepId) return;
466
- const durationMs = typeof params.durationMs === "number" && Number.isFinite(params.durationMs) ? Math.max(0, Math.floor(params.durationMs)) : Math.max(0, params.endTime - (this.#runStartTime ?? params.endTime));
898
+ const durationMs = typeof params.durationMs === "number" && Number.isFinite(params.durationMs) ? Math.max(0, Math.floor(params.durationMs)) : Math.max(
899
+ 0,
900
+ params.endTime - (this.#lifecycle.runStartTime ?? params.endTime)
901
+ );
467
902
  const event = {
468
903
  schemaVersion: "0.1",
469
904
  event: "step_completed",
@@ -477,10 +912,12 @@ var LangChainTracePersistence = class {
477
912
  };
478
913
  await this.#write(event);
479
914
  if (params.status === "error") {
480
- this.#sawError = true;
481
- if (params.errorMessage) this.#lastErrorMessage = params.errorMessage;
915
+ noteTerminalError(
916
+ this.#lifecycle,
917
+ params.errorMessage ?? "adapter step error"
918
+ );
482
919
  }
483
- this.#activeLcRunIds.delete(params.lcRunId);
920
+ endCallbackRun(this.#lifecycle, params.lcRunId);
484
921
  await this.#scheduleStandaloneFinalization(params.endTime);
485
922
  } catch (err) {
486
923
  this.#warn(err);
@@ -489,27 +926,33 @@ var LangChainTracePersistence = class {
489
926
  /** Point-in-time adapter events (e.g. agent action) — writes start + completed pair. */
490
927
  async onInstantStep(params) {
491
928
  try {
492
- this.#finalizationToken += 1;
493
- this.noteRoot(params.lcRunId, params.lcParentRunId);
494
- this.#activeLcRunIds.add(params.lcRunId);
495
- if (this.#standalone && !this.#runStarted) {
496
- await this.#ensureRunStarted(params.timestamp, params.attributes);
497
- }
929
+ this.#prepareForStart();
498
930
  const stepId = advanced.createStepId();
499
931
  this.#lcToStepId.set(params.lcRunId, stepId);
500
- const parentId = this.resolveParentId(params.lcParentRunId);
501
- const metadata = toStepMetadata(params.attributes);
502
- if (params.lcParentRunId && !parentId) {
503
- metadata.parentMapping = "unresolved";
504
- metadata.unresolvedParentRunId = params.lcParentRunId;
932
+ this.#registerStepIndexes(stepId, params.name, params.attributes);
933
+ beginCallbackRun(this.#lifecycle, {
934
+ lcRunId: params.lcRunId,
935
+ parentLcRunId: params.lcParentRunId,
936
+ startedAt: params.timestamp,
937
+ kind: params.kind,
938
+ stepId
939
+ });
940
+ if (this.#standalone && !this.#lifecycle.envelopeStarted) {
941
+ await this.#ensureRunStarted(params.timestamp, params.attributes);
505
942
  }
943
+ const resolution = await this.#maybeAttachSyntheticGroup(
944
+ this.#resolveParent(params.lcParentRunId, params.attributes),
945
+ params.timestamp
946
+ );
947
+ const metadata = toStepMetadata(params.attributes);
948
+ applyParentResolutionMetadata(metadata, resolution);
506
949
  const started = {
507
950
  schemaVersion: "0.1",
508
951
  event: "step_started",
509
952
  timestamp: params.timestamp,
510
953
  runId: this.#runId,
511
954
  stepId,
512
- ...parentId ? { parentId } : {},
955
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
513
956
  name: params.name,
514
957
  type: kindToStepType(params.kind),
515
958
  startTime: params.timestamp,
@@ -529,15 +972,71 @@ var LangChainTracePersistence = class {
529
972
  };
530
973
  await this.#write(completed);
531
974
  if (params.status === "error") {
532
- this.#sawError = true;
533
- if (params.errorMessage) this.#lastErrorMessage = params.errorMessage;
975
+ noteTerminalError(
976
+ this.#lifecycle,
977
+ params.errorMessage ?? "adapter step error"
978
+ );
534
979
  }
535
- this.#activeLcRunIds.delete(params.lcRunId);
980
+ endCallbackRun(this.#lifecycle, params.lcRunId);
536
981
  await this.#scheduleStandaloneFinalization(params.timestamp);
537
982
  } catch (err) {
538
983
  this.#warn(err);
539
984
  }
540
985
  }
986
+ /**
987
+ * Drain deferred microtask finalization. Idempotent; never throws to callers.
988
+ * @experimental
989
+ */
990
+ async flush() {
991
+ try {
992
+ await Promise.resolve();
993
+ await Promise.resolve();
994
+ } catch (err) {
995
+ this.#warn(err);
996
+ }
997
+ }
998
+ /**
999
+ * Force-complete the standalone envelope when safe/started.
1000
+ * Works even if active callback runs remain (unusual framework shapes / serverless).
1001
+ * Idempotent; never throws to callers.
1002
+ * @experimental
1003
+ */
1004
+ async finalize(options = {}) {
1005
+ try {
1006
+ if (!this.#standalone) return false;
1007
+ if (this.#lifecycle.finalized) return false;
1008
+ if (!this.#lifecycle.envelopeStarted) return false;
1009
+ bumpCompletionGeneration(this.#lifecycle);
1010
+ const status = options.status ?? (this.#lifecycle.terminalError ? "error" : "success");
1011
+ if (status === "error") {
1012
+ noteTerminalError(
1013
+ this.#lifecycle,
1014
+ options.errorMessage ?? this.#lifecycle.terminalError?.message ?? "adapter finalize error"
1015
+ );
1016
+ }
1017
+ await this.#ensureRunCompleted(
1018
+ options.endTime ?? Date.now(),
1019
+ status,
1020
+ status === "error" ? options.errorMessage ?? this.#lifecycle.terminalError?.message : void 0
1021
+ );
1022
+ return this.#lifecycle.finalized;
1023
+ } catch (err) {
1024
+ this.#warn(err);
1025
+ return false;
1026
+ }
1027
+ }
1028
+ /**
1029
+ * Flush + finalize. Idempotent; never throws to callers.
1030
+ * @experimental
1031
+ */
1032
+ async close() {
1033
+ try {
1034
+ await this.flush();
1035
+ await this.finalize();
1036
+ } catch (err) {
1037
+ this.#warn(err);
1038
+ }
1039
+ }
541
1040
  /**
542
1041
  * When the last active LangChain callback ends, yield one microtask so a
543
1042
  * same-turn sibling start can cancel finalization, then write run_completed
@@ -545,24 +1044,20 @@ var LangChainTracePersistence = class {
545
1044
  * block the envelope.
546
1045
  */
547
1046
  async #scheduleStandaloneFinalization(endTime) {
548
- if (!this.#standalone || this.#runCompleted || !this.#runStarted) return;
549
- if (this.#activeLcRunIds.size > 0) return;
550
- const token = ++this.#finalizationToken;
1047
+ if (!this.#standalone || !canScheduleFinalize(this.#lifecycle)) return;
1048
+ const generation = this.#lifecycle.completionGeneration;
551
1049
  await Promise.resolve();
552
- if (token !== this.#finalizationToken) return;
553
- if (!this.#standalone || this.#runCompleted || !this.#runStarted) return;
554
- if (this.#activeLcRunIds.size > 0) return;
555
- const status = this.#sawError ? "error" : "success";
1050
+ if (this.#lifecycle.completionGeneration !== generation) return;
1051
+ if (!this.#standalone || !canScheduleFinalize(this.#lifecycle)) return;
1052
+ const status = this.#lifecycle.terminalError ? "error" : "success";
556
1053
  await this.#ensureRunCompleted(
557
1054
  endTime,
558
1055
  status,
559
- status === "error" ? this.#lastErrorMessage : void 0
1056
+ status === "error" ? this.#lifecycle.terminalError?.message : void 0
560
1057
  );
561
1058
  }
562
1059
  async #ensureRunStarted(startTime, attrs) {
563
- if (this.#runStarted) return;
564
- this.#runStarted = true;
565
- this.#runStartTime = startTime;
1060
+ if (!markEnvelopeStarted(this.#lifecycle, startTime)) return;
566
1061
  await advanced.initializeTraceFile(this.#runId, this.#traceDir);
567
1062
  const metadata = {
568
1063
  adapter: "langchain",
@@ -582,9 +1077,21 @@ var LangChainTracePersistence = class {
582
1077
  await this.#write(event);
583
1078
  }
584
1079
  async #ensureRunCompleted(endTime, stepStatus, errorMessage) {
585
- if (this.#runCompleted || !this.#runStarted) return;
586
- this.#runCompleted = true;
587
- const startTime = this.#runStartTime ?? endTime;
1080
+ if (!markFinalized(this.#lifecycle)) return;
1081
+ for (const [, syntheticStepId] of this.#syntheticByLabel) {
1082
+ const completed = {
1083
+ schemaVersion: "0.1",
1084
+ event: "step_completed",
1085
+ timestamp: endTime,
1086
+ runId: this.#runId,
1087
+ stepId: syntheticStepId,
1088
+ status: "success",
1089
+ endTime,
1090
+ durationMs: Math.max(0, endTime - (this.#lifecycle.runStartTime ?? endTime))
1091
+ };
1092
+ await this.#write(completed);
1093
+ }
1094
+ const startTime = this.#lifecycle.runStartTime ?? endTime;
588
1095
  const durationMs = Math.max(0, endTime - startTime);
589
1096
  const runStatus = stepStatus === "error" ? "error" : "success";
590
1097
  const event = {
@@ -614,20 +1121,11 @@ var LangChainTracePersistence = class {
614
1121
  function isRecord2(v) {
615
1122
  return typeof v === "object" && v !== null && !Array.isArray(v);
616
1123
  }
617
- function serializedLabel(s) {
1124
+ function serializedLabel2(s) {
618
1125
  if (typeof s.name === "string" && s.name.trim()) return s.name;
619
1126
  if (Array.isArray(s.id) && s.id.length > 0) return s.id[s.id.length - 1];
620
1127
  return s.type;
621
1128
  }
622
- function resolveToolDisplayName(tool, runName, metadata) {
623
- const fromRun = typeof runName === "string" ? runName.trim() : "";
624
- if (fromRun) return fromRun;
625
- const metaName = metadata?.toolName;
626
- if (typeof metaName === "string" && metaName.trim()) return metaName.trim();
627
- const metaTool = metadata?.tool;
628
- if (typeof metaTool === "string" && metaTool.trim()) return metaTool.trim();
629
- return serializedLabel(tool) ?? "tool";
630
- }
631
1129
  function errorShape(err) {
632
1130
  if (err instanceof Error) {
633
1131
  return { errorName: err.name, errorMessage: err.message };
@@ -636,6 +1134,11 @@ function errorShape(err) {
636
1134
  }
637
1135
  var AgentInspectCallback = class extends base.BaseCallbackHandler {
638
1136
  name = "agent-inspect";
1137
+ /**
1138
+ * Ensure LangGraph/LangChain awaits async handler work (persistence) before
1139
+ * invoke/stream settles. Required for deterministic standalone envelopes.
1140
+ */
1141
+ awaitHandlers = true;
639
1142
  #opts;
640
1143
  #redactor;
641
1144
  #persistence;
@@ -646,16 +1149,22 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
646
1149
  #rootRunId;
647
1150
  constructor(options = {}) {
648
1151
  super({});
1152
+ const intent = resolvePersistIntent(options);
649
1153
  this.#opts = {
650
1154
  capture: options.capture ?? "metadata-only",
651
1155
  silent: options.silent ?? false,
652
1156
  maxPreviewChars: options.maxPreviewChars ?? 200,
653
- persist: options.persist ?? false,
654
1157
  runName: options.runName ?? "langchain-agent",
655
- ...options
1158
+ ...options,
1159
+ persist: intent.persist
656
1160
  };
657
1161
  this.#redactor = new logs.Redactor({ rules: this.#opts.redact });
658
- if (this.#opts.persist) {
1162
+ if (intent.contradictory && !this.#opts.silent) {
1163
+ console.error(
1164
+ "[agent-inspect:langchain] persist:false with traceDir set \u2014 traces stay in-memory; remove traceDir or set persist:true"
1165
+ );
1166
+ }
1167
+ if (intent.persist) {
659
1168
  this.#persistence = new LangChainTracePersistence({
660
1169
  runName: this.#opts.runName,
661
1170
  traceDir: this.#opts.traceDir,
@@ -681,6 +1190,91 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
681
1190
  this.#rootRunId = void 0;
682
1191
  this.#persistence?.reset();
683
1192
  }
1193
+ /**
1194
+ * Bounded adapter diagnostics for the current invocation (counts only).
1195
+ * Safe for CLI/MCP summaries — no absolute paths or payloads.
1196
+ *
1197
+ * @experimental
1198
+ */
1199
+ getDiagnostics() {
1200
+ const base = this.#persistence?.getDiagnostics() ?? {
1201
+ lateEventCount: 0,
1202
+ activeRunCount: 0,
1203
+ endedRunCount: 0,
1204
+ pendingRelationshipCount: 0,
1205
+ knownRelationshipCount: 0,
1206
+ syntheticGroupCount: 0,
1207
+ envelopeStarted: false,
1208
+ finalized: false,
1209
+ completionGeneration: 0,
1210
+ hasTerminalError: false
1211
+ };
1212
+ return {
1213
+ ...base,
1214
+ inMemoryEventCount: this.#events.length,
1215
+ deferredPersistStartCount: this.#deferredPersistStart.size
1216
+ };
1217
+ }
1218
+ /**
1219
+ * Drain deferred completion work (microtask finalization).
1220
+ * Idempotent. Failures are isolated and never thrown to user code.
1221
+ *
1222
+ * @experimental Additive finalization API for serverless / unusual callback shapes.
1223
+ */
1224
+ async flush() {
1225
+ try {
1226
+ await this.#persistence?.flush();
1227
+ } catch (err) {
1228
+ if (!this.#opts.silent) {
1229
+ console.error("[agent-inspect:langchain]", err);
1230
+ }
1231
+ }
1232
+ }
1233
+ /**
1234
+ * Complete the standalone envelope when started. Fallback when automatic
1235
+ * completion cannot run (e.g. process shutdown). Idempotent.
1236
+ * Failures are isolated and never thrown to user code.
1237
+ *
1238
+ * @experimental Additive finalization API for serverless / unusual callback shapes.
1239
+ */
1240
+ async finalize(options) {
1241
+ try {
1242
+ await this.#persistence?.finalize(options);
1243
+ } catch (err) {
1244
+ if (!this.#opts.silent) {
1245
+ console.error("[agent-inspect:langchain]", err);
1246
+ }
1247
+ }
1248
+ }
1249
+ /**
1250
+ * `flush()` then `finalize()`. Idempotent. Failures are isolated.
1251
+ *
1252
+ * @experimental Additive finalization API for serverless / unusual callback shapes.
1253
+ */
1254
+ async close() {
1255
+ try {
1256
+ await this.flush();
1257
+ await this.finalize();
1258
+ } catch (err) {
1259
+ if (!this.#opts.silent) {
1260
+ console.error("[agent-inspect:langchain]", err);
1261
+ }
1262
+ }
1263
+ }
1264
+ /**
1265
+ * When persist mode has already finalized an envelope, rotate to a new
1266
+ * invocation before the next start so reused handlers do not mix runs.
1267
+ */
1268
+ #prepareCallbackInvocation() {
1269
+ const persistence = this.#persistence;
1270
+ if (!persistence?.lifecycle.finalized) return;
1271
+ this.#events = [];
1272
+ this.#starts.clear();
1273
+ this.#streamState.clear();
1274
+ this.#deferredPersistStart.clear();
1275
+ this.#rootRunId = void 0;
1276
+ persistence.beginNewInvocation();
1277
+ }
684
1278
  #streamPreviewLimit() {
685
1279
  if (this.#opts.capture !== "preview") return 0;
686
1280
  return this.#opts.maxStreamPreviewChars ?? this.#opts.maxPreviewChars ?? 200;
@@ -842,9 +1436,10 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
842
1436
  });
843
1437
  }
844
1438
  async handleChainStart(chain, inputs, runId, runType, tags, metadata, runName, parentRunId, _extra) {
1439
+ this.#prepareCallbackInvocation();
845
1440
  this.#ensureRoot(runId, parentRunId);
846
1441
  this.#rememberStart(runId, "CHAIN");
847
- const label = serializedLabel(chain) ?? "chain";
1442
+ const label = serializedLabel2(chain) ?? "chain";
848
1443
  const previews = {};
849
1444
  if (this.#opts.capture === "preview") previews.inputPreview = inputs;
850
1445
  const attrs = {
@@ -923,6 +1518,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
923
1518
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
924
1519
  }
925
1520
  async handleLLMStart(llm, prompts, runId, parentRunId, _extraParams, tags, metadata, runName) {
1521
+ this.#prepareCallbackInvocation();
926
1522
  this.#ensureRoot(runId, parentRunId);
927
1523
  this.#rememberStart(runId, "LLM");
928
1524
  const model = extractModelName(llm);
@@ -955,6 +1551,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
955
1551
  await this.#persistLlmStepStart(runId, parentRunId, stepName, "LLM", ts, attrs);
956
1552
  }
957
1553
  async handleChatModelStart(llm, messages, runId, parentRunId, _extraParams, tags, metadata, runName) {
1554
+ this.#prepareCallbackInvocation();
958
1555
  this.#ensureRoot(runId, parentRunId);
959
1556
  this.#rememberStart(runId, "LLM");
960
1557
  const model = extractModelName(llm);
@@ -1073,21 +1670,22 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1073
1670
  });
1074
1671
  this.#clearStreamState(runId);
1075
1672
  }
1076
- async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName, _toolCallId) {
1673
+ async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName, toolCallId) {
1674
+ this.#prepareCallbackInvocation();
1077
1675
  this.#ensureRoot(runId, parentRunId);
1078
1676
  this.#rememberStart(runId, "TOOL");
1079
- const toolName = resolveToolDisplayName(tool, runName, metadata);
1677
+ const identity = resolveToolIdentity(tool, runName, metadata, toolCallId);
1080
1678
  const previews = {};
1081
1679
  if (this.#opts.capture === "preview") previews.inputPreview = input;
1082
1680
  const attrs = {
1083
- ...this.#baseAttrs(runId, parentRunId, tags, runName),
1084
- tool: toolName
1681
+ ...this.#baseAttrs(runId, parentRunId, tags, runName)
1085
1682
  };
1683
+ applyToolIdentityAttributes(attrs, identity);
1086
1684
  this.#mergeMetadata(attrs, metadata);
1087
1685
  this.#applyPreview(attrs, previews);
1088
1686
  this.#rememberStartMetadata(runId, attrs);
1089
1687
  const ts = Date.now();
1090
- const stepName = `tool:${toolName}`;
1688
+ const stepName = `tool:${identity.displayName}`;
1091
1689
  this.#pushEvent({
1092
1690
  eventId: `${runId}:TOOL:start`,
1093
1691
  runId: this.#traceRunId(runId),
@@ -1157,9 +1755,10 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1157
1755
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
1158
1756
  }
1159
1757
  async handleRetrieverStart(retriever, _query, runId, parentRunId, tags, metadata, name) {
1758
+ this.#prepareCallbackInvocation();
1160
1759
  this.#ensureRoot(runId, parentRunId);
1161
1760
  this.#rememberStart(runId, "RETRIEVER");
1162
- const rname = name ?? serializedLabel(retriever) ?? "retriever";
1761
+ const rname = name ?? serializedLabel2(retriever) ?? "retriever";
1163
1762
  const attrs = {
1164
1763
  ...this.#baseAttrs(runId, parentRunId, tags, void 0),
1165
1764
  retriever: rname
@@ -1240,6 +1839,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1240
1839
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
1241
1840
  }
1242
1841
  async handleAgentAction(action, runId, parentRunId, tags) {
1842
+ this.#prepareCallbackInvocation();
1243
1843
  this.#ensureRoot(runId, parentRunId);
1244
1844
  const attrs = {
1245
1845
  ...this.#baseAttrs(runId, parentRunId, tags, void 0),