martin-loop 0.1.4 → 1.3.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.
- package/CODE_OF_CONDUCT.md +32 -0
- package/README.md +172 -227
- package/demo/seeded-workspace/README.md +35 -0
- package/demo/seeded-workspace/TASKS.md +29 -0
- package/demo/seeded-workspace/martin.config.yaml +11 -0
- package/demo/seeded-workspace/package.json +8 -0
- package/demo/seeded-workspace/src/invoice-summary.js +11 -0
- package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
- package/dist/bin/martin-loop.js +0 -0
- package/dist/vendor/adapters/claude-cli.d.ts +19 -4
- package/dist/vendor/adapters/claude-cli.js +55 -24
- package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
- package/dist/vendor/adapters/cli-bridge.js +154 -28
- package/dist/vendor/adapters/counter.d.ts +1 -0
- package/dist/vendor/adapters/counter.js +4 -0
- package/dist/vendor/adapters/git-baseline.d.ts +50 -0
- package/dist/vendor/adapters/git-baseline.js +233 -0
- package/dist/vendor/adapters/index.d.ts +1 -0
- package/dist/vendor/adapters/index.js +1 -0
- package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
- package/dist/vendor/adapters/openrouter-adapter.js +302 -0
- package/dist/vendor/adapters/usage.d.ts +48 -0
- package/dist/vendor/adapters/usage.js +66 -0
- package/dist/vendor/adapters/verifier-only.d.ts +7 -0
- package/dist/vendor/adapters/verifier-only.js +57 -0
- package/dist/vendor/cli/bin/exit.d.ts +12 -0
- package/dist/vendor/cli/bin/exit.js +28 -0
- package/dist/vendor/cli/commands/analyze.d.ts +5 -0
- package/dist/vendor/cli/commands/analyze.js +58 -0
- package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
- package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
- package/dist/vendor/cli/commands/audit.d.ts +8 -0
- package/dist/vendor/cli/commands/audit.js +199 -0
- package/dist/vendor/cli/commands/corpus.d.ts +5 -0
- package/dist/vendor/cli/commands/corpus.js +60 -0
- package/dist/vendor/cli/commands/doctor.d.ts +8 -0
- package/dist/vendor/cli/commands/doctor.js +219 -0
- package/dist/vendor/cli/commands/explain.d.ts +17 -0
- package/dist/vendor/cli/commands/explain.js +176 -0
- package/dist/vendor/cli/commands/export.d.ts +5 -0
- package/dist/vendor/cli/commands/export.js +60 -0
- package/dist/vendor/cli/commands/governance.d.ts +8 -0
- package/dist/vendor/cli/commands/governance.js +95 -0
- package/dist/vendor/cli/commands/improve.d.ts +18 -0
- package/dist/vendor/cli/commands/improve.js +396 -0
- package/dist/vendor/cli/commands/init.d.ts +8 -0
- package/dist/vendor/cli/commands/init.js +281 -0
- package/dist/vendor/cli/commands/migration.d.ts +8 -0
- package/dist/vendor/cli/commands/migration.js +67 -0
- package/dist/vendor/cli/commands/prior.d.ts +23 -0
- package/dist/vendor/cli/commands/prior.js +145 -0
- package/dist/vendor/cli/commands/resume.d.ts +21 -0
- package/dist/vendor/cli/commands/resume.js +73 -0
- package/dist/vendor/cli/commands/verify.d.ts +6 -0
- package/dist/vendor/cli/commands/verify.js +43 -0
- package/dist/vendor/cli/index.d.ts +6 -1
- package/dist/vendor/cli/index.js +124 -7
- package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
- package/dist/vendor/cli/research/public-corpus.js +151 -0
- package/dist/vendor/cli/ui/error-card.d.ts +38 -0
- package/dist/vendor/cli/ui/error-card.js +103 -0
- package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
- package/dist/vendor/cli/ui/mission-brief.js +173 -0
- package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
- package/dist/vendor/cli/ui/summary-card.js +102 -0
- package/dist/vendor/contracts/audit.d.ts +46 -0
- package/dist/vendor/contracts/audit.js +360 -0
- package/dist/vendor/contracts/index.d.ts +3 -1
- package/dist/vendor/contracts/post-phase15.d.ts +240 -0
- package/dist/vendor/contracts/post-phase15.js +166 -0
- package/dist/vendor/core/agent/mandates.d.ts +46 -0
- package/dist/vendor/core/agent/mandates.js +178 -0
- package/dist/vendor/core/agent/receipts.d.ts +38 -0
- package/dist/vendor/core/agent/receipts.js +131 -0
- package/dist/vendor/core/agent/signing.d.ts +17 -0
- package/dist/vendor/core/agent/signing.js +91 -0
- package/dist/vendor/core/attestation/sign.d.ts +25 -0
- package/dist/vendor/core/attestation/sign.js +216 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
- package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
- package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
- package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
- package/dist/vendor/core/autonomy/envelope.js +27 -0
- package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
- package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
- package/dist/vendor/core/autonomy/resume.d.ts +15 -0
- package/dist/vendor/core/autonomy/resume.js +23 -0
- package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
- package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
- package/dist/vendor/core/compiler.d.ts +2 -0
- package/dist/vendor/core/compiler.js +10 -4
- package/dist/vendor/core/context-distillation.d.ts +3 -0
- package/dist/vendor/core/context-distillation.js +44 -0
- package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
- package/dist/vendor/core/context-flow/compile-context.js +111 -0
- package/dist/vendor/core/context-flow/entities.d.ts +2 -0
- package/dist/vendor/core/context-flow/entities.js +44 -0
- package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
- package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
- package/dist/vendor/core/context-flow/index.d.ts +11 -0
- package/dist/vendor/core/context-flow/index.js +24 -0
- package/dist/vendor/core/context-flow/labels.d.ts +3 -0
- package/dist/vendor/core/context-flow/labels.js +17 -0
- package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
- package/dist/vendor/core/context-flow/normalizer.js +69 -0
- package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
- package/dist/vendor/core/context-flow/profiles.js +36 -0
- package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
- package/dist/vendor/core/context-flow/redaction.js +6 -0
- package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
- package/dist/vendor/core/context-flow/sensitivity.js +27 -0
- package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
- package/dist/vendor/core/context-flow/sync-preview.js +22 -0
- package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
- package/dist/vendor/core/context-flow/token-estimator.js +13 -0
- package/dist/vendor/core/context-flow/types.d.ts +91 -0
- package/dist/vendor/core/context-flow/types.js +2 -0
- package/dist/vendor/core/context-integrity.d.ts +26 -0
- package/dist/vendor/core/context-integrity.js +56 -0
- package/dist/vendor/core/context-utility.d.ts +47 -0
- package/dist/vendor/core/context-utility.js +405 -0
- package/dist/vendor/core/cost/pipeline.d.ts +92 -0
- package/dist/vendor/core/cost/pipeline.js +141 -0
- package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
- package/dist/vendor/core/cost/tagged-cost.js +55 -0
- package/dist/vendor/core/cost-governor.d.ts +2 -0
- package/dist/vendor/core/cost-governor.js +50 -0
- package/dist/vendor/core/cve/cve-check.d.ts +80 -0
- package/dist/vendor/core/cve/cve-check.js +172 -0
- package/dist/vendor/core/digital-twin/index.d.ts +27 -0
- package/dist/vendor/core/digital-twin/index.js +90 -0
- package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
- package/dist/vendor/core/drift/drift-graph.js +100 -0
- package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
- package/dist/vendor/core/drift/objective-lock.js +88 -0
- package/dist/vendor/core/drift/scope.d.ts +46 -0
- package/dist/vendor/core/drift/scope.js +102 -0
- package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
- package/dist/vendor/core/drift/signature-lock.js +202 -0
- package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
- package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
- package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
- package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
- package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
- package/dist/vendor/core/evidence/claim-audit.js +89 -0
- package/dist/vendor/core/exit-intelligence.d.ts +2 -0
- package/dist/vendor/core/exit-intelligence.js +58 -0
- package/dist/vendor/core/explain/formatter.d.ts +42 -0
- package/dist/vendor/core/explain/formatter.js +171 -0
- package/dist/vendor/core/explain/timeline.d.ts +29 -0
- package/dist/vendor/core/explain/timeline.js +213 -0
- package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
- package/dist/vendor/core/failure-taxonomy.js +76 -0
- package/dist/vendor/core/gateway/index.d.ts +10 -0
- package/dist/vendor/core/gateway/index.js +12 -0
- package/dist/vendor/core/gateway/registry.d.ts +40 -0
- package/dist/vendor/core/gateway/registry.js +97 -0
- package/dist/vendor/core/gateway/transport.d.ts +31 -0
- package/dist/vendor/core/gateway/transport.js +82 -0
- package/dist/vendor/core/gateway/vault.d.ts +19 -0
- package/dist/vendor/core/gateway/vault.js +29 -0
- package/dist/vendor/core/graph/adapters.d.ts +43 -0
- package/dist/vendor/core/graph/adapters.js +91 -0
- package/dist/vendor/core/graph/hotspots.d.ts +22 -0
- package/dist/vendor/core/graph/hotspots.js +30 -0
- package/dist/vendor/core/graph/index.d.ts +1 -0
- package/dist/vendor/core/graph/index.js +2 -0
- package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
- package/dist/vendor/core/honey/honey-tokens.js +44 -0
- package/dist/vendor/core/index.d.ts +7 -4
- package/dist/vendor/core/index.js +222 -64
- package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
- package/dist/vendor/core/learning/bayesian-update.js +60 -0
- package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
- package/dist/vendor/core/learning/prior-sets.js +111 -0
- package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
- package/dist/vendor/core/learning/promotion-gate.js +23 -0
- package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
- package/dist/vendor/core/leash/blast-radius.js +156 -0
- package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
- package/dist/vendor/core/leash/policy-leash.js +117 -0
- package/dist/vendor/core/memo/memo.d.ts +63 -0
- package/dist/vendor/core/memo/memo.js +97 -0
- package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
- package/dist/vendor/core/memory/learning-pipeline.js +391 -0
- package/dist/vendor/core/memory/palace.d.ts +84 -0
- package/dist/vendor/core/memory/palace.js +379 -0
- package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
- package/dist/vendor/core/merge/ast-merge.js +350 -0
- package/dist/vendor/core/merge/text-merge.d.ts +12 -0
- package/dist/vendor/core/merge/text-merge.js +182 -0
- package/dist/vendor/core/otel/tracer.d.ts +45 -0
- package/dist/vendor/core/otel/tracer.js +116 -0
- package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
- package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
- package/dist/vendor/core/parallel/scorer.d.ts +24 -0
- package/dist/vendor/core/parallel/scorer.js +65 -0
- package/dist/vendor/core/pattern-detection.d.ts +64 -0
- package/dist/vendor/core/pattern-detection.js +108 -0
- package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
- package/dist/vendor/core/persistence/checkpoint.js +156 -0
- package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
- package/dist/vendor/core/persistence/cleanup.js +131 -0
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
- package/dist/vendor/core/persistence/runs-reader.js +84 -0
- package/dist/vendor/core/persistence/store.d.ts +6 -1
- package/dist/vendor/core/persistence/store.js +5 -0
- package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
- package/dist/vendor/core/policy/file-touch-quota.js +105 -0
- package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
- package/dist/vendor/core/policy/policy-loader.js +170 -0
- package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
- package/dist/vendor/core/policy/policy-schema.js +78 -0
- package/dist/vendor/core/policy.d.ts +6 -0
- package/dist/vendor/core/probe/probe.d.ts +49 -0
- package/dist/vendor/core/probe/probe.js +115 -0
- package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
- package/dist/vendor/core/proof/patch-proof.js +84 -0
- package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
- package/dist/vendor/core/proof/semantic-probe.js +82 -0
- package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
- package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
- package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
- package/dist/vendor/core/red-blue/red-phase.js +141 -0
- package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
- package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
- package/dist/vendor/core/replay/replay.d.ts +85 -0
- package/dist/vendor/core/replay/replay.js +109 -0
- package/dist/vendor/core/router/engine.d.ts +54 -0
- package/dist/vendor/core/router/engine.js +131 -0
- package/dist/vendor/core/router/index.d.ts +1 -0
- package/dist/vendor/core/router/index.js +2 -0
- package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
- package/dist/vendor/core/router/trust-calibration.js +127 -0
- package/dist/vendor/core/run-martin.d.ts +2 -0
- package/dist/vendor/core/run-martin.js +287 -0
- package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
- package/dist/vendor/core/security/cve-scanner.js +178 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
- package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
- package/dist/vendor/core/sentinel/progress-guard.js +46 -0
- package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
- package/dist/vendor/core/siem/siem-emitter.js +157 -0
- package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
- package/dist/vendor/core/strategy/attempt-brief.js +89 -0
- package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
- package/dist/vendor/core/summarize/diff-summary.js +204 -0
- package/dist/vendor/core/surface-signals.d.ts +21 -0
- package/dist/vendor/core/surface-signals.js +139 -0
- package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
- package/dist/vendor/core/truth/truth-wall.js +69 -0
- package/dist/vendor/core/truth-spine.d.ts +26 -0
- package/dist/vendor/core/truth-spine.js +62 -0
- package/dist/vendor/core/types.d.ts +115 -0
- package/dist/vendor/core/types.js +2 -0
- package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
- package/dist/vendor/core/verification/tiered-verify.js +29 -0
- package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
- package/dist/vendor/core/verifier-pyramid.js +111 -0
- package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
- package/dist/vendor/core/workflow-artifacts.js +668 -0
- package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
- package/dist/vendor/core/wrap/supervised-run.js +178 -0
- package/docs/assets/cli-animated.svg +139 -0
- package/docs/assets/cli-static.svg +34 -0
- package/docs/assets/github-hero-v2.svg +23 -0
- package/docs/assets/martin-raplph.png.jpg +0 -0
- package/docs/assets/martinloop-logo.png +0 -0
- package/docs/assets/nvidia-inception-program-light.png +0 -0
- package/docs/assets/nvidia-inception-program.png +0 -0
- package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
- package/docs/assets/side-by-side.svg +134 -0
- package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
- package/docs/oss/EXAMPLES.md +9 -1
- package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
- package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
- package/docs/oss/QUICKSTART.md +39 -4
- package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
- package/docs/oss/README.md +7 -4
- package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
- package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
- package/package.json +129 -49
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
export function formatRunTimeline(input) {
|
|
2
|
+
const entries = buildRunTimelineEntries(input);
|
|
3
|
+
const json = {
|
|
4
|
+
loopId: input.loopId,
|
|
5
|
+
objective: input.objective,
|
|
6
|
+
...(input.lifecycleState ? { lifecycleState: input.lifecycleState } : {}),
|
|
7
|
+
entries
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
markdown: renderTimelineMarkdown(json),
|
|
11
|
+
html: renderTimelineHtml(json),
|
|
12
|
+
json
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function buildRunTimelineEntries(input) {
|
|
16
|
+
const loopEntries = (input.events ?? []).map(mapLoopEvent);
|
|
17
|
+
const ledgerEntries = (input.ledgerEvents ?? []).map(mapLedgerEvent);
|
|
18
|
+
return [...loopEntries, ...ledgerEntries].sort((left, right) => left.timestamp.localeCompare(right.timestamp));
|
|
19
|
+
}
|
|
20
|
+
function mapLoopEvent(event) {
|
|
21
|
+
switch (event.type) {
|
|
22
|
+
case "run.started":
|
|
23
|
+
return {
|
|
24
|
+
timestamp: event.timestamp,
|
|
25
|
+
source: "loop",
|
|
26
|
+
label: "Run started",
|
|
27
|
+
detail: stringifyPayload(event.payload),
|
|
28
|
+
severity: "info"
|
|
29
|
+
};
|
|
30
|
+
case "attempt.started":
|
|
31
|
+
return {
|
|
32
|
+
timestamp: event.timestamp,
|
|
33
|
+
source: "loop",
|
|
34
|
+
label: "Attempt started",
|
|
35
|
+
detail: stringifyPayload(event.payload),
|
|
36
|
+
severity: "info"
|
|
37
|
+
};
|
|
38
|
+
case "attempt.completed":
|
|
39
|
+
return {
|
|
40
|
+
timestamp: event.timestamp,
|
|
41
|
+
source: "loop",
|
|
42
|
+
label: "Attempt completed",
|
|
43
|
+
detail: stringifyPayload(event.payload),
|
|
44
|
+
severity: "ok"
|
|
45
|
+
};
|
|
46
|
+
case "pattern.detected":
|
|
47
|
+
return {
|
|
48
|
+
timestamp: event.timestamp,
|
|
49
|
+
source: "loop",
|
|
50
|
+
label: "Pattern detected",
|
|
51
|
+
detail: stringifyPayload(event.payload),
|
|
52
|
+
severity: "warn"
|
|
53
|
+
};
|
|
54
|
+
case "failure.classified":
|
|
55
|
+
return {
|
|
56
|
+
timestamp: event.timestamp,
|
|
57
|
+
source: "loop",
|
|
58
|
+
label: "Failure classified",
|
|
59
|
+
detail: stringifyPayload(event.payload),
|
|
60
|
+
severity: "warn"
|
|
61
|
+
};
|
|
62
|
+
case "intervention.selected":
|
|
63
|
+
return {
|
|
64
|
+
timestamp: event.timestamp,
|
|
65
|
+
source: "loop",
|
|
66
|
+
label: "Intervention selected",
|
|
67
|
+
detail: stringifyPayload(event.payload),
|
|
68
|
+
severity: "info"
|
|
69
|
+
};
|
|
70
|
+
case "verification.completed":
|
|
71
|
+
return {
|
|
72
|
+
timestamp: event.timestamp,
|
|
73
|
+
source: "loop",
|
|
74
|
+
label: "Verification completed",
|
|
75
|
+
detail: stringifyPayload(event.payload),
|
|
76
|
+
severity: "ok"
|
|
77
|
+
};
|
|
78
|
+
case "budget.updated":
|
|
79
|
+
return {
|
|
80
|
+
timestamp: event.timestamp,
|
|
81
|
+
source: "loop",
|
|
82
|
+
label: "Budget updated",
|
|
83
|
+
detail: stringifyPayload(event.payload),
|
|
84
|
+
severity: "info"
|
|
85
|
+
};
|
|
86
|
+
case "run.completed":
|
|
87
|
+
return {
|
|
88
|
+
timestamp: event.timestamp,
|
|
89
|
+
source: "loop",
|
|
90
|
+
label: "Run completed",
|
|
91
|
+
detail: stringifyPayload(event.payload),
|
|
92
|
+
severity: "ok"
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function mapLedgerEvent(event) {
|
|
97
|
+
const labelMap = {
|
|
98
|
+
"contract.created": "Contract created",
|
|
99
|
+
"attempt.admitted": "Attempt admitted",
|
|
100
|
+
"attempt.rejected": "Attempt rejected",
|
|
101
|
+
"prompt.compiled": "Prompt compiled",
|
|
102
|
+
"patch.generated": "Patch generated",
|
|
103
|
+
"verification.completed": "Verification completed",
|
|
104
|
+
"grounding.violations_found": "Grounding violation",
|
|
105
|
+
"safety.violations_found": "Safety violation",
|
|
106
|
+
"budget.settled": "Budget settled",
|
|
107
|
+
"attempt.kept": "Attempt kept",
|
|
108
|
+
"attempt.discarded": "Attempt discarded",
|
|
109
|
+
"run.exited": "Run exited"
|
|
110
|
+
};
|
|
111
|
+
const severity = event.kind === "attempt.rejected" ||
|
|
112
|
+
event.kind === "grounding.violations_found" ||
|
|
113
|
+
event.kind === "safety.violations_found" ||
|
|
114
|
+
event.kind === "attempt.discarded" ||
|
|
115
|
+
event.kind === "run.exited"
|
|
116
|
+
? "warn"
|
|
117
|
+
: event.kind === "attempt.kept" || event.kind === "verification.completed"
|
|
118
|
+
? "ok"
|
|
119
|
+
: "info";
|
|
120
|
+
return {
|
|
121
|
+
timestamp: event.timestamp,
|
|
122
|
+
source: "ledger",
|
|
123
|
+
label: labelMap[event.kind],
|
|
124
|
+
detail: stringifyPayload(event.payload),
|
|
125
|
+
severity,
|
|
126
|
+
...(event.attemptIndex !== undefined ? { attemptIndex: event.attemptIndex } : {})
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function renderTimelineMarkdown(timeline) {
|
|
130
|
+
const lines = [
|
|
131
|
+
`# Run Timeline — ${timeline.loopId}`,
|
|
132
|
+
"",
|
|
133
|
+
`**Objective:** ${timeline.objective}`,
|
|
134
|
+
...(timeline.lifecycleState ? [`**Lifecycle:** ${timeline.lifecycleState}`, ""] : [""]),
|
|
135
|
+
"| Time | Source | Event | Detail |",
|
|
136
|
+
"| --- | --- | --- | --- |",
|
|
137
|
+
...timeline.entries.map((entry) => {
|
|
138
|
+
const detail = entry.detail.replace(/\|/g, "\\|");
|
|
139
|
+
return `| ${entry.timestamp} | ${entry.source} | ${entry.label} | ${detail} |`;
|
|
140
|
+
})
|
|
141
|
+
];
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
function renderTimelineHtml(timeline) {
|
|
145
|
+
const rows = timeline.entries
|
|
146
|
+
.map((entry) => {
|
|
147
|
+
const badgeClass = `badge-${entry.severity}`;
|
|
148
|
+
return [
|
|
149
|
+
"<tr>",
|
|
150
|
+
`<td>${escapeHtml(entry.timestamp)}</td>`,
|
|
151
|
+
`<td><span class="source-chip">${escapeHtml(entry.source)}</span></td>`,
|
|
152
|
+
`<td><span class="badge ${badgeClass}">${escapeHtml(entry.label)}</span></td>`,
|
|
153
|
+
`<td>${escapeHtml(entry.detail)}</td>`,
|
|
154
|
+
"</tr>"
|
|
155
|
+
].join("");
|
|
156
|
+
})
|
|
157
|
+
.join("\n");
|
|
158
|
+
return [
|
|
159
|
+
"<!DOCTYPE html>",
|
|
160
|
+
'<html lang="en">',
|
|
161
|
+
"<head>",
|
|
162
|
+
'<meta charset="UTF-8" />',
|
|
163
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
|
|
164
|
+
`<title>Run Timeline — ${escapeHtml(timeline.loopId)}</title>`,
|
|
165
|
+
"<style>",
|
|
166
|
+
"body{margin:0;background:#f8f5ed;color:#18181b;font-family:'General Sans',-apple-system,BlinkMacSystemFont,sans-serif;padding:40px;}",
|
|
167
|
+
"h1{font-family:'Fraunces',Georgia,serif;font-size:42px;line-height:1.05;margin:0 0 12px;}",
|
|
168
|
+
"p.meta{color:#5c6170;margin:0 0 28px;font-size:16px;}",
|
|
169
|
+
".wrap{max-width:1100px;margin:0 auto;}",
|
|
170
|
+
"table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #e6e0d4;border-radius:18px;overflow:hidden;box-shadow:0 18px 50px rgba(17,17,17,.06);}",
|
|
171
|
+
"th,td{padding:14px 16px;border-bottom:1px solid #eee8dc;text-align:left;vertical-align:top;font-size:14px;}",
|
|
172
|
+
"th{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:#6b7280;background:#fcfbf7;}",
|
|
173
|
+
".badge,.source-chip{display:inline-flex;align-items:center;border-radius:999px;padding:4px 10px;font-size:12px;font-weight:700;}",
|
|
174
|
+
".badge-info{background:#ece8ff;color:#4f46e5;}.badge-ok{background:#dcfce7;color:#166534;}.badge-warn{background:#fef3c7;color:#92400e;}",
|
|
175
|
+
".source-chip{background:#f3f4f6;color:#4b5563;}",
|
|
176
|
+
"</style>",
|
|
177
|
+
"</head>",
|
|
178
|
+
"<body>",
|
|
179
|
+
'<div class="wrap">',
|
|
180
|
+
`<h1>Run timeline</h1>`,
|
|
181
|
+
`<p class="meta"><strong>Loop:</strong> ${escapeHtml(timeline.loopId)}<br/><strong>Objective:</strong> ${escapeHtml(timeline.objective)}${timeline.lifecycleState ? `<br/><strong>Lifecycle:</strong> ${escapeHtml(timeline.lifecycleState)}` : ""}</p>`,
|
|
182
|
+
"<table>",
|
|
183
|
+
"<thead><tr><th>Time</th><th>Source</th><th>Event</th><th>Detail</th></tr></thead>",
|
|
184
|
+
`<tbody>${rows}</tbody>`,
|
|
185
|
+
"</table>",
|
|
186
|
+
"</div>",
|
|
187
|
+
"</body>",
|
|
188
|
+
"</html>"
|
|
189
|
+
].join("");
|
|
190
|
+
}
|
|
191
|
+
function stringifyPayload(payload) {
|
|
192
|
+
const flattened = Object.entries(payload)
|
|
193
|
+
.map(([key, value]) => `${key}=${formatValue(value)}`)
|
|
194
|
+
.join("; ");
|
|
195
|
+
return flattened || "no additional payload";
|
|
196
|
+
}
|
|
197
|
+
function formatValue(value) {
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
return value.join(", ");
|
|
200
|
+
}
|
|
201
|
+
if (value && typeof value === "object") {
|
|
202
|
+
return JSON.stringify(value);
|
|
203
|
+
}
|
|
204
|
+
return String(value);
|
|
205
|
+
}
|
|
206
|
+
function escapeHtml(value) {
|
|
207
|
+
return value
|
|
208
|
+
.replace(/&/g, "&")
|
|
209
|
+
.replace(/</g, "<")
|
|
210
|
+
.replace(/>/g, ">")
|
|
211
|
+
.replace(/"/g, """);
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function classifyFailure(input) {
|
|
2
|
+
const failureClass = detectFailureClass(input);
|
|
3
|
+
const repeatedCount = countRecentMatches(input.attempts, failureClass);
|
|
4
|
+
const retryable = failureClass !== "environment_mismatch";
|
|
5
|
+
const recommendedIntervention = chooseIntervention(failureClass, repeatedCount);
|
|
6
|
+
return {
|
|
7
|
+
failureClass,
|
|
8
|
+
retryable,
|
|
9
|
+
recommendedIntervention,
|
|
10
|
+
rationale: buildRationale(failureClass, input, repeatedCount, recommendedIntervention)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function detectFailureClass(input) {
|
|
14
|
+
const hint = input.result.failure?.classHint;
|
|
15
|
+
if (hint) {
|
|
16
|
+
return hint;
|
|
17
|
+
}
|
|
18
|
+
const haystack = [
|
|
19
|
+
input.result.summary,
|
|
20
|
+
input.result.verification.summary,
|
|
21
|
+
input.result.failure?.message
|
|
22
|
+
]
|
|
23
|
+
.filter((value) => typeof value === "string")
|
|
24
|
+
.join(" ")
|
|
25
|
+
.toLowerCase();
|
|
26
|
+
if (containsAny(haystack, ["enoent", "command not found", "missing from path", "permission denied"])) {
|
|
27
|
+
return "environment_mismatch";
|
|
28
|
+
}
|
|
29
|
+
if (containsAny(haystack, ["syntax", "parse", "eslint", "ts1"])) {
|
|
30
|
+
return "syntax_error";
|
|
31
|
+
}
|
|
32
|
+
if (containsAny(haystack, ["budget", "token", "limit", "cost"])) {
|
|
33
|
+
return "budget_pressure";
|
|
34
|
+
}
|
|
35
|
+
if (containsAny(haystack, ["scope", "too broad", "expanded"])) {
|
|
36
|
+
return "scope_creep";
|
|
37
|
+
}
|
|
38
|
+
if (containsAny(haystack, ["hallucin", "invented", "made up"])) {
|
|
39
|
+
return "hallucination";
|
|
40
|
+
}
|
|
41
|
+
if (!input.result.verification.passed) {
|
|
42
|
+
return "verification_failure";
|
|
43
|
+
}
|
|
44
|
+
return "logic_error";
|
|
45
|
+
}
|
|
46
|
+
function chooseIntervention(failureClass, repeatedCount) {
|
|
47
|
+
if (failureClass === "environment_mismatch") {
|
|
48
|
+
return repeatedCount >= 2 ? "switch_adapter" : "escalate_human";
|
|
49
|
+
}
|
|
50
|
+
if (failureClass === "budget_pressure") {
|
|
51
|
+
return "compress_context";
|
|
52
|
+
}
|
|
53
|
+
if (failureClass === "scope_creep") {
|
|
54
|
+
return "tighten_task";
|
|
55
|
+
}
|
|
56
|
+
if (failureClass === "verification_failure" || failureClass === "syntax_error") {
|
|
57
|
+
return "run_verifier";
|
|
58
|
+
}
|
|
59
|
+
if (repeatedCount >= 2) {
|
|
60
|
+
return "change_model";
|
|
61
|
+
}
|
|
62
|
+
return "change_model";
|
|
63
|
+
}
|
|
64
|
+
function buildRationale(failureClass, input, repeatedCount, intervention) {
|
|
65
|
+
const evidence = input.result.failure?.message ?? input.result.verification.summary;
|
|
66
|
+
const repetitionNote = repeatedCount >= 2 ? ` The same failure appeared in ${repeatedCount} recent attempts.` : "";
|
|
67
|
+
return `${failureClass} inferred from "${evidence}". Recommended intervention: ${intervention}.${repetitionNote}`;
|
|
68
|
+
}
|
|
69
|
+
function countRecentMatches(attempts, failureClass) {
|
|
70
|
+
const recent = attempts.slice(-3).filter((attempt) => attempt.failureClass === failureClass);
|
|
71
|
+
return recent.length;
|
|
72
|
+
}
|
|
73
|
+
function containsAny(value, needles) {
|
|
74
|
+
return needles.some((needle) => value.includes(needle));
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=failure-taxonomy.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./registry.js";
|
|
2
|
+
export * from "./transport.js";
|
|
3
|
+
export * from "./vault.js";
|
|
4
|
+
import { McpServerRegistry, type McpServerConfig } from "./registry.js";
|
|
5
|
+
import { McpHttpTransport } from "./transport.js";
|
|
6
|
+
import type { McpTokenVault } from "./vault.js";
|
|
7
|
+
export declare function createGateway(servers: McpServerConfig[], vault?: McpTokenVault): {
|
|
8
|
+
registry: McpServerRegistry;
|
|
9
|
+
transport: McpHttpTransport;
|
|
10
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./registry.js";
|
|
2
|
+
export * from "./transport.js";
|
|
3
|
+
export * from "./vault.js";
|
|
4
|
+
import { McpServerRegistry } from "./registry.js";
|
|
5
|
+
import { McpHttpTransport } from "./transport.js";
|
|
6
|
+
export function createGateway(servers, vault) {
|
|
7
|
+
return {
|
|
8
|
+
registry: new McpServerRegistry(servers),
|
|
9
|
+
transport: new McpHttpTransport(vault)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface McpServerConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
url: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
trustTier: "system" | "verified" | "untrusted";
|
|
7
|
+
allowedTools: string[];
|
|
8
|
+
blockedTools: string[];
|
|
9
|
+
}
|
|
10
|
+
export type ToolPolicyDecision = {
|
|
11
|
+
action: "allow";
|
|
12
|
+
} | {
|
|
13
|
+
action: "deny";
|
|
14
|
+
reason: string;
|
|
15
|
+
} | {
|
|
16
|
+
action: "simulate";
|
|
17
|
+
mockResponse: unknown;
|
|
18
|
+
} | {
|
|
19
|
+
action: "review";
|
|
20
|
+
reason: string;
|
|
21
|
+
};
|
|
22
|
+
export interface ToolExecutionAttempt {
|
|
23
|
+
serverId: string;
|
|
24
|
+
toolName: string;
|
|
25
|
+
args: Record<string, unknown>;
|
|
26
|
+
context: {
|
|
27
|
+
loopId: string;
|
|
28
|
+
executionProfile?: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare class McpServerRegistry {
|
|
32
|
+
private readonly servers;
|
|
33
|
+
constructor(initialServers?: McpServerConfig[]);
|
|
34
|
+
register(server: McpServerConfig): void;
|
|
35
|
+
unregister(serverId: string): boolean;
|
|
36
|
+
getServer(serverId: string): McpServerConfig | undefined;
|
|
37
|
+
listServers(): McpServerConfig[];
|
|
38
|
+
evaluateToolPolicy(attempt: ToolExecutionAttempt): ToolPolicyDecision;
|
|
39
|
+
private matchesPattern;
|
|
40
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getTracer } from "../otel/tracer.js";
|
|
2
|
+
export class McpServerRegistry {
|
|
3
|
+
servers = new Map();
|
|
4
|
+
constructor(initialServers = []) {
|
|
5
|
+
for (const server of initialServers) {
|
|
6
|
+
this.register(server);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
register(server) {
|
|
10
|
+
if (!server.id || !server.url) {
|
|
11
|
+
throw new Error(`Invalid MCP server configuration for: ${server.name}`);
|
|
12
|
+
}
|
|
13
|
+
this.servers.set(server.id, server);
|
|
14
|
+
}
|
|
15
|
+
unregister(serverId) {
|
|
16
|
+
return this.servers.delete(serverId);
|
|
17
|
+
}
|
|
18
|
+
getServer(serverId) {
|
|
19
|
+
return this.servers.get(serverId);
|
|
20
|
+
}
|
|
21
|
+
listServers() {
|
|
22
|
+
return Array.from(this.servers.values());
|
|
23
|
+
}
|
|
24
|
+
evaluateToolPolicy(attempt) {
|
|
25
|
+
const tracer = getTracer();
|
|
26
|
+
const span = tracer.startSpan("martin.gateway_policy", {
|
|
27
|
+
"tool.name": attempt.toolName,
|
|
28
|
+
"server.id": attempt.serverId,
|
|
29
|
+
"context.loop": attempt.context.loopId
|
|
30
|
+
});
|
|
31
|
+
const finish = (decision) => {
|
|
32
|
+
tracer.endSpan(span, decision.action === "deny" ? "ERROR" : "OK");
|
|
33
|
+
return decision;
|
|
34
|
+
};
|
|
35
|
+
const server = this.getServer(attempt.serverId);
|
|
36
|
+
if (!server) {
|
|
37
|
+
return finish({
|
|
38
|
+
action: "deny",
|
|
39
|
+
reason: `Server ID '${attempt.serverId}' not registered.`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (server.trustTier === "untrusted" &&
|
|
43
|
+
attempt.context.executionProfile === "ci_safe") {
|
|
44
|
+
return finish({
|
|
45
|
+
action: "deny",
|
|
46
|
+
reason: "Untrusted MCP servers are disabled under ci_safe profile."
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Direct blocklist
|
|
50
|
+
if (this.matchesPattern(attempt.toolName, server.blockedTools)) {
|
|
51
|
+
return finish({
|
|
52
|
+
action: "deny",
|
|
53
|
+
reason: `Tool '${attempt.toolName}' is blocked by server policy.`
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Allowlist
|
|
57
|
+
if (server.allowedTools.length > 0 &&
|
|
58
|
+
!this.matchesPattern(attempt.toolName, server.allowedTools)) {
|
|
59
|
+
return finish({
|
|
60
|
+
action: "deny",
|
|
61
|
+
reason: `Tool '${attempt.toolName}' is not in the server allowlist.`
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Dangerous patterns require dry-run ("simulate") if not strongly verified
|
|
65
|
+
if (server.trustTier !== "system") {
|
|
66
|
+
const stringifiedArgs = JSON.stringify(attempt.args);
|
|
67
|
+
if (stringifiedArgs.includes("rm -rf") ||
|
|
68
|
+
stringifiedArgs.includes("drop table")) {
|
|
69
|
+
return finish({
|
|
70
|
+
action: "simulate",
|
|
71
|
+
mockResponse: {
|
|
72
|
+
success: true,
|
|
73
|
+
stdout: "",
|
|
74
|
+
stderr: "DRY-RUN: Command intercepted and skipped."
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return finish({ action: "allow" });
|
|
80
|
+
}
|
|
81
|
+
matchesPattern(tool, patterns) {
|
|
82
|
+
for (const p of patterns) {
|
|
83
|
+
if (p === "*")
|
|
84
|
+
return true;
|
|
85
|
+
if (p.endsWith("*")) {
|
|
86
|
+
const prefix = p.slice(0, -1);
|
|
87
|
+
if (tool.startsWith(prefix))
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
else if (p === tool) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { McpServerConfig } from "./registry.js";
|
|
2
|
+
import type { McpTokenVault } from "./vault.js";
|
|
3
|
+
export interface JsonRpcRequest {
|
|
4
|
+
jsonrpc: "2.0";
|
|
5
|
+
id: string | number;
|
|
6
|
+
method: string;
|
|
7
|
+
params?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface JsonRpcResponse<T = unknown> {
|
|
10
|
+
jsonrpc: "2.0";
|
|
11
|
+
id: string | number;
|
|
12
|
+
result?: T;
|
|
13
|
+
error?: {
|
|
14
|
+
code: number;
|
|
15
|
+
message: string;
|
|
16
|
+
data?: unknown;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare class McpTransportError extends Error {
|
|
20
|
+
readonly status?: number | undefined;
|
|
21
|
+
readonly data?: unknown | undefined;
|
|
22
|
+
constructor(message: string, status?: number | undefined, data?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
export declare class McpHttpTransport {
|
|
25
|
+
private readonly vault?;
|
|
26
|
+
constructor(vault?: McpTokenVault | undefined);
|
|
27
|
+
/**
|
|
28
|
+
* Dispatches a raw JSON-RPC 2.0 request to an external MCP HTTP server.
|
|
29
|
+
*/
|
|
30
|
+
executeCommand<T = unknown>(server: McpServerConfig, method: string, params: Record<string, unknown>, requestId: string): Promise<T>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export class McpTransportError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
data;
|
|
4
|
+
constructor(message, status, data) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.data = data;
|
|
8
|
+
this.name = "McpTransportError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class McpHttpTransport {
|
|
12
|
+
vault;
|
|
13
|
+
constructor(vault) {
|
|
14
|
+
this.vault = vault;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Dispatches a raw JSON-RPC 2.0 request to an external MCP HTTP server.
|
|
18
|
+
*/
|
|
19
|
+
async executeCommand(server, method, params, requestId) {
|
|
20
|
+
const payload = {
|
|
21
|
+
jsonrpc: "2.0",
|
|
22
|
+
id: requestId,
|
|
23
|
+
method,
|
|
24
|
+
params
|
|
25
|
+
};
|
|
26
|
+
const headers = new Headers({
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
"User-Agent": "Martin-Loop-Gateway/0.1.0"
|
|
29
|
+
});
|
|
30
|
+
if (server.headers) {
|
|
31
|
+
for (const [key, value] of Object.entries(server.headers)) {
|
|
32
|
+
headers.set(key, value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (this.vault) {
|
|
36
|
+
const dynamicHeaders = await this.vault.resolveHeaders(server.id);
|
|
37
|
+
for (const [key, value] of Object.entries(dynamicHeaders)) {
|
|
38
|
+
headers.set(key, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
// 30s timeout so the loop doesn't hang indefinitely on a bad server
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeout = setTimeout(() => controller.abort(), 30000);
|
|
45
|
+
const response = await fetch(server.url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers,
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
signal: controller.signal
|
|
50
|
+
});
|
|
51
|
+
clearTimeout(timeout);
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new McpTransportError(`HTTP Error ${response.status}: Failed to reach MCP Server '${server.id}'`, response.status);
|
|
54
|
+
}
|
|
55
|
+
const rawJson = await response.text();
|
|
56
|
+
let rpcResponse;
|
|
57
|
+
try {
|
|
58
|
+
rpcResponse = JSON.parse(rawJson);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw new McpTransportError(`Invalid JSON received from MCP Server '${server.id}'`);
|
|
62
|
+
}
|
|
63
|
+
if (rpcResponse.error) {
|
|
64
|
+
throw new McpTransportError(`MCP Server Internal Error: ${rpcResponse.error.message}`, undefined, rpcResponse.error.data);
|
|
65
|
+
}
|
|
66
|
+
// JSON-RPC 2.0 requires `result` to exist on success
|
|
67
|
+
if (rpcResponse.result === undefined) {
|
|
68
|
+
throw new McpTransportError(`Malformed JSON-RPC response from '${server.id}': Missing result.`);
|
|
69
|
+
}
|
|
70
|
+
return rpcResponse.result;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e instanceof McpTransportError) {
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
const err = e;
|
|
77
|
+
const errMessage = err.name === "AbortError" ? "Request Timeout" : err.message;
|
|
78
|
+
throw new McpTransportError(`Network error dispatching to MCP Server '${server.id}': ${errMessage}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface McpTokenVault {
|
|
2
|
+
/**
|
|
3
|
+
* Resolves authentication headers for a given server dynamically.
|
|
4
|
+
* Can be implemented via OAuth, Secret Managers, or static config.
|
|
5
|
+
*/
|
|
6
|
+
resolveHeaders(serverId: string): Promise<Record<string, string>>;
|
|
7
|
+
}
|
|
8
|
+
export declare class StaticTokenVault implements McpTokenVault {
|
|
9
|
+
private readonly staticHeaders;
|
|
10
|
+
constructor(staticHeaders: Map<string, Record<string, string>>);
|
|
11
|
+
resolveHeaders(serverId: string): Promise<Record<string, string>>;
|
|
12
|
+
}
|
|
13
|
+
export declare class OAuthProxyVault implements McpTokenVault {
|
|
14
|
+
private readonly oauthTokenEndpoint;
|
|
15
|
+
private readonly clientId;
|
|
16
|
+
private readonly clientSecret;
|
|
17
|
+
constructor(oauthTokenEndpoint: string, clientId: string, clientSecret: string);
|
|
18
|
+
resolveHeaders(serverId: string): Promise<Record<string, string>>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class StaticTokenVault {
|
|
2
|
+
staticHeaders;
|
|
3
|
+
constructor(staticHeaders) {
|
|
4
|
+
this.staticHeaders = staticHeaders;
|
|
5
|
+
}
|
|
6
|
+
async resolveHeaders(serverId) {
|
|
7
|
+
return this.staticHeaders.get(serverId) ?? {};
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class OAuthProxyVault {
|
|
11
|
+
oauthTokenEndpoint;
|
|
12
|
+
clientId;
|
|
13
|
+
clientSecret;
|
|
14
|
+
constructor(oauthTokenEndpoint, clientId, clientSecret) {
|
|
15
|
+
this.oauthTokenEndpoint = oauthTokenEndpoint;
|
|
16
|
+
this.clientId = clientId;
|
|
17
|
+
this.clientSecret = clientSecret;
|
|
18
|
+
}
|
|
19
|
+
async resolveHeaders(serverId) {
|
|
20
|
+
// Basic dynamic OAuth token fetch simulation for multi-tenant isolation
|
|
21
|
+
// Without actually making real un-mocked requests that could crash tests
|
|
22
|
+
const token = Buffer.from(`${this.clientId}:${this.clientSecret}-${serverId}`).toString("base64");
|
|
23
|
+
return {
|
|
24
|
+
Authorization: `Bearer ${token}`,
|
|
25
|
+
"X-Martin-Scope": "dynamic-vault"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=vault.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RepoHotspot } from "./hotspots.js";
|
|
2
|
+
export interface MartinGraphAdapter {
|
|
3
|
+
queryRegressions(targetFiles: string[], objective: string): Promise<RepoHotspot[]>;
|
|
4
|
+
ingestRun(record: unknown): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Abstract interface to ensure the system can hot-swap external graph engines
|
|
8
|
+
* (like Neo4j) without rewriting the core calculation logic when workspace scale grows.
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class AbstractGraphStore implements MartinGraphAdapter {
|
|
11
|
+
abstract queryRegressions(targetFiles: string[], objective: string): Promise<RepoHotspot[]>;
|
|
12
|
+
abstract ingestRun(record: unknown): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare class JsonAdjacencyGraphAdapter extends AbstractGraphStore {
|
|
15
|
+
private readonly adjacencyFilePath;
|
|
16
|
+
constructor(adjacencyFilePath: string);
|
|
17
|
+
queryRegressions(targetFiles: string[], _objective: string): Promise<RepoHotspot[]>;
|
|
18
|
+
ingestRun(_record: unknown): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* GitBlameGraphAdapter
|
|
22
|
+
*
|
|
23
|
+
* Derives repository hotspots from live git history — no static JSON file required.
|
|
24
|
+
*
|
|
25
|
+
* For each target file, runs `git log --oneline --follow -- <file>` to count
|
|
26
|
+
* how many commits have touched it. Files with a commit count >= the configured
|
|
27
|
+
* threshold are returned as hotspots. The regression count is the git commit count,
|
|
28
|
+
* which is a reliable, repo-native signal of churn / instability.
|
|
29
|
+
*
|
|
30
|
+
* Fail-open: any git error (not a repo, file not tracked, git not installed) returns
|
|
31
|
+
* an empty result for that file — never throws.
|
|
32
|
+
*/
|
|
33
|
+
export declare class GitBlameGraphAdapter extends AbstractGraphStore {
|
|
34
|
+
private readonly repoRoot;
|
|
35
|
+
/** Files with >= this many commits are classified as hotspots. Default: 5 */
|
|
36
|
+
private readonly commitThreshold;
|
|
37
|
+
constructor(repoRoot: string,
|
|
38
|
+
/** Files with >= this many commits are classified as hotspots. Default: 5 */
|
|
39
|
+
commitThreshold?: number);
|
|
40
|
+
queryRegressions(targetFiles: string[], _objective: string): Promise<RepoHotspot[]>;
|
|
41
|
+
ingestRun(_record: unknown): Promise<void>;
|
|
42
|
+
private countCommits;
|
|
43
|
+
}
|