thumbgate 1.35.0 → 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 (196) 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 +336 -3
  24. package/bin/futureagi-bridge +9 -0
  25. package/config/gate-templates.json +581 -4
  26. package/config/gates/actor-critic-audit.json +34 -0
  27. package/config/gates/default.json +9 -3
  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 +6 -0
  34. package/config/model-candidates.json +312 -29
  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 +390 -148
  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/billing.js +1 -1
  89. package/scripts/broker-execution-receipts.js +719 -0
  90. package/scripts/budget-aware-gates-proof.js +423 -0
  91. package/scripts/claude-feedback-sync.js +29 -3
  92. package/scripts/claw-harness-production.js +237 -0
  93. package/scripts/cli-schema.js +163 -1
  94. package/scripts/codex-runbook-flywheel.js +318 -0
  95. package/scripts/context-footprint.js +186 -0
  96. package/scripts/contextfs.js +143 -61
  97. package/scripts/dashboard.js +251 -32
  98. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  99. package/scripts/docker-sandbox-planner.js +18 -0
  100. package/scripts/double-blind-eval-protocol.js +252 -0
  101. package/scripts/edotenv-rl-gateway.js +259 -0
  102. package/scripts/ensure-production-search-corpus.js +162 -0
  103. package/scripts/eval-holdout.js +311 -0
  104. package/scripts/feedback-aggregate.js +21 -2
  105. package/scripts/feedback-loop.js +87 -5
  106. package/scripts/feedback-quality.js +9 -0
  107. package/scripts/file-ledger-lock.js +4 -1
  108. package/scripts/financial-control-plane.js +41 -1
  109. package/scripts/find-dormant-requires.js +118 -0
  110. package/scripts/fs-utils.js +84 -8
  111. package/scripts/gates-engine.js +810 -63
  112. package/scripts/generate-case-study-outreach.js +24 -15
  113. package/scripts/git-at-scale.js +628 -0
  114. package/scripts/governance-conflict-audit.js +1650 -0
  115. package/scripts/governance-difficulty-curriculum.js +328 -0
  116. package/scripts/graphrag-retrieval.js +275 -0
  117. package/scripts/gurobi-optimizer.js +324 -0
  118. package/scripts/gurobi_optimizer.py +485 -0
  119. package/scripts/harness-selector.js +82 -1
  120. package/scripts/hidden-entry-points.js +284 -0
  121. package/scripts/human-escalation.js +199 -1
  122. package/scripts/hybrid-feedback-context.js +152 -19
  123. package/scripts/intent-governed-execution.js +602 -0
  124. package/scripts/intervention-policy.js +123 -20
  125. package/scripts/jit-harness-compose.js +628 -0
  126. package/scripts/jsonl-watcher.js +10 -0
  127. package/scripts/lesson-embedding-index.js +95 -12
  128. package/scripts/lesson-retrieval.js +105 -19
  129. package/scripts/local-model-profile.js +19 -2
  130. package/scripts/mailer/resend-mailer.js +1 -1
  131. package/scripts/matryoshka-embedding.js +235 -0
  132. package/scripts/mcp-oauth.js +42 -4
  133. package/scripts/mcp-session-handles.js +1016 -0
  134. package/scripts/mcp-wiring-doctor.js +314 -0
  135. package/scripts/memory-firewall.js +115 -2
  136. package/scripts/memory-scope-readiness.js +299 -0
  137. package/scripts/memory-vs-rag-route.js +161 -0
  138. package/scripts/model-tier-router.js +148 -21
  139. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  140. package/scripts/openui-catalog-compose-honesty.js +593 -0
  141. package/scripts/operational-integrity.js +19 -1
  142. package/scripts/override-audit.js +213 -0
  143. package/scripts/package-manager-honesty-doctor.js +458 -0
  144. package/scripts/pr-manager.js +63 -1
  145. package/scripts/prove-herdr-adapter.js +52 -0
  146. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  147. package/scripts/prove-workos.js +73 -0
  148. package/scripts/provider-attestation-conformance.js +192 -0
  149. package/scripts/provider-receipt-contract.js +136 -0
  150. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  151. package/scripts/radware-threat-defense.js +280 -0
  152. package/scripts/rag-embedding-identity.js +221 -0
  153. package/scripts/rag-precision-guardrails.js +112 -2
  154. package/scripts/remote-feedback-capture.js +159 -0
  155. package/scripts/research-agent-harness.js +256 -0
  156. package/scripts/rsi-safety-hillclimb.js +200 -0
  157. package/scripts/rule-sprawl.js +188 -0
  158. package/scripts/schedule-manager.js +147 -0
  159. package/scripts/self-heal.js +8 -0
  160. package/scripts/session-lease.js +415 -0
  161. package/scripts/simatree-data-governance.js +347 -0
  162. package/scripts/slo-alert-engine.js +172 -7
  163. package/scripts/solver-parity.js +539 -0
  164. package/scripts/stealth-memory-injection-gate.js +333 -0
  165. package/scripts/switchyard-router.js +366 -0
  166. package/scripts/telemetry-analytics.js +84 -27
  167. package/scripts/temporal-decay-weighting.js +138 -0
  168. package/scripts/test-all.js +165 -0
  169. package/scripts/token-savings.js +42 -0
  170. package/scripts/tool-kpi-tracker.js +108 -5
  171. package/scripts/tool-registry.js +193 -5
  172. package/scripts/universal-claim-evaluator.js +14 -2
  173. package/scripts/vector-store.js +279 -9
  174. package/scripts/workflow-notebook.js +391 -0
  175. package/scripts/workflow-sentinel.js +111 -12
  176. package/scripts/workos-production-guard.js +260 -0
  177. package/scripts/workspace-search-route.js +515 -0
  178. package/server.json +2 -2
  179. package/src/agent-identity-boundary.js +76 -0
  180. package/src/agent-retrieval-cache.js +155 -0
  181. package/src/alert-noise-ledger.js +502 -0
  182. package/src/api/server.js +724 -153
  183. package/src/git-fast-cache.js +220 -0
  184. package/src/git-wal-sync.js +156 -0
  185. package/src/hash-anchored-edit.js +82 -0
  186. package/src/hermes-platform-protocol.js +475 -0
  187. package/src/hermes-sync-plane.js +241 -0
  188. package/src/index.js +30 -1
  189. package/src/iso42001-compliance-guard.js +97 -0
  190. package/src/latency-budget.js +244 -0
  191. package/src/mcp-writeguard.js +316 -0
  192. package/src/miminions-adapter.js +106 -0
  193. package/src/pipeline-compass.js +104 -0
  194. package/src/ppl-alert-pipeline.js +284 -0
  195. package/src/rendezvous-router.js +90 -0
  196. package/src/security-questionnaire.js +195 -0
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Qwen3.8-Max & Alibaba Cloud Token Plan Cost Optimizer
6
+ *
7
+ * Pricing sources (2026): OpenRouter Qwen3.8-Max, Claude platform pricing
8
+ * (Sonnet 5 intro through 2026-08-31 then $3/$15), Gemini 2.5 tiered ranges.
9
+ * Token Plan 2x promo modeled as 0.5× effective cost when useTokenPlanPromo.
10
+ */
11
+
12
+ const PRICING_TABLE = Object.freeze({
13
+ 'qwen3.8-max': {
14
+ inputPerM: 2.00,
15
+ outputPerM: 6.00,
16
+ label: 'Qwen3.8-Max (OpenRouter / Model Studio)',
17
+ contextWindow: 1_000_000,
18
+ },
19
+ // Token Plan list prices from Alibaba Model Studio (approx mid of published ranges).
20
+ 'qwen3.7-plus': { inputPerM: 0.64, outputPerM: 2.56, label: 'Qwen3.7-Plus (Model Studio)' },
21
+ 'qwen3.6-flash': { inputPerM: 0.50, outputPerM: 2.50, label: 'Qwen3.6-Flash (Model Studio)' },
22
+ 'claude-sonnet-5-intro': {
23
+ inputPerM: 2.00,
24
+ outputPerM: 10.00,
25
+ label: 'Claude Sonnet 5 (Intro through 2026-08-31)',
26
+ },
27
+ 'claude-sonnet-4.6': { inputPerM: 3.00, outputPerM: 15.00, label: 'Claude Sonnet 4.6' },
28
+ 'claude-sonnet-5-standard': {
29
+ inputPerM: 3.00,
30
+ outputPerM: 15.00,
31
+ label: 'Claude Sonnet 5 (Post-Aug 31, 2026)',
32
+ },
33
+ // Conservative (high) end of Gemini 2.5 Pro published range for apples-to-apples "worst case Gemini".
34
+ 'gemini-2.5-pro': {
35
+ inputPerM: 2.50,
36
+ outputPerM: 15.00,
37
+ label: 'Gemini 2.5 Pro (high tier of $1.25–2.50 / $10–15)',
38
+ inputPerMLow: 1.25,
39
+ outputPerMLow: 10.00,
40
+ },
41
+ 'gemini-2.5-flash': { inputPerM: 0.30, outputPerM: 2.50, label: 'Gemini 2.5 Flash' },
42
+ });
43
+
44
+ /** After this date Claude Sonnet 5 uses standard $3/$15 (not intro $2/$10). */
45
+ const CLAUDE_SONNET_5_INTRO_ENDS = '2026-09-01';
46
+
47
+ /**
48
+ * Recommend flash vs plus vs max for a workload (pairs with adapters/qwen resolveQwenRoleRoute).
49
+ */
50
+ function recommendQwenTier(workload, options = {}) {
51
+ const id = String(workload || 'pretool-gating').toLowerCase();
52
+ if (id.includes('long') || id.includes('claw') || id.includes('autonomous')) {
53
+ return {
54
+ modelKey: 'qwen3.8-max',
55
+ reason: 'Long-horizon / claw-style work benefits from flagship reasoning.',
56
+ costSample: calculateTokenCost('qwen3.8-max', options.inputTokensM || 1, options.outputTokensM || 1),
57
+ };
58
+ }
59
+ if (id.includes('cheap') || id.includes('pretool') || id.includes('gate') || id.includes('flash')) {
60
+ return {
61
+ modelKey: 'qwen3.6-flash',
62
+ reason: 'Gate and triage paths should stay on Flash under Token Plan.',
63
+ costSample: calculateTokenCost('qwen3.6-flash', options.inputTokensM || 1, options.outputTokensM || 1),
64
+ };
65
+ }
66
+ return {
67
+ modelKey: 'qwen3.7-plus',
68
+ reason: 'Default balanced coding / vision path.',
69
+ costSample: calculateTokenCost('qwen3.7-plus', options.inputTokensM || 1, options.outputTokensM || 1),
70
+ };
71
+ }
72
+
73
+ function calculateTokenCost(modelKey, inputTokensM = 1, outputTokensM = 1) {
74
+ const model = PRICING_TABLE[modelKey];
75
+ if (!model) {
76
+ throw new Error(`Unknown model key: ${modelKey}`);
77
+ }
78
+ const inputCost = inputTokensM * model.inputPerM;
79
+ const outputCost = outputTokensM * model.outputPerM;
80
+ return {
81
+ modelKey,
82
+ label: model.label,
83
+ inputCost,
84
+ outputCost,
85
+ totalCost: inputCost + outputCost,
86
+ };
87
+ }
88
+
89
+ function applyTokenPlanPromo(totalCostUsd, useTokenPlanPromo) {
90
+ if (!useTokenPlanPromo) return Number(totalCostUsd);
91
+ // Limited-time 2x usage-per-credit → model as 0.5× effective $ for same work.
92
+ return Number(totalCostUsd) * 0.5;
93
+ }
94
+
95
+ function calculateQwenSavingsVsClaude(options = {}) {
96
+ const inputM = options.inputTokensM || 1;
97
+ const outputM = options.outputTokensM || 1;
98
+ const useTokenPlanPromo = Boolean(options.useTokenPlanPromo);
99
+ const claudeKey = options.claudeModelKey || 'claude-sonnet-4.6';
100
+
101
+ const qwenBase = calculateTokenCost('qwen3.8-max', inputM, outputM);
102
+ const claude = calculateTokenCost(claudeKey, inputM, outputM);
103
+
104
+ const qwenEffectiveTotal = applyTokenPlanPromo(qwenBase.totalCost, useTokenPlanPromo);
105
+ const rawSavingsUsd = claude.totalCost - qwenEffectiveTotal;
106
+ const savingsPercent = Number(((rawSavingsUsd / claude.totalCost) * 100).toFixed(1));
107
+
108
+ return {
109
+ inputTokensM: inputM,
110
+ outputTokensM: outputM,
111
+ useTokenPlanPromo,
112
+ claudeModelKey: claudeKey,
113
+ qwenCostUsd: Number(qwenEffectiveTotal.toFixed(2)),
114
+ claudeCostUsd: Number(claude.totalCost.toFixed(2)),
115
+ savingsUsd: Number(rawSavingsUsd.toFixed(2)),
116
+ savingsPercent,
117
+ recommendation: savingsPercent > 50
118
+ ? 'ROUTE_HIGH_VOLUME_TO_QWEN38_MAX'
119
+ : 'EVALUATE_HYBRID_ROUTING',
120
+ };
121
+ }
122
+
123
+ function calculateCostPerSuccessfulOutcome(options = {}) {
124
+ const {
125
+ modelKey,
126
+ inputTokensM = 1,
127
+ outputTokensM = 1,
128
+ successRate,
129
+ sampleSize,
130
+ costMultiplier = 1,
131
+ minimumSampleSize = 20,
132
+ } = options;
133
+
134
+ if (!Number.isFinite(successRate) || successRate <= 0 || successRate > 1) {
135
+ throw new Error('successRate must be greater than 0 and less than or equal to 1');
136
+ }
137
+ if (!Number.isInteger(sampleSize) || sampleSize < 0) {
138
+ throw new Error('sampleSize must be a non-negative integer');
139
+ }
140
+ if (!Number.isFinite(costMultiplier) || costMultiplier <= 0 || costMultiplier > 1) {
141
+ throw new Error('costMultiplier must be greater than 0 and less than or equal to 1');
142
+ }
143
+
144
+ const tokenCost = calculateTokenCost(modelKey, inputTokensM, outputTokensM);
145
+ const effectiveCostUsd = tokenCost.totalCost * costMultiplier;
146
+ return {
147
+ modelKey,
148
+ sampleSize,
149
+ successRate,
150
+ telemetryQualified: sampleSize >= minimumSampleSize,
151
+ effectiveCostUsd: Number(effectiveCostUsd.toFixed(4)),
152
+ costPerSuccessfulOutcomeUsd: Number((effectiveCostUsd / successRate).toFixed(4)),
153
+ };
154
+ }
155
+
156
+ function recommendQwenByCostPerSuccess(options = {}) {
157
+ const minimumSampleSize = options.minimumSampleSize ?? 20;
158
+ const minimumSavingsPercent = options.minimumSavingsPercent ?? 15;
159
+ const incumbent = calculateCostPerSuccessfulOutcome({
160
+ modelKey: options.incumbentModelKey || 'claude-sonnet-5-standard',
161
+ inputTokensM: options.inputTokensM,
162
+ outputTokensM: options.outputTokensM,
163
+ successRate: options.incumbentSuccessRate,
164
+ sampleSize: options.incumbentSampleSize,
165
+ minimumSampleSize,
166
+ });
167
+ const qwen = calculateCostPerSuccessfulOutcome({
168
+ modelKey: 'qwen3.8-max',
169
+ inputTokensM: options.inputTokensM,
170
+ outputTokensM: options.outputTokensM,
171
+ successRate: options.qwenSuccessRate,
172
+ sampleSize: options.qwenSampleSize,
173
+ costMultiplier: options.verifiedTokenPlanMultiplier ?? 1,
174
+ minimumSampleSize,
175
+ });
176
+
177
+ if (!incumbent.telemetryQualified || !qwen.telemetryQualified) {
178
+ return { recommendation: 'HOLD_INCUMBENT', reason: 'INSUFFICIENT_OUTCOME_TELEMETRY', incumbent, qwen };
179
+ }
180
+
181
+ const savingsPercent = Number((
182
+ ((incumbent.costPerSuccessfulOutcomeUsd - qwen.costPerSuccessfulOutcomeUsd)
183
+ / incumbent.costPerSuccessfulOutcomeUsd) * 100
184
+ ).toFixed(1));
185
+ const routeToQwen = savingsPercent >= minimumSavingsPercent;
186
+ return {
187
+ recommendation: routeToQwen ? 'ROUTE_HIGH_VOLUME_TO_QWEN38_MAX' : 'HOLD_INCUMBENT',
188
+ reason: routeToQwen ? 'LOWER_VERIFIED_COST_PER_SUCCESS' : 'SAVINGS_BELOW_THRESHOLD',
189
+ savingsPercent,
190
+ minimumSavingsPercent,
191
+ incumbent,
192
+ qwen,
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Full stack snapshot for the dual-lane policy (volume → Qwen, quality → Claude/Gemini).
198
+ */
199
+ function compareStackPricing(options = {}) {
200
+ const inputM = options.inputTokensM || 1;
201
+ const outputM = options.outputTokensM || 1;
202
+ const useTokenPlanPromo = Boolean(options.useTokenPlanPromo);
203
+ const asOf = options.asOf || new Date().toISOString().slice(0, 10);
204
+
205
+ const rows = [
206
+ 'qwen3.8-max',
207
+ 'qwen3.7-plus',
208
+ 'qwen3.6-flash',
209
+ 'claude-sonnet-5-intro',
210
+ 'claude-sonnet-5-standard',
211
+ 'claude-sonnet-4.6',
212
+ 'gemini-2.5-pro',
213
+ 'gemini-2.5-flash',
214
+ ].map((key) => {
215
+ const base = calculateTokenCost(key, inputM, outputM);
216
+ const effective = key.startsWith('qwen')
217
+ ? applyTokenPlanPromo(base.totalCost, useTokenPlanPromo)
218
+ : base.totalCost;
219
+ return {
220
+ modelKey: key,
221
+ label: base.label,
222
+ inputCost: base.inputCost,
223
+ outputCost: base.outputCost,
224
+ totalCost: base.totalCost,
225
+ effectiveCost: Number(effective.toFixed(4)),
226
+ };
227
+ });
228
+
229
+ const qwen = rows.find((r) => r.modelKey === 'qwen3.8-max');
230
+ const claudePost = rows.find((r) => r.modelKey === 'claude-sonnet-5-standard');
231
+ const outputSavingsVsClaudePost = claudePost.outputCost > 0
232
+ ? Number((((claudePost.outputCost - qwen.outputCost) / claudePost.outputCost) * 100).toFixed(1))
233
+ : 0;
234
+
235
+ return {
236
+ asOf,
237
+ inputTokensM: inputM,
238
+ outputTokensM: outputM,
239
+ useTokenPlanPromo,
240
+ claudeSonnet5IntroEnds: CLAUDE_SONNET_5_INTRO_ENDS,
241
+ claudeSonnet5OnIntroPricing: asOf < CLAUDE_SONNET_5_INTRO_ENDS,
242
+ rows,
243
+ highlights: {
244
+ qwenOutputPerM: PRICING_TABLE['qwen3.8-max'].outputPerM,
245
+ claudeStandardOutputPerM: PRICING_TABLE['claude-sonnet-5-standard'].outputPerM,
246
+ outputTokenSavingsPercentVsClaudeStandard: outputSavingsVsClaudePost,
247
+ note: 'Qwen3.8-Max $6/M output vs Claude $15/M is the primary cash lever; Token Plan promo stacks further.',
248
+ },
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Practical dual-stack policy from cost analysis:
254
+ * - high-volume / bulk / automation → Qwen (Flash for gates, Max for long agentic)
255
+ * - high-stakes reasoning / architecture / quality-critical → Claude or Gemini
256
+ * - local preferred when privacyRoute=local or sensitive
257
+ */
258
+ function recommendCostQualitySplit(task = {}, options = {}) {
259
+ const tags = Array.isArray(task.tags) ? task.tags.map((t) => String(t).toLowerCase()) : [];
260
+ const type = String(task.type || task.workload || '').toLowerCase();
261
+ const risk = String(task.riskLevel || 'medium').toLowerCase();
262
+ const costPrimary = task.costPriority === 'primary'
263
+ || tags.includes('cost-sensitive')
264
+ || tags.includes('high-volume')
265
+ || tags.includes('bulk')
266
+ || tags.includes('automation')
267
+ || Boolean(task.highVolume);
268
+ const qualityCritical = risk === 'high'
269
+ || tags.includes('architecture')
270
+ || tags.includes('reasoning-critical')
271
+ || tags.includes('quality-critical')
272
+ || type.includes('architecture')
273
+ || type.includes('complex-debugging');
274
+ const longHorizon = tags.includes('long-horizon')
275
+ || type.includes('long-trace')
276
+ || Boolean(task.longHorizon)
277
+ || (Number(task.contextTokens) || 0) >= 128000;
278
+ const sensitive = task.privacyRoute === 'local'
279
+ || Boolean(task.sensitive)
280
+ || tags.includes('pii')
281
+ || tags.includes('secrets');
282
+
283
+ const useTokenPlanPromo = Boolean(options.useTokenPlanPromo);
284
+ const volume = {
285
+ inputTokensM: options.inputTokensM || 1,
286
+ outputTokensM: options.outputTokensM || 1,
287
+ };
288
+ const vsClaude = calculateQwenSavingsVsClaude({
289
+ ...volume,
290
+ useTokenPlanPromo,
291
+ claudeModelKey: options.claudeModelKey || 'claude-sonnet-5-standard',
292
+ });
293
+
294
+ if (sensitive) {
295
+ return {
296
+ lane: 'local-or-private',
297
+ primaryProvider: 'local',
298
+ primaryModel: options.localModel || null,
299
+ fallbackProvider: null,
300
+ reason: 'Sensitive / privacyRoute=local — do not send bulk payloads to Model Studio without explicit allow.',
301
+ savings: vsClaude,
302
+ requiresBudgetApproval: false,
303
+ };
304
+ }
305
+
306
+ if (qualityCritical && !costPrimary) {
307
+ return {
308
+ lane: 'quality',
309
+ primaryProvider: options.qualityProvider || 'anthropic',
310
+ primaryModel: options.qualityModel || 'claude-sonnet-5-standard',
311
+ fallbackProvider: 'gemini',
312
+ fallbackModel: 'gemini-2.5-pro',
313
+ reason: 'High-stakes reasoning — keep Claude/Gemini; do not force Qwen solely for price.',
314
+ savings: vsClaude,
315
+ requiresBudgetApproval: false,
316
+ };
317
+ }
318
+
319
+ if (costPrimary || longHorizon) {
320
+ const telemetry = options.outcomeTelemetry;
321
+ if (!telemetry) {
322
+ return {
323
+ lane: 'quality',
324
+ primaryProvider: options.qualityProvider || 'anthropic',
325
+ primaryModel: options.qualityModel || 'claude-sonnet-5-standard',
326
+ fallbackProvider: 'model-studio',
327
+ fallbackModel: 'qwen3.8-max',
328
+ reason: 'Hold incumbent until Qwen and incumbent have qualified task-outcome telemetry.',
329
+ savings: vsClaude,
330
+ requiresBudgetApproval: false,
331
+ routingEvidence: { recommendation: 'HOLD_INCUMBENT', reason: 'INSUFFICIENT_OUTCOME_TELEMETRY' },
332
+ };
333
+ }
334
+ const routingEvidence = recommendQwenByCostPerSuccess({
335
+ ...volume,
336
+ ...telemetry,
337
+ verifiedTokenPlanMultiplier: useTokenPlanPromo ? 0.5 : 1,
338
+ });
339
+ if (routingEvidence.recommendation !== 'ROUTE_HIGH_VOLUME_TO_QWEN38_MAX') {
340
+ return {
341
+ lane: 'quality',
342
+ primaryProvider: options.qualityProvider || 'anthropic',
343
+ primaryModel: options.qualityModel || 'claude-sonnet-5-standard',
344
+ fallbackProvider: 'model-studio',
345
+ fallbackModel: 'qwen3.8-max',
346
+ reason: 'Hold incumbent because Qwen has not proven lower cost per successful outcome.',
347
+ savings: vsClaude,
348
+ requiresBudgetApproval: false,
349
+ routingEvidence,
350
+ };
351
+ }
352
+ const qwenTier = recommendQwenTier(
353
+ longHorizon ? 'long-trace-review' : (type.includes('gate') || type.includes('pretool') ? 'pretool-gating' : 'cheap-fast-path'),
354
+ volume,
355
+ );
356
+ return {
357
+ lane: 'cost-volume',
358
+ primaryProvider: 'model-studio',
359
+ primaryModel: qwenTier.modelKey,
360
+ candidateId: qwenTier.modelKey === 'qwen3.8-max'
361
+ ? 'alibaba/qwen3.8-max'
362
+ : qwenTier.modelKey === 'qwen3.6-flash'
363
+ ? 'alibaba/qwen3.6-flash'
364
+ : 'alibaba/qwen3.7-plus',
365
+ fallbackProvider: options.qualityProvider || 'anthropic',
366
+ fallbackModel: options.qualityModel || 'claude-sonnet-5-standard',
367
+ reason: longHorizon
368
+ ? 'Long / high-output agentic work: Qwen3.8-Max for output-token savings; keep Claude as quality fallback.'
369
+ : 'High-volume cost-sensitive work: route to Qwen under Token Plan; escalate quality-critical to Claude/Gemini.',
370
+ savings: vsClaude,
371
+ requiresBudgetApproval: true,
372
+ qwenTier,
373
+ routingEvidence,
374
+ };
375
+ }
376
+
377
+ return {
378
+ lane: 'balanced',
379
+ primaryProvider: 'model-studio',
380
+ primaryModel: 'qwen3.7-plus',
381
+ candidateId: 'alibaba/qwen3.7-plus',
382
+ fallbackProvider: 'anthropic',
383
+ fallbackModel: 'claude-sonnet-5-standard',
384
+ reason: 'Default: Plus for coding throughput; quality fallback on failure or high risk.',
385
+ savings: vsClaude,
386
+ requiresBudgetApproval: true,
387
+ };
388
+ }
389
+
390
+ module.exports = {
391
+ PRICING_TABLE,
392
+ CLAUDE_SONNET_5_INTRO_ENDS,
393
+ calculateTokenCost,
394
+ calculateQwenSavingsVsClaude,
395
+ calculateCostPerSuccessfulOutcome,
396
+ recommendQwenByCostPerSuccess,
397
+ recommendQwenTier,
398
+ compareStackPricing,
399
+ recommendCostQualitySplit,
400
+ applyTokenPlanPromo,
401
+ };
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Radware-inspired threat defense for ThumbGate PreToolUse.
6
+ *
7
+ * Steals process (not product) from Radware Bot Manager / AI DDoS defense:
8
+ * detect → challenge → block severity ladder, real-time interdiction,
9
+ * adaptive rate circuit breaker.
10
+ *
11
+ * Vectors:
12
+ * 1. ShadowLeak — secondary egress / parameter stuffing exfiltration
13
+ * 2. ZombieAgent — indirect prompt injection hijacking agent loops
14
+ * 3. Token-drain bursts — sub-minute volumetric circuit breaker
15
+ *
16
+ * Reference: Radware Global Threat Analysis Report 2026 (RWI-6283);
17
+ * Bot Manager Essentials (detect / challenge / mitigate).
18
+ */
19
+
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+ const os = require('node:os');
23
+
24
+ const SHADOWLEAK_PATTERNS = [
25
+ // Bounded classes — avoid nested quantifiers on attacker-controlled input (CodeQL js/polynomial-redos).
26
+ /!\[[^\]\n]{0,200}\]\(https?:\/\/[^)\s]{1,300}\?(?:data|token|key|cookie|leak|mem|secret)=[^)\s]{0,300}\)/i,
27
+ /\bcurl[ \t][^|\r\n]{0,500}(?:-d|--data|-F)[ \t][^|\r\n]{0,400}(?:token|secret|password|credential|env|auth)/i,
28
+ /\b(?:fetch|axios|request)\s*\(\s*[`'"]https?:\/\/[^`'"]{0,300}(?:exfil|leak|webhook|collect)/i,
29
+ ];
30
+
31
+ const ZOMBIEAGENT_PATTERNS = [
32
+ /\b(?:system\s*override|act\s*as\s*zombie|unrestricted\s*agent|drop\s*prior\s*instructions|ignore\s*all\s*guardrails)\b/i,
33
+ /\b(?:execute\s*without\s*approval|silently\s*forward\s*to|covert\s*execution|bypass\s*firewall)\b/i,
34
+ ];
35
+
36
+ /** Bot Manager–style “suspicious automation” — challenge, do not hard-block. */
37
+ const SUSPICIOUS_BOT_PATTERNS = [
38
+ /\b(?:scrape\s+all|mass[- ]scrape|credential\s*stuff|ato\s*attack|account\s*takeover\s*spray)\b/i,
39
+ /\b(?:headless\s+flood|burst\s+tool\s+calls|token\s*drain\s*loop)\b/i,
40
+ ];
41
+
42
+ const SEVERITY_RANK = {
43
+ none: 0,
44
+ low: 1,
45
+ medium: 2,
46
+ high: 3,
47
+ critical: 4,
48
+ };
49
+
50
+ const ACTION_FOR_SEVERITY = {
51
+ none: 'allow',
52
+ low: 'detect',
53
+ medium: 'challenge',
54
+ high: 'block',
55
+ critical: 'block',
56
+ };
57
+
58
+ function defaultHistoryPath() {
59
+ const root = process.env.THUMBGATE_HOME
60
+ || path.join(os.homedir(), '.thumbgate');
61
+ return path.join(root, 'radware-call-history.json');
62
+ }
63
+
64
+ function loadCallTimestamps(historyPath = defaultHistoryPath()) {
65
+ try {
66
+ const raw = fs.readFileSync(historyPath, 'utf8');
67
+ const parsed = JSON.parse(raw);
68
+ return Array.isArray(parsed.timestamps) ? parsed.timestamps.filter((n) => Number.isFinite(n)) : [];
69
+ } catch {
70
+ return [];
71
+ }
72
+ }
73
+
74
+ function persistCallTimestamp(historyPath = defaultHistoryPath(), now = Date.now()) {
75
+ // Never pollute operator history from the node:test runner or CI matrices —
76
+ // those call evaluateGates hundreds of times and would false-trip the breaker.
77
+ if (
78
+ process.env.NODE_TEST
79
+ || process.env.NODE_TEST_CONTEXT
80
+ || process.env.CI
81
+ || process.env.GITHUB_ACTIONS
82
+ || process.env.npm_lifecycle_event === 'test'
83
+ ) {
84
+ return loadCallTimestamps(historyPath);
85
+ }
86
+ const timestamps = loadCallTimestamps(historyPath)
87
+ .filter((ts) => ts >= now - 120000)
88
+ .concat(now);
89
+ fs.mkdirSync(path.dirname(historyPath), { recursive: true });
90
+ fs.writeFileSync(historyPath, JSON.stringify({ timestamps }, null, 2));
91
+ return timestamps;
92
+ }
93
+
94
+ /**
95
+ * Bot Manager severity ladder: detect → challenge → block.
96
+ * @param {string|object} payload
97
+ * @param {object} [options]
98
+ */
99
+ function evaluateThreat(payload, options = {}) {
100
+ const raw = typeof payload === 'string' ? payload : JSON.stringify(payload || '');
101
+ // Hard length cap before regex evaluation (ReDoS defense-in-depth).
102
+ const text = raw.length > 20000 ? raw.slice(0, 20000) : raw;
103
+ const detections = [];
104
+ let severity = 'none';
105
+ let threatType = null;
106
+
107
+ for (const pattern of SHADOWLEAK_PATTERNS) {
108
+ if (pattern.test(text)) {
109
+ detections.push({
110
+ type: 'ShadowLeak',
111
+ pattern: pattern.source,
112
+ description: 'Sensitive data exfiltration via secondary egress or parameter stuffing',
113
+ });
114
+ threatType = 'ShadowLeak';
115
+ severity = 'critical';
116
+ break;
117
+ }
118
+ }
119
+
120
+ for (const pattern of ZOMBIEAGENT_PATTERNS) {
121
+ if (pattern.test(text)) {
122
+ detections.push({
123
+ type: 'ZombieAgent',
124
+ pattern: pattern.source,
125
+ description: 'Indirect prompt injection attempting agent loop hijacking',
126
+ });
127
+ threatType = threatType ? `${threatType}+ZombieAgent` : 'ZombieAgent';
128
+ severity = 'critical';
129
+ break;
130
+ }
131
+ }
132
+
133
+ if (severity === 'none') {
134
+ for (const pattern of SUSPICIOUS_BOT_PATTERNS) {
135
+ if (pattern.test(text)) {
136
+ detections.push({
137
+ type: 'SuspiciousBot',
138
+ pattern: pattern.source,
139
+ description: 'Bot Manager–style suspicious automation; challenge before allow',
140
+ });
141
+ threatType = 'SuspiciousBot';
142
+ severity = 'medium';
143
+ break;
144
+ }
145
+ }
146
+ }
147
+
148
+ const action = ACTION_FOR_SEVERITY[severity] || 'allow';
149
+ const blocked = action === 'block';
150
+ const challenged = action === 'challenge';
151
+
152
+ return {
153
+ verdict: blocked ? 'DENY' : challenged ? 'CHALLENGE' : detections.length ? 'DETECT' : 'ALLOW',
154
+ blocked,
155
+ challenged,
156
+ action,
157
+ threatType,
158
+ severity,
159
+ severityRank: SEVERITY_RANK[severity] || 0,
160
+ confidence: blocked || challenged ? 0.99 : 1.0,
161
+ detections,
162
+ evaluatedAt: new Date().toISOString(),
163
+ receipt: blocked
164
+ ? `threat_defense_interdicted=true:type=${threatType || 'unknown'}:action=block`
165
+ : challenged
166
+ ? `threat_defense_challenge=true:type=${threatType || 'unknown'}:action=challenge`
167
+ : detections.length
168
+ ? `threat_defense_detect=true:type=${threatType || 'unknown'}:action=detect`
169
+ : 'threat_defense_passed=true',
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Volumetric token-drain circuit breaker (Radware AI DDoS / Bot Manager mitigate).
175
+ * @param {Array<number>} callTimestamps
176
+ * @param {object} [limits]
177
+ */
178
+ function checkRateBurst(callTimestamps = [], limits = {}) {
179
+ const maxPerMinute = limits.maxPerMinute || 60;
180
+ const now = limits.now || Date.now();
181
+ const oneMinuteAgo = now - 60000;
182
+ const recentCalls = callTimestamps.filter((ts) => ts >= oneMinuteAgo);
183
+ const tripped = recentCalls.length >= maxPerMinute;
184
+
185
+ return {
186
+ tripped,
187
+ callCount: recentCalls.length,
188
+ maxPerMinute,
189
+ verdict: tripped ? 'CIRCUIT_OPEN' : 'NORMAL',
190
+ severity: tripped ? 'high' : 'none',
191
+ action: tripped ? 'block' : 'allow',
192
+ message: tripped
193
+ ? `Rate burst limit exceeded (${recentCalls.length}/${maxPerMinute} calls/min). Circuit breaker open.`
194
+ : 'Rate within safe operational envelope.',
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Combined pretool defense used by harness-selector.
200
+ * Persists call history so the circuit breaker is live, not declarative theater.
201
+ */
202
+ function evaluatePretoolDefense(payload, options = {}) {
203
+ const historyPath = options.historyPath || defaultHistoryPath();
204
+ const now = options.now || Date.now();
205
+ const record = options.record !== false;
206
+ const timestamps = record
207
+ ? persistCallTimestamp(historyPath, now)
208
+ : loadCallTimestamps(historyPath);
209
+ const threat = evaluateThreat(payload, options);
210
+ const burst = checkRateBurst(timestamps, {
211
+ maxPerMinute: options.maxPerMinute || 60,
212
+ now,
213
+ });
214
+
215
+ if (burst.tripped && (SEVERITY_RANK[burst.severity] || 0) >= (SEVERITY_RANK[threat.severity] || 0)) {
216
+ return {
217
+ ...threat,
218
+ verdict: 'DENY',
219
+ blocked: true,
220
+ challenged: false,
221
+ action: 'block',
222
+ severity: 'high',
223
+ severityRank: SEVERITY_RANK.high,
224
+ threatType: threat.threatType ? `${threat.threatType}+RateBurst` : 'RateBurst',
225
+ rateBurst: burst,
226
+ selectHarness: true,
227
+ receipt: `threat_defense_interdicted=true:type=RateBurst:action=block`,
228
+ };
229
+ }
230
+
231
+ return {
232
+ ...threat,
233
+ rateBurst: burst,
234
+ selectHarness: threat.blocked || threat.challenged || threat.severity !== 'none',
235
+ };
236
+ }
237
+
238
+ function handleDoctor(stdout = process.stdout) {
239
+ stdout.write('Radware / Bot Manager Threat Defense Doctor Check:\n');
240
+ stdout.write(' ✓ ShadowLeak data exfiltration firewall loaded\n');
241
+ stdout.write(' ✓ ZombieAgent indirect prompt injection interdiction ready\n');
242
+ stdout.write(' ✓ Suspicious-bot challenge ladder (detect→challenge→block)\n');
243
+ stdout.write(' ✓ Algorithmic token-drain circuit breaker operational\n');
244
+ return 0;
245
+ }
246
+
247
+ function mainCli(args = process.argv.slice(2), stdout = process.stdout) {
248
+ if (args.includes('--doctor')) {
249
+ return handleDoctor(stdout);
250
+ }
251
+ if (args.includes('--eval') || args.includes('--check')) {
252
+ const inputIdx = Math.max(args.indexOf('--eval'), args.indexOf('--check')) + 1;
253
+ const input = args[inputIdx] || '';
254
+ const res = evaluatePretoolDefense(input, { record: false });
255
+ stdout.write(`${JSON.stringify(res, null, 2)}\n`);
256
+ return res.blocked ? 1 : 0;
257
+ }
258
+ stdout.write('Usage: radware-threat-defense [--doctor | --eval <payload>]\n');
259
+ return 0;
260
+ }
261
+
262
+ if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
263
+ process.exit(mainCli());
264
+ }
265
+
266
+ module.exports = {
267
+ evaluateThreat,
268
+ evaluatePretoolDefense,
269
+ checkRateBurst,
270
+ loadCallTimestamps,
271
+ persistCallTimestamp,
272
+ defaultHistoryPath,
273
+ handleDoctor,
274
+ mainCli,
275
+ SHADOWLEAK_PATTERNS,
276
+ ZOMBIEAGENT_PATTERNS,
277
+ SUSPICIOUS_BOT_PATTERNS,
278
+ ACTION_FOR_SEVERITY,
279
+ SEVERITY_RANK,
280
+ };