@duckcodeailabs/dql-agent 1.14.3-rc.1 → 1.14.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-run-engine.d.ts +177 -3
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +2067 -57
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/agent-run-gates.d.ts.map +1 -1
- package/dist/agent-run-gates.js +51 -3
- package/dist/agent-run-gates.js.map +1 -1
- package/dist/agent-run-store.d.ts.map +1 -1
- package/dist/agent-run-store.js +544 -9
- package/dist/agent-run-store.js.map +1 -1
- package/dist/agentic/analyst-loop.d.ts +2 -0
- package/dist/agentic/analyst-loop.d.ts.map +1 -1
- package/dist/agentic/analyst-loop.js +41 -4
- package/dist/agentic/analyst-loop.js.map +1 -1
- package/dist/agentic/ledger-tools.d.ts.map +1 -1
- package/dist/agentic/ledger-tools.js +8 -1
- package/dist/agentic/ledger-tools.js.map +1 -1
- package/dist/agentic/orchestrator-policy.d.ts +3 -1
- package/dist/agentic/orchestrator-policy.d.ts.map +1 -1
- package/dist/agentic/orchestrator-policy.js +1 -1
- package/dist/agentic/orchestrator-policy.js.map +1 -1
- package/dist/agentic/tool-loop.d.ts +13 -1
- package/dist/agentic/tool-loop.d.ts.map +1 -1
- package/dist/agentic/tool-loop.js +441 -32
- package/dist/agentic/tool-loop.js.map +1 -1
- package/dist/analytical-frame.d.ts +19 -0
- package/dist/analytical-frame.d.ts.map +1 -1
- package/dist/analytical-frame.js +170 -12
- package/dist/analytical-frame.js.map +1 -1
- package/dist/analytical-orchestration.d.ts +949 -10
- package/dist/analytical-orchestration.d.ts.map +1 -1
- package/dist/analytical-orchestration.js +499 -56
- package/dist/analytical-orchestration.js.map +1 -1
- package/dist/answer-loop.d.ts +166 -2
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +654 -18
- package/dist/answer-loop.js.map +1 -1
- package/dist/answer-shape.d.ts +27 -0
- package/dist/answer-shape.d.ts.map +1 -1
- package/dist/answer-shape.js +55 -0
- package/dist/answer-shape.js.map +1 -1
- package/dist/ask-observability/instrumentation.d.ts.map +1 -1
- package/dist/ask-observability/instrumentation.js +52 -1
- package/dist/ask-observability/instrumentation.js.map +1 -1
- package/dist/ask-observability/portable.js +562 -0
- package/dist/ask-observability/portable.js.map +1 -1
- package/dist/ask-observability/types.d.ts +26 -3
- package/dist/ask-observability/types.d.ts.map +1 -1
- package/dist/ask-observability/types.js.map +1 -1
- package/dist/ask-runtime/ask-agent-runtime-v2.d.ts +850 -0
- package/dist/ask-runtime/ask-agent-runtime-v2.d.ts.map +1 -0
- package/dist/ask-runtime/ask-agent-runtime-v2.js +1716 -0
- package/dist/ask-runtime/ask-agent-runtime-v2.js.map +1 -0
- package/dist/ask-runtime/ask-analyst-runtime.d.ts +95 -0
- package/dist/ask-runtime/ask-analyst-runtime.d.ts.map +1 -0
- package/dist/ask-runtime/ask-analyst-runtime.js +8280 -0
- package/dist/ask-runtime/ask-analyst-runtime.js.map +1 -0
- package/dist/conversation/session-store.d.ts +31 -0
- package/dist/conversation/session-store.d.ts.map +1 -1
- package/dist/conversation/session-store.js +38 -0
- package/dist/conversation/session-store.js.map +1 -1
- package/dist/conversation/snapshot.d.ts +4 -1
- package/dist/conversation/snapshot.d.ts.map +1 -1
- package/dist/conversation/snapshot.js +85 -3
- package/dist/conversation/snapshot.js.map +1 -1
- package/dist/conversation/turn-trust.d.ts.map +1 -1
- package/dist/conversation/turn-trust.js +2 -0
- package/dist/conversation/turn-trust.js.map +1 -1
- package/dist/embeddings/provider.d.ts +1 -0
- package/dist/embeddings/provider.d.ts.map +1 -1
- package/dist/embeddings/provider.js +18 -0
- package/dist/embeddings/provider.js.map +1 -1
- package/dist/generated-analytical-proposal.js +37 -2
- package/dist/generated-analytical-proposal.js.map +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts +77 -1
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js +1 -1
- package/dist/intent-controller.js.map +1 -1
- package/dist/kg/build.js +32 -2
- package/dist/kg/build.js.map +1 -1
- package/dist/meaning-resolution.d.ts +129 -0
- package/dist/meaning-resolution.d.ts.map +1 -1
- package/dist/meaning-resolution.js +108 -15
- package/dist/meaning-resolution.js.map +1 -1
- package/dist/metadata/analysis-planner.js +57 -6
- package/dist/metadata/analysis-planner.js.map +1 -1
- package/dist/metadata/catalog.d.ts +14 -0
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +28 -1
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/meaning-evidence.d.ts +19 -0
- package/dist/metadata/meaning-evidence.d.ts.map +1 -1
- package/dist/metadata/meaning-evidence.js +113 -6
- package/dist/metadata/meaning-evidence.js.map +1 -1
- package/dist/provider-egress.d.ts +4 -1
- package/dist/provider-egress.d.ts.map +1 -1
- package/dist/provider-egress.js +50 -5
- package/dist/provider-egress.js.map +1 -1
- package/dist/providers/claude.d.ts +2 -2
- package/dist/providers/claude.d.ts.map +1 -1
- package/dist/providers/claude.js +191 -28
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +191 -21
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/types.d.ts +79 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/requirement-clauses.d.ts +23 -0
- package/dist/requirement-clauses.d.ts.map +1 -0
- package/dist/requirement-clauses.js +70 -0
- package/dist/requirement-clauses.js.map +1 -0
- package/dist/resolved-analytical-plan.d.ts.map +1 -1
- package/dist/resolved-analytical-plan.js +201 -15
- package/dist/resolved-analytical-plan.js.map +1 -1
- package/dist/router.d.ts +109 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +753 -76
- package/dist/router.js.map +1 -1
- package/package.json +4 -4
|
@@ -312,6 +312,10 @@ function redactRunReceipt(value, trace, profile, salt) {
|
|
|
312
312
|
?? (source.cascade && typeof source.cascade === 'object'
|
|
313
313
|
? source.cascade.terminalGap
|
|
314
314
|
: undefined), profile, salt);
|
|
315
|
+
const runtimeReceiptV5 = portableRuntimeReceiptV5(record.diagnosticReceiptV5, profile, salt);
|
|
316
|
+
const runtimeReceiptV6 = portableRuntimeReceiptV6(record.diagnosticReceiptV6, profile, salt);
|
|
317
|
+
const runtimeReceiptV7 = portableRuntimeReceiptV7(record.diagnosticReceiptV7, profile, salt);
|
|
318
|
+
const runtimeReceiptV8 = portableRuntimeReceiptV8(record.diagnosticReceiptV8, profile, salt);
|
|
315
319
|
return {
|
|
316
320
|
version: 1,
|
|
317
321
|
traceReference: {
|
|
@@ -326,6 +330,10 @@ function redactRunReceipt(value, trace, profile, salt) {
|
|
|
326
330
|
planFrozen: source.planFrozen === true,
|
|
327
331
|
finalStopReason: typeof source.finalStopReason === 'string' ? source.finalStopReason : undefined,
|
|
328
332
|
...(terminalGap ? { terminalGap } : {}),
|
|
333
|
+
...(runtimeReceiptV5 ? { runtimeReceiptV5 } : {}),
|
|
334
|
+
...(runtimeReceiptV6 ? { runtimeReceiptV6 } : {}),
|
|
335
|
+
...(runtimeReceiptV7 ? { runtimeReceiptV7 } : {}),
|
|
336
|
+
...(runtimeReceiptV8 ? { runtimeReceiptV8 } : {}),
|
|
329
337
|
telemetry: sanitizeTelemetry(record.telemetry),
|
|
330
338
|
fingerprints: {
|
|
331
339
|
plan: map(findFingerprint(record, 'planFingerprint'), 'plan'),
|
|
@@ -334,6 +342,560 @@ function redactRunReceipt(value, trace, profile, salt) {
|
|
|
334
342
|
},
|
|
335
343
|
};
|
|
336
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* V8 is the bounded Ask-tool-runtime story. Strict exports retain its
|
|
347
|
+
* allowlisted enums, counts, timings, and pseudonymous identifiers only. In
|
|
348
|
+
* particular, this deliberately does not make generated DQL/SQL exportable:
|
|
349
|
+
* the local trace contract never retains query text, result rows, prompts,
|
|
350
|
+
* provider responses, credentials, or chain-of-thought.
|
|
351
|
+
*/
|
|
352
|
+
function portableRuntimeReceiptV8(value, profile, salt) {
|
|
353
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
354
|
+
return undefined;
|
|
355
|
+
const receipt = value;
|
|
356
|
+
if (receipt.version !== 8)
|
|
357
|
+
return undefined;
|
|
358
|
+
const record = (candidate) => candidate && typeof candidate === 'object' && !Array.isArray(candidate) ? candidate : undefined;
|
|
359
|
+
const count = (candidate, maximum = 10_000) => typeof candidate === 'number' && Number.isFinite(candidate)
|
|
360
|
+
? Math.max(0, Math.min(maximum, Math.floor(candidate)))
|
|
361
|
+
: undefined;
|
|
362
|
+
const identifier = (candidate, kind) => typeof candidate === 'string' && candidate.length > 0 && candidate.length <= 256
|
|
363
|
+
? (profile === 'strict' ? pseudo(candidate, salt, kind) : candidate)
|
|
364
|
+
: undefined;
|
|
365
|
+
const safeCode = (candidate) => typeof candidate === 'string' && /^[A-Z][A-Z0-9_:-]{1,95}$/.test(candidate)
|
|
366
|
+
? candidate
|
|
367
|
+
: 'UNRECOGNIZED_REASON';
|
|
368
|
+
const modes = new Set(['legacy_v1', 'shadow_v2', 'authoritative_v2']);
|
|
369
|
+
const turns = new Set(['analytics', 'definition', 'business_context', 'prior_result', 'general', 'clarification_response', 'research']);
|
|
370
|
+
const sources = new Set(['certified', 'semantic', 'governed_relational', 'dbt_manifest', 'runtime_schema', 'vector', 'conversation', 'business']);
|
|
371
|
+
const sourceStates = new Set(['available', 'empty', 'stale', 'unavailable', 'errored', 'skipped']);
|
|
372
|
+
const tools = new Set(['inspect_ask_context', 'inspect_conversation_result', 'inspect_business_context', 'inspect_certified_candidates', 'run_certified', 'inspect_semantic_candidates', 'compile_and_run_semantic', 'inspect_relational_context', 'compile_and_run_dql', 'validate_and_run_sql', 'search_values', 'request_clarification', 'finish_answer']);
|
|
373
|
+
const outcomes = new Set(['eligible', 'executed', 'ineligible', 'unavailable', 'ambiguous', 'needs_input', 'denied', 'error']);
|
|
374
|
+
const tiers = new Set(['certified', 'semantic', 'governed_relational', 'exploratory_sql']);
|
|
375
|
+
const origins = new Set(['retrieval', 'agent_control', 'tool', 'validation', 'freeze', 'execution', 'provider', 'narration']);
|
|
376
|
+
const terminalKinds = new Set(['finish_answer', 'clarification', 'gap', 'provider_failure', 'execution_failure', 'denied', 'budget_exhausted']);
|
|
377
|
+
const semanticEngines = new Set(['native', 'metricflow-cli', 'dbt-cloud']);
|
|
378
|
+
const providerPhases = new Set(['preflight', 'classification', 'meaning_resolution', 'planning', 'generation', 'repair', 'narration', 'agent_control', 'tool_followup', 'unknown']);
|
|
379
|
+
const providerCauses = new Set(['authentication', 'model_not_found', 'rate_limited', 'gateway', 'network', 'provider_timeout', 'run_deadline', 'admission_denied', 'dispatch_budget', 'cancelled', 'unknown']);
|
|
380
|
+
const narrations = new Set(['fact_bound', 'deterministic_fallback', 'not_retained', 'not_applicable']);
|
|
381
|
+
if (typeof receipt.mode !== 'string' || !modes.has(receipt.mode)
|
|
382
|
+
|| typeof receipt.turnClass !== 'string' || !turns.has(receipt.turnClass))
|
|
383
|
+
return undefined;
|
|
384
|
+
const contextCoverage = Array.isArray(receipt.contextCoverage)
|
|
385
|
+
? receipt.contextCoverage.flatMap((entry) => {
|
|
386
|
+
const item = record(entry);
|
|
387
|
+
if (!item || typeof item.source !== 'string' || !sources.has(item.source)
|
|
388
|
+
|| typeof item.status !== 'string' || !sourceStates.has(item.status))
|
|
389
|
+
return [];
|
|
390
|
+
return [{
|
|
391
|
+
source: item.source,
|
|
392
|
+
status: item.status,
|
|
393
|
+
...(count(item.admittedCandidateCount) !== undefined ? { admittedCandidateCount: count(item.admittedCandidateCount) } : {}),
|
|
394
|
+
...(count(item.excludedCandidateCount) !== undefined ? { excludedCandidateCount: count(item.excludedCandidateCount) } : {}),
|
|
395
|
+
reasonCodes: Array.isArray(item.reasonCodes) ? item.reasonCodes.slice(0, 8).map(safeCode) : [],
|
|
396
|
+
}];
|
|
397
|
+
})
|
|
398
|
+
: [];
|
|
399
|
+
const observations = Array.isArray(receipt.observations)
|
|
400
|
+
? receipt.observations.slice(0, 24).flatMap((entry) => {
|
|
401
|
+
const item = record(entry);
|
|
402
|
+
if (!item || typeof item.tool !== 'string' || !tools.has(item.tool)
|
|
403
|
+
|| typeof item.outcome !== 'string' || !outcomes.has(item.outcome))
|
|
404
|
+
return [];
|
|
405
|
+
const provider = record(item.provider);
|
|
406
|
+
const safeProvider = provider && typeof provider.phase === 'string' && providerPhases.has(provider.phase)
|
|
407
|
+
&& typeof provider.cause === 'string' && providerCauses.has(provider.cause)
|
|
408
|
+
&& typeof provider.retryable === 'boolean'
|
|
409
|
+
? { phase: provider.phase, cause: provider.cause, retryable: provider.retryable, safeAction: safeCode(provider.safeAction) }
|
|
410
|
+
: undefined;
|
|
411
|
+
return [{
|
|
412
|
+
tool: item.tool,
|
|
413
|
+
outcome: item.outcome,
|
|
414
|
+
...(typeof item.tier === 'string' && tiers.has(item.tier) ? { tier: item.tier } : {}),
|
|
415
|
+
reasonCode: safeCode(item.reasonCode),
|
|
416
|
+
candidateIds: Array.isArray(item.candidateIds)
|
|
417
|
+
? item.candidateIds.slice(0, 48).flatMap((id) => identifier(id, 'candidate') ? [identifier(id, 'candidate')] : [])
|
|
418
|
+
: [],
|
|
419
|
+
...(identifier(item.planId, 'plan') ? { planId: identifier(item.planId, 'plan') } : {}),
|
|
420
|
+
...(typeof item.frozen === 'boolean' ? { frozen: item.frozen } : {}),
|
|
421
|
+
...(typeof item.retryable === 'boolean' ? { retryable: item.retryable } : {}),
|
|
422
|
+
...(typeof item.safeAction === 'string' ? { safeAction: safeCode(item.safeAction) } : {}),
|
|
423
|
+
...(count(item.durationMs, 86_400_000) !== undefined ? { durationMs: count(item.durationMs, 86_400_000) } : {}),
|
|
424
|
+
...(identifier(item.inputFingerprint, 'input') ? { inputFingerprint: identifier(item.inputFingerprint, 'input') } : {}),
|
|
425
|
+
...(identifier(item.outputFingerprint, 'output') ? { outputFingerprint: identifier(item.outputFingerprint, 'output') } : {}),
|
|
426
|
+
...(typeof item.origin === 'string' && origins.has(item.origin) ? { origin: item.origin } : {}),
|
|
427
|
+
...(safeProvider ? { provider: safeProvider } : {}),
|
|
428
|
+
}];
|
|
429
|
+
})
|
|
430
|
+
: [];
|
|
431
|
+
const tierAttempts = Array.isArray(receipt.tierAttempts)
|
|
432
|
+
? receipt.tierAttempts.slice(0, 8).flatMap((entry) => {
|
|
433
|
+
const item = record(entry);
|
|
434
|
+
if (!item || typeof item.tier !== 'string' || !tiers.has(item.tier)
|
|
435
|
+
|| typeof item.outcome !== 'string' || !outcomes.has(item.outcome)
|
|
436
|
+
|| typeof item.frozen !== 'boolean')
|
|
437
|
+
return [];
|
|
438
|
+
return [{
|
|
439
|
+
tier: item.tier,
|
|
440
|
+
outcome: item.outcome,
|
|
441
|
+
reasonCode: safeCode(item.reasonCode),
|
|
442
|
+
frozen: item.frozen,
|
|
443
|
+
candidateIds: Array.isArray(item.candidateIds)
|
|
444
|
+
? item.candidateIds.slice(0, 48).flatMap((id) => identifier(id, 'candidate') ? [identifier(id, 'candidate')] : [])
|
|
445
|
+
: [],
|
|
446
|
+
...(count(item.durationMs, 86_400_000) !== undefined ? { durationMs: count(item.durationMs, 86_400_000) } : {}),
|
|
447
|
+
}];
|
|
448
|
+
})
|
|
449
|
+
: [];
|
|
450
|
+
const terminal = record(receipt.terminalOutcome);
|
|
451
|
+
const safeTerminal = terminal && typeof terminal.kind === 'string' && terminalKinds.has(terminal.kind)
|
|
452
|
+
&& typeof terminal.origin === 'string' && origins.has(terminal.origin)
|
|
453
|
+
? {
|
|
454
|
+
kind: terminal.kind,
|
|
455
|
+
origin: terminal.origin,
|
|
456
|
+
reasonCode: safeCode(terminal.reasonCode),
|
|
457
|
+
...(typeof terminal.safeAction === 'string' ? { safeAction: safeCode(terminal.safeAction) } : {}),
|
|
458
|
+
}
|
|
459
|
+
: undefined;
|
|
460
|
+
const outcome = record(receipt.outcome);
|
|
461
|
+
const safeOutcome = outcome && typeof outcome.connectionAttempted === 'boolean'
|
|
462
|
+
&& typeof outcome.narration === 'string' && narrations.has(outcome.narration)
|
|
463
|
+
? {
|
|
464
|
+
connectionAttempted: outcome.connectionAttempted,
|
|
465
|
+
executionAttempts: count(outcome.executionAttempts) ?? 0,
|
|
466
|
+
factCount: count(outcome.factCount) ?? 0,
|
|
467
|
+
narration: outcome.narration,
|
|
468
|
+
}
|
|
469
|
+
: { connectionAttempted: false, executionAttempts: 0, factCount: 0, narration: 'not_retained' };
|
|
470
|
+
const semanticRuntime = record(receipt.semanticRuntime);
|
|
471
|
+
const safeSemanticRuntime = semanticRuntime
|
|
472
|
+
&& semanticRuntime.version === 1
|
|
473
|
+
&& typeof semanticRuntime.preference === 'string'
|
|
474
|
+
&& (semanticRuntime.preference === 'auto' || semanticEngines.has(semanticRuntime.preference))
|
|
475
|
+
&& typeof semanticRuntime.readiness === 'string'
|
|
476
|
+
&& (semanticRuntime.readiness === 'ready' || semanticRuntime.readiness === 'unavailable')
|
|
477
|
+
&& (semanticRuntime.selectedEngine === undefined
|
|
478
|
+
|| (typeof semanticRuntime.selectedEngine === 'string' && semanticEngines.has(semanticRuntime.selectedEngine)))
|
|
479
|
+
? {
|
|
480
|
+
version: 1,
|
|
481
|
+
preference: semanticRuntime.preference,
|
|
482
|
+
...(typeof semanticRuntime.selectedEngine === 'string' ? { selectedEngine: semanticRuntime.selectedEngine } : {}),
|
|
483
|
+
readiness: semanticRuntime.readiness,
|
|
484
|
+
}
|
|
485
|
+
: undefined;
|
|
486
|
+
return {
|
|
487
|
+
version: 8,
|
|
488
|
+
mode: receipt.mode,
|
|
489
|
+
turnClass: receipt.turnClass,
|
|
490
|
+
...(identifier(receipt.snapshotId, 'snapshot') ? { snapshotId: identifier(receipt.snapshotId, 'snapshot') } : {}),
|
|
491
|
+
retainedCandidateCount: count(receipt.retainedCandidateCount) ?? 0,
|
|
492
|
+
initialCandidateCount: count(receipt.initialCandidateCount) ?? 0,
|
|
493
|
+
expansionCount: count(receipt.expansionCount) ?? 0,
|
|
494
|
+
objective: typeof receipt.objective === 'string' && turns.has(receipt.objective) ? receipt.objective : receipt.turnClass,
|
|
495
|
+
contextCoverage,
|
|
496
|
+
excludedCandidateCount: count(receipt.excludedCandidateCount) ?? 0,
|
|
497
|
+
exclusionReasonCodes: Array.isArray(receipt.exclusionReasonCodes) ? receipt.exclusionReasonCodes.slice(0, 8).map(safeCode) : [],
|
|
498
|
+
observations,
|
|
499
|
+
tierAttempts,
|
|
500
|
+
...(typeof receipt.controllerTier === 'string' && tiers.has(receipt.controllerTier)
|
|
501
|
+
? { controllerTier: receipt.controllerTier }
|
|
502
|
+
: {}),
|
|
503
|
+
...(safeSemanticRuntime ? { semanticRuntime: safeSemanticRuntime } : {}),
|
|
504
|
+
planFrozen: receipt.planFrozen === true,
|
|
505
|
+
...(safeTerminal ? { terminalOutcome: safeTerminal } : {}),
|
|
506
|
+
outcome: safeOutcome,
|
|
507
|
+
toolDurationMs: count(receipt.toolDurationMs, 86_400_000) ?? 0,
|
|
508
|
+
finalStopReason: safeCode(receipt.finalStopReason),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* V6 adds the short reader-facing decision path to V5. Keep the portable
|
|
513
|
+
* shape deliberately narrower than the persisted receipt: a support bundle
|
|
514
|
+
* needs counts, enum outcomes, and pseudonymous identity fingerprints, never
|
|
515
|
+
* raw requirements, member values, candidate lifecycle prose, or answer text.
|
|
516
|
+
*/
|
|
517
|
+
function portableRuntimeReceiptV6(value, profile, salt) {
|
|
518
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
519
|
+
return undefined;
|
|
520
|
+
const receipt = value;
|
|
521
|
+
if (receipt.version !== 6 || typeof receipt.runId !== 'string')
|
|
522
|
+
return undefined;
|
|
523
|
+
const record = (candidate) => candidate && typeof candidate === 'object' && !Array.isArray(candidate) ? candidate : undefined;
|
|
524
|
+
// The V5 projection is an explicit allowlist for all inherited fields. Do
|
|
525
|
+
// not copy V6 state/workspace wholesale merely because it extends V5.
|
|
526
|
+
const base = portableRuntimeReceiptV5({ ...receipt, version: 5 }, profile, salt);
|
|
527
|
+
if (!base)
|
|
528
|
+
return undefined;
|
|
529
|
+
const planning = record(receipt.planning);
|
|
530
|
+
const allowedModes = new Set(['exact_fast_path', 'initial_planner', 'targeted_revision', 'deterministic_binding']);
|
|
531
|
+
const allowedVerification = new Set(['valid', 'needs_targeted_context', 'ambiguous', 'denied', 'invalid']);
|
|
532
|
+
const allowedStages = new Set(['retrieval', 'role_coverage', 'planner', 'verification', 'targeted_recovery', 'cascade', 'freeze', 'connection', 'execution', 'facts']);
|
|
533
|
+
const allowedStatuses = new Set(['completed', 'skipped', 'blocked', 'unavailable']);
|
|
534
|
+
const allowedRoles = new Set(['metric', 'entity_key', 'entity_label', 'categorical_dimension', 'time_dimension', 'member', 'relationship', 'context']);
|
|
535
|
+
const allowedTiers = new Set(['certified', 'semantic', 'governed_relational', 'exploratory_sql', 'clarify_or_gap']);
|
|
536
|
+
const allowedTierOutcomes = new Set(['executable', 'ineligible', 'unavailable', 'ambiguous', 'denied']);
|
|
537
|
+
const allowedBoundaries = new Set(['plan.compile', 'semantic.compile', 'sql.authorize', 'sql.execute', 'provider', 'cascade', 'run']);
|
|
538
|
+
const allowedOrigins = new Set(['internal_invariant', 'governance_gate', 'semantic_compiler', 'plan_compiler', 'provider', 'warehouse', 'unknown']);
|
|
539
|
+
const allowedImpacts = new Set(['execution_not_attempted', 'execution_failed', 'answer_not_produced', 'run_cancelled']);
|
|
540
|
+
const allowedActions = new Set(['export_redacted_trace', 'configure_connection', 'change_authorized_connection', 'inspect_failure', 'retry_same_plan', 'refresh_snapshot', 'edit_dql', 'open_sql_notebook', 'request_access', 'reapply_semantic_runtime', 'review_analytical_failure', 'inspect_research_failures', 'none']);
|
|
541
|
+
const finiteCount = (candidate) => typeof candidate === 'number' && Number.isFinite(candidate)
|
|
542
|
+
? Math.max(0, Math.min(10_000, Math.floor(candidate)))
|
|
543
|
+
: undefined;
|
|
544
|
+
const pseudonym = (candidate, kind) => typeof candidate === 'string'
|
|
545
|
+
? (profile === 'strict' ? pseudo(candidate, salt, kind) : candidate)
|
|
546
|
+
: undefined;
|
|
547
|
+
const verificationRecord = planning ? record(planning.verification) : undefined;
|
|
548
|
+
const safePlanning = planning
|
|
549
|
+
&& planning.version === 1
|
|
550
|
+
&& typeof planning.mode === 'string'
|
|
551
|
+
&& allowedModes.has(planning.mode)
|
|
552
|
+
&& verificationRecord
|
|
553
|
+
&& verificationRecord.version === 1
|
|
554
|
+
&& typeof verificationRecord.status === 'string'
|
|
555
|
+
&& allowedVerification.has(verificationRecord.status)
|
|
556
|
+
? {
|
|
557
|
+
version: 1,
|
|
558
|
+
mode: planning.mode,
|
|
559
|
+
...(finiteCount(planning.plannerCalls) !== undefined ? { plannerCalls: finiteCount(planning.plannerCalls) } : {}),
|
|
560
|
+
...(finiteCount(planning.revisionCalls) !== undefined ? { revisionCalls: finiteCount(planning.revisionCalls) } : {}),
|
|
561
|
+
verification: {
|
|
562
|
+
version: 1,
|
|
563
|
+
status: verificationRecord.status,
|
|
564
|
+
missingRoleCount: Array.isArray(verificationRecord.missingRoles)
|
|
565
|
+
? verificationRecord.missingRoles.filter((role) => typeof role === 'string').slice(0, 8).length
|
|
566
|
+
: 0,
|
|
567
|
+
candidateIds: Array.isArray(verificationRecord.candidateIds)
|
|
568
|
+
? verificationRecord.candidateIds
|
|
569
|
+
.filter((id) => typeof id === 'string')
|
|
570
|
+
.slice(0, 32)
|
|
571
|
+
.map((id) => pseudonym(id, 'candidate'))
|
|
572
|
+
: [],
|
|
573
|
+
reasonCode: typeof verificationRecord.reasonCode === 'string'
|
|
574
|
+
&& /^[a-z0-9_:-]{1,96}$/.test(verificationRecord.reasonCode)
|
|
575
|
+
? verificationRecord.reasonCode
|
|
576
|
+
: 'unrecognized_reason',
|
|
577
|
+
},
|
|
578
|
+
}
|
|
579
|
+
: undefined;
|
|
580
|
+
const story = Array.isArray(receipt.story)
|
|
581
|
+
? receipt.story.flatMap((step) => {
|
|
582
|
+
const item = record(step);
|
|
583
|
+
if (!item || typeof item.stage !== 'string' || !allowedStages.has(item.stage)
|
|
584
|
+
|| typeof item.status !== 'string' || !allowedStatuses.has(item.status))
|
|
585
|
+
return [];
|
|
586
|
+
return [{
|
|
587
|
+
stage: item.stage,
|
|
588
|
+
status: item.status,
|
|
589
|
+
reasonCode: typeof item.reasonCode === 'string' && /^[a-z0-9_:-]{1,96}$/.test(item.reasonCode)
|
|
590
|
+
? item.reasonCode
|
|
591
|
+
: 'unrecognized_reason',
|
|
592
|
+
}];
|
|
593
|
+
})
|
|
594
|
+
: [];
|
|
595
|
+
const roleCoverage = Array.isArray(receipt.roleCoverage)
|
|
596
|
+
? receipt.roleCoverage.flatMap((entry) => {
|
|
597
|
+
const item = record(entry);
|
|
598
|
+
if (!item || typeof item.role !== 'string' || !allowedRoles.has(item.role))
|
|
599
|
+
return [];
|
|
600
|
+
const candidateCount = finiteCount(item.candidateCount);
|
|
601
|
+
return candidateCount === undefined ? [] : [{
|
|
602
|
+
role: item.role,
|
|
603
|
+
candidateCount,
|
|
604
|
+
...(item.state === 'alternatives' || item.state === 'proven' ? { state: item.state } : {}),
|
|
605
|
+
}];
|
|
606
|
+
})
|
|
607
|
+
: [];
|
|
608
|
+
const cascade = record(receipt.cascade);
|
|
609
|
+
const safeCascade = cascade && typeof cascade.planFrozen === 'boolean'
|
|
610
|
+
? {
|
|
611
|
+
attempts: Array.isArray(cascade.attempts)
|
|
612
|
+
? cascade.attempts.flatMap((entry) => {
|
|
613
|
+
const item = record(entry);
|
|
614
|
+
if (!item || typeof item.tier !== 'string' || !allowedTiers.has(item.tier)
|
|
615
|
+
|| typeof item.outcome !== 'string' || !allowedTierOutcomes.has(item.outcome)
|
|
616
|
+
|| typeof item.planFrozen !== 'boolean')
|
|
617
|
+
return [];
|
|
618
|
+
return [{ tier: item.tier, outcome: item.outcome, planFrozen: item.planFrozen }];
|
|
619
|
+
})
|
|
620
|
+
: [],
|
|
621
|
+
...(typeof cascade.selectedTier === 'string' && allowedTiers.has(cascade.selectedTier) ? { selectedTier: cascade.selectedTier } : {}),
|
|
622
|
+
...(typeof cascade.stopReason === 'string' && /^[a-z0-9_:-]{1,96}$/.test(cascade.stopReason) ? { stopReason: cascade.stopReason } : {}),
|
|
623
|
+
planFrozen: cascade.planFrozen,
|
|
624
|
+
}
|
|
625
|
+
: undefined;
|
|
626
|
+
const origin = record(receipt.origin);
|
|
627
|
+
const safeOrigin = origin
|
|
628
|
+
&& typeof origin.boundary === 'string' && allowedBoundaries.has(origin.boundary)
|
|
629
|
+
&& typeof origin.origin === 'string' && allowedOrigins.has(origin.origin)
|
|
630
|
+
&& typeof origin.impact === 'string' && allowedImpacts.has(origin.impact)
|
|
631
|
+
? { boundary: origin.boundary, origin: origin.origin, impact: origin.impact }
|
|
632
|
+
: undefined;
|
|
633
|
+
const connection = record(receipt.connection);
|
|
634
|
+
const execution = record(receipt.execution);
|
|
635
|
+
const facts = record(receipt.facts);
|
|
636
|
+
const safeConnection = connection && typeof connection.attempted === 'boolean'
|
|
637
|
+
? { attempted: connection.attempted }
|
|
638
|
+
: undefined;
|
|
639
|
+
const executionAttempts = execution ? finiteCount(execution.attempts) : undefined;
|
|
640
|
+
const safeFacts = facts ? {
|
|
641
|
+
...(finiteCount(facts.factCount) !== undefined ? { factCount: finiteCount(facts.factCount) } : {}),
|
|
642
|
+
...(typeof facts.resultFingerprint === 'string' ? { resultFingerprint: pseudonym(facts.resultFingerprint, 'result') } : {}),
|
|
643
|
+
} : undefined;
|
|
644
|
+
const safeTelemetry = sanitizeTelemetry(receipt.telemetry);
|
|
645
|
+
return {
|
|
646
|
+
...base,
|
|
647
|
+
version: 6,
|
|
648
|
+
...(safePlanning ? { planning: safePlanning } : {}),
|
|
649
|
+
roleCoverage,
|
|
650
|
+
...(safeCascade ? { cascade: safeCascade } : {}),
|
|
651
|
+
...(safeOrigin ? { origin: safeOrigin } : {}),
|
|
652
|
+
...(safeConnection ? { connection: safeConnection } : {}),
|
|
653
|
+
...(executionAttempts !== undefined ? { execution: { attempts: executionAttempts } } : {}),
|
|
654
|
+
...(safeTelemetry ? { telemetry: safeTelemetry } : {}),
|
|
655
|
+
...(safeFacts ? { facts: safeFacts } : {}),
|
|
656
|
+
...(typeof receipt.safeNextAction === 'string' && allowedActions.has(receipt.safeNextAction) ? { safeNextAction: receipt.safeNextAction } : {}),
|
|
657
|
+
story,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* V7 is a count/enum-only first-read projection. Reuse the explicit V6
|
|
662
|
+
* allowlist for inherited fields, then independently allowlist the small
|
|
663
|
+
* inspector rather than copying its object wholesale into a support bundle.
|
|
664
|
+
*/
|
|
665
|
+
function portableRuntimeReceiptV7(value, profile, salt) {
|
|
666
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
667
|
+
return undefined;
|
|
668
|
+
const receipt = value;
|
|
669
|
+
if (receipt.version !== 7 || typeof receipt.runId !== 'string')
|
|
670
|
+
return undefined;
|
|
671
|
+
const base = portableRuntimeReceiptV6({ ...receipt, version: 6 }, profile, salt);
|
|
672
|
+
if (!base)
|
|
673
|
+
return undefined;
|
|
674
|
+
const inspector = receipt.inspector && typeof receipt.inspector === 'object' && !Array.isArray(receipt.inspector)
|
|
675
|
+
? receipt.inspector
|
|
676
|
+
: undefined;
|
|
677
|
+
const record = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
678
|
+
const count = (value) => typeof value === 'number' && Number.isFinite(value)
|
|
679
|
+
? Math.max(0, Math.min(10_000, Math.floor(value)))
|
|
680
|
+
: undefined;
|
|
681
|
+
const understood = record(inspector?.understood);
|
|
682
|
+
const evidence = record(inspector?.evidence);
|
|
683
|
+
const planning = record(inspector?.planning);
|
|
684
|
+
const route = record(inspector?.route);
|
|
685
|
+
const outcome = record(inspector?.outcome);
|
|
686
|
+
const allowedKinds = new Set(['conversation', 'definition', 'lookup', 'ranking', 'breakdown', 'comparison', 'drilldown', 'diagnosis', 'research', 'compound', 'clarification', 'aggregation']);
|
|
687
|
+
const allowedBindings = new Set(['none', 'structured_clarification', 'prior_result', 'task_dependency']);
|
|
688
|
+
const allowedModes = new Set(['exact_fast_path', 'initial_planner', 'targeted_revision', 'deterministic_binding']);
|
|
689
|
+
const allowedVerification = new Set(['valid', 'needs_targeted_context', 'ambiguous', 'denied', 'invalid']);
|
|
690
|
+
const allowedTiers = new Set(['certified', 'semantic', 'governed_relational', 'exploratory_sql', 'clarify_or_gap']);
|
|
691
|
+
const allowedNarration = new Set(['fact_bound', 'result_without_facts', 'not_applicable']);
|
|
692
|
+
const safeInspector = understood && evidence && planning && route && outcome
|
|
693
|
+
&& typeof understood.questionKind === 'string' && allowedKinds.has(understood.questionKind)
|
|
694
|
+
&& typeof understood.conversationBinding === 'string' && allowedBindings.has(understood.conversationBinding)
|
|
695
|
+
&& typeof understood.entityRequested === 'boolean' && typeof understood.hasBoundFilter === 'boolean'
|
|
696
|
+
&& typeof evidence.recoveryAttempted === 'boolean'
|
|
697
|
+
&& typeof planning.mode === 'string' && allowedModes.has(planning.mode)
|
|
698
|
+
&& typeof planning.verification === 'string' && allowedVerification.has(planning.verification)
|
|
699
|
+
&& typeof route.planFrozen === 'boolean' && typeof route.reviewRequired === 'boolean'
|
|
700
|
+
&& typeof outcome.connectionAttempted === 'boolean'
|
|
701
|
+
&& typeof outcome.narration === 'string' && allowedNarration.has(outcome.narration)
|
|
702
|
+
? {
|
|
703
|
+
understood: {
|
|
704
|
+
questionKind: understood.questionKind,
|
|
705
|
+
conversationBinding: understood.conversationBinding,
|
|
706
|
+
...(count(understood.measureCount) !== undefined ? { measureCount: count(understood.measureCount) } : {}),
|
|
707
|
+
...(count(understood.dimensionCount) !== undefined ? { dimensionCount: count(understood.dimensionCount) } : {}),
|
|
708
|
+
entityRequested: understood.entityRequested,
|
|
709
|
+
hasBoundFilter: understood.hasBoundFilter,
|
|
710
|
+
},
|
|
711
|
+
evidence: {
|
|
712
|
+
...(count(evidence.admittedCandidateCount) !== undefined ? { admittedCandidateCount: count(evidence.admittedCandidateCount) } : {}),
|
|
713
|
+
...(count(evidence.roleCount) !== undefined ? { roleCount: count(evidence.roleCount) } : {}),
|
|
714
|
+
recoveryAttempted: evidence.recoveryAttempted,
|
|
715
|
+
},
|
|
716
|
+
planning: {
|
|
717
|
+
mode: planning.mode,
|
|
718
|
+
...(count(planning.plannerCalls) !== undefined ? { plannerCalls: count(planning.plannerCalls) } : {}),
|
|
719
|
+
verification: planning.verification,
|
|
720
|
+
},
|
|
721
|
+
route: {
|
|
722
|
+
...(typeof route.selectedTier === 'string' && allowedTiers.has(route.selectedTier) ? { selectedTier: route.selectedTier } : {}),
|
|
723
|
+
...(count(route.tierAttemptCount) !== undefined ? { tierAttemptCount: count(route.tierAttemptCount) } : {}),
|
|
724
|
+
planFrozen: route.planFrozen,
|
|
725
|
+
reviewRequired: route.reviewRequired,
|
|
726
|
+
},
|
|
727
|
+
outcome: {
|
|
728
|
+
connectionAttempted: outcome.connectionAttempted,
|
|
729
|
+
...(count(outcome.executionAttempts) !== undefined ? { executionAttempts: count(outcome.executionAttempts) } : {}),
|
|
730
|
+
...(count(outcome.factCount) !== undefined ? { factCount: count(outcome.factCount) } : {}),
|
|
731
|
+
narration: outcome.narration,
|
|
732
|
+
},
|
|
733
|
+
}
|
|
734
|
+
: undefined;
|
|
735
|
+
return {
|
|
736
|
+
...base,
|
|
737
|
+
version: 7,
|
|
738
|
+
...(safeInspector ? { inspector: safeInspector } : {}),
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Keep portable V5 parity with the full local trace without exporting raw Ask
|
|
743
|
+
* state. The V5 receipt is already content-free; strict export additionally
|
|
744
|
+
* pseudonymizes fact/result/question identities while retaining its summary
|
|
745
|
+
* fingerprint so a support bundle can be matched to the on-device trace.
|
|
746
|
+
*/
|
|
747
|
+
function portableRuntimeReceiptV5(value, profile, salt) {
|
|
748
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
749
|
+
return undefined;
|
|
750
|
+
const receipt = value;
|
|
751
|
+
if (receipt.version !== 5 || typeof receipt.runId !== 'string')
|
|
752
|
+
return undefined;
|
|
753
|
+
const record = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
754
|
+
const summary = record(receipt.summary);
|
|
755
|
+
const state = record(receipt.state);
|
|
756
|
+
const businessAnswer = record(receipt.businessAnswer);
|
|
757
|
+
const provider = record(receipt.provider);
|
|
758
|
+
const pseudonym = (value, kind) => typeof value === 'string'
|
|
759
|
+
? (profile === 'strict' ? pseudo(value, salt, kind) : value)
|
|
760
|
+
: undefined;
|
|
761
|
+
const safeSummary = summary ? {
|
|
762
|
+
version: summary.version,
|
|
763
|
+
summaryFingerprint: summary.summaryFingerprint,
|
|
764
|
+
runtimeMode: summary.runtimeMode,
|
|
765
|
+
selectedCompiler: summary.selectedCompiler,
|
|
766
|
+
programTaskCount: summary.programTaskCount,
|
|
767
|
+
admittedCandidateCount: summary.admittedCandidateCount,
|
|
768
|
+
toolCallCount: summary.toolCallCount,
|
|
769
|
+
executionAttempts: summary.executionAttempts,
|
|
770
|
+
} : undefined;
|
|
771
|
+
const safeState = state ? {
|
|
772
|
+
version: state.version,
|
|
773
|
+
mode: state.mode,
|
|
774
|
+
phase: state.phase,
|
|
775
|
+
questionFingerprint: pseudonym(state.questionFingerprint, 'question'),
|
|
776
|
+
kind: state.kind,
|
|
777
|
+
requirementCounts: state.requirementCounts,
|
|
778
|
+
mission: state.mission,
|
|
779
|
+
workspace: portableRuntimeWorkspaceV5(state.workspace, profile, salt),
|
|
780
|
+
program: (() => {
|
|
781
|
+
const program = record(state.program);
|
|
782
|
+
return program ? {
|
|
783
|
+
id: pseudonym(program.id, 'program'),
|
|
784
|
+
taskCount: program.taskCount,
|
|
785
|
+
candidateCount: program.candidateCount,
|
|
786
|
+
requiredRoles: program.requiredRoles,
|
|
787
|
+
outputAssertionCount: program.outputAssertionCount,
|
|
788
|
+
} : undefined;
|
|
789
|
+
})(),
|
|
790
|
+
resolvedPlan: (() => {
|
|
791
|
+
const plan = record(state.resolvedPlan);
|
|
792
|
+
return plan ? {
|
|
793
|
+
version: plan.version,
|
|
794
|
+
programId: pseudonym(plan.programId, 'program'),
|
|
795
|
+
compiler: plan.compiler,
|
|
796
|
+
selectedTier: plan.selectedTier,
|
|
797
|
+
planFrozen: plan.planFrozen,
|
|
798
|
+
reviewRequired: plan.reviewRequired,
|
|
799
|
+
planFingerprint: pseudonym(plan.planFingerprint, 'plan'),
|
|
800
|
+
} : undefined;
|
|
801
|
+
})(),
|
|
802
|
+
counters: state.counters,
|
|
803
|
+
} : undefined;
|
|
804
|
+
const safeBusinessAnswer = businessAnswer ? {
|
|
805
|
+
version: businessAnswer.version,
|
|
806
|
+
mode: businessAnswer.mode,
|
|
807
|
+
trustState: businessAnswer.trustState,
|
|
808
|
+
factIds: Array.isArray(businessAnswer.factIds)
|
|
809
|
+
? businessAnswer.factIds.filter((id) => typeof id === 'string').map((id) => pseudonym(id, 'fact'))
|
|
810
|
+
: [],
|
|
811
|
+
resultFingerprint: pseudonym(businessAnswer.resultFingerprint, 'result'),
|
|
812
|
+
limitationCount: businessAnswer.limitationCount,
|
|
813
|
+
} : undefined;
|
|
814
|
+
const safeProvider = provider ? {
|
|
815
|
+
version: provider.version,
|
|
816
|
+
cause: provider.cause,
|
|
817
|
+
phase: provider.phase,
|
|
818
|
+
retryable: provider.retryable,
|
|
819
|
+
safeAction: provider.safeAction,
|
|
820
|
+
httpStatusClass: provider.httpStatusClass,
|
|
821
|
+
providerFingerprint: pseudonym(provider.providerFingerprint, 'provider'),
|
|
822
|
+
modelFingerprint: pseudonym(provider.modelFingerprint, 'model'),
|
|
823
|
+
baseOriginFingerprint: pseudonym(provider.baseOriginFingerprint, 'origin'),
|
|
824
|
+
} : undefined;
|
|
825
|
+
return {
|
|
826
|
+
version: 5,
|
|
827
|
+
runId: pseudonym(receipt.runId, 'run'),
|
|
828
|
+
...(safeState ? { state: safeState } : {}),
|
|
829
|
+
...(safeSummary ? { summary: safeSummary } : {}),
|
|
830
|
+
...(safeBusinessAnswer ? { businessAnswer: safeBusinessAnswer } : {}),
|
|
831
|
+
...(safeProvider ? { provider: safeProvider } : {}),
|
|
832
|
+
finalStopReason: receipt.finalStopReason,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function portableRuntimeWorkspaceV5(value, profile, salt) {
|
|
836
|
+
const workspace = value && typeof value === 'object' && !Array.isArray(value)
|
|
837
|
+
? value
|
|
838
|
+
: undefined;
|
|
839
|
+
if (!workspace)
|
|
840
|
+
return undefined;
|
|
841
|
+
const pseudoIdentifier = (value, kind) => typeof value === 'string'
|
|
842
|
+
? (profile === 'strict' ? pseudo(value, salt, kind) : value)
|
|
843
|
+
: undefined;
|
|
844
|
+
const sourceValues = new Set([
|
|
845
|
+
'certified', 'semantic', 'governed_relational', 'exploratory', 'dbt_manifest', 'runtime_schema', 'vector', 'conversation', 'business_context',
|
|
846
|
+
]);
|
|
847
|
+
const statuses = new Set(['available', 'empty', 'stale', 'unavailable', 'errored', 'skipped']);
|
|
848
|
+
const toolKinds = new Set(['retrieve_snapshot', 'candidate_extension', 'compiler_broker', 'provider_meaning', 'execute', 'repair']);
|
|
849
|
+
const toolStatuses = new Set(['completed', 'skipped', 'failed']);
|
|
850
|
+
const reasonCodes = new Set([
|
|
851
|
+
'snapshot_acquired', 'broker_retrieval_required', 'snapshot_retrieval_failed', 'shadow_preserves_legacy_decision',
|
|
852
|
+
'meaning_resolution_completed', 'canonical_or_structured_binding', 'meaning_resolution_failed', 'meaning_resolution_unavailable',
|
|
853
|
+
'execution_completed', 'execution_failed', 'repair_completed', 'repair_exhausted', 'no_compiler_selected',
|
|
854
|
+
]);
|
|
855
|
+
const finiteCount = (value) => typeof value === 'number' && Number.isFinite(value)
|
|
856
|
+
? Math.max(0, Math.min(10_000, Math.floor(value)))
|
|
857
|
+
: undefined;
|
|
858
|
+
const sourceCoverage = Array.isArray(workspace.sourceCoverage)
|
|
859
|
+
? workspace.sourceCoverage.flatMap((coverage) => {
|
|
860
|
+
if (!coverage || typeof coverage !== 'object' || Array.isArray(coverage))
|
|
861
|
+
return [];
|
|
862
|
+
const record = coverage;
|
|
863
|
+
if (typeof record.source !== 'string' || !sourceValues.has(record.source)
|
|
864
|
+
|| typeof record.status !== 'string' || !statuses.has(record.status))
|
|
865
|
+
return [];
|
|
866
|
+
const candidateCount = finiteCount(record.candidateCount);
|
|
867
|
+
return [{ source: record.source, status: record.status, ...(candidateCount !== undefined ? { candidateCount } : {}) }];
|
|
868
|
+
})
|
|
869
|
+
: [];
|
|
870
|
+
const tools = Array.isArray(workspace.tools)
|
|
871
|
+
? workspace.tools.flatMap((tool) => {
|
|
872
|
+
if (!tool || typeof tool !== 'object' || Array.isArray(tool))
|
|
873
|
+
return [];
|
|
874
|
+
const record = tool;
|
|
875
|
+
if (typeof record.id !== 'string' || typeof record.kind !== 'string' || !toolKinds.has(record.kind)
|
|
876
|
+
|| typeof record.status !== 'string' || !toolStatuses.has(record.status))
|
|
877
|
+
return [];
|
|
878
|
+
return [{
|
|
879
|
+
id: pseudoIdentifier(record.id, 'tool'),
|
|
880
|
+
kind: record.kind,
|
|
881
|
+
status: record.status,
|
|
882
|
+
reasonCode: typeof record.reasonCode === 'string' && reasonCodes.has(record.reasonCode)
|
|
883
|
+
? record.reasonCode
|
|
884
|
+
: 'unrecognized_reason',
|
|
885
|
+
}];
|
|
886
|
+
})
|
|
887
|
+
: [];
|
|
888
|
+
const admittedCandidateCount = finiteCount(workspace.admittedCandidateCount);
|
|
889
|
+
const excludedCandidateCount = finiteCount(workspace.excludedCandidateCount);
|
|
890
|
+
return {
|
|
891
|
+
...(typeof workspace.snapshotId === 'string' ? { snapshotId: pseudoIdentifier(workspace.snapshotId, 'snapshot') } : {}),
|
|
892
|
+
...(typeof workspace.sourceFingerprint === 'string' ? { sourceFingerprint: pseudoIdentifier(workspace.sourceFingerprint, 'source') } : {}),
|
|
893
|
+
...(admittedCandidateCount !== undefined ? { admittedCandidateCount } : {}),
|
|
894
|
+
...(excludedCandidateCount !== undefined ? { excludedCandidateCount } : {}),
|
|
895
|
+
sourceCoverage,
|
|
896
|
+
tools,
|
|
897
|
+
};
|
|
898
|
+
}
|
|
337
899
|
/**
|
|
338
900
|
* Receipt export is intentionally stricter than its source type: only the
|
|
339
901
|
* one enumerated relationship proof requirement is portable. This prevents a
|