@martinloop/mcp 0.3.9 → 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 (96) hide show
  1. package/README.md +82 -141
  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 +47 -95
  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/preflight.d.ts +2 -0
  11. package/dist/tools/preflight.js +42 -2
  12. package/dist/tools/run-dossier.d.ts +3 -1
  13. package/dist/tools/run-dossier.js +40 -2
  14. package/dist/tools/run-loop.d.ts +1 -4
  15. package/dist/tools/run-loop.js +6 -15
  16. package/dist/tools/tool-support.d.ts +2 -1
  17. package/dist/tools/tool-support.js +1 -12
  18. package/dist/vendor/adapters/claude-cli.js +38 -35
  19. package/dist/vendor/adapters/cli-bridge.d.ts +9 -1
  20. package/dist/vendor/adapters/cli-bridge.js +54 -13
  21. package/dist/vendor/adapters/codex-launcher.d.ts +61 -1
  22. package/dist/vendor/adapters/codex-launcher.js +118 -19
  23. package/dist/vendor/adapters/index.d.ts +1 -3
  24. package/dist/vendor/adapters/index.js +1 -3
  25. package/dist/vendor/adapters/openai-compatible.js +51 -12
  26. package/dist/vendor/contracts/adapter-capabilities.d.ts +1 -1
  27. package/dist/vendor/contracts/context-handoff.d.ts +100 -0
  28. package/dist/vendor/contracts/context-handoff.js +11 -0
  29. package/dist/vendor/contracts/context-manifest.d.ts +195 -0
  30. package/dist/vendor/contracts/context-manifest.js +17 -0
  31. package/dist/vendor/contracts/context-shadow.d.ts +75 -0
  32. package/dist/vendor/contracts/context-shadow.js +8 -0
  33. package/dist/vendor/contracts/delivery.d.ts +47 -0
  34. package/dist/vendor/contracts/delivery.js +18 -0
  35. package/dist/vendor/contracts/exits.d.ts +126 -0
  36. package/dist/vendor/contracts/exits.js +32 -0
  37. package/dist/vendor/contracts/index.d.ts +41 -2
  38. package/dist/vendor/contracts/index.js +24 -0
  39. package/dist/vendor/contracts/mission.d.ts +102 -0
  40. package/dist/vendor/contracts/mission.js +64 -0
  41. package/dist/vendor/contracts/operator.d.ts +3 -1
  42. package/dist/vendor/contracts/operator.js +2 -1
  43. package/dist/vendor/contracts/verified-handoff.d.ts +100 -0
  44. package/dist/vendor/contracts/verified-handoff.js +35 -0
  45. package/dist/vendor/core/context-chain-gate.d.ts +89 -0
  46. package/dist/vendor/core/context-chain-gate.js +173 -0
  47. package/dist/vendor/core/context-compiler.d.ts +53 -0
  48. package/dist/vendor/core/context-compiler.js +261 -0
  49. package/dist/vendor/core/context-handoff.d.ts +42 -0
  50. package/dist/vendor/core/context-handoff.js +167 -0
  51. package/dist/vendor/core/context-shadow.d.ts +39 -0
  52. package/dist/vendor/core/context-shadow.js +200 -0
  53. package/dist/vendor/core/delivery/index.d.ts +6 -0
  54. package/dist/vendor/core/delivery/index.js +4 -0
  55. package/dist/vendor/core/delivery/message-client.d.ts +18 -0
  56. package/dist/vendor/core/delivery/message-client.js +38 -0
  57. package/dist/vendor/core/delivery/message-ledger.d.ts +35 -0
  58. package/dist/vendor/core/delivery/message-ledger.js +97 -0
  59. package/dist/vendor/core/delivery/message-schema.d.ts +16 -0
  60. package/dist/vendor/core/delivery/message-schema.js +111 -0
  61. package/dist/vendor/core/delivery/update-check.d.ts +19 -0
  62. package/dist/vendor/core/delivery/update-check.js +107 -0
  63. package/dist/vendor/core/exit-signal.d.ts +88 -0
  64. package/dist/vendor/core/exit-signal.js +339 -0
  65. package/dist/vendor/core/exits.d.ts +41 -0
  66. package/dist/vendor/core/exits.js +258 -0
  67. package/dist/vendor/core/index.d.ts +44 -1
  68. package/dist/vendor/core/index.js +1088 -662
  69. package/dist/vendor/core/mission/aggregate.d.ts +27 -0
  70. package/dist/vendor/core/mission/aggregate.js +42 -0
  71. package/dist/vendor/core/mission/index.d.ts +2 -0
  72. package/dist/vendor/core/mission/index.js +1 -0
  73. package/dist/vendor/core/persistence/compiler.d.ts +8 -0
  74. package/dist/vendor/core/persistence/compiler.js +156 -0
  75. package/dist/vendor/core/persistence/context-handoff-store.d.ts +68 -0
  76. package/dist/vendor/core/persistence/context-handoff-store.js +112 -0
  77. package/dist/vendor/core/persistence/index.d.ts +4 -0
  78. package/dist/vendor/core/persistence/index.js +2 -0
  79. package/dist/vendor/core/persistence/ledger.d.ts +1 -1
  80. package/dist/vendor/core/persistence/mission-store.d.ts +56 -0
  81. package/dist/vendor/core/persistence/mission-store.js +281 -0
  82. package/dist/vendor/core/policy.d.ts +2 -0
  83. package/dist/vendor/core/policy.js +5 -2
  84. package/dist/vendor/core/rollback.js +15 -1
  85. package/dist/vendor/core/savings.d.ts +13 -0
  86. package/dist/vendor/core/savings.js +22 -0
  87. package/dist/vendor/core/termination-store.d.ts +15 -0
  88. package/dist/vendor/core/termination-store.js +44 -0
  89. package/dist/vendor/core/verified-handoff.d.ts +67 -0
  90. package/dist/vendor/core/verified-handoff.js +210 -0
  91. package/package.json +3 -13
  92. package/server.json +3 -3
  93. package/dist/vendor/adapters/stub-agent-cli.d.ts +0 -8
  94. package/dist/vendor/adapters/stub-agent-cli.js +0 -40
  95. package/dist/vendor/adapters/verifier-only.d.ts +0 -9
  96. package/dist/vendor/adapters/verifier-only.js +0 -63
@@ -69,6 +69,23 @@ function estimateCost(model, inputChars, outputChars) {
69
69
  const actualUsd = (tokensIn / 1000) * pricing.inputPer1K + (tokensOut / 1000) * pricing.outputPer1K;
70
70
  return { tokensIn, tokensOut, actualUsd };
71
71
  }
72
+ function normalizeOpenAiCompatibleUsage(input) {
73
+ const hasKnownPricing = KNOWN_MODEL_PRICING[input.model] !== undefined;
74
+ const pricing = KNOWN_MODEL_PRICING[input.model] ?? {
75
+ inputPer1K: FALLBACK_INPUT_PER_1K,
76
+ outputPer1K: FALLBACK_OUTPUT_PER_1K
77
+ };
78
+ const actualUsd = (input.tokensIn / 1000) * pricing.inputPer1K +
79
+ (input.tokensOut / 1000) * pricing.outputPer1K;
80
+ const provenance = input.usageWasFullyProviderReported && hasKnownPricing ? "actual" : "estimated";
81
+ return normalizeUsage({
82
+ actualUsd,
83
+ ...(provenance === "estimated" ? { estimatedUsd: actualUsd } : {}),
84
+ tokensIn: input.tokensIn,
85
+ tokensOut: input.tokensOut,
86
+ provenance
87
+ });
88
+ }
72
89
  // ---------------------------------------------------------------------------
73
90
  // Prompt builder
74
91
  // ---------------------------------------------------------------------------
@@ -172,10 +189,12 @@ export function createOpenAiCompatibleAdapter(options) {
172
189
  // indicate a permanent configuration problem.
173
190
  const MAX_RETRIES = 3;
174
191
  const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);
192
+ const NON_RETRYABLE_STATUS = new Set([400, 401, 403]);
175
193
  const endpoint = `${baseUrl}/v1/chat/completions`;
176
194
  let responseText = "";
177
195
  let tokensIn = estimated.tokensIn;
178
196
  let tokensOut = 0;
197
+ let usageWasFullyProviderReported = false;
179
198
  const headers = { "Content-Type": "application/json" };
180
199
  if (apiKey)
181
200
  headers["Authorization"] = `Bearer ${apiKey}`;
@@ -207,6 +226,17 @@ export function createOpenAiCompatibleAdapter(options) {
207
226
  const body = (await res.json());
208
227
  if (!res.ok || body.error) {
209
228
  const errMsg = body.error?.message ?? `HTTP ${res.status}`;
229
+ // Fail immediately on non-retryable errors (auth, bad request)
230
+ if (NON_RETRYABLE_STATUS.has(res.status)) {
231
+ return {
232
+ status: "failed",
233
+ summary: `${model} API error: ${errMsg}`,
234
+ usage: normalizeUsage({ actualUsd: 0, tokensIn: 0, tokensOut: 0, provenance: "unavailable" }),
235
+ verification: { passed: false, summary: "API call failed before verifier." },
236
+ failure: { message: errMsg, classHint: "infrastructure_error" }
237
+ };
238
+ }
239
+ // Retry on transient errors
210
240
  if (RETRYABLE_STATUS.has(res.status) && attempt < MAX_RETRIES - 1) {
211
241
  lastError = errMsg;
212
242
  // Exponential backoff: 1s, 2s, 4s
@@ -223,8 +253,13 @@ export function createOpenAiCompatibleAdapter(options) {
223
253
  }
224
254
  responseText = body.choices?.[0]?.message?.content ?? "";
225
255
  if (body.usage) {
226
- tokensIn = body.usage.prompt_tokens ?? tokensIn;
227
- tokensOut = body.usage.completion_tokens ?? 0;
256
+ const providerPromptTokens = body.usage.prompt_tokens;
257
+ const providerCompletionTokens = body.usage.completion_tokens;
258
+ usageWasFullyProviderReported =
259
+ typeof providerPromptTokens === "number" &&
260
+ typeof providerCompletionTokens === "number";
261
+ tokensIn = providerPromptTokens ?? tokensIn;
262
+ tokensOut = providerCompletionTokens ?? Math.ceil(responseText.length / CHARS_PER_TOKEN);
228
263
  }
229
264
  else {
230
265
  tokensOut = Math.ceil(responseText.length / CHARS_PER_TOKEN);
@@ -267,33 +302,37 @@ export function createOpenAiCompatibleAdapter(options) {
267
302
  return {
268
303
  status: "failed",
269
304
  summary: `${model} returned an empty response.`,
270
- usage: normalizeUsage({ actualUsd: 0, tokensIn, tokensOut: 0, provenance: "actual" }),
305
+ usage: normalizeOpenAiCompatibleUsage({
306
+ model,
307
+ tokensIn,
308
+ tokensOut: 0,
309
+ usageWasFullyProviderReported
310
+ }),
271
311
  verification: { passed: false, summary: "Empty response — nothing to verify." },
272
312
  failure: { message: "empty_response" }
273
313
  };
274
314
  }
275
315
  // Run verification
276
- const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, request.context.verificationStack);
316
+ const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, request.context.verificationStack, undefined, {
317
+ runId: request.loopId,
318
+ workspaceId: request.workspaceId,
319
+ cwd: workingDirectory,
320
+ });
277
321
  const execution = {
278
322
  changedFiles: hasVerificationSteps
279
323
  ? (await readGitChangedFiles(workingDirectory, 5_000)).filter((file) => !baselineChangedFiles.has(file))
280
324
  : []
281
325
  };
282
- const pricing = KNOWN_MODEL_PRICING[model] ?? {
283
- inputPer1K: FALLBACK_INPUT_PER_1K,
284
- outputPer1K: FALLBACK_OUTPUT_PER_1K
285
- };
286
- const actualUsd = (tokensIn / 1000) * pricing.inputPer1K + (tokensOut / 1000) * pricing.outputPer1K;
287
326
  return {
288
327
  status: verification.passed ? "completed" : "failed",
289
328
  summary: verification.passed
290
329
  ? `${model} completed the task. Verifier passed.`
291
330
  : `${model} completed but verifier failed: ${verification.summary}`,
292
- usage: normalizeUsage({
293
- actualUsd,
331
+ usage: normalizeOpenAiCompatibleUsage({
332
+ model,
294
333
  tokensIn,
295
334
  tokensOut,
296
- provenance: "actual"
335
+ usageWasFullyProviderReported
297
336
  }),
298
337
  verification,
299
338
  execution,
@@ -1,5 +1,5 @@
1
1
  export type DiffVisibilityLevel = "none" | "git" | "adapter_reported";
2
- export type VerifierCompatibility = "full" | "verify_only" | "unsupported";
2
+ export type VerifierCompatibility = "full" | "proof" | "unsupported";
3
3
  export type SandboxExpectation = "host_process" | "workspace_write" | "provider_managed" | "not_applicable";
4
4
  export type LaunchReadiness = "path_lookup" | "configured_endpoint" | "built_in";
5
5
  export type AdapterUsageSettlement = "actual" | "estimated" | "unavailable";
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Context Handoff — A-CTX-2 contracts.
3
+ *
4
+ * Chain verification circuit breaker for upstream-to-downstream context handoff.
5
+ *
6
+ * Rule: a downstream agent MUST NOT execute before verifyContextHandoff returns ok=true.
7
+ * Healthy handoffs are silent. Broken handoffs stop execution and return one reason code
8
+ * plus one recovery instruction. Never silently discard excluded or unverifiable context.
9
+ */
10
+ /**
11
+ * Overall integrity state of the upstream chain.
12
+ * "verified" — all upstream receipts and artifacts check out
13
+ * "evidence_boundary" — upstream integrity could not be established (no receipt)
14
+ * "tamper_detected" — receipt hash or artifact hash does not match
15
+ * "incomplete" — required fields or artifacts are absent
16
+ * "unsupported_schema"— handoff schemaVersion is not recognised
17
+ */
18
+ export type ChainIntegrityState = "verified" | "evidence_boundary" | "tamper_detected" | "incomplete" | "unsupported_schema";
19
+ export type HandoffClaimState = "verified" | "unverified" | "rejected" | "unknown";
20
+ /** A single verifiable statement carried across the handoff boundary. */
21
+ export interface ContextHandoffClaim {
22
+ claimId: string;
23
+ statement: string;
24
+ evidenceRefs: string[];
25
+ verificationState: HandoffClaimState;
26
+ }
27
+ /** A content-addressed artifact that must be present and hash-stable across the boundary. */
28
+ export interface ContextHandoffArtifact {
29
+ path?: string;
30
+ sha256: string;
31
+ required: boolean;
32
+ label?: string;
33
+ }
34
+ export declare const HANDOFF_SCHEMA_VERSION: "martin.handoff.v1";
35
+ /**
36
+ * Immutable record produced by the upstream agent and verified before the
37
+ * downstream agent executes.
38
+ *
39
+ * upstreamIntegrity propagates from the parent handoff — it can never be
40
+ * upgraded by downstream success. A "tamper_detected" upstream remains
41
+ * "tamper_detected" regardless of what the current agent does.
42
+ */
43
+ export interface ContextHandoffReceipt {
44
+ schemaVersion: typeof HANDOFF_SCHEMA_VERSION;
45
+ handoffId: string;
46
+ chainId: string;
47
+ missionId?: string;
48
+ producerRunId: string;
49
+ /** SHA-256 of the upstream run's canonical receipt file. */
50
+ producerReceiptHash: string;
51
+ /** Parent handoff IDs for multi-hop chain lineage. */
52
+ parentHandoffIds?: string[];
53
+ claims: ContextHandoffClaim[];
54
+ artifacts: ContextHandoffArtifact[];
55
+ /** Free-text assumptions that have not yet been verified at handoff time. */
56
+ unresolvedAssumptions: string[];
57
+ upstreamIntegrity: "verified" | "evidence_boundary" | "tamper_detected" | "incomplete";
58
+ createdAt: string;
59
+ }
60
+ /**
61
+ * Safe ledger record for a context object that was denied before reaching
62
+ * executable context.
63
+ *
64
+ * MUST NOT contain the secret value or raw denied content — only safe
65
+ * identity, hashes, and reason codes.
66
+ */
67
+ export interface ContextExclusionDecision {
68
+ decision: "excluded";
69
+ objectId: string;
70
+ reasonCode: "secret_detected" | "policy_denied" | "authority_boundary" | "integrity_unverified";
71
+ reason: string;
72
+ /** SHA-256 of the denied content — never the content itself. */
73
+ sourceHash?: string;
74
+ excludedAt: string;
75
+ }
76
+ export interface ContextHandoffVerification {
77
+ ok: boolean;
78
+ integrity: ChainIntegrityState;
79
+ reasons: Array<{
80
+ code: string;
81
+ message: string;
82
+ claimId?: string;
83
+ artifactSha256?: string;
84
+ }>;
85
+ }
86
+ /**
87
+ * Decision produced by decideContextCircuitBreak.
88
+ *
89
+ * Healthy: { shouldStop: false, silent: true }
90
+ * Broken: { shouldStop: true, silent: false, reasonCode, message, nextAction }
91
+ */
92
+ export interface ContextCircuitBreakResult {
93
+ shouldStop: boolean;
94
+ /** true = healthy, no human-visible output; false = must surface to caller */
95
+ silent: boolean;
96
+ verification: ContextHandoffVerification;
97
+ reasonCode?: string;
98
+ message?: string;
99
+ nextAction?: string;
100
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Context Handoff — A-CTX-2 contracts.
3
+ *
4
+ * Chain verification circuit breaker for upstream-to-downstream context handoff.
5
+ *
6
+ * Rule: a downstream agent MUST NOT execute before verifyContextHandoff returns ok=true.
7
+ * Healthy handoffs are silent. Broken handoffs stop execution and return one reason code
8
+ * plus one recovery instruction. Never silently discard excluded or unverifiable context.
9
+ */
10
+ // ─── Receipt ──────────────────────────────────────────────────────────────────
11
+ export const HANDOFF_SCHEMA_VERSION = "martin.handoff.v1";
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Context Runtime — A-CTX-1 contracts.
3
+ *
4
+ * Defines the governed context layer: objects, budgets, manifests, ledger
5
+ * entries, fault protocol, and continuation checkpoints.
6
+ *
7
+ * Non-negotiable invariants (enforced in implementation, not type system):
8
+ * - No `confidence` float anywhere — use UsageEvidence enums only
9
+ * - No `outcome_delta` field anywhere — permanently banned
10
+ * - `nowMs` is always a captured input; never Date.now() inside a compiler
11
+ * - Secret scan hit → deny persistence entirely; reason-coded ledger entry only
12
+ * - Fault budget accumulates by taskId, NOT by runId
13
+ */
14
+ /**
15
+ * Evidence quality for a metric or measurement.
16
+ *
17
+ * "observed" = distinctive substring from the object appears verbatim in model output
18
+ * "inferred" = weak kind correlation, narrow and documented per kind
19
+ * "unknown" = default; metrics MUST be withheld, never reported as zero
20
+ */
21
+ export type UsageEvidence = "observed" | "inferred" | "unknown";
22
+ export type ContextKind = "mission" | "policy" | "security" | "task" | "continuation" | "working_diff" | "diagnostic" | "source" | "test" | "decision" | "todo" | "receipt_summary" | "tool_result";
23
+ export type ContextPriority = "required" | "high" | "normal" | "low";
24
+ export type ContextTrust = "authoritative" | "workspace" | "external" | "untrusted";
25
+ export type ContextSensitivity = "public" | "workspace" | "restricted" | "secret";
26
+ /** A typed, hashed, provenance-tracked object eligible for working-set inclusion. */
27
+ export interface ContextObject {
28
+ /** Stable identifier across revisions. */
29
+ id: string;
30
+ kind: ContextKind;
31
+ priority: ContextPriority;
32
+ trust: ContextTrust;
33
+ sensitivity: ContextSensitivity;
34
+ /** File path, receipt hash, or MCP URI — where the object originates. */
35
+ sourceRef: string;
36
+ /** SHA-256 of raw content. */
37
+ contentHash: string;
38
+ /** Git SHA or equivalent revision of the source file, if known. */
39
+ sourceRevisionHash?: string;
40
+ /** Pre-render token estimate used during selection. */
41
+ estimatedTokens: number;
42
+ /** Set iff the object was manually pinned; counts against pinnedTokensMax. */
43
+ pinnedBy?: string;
44
+ tags?: string[];
45
+ metadata?: Record<string, unknown>;
46
+ }
47
+ /**
48
+ * Token budget for one context compilation pass.
49
+ *
50
+ * pinnedTokensMax REQUIRED: caps manual-pin tokens to prevent deterministic
51
+ * selector starvation. Any pin that would exceed this cap must be rejected.
52
+ */
53
+ export interface ContextBudget {
54
+ modelWindowTokens: number;
55
+ systemReserveTokens: number;
56
+ outputReserveTokens: number;
57
+ toolReserveTokens: number;
58
+ overflowReserveTokens: number;
59
+ maxWorkingSetTokens: number;
60
+ /** Hard cap on manually-pinned token allocation. */
61
+ pinnedTokensMax: number;
62
+ }
63
+ /**
64
+ * The compiler's per-object inclusion/exclusion record.
65
+ *
66
+ * "included_truncated" is DISTINCT from "included" — truncation must be
67
+ * explicit in every receipt. Never use "included" for a truncated object.
68
+ */
69
+ export interface ContextCandidateDecision {
70
+ objectId: string;
71
+ decision: "included" | "included_truncated" | "excluded" | "deferred" | "fault_loaded";
72
+ /**
73
+ * Only present when decision === "included_truncated".
74
+ * Records the pre-truncation token count for receipts.
75
+ */
76
+ truncatedFromTokens?: number;
77
+ /** Machine-readable reason code for this decision. */
78
+ reason: string;
79
+ /** Actual post-render token count after adapter recount. Omitted pre-render. */
80
+ renderedTokens?: number;
81
+ }
82
+ export declare const CONTEXT_MANIFEST_VERSION: "context-manifest/1";
83
+ /**
84
+ * Immutable record of one context compilation pass.
85
+ *
86
+ * Determinism invariant:
87
+ * same inputs + policyHash + adapterVersion + nowMs → same manifestHash
88
+ *
89
+ * nowMs MUST be an explicit captured input — never Date.now() inside the
90
+ * compiler or rankContextObject.
91
+ */
92
+ export interface ContextManifest {
93
+ schemaVersion: typeof CONTEXT_MANIFEST_VERSION;
94
+ /** SHA-256 of (decisions + budget + policyHash + adapterVersion + nowMs). */
95
+ manifestHash: string;
96
+ taskId: string;
97
+ runId: string;
98
+ /** Explicit input — never Date.now() inside the compiler. */
99
+ nowMs: number;
100
+ adapterVersion: string;
101
+ policyHash: string;
102
+ budget: ContextBudget;
103
+ decisions: ContextCandidateDecision[];
104
+ totalRenderedTokens: number;
105
+ /** Render pass count; must be ≤ MAX_RENDER_PASSES (5). */
106
+ renderPasses: number;
107
+ compilationDurationMs: number;
108
+ }
109
+ export declare const CONTEXT_LEDGER_VERSION: "context-ledger/1";
110
+ /**
111
+ * Fourth ledger entry type — on the SAME receipt chain as cost and verification.
112
+ *
113
+ * FORBIDDEN: confidence field, outcome_delta field — both permanently banned.
114
+ * overflowCount and faultBudgetUsed accumulate by taskId, NOT by runId.
115
+ */
116
+ export interface ContextLedgerEntry {
117
+ schemaVersion: typeof CONTEXT_LEDGER_VERSION;
118
+ entryType: "context";
119
+ taskId: string;
120
+ runId: string;
121
+ manifestHash: string;
122
+ totalIncluded: number;
123
+ totalExcluded: number;
124
+ totalFaultLoaded: number;
125
+ /** Cumulative overflow count — resets per task, never per run. */
126
+ overflowCount: number;
127
+ /** Cumulative fault budget consumed — resets per task, never per run. */
128
+ faultBudgetUsed: number;
129
+ adapterVersion: string;
130
+ }
131
+ /** Request to demand-load a missing context object on fault. */
132
+ export interface ContextFaultRequest {
133
+ objectId: string;
134
+ taskId: string;
135
+ runId: string;
136
+ reason: string;
137
+ }
138
+ /** Result of a context fault resolution attempt. */
139
+ export interface ContextFaultResult {
140
+ objectId: string;
141
+ status: "loaded" | "unavailable" | "secret_blocked" | "budget_exceeded";
142
+ /** Post-render token count when status === "loaded". */
143
+ renderedTokens?: number;
144
+ ledgerEventEmitted: boolean;
145
+ }
146
+ /** Minimal task item tracked within a ContinuationCheckpoint. */
147
+ export interface TaskItem {
148
+ id: string;
149
+ description: string;
150
+ status: "pending" | "in_progress" | "completed" | "failed";
151
+ createdAtMs: number;
152
+ completedAtMs?: number;
153
+ }
154
+ /**
155
+ * Durable task state preserved outside the provider transcript.
156
+ *
157
+ * Write discipline (all enforced in CheckpointStore):
158
+ * - All writes MUST go through CheckpointStore.withTaskLock (CAS-locked).
159
+ * - Reject if store.read(taskId).sequenceNumber !== checkpoint.sequenceNumber - 1.
160
+ * - All array fields MUST be bounded before persistence.
161
+ * - Old completed entries MUST be compacted into completedSummaryHash.
162
+ *
163
+ * Testing requirement: two real concurrent Node processes, not jest mocks.
164
+ */
165
+ export interface ContinuationCheckpoint {
166
+ schemaVersion: "checkpoint/1";
167
+ taskId: string;
168
+ /** CAS lock key: reject write if stored sequenceNumber !== this - 1. */
169
+ sequenceNumber: number;
170
+ /** SHA-256 of the previous checkpoint — integrity chain. */
171
+ parentHash: string;
172
+ /** Old completed[] compacted into a hash-referenced summary to bound size. */
173
+ completedSummaryHash?: string;
174
+ pending: TaskItem[];
175
+ inProgress: TaskItem[];
176
+ decisions: ContextCandidateDecision[];
177
+ createdAtMs: number;
178
+ }
179
+ /**
180
+ * Governs what may enter the working set, how much, and why.
181
+ * policyHash must be included in every manifestHash computation.
182
+ */
183
+ export interface ContextPolicy {
184
+ policyHash: string;
185
+ /** Sensitivity levels that are unconditionally excluded. */
186
+ deniedSensitivities: ContextSensitivity[];
187
+ /** Trust levels that are unconditionally excluded. */
188
+ deniedTrustLevels: ContextTrust[];
189
+ /** Behaviour when required objects alone exceed budget. */
190
+ requiredOverBudgetAction: "explicit_escalation" | "fail_closed";
191
+ /** Maximum allowed compiler wall-clock duration in ms. */
192
+ maxCompilerDurationMs: number;
193
+ /** Maximum overhead ratio: compilationDurationMs / totalRenderedTokens. */
194
+ maxOverheadRatio: number;
195
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Context Runtime — A-CTX-1 contracts.
3
+ *
4
+ * Defines the governed context layer: objects, budgets, manifests, ledger
5
+ * entries, fault protocol, and continuation checkpoints.
6
+ *
7
+ * Non-negotiable invariants (enforced in implementation, not type system):
8
+ * - No `confidence` float anywhere — use UsageEvidence enums only
9
+ * - No `outcome_delta` field anywhere — permanently banned
10
+ * - `nowMs` is always a captured input; never Date.now() inside a compiler
11
+ * - Secret scan hit → deny persistence entirely; reason-coded ledger entry only
12
+ * - Fault budget accumulates by taskId, NOT by runId
13
+ */
14
+ // ─── ContextManifest ─────────────────────────────────────────────────────────
15
+ export const CONTEXT_MANIFEST_VERSION = "context-manifest/1";
16
+ // ─── ContextLedgerEntry ───────────────────────────────────────────────────────
17
+ export const CONTEXT_LEDGER_VERSION = "context-ledger/1";
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Context Shadow — A-CTX-0 schema contracts.
3
+ *
4
+ * Shadow mode observes the existing prompt without altering it.
5
+ * No source text is retained in manifests or receipts — only hashes and counts.
6
+ */
7
+ export declare const CONTEXT_SHADOW_MANIFEST_VERSION: "context-shadow-manifest/1";
8
+ export declare const CONTEXT_C5_VERSION: "context-c5/1";
9
+ export type ContextEvidence = "observed" | "configured" | "inferred" | "unknown";
10
+ export type ContextShadowSegmentKind = "system" | "mission" | "task" | "workspace" | "diagnostic" | "tool" | "other";
11
+ export interface ContextShadowSegmentInput {
12
+ segmentId: string;
13
+ kind: ContextShadowSegmentKind;
14
+ required: boolean;
15
+ text: string;
16
+ }
17
+ export interface ContextShadowDecisionV1 {
18
+ segmentId: string;
19
+ kind: string;
20
+ /** SHA-256 of segment.text — never the source text itself. */
21
+ contentHash: string;
22
+ estimatedTokens: number;
23
+ /** Always true in shadow mode — actual prompt is never altered. */
24
+ actuallyIncluded: true;
25
+ /** What the shadow compiler would have decided if it controlled selection. */
26
+ proposedDecision: "included" | "excluded";
27
+ reason: "required" | "within_shadow_budget" | "shadow_budget_exhausted" | "required_over_budget";
28
+ }
29
+ export interface ContextShadowManifestV1 {
30
+ schemaVersion: typeof CONTEXT_SHADOW_MANIFEST_VERSION;
31
+ manifestId: string;
32
+ /** SHA-256 of the canonical JSON of the manifest (excluding manifestId and manifestHash). */
33
+ manifestHash: string;
34
+ /** SHA-256 of the canonical compiler input, for cross-run determinism checks. */
35
+ compilerInputHash: string;
36
+ runId: string;
37
+ taskId?: string;
38
+ adapter: string;
39
+ model?: string;
40
+ capturedAt: string;
41
+ mode: "shadow";
42
+ modelWindowTokens: number | null;
43
+ modelWindowEvidence: ContextEvidence;
44
+ shadowBudgetTokens: number;
45
+ /** SHA-256 of framed source segments — confirms prompt identity without storing content. */
46
+ actualPromptHash: string;
47
+ /** Estimated token count of all segments (actual prompt). */
48
+ actualEstimatedTokens: number;
49
+ /** Estimated token count of shadow-selected segments only. */
50
+ proposedEstimatedTokens: number;
51
+ /** True if required segments alone exceed shadowBudgetTokens. */
52
+ requiredOverBudget: boolean;
53
+ decisions: ContextShadowDecisionV1[];
54
+ }
55
+ export interface ContextC5EnvelopeV1 {
56
+ schemaVersion: typeof CONTEXT_C5_VERSION;
57
+ resource: "context";
58
+ event: "shadow_compiled";
59
+ runId: string;
60
+ taskId?: string;
61
+ manifestId: string;
62
+ manifestHash: string;
63
+ adapter: string;
64
+ model?: string;
65
+ mode: "shadow";
66
+ actualEstimatedTokens: number;
67
+ proposedEstimatedTokens: number;
68
+ candidateCount: number;
69
+ includedCount: number;
70
+ excludedCount: number;
71
+ requiredOverBudget: boolean;
72
+ modelWindowTokens: number | null;
73
+ modelWindowEvidence: ContextEvidence;
74
+ createdAt: string;
75
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Context Shadow — A-CTX-0 schema contracts.
3
+ *
4
+ * Shadow mode observes the existing prompt without altering it.
5
+ * No source text is retained in manifests or receipts — only hashes and counts.
6
+ */
7
+ export const CONTEXT_SHADOW_MANIFEST_VERSION = "context-shadow-manifest/1";
8
+ export const CONTEXT_C5_VERSION = "context-c5/1";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * R4 Delivery — M1 Contract
3
+ *
4
+ * Shared with Lane B (Control Plane). Do not modify unilaterally.
5
+ * The server selects one message; the client never downloads the catalog.
6
+ */
7
+ export declare const DELIVERY_MESSAGE_SCHEMA_VERSION: "delivery-message/1";
8
+ export declare const DELIVERY_RECORD_SCHEMA_VERSION: "delivery-record/1";
9
+ export declare const MESSAGE_SELECTION_RESPONSE_SCHEMA_VERSION: "martin-message-selection/1";
10
+ export declare const ALLOWED_ACTION_TYPES: readonly ["upgrade_cli", "upgrade_mcp", "submit_feedback", "open_release_notes", "dismiss", "view_spend_report", "view_run_explain"];
11
+ export type ActionType = (typeof ALLOWED_ACTION_TYPES)[number];
12
+ export type MessageKind = "update" | "feedback_request" | "milestone";
13
+ export interface DeliveryMessage {
14
+ schemaVersion: typeof DELIVERY_MESSAGE_SCHEMA_VERSION;
15
+ id: string;
16
+ revision: number;
17
+ kind: MessageKind;
18
+ title: string;
19
+ body: string;
20
+ action: {
21
+ type: ActionType;
22
+ url?: string;
23
+ targetVersion?: string;
24
+ };
25
+ expiresAt: string;
26
+ cooldownHours: number;
27
+ }
28
+ export interface MessageSelectionResponse {
29
+ schemaVersion: typeof MESSAGE_SELECTION_RESPONSE_SCHEMA_VERSION;
30
+ message?: DeliveryMessage;
31
+ }
32
+ /** Local cooldown/dismissal state — persisted in .martin/delivery-record.json */
33
+ export interface DeliveryRecord {
34
+ schemaVersion: typeof DELIVERY_RECORD_SCHEMA_VERSION;
35
+ lastMessageId?: string;
36
+ lastShownAtEpochMs?: number;
37
+ dismissedIds: string[];
38
+ cooldownUntilEpochMs?: number;
39
+ cachedMessage?: DeliveryMessage;
40
+ cachedAtEpochMs?: number;
41
+ }
42
+ /** Structured update field for --json and MCP responses */
43
+ export interface UpdateAvailableField {
44
+ targetVersion: string;
45
+ kind: "cli" | "mcp";
46
+ message?: string;
47
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * R4 Delivery — M1 Contract
3
+ *
4
+ * Shared with Lane B (Control Plane). Do not modify unilaterally.
5
+ * The server selects one message; the client never downloads the catalog.
6
+ */
7
+ export const DELIVERY_MESSAGE_SCHEMA_VERSION = "delivery-message/1";
8
+ export const DELIVERY_RECORD_SCHEMA_VERSION = "delivery-record/1";
9
+ export const MESSAGE_SELECTION_RESPONSE_SCHEMA_VERSION = "martin-message-selection/1";
10
+ export const ALLOWED_ACTION_TYPES = [
11
+ "upgrade_cli",
12
+ "upgrade_mcp",
13
+ "submit_feedback",
14
+ "open_release_notes",
15
+ "dismiss",
16
+ "view_spend_report",
17
+ "view_run_explain",
18
+ ];