@martinloop/mcp 0.3.8 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +90 -106
  2. package/dist/discovery-metadata.d.ts +1 -1
  3. package/dist/discovery-metadata.js +1 -1
  4. package/dist/resources.d.ts +1 -2
  5. package/dist/resources.js +190 -238
  6. package/dist/server-validation.d.ts +1 -1
  7. package/dist/server-validation.js +16 -11
  8. package/dist/server.d.ts +23 -0
  9. package/dist/server.js +152 -7
  10. package/dist/tools/run-dossier.d.ts +3 -1
  11. package/dist/tools/run-dossier.js +40 -2
  12. package/dist/tools/run-loop.d.ts +1 -4
  13. package/dist/tools/run-loop.js +6 -15
  14. package/dist/tools/tool-support.d.ts +2 -1
  15. package/dist/tools/tool-support.js +1 -12
  16. package/dist/vendor/adapters/claude-cli.js +38 -35
  17. package/dist/vendor/adapters/cli-bridge.d.ts +9 -1
  18. package/dist/vendor/adapters/cli-bridge.js +54 -13
  19. package/dist/vendor/adapters/codex-launcher.d.ts +61 -1
  20. package/dist/vendor/adapters/codex-launcher.js +118 -19
  21. package/dist/vendor/adapters/index.d.ts +1 -3
  22. package/dist/vendor/adapters/index.js +1 -3
  23. package/dist/vendor/adapters/openai-compatible.js +57 -18
  24. package/dist/vendor/contracts/adapter-capabilities.d.ts +1 -1
  25. package/dist/vendor/contracts/context-handoff.d.ts +100 -0
  26. package/dist/vendor/contracts/context-handoff.js +11 -0
  27. package/dist/vendor/contracts/context-manifest.d.ts +195 -0
  28. package/dist/vendor/contracts/context-manifest.js +17 -0
  29. package/dist/vendor/contracts/context-shadow.d.ts +75 -0
  30. package/dist/vendor/contracts/context-shadow.js +8 -0
  31. package/dist/vendor/contracts/exits.d.ts +126 -0
  32. package/dist/vendor/contracts/exits.js +32 -0
  33. package/dist/vendor/contracts/index.d.ts +39 -2
  34. package/dist/vendor/contracts/index.js +22 -0
  35. package/dist/vendor/contracts/mission.d.ts +102 -0
  36. package/dist/vendor/contracts/mission.js +64 -0
  37. package/dist/vendor/contracts/operator.d.ts +3 -1
  38. package/dist/vendor/contracts/operator.js +2 -1
  39. package/dist/vendor/contracts/verified-handoff.d.ts +100 -0
  40. package/dist/vendor/contracts/verified-handoff.js +35 -0
  41. package/dist/vendor/core/context-chain-gate.d.ts +89 -0
  42. package/dist/vendor/core/context-chain-gate.js +173 -0
  43. package/dist/vendor/core/context-compiler.d.ts +53 -0
  44. package/dist/vendor/core/context-compiler.js +261 -0
  45. package/dist/vendor/core/context-handoff.d.ts +42 -0
  46. package/dist/vendor/core/context-handoff.js +167 -0
  47. package/dist/vendor/core/context-shadow.d.ts +39 -0
  48. package/dist/vendor/core/context-shadow.js +200 -0
  49. package/dist/vendor/core/exit-signal.d.ts +88 -0
  50. package/dist/vendor/core/exit-signal.js +339 -0
  51. package/dist/vendor/core/exits.d.ts +41 -0
  52. package/dist/vendor/core/exits.js +258 -0
  53. package/dist/vendor/core/index.d.ts +42 -1
  54. package/dist/vendor/core/index.js +1086 -662
  55. package/dist/vendor/core/mission/aggregate.d.ts +27 -0
  56. package/dist/vendor/core/mission/aggregate.js +42 -0
  57. package/dist/vendor/core/mission/index.d.ts +2 -0
  58. package/dist/vendor/core/mission/index.js +1 -0
  59. package/dist/vendor/core/persistence/compiler.d.ts +8 -0
  60. package/dist/vendor/core/persistence/compiler.js +156 -0
  61. package/dist/vendor/core/persistence/context-handoff-store.d.ts +68 -0
  62. package/dist/vendor/core/persistence/context-handoff-store.js +112 -0
  63. package/dist/vendor/core/persistence/index.d.ts +4 -0
  64. package/dist/vendor/core/persistence/index.js +2 -0
  65. package/dist/vendor/core/persistence/ledger.d.ts +1 -1
  66. package/dist/vendor/core/persistence/mission-store.d.ts +56 -0
  67. package/dist/vendor/core/persistence/mission-store.js +281 -0
  68. package/dist/vendor/core/policy.d.ts +2 -0
  69. package/dist/vendor/core/policy.js +5 -2
  70. package/dist/vendor/core/rollback.js +15 -1
  71. package/dist/vendor/core/savings.d.ts +13 -0
  72. package/dist/vendor/core/savings.js +22 -0
  73. package/dist/vendor/core/termination-store.d.ts +15 -0
  74. package/dist/vendor/core/termination-store.js +44 -0
  75. package/dist/vendor/core/verified-handoff.d.ts +67 -0
  76. package/dist/vendor/core/verified-handoff.js +210 -0
  77. package/package.json +76 -83
  78. package/server.json +21 -21
  79. package/LICENSE +0 -216
  80. package/dist/vendor/adapters/adapters/src/claude-cli.d.ts +0 -146
  81. package/dist/vendor/adapters/adapters/src/claude-cli.js +0 -1100
  82. package/dist/vendor/adapters/adapters/src/cli-bridge.d.ts +0 -103
  83. package/dist/vendor/adapters/adapters/src/cli-bridge.js +0 -569
  84. package/dist/vendor/adapters/adapters/src/codex-launcher.d.ts +0 -78
  85. package/dist/vendor/adapters/adapters/src/codex-launcher.js +0 -663
  86. package/dist/vendor/adapters/adapters/src/direct-provider.d.ts +0 -10
  87. package/dist/vendor/adapters/adapters/src/direct-provider.js +0 -40
  88. package/dist/vendor/adapters/adapters/src/index.d.ts +0 -8
  89. package/dist/vendor/adapters/adapters/src/index.js +0 -8
  90. package/dist/vendor/adapters/adapters/src/openai-compatible.d.ts +0 -62
  91. package/dist/vendor/adapters/adapters/src/openai-compatible.js +0 -306
  92. package/dist/vendor/adapters/adapters/src/runtime-support.d.ts +0 -17
  93. package/dist/vendor/adapters/adapters/src/runtime-support.js +0 -59
  94. package/dist/vendor/adapters/adapters/src/stub-agent-cli.d.ts +0 -8
  95. package/dist/vendor/adapters/adapters/src/stub-agent-cli.js +0 -40
  96. package/dist/vendor/adapters/adapters/src/stub-direct-provider.d.ts +0 -8
  97. package/dist/vendor/adapters/adapters/src/stub-direct-provider.js +0 -12
  98. package/dist/vendor/adapters/adapters/src/verifier-only.d.ts +0 -9
  99. package/dist/vendor/adapters/adapters/src/verifier-only.js +0 -63
  100. package/dist/vendor/adapters/contracts/src/context-graph.d.ts +0 -48
  101. package/dist/vendor/adapters/contracts/src/context-graph.js +0 -26
  102. package/dist/vendor/adapters/contracts/src/execution-policy.d.ts +0 -78
  103. package/dist/vendor/adapters/contracts/src/execution-policy.js +0 -43
  104. package/dist/vendor/adapters/contracts/src/governance.d.ts +0 -24
  105. package/dist/vendor/adapters/contracts/src/governance.js +0 -11
  106. package/dist/vendor/adapters/contracts/src/identity.d.ts +0 -29
  107. package/dist/vendor/adapters/contracts/src/identity.js +0 -30
  108. package/dist/vendor/adapters/contracts/src/index.d.ts +0 -412
  109. package/dist/vendor/adapters/contracts/src/index.js +0 -225
  110. package/dist/vendor/adapters/contracts/src/operator.d.ts +0 -19
  111. package/dist/vendor/adapters/contracts/src/operator.js +0 -11
  112. package/dist/vendor/adapters/contracts/src/trajectory.d.ts +0 -26
  113. package/dist/vendor/adapters/contracts/src/trajectory.js +0 -27
  114. package/dist/vendor/adapters/core/src/compiler.d.ts +0 -52
  115. package/dist/vendor/adapters/core/src/compiler.js +0 -52
  116. package/dist/vendor/adapters/core/src/context-graph.d.ts +0 -3
  117. package/dist/vendor/adapters/core/src/context-graph.js +0 -371
  118. package/dist/vendor/adapters/core/src/context-integrity.d.ts +0 -26
  119. package/dist/vendor/adapters/core/src/context-integrity.js +0 -80
  120. package/dist/vendor/adapters/core/src/grounding.d.ts +0 -38
  121. package/dist/vendor/adapters/core/src/grounding.js +0 -273
  122. package/dist/vendor/adapters/core/src/identity.d.ts +0 -20
  123. package/dist/vendor/adapters/core/src/identity.js +0 -86
  124. package/dist/vendor/adapters/core/src/index.d.ts +0 -186
  125. package/dist/vendor/adapters/core/src/index.js +0 -1495
  126. package/dist/vendor/adapters/core/src/leash.d.ts +0 -48
  127. package/dist/vendor/adapters/core/src/leash.js +0 -497
  128. package/dist/vendor/adapters/core/src/persistence/compiler.d.ts +0 -18
  129. package/dist/vendor/adapters/core/src/persistence/compiler.js +0 -34
  130. package/dist/vendor/adapters/core/src/persistence/index.d.ts +0 -10
  131. package/dist/vendor/adapters/core/src/persistence/index.js +0 -5
  132. package/dist/vendor/adapters/core/src/persistence/integrity.d.ts +0 -39
  133. package/dist/vendor/adapters/core/src/persistence/integrity.js +0 -252
  134. package/dist/vendor/adapters/core/src/persistence/ledger.d.ts +0 -23
  135. package/dist/vendor/adapters/core/src/persistence/ledger.js +0 -9
  136. package/dist/vendor/adapters/core/src/persistence/memory-store.d.ts +0 -63
  137. package/dist/vendor/adapters/core/src/persistence/memory-store.js +0 -115
  138. package/dist/vendor/adapters/core/src/persistence/runs-reader.d.ts +0 -66
  139. package/dist/vendor/adapters/core/src/persistence/runs-reader.js +0 -140
  140. package/dist/vendor/adapters/core/src/persistence/store.d.ts +0 -89
  141. package/dist/vendor/adapters/core/src/persistence/store.js +0 -125
  142. package/dist/vendor/adapters/core/src/persistence/trace-store.d.ts +0 -85
  143. package/dist/vendor/adapters/core/src/persistence/trace-store.js +0 -125
  144. package/dist/vendor/adapters/core/src/policy-compiler.d.ts +0 -2
  145. package/dist/vendor/adapters/core/src/policy-compiler.js +0 -195
  146. package/dist/vendor/adapters/core/src/policy.d.ts +0 -156
  147. package/dist/vendor/adapters/core/src/policy.js +0 -704
  148. package/dist/vendor/adapters/core/src/repo-analyzer.d.ts +0 -53
  149. package/dist/vendor/adapters/core/src/repo-analyzer.js +0 -263
  150. package/dist/vendor/adapters/core/src/rollback.d.ts +0 -15
  151. package/dist/vendor/adapters/core/src/rollback.js +0 -253
  152. package/dist/vendor/adapters/core/src/routing.d.ts +0 -82
  153. package/dist/vendor/adapters/core/src/routing.js +0 -336
  154. package/dist/vendor/adapters/core/src/test-integrity.d.ts +0 -64
  155. package/dist/vendor/adapters/core/src/test-integrity.js +0 -197
  156. package/dist/vendor/adapters/core/src/trajectory.d.ts +0 -18
  157. package/dist/vendor/adapters/core/src/trajectory.js +0 -295
  158. package/dist/vendor/adapters/stub-agent-cli.d.ts +0 -8
  159. package/dist/vendor/adapters/stub-agent-cli.js +0 -40
  160. package/dist/vendor/adapters/verifier-only.d.ts +0 -9
  161. package/dist/vendor/adapters/verifier-only.js +0 -63
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Context Compiler — A-CTX-1
3
+ *
4
+ * Deterministic working-set selector. Given a set of ContextObject candidates,
5
+ * a budget, a policy, and an explicit nowMs, produces a ContextManifest whose
6
+ * hash is identical for identical inputs on any platform.
7
+ *
8
+ * Shadow mode: the manifest is computed and emitted but the original provider
9
+ * packet is returned byte-for-byte unchanged.
10
+ *
11
+ * Standing invariants:
12
+ * - nowMs is ALWAYS a captured input — never Date.now() internally
13
+ * - No raw source text in manifest or ledger receipt — hashes and counts only
14
+ * - No confidence float, no outcome_delta — both permanently banned
15
+ * - Fault budget accumulates by taskId, not runId
16
+ * - required-over-budget is an explicit failure, never a silent drop
17
+ * - MAX_RENDER_PASSES = 5 — hard constant
18
+ */
19
+ import type { ContextBudget, ContextLedgerEntry, ContextManifest, ContextObject, ContextPolicy } from "../contracts/index.js";
20
+ export declare const MAX_RENDER_PASSES = 5;
21
+ /**
22
+ * Minimal adapter interface for token recounting.
23
+ * Implementations may call the provider SDK's token counter.
24
+ * Must not make network calls to the provider inference endpoint.
25
+ */
26
+ export interface ContextAdapter {
27
+ readonly version: string;
28
+ /** Recount the actual rendered tokens for a serialized text segment. */
29
+ recountTokens(text: string): number;
30
+ }
31
+ /** Fallback adapter: char÷4 heuristic, no external calls. */
32
+ export declare const HEURISTIC_ADAPTER: ContextAdapter;
33
+ export interface CompileContextInput {
34
+ taskId: string;
35
+ runId: string;
36
+ /** Must be a captured value — never call Date.now() inside the compiler. */
37
+ nowMs: number;
38
+ candidates: readonly ContextObject[];
39
+ budget: ContextBudget;
40
+ policy: ContextPolicy;
41
+ adapter: ContextAdapter;
42
+ }
43
+ export type CompileContextOutput = {
44
+ ok: true;
45
+ manifest: ContextManifest;
46
+ ledgerEntry: ContextLedgerEntry;
47
+ } | {
48
+ ok: false;
49
+ reason: "required_over_budget" | "recount_exceeded_passes";
50
+ manifest: null;
51
+ ledgerEntry: null;
52
+ };
53
+ export declare function compileContext(input: CompileContextInput): CompileContextOutput;
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Context Compiler — A-CTX-1
3
+ *
4
+ * Deterministic working-set selector. Given a set of ContextObject candidates,
5
+ * a budget, a policy, and an explicit nowMs, produces a ContextManifest whose
6
+ * hash is identical for identical inputs on any platform.
7
+ *
8
+ * Shadow mode: the manifest is computed and emitted but the original provider
9
+ * packet is returned byte-for-byte unchanged.
10
+ *
11
+ * Standing invariants:
12
+ * - nowMs is ALWAYS a captured input — never Date.now() internally
13
+ * - No raw source text in manifest or ledger receipt — hashes and counts only
14
+ * - No confidence float, no outcome_delta — both permanently banned
15
+ * - Fault budget accumulates by taskId, not runId
16
+ * - required-over-budget is an explicit failure, never a silent drop
17
+ * - MAX_RENDER_PASSES = 5 — hard constant
18
+ */
19
+ import { createHash } from "node:crypto";
20
+ import { CONTEXT_LEDGER_VERSION, CONTEXT_MANIFEST_VERSION } from "../contracts/index.js";
21
+ // ─── Constants ────────────────────────────────────────────────────────────────
22
+ export const MAX_RENDER_PASSES = 5;
23
+ /** Fallback adapter: char÷4 heuristic, no external calls. */
24
+ export const HEURISTIC_ADAPTER = {
25
+ version: "heuristic@1",
26
+ recountTokens: (text) => text.length === 0 ? 0 : Math.max(1, Math.ceil(Buffer.byteLength(text, "utf8") / 4))
27
+ };
28
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
29
+ function sha256(value) {
30
+ return createHash("sha256").update(value, "utf8").digest("hex");
31
+ }
32
+ function canonicalize(value) {
33
+ if (Array.isArray(value))
34
+ return value.map(canonicalize);
35
+ if (value !== null && typeof value === "object") {
36
+ return Object.fromEntries(Object.entries(value)
37
+ .sort(([a], [b]) => a.localeCompare(b))
38
+ .map(([k, v]) => [k, canonicalize(v)]));
39
+ }
40
+ return value;
41
+ }
42
+ function canonicalJson(value) {
43
+ return JSON.stringify(canonicalize(value));
44
+ }
45
+ /** Priority order for selection: required → pinned → high → normal → low */
46
+ const PRIORITY_RANK = {
47
+ required: 0,
48
+ high: 2,
49
+ normal: 3,
50
+ low: 4
51
+ };
52
+ /** Pinned objects slot between required and high. */
53
+ function selectionRank(obj) {
54
+ if (obj.priority === "required")
55
+ return 0;
56
+ if (obj.pinnedBy !== undefined)
57
+ return 1;
58
+ return PRIORITY_RANK[obj.priority];
59
+ }
60
+ // ─── Compiler ────────────────────────────────────────────────────────────────
61
+ export function compileContext(input) {
62
+ const { taskId, runId, nowMs, candidates, budget, policy, adapter } = input;
63
+ // ── Eligibility filter — policy-denied objects become governed exclusion records ──
64
+ //
65
+ // A-CTX-2 fix: denied objects must NOT silently disappear. They enter decisions[]
66
+ // as "excluded" with a reason code so the exclusion is visible as governed evidence.
67
+ // executableContext = admittedObjects only; ledgerDecisions = admitted + excluded.
68
+ const policyExcluded = [];
69
+ const eligibleList = [];
70
+ for (const obj of candidates) {
71
+ if (policy.deniedSensitivities.includes(obj.sensitivity)) {
72
+ policyExcluded.push({
73
+ objectId: obj.id,
74
+ decision: "excluded",
75
+ reason: `sensitivity_denied:${obj.sensitivity}`
76
+ });
77
+ }
78
+ else if (policy.deniedTrustLevels.includes(obj.trust)) {
79
+ policyExcluded.push({
80
+ objectId: obj.id,
81
+ decision: "excluded",
82
+ reason: `trust_denied:${obj.trust}`
83
+ });
84
+ }
85
+ else {
86
+ eligibleList.push(obj);
87
+ }
88
+ }
89
+ const eligible = eligibleList;
90
+ // ── Compute compilerInputHash ────────────────────────────────────────────
91
+ const inputFingerprint = {
92
+ taskId,
93
+ runId,
94
+ nowMs,
95
+ adapterVersion: adapter.version,
96
+ policyHash: policy.policyHash,
97
+ budget: {
98
+ modelWindowTokens: budget.modelWindowTokens,
99
+ maxWorkingSetTokens: budget.maxWorkingSetTokens,
100
+ pinnedTokensMax: budget.pinnedTokensMax
101
+ },
102
+ candidateIds: eligible.map((o) => o.id).sort()
103
+ };
104
+ // ── Pinned token cap ─────────────────────────────────────────────────────
105
+ const pinnedTokensTotal = eligible
106
+ .filter((o) => o.pinnedBy !== undefined)
107
+ .reduce((s, o) => s + o.estimatedTokens, 0);
108
+ if (pinnedTokensTotal > budget.pinnedTokensMax) {
109
+ // Fail-closed: reject the entire batch, not individual pins
110
+ return { ok: false, reason: "required_over_budget", manifest: null, ledgerEntry: null };
111
+ }
112
+ // ── Sort by selection rank, then objectId as stable tie-breaker ──────────
113
+ const sorted = [...eligible].sort((a, b) => {
114
+ const rankDiff = selectionRank(a) - selectionRank(b);
115
+ if (rankDiff !== 0)
116
+ return rankDiff;
117
+ // Stable tie-breaker: objectId lexicographic
118
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
119
+ });
120
+ // ── Selection pass ───────────────────────────────────────────────────────
121
+ let remaining = budget.maxWorkingSetTokens;
122
+ const decisions = [];
123
+ let requiredOverBudget = false;
124
+ // Required objects total token check first
125
+ const requiredTokens = sorted
126
+ .filter((o) => o.priority === "required")
127
+ .reduce((s, o) => s + o.estimatedTokens, 0);
128
+ if (requiredTokens > remaining) {
129
+ requiredOverBudget = true;
130
+ if (policy.requiredOverBudgetAction === "fail_closed") {
131
+ return { ok: false, reason: "required_over_budget", manifest: null, ledgerEntry: null };
132
+ }
133
+ // explicit_escalation: record and continue — all required marked excluded
134
+ }
135
+ for (const obj of sorted) {
136
+ const isRequired = obj.priority === "required";
137
+ if (requiredOverBudget && isRequired) {
138
+ decisions.push({
139
+ objectId: obj.id,
140
+ decision: "excluded",
141
+ reason: "required_over_budget"
142
+ });
143
+ continue;
144
+ }
145
+ if (obj.estimatedTokens <= remaining) {
146
+ decisions.push({
147
+ objectId: obj.id,
148
+ decision: "included",
149
+ reason: isRequired ? "required" : obj.pinnedBy !== undefined ? "pinned" : "within_budget"
150
+ });
151
+ remaining -= obj.estimatedTokens;
152
+ }
153
+ else {
154
+ // Attempt truncation for required/pinned, exclude otherwise
155
+ if (isRequired || obj.pinnedBy !== undefined) {
156
+ decisions.push({
157
+ objectId: obj.id,
158
+ decision: "included_truncated",
159
+ truncatedFromTokens: obj.estimatedTokens,
160
+ reason: isRequired ? "required_truncated" : "pinned_truncated"
161
+ });
162
+ // Truncated objects consume whatever is left
163
+ remaining = 0;
164
+ }
165
+ else {
166
+ decisions.push({
167
+ objectId: obj.id,
168
+ decision: "excluded",
169
+ reason: "budget_exhausted"
170
+ });
171
+ }
172
+ }
173
+ }
174
+ // ── Adapter recount (up to MAX_RENDER_PASSES) ────────────────────────────
175
+ const includedDecisions = decisions.filter((d) => d.decision === "included" || d.decision === "included_truncated");
176
+ let totalRenderedTokens = 0;
177
+ let renderPasses = 0;
178
+ let recountConverged = false;
179
+ const includedObjects = new Map(eligible.map((o) => [o.id, o]));
180
+ for (let pass = 0; pass < MAX_RENDER_PASSES; pass++) {
181
+ renderPasses = pass + 1;
182
+ let passTotal = 0;
183
+ for (const d of includedDecisions) {
184
+ const obj = includedObjects.get(d.objectId);
185
+ if (!obj)
186
+ continue;
187
+ // In shadow mode we use the contentHash as a proxy text for recounting
188
+ const recounted = adapter.recountTokens(obj.contentHash);
189
+ d.renderedTokens = recounted;
190
+ passTotal += recounted;
191
+ }
192
+ if (passTotal === totalRenderedTokens && pass > 0) {
193
+ recountConverged = true;
194
+ break;
195
+ }
196
+ totalRenderedTokens = passTotal;
197
+ if (pass === 0)
198
+ recountConverged = false;
199
+ }
200
+ if (!recountConverged && renderPasses >= MAX_RENDER_PASSES) {
201
+ return { ok: false, reason: "recount_exceeded_passes", manifest: null, ledgerEntry: null };
202
+ }
203
+ // ── Merge policy exclusions — excluded objects precede selection decisions ──
204
+ //
205
+ // A-CTX-2: executableContext = admittedObjects (decisions[]);
206
+ // ledgerDecisions = [...policyExcluded, ...decisions]
207
+ // Policy-excluded objects never enter the admitted working set, but their
208
+ // exclusion is visible as governed evidence in the manifest and ledger.
209
+ const allDecisions = [...policyExcluded, ...decisions];
210
+ // ── Manifest hash ────────────────────────────────────────────────────────
211
+ const manifestHashInput = canonicalJson({
212
+ adapterVersion: adapter.version,
213
+ budget: inputFingerprint.budget,
214
+ decisions: allDecisions.map((d) => ({
215
+ decision: d.decision,
216
+ objectId: d.objectId,
217
+ reason: d.reason,
218
+ ...(d.truncatedFromTokens !== undefined
219
+ ? { truncatedFromTokens: d.truncatedFromTokens }
220
+ : {})
221
+ })),
222
+ nowMs,
223
+ policyHash: policy.policyHash,
224
+ runId,
225
+ taskId,
226
+ totalRenderedTokens
227
+ });
228
+ const manifestHash = sha256(manifestHashInput);
229
+ const compilationDurationMs = 0; // In shadow mode: nowMs is frozen; duration is metadata-only
230
+ const manifest = {
231
+ schemaVersion: CONTEXT_MANIFEST_VERSION,
232
+ manifestHash,
233
+ taskId,
234
+ runId,
235
+ nowMs,
236
+ adapterVersion: adapter.version,
237
+ policyHash: policy.policyHash,
238
+ budget,
239
+ decisions: allDecisions,
240
+ totalRenderedTokens,
241
+ renderPasses,
242
+ compilationDurationMs
243
+ };
244
+ const totalIncluded = allDecisions.filter((d) => d.decision === "included" || d.decision === "included_truncated").length;
245
+ const totalExcluded = allDecisions.filter((d) => d.decision === "excluded").length;
246
+ const totalFaultLoaded = allDecisions.filter((d) => d.decision === "fault_loaded").length;
247
+ const ledgerEntry = {
248
+ schemaVersion: CONTEXT_LEDGER_VERSION,
249
+ entryType: "context",
250
+ taskId,
251
+ runId,
252
+ manifestHash,
253
+ totalIncluded,
254
+ totalExcluded,
255
+ totalFaultLoaded,
256
+ overflowCount: requiredOverBudget ? 1 : 0,
257
+ faultBudgetUsed: 0,
258
+ adapterVersion: adapter.version
259
+ };
260
+ return { ok: true, manifest, ledgerEntry };
261
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Context Handoff Verifier — A-CTX-2
3
+ *
4
+ * Pure functions for verifying upstream-to-downstream context handoffs and
5
+ * deciding whether to circuit-break before the downstream agent executes.
6
+ *
7
+ * Standing rules:
8
+ * - These functions are pure and side-effect-free.
9
+ * - verifyContextHandoff fails closed — any ambiguity → not ok.
10
+ * - decideContextCircuitBreak is the sole gate before adapter execution.
11
+ * - The downstream adapter MUST NOT be called when shouldStop is true.
12
+ */
13
+ import type { ContextCircuitBreakResult, ContextHandoffReceipt, ContextHandoffVerification } from "../contracts/index.js";
14
+ export interface VerifyContextHandoffInput {
15
+ handoff: ContextHandoffReceipt;
16
+ /** true when the producer receipt file hash has been independently confirmed */
17
+ producerReceiptVerified: boolean;
18
+ /** Map of sha256 → true for every artifact hash available to the verifier */
19
+ availableArtifacts: ReadonlyMap<string, true>;
20
+ }
21
+ /**
22
+ * Deterministically verifies a context handoff.
23
+ *
24
+ * Returns ok=false when ANY of the following is true:
25
+ * - schemaVersion is not in SUPPORTED_SCHEMAS
26
+ * - producerReceiptVerified is false
27
+ * - upstreamIntegrity is not "verified"
28
+ * - a required artifact is absent from availableArtifacts
29
+ * - an artifact sha256 does not appear in availableArtifacts (hash changed)
30
+ * - any required claim has state "unverified" or "rejected"
31
+ * - any claim has state "unknown"
32
+ * - unresolvedAssumptions is non-empty
33
+ * - handoffId, chainId, producerRunId, or producerReceiptHash is blank
34
+ */
35
+ export declare function verifyContextHandoff(input: VerifyContextHandoffInput): ContextHandoffVerification;
36
+ /**
37
+ * Converts a ContextHandoffVerification into a gate decision.
38
+ *
39
+ * Healthy: { shouldStop: false, silent: true }
40
+ * Broken: { shouldStop: true, silent: false, reasonCode, message, nextAction }
41
+ */
42
+ export declare function decideContextCircuitBreak(verification: ContextHandoffVerification): ContextCircuitBreakResult;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Context Handoff Verifier — A-CTX-2
3
+ *
4
+ * Pure functions for verifying upstream-to-downstream context handoffs and
5
+ * deciding whether to circuit-break before the downstream agent executes.
6
+ *
7
+ * Standing rules:
8
+ * - These functions are pure and side-effect-free.
9
+ * - verifyContextHandoff fails closed — any ambiguity → not ok.
10
+ * - decideContextCircuitBreak is the sole gate before adapter execution.
11
+ * - The downstream adapter MUST NOT be called when shouldStop is true.
12
+ */
13
+ import { HANDOFF_SCHEMA_VERSION } from "../contracts/index.js";
14
+ // ─── Supported schema versions ────────────────────────────────────────────────
15
+ const SUPPORTED_SCHEMAS = new Set([HANDOFF_SCHEMA_VERSION]);
16
+ /**
17
+ * Deterministically verifies a context handoff.
18
+ *
19
+ * Returns ok=false when ANY of the following is true:
20
+ * - schemaVersion is not in SUPPORTED_SCHEMAS
21
+ * - producerReceiptVerified is false
22
+ * - upstreamIntegrity is not "verified"
23
+ * - a required artifact is absent from availableArtifacts
24
+ * - an artifact sha256 does not appear in availableArtifacts (hash changed)
25
+ * - any required claim has state "unverified" or "rejected"
26
+ * - any claim has state "unknown"
27
+ * - unresolvedAssumptions is non-empty
28
+ * - handoffId, chainId, producerRunId, or producerReceiptHash is blank
29
+ */
30
+ export function verifyContextHandoff(input) {
31
+ const { handoff, producerReceiptVerified, availableArtifacts } = input;
32
+ const reasons = [];
33
+ // ── Schema ──────────────────────────────────────────────────────────────────
34
+ if (!SUPPORTED_SCHEMAS.has(handoff.schemaVersion)) {
35
+ return {
36
+ ok: false,
37
+ integrity: "unsupported_schema",
38
+ reasons: [
39
+ {
40
+ code: "unsupported_schema",
41
+ message: `Handoff schemaVersion "${handoff.schemaVersion}" is not supported. Supported: ${[...SUPPORTED_SCHEMAS].join(", ")}.`
42
+ }
43
+ ]
44
+ };
45
+ }
46
+ // ── Required identity fields ─────────────────────────────────────────────────
47
+ if (!isPresent(handoff.handoffId)) {
48
+ reasons.push({ code: "missing_handoff_id", message: "handoffId is required." });
49
+ }
50
+ if (!isPresent(handoff.chainId)) {
51
+ reasons.push({ code: "missing_chain_id", message: "chainId is required." });
52
+ }
53
+ if (!isPresent(handoff.producerRunId)) {
54
+ reasons.push({ code: "missing_producer_run_id", message: "producerRunId is required." });
55
+ }
56
+ if (!isPresent(handoff.producerReceiptHash)) {
57
+ reasons.push({ code: "missing_producer_receipt_hash", message: "producerReceiptHash is required." });
58
+ }
59
+ // ── Producer receipt verification ────────────────────────────────────────────
60
+ if (!producerReceiptVerified) {
61
+ reasons.push({
62
+ code: "producer_receipt_unverified",
63
+ message: "The producer receipt hash could not be independently verified."
64
+ });
65
+ }
66
+ // ── Upstream integrity propagation ───────────────────────────────────────────
67
+ if (handoff.upstreamIntegrity !== "verified") {
68
+ reasons.push({
69
+ code: `upstream_integrity_${handoff.upstreamIntegrity}`,
70
+ message: `Upstream integrity is "${handoff.upstreamIntegrity}" — cannot proceed.`
71
+ });
72
+ }
73
+ // ── Artifact verification ────────────────────────────────────────────────────
74
+ for (const artifact of handoff.artifacts) {
75
+ const present = availableArtifacts.has(artifact.sha256);
76
+ if (artifact.required && !present) {
77
+ reasons.push({
78
+ code: "missing_required_artifact",
79
+ message: `Required artifact is absent (sha256: ${artifact.sha256}${artifact.label ? `, label: ${artifact.label}` : ""}).`,
80
+ artifactSha256: artifact.sha256
81
+ });
82
+ }
83
+ else if (!present) {
84
+ reasons.push({
85
+ code: "artifact_hash_mismatch",
86
+ message: `Artifact hash not found in available set (sha256: ${artifact.sha256}${artifact.label ? `, label: ${artifact.label}` : ""}).`,
87
+ artifactSha256: artifact.sha256
88
+ });
89
+ }
90
+ }
91
+ // ── Claim verification ────────────────────────────────────────────────────────
92
+ for (const claim of handoff.claims) {
93
+ if (claim.verificationState === "rejected") {
94
+ reasons.push({
95
+ code: "claim_rejected",
96
+ message: `Claim "${claim.claimId}" was rejected: ${claim.statement}`,
97
+ claimId: claim.claimId
98
+ });
99
+ }
100
+ else if (claim.verificationState === "unverified") {
101
+ reasons.push({
102
+ code: "claim_unverified",
103
+ message: `Claim "${claim.claimId}" is unverified: ${claim.statement}`,
104
+ claimId: claim.claimId
105
+ });
106
+ }
107
+ else if (claim.verificationState === "unknown") {
108
+ reasons.push({
109
+ code: "claim_unknown",
110
+ message: `Claim "${claim.claimId}" has unknown verification state: ${claim.statement}`,
111
+ claimId: claim.claimId
112
+ });
113
+ }
114
+ }
115
+ // ── Unresolved assumptions ────────────────────────────────────────────────────
116
+ if (handoff.unresolvedAssumptions.length > 0) {
117
+ reasons.push({
118
+ code: "unresolved_assumptions",
119
+ message: `${handoff.unresolvedAssumptions.length} assumption(s) remain unresolved: ${handoff.unresolvedAssumptions.slice(0, 3).join("; ")}${handoff.unresolvedAssumptions.length > 3 ? " …" : ""}`
120
+ });
121
+ }
122
+ const ok = reasons.length === 0;
123
+ const integrity = ok ? "verified" : deriveIntegrity(handoff, reasons);
124
+ return { ok, integrity, reasons };
125
+ }
126
+ // ─── Circuit breaker ──────────────────────────────────────────────────────────
127
+ /**
128
+ * Converts a ContextHandoffVerification into a gate decision.
129
+ *
130
+ * Healthy: { shouldStop: false, silent: true }
131
+ * Broken: { shouldStop: true, silent: false, reasonCode, message, nextAction }
132
+ */
133
+ export function decideContextCircuitBreak(verification) {
134
+ if (verification.ok) {
135
+ return {
136
+ shouldStop: false,
137
+ silent: true,
138
+ verification
139
+ };
140
+ }
141
+ const first = verification.reasons[0];
142
+ return {
143
+ shouldStop: true,
144
+ silent: false,
145
+ verification,
146
+ reasonCode: first?.code ?? "handoff_verification_failed",
147
+ message: first?.message ?? "Context handoff verification failed.",
148
+ nextAction: "Repair or replace the upstream handoff receipt, then retry."
149
+ };
150
+ }
151
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
152
+ function isPresent(value) {
153
+ return typeof value === "string" && value.trim().length > 0;
154
+ }
155
+ function deriveIntegrity(handoff, reasons) {
156
+ const codes = reasons.map((r) => r.code);
157
+ if (codes.includes("unsupported_schema"))
158
+ return "unsupported_schema";
159
+ if (codes.some((c) => c === "upstream_integrity_tamper_detected" ||
160
+ c === "artifact_hash_mismatch")) {
161
+ return "tamper_detected";
162
+ }
163
+ if (codes.includes("producer_receipt_unverified") || codes.includes("upstream_integrity_evidence_boundary")) {
164
+ return "evidence_boundary";
165
+ }
166
+ return "incomplete";
167
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Context Shadow Compiler — A-CTX-0
3
+ *
4
+ * Deterministic shadow manifest emitter. Observes the compiled prompt and
5
+ * records what a context-aware selector would have chosen, without altering
6
+ * the actual provider input in any way.
7
+ *
8
+ * Invariants:
9
+ * - nowMs is always a captured input; Date.now() is never called internally
10
+ * - No source text appears in any output type
11
+ * - Required segments are always preferred first
12
+ * - required-over-budget is recorded, never thrown
13
+ * - Manifest hash covers metadata only (no content)
14
+ */
15
+ import type { ContextC5EnvelopeV1, ContextEvidence, ContextShadowManifestV1 } from "../contracts/index.js";
16
+ export interface ContextShadowSegment {
17
+ segmentId: string;
18
+ kind: string;
19
+ required: boolean;
20
+ text: string;
21
+ }
22
+ export interface CompileContextShadowInput {
23
+ runId: string;
24
+ taskId?: string;
25
+ adapter: string;
26
+ model?: string;
27
+ /** Milliseconds since epoch — must be explicit. Never call Date.now() here. */
28
+ nowMs: number;
29
+ shadowBudgetTokens: number;
30
+ modelWindowTokens?: number;
31
+ modelWindowEvidence?: ContextEvidence;
32
+ segments: readonly ContextShadowSegment[];
33
+ }
34
+ export interface CompileContextShadowResult {
35
+ manifest: ContextShadowManifestV1;
36
+ receipt: ContextC5EnvelopeV1;
37
+ }
38
+ export declare function estimateContextTokens(text: string): number;
39
+ export declare function compileContextShadow(input: CompileContextShadowInput): CompileContextShadowResult;