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
@@ -0,0 +1,536 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * NVIDIA speculative-decoding AL/D doctor for ThumbGate.
6
+ *
7
+ * Steals measurable mechanics from:
8
+ * https://developer.nvidia.com/blog/co-designing-ai-models-using-speculative-decoding-for-faster-llm-inference/
9
+ *
10
+ * Transfers (only):
11
+ * - speedup ≈ AL / (1 + ρD) at low latency
12
+ * - attention draft length D = 128/G - 1
13
+ * - tile alignment: G × (1 + D) multiple of 128
14
+ * - increase D only while AL gains justify draft cost
15
+ * - pick draft mechanism by AL vs draft overhead (not brand)
16
+ *
17
+ * Does NOT clone TensorRT-LLM, EAGLE training, Model-Optimizer, or SPEED-Bench.
18
+ * Maps findings onto existing checkpoint-speculative-decoding-acceptance gate.
19
+ */
20
+
21
+ const path = require('node:path');
22
+
23
+ const ATTENTION_TILE = 128;
24
+ const DEFAULT_MIN_ACCEPT_LENGTH = 2;
25
+ const SOURCE_URL =
26
+ 'https://developer.nvidia.com/blog/co-designing-ai-models-using-speculative-decoding-for-faster-llm-inference/';
27
+
28
+ function normalizeBoolean(value) {
29
+ if (value === true) return true;
30
+ if (value === false || value === undefined || value === null) return false;
31
+ return /^(1|true|yes|on)$/i.test(String(value).trim());
32
+ }
33
+
34
+ function toNumber(value) {
35
+ if (value === undefined || value === null || value === '') return null;
36
+ const num = Number(value);
37
+ return Number.isFinite(num) ? num : null;
38
+ }
39
+
40
+ function round4(value) {
41
+ return Number(Number(value).toFixed(4));
42
+ }
43
+
44
+ /**
45
+ * Guideline 2: when attention dominates decode, D = 128/G - 1.
46
+ * @param {number} queryHeadsPerKvHead G
47
+ * @returns {number|null}
48
+ */
49
+ function optimalDraftLengthForAttention(queryHeadsPerKvHead) {
50
+ const g = Number(queryHeadsPerKvHead);
51
+ if (!Number.isFinite(g) || g <= 0) return null;
52
+ return Math.max(0, Math.floor(ATTENTION_TILE / g) - 1);
53
+ }
54
+
55
+ /**
56
+ * Guideline 3: prefer D where G × (1 + D) is a multiple of 128.
57
+ */
58
+ function isTileAligned(queryHeadsPerKvHead, draftLength) {
59
+ const g = Number(queryHeadsPerKvHead);
60
+ const d = Number(draftLength);
61
+ if (!Number.isFinite(g) || g <= 0 || !Number.isFinite(d) || d < 0) return false;
62
+ return (g * (1 + d)) % ATTENTION_TILE === 0;
63
+ }
64
+
65
+ /**
66
+ * Next tile-aligned draft length at or above candidate D (or null).
67
+ */
68
+ function nearestTileAlignedDraftLength(queryHeadsPerKvHead, draftLength) {
69
+ const g = Number(queryHeadsPerKvHead);
70
+ let d = Math.max(0, Math.floor(Number(draftLength) || 0));
71
+ if (!Number.isFinite(g) || g <= 0) return null;
72
+ for (let i = 0; i < ATTENTION_TILE; i += 1) {
73
+ if (isTileAligned(g, d + i)) return d + i;
74
+ }
75
+ return null;
76
+ }
77
+
78
+ /** Draft overhead O_d = ρD */
79
+ function draftOverhead(draftDepthRatio, draftLength) {
80
+ const rho = Number(draftDepthRatio);
81
+ const d = Number(draftLength);
82
+ if (!Number.isFinite(rho) || rho < 0 || !Number.isFinite(d) || d < 0) return null;
83
+ return round4(rho * d);
84
+ }
85
+
86
+ /**
87
+ * Low-latency approximation: speedup = AL / (1 + ρD)
88
+ * AL ranges from 1 to 1+D (target always emits one ground-truth token).
89
+ */
90
+ function theoreticalSpeedup(acceptLength, draftLength, draftDepthRatio) {
91
+ const al = Number(acceptLength);
92
+ const d = Number(draftLength);
93
+ const rho = draftDepthRatio === undefined || draftDepthRatio === null
94
+ ? 0
95
+ : Number(draftDepthRatio);
96
+ if (!Number.isFinite(al) || al <= 0) return null;
97
+ if (!Number.isFinite(d) || d < 0) return null;
98
+ if (!Number.isFinite(rho) || rho < 0) return null;
99
+ const denom = 1 + (rho * d);
100
+ if (denom <= 0) return null;
101
+ return round4(al / denom);
102
+ }
103
+
104
+ /**
105
+ * Guideline 4 helper: raising D helps only while AL gain beats added draft cost.
106
+ * Compare speedup at (D, AL) vs (D+deltaD, AL+deltaAL) under fixed ρ.
107
+ */
108
+ function shouldIncreaseDraftLength({
109
+ acceptLength,
110
+ draftLength,
111
+ draftDepthRatio = 0,
112
+ nextAcceptLength,
113
+ nextDraftLength,
114
+ }) {
115
+ const current = theoreticalSpeedup(acceptLength, draftLength, draftDepthRatio);
116
+ const next = theoreticalSpeedup(nextAcceptLength, nextDraftLength, draftDepthRatio);
117
+ if (current === null || next === null) return null;
118
+ return {
119
+ currentSpeedup: current,
120
+ nextSpeedup: next,
121
+ increase: next > current,
122
+ deltaSpeedup: round4(next - current),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Guideline 5 (ThumbGate-shaped): recommend a draft *class*, not a vendor stack.
128
+ * Never claims TensorRT/EAGLE/Model-Optimizer readiness.
129
+ */
130
+ function recommendDraftMechanism(options = {}) {
131
+ const workload = String(options.workload || options.workloadType || 'agentic').trim().toLowerCase();
132
+ const latencyRegion = String(options.latencyRegion || options.latency || 'low').trim().toLowerCase();
133
+ const modelSize = String(options.modelSize || options.size || 'medium').trim().toLowerCase();
134
+ const repetitive = normalizeBoolean(options.repetitive)
135
+ || /repetit|tool.?loop|code.?complete|suffix|ngram/.test(workload);
136
+
137
+ if (repetitive) {
138
+ return {
139
+ mechanism: 'suffix-ngram',
140
+ draftDepthRatioHint: 0,
141
+ reason: 'Repetitive agent/tool token streams get free O(1) drafts; no learned drafter to retrain after target fine-tunes.',
142
+ bestFor: 'high-repetition coding agents and tool-call loops',
143
+ };
144
+ }
145
+
146
+ if (latencyRegion === 'low' && /small|tiny|7b|8b|3b/.test(modelSize)) {
147
+ return {
148
+ mechanism: 'parallel-draft-head',
149
+ draftDepthRatioHint: 0.05,
150
+ reason: 'Small targets at low latency: parallel draft heads keep O_d low even when AL is modest (DFlash/DSpark class).',
151
+ bestFor: 'batch-1 interactive small models',
152
+ };
153
+ }
154
+
155
+ if (/large|70b|120b|405b|moe/.test(modelSize)) {
156
+ return {
157
+ mechanism: 'target-attached-mtp',
158
+ draftDepthRatioHint: 0.02,
159
+ reason: 'Large GPU targets: one-layer MTP-style heads keep draft overhead tiny relative to L_target while AL stays competitive.',
160
+ bestFor: 'large self-hosted targets on GPUs',
161
+ };
162
+ }
163
+
164
+ return {
165
+ mechanism: 'external-small-draft',
166
+ draftDepthRatioHint: 0.15,
167
+ reason: 'Default: small external draft can raise AL, but only if measured O_d still leaves AL/(1+ρD) above 1. Re-measure AL after every target fine-tune.',
168
+ bestFor: 'general agentic workloads with measured AL evidence',
169
+ };
170
+ }
171
+
172
+ function normalizeOptions(options = {}) {
173
+ const queryHeads = toNumber(options['query-heads'] || options.queryHeads);
174
+ const kvHeads = toNumber(options['kv-heads'] || options.kvHeads);
175
+ let g = toNumber(options['query-heads-per-kv'] || options.g || options.G);
176
+ if (g === null && queryHeads !== null && kvHeads !== null && kvHeads > 0) {
177
+ g = queryHeads / kvHeads;
178
+ }
179
+
180
+ return {
181
+ workload: String(options.workload || options.name || 'speculative-decoding').trim() || 'speculative-decoding',
182
+ model: String(options.model || 'target-model').trim() || 'target-model',
183
+ speculativeDecoding: normalizeBoolean(
184
+ options['speculative-decoding'] || options.speculative || options.mtp || options.eagle || options.enabled
185
+ ),
186
+ acceptLength: toNumber(options['accept-length'] || options.al || options.AL),
187
+ draftLength: toNumber(options['draft-length'] || options.d || options.D),
188
+ draftDepthRatio: toNumber(options['draft-depth-ratio'] || options.rho || options['depth-ratio']),
189
+ claimedSpeedup: toNumber(options['claimed-speedup'] || options.speedup || options['claimed-x']),
190
+ minAcceptLength: toNumber(options['min-accept-length']) ?? DEFAULT_MIN_ACCEPT_LENGTH,
191
+ queryHeadsPerKvHead: g,
192
+ queryHeads,
193
+ kvHeads,
194
+ attentionDominated: normalizeBoolean(options['attention-dominated'] || options.attention),
195
+ latencyRegion: String(options['latency-region'] || options.latency || 'low').trim().toLowerCase() || 'low',
196
+ modelSize: String(options['model-size'] || options.size || 'medium').trim().toLowerCase() || 'medium',
197
+ repetitive: normalizeBoolean(options.repetitive),
198
+ nextAcceptLength: toNumber(options['next-accept-length'] || options['al-next']),
199
+ nextDraftLength: toNumber(options['next-draft-length'] || options['d-next']),
200
+ cacheCoherenceEval: normalizeBoolean(options['cache-coherence-eval'] || options['cache-eval']),
201
+ strict: normalizeBoolean(options.strict),
202
+ };
203
+ }
204
+
205
+ function buildFindings(options, metrics, mechanism) {
206
+ const findings = [];
207
+
208
+ if (options.speculativeDecoding && options.acceptLength === null) {
209
+ findings.push({
210
+ id: 'accept_length_missing',
211
+ severity: 'fail',
212
+ gateId: 'checkpoint-speculative-decoding-acceptance',
213
+ message: 'Speculative decoding is enabled but measured accept length (AL) is missing. SPEED-Bench-style AL evidence is required before treating speculation as a speedup.',
214
+ });
215
+ }
216
+
217
+ if (options.acceptLength !== null && options.acceptLength < options.minAcceptLength) {
218
+ findings.push({
219
+ id: 'accept_length_below_floor',
220
+ severity: 'fail',
221
+ gateId: 'checkpoint-speculative-decoding-acceptance',
222
+ message: `Accept length ${options.acceptLength} is below the floor ${options.minAcceptLength}. Do not route production traffic until AL recovers.`,
223
+ });
224
+ }
225
+
226
+ if (
227
+ options.acceptLength !== null
228
+ && options.draftLength !== null
229
+ && options.acceptLength > (1 + options.draftLength)
230
+ ) {
231
+ findings.push({
232
+ id: 'accept_length_impossible',
233
+ severity: 'fail',
234
+ gateId: 'checkpoint-speculative-decoding-acceptance',
235
+ message: `AL=${options.acceptLength} exceeds 1+D=${1 + options.draftLength}. Measured AL must be in [1, 1+D].`,
236
+ });
237
+ }
238
+
239
+ if (
240
+ metrics.theoreticalSpeedup !== null
241
+ && options.claimedSpeedup !== null
242
+ && options.claimedSpeedup > metrics.theoreticalSpeedup + 1e-9
243
+ ) {
244
+ findings.push({
245
+ id: 'claimed_speedup_over_theory',
246
+ severity: 'fail',
247
+ gateId: 'checkpoint-speculative-decoding-acceptance',
248
+ message: `Claimed speedup ${options.claimedSpeedup}x exceeds theoretical AL/(1+ρD)=${metrics.theoreticalSpeedup}x. Reject the throughput claim.`,
249
+ });
250
+ }
251
+
252
+ if (options.speculativeDecoding && !options.cacheCoherenceEval) {
253
+ findings.push({
254
+ id: 'cache_coherence_missing',
255
+ severity: 'warn',
256
+ gateId: 'checkpoint-speculative-decoding-acceptance',
257
+ message: 'Speculation enabled without cache-coherence / rollback evidence. Pair with require-hybrid-prefix-cache-coherence-eval before production.',
258
+ });
259
+ }
260
+
261
+ if (options.attentionDominated && options.queryHeadsPerKvHead !== null) {
262
+ const optimal = metrics.attentionOptimalDraftLength;
263
+ if (optimal !== null && options.draftLength !== null) {
264
+ if (options.draftLength > optimal && !metrics.tileAligned) {
265
+ findings.push({
266
+ id: 'tile_underutilized',
267
+ severity: 'warn',
268
+ gateId: 'checkpoint-speculative-decoding-acceptance',
269
+ message: `Attention-dominated: D=${options.draftLength} exceeds D*=${optimal} and G×(1+D) is not a multiple of ${ATTENTION_TILE}. Prefer tile-aligned D=${metrics.tileAlignedDraftLength}.`,
270
+ });
271
+ }
272
+ } else if (optimal !== null && options.draftLength === null) {
273
+ findings.push({
274
+ id: 'suggest_attention_draft_length',
275
+ severity: 'info',
276
+ gateId: 'checkpoint-speculative-decoding-acceptance',
277
+ message: `Attention-dominated workload: start with D=${optimal} (128/G - 1) for G=${options.queryHeadsPerKvHead}.`,
278
+ });
279
+ }
280
+ }
281
+
282
+ if (metrics.draftIncreaseDecision && metrics.draftIncreaseDecision.increase === false) {
283
+ findings.push({
284
+ id: 'draft_increase_not_justified',
285
+ severity: 'warn',
286
+ gateId: 'checkpoint-speculative-decoding-acceptance',
287
+ message: `Guideline 4: raising D to ${options.nextDraftLength} drops speedup from ${metrics.draftIncreaseDecision.currentSpeedup}x to ${metrics.draftIncreaseDecision.nextSpeedup}x. Keep current D.`,
288
+ });
289
+ }
290
+
291
+ if (mechanism && options.draftDepthRatio === null && mechanism.draftDepthRatioHint !== undefined) {
292
+ findings.push({
293
+ id: 'draft_depth_ratio_assumed',
294
+ severity: 'info',
295
+ gateId: null,
296
+ message: `No --draft-depth-ratio provided. Mechanism hint ρ≈${mechanism.draftDepthRatioHint} for ${mechanism.mechanism}; measure serve-time O_d before claiming speedup.`,
297
+ });
298
+ }
299
+
300
+ return findings;
301
+ }
302
+
303
+ function buildNvidiaSpecDecodeAlDoctorReport(rawOptions = {}) {
304
+ const options = normalizeOptions(rawOptions);
305
+ const rhoForTheory = options.draftDepthRatio === null ? 0 : options.draftDepthRatio;
306
+ const attentionOptimalDraftLength = optimalDraftLengthForAttention(options.queryHeadsPerKvHead);
307
+ const tileAligned = options.queryHeadsPerKvHead !== null && options.draftLength !== null
308
+ ? isTileAligned(options.queryHeadsPerKvHead, options.draftLength)
309
+ : null;
310
+ const tileAlignedDraftLength = options.queryHeadsPerKvHead !== null
311
+ ? nearestTileAlignedDraftLength(
312
+ options.queryHeadsPerKvHead,
313
+ options.draftLength ?? attentionOptimalDraftLength ?? 0
314
+ )
315
+ : null;
316
+
317
+ const mechanism = recommendDraftMechanism({
318
+ workload: options.workload,
319
+ latencyRegion: options.latencyRegion,
320
+ modelSize: options.modelSize,
321
+ repetitive: options.repetitive,
322
+ });
323
+
324
+ const metrics = {
325
+ acceptLength: options.acceptLength,
326
+ draftLength: options.draftLength,
327
+ draftDepthRatio: options.draftDepthRatio,
328
+ draftOverhead: draftOverhead(rhoForTheory, options.draftLength ?? 0),
329
+ theoreticalSpeedup: options.acceptLength !== null && options.draftLength !== null
330
+ ? theoreticalSpeedup(options.acceptLength, options.draftLength, rhoForTheory)
331
+ : null,
332
+ claimedSpeedup: options.claimedSpeedup,
333
+ attentionOptimalDraftLength,
334
+ tileAligned,
335
+ tileAlignedDraftLength,
336
+ draftIncreaseDecision: (
337
+ options.acceptLength !== null
338
+ && options.draftLength !== null
339
+ && options.nextAcceptLength !== null
340
+ && options.nextDraftLength !== null
341
+ )
342
+ ? shouldIncreaseDraftLength({
343
+ acceptLength: options.acceptLength,
344
+ draftLength: options.draftLength,
345
+ draftDepthRatio: rhoForTheory,
346
+ nextAcceptLength: options.nextAcceptLength,
347
+ nextDraftLength: options.nextDraftLength,
348
+ })
349
+ : null,
350
+ };
351
+
352
+ const findings = buildFindings(options, metrics, mechanism);
353
+ const failCount = findings.filter((f) => f.severity === 'fail').length;
354
+ const warnCount = findings.filter((f) => f.severity === 'warn').length;
355
+ const recommendedGateIds = [...new Set(
356
+ findings.map((f) => f.gateId).filter(Boolean)
357
+ )];
358
+
359
+ let status = 'ready';
360
+ if (failCount > 0) status = 'fail';
361
+ else if (warnCount > 0 || findings.some((f) => f.severity === 'info' && f.id !== 'draft_depth_ratio_assumed')) {
362
+ status = 'actionable';
363
+ } else if (
364
+ options.speculativeDecoding
365
+ && options.acceptLength !== null
366
+ && options.acceptLength >= options.minAcceptLength
367
+ && (options.claimedSpeedup === null
368
+ || metrics.theoreticalSpeedup === null
369
+ || options.claimedSpeedup <= metrics.theoreticalSpeedup + 1e-9)
370
+ ) {
371
+ status = 'ready';
372
+ } else if (!options.speculativeDecoding && options.acceptLength === null) {
373
+ status = 'ready';
374
+ }
375
+
376
+ return {
377
+ name: 'thumbgate-nvidia-specdecode-al-doctor',
378
+ status,
379
+ source: SOURCE_URL,
380
+ disclaimer: 'Process steal of AL/D co-design guidelines only. Not affiliated with NVIDIA. Does not ship TensorRT-LLM, EAGLE, or Model-Optimizer.',
381
+ workload: options.workload,
382
+ model: options.model,
383
+ metrics,
384
+ mechanism,
385
+ findings,
386
+ summary: {
387
+ failCount,
388
+ warnCount,
389
+ findingCount: findings.length,
390
+ recommendedGateCount: recommendedGateIds.length,
391
+ },
392
+ recommendedGates: recommendedGateIds,
393
+ nextActions: [
394
+ 'Measure accept length (AL) on realistic agent prompts before claiming speculation speedup.',
395
+ 'Use speedup ≈ AL/(1+ρD); reject claims above that ceiling.',
396
+ 'For attention-heavy decode, start at D=128/G-1 and keep G×(1+D) tile-aligned.',
397
+ 'Increase D only while measured AL gains beat added draft overhead.',
398
+ 'Wire evidence through deepseek-v4-runtime-guardrails --speculative-decoding --accept-length=… and gate checkpoint-speculative-decoding-acceptance.',
399
+ ],
400
+ exampleCommand:
401
+ 'npx thumbgate nvidia-specdecode-al-doctor --speculative-decoding --accept-length=1.4 --draft-length=7 --draft-depth-ratio=0.05 --claimed-speedup=3 --query-heads-per-kv=8 --attention-dominated --json',
402
+ };
403
+ }
404
+
405
+ function formatNvidiaSpecDecodeAlDoctorReport(report) {
406
+ const lines = [
407
+ '',
408
+ 'ThumbGate NVIDIA Speculative-Decoding AL/D Doctor',
409
+ '-'.repeat(48),
410
+ `Status : ${report.status}`,
411
+ `Workload: ${report.workload}`,
412
+ `Model : ${report.model}`,
413
+ `Source : ${report.source}`,
414
+ `Findings: ${report.summary.findingCount} (fail=${report.summary.failCount}, warn=${report.summary.warnCount})`,
415
+ ];
416
+
417
+ if (report.metrics.acceptLength !== null) lines.push(`AL : ${report.metrics.acceptLength}`);
418
+ if (report.metrics.draftLength !== null) lines.push(`D : ${report.metrics.draftLength}`);
419
+ if (report.metrics.draftDepthRatio !== null) lines.push(`ρ : ${report.metrics.draftDepthRatio}`);
420
+ if (report.metrics.theoreticalSpeedup !== null) {
421
+ lines.push(`Theory : ${report.metrics.theoreticalSpeedup}x (AL/(1+ρD))`);
422
+ }
423
+ if (report.metrics.claimedSpeedup !== null) lines.push(`Claimed : ${report.metrics.claimedSpeedup}x`);
424
+ if (report.metrics.attentionOptimalDraftLength !== null) {
425
+ lines.push(`D*(attn): ${report.metrics.attentionOptimalDraftLength}`);
426
+ }
427
+ if (report.metrics.tileAligned !== null) {
428
+ lines.push(`Tile OK : ${report.metrics.tileAligned ? 'yes' : 'no'}`);
429
+ }
430
+
431
+ lines.push('', `Mechanism: ${report.mechanism.mechanism}`);
432
+ lines.push(` ${report.mechanism.reason}`);
433
+
434
+ if (report.findings.length > 0) {
435
+ lines.push('', 'Findings:');
436
+ for (const finding of report.findings) {
437
+ const gate = finding.gateId ? ` [${finding.gateId}]` : '';
438
+ lines.push(` - [${finding.severity}] ${finding.id}${gate}`);
439
+ lines.push(` ${finding.message}`);
440
+ }
441
+ }
442
+
443
+ lines.push('', 'Next actions:');
444
+ for (const action of report.nextActions) lines.push(` - ${action}`);
445
+ lines.push('', `Example: ${report.exampleCommand}`);
446
+ lines.push(`Note: ${report.disclaimer}`, '');
447
+ return `${lines.join('\n')}\n`;
448
+ }
449
+
450
+ function parseCliArgs(argv) {
451
+ const options = {};
452
+ for (const arg of argv) {
453
+ if (arg === '--json') {
454
+ options.json = true;
455
+ continue;
456
+ }
457
+ if (arg === '--strict') {
458
+ options.strict = true;
459
+ continue;
460
+ }
461
+ if (arg === '--help' || arg === '-h') {
462
+ options.help = true;
463
+ continue;
464
+ }
465
+ const m = /^--([^=]+)(?:=(.*))?$/.exec(arg);
466
+ if (!m) continue;
467
+ const key = m[1];
468
+ const raw = m[2] === undefined ? true : m[2];
469
+ options[key] = raw;
470
+ }
471
+ return options;
472
+ }
473
+
474
+ function printHelp() {
475
+ process.stdout.write(`Usage: node scripts/nvidia-specdecode-al-doctor.js [flags]
476
+
477
+ Flags:
478
+ --speculative-decoding Speculation path is active
479
+ --accept-length=AL Measured acceptance length
480
+ --draft-length=D Draft tokens per target iteration
481
+ --draft-depth-ratio=ρ L_draft / L_target (default 0 for theory)
482
+ --claimed-speedup=X Throughput claim to check against AL/(1+ρD)
483
+ --min-accept-length=N Floor before production (default 2)
484
+ --query-heads-per-kv=G Attention group size G
485
+ --attention-dominated Apply D=128/G-1 + tile guidance
486
+ --latency-region=low|throughput Pareto region hint for mechanism pick
487
+ --model-size=small|medium|large Mechanism sizing hint
488
+ --repetitive Prefer suffix/n-gram drafts
489
+ --next-accept-length / --next-draft-length
490
+ Guideline-4 compare for raising D
491
+ --cache-coherence-eval Rollback/coherence evidence present
492
+ --strict Exit 1 on fail/actionable
493
+ --json Machine-readable report
494
+
495
+ Source: ${SOURCE_URL}
496
+ `);
497
+ }
498
+
499
+ function runCli(argv = process.argv.slice(2)) {
500
+ const args = parseCliArgs(argv);
501
+ if (args.help) {
502
+ printHelp();
503
+ return 0;
504
+ }
505
+ const report = buildNvidiaSpecDecodeAlDoctorReport(args);
506
+ if (args.json) {
507
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
508
+ } else {
509
+ process.stdout.write(formatNvidiaSpecDecodeAlDoctorReport(report));
510
+ }
511
+ if (args.strict && report.status !== 'ready') return 1;
512
+ if (report.status === 'fail') return 1;
513
+ return 0;
514
+ }
515
+
516
+ module.exports = {
517
+ ATTENTION_TILE,
518
+ DEFAULT_MIN_ACCEPT_LENGTH,
519
+ SOURCE_URL,
520
+ optimalDraftLengthForAttention,
521
+ isTileAligned,
522
+ nearestTileAlignedDraftLength,
523
+ draftOverhead,
524
+ theoreticalSpeedup,
525
+ shouldIncreaseDraftLength,
526
+ recommendDraftMechanism,
527
+ normalizeOptions,
528
+ buildNvidiaSpecDecodeAlDoctorReport,
529
+ formatNvidiaSpecDecodeAlDoctorReport,
530
+ parseCliArgs,
531
+ runCli,
532
+ };
533
+
534
+ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(__filename)) {
535
+ process.exitCode = runCli(process.argv.slice(2));
536
+ }