@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.
Files changed (128) hide show
  1. package/dist/agent-run-engine.d.ts +177 -3
  2. package/dist/agent-run-engine.d.ts.map +1 -1
  3. package/dist/agent-run-engine.js +2067 -57
  4. package/dist/agent-run-engine.js.map +1 -1
  5. package/dist/agent-run-gates.d.ts.map +1 -1
  6. package/dist/agent-run-gates.js +51 -3
  7. package/dist/agent-run-gates.js.map +1 -1
  8. package/dist/agent-run-store.d.ts.map +1 -1
  9. package/dist/agent-run-store.js +544 -9
  10. package/dist/agent-run-store.js.map +1 -1
  11. package/dist/agentic/analyst-loop.d.ts +2 -0
  12. package/dist/agentic/analyst-loop.d.ts.map +1 -1
  13. package/dist/agentic/analyst-loop.js +41 -4
  14. package/dist/agentic/analyst-loop.js.map +1 -1
  15. package/dist/agentic/ledger-tools.d.ts.map +1 -1
  16. package/dist/agentic/ledger-tools.js +8 -1
  17. package/dist/agentic/ledger-tools.js.map +1 -1
  18. package/dist/agentic/orchestrator-policy.d.ts +3 -1
  19. package/dist/agentic/orchestrator-policy.d.ts.map +1 -1
  20. package/dist/agentic/orchestrator-policy.js +1 -1
  21. package/dist/agentic/orchestrator-policy.js.map +1 -1
  22. package/dist/agentic/tool-loop.d.ts +13 -1
  23. package/dist/agentic/tool-loop.d.ts.map +1 -1
  24. package/dist/agentic/tool-loop.js +441 -32
  25. package/dist/agentic/tool-loop.js.map +1 -1
  26. package/dist/analytical-frame.d.ts +19 -0
  27. package/dist/analytical-frame.d.ts.map +1 -1
  28. package/dist/analytical-frame.js +170 -12
  29. package/dist/analytical-frame.js.map +1 -1
  30. package/dist/analytical-orchestration.d.ts +949 -10
  31. package/dist/analytical-orchestration.d.ts.map +1 -1
  32. package/dist/analytical-orchestration.js +499 -56
  33. package/dist/analytical-orchestration.js.map +1 -1
  34. package/dist/answer-loop.d.ts +166 -2
  35. package/dist/answer-loop.d.ts.map +1 -1
  36. package/dist/answer-loop.js +654 -18
  37. package/dist/answer-loop.js.map +1 -1
  38. package/dist/answer-shape.d.ts +27 -0
  39. package/dist/answer-shape.d.ts.map +1 -1
  40. package/dist/answer-shape.js +55 -0
  41. package/dist/answer-shape.js.map +1 -1
  42. package/dist/ask-observability/instrumentation.d.ts.map +1 -1
  43. package/dist/ask-observability/instrumentation.js +52 -1
  44. package/dist/ask-observability/instrumentation.js.map +1 -1
  45. package/dist/ask-observability/portable.js +562 -0
  46. package/dist/ask-observability/portable.js.map +1 -1
  47. package/dist/ask-observability/types.d.ts +26 -3
  48. package/dist/ask-observability/types.d.ts.map +1 -1
  49. package/dist/ask-observability/types.js.map +1 -1
  50. package/dist/ask-runtime/ask-agent-runtime-v2.d.ts +850 -0
  51. package/dist/ask-runtime/ask-agent-runtime-v2.d.ts.map +1 -0
  52. package/dist/ask-runtime/ask-agent-runtime-v2.js +1716 -0
  53. package/dist/ask-runtime/ask-agent-runtime-v2.js.map +1 -0
  54. package/dist/ask-runtime/ask-analyst-runtime.d.ts +95 -0
  55. package/dist/ask-runtime/ask-analyst-runtime.d.ts.map +1 -0
  56. package/dist/ask-runtime/ask-analyst-runtime.js +8280 -0
  57. package/dist/ask-runtime/ask-analyst-runtime.js.map +1 -0
  58. package/dist/conversation/session-store.d.ts +31 -0
  59. package/dist/conversation/session-store.d.ts.map +1 -1
  60. package/dist/conversation/session-store.js +38 -0
  61. package/dist/conversation/session-store.js.map +1 -1
  62. package/dist/conversation/snapshot.d.ts +4 -1
  63. package/dist/conversation/snapshot.d.ts.map +1 -1
  64. package/dist/conversation/snapshot.js +85 -3
  65. package/dist/conversation/snapshot.js.map +1 -1
  66. package/dist/conversation/turn-trust.d.ts.map +1 -1
  67. package/dist/conversation/turn-trust.js +2 -0
  68. package/dist/conversation/turn-trust.js.map +1 -1
  69. package/dist/embeddings/provider.d.ts +1 -0
  70. package/dist/embeddings/provider.d.ts.map +1 -1
  71. package/dist/embeddings/provider.js +18 -0
  72. package/dist/embeddings/provider.js.map +1 -1
  73. package/dist/generated-analytical-proposal.js +37 -2
  74. package/dist/generated-analytical-proposal.js.map +1 -1
  75. package/dist/index.d.ts +15 -8
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +9 -4
  78. package/dist/index.js.map +1 -1
  79. package/dist/intent-controller.d.ts +77 -1
  80. package/dist/intent-controller.d.ts.map +1 -1
  81. package/dist/intent-controller.js +1 -1
  82. package/dist/intent-controller.js.map +1 -1
  83. package/dist/kg/build.js +32 -2
  84. package/dist/kg/build.js.map +1 -1
  85. package/dist/meaning-resolution.d.ts +129 -0
  86. package/dist/meaning-resolution.d.ts.map +1 -1
  87. package/dist/meaning-resolution.js +108 -15
  88. package/dist/meaning-resolution.js.map +1 -1
  89. package/dist/metadata/analysis-planner.js +57 -6
  90. package/dist/metadata/analysis-planner.js.map +1 -1
  91. package/dist/metadata/catalog.d.ts +14 -0
  92. package/dist/metadata/catalog.d.ts.map +1 -1
  93. package/dist/metadata/catalog.js +28 -1
  94. package/dist/metadata/catalog.js.map +1 -1
  95. package/dist/metadata/meaning-evidence.d.ts +19 -0
  96. package/dist/metadata/meaning-evidence.d.ts.map +1 -1
  97. package/dist/metadata/meaning-evidence.js +113 -6
  98. package/dist/metadata/meaning-evidence.js.map +1 -1
  99. package/dist/provider-egress.d.ts +4 -1
  100. package/dist/provider-egress.d.ts.map +1 -1
  101. package/dist/provider-egress.js +50 -5
  102. package/dist/provider-egress.js.map +1 -1
  103. package/dist/providers/claude.d.ts +2 -2
  104. package/dist/providers/claude.d.ts.map +1 -1
  105. package/dist/providers/claude.js +191 -28
  106. package/dist/providers/claude.js.map +1 -1
  107. package/dist/providers/index.d.ts +1 -1
  108. package/dist/providers/index.d.ts.map +1 -1
  109. package/dist/providers/index.js.map +1 -1
  110. package/dist/providers/openai.d.ts +2 -2
  111. package/dist/providers/openai.d.ts.map +1 -1
  112. package/dist/providers/openai.js +191 -21
  113. package/dist/providers/openai.js.map +1 -1
  114. package/dist/providers/types.d.ts +79 -1
  115. package/dist/providers/types.d.ts.map +1 -1
  116. package/dist/providers/types.js.map +1 -1
  117. package/dist/requirement-clauses.d.ts +23 -0
  118. package/dist/requirement-clauses.d.ts.map +1 -0
  119. package/dist/requirement-clauses.js +70 -0
  120. package/dist/requirement-clauses.js.map +1 -0
  121. package/dist/resolved-analytical-plan.d.ts.map +1 -1
  122. package/dist/resolved-analytical-plan.js +201 -15
  123. package/dist/resolved-analytical-plan.js.map +1 -1
  124. package/dist/router.d.ts +109 -2
  125. package/dist/router.d.ts.map +1 -1
  126. package/dist/router.js +753 -76
  127. package/dist/router.js.map +1 -1
  128. 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