@tangle-network/agent-app 0.44.26 → 0.44.28

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.
@@ -1,6 +1,113 @@
1
1
  import { CompletionRequirement, RuntimeEventLike } from '@tangle-network/agent-eval';
2
2
  export { CompletionRequirement, CompletionVerdict, CorrectnessChecker, ProducedState, RuntimeEventLike, SatisfiedBy, TaskGold, createLlmCorrectnessChecker, extractProducedState, verifyCompletion, weightedComposite } from '@tangle-network/agent-eval';
3
- import { A as AppToolProducedEvent } from '../types-BCxK0wyS.js';
3
+ import { c as AppToolProducedEvent } from '../types-DbU-oO5h.js';
4
+
5
+ /**
6
+ * Calibration — prove a gate can FAIL before believing that it PASSED, and
7
+ * prove a probe can SEE before believing the zero it reported.
8
+ *
9
+ * Every expensive failure this package has shipped was a check that verified
10
+ * its own input instead of the world, and reported green:
11
+ *
12
+ * - `quote_verification 16/16` passed a work product whose nine citations
13
+ * claimed a figure the cited document never mentions.
14
+ * - `audit_form passed=3 failed=0` passed a form fill whose values landed in
15
+ * "Combat zone" — the audit re-read the field paths the writer had just
16
+ * invented, so a wrong path could not be wrong.
17
+ * - `evidence_coverage 0/0` passed vacuously because the artifact it was
18
+ * counting targets from was null.
19
+ * - A benchmark reported 6/8 for the product while a bare
20
+ * "You are a helpful assistant." scored 8/8 on the same cases.
21
+ *
22
+ * A gate is only evidence if it REJECTS something. A metric is only evidence
23
+ * if a worse system scores worse on it. Neither property is implied by a
24
+ * green run, and neither is visible in the output — which is why both have to
25
+ * be asserted separately, in code, next to the gate.
26
+ *
27
+ * This module is deliberately domain-free: a "gate" is any predicate over any
28
+ * input. Products supply the known-good and known-bad cases, because only the
29
+ * product knows what bad looks like in its domain.
30
+ */
31
+ /** A case whose verdict is known in advance, used to calibrate a gate. */
32
+ interface CalibrationCase<TInput> {
33
+ /** What this case represents, e.g. `'quote that does not occur in the source'`. */
34
+ readonly label: string;
35
+ readonly input: TInput;
36
+ /** `'reject'` — the gate MUST refuse this. `'accept'` — it MUST allow it. */
37
+ readonly expect: 'accept' | 'reject';
38
+ }
39
+ interface CalibrationOutcome {
40
+ readonly label: string;
41
+ readonly expected: 'accept' | 'reject';
42
+ readonly actual: 'accept' | 'reject';
43
+ readonly ok: boolean;
44
+ /** Set when the gate threw; a throw counts as `'reject'`. */
45
+ readonly threw?: string;
46
+ }
47
+ interface CalibrationReport {
48
+ /** True only when every case matched AND both controls were present. */
49
+ readonly discriminates: boolean;
50
+ readonly outcomes: readonly CalibrationOutcome[];
51
+ readonly failures: readonly CalibrationOutcome[];
52
+ /** Why the gate is not trustworthy. Absent when `discriminates` is true. */
53
+ readonly reason?: string;
54
+ }
55
+ /**
56
+ * A gate under calibration. Returning `false` OR throwing both count as a
57
+ * rejection — a fail-loud gate (`ToolInputError`) and a boolean gate calibrate
58
+ * through the same path.
59
+ */
60
+ type GateFn<TInput> = (input: TInput) => boolean | Promise<boolean>;
61
+ /**
62
+ * Run a gate against cases whose verdicts are known, and report whether it
63
+ * actually discriminates.
64
+ *
65
+ * Requires BOTH controls:
66
+ * - at least one `'reject'` case — without it a gate that returns `true`
67
+ * unconditionally is indistinguishable from a working one. This is the
68
+ * control that all four failures above were missing.
69
+ * - at least one `'accept'` case — without it a gate that refuses everything
70
+ * scores perfectly, and an unsatisfiable gate does not stop bad work, it
71
+ * selects for invented work (measured: 38 fabricated citations written to
72
+ * clear a coverage gate no honest answer could satisfy).
73
+ */
74
+ declare function calibrateGate<TInput>(gate: GateFn<TInput>, cases: readonly CalibrationCase<TInput>[]): Promise<CalibrationReport>;
75
+ /**
76
+ * `calibrateGate`, but throws instead of reporting. Use in a test or at wiring
77
+ * time so an uncalibrated gate cannot ship silently.
78
+ */
79
+ declare function assertGateDiscriminates<TInput>(name: string, gate: GateFn<TInput>, cases: readonly CalibrationCase<TInput>[]): Promise<CalibrationReport>;
80
+ interface ProbeReport<TValue> {
81
+ /** True when the positive control registered something, so a zero is real. */
82
+ readonly canSee: boolean;
83
+ readonly measured: number;
84
+ readonly control: number;
85
+ readonly value: TValue;
86
+ readonly reason?: string;
87
+ }
88
+ /**
89
+ * Measure something, but only after proving the instrument can register a
90
+ * non-zero — because an absence is a claim about the measurement first.
91
+ *
92
+ * Six blind probes were mistaken for real zeros in a single day: a SQL `LIKE`
93
+ * over a column the product encrypts (which equally returned 0 for `"text"`
94
+ * across every row — the tell), a `grep` run against worktrees on stale
95
+ * branches, a status-code comparison that never read the response bodies, a
96
+ * live event count taken from a run that was never dispatched, `rg --hidden`
97
+ * silently exiting 2 because `rg` was aliased to `grep`, and an intercepted
98
+ * `git show` returning an empty diff. Each nearly caused a wrong fix.
99
+ *
100
+ * `control` must count something that MUST exist. If it counts zero, the
101
+ * measurement is unusable regardless of what `measure` returned.
102
+ */
103
+ declare function measureWithControl<TValue>(opts: {
104
+ readonly measure: () => TValue | Promise<TValue>;
105
+ /** Must return a value whose count is non-zero, or the probe is blind. */
106
+ readonly control: () => TValue | Promise<TValue>;
107
+ readonly count: (value: TValue) => number;
108
+ /** Describes what the control counts, for the failure message. */
109
+ readonly controlLabel: string;
110
+ }): Promise<ProbeReport<TValue>>;
4
111
 
5
112
  /**
6
113
  * Eval — the app-shell BRIDGE to `@tangle-network/agent-eval`, not a reimpl.
@@ -47,4 +154,4 @@ declare function createTokenRecallChecker(opts?: {
47
154
  reason: string;
48
155
  }>;
49
156
 
50
- export { createTokenRecallChecker, producedFromToolEvents };
157
+ export { type CalibrationCase, type CalibrationOutcome, type CalibrationReport, type GateFn, type ProbeReport, assertGateDiscriminates, calibrateGate, createTokenRecallChecker, measureWithControl, producedFromToolEvents };
@@ -1,5 +1,46 @@
1
1
  // src/eval/index.ts
2
2
  import { verifyCompletion, extractProducedState, weightedComposite, createLlmCorrectnessChecker } from "@tangle-network/agent-eval";
3
+
4
+ // src/eval/calibration.ts
5
+ async function calibrateGate(gate, cases) {
6
+ const outcomes = [];
7
+ for (const c of cases) {
8
+ let actual;
9
+ let threw;
10
+ try {
11
+ actual = await gate(c.input) ? "accept" : "reject";
12
+ } catch (err) {
13
+ actual = "reject";
14
+ threw = err instanceof Error ? err.message : String(err);
15
+ }
16
+ outcomes.push({ label: c.label, expected: c.expect, actual, ok: actual === c.expect, ...threw ? { threw } : {} });
17
+ }
18
+ const failures = outcomes.filter((o) => !o.ok);
19
+ const hasNegative = cases.some((c) => c.expect === "reject");
20
+ const hasPositive = cases.some((c) => c.expect === "accept");
21
+ const reason = !hasNegative ? "no negative control: every case expects acceptance, so a gate that never refuses would score perfectly" : !hasPositive ? "no positive control: every case expects rejection, so a gate that refuses everything would score perfectly" : failures.length > 0 ? `${failures.length}/${outcomes.length} cases disagreed: ${failures.map((f) => `${f.label} expected ${f.expected}, got ${f.actual}`).join("; ")}` : void 0;
22
+ return { discriminates: reason === void 0, outcomes, failures, ...reason ? { reason } : {} };
23
+ }
24
+ async function assertGateDiscriminates(name, gate, cases) {
25
+ const report = await calibrateGate(gate, cases);
26
+ if (!report.discriminates) throw new Error(`gate "${name}" is not evidence \u2014 ${report.reason}`);
27
+ return report;
28
+ }
29
+ async function measureWithControl(opts) {
30
+ const value = await opts.measure();
31
+ const controlValue = await opts.control();
32
+ const measured = opts.count(value);
33
+ const control = opts.count(controlValue);
34
+ return control > 0 ? { canSee: true, measured, control, value } : {
35
+ canSee: false,
36
+ measured,
37
+ control,
38
+ value,
39
+ reason: `probe is blind: the positive control (${opts.controlLabel}) counted 0, so the measured ${measured} carries no information`
40
+ };
41
+ }
42
+
43
+ // src/eval/index.ts
3
44
  function producedFromToolEvents(events) {
4
45
  return events.map(
5
46
  (e) => e.type === "proposal_created" ? { type: "proposal_created", proposalId: e.proposalId, title: e.title, status: e.status, content: e.content } : { type: "artifact", artifactId: `vault:${e.path}`, name: e.path, uri: `vault://${e.path}`, mimeType: "text/markdown", content: e.content }
@@ -21,9 +62,12 @@ function createTokenRecallChecker(opts = {}) {
21
62
  };
22
63
  }
23
64
  export {
65
+ assertGateDiscriminates,
66
+ calibrateGate,
24
67
  createLlmCorrectnessChecker,
25
68
  createTokenRecallChecker,
26
69
  extractProducedState,
70
+ measureWithControl,
27
71
  producedFromToolEvents,
28
72
  verifyCompletion,
29
73
  weightedComposite
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/eval/index.ts"],"sourcesContent":["/**\n * Eval — the app-shell BRIDGE to `@tangle-network/agent-eval`, not a reimpl.\n *\n * The completion/scoring ENGINE lives in agent-eval (a peer dependency):\n * `verifyCompletion`, `extractProducedState`, `weightedComposite`,\n * `createLlmCorrectnessChecker`, and the `CompletionRequirement` / `TaskGold` /\n * `ProducedState` types — all re-exported here so a consumer has one import\n * root. This module adds only what agent-eval doesn't have and what is\n * app-shell-specific:\n *\n * 1. {@link producedFromToolEvents} — the bridge: turn the structured app-tool\n * side channel's `AppToolProducedEvent`s (from a tool runtime executor's\n * `onProduced`) into the `RuntimeEventLike`s agent-eval's\n * `extractProducedState` consumes. This is the one piece that knows about\n * the app-tool channel, so it belongs here, not in the engine.\n * 2. {@link createTokenRecallChecker} — a deterministic, no-LLM\n * `CorrectnessChecker` (agent-eval ships only the LLM one). For apps/tests\n * that gate completion without a judge call.\n *\n * Full campaigns (persona simulation, traces, scorecards, held-out gates) are\n * agent-eval's `runEvalCampaign` / `AgentDriver` / `BenchmarkRunner` — use them\n * directly; this module composes with them.\n */\nimport type { RuntimeEventLike, CompletionRequirement } from '@tangle-network/agent-eval'\nimport type { AppToolProducedEvent } from '../tools/types'\n\n// Re-export the engine so consumers import completion + scoring from one place.\nexport { verifyCompletion, extractProducedState, weightedComposite, createLlmCorrectnessChecker } from '@tangle-network/agent-eval'\nexport type {\n CompletionRequirement,\n TaskGold,\n ProducedState,\n SatisfiedBy,\n CompletionVerdict,\n CorrectnessChecker,\n RuntimeEventLike,\n} from '@tangle-network/agent-eval'\n\n/**\n * Bridge the app-tool side channel's produced events into the runtime-event\n * shape agent-eval's `extractProducedState` reads. Pipe it:\n * `verifyCompletion(taskGold, extractProducedState(producedFromToolEvents(events)), checker)`\n */\nexport function producedFromToolEvents(events: readonly AppToolProducedEvent[]): RuntimeEventLike[] {\n return events.map((e) =>\n e.type === 'proposal_created'\n ? { type: 'proposal_created', proposalId: e.proposalId, title: e.title, status: e.status, content: e.content }\n : { type: 'artifact', artifactId: `vault:${e.path}`, name: e.path, uri: `vault://${e.path}`, mimeType: 'text/markdown', content: e.content },\n )\n}\n\nconst STOPWORDS = new Set(['the', 'a', 'an', 'and', 'or', 'for', 'to', 'of', 'in', 'on', 'with', 'review', 'update', 'new', 'proposed'])\n\n/**\n * A deterministic `CorrectnessChecker` (agent-eval exports only\n * `createLlmCorrectnessChecker`). A produced item fulfils a requirement when\n * its content is substantive and recalls ≥ `minRecall` of the requirement\n * title's significant tokens. No network — the default gate for apps/tests\n * without an LLM judge. Pass to `verifyCompletion` as the checker.\n */\nexport function createTokenRecallChecker(opts: { minRecall?: number; minContentLength?: number } = {}): (\n requirement: CompletionRequirement,\n content: string,\n) => Promise<{ correct: boolean; reason: string }> {\n const minRecall = opts.minRecall ?? 0.5\n const minLen = opts.minContentLength ?? 120\n return async (requirement, content) => {\n const body = content.trim()\n if (body.length < minLen) return { correct: false, reason: `content too thin (${body.length} chars) to be the deliverable` }\n const tokens = requirement.title.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !STOPWORDS.has(t))\n if (tokens.length === 0) return { correct: true, reason: 'requirement title has no significant tokens — structural match accepted' }\n const lower = body.toLowerCase()\n const hits = tokens.filter((t) => lower.includes(t)).length\n const recall = hits / tokens.length\n return recall >= minRecall\n ? { correct: true, reason: `content recalls ${hits}/${tokens.length} requirement tokens` }\n : { correct: false, reason: `content recalls only ${hits}/${tokens.length} requirement tokens` }\n }\n}\n"],"mappings":";AA2BA,SAAS,kBAAkB,sBAAsB,mBAAmB,mCAAmC;AAgBhG,SAAS,uBAAuB,QAA6D;AAClG,SAAO,OAAO;AAAA,IAAI,CAAC,MACjB,EAAE,SAAS,qBACP,EAAE,MAAM,oBAAoB,YAAY,EAAE,YAAY,OAAO,EAAE,OAAO,QAAQ,EAAE,QAAQ,SAAS,EAAE,QAAQ,IAC3G,EAAE,MAAM,YAAY,YAAY,SAAS,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,EAAE,IAAI,IAAI,UAAU,iBAAiB,SAAS,EAAE,QAAQ;AAAA,EAC/I;AACF;AAEA,IAAM,YAAY,oBAAI,IAAI,CAAC,OAAO,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,QAAQ,UAAU,UAAU,OAAO,UAAU,CAAC;AAShI,SAAS,yBAAyB,OAA0D,CAAC,GAGjD;AACjD,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,SAAS,KAAK,oBAAoB;AACxC,SAAO,OAAO,aAAa,YAAY;AACrC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,SAAS,OAAO,QAAQ,qBAAqB,KAAK,MAAM,gCAAgC;AAC3H,UAAM,SAAS,YAAY,MAAM,YAAY,EAAE,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;AAClH,QAAI,OAAO,WAAW,EAAG,QAAO,EAAE,SAAS,MAAM,QAAQ,+EAA0E;AACnI,UAAM,QAAQ,KAAK,YAAY;AAC/B,UAAM,OAAO,OAAO,OAAO,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE;AACrD,UAAM,SAAS,OAAO,OAAO;AAC7B,WAAO,UAAU,YACb,EAAE,SAAS,MAAM,QAAQ,mBAAmB,IAAI,IAAI,OAAO,MAAM,sBAAsB,IACvF,EAAE,SAAS,OAAO,QAAQ,wBAAwB,IAAI,IAAI,OAAO,MAAM,sBAAsB;AAAA,EACnG;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/eval/index.ts","../../src/eval/calibration.ts"],"sourcesContent":["/**\n * Eval — the app-shell BRIDGE to `@tangle-network/agent-eval`, not a reimpl.\n *\n * The completion/scoring ENGINE lives in agent-eval (a peer dependency):\n * `verifyCompletion`, `extractProducedState`, `weightedComposite`,\n * `createLlmCorrectnessChecker`, and the `CompletionRequirement` / `TaskGold` /\n * `ProducedState` types — all re-exported here so a consumer has one import\n * root. This module adds only what agent-eval doesn't have and what is\n * app-shell-specific:\n *\n * 1. {@link producedFromToolEvents} — the bridge: turn the structured app-tool\n * side channel's `AppToolProducedEvent`s (from a tool runtime executor's\n * `onProduced`) into the `RuntimeEventLike`s agent-eval's\n * `extractProducedState` consumes. This is the one piece that knows about\n * the app-tool channel, so it belongs here, not in the engine.\n * 2. {@link createTokenRecallChecker} — a deterministic, no-LLM\n * `CorrectnessChecker` (agent-eval ships only the LLM one). For apps/tests\n * that gate completion without a judge call.\n *\n * Full campaigns (persona simulation, traces, scorecards, held-out gates) are\n * agent-eval's `runEvalCampaign` / `AgentDriver` / `BenchmarkRunner` — use them\n * directly; this module composes with them.\n */\nimport type { RuntimeEventLike, CompletionRequirement } from '@tangle-network/agent-eval'\nimport type { AppToolProducedEvent } from '../tools/types'\n\n// Re-export the engine so consumers import completion + scoring from one place.\nexport { verifyCompletion, extractProducedState, weightedComposite, createLlmCorrectnessChecker } from '@tangle-network/agent-eval'\n\n// Calibration: prove a gate can FAIL before believing it PASSED, and prove a\n// probe can SEE before believing the zero it reported. Domain-free, so it\n// calibrates a citation gate, a form audit, or a benchmark equally.\nexport { calibrateGate, assertGateDiscriminates, measureWithControl } from './calibration'\nexport type { CalibrationCase, CalibrationOutcome, CalibrationReport, GateFn, ProbeReport } from './calibration'\nexport type {\n CompletionRequirement,\n TaskGold,\n ProducedState,\n SatisfiedBy,\n CompletionVerdict,\n CorrectnessChecker,\n RuntimeEventLike,\n} from '@tangle-network/agent-eval'\n\n/**\n * Bridge the app-tool side channel's produced events into the runtime-event\n * shape agent-eval's `extractProducedState` reads. Pipe it:\n * `verifyCompletion(taskGold, extractProducedState(producedFromToolEvents(events)), checker)`\n */\nexport function producedFromToolEvents(events: readonly AppToolProducedEvent[]): RuntimeEventLike[] {\n return events.map((e) =>\n e.type === 'proposal_created'\n ? { type: 'proposal_created', proposalId: e.proposalId, title: e.title, status: e.status, content: e.content }\n : { type: 'artifact', artifactId: `vault:${e.path}`, name: e.path, uri: `vault://${e.path}`, mimeType: 'text/markdown', content: e.content },\n )\n}\n\nconst STOPWORDS = new Set(['the', 'a', 'an', 'and', 'or', 'for', 'to', 'of', 'in', 'on', 'with', 'review', 'update', 'new', 'proposed'])\n\n/**\n * A deterministic `CorrectnessChecker` (agent-eval exports only\n * `createLlmCorrectnessChecker`). A produced item fulfils a requirement when\n * its content is substantive and recalls ≥ `minRecall` of the requirement\n * title's significant tokens. No network — the default gate for apps/tests\n * without an LLM judge. Pass to `verifyCompletion` as the checker.\n */\nexport function createTokenRecallChecker(opts: { minRecall?: number; minContentLength?: number } = {}): (\n requirement: CompletionRequirement,\n content: string,\n) => Promise<{ correct: boolean; reason: string }> {\n const minRecall = opts.minRecall ?? 0.5\n const minLen = opts.minContentLength ?? 120\n return async (requirement, content) => {\n const body = content.trim()\n if (body.length < minLen) return { correct: false, reason: `content too thin (${body.length} chars) to be the deliverable` }\n const tokens = requirement.title.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !STOPWORDS.has(t))\n if (tokens.length === 0) return { correct: true, reason: 'requirement title has no significant tokens — structural match accepted' }\n const lower = body.toLowerCase()\n const hits = tokens.filter((t) => lower.includes(t)).length\n const recall = hits / tokens.length\n return recall >= minRecall\n ? { correct: true, reason: `content recalls ${hits}/${tokens.length} requirement tokens` }\n : { correct: false, reason: `content recalls only ${hits}/${tokens.length} requirement tokens` }\n }\n}\n","/**\n * Calibration — prove a gate can FAIL before believing that it PASSED, and\n * prove a probe can SEE before believing the zero it reported.\n *\n * Every expensive failure this package has shipped was a check that verified\n * its own input instead of the world, and reported green:\n *\n * - `quote_verification 16/16` passed a work product whose nine citations\n * claimed a figure the cited document never mentions.\n * - `audit_form passed=3 failed=0` passed a form fill whose values landed in\n * \"Combat zone\" — the audit re-read the field paths the writer had just\n * invented, so a wrong path could not be wrong.\n * - `evidence_coverage 0/0` passed vacuously because the artifact it was\n * counting targets from was null.\n * - A benchmark reported 6/8 for the product while a bare\n * \"You are a helpful assistant.\" scored 8/8 on the same cases.\n *\n * A gate is only evidence if it REJECTS something. A metric is only evidence\n * if a worse system scores worse on it. Neither property is implied by a\n * green run, and neither is visible in the output — which is why both have to\n * be asserted separately, in code, next to the gate.\n *\n * This module is deliberately domain-free: a \"gate\" is any predicate over any\n * input. Products supply the known-good and known-bad cases, because only the\n * product knows what bad looks like in its domain.\n */\n\n/** A case whose verdict is known in advance, used to calibrate a gate. */\nexport interface CalibrationCase<TInput> {\n /** What this case represents, e.g. `'quote that does not occur in the source'`. */\n readonly label: string\n readonly input: TInput\n /** `'reject'` — the gate MUST refuse this. `'accept'` — it MUST allow it. */\n readonly expect: 'accept' | 'reject'\n}\n\nexport interface CalibrationOutcome {\n readonly label: string\n readonly expected: 'accept' | 'reject'\n readonly actual: 'accept' | 'reject'\n readonly ok: boolean\n /** Set when the gate threw; a throw counts as `'reject'`. */\n readonly threw?: string\n}\n\nexport interface CalibrationReport {\n /** True only when every case matched AND both controls were present. */\n readonly discriminates: boolean\n readonly outcomes: readonly CalibrationOutcome[]\n readonly failures: readonly CalibrationOutcome[]\n /** Why the gate is not trustworthy. Absent when `discriminates` is true. */\n readonly reason?: string\n}\n\n/**\n * A gate under calibration. Returning `false` OR throwing both count as a\n * rejection — a fail-loud gate (`ToolInputError`) and a boolean gate calibrate\n * through the same path.\n */\nexport type GateFn<TInput> = (input: TInput) => boolean | Promise<boolean>\n\n/**\n * Run a gate against cases whose verdicts are known, and report whether it\n * actually discriminates.\n *\n * Requires BOTH controls:\n * - at least one `'reject'` case — without it a gate that returns `true`\n * unconditionally is indistinguishable from a working one. This is the\n * control that all four failures above were missing.\n * - at least one `'accept'` case — without it a gate that refuses everything\n * scores perfectly, and an unsatisfiable gate does not stop bad work, it\n * selects for invented work (measured: 38 fabricated citations written to\n * clear a coverage gate no honest answer could satisfy).\n */\nexport async function calibrateGate<TInput>(\n gate: GateFn<TInput>,\n cases: readonly CalibrationCase<TInput>[],\n): Promise<CalibrationReport> {\n const outcomes: CalibrationOutcome[] = []\n for (const c of cases) {\n let actual: 'accept' | 'reject'\n let threw: string | undefined\n try {\n actual = (await gate(c.input)) ? 'accept' : 'reject'\n } catch (err) {\n actual = 'reject'\n threw = err instanceof Error ? err.message : String(err)\n }\n outcomes.push({ label: c.label, expected: c.expect, actual, ok: actual === c.expect, ...(threw ? { threw } : {}) })\n }\n\n const failures = outcomes.filter((o) => !o.ok)\n const hasNegative = cases.some((c) => c.expect === 'reject')\n const hasPositive = cases.some((c) => c.expect === 'accept')\n\n const reason = !hasNegative\n ? 'no negative control: every case expects acceptance, so a gate that never refuses would score perfectly'\n : !hasPositive\n ? 'no positive control: every case expects rejection, so a gate that refuses everything would score perfectly'\n : failures.length > 0\n ? `${failures.length}/${outcomes.length} cases disagreed: ${failures.map((f) => `${f.label} expected ${f.expected}, got ${f.actual}`).join('; ')}`\n : undefined\n\n return { discriminates: reason === undefined, outcomes, failures, ...(reason ? { reason } : {}) }\n}\n\n/**\n * `calibrateGate`, but throws instead of reporting. Use in a test or at wiring\n * time so an uncalibrated gate cannot ship silently.\n */\nexport async function assertGateDiscriminates<TInput>(\n name: string,\n gate: GateFn<TInput>,\n cases: readonly CalibrationCase<TInput>[],\n): Promise<CalibrationReport> {\n const report = await calibrateGate(gate, cases)\n if (!report.discriminates) throw new Error(`gate \"${name}\" is not evidence — ${report.reason}`)\n return report\n}\n\nexport interface ProbeReport<TValue> {\n /** True when the positive control registered something, so a zero is real. */\n readonly canSee: boolean\n readonly measured: number\n readonly control: number\n readonly value: TValue\n readonly reason?: string\n}\n\n/**\n * Measure something, but only after proving the instrument can register a\n * non-zero — because an absence is a claim about the measurement first.\n *\n * Six blind probes were mistaken for real zeros in a single day: a SQL `LIKE`\n * over a column the product encrypts (which equally returned 0 for `\"text\"`\n * across every row — the tell), a `grep` run against worktrees on stale\n * branches, a status-code comparison that never read the response bodies, a\n * live event count taken from a run that was never dispatched, `rg --hidden`\n * silently exiting 2 because `rg` was aliased to `grep`, and an intercepted\n * `git show` returning an empty diff. Each nearly caused a wrong fix.\n *\n * `control` must count something that MUST exist. If it counts zero, the\n * measurement is unusable regardless of what `measure` returned.\n */\nexport async function measureWithControl<TValue>(opts: {\n readonly measure: () => TValue | Promise<TValue>\n /** Must return a value whose count is non-zero, or the probe is blind. */\n readonly control: () => TValue | Promise<TValue>\n readonly count: (value: TValue) => number\n /** Describes what the control counts, for the failure message. */\n readonly controlLabel: string\n}): Promise<ProbeReport<TValue>> {\n const value = await opts.measure()\n const controlValue = await opts.control()\n const measured = opts.count(value)\n const control = opts.count(controlValue)\n return control > 0\n ? { canSee: true, measured, control, value }\n : {\n canSee: false,\n measured,\n control,\n value,\n reason: `probe is blind: the positive control (${opts.controlLabel}) counted 0, so the measured ${measured} carries no information`,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { a as AppToolContext, b as AppToolName, c as AppToolDefinition } from './types-BCxK0wyS.js';
2
- import { T as ToolHeaderNames } from './auth-anc7mv2W.js';
1
+ import { A as AppToolContext, a as AppToolName, b as AppToolDefinition } from './types-DbU-oO5h.js';
2
+ import { T as ToolHeaderNames } from './auth-DJs6lfAs.js';
3
3
 
4
4
  /** Default route path each app tool is served at. A product mounts its routes
5
5
  * at these paths (or supplies its own via {@link BuildMcpServerOptions.paths}). */
@@ -1,6 +1,6 @@
1
1
  import { KeyProvisioner, KeyCrypto, WorkspaceKeyManager, WorkspaceKeyStore } from '../billing/index.js';
2
2
  import { KnowledgeStateAccessor } from '../knowledge/index.js';
3
- import { d as AppToolHandlers } from '../types-BCxK0wyS.js';
3
+ import { d as AppToolHandlers } from '../types-DbU-oO5h.js';
4
4
  import { KvLike } from '../web/index.js';
5
5
  import '@tangle-network/agent-eval';
6
6
 
@@ -4,9 +4,9 @@ import * as _tangle_network_agent_runtime from '@tangle-network/agent-runtime';
4
4
  import { ToolLoopMessage } from '@tangle-network/agent-runtime';
5
5
  export { RunToolLoopOptions as AppToolLoopOptions, ToolLoopAssistantToolCall as LoopAssistantToolCall, ToolLoopMessage as LoopMessage, ToolLoopCall as LoopToolCall, StreamToolLoopOptions as StreamAppToolLoopOptions, StreamToolLoopYield as StreamLoopYield, ToolLoopEvent, ToolLoopResult, ToolLoopStopReason, runToolLoop as runAppToolLoop, streamToolLoop as streamAppToolLoop } from '@tangle-network/agent-runtime';
6
6
  import { CertifiedProfile } from '@tangle-network/agent-runtime/intelligence';
7
- import { A as AppToolMcpServer } from '../mcp-D3qVzbE1.js';
8
- import '../types-BCxK0wyS.js';
9
- import '../auth-anc7mv2W.js';
7
+ import { A as AppToolMcpServer } from '../mcp-Dt4V4ZLT.js';
8
+ import '../types-DbU-oO5h.js';
9
+ import '../auth-DJs6lfAs.js';
10
10
 
11
11
  /**
12
12
  * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.
@@ -1,8 +1,8 @@
1
1
  import { SandboxInstance, ProvisionEvent, StorageConfig, ScopedTokenScope, TurnDriveResult, Sandbox, MintScopedTokenOptions } from '@tangle-network/sandbox';
2
2
  export { StorageConfig } from '@tangle-network/sandbox';
3
3
  import { AgentProfileMcpServer, AgentProfileFileMount, AgentProfile } from '@tangle-network/agent-interface';
4
- import { T as ToolHeaderNames } from '../auth-anc7mv2W.js';
5
- import { b as AppToolName, a as AppToolContext } from '../types-BCxK0wyS.js';
4
+ import { T as ToolHeaderNames } from '../auth-DJs6lfAs.js';
5
+ import { a as AppToolName, A as AppToolContext } from '../types-DbU-oO5h.js';
6
6
  import { Harness } from '../harness/index.js';
7
7
  import { a as TangleExecutionEnvironment } from '../model-CdCDfBA9.js';
8
8
  import { b as ProfileFingerprint, C as ComposeProfileBudget } from '../budget-BOucfcb_.js';
@@ -840,6 +840,9 @@ declare function attachReasoningEffort(profile: AgentProfile, harness: Harness,
840
840
  interface StreamSandboxPromptOptions {
841
841
  sessionId?: string;
842
842
  executionId?: string;
843
+ /** Stable idempotency key for one logical dispatch. Reuse it with the same
844
+ * `sessionId` when a caller may retry the initial request. */
845
+ turnId?: string;
843
846
  lastEventId?: string;
844
847
  systemPrompt?: string;
845
848
  model?: string;
@@ -951,9 +954,6 @@ interface DriveSandboxTurnOptions extends StreamSandboxPromptOptions {
951
954
  * MUST reuse it so a crash + re-drive finds the in-flight session instead of
952
955
  * starting a second agent run. */
953
956
  sessionId: string;
954
- /** Turn idempotency key for the platform's completed-turn cache. Defaults to
955
- * `sessionId` (correct for the one-turn-per-session shape detached drivers use). */
956
- turnId?: string;
957
957
  /** Wall-clock cap in ms from the session's start. A still-running session past
958
958
  * the cap is cancelled and reported `failed` — bounds an unattended run (e.g. a
959
959
  * turn that stalled on an interactive question nothing will answer). Omit for no cap. */
@@ -63,7 +63,7 @@ import {
63
63
  verifySandboxTerminalToken,
64
64
  verifyTerminalProxyToken,
65
65
  writeProfileFilesToBox
66
- } from "../chunk-Q74BS43G.js";
66
+ } from "../chunk-7775L5NN.js";
67
67
  import "../chunk-LWSJK546.js";
68
68
  import "../chunk-CQZSAR77.js";
69
69
  import "../chunk-ICOHEZK6.js";
@@ -1,10 +1,10 @@
1
1
  import { j as SequenceMediaKind, f as SequenceTimeline, k as TimelineInterval, b as SequenceStore } from '../store-B7LLlk9p.js';
2
2
  export { M as MIN_SEQUENCE_CLIP_FRAMES, N as NewSequenceClip, l as NewSequenceDecision, m as NewSequenceTrack, g as SequenceClip, a as SequenceClipMedia, n as SequenceClipPatch, o as SequenceDecision, d as SequenceExportFormat, h as SequenceExportRecord, p as SequenceExportStatus, q as SequenceFrameSnapshot, i as SequenceMeta, r as SequenceStatus, S as SequenceStoreScope, e as SequenceTrack, c as SequenceTrackKind, T as TimelineClipBounds, s as assertClipFitsSequence, t as chooseCaptionPlacement, u as clampClipDuration, v as clampClipStart, w as formatSeconds, x as formatTimecode, y as framesToSeconds, z as secondsToFrames, A as snapshotFrame, B as trackIntervals } from '../store-B7LLlk9p.js';
3
3
  export { A as AddCaptionOperation, C as CaptionTargetResolution, a as CreateTrackOperation, D as DeleteClipOperation, E as ExtendSequenceOperation, M as MoveClipOperation, P as PlaceClipOperation, Q as QueueExportOperation, S as SEQUENCE_OPERATION_TYPES, b as SequenceApplyResult, c as SequenceOperation, d as SequenceOperationContext, e as SequenceOperationType, f as SequencePlan, g as SetClipDisabledOperation, h as SetClipTextOperation, i as SplitClipOperation, T as TrimClipOperation, j as applySequenceOperation, k as applySequenceOperations, l as assertSequenceMediaUrl, m as captionTrackNameForLanguage, n as lastClipEndFrame, p as parseSequenceOperations, r as resolveCaptionPlacement, o as resolveCaptionTarget, q as resolvePlaceClipTrack, v as validateAddCaption, s as validateCreateTrack, t as validateDeleteClip, u as validateExtendSequence, w as validateMoveClip, x as validatePlaceClip, y as validateQueueExport, z as validateSequenceOperation, B as validateSequenceOperations, F as validateSetClipDisabled, G as validateSetClipText, H as validateSplitClip, I as validateTrimClip } from '../apply-6wlMOLf8.js';
4
- import { S as ScopedMcpServerEntryOptions, A as AppToolMcpServer } from '../mcp-D3qVzbE1.js';
4
+ import { S as ScopedMcpServerEntryOptions, A as AppToolMcpServer } from '../mcp-Dt4V4ZLT.js';
5
5
  export { M as SEQUENCES_MCP_PROTOCOL_VERSIONS } from '../mcp-rpc-CzU5LWWT.js';
6
- import '../types-BCxK0wyS.js';
7
- import '../auth-anc7mv2W.js';
6
+ import '../types-DbU-oO5h.js';
7
+ import '../auth-DJs6lfAs.js';
8
8
 
9
9
  /**
10
10
  * Pure interchange-format builders over `SequenceTimeline` — SRT, WebVTT,
@@ -32,6 +32,14 @@ interface DatabaseProviderOptions {
32
32
  * existing wording so callers see a familiar message. */
33
33
  notReadyMessage?: string;
34
34
  }
35
+ /** A database driver that can execute related SQLite statements as one batch.
36
+ * Cloudflare D1 and libsql expose this method; portable local drivers may not. */
37
+ interface SqliteBatchDatabase {
38
+ batch?: (statements: [unknown, ...unknown[]]) => Promise<unknown[]>;
39
+ }
40
+ /** Execute related SQLite statements in one transactional driver batch when
41
+ * supported, or sequentially in the same order for portable local drivers. */
42
+ declare function runSqliteStatements(db: SqliteBatchDatabase, statements: [unknown, ...unknown[]]): Promise<unknown[]>;
35
43
  /**
36
44
  * Create a swappable database provider. `DB` is the injected instance's type
37
45
  * (e.g. a drizzle `Database`); the proxy is typed as `DB` so callers keep full
@@ -77,4 +85,4 @@ interface KVStore {
77
85
  */
78
86
  declare function createInMemoryKV(initial?: Record<string, string>): KVStore;
79
87
 
80
- export { type DatabaseProvider, type DatabaseProviderOptions, type KVGetWithMetadataResult, type KVListResult, type KVPutOptions, type KVStore, createDatabaseProvider, createInMemoryKV };
88
+ export { type DatabaseProvider, type DatabaseProviderOptions, type KVGetWithMetadataResult, type KVListResult, type KVPutOptions, type KVStore, type SqliteBatchDatabase, createDatabaseProvider, createInMemoryKV, runSqliteStatements };
@@ -1,57 +1,11 @@
1
- // src/store/index.ts
2
- function createDatabaseProvider(options = {}) {
3
- const message = options.notReadyMessage ?? "Database not initialized \u2014 call setDatabase() first.";
4
- let current = null;
5
- const db = new Proxy({}, {
6
- get(_target, prop) {
7
- if (!current) throw new Error(message);
8
- const value = current[prop];
9
- return typeof value === "function" ? value.bind(current) : value;
10
- },
11
- has(_target, prop) {
12
- return current !== null && prop in current;
13
- }
14
- });
15
- return {
16
- db,
17
- setDatabase(database) {
18
- current = database;
19
- },
20
- isReady() {
21
- return current !== null;
22
- },
23
- reset() {
24
- current = null;
25
- }
26
- };
27
- }
28
- function createInMemoryKV(initial) {
29
- const store = new Map(
30
- initial ? Object.entries(initial).map(([k, v]) => [k, { value: v, metadata: null }]) : []
31
- );
32
- return {
33
- async get(key) {
34
- return store.get(key)?.value ?? null;
35
- },
36
- async getWithMetadata(key) {
37
- const entry = store.get(key);
38
- return { value: entry?.value ?? null, metadata: entry?.metadata ?? null };
39
- },
40
- async put(key, value, options) {
41
- store.set(key, { value, metadata: options?.metadata ?? null });
42
- },
43
- async delete(key) {
44
- store.delete(key);
45
- },
46
- async list(options) {
47
- const prefix = options?.prefix ?? "";
48
- const keys = [...store.keys()].filter((k) => k.startsWith(prefix)).sort().map((name) => ({ name }));
49
- return { keys, list_complete: true };
50
- }
51
- };
52
- }
1
+ import {
2
+ createDatabaseProvider,
3
+ createInMemoryKV,
4
+ runSqliteStatements
5
+ } from "../chunk-LRHVCVEW.js";
53
6
  export {
54
7
  createDatabaseProvider,
55
- createInMemoryKV
8
+ createInMemoryKV,
9
+ runSqliteStatements
56
10
  };
57
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/store/index.ts"],"sourcesContent":["/**\n * Swappable database provider — the seam that decouples the agent's persistence\n * from any one driver.\n *\n * The agent core (and the app's server modules) import a single `db` and use it\n * directly. That `db` is a lazy proxy: it forwards to whatever database instance\n * the runtime injects via {@link DatabaseProvider.setDatabase}. So the SAME core\n * runs on:\n * - Cloudflare D1 (`setDatabase(drizzle(d1, schema))`) — prod\n * - SQLite / miniflare (`setDatabase(drizzle(betterSqlite, schema))`) — eval / the portable inner shell\n * - libsql / Turso, Postgres (`setDatabase(drizzle(client, schema))`) — a future hosted DB\n *\n * Adding a new database is one adapter (a drizzle instance over a new driver) +\n * a `setDatabase` call. None of the modules importing `db` change. Substrate-\n * free and driver-agnostic: this module knows nothing about D1, drizzle, or any\n * schema — it only forwards property access to the injected instance.\n */\n\nexport interface DatabaseProvider<DB> {\n /** The injected database, as a lazy proxy. Throws (with `notReadyMessage`)\n * on any access before {@link setDatabase} is called. */\n readonly db: DB\n /** Inject the active database instance (any driver's client). */\n setDatabase(database: DB): void\n /** True once a database has been injected. */\n isReady(): boolean\n /** Clear the injected database (next access throws again). Mainly for tests. */\n reset(): void\n}\n\n/** Define options for configuring database provider behavior including error messaging */\nexport interface DatabaseProviderOptions {\n /** Error thrown when `db` is accessed before injection. Keep the product's\n * existing wording so callers see a familiar message. */\n notReadyMessage?: string\n}\n\n/**\n * Create a swappable database provider. `DB` is the injected instance's type\n * (e.g. a drizzle `Database`); the proxy is typed as `DB` so callers keep full\n * typing and their existing query syntax.\n */\nexport function createDatabaseProvider<DB extends object>(\n options: DatabaseProviderOptions = {},\n): DatabaseProvider<DB> {\n const message = options.notReadyMessage ?? 'Database not initialized — call setDatabase() first.'\n let current: DB | null = null\n\n const db = new Proxy({} as DB, {\n get(_target, prop) {\n if (!current) throw new Error(message)\n const value = (current as Record<string | symbol, unknown>)[prop]\n // Bind methods to the real instance so `this` resolves correctly through\n // the proxy (works for drizzle's query builders and class-based stores).\n return typeof value === 'function' ? (value as (...args: unknown[]) => unknown).bind(current) : value\n },\n has(_target, prop) {\n return current !== null && prop in (current as object)\n },\n })\n\n return {\n db,\n setDatabase(database: DB) {\n current = database\n },\n isReady() {\n return current !== null\n },\n reset() {\n current = null\n },\n }\n}\n\n// ── KV store port (the vault backend) ───────────────────────────────────────\n//\n// The vault (workspace files) is a key/value store. In production it's a\n// Cloudflare `KVNamespace`; the portable inner shell injects an in-memory (or\n// other) implementation. This is the subset of the KV API the vault uses —\n// `KVNamespace` satisfies it structurally, so prod passes the binding unchanged,\n// and `createInMemoryKV()` supplies the portable adapter for sandbox/eval.\n\n/** Describe the result of listing keys with completion status and optional pagination cursor */\nexport interface KVListResult {\n keys: { name: string }[]\n list_complete: boolean\n cursor?: string\n}\n\n/** Define options for storing a key-value pair with expiration and metadata settings */\nexport interface KVPutOptions {\n expiration?: number\n expirationTtl?: number\n metadata?: unknown\n}\n\n/** Resolve a key-value pair retrieval including its associated metadata and value */\nexport interface KVGetWithMetadataResult {\n value: string | null\n metadata: unknown | null\n}\n\n/** Define a key-value store interface for asynchronous data retrieval, storage, deletion, and listing */\nexport interface KVStore {\n get(key: string): Promise<string | null>\n /** Read a value with its stored metadata (e.g. the vault's encrypted/hasPII flags). */\n getWithMetadata(key: string): Promise<KVGetWithMetadataResult>\n put(key: string, value: string, options?: KVPutOptions): Promise<void>\n delete(key: string): Promise<void>\n list(options?: { prefix?: string; cursor?: string; limit?: number }): Promise<KVListResult>\n}\n\n/**\n * In-memory {@link KVStore} — the portable vault backend for sandbox/eval runs.\n * Backed by a Map; `list` returns all prefix-matched keys in one complete page\n * (no real pagination needed in-process). Seed with `initial` entries if useful.\n */\nexport function createInMemoryKV(initial?: Record<string, string>): KVStore {\n const store = new Map<string, { value: string; metadata: unknown }>(\n initial ? Object.entries(initial).map(([k, v]) => [k, { value: v, metadata: null }]) : [],\n )\n return {\n async get(key) {\n return store.get(key)?.value ?? null\n },\n async getWithMetadata(key) {\n const entry = store.get(key)\n return { value: entry?.value ?? null, metadata: entry?.metadata ?? null }\n },\n async put(key, value, options) {\n store.set(key, { value, metadata: options?.metadata ?? null })\n },\n async delete(key) {\n store.delete(key)\n },\n async list(options) {\n const prefix = options?.prefix ?? ''\n const keys = [...store.keys()]\n .filter((k) => k.startsWith(prefix))\n .sort()\n .map((name) => ({ name }))\n return { keys, list_complete: true }\n },\n }\n}\n"],"mappings":";AA0CO,SAAS,uBACd,UAAmC,CAAC,GACd;AACtB,QAAM,UAAU,QAAQ,mBAAmB;AAC3C,MAAI,UAAqB;AAEzB,QAAM,KAAK,IAAI,MAAM,CAAC,GAAS;AAAA,IAC7B,IAAI,SAAS,MAAM;AACjB,UAAI,CAAC,QAAS,OAAM,IAAI,MAAM,OAAO;AACrC,YAAM,QAAS,QAA6C,IAAI;AAGhE,aAAO,OAAO,UAAU,aAAc,MAA0C,KAAK,OAAO,IAAI;AAAA,IAClG;AAAA,IACA,IAAI,SAAS,MAAM;AACjB,aAAO,YAAY,QAAQ,QAAS;AAAA,IACtC;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,YAAY,UAAc;AACxB,gBAAU;AAAA,IACZ;AAAA,IACA,UAAU;AACR,aAAO,YAAY;AAAA,IACrB;AAAA,IACA,QAAQ;AACN,gBAAU;AAAA,IACZ;AAAA,EACF;AACF;AA6CO,SAAS,iBAAiB,SAA2C;AAC1E,QAAM,QAAQ,IAAI;AAAA,IAChB,UAAU,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,KAAK,CAAC,CAAC,IAAI,CAAC;AAAA,EAC1F;AACA,SAAO;AAAA,IACL,MAAM,IAAI,KAAK;AACb,aAAO,MAAM,IAAI,GAAG,GAAG,SAAS;AAAA,IAClC;AAAA,IACA,MAAM,gBAAgB,KAAK;AACzB,YAAM,QAAQ,MAAM,IAAI,GAAG;AAC3B,aAAO,EAAE,OAAO,OAAO,SAAS,MAAM,UAAU,OAAO,YAAY,KAAK;AAAA,IAC1E;AAAA,IACA,MAAM,IAAI,KAAK,OAAO,SAAS;AAC7B,YAAM,IAAI,KAAK,EAAE,OAAO,UAAU,SAAS,YAAY,KAAK,CAAC;AAAA,IAC/D;AAAA,IACA,MAAM,OAAO,KAAK;AAChB,YAAM,OAAO,GAAG;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,SAAS;AAClB,YAAM,SAAS,SAAS,UAAU;AAClC,YAAM,OAAO,CAAC,GAAG,MAAM,KAAK,CAAC,EAC1B,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC,EAClC,KAAK,EACL,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE;AAC3B,aAAO,EAAE,MAAM,eAAe,KAAK;AAAA,IACrC;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,8 +1,8 @@
1
- import { T as ToolHeaderNames } from '../auth-anc7mv2W.js';
2
- export { A as AuthenticateOptions, D as DEFAULT_HEADER_NAMES, a as ToolAuthResult, b as authenticateToolRequest, r as readToolArgs } from '../auth-anc7mv2W.js';
3
- import { e as AppToolTaxonomy, d as AppToolHandlers, c as AppToolDefinition, a as AppToolContext, A as AppToolProducedEvent, f as AppToolOutcome, b as AppToolName } from '../types-BCxK0wyS.js';
4
- export { g as APP_TOOL_NAMES, h as AddCitationArgs, i as AddCitationResult, B as BuildAppToolsOptions, O as OpenAIFunctionTool, R as RenderUiArgs, j as RenderUiResult, S as ScheduleFollowupArgs, k as ScheduleFollowupResult, l as SubmitProposalArgs, m as SubmitProposalResult, n as buildAppToolOpenAITools, o as customToolToOpenAI, p as defineAppTool, q as findCustomTool, r as isAppToolName } from '../types-BCxK0wyS.js';
5
- export { A as AppToolMcpServer, B as BuildHttpMcpServerOptions, a as BuildMcpServerOptions, D as DEFAULT_APP_TOOL_PATHS, S as ScopedMcpServerEntryOptions, b as buildAppToolMcpServer, c as buildHttpMcpServer, d as buildScopedMcpServerEntry } from '../mcp-D3qVzbE1.js';
1
+ import { T as ToolHeaderNames } from '../auth-DJs6lfAs.js';
2
+ export { A as AuthenticateOptions, D as DEFAULT_HEADER_NAMES, a as ToolAuthResult, b as authenticateToolRequest, r as readToolArgs } from '../auth-DJs6lfAs.js';
3
+ import { e as AppToolTaxonomy, d as AppToolHandlers, b as AppToolDefinition, A as AppToolContext, c as AppToolProducedEvent, f as AppToolOutcome, a as AppToolName } from '../types-DbU-oO5h.js';
4
+ export { g as APP_TOOL_NAMES, h as AddCitationArgs, i as AddCitationResult, B as BuildAppToolsOptions, O as OpenAIFunctionTool, R as RenderUiArgs, j as RenderUiResult, S as ScheduleFollowupArgs, k as ScheduleFollowupResult, l as SubmitProposalArgs, m as SubmitProposalResult, n as buildAppToolOpenAITools, o as customToolToOpenAI, p as defineAppTool, q as findCustomTool, r as isAppToolName } from '../types-DbU-oO5h.js';
5
+ export { A as AppToolMcpServer, B as BuildHttpMcpServerOptions, a as BuildMcpServerOptions, D as DEFAULT_APP_TOOL_PATHS, S as ScopedMcpServerEntryOptions, b as buildAppToolMcpServer, c as buildHttpMcpServer, d as buildScopedMcpServerEntry } from '../mcp-Dt4V4ZLT.js';
6
6
  export { C as CreateMcpToolHandlerOptions, M as MCP_PROTOCOL_VERSIONS, b as McpProtocolVersion, c as McpServerInfo, a as McpToolDefinition, d as createMcpToolHandler } from '../mcp-rpc-CzU5LWWT.js';
7
7
 
8
8
  /** A correctable bad-input error a tool handler throws; the HTTP layer maps it
@@ -219,4 +219,4 @@ type AppToolOutcome = {
219
219
  status?: number;
220
220
  };
221
221
 
222
- export { type AppToolProducedEvent as A, type BuildAppToolsOptions as B, type OpenAIFunctionTool as O, type RenderUiArgs as R, type ScheduleFollowupArgs as S, type AppToolContext as a, type AppToolName as b, type AppToolDefinition as c, type AppToolHandlers as d, type AppToolTaxonomy as e, type AppToolOutcome as f, APP_TOOL_NAMES as g, type AddCitationArgs as h, type AddCitationResult as i, type RenderUiResult as j, type ScheduleFollowupResult as k, type SubmitProposalArgs as l, type SubmitProposalResult as m, buildAppToolOpenAITools as n, customToolToOpenAI as o, defineAppTool as p, findCustomTool as q, isAppToolName as r };
222
+ export { type AppToolContext as A, type BuildAppToolsOptions as B, type OpenAIFunctionTool as O, type RenderUiArgs as R, type ScheduleFollowupArgs as S, type AppToolName as a, type AppToolDefinition as b, type AppToolProducedEvent as c, type AppToolHandlers as d, type AppToolTaxonomy as e, type AppToolOutcome as f, APP_TOOL_NAMES as g, type AddCitationArgs as h, type AddCitationResult as i, type RenderUiResult as j, type ScheduleFollowupResult as k, type SubmitProposalArgs as l, type SubmitProposalResult as m, buildAppToolOpenAITools as n, customToolToOpenAI as o, defineAppTool as p, findCustomTool as q, isAppToolName as r };
@@ -1,6 +1,6 @@
1
1
  import { a as WorkProductProvenance, d as WorkProductStorePort, e as WorkProductAuditEvent, b as WorkProductRecord, f as WorkProductArtifact, Q as QualityCheck, E as EvidenceEntry, g as ExceptionEntry, h as WorkProductStatus, c as WorkProductPersistedPart } from '../types-DB82fktc.js';
2
2
  export { A as AgentCheckInput, i as EvidenceLocator, j as EvidenceSpan, k as ExceptionSeverity, P as ProfileBacktestSummary, l as QuoteBasis, m as WorkProductParseResult, n as WorkProductPatch, W as WorkProductRef, o as WorkProductUpdateGuard, p as WorkProductVersionEntry, q as isWorkProductStatus, r as parseAgentCheckInput, s as parseArtifactInput, t as parseEvidenceInput, u as parseExceptionInput, v as persistedPartToWorkProduct, w as unresolvedBlockingExceptions, x as workProductToPersistedPart } from '../types-DB82fktc.js';
3
- import { a as AppToolContext, c as AppToolDefinition } from '../types-BCxK0wyS.js';
3
+ import { A as AppToolContext, b as AppToolDefinition } from '../types-DbU-oO5h.js';
4
4
  import { JudgeVerdict } from '@tangle-network/agent-eval';
5
5
  import { T as TrustItem } from '../trust-gate-Dcm5xSva.js';
6
6
  export { R as ReviewQueueInputs, a as ReviewQueueItem, b as ReviewQueuePendingAsk, c as ReviewQueueState, d as ReviewQueueThread, p as parseReviewQueueItem, e as projectReviewQueue } from '../queue-vRI0Qx3X.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.44.26",
3
+ "version": "0.44.28",
4
4
  "packageManager": "pnpm@10.33.4",
5
5
  "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [