thumbgate 0.9.10

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 (364) hide show
  1. package/.claude-plugin/README.md +134 -0
  2. package/.claude-plugin/bundle/icon.png +0 -0
  3. package/.claude-plugin/bundle/icon.svg +18 -0
  4. package/.claude-plugin/bundle/server/index.js +24 -0
  5. package/.claude-plugin/marketplace.json +36 -0
  6. package/.claude-plugin/plugin.json +21 -0
  7. package/.well-known/mcp/server-card.json +231 -0
  8. package/LICENSE +21 -0
  9. package/README.md +375 -0
  10. package/adapters/README.md +9 -0
  11. package/adapters/amp/skills/thumbgate-feedback/SKILL.md +22 -0
  12. package/adapters/chatgpt/INSTALL.md +83 -0
  13. package/adapters/chatgpt/openapi.yaml +1281 -0
  14. package/adapters/claude/.mcp.json +14 -0
  15. package/adapters/codex/config.toml +9 -0
  16. package/adapters/gemini/function-declarations.json +224 -0
  17. package/adapters/mcp/server-stdio.js +788 -0
  18. package/adapters/opencode/opencode.json +15 -0
  19. package/bin/cli.js +1484 -0
  20. package/bin/memory.sh +64 -0
  21. package/bin/obsidian-sync.sh +20 -0
  22. package/bin/postinstall.js +37 -0
  23. package/config/build-metadata.json +4 -0
  24. package/config/e2e-critical-flows.json +45 -0
  25. package/config/gate-templates.json +77 -0
  26. package/config/gates/claim-verification.json +29 -0
  27. package/config/gates/computer-use.json +39 -0
  28. package/config/gates/default.json +117 -0
  29. package/config/github-about.json +25 -0
  30. package/config/mcp-allowlists.json +135 -0
  31. package/config/model-tiers.json +33 -0
  32. package/config/partner-routing.json +132 -0
  33. package/config/policy-bundles/constrained-v1.json +64 -0
  34. package/config/policy-bundles/default-v1.json +91 -0
  35. package/config/rubrics/default-v1.json +52 -0
  36. package/config/skill-packs/react-testing.json +23 -0
  37. package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
  38. package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
  39. package/config/skill-specs/pr-reviewer.json +9 -0
  40. package/config/skill-specs/release-status.json +9 -0
  41. package/config/skill-specs/ticket-triage.json +9 -0
  42. package/config/subagent-profiles.json +32 -0
  43. package/config/tessl-tiles.json +29 -0
  44. package/config/thumbgate-settings.managed.json +12 -0
  45. package/openapi/openapi.yaml +1281 -0
  46. package/package.json +283 -0
  47. package/plugins/amp-skill/INSTALL.md +52 -0
  48. package/plugins/amp-skill/SKILL.md +64 -0
  49. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
  50. package/plugins/claude-codex-bridge/.mcp.json +12 -0
  51. package/plugins/claude-codex-bridge/INSTALL.md +43 -0
  52. package/plugins/claude-codex-bridge/README.md +46 -0
  53. package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
  54. package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
  55. package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
  56. package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
  57. package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
  58. package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
  59. package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
  60. package/plugins/claude-skill/INSTALL.md +55 -0
  61. package/plugins/claude-skill/SKILL.md +46 -0
  62. package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
  63. package/plugins/codex-profile/.mcp.json +12 -0
  64. package/plugins/codex-profile/AGENTS.md +20 -0
  65. package/plugins/codex-profile/INSTALL.md +66 -0
  66. package/plugins/codex-profile/README.md +37 -0
  67. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
  68. package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
  69. package/plugins/cursor-marketplace/LICENSE +21 -0
  70. package/plugins/cursor-marketplace/README.md +124 -0
  71. package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
  72. package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
  73. package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
  74. package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
  75. package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
  76. package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
  77. package/plugins/cursor-marketplace/mcp.json +12 -0
  78. package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
  79. package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
  80. package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
  81. package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
  82. package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
  83. package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
  84. package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
  85. package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
  86. package/plugins/gemini-extension/INSTALL.md +92 -0
  87. package/plugins/gemini-extension/gemini_prompt.txt +14 -0
  88. package/plugins/gemini-extension/tool_contract.json +45 -0
  89. package/plugins/opencode-profile/INSTALL.md +57 -0
  90. package/public/assets/instagram-card.png +0 -0
  91. package/public/assets/tiktok-agent-memory.mp4 +0 -0
  92. package/public/blog.html +400 -0
  93. package/public/dashboard.html +1093 -0
  94. package/public/guide.html +317 -0
  95. package/public/index.html +1014 -0
  96. package/public/learn/agent-harness-pattern.html +180 -0
  97. package/public/learn/ai-agent-persistent-memory.html +202 -0
  98. package/public/learn/learn.css +45 -0
  99. package/public/learn/mcp-pre-action-gates-explained.html +172 -0
  100. package/public/learn/stop-ai-agent-force-push.html +134 -0
  101. package/public/learn/vibe-coding-safety-net.html +142 -0
  102. package/public/learn.html +213 -0
  103. package/public/lessons.html +650 -0
  104. package/public/vercel.json +8 -0
  105. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  106. package/scripts/a2ui-engine.js +73 -0
  107. package/scripts/access-anomaly-detector.js +12 -0
  108. package/scripts/adk-consolidator.js +266 -0
  109. package/scripts/agent-readiness.js +220 -0
  110. package/scripts/agent-security-hardening.js +227 -0
  111. package/scripts/agentic-data-pipeline.js +847 -0
  112. package/scripts/analytics-report.js +328 -0
  113. package/scripts/analytics-window.js +158 -0
  114. package/scripts/async-job-runner.js +1001 -0
  115. package/scripts/audit-trail.js +398 -0
  116. package/scripts/auto-promote-gates.js +299 -0
  117. package/scripts/auto-wire-hooks.js +312 -0
  118. package/scripts/autonomous-sales-agent.js +39 -0
  119. package/scripts/autoresearch-runner.js +216 -0
  120. package/scripts/background-agent-governance.js +237 -0
  121. package/scripts/behavioral-extraction.js +97 -0
  122. package/scripts/belief-update.js +84 -0
  123. package/scripts/billing.js +2438 -0
  124. package/scripts/bot-detector.js +50 -0
  125. package/scripts/budget-guard.js +173 -0
  126. package/scripts/build-claude-mcpb.js +189 -0
  127. package/scripts/build-metadata.js +97 -0
  128. package/scripts/check-congruence.js +322 -0
  129. package/scripts/cli-feedback.js +135 -0
  130. package/scripts/cli-telemetry.js +87 -0
  131. package/scripts/cloudflare-dynamic-sandbox.js +315 -0
  132. package/scripts/code-reasoning.js +350 -0
  133. package/scripts/codegraph-context.js +466 -0
  134. package/scripts/commercial-offer.js +56 -0
  135. package/scripts/computer-use-firewall.js +250 -0
  136. package/scripts/context-engine.js +694 -0
  137. package/scripts/contextfs.js +1287 -0
  138. package/scripts/conversation-context.js +119 -0
  139. package/scripts/creator-campaigns.js +239 -0
  140. package/scripts/daemon-manager.js +108 -0
  141. package/scripts/daily-digest.js +11 -0
  142. package/scripts/dashboard-render-spec.js +395 -0
  143. package/scripts/dashboard.js +1058 -0
  144. package/scripts/data-governance.js +173 -0
  145. package/scripts/delegation-runtime.js +900 -0
  146. package/scripts/deploy-gcp.sh +44 -0
  147. package/scripts/deploy-policy.js +263 -0
  148. package/scripts/disagreement-mining.js +315 -0
  149. package/scripts/dispatch-brief.js +159 -0
  150. package/scripts/distribution-surfaces.js +44 -0
  151. package/scripts/dpo-optimizer.js +209 -0
  152. package/scripts/ephemeral-agent-store.js +219 -0
  153. package/scripts/eval-harness.js +56 -0
  154. package/scripts/evolution-state.js +241 -0
  155. package/scripts/experiment-tracker.js +267 -0
  156. package/scripts/export-databricks-bundle.js +242 -0
  157. package/scripts/export-dpo-pairs.js +345 -0
  158. package/scripts/export-kto-pairs.js +310 -0
  159. package/scripts/export-training.js +448 -0
  160. package/scripts/failure-diagnostics.js +558 -0
  161. package/scripts/feedback-attribution.js +313 -0
  162. package/scripts/feedback-fallback.js +111 -0
  163. package/scripts/feedback-history-distiller.js +391 -0
  164. package/scripts/feedback-inbox-read.js +162 -0
  165. package/scripts/feedback-loop.js +1887 -0
  166. package/scripts/feedback-paths.js +145 -0
  167. package/scripts/feedback-quality.js +139 -0
  168. package/scripts/feedback-root-consolidator.js +238 -0
  169. package/scripts/feedback-schema.js +426 -0
  170. package/scripts/feedback-session.js +286 -0
  171. package/scripts/feedback-to-memory.js +185 -0
  172. package/scripts/feedback-to-rules.js +163 -0
  173. package/scripts/filesystem-search.js +404 -0
  174. package/scripts/funnel-analytics.js +35 -0
  175. package/scripts/gate-satisfy.js +42 -0
  176. package/scripts/gate-stats.js +116 -0
  177. package/scripts/gate-templates.js +70 -0
  178. package/scripts/gates-engine.js +816 -0
  179. package/scripts/generate-paperbanana-diagrams.sh +99 -0
  180. package/scripts/generate-pretool-hook.sh +40 -0
  181. package/scripts/github-about.js +350 -0
  182. package/scripts/github-outreach.js +65 -0
  183. package/scripts/gtm-revenue-loop.js +520 -0
  184. package/scripts/hallucination-detector.js +226 -0
  185. package/scripts/hf-papers.js +317 -0
  186. package/scripts/history-distiller.js +200 -0
  187. package/scripts/hook-auto-capture.sh +95 -0
  188. package/scripts/hook-stop-pr-thread-check.sh +68 -0
  189. package/scripts/hook-stop-self-score.sh +51 -0
  190. package/scripts/hook-stop-verify-deploy.sh +31 -0
  191. package/scripts/hook-thumbgate-cache-updater.js +48 -0
  192. package/scripts/hook-verify-before-done.sh +20 -0
  193. package/scripts/hosted-config.js +170 -0
  194. package/scripts/hybrid-feedback-context.js +676 -0
  195. package/scripts/install-mcp.js +159 -0
  196. package/scripts/intent-router.js +392 -0
  197. package/scripts/internal-agent-bootstrap.js +490 -0
  198. package/scripts/jsonl-watcher.js +155 -0
  199. package/scripts/lesson-db.js +613 -0
  200. package/scripts/lesson-inference.js +315 -0
  201. package/scripts/lesson-retrieval.js +95 -0
  202. package/scripts/lesson-rotation.js +137 -0
  203. package/scripts/lesson-search.js +644 -0
  204. package/scripts/lesson-synthesis.js +196 -0
  205. package/scripts/license.js +50 -0
  206. package/scripts/local-model-profile.js +383 -0
  207. package/scripts/markdown-escape.js +12 -0
  208. package/scripts/marketing-experiment.js +671 -0
  209. package/scripts/mcp-config.js +149 -0
  210. package/scripts/mcp-policy.js +99 -0
  211. package/scripts/memalign-recall.js +111 -0
  212. package/scripts/memory-firewall.js +222 -0
  213. package/scripts/memory-migration.js +296 -0
  214. package/scripts/meta-policy.js +194 -0
  215. package/scripts/metered-billing.js +16 -0
  216. package/scripts/model-tier-router.js +301 -0
  217. package/scripts/money-watcher.js +71 -0
  218. package/scripts/multi-hop-recall.js +240 -0
  219. package/scripts/natural-language-harness.js +330 -0
  220. package/scripts/obsidian-export.js +712 -0
  221. package/scripts/operational-dashboard.js +103 -0
  222. package/scripts/operational-summary.js +93 -0
  223. package/scripts/optimize-context.js +17 -0
  224. package/scripts/org-dashboard.js +201 -0
  225. package/scripts/partner-orchestration.js +146 -0
  226. package/scripts/per-step-scoring.js +165 -0
  227. package/scripts/perplexity-marketing.js +466 -0
  228. package/scripts/pii-scanner.js +153 -0
  229. package/scripts/plan-gate.js +154 -0
  230. package/scripts/post-everywhere.js +308 -0
  231. package/scripts/post-to-x-retry.sh +22 -0
  232. package/scripts/post-to-x.js +369 -0
  233. package/scripts/pr-manager.js +236 -0
  234. package/scripts/predictive-insights.js +356 -0
  235. package/scripts/principle-extractor.js +162 -0
  236. package/scripts/pro-features.js +40 -0
  237. package/scripts/pro-local-dashboard.js +174 -0
  238. package/scripts/problem-detail.js +53 -0
  239. package/scripts/product-feedback.js +134 -0
  240. package/scripts/profile-router.js +245 -0
  241. package/scripts/prompt-dlp.js +221 -0
  242. package/scripts/prompt-guard.js +83 -0
  243. package/scripts/prove-adapters.js +863 -0
  244. package/scripts/prove-attribution.js +365 -0
  245. package/scripts/prove-automation.js +653 -0
  246. package/scripts/prove-autoresearch.js +304 -0
  247. package/scripts/prove-claim-verification.js +277 -0
  248. package/scripts/prove-cloudflare-sandbox.js +163 -0
  249. package/scripts/prove-data-pipeline.js +410 -0
  250. package/scripts/prove-data-quality.js +227 -0
  251. package/scripts/prove-evolution.js +352 -0
  252. package/scripts/prove-harnesses.js +287 -0
  253. package/scripts/prove-intelligence.js +259 -0
  254. package/scripts/prove-lancedb.js +371 -0
  255. package/scripts/prove-local-intelligence.js +342 -0
  256. package/scripts/prove-loop-closure.js +263 -0
  257. package/scripts/prove-predictive-insights.js +357 -0
  258. package/scripts/prove-runtime.js +350 -0
  259. package/scripts/prove-seo-gsd.js +234 -0
  260. package/scripts/prove-settings.js +279 -0
  261. package/scripts/prove-subway-upgrades.js +277 -0
  262. package/scripts/prove-tessl.js +229 -0
  263. package/scripts/prove-training-export.js +327 -0
  264. package/scripts/prove-workflow-contract.js +116 -0
  265. package/scripts/prove-xmemory.js +332 -0
  266. package/scripts/publish-decision.js +133 -0
  267. package/scripts/pulse.js +80 -0
  268. package/scripts/rate-limiter.js +125 -0
  269. package/scripts/reddit-dm-outreach.js +182 -0
  270. package/scripts/reddit-monitor-cron.sh +26 -0
  271. package/scripts/reflector-agent.js +221 -0
  272. package/scripts/reminder-engine.js +132 -0
  273. package/scripts/revenue-status.js +472 -0
  274. package/scripts/risk-scorer.js +458 -0
  275. package/scripts/rlaif-self-audit.js +129 -0
  276. package/scripts/rubric-engine.js +230 -0
  277. package/scripts/schedule-manager.js +251 -0
  278. package/scripts/secret-scanner.js +414 -0
  279. package/scripts/self-heal.js +147 -0
  280. package/scripts/self-healing-check.js +188 -0
  281. package/scripts/semantic-layer.js +98 -0
  282. package/scripts/seo-gsd.js +1153 -0
  283. package/scripts/settings-hierarchy.js +214 -0
  284. package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
  285. package/scripts/skill-exporter.js +262 -0
  286. package/scripts/skill-generator.js +446 -0
  287. package/scripts/skill-materializer.js +134 -0
  288. package/scripts/skill-packs.js +136 -0
  289. package/scripts/skill-proposer.js +99 -0
  290. package/scripts/skill-quality-tracker.js +284 -0
  291. package/scripts/slo-alert-engine.js +14 -0
  292. package/scripts/slow-loop.js +72 -0
  293. package/scripts/social-analytics/db/schema.sql +32 -0
  294. package/scripts/social-analytics/digest.js +256 -0
  295. package/scripts/social-analytics/generate-instagram-card.js +97 -0
  296. package/scripts/social-analytics/instagram-thumbgate-post.js +73 -0
  297. package/scripts/social-analytics/mcp-server.js +289 -0
  298. package/scripts/social-analytics/normalizer.js +580 -0
  299. package/scripts/social-analytics/notify.js +162 -0
  300. package/scripts/social-analytics/poll-all.js +107 -0
  301. package/scripts/social-analytics/pollers/github.js +195 -0
  302. package/scripts/social-analytics/pollers/instagram.js +253 -0
  303. package/scripts/social-analytics/pollers/linkedin.js +330 -0
  304. package/scripts/social-analytics/pollers/plausible.js +247 -0
  305. package/scripts/social-analytics/pollers/reddit.js +306 -0
  306. package/scripts/social-analytics/pollers/threads.js +233 -0
  307. package/scripts/social-analytics/pollers/tiktok.js +203 -0
  308. package/scripts/social-analytics/pollers/x.js +227 -0
  309. package/scripts/social-analytics/pollers/youtube.js +304 -0
  310. package/scripts/social-analytics/pollers/zernio.js +180 -0
  311. package/scripts/social-analytics/publish-instagram-thumbgate.js +85 -0
  312. package/scripts/social-analytics/publishers/devto.js +122 -0
  313. package/scripts/social-analytics/publishers/instagram.js +317 -0
  314. package/scripts/social-analytics/publishers/linkedin.js +294 -0
  315. package/scripts/social-analytics/publishers/reddit.js +390 -0
  316. package/scripts/social-analytics/publishers/threads.js +275 -0
  317. package/scripts/social-analytics/publishers/tiktok.js +217 -0
  318. package/scripts/social-analytics/publishers/x.js +259 -0
  319. package/scripts/social-analytics/publishers/youtube.js +223 -0
  320. package/scripts/social-analytics/publishers/zernio.js +209 -0
  321. package/scripts/social-analytics/run-digest.js +34 -0
  322. package/scripts/social-analytics/store.js +257 -0
  323. package/scripts/social-analytics/utm.js +143 -0
  324. package/scripts/social-pipeline.js +2628 -0
  325. package/scripts/social-quality-gate.js +18 -0
  326. package/scripts/social-reply-monitor.js +445 -0
  327. package/scripts/status-dashboard.js +155 -0
  328. package/scripts/statusline-lesson.js +16 -0
  329. package/scripts/statusline-tower.js +8 -0
  330. package/scripts/statusline.sh +116 -0
  331. package/scripts/stripe-live-status.js +115 -0
  332. package/scripts/subagent-profiles.js +79 -0
  333. package/scripts/sync-gh-secrets-from-env.sh +70 -0
  334. package/scripts/sync-github-about.js +52 -0
  335. package/scripts/sync-version.js +451 -0
  336. package/scripts/synthetic-dpo.js +234 -0
  337. package/scripts/telemetry-analytics.js +821 -0
  338. package/scripts/tessl-export.js +371 -0
  339. package/scripts/test-coverage.js +120 -0
  340. package/scripts/thompson-sampling.js +417 -0
  341. package/scripts/thumbgate-search.js +189 -0
  342. package/scripts/tool-kpi-tracker.js +12 -0
  343. package/scripts/tool-registry.js +811 -0
  344. package/scripts/train_from_feedback.py +910 -0
  345. package/scripts/user-profile.js +78 -0
  346. package/scripts/validate-feedback.js +580 -0
  347. package/scripts/validate-workflow-contract.js +287 -0
  348. package/scripts/vector-store.js +198 -0
  349. package/scripts/verification-loop.js +291 -0
  350. package/scripts/verify-obsidian-setup.sh +269 -0
  351. package/scripts/verify-run.js +269 -0
  352. package/scripts/webhook-delivery.js +62 -0
  353. package/scripts/weekly-auto-post.js +124 -0
  354. package/scripts/workflow-runs.js +154 -0
  355. package/scripts/workflow-sprint-intake.js +475 -0
  356. package/scripts/workspace-evolver.js +374 -0
  357. package/scripts/x-autonomous-marketing.js +139 -0
  358. package/scripts/xmemory-lite.js +405 -0
  359. package/skills/agent-memory/SKILL.md +97 -0
  360. package/skills/solve-architecture-autonomy/SKILL.md +17 -0
  361. package/skills/solve-architecture-autonomy/tool.js +33 -0
  362. package/skills/thumbgate/SKILL.md +114 -0
  363. package/skills/thumbgate-feedback/SKILL.md +49 -0
  364. package/src/api/server.js +4208 -0
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * GPT-5.4 Tier Router — routes tasks to nano/mini/frontier based on
6
+ * task complexity, context size, risk level, and retry count.
7
+ * Includes frontier budget control.
8
+ */
9
+
10
+ const path = require('path');
11
+ const { recommendInferenceBackend } = require('./local-model-profile');
12
+
13
+ const CONFIG_PATH = path.join(__dirname, '..', 'config', 'model-tiers.json');
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Load config
17
+ // ---------------------------------------------------------------------------
18
+
19
+ let _config;
20
+ function loadConfig() {
21
+ if (!_config) _config = require(CONFIG_PATH);
22
+ return _config;
23
+ }
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Model tiers
27
+ // ---------------------------------------------------------------------------
28
+
29
+ const TIERS = {
30
+ nano: { label: 'nano', costMultiplier: 0.1, maxContext: 32000 },
31
+ mini: { label: 'mini', costMultiplier: 0.4, maxContext: 200000 },
32
+ frontier: { label: 'frontier', costMultiplier: 1.0, maxContext: 1000000 },
33
+ };
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Task classification → tier mapping
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /**
40
+ * Classify a task and route it to the appropriate model tier.
41
+ *
42
+ * @param {object} task
43
+ * @param {string} task.type — task type identifier
44
+ * @param {number} [task.contextTokens] — estimated context window usage
45
+ * @param {string} [task.riskLevel] — 'low' | 'medium' | 'high'
46
+ * @param {number} [task.retryCount] — how many times this task has been retried
47
+ * @param {string[]} [task.tags] — freeform tags for classification
48
+ * @returns {{ tier: string, reason: string, escalated: boolean }}
49
+ */
50
+ function classifyTask(task = {}) {
51
+ const { type, contextTokens = 0, riskLevel, retryCount = 0, tags = [] } = task;
52
+ const config = loadConfig();
53
+ const escalation = config.escalationRules;
54
+ const archTags = escalation.architectureTags || [];
55
+
56
+ // --- Escalation checks (override normal routing) ---
57
+
58
+ // 1. Context exceeds frontier threshold
59
+ if (contextTokens > escalation.contextThreshold) {
60
+ return {
61
+ tier: 'frontier',
62
+ reason: `context size ${contextTokens} exceeds threshold ${escalation.contextThreshold}`,
63
+ escalated: true,
64
+ };
65
+ }
66
+
67
+ // 2. High risk + retried enough
68
+ if (riskLevel === 'high' && retryCount >= escalation.failureRetryThreshold) {
69
+ return {
70
+ tier: 'frontier',
71
+ reason: `high risk with ${retryCount} retries (threshold: ${escalation.failureRetryThreshold})`,
72
+ escalated: true,
73
+ };
74
+ }
75
+
76
+ // 3. Architecture / cross-file tags
77
+ const matchedTag = tags.find((t) => archTags.includes(t));
78
+ if (matchedTag) {
79
+ return {
80
+ tier: 'frontier',
81
+ reason: `tag "${matchedTag}" matches architecture escalation`,
82
+ escalated: true,
83
+ };
84
+ }
85
+
86
+ // --- Normal tier routing by task type ---
87
+
88
+ const tiers = config.tiers;
89
+ for (const tierName of ['nano', 'mini', 'frontier']) {
90
+ if (tiers[tierName].taskTypes.includes(type)) {
91
+ return {
92
+ tier: tierName,
93
+ reason: `task type "${type}" mapped to ${tierName}`,
94
+ escalated: false,
95
+ };
96
+ }
97
+ }
98
+
99
+ // Unknown type defaults to mini
100
+ return {
101
+ tier: 'mini',
102
+ reason: `unknown task type "${type}" — defaulting to mini`,
103
+ escalated: false,
104
+ };
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Escalation logic
109
+ // ---------------------------------------------------------------------------
110
+
111
+ /**
112
+ * Determine whether a task should be escalated from its current tier.
113
+ *
114
+ * @param {object} task — same shape as classifyTask input
115
+ * @param {object[]} history — array of { tier, success } from previous attempts
116
+ * @returns {{ escalate: boolean, from: string, to: string, reason: string }}
117
+ */
118
+ function shouldEscalate(task = {}, history = []) {
119
+ const { contextTokens = 0, riskLevel, retryCount = 0, tags = [] } = task;
120
+ const config = loadConfig();
121
+ const rules = config.escalationRules;
122
+ const archTags = rules.architectureTags || [];
123
+
124
+ const currentTier = classifyTask(task).tier;
125
+
126
+ // 1. Context exceeds threshold
127
+ if (contextTokens > rules.contextThreshold && currentTier !== 'frontier') {
128
+ return {
129
+ escalate: true,
130
+ from: currentTier,
131
+ to: 'frontier',
132
+ reason: `context ${contextTokens} > threshold ${rules.contextThreshold}`,
133
+ };
134
+ }
135
+
136
+ // 2. High risk + retries
137
+ if (riskLevel === 'high' && retryCount >= rules.failureRetryThreshold && currentTier !== 'frontier') {
138
+ return {
139
+ escalate: true,
140
+ from: currentTier,
141
+ to: 'frontier',
142
+ reason: `high risk with ${retryCount} retries`,
143
+ };
144
+ }
145
+
146
+ // 3. Architecture tags
147
+ const matchedTag = tags.find((t) => archTags.includes(t));
148
+ if (matchedTag && currentTier !== 'frontier') {
149
+ return {
150
+ escalate: true,
151
+ from: currentTier,
152
+ to: 'frontier',
153
+ reason: `architecture tag "${matchedTag}"`,
154
+ };
155
+ }
156
+
157
+ // 4. Two consecutive failures at mini tier
158
+ if (history.length >= 2) {
159
+ const lastTwo = history.slice(-2);
160
+ if (lastTwo.every((h) => h.tier === 'mini' && !h.success)) {
161
+ return {
162
+ escalate: true,
163
+ from: 'mini',
164
+ to: 'frontier',
165
+ reason: 'two consecutive failures at mini tier',
166
+ };
167
+ }
168
+ }
169
+
170
+ return {
171
+ escalate: false,
172
+ from: currentTier,
173
+ to: currentTier,
174
+ reason: 'no escalation needed',
175
+ };
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Frontier budget tracker
180
+ // ---------------------------------------------------------------------------
181
+
182
+ class FrontierBudget {
183
+ /**
184
+ * @param {object} [options]
185
+ * @param {number} [options.tokenCap] — max frontier tokens per session (default 500000)
186
+ * @param {boolean} [options.requireReason] — require a reason string for spend (default true)
187
+ */
188
+ constructor(options = {}) {
189
+ const config = loadConfig();
190
+ const defaults = config.tiers.frontier.budgetDefaults || {};
191
+ this.tokenCap = options.tokenCap ?? defaults.tokenCap ?? 500000;
192
+ this.requireReason = options.requireReason ?? defaults.requireReason ?? true;
193
+ this.spent = 0;
194
+ this.invocations = [];
195
+ }
196
+
197
+ /**
198
+ * Check whether a spend is allowed without deducting.
199
+ * @param {number} tokens
200
+ * @param {string} [reason]
201
+ * @returns {{ allowed: boolean, remaining: number, reason: string }}
202
+ */
203
+ canSpend(tokens, reason) {
204
+ if (this.requireReason && !reason) {
205
+ return {
206
+ allowed: false,
207
+ remaining: this.tokenCap - this.spent,
208
+ reason: 'reason is required for frontier spend',
209
+ };
210
+ }
211
+ const remaining = this.tokenCap - this.spent;
212
+ if (tokens > remaining) {
213
+ return {
214
+ allowed: false,
215
+ remaining,
216
+ reason: `requested ${tokens} exceeds remaining ${remaining}`,
217
+ };
218
+ }
219
+ return {
220
+ allowed: true,
221
+ remaining,
222
+ reason: 'within budget',
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Deduct tokens from the budget.
228
+ * @param {number} tokens
229
+ * @param {string} [reason]
230
+ * @returns {{ success: boolean, spent: number, remaining: number, reason: string }}
231
+ */
232
+ spend(tokens, reason) {
233
+ const check = this.canSpend(tokens, reason);
234
+ if (!check.allowed) {
235
+ return { success: false, spent: this.spent, remaining: check.remaining, reason: check.reason };
236
+ }
237
+ this.spent += tokens;
238
+ this.invocations.push({ tokens, reason, timestamp: new Date().toISOString() });
239
+ return {
240
+ success: true,
241
+ spent: this.spent,
242
+ remaining: this.tokenCap - this.spent,
243
+ reason: `spent ${tokens} tokens — ${reason}`,
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Return current budget status.
249
+ * @returns {{ spent: number, remaining: number, cap: number, invocations: number }}
250
+ */
251
+ status() {
252
+ return {
253
+ spent: this.spent,
254
+ remaining: this.tokenCap - this.spent,
255
+ cap: this.tokenCap,
256
+ invocations: this.invocations.length,
257
+ };
258
+ }
259
+
260
+ /** Reset the budget for a new session. */
261
+ reset() {
262
+ this.spent = 0;
263
+ this.invocations = [];
264
+ }
265
+ }
266
+
267
+ function recommendExecutionPlan(task = {}, env = process.env) {
268
+ const classification = classifyTask(task);
269
+ const inference = recommendInferenceBackend(task, env);
270
+
271
+ return {
272
+ tier: classification.tier,
273
+ escalated: classification.escalated,
274
+ tierReason: classification.reason,
275
+ backendId: inference.backend.id,
276
+ providerMode: inference.backend.providerMode,
277
+ workloadClass: inference.workloadClass,
278
+ recommendationClass: inference.recommendationClass,
279
+ indexCacheEligible: inference.backend.indexCacheEligible,
280
+ indexCacheEnabled: inference.backend.indexCacheEnabled,
281
+ reason: `${classification.reason}; ${inference.reason}`,
282
+ };
283
+ }
284
+
285
+ // ---------------------------------------------------------------------------
286
+ // Exports
287
+ // ---------------------------------------------------------------------------
288
+
289
+ module.exports = { TIERS, classifyTask, shouldEscalate, FrontierBudget, recommendExecutionPlan };
290
+
291
+ // ---------------------------------------------------------------------------
292
+ // CLI
293
+ // ---------------------------------------------------------------------------
294
+
295
+ if (require.main === module) {
296
+ const taskType = process.argv[2] || 'code-edit';
297
+ const result = classifyTask({ type: taskType });
298
+ const execution = recommendExecutionPlan({ type: taskType });
299
+ const budget = new FrontierBudget();
300
+ console.log(JSON.stringify({ classification: result, execution, budget: budget.status() }, null, 2));
301
+ }
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * money-watcher.js
4
+ * Continuously polls the commercial summary for net-new paid orders or booked revenue.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { getOperationalBillingSummary } = require('./operational-summary');
10
+
11
+ function getCommercialRevenueSnapshot(summary) {
12
+ const revenue = summary.revenue || {};
13
+ return {
14
+ paidOrders: revenue.paidOrders || 0,
15
+ bookedRevenueCents: revenue.bookedRevenueCents || 0,
16
+ latestPaidAt: revenue.latestPaidAt || null,
17
+ latestPaidOrder: revenue.latestPaidOrder || null,
18
+ };
19
+ }
20
+
21
+ async function watchMoney(intervalMs = 10000) {
22
+ console.log('👀 Money Watcher activated. Polling billing summary for commercial changes...');
23
+ const initialState = await getOperationalBillingSummary();
24
+ let initialSnapshot = getCommercialRevenueSnapshot(initialState.summary);
25
+ let polling = false;
26
+
27
+ return setInterval(async () => {
28
+ if (polling) return;
29
+ polling = true;
30
+ try {
31
+ const { source, summary, fallbackReason } = await getOperationalBillingSummary();
32
+ const currentSnapshot = getCommercialRevenueSnapshot(summary);
33
+
34
+ const newPaidOrders = currentSnapshot.paidOrders - initialSnapshot.paidOrders;
35
+ const newBookedRevenue = currentSnapshot.bookedRevenueCents - initialSnapshot.bookedRevenueCents;
36
+
37
+ if (newPaidOrders > 0 || newBookedRevenue > 0) {
38
+ console.log('\n🚨🚨🚨 COMMERCIAL ALERT: NET-NEW PAID ACTIVITY DETECTED! 🚨🚨🚨');
39
+ console.log('Operational billing summary:');
40
+ console.log(JSON.stringify({
41
+ source,
42
+ fallbackReason,
43
+ newPaidOrders,
44
+ newBookedRevenueCents: newBookedRevenue,
45
+ latestPaidAt: currentSnapshot.latestPaidAt,
46
+ latestPaidOrder: currentSnapshot.latestPaidOrder,
47
+ bookedRevenueCents: currentSnapshot.bookedRevenueCents,
48
+ activeKeys: summary.keys.active,
49
+ totalUsage: summary.keys.totalUsage,
50
+ }, null, 2));
51
+
52
+ process.stdout.write('\x07');
53
+ initialSnapshot = currentSnapshot;
54
+ }
55
+ } finally {
56
+ polling = false;
57
+ }
58
+ }, intervalMs);
59
+ }
60
+
61
+ if (require.main === module) {
62
+ watchMoney().catch((err) => {
63
+ console.error(err && err.message ? err.message : err);
64
+ process.exit(1);
65
+ });
66
+ }
67
+
68
+ module.exports = {
69
+ getCommercialRevenueSnapshot,
70
+ watchMoney,
71
+ };
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Multi-Hop Recall — chains related lessons across hops for deeper context.
6
+ *
7
+ * Single-hop: query → top N lessons (current behavior)
8
+ * Multi-hop: query → hop-1 lessons → extract tags/domains/rootCauses →
9
+ * hop-2 lessons (related) → deduplicate → ranked chain
10
+ *
11
+ * Inspired by Chroma Context-1's multi-hop retrieval pattern.
12
+ * Pro-only feature — gated via requirePro('multi-hop-recall').
13
+ *
14
+ * @module multi-hop-recall
15
+ */
16
+
17
+ const { requirePro } = require('./pro-features');
18
+
19
+ /**
20
+ * Extract expansion terms from a set of lessons for the next hop.
21
+ * Pulls tags, domains, rootCauses, and key phrases from whatToChange.
22
+ */
23
+ function extractExpansionTerms(lessons) {
24
+ const terms = new Set();
25
+
26
+ for (const lesson of lessons) {
27
+ // Tags
28
+ const tags = Array.isArray(lesson.tags) ? lesson.tags : safeParseTags(lesson.tags);
29
+ for (const tag of tags) {
30
+ if (tag && tag.length > 2) terms.add(tag);
31
+ }
32
+
33
+ // Domain
34
+ if (lesson.domain && lesson.domain !== 'general') {
35
+ terms.add(lesson.domain);
36
+ }
37
+
38
+ // Root cause category
39
+ if (lesson.rootCause) {
40
+ terms.add(lesson.rootCause);
41
+ }
42
+
43
+ // Key phrases from whatToChange (3+ char words, skip stopwords)
44
+ if (lesson.whatToChange) {
45
+ const words = lesson.whatToChange
46
+ .toLowerCase()
47
+ .replace(/[^a-z0-9\s]/g, '')
48
+ .split(/\s+/)
49
+ .filter((w) => w.length > 3 && !STOPWORDS.has(w));
50
+ for (const word of words.slice(0, 5)) {
51
+ terms.add(word);
52
+ }
53
+ }
54
+ }
55
+
56
+ return Array.from(terms);
57
+ }
58
+
59
+ const STOPWORDS = new Set([
60
+ 'this', 'that', 'with', 'from', 'have', 'been', 'were', 'will',
61
+ 'should', 'would', 'could', 'about', 'their', 'there', 'which',
62
+ 'when', 'what', 'than', 'then', 'them', 'they', 'into', 'some',
63
+ 'also', 'more', 'very', 'just', 'does', 'done', 'make', 'made',
64
+ ]);
65
+
66
+ function safeParseTags(tags) {
67
+ if (Array.isArray(tags)) return tags;
68
+ if (typeof tags === 'string') {
69
+ try { return JSON.parse(tags); } catch { return []; }
70
+ }
71
+ return [];
72
+ }
73
+
74
+ /**
75
+ * Deduplicate lessons by ID, keeping first occurrence (higher-ranked).
76
+ */
77
+ function deduplicateById(lessons) {
78
+ const seen = new Set();
79
+ return lessons.filter((l) => {
80
+ const id = l.id;
81
+ if (seen.has(id)) return false;
82
+ seen.add(id);
83
+ return true;
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Score a lesson's relevance to a set of expansion terms.
89
+ * Higher = more relevant to the chain.
90
+ */
91
+ function scoreRelevance(lesson, expansionTerms) {
92
+ let score = 0;
93
+ const termSet = new Set(expansionTerms.map((t) => t.toLowerCase()));
94
+
95
+ // Tag overlap: 3 points per matching tag
96
+ const tags = Array.isArray(lesson.tags) ? lesson.tags : safeParseTags(lesson.tags);
97
+ for (const tag of tags) {
98
+ if (termSet.has(tag.toLowerCase())) score += 3;
99
+ }
100
+
101
+ // Domain match: 2 points
102
+ if (lesson.domain && termSet.has(lesson.domain.toLowerCase())) score += 2;
103
+
104
+ // Root cause match: 2 points
105
+ if (lesson.rootCause && termSet.has(lesson.rootCause.toLowerCase())) score += 2;
106
+
107
+ // Content overlap: 1 point per matching word in whatToChange
108
+ if (lesson.whatToChange) {
109
+ const words = lesson.whatToChange.toLowerCase().split(/\s+/);
110
+ for (const word of words) {
111
+ if (word.length > 3 && termSet.has(word)) score += 1;
112
+ }
113
+ }
114
+
115
+ return score;
116
+ }
117
+
118
+ /**
119
+ * Perform multi-hop recall over a lesson database.
120
+ *
121
+ * @param {Function} searchFn - Search function: (query, options) => lesson[]
122
+ * Must accept (query, { limit, signal, domain }) and return lesson rows.
123
+ * @param {string} query - Initial search query
124
+ * @param {object} [options]
125
+ * @param {number} [options.maxHops=2] - Maximum chain depth (1 = single-hop, 2+ = multi-hop)
126
+ * @param {number} [options.hopLimit=10] - Max results per hop
127
+ * @param {number} [options.totalLimit=15] - Max total results across all hops
128
+ * @param {string} [options.signal] - Filter by 'positive' or 'negative'
129
+ * @param {boolean} [options.skipProCheck=false] - Skip Pro license check (for testing)
130
+ * @param {Function} [options.requireProFn=requirePro] - Injectable Pro gate helper for testing
131
+ * @returns {{ results: object[], hops: object[], totalHops: number, expansionTerms: string[] }}
132
+ */
133
+ function multiHopRecall(searchFn, query, options = {}) {
134
+ const {
135
+ maxHops = 2,
136
+ hopLimit = 10,
137
+ totalLimit = 15,
138
+ signal,
139
+ skipProCheck = false,
140
+ requireProFn = requirePro,
141
+ } = options;
142
+
143
+ // Pro gate (unless testing)
144
+ if (!skipProCheck && !requireProFn('multi-hop-recall')) {
145
+ return { results: [], hops: [], totalHops: 0, expansionTerms: [], proRequired: true };
146
+ }
147
+
148
+ // Clamp hops to [1, 3] to prevent runaway chains
149
+ const hops = Math.max(1, Math.min(maxHops, 3));
150
+ const allResults = [];
151
+ const hopMetadata = [];
152
+ let expansionTerms = [];
153
+
154
+ // Hop 1: direct query search
155
+ const hop1Results = searchFn(query, { limit: hopLimit, signal });
156
+ allResults.push(...hop1Results);
157
+ hopMetadata.push({
158
+ hop: 1,
159
+ query,
160
+ resultsCount: hop1Results.length,
161
+ type: 'direct',
162
+ });
163
+
164
+ if (hops < 2 || hop1Results.length === 0) {
165
+ return {
166
+ results: deduplicateById(allResults).slice(0, totalLimit),
167
+ hops: hopMetadata,
168
+ totalHops: 1,
169
+ expansionTerms: [],
170
+ };
171
+ }
172
+
173
+ // Extract expansion terms from hop 1
174
+ expansionTerms = extractExpansionTerms(hop1Results);
175
+
176
+ if (expansionTerms.length === 0) {
177
+ return {
178
+ results: deduplicateById(allResults).slice(0, totalLimit),
179
+ hops: hopMetadata,
180
+ totalHops: 1,
181
+ expansionTerms: [],
182
+ };
183
+ }
184
+
185
+ // Hop 2+: search using expansion terms
186
+ for (let hop = 2; hop <= hops; hop++) {
187
+ // Build expansion query from top terms (limit to 5 to keep FTS manageable)
188
+ const queryTerms = expansionTerms.slice(0, 5).join(' OR ');
189
+ const hopResults = searchFn(queryTerms, { limit: hopLimit, signal });
190
+
191
+ // Score and sort by relevance to expansion terms
192
+ const scored = hopResults
193
+ .map((lesson) => ({ ...lesson, _hopScore: scoreRelevance(lesson, expansionTerms) }))
194
+ .filter((l) => l._hopScore > 0)
195
+ .sort((a, b) => b._hopScore - a._hopScore);
196
+
197
+ allResults.push(...scored);
198
+ hopMetadata.push({
199
+ hop,
200
+ query: queryTerms,
201
+ resultsCount: scored.length,
202
+ type: 'expansion',
203
+ termsUsed: expansionTerms.slice(0, 5),
204
+ });
205
+
206
+ // Extract new terms for next hop (if any)
207
+ if (hop < hops && scored.length > 0) {
208
+ const newTerms = extractExpansionTerms(scored);
209
+ // Only add truly new terms
210
+ const existingSet = new Set(expansionTerms);
211
+ const novel = newTerms.filter((t) => !existingSet.has(t));
212
+ expansionTerms = [...expansionTerms, ...novel];
213
+ }
214
+ }
215
+
216
+ // Deduplicate and cap at totalLimit
217
+ const deduplicated = deduplicateById(allResults).slice(0, totalLimit);
218
+
219
+ // Tag each result with its hop number
220
+ const hop1Ids = new Set(hop1Results.map((l) => l.id));
221
+ const tagged = deduplicated.map((l) => ({
222
+ ...l,
223
+ _hop: hop1Ids.has(l.id) ? 1 : 2,
224
+ }));
225
+
226
+ return {
227
+ results: tagged,
228
+ hops: hopMetadata,
229
+ totalHops: hopMetadata.length,
230
+ expansionTerms,
231
+ };
232
+ }
233
+
234
+ module.exports = {
235
+ multiHopRecall,
236
+ extractExpansionTerms,
237
+ scoreRelevance,
238
+ deduplicateById,
239
+ STOPWORDS,
240
+ };