@tangle-network/agent-runtime 0.194.0 → 0.195.1

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 (93) hide show
  1. package/dist/{activation-BQFIiyUG.js → activation-B4adh0sA.js} +3 -3
  2. package/dist/{activation-BQFIiyUG.js.map → activation-B4adh0sA.js.map} +1 -1
  3. package/dist/agent.d.ts +1 -1
  4. package/dist/agent.js +2 -2
  5. package/dist/candidate-execution/index.js +4 -4
  6. package/dist/{candidate-execution-B6CDW-fp.js → candidate-execution-nvqVIMyS.js} +5 -4
  7. package/dist/{candidate-execution-B6CDW-fp.js.map → candidate-execution-nvqVIMyS.js.map} +1 -1
  8. package/dist/{coordination-driver-iSN-m3VX.js → coordination-driver-CuShDjRV.js} +3 -3
  9. package/dist/{coordination-driver-iSN-m3VX.js.map → coordination-driver-CuShDjRV.js.map} +1 -1
  10. package/dist/{delegate-DCC2muUd.js → delegate-CSFC-Kh3.js} +2 -2
  11. package/dist/{delegate-DCC2muUd.js.map → delegate-CSFC-Kh3.js.map} +1 -1
  12. package/dist/durable.d.ts +1 -1
  13. package/dist/durable.js +2 -2
  14. package/dist/{environment-provider-Dr-wfnHg.js → environment-provider-Z1JazbJ1.js} +108 -10
  15. package/dist/environment-provider-Z1JazbJ1.js.map +1 -0
  16. package/dist/environment-provider.js +1 -1
  17. package/dist/{graph-DghidDx5.js → graph-DqPUeri6.js} +3 -3
  18. package/dist/{graph-DghidDx5.js.map → graph-DqPUeri6.js.map} +1 -1
  19. package/dist/graph.d.ts +1 -1
  20. package/dist/graph.js +4 -4
  21. package/dist/{improve-COGzLCiY.d.ts → improve-7VTwse1c.d.ts} +4 -4
  22. package/dist/{improvement-cycle-DeS1ZC5B.js → improvement-cycle-DEbciBTL.js} +20 -6
  23. package/dist/{improvement-cycle-DeS1ZC5B.js.map → improvement-cycle-DEbciBTL.js.map} +1 -1
  24. package/dist/{index-CrBgLCIf.d.ts → index-CVD30soi.d.ts} +2 -2
  25. package/dist/{index-BEPjOPwH.d.ts → index-DNx0nWZH.d.ts} +9 -2
  26. package/dist/index.d.ts +6 -5
  27. package/dist/index.js +72 -26
  28. package/dist/index.js.map +1 -1
  29. package/dist/intelligence.d.ts +1 -1
  30. package/dist/intelligence.js +7 -6
  31. package/dist/intelligence.js.map +1 -1
  32. package/dist/kernel.d.ts +1 -1
  33. package/dist/kernel.js +9 -9
  34. package/dist/{knowledge-B5okOzBQ.js → knowledge-B0Mrp-XB.js} +7 -17
  35. package/dist/knowledge-B0Mrp-XB.js.map +1 -0
  36. package/dist/knowledge.d.ts +1 -1
  37. package/dist/knowledge.js +1 -1
  38. package/dist/{loop-runner-bin-U1J6x_UF.js → loop-runner-bin-C5K46H0Q.js} +3 -3
  39. package/dist/{loop-runner-bin-U1J6x_UF.js.map → loop-runner-bin-C5K46H0Q.js.map} +1 -1
  40. package/dist/{loop-runner-bin-Clc5IAwu.d.ts → loop-runner-bin-b4585v9Z.d.ts} +2 -2
  41. package/dist/loop-runner-bin.d.ts +1 -1
  42. package/dist/loop-runner-bin.js +1 -1
  43. package/dist/mcp/bin.js +3 -3
  44. package/dist/mcp/index.d.ts +1 -1
  45. package/dist/mcp/index.js +5 -5
  46. package/dist/{prepare-C29kNAon.js → prepare-DDGp0-rW.js} +4 -54
  47. package/dist/prepare-DDGp0-rW.js.map +1 -0
  48. package/dist/profiles.js +454 -1
  49. package/dist/profiles.js.map +1 -1
  50. package/dist/{protected-model-port-CKYND416.js → protected-model-port-DxFN8DLS.js} +3 -2
  51. package/dist/{protected-model-port-CKYND416.js.map → protected-model-port-DxFN8DLS.js.map} +1 -1
  52. package/dist/{protected-redaction--F3v1oo8.js → protected-redaction-wGo44k2K.js} +54 -3
  53. package/dist/protected-redaction-wGo44k2K.js.map +1 -0
  54. package/dist/{provision-supervisor-CRPbnJiB.js → provision-supervisor-chyz0_Aj.js} +4 -4
  55. package/dist/{provision-supervisor-CRPbnJiB.js.map → provision-supervisor-chyz0_Aj.js.map} +1 -1
  56. package/dist/{redact-BOU77QfQ.js → redact-Dv6PeuQz.js} +2 -2
  57. package/dist/{redact-BOU77QfQ.js.map → redact-Dv6PeuQz.js.map} +1 -1
  58. package/dist/{runtime-BVrqSu0-.js → runtime-BZryF6gD.js} +140 -65
  59. package/dist/runtime-BZryF6gD.js.map +1 -0
  60. package/dist/{server-D6XmE1Du.js → server-BorSFmn8.js} +3 -3
  61. package/dist/{server-D6XmE1Du.js.map → server-BorSFmn8.js.map} +1 -1
  62. package/dist/{structural-rollout-BcPXFzVb.js → structural-rollout-pOqANr8Y.js} +23 -10
  63. package/dist/structural-rollout-pOqANr8Y.js.map +1 -0
  64. package/dist/{supervise-C0V3pZXK.js → supervise-BReI5ATC.js} +4 -4
  65. package/dist/{supervise-C0V3pZXK.js.map → supervise-BReI5ATC.js.map} +1 -1
  66. package/dist/testing.d.ts +1 -1
  67. package/dist/testing.js +14 -13
  68. package/dist/testing.js.map +1 -1
  69. package/dist/tui/index.d.ts +1 -1
  70. package/dist/tui/index.js +1 -1
  71. package/dist/{workspace-archive-BCezkeIk.js → workspace-archive-Ybomp7AN.js} +3 -2
  72. package/dist/{workspace-archive-BCezkeIk.js.map → workspace-archive-Ybomp7AN.js.map} +1 -1
  73. package/package.json +4 -4
  74. package/skills/agent-graphs/SKILL.md +28 -123
  75. package/skills/agent-graphs/references/authoring.md +34 -0
  76. package/skills/agent-graphs/references/measurement.md +18 -0
  77. package/skills/build-with-agent-runtime/SKILL.md +31 -117
  78. package/skills/build-with-agent-runtime/references/improvement.md +32 -0
  79. package/skills/codemode/SKILL.md +22 -34
  80. package/skills/codemode/references/runtime-execution.md +14 -0
  81. package/skills/generate-eval/SKILL.md +2 -1
  82. package/skills/loop-writer/SKILL.md +30 -84
  83. package/skills/supervise/SKILL.md +43 -95
  84. package/skills/supervise/references/profile-authoring.md +19 -0
  85. package/dist/environment-provider-Dr-wfnHg.js.map +0 -1
  86. package/dist/knowledge-B5okOzBQ.js.map +0 -1
  87. package/dist/prepare-C29kNAon.js.map +0 -1
  88. package/dist/protected-redaction--F3v1oo8.js.map +0 -1
  89. package/dist/researcher-Cp4JRbFp.js +0 -457
  90. package/dist/researcher-Cp4JRbFp.js.map +0 -1
  91. package/dist/runtime-BVrqSu0-.js.map +0 -1
  92. package/dist/structural-rollout-BcPXFzVb.js.map +0 -1
  93. package/skills/agent-graphs/IMPROVE.md +0 -58
package/dist/profiles.js CHANGED
@@ -1,6 +1,8 @@
1
- import { i as researcherProfile, n as createResearcherValidator, r as multiHarnessResearcherFanout } from "./researcher-Cp4JRbFp.js";
1
+ import { i as ConfigError } from "./errors-CDZ8XsVj.js";
2
+ import { t as assertExecutableAgentProfile } from "./model-policy-Wf2MxXJ9.js";
2
3
  import { n as UI_LENSES, t as UI_FINDING_SEVERITIES } from "./substrate-B0TYNrXn.js";
3
4
  import { t as coderTaskToPrompt } from "./coder-yhVWbdWc.js";
5
+ import { agentProfileSchema } from "@tangle-network/agent-interface";
4
6
  import { promises } from "node:fs";
5
7
  import path from "node:path";
6
8
  //#region src/profiles/ui-auditor/slugify.ts
@@ -317,6 +319,457 @@ async function writeAuditIndex(workspaceDir) {
317
319
  return out;
318
320
  }
319
321
  //#endregion
322
+ //#region src/profiles/researcher.ts
323
+ /**
324
+ * Opinionated preset for source-grounded research
325
+ * tasks. The agent is told to:
326
+ * - bound its work to a single `knowledgeNamespace`
327
+ * - emit `items[]` carrying provenance + confidence
328
+ * - emit `citations[]` linking quotes back to source urls
329
+ * - emit `proposedWrites[]` — never call materialize itself
330
+ * - describe `gaps` it could not answer
331
+ *
332
+ * The profile is stateless and agent-agnostic. The caller supplies the exact execution profile;
333
+ * this preset adds only the researcher prompt, tools, parser, and validator.
334
+ *
335
+ * Propose-don't-apply: the profile NEVER writes to the knowledge base.
336
+ * It produces `proposedWrites: KnowledgeUpdate[]` in the output. The
337
+ * caller (gtm-agent, journey-eval, user) decides whether to feed those
338
+ * updates through `applyKnowledgeWriteBlocks` / a KbStore put.
339
+ *
340
+ * Namespace isolation: every `KnowledgeItem` + `KnowledgeUpdate` in the
341
+ * output carries `namespace`. The validator hard-fails when any item
342
+ * touches a namespace other than `task.knowledgeNamespace`.
343
+ *
344
+ * @experimental
345
+ */
346
+ const DEFAULT_CITATION_DENSITY_MIN = .7;
347
+ /** Build a source-grounded researcher profile with output parsing and validation. @experimental */
348
+ function researcherProfile(options) {
349
+ const base = agentProfileSchema.parse(options.profile);
350
+ assertExecutableAgentProfile(base, "researcherProfile");
351
+ const name = options.name ?? base.name;
352
+ const systemPrompt = options.systemPrompt ?? RESEARCHER_SYSTEM_PROMPT;
353
+ const citationDensityMin = options.citationDensityMin ?? DEFAULT_CITATION_DENSITY_MIN;
354
+ const profile = {
355
+ ...base,
356
+ name,
357
+ description: base.description ?? "Source-grounded research agent. Propose-don't-apply.",
358
+ prompt: {
359
+ ...base.prompt,
360
+ systemPrompt
361
+ },
362
+ tools: {
363
+ web_search: true,
364
+ fs: true,
365
+ shell: true,
366
+ ...base.tools
367
+ },
368
+ metadata: {
369
+ ...base.metadata,
370
+ specialty: "researcher"
371
+ }
372
+ };
373
+ return {
374
+ profile,
375
+ taskToPrompt: formatResearcherPrompt,
376
+ output: { parse: parseResearcherEvents },
377
+ validator: options.task ? createResearcherValidator(options.task, { citationDensityMin }) : createResearcherValidator({
378
+ question: "",
379
+ knowledgeNamespace: ""
380
+ }, {
381
+ citationDensityMin,
382
+ namespaceCheck: false
383
+ }),
384
+ agentRunSpec: {
385
+ name,
386
+ profile,
387
+ taskToPrompt: formatResearcherPrompt
388
+ }
389
+ };
390
+ }
391
+ /**
392
+ * Build a fanout topology over multiple harnesses. The kernel round-robins
393
+ * `agentRuns` across the N parallel iterations and the `FanoutVote` driver
394
+ * picks the highest-scoring valid output.
395
+ *
396
+ * @experimental
397
+ */
398
+ function multiHarnessResearcherFanout(options) {
399
+ if (options.profiles.length === 0) throw new ConfigError("multiHarnessResearcherFanout: at least one exact profile is required");
400
+ const agentRuns = options.profiles.map((profile) => {
401
+ const { agentRunSpec } = researcherProfile({ profile });
402
+ return agentRunSpec;
403
+ });
404
+ const { output, validator } = researcherProfile({
405
+ profile: options.profiles[0],
406
+ citationDensityMin: options.citationDensityMin,
407
+ task: options.task
408
+ });
409
+ return {
410
+ agentRuns,
411
+ output,
412
+ validator,
413
+ driver: {
414
+ name: "researcher-fanout",
415
+ async plan(task, history) {
416
+ return history.length === 0 ? Array.from({ length: agentRuns.length }, () => task) : [];
417
+ },
418
+ decide() {
419
+ return "done";
420
+ },
421
+ describePlan() {
422
+ return { kind: "fanout" };
423
+ }
424
+ }
425
+ };
426
+ }
427
+ /**
428
+ * Build a validator that closes over a specific `ResearchTask`'s constraints.
429
+ *
430
+ * Checks in order:
431
+ * 1. Items must be non-empty.
432
+ * 2. Every item carries `evidence.length >= 1`.
433
+ * 3. Every item + proposedWrite is scoped to `task.knowledgeNamespace`
434
+ * (hard-fail on any namespace mismatch — defence in depth for the
435
+ * multi-tenant invariant).
436
+ * 4. Citation density (citations with quote / items) >= floor.
437
+ *
438
+ * Aggregate score:
439
+ * 0.4 · citation_density
440
+ * + 0.2 · source_diversity (distinct sources / max(items, 1))
441
+ * + 0.2 · recency_match (mean fraction within `recencyWindow`)
442
+ * + 0.2 · (1 − gaps/maxGaps), maxGaps = max(items, 1)
443
+ *
444
+ * @experimental
445
+ */
446
+ function createResearcherValidator(task, config = {}) {
447
+ const citationDensityMin = config.citationDensityMin ?? DEFAULT_CITATION_DENSITY_MIN;
448
+ const namespaceCheck = config.namespaceCheck ?? true;
449
+ return { async validate(output) {
450
+ const notes = [];
451
+ const scores = {};
452
+ let pass = true;
453
+ if (!Array.isArray(output.items) || output.items.length === 0) {
454
+ pass = false;
455
+ notes.push("no items");
456
+ scores.items = 0;
457
+ } else scores.items = 1;
458
+ const missingEvidence = (output.items ?? []).filter((item) => !Array.isArray(item.evidence) || item.evidence.length === 0);
459
+ if (missingEvidence.length > 0) {
460
+ pass = false;
461
+ notes.push(`${missingEvidence.length} item(s) without evidence`);
462
+ scores.provenance = 0;
463
+ } else scores.provenance = 1;
464
+ if (namespaceCheck) {
465
+ const foreignItems = (output.items ?? []).filter((item) => item.namespace !== task.knowledgeNamespace);
466
+ const foreignWrites = (output.proposedWrites ?? []).filter((write) => write.namespace !== task.knowledgeNamespace);
467
+ if (foreignItems.length > 0 || foreignWrites.length > 0) {
468
+ pass = false;
469
+ notes.push(`namespace violation: ${foreignItems.length} item(s) + ${foreignWrites.length} write(s) outside ${task.knowledgeNamespace}`);
470
+ scores.namespace = 0;
471
+ } else scores.namespace = 1;
472
+ }
473
+ const itemCount = Math.max(output.items?.length ?? 0, 1);
474
+ const citationsWithQuote = (output.citations ?? []).filter((citation) => typeof citation.quote === "string" && citation.quote.length > 0).length;
475
+ const citationDensity = Math.min(1, citationsWithQuote / itemCount);
476
+ if (citationDensity < citationDensityMin) {
477
+ pass = false;
478
+ notes.push(`citation density ${citationDensity.toFixed(2)} below floor ${citationDensityMin.toFixed(2)}`);
479
+ }
480
+ scores.citation_density = citationDensity;
481
+ const sourceSet = /* @__PURE__ */ new Set();
482
+ for (const item of output.items ?? []) for (const evidence of item.evidence ?? []) if (evidence.source) sourceSet.add(evidence.source);
483
+ scores.source_diversity = Math.min(1, sourceSet.size / itemCount);
484
+ scores.recency_match = recencyMatchScore(output.items ?? [], task.recencyWindow);
485
+ const maxGaps = itemCount;
486
+ const gapCount = output.gaps?.length ?? 0;
487
+ scores.gap_coverage = Math.max(0, 1 - gapCount / maxGaps);
488
+ const score = .4 * scores.citation_density + .2 * scores.source_diversity + .2 * scores.recency_match + .2 * scores.gap_coverage;
489
+ const verdict = {
490
+ valid: pass,
491
+ score: Number.isFinite(score) ? score : 0,
492
+ scores
493
+ };
494
+ if (notes.length > 0) verdict.notes = notes.join("; ");
495
+ return verdict;
496
+ } };
497
+ }
498
+ function recencyMatchScore(items, window) {
499
+ if (!window || window.since === void 0 && window.until === void 0) return 1;
500
+ if (items.length === 0) return 0;
501
+ const sinceMs = window.since?.getTime() ?? Number.NEGATIVE_INFINITY;
502
+ const untilMs = window.until?.getTime() ?? Number.POSITIVE_INFINITY;
503
+ let hits = 0;
504
+ let total = 0;
505
+ for (const item of items) for (const evidence of item.evidence ?? []) {
506
+ if (typeof evidence.capturedAt !== "number") continue;
507
+ total += 1;
508
+ if (evidence.capturedAt >= sinceMs && evidence.capturedAt <= untilMs) hits += 1;
509
+ }
510
+ return total === 0 ? 0 : hits / total;
511
+ }
512
+ /** Built-in source-grounded research contract added to a caller-owned exact profile. */
513
+ const RESEARCHER_SYSTEM_PROMPT = [
514
+ "You are a research agent. Your job is to answer a research question with",
515
+ "source-grounded knowledge items that the caller will choose whether to",
516
+ "persist to a multi-tenant knowledge base.",
517
+ "",
518
+ "Hard rules:",
519
+ " 1. Every item you emit MUST carry the task's knowledgeNamespace exactly.",
520
+ " Never write to a different namespace.",
521
+ " 2. Every item MUST carry at least one evidence entry with a source.",
522
+ " A quote + url is strongly preferred; capturedAt is unix ms.",
523
+ " 3. You propose writes — you do NOT apply them. The caller decides.",
524
+ " 4. Self-report confidence honestly in [0, 1]. Do not inflate.",
525
+ " 5. List what you could not answer in `gaps`. Better to admit a gap",
526
+ " than fabricate.",
527
+ "",
528
+ "When you finish, emit a single final structured message of the shape:",
529
+ " ```json",
530
+ " { \"items\": [{ \"id\": \"...\", \"namespace\": \"...\", \"claim\": \"...\",",
531
+ " \"evidence\": [{ \"source\": \"...\", \"quote\": \"...\",",
532
+ " \"url\": \"...\", \"capturedAt\": 0 }],",
533
+ " \"confidence\": 0.0,",
534
+ " \"authoredBy\": { \"kind\": \"agent\", \"id\": \"...\" } }],",
535
+ " \"citations\": [{ \"url\": \"...\", \"quote\": \"...\", \"confidence\": 0.0 }],",
536
+ " \"proposedWrites\": [{ \"kind\": \"insert\", \"namespace\": \"...\",",
537
+ " \"item\": { /* same shape as items[] */ } }],",
538
+ " \"gaps\": [\"...\"],",
539
+ " \"notes\": \"free-form commentary\" }",
540
+ " ```"
541
+ ].join("\n");
542
+ function formatResearcherPrompt(task) {
543
+ const sources = task.sources?.length ? task.sources.join(", ") : "(no preference)";
544
+ const window = formatRecencyWindow(task.recencyWindow);
545
+ return [
546
+ `Question: ${task.question}`,
547
+ `Knowledge namespace (DO NOT cross): ${task.knowledgeNamespace}`,
548
+ `Scope: ${task.scope ?? "(unspecified)"}`,
549
+ `Preferred sources: ${sources}`,
550
+ `Recency window: ${window}`,
551
+ `Max items: ${task.maxItems ?? "(no cap)"}`,
552
+ `Per-item minimum confidence: ${task.minConfidence ?? "(no floor)"}`,
553
+ "",
554
+ "Produce knowledge items with provenance + citations + proposed writes.",
555
+ "List gaps for anything you could not answer. Emit the final JSON",
556
+ "result block exactly as instructed."
557
+ ].join("\n");
558
+ }
559
+ function formatRecencyWindow(window) {
560
+ if (!window) return "(none)";
561
+ return `${window.since ? window.since.toISOString() : "-∞"} .. ${window.until ? window.until.toISOString() : "now"}`;
562
+ }
563
+ /**
564
+ * Walk the event stream and return the last structured `research.result`
565
+ * payload. Falls back to scanning text deltas for a fenced JSON block.
566
+ */
567
+ function parseResearcherEvents(events) {
568
+ for (let i = events.length - 1; i >= 0; i -= 1) {
569
+ const event = events[i];
570
+ if (!event) continue;
571
+ const type = String(event.type ?? "");
572
+ const data = isRecord(event.data) ? event.data : {};
573
+ if (type === "result" || type === "final" || type === "research.result") {
574
+ const direct = coerceResearchOutput(data.result ?? data.output ?? data);
575
+ const finalText = pickString(data.finalText);
576
+ const fenced = finalText ? extractFencedJson(finalText) : void 0;
577
+ const fromFinalText = fenced ? coerceResearchOutput(fenced) : void 0;
578
+ if (direct && !isEmptyOutput(direct)) return direct;
579
+ if (fromFinalText) return fromFinalText;
580
+ if (direct) return direct;
581
+ }
582
+ }
583
+ for (let i = events.length - 1; i >= 0; i -= 1) {
584
+ const event = events[i];
585
+ if (!event) continue;
586
+ const data = isRecord(event.data) ? event.data : {};
587
+ const text = pickString(data.text) ?? pickString(data.delta) ?? pickString(data.finalText);
588
+ if (!text) continue;
589
+ const fenced = extractFencedJson(text);
590
+ if (!fenced) continue;
591
+ const coerced = coerceResearchOutput(fenced);
592
+ if (coerced) return coerced;
593
+ }
594
+ return {
595
+ items: [],
596
+ citations: [],
597
+ proposedWrites: []
598
+ };
599
+ }
600
+ function isRecord(value) {
601
+ return value !== null && typeof value === "object" && !Array.isArray(value);
602
+ }
603
+ /** A coerced output that carries no items, citations, or proposed writes. */
604
+ function isEmptyOutput(o) {
605
+ return o.items.length === 0 && o.citations.length === 0 && o.proposedWrites.length === 0;
606
+ }
607
+ function pickString(value) {
608
+ return typeof value === "string" && value.length > 0 ? value : void 0;
609
+ }
610
+ function extractFencedJson(text) {
611
+ const match = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
612
+ if (!match) return void 0;
613
+ const body = (match[1] ?? "").trim();
614
+ if (!body) return void 0;
615
+ try {
616
+ return JSON.parse(body);
617
+ } catch {
618
+ return;
619
+ }
620
+ }
621
+ function coerceResearchOutput(value) {
622
+ if (!isRecord(value)) return void 0;
623
+ const items = coerceItems(value.items);
624
+ const citations = coerceCitations(value.citations);
625
+ const proposedWrites = coerceProposedWrites(value.proposedWrites);
626
+ if (items === void 0 && citations === void 0 && proposedWrites === void 0) return;
627
+ const output = {
628
+ items: items ?? [],
629
+ citations: citations ?? [],
630
+ proposedWrites: proposedWrites ?? []
631
+ };
632
+ if (Array.isArray(value.gaps)) output.gaps = value.gaps.filter((entry) => typeof entry === "string");
633
+ const notes = pickString(value.notes);
634
+ if (notes) output.notes = notes;
635
+ const known = /* @__PURE__ */ new Set([
636
+ "items",
637
+ "citations",
638
+ "proposedWrites",
639
+ "gaps",
640
+ "notes"
641
+ ]);
642
+ const extras = {};
643
+ let extrasCount = 0;
644
+ for (const [key, val] of Object.entries(value)) {
645
+ if (known.has(key)) continue;
646
+ extras[key] = val;
647
+ extrasCount += 1;
648
+ }
649
+ if (extrasCount > 0) output.raw = extras;
650
+ return output;
651
+ }
652
+ function coerceItems(value) {
653
+ if (!Array.isArray(value)) return void 0;
654
+ const out = [];
655
+ for (const entry of value) {
656
+ if (!isRecord(entry)) continue;
657
+ const id = pickString(entry.id);
658
+ const namespace = pickString(entry.namespace);
659
+ const claim = pickString(entry.claim);
660
+ const evidence = coerceEvidence(entry.evidence);
661
+ const confidence = toFiniteNumber(entry.confidence);
662
+ const authoredBy = coerceAuthoredBy(entry.authoredBy);
663
+ if (!id || !namespace || !claim || !authoredBy) continue;
664
+ const item = {
665
+ id,
666
+ namespace,
667
+ claim,
668
+ evidence,
669
+ confidence: clamp01(confidence),
670
+ authoredBy
671
+ };
672
+ if (Array.isArray(entry.supersedes)) item.supersedes = entry.supersedes.filter((s) => typeof s === "string");
673
+ const retractedAt = toFiniteNumber(entry.retractedAt);
674
+ if (retractedAt > 0) item.retractedAt = retractedAt;
675
+ out.push(item);
676
+ }
677
+ return out;
678
+ }
679
+ function coerceEvidence(value) {
680
+ if (!Array.isArray(value)) return [];
681
+ const out = [];
682
+ for (const entry of value) {
683
+ if (!isRecord(entry)) continue;
684
+ const source = pickString(entry.source);
685
+ if (!source) continue;
686
+ const item = {
687
+ source,
688
+ capturedAt: toFiniteNumber(entry.capturedAt)
689
+ };
690
+ const quote = pickString(entry.quote);
691
+ if (quote) item.quote = quote;
692
+ const url = pickString(entry.url);
693
+ if (url) item.url = url;
694
+ out.push(item);
695
+ }
696
+ return out;
697
+ }
698
+ function coerceAuthoredBy(value) {
699
+ if (!isRecord(value)) return void 0;
700
+ const kind = value.kind === "human" || value.kind === "agent" ? value.kind : void 0;
701
+ const id = pickString(value.id);
702
+ if (!kind || !id) return void 0;
703
+ return {
704
+ kind,
705
+ id
706
+ };
707
+ }
708
+ function coerceCitations(value) {
709
+ if (!Array.isArray(value)) return void 0;
710
+ const out = [];
711
+ for (const entry of value) {
712
+ if (!isRecord(entry)) continue;
713
+ const url = pickString(entry.url);
714
+ const quote = pickString(entry.quote);
715
+ if (!url || !quote) continue;
716
+ out.push({
717
+ url,
718
+ quote,
719
+ confidence: clamp01(toFiniteNumber(entry.confidence))
720
+ });
721
+ }
722
+ return out;
723
+ }
724
+ function coerceProposedWrites(value) {
725
+ if (!Array.isArray(value)) return void 0;
726
+ const out = [];
727
+ for (const entry of value) {
728
+ if (!isRecord(entry)) continue;
729
+ const namespace = pickString(entry.namespace);
730
+ if (!namespace) continue;
731
+ if (entry.kind === "insert") {
732
+ const item = coerceItems([entry.item])?.[0];
733
+ if (!item) continue;
734
+ out.push({
735
+ kind: "insert",
736
+ namespace,
737
+ item
738
+ });
739
+ } else if (entry.kind === "supersede") {
740
+ const previousId = pickString(entry.previousId);
741
+ const item = coerceItems([entry.item])?.[0];
742
+ if (!previousId || !item) continue;
743
+ out.push({
744
+ kind: "supersede",
745
+ namespace,
746
+ previousId,
747
+ item
748
+ });
749
+ } else if (entry.kind === "retract") {
750
+ const itemId = pickString(entry.itemId);
751
+ const reason = pickString(entry.reason);
752
+ if (!itemId || !reason) continue;
753
+ out.push({
754
+ kind: "retract",
755
+ namespace,
756
+ itemId,
757
+ reason
758
+ });
759
+ }
760
+ }
761
+ return out;
762
+ }
763
+ function toFiniteNumber(value) {
764
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
765
+ }
766
+ function clamp01(value) {
767
+ if (!Number.isFinite(value)) return 0;
768
+ if (value < 0) return 0;
769
+ if (value > 1) return 1;
770
+ return value;
771
+ }
772
+ //#endregion
320
773
  //#region src/profiles/ui-auditor/lens-prompts.ts
321
774
  /** Cross-lens rules injected into every UI audit iteration: finding quality standards and scope limits. @experimental */
322
775
  const SHARED_AUDITOR_RULES = `