@saptools/service-flow 0.1.50 → 0.1.51

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.
@@ -2842,8 +2842,8 @@ function buildRemoteQueryTarget(input) {
2842
2842
  // src/linker/service-resolver.ts
2843
2843
  function rows(db, operationPath, workspaceId) {
2844
2844
  const names = operationLookupNames(operationPath);
2845
- return db.prepare(
2846
- `SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score,'' reasons
2845
+ const result = db.prepare(
2846
+ `SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score
2847
2847
  FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id
2848
2848
  WHERE (? IS NULL OR r.workspace_id=?) AND (o.operation_path IN (?,?) OR o.operation_name IN (?,?)) ORDER BY r.name,s.service_path,o.operation_name`
2849
2849
  ).all(
@@ -2854,6 +2854,11 @@ function rows(db, operationPath, workspaceId) {
2854
2854
  names.name,
2855
2855
  names.simpleName
2856
2856
  );
2857
+ return result.map((row) => ({
2858
+ ...row,
2859
+ score: Number(row.score ?? 0),
2860
+ reasons: []
2861
+ }));
2857
2862
  }
2858
2863
  function operationLookupNames(operationPath) {
2859
2864
  const name = operationPath.replace(/^\//, "");
@@ -2870,7 +2875,11 @@ function resolveOperation(db, signals, workspaceId) {
2870
2875
  if (missing.length > 0)
2871
2876
  return {
2872
2877
  status: "dynamic",
2873
- candidates: signals.operationPath ? rows(db, signals.operationPath, workspaceId) : [],
2878
+ candidates: signals.operationPath ? rows(db, signals.operationPath, workspaceId).map((candidate) => ({
2879
+ ...candidate,
2880
+ score: 0.2,
2881
+ reasons: ["operation_path_match"]
2882
+ })) : [],
2874
2883
  reasons: [...new Set(missing)].map((name) => `missing_variable:${name}`)
2875
2884
  };
2876
2885
  if (!signals.operationPath)
@@ -3233,7 +3242,13 @@ function compactCandidateScores(candidates) {
3233
3242
  return candidates.flatMap((candidate) => {
3234
3243
  const row = objectValue(candidate);
3235
3244
  if (!row) return [];
3236
- return [{ repo: row.repoName, servicePath: row.servicePath, operationPath: row.operationPath, score: row.score, reasons: row.reasons }];
3245
+ return [{
3246
+ repo: row.repoName,
3247
+ servicePath: row.servicePath,
3248
+ operationPath: row.operationPath,
3249
+ score: row.score,
3250
+ reasons: Array.isArray(row.reasons) ? row.reasons.filter((reason) => typeof reason === "string") : ["operation_path_match"]
3251
+ }];
3237
3252
  });
3238
3253
  }
3239
3254
  function placeholderKeys(values) {
@@ -3642,6 +3657,303 @@ function fullSelectorSuggestions(candidates) {
3642
3657
  }))].sort();
3643
3658
  }
3644
3659
 
3660
+ // src/trace/dynamic-targets.ts
3661
+ function analyzeDynamicTargetCandidates(db, evidence, workspaceId, mode, maxCandidates) {
3662
+ const templates = templatesFromEvidence(evidence);
3663
+ const missingVariables = allMissingVariables(evidence, templates);
3664
+ if (missingVariables.length === 0) return void 0;
3665
+ const order = variableOrder(templates, missingVariables);
3666
+ const candidates = rankedCandidates(
3667
+ db,
3668
+ candidateTargets(db, evidence, workspaceId),
3669
+ referenceRows(db, workspaceId),
3670
+ templates,
3671
+ order
3672
+ );
3673
+ const inference = inferenceDecision(candidates);
3674
+ const shownCandidates = candidates.slice(0, maxCandidates);
3675
+ return {
3676
+ mode,
3677
+ candidateCount: candidates.length,
3678
+ shownCandidateCount: shownCandidates.length,
3679
+ omittedCandidateCount: Math.max(0, candidates.length - shownCandidates.length),
3680
+ missingVariables,
3681
+ candidates,
3682
+ shownCandidates,
3683
+ suggestedVarSets: suggestedVarSets(candidates, missingVariables, order),
3684
+ inference
3685
+ };
3686
+ }
3687
+ function candidateTargets(db, evidence, workspaceId) {
3688
+ const embedded = rowsFromEvidence(evidence.candidates);
3689
+ if (embedded.length > 0) return embedded;
3690
+ const operationPath = stringValue4(evidence.operationPath);
3691
+ if (!operationPath || extractPlaceholders(operationPath).length > 0) return [];
3692
+ return queryOperationTargets(db, operationPath, workspaceId);
3693
+ }
3694
+ function rowsFromEvidence(value) {
3695
+ if (!Array.isArray(value)) return [];
3696
+ return value.flatMap((item) => {
3697
+ const row = record(item);
3698
+ const operationId = numberValue(row.operationId);
3699
+ const repoName = stringValue4(row.repoName);
3700
+ const servicePath = stringValue4(row.servicePath);
3701
+ const operationPath = stringValue4(row.operationPath);
3702
+ const operationName = stringValue4(row.operationName) ?? operationPath?.replace(/^\//, "");
3703
+ if (operationId === void 0 || !repoName || !servicePath || !operationPath || !operationName) return [];
3704
+ return [{
3705
+ operationId,
3706
+ repoName,
3707
+ serviceName: stringValue4(row.serviceName) ?? "",
3708
+ qualifiedName: stringValue4(row.qualifiedName) ?? "",
3709
+ servicePath,
3710
+ operationPath,
3711
+ operationName,
3712
+ sourceFile: stringValue4(row.sourceFile) ?? "",
3713
+ sourceLine: numberValue(row.sourceLine) ?? 0,
3714
+ repoId: numberValue(row.repoId),
3715
+ packageName: stringValue4(row.packageName),
3716
+ score: numberValue(row.score) ?? 0,
3717
+ reasons: stringArray(row.reasons)
3718
+ }];
3719
+ });
3720
+ }
3721
+ function queryOperationTargets(db, operationPath, workspaceId) {
3722
+ const simple = operationPath.replace(/^\//, "").split(".").at(-1) ?? operationPath;
3723
+ const rows2 = db.prepare(
3724
+ `SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,
3725
+ s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,
3726
+ o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,
3727
+ o.source_line sourceLine
3728
+ FROM cds_operations o JOIN cds_services s ON s.id=o.service_id
3729
+ JOIN repositories r ON r.id=s.repo_id
3730
+ WHERE (? IS NULL OR r.workspace_id=?)
3731
+ AND (o.operation_path IN (?,?) OR o.operation_name=?)
3732
+ ORDER BY r.name,s.service_path,o.operation_name`
3733
+ ).all(workspaceId, workspaceId, operationPath, `/${simple}`, simple);
3734
+ return rows2.map((row) => ({
3735
+ operationId: Number(row.operationId),
3736
+ repoId: numberValue(row.repoId),
3737
+ repoName: String(row.repoName),
3738
+ packageName: stringValue4(row.packageName),
3739
+ serviceName: String(row.serviceName),
3740
+ qualifiedName: String(row.qualifiedName),
3741
+ servicePath: String(row.servicePath),
3742
+ operationPath: String(row.operationPath),
3743
+ operationName: String(row.operationName),
3744
+ sourceFile: String(row.sourceFile),
3745
+ sourceLine: Number(row.sourceLine),
3746
+ score: 0.2,
3747
+ reasons: ["operation_path_match"]
3748
+ }));
3749
+ }
3750
+ function rankedCandidates(db, candidates, references, templates, order) {
3751
+ const ranked = candidates.map((candidate) => candidateEvidence2(db, candidate, references, templates, order));
3752
+ return ranked.sort((a, b) => b.score - a.score || a.repoName.localeCompare(b.repoName) || a.servicePath.localeCompare(b.servicePath));
3753
+ }
3754
+ function candidateEvidence2(db, candidate, references, templates, order) {
3755
+ const state = emptyCandidate(candidate);
3756
+ applyDirectTemplate(state, templates.operationPath, candidate.operationPath, "operation_path");
3757
+ applyDirectTemplate(state, templates.servicePath, candidate.servicePath, "service_path");
3758
+ const refs = references.filter((item) => item.servicePath === candidate.servicePath);
3759
+ applyReferenceTemplate(state, templates.alias, refs, "alias");
3760
+ applyReferenceTemplate(state, templates.destination, refs, "destination");
3761
+ if (hasResolvedImplementation(db, candidate.operationId)) addScore(state, 0.1, "implementation_edge_resolved");
3762
+ state.missingVariables = order.filter((key) => state.derivedVariables[key] === void 0);
3763
+ if (state.missingVariables.length === 0) addScore(state, 0.15, "all_runtime_variables_derived");
3764
+ else state.rejectedReasons.push("missing_required_runtime_variable");
3765
+ state.score = Math.max(0, Math.min(1, state.score));
3766
+ state.cli = state.missingVariables.length === 0 ? cliFor(state.derivedVariables, order) : void 0;
3767
+ return state;
3768
+ }
3769
+ function emptyCandidate(candidate) {
3770
+ return {
3771
+ candidateOperationId: candidate.operationId,
3772
+ repoName: candidate.repoName,
3773
+ packageName: candidate.packageName ?? void 0,
3774
+ servicePath: candidate.servicePath,
3775
+ operationPath: candidate.operationPath,
3776
+ operationName: candidate.operationName,
3777
+ derivedVariables: {},
3778
+ derivedVariableSources: {},
3779
+ missingVariables: [],
3780
+ score: Math.max(0.2, Number(candidate.score ?? 0)),
3781
+ reasons: nonEmptyStrings(candidate.reasons, ["operation_path_match"]),
3782
+ rejectedReasons: []
3783
+ };
3784
+ }
3785
+ function applyDirectTemplate(state, template, concrete, kind) {
3786
+ const matched = matchTemplate(template, concrete);
3787
+ if (!template) return;
3788
+ if (!matched) {
3789
+ state.rejectedReasons.push(`${kind}_template_mismatch`);
3790
+ return;
3791
+ }
3792
+ mergeDerived(state, matched, `${kind}_template`);
3793
+ addScore(state, kind === "service_path" ? 0.35 : 0.25, `${kind}_template_match`);
3794
+ }
3795
+ function applyReferenceTemplate(state, template, refs, kind) {
3796
+ if (!template || extractPlaceholders(template).length === 0) return;
3797
+ for (const ref of refs) {
3798
+ const concrete = kind === "alias" ? ref.alias : ref.destination;
3799
+ const matched = isConcrete(concrete) ? matchTemplate(template, concrete) : void 0;
3800
+ if (!matched) continue;
3801
+ mergeDerived(state, matched, `${ref.sourceKind}.${kind}`);
3802
+ addScore(state, 0.2, `${kind}_template_match`);
3803
+ return;
3804
+ }
3805
+ if (refs.some((ref) => isConcrete(kind === "alias" ? ref.alias : ref.destination)))
3806
+ state.rejectedReasons.push(`${kind}_template_mismatch`);
3807
+ }
3808
+ function inferenceDecision(candidates) {
3809
+ const complete = candidates.filter((candidate) => candidate.missingVariables.length === 0 && candidate.rejectedReasons.length === 0);
3810
+ const first = complete[0];
3811
+ const second = complete[1];
3812
+ if (!first) return { status: "unresolved", reason: "missing_required_runtime_variable" };
3813
+ if (first.score < 0.85) return { status: "unresolved", reason: "candidate_score_below_inference_threshold" };
3814
+ if (second && first.score - second.score <= 0.05) {
3815
+ for (const candidate of complete.filter((item) => first.score - item.score <= 0.05))
3816
+ candidate.rejectedReasons.push("candidate_tied_with_equal_score");
3817
+ return { status: "ambiguous", reason: "candidate_tied_with_equal_score" };
3818
+ }
3819
+ return {
3820
+ status: "resolved",
3821
+ candidateOperationId: first.candidateOperationId,
3822
+ inferredVariables: first.derivedVariables,
3823
+ score: first.score,
3824
+ reasons: first.reasons
3825
+ };
3826
+ }
3827
+ function suggestedVarSets(candidates, required, order) {
3828
+ const seen = /* @__PURE__ */ new Set();
3829
+ return candidates.flatMap((candidate) => {
3830
+ if (required.some((key) => candidate.derivedVariables[key] === void 0)) return [];
3831
+ const cli = cliFor(candidate.derivedVariables, order);
3832
+ if (seen.has(cli)) return [];
3833
+ seen.add(cli);
3834
+ return [{ variables: candidate.derivedVariables, cli }];
3835
+ });
3836
+ }
3837
+ function referenceRows(db, workspaceId) {
3838
+ const rows2 = db.prepare(
3839
+ `SELECT req.alias alias,req.destination destination,req.service_path servicePath,
3840
+ 'cds_require' sourceKind,r.name repoName
3841
+ FROM cds_requires req JOIN repositories r ON r.id=req.repo_id
3842
+ WHERE (? IS NULL OR r.workspace_id=?)
3843
+ UNION ALL
3844
+ SELECT COALESCE(b.alias,b.alias_expr) alias,b.destination_expr destination,
3845
+ b.service_path_expr servicePath,'service_binding' sourceKind,r.name repoName
3846
+ FROM service_bindings b JOIN repositories r ON r.id=b.repo_id
3847
+ WHERE (? IS NULL OR r.workspace_id=?)`
3848
+ ).all(workspaceId, workspaceId, workspaceId, workspaceId);
3849
+ return rows2.map((row) => ({
3850
+ alias: stringValue4(row.alias),
3851
+ destination: stringValue4(row.destination),
3852
+ servicePath: stringValue4(row.servicePath),
3853
+ sourceKind: String(row.sourceKind),
3854
+ repoName: String(row.repoName)
3855
+ }));
3856
+ }
3857
+ function allMissingVariables(evidence, templates) {
3858
+ const fromSubstitutions = Object.values(record(evidence.runtimeSubstitutions)).flatMap((value) => stringArray(record(value).missing));
3859
+ const fromTemplates = [
3860
+ templates.servicePath,
3861
+ templates.operationPath,
3862
+ templates.alias,
3863
+ templates.destination
3864
+ ].flatMap((value) => extractPlaceholders(value));
3865
+ return [.../* @__PURE__ */ new Set([...fromSubstitutions, ...fromTemplates])].sort();
3866
+ }
3867
+ function variableOrder(templates, missingVariables) {
3868
+ const ordered = [
3869
+ templates.servicePath,
3870
+ templates.operationPath,
3871
+ templates.alias,
3872
+ templates.destination
3873
+ ].flatMap((value) => extractPlaceholders(value));
3874
+ return [.../* @__PURE__ */ new Set([...ordered, ...missingVariables])];
3875
+ }
3876
+ function templatesFromEvidence(evidence) {
3877
+ return {
3878
+ servicePath: stringValue4(evidence.servicePath),
3879
+ operationPath: stringValue4(evidence.operationPath),
3880
+ alias: stringValue4(evidence.serviceAliasExpr ?? evidence.serviceAlias),
3881
+ destination: stringValue4(evidence.destination)
3882
+ };
3883
+ }
3884
+ function matchTemplate(template, concrete) {
3885
+ if (!template || !concrete) return void 0;
3886
+ const keys = extractPlaceholders(template);
3887
+ if (keys.length === 0) return template === concrete ? {} : void 0;
3888
+ const regex = new RegExp(`^${templateToPattern(template)}$`);
3889
+ const match = regex.exec(concrete);
3890
+ if (!match) return void 0;
3891
+ const values = {};
3892
+ for (let index = 0; index < keys.length; index += 1) {
3893
+ const key = keys[index];
3894
+ const value = match[index + 1];
3895
+ if (!key || value === void 0) return void 0;
3896
+ if (values[key] !== void 0 && values[key] !== value) return void 0;
3897
+ values[key] = value;
3898
+ }
3899
+ return values;
3900
+ }
3901
+ function templateToPattern(template) {
3902
+ let pattern = "";
3903
+ let lastIndex = 0;
3904
+ for (const match of template.matchAll(/\$\{([^}]*)\}/g)) {
3905
+ pattern += escapeRegex(template.slice(lastIndex, match.index));
3906
+ pattern += "([^/]+?)";
3907
+ lastIndex = (match.index ?? 0) + match[0].length;
3908
+ }
3909
+ return `${pattern}${escapeRegex(template.slice(lastIndex))}`;
3910
+ }
3911
+ function mergeDerived(state, values, sourceKind) {
3912
+ for (const [key, value] of Object.entries(values)) {
3913
+ if (state.derivedVariables[key] !== void 0 && state.derivedVariables[key] !== value) {
3914
+ state.rejectedReasons.push("template_variable_conflict");
3915
+ continue;
3916
+ }
3917
+ state.derivedVariables[key] = value;
3918
+ state.derivedVariableSources[key] = { sourceKind, value };
3919
+ }
3920
+ }
3921
+ function addScore(state, amount, reason) {
3922
+ state.score += amount;
3923
+ if (!state.reasons.includes(reason)) state.reasons.push(reason);
3924
+ }
3925
+ function hasResolvedImplementation(db, operationId) {
3926
+ const row = db.prepare(
3927
+ "SELECT 1 FROM graph_edges WHERE edge_type='OPERATION_IMPLEMENTED_BY_HANDLER' AND status='resolved' AND from_kind='operation' AND from_id=? LIMIT 1"
3928
+ ).get(String(operationId));
3929
+ return Boolean(row);
3930
+ }
3931
+ function cliFor(variables, order) {
3932
+ return order.filter((key) => variables[key] !== void 0).map((key) => `--var ${key}=${variables[key]}`).join(" ");
3933
+ }
3934
+ function record(value) {
3935
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3936
+ }
3937
+ function stringValue4(value) {
3938
+ return typeof value === "string" ? value : void 0;
3939
+ }
3940
+ function numberValue(value) {
3941
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3942
+ }
3943
+ function stringArray(value) {
3944
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
3945
+ }
3946
+ function nonEmptyStrings(value, fallback) {
3947
+ const values = stringArray(value).filter((item) => item.length > 0);
3948
+ return values.length > 0 ? values : fallback;
3949
+ }
3950
+ function isConcrete(value) {
3951
+ return typeof value === "string" && value.length > 0 && extractPlaceholders(value).length === 0;
3952
+ }
3953
+ function escapeRegex(value) {
3954
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3955
+ }
3956
+
3645
3957
  // src/trace/evidence.ts
3646
3958
  function baseTraceEvidence(row, call, persistedEvidence, contextualEvidence) {
3647
3959
  const evidence = { ...persistedEvidence, ...contextualEvidence ?? {} };
@@ -3660,23 +3972,45 @@ function baseTraceEvidence(row, call, persistedEvidence, contextualEvidence) {
3660
3972
  persistedResolution: persistedResolution(row)
3661
3973
  };
3662
3974
  }
3663
- function runtimeResolution(db, row, evidence, vars, workspaceId, contextualUnresolvedReason) {
3664
- const substituted = evidenceWithRuntimeVariables(evidence, vars);
3665
- if (!isRemoteRuntimeCandidate(row, evidence, vars)) {
3975
+ function runtimeResolution(db, row, evidence, options, workspaceId, contextualUnresolvedReason) {
3976
+ const substituted = evidenceWithRuntimeVariables(evidence, options.vars);
3977
+ const dynamicMode = options.dynamicMode ?? "strict";
3978
+ const analysis = analyzeDynamicTargetCandidates(
3979
+ db,
3980
+ substituted,
3981
+ workspaceId,
3982
+ dynamicMode,
3983
+ positiveCandidateCap(options.maxDynamicCandidates)
3984
+ );
3985
+ const enriched = analysis ? evidenceWithDynamicAnalysis(substituted, analysis) : substituted;
3986
+ if (dynamicMode === "infer") {
3987
+ const inferred = inferredTarget(analysis);
3988
+ if (inferred) {
3989
+ const resolvedRow = { ...row, status: "resolved", to_kind: "operation", to_id: String(inferred.operationId), unresolved_reason: void 0, confidence: inferred.score };
3990
+ const resolution2 = { status: "resolved", target: inferred, candidates: [], reasons: inferred.reasons };
3991
+ return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, void 0, resolution2), target: inferred };
3992
+ }
3993
+ }
3994
+ if (!isRemoteRuntimeCandidate(row, evidence, options.vars)) {
3666
3995
  const unresolvedReason2 = contextualUnresolvedReason ?? row.unresolved_reason;
3667
- const withSections = withEffectiveResolution(substituted, row, unresolvedReason2);
3996
+ const withSections = withEffectiveResolution(enriched, row, unresolvedReason2);
3668
3997
  return { row, evidence: withSections, unresolvedReason: unresolvedReason2 };
3669
3998
  }
3670
- const resolution = resolveRuntimeOperation(db, substituted, workspaceId);
3999
+ const resolution = resolveRuntimeOperation(db, enriched, workspaceId);
3671
4000
  if (resolution.target) {
3672
4001
  const resolvedRow = { ...row, status: "resolved", to_kind: "operation", to_id: String(resolution.target.operationId), unresolved_reason: void 0, confidence: Math.max(0, Math.min(1, resolution.target.score)) };
3673
- return { row: resolvedRow, evidence: withEffectiveResolution(substituted, resolvedRow, void 0, resolution), target: resolution.target };
4002
+ return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, void 0, resolution), target: resolution.target };
3674
4003
  }
3675
4004
  const unresolvedReason = runtimeUnresolvedReason(resolution);
3676
- return { row, evidence: withEffectiveResolution(substituted, row, unresolvedReason, resolution), unresolvedReason };
4005
+ return { row, evidence: withEffectiveResolution(enriched, row, unresolvedReason, resolution), unresolvedReason };
3677
4006
  }
3678
4007
  function runtimeVariableDiagnostic(edges) {
3679
4008
  const missing = /* @__PURE__ */ new Set();
4009
+ let candidateCount = 0;
4010
+ let shownCandidateCount = 0;
4011
+ let omittedCandidateCount = 0;
4012
+ const candidateSuggestions = [];
4013
+ const suggestedVarSets2 = [];
3680
4014
  for (const edge of edges) {
3681
4015
  const effective = parseObject(edge.evidence.effectiveResolution);
3682
4016
  if (!["dynamic", "unresolved", "ambiguous"].includes(String(effective.status ?? "")))
@@ -3685,6 +4019,12 @@ function runtimeVariableDiagnostic(edges) {
3685
4019
  if (!substitutions || typeof substitutions !== "object" || Array.isArray(substitutions)) continue;
3686
4020
  for (const value of Object.values(substitutions))
3687
4021
  for (const key of value.missing ?? []) missing.add(key);
4022
+ const exploration = parseObject(edge.evidence.dynamicTargetExploration);
4023
+ candidateCount += numeric(exploration.candidateCount);
4024
+ shownCandidateCount += numeric(exploration.shownCandidateCount);
4025
+ omittedCandidateCount += numeric(exploration.omittedCandidateCount);
4026
+ candidateSuggestions.push(...recordArray(edge.evidence.dynamicTargetCandidateSuggestions));
4027
+ suggestedVarSets2.push(...recordArray(exploration.suggestedVarSets));
3688
4028
  }
3689
4029
  const missingVariables = [...missing].sort();
3690
4030
  if (missingVariables.length === 0) return void 0;
@@ -3693,25 +4033,34 @@ function runtimeVariableDiagnostic(edges) {
3693
4033
  code: "trace_runtime_variables_missing",
3694
4034
  message: `Runtime variables are required to resolve dynamic trace targets: ${missingVariables.join(", ")}`,
3695
4035
  missingVariables,
3696
- suggestions: missingVariables.map((key) => `--var ${key}=<value>`)
4036
+ suggestions: missingVariables.map((key) => `--var ${key}=<value>`),
4037
+ candidateCount,
4038
+ shownCandidateCount,
4039
+ omittedCandidateCount,
4040
+ candidateSuggestions: candidateSuggestions.slice(0, 5),
4041
+ suggestedVarSets: uniqueCliRows(suggestedVarSets2).slice(0, 5),
4042
+ copyableExamples: [
4043
+ ...uniqueCliRows(suggestedVarSets2).slice(0, 3).flatMap((row) => typeof row.cli === "string" ? [row.cli] : []),
4044
+ ...candidateCount > 0 ? ["--dynamic-mode candidates --max-dynamic-candidates 20"] : []
4045
+ ]
3697
4046
  };
3698
4047
  }
3699
4048
  function edgeTarget(row, evidence) {
3700
4049
  const effective = parseObject(evidence.effectiveResolution);
3701
- const targetServicePath = stringValue4(effective.targetServicePath ?? evidence.targetServicePath);
3702
- const targetOperationPath = stringValue4(effective.targetOperationPath ?? evidence.targetOperationPath);
4050
+ const targetServicePath = stringValue5(effective.targetServicePath ?? evidence.targetServicePath);
4051
+ const targetOperationPath = stringValue5(effective.targetOperationPath ?? evidence.targetOperationPath);
3703
4052
  if (targetServicePath && targetOperationPath) return `${targetServicePath}${targetOperationPath}`;
3704
4053
  const runtimeCandidate = evidence.runtimeResolvedCandidate;
3705
4054
  if (runtimeCandidate?.servicePath && runtimeCandidate.operationPath) return `${runtimeCandidate.servicePath}${runtimeCandidate.operationPath}`;
3706
- const servicePath = stringValue4(evidence.servicePath);
3707
- const operationPath = stringValue4(evidence.operationPath);
3708
- const targetOperation = stringValue4(evidence.targetOperation);
3709
- const targetRepo = stringValue4(evidence.targetRepo) ?? "";
4055
+ const servicePath = stringValue5(evidence.servicePath);
4056
+ const operationPath = stringValue5(evidence.operationPath);
4057
+ const targetOperation = stringValue5(evidence.targetOperation);
4058
+ const targetRepo = stringValue5(evidence.targetRepo) ?? "";
3710
4059
  if (row.edge_type === "HANDLER_RUNS_DB_QUERY") return `Entity: ${row.to_id || "unknown"}`;
3711
- if (row.edge_type === "HANDLER_RUNS_REMOTE_QUERY") return stringValue4(evidence.remoteQueryTarget) ?? `Remote query: ${row.to_id || "unknown"}`;
4060
+ if (row.edge_type === "HANDLER_RUNS_REMOTE_QUERY") return stringValue5(evidence.remoteQueryTarget) ?? `Remote query: ${row.to_id || "unknown"}`;
3712
4061
  if (row.edge_type === "HANDLER_CALLS_EXTERNAL_HTTP") {
3713
4062
  const target = parseObject(evidence.externalTarget);
3714
- return stringValue4(target.label) ?? `External endpoint: ${row.to_id || "unknown"}`;
4063
+ return stringValue5(target.label) ?? `External endpoint: ${row.to_id || "unknown"}`;
3715
4064
  }
3716
4065
  if (servicePath && operationPath) return `${servicePath}${operationPath}`;
3717
4066
  return targetOperation ? `${targetRepo}:${targetOperation}` : row.to_id;
@@ -3750,12 +4099,12 @@ function withEffectiveResolution(evidence, row, unresolvedReason, resolution) {
3750
4099
  return { ...rest, effectiveResolution: current, linker: { status: current.status, confidence: current.confidence, reason: unresolvedReason, edgeType: current.edgeType } };
3751
4100
  }
3752
4101
  function resolveRuntimeOperation(db, evidence, workspaceId) {
3753
- const servicePath = stringValue4(evidence.servicePath);
3754
- const rawOperationPath = stringValue4(evidence.operationPath);
4102
+ const servicePath = stringValue5(evidence.servicePath);
4103
+ const rawOperationPath = stringValue5(evidence.operationPath);
3755
4104
  const normalized = normalizeODataOperationInvocationPath(rawOperationPath);
3756
- const operationPath = normalized?.wasInvocation ? normalized.normalizedOperationPath : stringValue4(evidence.normalizedOperationPath) ?? rawOperationPath;
3757
- const alias = stringValue4(evidence.serviceAliasExpr ?? evidence.serviceAlias);
3758
- const destination = stringValue4(evidence.destination);
4105
+ const operationPath = normalized?.wasInvocation ? normalized.normalizedOperationPath : stringValue5(evidence.normalizedOperationPath) ?? rawOperationPath;
4106
+ const alias = stringValue5(evidence.serviceAliasExpr ?? evidence.serviceAlias);
4107
+ const destination = stringValue5(evidence.destination);
3759
4108
  return resolveOperation(db, { servicePath, operationPath, alias, destination, hasExplicitOverride: true, isDynamic: true }, workspaceId);
3760
4109
  }
3761
4110
  function evidenceWithRuntimeVariables(evidence, vars) {
@@ -3767,6 +4116,22 @@ function evidenceWithRuntimeVariables(evidence, vars) {
3767
4116
  if (Object.keys(vars ?? {}).length > 0) next.runtimeVariablesApplied = true;
3768
4117
  return next;
3769
4118
  }
4119
+ function evidenceWithDynamicAnalysis(evidence, analysis) {
4120
+ return {
4121
+ ...evidence,
4122
+ dynamicTargetExploration: {
4123
+ mode: analysis.mode,
4124
+ missingVariables: analysis.missingVariables,
4125
+ candidateCount: analysis.candidateCount,
4126
+ shownCandidateCount: analysis.shownCandidateCount,
4127
+ omittedCandidateCount: analysis.omittedCandidateCount,
4128
+ suggestedVarSets: analysis.suggestedVarSets
4129
+ },
4130
+ dynamicTargetCandidates: analysis.candidates,
4131
+ dynamicTargetCandidateSuggestions: analysis.shownCandidates,
4132
+ dynamicTargetInference: analysis.inference
4133
+ };
4134
+ }
3770
4135
  function runtimeSubstitutions(evidence, vars) {
3771
4136
  const substitutions = {};
3772
4137
  for (const key of ["servicePath", "operationPath", "serviceAliasExpr", "serviceAlias", "destination"]) {
@@ -3776,7 +4141,7 @@ function runtimeSubstitutions(evidence, vars) {
3776
4141
  return substitutions;
3777
4142
  }
3778
4143
  function substitutionValue(evidence, key) {
3779
- const value = stringValue4(evidence[key]);
4144
+ const value = stringValue5(evidence[key]);
3780
4145
  if (key !== "operationPath") return value;
3781
4146
  const normalized = normalizeODataOperationInvocationPath(value);
3782
4147
  return normalized?.wasInvocation ? normalized.normalizedOperationPath : value;
@@ -3787,6 +4152,29 @@ function isRemoteRuntimeCandidate(row, evidence, vars) {
3787
4152
  if (!["DYNAMIC_EDGE_CANDIDATE", "UNRESOLVED_EDGE", "REMOTE_CALL_RESOLVES_TO_OPERATION"].includes(row.edge_type)) return false;
3788
4153
  return ["servicePath", "operationPath", "serviceAliasExpr", "serviceAlias", "destination"].some((key) => hasRuntimeVariable(evidence[key], vars));
3789
4154
  }
4155
+ function inferredTarget(analysis) {
4156
+ if (analysis?.inference.status !== "resolved") return void 0;
4157
+ const id = Number(analysis.inference.candidateOperationId);
4158
+ const candidate = analysis.candidates.find((item) => item.candidateOperationId === id);
4159
+ if (!candidate) return void 0;
4160
+ return targetFromCandidate(candidate);
4161
+ }
4162
+ function targetFromCandidate(candidate) {
4163
+ return {
4164
+ operationId: candidate.candidateOperationId,
4165
+ repoName: candidate.repoName,
4166
+ serviceName: "",
4167
+ qualifiedName: "",
4168
+ servicePath: candidate.servicePath,
4169
+ operationPath: candidate.operationPath,
4170
+ operationName: candidate.operationName,
4171
+ packageName: candidate.packageName,
4172
+ score: candidate.score,
4173
+ reasons: candidate.reasons,
4174
+ sourceFile: "",
4175
+ sourceLine: 0
4176
+ };
4177
+ }
3790
4178
  function hasRuntimeVariable(value, vars) {
3791
4179
  return typeof value === "string" && extractPlaceholders(value).some((key) => Object.hasOwn(vars, key));
3792
4180
  }
@@ -3798,9 +4186,56 @@ function runtimeUnresolvedReason(resolution) {
3798
4186
  function parseObject(value) {
3799
4187
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3800
4188
  }
3801
- function stringValue4(value) {
4189
+ function stringValue5(value) {
3802
4190
  return typeof value === "string" ? value : void 0;
3803
4191
  }
4192
+ function numeric(value) {
4193
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
4194
+ }
4195
+ function recordArray(value) {
4196
+ return Array.isArray(value) ? value.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
4197
+ }
4198
+ function uniqueCliRows(rows2) {
4199
+ const seen = /* @__PURE__ */ new Set();
4200
+ return rows2.filter((row) => {
4201
+ const cli = typeof row.cli === "string" ? row.cli : JSON.stringify(row);
4202
+ if (seen.has(cli)) return false;
4203
+ seen.add(cli);
4204
+ return true;
4205
+ });
4206
+ }
4207
+ function positiveCandidateCap(value) {
4208
+ return Number.isFinite(value) && Number(value) > 0 ? Math.floor(Number(value)) : 5;
4209
+ }
4210
+
4211
+ // src/trace/dynamic-branches.ts
4212
+ function dynamicCandidateBranches(depth, call, evidence) {
4213
+ const exploration = objectRecord(evidence.dynamicTargetExploration);
4214
+ return recordArray2(evidence.dynamicTargetCandidateSuggestions).map((candidate) => ({
4215
+ step: depth,
4216
+ type: "dynamic_candidate_branch",
4217
+ from: `${call.repoName}:${call.source_file}:${call.source_line}`,
4218
+ to: `${String(candidate.servicePath ?? "")}${String(candidate.operationPath ?? "")}`,
4219
+ evidence: {
4220
+ ...candidate,
4221
+ exploratory: true,
4222
+ dynamicMode: String(exploration.mode ?? "candidates"),
4223
+ selected: false,
4224
+ omittedCandidateCount: numericValue(exploration.omittedCandidateCount)
4225
+ },
4226
+ confidence: numericValue(candidate.score),
4227
+ unresolvedReason: "Exploratory dynamic target candidate; provide runtime variables to select it"
4228
+ }));
4229
+ }
4230
+ function objectRecord(value) {
4231
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4232
+ }
4233
+ function recordArray2(value) {
4234
+ return Array.isArray(value) ? value.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
4235
+ }
4236
+ function numericValue(value) {
4237
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
4238
+ }
3804
4239
 
3805
4240
  // src/trace/trace-engine.ts
3806
4241
  function normalizeOperation(value) {
@@ -4269,7 +4704,11 @@ function trace(db, start, options) {
4269
4704
  String(row.evidence_json || "{}")
4270
4705
  );
4271
4706
  const rawEvidence = baseTraceEvidence(row, call, persistedEvidence, contextual.evidence);
4272
- const effective = runtimeResolution(db, row, rawEvidence, options.vars, workspaceIdForCall(db, String(call.id)), contextual.unresolvedReason);
4707
+ const effective = runtimeResolution(db, row, rawEvidence, {
4708
+ vars: options.vars,
4709
+ dynamicMode: options.dynamicMode ?? "strict",
4710
+ maxDynamicCandidates: options.maxDynamicCandidates
4711
+ }, workspaceIdForCall(db, String(call.id)), contextual.unresolvedReason);
4273
4712
  const evidence = effective.evidence;
4274
4713
  const effectiveRow = effective.row;
4275
4714
  const targetNode = `${effectiveRow.to_kind}:${effectiveRow.to_id}`;
@@ -4289,6 +4728,8 @@ function trace(db, start, options) {
4289
4728
  confidence: Number(effectiveRow.confidence ?? call.confidence),
4290
4729
  unresolvedReason: effective.unresolvedReason
4291
4730
  });
4731
+ if ((options.dynamicMode ?? "strict") === "candidates")
4732
+ edges.push(...dynamicCandidateBranches(current.depth, call, evidence));
4292
4733
  if (effectiveRow.to_kind === "operation") {
4293
4734
  const implementation = implementationScope(db, effectiveRow.to_id);
4294
4735
  const contextSelection = contextImplementationMethodId(implementation.edge, current.repoId, evidence, hintOptions);
@@ -4371,4 +4812,4 @@ export {
4371
4812
  parseVars,
4372
4813
  trace
4373
4814
  };
4374
- //# sourceMappingURL=chunk-52OUS3MO.js.map
4815
+ //# sourceMappingURL=chunk-YZJKE5UX.js.map