thumbgate 1.34.3 → 1.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
- package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
- package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
- package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
- package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/llms.txt +1 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/CONTRIBUTING.md +95 -0
- package/README.md +195 -632
- package/THIRD_PARTY_NOTICES.md +89 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/future-agi/.mcp.json +8 -0
- package/adapters/future-agi/FUTURE_AGI.md +23 -0
- package/adapters/future-agi/config.toml +3 -0
- package/adapters/future-agi/future-agi-bridge.js +9 -0
- package/adapters/future-agi/opencode.json +8 -0
- package/adapters/herdr/herdr-plugin.toml +18 -0
- package/adapters/mcp/server-stdio.js +238 -25
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/workos/WORKOS.md +52 -0
- package/bin/cli.js +373 -5
- package/bin/futureagi-bridge +9 -0
- package/config/gate-templates.json +653 -4
- package/config/gates/actor-critic-audit.json +34 -0
- package/config/gates/default.json +21 -2
- package/config/gates/five-walls-governance.json +34 -0
- package/config/gates/future-agi-guardrails.json +34 -0
- package/config/gates/radware-threat-defense-2026.json +61 -0
- package/config/gates/simatree-data-governance.json +33 -0
- package/config/mcp-allowlists.json +4 -0
- package/config/merge-quality-checks.json +10 -1
- package/config/model-candidates.json +382 -24
- package/config/model-tiers.json +18 -0
- package/config/post-deploy-marketing-pages.json +10 -0
- package/config/progressive/01-wire-only.json +11 -0
- package/config/progressive/02-dashboard-empty-ok.json +10 -0
- package/config/progressive/03-one-lesson.json +10 -0
- package/config/progressive/04-warn-fires.json +11 -0
- package/config/progressive/05-strict-optional.json +11 -0
- package/config/progressive/README.md +15 -0
- package/config/schemas/broker-execution-receipt.schema.json +139 -0
- package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
- package/conformance/provider-attestation/vectors.json +320 -0
- package/docs/specs/provider-execution-attestation-v1.md +69 -0
- package/openapi/openapi.yaml +15 -0
- package/package.json +401 -147
- package/public/about.html +2 -2
- package/public/ai-malpractice-prevention.html +7 -7
- package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
- package/public/blog/a-receipt-is-not-world-state.html +388 -0
- package/public/blog/git-at-agent-scale.html +374 -0
- package/public/blog/no-llm-in-the-gate.html +133 -0
- package/public/blog.html +80 -0
- package/public/case-studies.html +16 -1
- package/public/compare.html +28 -0
- package/public/diagnostic.html +216 -7
- package/public/docs/connectors.html +39 -0
- package/public/federal.html +2 -2
- package/public/founders.html +639 -0
- package/public/index.html +87 -9
- package/public/install.html +8 -8
- package/public/learn.html +39 -0
- package/public/numbers.html +2 -2
- package/public/peter.html +310 -0
- package/public/platform-partners.html +119 -0
- package/public/pricing.html +24 -3
- package/public/privacy.html +117 -0
- package/public/pro.html +17 -0
- package/public/support.html +62 -0
- package/public/terms.html +130 -0
- package/public/third-party-notices.html +95 -0
- package/public/yt.html +351 -0
- package/scripts/action-receipts.js +133 -3
- package/scripts/adaptive-governance-arena.js +349 -0
- package/scripts/admin-override.js +205 -0
- package/scripts/agent-action-inventory.js +869 -0
- package/scripts/agent-audit-trace.js +42 -2
- package/scripts/agent-egress-policy.js +1117 -0
- package/scripts/agent-memory-lifecycle.js +141 -2
- package/scripts/agent-operations-planner.js +441 -1
- package/scripts/agent-readiness.js +68 -0
- package/scripts/agent-security-central.js +647 -0
- package/scripts/allowlist-bridge-honesty.js +417 -0
- package/scripts/async-job-runner.js +102 -11
- package/scripts/audit-trail.js +212 -0
- package/scripts/auto-promote-gates.js +178 -27
- package/scripts/billing.js +1 -1
- package/scripts/broker-execution-receipts.js +719 -0
- package/scripts/budget-aware-gates-proof.js +423 -0
- package/scripts/claude-feedback-sync.js +29 -3
- package/scripts/claw-harness-production.js +237 -0
- package/scripts/cli-progress.js +111 -0
- package/scripts/cli-schema.js +163 -1
- package/scripts/codex-runbook-flywheel.js +318 -0
- package/scripts/context-footprint.js +186 -0
- package/scripts/contextfs.js +143 -61
- package/scripts/dashboard-limits.js +27 -0
- package/scripts/dashboard.js +279 -9
- package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
- package/scripts/docker-sandbox-planner.js +18 -0
- package/scripts/double-blind-eval-protocol.js +252 -0
- package/scripts/edotenv-rl-gateway.js +259 -0
- package/scripts/ensure-production-search-corpus.js +162 -0
- package/scripts/eval-holdout.js +311 -0
- package/scripts/feedback-aggregate.js +21 -2
- package/scripts/feedback-loop.js +87 -5
- package/scripts/feedback-quality.js +9 -0
- package/scripts/file-ledger-lock.js +4 -1
- package/scripts/financial-control-plane.js +41 -1
- package/scripts/find-dormant-requires.js +118 -0
- package/scripts/fs-utils.js +84 -8
- package/scripts/gate-stats.js +2 -2
- package/scripts/gates-engine.js +859 -58
- package/scripts/generate-case-study-outreach.js +24 -15
- package/scripts/git-at-scale.js +628 -0
- package/scripts/governance-conflict-audit.js +1650 -0
- package/scripts/governance-difficulty-curriculum.js +328 -0
- package/scripts/graphrag-retrieval.js +275 -0
- package/scripts/gurobi-optimizer.js +324 -0
- package/scripts/gurobi_optimizer.py +485 -0
- package/scripts/harness-selector.js +82 -1
- package/scripts/hidden-entry-points.js +284 -0
- package/scripts/human-escalation.js +199 -1
- package/scripts/hybrid-feedback-context.js +152 -19
- package/scripts/intent-governed-execution.js +602 -0
- package/scripts/intervention-policy.js +123 -20
- package/scripts/jit-harness-compose.js +628 -0
- package/scripts/jsonl-watcher.js +10 -0
- package/scripts/lesson-embedding-index.js +95 -12
- package/scripts/lesson-retrieval.js +105 -19
- package/scripts/local-model-profile.js +19 -2
- package/scripts/mailer/resend-mailer.js +1 -1
- package/scripts/matryoshka-embedding.js +235 -0
- package/scripts/mcp-oauth.js +42 -4
- package/scripts/mcp-session-handles.js +1016 -0
- package/scripts/mcp-wiring-doctor.js +314 -0
- package/scripts/memory-firewall.js +115 -2
- package/scripts/memory-scope-readiness.js +299 -0
- package/scripts/memory-vs-rag-route.js +161 -0
- package/scripts/model-tier-router.js +148 -21
- package/scripts/nvidia-specdecode-al-doctor.js +536 -0
- package/scripts/openui-catalog-compose-honesty.js +593 -0
- package/scripts/operational-integrity.js +19 -1
- package/scripts/override-audit.js +213 -0
- package/scripts/package-manager-honesty-doctor.js +458 -0
- package/scripts/pr-manager.js +63 -1
- package/scripts/prove-herdr-adapter.js +52 -0
- package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
- package/scripts/prove-workos.js +73 -0
- package/scripts/provider-attestation-conformance.js +192 -0
- package/scripts/provider-receipt-contract.js +136 -0
- package/scripts/qwen38-max-cost-optimizer.js +401 -0
- package/scripts/radware-threat-defense.js +280 -0
- package/scripts/rag-embedding-identity.js +221 -0
- package/scripts/rag-precision-guardrails.js +112 -2
- package/scripts/remote-feedback-capture.js +159 -0
- package/scripts/research-agent-harness.js +256 -0
- package/scripts/rsi-safety-hillclimb.js +200 -0
- package/scripts/rule-sprawl.js +188 -0
- package/scripts/schedule-manager.js +147 -0
- package/scripts/self-heal.js +8 -0
- package/scripts/session-lease.js +415 -0
- package/scripts/simatree-data-governance.js +347 -0
- package/scripts/slo-alert-engine.js +172 -7
- package/scripts/solver-parity.js +539 -0
- package/scripts/stealth-memory-injection-gate.js +333 -0
- package/scripts/switchyard-router.js +366 -0
- package/scripts/telemetry-analytics.js +84 -27
- package/scripts/temporal-decay-weighting.js +138 -0
- package/scripts/test-all.js +165 -0
- package/scripts/token-savings.js +42 -0
- package/scripts/tool-kpi-tracker.js +108 -5
- package/scripts/tool-registry.js +193 -5
- package/scripts/universal-claim-evaluator.js +14 -2
- package/scripts/vector-store.js +279 -9
- package/scripts/workflow-notebook.js +391 -0
- package/scripts/workflow-sentinel.js +111 -12
- package/scripts/workos-production-guard.js +260 -0
- package/scripts/workspace-search-route.js +515 -0
- package/server.json +2 -2
- package/src/agent-identity-boundary.js +76 -0
- package/src/agent-retrieval-cache.js +155 -0
- package/src/alert-noise-ledger.js +502 -0
- package/src/api/server.js +802 -185
- package/src/git-fast-cache.js +220 -0
- package/src/git-wal-sync.js +156 -0
- package/src/hash-anchored-edit.js +82 -0
- package/src/hermes-platform-protocol.js +475 -0
- package/src/hermes-sync-plane.js +241 -0
- package/src/index.js +30 -1
- package/src/iso42001-compliance-guard.js +97 -0
- package/src/latency-budget.js +244 -0
- package/src/mcp-writeguard.js +316 -0
- package/src/miminions-adapter.js +106 -0
- package/src/pipeline-compass.js +104 -0
- package/src/ppl-alert-pipeline.js +284 -0
- package/src/rendezvous-router.js +90 -0
- package/src/security-questionnaire.js +195 -0
|
@@ -7,6 +7,12 @@ const path = require('node:path');
|
|
|
7
7
|
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
8
8
|
const { readJsonl } = require('./fs-utils');
|
|
9
9
|
|
|
10
|
+
const TOOL_CALL_OUTCOMES = Object.freeze({
|
|
11
|
+
SUCCESSFUL: 'successful',
|
|
12
|
+
FAILED: 'failed',
|
|
13
|
+
BLOCKED: 'blocked',
|
|
14
|
+
});
|
|
15
|
+
|
|
10
16
|
function getKpiLogPath(options = {}) {
|
|
11
17
|
return path.join(
|
|
12
18
|
options.feedbackDir ? path.resolve(options.feedbackDir) : resolveFeedbackDir(),
|
|
@@ -14,25 +20,69 @@ function getKpiLogPath(options = {}) {
|
|
|
14
20
|
);
|
|
15
21
|
}
|
|
16
22
|
|
|
23
|
+
/**
|
|
24
|
+
* WriteGuard-aligned outcome taxonomy for every MCP attempt.
|
|
25
|
+
* Prefer explicit `outcome`; otherwise derive from blocked/denied/success.
|
|
26
|
+
*/
|
|
27
|
+
function normalizeToolCallOutcome({
|
|
28
|
+
outcome,
|
|
29
|
+
success,
|
|
30
|
+
blocked,
|
|
31
|
+
metadata,
|
|
32
|
+
} = {}) {
|
|
33
|
+
if (
|
|
34
|
+
outcome === TOOL_CALL_OUTCOMES.SUCCESSFUL
|
|
35
|
+
|| outcome === TOOL_CALL_OUTCOMES.FAILED
|
|
36
|
+
|| outcome === TOOL_CALL_OUTCOMES.BLOCKED
|
|
37
|
+
) {
|
|
38
|
+
return outcome;
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
blocked === true
|
|
42
|
+
|| metadata?.denied === true
|
|
43
|
+
|| metadata?.outcome === TOOL_CALL_OUTCOMES.BLOCKED
|
|
44
|
+
) {
|
|
45
|
+
return TOOL_CALL_OUTCOMES.BLOCKED;
|
|
46
|
+
}
|
|
47
|
+
if (success === false) return TOOL_CALL_OUTCOMES.FAILED;
|
|
48
|
+
return TOOL_CALL_OUTCOMES.SUCCESSFUL;
|
|
49
|
+
}
|
|
50
|
+
|
|
17
51
|
function recordToolCall({
|
|
18
52
|
toolName,
|
|
19
53
|
serverName,
|
|
20
54
|
latencyMs,
|
|
21
55
|
success,
|
|
56
|
+
blocked,
|
|
57
|
+
outcome,
|
|
22
58
|
agentId,
|
|
59
|
+
clientId,
|
|
60
|
+
sessionId,
|
|
61
|
+
writeRiskTier,
|
|
23
62
|
metadata,
|
|
24
63
|
feedbackDir,
|
|
25
64
|
} = {}) {
|
|
26
65
|
const logPath = getKpiLogPath({ feedbackDir });
|
|
27
66
|
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
67
|
+
const normalizedOutcome = normalizeToolCallOutcome({
|
|
68
|
+
outcome,
|
|
69
|
+
success,
|
|
70
|
+
blocked,
|
|
71
|
+
metadata,
|
|
72
|
+
});
|
|
28
73
|
const entry = {
|
|
29
74
|
id: `kpi_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`,
|
|
30
75
|
timestamp: new Date().toISOString(),
|
|
31
76
|
toolName: toolName || 'unknown',
|
|
32
77
|
serverName: serverName || 'default',
|
|
33
78
|
latencyMs: typeof latencyMs === 'number' ? latencyMs : 0,
|
|
34
|
-
|
|
79
|
+
// Backward-compatible boolean: blocked and failed are both non-success.
|
|
80
|
+
success: normalizedOutcome === TOOL_CALL_OUTCOMES.SUCCESSFUL,
|
|
81
|
+
outcome: normalizedOutcome,
|
|
35
82
|
agentId: agentId || 'unknown',
|
|
83
|
+
clientId: clientId || metadata?.clientId || null,
|
|
84
|
+
sessionId: sessionId || metadata?.sessionId || null,
|
|
85
|
+
writeRiskTier: writeRiskTier || metadata?.writeRiskTier || null,
|
|
36
86
|
metadata: metadata || {},
|
|
37
87
|
};
|
|
38
88
|
fs.appendFileSync(logPath, `${JSON.stringify(entry)}\n`);
|
|
@@ -45,11 +95,32 @@ function percentile(sorted, quantile) {
|
|
|
45
95
|
return sorted[Math.max(0, index)];
|
|
46
96
|
}
|
|
47
97
|
|
|
98
|
+
function emptyOutcomeCounts() {
|
|
99
|
+
return {
|
|
100
|
+
successful: 0,
|
|
101
|
+
failed: 0,
|
|
102
|
+
blocked: 0,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function pickDominantTier(tierCounts = {}) {
|
|
107
|
+
let best = null;
|
|
108
|
+
let bestCount = 0;
|
|
109
|
+
for (const [tier, count] of Object.entries(tierCounts)) {
|
|
110
|
+
if (count > bestCount) {
|
|
111
|
+
best = tier;
|
|
112
|
+
bestCount = count;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return best;
|
|
116
|
+
}
|
|
117
|
+
|
|
48
118
|
function computeToolKpis({ periodHours = 24, feedbackDir } = {}) {
|
|
49
119
|
const entries = readJsonl(getKpiLogPath({ feedbackDir }));
|
|
50
120
|
const cutoff = Date.now() - periodHours * 60 * 60 * 1000;
|
|
51
121
|
const recent = entries.filter((entry) => new Date(entry.timestamp).getTime() > cutoff);
|
|
52
122
|
const byTool = {};
|
|
123
|
+
const outcomes = emptyOutcomeCounts();
|
|
53
124
|
for (const entry of recent) {
|
|
54
125
|
const key = entry.toolName;
|
|
55
126
|
if (!byTool[key]) {
|
|
@@ -58,11 +129,27 @@ function computeToolKpis({ periodHours = 24, feedbackDir } = {}) {
|
|
|
58
129
|
calls: [],
|
|
59
130
|
successes: 0,
|
|
60
131
|
failures: 0,
|
|
132
|
+
blocked: 0,
|
|
133
|
+
outcomes: emptyOutcomeCounts(),
|
|
134
|
+
tierCounts: {},
|
|
61
135
|
};
|
|
62
136
|
}
|
|
63
137
|
byTool[key].calls.push(entry.latencyMs);
|
|
64
|
-
|
|
65
|
-
|
|
138
|
+
const tier = entry.writeRiskTier || entry.metadata?.writeRiskTier || null;
|
|
139
|
+
if (tier) {
|
|
140
|
+
byTool[key].tierCounts[tier] = (byTool[key].tierCounts[tier] || 0) + 1;
|
|
141
|
+
}
|
|
142
|
+
const normalized = normalizeToolCallOutcome(entry);
|
|
143
|
+
byTool[key].outcomes[normalized] += 1;
|
|
144
|
+
outcomes[normalized] += 1;
|
|
145
|
+
if (normalized === TOOL_CALL_OUTCOMES.SUCCESSFUL) {
|
|
146
|
+
byTool[key].successes += 1;
|
|
147
|
+
} else if (normalized === TOOL_CALL_OUTCOMES.BLOCKED) {
|
|
148
|
+
byTool[key].blocked += 1;
|
|
149
|
+
byTool[key].failures += 1;
|
|
150
|
+
} else {
|
|
151
|
+
byTool[key].failures += 1;
|
|
152
|
+
}
|
|
66
153
|
}
|
|
67
154
|
const tools = Object.values(byTool)
|
|
68
155
|
.map((tool) => {
|
|
@@ -77,6 +164,9 @@ function computeToolKpis({ periodHours = 24, feedbackDir } = {}) {
|
|
|
77
164
|
p95: Math.round(percentile(sorted, 95)),
|
|
78
165
|
successes: tool.successes,
|
|
79
166
|
failures: tool.failures,
|
|
167
|
+
blocked: tool.blocked,
|
|
168
|
+
outcomes: tool.outcomes,
|
|
169
|
+
writeRiskTier: pickDominantTier(tool.tierCounts),
|
|
80
170
|
};
|
|
81
171
|
})
|
|
82
172
|
.sort((left, right) => right.requestCount - left.requestCount);
|
|
@@ -84,9 +174,18 @@ function computeToolKpis({ periodHours = 24, feedbackDir } = {}) {
|
|
|
84
174
|
const byServer = {};
|
|
85
175
|
for (const entry of recent) {
|
|
86
176
|
const key = entry.serverName;
|
|
87
|
-
if (!byServer[key])
|
|
177
|
+
if (!byServer[key]) {
|
|
178
|
+
byServer[key] = {
|
|
179
|
+
serverName: key,
|
|
180
|
+
total: 0,
|
|
181
|
+
successes: 0,
|
|
182
|
+
outcomes: emptyOutcomeCounts(),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
88
185
|
byServer[key].total += 1;
|
|
89
|
-
|
|
186
|
+
const normalized = normalizeToolCallOutcome(entry);
|
|
187
|
+
byServer[key].outcomes[normalized] += 1;
|
|
188
|
+
if (normalized === TOOL_CALL_OUTCOMES.SUCCESSFUL) byServer[key].successes += 1;
|
|
90
189
|
}
|
|
91
190
|
const servers = Object.values(byServer).map((server) => ({
|
|
92
191
|
serverName: server.serverName,
|
|
@@ -94,11 +193,13 @@ function computeToolKpis({ periodHours = 24, feedbackDir } = {}) {
|
|
|
94
193
|
successRate: server.total > 0
|
|
95
194
|
? Math.round((server.successes / server.total) * 1000) / 10
|
|
96
195
|
: 100,
|
|
196
|
+
outcomes: server.outcomes,
|
|
97
197
|
}));
|
|
98
198
|
return {
|
|
99
199
|
periodHours,
|
|
100
200
|
totalCalls: recent.length,
|
|
101
201
|
evidenceStatus: recent.length > 0 ? 'measured' : 'insufficient_evidence',
|
|
202
|
+
outcomes,
|
|
102
203
|
tools,
|
|
103
204
|
servers,
|
|
104
205
|
};
|
|
@@ -116,9 +217,11 @@ function getAtRiskTools({
|
|
|
116
217
|
}
|
|
117
218
|
|
|
118
219
|
module.exports = {
|
|
220
|
+
TOOL_CALL_OUTCOMES,
|
|
119
221
|
computeToolKpis,
|
|
120
222
|
getAtRiskTools,
|
|
121
223
|
getKpiLogPath,
|
|
224
|
+
normalizeToolCallOutcome,
|
|
122
225
|
percentile,
|
|
123
226
|
recordToolCall,
|
|
124
227
|
};
|
package/scripts/tool-registry.js
CHANGED
|
@@ -12,24 +12,68 @@ function humanizeTitle(name) {
|
|
|
12
12
|
.join(' ');
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// WriteGuard-style write risk tiers mapped onto existing MCP annotations.
|
|
16
|
+
// Explicit annotations.writeRiskTier always wins; otherwise derive from hints + name.
|
|
17
|
+
const WRITE_RISK_TIERS = Object.freeze({
|
|
18
|
+
READ_ONLY: 'read-only',
|
|
19
|
+
MINIMAL_IMPACT: 'minimal-impact',
|
|
20
|
+
CONTAINED_WRITE: 'contained-write',
|
|
21
|
+
CRITICAL: 'critical',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const CRITICAL_WRITE_NAME_RE = /(approve_protected|satisfy_gate|break[_-]?glass|create_purchase|reserve_purchase|reconcile_purchase|record_broker|verify_broker|reconcile_broker|parallel_workflow|bootstrap_internal_agent|run_managed_lesson_agent|run_harness|run_autoresearch)/i;
|
|
25
|
+
const MINIMAL_WRITE_NAME_RE = /(append_feedback_context|open_feedback_session|request_human_escalation|report_product_issue|native_messaging_audit|detect_noop)/i;
|
|
26
|
+
|
|
27
|
+
function inferWriteRiskTier(toolName, annotations = {}) {
|
|
28
|
+
if (annotations.writeRiskTier) return annotations.writeRiskTier;
|
|
29
|
+
if (annotations.readOnlyHint === true) return WRITE_RISK_TIERS.READ_ONLY;
|
|
30
|
+
const name = String(toolName || '');
|
|
31
|
+
if (CRITICAL_WRITE_NAME_RE.test(name)) return WRITE_RISK_TIERS.CRITICAL;
|
|
32
|
+
if (MINIMAL_WRITE_NAME_RE.test(name)) return WRITE_RISK_TIERS.MINIMAL_IMPACT;
|
|
33
|
+
// Destructive or unhinted tools default to contained write (conservative).
|
|
34
|
+
return WRITE_RISK_TIERS.CONTAINED_WRITE;
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
function readOnlyTool(tool) {
|
|
38
|
+
const title = tool.title || humanizeTitle(tool.name);
|
|
16
39
|
return {
|
|
17
40
|
...tool,
|
|
18
|
-
title
|
|
41
|
+
title,
|
|
19
42
|
annotations: {
|
|
20
|
-
title
|
|
43
|
+
title,
|
|
21
44
|
readOnlyHint: true,
|
|
45
|
+
thumbgateScope: 'mcp:read',
|
|
46
|
+
writeRiskTier: WRITE_RISK_TIERS.READ_ONLY,
|
|
22
47
|
},
|
|
23
48
|
};
|
|
24
49
|
}
|
|
25
50
|
|
|
51
|
+
function inferThumbgateScope(toolName, annotations = {}) {
|
|
52
|
+
if (annotations.thumbgateScope) return annotations.thumbgateScope;
|
|
53
|
+
if (annotations.readOnlyHint === true) return 'mcp:read';
|
|
54
|
+
const name = String(toolName || '');
|
|
55
|
+
if (/(^|_)feedback($|_)|capture_memory_feedback|capture_feedback/.test(name)) {
|
|
56
|
+
return 'mcp:feedback';
|
|
57
|
+
}
|
|
58
|
+
if (/(^|_)gate($|_)|prevention_rules|enforcement_matrix|satisfy_gate/.test(name)) {
|
|
59
|
+
return 'mcp:gates';
|
|
60
|
+
}
|
|
61
|
+
return 'mcp:write';
|
|
62
|
+
}
|
|
63
|
+
|
|
26
64
|
function destructiveTool(tool) {
|
|
65
|
+
const title = tool.title || humanizeTitle(tool.name);
|
|
66
|
+
const baseAnnotations = {
|
|
67
|
+
title,
|
|
68
|
+
destructiveHint: true,
|
|
69
|
+
thumbgateScope: inferThumbgateScope(tool.name, { destructiveHint: true }),
|
|
70
|
+
};
|
|
27
71
|
return {
|
|
28
72
|
...tool,
|
|
29
|
-
title
|
|
73
|
+
title,
|
|
30
74
|
annotations: {
|
|
31
|
-
|
|
32
|
-
|
|
75
|
+
...baseAnnotations,
|
|
76
|
+
writeRiskTier: inferWriteRiskTier(tool.name, baseAnnotations),
|
|
33
77
|
},
|
|
34
78
|
};
|
|
35
79
|
}
|
|
@@ -60,6 +104,20 @@ const GOAL_CONTRACT_SCHEMA = {
|
|
|
60
104
|
},
|
|
61
105
|
};
|
|
62
106
|
|
|
107
|
+
const CONTEXT_LIST_BLOCKS = ['businessData', 'examples', 'procedures', 'constraints', 'rubric'];
|
|
108
|
+
const NON_EMPTY_STRING = { type: 'string', minLength: 1 };
|
|
109
|
+
const CONTEXT_ENVELOPE_SCHEMA = {
|
|
110
|
+
type: 'object',
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
required: ['goal', ...CONTEXT_LIST_BLOCKS],
|
|
113
|
+
properties: {
|
|
114
|
+
goal: NON_EMPTY_STRING,
|
|
115
|
+
...Object.fromEntries(CONTEXT_LIST_BLOCKS.map((key) => [
|
|
116
|
+
key, { type: 'array', minItems: 1, items: NON_EMPTY_STRING },
|
|
117
|
+
])),
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
63
121
|
const TASK_OUTCOME_INPUT_SCHEMA = {
|
|
64
122
|
type: 'object',
|
|
65
123
|
additionalProperties: false,
|
|
@@ -207,6 +265,16 @@ const TOOLS = [
|
|
|
207
265
|
budgetCompliant: { type: 'boolean' },
|
|
208
266
|
},
|
|
209
267
|
},
|
|
268
|
+
memorySource: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
additionalProperties: false,
|
|
271
|
+
description: 'Provenance for content being considered for durable memory. External email, web, document, file, webhook, browser, or tool-output content should be marked untrusted.',
|
|
272
|
+
properties: {
|
|
273
|
+
type: { type: 'string' },
|
|
274
|
+
identifier: { type: 'string' },
|
|
275
|
+
trust: { type: 'string', enum: ['trusted', 'untrusted'] },
|
|
276
|
+
},
|
|
277
|
+
},
|
|
210
278
|
},
|
|
211
279
|
},
|
|
212
280
|
}),
|
|
@@ -359,6 +427,44 @@ const TOOLS = [
|
|
|
359
427
|
type: 'number',
|
|
360
428
|
description: 'Optional total character budget for compacted feedback entries.',
|
|
361
429
|
},
|
|
430
|
+
matryoshkaEmbedding: {
|
|
431
|
+
type: 'boolean',
|
|
432
|
+
description: 'When true, include Matryoshka embedding compaction footprint tiers. Default: true for MCP plan_context_footprint.',
|
|
433
|
+
},
|
|
434
|
+
matryoshkaOptions: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
description: 'Optional Matryoshka compaction knobs. Invalid dimensions/counts are rejected.',
|
|
437
|
+
properties: {
|
|
438
|
+
fullDimension: {
|
|
439
|
+
type: 'integer',
|
|
440
|
+
minimum: 1,
|
|
441
|
+
description: 'Full embedding dimension before truncation. Default: 1536.',
|
|
442
|
+
},
|
|
443
|
+
targetDimensions: {
|
|
444
|
+
type: 'array',
|
|
445
|
+
description: 'Truncation tiers to plan (each <= fullDimension). Default: [768,512,256,128].',
|
|
446
|
+
items: { type: 'integer', minimum: 1 },
|
|
447
|
+
},
|
|
448
|
+
itemCount: {
|
|
449
|
+
type: 'integer',
|
|
450
|
+
minimum: 1,
|
|
451
|
+
description: 'Number of vectors for memory estimates. Default: 1000.',
|
|
452
|
+
},
|
|
453
|
+
qualityVerified: {
|
|
454
|
+
type: 'boolean',
|
|
455
|
+
description: 'Set true only when provider-specific golden RAG eval evidence is available.',
|
|
456
|
+
},
|
|
457
|
+
behaviorPreserved: {
|
|
458
|
+
type: 'boolean',
|
|
459
|
+
description: 'Must be true with qualityVerified to claim behavior preservation.',
|
|
460
|
+
},
|
|
461
|
+
accuracyLossEstimatePct: {
|
|
462
|
+
type: 'number',
|
|
463
|
+
description: 'Measured accuracy loss percent from golden eval (only used when qualityVerified).',
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
additionalProperties: false,
|
|
467
|
+
},
|
|
362
468
|
},
|
|
363
469
|
},
|
|
364
470
|
}),
|
|
@@ -821,6 +927,8 @@ const TOOLS = [
|
|
|
821
927
|
maxItems: { type: 'number' },
|
|
822
928
|
maxChars: { type: 'number' },
|
|
823
929
|
namespaces: { type: 'array', items: { type: 'string' } },
|
|
930
|
+
strategy: { type: 'string', enum: ['summarize-then-expand'] },
|
|
931
|
+
contextEnvelope: CONTEXT_ENVELOPE_SCHEMA,
|
|
824
932
|
},
|
|
825
933
|
},
|
|
826
934
|
}),
|
|
@@ -1070,6 +1178,11 @@ const TOOLS = [
|
|
|
1070
1178
|
actionId: { type: 'string', description: 'Identifier of the tracked action this receipt pairs with' },
|
|
1071
1179
|
toolName: { type: 'string', description: 'Name of the tool that was invoked' },
|
|
1072
1180
|
toolInput: { type: 'object', description: 'Structured input the tool was called with' },
|
|
1181
|
+
target: { type: 'string', description: 'Optional target path, host, or resource the action touched' },
|
|
1182
|
+
principal: { type: 'string', description: 'Optional agent/principal identity recorded on the receipt' },
|
|
1183
|
+
decision: { type: 'string', description: 'Optional gate decision (allow/warn/deny)' },
|
|
1184
|
+
idempotencyKey: { type: 'string', description: 'Optional idempotency key for dedupe of the signed receipt' },
|
|
1185
|
+
providerEventId: { type: 'string', description: 'Optional upstream provider event id' },
|
|
1073
1186
|
diff: { type: 'string', description: 'Optional unified diff or change summary produced by the action' },
|
|
1074
1187
|
exitCode: { type: 'number', description: 'Optional command exit code outcome' },
|
|
1075
1188
|
testOutcome: { type: 'string', description: 'Optional test outcome (e.g. passed, failed, 12/12)' },
|
|
@@ -1089,6 +1202,69 @@ const TOOLS = [
|
|
|
1089
1202
|
},
|
|
1090
1203
|
},
|
|
1091
1204
|
}),
|
|
1205
|
+
readOnlyTool({
|
|
1206
|
+
name: 'verify_broker_execution_receipt',
|
|
1207
|
+
title: 'Verify Broker Execution Receipt',
|
|
1208
|
+
description: 'Verify a broker-signed execution receipt (Ed25519). Rejects agent-minted or rewritten "proof". ThumbGate verifies; the broker holds provider credentials and signs.',
|
|
1209
|
+
inputSchema: {
|
|
1210
|
+
type: 'object',
|
|
1211
|
+
properties: {
|
|
1212
|
+
receipt: { type: 'object', description: 'Broker execution receipt object (schema broker-execution-receipt-v1)' },
|
|
1213
|
+
},
|
|
1214
|
+
},
|
|
1215
|
+
}),
|
|
1216
|
+
// Registered for discovery/docs only. Default MCP allowlist excludes this tool,
|
|
1217
|
+
// and the server refuses agent-surface issuance even if a host key is present.
|
|
1218
|
+
destructiveTool({
|
|
1219
|
+
name: 'issue_broker_execution_receipt',
|
|
1220
|
+
title: 'Issue Broker Execution Receipt (broker host only)',
|
|
1221
|
+
description: 'Not available on the agent MCP surface. Brokers issue receipts out-of-band via scripts/broker-execution-receipts.js with THUMBGATE_BROKER_SIGNING_KEY.',
|
|
1222
|
+
inputSchema: {
|
|
1223
|
+
type: 'object',
|
|
1224
|
+
required: ['principal', 'target', 'idempotencyKey'],
|
|
1225
|
+
properties: {
|
|
1226
|
+
principal: { type: 'object', description: '{ id, kind } of the acting agent/user' },
|
|
1227
|
+
target: { type: 'object', description: '{ provider, action, resource? }' },
|
|
1228
|
+
decision: { type: 'string', description: 'allow | execute | deny' },
|
|
1229
|
+
idempotencyKey: { type: 'string' },
|
|
1230
|
+
providerEventId: { type: 'string' },
|
|
1231
|
+
brokerId: { type: 'string' },
|
|
1232
|
+
previousReceiptHash: { type: 'string' },
|
|
1233
|
+
metadata: { type: 'object' },
|
|
1234
|
+
},
|
|
1235
|
+
},
|
|
1236
|
+
}),
|
|
1237
|
+
destructiveTool({
|
|
1238
|
+
name: 'record_broker_execution_receipt',
|
|
1239
|
+
title: 'Record Broker Execution Receipt',
|
|
1240
|
+
description: 'Append a verified broker-signed execution receipt to the local hash-chained ledger.',
|
|
1241
|
+
inputSchema: {
|
|
1242
|
+
type: 'object',
|
|
1243
|
+
properties: {
|
|
1244
|
+
receipt: { type: 'object', description: 'Verified broker execution receipt' },
|
|
1245
|
+
},
|
|
1246
|
+
},
|
|
1247
|
+
}),
|
|
1248
|
+
readOnlyTool({
|
|
1249
|
+
name: 'get_broker_execution_receipts',
|
|
1250
|
+
title: 'Get Broker Execution Receipts',
|
|
1251
|
+
description: 'Read recent broker-signed execution receipts from the local ledger.',
|
|
1252
|
+
inputSchema: {
|
|
1253
|
+
type: 'object',
|
|
1254
|
+
properties: {
|
|
1255
|
+
limit: { type: 'number', description: 'Max receipts to return (default 20)' },
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
}),
|
|
1259
|
+
readOnlyTool({
|
|
1260
|
+
name: 'reconcile_broker_receipt_chain',
|
|
1261
|
+
title: 'Reconcile Broker Receipt Chain',
|
|
1262
|
+
description: 'Verify signatures and hash-chain continuity for all broker execution receipts on the ledger.',
|
|
1263
|
+
inputSchema: {
|
|
1264
|
+
type: 'object',
|
|
1265
|
+
properties: {},
|
|
1266
|
+
},
|
|
1267
|
+
}),
|
|
1092
1268
|
destructiveTool({
|
|
1093
1269
|
name: 'record_task_outcome',
|
|
1094
1270
|
title: 'Record Verified Task Outcome',
|
|
@@ -1164,11 +1340,14 @@ const TOOLS = [
|
|
|
1164
1340
|
id: { type: 'string', minLength: 1 },
|
|
1165
1341
|
kind: { type: 'string', enum: ['agent', 'service', 'human'] },
|
|
1166
1342
|
displayName: { type: 'string' },
|
|
1343
|
+
role: { type: 'string', enum: ['admin', 'reviewer'] },
|
|
1167
1344
|
},
|
|
1168
1345
|
},
|
|
1169
1346
|
evidence: { type: 'array', minItems: 1, items: { type: 'string', minLength: 1 } },
|
|
1170
1347
|
ttlMs: { type: 'number', minimum: 1 },
|
|
1171
1348
|
idempotencyKey: { type: 'string' },
|
|
1349
|
+
approvalContextDigest: { type: 'string', pattern: '^[a-fA-F0-9]{64}$' },
|
|
1350
|
+
requiredReviewerRole: { type: 'string', enum: ['admin'] },
|
|
1172
1351
|
},
|
|
1173
1352
|
},
|
|
1174
1353
|
}),
|
|
@@ -1841,10 +2020,19 @@ const NORMALIZED_TOOLS = TOOLS.map((tool) => {
|
|
|
1841
2020
|
...existing,
|
|
1842
2021
|
...(hasHint ? {} : { destructiveHint: true }),
|
|
1843
2022
|
};
|
|
2023
|
+
if (!annotations.thumbgateScope) {
|
|
2024
|
+
annotations.thumbgateScope = inferThumbgateScope(tool.name, annotations);
|
|
2025
|
+
}
|
|
2026
|
+
if (!annotations.writeRiskTier) {
|
|
2027
|
+
annotations.writeRiskTier = inferWriteRiskTier(tool.name, annotations);
|
|
2028
|
+
}
|
|
1844
2029
|
return { ...tool, title, annotations };
|
|
1845
2030
|
});
|
|
1846
2031
|
|
|
1847
2032
|
module.exports = {
|
|
1848
2033
|
TOOLS: NORMALIZED_TOOLS,
|
|
2034
|
+
WRITE_RISK_TIERS,
|
|
1849
2035
|
humanizeTitle,
|
|
2036
|
+
inferThumbgateScope,
|
|
2037
|
+
inferWriteRiskTier,
|
|
1850
2038
|
};
|
|
@@ -456,13 +456,25 @@ function assertSelectOnly(query) {
|
|
|
456
456
|
}
|
|
457
457
|
|
|
458
458
|
function readSqliteCount(repoRoot, verifier) {
|
|
459
|
-
const Database = require('better-sqlite3');
|
|
460
459
|
const dbPath = resolveSafePath(repoRoot, verifier.dbPath || verifier.path);
|
|
461
460
|
if (!fs.existsSync(dbPath)) {
|
|
462
461
|
throw new Error(`sqlite database not found: ${verifier.dbPath || verifier.path}`);
|
|
463
462
|
}
|
|
464
463
|
const query = assertSelectOnly(verifier.query);
|
|
465
|
-
|
|
464
|
+
let db = null;
|
|
465
|
+
let isBuiltin = false;
|
|
466
|
+
try {
|
|
467
|
+
const BetterDatabase = require('better-sqlite3');
|
|
468
|
+
db = new BetterDatabase(dbPath, { readonly: true, fileMustExist: true });
|
|
469
|
+
} catch {
|
|
470
|
+
try {
|
|
471
|
+
const sqlite = require('node:sqlite');
|
|
472
|
+
db = new sqlite.DatabaseSync(dbPath, { readOnly: true, open: true });
|
|
473
|
+
isBuiltin = true;
|
|
474
|
+
} catch {
|
|
475
|
+
throw new Error('SQLite driver not available (install better-sqlite3 or use Node 22.5+ node:sqlite)');
|
|
476
|
+
}
|
|
477
|
+
}
|
|
466
478
|
try {
|
|
467
479
|
const row = db.prepare(query).get();
|
|
468
480
|
if (!row || typeof row !== 'object') {
|