@rhei-team/rhei 1.0.0-beta.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.
Files changed (30) hide show
  1. package/README.md +1048 -0
  2. package/bin/rhei-mcp.js +3 -0
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.js +86366 -0
  5. package/dist/premium/contracts.d.ts +445 -0
  6. package/dist/premium/contracts.js +97 -0
  7. package/dist/vendor/rhei-core/briefs.js +1276 -0
  8. package/dist/vendor/rhei-core/codeAgent.js +615 -0
  9. package/dist/vendor/rhei-core/codeEditSession.js +293 -0
  10. package/dist/vendor/rhei-core/codeIntelligence.js +4287 -0
  11. package/dist/vendor/rhei-core/codeMarket.js +8946 -0
  12. package/dist/vendor/rhei-core/codeReviewIntelligence.js +5918 -0
  13. package/dist/vendor/rhei-core/codeSemantics.js +172427 -0
  14. package/dist/vendor/rhei-core/codeStory.js +667 -0
  15. package/dist/vendor/rhei-core/codeStrategyPlan.js +663 -0
  16. package/dist/vendor/rhei-core/codeTrail.js +2781 -0
  17. package/dist/vendor/rhei-core/codeWorkHandoff.js +281 -0
  18. package/dist/vendor/rhei-core/contextQuery.js +1119 -0
  19. package/dist/vendor/rhei-core/contextRouting.js +2052 -0
  20. package/dist/vendor/rhei-core/evidenceLedger.js +5336 -0
  21. package/dist/vendor/rhei-core/executionSafety.js +0 -0
  22. package/dist/vendor/rhei-core/goalIntelligence.js +2218 -0
  23. package/dist/vendor/rhei-core/model-lanes.js +75 -0
  24. package/dist/vendor/rhei-core/now.js +127 -0
  25. package/dist/vendor/rhei-core/package.json +29 -0
  26. package/dist/vendor/rhei-core/programPlan.js +3153 -0
  27. package/dist/vendor/rhei-core/search.js +196 -0
  28. package/dist/vendor/rhei-core/serviceIntelligence.js +1734 -0
  29. package/dist/vendor/rhei-core/workflowPlan.js +1660 -0
  30. package/package.json +41 -0
@@ -0,0 +1,1734 @@
1
+ // ../core/src/serviceIntelligence/types.ts
2
+ var SERVICE_INTELLIGENCE_REPORT_VERSION = 1;
3
+ var SERVICE_INTELLIGENCE_REPORT_KIND = "service_intelligence_report";
4
+ // ../core/src/contextLens/relationshipTaxonomy.ts
5
+ var CONNECTION_RELATIONSHIP_TYPE_REGISTRY = {
6
+ related_to: { direction: "symmetric", riskLevel: "low" },
7
+ knows: { direction: "symmetric", riskLevel: "low" },
8
+ mentions: { direction: "symmetric", riskLevel: "low" },
9
+ references: { direction: "symmetric", riskLevel: "low" },
10
+ contradicts: { direction: "symmetric", riskLevel: "high" },
11
+ owns: { direction: "directed", riskLevel: "medium" },
12
+ owned_by: { direction: "directed", riskLevel: "medium" },
13
+ depends_on: { direction: "directed", riskLevel: "medium" },
14
+ implements: { direction: "directed", riskLevel: "medium" },
15
+ documents: { direction: "directed", riskLevel: "medium" },
16
+ calls: { direction: "directed", riskLevel: "medium" },
17
+ imports: { direction: "directed", riskLevel: "medium" },
18
+ affects: { direction: "directed", riskLevel: "medium" },
19
+ affected_by: { direction: "directed", riskLevel: "medium" },
20
+ uses: { direction: "directed", riskLevel: "medium" },
21
+ uses_context: { direction: "directed", riskLevel: "medium" },
22
+ supports: { direction: "directed", riskLevel: "medium" },
23
+ tested_by: { direction: "directed", riskLevel: "medium" },
24
+ covers: { direction: "directed", riskLevel: "medium" },
25
+ verifies: { direction: "directed", riskLevel: "medium" },
26
+ deploys: { direction: "directed", riskLevel: "medium" },
27
+ configures: { direction: "directed", riskLevel: "medium" },
28
+ publishes_to: { direction: "directed", riskLevel: "medium" },
29
+ subscribes_to: { direction: "directed", riskLevel: "medium" },
30
+ reads_from: { direction: "directed", riskLevel: "medium" },
31
+ observes: { direction: "directed", riskLevel: "medium" },
32
+ authenticates: { direction: "directed", riskLevel: "high" },
33
+ authorizes: { direction: "directed", riskLevel: "high" },
34
+ supersedes: { direction: "directed", riskLevel: "high" },
35
+ source_of_truth_for: { direction: "directed", riskLevel: "high" },
36
+ replaces: { direction: "directed", riskLevel: "high" },
37
+ blocks: { direction: "directed", riskLevel: "high" },
38
+ blocked_by: { direction: "directed", riskLevel: "high" },
39
+ writes_to: { direction: "directed", riskLevel: "medium" },
40
+ stores_in: { direction: "directed", riskLevel: "medium" },
41
+ policy_for: { direction: "directed", riskLevel: "high" }
42
+ };
43
+ var CONNECTION_RELATIONSHIP_TYPES = Object.keys(CONNECTION_RELATIONSHIP_TYPE_REGISTRY);
44
+ function sanitizeConnectionRelationshipType(value) {
45
+ if (typeof value !== "string")
46
+ return null;
47
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
48
+ return normalized.length > 0 ? normalized : null;
49
+ }
50
+
51
+ // ../core/src/contextLens/serviceConnections.ts
52
+ var SERVICE_CONNECTION_OTEL_IDENTITY_KEYS = {
53
+ serviceName: "service.name",
54
+ serviceNamespace: "service.namespace",
55
+ serviceVersion: "service.version",
56
+ deploymentEnvironment: "deployment.environment.name"
57
+ };
58
+ var SERVICE_CONNECTION_IDENTITY_SCOPES = ["logical_service", "deployment_environment"];
59
+ var DEFAULT_SERVICE_CONNECTION_IDENTITY_SCOPE = "logical_service";
60
+ var SERVICE_CONNECTION_LIFECYCLE_STATES = [
61
+ "hint",
62
+ "candidate",
63
+ "verified",
64
+ "stale",
65
+ "rejected",
66
+ "retracted"
67
+ ];
68
+ var DEFAULT_SERVICE_CONNECTION_LIFECYCLE_STATE = "hint";
69
+ var SERVICE_CONNECTION_OBSERVATION_STATUSES = [
70
+ "present",
71
+ "absent_in_latest_scan",
72
+ "changed",
73
+ "unknown"
74
+ ];
75
+ var SERVICE_CONNECTION_SENSITIVITY_LEVELS = ["public", "internal", "sensitive", "secret"];
76
+ var SERVICE_CONNECTION_RELATIONSHIP_TYPES = [
77
+ "publishes_to",
78
+ "subscribes_to",
79
+ "writes_to",
80
+ "stores_in",
81
+ "reads_from",
82
+ "observes",
83
+ "blocked_by"
84
+ ];
85
+ var SERVICE_CONNECTION_RELATIONSHIP_SEMANTICS = {
86
+ publishes_to: {
87
+ relationshipType: "publishes_to",
88
+ sourceDisplayLabel: "publishes to",
89
+ inverseDisplayLabel: "receives publications from",
90
+ sourceServiceMeaning: "The source service emits messages/events to the target service resource or broker surface."
91
+ },
92
+ subscribes_to: {
93
+ relationshipType: "subscribes_to",
94
+ sourceDisplayLabel: "subscribes to",
95
+ inverseDisplayLabel: "has subscriber",
96
+ sourceServiceMeaning: "The source service consumes messages/events from the target service resource or broker surface."
97
+ },
98
+ writes_to: {
99
+ relationshipType: "writes_to",
100
+ sourceDisplayLabel: "writes to",
101
+ inverseDisplayLabel: "is written by",
102
+ sourceServiceMeaning: "The source service mutates or sends write operations to the target resource."
103
+ },
104
+ stores_in: {
105
+ relationshipType: "stores_in",
106
+ sourceDisplayLabel: "stores in",
107
+ inverseDisplayLabel: "stores data for",
108
+ sourceServiceMeaning: "The source service persists durable data in the target storage resource."
109
+ },
110
+ reads_from: {
111
+ relationshipType: "reads_from",
112
+ sourceDisplayLabel: "reads from",
113
+ inverseDisplayLabel: "is read by",
114
+ sourceServiceMeaning: "The source service reads or queries data from the target resource without implying mutation."
115
+ },
116
+ observes: {
117
+ relationshipType: "observes",
118
+ sourceDisplayLabel: "observes",
119
+ inverseDisplayLabel: "is observed by",
120
+ sourceServiceMeaning: "The source service sends telemetry or monitoring signals to the target observability surface."
121
+ },
122
+ blocked_by: {
123
+ relationshipType: "blocked_by",
124
+ sourceDisplayLabel: "is blocked by",
125
+ inverseDisplayLabel: "blocks",
126
+ sourceServiceMeaning: "The source service depends on a missing, failing, policy-restricted, or sensitive target surface before progress can continue."
127
+ }
128
+ };
129
+ var SERVICE_CONNECTION_ENDPOINT_KINDS = ["service", "resource"];
130
+ var SERVICE_CONNECTION_SURFACE_KINDS = [
131
+ "api",
132
+ "queue",
133
+ "topic",
134
+ "stream",
135
+ "datastore",
136
+ "cache",
137
+ "object_store",
138
+ "observability_sink",
139
+ "config",
140
+ "secret",
141
+ "feature_flag",
142
+ "runtime",
143
+ "external_service",
144
+ "unknown"
145
+ ];
146
+ var SERVICE_CONNECTION_EVIDENCE_SOURCE_KINDS = [
147
+ "code_static",
148
+ "config_static",
149
+ "runtime_receipt",
150
+ "manual_report",
151
+ "semantic_advisory"
152
+ ];
153
+ var SERVICE_CONNECTION_EVIDENCE_TIERS = [
154
+ "explicit_user_action",
155
+ "accepted_truth_candidate",
156
+ "deterministic_code_edge",
157
+ "receipt_backed",
158
+ "explicit_source_language",
159
+ "repeated_reference",
160
+ "llm_classification",
161
+ "co_mention",
162
+ "vector_similarity"
163
+ ];
164
+ var SERVICE_CONNECTION_CANDIDATE_KINDS = [
165
+ "http_api",
166
+ "database",
167
+ "auth_provider",
168
+ "provider_client",
169
+ "object_store",
170
+ "queue_topic",
171
+ "cache",
172
+ "secret_config",
173
+ "observability_sink",
174
+ "runtime_owner",
175
+ "test_proof",
176
+ "doc_proof",
177
+ "unknown_service_surface"
178
+ ];
179
+ var SERVICE_CONNECTION_DIRECTION_HINTS = [
180
+ "inbound_api",
181
+ "outbound_provider",
182
+ "reads_secret",
183
+ "reads_datastore",
184
+ "writes_datastore",
185
+ "publishes_queue",
186
+ "consumes_queue",
187
+ "reads_cache",
188
+ "writes_cache",
189
+ "emits_telemetry",
190
+ "owns_runtime",
191
+ "proves_with_test",
192
+ "documents",
193
+ "unknown"
194
+ ];
195
+ var SERVICE_CONNECTION_STATIC_EVIDENCE_TIERS = [
196
+ "path_only",
197
+ "import_backed",
198
+ "call_backed",
199
+ "env_backed",
200
+ "schema_backed",
201
+ "test_backed",
202
+ "doc_backed",
203
+ "mixed_backed"
204
+ ];
205
+ var DEFAULT_SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND = "code_static";
206
+ var FALLBACK_SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND = "semantic_advisory";
207
+ var DEFAULT_SERVICE_CONNECTION_STATE = "observed";
208
+ var SERVICE_CONNECTION_EVIDENCE_SOURCE_CAPS = {
209
+ maxSourcesPerConnection: 8,
210
+ maxMetadataKeys: 20,
211
+ maxSummaryLength: 240
212
+ };
213
+ var SERVICE_CONNECTION_EVIDENCE_SOURCE_DEFAULTS = {
214
+ code_static: {
215
+ label: "Static code evidence",
216
+ evidenceTier: "deterministic_code_edge",
217
+ defaultConfidence: 0.82,
218
+ state: "high_confidence_hint"
219
+ },
220
+ config_static: {
221
+ label: "Static configuration evidence",
222
+ evidenceTier: "explicit_source_language",
223
+ defaultConfidence: 0.75,
224
+ state: "usable_hint"
225
+ },
226
+ runtime_receipt: {
227
+ label: "Runtime receipt evidence",
228
+ evidenceTier: "receipt_backed",
229
+ defaultConfidence: 0.8,
230
+ state: "high_confidence_hint"
231
+ },
232
+ manual_report: {
233
+ label: "Manual report",
234
+ evidenceTier: "explicit_user_action",
235
+ defaultConfidence: 0.8,
236
+ state: "review_candidate"
237
+ },
238
+ semantic_advisory: {
239
+ label: "Semantic advisory",
240
+ evidenceTier: "llm_classification",
241
+ defaultConfidence: 0.5,
242
+ confidenceCap: 0.84,
243
+ state: "observed"
244
+ }
245
+ };
246
+ var SERVICE_CONNECTION_RELATIONSHIP_TYPE_SET = new Set(SERVICE_CONNECTION_RELATIONSHIP_TYPES);
247
+ var SERVICE_CONNECTION_ENDPOINT_KIND_SET = new Set(SERVICE_CONNECTION_ENDPOINT_KINDS);
248
+ var SERVICE_CONNECTION_SURFACE_KIND_SET = new Set(SERVICE_CONNECTION_SURFACE_KINDS);
249
+ var SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND_SET = new Set(SERVICE_CONNECTION_EVIDENCE_SOURCE_KINDS);
250
+ var SERVICE_CONNECTION_EVIDENCE_TIER_SET = new Set(SERVICE_CONNECTION_EVIDENCE_TIERS);
251
+ var SERVICE_CONNECTION_CANDIDATE_KIND_SET = new Set(SERVICE_CONNECTION_CANDIDATE_KINDS);
252
+ var SERVICE_CONNECTION_DIRECTION_HINT_SET = new Set(SERVICE_CONNECTION_DIRECTION_HINTS);
253
+ var SERVICE_CONNECTION_STATIC_EVIDENCE_TIER_SET = new Set(SERVICE_CONNECTION_STATIC_EVIDENCE_TIERS);
254
+ var SERVICE_CONNECTION_IDENTITY_SCOPE_SET = new Set(SERVICE_CONNECTION_IDENTITY_SCOPES);
255
+ var SERVICE_CONNECTION_LIFECYCLE_STATE_SET = new Set(SERVICE_CONNECTION_LIFECYCLE_STATES);
256
+ var SERVICE_CONNECTION_CONNECTED_WORK_STATE_SET = new Set([
257
+ "observed",
258
+ "usable_hint",
259
+ "high_confidence_hint",
260
+ "review_candidate",
261
+ "canonical",
262
+ "dismissed",
263
+ "stale",
264
+ "conflicted",
265
+ "invalidated"
266
+ ]);
267
+ var SERVICE_CONNECTION_OBSERVATION_STATUS_SET = new Set(SERVICE_CONNECTION_OBSERVATION_STATUSES);
268
+ var SERVICE_CONNECTION_SENSITIVITY_LEVEL_SET = new Set(SERVICE_CONNECTION_SENSITIVITY_LEVELS);
269
+ var SERVICE_CONNECTION_SURFACE_KIND_ALIASES = {
270
+ api: ["api", "http", "rest", "graphql", "route", "endpoint", "webhook", "callback"],
271
+ queue: ["queue", "sqs", "worker", "job"],
272
+ topic: ["topic", "pubsub", "kafka", "nats"],
273
+ stream: ["stream", "event", "events", "kinesis", "topic"],
274
+ datastore: ["datastore", "database", "db", "postgres", "mysql", "sqlite", "convex", "table", "sql", "neo4j", "memgraph"],
275
+ cache: ["cache", "redis", "kv"],
276
+ object_store: ["object", "bucket", "blob", "s3", "r2", "storage"],
277
+ observability_sink: ["observability", "sink", "log", "logs", "metric", "metrics", "trace", "tracing", "monitor", "alert"],
278
+ config: ["config", "configuration", "settings", "env"],
279
+ secret: ["secret", "credential", "credentials", "token", "password", "api_key", "apikey", "private_key"],
280
+ feature_flag: ["feature", "flag", "feature_flag", "launchdarkly", "split"],
281
+ runtime: ["runtime", "scheduler", "schedule", "cron", "worker", "function"],
282
+ external_service: ["external", "third-party", "third_party", "vendor", "saas"],
283
+ unknown: ["unknown"]
284
+ };
285
+ function normalizeOptionalString(value) {
286
+ if (typeof value !== "string")
287
+ return;
288
+ const trimmed = value.trim();
289
+ return trimmed.length > 0 ? trimmed : undefined;
290
+ }
291
+ function normalizeStringArray(value, cap = 20) {
292
+ if (!Array.isArray(value))
293
+ return;
294
+ const items = value.map(normalizeOptionalString).filter((item) => Boolean(item)).slice(0, cap);
295
+ return items.length > 0 ? items : undefined;
296
+ }
297
+ function normalizeFiniteNumber(value) {
298
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
299
+ }
300
+ function recordFromUnknown(value) {
301
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
302
+ }
303
+ function normalizeIdentityScope(value) {
304
+ if (typeof value !== "string")
305
+ return DEFAULT_SERVICE_CONNECTION_IDENTITY_SCOPE;
306
+ return SERVICE_CONNECTION_IDENTITY_SCOPE_SET.has(value) ? value : DEFAULT_SERVICE_CONNECTION_IDENTITY_SCOPE;
307
+ }
308
+ function normalizeServiceConnectionLifecycleState(value, fallback = DEFAULT_SERVICE_CONNECTION_LIFECYCLE_STATE) {
309
+ if (typeof value !== "string")
310
+ return fallback;
311
+ return SERVICE_CONNECTION_LIFECYCLE_STATE_SET.has(value) ? value : fallback;
312
+ }
313
+ function normalizeConnectedWorkState(value) {
314
+ return typeof value === "string" && SERVICE_CONNECTION_CONNECTED_WORK_STATE_SET.has(value) ? value : undefined;
315
+ }
316
+ function serviceConnectionLifecycleStateFromConnectedWorkState(state) {
317
+ switch (state) {
318
+ case "review_candidate":
319
+ return "candidate";
320
+ case "high_confidence_hint":
321
+ case "canonical":
322
+ return "verified";
323
+ case "stale":
324
+ return "stale";
325
+ case "dismissed":
326
+ case "conflicted":
327
+ case "invalidated":
328
+ return "rejected";
329
+ case "observed":
330
+ case "usable_hint":
331
+ default:
332
+ return "hint";
333
+ }
334
+ }
335
+ function connectedWorkStateForServiceConnectionLifecycleState(lifecycleState) {
336
+ switch (lifecycleState) {
337
+ case "candidate":
338
+ return "review_candidate";
339
+ case "verified":
340
+ return "high_confidence_hint";
341
+ case "stale":
342
+ return "stale";
343
+ case "rejected":
344
+ case "retracted":
345
+ return "invalidated";
346
+ case "hint":
347
+ default:
348
+ return DEFAULT_SERVICE_CONNECTION_STATE;
349
+ }
350
+ }
351
+ function normalizeObservationStatus(value) {
352
+ if (typeof value !== "string")
353
+ return;
354
+ return SERVICE_CONNECTION_OBSERVATION_STATUS_SET.has(value) ? value : undefined;
355
+ }
356
+ function normalizeCandidateKind(value) {
357
+ return typeof value === "string" && SERVICE_CONNECTION_CANDIDATE_KIND_SET.has(value) ? value : undefined;
358
+ }
359
+ function normalizeDirectionHint(value) {
360
+ return typeof value === "string" && SERVICE_CONNECTION_DIRECTION_HINT_SET.has(value) ? value : undefined;
361
+ }
362
+ function normalizeStaticEvidenceTier(value) {
363
+ return typeof value === "string" && SERVICE_CONNECTION_STATIC_EVIDENCE_TIER_SET.has(value) ? value : undefined;
364
+ }
365
+ function normalizeSensitivityLevel(value) {
366
+ if (typeof value !== "string")
367
+ return;
368
+ return SERVICE_CONNECTION_SENSITIVITY_LEVEL_SET.has(value) ? value : undefined;
369
+ }
370
+ function normalizeServiceConnectionIdentityMetadata(value) {
371
+ const record = recordFromUnknown(value);
372
+ if (!record)
373
+ return;
374
+ const serviceName = normalizeOptionalString(record["serviceName"] ?? record[SERVICE_CONNECTION_OTEL_IDENTITY_KEYS.serviceName]);
375
+ const serviceNamespace = normalizeOptionalString(record["serviceNamespace"] ?? record[SERVICE_CONNECTION_OTEL_IDENTITY_KEYS.serviceNamespace]);
376
+ const serviceVersion = normalizeOptionalString(record["serviceVersion"] ?? record[SERVICE_CONNECTION_OTEL_IDENTITY_KEYS.serviceVersion]);
377
+ const deploymentEnvironment = normalizeOptionalString(record["deploymentEnvironment"] ?? record[SERVICE_CONNECTION_OTEL_IDENTITY_KEYS.deploymentEnvironment]);
378
+ const canonicalLogicalServiceId = deriveCanonicalLogicalServiceId({ serviceName, serviceNamespace });
379
+ if (!serviceName && !serviceNamespace && !serviceVersion && !deploymentEnvironment)
380
+ return;
381
+ return { serviceName, serviceNamespace, serviceVersion, deploymentEnvironment, canonicalLogicalServiceId };
382
+ }
383
+ function normalizeServiceConnectionScopeMetadata(value) {
384
+ const record = recordFromUnknown(value);
385
+ if (!record)
386
+ return;
387
+ const scope = {
388
+ identityScope: normalizeIdentityScope(record["identityScope"]),
389
+ deploymentEnvironment: normalizeOptionalString(record["deploymentEnvironment"] ?? record[SERVICE_CONNECTION_OTEL_IDENTITY_KEYS.deploymentEnvironment]),
390
+ region: normalizeOptionalString(record["region"]),
391
+ cluster: normalizeOptionalString(record["cluster"]),
392
+ account: normalizeOptionalString(record["account"]),
393
+ tenant: normalizeOptionalString(record["tenant"])
394
+ };
395
+ return Object.values(scope).some((item) => item !== undefined) ? scope : undefined;
396
+ }
397
+ function normalizeServiceConnectionMetadata(value) {
398
+ const record = recordFromUnknown(value);
399
+ if (!record)
400
+ return;
401
+ const entries = Object.entries(record).filter(([key]) => key.trim().length > 0).slice(0, SERVICE_CONNECTION_EVIDENCE_SOURCE_CAPS.maxMetadataKeys);
402
+ const base = entries.length > 0 ? Object.fromEntries(entries) : {};
403
+ const identity = normalizeServiceConnectionIdentityMetadata(record["identity"] ?? record);
404
+ const scope = normalizeServiceConnectionScopeMetadata(record["scope"]);
405
+ const catalogRecord = recordFromUnknown(record["catalog"]);
406
+ const resourceRecord = recordFromUnknown(record["resource"]);
407
+ const corroborationRecord = recordFromUnknown(record["corroboration"]);
408
+ const observationStatusRecord = recordFromUnknown(record["observationStatus"]);
409
+ const sensitivityRecord = recordFromUnknown(record["sensitivity"]);
410
+ const metadata = {
411
+ ...base,
412
+ identity,
413
+ scope,
414
+ catalog: catalogRecord ? {
415
+ catalogProvider: normalizeOptionalString(catalogRecord["catalogProvider"]),
416
+ componentRef: normalizeOptionalString(catalogRecord["componentRef"]),
417
+ systemRef: normalizeOptionalString(catalogRecord["systemRef"]),
418
+ ownerRef: normalizeOptionalString(catalogRecord["ownerRef"]),
419
+ resourceRef: normalizeOptionalString(catalogRecord["resourceRef"]),
420
+ catalogId: normalizeOptionalString(catalogRecord["catalogId"]),
421
+ catalogSource: normalizeOptionalString(catalogRecord["catalogSource"]),
422
+ owner: normalizeOptionalString(catalogRecord["owner"]),
423
+ lifecycle: normalizeOptionalString(catalogRecord["lifecycle"]),
424
+ tags: normalizeStringArray(catalogRecord["tags"])
425
+ } : undefined,
426
+ resource: resourceRecord ? {
427
+ resourceKind: normalizeServiceConnectionSurfaceKind(resourceRecord["resourceKind"]) ?? undefined,
428
+ provider: normalizeOptionalString(resourceRecord["provider"]),
429
+ protocol: normalizeOptionalString(resourceRecord["protocol"]),
430
+ protocols: normalizeStringArray(resourceRecord["protocols"]),
431
+ operation: normalizeOptionalString(resourceRecord["operation"]),
432
+ endpoint: normalizeOptionalString(resourceRecord["endpoint"]),
433
+ database: normalizeOptionalString(resourceRecord["database"]),
434
+ schema: normalizeOptionalString(resourceRecord["schema"]),
435
+ table: normalizeOptionalString(resourceRecord["table"]),
436
+ topic: normalizeOptionalString(resourceRecord["topic"]),
437
+ bucket: normalizeOptionalString(resourceRecord["bucket"])
438
+ } : undefined,
439
+ corroboration: corroborationRecord ? {
440
+ sourceCount: normalizeFiniteNumber(corroborationRecord["sourceCount"]),
441
+ evidenceSourceKinds: Array.isArray(corroborationRecord["evidenceSourceKinds"]) ? corroborationRecord["evidenceSourceKinds"].map(normalizeServiceConnectionEvidenceSourceKind).filter((item) => Boolean(item)) : undefined,
442
+ evidenceTiers: Array.isArray(corroborationRecord["evidenceTiers"]) ? corroborationRecord["evidenceTiers"].map(normalizeServiceConnectionEvidenceTier).filter((item) => Boolean(item)) : undefined,
443
+ notes: normalizeStringArray(corroborationRecord["notes"])
444
+ } : undefined,
445
+ observationStatus: observationStatusRecord ? {
446
+ status: normalizeObservationStatus(observationStatusRecord["status"]),
447
+ observedAtMs: normalizeFiniteNumber(observationStatusRecord["observedAtMs"]),
448
+ lastSeenAtMs: normalizeFiniteNumber(observationStatusRecord["lastSeenAtMs"]),
449
+ changedAtMs: normalizeFiniteNumber(observationStatusRecord["changedAtMs"]),
450
+ reason: normalizeOptionalString(observationStatusRecord["reason"])
451
+ } : undefined,
452
+ sensitivity: sensitivityRecord ? {
453
+ level: normalizeSensitivityLevel(sensitivityRecord["level"]),
454
+ reason: normalizeOptionalString(sensitivityRecord["reason"]),
455
+ containsSecret: typeof sensitivityRecord["containsSecret"] === "boolean" ? sensitivityRecord["containsSecret"] : undefined,
456
+ redactionHint: sensitivityRecord["redactionHint"] === "none" || sensitivityRecord["redactionHint"] === "partial" || sensitivityRecord["redactionHint"] === "full" ? sensitivityRecord["redactionHint"] : undefined
457
+ } : undefined
458
+ };
459
+ return Object.values(metadata).some((item) => item !== undefined) ? metadata : undefined;
460
+ }
461
+ function normalizeMetadata(value) {
462
+ return normalizeServiceConnectionMetadata(value);
463
+ }
464
+ function normalizeConfidence(value, fallback) {
465
+ if (typeof value !== "number" || !Number.isFinite(value))
466
+ return fallback;
467
+ return Math.max(0, Math.min(1, Number(value.toFixed(3))));
468
+ }
469
+ function normalizeTokenValue(value) {
470
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
471
+ }
472
+ function surfaceAliasMatches(alias, haystack, tokens) {
473
+ const normalizedAlias = normalizeTokenValue(alias);
474
+ if (!normalizedAlias)
475
+ return false;
476
+ if (normalizedAlias.length <= 3)
477
+ return tokens.has(normalizedAlias);
478
+ return tokens.has(normalizedAlias) || haystack.includes(normalizedAlias.replace(/_/g, " "));
479
+ }
480
+ function endpointLabel(endpoint) {
481
+ return endpoint.label ?? endpoint.resourceId ?? endpoint.serviceId ?? endpoint.id;
482
+ }
483
+ function endpointLogicalServiceId(endpoint) {
484
+ return endpoint.metadata?.identity?.canonicalLogicalServiceId ?? endpoint.serviceId;
485
+ }
486
+ function connectionIdentityScope(connection) {
487
+ return normalizeIdentityScope(connection.metadata?.scope?.identityScope ?? connection.source.metadata?.scope?.identityScope);
488
+ }
489
+ function deriveServiceConnectionScopeKey(input = {}) {
490
+ const explicitScopeKey = normalizeOptionalString(input.scopeKey);
491
+ if (explicitScopeKey)
492
+ return normalizeIdentityPart(explicitScopeKey);
493
+ const metadata = normalizeMetadata(input.metadata);
494
+ const scope = input.scope ?? metadata?.scope;
495
+ const parts = [
496
+ normalizeOptionalString(input.projectId) ? `project:${normalizeIdentityPart(normalizeOptionalString(input.projectId))}` : undefined,
497
+ normalizeOptionalString(input.repoId) ? `repo:${normalizeIdentityPart(normalizeOptionalString(input.repoId))}` : undefined,
498
+ scope?.identityScope ? `identity:${scope.identityScope}` : undefined,
499
+ scope?.deploymentEnvironment ? `env:${normalizeIdentityPart(scope.deploymentEnvironment)}` : undefined,
500
+ scope?.region ? `region:${normalizeIdentityPart(scope.region)}` : undefined,
501
+ scope?.cluster ? `cluster:${normalizeIdentityPart(scope.cluster)}` : undefined,
502
+ scope?.account ? `account:${normalizeIdentityPart(scope.account)}` : undefined,
503
+ scope?.tenant ? `tenant:${normalizeIdentityPart(scope.tenant)}` : undefined
504
+ ].filter((part) => Boolean(part));
505
+ return parts.length > 0 ? parts.join("|") : `identity:${DEFAULT_SERVICE_CONNECTION_IDENTITY_SCOPE}`;
506
+ }
507
+ function deriveServiceConnectionStableKey(connection, options = {}) {
508
+ const relationshipType = normalizeServiceConnectionRelationshipType(connection.relationshipType);
509
+ if (!relationshipType)
510
+ throw new Error("Invalid ServiceConnectionV1 relationshipType");
511
+ const scopeKey = deriveServiceConnectionScopeKey({
512
+ projectId: options.projectId ?? connection.projectId,
513
+ repoId: options.repoId,
514
+ scopeKey: options.scopeKey,
515
+ metadata: connection.metadata
516
+ });
517
+ return [
518
+ "service_connection:v1",
519
+ scopeKey,
520
+ normalizeEndpointIdentityPart(connection.source),
521
+ relationshipType,
522
+ normalizeEndpointIdentityPart(connection.target)
523
+ ].join(":");
524
+ }
525
+ function connectionId(connection) {
526
+ return connection.id ?? deriveServiceConnectionStableKey(connection);
527
+ }
528
+ function normalizeEndpointIdentityPart(endpoint) {
529
+ return `${endpoint.kind}:${normalizeIdentityPart(endpoint.id)}`;
530
+ }
531
+ function normalizeIdentityPart(value) {
532
+ return value.trim().toLowerCase().replace(/[^a-z0-9_@./-]+/g, "_").replace(/^_+|_+$/g, "") || "unknown";
533
+ }
534
+ function truncateSummary(value) {
535
+ if (value.length <= SERVICE_CONNECTION_EVIDENCE_SOURCE_CAPS.maxSummaryLength)
536
+ return value;
537
+ return `${value.slice(0, SERVICE_CONNECTION_EVIDENCE_SOURCE_CAPS.maxSummaryLength - 1)}…`;
538
+ }
539
+ function normalizeServiceConnectionEndpointId(value) {
540
+ if (typeof value !== "string")
541
+ return null;
542
+ const normalized = value.trim().toLowerCase().replace(/\s+/g, " ");
543
+ return normalized.length > 0 ? normalized : null;
544
+ }
545
+ function deriveCanonicalLogicalServiceId(input) {
546
+ const serviceName = normalizeServiceConnectionEndpointId(input.serviceName);
547
+ if (!serviceName)
548
+ return;
549
+ const serviceNamespace = normalizeServiceConnectionEndpointId(input.serviceNamespace);
550
+ return serviceNamespace ? `${serviceNamespace}/${serviceName}` : serviceName;
551
+ }
552
+ function deriveServiceConnectionEndpointId(input) {
553
+ const fallbackId = normalizeServiceConnectionEndpointId(input.fallbackId) ?? undefined;
554
+ const logicalServiceId = input.identity?.canonicalLogicalServiceId ?? deriveCanonicalLogicalServiceId({
555
+ serviceName: input.identity?.serviceName,
556
+ serviceNamespace: input.identity?.serviceNamespace
557
+ });
558
+ if (!logicalServiceId)
559
+ return fallbackId;
560
+ if (input.scope?.identityScope === "deployment_environment") {
561
+ const deploymentEnvironment = normalizeServiceConnectionEndpointId(input.scope.deploymentEnvironment ?? input.identity?.deploymentEnvironment);
562
+ return deploymentEnvironment ? `${logicalServiceId}@${deploymentEnvironment}` : logicalServiceId;
563
+ }
564
+ return logicalServiceId;
565
+ }
566
+ function serviceConnectionRelationshipSemantics(relationshipType) {
567
+ return SERVICE_CONNECTION_RELATIONSHIP_SEMANTICS[relationshipType];
568
+ }
569
+ function normalizeServiceConnectionRelationshipType(value) {
570
+ const sanitized = sanitizeConnectionRelationshipType(value);
571
+ if (!sanitized || !SERVICE_CONNECTION_RELATIONSHIP_TYPE_SET.has(sanitized))
572
+ return null;
573
+ return sanitized;
574
+ }
575
+ function serviceConnectionRelationshipDirection(relationshipType) {
576
+ return CONNECTION_RELATIONSHIP_TYPE_REGISTRY[relationshipType].direction;
577
+ }
578
+ function serviceConnectionRelationshipRiskLevel(relationshipType) {
579
+ return CONNECTION_RELATIONSHIP_TYPE_REGISTRY[relationshipType].riskLevel;
580
+ }
581
+ function isServiceConnectionEndpointKind(value) {
582
+ return typeof value === "string" && SERVICE_CONNECTION_ENDPOINT_KIND_SET.has(value);
583
+ }
584
+ function normalizeServiceConnectionEndpointKind(value) {
585
+ return isServiceConnectionEndpointKind(value) ? value : null;
586
+ }
587
+ function normalizeServiceConnectionSurfaceKind(value) {
588
+ if (typeof value !== "string")
589
+ return null;
590
+ const normalized = normalizeTokenValue(value);
591
+ return SERVICE_CONNECTION_SURFACE_KIND_SET.has(normalized) ? normalized : null;
592
+ }
593
+ function inferServiceConnectionSurfaceKind(input) {
594
+ const explicit = normalizeServiceConnectionSurfaceKind(input.kind);
595
+ if (explicit)
596
+ return explicit;
597
+ const metadata = normalizeMetadata(input.metadata);
598
+ const metadataKind = normalizeServiceConnectionSurfaceKind(metadata?.["surfaceKind"] ?? metadata?.["resourceKind"] ?? metadata?.resource?.resourceKind);
599
+ if (metadataKind)
600
+ return metadataKind;
601
+ const rawHaystack = [input.label, input.id, input.uri, input.path].map((value) => typeof value === "string" ? value.toLowerCase() : "").join(" ");
602
+ const tokens = new Set(rawHaystack.split(/[^a-z0-9]+/g).filter(Boolean));
603
+ const haystack = rawHaystack.replace(/[^a-z0-9]+/g, " ");
604
+ for (const kind of SERVICE_CONNECTION_SURFACE_KINDS) {
605
+ if (kind === "unknown")
606
+ continue;
607
+ if (SERVICE_CONNECTION_SURFACE_KIND_ALIASES[kind].some((alias) => surfaceAliasMatches(alias, haystack, tokens)))
608
+ return kind;
609
+ }
610
+ return "unknown";
611
+ }
612
+ function toServiceConnectionEndpoint(input) {
613
+ const kind = normalizeServiceConnectionEndpointKind(input.kind);
614
+ const inputMetadata = normalizeMetadata(input.metadata);
615
+ const identity = normalizeServiceConnectionIdentityMetadata(input.identity ?? inputMetadata?.identity ?? input.metadata);
616
+ const scope = normalizeServiceConnectionScopeMetadata(input.scope ?? inputMetadata?.scope);
617
+ const id = deriveServiceConnectionEndpointId({ identity, scope, fallbackId: input.id });
618
+ if (!kind || !id)
619
+ return null;
620
+ const metadata = normalizeMetadata({
621
+ ...inputMetadata ?? {},
622
+ identity: identity ?? inputMetadata?.identity,
623
+ scope: scope ?? inputMetadata?.scope
624
+ });
625
+ const resourceKind = normalizeServiceConnectionSurfaceKind(input.resourceKind) ?? normalizeServiceConnectionSurfaceKind(metadata?.resource?.resourceKind) ?? inferServiceConnectionSurfaceKind({
626
+ kind: input.resourceKind,
627
+ label: input.label,
628
+ id,
629
+ uri: input.uri,
630
+ path: input.path,
631
+ metadata
632
+ });
633
+ return {
634
+ schemaVersion: 1,
635
+ kind,
636
+ id,
637
+ label: normalizeOptionalString(input.label),
638
+ serviceId: normalizeServiceConnectionEndpointId(input.serviceId) ?? metadata?.identity?.canonicalLogicalServiceId,
639
+ resourceId: normalizeServiceConnectionEndpointId(input.resourceId) ?? undefined,
640
+ resourceKind,
641
+ uri: normalizeOptionalString(input.uri),
642
+ path: normalizeOptionalString(input.path),
643
+ hash: normalizeOptionalString(input.hash),
644
+ revision: normalizeOptionalString(input.revision),
645
+ metadata
646
+ };
647
+ }
648
+ function normalizeServiceConnectionEvidenceSourceKind(value) {
649
+ if (typeof value !== "string")
650
+ return null;
651
+ const normalized = normalizeTokenValue(value);
652
+ return SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND_SET.has(normalized) ? normalized : null;
653
+ }
654
+ function resolveServiceConnectionEvidenceSourceKind(value) {
655
+ if (value === undefined || value === null || value === "")
656
+ return DEFAULT_SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND;
657
+ return normalizeServiceConnectionEvidenceSourceKind(value) ?? FALLBACK_SERVICE_CONNECTION_EVIDENCE_SOURCE_KIND;
658
+ }
659
+ function normalizeServiceConnectionEvidenceTier(value) {
660
+ if (typeof value !== "string")
661
+ return null;
662
+ const normalized = normalizeTokenValue(value);
663
+ return SERVICE_CONNECTION_EVIDENCE_TIER_SET.has(normalized) ? normalized : null;
664
+ }
665
+ function serviceConnectionEvidenceSourceDefaults(kind) {
666
+ return SERVICE_CONNECTION_EVIDENCE_SOURCE_DEFAULTS[resolveServiceConnectionEvidenceSourceKind(kind)];
667
+ }
668
+ function capServiceConnectionEvidenceConfidence(kind, confidence) {
669
+ const defaults = serviceConnectionEvidenceSourceDefaults(kind);
670
+ const normalized = normalizeConfidence(confidence, defaults.defaultConfidence);
671
+ return defaults.confidenceCap === undefined ? normalized : Math.min(defaults.confidenceCap, normalized);
672
+ }
673
+ function normalizeServiceConnectionEvidenceSource(input) {
674
+ const kind = resolveServiceConnectionEvidenceSourceKind(input.kind);
675
+ const defaults = serviceConnectionEvidenceSourceDefaults(kind);
676
+ const requestedTier = normalizeServiceConnectionEvidenceTier(input.evidenceTier);
677
+ return {
678
+ schemaVersion: 1,
679
+ kind,
680
+ id: normalizeOptionalString(input.id),
681
+ label: normalizeOptionalString(input.label) ?? defaults.label,
682
+ evidenceTier: requestedTier === defaults.evidenceTier ? requestedTier : defaults.evidenceTier,
683
+ confidence: capServiceConnectionEvidenceConfidence(kind, input.confidence),
684
+ uri: normalizeOptionalString(input.uri),
685
+ path: normalizeOptionalString(input.path),
686
+ hash: normalizeOptionalString(input.hash),
687
+ revision: normalizeOptionalString(input.revision),
688
+ metadata: normalizeMetadata(input.metadata)
689
+ };
690
+ }
691
+ function defaultServiceConnectionEvidenceTier(evidenceSources) {
692
+ return evidenceSources?.[0]?.evidenceTier ?? SERVICE_CONNECTION_EVIDENCE_SOURCE_DEFAULTS.code_static.evidenceTier;
693
+ }
694
+ function defaultServiceConnectionState(evidenceSources) {
695
+ return evidenceSources?.[0] ? serviceConnectionEvidenceSourceDefaults(evidenceSources[0].kind).state : DEFAULT_SERVICE_CONNECTION_STATE;
696
+ }
697
+ function normalizeServiceConnectionV1(input, options = {}) {
698
+ const relationshipType = normalizeServiceConnectionRelationshipType(input.relationshipType);
699
+ if (!relationshipType)
700
+ throw new Error("Invalid ServiceConnectionV1 relationshipType");
701
+ if (input.direction !== undefined && input.direction !== "directed") {
702
+ throw new Error("ServiceConnectionV1 only supports directed relationships");
703
+ }
704
+ const source = input.source ? toServiceConnectionEndpoint(input.source) : null;
705
+ const target = input.target ? toServiceConnectionEndpoint(input.target) : null;
706
+ if (!source || source.kind !== "service")
707
+ throw new Error("ServiceConnectionV1 source must be a service endpoint");
708
+ if (!target)
709
+ throw new Error("ServiceConnectionV1 target must be a service or resource endpoint");
710
+ const evidenceSources = Array.isArray(input.evidenceSources) ? input.evidenceSources.map((sourceInput) => normalizeServiceConnectionEvidenceSource(sourceInput)).slice(0, SERVICE_CONNECTION_EVIDENCE_SOURCE_CAPS.maxSourcesPerConnection) : undefined;
711
+ const evidenceTier = normalizeServiceConnectionEvidenceTier(input.evidenceTier) ?? defaultServiceConnectionEvidenceTier(evidenceSources);
712
+ const explicitState = normalizeConnectedWorkState(input.state);
713
+ const lifecycleState = normalizeServiceConnectionLifecycleState(input.lifecycleState, serviceConnectionLifecycleStateFromConnectedWorkState(explicitState));
714
+ const state = explicitState ?? connectedWorkStateForServiceConnectionLifecycleState(lifecycleState);
715
+ const metadata = normalizeMetadata(input.metadata);
716
+ const createdAtMs = normalizeFiniteNumber(input.createdAtMs) ?? options.generatedAtMs;
717
+ const updatedAtMs = normalizeFiniteNumber(input.updatedAtMs) ?? options.generatedAtMs ?? createdAtMs;
718
+ const normalizedWithoutId = {
719
+ schemaVersion: 1,
720
+ id: normalizeOptionalString(input.id),
721
+ projectId: normalizeOptionalString(options.projectId) ?? normalizeOptionalString(input.projectId),
722
+ relationshipType,
723
+ direction: "directed",
724
+ source,
725
+ target,
726
+ sourceSurfaceKind: normalizeServiceConnectionSurfaceKind(input.sourceSurfaceKind) ?? source.resourceKind,
727
+ targetSurfaceKind: normalizeServiceConnectionSurfaceKind(input.targetSurfaceKind) ?? target.resourceKind,
728
+ evidenceTier,
729
+ state,
730
+ lifecycleState,
731
+ confidence: normalizeConfidence(input.confidence, evidenceSources?.[0]?.confidence ?? 0.5),
732
+ evidenceSources,
733
+ candidateKind: normalizeCandidateKind(input.candidateKind),
734
+ directionHint: normalizeDirectionHint(input.directionHint),
735
+ edgeLabel: normalizeOptionalString(input.edgeLabel),
736
+ staticEvidenceTier: normalizeStaticEvidenceTier(input.staticEvidenceTier),
737
+ missBuckets: normalizeStringArray(input.missBuckets),
738
+ reportOnly: input.reportOnly === true ? true : undefined,
739
+ advisoryOnly: input.advisoryOnly === true ? true : undefined,
740
+ noAuthority: input.noAuthority === true ? true : undefined,
741
+ summary: normalizeOptionalString(input.summary) ? truncateSummary(normalizeOptionalString(input.summary)) : undefined,
742
+ createdAtMs,
743
+ updatedAtMs,
744
+ metadata
745
+ };
746
+ const id = normalizedWithoutId.id ?? deriveServiceConnectionStableKey(normalizedWithoutId, {
747
+ projectId: normalizedWithoutId.projectId,
748
+ repoId: options.repoId,
749
+ scopeKey: options.scopeKey
750
+ });
751
+ return { ...normalizedWithoutId, id };
752
+ }
753
+ function summarizeServiceConnection(connection) {
754
+ const sourceLabel = endpointLabel(connection.source);
755
+ const targetLabel = endpointLabel(connection.target);
756
+ const sourceSurfaceKind = connection.sourceSurfaceKind ?? connection.source.resourceKind ?? inferServiceConnectionSurfaceKind(connection.source);
757
+ const targetSurfaceKind = connection.targetSurfaceKind ?? connection.target.resourceKind ?? inferServiceConnectionSurfaceKind(connection.target);
758
+ const evidenceTier = connection.evidenceTier ?? defaultServiceConnectionEvidenceTier(connection.evidenceSources);
759
+ const state = connection.state ?? defaultServiceConnectionState(connection.evidenceSources);
760
+ const lifecycleState = connection.lifecycleState ?? serviceConnectionLifecycleStateFromConnectedWorkState(state);
761
+ const semantics = serviceConnectionRelationshipSemantics(connection.relationshipType);
762
+ const title = truncateSummary(connection.summary ?? `${sourceLabel} ${semantics.sourceDisplayLabel} ${targetLabel}`);
763
+ return {
764
+ schemaVersion: 1,
765
+ id: connectionId(connection),
766
+ title,
767
+ subtitle: `${sourceSurfaceKind} → ${targetSurfaceKind}`,
768
+ relationshipType: connection.relationshipType,
769
+ direction: serviceConnectionRelationshipDirection(connection.relationshipType),
770
+ riskLevel: serviceConnectionRelationshipRiskLevel(connection.relationshipType),
771
+ sourceDisplayLabel: semantics.sourceDisplayLabel,
772
+ inverseDisplayLabel: semantics.inverseDisplayLabel,
773
+ sourceServiceMeaning: semantics.sourceServiceMeaning,
774
+ sourceLogicalServiceId: endpointLogicalServiceId(connection.source),
775
+ targetLogicalServiceId: endpointLogicalServiceId(connection.target),
776
+ identityScope: connectionIdentityScope(connection),
777
+ sourceLabel,
778
+ targetLabel,
779
+ sourceSurfaceKind,
780
+ targetSurfaceKind,
781
+ evidenceTier,
782
+ state,
783
+ lifecycleState,
784
+ confidence: normalizeConfidence(connection.confidence, 0.5),
785
+ evidenceSourceCount: connection.evidenceSources?.length ?? 0,
786
+ updatedAtMs: connection.updatedAtMs
787
+ };
788
+ }
789
+
790
+ // ../core/src/serviceConnectionEvidence/types.ts
791
+ var SERVICE_CONNECTION_EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
792
+ var SERVICE_CONNECTION_EVIDENCE_BUNDLE_KIND = "service_connection_evidence_bundle";
793
+ var SERVICE_CONNECTION_EVIDENCE_TOKEN_ESTIMATE_VERSION = "char-div-4-v1";
794
+
795
+ // ../core/src/serviceConnectionEvidence/builders.ts
796
+ var DEFAULT_GENERATED_AT = Date.parse("2026-05-18T00:00:00.000Z");
797
+ var DEFAULT_SOURCE = "explicit";
798
+ var PATH_ONLY_TOKEN_ESTIMATE = 24;
799
+ var PROOF_HINT_TOKEN_ESTIMATE = 18;
800
+ var FRESHNESS_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
801
+ var REQUIRED_PROOF_HINTS_BY_MISS_BUCKET = {
802
+ candidate_without_runtime_owner: "Add runtime owner evidence: entrypoint, deployment, package, or process owner.",
803
+ secret_without_consumer: "Link secret/config key to a source consumer.",
804
+ datastore_without_tests: "Add targeted datastore test or migration proof.",
805
+ route_without_handler: "Link route file to handler/controller symbol.",
806
+ provider_client_without_secret: "Link provider client to config/env key evidence.",
807
+ path_only_candidate_needs_review: "Add import, call, env, schema, test, or doc evidence before treating as dependency."
808
+ };
809
+ var REPORT_ONLY_GUARDRAILS = {
810
+ reportOnly: true,
811
+ advisoryOnly: true,
812
+ noAuthority: true,
813
+ noLive: true,
814
+ noProductionWrites: true,
815
+ noConvexWrites: true,
816
+ noProviderCalls: true,
817
+ noNetworkCalls: true,
818
+ noFilesystemSourceScan: true,
819
+ noHiddenExecution: true,
820
+ noSecretsIncluded: true,
821
+ redactedReferencesOnly: true,
822
+ contextDoesNotMutateCanon: true,
823
+ sourceContextGrantsNoAuthority: true,
824
+ workingSetGrantsAuthority: false
825
+ };
826
+ var NO_LIVE = {
827
+ pass: true,
828
+ productionWrites: false,
829
+ convexWrites: false,
830
+ providers: false,
831
+ network: false,
832
+ memgraph: false,
833
+ filesystemSourceScan: false,
834
+ hiddenExecution: false
835
+ };
836
+ var SECRET_VALUE_PATTERN = /\b([A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|API_KEY|PRIVATE_KEY|ACCESS_KEY)[A-Z0-9_]*)\s*[:=]\s*([^\s,;]+)/gi;
837
+ var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi;
838
+ function buildServiceConnectionEvidenceBundleV1(args = {}) {
839
+ const generatedAt = args.generatedAt ?? DEFAULT_GENERATED_AT;
840
+ const source = args.source ?? DEFAULT_SOURCE;
841
+ const warnings = [];
842
+ const normalizedConnections = normalizeConnections(args, generatedAt, warnings);
843
+ const staticEvidenceRefs = normalizeStaticEvidenceRefs(args.staticEvidenceRefs, warnings);
844
+ const refs = normalizeEvidenceRefs(args, normalizedConnections, generatedAt, warnings, staticEvidenceRefs);
845
+ const grouped = buildConnectionSummaries(args, normalizedConnections, refs);
846
+ const requiredProofHints = uniqueStrings([
847
+ ...grouped.flatMap((summary) => summary.requiredProofHints),
848
+ ...(args.missBuckets ?? []).map((bucket) => REQUIRED_PROOF_HINTS_BY_MISS_BUCKET[bucket]).filter((hint) => Boolean(hint))
849
+ ]).sort();
850
+ const tokenSummary = buildTokenSummary(grouped, refs, requiredProofHints);
851
+ if (typeof args.tokenBudget === "number" && tokenSummary.total > args.tokenBudget) {
852
+ warnings.push("service_connection_evidence:token_budget_exceeded");
853
+ }
854
+ return {
855
+ schemaVersion: SERVICE_CONNECTION_EVIDENCE_BUNDLE_SCHEMA_VERSION,
856
+ kind: SERVICE_CONNECTION_EVIDENCE_BUNDLE_KIND,
857
+ bundleId: args.bundleId ?? defaultBundleId(args, grouped, refs),
858
+ generatedAt,
859
+ source,
860
+ connectionSummaries: grouped,
861
+ evidenceRefs: refs,
862
+ requiredProofHints,
863
+ tokenSummary,
864
+ statusSummary: buildStatusSummary(grouped, refs),
865
+ redactionSummary: buildRedactionSummary(refs),
866
+ warnings: uniqueStrings(warnings).sort(),
867
+ staticEvidenceRefs,
868
+ missBuckets: uniqueStrings(args.missBuckets ?? []).sort(),
869
+ candidateKindCounts: args.candidateKindCounts,
870
+ evidenceTierCounts: args.evidenceTierCounts,
871
+ guardrails: REPORT_ONLY_GUARDRAILS,
872
+ noLive: NO_LIVE,
873
+ reportOnly: true,
874
+ advisoryOnly: true,
875
+ noAuthority: true
876
+ };
877
+ }
878
+ function normalizeConnections(args, generatedAt, warnings) {
879
+ const output = [];
880
+ for (const input of args.serviceConnections ?? []) {
881
+ try {
882
+ const connection = isNormalizedServiceConnection(input) ? normalizeServiceConnectionV1(input, { generatedAtMs: generatedAt }) : normalizeServiceConnectionV1(input, { generatedAtMs: generatedAt });
883
+ output.push({ connection, summary: summarizeServiceConnection(connection) });
884
+ } catch (error) {
885
+ warnings.push(`service_connection_evidence:invalid_connection:${error instanceof Error ? error.message : "unknown"}`);
886
+ }
887
+ }
888
+ return output.sort((a, b) => a.summary.id.localeCompare(b.summary.id));
889
+ }
890
+ function normalizeEvidenceRefs(args, normalizedConnections, generatedAt, warnings, staticEvidenceRefs) {
891
+ const inputs = [];
892
+ inputs.push(...args.serviceConnectionRefs ?? []);
893
+ inputs.push(...(args.serviceConnectionIds ?? []).map((serviceConnectionId) => ({ serviceConnectionId })));
894
+ inputs.push(...(args.evidencePaths ?? []).map((evidencePath) => ({
895
+ evidencePath,
896
+ evidenceLabel: args.evidenceLabels?.[evidencePath]
897
+ })));
898
+ for (const item of normalizedConnections) {
899
+ inputs.push(...connectionEvidenceInputs(item.connection, item.summary, generatedAt));
900
+ }
901
+ inputs.push(...staticEvidenceRefs.map(staticEvidenceInput));
902
+ const deduped = new Map;
903
+ for (const input of inputs) {
904
+ const normalized = normalizeEvidenceRefInput({ input, defaultLifecycleState: args.defaultLifecycleState, defaultRelationshipType: args.defaultRelationshipType });
905
+ if (!normalized) {
906
+ warnings.push("service_connection_evidence:empty_ref_skipped");
907
+ continue;
908
+ }
909
+ const existing = deduped.get(normalized.evidenceId);
910
+ if (!existing || normalized.tokenEstimate > existing.tokenEstimate) {
911
+ deduped.set(normalized.evidenceId, normalized);
912
+ }
913
+ }
914
+ return Array.from(deduped.values()).sort(compareEvidenceRefs);
915
+ }
916
+ function normalizeStaticEvidenceRefs(refs, warnings) {
917
+ if (!Array.isArray(refs))
918
+ return [];
919
+ const deduped = new Map;
920
+ for (const ref of refs) {
921
+ const kind = cleanString(ref.kind);
922
+ const detail = cleanString(ref.detail);
923
+ if (!kind || !detail) {
924
+ warnings.push("service_connection_evidence:empty_static_ref_skipped");
925
+ continue;
926
+ }
927
+ const containsSecret = kind === "env" || ref.redacted === true;
928
+ const valueLabel = ref.valueLabel ? redactText(ref.valueLabel, containsSecret).text : undefined;
929
+ const normalized = {
930
+ serviceConnectionId: cleanString(ref.serviceConnectionId),
931
+ candidateId: cleanString(ref.candidateId),
932
+ kind: ref.kind,
933
+ path: cleanString(ref.path),
934
+ symbol: cleanString(ref.symbol),
935
+ stableKey: cleanString(ref.stableKey),
936
+ valueLabel,
937
+ detail: redactText(detail, containsSecret).text,
938
+ confidenceDelta: typeof ref.confidenceDelta === "number" && Number.isFinite(ref.confidenceDelta) ? Math.max(0, Math.min(1, ref.confidenceDelta)) : undefined,
939
+ redacted: containsSecret ? true : undefined
940
+ };
941
+ const key = [
942
+ normalized.kind,
943
+ normalized.path ?? "",
944
+ normalized.symbol ?? "",
945
+ normalized.stableKey ?? "",
946
+ normalized.valueLabel ?? ""
947
+ ].join("|");
948
+ deduped.set(key, normalized);
949
+ }
950
+ return Array.from(deduped.values()).sort((a, b) => {
951
+ const left = [a.kind, a.path ?? "", a.symbol ?? "", a.stableKey ?? "", a.valueLabel ?? ""].join("|");
952
+ const right = [b.kind, b.path ?? "", b.symbol ?? "", b.stableKey ?? "", b.valueLabel ?? ""].join("|");
953
+ return left.localeCompare(right);
954
+ });
955
+ }
956
+ function staticEvidenceInput(ref) {
957
+ const serviceConnectionId = ref.serviceConnectionId ?? ref.candidateId ?? "service:static_candidate";
958
+ return {
959
+ serviceConnectionId,
960
+ evidenceRef: ref.stableKey ?? ref.path ?? `${ref.kind}:${ref.detail}`,
961
+ evidencePath: ref.path,
962
+ evidenceLabel: ref.valueLabel ?? ref.detail,
963
+ status: ref.kind === "miss_bucket" ? "missing_evidence" : "needs_review",
964
+ freshness: "unknown",
965
+ tokenEstimate: estimateTextTokens([ref.kind, ref.path, ref.symbol, ref.valueLabel, ref.detail].filter(Boolean).join(" ")),
966
+ containsSecret: ref.redacted === true || ref.kind === "env"
967
+ };
968
+ }
969
+ function connectionEvidenceInputs(connection, summary, generatedAt) {
970
+ const sources = connection.evidenceSources && connection.evidenceSources.length > 0 ? connection.evidenceSources : [undefined];
971
+ return sources.map((source, index) => ({
972
+ serviceConnectionId: summary.id,
973
+ serviceConnectionRef: connection.id,
974
+ evidenceRef: evidenceRefFromSource(source, summary.id, index),
975
+ evidencePath: source?.path ?? source?.uri,
976
+ evidenceLabel: source?.label ?? summary.title,
977
+ lifecycleState: summary.lifecycleState,
978
+ relationshipType: summary.relationshipType,
979
+ status: observationStatusFromSource(source),
980
+ freshness: freshnessForConnection(connection, generatedAt),
981
+ tokenEstimate: estimateTextTokens([summary.title, source?.path, source?.uri, source?.label].filter(Boolean).join(" ")),
982
+ containsSecret: source?.metadata?.sensitivity?.containsSecret === true
983
+ }));
984
+ }
985
+ function normalizeEvidenceRefInput(args) {
986
+ const rawServiceConnectionId = cleanString(args.input.serviceConnectionId) ?? cleanString(args.input.serviceConnectionRef) ?? "service:unknown";
987
+ const serviceIdRedaction = redactText(rawServiceConnectionId, args.input.containsSecret === true);
988
+ const rawRef = cleanString(args.input.evidenceRef) ?? cleanString(args.input.evidencePath) ?? cleanString(args.input.serviceConnectionRef) ?? rawServiceConnectionId;
989
+ const refRedaction = redactText(rawRef, args.input.containsSecret === true);
990
+ const rawPath = cleanString(args.input.evidencePath);
991
+ const pathRedaction = rawPath ? redactText(rawPath, args.input.containsSecret === true) : undefined;
992
+ const rawLabel = cleanString(args.input.evidenceLabel) ?? cleanString(args.input.label) ?? cleanString(args.input.evidencePath) ?? rawServiceConnectionId;
993
+ const labelRedaction = redactText(rawLabel, args.input.containsSecret === true);
994
+ const evidenceRef = refRedaction.text;
995
+ if (!evidenceRef)
996
+ return null;
997
+ const lifecycleState = args.input.lifecycleState ? normalizeServiceConnectionLifecycleState(args.input.lifecycleState, args.defaultLifecycleState) : args.defaultLifecycleState;
998
+ const status = normalizeStatus(args.input.status, lifecycleState);
999
+ const freshness = args.input.freshness ?? freshnessFromStatus(status);
1000
+ const redaction = mergeRedactions([serviceIdRedaction, refRedaction, labelRedaction, pathRedaction].filter((item) => Boolean(item)));
1001
+ const serviceConnectionId = serviceIdRedaction.text || "service:redacted";
1002
+ const stableKey = [serviceConnectionId, evidenceRef, lifecycleState ?? "", args.input.relationshipType ?? args.defaultRelationshipType ?? ""].join("|");
1003
+ const tokenEstimate = positiveInteger(args.input.tokenEstimate) ?? Math.max(PATH_ONLY_TOKEN_ESTIMATE, estimateTextTokens([serviceConnectionId, evidenceRef, pathRedaction?.text, labelRedaction.text].filter(Boolean).join(" ")));
1004
+ return {
1005
+ evidenceId: `service-evidence:${hashText(stableKey).slice(0, 16)}`,
1006
+ serviceConnectionId,
1007
+ serviceConnectionRef: cleanString(args.input.serviceConnectionRef),
1008
+ evidenceRef,
1009
+ evidencePath: pathRedaction?.text,
1010
+ evidenceLabel: labelRedaction.text || "Service connection evidence",
1011
+ relationshipType: args.input.relationshipType ?? args.defaultRelationshipType,
1012
+ lifecycleState,
1013
+ status,
1014
+ freshness,
1015
+ tokenEstimate,
1016
+ requiredProofHints: uniqueStrings(args.input.requiredProofHints ?? []).sort(),
1017
+ redaction,
1018
+ reportOnly: true,
1019
+ advisoryOnly: true,
1020
+ noAuthority: true
1021
+ };
1022
+ }
1023
+ function buildConnectionSummaries(args, normalizedConnections, refs) {
1024
+ const summaryById = new Map(normalizedConnections.map((item) => [item.summary.id, item.summary]));
1025
+ const connectionIds = uniqueStrings([
1026
+ ...refs.map((ref) => ref.serviceConnectionId),
1027
+ ...args.serviceConnectionIds ?? [],
1028
+ ...normalizedConnections.map((item) => item.summary.id)
1029
+ ]).sort();
1030
+ return connectionIds.map((serviceConnectionId) => {
1031
+ const matchingRefs = refs.filter((ref) => ref.serviceConnectionId === serviceConnectionId);
1032
+ const compactSummary = summaryById.get(serviceConnectionId);
1033
+ const statuses = matchingRefs.map((ref) => ref.status);
1034
+ const freshnessValues = matchingRefs.map((ref) => ref.freshness);
1035
+ const lifecycleState = compactSummary?.lifecycleState ?? firstDefined(matchingRefs.map((ref) => ref.lifecycleState));
1036
+ const status = statusForRefs(statuses, lifecycleState);
1037
+ const freshness = freshnessForRefs(freshnessValues, status);
1038
+ const requiredProofHints = uniqueStrings([
1039
+ ...matchingRefs.flatMap((ref) => ref.requiredProofHints),
1040
+ ...proofHintsFor({
1041
+ status,
1042
+ freshness,
1043
+ lifecycleState,
1044
+ relationshipType: compactSummary?.relationshipType ?? firstDefined(matchingRefs.map((ref) => ref.relationshipType)),
1045
+ evidenceRefCount: matchingRefs.length
1046
+ })
1047
+ ]).sort();
1048
+ const title = compactSummary?.title ?? serviceConnectionId;
1049
+ const label = compactSummary?.subtitle ? `${title} (${compactSummary.subtitle})` : title;
1050
+ const evidenceRefTokens = matchingRefs.reduce((total, ref) => total + ref.tokenEstimate, 0);
1051
+ const summaryTokenEstimate = estimateTextTokens([label, ...requiredProofHints].join(" "));
1052
+ return {
1053
+ serviceConnectionId,
1054
+ title,
1055
+ label,
1056
+ relationshipType: compactSummary?.relationshipType ?? firstDefined(matchingRefs.map((ref) => ref.relationshipType)),
1057
+ lifecycleState,
1058
+ status,
1059
+ freshness,
1060
+ evidenceRefs: uniqueStrings(matchingRefs.map((ref) => ref.evidenceRef)).sort(),
1061
+ evidencePaths: uniqueStrings(matchingRefs.map((ref) => ref.evidencePath).filter((path) => Boolean(path))).sort(),
1062
+ requiredProofHints,
1063
+ tokenEstimate: summaryTokenEstimate + evidenceRefTokens,
1064
+ compactSummary,
1065
+ redaction: mergeRedactions(matchingRefs.map((ref) => ref.redaction)),
1066
+ reportOnly: true,
1067
+ advisoryOnly: true,
1068
+ noAuthority: true
1069
+ };
1070
+ }).sort((a, b) => a.serviceConnectionId.localeCompare(b.serviceConnectionId));
1071
+ }
1072
+ function buildTokenSummary(summaries, refs, requiredProofHints) {
1073
+ const connectionSummaryTokens = summaries.reduce((total, summary) => total + estimateTextTokens([summary.title, summary.label].join(" ")), 0);
1074
+ const evidenceRefTokens = refs.reduce((total, ref) => total + ref.tokenEstimate, 0);
1075
+ const proofHintTokens = requiredProofHints.length * PROOF_HINT_TOKEN_ESTIMATE;
1076
+ return {
1077
+ estimateVersion: SERVICE_CONNECTION_EVIDENCE_TOKEN_ESTIMATE_VERSION,
1078
+ total: connectionSummaryTokens + evidenceRefTokens + proofHintTokens,
1079
+ connectionSummaryTokens,
1080
+ evidenceRefTokens,
1081
+ proofHintTokens,
1082
+ byConnection: summaries.map((summary) => ({
1083
+ serviceConnectionId: summary.serviceConnectionId,
1084
+ evidenceRefCount: summary.evidenceRefs.length,
1085
+ tokenEstimate: summary.tokenEstimate
1086
+ })),
1087
+ byEvidenceRef: refs.map((ref) => ({
1088
+ evidenceId: ref.evidenceId,
1089
+ serviceConnectionId: ref.serviceConnectionId,
1090
+ tokenEstimate: ref.tokenEstimate
1091
+ }))
1092
+ };
1093
+ }
1094
+ function buildStatusSummary(summaries, refs) {
1095
+ const statuses = summaries.map((summary) => summary.status);
1096
+ const freshness = summaries.reduce((counts, summary) => {
1097
+ counts[summary.freshness] += 1;
1098
+ return counts;
1099
+ }, { fresh: 0, stale: 0, unknown: 0 });
1100
+ return {
1101
+ totalConnections: summaries.length,
1102
+ totalEvidenceRefs: refs.length,
1103
+ presentCount: statuses.filter((status) => status === "present").length,
1104
+ missingEvidenceCount: statuses.filter((status) => status === "missing_evidence").length,
1105
+ needsReviewCount: statuses.filter((status) => status === "needs_review").length,
1106
+ staleCount: statuses.filter((status) => status === "stale").length,
1107
+ rejectedCount: statuses.filter((status) => status === "rejected").length,
1108
+ freshness
1109
+ };
1110
+ }
1111
+ function buildRedactionSummary(refs) {
1112
+ const redactedReferenceCount = refs.filter((ref) => ref.redaction.status === "redacted").length;
1113
+ const blockedSecretInputCount = refs.filter((ref) => ref.redaction.status === "secret_input_blocked").length;
1114
+ const cleanReferenceCount = refs.filter((ref) => ref.redaction.status === "clean").length;
1115
+ return {
1116
+ posture: "no_secret_values",
1117
+ noSecretValuesIncluded: true,
1118
+ redactedReferenceCount,
1119
+ blockedSecretInputCount,
1120
+ cleanReferenceCount,
1121
+ reasonCodes: uniqueStrings(refs.flatMap((ref) => ref.redaction.reasonCodes)).sort()
1122
+ };
1123
+ }
1124
+ function evidenceRefFromSource(source, serviceConnectionId, index) {
1125
+ return source?.id ?? source?.path ?? source?.uri ?? `${serviceConnectionId}:evidence:${index + 1}`;
1126
+ }
1127
+ function observationStatusFromSource(source) {
1128
+ return source?.metadata?.observationStatus?.status;
1129
+ }
1130
+ function freshnessForConnection(connection, generatedAt) {
1131
+ if (connection.lifecycleState === "stale")
1132
+ return "stale";
1133
+ if (!connection.updatedAtMs)
1134
+ return "unknown";
1135
+ const ageMs = Math.max(0, generatedAt - connection.updatedAtMs);
1136
+ return ageMs <= FRESHNESS_WINDOW_MS ? "fresh" : "unknown";
1137
+ }
1138
+ function normalizeStatus(status, lifecycleState) {
1139
+ if (status === "absent_in_latest_scan")
1140
+ return "stale";
1141
+ if (status === "changed")
1142
+ return "needs_review";
1143
+ if (status === "unknown")
1144
+ return lifecycleState === "verified" ? "present" : "needs_review";
1145
+ if (status === "present" || status === "missing_evidence" || status === "needs_review" || status === "stale" || status === "rejected")
1146
+ return status;
1147
+ if (lifecycleState === "rejected" || lifecycleState === "retracted")
1148
+ return "rejected";
1149
+ if (lifecycleState === "stale")
1150
+ return "stale";
1151
+ if (lifecycleState === "verified")
1152
+ return "present";
1153
+ return "needs_review";
1154
+ }
1155
+ function statusForRefs(statuses, lifecycleState) {
1156
+ if (statuses.length === 0)
1157
+ return "missing_evidence";
1158
+ if (statuses.includes("rejected"))
1159
+ return "rejected";
1160
+ if (statuses.includes("stale"))
1161
+ return "stale";
1162
+ if (statuses.includes("needs_review"))
1163
+ return lifecycleState === "verified" ? "present" : "needs_review";
1164
+ return "present";
1165
+ }
1166
+ function freshnessFromStatus(status) {
1167
+ if (status === "stale" || status === "rejected")
1168
+ return "stale";
1169
+ if (status === "present")
1170
+ return "fresh";
1171
+ return "unknown";
1172
+ }
1173
+ function freshnessForRefs(freshnessValues, status) {
1174
+ if (freshnessValues.length === 0)
1175
+ return freshnessFromStatus(status);
1176
+ if (freshnessValues.includes("stale"))
1177
+ return "stale";
1178
+ if (freshnessValues.every((freshness) => freshness === "fresh"))
1179
+ return "fresh";
1180
+ return "unknown";
1181
+ }
1182
+ function proofHintsFor(args) {
1183
+ const hints = ["Keep service connection evidence report-only until a governance path promotes it."];
1184
+ if (args.evidenceRefCount === 0 || args.status === "missing_evidence") {
1185
+ hints.push("Attach at least one explicit evidence ref or path before relying on this connection.");
1186
+ }
1187
+ if (!args.lifecycleState || args.lifecycleState === "hint" || args.lifecycleState === "candidate") {
1188
+ hints.push("Corroborate hint/candidate service connections with deterministic code, config, receipt, or explicit user-action evidence.");
1189
+ }
1190
+ if (args.freshness === "stale" || args.status === "stale") {
1191
+ hints.push("Refresh stale service connection evidence before using it for workflow decisions.");
1192
+ }
1193
+ if (args.relationshipType === "blocked_by") {
1194
+ hints.push("Capture the blocking condition and owner before planning dependent code-work.");
1195
+ }
1196
+ hints.push("Do not include raw credentials, tokens, passwords, or provider payloads in evidence labels or paths.");
1197
+ return uniqueStrings(hints).sort();
1198
+ }
1199
+ function redactText(value, blockSecretInput) {
1200
+ if (blockSecretInput) {
1201
+ return {
1202
+ text: `redacted:${hashText(value).slice(0, 12)}`,
1203
+ redaction: {
1204
+ status: "secret_input_blocked",
1205
+ containsSecret: false,
1206
+ noSecretValuesIncluded: true,
1207
+ redactionApplied: true,
1208
+ reasonCodes: ["redaction:explicit_secret_input_blocked"]
1209
+ }
1210
+ };
1211
+ }
1212
+ const redacted = value.replace(SECRET_VALUE_PATTERN, (_match, key) => `${key}=<redacted>`).replace(BEARER_PATTERN, "Bearer <redacted>");
1213
+ if (redacted !== value) {
1214
+ return {
1215
+ text: redacted,
1216
+ redaction: {
1217
+ status: "redacted",
1218
+ containsSecret: false,
1219
+ noSecretValuesIncluded: true,
1220
+ redactionApplied: true,
1221
+ reasonCodes: ["redaction:secret_value_pattern"]
1222
+ }
1223
+ };
1224
+ }
1225
+ return { text: value, redaction: cleanRedaction() };
1226
+ }
1227
+ function mergeRedactions(items) {
1228
+ const redactions = items.map((item) => ("redaction" in item) ? item.redaction : item);
1229
+ if (redactions.some((redaction) => redaction.status === "secret_input_blocked")) {
1230
+ return {
1231
+ status: "secret_input_blocked",
1232
+ containsSecret: false,
1233
+ noSecretValuesIncluded: true,
1234
+ redactionApplied: true,
1235
+ reasonCodes: uniqueStrings(redactions.flatMap((redaction) => redaction.reasonCodes)).sort()
1236
+ };
1237
+ }
1238
+ if (redactions.some((redaction) => redaction.status === "redacted")) {
1239
+ return {
1240
+ status: "redacted",
1241
+ containsSecret: false,
1242
+ noSecretValuesIncluded: true,
1243
+ redactionApplied: true,
1244
+ reasonCodes: uniqueStrings(redactions.flatMap((redaction) => redaction.reasonCodes)).sort()
1245
+ };
1246
+ }
1247
+ return cleanRedaction();
1248
+ }
1249
+ function cleanRedaction() {
1250
+ return {
1251
+ status: "clean",
1252
+ containsSecret: false,
1253
+ noSecretValuesIncluded: true,
1254
+ redactionApplied: false,
1255
+ reasonCodes: ["redaction:no_secret_values_detected"]
1256
+ };
1257
+ }
1258
+ function defaultBundleId(args, summaries, refs) {
1259
+ const seed = [args.source ?? DEFAULT_SOURCE, ...summaries.map((summary) => summary.serviceConnectionId), ...refs.map((ref) => ref.evidenceId)].join("|");
1260
+ return `service-connection-evidence:${hashText(seed || "empty").slice(0, 16)}`;
1261
+ }
1262
+ function isNormalizedServiceConnection(input) {
1263
+ return input.schemaVersion === 1;
1264
+ }
1265
+ function compareEvidenceRefs(a, b) {
1266
+ return a.serviceConnectionId.localeCompare(b.serviceConnectionId) || a.evidenceRef.localeCompare(b.evidenceRef) || a.evidenceId.localeCompare(b.evidenceId);
1267
+ }
1268
+ function cleanString(value) {
1269
+ if (typeof value !== "string")
1270
+ return;
1271
+ const text = value.trim();
1272
+ return text.length > 0 ? text : undefined;
1273
+ }
1274
+ function firstDefined(items) {
1275
+ return items.find((item) => item !== undefined);
1276
+ }
1277
+ function positiveInteger(value) {
1278
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
1279
+ }
1280
+ function estimateTextTokens(text) {
1281
+ return Math.max(1, Math.ceil(text.length / 4));
1282
+ }
1283
+ function uniqueStrings(values) {
1284
+ return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
1285
+ }
1286
+ function hashText(value) {
1287
+ let hash = 2166136261;
1288
+ for (let index = 0;index < value.length; index += 1) {
1289
+ hash ^= value.charCodeAt(index);
1290
+ hash = Math.imul(hash, 16777619);
1291
+ }
1292
+ return (hash >>> 0).toString(16).padStart(8, "0");
1293
+ }
1294
+
1295
+ // ../core/src/serviceIntelligence/builders.ts
1296
+ var DEFAULT_GENERATED_AT2 = Date.parse("2026-05-18T00:00:00.000Z");
1297
+ var DEFAULT_SOURCE2 = "explicit";
1298
+ var DEFAULT_BACKEND_REFS = [
1299
+ "packages/core/src/contextLens/serviceConnections.ts",
1300
+ "packages/core/src/serviceConnectionEvidence/builders.ts",
1301
+ "packages/core/src/serviceIntelligence/builders.ts",
1302
+ "packages/mcp-server/src/codeContextTools.ts"
1303
+ ];
1304
+ var DEFAULT_UI_REFS = [
1305
+ "apps/web/src/components/code-intelligence/ServiceImpactCard.tsx",
1306
+ "apps/web/src/components/code-workroom/ServiceIntelligencePanel.tsx",
1307
+ "apps/web/src/components/code-workroom/CodeWorkroomView.tsx"
1308
+ ];
1309
+ var DEFAULT_TEST_REFS = ["convex-tests/codeLayer/serviceIntelligence.test.ts"];
1310
+ var DEFAULT_DOC_REFS = ["docs/planning/MLP1.md", "docs/execution/CODE.md"];
1311
+ function buildServiceIntelligenceReportV1(args = {}) {
1312
+ const generatedAt = args.generatedAt ?? DEFAULT_GENERATED_AT2;
1313
+ const source = args.source ?? DEFAULT_SOURCE2;
1314
+ const connections = normalizeServiceConnections(args, generatedAt);
1315
+ const evidenceBundle = args.evidenceBundle ?? buildServiceConnectionEvidenceBundleV1({
1316
+ ...args.evidence ?? {},
1317
+ generatedAt,
1318
+ source,
1319
+ serviceConnections: connections,
1320
+ missBuckets: uniqueStrings2([
1321
+ ...(args.evidence ?? {}).missBuckets ?? [],
1322
+ ...staticCandidateMissBuckets(args.staticCandidateGraph)
1323
+ ]),
1324
+ candidateKindCounts: staticCandidateKindCounts(args.staticCandidateGraph),
1325
+ evidenceTierCounts: staticEvidenceTierCounts(args.staticCandidateGraph)
1326
+ });
1327
+ const serviceMap = buildServiceMap(connections, evidenceBundle);
1328
+ const summary = buildSummary(serviceMap.nodes, serviceMap.edges, evidenceBundle);
1329
+ const status = summary.status;
1330
+ const nextImplementableSlice = buildNextSlice(args);
1331
+ const reportId = args.reportId ?? defaultReportId({
1332
+ source,
1333
+ goal: args.goal,
1334
+ connectionIds: serviceMap.edges.map((edge) => edge.id)
1335
+ });
1336
+ return {
1337
+ schemaVersion: SERVICE_INTELLIGENCE_REPORT_VERSION,
1338
+ reportKind: SERVICE_INTELLIGENCE_REPORT_KIND,
1339
+ reportId,
1340
+ generatedAt,
1341
+ source,
1342
+ goal: cleanString2(args.goal),
1343
+ status,
1344
+ summary,
1345
+ serviceMap,
1346
+ evidenceBundle,
1347
+ connectedSurfaceCoverage: args.connectedSurfaceCoverage ? connectedSurfaceSummary(args.connectedSurfaceCoverage) : undefined,
1348
+ absenceProof: buildAbsenceProof(evidenceBundle),
1349
+ productSummary: buildProductSummary({
1350
+ status,
1351
+ summary,
1352
+ nextImplementableSlice,
1353
+ uiRefs: args.uiRefs
1354
+ }),
1355
+ guardrails: serviceIntelligenceGuardrails(evidenceBundle.guardrails),
1356
+ noLive: serviceIntelligenceNoLive(evidenceBundle.noLive),
1357
+ reportOnly: true,
1358
+ advisoryOnly: true,
1359
+ noAuthority: true
1360
+ };
1361
+ }
1362
+ function normalizeServiceConnections(args, generatedAt) {
1363
+ const inputs = [
1364
+ ...args.serviceConnections ?? [],
1365
+ ...normalizeStaticCandidateGraph(args.staticCandidateGraph)
1366
+ ];
1367
+ return inputs.map((input) => {
1368
+ try {
1369
+ return normalizeServiceConnectionV1(input, { generatedAtMs: generatedAt });
1370
+ } catch {
1371
+ return null;
1372
+ }
1373
+ }).filter((connection) => connection !== null).sort((left, right) => summarizeServiceConnection(left).id.localeCompare(summarizeServiceConnection(right).id));
1374
+ }
1375
+ function normalizeStaticCandidateGraph(graph) {
1376
+ const edges = Array.isArray(graph?.edges) ? graph.edges : [];
1377
+ const nodeById = new Map((graph?.nodes ?? []).filter((node) => typeof node.nodeId === "string").map((node) => [node.nodeId, node]));
1378
+ return edges.filter((edge) => edge.sourceNodeId && edge.targetNodeId).map((edge) => {
1379
+ const sourceNode = nodeById.get(edge.sourceNodeId);
1380
+ const targetNode = nodeById.get(edge.targetNodeId);
1381
+ const relationshipType = relationshipTypeForDirection(edge.direction);
1382
+ const surfaceKind = surfaceKindForCandidate(edge.kind);
1383
+ return {
1384
+ id: edge.candidateId ?? edge.edgeId,
1385
+ relationshipType,
1386
+ direction: "directed",
1387
+ source: {
1388
+ kind: "service",
1389
+ id: edge.sourceNodeId,
1390
+ label: sourceNode?.label ?? edge.sourceNodeId,
1391
+ serviceId: edge.sourceNodeId
1392
+ },
1393
+ target: {
1394
+ kind: "resource",
1395
+ id: edge.targetNodeId,
1396
+ label: targetNode?.label ?? edge.targetNodeId,
1397
+ resourceId: edge.targetNodeId,
1398
+ resourceKind: surfaceKind
1399
+ },
1400
+ targetSurfaceKind: surfaceKind,
1401
+ lifecycleState: edge.evidenceTier === "path_only" ? "hint" : "candidate",
1402
+ state: edge.evidenceTier === "path_only" ? "usable_hint" : "review_candidate",
1403
+ confidence: edge.confidence,
1404
+ candidateKind: edge.kind,
1405
+ directionHint: edge.direction,
1406
+ edgeLabel: edge.edgeLabel,
1407
+ staticEvidenceTier: edge.evidenceTier,
1408
+ missBuckets: edge.missBuckets,
1409
+ reportOnly: true,
1410
+ advisoryOnly: true,
1411
+ noAuthority: true,
1412
+ evidenceSources: [{
1413
+ kind: "code_static",
1414
+ label: edge.edgeLabel ?? "Static service connection candidate",
1415
+ evidenceTier: "deterministic_code_edge",
1416
+ confidence: edge.confidence
1417
+ }],
1418
+ summary: edge.edgeLabel
1419
+ };
1420
+ });
1421
+ }
1422
+ function relationshipTypeForDirection(direction) {
1423
+ switch (direction) {
1424
+ case "publishes_queue":
1425
+ return "publishes_to";
1426
+ case "consumes_queue":
1427
+ return "subscribes_to";
1428
+ case "writes_datastore":
1429
+ case "writes_cache":
1430
+ return "writes_to";
1431
+ case "reads_datastore":
1432
+ case "reads_cache":
1433
+ return "reads_from";
1434
+ case "emits_telemetry":
1435
+ return "observes";
1436
+ case "reads_secret":
1437
+ case "outbound_provider":
1438
+ case "inbound_api":
1439
+ case "owns_runtime":
1440
+ case "proves_with_test":
1441
+ case "documents":
1442
+ default:
1443
+ return "blocked_by";
1444
+ }
1445
+ }
1446
+ function surfaceKindForCandidate(kind) {
1447
+ switch (kind) {
1448
+ case "http_api":
1449
+ return "api";
1450
+ case "database":
1451
+ return "datastore";
1452
+ case "object_store":
1453
+ return "object_store";
1454
+ case "queue_topic":
1455
+ return "queue";
1456
+ case "cache":
1457
+ return "cache";
1458
+ case "secret_config":
1459
+ return "secret";
1460
+ case "observability_sink":
1461
+ return "observability_sink";
1462
+ case "runtime_owner":
1463
+ return "runtime";
1464
+ case "auth_provider":
1465
+ case "provider_client":
1466
+ return "external_service";
1467
+ default:
1468
+ return "unknown";
1469
+ }
1470
+ }
1471
+ function staticCandidateMissBuckets(graph) {
1472
+ return uniqueStrings2([
1473
+ ...(graph?.edges ?? []).flatMap((edge) => edge.missBuckets ?? []),
1474
+ ...stringValues((graph?.missBuckets ?? []).map((bucket) => bucket.bucket))
1475
+ ]);
1476
+ }
1477
+ function staticCandidateKindCounts(graph) {
1478
+ const counts = countStrings(stringValues((graph?.edges ?? []).map((edge) => edge.kind)));
1479
+ return Object.keys(counts).length > 0 ? counts : undefined;
1480
+ }
1481
+ function staticEvidenceTierCounts(graph) {
1482
+ const counts = countStrings(stringValues((graph?.edges ?? []).map((edge) => edge.evidenceTier)));
1483
+ return Object.keys(counts).length > 0 ? counts : undefined;
1484
+ }
1485
+ function buildServiceMap(connections, evidenceBundle) {
1486
+ const nodeMap = new Map;
1487
+ const evidenceByConnectionId = new Map(evidenceBundle.connectionSummaries.map((summary) => [summary.serviceConnectionId, summary]));
1488
+ const edges = connections.map((connection) => {
1489
+ const summary = summarizeServiceConnection(connection);
1490
+ upsertNode(nodeMap, connection.source, summary.id);
1491
+ upsertNode(nodeMap, connection.target, summary.id);
1492
+ const evidenceSummary = evidenceByConnectionId.get(summary.id);
1493
+ return {
1494
+ id: summary.id,
1495
+ sourceNodeId: nodeId(connection.source),
1496
+ targetNodeId: nodeId(connection.target),
1497
+ title: summary.title,
1498
+ relationshipType: summary.relationshipType,
1499
+ lifecycleState: summary.lifecycleState,
1500
+ status: evidenceSummary?.status ?? statusForLifecycle(summary.lifecycleState),
1501
+ confidence: summary.confidence,
1502
+ evidenceRefCount: evidenceSummary?.evidenceRefs.length ?? 0,
1503
+ evidencePaths: evidenceSummary?.evidencePaths ?? [],
1504
+ summary,
1505
+ candidateKind: connection.candidateKind,
1506
+ directionHint: connection.directionHint,
1507
+ edgeLabel: connection.edgeLabel,
1508
+ evidenceTier: connection.staticEvidenceTier,
1509
+ missBuckets: connection.missBuckets,
1510
+ advisoryOnly: connection.advisoryOnly,
1511
+ noAuthority: connection.noAuthority
1512
+ };
1513
+ });
1514
+ return {
1515
+ schemaVersion: 1,
1516
+ nodes: Array.from(nodeMap.values()).sort((left, right) => left.id.localeCompare(right.id)),
1517
+ edges
1518
+ };
1519
+ }
1520
+ function upsertNode(nodeMap, endpoint, serviceConnectionId) {
1521
+ const id = nodeId(endpoint);
1522
+ const existing = nodeMap.get(id);
1523
+ if (existing) {
1524
+ existing.serviceConnectionIds = uniqueStrings2([...existing.serviceConnectionIds, serviceConnectionId]).sort();
1525
+ return;
1526
+ }
1527
+ nodeMap.set(id, {
1528
+ id,
1529
+ endpointKind: endpoint.kind,
1530
+ label: endpoint.label ?? endpoint.resourceId ?? endpoint.serviceId ?? endpoint.id,
1531
+ surfaceKind: endpoint.resourceKind ?? "unknown",
1532
+ logicalServiceId: endpoint.metadata?.identity?.canonicalLogicalServiceId ?? endpoint.serviceId,
1533
+ serviceConnectionIds: [serviceConnectionId]
1534
+ });
1535
+ }
1536
+ function buildSummary(nodes, edges, evidenceBundle) {
1537
+ const lifecycleStates = edges.map((edge) => edge.lifecycleState);
1538
+ const surfaceKinds = uniqueStrings2(nodes.map((node) => node.surfaceKind)).sort();
1539
+ const missingEvidenceCount = evidenceBundle.statusSummary.missingEvidenceCount;
1540
+ const needsReviewCount = evidenceBundle.statusSummary.needsReviewCount;
1541
+ const staleConnectionCount = evidenceBundle.statusSummary.staleCount + lifecycleStates.filter((state) => state === "stale").length;
1542
+ const status = serviceIntelligenceStatus({
1543
+ connectionCount: edges.length,
1544
+ missingEvidenceCount,
1545
+ needsReviewCount,
1546
+ staleConnectionCount
1547
+ });
1548
+ return {
1549
+ status,
1550
+ serviceCount: nodes.filter((node) => node.endpointKind === "service").length,
1551
+ resourceCount: nodes.filter((node) => node.endpointKind === "resource").length,
1552
+ connectionCount: edges.length,
1553
+ verifiedConnectionCount: lifecycleStates.filter((state) => state === "verified").length,
1554
+ candidateConnectionCount: lifecycleStates.filter((state) => state === "candidate" || state === "hint").length,
1555
+ staleConnectionCount,
1556
+ missingEvidenceCount,
1557
+ needsReviewCount,
1558
+ relationshipTypes: uniqueStrings2(edges.map((edge) => edge.relationshipType)).sort(),
1559
+ surfaceKinds,
1560
+ pathOnlyConnectionCount: edges.filter((edge) => edge.evidenceTier === "path_only").length,
1561
+ importBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "import_backed").length,
1562
+ callBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "call_backed").length,
1563
+ envBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "env_backed").length,
1564
+ schemaBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "schema_backed").length,
1565
+ testBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "test_backed").length,
1566
+ docBackedConnectionCount: edges.filter((edge) => edge.evidenceTier === "doc_backed").length,
1567
+ missBucketCounts: countStrings(edges.flatMap((edge) => edge.missBuckets ?? [])),
1568
+ candidateKindCounts: countStrings(stringValues(edges.map((edge) => edge.candidateKind))),
1569
+ directionCounts: countStrings(stringValues(edges.map((edge) => edge.directionHint)))
1570
+ };
1571
+ }
1572
+ function serviceIntelligenceStatus(args) {
1573
+ if (args.connectionCount === 0)
1574
+ return "empty";
1575
+ if (args.staleConnectionCount > 0)
1576
+ return "has_stale_edges";
1577
+ if (args.missingEvidenceCount > 0 || args.needsReviewCount > 0)
1578
+ return "needs_evidence";
1579
+ return "advisory_map";
1580
+ }
1581
+ function buildAbsenceProof(evidenceBundle) {
1582
+ const guardrailEvidence = [
1583
+ "guardrails.reportOnly=true",
1584
+ "guardrails.noAuthority=true",
1585
+ "guardrails.noProviderCalls=true",
1586
+ "guardrails.noNetworkCalls=true",
1587
+ "guardrails.noConvexWrites=true",
1588
+ "guardrails.contextDoesNotMutateCanon=true",
1589
+ `noLive.providers=${String(evidenceBundle.noLive.providers)}`,
1590
+ `noLive.network=${String(evidenceBundle.noLive.network)}`,
1591
+ `noLive.convexWrites=${String(evidenceBundle.noLive.convexWrites)}`,
1592
+ `noLive.filesystemSourceScan=${String(evidenceBundle.noLive.filesystemSourceScan)}`
1593
+ ];
1594
+ return [
1595
+ {
1596
+ claim: "This ServiceIntelligenceReportV1 did not ingest a live Runtime Surface Map.",
1597
+ status: "proven_for_report",
1598
+ scope: "this_report",
1599
+ evidence: guardrailEvidence,
1600
+ reasonCodes: ["absence:no_live_runtime_surface_ingest", "guardrail:no_provider_network_or_source_scan"]
1601
+ },
1602
+ {
1603
+ claim: "This report did not persist service connections, promote relationship memory, or mutate canon.",
1604
+ status: "proven_for_report",
1605
+ scope: "this_report",
1606
+ evidence: [
1607
+ "guardrails.noConvexWrites=true",
1608
+ "guardrails.contextDoesNotMutateCanon=true",
1609
+ "guardrails.workingSetGrantsAuthority=false",
1610
+ "noLive.graphWrites=false",
1611
+ "noLive.memoryPromotion=false"
1612
+ ],
1613
+ reasonCodes: ["absence:no_canon_mutation", "absence:no_memory_promotion"]
1614
+ },
1615
+ {
1616
+ claim: "Repo-wide service topology completeness is not claimed by this report.",
1617
+ status: "not_claimed",
1618
+ scope: "repo_wide",
1619
+ evidence: ["Report scope is limited to explicitly supplied ServiceConnectionV1/evidence inputs."],
1620
+ reasonCodes: ["absence:repo_wide_completeness_not_claimed"]
1621
+ }
1622
+ ];
1623
+ }
1624
+ function buildProductSummary(args) {
1625
+ const { summary } = args;
1626
+ return {
1627
+ backendStatus: summary.connectionCount === 0 ? "Backend status: ServiceConnectionV1/report contracts are available, but no service connections were supplied to this report." : `Backend status: ${summary.connectionCount} ServiceConnectionV1 edge(s) mapped across ${summary.serviceCount} service node(s) and ${summary.resourceCount} resource node(s).`,
1628
+ frontendStatus: args.uiRefs && args.uiRefs.length > 0 ? `Frontend status: UI refs supplied for read-only rendering (${args.uiRefs.join(", ")}).` : "Frontend status: the report is UI-ready, but no consuming UI ref was supplied by the caller.",
1629
+ absenceStatus: "Absence proof is scoped to this report: no live runtime-map ingest, provider call, network call, graph write, memory promotion, or canon mutation occurred.",
1630
+ guardrailStatus: "Report-only, advisory-only, no-authority. Service evidence can explain context but cannot execute, promote, write, or call providers.",
1631
+ actionabilityStatus: `${args.nextImplementableSlice.files.length} implementable file path(s) supplied for the next product slice.`,
1632
+ nextImplementableSlice: args.nextImplementableSlice
1633
+ };
1634
+ }
1635
+ function buildNextSlice(args) {
1636
+ const backendFiles = uniqueStrings2([...args.backendRefs ?? [], ...DEFAULT_BACKEND_REFS]);
1637
+ const uiFiles = uniqueStrings2([...args.uiRefs ?? [], ...DEFAULT_UI_REFS]);
1638
+ const testFiles = uniqueStrings2([...args.testRefs ?? [], ...DEFAULT_TEST_REFS]);
1639
+ const docsFiles = uniqueStrings2([...args.docsRefs ?? [], ...DEFAULT_DOC_REFS]);
1640
+ return {
1641
+ title: "Service Intelligence V1 read-only report slice",
1642
+ summary: "Wire ServiceConnectionV1 evidence into a first-class report, MCP payload, and read-only UI panel before any live runtime-map or connector-write work.",
1643
+ files: uniqueStrings2([...backendFiles, ...uiFiles, ...testFiles, ...docsFiles]),
1644
+ backendFiles,
1645
+ uiFiles,
1646
+ testFiles,
1647
+ docsFiles,
1648
+ steps: [
1649
+ "Build or pass ServiceConnectionV1 inputs with explicit evidence refs.",
1650
+ "Generate ServiceIntelligenceReportV1 and inspect its absenceProof before using it in agent context.",
1651
+ "Render the report as read-only service/resource topology and evidence status in Code Workroom.",
1652
+ "Add promotion or live-runtime-map work only as a later gated MLP2 slice."
1653
+ ],
1654
+ guardrails: [
1655
+ "Do not treat ServiceConnectionV1 as canon without Truth Gate or another explicit governance path.",
1656
+ "Do not add provider calls, network calls, source scans, connector writes, memory promotion, or graph writes to this report builder.",
1657
+ "Do not expose merge/comment/approval/write controls from the UI panel."
1658
+ ]
1659
+ };
1660
+ }
1661
+ function serviceIntelligenceGuardrails(guardrails) {
1662
+ return {
1663
+ ...guardrails,
1664
+ noGraphWrites: true,
1665
+ noMemoryPromotion: true,
1666
+ noConnectorWrites: true
1667
+ };
1668
+ }
1669
+ function serviceIntelligenceNoLive(noLive) {
1670
+ return {
1671
+ ...noLive,
1672
+ graphWrites: false,
1673
+ memoryPromotion: false,
1674
+ connectorWrites: false
1675
+ };
1676
+ }
1677
+ function connectedSurfaceSummary(report) {
1678
+ return {
1679
+ reportKind: report.reportKind,
1680
+ version: report.version,
1681
+ readiness: report.readiness,
1682
+ policy: report.policy,
1683
+ metrics: report.metrics,
1684
+ noLiveInvariants: report.noLiveInvariants,
1685
+ productSurfaces: report.productSurfaces
1686
+ };
1687
+ }
1688
+ function statusForLifecycle(lifecycleState) {
1689
+ if (lifecycleState === "verified")
1690
+ return "present";
1691
+ if (lifecycleState === "stale")
1692
+ return "stale";
1693
+ if (lifecycleState === "rejected" || lifecycleState === "retracted")
1694
+ return "rejected";
1695
+ return "needs_review";
1696
+ }
1697
+ function nodeId(endpoint) {
1698
+ return `${endpoint.kind}:${endpoint.id}`;
1699
+ }
1700
+ function defaultReportId(args) {
1701
+ const seed = [args.source, args.goal ?? "", ...args.connectionIds].join("|");
1702
+ return `service-intelligence:${hashText2(seed || "empty").slice(0, 16)}`;
1703
+ }
1704
+ function cleanString2(value) {
1705
+ if (typeof value !== "string")
1706
+ return;
1707
+ const text = value.trim();
1708
+ return text.length > 0 ? text : undefined;
1709
+ }
1710
+ function uniqueStrings2(values) {
1711
+ return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
1712
+ }
1713
+ function stringValues(values) {
1714
+ return values.filter((value) => typeof value === "string" && value.trim().length > 0);
1715
+ }
1716
+ function countStrings(values) {
1717
+ return values.reduce((counts, value) => {
1718
+ counts[value] = (counts[value] ?? 0) + 1;
1719
+ return counts;
1720
+ }, {});
1721
+ }
1722
+ function hashText2(value) {
1723
+ let hash = 2166136261;
1724
+ for (let index = 0;index < value.length; index += 1) {
1725
+ hash ^= value.charCodeAt(index);
1726
+ hash = Math.imul(hash, 16777619);
1727
+ }
1728
+ return (hash >>> 0).toString(16).padStart(8, "0");
1729
+ }
1730
+ export {
1731
+ buildServiceIntelligenceReportV1,
1732
+ SERVICE_INTELLIGENCE_REPORT_VERSION,
1733
+ SERVICE_INTELLIGENCE_REPORT_KIND
1734
+ };