@agent-inspect/langchain 6.7.4 → 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
@@ -1,9 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var path = require('path');
3
4
  var base = require('@langchain/core/callbacks/base');
4
5
  var advanced = require('agent-inspect/advanced');
5
6
  var logs = require('agent-inspect/logs');
6
7
 
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var path__default = /*#__PURE__*/_interopDefault(path);
11
+
7
12
  // packages/langchain/src/agent-inspect-callback.ts
8
13
 
9
14
  // packages/langchain/src/metadata.ts
@@ -318,6 +323,247 @@ function streamMetadataFromState(state, options) {
318
323
  }
319
324
  return out;
320
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
321
567
  function kindToStepType(kind) {
322
568
  switch (kind) {
323
569
  case "LLM":
@@ -347,27 +593,30 @@ var LangChainTracePersistence = class {
347
593
  #standalone;
348
594
  #silent;
349
595
  #safety;
350
- #runStarted = false;
351
- #runCompleted = false;
352
- #runStartTime;
353
- #rootLcRunId;
354
- /** Live LangChain callback runIds for standalone envelope finalization. */
355
- #activeLcRunIds = /* @__PURE__ */ new Set();
356
- #sawError = false;
357
- #lastErrorMessage;
358
- #finalizationToken = 0;
596
+ #lifecycle;
359
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;
360
607
  constructor(options = {}) {
361
608
  const inContext = advanced.hasActiveContext();
362
609
  this.#standalone = !inContext;
363
610
  this.#silent = options.silent ?? false;
364
611
  this.#traceDir = inContext ? advanced.getTraceDirFromContext() ?? advanced.resolveTraceDir({ dir: options.traceDir }) : advanced.resolveTraceDir({ dir: options.traceDir });
365
- 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();
366
614
  this.#runName = options.runName ?? "langchain-agent";
367
615
  this.#safety = advanced.resolveTraceSafetyOptions({
368
616
  redact: options.redact ? { rules: options.redact } : true,
369
617
  maxPreviewLength: options.maxPreviewChars
370
618
  });
619
+ this.#lifecycle = createInvocationState(this.#runId);
371
620
  }
372
621
  get runId() {
373
622
  return this.#runId;
@@ -375,21 +624,183 @@ var LangChainTracePersistence = class {
375
624
  get traceDir() {
376
625
  return this.#traceDir;
377
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
+ }
378
670
  reset() {
379
- this.#finalizationToken += 1;
380
- this.#runStarted = false;
381
- this.#runCompleted = false;
382
- this.#runStartTime = void 0;
383
- this.#rootLcRunId = void 0;
384
- this.#activeLcRunIds.clear();
385
- this.#sawError = false;
386
- this.#lastErrorMessage = void 0;
671
+ resetInvocationState(this.#lifecycle);
672
+ this.#clearStepIndexes();
673
+ this.#lateEventCount = 0;
674
+ }
675
+ #clearStepIndexes() {
387
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}`;
388
693
  }
389
- noteRoot(lcRunId, parentRunId) {
390
- if (!parentRunId && !this.#rootLcRunId) {
391
- this.#rootLcRunId = lcRunId;
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);
392
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
+ };
792
+ }
793
+ /** Rotate when a prior standalone invocation already finalized. */
794
+ #prepareForStart() {
795
+ if (this.#standalone && this.#lifecycle.finalized) {
796
+ this.beginNewInvocation();
797
+ }
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) {
393
804
  }
394
805
  resolveParentId(lcParentRunId) {
395
806
  if (!lcParentRunId) return void 0;
@@ -397,27 +808,33 @@ var LangChainTracePersistence = class {
397
808
  }
398
809
  async onStepStart(params) {
399
810
  try {
400
- this.#finalizationToken += 1;
401
- this.noteRoot(params.lcRunId, params.lcParentRunId);
402
- this.#activeLcRunIds.add(params.lcRunId);
403
- if (this.#standalone && !this.#runStarted) {
404
- await this.#ensureRunStarted(params.startTime, params.attributes);
405
- }
811
+ this.#prepareForStart();
406
812
  const stepId = advanced.createStepId();
407
813
  this.#lcToStepId.set(params.lcRunId, stepId);
408
- const parentId = this.resolveParentId(params.lcParentRunId);
409
- const metadata = toStepMetadata(params.attributes);
410
- if (params.lcParentRunId && !parentId) {
411
- metadata.parentMapping = "unresolved";
412
- 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);
413
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);
414
831
  const event = {
415
832
  schemaVersion: "0.1",
416
833
  event: "step_started",
417
834
  timestamp: params.startTime,
418
835
  runId: this.#runId,
419
836
  stepId,
420
- ...parentId ? { parentId } : {},
837
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
421
838
  name: params.name,
422
839
  type: kindToStepType(params.kind),
423
840
  startTime: params.startTime,
@@ -430,35 +847,58 @@ var LangChainTracePersistence = class {
430
847
  }
431
848
  async onStepEnd(params) {
432
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
+ }
433
854
  let stepId = this.#lcToStepId.get(params.lcRunId);
434
855
  if (!stepId && params.completionAttributes) {
856
+ if (this.#lifecycle.finalized) {
857
+ this.#lateEventCount += 1;
858
+ return;
859
+ }
435
860
  stepId = advanced.createStepId();
436
861
  this.#lcToStepId.set(params.lcRunId, stepId);
437
- const parentId = this.resolveParentId(params.lcParentRunId);
438
- const metadata = toStepMetadata(params.completionAttributes);
439
- if (params.lcParentRunId && !parentId) {
440
- metadata.parentMapping = "unresolved";
441
- metadata.unresolvedParentRunId = params.lcParentRunId;
442
- }
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
+ });
443
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);
444
878
  const started = {
445
879
  schemaVersion: "0.1",
446
880
  event: "step_started",
447
881
  timestamp: startTime,
448
882
  runId: this.#runId,
449
883
  stepId,
450
- ...parentId ? { parentId } : {},
451
- name: String(params.completionAttributes.name ?? "llm:llm"),
884
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
885
+ name: synthName,
452
886
  type: kindToStepType(
453
887
  params.completionAttributes.kind ?? "LLM"
454
888
  ),
455
889
  startTime,
456
890
  metadata
457
891
  };
892
+ if (this.#standalone && !this.#lifecycle.envelopeStarted) {
893
+ await this.#ensureRunStarted(startTime, params.completionAttributes);
894
+ }
458
895
  await this.#write(started);
459
896
  }
460
897
  if (!stepId) return;
461
- 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
+ );
462
902
  const event = {
463
903
  schemaVersion: "0.1",
464
904
  event: "step_completed",
@@ -472,10 +912,12 @@ var LangChainTracePersistence = class {
472
912
  };
473
913
  await this.#write(event);
474
914
  if (params.status === "error") {
475
- this.#sawError = true;
476
- if (params.errorMessage) this.#lastErrorMessage = params.errorMessage;
915
+ noteTerminalError(
916
+ this.#lifecycle,
917
+ params.errorMessage ?? "adapter step error"
918
+ );
477
919
  }
478
- this.#activeLcRunIds.delete(params.lcRunId);
920
+ endCallbackRun(this.#lifecycle, params.lcRunId);
479
921
  await this.#scheduleStandaloneFinalization(params.endTime);
480
922
  } catch (err) {
481
923
  this.#warn(err);
@@ -484,27 +926,33 @@ var LangChainTracePersistence = class {
484
926
  /** Point-in-time adapter events (e.g. agent action) — writes start + completed pair. */
485
927
  async onInstantStep(params) {
486
928
  try {
487
- this.#finalizationToken += 1;
488
- this.noteRoot(params.lcRunId, params.lcParentRunId);
489
- this.#activeLcRunIds.add(params.lcRunId);
490
- if (this.#standalone && !this.#runStarted) {
491
- await this.#ensureRunStarted(params.timestamp, params.attributes);
492
- }
929
+ this.#prepareForStart();
493
930
  const stepId = advanced.createStepId();
494
931
  this.#lcToStepId.set(params.lcRunId, stepId);
495
- const parentId = this.resolveParentId(params.lcParentRunId);
496
- const metadata = toStepMetadata(params.attributes);
497
- if (params.lcParentRunId && !parentId) {
498
- metadata.parentMapping = "unresolved";
499
- 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);
500
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);
501
949
  const started = {
502
950
  schemaVersion: "0.1",
503
951
  event: "step_started",
504
952
  timestamp: params.timestamp,
505
953
  runId: this.#runId,
506
954
  stepId,
507
- ...parentId ? { parentId } : {},
955
+ ...resolution.parentStepId ? { parentId: resolution.parentStepId } : {},
508
956
  name: params.name,
509
957
  type: kindToStepType(params.kind),
510
958
  startTime: params.timestamp,
@@ -524,15 +972,71 @@ var LangChainTracePersistence = class {
524
972
  };
525
973
  await this.#write(completed);
526
974
  if (params.status === "error") {
527
- this.#sawError = true;
528
- if (params.errorMessage) this.#lastErrorMessage = params.errorMessage;
975
+ noteTerminalError(
976
+ this.#lifecycle,
977
+ params.errorMessage ?? "adapter step error"
978
+ );
529
979
  }
530
- this.#activeLcRunIds.delete(params.lcRunId);
980
+ endCallbackRun(this.#lifecycle, params.lcRunId);
531
981
  await this.#scheduleStandaloneFinalization(params.timestamp);
532
982
  } catch (err) {
533
983
  this.#warn(err);
534
984
  }
535
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
+ }
536
1040
  /**
537
1041
  * When the last active LangChain callback ends, yield one microtask so a
538
1042
  * same-turn sibling start can cancel finalization, then write run_completed
@@ -540,24 +1044,20 @@ var LangChainTracePersistence = class {
540
1044
  * block the envelope.
541
1045
  */
542
1046
  async #scheduleStandaloneFinalization(endTime) {
543
- if (!this.#standalone || this.#runCompleted || !this.#runStarted) return;
544
- if (this.#activeLcRunIds.size > 0) return;
545
- const token = ++this.#finalizationToken;
1047
+ if (!this.#standalone || !canScheduleFinalize(this.#lifecycle)) return;
1048
+ const generation = this.#lifecycle.completionGeneration;
546
1049
  await Promise.resolve();
547
- if (token !== this.#finalizationToken) return;
548
- if (!this.#standalone || this.#runCompleted || !this.#runStarted) return;
549
- if (this.#activeLcRunIds.size > 0) return;
550
- 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";
551
1053
  await this.#ensureRunCompleted(
552
1054
  endTime,
553
1055
  status,
554
- status === "error" ? this.#lastErrorMessage : void 0
1056
+ status === "error" ? this.#lifecycle.terminalError?.message : void 0
555
1057
  );
556
1058
  }
557
1059
  async #ensureRunStarted(startTime, attrs) {
558
- if (this.#runStarted) return;
559
- this.#runStarted = true;
560
- this.#runStartTime = startTime;
1060
+ if (!markEnvelopeStarted(this.#lifecycle, startTime)) return;
561
1061
  await advanced.initializeTraceFile(this.#runId, this.#traceDir);
562
1062
  const metadata = {
563
1063
  adapter: "langchain",
@@ -577,9 +1077,21 @@ var LangChainTracePersistence = class {
577
1077
  await this.#write(event);
578
1078
  }
579
1079
  async #ensureRunCompleted(endTime, stepStatus, errorMessage) {
580
- if (this.#runCompleted || !this.#runStarted) return;
581
- this.#runCompleted = true;
582
- 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;
583
1095
  const durationMs = Math.max(0, endTime - startTime);
584
1096
  const runStatus = stepStatus === "error" ? "error" : "success";
585
1097
  const event = {
@@ -609,20 +1121,11 @@ var LangChainTracePersistence = class {
609
1121
  function isRecord2(v) {
610
1122
  return typeof v === "object" && v !== null && !Array.isArray(v);
611
1123
  }
612
- function serializedLabel(s) {
1124
+ function serializedLabel2(s) {
613
1125
  if (typeof s.name === "string" && s.name.trim()) return s.name;
614
1126
  if (Array.isArray(s.id) && s.id.length > 0) return s.id[s.id.length - 1];
615
1127
  return s.type;
616
1128
  }
617
- function resolveToolDisplayName(tool, runName, metadata) {
618
- const fromRun = typeof runName === "string" ? runName.trim() : "";
619
- if (fromRun) return fromRun;
620
- const metaName = metadata?.toolName;
621
- if (typeof metaName === "string" && metaName.trim()) return metaName.trim();
622
- const metaTool = metadata?.tool;
623
- if (typeof metaTool === "string" && metaTool.trim()) return metaTool.trim();
624
- return serializedLabel(tool) ?? "tool";
625
- }
626
1129
  function errorShape(err) {
627
1130
  if (err instanceof Error) {
628
1131
  return { errorName: err.name, errorMessage: err.message };
@@ -631,6 +1134,11 @@ function errorShape(err) {
631
1134
  }
632
1135
  var AgentInspectCallback = class extends base.BaseCallbackHandler {
633
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;
634
1142
  #opts;
635
1143
  #redactor;
636
1144
  #persistence;
@@ -641,16 +1149,22 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
641
1149
  #rootRunId;
642
1150
  constructor(options = {}) {
643
1151
  super({});
1152
+ const intent = resolvePersistIntent(options);
644
1153
  this.#opts = {
645
1154
  capture: options.capture ?? "metadata-only",
646
1155
  silent: options.silent ?? false,
647
1156
  maxPreviewChars: options.maxPreviewChars ?? 200,
648
- persist: options.persist ?? false,
649
1157
  runName: options.runName ?? "langchain-agent",
650
- ...options
1158
+ ...options,
1159
+ persist: intent.persist
651
1160
  };
652
1161
  this.#redactor = new logs.Redactor({ rules: this.#opts.redact });
653
- 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) {
654
1168
  this.#persistence = new LangChainTracePersistence({
655
1169
  runName: this.#opts.runName,
656
1170
  traceDir: this.#opts.traceDir,
@@ -676,6 +1190,91 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
676
1190
  this.#rootRunId = void 0;
677
1191
  this.#persistence?.reset();
678
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
+ }
679
1278
  #streamPreviewLimit() {
680
1279
  if (this.#opts.capture !== "preview") return 0;
681
1280
  return this.#opts.maxStreamPreviewChars ?? this.#opts.maxPreviewChars ?? 200;
@@ -770,7 +1369,11 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
770
1369
  if (parentRunId) out.parentRunId = parentRunId;
771
1370
  if (this.#opts.runName) out.adapterRunName = this.#opts.runName;
772
1371
  if (runNameArg) out.runName = runNameArg;
773
- if (this.#opts.traceDir) out.traceDir = this.#opts.traceDir;
1372
+ if (this.#opts.persist) out.traceStorage = "local";
1373
+ const configuredTraceDir = this.#opts.traceDir?.trim();
1374
+ if (configuredTraceDir && !path__default.default.isAbsolute(configuredTraceDir) && !/^[A-Za-z]:[\\/]/.test(configuredTraceDir)) {
1375
+ out.workspaceRelativeTraceDir = configuredTraceDir;
1376
+ }
774
1377
  const cap = this.#opts.capture;
775
1378
  if (cap !== "none" && tags?.length) out.tags = [...tags];
776
1379
  return out;
@@ -833,9 +1436,10 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
833
1436
  });
834
1437
  }
835
1438
  async handleChainStart(chain, inputs, runId, runType, tags, metadata, runName, parentRunId, _extra) {
1439
+ this.#prepareCallbackInvocation();
836
1440
  this.#ensureRoot(runId, parentRunId);
837
1441
  this.#rememberStart(runId, "CHAIN");
838
- const label = serializedLabel(chain) ?? "chain";
1442
+ const label = serializedLabel2(chain) ?? "chain";
839
1443
  const previews = {};
840
1444
  if (this.#opts.capture === "preview") previews.inputPreview = inputs;
841
1445
  const attrs = {
@@ -914,6 +1518,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
914
1518
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
915
1519
  }
916
1520
  async handleLLMStart(llm, prompts, runId, parentRunId, _extraParams, tags, metadata, runName) {
1521
+ this.#prepareCallbackInvocation();
917
1522
  this.#ensureRoot(runId, parentRunId);
918
1523
  this.#rememberStart(runId, "LLM");
919
1524
  const model = extractModelName(llm);
@@ -946,6 +1551,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
946
1551
  await this.#persistLlmStepStart(runId, parentRunId, stepName, "LLM", ts, attrs);
947
1552
  }
948
1553
  async handleChatModelStart(llm, messages, runId, parentRunId, _extraParams, tags, metadata, runName) {
1554
+ this.#prepareCallbackInvocation();
949
1555
  this.#ensureRoot(runId, parentRunId);
950
1556
  this.#rememberStart(runId, "LLM");
951
1557
  const model = extractModelName(llm);
@@ -1064,21 +1670,22 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1064
1670
  });
1065
1671
  this.#clearStreamState(runId);
1066
1672
  }
1067
- async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName, _toolCallId) {
1673
+ async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName, toolCallId) {
1674
+ this.#prepareCallbackInvocation();
1068
1675
  this.#ensureRoot(runId, parentRunId);
1069
1676
  this.#rememberStart(runId, "TOOL");
1070
- const toolName = resolveToolDisplayName(tool, runName, metadata);
1677
+ const identity = resolveToolIdentity(tool, runName, metadata, toolCallId);
1071
1678
  const previews = {};
1072
1679
  if (this.#opts.capture === "preview") previews.inputPreview = input;
1073
1680
  const attrs = {
1074
- ...this.#baseAttrs(runId, parentRunId, tags, runName),
1075
- tool: toolName
1681
+ ...this.#baseAttrs(runId, parentRunId, tags, runName)
1076
1682
  };
1683
+ applyToolIdentityAttributes(attrs, identity);
1077
1684
  this.#mergeMetadata(attrs, metadata);
1078
1685
  this.#applyPreview(attrs, previews);
1079
1686
  this.#rememberStartMetadata(runId, attrs);
1080
1687
  const ts = Date.now();
1081
- const stepName = `tool:${toolName}`;
1688
+ const stepName = `tool:${identity.displayName}`;
1082
1689
  this.#pushEvent({
1083
1690
  eventId: `${runId}:TOOL:start`,
1084
1691
  runId: this.#traceRunId(runId),
@@ -1148,9 +1755,10 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1148
1755
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
1149
1756
  }
1150
1757
  async handleRetrieverStart(retriever, _query, runId, parentRunId, tags, metadata, name) {
1758
+ this.#prepareCallbackInvocation();
1151
1759
  this.#ensureRoot(runId, parentRunId);
1152
1760
  this.#rememberStart(runId, "RETRIEVER");
1153
- const rname = name ?? serializedLabel(retriever) ?? "retriever";
1761
+ const rname = name ?? serializedLabel2(retriever) ?? "retriever";
1154
1762
  const attrs = {
1155
1763
  ...this.#baseAttrs(runId, parentRunId, tags, void 0),
1156
1764
  retriever: rname
@@ -1231,6 +1839,7 @@ var AgentInspectCallback = class extends base.BaseCallbackHandler {
1231
1839
  await this.#persistStepEnd(runId, parentRunId, "error", ts, durationMs, errorMessage);
1232
1840
  }
1233
1841
  async handleAgentAction(action, runId, parentRunId, tags) {
1842
+ this.#prepareCallbackInvocation();
1234
1843
  this.#ensureRoot(runId, parentRunId);
1235
1844
  const attrs = {
1236
1845
  ...this.#baseAttrs(runId, parentRunId, tags, void 0),