thumbgate 0.9.9
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/.claude-plugin/README.md +134 -0
- package/.claude-plugin/bundle/icon.png +0 -0
- package/.claude-plugin/bundle/icon.svg +18 -0
- package/.claude-plugin/bundle/server/index.js +24 -0
- package/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +21 -0
- package/.well-known/mcp/server-card.json +231 -0
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/adapters/README.md +9 -0
- package/adapters/amp/skills/rlhf-feedback/SKILL.md +22 -0
- package/adapters/chatgpt/INSTALL.md +83 -0
- package/adapters/chatgpt/openapi.yaml +1281 -0
- package/adapters/claude/.mcp.json +14 -0
- package/adapters/codex/config.toml +9 -0
- package/adapters/gemini/function-declarations.json +224 -0
- package/adapters/mcp/server-stdio.js +788 -0
- package/adapters/opencode/opencode.json +15 -0
- package/bin/cli.js +1483 -0
- package/bin/memory.sh +64 -0
- package/bin/obsidian-sync.sh +20 -0
- package/bin/postinstall.js +37 -0
- package/config/build-metadata.json +4 -0
- package/config/e2e-critical-flows.json +45 -0
- package/config/gate-templates.json +77 -0
- package/config/gates/claim-verification.json +29 -0
- package/config/gates/computer-use.json +39 -0
- package/config/gates/default.json +117 -0
- package/config/github-about.json +25 -0
- package/config/mcp-allowlists.json +135 -0
- package/config/model-tiers.json +33 -0
- package/config/partner-routing.json +132 -0
- package/config/policy-bundles/constrained-v1.json +64 -0
- package/config/policy-bundles/default-v1.json +91 -0
- package/config/rubrics/default-v1.json +52 -0
- package/config/skill-packs/react-testing.json +23 -0
- package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
- package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
- package/config/skill-specs/pr-reviewer.json +9 -0
- package/config/skill-specs/release-status.json +9 -0
- package/config/skill-specs/ticket-triage.json +9 -0
- package/config/subagent-profiles.json +32 -0
- package/config/tessl-tiles.json +29 -0
- package/config/thumbgate-settings.managed.json +12 -0
- package/openapi/openapi.yaml +1281 -0
- package/package.json +286 -0
- package/plugins/amp-skill/INSTALL.md +52 -0
- package/plugins/amp-skill/SKILL.md +64 -0
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
- package/plugins/claude-codex-bridge/.mcp.json +12 -0
- package/plugins/claude-codex-bridge/INSTALL.md +43 -0
- package/plugins/claude-codex-bridge/README.md +46 -0
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
- package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
- package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
- package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
- package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
- package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
- package/plugins/claude-skill/INSTALL.md +55 -0
- package/plugins/claude-skill/SKILL.md +46 -0
- package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
- package/plugins/codex-profile/.mcp.json +12 -0
- package/plugins/codex-profile/AGENTS.md +20 -0
- package/plugins/codex-profile/INSTALL.md +66 -0
- package/plugins/codex-profile/README.md +37 -0
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
- package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
- package/plugins/cursor-marketplace/LICENSE +21 -0
- package/plugins/cursor-marketplace/README.md +124 -0
- package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
- package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
- package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
- package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
- package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
- package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
- package/plugins/cursor-marketplace/mcp.json +12 -0
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
- package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
- package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
- package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
- package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
- package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
- package/plugins/gemini-extension/INSTALL.md +92 -0
- package/plugins/gemini-extension/gemini_prompt.txt +14 -0
- package/plugins/gemini-extension/tool_contract.json +45 -0
- package/plugins/opencode-profile/INSTALL.md +57 -0
- package/public/assets/instagram-card.png +0 -0
- package/public/assets/tiktok-agent-memory.mp4 +0 -0
- package/public/blog.html +400 -0
- package/public/dashboard.html +1093 -0
- package/public/guide.html +317 -0
- package/public/index.html +1195 -0
- package/public/learn/agent-harness-pattern.html +180 -0
- package/public/learn/ai-agent-persistent-memory.html +202 -0
- package/public/learn/learn.css +45 -0
- package/public/learn/mcp-pre-action-gates-explained.html +172 -0
- package/public/learn/stop-ai-agent-force-push.html +134 -0
- package/public/learn/vibe-coding-safety-net.html +142 -0
- package/public/learn.html +213 -0
- package/public/lessons.html +650 -0
- package/public/vercel.json +8 -0
- package/scripts/__pycache__/train_from_feedback.cpython-314.pyc +0 -0
- package/scripts/a2ui-engine.js +73 -0
- package/scripts/access-anomaly-detector.js +12 -0
- package/scripts/adk-consolidator.js +266 -0
- package/scripts/agent-readiness.js +220 -0
- package/scripts/agent-security-hardening.js +227 -0
- package/scripts/agentic-data-pipeline.js +847 -0
- package/scripts/analytics-report.js +328 -0
- package/scripts/analytics-window.js +158 -0
- package/scripts/async-job-runner.js +1001 -0
- package/scripts/audit-trail.js +398 -0
- package/scripts/auto-promote-gates.js +293 -0
- package/scripts/auto-wire-hooks.js +316 -0
- package/scripts/autonomous-sales-agent.js +39 -0
- package/scripts/autoresearch-runner.js +216 -0
- package/scripts/background-agent-governance.js +237 -0
- package/scripts/behavioral-extraction.js +93 -0
- package/scripts/belief-update.js +84 -0
- package/scripts/billing.js +2438 -0
- package/scripts/bot-detector.js +50 -0
- package/scripts/budget-guard.js +173 -0
- package/scripts/build-claude-mcpb.js +189 -0
- package/scripts/build-metadata.js +97 -0
- package/scripts/check-congruence.js +322 -0
- package/scripts/cli-feedback.js +135 -0
- package/scripts/cli-telemetry.js +87 -0
- package/scripts/cloudflare-dynamic-sandbox.js +315 -0
- package/scripts/code-reasoning.js +350 -0
- package/scripts/codegraph-context.js +466 -0
- package/scripts/commercial-offer.js +56 -0
- package/scripts/computer-use-firewall.js +250 -0
- package/scripts/context-engine.js +694 -0
- package/scripts/contextfs.js +1287 -0
- package/scripts/conversation-context.js +119 -0
- package/scripts/creator-campaigns.js +239 -0
- package/scripts/daemon-manager.js +108 -0
- package/scripts/daily-digest.js +11 -0
- package/scripts/dashboard-render-spec.js +395 -0
- package/scripts/dashboard.js +1058 -0
- package/scripts/data-governance.js +173 -0
- package/scripts/delegation-runtime.js +900 -0
- package/scripts/deploy-gcp.sh +44 -0
- package/scripts/deploy-policy.js +231 -0
- package/scripts/disagreement-mining.js +315 -0
- package/scripts/dispatch-brief.js +159 -0
- package/scripts/distribution-surfaces.js +44 -0
- package/scripts/dpo-optimizer.js +206 -0
- package/scripts/ensure-repo-bootstrap.js +129 -0
- package/scripts/ephemeral-agent-store.js +219 -0
- package/scripts/eval-harness.js +56 -0
- package/scripts/evolution-state.js +241 -0
- package/scripts/experiment-tracker.js +267 -0
- package/scripts/export-databricks-bundle.js +242 -0
- package/scripts/export-dpo-pairs.js +344 -0
- package/scripts/export-kto-pairs.js +309 -0
- package/scripts/export-training.js +450 -0
- package/scripts/failure-diagnostics.js +558 -0
- package/scripts/feedback-attribution.js +313 -0
- package/scripts/feedback-fallback.js +110 -0
- package/scripts/feedback-history-distiller.js +391 -0
- package/scripts/feedback-inbox-read.js +162 -0
- package/scripts/feedback-loop.js +1887 -0
- package/scripts/feedback-paths.js +145 -0
- package/scripts/feedback-quality.js +139 -0
- package/scripts/feedback-root-consolidator.js +238 -0
- package/scripts/feedback-schema.js +426 -0
- package/scripts/feedback-session.js +286 -0
- package/scripts/feedback-to-memory.js +185 -0
- package/scripts/feedback-to-rules.js +164 -0
- package/scripts/filesystem-search.js +405 -0
- package/scripts/funnel-analytics.js +35 -0
- package/scripts/gate-satisfy.js +42 -0
- package/scripts/gate-stats.js +116 -0
- package/scripts/gate-templates.js +70 -0
- package/scripts/gates-engine.js +816 -0
- package/scripts/generate-paperbanana-diagrams.sh +99 -0
- package/scripts/generate-pretool-hook.sh +40 -0
- package/scripts/github-about.js +350 -0
- package/scripts/github-outreach.js +65 -0
- package/scripts/gtm-revenue-loop.js +520 -0
- package/scripts/hallucination-detector.js +226 -0
- package/scripts/hf-papers.js +317 -0
- package/scripts/history-distiller.js +200 -0
- package/scripts/hook-auto-capture.sh +100 -0
- package/scripts/hook-stop-pr-thread-check.sh +68 -0
- package/scripts/hook-stop-self-score.sh +51 -0
- package/scripts/hook-stop-verify-deploy.sh +31 -0
- package/scripts/hook-thumbgate-cache-updater.js +48 -0
- package/scripts/hook-verify-before-done.sh +20 -0
- package/scripts/hosted-config.js +156 -0
- package/scripts/hybrid-feedback-context.js +675 -0
- package/scripts/install-mcp.js +159 -0
- package/scripts/intent-router.js +392 -0
- package/scripts/internal-agent-bootstrap.js +490 -0
- package/scripts/jsonl-watcher.js +155 -0
- package/scripts/lesson-db.js +613 -0
- package/scripts/lesson-inference.js +310 -0
- package/scripts/lesson-retrieval.js +95 -0
- package/scripts/lesson-rotation.js +137 -0
- package/scripts/lesson-search.js +644 -0
- package/scripts/lesson-synthesis.js +196 -0
- package/scripts/license.js +50 -0
- package/scripts/local-model-profile.js +384 -0
- package/scripts/markdown-escape.js +12 -0
- package/scripts/marketing-experiment.js +671 -0
- package/scripts/mcp-config.js +149 -0
- package/scripts/mcp-policy.js +99 -0
- package/scripts/memalign-recall.js +111 -0
- package/scripts/memory-firewall.js +222 -0
- package/scripts/memory-migration.js +296 -0
- package/scripts/meta-policy.js +190 -0
- package/scripts/metered-billing.js +16 -0
- package/scripts/model-tier-router.js +301 -0
- package/scripts/money-watcher.js +71 -0
- package/scripts/multi-hop-recall.js +240 -0
- package/scripts/natural-language-harness.js +330 -0
- package/scripts/obsidian-export.js +713 -0
- package/scripts/operational-dashboard.js +103 -0
- package/scripts/operational-summary.js +93 -0
- package/scripts/optimize-context.js +17 -0
- package/scripts/org-dashboard.js +201 -0
- package/scripts/partner-orchestration.js +146 -0
- package/scripts/per-step-scoring.js +165 -0
- package/scripts/perplexity-marketing.js +466 -0
- package/scripts/pii-scanner.js +153 -0
- package/scripts/plan-gate.js +154 -0
- package/scripts/post-everywhere.js +308 -0
- package/scripts/post-to-x-retry.sh +22 -0
- package/scripts/post-to-x.js +369 -0
- package/scripts/pr-manager.js +236 -0
- package/scripts/predictive-insights.js +356 -0
- package/scripts/principle-extractor.js +162 -0
- package/scripts/pro-features.js +40 -0
- package/scripts/pro-local-dashboard.js +174 -0
- package/scripts/problem-detail.js +53 -0
- package/scripts/product-feedback.js +134 -0
- package/scripts/profile-router.js +245 -0
- package/scripts/prompt-dlp.js +221 -0
- package/scripts/prompt-guard.js +83 -0
- package/scripts/prove-adapters.js +863 -0
- package/scripts/prove-attribution.js +365 -0
- package/scripts/prove-automation.js +653 -0
- package/scripts/prove-autoresearch.js +304 -0
- package/scripts/prove-claim-verification.js +277 -0
- package/scripts/prove-cloudflare-sandbox.js +163 -0
- package/scripts/prove-data-pipeline.js +410 -0
- package/scripts/prove-data-quality.js +227 -0
- package/scripts/prove-evolution.js +352 -0
- package/scripts/prove-harnesses.js +287 -0
- package/scripts/prove-intelligence.js +259 -0
- package/scripts/prove-lancedb.js +371 -0
- package/scripts/prove-local-intelligence.js +342 -0
- package/scripts/prove-loop-closure.js +263 -0
- package/scripts/prove-predictive-insights.js +357 -0
- package/scripts/prove-runtime.js +350 -0
- package/scripts/prove-seo-gsd.js +234 -0
- package/scripts/prove-settings.js +279 -0
- package/scripts/prove-subway-upgrades.js +277 -0
- package/scripts/prove-tessl.js +229 -0
- package/scripts/prove-training-export.js +327 -0
- package/scripts/prove-workflow-contract.js +116 -0
- package/scripts/prove-xmemory.js +332 -0
- package/scripts/publish-decision.js +133 -0
- package/scripts/pulse.js +80 -0
- package/scripts/rate-limiter.js +125 -0
- package/scripts/reddit-dm-outreach.js +182 -0
- package/scripts/reddit-monitor-cron.sh +26 -0
- package/scripts/reflector-agent.js +221 -0
- package/scripts/reminder-engine.js +132 -0
- package/scripts/revenue-status.js +472 -0
- package/scripts/risk-scorer.js +459 -0
- package/scripts/rlaif-self-audit.js +129 -0
- package/scripts/rlhf_session_start.sh +32 -0
- package/scripts/rubric-engine.js +230 -0
- package/scripts/schedule-manager.js +251 -0
- package/scripts/secret-scanner.js +414 -0
- package/scripts/self-heal.js +147 -0
- package/scripts/self-healing-check.js +188 -0
- package/scripts/semantic-layer.js +98 -0
- package/scripts/seo-gsd.js +1153 -0
- package/scripts/settings-hierarchy.js +214 -0
- package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
- package/scripts/skill-exporter.js +262 -0
- package/scripts/skill-generator.js +446 -0
- package/scripts/skill-materializer.js +134 -0
- package/scripts/skill-packs.js +136 -0
- package/scripts/skill-proposer.js +99 -0
- package/scripts/skill-quality-tracker.js +282 -0
- package/scripts/slo-alert-engine.js +14 -0
- package/scripts/slow-loop.js +72 -0
- package/scripts/social-analytics/db/schema.sql +32 -0
- package/scripts/social-analytics/db/social-analytics.db +0 -0
- package/scripts/social-analytics/digest.js +256 -0
- package/scripts/social-analytics/generate-instagram-card.js +97 -0
- package/scripts/social-analytics/instagram-thumbgate-post.js +107 -0
- package/scripts/social-analytics/load-env.js +46 -0
- package/scripts/social-analytics/mcp-server.js +289 -0
- package/scripts/social-analytics/normalizer.js +580 -0
- package/scripts/social-analytics/notify.js +162 -0
- package/scripts/social-analytics/poll-all.js +92 -0
- package/scripts/social-analytics/pollers/github.js +195 -0
- package/scripts/social-analytics/pollers/instagram.js +253 -0
- package/scripts/social-analytics/pollers/linkedin.js +330 -0
- package/scripts/social-analytics/pollers/plausible.js +247 -0
- package/scripts/social-analytics/pollers/reddit.js +306 -0
- package/scripts/social-analytics/pollers/threads.js +233 -0
- package/scripts/social-analytics/pollers/tiktok.js +203 -0
- package/scripts/social-analytics/pollers/x.js +227 -0
- package/scripts/social-analytics/pollers/youtube.js +304 -0
- package/scripts/social-analytics/pollers/zernio.js +183 -0
- package/scripts/social-analytics/publish-instagram-thumbgate.js +98 -0
- package/scripts/social-analytics/publish-thumbgate-launch.js +316 -0
- package/scripts/social-analytics/publishers/devto.js +122 -0
- package/scripts/social-analytics/publishers/instagram.js +317 -0
- package/scripts/social-analytics/publishers/linkedin.js +294 -0
- package/scripts/social-analytics/publishers/reddit.js +390 -0
- package/scripts/social-analytics/publishers/threads.js +275 -0
- package/scripts/social-analytics/publishers/tiktok.js +217 -0
- package/scripts/social-analytics/publishers/x.js +259 -0
- package/scripts/social-analytics/publishers/youtube.js +223 -0
- package/scripts/social-analytics/publishers/zernio.js +378 -0
- package/scripts/social-analytics/run-digest.js +34 -0
- package/scripts/social-analytics/store.js +257 -0
- package/scripts/social-analytics/utm.js +143 -0
- package/scripts/social-pipeline.js +2628 -0
- package/scripts/social-quality-gate.js +18 -0
- package/scripts/social-reply-monitor.js +445 -0
- package/scripts/status-dashboard.js +155 -0
- package/scripts/statusline-lesson.js +16 -0
- package/scripts/statusline-tower.js +8 -0
- package/scripts/statusline.sh +116 -0
- package/scripts/stripe-live-status.js +115 -0
- package/scripts/subagent-profiles.js +79 -0
- package/scripts/sync-gh-secrets-from-env.sh +70 -0
- package/scripts/sync-github-about.js +52 -0
- package/scripts/sync-version.js +447 -0
- package/scripts/synthetic-dpo.js +234 -0
- package/scripts/telemetry-analytics.js +821 -0
- package/scripts/tessl-export.js +371 -0
- package/scripts/test-coverage.js +120 -0
- package/scripts/thompson-sampling.js +417 -0
- package/scripts/thumbgate-search.js +189 -0
- package/scripts/tool-kpi-tracker.js +12 -0
- package/scripts/tool-registry.js +811 -0
- package/scripts/train_from_feedback.py +933 -0
- package/scripts/user-profile.js +78 -0
- package/scripts/validate-feedback.js +581 -0
- package/scripts/validate-workflow-contract.js +287 -0
- package/scripts/vector-store.js +197 -0
- package/scripts/verification-loop.js +291 -0
- package/scripts/verify-obsidian-setup.sh +269 -0
- package/scripts/verify-run.js +269 -0
- package/scripts/webhook-delivery.js +62 -0
- package/scripts/weekly-auto-post.js +124 -0
- package/scripts/workflow-runs.js +154 -0
- package/scripts/workflow-sprint-intake.js +475 -0
- package/scripts/workspace-evolver.js +374 -0
- package/scripts/x-autonomous-marketing.js +139 -0
- package/scripts/xmemory-lite.js +405 -0
- package/skills/agent-memory/SKILL.md +97 -0
- package/skills/rlhf-feedback/SKILL.md +49 -0
- package/skills/solve-architecture-autonomy/SKILL.md +17 -0
- package/skills/solve-architecture-autonomy/tool.js +33 -0
- package/skills/thumbgate/SKILL.md +114 -0
- package/src/api/server.js +4206 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { resolveAnalyticsWindow } = require('./analytics-window');
|
|
4
|
+
const { getBillingSummaryLive } = require('./billing');
|
|
5
|
+
const { generateDashboard } = require('./dashboard');
|
|
6
|
+
const { getFeedbackPaths } = require('./feedback-loop');
|
|
7
|
+
const { resolveHostedBillingConfig } = require('./hosted-config');
|
|
8
|
+
|
|
9
|
+
function normalizeText(value) {
|
|
10
|
+
if (value === undefined || value === null) return null;
|
|
11
|
+
const text = String(value).trim();
|
|
12
|
+
return text || null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function shouldPreferHostedDashboard() {
|
|
16
|
+
return String(process.env.THUMBGATE_METRICS_SOURCE || '').trim().toLowerCase() !== 'local';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveHostedDashboardConfig() {
|
|
20
|
+
const runtimeConfig = resolveHostedBillingConfig();
|
|
21
|
+
const apiBaseUrl = normalizeText(process.env.THUMBGATE_BILLING_API_BASE_URL) || runtimeConfig.billingApiBaseUrl;
|
|
22
|
+
const apiKey = normalizeText(process.env.THUMBGATE_API_KEY);
|
|
23
|
+
return {
|
|
24
|
+
apiBaseUrl,
|
|
25
|
+
apiKey,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function buildOperationalDashboard(options = {}) {
|
|
30
|
+
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
31
|
+
const feedbackDir = options.feedbackDir || getFeedbackPaths().FEEDBACK_DIR;
|
|
32
|
+
const billingSummary = await getBillingSummaryLive(analyticsWindow);
|
|
33
|
+
|
|
34
|
+
return generateDashboard(feedbackDir, {
|
|
35
|
+
analyticsWindow,
|
|
36
|
+
billingSummary,
|
|
37
|
+
billingSource: 'live',
|
|
38
|
+
billingFallbackReason: null,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function fetchHostedDashboard(options = {}, config = resolveHostedDashboardConfig()) {
|
|
43
|
+
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
44
|
+
if (!shouldPreferHostedDashboard()) {
|
|
45
|
+
const err = new Error('Hosted operational dashboard is disabled.');
|
|
46
|
+
err.code = 'hosted_dashboard_disabled';
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
if (!config.apiBaseUrl || !config.apiKey) {
|
|
50
|
+
const err = new Error('Hosted operational dashboard is not configured.');
|
|
51
|
+
err.code = 'hosted_dashboard_unconfigured';
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const requestUrl = new URL('/v1/dashboard', config.apiBaseUrl);
|
|
56
|
+
requestUrl.searchParams.set('window', analyticsWindow.window);
|
|
57
|
+
requestUrl.searchParams.set('timezone', analyticsWindow.timeZone);
|
|
58
|
+
requestUrl.searchParams.set('now', analyticsWindow.now);
|
|
59
|
+
|
|
60
|
+
const response = await fetch(requestUrl, {
|
|
61
|
+
method: 'GET',
|
|
62
|
+
headers: {
|
|
63
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
64
|
+
accept: 'application/json',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const detail = await response.text().catch(() => '');
|
|
70
|
+
const err = new Error(`Hosted operational dashboard request failed (${response.status}): ${detail || 'unknown error'}`);
|
|
71
|
+
err.code = 'hosted_dashboard_http_error';
|
|
72
|
+
err.status = response.status;
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return response.json();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function getOperationalDashboard(options = {}) {
|
|
80
|
+
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
81
|
+
try {
|
|
82
|
+
const data = await fetchHostedDashboard(analyticsWindow);
|
|
83
|
+
return {
|
|
84
|
+
source: 'hosted',
|
|
85
|
+
data,
|
|
86
|
+
fallbackReason: null,
|
|
87
|
+
};
|
|
88
|
+
} catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
source: 'local',
|
|
91
|
+
data: await buildOperationalDashboard(analyticsWindow),
|
|
92
|
+
fallbackReason: err && err.message ? err.message : 'hosted_dashboard_unavailable',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = {
|
|
98
|
+
buildOperationalDashboard,
|
|
99
|
+
fetchHostedDashboard,
|
|
100
|
+
getOperationalDashboard,
|
|
101
|
+
resolveHostedDashboardConfig,
|
|
102
|
+
shouldPreferHostedDashboard,
|
|
103
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getBillingSummaryLive } = require('./billing');
|
|
4
|
+
const { resolveAnalyticsWindow } = require('./analytics-window');
|
|
5
|
+
const { resolveHostedBillingConfig } = require('./hosted-config');
|
|
6
|
+
|
|
7
|
+
function normalizeText(value) {
|
|
8
|
+
if (value === undefined || value === null) return null;
|
|
9
|
+
const text = String(value).trim();
|
|
10
|
+
return text || null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function shouldPreferHostedSummary() {
|
|
14
|
+
return String(process.env.THUMBGATE_METRICS_SOURCE || '').trim().toLowerCase() !== 'local';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveHostedSummaryConfig() {
|
|
18
|
+
const runtimeConfig = resolveHostedBillingConfig();
|
|
19
|
+
const apiBaseUrl = normalizeText(process.env.THUMBGATE_BILLING_API_BASE_URL) || runtimeConfig.billingApiBaseUrl;
|
|
20
|
+
const apiKey = normalizeText(process.env.THUMBGATE_API_KEY);
|
|
21
|
+
return {
|
|
22
|
+
apiBaseUrl,
|
|
23
|
+
apiKey,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function fetchHostedBillingSummary(options = {}, config = resolveHostedSummaryConfig()) {
|
|
28
|
+
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
29
|
+
if (!shouldPreferHostedSummary()) {
|
|
30
|
+
const err = new Error('Hosted operational summary is disabled.');
|
|
31
|
+
err.code = 'hosted_summary_disabled';
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
if (!config.apiBaseUrl || !config.apiKey) {
|
|
35
|
+
const err = new Error('Hosted operational summary is not configured.');
|
|
36
|
+
err.code = 'hosted_summary_unconfigured';
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const requestUrl = new URL('/v1/billing/summary', config.apiBaseUrl);
|
|
41
|
+
requestUrl.searchParams.set('window', analyticsWindow.window);
|
|
42
|
+
requestUrl.searchParams.set('timezone', analyticsWindow.timeZone);
|
|
43
|
+
if (options.now !== undefined && options.now !== null && options.now !== '') {
|
|
44
|
+
requestUrl.searchParams.set('now', analyticsWindow.now);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const response = await fetch(requestUrl, {
|
|
48
|
+
method: 'GET',
|
|
49
|
+
headers: {
|
|
50
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
51
|
+
accept: 'application/json',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const detail = await response.text().catch(() => '');
|
|
57
|
+
const err = new Error(`Hosted operational summary request failed (${response.status}): ${detail || 'unknown error'}`);
|
|
58
|
+
err.code = 'hosted_summary_http_error';
|
|
59
|
+
err.status = response.status;
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return response.json();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function getOperationalBillingSummary(options = {}) {
|
|
67
|
+
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
68
|
+
try {
|
|
69
|
+
const summary = await fetchHostedBillingSummary(analyticsWindow);
|
|
70
|
+
return {
|
|
71
|
+
source: 'hosted',
|
|
72
|
+
summary,
|
|
73
|
+
fallbackReason: null,
|
|
74
|
+
};
|
|
75
|
+
} catch (err) {
|
|
76
|
+
const reason = err && err.message ? err.message : 'hosted_summary_unavailable';
|
|
77
|
+
// TODO: Configure hosted billing via THUMBGATE_BILLING_API_BASE_URL and THUMBGATE_API_KEY
|
|
78
|
+
// to avoid falling back to local state. See docs/PRICING_RESEARCH_2026-03-10.md
|
|
79
|
+
console.warn(`[operational-summary] Hosted billing not configured — falling back to local state. Reason: ${reason}`);
|
|
80
|
+
return {
|
|
81
|
+
source: 'local',
|
|
82
|
+
summary: await getBillingSummaryLive(analyticsWindow),
|
|
83
|
+
fallbackReason: reason,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
fetchHostedBillingSummary,
|
|
90
|
+
getOperationalBillingSummary,
|
|
91
|
+
resolveHostedSummaryConfig,
|
|
92
|
+
shouldPreferHostedSummary,
|
|
93
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const CLAUDE_MD_PATH = path.join(process.cwd(), 'CLAUDE.md');
|
|
5
|
+
const THUMBGATE_DIR = path.join(process.cwd(), '.rlhf');
|
|
6
|
+
const RULES_PATH = path.join(THUMBGATE_DIR, 'prevention-rules.md');
|
|
7
|
+
function optimize() {
|
|
8
|
+
console.log('🚀 [Context Optimizer] Starting CLAUDE.md migration...');
|
|
9
|
+
if (!fs.existsSync(CLAUDE_MD_PATH)) return;
|
|
10
|
+
const content = fs.readFileSync(CLAUDE_MD_PATH, 'utf8');
|
|
11
|
+
if (!fs.existsSync(THUMBGATE_DIR)) fs.mkdirSync(THUMBGATE_DIR, { recursive: true });
|
|
12
|
+
const migrationHeader = '\n### [MIGRATED] Rules from CLAUDE.md\n';
|
|
13
|
+
fs.appendFileSync(RULES_PATH, migrationHeader + content.slice(0, 500) + '\n');
|
|
14
|
+
console.log('✅ Migrated rules to the Pre-Action Gates.');
|
|
15
|
+
}
|
|
16
|
+
if (require.main === module) optimize();
|
|
17
|
+
module.exports = { optimize };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Org Dashboard — Multi-Agent Orchestration Visibility
|
|
6
|
+
*
|
|
7
|
+
* Aggregates gate decisions, audit trails, and session data across
|
|
8
|
+
* multiple agent sessions into a single org-wide view. CIOs want to
|
|
9
|
+
* see what ALL their agents are doing, not just one at a time.
|
|
10
|
+
*
|
|
11
|
+
* "I'm not going to have 10,000 agents running in the environment
|
|
12
|
+
* that I don't know what they're doing" — CIO.com, March 2026
|
|
13
|
+
*
|
|
14
|
+
* Pro feature: free tier gets single-agent dashboard only.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
20
|
+
const { readAuditLog, auditStats, skillAdherence } = require('./audit-trail');
|
|
21
|
+
const { isProTier } = require('./rate-limiter');
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Agent Registry
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const REGISTRY_FILENAME = 'agent-registry.jsonl';
|
|
28
|
+
|
|
29
|
+
function getRegistryPath() {
|
|
30
|
+
return path.join(resolveFeedbackDir(), REGISTRY_FILENAME);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register an agent session. Called on MCP server startup or agent bootstrap.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} params
|
|
37
|
+
* @param {string} params.agentId - Unique agent identifier
|
|
38
|
+
* @param {string} [params.source] - Where the agent was spawned from (cli, mcp, github, slack)
|
|
39
|
+
* @param {string} [params.project] - Project/repo name
|
|
40
|
+
* @param {string} [params.branch] - Git branch
|
|
41
|
+
* @param {object} [params.metadata] - Arbitrary metadata
|
|
42
|
+
* @returns {object} The registered agent record
|
|
43
|
+
*/
|
|
44
|
+
function registerAgent({ agentId, source, project, branch, metadata } = {}) {
|
|
45
|
+
const id = agentId || `agent_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
46
|
+
const record = {
|
|
47
|
+
id,
|
|
48
|
+
registeredAt: new Date().toISOString(),
|
|
49
|
+
lastSeenAt: new Date().toISOString(),
|
|
50
|
+
source: source || 'unknown',
|
|
51
|
+
project: project || path.basename(process.cwd()),
|
|
52
|
+
branch: branch || null,
|
|
53
|
+
toolCalls: 0,
|
|
54
|
+
gateBlocks: 0,
|
|
55
|
+
gateWarns: 0,
|
|
56
|
+
metadata: metadata || {},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const registryPath = getRegistryPath();
|
|
60
|
+
const dir = path.dirname(registryPath);
|
|
61
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
62
|
+
fs.appendFileSync(registryPath, JSON.stringify(record) + '\n');
|
|
63
|
+
return record;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Record agent activity — called after each tool call evaluation.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} agentId
|
|
70
|
+
* @param {string} decision - 'allow' | 'deny' | 'warn'
|
|
71
|
+
*/
|
|
72
|
+
function recordAgentActivity(agentId, decision) {
|
|
73
|
+
const registryPath = getRegistryPath();
|
|
74
|
+
if (!fs.existsSync(registryPath)) return;
|
|
75
|
+
|
|
76
|
+
const lines = fs.readFileSync(registryPath, 'utf-8').trim().split('\n');
|
|
77
|
+
const updated = [];
|
|
78
|
+
let found = false;
|
|
79
|
+
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
try {
|
|
82
|
+
const record = JSON.parse(line);
|
|
83
|
+
if (record.id === agentId && !found) {
|
|
84
|
+
record.lastSeenAt = new Date().toISOString();
|
|
85
|
+
record.toolCalls = (record.toolCalls || 0) + 1;
|
|
86
|
+
if (decision === 'deny') record.gateBlocks = (record.gateBlocks || 0) + 1;
|
|
87
|
+
if (decision === 'warn') record.gateWarns = (record.gateWarns || 0) + 1;
|
|
88
|
+
found = true;
|
|
89
|
+
}
|
|
90
|
+
updated.push(JSON.stringify(record));
|
|
91
|
+
} catch {
|
|
92
|
+
updated.push(line);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(registryPath, updated.join('\n') + '\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Load all registered agent sessions.
|
|
101
|
+
*/
|
|
102
|
+
function loadAgentRegistry() {
|
|
103
|
+
const registryPath = getRegistryPath();
|
|
104
|
+
if (!fs.existsSync(registryPath)) return [];
|
|
105
|
+
const raw = fs.readFileSync(registryPath, 'utf-8').trim();
|
|
106
|
+
if (!raw) return [];
|
|
107
|
+
return raw.split('\n').map(line => {
|
|
108
|
+
try { return JSON.parse(line); }
|
|
109
|
+
catch { return null; }
|
|
110
|
+
}).filter(Boolean);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Org Dashboard Aggregation
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Generate org-wide dashboard aggregating all agent sessions.
|
|
119
|
+
* Pro feature — returns limited data on free tier.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} [opts]
|
|
122
|
+
* @param {number} [opts.windowHours=24] - Lookback window in hours
|
|
123
|
+
* @returns {object} Org dashboard data
|
|
124
|
+
*/
|
|
125
|
+
function generateOrgDashboard(opts = {}) {
|
|
126
|
+
const windowHours = opts.windowHours || 24;
|
|
127
|
+
const cutoff = Date.now() - windowHours * 60 * 60 * 1000;
|
|
128
|
+
const pro = typeof opts.proOverride === 'boolean'
|
|
129
|
+
? opts.proOverride
|
|
130
|
+
: isProTier(opts.authContext);
|
|
131
|
+
|
|
132
|
+
// Load all agents
|
|
133
|
+
const allAgents = loadAgentRegistry();
|
|
134
|
+
const activeAgents = allAgents.filter(a => new Date(a.lastSeenAt).getTime() > cutoff);
|
|
135
|
+
|
|
136
|
+
// Aggregate audit trail
|
|
137
|
+
const audit = auditStats();
|
|
138
|
+
const adherence = skillAdherence();
|
|
139
|
+
|
|
140
|
+
// Per-agent summary
|
|
141
|
+
const agentSummaries = activeAgents.map(a => ({
|
|
142
|
+
id: a.id,
|
|
143
|
+
source: a.source,
|
|
144
|
+
project: a.project,
|
|
145
|
+
branch: a.branch,
|
|
146
|
+
registeredAt: a.registeredAt,
|
|
147
|
+
lastSeenAt: a.lastSeenAt,
|
|
148
|
+
toolCalls: a.toolCalls || 0,
|
|
149
|
+
gateBlocks: a.gateBlocks || 0,
|
|
150
|
+
gateWarns: a.gateWarns || 0,
|
|
151
|
+
adherenceRate: a.toolCalls > 0
|
|
152
|
+
? Math.round(((a.toolCalls - (a.gateBlocks || 0) - (a.gateWarns || 0)) / a.toolCalls) * 10000) / 100
|
|
153
|
+
: 100,
|
|
154
|
+
}));
|
|
155
|
+
|
|
156
|
+
// Top blocked gates across all agents
|
|
157
|
+
const topBlockedGates = Object.entries(audit.byGate || {})
|
|
158
|
+
.map(([gateId, counts]) => ({ gateId, blocked: counts.deny || 0, warned: counts.warn || 0 }))
|
|
159
|
+
.sort((a, b) => b.blocked - a.blocked)
|
|
160
|
+
.slice(0, 10);
|
|
161
|
+
|
|
162
|
+
// Risk agents — those with lowest adherence
|
|
163
|
+
const riskAgents = agentSummaries
|
|
164
|
+
.filter(a => a.toolCalls >= 3)
|
|
165
|
+
.sort((a, b) => a.adherenceRate - b.adherenceRate)
|
|
166
|
+
.slice(0, 5);
|
|
167
|
+
|
|
168
|
+
const summary = {
|
|
169
|
+
windowHours,
|
|
170
|
+
totalAgents: allAgents.length,
|
|
171
|
+
activeAgents: activeAgents.length,
|
|
172
|
+
totalToolCalls: audit.total,
|
|
173
|
+
totalBlocked: audit.deny,
|
|
174
|
+
totalWarned: audit.warn,
|
|
175
|
+
totalAllowed: audit.allow,
|
|
176
|
+
orgAdherenceRate: adherence.overall,
|
|
177
|
+
topBlockedGates,
|
|
178
|
+
riskAgents: pro ? riskAgents : riskAgents.slice(0, 1),
|
|
179
|
+
agents: pro ? agentSummaries : agentSummaries.slice(0, 3),
|
|
180
|
+
proRequired: !pro,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
if (!pro) {
|
|
184
|
+
summary.upgradeMessage = 'Upgrade to Pro for full org visibility — all agents, all gates, all history. https://thumbgate-production.up.railway.app/checkout/pro';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return summary;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Exports
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
module.exports = {
|
|
195
|
+
registerAgent,
|
|
196
|
+
recordAgentActivity,
|
|
197
|
+
loadAgentRegistry,
|
|
198
|
+
generateOrgDashboard,
|
|
199
|
+
getRegistryPath,
|
|
200
|
+
REGISTRY_FILENAME,
|
|
201
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const PROJECT_ROOT = path.join(__dirname, '..');
|
|
7
|
+
const DEFAULT_CONFIG_PATH = path.join(PROJECT_ROOT, 'config', 'partner-routing.json');
|
|
8
|
+
|
|
9
|
+
function clamp(value, min, max) {
|
|
10
|
+
return Math.min(max, Math.max(min, value));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function loadPartnerRoutingConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
14
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
|
|
17
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
18
|
+
throw new Error('Invalid partner routing config: expected object');
|
|
19
|
+
}
|
|
20
|
+
if (!parsed.defaultProfile || typeof parsed.defaultProfile !== 'string') {
|
|
21
|
+
throw new Error('Invalid partner routing config: missing defaultProfile');
|
|
22
|
+
}
|
|
23
|
+
if (!parsed.profiles || typeof parsed.profiles !== 'object') {
|
|
24
|
+
throw new Error('Invalid partner routing config: missing profiles');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizePartnerProfile(partnerProfile, config = loadPartnerRoutingConfig()) {
|
|
31
|
+
if (!partnerProfile) {
|
|
32
|
+
return config.defaultProfile;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const raw = String(partnerProfile).trim().toLowerCase();
|
|
36
|
+
const alias = config.aliases && config.aliases[raw];
|
|
37
|
+
const resolved = alias || raw;
|
|
38
|
+
|
|
39
|
+
if (!config.profiles[resolved]) {
|
|
40
|
+
throw new Error(`Unknown partner profile: ${partnerProfile}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getPartnerCategory(partnerProfile) {
|
|
47
|
+
return `partner_${partnerProfile}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function scaleBudgetValue(value, multiplier) {
|
|
51
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
const effectiveMultiplier = Number.isFinite(multiplier) && multiplier > 0 ? multiplier : 1;
|
|
55
|
+
return Math.max(1, Math.round(value * effectiveMultiplier));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function scaleTokenBudget(tokenBudget, multipliers = {}) {
|
|
59
|
+
if (!tokenBudget || typeof tokenBudget !== 'object') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
total: scaleBudgetValue(tokenBudget.total, multipliers.total),
|
|
65
|
+
perAction: scaleBudgetValue(tokenBudget.perAction, multipliers.perAction),
|
|
66
|
+
contextPack: scaleBudgetValue(tokenBudget.contextPack, multipliers.contextPack),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildPartnerStrategy(options = {}) {
|
|
71
|
+
const config = loadPartnerRoutingConfig(options.configPath);
|
|
72
|
+
const profile = normalizePartnerProfile(options.partnerProfile, config);
|
|
73
|
+
const profileConfig = config.profiles[profile] || {};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
profile,
|
|
77
|
+
label: profileConfig.label || profile,
|
|
78
|
+
description: profileConfig.description || '',
|
|
79
|
+
verificationMode: profileConfig.verificationMode || 'standard',
|
|
80
|
+
maxRetryDelta: Number.isFinite(profileConfig.maxRetryDelta) ? profileConfig.maxRetryDelta : 0,
|
|
81
|
+
rewardBias: Number.isFinite(profileConfig.rewardBias) ? profileConfig.rewardBias : 0,
|
|
82
|
+
partnerCategory: getPartnerCategory(profile),
|
|
83
|
+
actionBiases: profileConfig.actionBiases || {},
|
|
84
|
+
recommendedChecks: Array.isArray(profileConfig.recommendedChecks) ? profileConfig.recommendedChecks.slice() : [],
|
|
85
|
+
tokenBudget: scaleTokenBudget(options.tokenBudget, profileConfig.tokenBudgetMultiplier || {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getPartnerActionBias(action, partnerStrategy) {
|
|
90
|
+
if (!action || !partnerStrategy || !partnerStrategy.actionBiases) {
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
return Number(partnerStrategy.actionBiases[action.name] || 0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveVerificationRetries(baseMaxRetries, partnerStrategy) {
|
|
97
|
+
const requested = Number.isFinite(baseMaxRetries) ? baseMaxRetries : 3;
|
|
98
|
+
const delta = partnerStrategy && Number.isFinite(partnerStrategy.maxRetryDelta)
|
|
99
|
+
? partnerStrategy.maxRetryDelta
|
|
100
|
+
: 0;
|
|
101
|
+
return Math.max(1, requested + delta);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function computePartnerReward(params = {}) {
|
|
105
|
+
const config = loadPartnerRoutingConfig(params.configPath);
|
|
106
|
+
const rewardModel = config.rewardModel || {};
|
|
107
|
+
const accepted = params.accepted === true;
|
|
108
|
+
const attempts = Number.isFinite(params.attempts) ? params.attempts : 1;
|
|
109
|
+
const violationCount = Number.isFinite(params.violationCount) ? params.violationCount : 0;
|
|
110
|
+
const partnerStrategy = params.partnerStrategy || buildPartnerStrategy({
|
|
111
|
+
partnerProfile: params.partnerProfile,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const baseReward = accepted ? Number(rewardModel.accepted || 1) : Number(rewardModel.rejected || -1);
|
|
115
|
+
const attemptPenalty = Math.max(0, attempts - 1) * Number(rewardModel.attemptPenalty || 0);
|
|
116
|
+
const violationPenalty = Math.min(
|
|
117
|
+
violationCount * Number(rewardModel.violationPenalty || 0),
|
|
118
|
+
Number(rewardModel.maxViolationPenalty || 0),
|
|
119
|
+
);
|
|
120
|
+
const rawReward = baseReward - attemptPenalty - violationPenalty + Number(partnerStrategy.rewardBias || 0);
|
|
121
|
+
const reward = Math.round(clamp(rawReward, -1, 1) * 1000) / 1000;
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
profile: partnerStrategy.profile,
|
|
125
|
+
reward,
|
|
126
|
+
weightMultiplier: 1 + Math.abs(reward),
|
|
127
|
+
components: {
|
|
128
|
+
baseReward,
|
|
129
|
+
attemptPenalty,
|
|
130
|
+
violationPenalty,
|
|
131
|
+
rewardBias: Number(partnerStrategy.rewardBias || 0),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
DEFAULT_CONFIG_PATH,
|
|
138
|
+
loadPartnerRoutingConfig,
|
|
139
|
+
normalizePartnerProfile,
|
|
140
|
+
getPartnerCategory,
|
|
141
|
+
scaleTokenBudget,
|
|
142
|
+
buildPartnerStrategy,
|
|
143
|
+
getPartnerActionBias,
|
|
144
|
+
resolveVerificationRetries,
|
|
145
|
+
computePartnerReward,
|
|
146
|
+
};
|