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.
- package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
- package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
- package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
- package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
- package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/llms.txt +1 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/CONTRIBUTING.md +95 -0
- package/README.md +195 -632
- package/THIRD_PARTY_NOTICES.md +89 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/future-agi/.mcp.json +8 -0
- package/adapters/future-agi/FUTURE_AGI.md +23 -0
- package/adapters/future-agi/config.toml +3 -0
- package/adapters/future-agi/future-agi-bridge.js +9 -0
- package/adapters/future-agi/opencode.json +8 -0
- package/adapters/herdr/herdr-plugin.toml +18 -0
- package/adapters/mcp/server-stdio.js +238 -25
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/workos/WORKOS.md +52 -0
- package/bin/cli.js +336 -3
- package/bin/futureagi-bridge +9 -0
- package/config/gate-templates.json +581 -4
- package/config/gates/actor-critic-audit.json +34 -0
- package/config/gates/default.json +9 -3
- package/config/gates/five-walls-governance.json +34 -0
- package/config/gates/future-agi-guardrails.json +34 -0
- package/config/gates/radware-threat-defense-2026.json +61 -0
- package/config/gates/simatree-data-governance.json +33 -0
- package/config/mcp-allowlists.json +4 -0
- package/config/merge-quality-checks.json +6 -0
- package/config/model-candidates.json +312 -29
- package/config/model-tiers.json +18 -0
- package/config/post-deploy-marketing-pages.json +10 -0
- package/config/progressive/01-wire-only.json +11 -0
- package/config/progressive/02-dashboard-empty-ok.json +10 -0
- package/config/progressive/03-one-lesson.json +10 -0
- package/config/progressive/04-warn-fires.json +11 -0
- package/config/progressive/05-strict-optional.json +11 -0
- package/config/progressive/README.md +15 -0
- package/config/schemas/broker-execution-receipt.schema.json +139 -0
- package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
- package/conformance/provider-attestation/vectors.json +320 -0
- package/docs/specs/provider-execution-attestation-v1.md +69 -0
- package/openapi/openapi.yaml +15 -0
- package/package.json +390 -148
- package/public/about.html +2 -2
- package/public/ai-malpractice-prevention.html +7 -7
- package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
- package/public/blog/a-receipt-is-not-world-state.html +388 -0
- package/public/blog/git-at-agent-scale.html +374 -0
- package/public/blog/no-llm-in-the-gate.html +133 -0
- package/public/blog.html +80 -0
- package/public/case-studies.html +16 -1
- package/public/compare.html +28 -0
- package/public/diagnostic.html +216 -7
- package/public/docs/connectors.html +39 -0
- package/public/federal.html +2 -2
- package/public/founders.html +639 -0
- package/public/index.html +87 -9
- package/public/install.html +8 -8
- package/public/learn.html +39 -0
- package/public/numbers.html +2 -2
- package/public/peter.html +310 -0
- package/public/platform-partners.html +119 -0
- package/public/pricing.html +24 -3
- package/public/privacy.html +117 -0
- package/public/pro.html +17 -0
- package/public/support.html +62 -0
- package/public/terms.html +130 -0
- package/public/third-party-notices.html +95 -0
- package/public/yt.html +351 -0
- package/scripts/action-receipts.js +133 -3
- package/scripts/adaptive-governance-arena.js +349 -0
- package/scripts/admin-override.js +205 -0
- package/scripts/agent-action-inventory.js +869 -0
- package/scripts/agent-audit-trace.js +42 -2
- package/scripts/agent-egress-policy.js +1117 -0
- package/scripts/agent-memory-lifecycle.js +141 -2
- package/scripts/agent-operations-planner.js +441 -1
- package/scripts/agent-readiness.js +68 -0
- package/scripts/agent-security-central.js +647 -0
- package/scripts/allowlist-bridge-honesty.js +417 -0
- package/scripts/async-job-runner.js +102 -11
- package/scripts/audit-trail.js +212 -0
- package/scripts/billing.js +1 -1
- package/scripts/broker-execution-receipts.js +719 -0
- package/scripts/budget-aware-gates-proof.js +423 -0
- package/scripts/claude-feedback-sync.js +29 -3
- package/scripts/claw-harness-production.js +237 -0
- package/scripts/cli-schema.js +163 -1
- package/scripts/codex-runbook-flywheel.js +318 -0
- package/scripts/context-footprint.js +186 -0
- package/scripts/contextfs.js +143 -61
- package/scripts/dashboard.js +251 -32
- package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
- package/scripts/docker-sandbox-planner.js +18 -0
- package/scripts/double-blind-eval-protocol.js +252 -0
- package/scripts/edotenv-rl-gateway.js +259 -0
- package/scripts/ensure-production-search-corpus.js +162 -0
- package/scripts/eval-holdout.js +311 -0
- package/scripts/feedback-aggregate.js +21 -2
- package/scripts/feedback-loop.js +87 -5
- package/scripts/feedback-quality.js +9 -0
- package/scripts/file-ledger-lock.js +4 -1
- package/scripts/financial-control-plane.js +41 -1
- package/scripts/find-dormant-requires.js +118 -0
- package/scripts/fs-utils.js +84 -8
- package/scripts/gates-engine.js +810 -63
- package/scripts/generate-case-study-outreach.js +24 -15
- package/scripts/git-at-scale.js +628 -0
- package/scripts/governance-conflict-audit.js +1650 -0
- package/scripts/governance-difficulty-curriculum.js +328 -0
- package/scripts/graphrag-retrieval.js +275 -0
- package/scripts/gurobi-optimizer.js +324 -0
- package/scripts/gurobi_optimizer.py +485 -0
- package/scripts/harness-selector.js +82 -1
- package/scripts/hidden-entry-points.js +284 -0
- package/scripts/human-escalation.js +199 -1
- package/scripts/hybrid-feedback-context.js +152 -19
- package/scripts/intent-governed-execution.js +602 -0
- package/scripts/intervention-policy.js +123 -20
- package/scripts/jit-harness-compose.js +628 -0
- package/scripts/jsonl-watcher.js +10 -0
- package/scripts/lesson-embedding-index.js +95 -12
- package/scripts/lesson-retrieval.js +105 -19
- package/scripts/local-model-profile.js +19 -2
- package/scripts/mailer/resend-mailer.js +1 -1
- package/scripts/matryoshka-embedding.js +235 -0
- package/scripts/mcp-oauth.js +42 -4
- package/scripts/mcp-session-handles.js +1016 -0
- package/scripts/mcp-wiring-doctor.js +314 -0
- package/scripts/memory-firewall.js +115 -2
- package/scripts/memory-scope-readiness.js +299 -0
- package/scripts/memory-vs-rag-route.js +161 -0
- package/scripts/model-tier-router.js +148 -21
- package/scripts/nvidia-specdecode-al-doctor.js +536 -0
- package/scripts/openui-catalog-compose-honesty.js +593 -0
- package/scripts/operational-integrity.js +19 -1
- package/scripts/override-audit.js +213 -0
- package/scripts/package-manager-honesty-doctor.js +458 -0
- package/scripts/pr-manager.js +63 -1
- package/scripts/prove-herdr-adapter.js +52 -0
- package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
- package/scripts/prove-workos.js +73 -0
- package/scripts/provider-attestation-conformance.js +192 -0
- package/scripts/provider-receipt-contract.js +136 -0
- package/scripts/qwen38-max-cost-optimizer.js +401 -0
- package/scripts/radware-threat-defense.js +280 -0
- package/scripts/rag-embedding-identity.js +221 -0
- package/scripts/rag-precision-guardrails.js +112 -2
- package/scripts/remote-feedback-capture.js +159 -0
- package/scripts/research-agent-harness.js +256 -0
- package/scripts/rsi-safety-hillclimb.js +200 -0
- package/scripts/rule-sprawl.js +188 -0
- package/scripts/schedule-manager.js +147 -0
- package/scripts/self-heal.js +8 -0
- package/scripts/session-lease.js +415 -0
- package/scripts/simatree-data-governance.js +347 -0
- package/scripts/slo-alert-engine.js +172 -7
- package/scripts/solver-parity.js +539 -0
- package/scripts/stealth-memory-injection-gate.js +333 -0
- package/scripts/switchyard-router.js +366 -0
- package/scripts/telemetry-analytics.js +84 -27
- package/scripts/temporal-decay-weighting.js +138 -0
- package/scripts/test-all.js +165 -0
- package/scripts/token-savings.js +42 -0
- package/scripts/tool-kpi-tracker.js +108 -5
- package/scripts/tool-registry.js +193 -5
- package/scripts/universal-claim-evaluator.js +14 -2
- package/scripts/vector-store.js +279 -9
- package/scripts/workflow-notebook.js +391 -0
- package/scripts/workflow-sentinel.js +111 -12
- package/scripts/workos-production-guard.js +260 -0
- package/scripts/workspace-search-route.js +515 -0
- package/server.json +2 -2
- package/src/agent-identity-boundary.js +76 -0
- package/src/agent-retrieval-cache.js +155 -0
- package/src/alert-noise-ledger.js +502 -0
- package/src/api/server.js +724 -153
- package/src/git-fast-cache.js +220 -0
- package/src/git-wal-sync.js +156 -0
- package/src/hash-anchored-edit.js +82 -0
- package/src/hermes-platform-protocol.js +475 -0
- package/src/hermes-sync-plane.js +241 -0
- package/src/index.js +30 -1
- package/src/iso42001-compliance-guard.js +97 -0
- package/src/latency-budget.js +244 -0
- package/src/mcp-writeguard.js +316 -0
- package/src/miminions-adapter.js +106 -0
- package/src/pipeline-compass.js +104 -0
- package/src/ppl-alert-pipeline.js +284 -0
- package/src/rendezvous-router.js +90 -0
- package/src/security-questionnaire.js +195 -0
|
@@ -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
|
+
};
|