@saptools/service-flow 0.1.67 → 0.1.69
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/CHANGELOG.md +20 -0
- package/README.md +27 -9
- package/TECHNICAL-NOTE.md +36 -0
- package/dist/chunk-3N3B5KHV.js +19596 -0
- package/dist/chunk-3N3B5KHV.js.map +1 -0
- package/dist/cli.js +2645 -521
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +67 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/001-index-summary.ts +22 -0
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/cli.ts +151 -87
- package/src/db/000-call-fact-repository.ts +499 -340
- package/src/db/001-fact-lifecycle.ts +60 -30
- package/src/db/002-fact-json-inventory.ts +169 -0
- package/src/db/003-current-fact-semantics.ts +699 -0
- package/src/db/004-package-target-invalidation.ts +183 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +464 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +210 -0
- package/src/db/009-binding-fact-semantics.ts +352 -0
- package/src/db/010-package-symbol-surface-semantics.ts +320 -0
- package/src/db/011-symbol-call-semantics.ts +144 -0
- package/src/db/012-binding-reference-proof.ts +268 -0
- package/src/db/013-index-publication-failure.ts +91 -0
- package/src/db/014-binding-helper-provenance.ts +17 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +130 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/cds-extension-resolver.ts +27 -3
- package/src/indexer/repository-indexer.ts +135 -13
- package/src/indexer/workspace-indexer.ts +237 -34
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +34 -11
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/006-event-template-link.ts +72 -0
- package/src/linker/007-call-edge-insertion.ts +568 -0
- package/src/linker/cross-repo-linker.ts +4 -166
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/000-direct-query-execution.ts +11 -0
- package/src/parsers/002-symbol-import-bindings.ts +516 -0
- package/src/parsers/003-package-public-surface.ts +661 -0
- package/src/parsers/004-fact-identity.ts +108 -0
- package/src/parsers/005-event-subscription-facts.ts +281 -0
- package/src/parsers/006-binding-identity.ts +348 -0
- package/src/parsers/007-source-fact-reconciliation.ts +105 -0
- package/src/parsers/008-package-surface-publication.ts +82 -0
- package/src/parsers/009-symbol-call-facts.ts +528 -0
- package/src/parsers/010-package-public-surface-analysis.ts +352 -0
- package/src/parsers/011-binding-lexical-scope.ts +583 -0
- package/src/parsers/012-package-fact-contract.ts +306 -0
- package/src/parsers/013-executable-body-eligibility.ts +35 -0
- package/src/parsers/014-service-binding-helper-flow.ts +306 -0
- package/src/parsers/015-service-binding-collector.ts +693 -0
- package/src/parsers/016-local-symbol-reference.ts +261 -0
- package/src/parsers/017-symbol-derived-contexts.ts +268 -0
- package/src/parsers/018-package-commonjs-syntax.ts +142 -0
- package/src/parsers/019-binding-assignment-targets.ts +76 -0
- package/src/parsers/020-stable-local-value.ts +217 -0
- package/src/parsers/021-binding-visibility.ts +168 -0
- package/src/parsers/022-outbound-expression-analysis.ts +700 -0
- package/src/parsers/023-outbound-call-classifier.ts +692 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +162 -512
- package/src/parsers/package-json-parser.ts +45 -3
- package/src/parsers/service-binding-parser-helpers.ts +86 -15
- package/src/parsers/service-binding-parser.ts +147 -597
- package/src/parsers/symbol-parser.ts +513 -352
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/007-implementation-start-diagnostic.ts +1 -0
- package/src/trace/011-event-subscriber-traversal.ts +100 -8
- package/src/trace/013-trace-root-scopes.ts +2 -3
- package/src/trace/014-compact-contract.ts +6 -0
- package/src/trace/015-trace-edge-recorder.ts +61 -4
- package/src/trace/016-compact-projector.ts +15 -17
- package/src/trace/019-trace-edge-semantics.ts +6 -10
- package/src/trace/020-compact-field-projection.ts +122 -38
- package/src/trace/021-compact-decision-normalization.ts +171 -0
- package/src/trace/022-trace-fact-preflight.ts +21 -0
- package/src/trace/023-nested-event-scopes.ts +23 -0
- package/src/trace/024-compact-observation-decision.ts +81 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +336 -0
- package/src/trace/027-trace-scope-execution.ts +566 -0
- package/src/trace/028-trace-operation-execution.ts +336 -0
- package/src/trace/029-trace-start-implementation.ts +172 -0
- package/src/trace/030-event-runtime-resolution.ts +151 -0
- package/src/trace/031-local-call-expansion.ts +37 -0
- package/src/trace/implementation-hints.ts +9 -6
- package/src/trace/selectors.ts +1 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +57 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-ZQABU7MR.js +0 -12151
- package/dist/chunk-ZQABU7MR.js.map +0 -1
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
|
-
import { applyVariables } from './dynamic-edge-resolver.js';
|
|
3
|
-
import { classifyODataPathIntent, normalizeODataOperationInvocationPath } from './odata-path-normalizer.js';
|
|
4
|
-
import { buildRemoteQueryTarget } from './remote-query-target.js';
|
|
5
|
-
import { resolveOperation } from './service-resolver.js';
|
|
6
2
|
import { linkHelperPackages } from './helper-package-linker.js';
|
|
7
|
-
import { externalHttpTarget } from './external-http-target.js';
|
|
8
3
|
import { linkImplementations as linkCanonicalImplementations } from './000-implementation-candidates.js';
|
|
9
|
-
import {
|
|
10
|
-
ambiguousPathCandidates,
|
|
11
|
-
linkedCallEvidence,
|
|
12
|
-
objectJson,
|
|
13
|
-
objectValue,
|
|
14
|
-
} from './002-call-evidence.js';
|
|
15
4
|
import { linkPackageImportSymbolCalls } from './003-package-import-symbol-resolver.js';
|
|
16
5
|
import { linkEventSubscriptionHandlers } from './004-event-subscription-handler-linker.js';
|
|
6
|
+
import { insertCallEdge } from './007-call-edge-insertion.js';
|
|
17
7
|
import { assertWorkspaceLinkable } from '../db/001-fact-lifecycle.js';
|
|
18
8
|
export interface LinkWorkspaceResult {
|
|
19
9
|
edgeCount: number;
|
|
@@ -37,12 +27,13 @@ export interface LinkWorkspaceResult {
|
|
|
37
27
|
export function linkWorkspace(db: Db, workspaceId: number, vars: Record<string, string> = {}): LinkWorkspaceResult {
|
|
38
28
|
return db.transaction(() => {
|
|
39
29
|
assertWorkspaceLinkable(db, workspaceId);
|
|
30
|
+
linkPackageImportSymbolCalls(db, workspaceId);
|
|
31
|
+
assertWorkspaceLinkable(db, workspaceId, 'terminal');
|
|
40
32
|
const generation = nextGraphGeneration(db, workspaceId);
|
|
41
33
|
db.prepare('DELETE FROM graph_edges WHERE workspace_id=?').run(workspaceId);
|
|
42
34
|
const deps = linkHelperPackages(db, workspaceId, generation);
|
|
43
|
-
linkPackageImportSymbolCalls(db, workspaceId);
|
|
44
35
|
const subscriptions = linkEventSubscriptionHandlers(
|
|
45
|
-
db, workspaceId, generation,
|
|
36
|
+
db, workspaceId, generation, vars,
|
|
46
37
|
);
|
|
47
38
|
const impl = linkCanonicalImplementations(db, workspaceId, generation);
|
|
48
39
|
const callSummary = linkCalls(db, workspaceId, vars, generation);
|
|
@@ -88,156 +79,3 @@ function linkCalls(db: Db, workspaceId: number, vars: Record<string, string>, ge
|
|
|
88
79
|
}
|
|
89
80
|
return { edgeCount, unresolvedCount, resolvedCount, remoteResolvedCount, localResolvedCount, ambiguousCount, dynamicCount, terminalCount };
|
|
90
81
|
}
|
|
91
|
-
function insertCallEdge(db: Db, workspaceId: number, call: Record<string, unknown>, vars: Record<string, string>, generation: number): { status: string; callType: string } {
|
|
92
|
-
const callType = String(call.call_type);
|
|
93
|
-
const rawOp = applyVariables(String(call.operation_path_expr ?? ''), vars);
|
|
94
|
-
const intent = classifyODataPathIntent(rawOp, call.method as string | undefined);
|
|
95
|
-
const isEntityQueryIntent = ['entity_query', 'entity_key_read', 'entity_navigation_query'].includes(intent.kind);
|
|
96
|
-
const resolutionRawOp = callType === 'remote_query' && isEntityQueryIntent ? intent.pathWithoutQuery : rawOp;
|
|
97
|
-
const normalized = normalizeODataOperationInvocationPath(resolutionRawOp);
|
|
98
|
-
const op = normalized?.normalizedOperationPath ?? resolutionRawOp;
|
|
99
|
-
const servicePath = applyVariables((call.servicePathExpr as string | undefined) ?? (call.requireServicePath as string | undefined), vars);
|
|
100
|
-
const destination = (call.destinationExpr as string | undefined) ?? (call.requireDestination as string | undefined);
|
|
101
|
-
const isDynamic = Boolean(Number(call.isDynamic ?? 0));
|
|
102
|
-
const isRemoteEntityCall = callType.startsWith('remote_entity_');
|
|
103
|
-
const indexedOperationCandidateCount = operationCandidateCount(db, workspaceId, op, intent.topLevelOperationName);
|
|
104
|
-
const credibleOperationSignal = Boolean(normalized?.wasInvocation) || (Boolean(intent.topLevelOperationNameCandidate) && indexedOperationCandidateCount > 0);
|
|
105
|
-
const strongEntitySignal = ['entity_media', 'entity_delete', 'entity_key_read', 'entity_navigation_query'].includes(intent.kind) || (intent.kind === 'entity_mutation' && (intent.hasEntityKeyPredicate || intent.hasNavigationSuffix));
|
|
106
|
-
const operationLikeRemoteEntity = isRemoteEntityCall && Boolean(op) && credibleOperationSignal && (!strongEntitySignal || indexedOperationCandidateCount > 0);
|
|
107
|
-
const isOperationCall = operationLikeRemoteEntity || ((callType === 'remote_action' || callType === 'local_service_call') || (callType === 'remote_query' && Boolean(op)));
|
|
108
|
-
const resolution = isOperationCall ? resolveOperation(db, { servicePath, operationPath: op, serviceName: call.local_service_name as string | undefined, repoId: callType === 'local_service_call' ? Number(call.repo_id) : undefined, alias: applyVariables((call.aliasExpr as string | undefined) ?? (call.alias as string | undefined), vars), destination: destination ? applyVariables(destination, vars) : undefined, isDynamic, hasExplicitOverride: Object.keys(vars).length > 0 || callType === 'local_service_call' }, workspaceId) : { status: 'unresolved' as const, candidates: [], reasons: [] };
|
|
109
|
-
const evidence: Record<string, unknown> = {
|
|
110
|
-
...linkedCallEvidence(
|
|
111
|
-
call,
|
|
112
|
-
resolution,
|
|
113
|
-
servicePath,
|
|
114
|
-
op,
|
|
115
|
-
destination ? applyVariables(destination, vars) : undefined,
|
|
116
|
-
normalized,
|
|
117
|
-
intent,
|
|
118
|
-
),
|
|
119
|
-
indexedOperationCandidateCount,
|
|
120
|
-
parserCallType: callType,
|
|
121
|
-
entityOperationPrecedence: operationPrecedence(
|
|
122
|
-
callType,
|
|
123
|
-
intent,
|
|
124
|
-
indexedOperationCandidateCount,
|
|
125
|
-
Boolean(resolution.target),
|
|
126
|
-
),
|
|
127
|
-
};
|
|
128
|
-
const pathAnalysis = objectValue(objectJson(call.evidence_json)?.pathAnalysis);
|
|
129
|
-
if (callType === 'remote_action' && pathAnalysis?.status === 'ambiguous') {
|
|
130
|
-
const candidatePaths = ambiguousPathCandidates(pathAnalysis);
|
|
131
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)').run(
|
|
132
|
-
workspaceId,
|
|
133
|
-
'UNRESOLVED_EDGE',
|
|
134
|
-
'ambiguous',
|
|
135
|
-
'call',
|
|
136
|
-
String(call.id),
|
|
137
|
-
'operation_candidates',
|
|
138
|
-
candidatePaths.items.join(','),
|
|
139
|
-
Number(call.confidence ?? 0.5),
|
|
140
|
-
JSON.stringify({
|
|
141
|
-
...evidence,
|
|
142
|
-
ambiguousOperationPathCandidateCount: candidatePaths.totalCount,
|
|
143
|
-
shownAmbiguousOperationPathCandidateCount: candidatePaths.shownCount,
|
|
144
|
-
omittedAmbiguousOperationPathCandidateCount: candidatePaths.omittedCount,
|
|
145
|
-
}),
|
|
146
|
-
0,
|
|
147
|
-
'Ambiguous operation path candidates require explicit disambiguation',
|
|
148
|
-
generation,
|
|
149
|
-
);
|
|
150
|
-
return { status: 'ambiguous', callType };
|
|
151
|
-
}
|
|
152
|
-
if (isRemoteEntityCall && (resolution.target || resolution.candidates.length > 0 || resolution.status === 'dynamic')) {
|
|
153
|
-
if (resolution.target) {
|
|
154
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, 'REMOTE_CALL_RESOLVES_TO_OPERATION', 'resolved', 'call', String(call.id), 'operation', String(resolution.target.operationId), resolution.target.score, JSON.stringify({ ...evidence, operationEntityPrecedence: 'indexed_operation_over_parser_entity' }), 0, generation);
|
|
155
|
-
return { status: 'resolved', callType };
|
|
156
|
-
}
|
|
157
|
-
const status = resolution.status === 'dynamic' ? 'dynamic' : resolution.status === 'ambiguous' ? 'ambiguous' : 'unresolved';
|
|
158
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, status === 'dynamic' ? 'DYNAMIC_EDGE_CANDIDATE' : 'UNRESOLVED_EDGE', status, 'call', String(call.id), 'operation_candidate', op ? `Remote action: ${op}` : 'Remote action: unknown path', Number(call.confidence ?? 0.2), JSON.stringify({ ...evidence, operationEntityPrecedence: resolution.candidates.length > 0 ? 'parser_entity_with_indexed_operation_candidates' : 'parser_entity_operation_candidate_without_indexed_match' }), status === 'dynamic' ? 1 : 0, unresolvedOperationReason(resolution), generation);
|
|
159
|
-
return { status, callType };
|
|
160
|
-
}
|
|
161
|
-
if (isRemoteEntityCall) {
|
|
162
|
-
const target = buildRemoteQueryTarget({ queryEntity: intent.entitySegment ?? call.query_entity, servicePath, serviceAlias: call.alias, serviceAliasExpr: call.aliasExpr, destination: destination ? applyVariables(destination, vars) : undefined, isDynamic, parserWarning: evidence.parserWarning });
|
|
163
|
-
const entityKind = callType;
|
|
164
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, 'HANDLER_ACCESSES_REMOTE_ENTITY', 'terminal', 'call', String(call.id), target.toKind, target.toId, Number(call.confidence ?? 0.5), JSON.stringify({ ...evidence, ...target.evidence, remoteEntityAccess: entityKind }), 0, generation);
|
|
165
|
-
return { status: 'terminal', callType };
|
|
166
|
-
}
|
|
167
|
-
if (callType === 'remote_query' && (isEntityQueryIntent || !op) && !resolution.target) {
|
|
168
|
-
const target = buildRemoteQueryTarget({ queryEntity: call.query_entity, servicePath, serviceAlias: call.alias, serviceAliasExpr: call.aliasExpr, destination: destination ? applyVariables(destination, vars) : undefined, isDynamic, parserWarning: evidence.parserWarning });
|
|
169
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, 'HANDLER_RUNS_REMOTE_QUERY', 'terminal', 'call', String(call.id), target.toKind, target.toId, Number(call.confidence ?? 0.5), JSON.stringify({ ...evidence, ...target.evidence }), 0, generation);
|
|
170
|
-
return { status: 'terminal', callType };
|
|
171
|
-
}
|
|
172
|
-
if (callType === 'local_service_call' && call.unresolved_reason === 'transport_client_method' && !resolution.target && resolution.candidates.length === 0) {
|
|
173
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, 'HANDLER_CALLS_TRANSPORT_METHOD', 'terminal', 'call', String(call.id), 'transport_method', String(op || 'transport_client_method'), Number(call.confidence ?? 0.5), JSON.stringify({ ...evidence, classification: 'transport_client_method' }), 0, generation);
|
|
174
|
-
return { status: 'terminal', callType };
|
|
175
|
-
}
|
|
176
|
-
if (resolution.target) {
|
|
177
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, callType === 'local_service_call' ? 'LOCAL_CALL_RESOLVES_TO_OPERATION' : 'REMOTE_CALL_RESOLVES_TO_OPERATION', 'resolved', 'call', String(call.id), 'operation', String(resolution.target.operationId), resolution.target.score, JSON.stringify(evidence), 0, generation);
|
|
178
|
-
return { status: 'resolved', callType };
|
|
179
|
-
}
|
|
180
|
-
const edgeType = callType === 'local_db_query' ? 'HANDLER_RUNS_DB_QUERY' : callType === 'external_http' ? 'HANDLER_CALLS_EXTERNAL_HTTP' : callType === 'async_emit' ? 'HANDLER_EMITS_EVENT' : callType === 'async_subscribe' ? 'EVENT_CONSUMED_BY_HANDLER' : resolution.status === 'dynamic' ? 'DYNAMIC_EDGE_CANDIDATE' : 'UNRESOLVED_EDGE';
|
|
181
|
-
const status = edgeType === 'DYNAMIC_EDGE_CANDIDATE' ? 'dynamic' : resolution.status === 'ambiguous' ? 'ambiguous' : edgeType === 'UNRESOLVED_EDGE' ? 'unresolved' : 'terminal';
|
|
182
|
-
const unresolvedReason = status === 'terminal' ? null : String(call.unresolved_reason ?? unresolvedOperationReason(resolution));
|
|
183
|
-
const externalTarget = callType === 'external_http' ? externalHttpTarget(call) : undefined;
|
|
184
|
-
const targetKind = callType === 'local_db_query' ? 'db_entity' : callType.startsWith('async_') ? 'event' : callType === 'external_http' ? (externalTarget?.toKind ?? 'external_endpoint') : 'operation_candidate';
|
|
185
|
-
const targetId = callType === 'local_db_query' ? String(call.query_entity ?? 'unknown') : callType === 'remote_action' ? (op ? `Remote action: ${op}` : (call.unresolved_reason === 'dynamic_operation_path_identifier' ? 'Remote action: dynamic path' : 'Remote action: unknown path')) : callType === 'external_http' ? String(externalTarget?.toId ?? 'unknown') : String(call.event_name_expr ?? op ?? 'unknown');
|
|
186
|
-
const graphLevelDynamic = edgeType === 'DYNAMIC_EDGE_CANDIDATE' && resolution.status === 'dynamic';
|
|
187
|
-
const finalEvidence = externalTarget ? { ...evidence, externalTarget } : evidence;
|
|
188
|
-
db.prepare('INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)').run(workspaceId, edgeType, status, 'call', String(call.id), targetKind, targetId, Number(call.confidence ?? 0.2), JSON.stringify(finalEvidence), graphLevelDynamic ? 1 : 0, unresolvedReason, generation);
|
|
189
|
-
return { status, callType };
|
|
190
|
-
}
|
|
191
|
-
function operationCandidateCount(db: Db, workspaceId: number, operationPath: string | undefined, operationName: string | undefined): number {
|
|
192
|
-
if (!operationPath && !operationName) return 0;
|
|
193
|
-
const normalizedName = operationName ?? operationPath?.replace(/^\//, '').split('.').at(-1);
|
|
194
|
-
const row = db.prepare(`SELECT COUNT(*) count FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=? AND (o.operation_path=? OR o.operation_path=? OR o.operation_name=?)`).get(workspaceId, operationPath, normalizedName ? `/${normalizedName}` : operationPath, normalizedName) as { count?: number } | undefined;
|
|
195
|
-
return Number(row?.count ?? 0);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function operationPrecedence(
|
|
199
|
-
callType: string,
|
|
200
|
-
intent: ReturnType<typeof classifyODataPathIntent>,
|
|
201
|
-
indexedOperationCandidateCount: number,
|
|
202
|
-
resolvedOperation: boolean,
|
|
203
|
-
): Record<string, unknown> {
|
|
204
|
-
if (resolvedOperation) {
|
|
205
|
-
return {
|
|
206
|
-
decision: 'operation',
|
|
207
|
-
reason: 'indexed_operation_with_strong_service_context',
|
|
208
|
-
indexedOperationCandidateCount,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
if (callType === 'remote_action' && intent.kind === 'operation_invocation') {
|
|
212
|
-
return {
|
|
213
|
-
decision: 'operation_candidate',
|
|
214
|
-
rejectionReason: indexedOperationCandidateCount > 0
|
|
215
|
-
? 'indexed_candidates_lack_unique_strong_service_context'
|
|
216
|
-
: 'no_indexed_operation_candidate',
|
|
217
|
-
indexedOperationCandidateCount,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
if (intent.kind.startsWith('entity_')) {
|
|
221
|
-
return {
|
|
222
|
-
decision: 'entity',
|
|
223
|
-
rejectionReason: indexedOperationCandidateCount > 0
|
|
224
|
-
? 'entity_shape_has_precedence_without_resolved_operation_context'
|
|
225
|
-
: 'entity_shape_has_no_indexed_operation_evidence',
|
|
226
|
-
indexedOperationCandidateCount,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
decision: 'unresolved',
|
|
231
|
-
rejectionReason: 'path_has_no_safe_entity_or_operation_precedence',
|
|
232
|
-
indexedOperationCandidateCount,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function unresolvedOperationReason(resolution: { candidates: unknown[]; status: string; reasons: string[] }): string {
|
|
237
|
-
if (resolution.status === 'dynamic') return `Dynamic target requires runtime variable overrides: ${(resolution.reasons.length ? resolution.reasons : ['missing runtime variables']).join(', ')}`;
|
|
238
|
-
if (resolution.candidates.length === 0) return 'No indexed target operation matched';
|
|
239
|
-
if (resolution.reasons.includes('operation_path_only_has_no_strong_target_signal')) return 'Operation candidates found but no strong service signal is available';
|
|
240
|
-
if (resolution.reasons.includes('candidate_score_below_resolution_threshold')) return 'Operation candidates found but resolution score is below threshold';
|
|
241
|
-
if (resolution.status === 'ambiguous') return 'Ambiguous operation candidates require a strong service signal';
|
|
242
|
-
return 'Operation candidates found but resolution could not select a target';
|
|
243
|
-
}
|