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.
Files changed (200) hide show
  1. package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
  2. package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
  3. package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
  4. package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
  5. package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
  6. package/.claude-plugin/plugin.json +1 -1
  7. package/.well-known/llms.txt +1 -0
  8. package/.well-known/mcp/server-card.json +1 -1
  9. package/CONTRIBUTING.md +95 -0
  10. package/README.md +195 -632
  11. package/THIRD_PARTY_NOTICES.md +89 -0
  12. package/adapters/claude/.mcp.json +2 -2
  13. package/adapters/forge/forge.yaml +3 -3
  14. package/adapters/future-agi/.mcp.json +8 -0
  15. package/adapters/future-agi/FUTURE_AGI.md +23 -0
  16. package/adapters/future-agi/config.toml +3 -0
  17. package/adapters/future-agi/future-agi-bridge.js +9 -0
  18. package/adapters/future-agi/opencode.json +8 -0
  19. package/adapters/herdr/herdr-plugin.toml +18 -0
  20. package/adapters/mcp/server-stdio.js +238 -25
  21. package/adapters/opencode/opencode.json +1 -1
  22. package/adapters/workos/WORKOS.md +52 -0
  23. package/bin/cli.js +373 -5
  24. package/bin/futureagi-bridge +9 -0
  25. package/config/gate-templates.json +653 -4
  26. package/config/gates/actor-critic-audit.json +34 -0
  27. package/config/gates/default.json +21 -2
  28. package/config/gates/five-walls-governance.json +34 -0
  29. package/config/gates/future-agi-guardrails.json +34 -0
  30. package/config/gates/radware-threat-defense-2026.json +61 -0
  31. package/config/gates/simatree-data-governance.json +33 -0
  32. package/config/mcp-allowlists.json +4 -0
  33. package/config/merge-quality-checks.json +10 -1
  34. package/config/model-candidates.json +382 -24
  35. package/config/model-tiers.json +18 -0
  36. package/config/post-deploy-marketing-pages.json +10 -0
  37. package/config/progressive/01-wire-only.json +11 -0
  38. package/config/progressive/02-dashboard-empty-ok.json +10 -0
  39. package/config/progressive/03-one-lesson.json +10 -0
  40. package/config/progressive/04-warn-fires.json +11 -0
  41. package/config/progressive/05-strict-optional.json +11 -0
  42. package/config/progressive/README.md +15 -0
  43. package/config/schemas/broker-execution-receipt.schema.json +139 -0
  44. package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
  45. package/conformance/provider-attestation/vectors.json +320 -0
  46. package/docs/specs/provider-execution-attestation-v1.md +69 -0
  47. package/openapi/openapi.yaml +15 -0
  48. package/package.json +401 -147
  49. package/public/about.html +2 -2
  50. package/public/ai-malpractice-prevention.html +7 -7
  51. package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
  52. package/public/blog/a-receipt-is-not-world-state.html +388 -0
  53. package/public/blog/git-at-agent-scale.html +374 -0
  54. package/public/blog/no-llm-in-the-gate.html +133 -0
  55. package/public/blog.html +80 -0
  56. package/public/case-studies.html +16 -1
  57. package/public/compare.html +28 -0
  58. package/public/diagnostic.html +216 -7
  59. package/public/docs/connectors.html +39 -0
  60. package/public/federal.html +2 -2
  61. package/public/founders.html +639 -0
  62. package/public/index.html +87 -9
  63. package/public/install.html +8 -8
  64. package/public/learn.html +39 -0
  65. package/public/numbers.html +2 -2
  66. package/public/peter.html +310 -0
  67. package/public/platform-partners.html +119 -0
  68. package/public/pricing.html +24 -3
  69. package/public/privacy.html +117 -0
  70. package/public/pro.html +17 -0
  71. package/public/support.html +62 -0
  72. package/public/terms.html +130 -0
  73. package/public/third-party-notices.html +95 -0
  74. package/public/yt.html +351 -0
  75. package/scripts/action-receipts.js +133 -3
  76. package/scripts/adaptive-governance-arena.js +349 -0
  77. package/scripts/admin-override.js +205 -0
  78. package/scripts/agent-action-inventory.js +869 -0
  79. package/scripts/agent-audit-trace.js +42 -2
  80. package/scripts/agent-egress-policy.js +1117 -0
  81. package/scripts/agent-memory-lifecycle.js +141 -2
  82. package/scripts/agent-operations-planner.js +441 -1
  83. package/scripts/agent-readiness.js +68 -0
  84. package/scripts/agent-security-central.js +647 -0
  85. package/scripts/allowlist-bridge-honesty.js +417 -0
  86. package/scripts/async-job-runner.js +102 -11
  87. package/scripts/audit-trail.js +212 -0
  88. package/scripts/auto-promote-gates.js +178 -27
  89. package/scripts/billing.js +1 -1
  90. package/scripts/broker-execution-receipts.js +719 -0
  91. package/scripts/budget-aware-gates-proof.js +423 -0
  92. package/scripts/claude-feedback-sync.js +29 -3
  93. package/scripts/claw-harness-production.js +237 -0
  94. package/scripts/cli-progress.js +111 -0
  95. package/scripts/cli-schema.js +163 -1
  96. package/scripts/codex-runbook-flywheel.js +318 -0
  97. package/scripts/context-footprint.js +186 -0
  98. package/scripts/contextfs.js +143 -61
  99. package/scripts/dashboard-limits.js +27 -0
  100. package/scripts/dashboard.js +279 -9
  101. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  102. package/scripts/docker-sandbox-planner.js +18 -0
  103. package/scripts/double-blind-eval-protocol.js +252 -0
  104. package/scripts/edotenv-rl-gateway.js +259 -0
  105. package/scripts/ensure-production-search-corpus.js +162 -0
  106. package/scripts/eval-holdout.js +311 -0
  107. package/scripts/feedback-aggregate.js +21 -2
  108. package/scripts/feedback-loop.js +87 -5
  109. package/scripts/feedback-quality.js +9 -0
  110. package/scripts/file-ledger-lock.js +4 -1
  111. package/scripts/financial-control-plane.js +41 -1
  112. package/scripts/find-dormant-requires.js +118 -0
  113. package/scripts/fs-utils.js +84 -8
  114. package/scripts/gate-stats.js +2 -2
  115. package/scripts/gates-engine.js +859 -58
  116. package/scripts/generate-case-study-outreach.js +24 -15
  117. package/scripts/git-at-scale.js +628 -0
  118. package/scripts/governance-conflict-audit.js +1650 -0
  119. package/scripts/governance-difficulty-curriculum.js +328 -0
  120. package/scripts/graphrag-retrieval.js +275 -0
  121. package/scripts/gurobi-optimizer.js +324 -0
  122. package/scripts/gurobi_optimizer.py +485 -0
  123. package/scripts/harness-selector.js +82 -1
  124. package/scripts/hidden-entry-points.js +284 -0
  125. package/scripts/human-escalation.js +199 -1
  126. package/scripts/hybrid-feedback-context.js +152 -19
  127. package/scripts/intent-governed-execution.js +602 -0
  128. package/scripts/intervention-policy.js +123 -20
  129. package/scripts/jit-harness-compose.js +628 -0
  130. package/scripts/jsonl-watcher.js +10 -0
  131. package/scripts/lesson-embedding-index.js +95 -12
  132. package/scripts/lesson-retrieval.js +105 -19
  133. package/scripts/local-model-profile.js +19 -2
  134. package/scripts/mailer/resend-mailer.js +1 -1
  135. package/scripts/matryoshka-embedding.js +235 -0
  136. package/scripts/mcp-oauth.js +42 -4
  137. package/scripts/mcp-session-handles.js +1016 -0
  138. package/scripts/mcp-wiring-doctor.js +314 -0
  139. package/scripts/memory-firewall.js +115 -2
  140. package/scripts/memory-scope-readiness.js +299 -0
  141. package/scripts/memory-vs-rag-route.js +161 -0
  142. package/scripts/model-tier-router.js +148 -21
  143. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  144. package/scripts/openui-catalog-compose-honesty.js +593 -0
  145. package/scripts/operational-integrity.js +19 -1
  146. package/scripts/override-audit.js +213 -0
  147. package/scripts/package-manager-honesty-doctor.js +458 -0
  148. package/scripts/pr-manager.js +63 -1
  149. package/scripts/prove-herdr-adapter.js +52 -0
  150. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  151. package/scripts/prove-workos.js +73 -0
  152. package/scripts/provider-attestation-conformance.js +192 -0
  153. package/scripts/provider-receipt-contract.js +136 -0
  154. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  155. package/scripts/radware-threat-defense.js +280 -0
  156. package/scripts/rag-embedding-identity.js +221 -0
  157. package/scripts/rag-precision-guardrails.js +112 -2
  158. package/scripts/remote-feedback-capture.js +159 -0
  159. package/scripts/research-agent-harness.js +256 -0
  160. package/scripts/rsi-safety-hillclimb.js +200 -0
  161. package/scripts/rule-sprawl.js +188 -0
  162. package/scripts/schedule-manager.js +147 -0
  163. package/scripts/self-heal.js +8 -0
  164. package/scripts/session-lease.js +415 -0
  165. package/scripts/simatree-data-governance.js +347 -0
  166. package/scripts/slo-alert-engine.js +172 -7
  167. package/scripts/solver-parity.js +539 -0
  168. package/scripts/stealth-memory-injection-gate.js +333 -0
  169. package/scripts/switchyard-router.js +366 -0
  170. package/scripts/telemetry-analytics.js +84 -27
  171. package/scripts/temporal-decay-weighting.js +138 -0
  172. package/scripts/test-all.js +165 -0
  173. package/scripts/token-savings.js +42 -0
  174. package/scripts/tool-kpi-tracker.js +108 -5
  175. package/scripts/tool-registry.js +193 -5
  176. package/scripts/universal-claim-evaluator.js +14 -2
  177. package/scripts/vector-store.js +279 -9
  178. package/scripts/workflow-notebook.js +391 -0
  179. package/scripts/workflow-sentinel.js +111 -12
  180. package/scripts/workos-production-guard.js +260 -0
  181. package/scripts/workspace-search-route.js +515 -0
  182. package/server.json +2 -2
  183. package/src/agent-identity-boundary.js +76 -0
  184. package/src/agent-retrieval-cache.js +155 -0
  185. package/src/alert-noise-ledger.js +502 -0
  186. package/src/api/server.js +802 -185
  187. package/src/git-fast-cache.js +220 -0
  188. package/src/git-wal-sync.js +156 -0
  189. package/src/hash-anchored-edit.js +82 -0
  190. package/src/hermes-platform-protocol.js +475 -0
  191. package/src/hermes-sync-plane.js +241 -0
  192. package/src/index.js +30 -1
  193. package/src/iso42001-compliance-guard.js +97 -0
  194. package/src/latency-budget.js +244 -0
  195. package/src/mcp-writeguard.js +316 -0
  196. package/src/miminions-adapter.js +106 -0
  197. package/src/pipeline-compass.js +104 -0
  198. package/src/ppl-alert-pipeline.js +284 -0
  199. package/src/rendezvous-router.js +90 -0
  200. 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
- success: success !== false,
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
- if (entry.success) byTool[key].successes += 1;
65
- else byTool[key].failures += 1;
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]) byServer[key] = { serverName: key, total: 0, successes: 0 };
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
- if (entry.success) byServer[key].successes += 1;
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
  };
@@ -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: tool.title || humanizeTitle(tool.name),
41
+ title,
19
42
  annotations: {
20
- title: tool.title || humanizeTitle(tool.name),
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: tool.title || humanizeTitle(tool.name),
73
+ title,
30
74
  annotations: {
31
- title: tool.title || humanizeTitle(tool.name),
32
- destructiveHint: true,
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
- const db = new Database(dbPath, { readonly: true, fileMustExist: true });
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') {