thumbgate 1.14.1 → 1.16.0

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 (150) hide show
  1. package/.claude-plugin/marketplace.json +6 -6
  2. package/.claude-plugin/plugin.json +3 -3
  3. package/.well-known/llms.txt +5 -5
  4. package/.well-known/mcp/server-card.json +1 -1
  5. package/README.md +60 -35
  6. package/adapters/chatgpt/openapi.yaml +118 -2
  7. package/adapters/claude/.mcp.json +2 -2
  8. package/adapters/mcp/server-stdio.js +217 -84
  9. package/adapters/opencode/opencode.json +1 -1
  10. package/bench/prompt-eval-suite.json +5 -1
  11. package/bin/cli.js +211 -8
  12. package/config/enforcement.json +59 -7
  13. package/config/evals/agent-safety-eval.json +338 -22
  14. package/config/gates/default.json +33 -0
  15. package/config/gates/routine.json +43 -0
  16. package/config/github-about.json +3 -3
  17. package/config/mcp-allowlists.json +4 -0
  18. package/config/merge-quality-checks.json +2 -1
  19. package/config/model-candidates.json +131 -0
  20. package/openapi/openapi.yaml +118 -2
  21. package/package.json +70 -51
  22. package/public/blog.html +7 -7
  23. package/public/codex-plugin.html +13 -7
  24. package/public/compare.html +29 -23
  25. package/public/dashboard.html +105 -12
  26. package/public/guide.html +28 -28
  27. package/public/index.html +233 -97
  28. package/public/learn.html +87 -20
  29. package/public/lessons.html +26 -2
  30. package/public/numbers.html +271 -0
  31. package/public/pro.html +89 -19
  32. package/scripts/agent-audit-trace.js +55 -0
  33. package/scripts/agent-memory-lifecycle.js +96 -0
  34. package/scripts/agent-readiness-plan.js +118 -0
  35. package/scripts/agentic-data-pipeline.js +21 -1
  36. package/scripts/agents-sdk-sandbox-plan.js +57 -0
  37. package/scripts/ai-org-governance.js +98 -0
  38. package/scripts/ai-search-distribution.js +43 -0
  39. package/scripts/artifact-agent-plan.js +81 -0
  40. package/scripts/billing.js +27 -8
  41. package/scripts/cli-feedback.js +2 -1
  42. package/scripts/cli-schema.js +60 -5
  43. package/scripts/code-mode-mcp-plan.js +71 -0
  44. package/scripts/commercial-offer.js +1 -1
  45. package/scripts/context-engine.js +1 -2
  46. package/scripts/context-manager.js +4 -1
  47. package/scripts/contextfs.js +214 -32
  48. package/scripts/dashboard-render-spec.js +1 -1
  49. package/scripts/dashboard.js +275 -9
  50. package/scripts/decision-journal.js +13 -3
  51. package/scripts/document-workflow-governance.js +62 -0
  52. package/scripts/enterprise-agent-rollout.js +34 -0
  53. package/scripts/experience-replay-governance.js +69 -0
  54. package/scripts/export-hf-dataset.js +1 -1
  55. package/scripts/feedback-loop.js +141 -9
  56. package/scripts/feedback-to-rules.js +17 -23
  57. package/scripts/gates-engine.js +4 -6
  58. package/scripts/growth-campaigns.js +49 -0
  59. package/scripts/harness-selector.js +145 -1
  60. package/scripts/hybrid-supervisor-agent.js +64 -0
  61. package/scripts/inference-cache-policy.js +72 -0
  62. package/scripts/inference-economics.js +53 -0
  63. package/scripts/internal-agent-bootstrap.js +12 -2
  64. package/scripts/knowledge-layer-plan.js +108 -0
  65. package/scripts/lesson-canonical.js +181 -0
  66. package/scripts/lesson-db.js +71 -10
  67. package/scripts/lesson-inference.js +183 -44
  68. package/scripts/lesson-search.js +4 -1
  69. package/scripts/lesson-synthesis.js +23 -2
  70. package/scripts/llm-client.js +157 -26
  71. package/scripts/mailer/resend-mailer.js +112 -1
  72. package/scripts/mcp-transport-strategy.js +66 -0
  73. package/scripts/memory-store-governance.js +60 -0
  74. package/scripts/meta-agent-loop.js +7 -13
  75. package/scripts/model-access-eligibility.js +38 -0
  76. package/scripts/model-migration-readiness.js +55 -0
  77. package/scripts/native-messaging-audit.js +514 -0
  78. package/scripts/operational-integrity.js +96 -3
  79. package/scripts/otel-declarative-config.js +56 -0
  80. package/scripts/perplexity-client.js +1 -1
  81. package/scripts/post-training-governance.js +34 -0
  82. package/scripts/pr-manager.js +47 -7
  83. package/scripts/private-core-boundary.js +72 -0
  84. package/scripts/production-agent-readiness.js +40 -0
  85. package/scripts/profile-router.js +16 -1
  86. package/scripts/prompt-eval.js +564 -32
  87. package/scripts/prompt-programs.js +93 -0
  88. package/scripts/provider-action-normalizer.js +585 -0
  89. package/scripts/rule-validator.js +285 -0
  90. package/scripts/scaling-law-claims.js +60 -0
  91. package/scripts/security-scanner.js +1 -1
  92. package/scripts/self-distill-agent.js +7 -32
  93. package/scripts/seo-gsd.js +400 -43
  94. package/scripts/skill-rag-router.js +53 -0
  95. package/scripts/spec-gate.js +1 -1
  96. package/scripts/student-consistent-training.js +73 -0
  97. package/scripts/synthetic-data-provenance.js +98 -0
  98. package/scripts/task-context-result.js +81 -0
  99. package/scripts/telemetry-analytics.js +149 -0
  100. package/scripts/thompson-sampling.js +2 -2
  101. package/scripts/token-savings.js +7 -6
  102. package/scripts/token-tco.js +46 -0
  103. package/scripts/tool-registry.js +75 -3
  104. package/scripts/verification-loop.js +10 -1
  105. package/scripts/verifier-scoring.js +71 -0
  106. package/scripts/workflow-sentinel.js +284 -28
  107. package/scripts/workspace-agent-routines.js +118 -0
  108. package/skills/thumbgate/SKILL.md +1 -1
  109. package/src/api/server.js +434 -120
  110. package/.claude-plugin/README.md +0 -170
  111. package/adapters/README.md +0 -12
  112. package/scripts/analytics-report.js +0 -328
  113. package/scripts/autonomous-workflow.js +0 -377
  114. package/scripts/billing-setup.js +0 -109
  115. package/scripts/creator-campaigns.js +0 -239
  116. package/scripts/cross-encoder-reranker.js +0 -235
  117. package/scripts/daemon-manager.js +0 -108
  118. package/scripts/decision-trace.js +0 -354
  119. package/scripts/delegation-runtime.js +0 -896
  120. package/scripts/dispatch-brief.js +0 -159
  121. package/scripts/distribution-surfaces.js +0 -110
  122. package/scripts/feedback-history-distiller.js +0 -382
  123. package/scripts/funnel-analytics.js +0 -35
  124. package/scripts/history-distiller.js +0 -200
  125. package/scripts/hosted-job-launcher.js +0 -256
  126. package/scripts/intent-router.js +0 -392
  127. package/scripts/lesson-reranker.js +0 -263
  128. package/scripts/lesson-retrieval.js +0 -148
  129. package/scripts/managed-lesson-agent.js +0 -183
  130. package/scripts/operational-dashboard.js +0 -103
  131. package/scripts/operational-summary.js +0 -129
  132. package/scripts/operator-artifacts.js +0 -608
  133. package/scripts/optimize-context.js +0 -17
  134. package/scripts/org-dashboard.js +0 -206
  135. package/scripts/partner-orchestration.js +0 -146
  136. package/scripts/predictive-insights.js +0 -356
  137. package/scripts/pulse.js +0 -80
  138. package/scripts/reflector-agent.js +0 -221
  139. package/scripts/sales-pipeline.js +0 -681
  140. package/scripts/session-episode-store.js +0 -329
  141. package/scripts/session-health-sensor.js +0 -242
  142. package/scripts/session-report.js +0 -120
  143. package/scripts/swarm-coordinator.js +0 -81
  144. package/scripts/tool-kpi-tracker.js +0 -12
  145. package/scripts/webhook-delivery.js +0 -62
  146. package/scripts/workflow-sprint-intake.js +0 -475
  147. package/skills/agent-memory/SKILL.md +0 -97
  148. package/skills/solve-architecture-autonomy/SKILL.md +0 -17
  149. package/skills/solve-architecture-autonomy/tool.js +0 -33
  150. package/skills/thumbgate-feedback/SKILL.md +0 -49
@@ -9,20 +9,23 @@
9
9
  * Detection priority (first match wins):
10
10
  * 1. THUMBGATE_HARNESS env var — explicit override
11
11
  * 2. Tool-name heuristic (Edit/Write/MultiEdit → code-edit)
12
- * 3. Command-text heuristic (deploy keywords → deploy, SQL keywords → db-write)
12
+ * 3. Command-text heuristic (deploy keywords → deploy, SQL keywords → db-write, routines → routine)
13
13
  * 4. null → load only default.json + auto-promoted gates
14
14
  *
15
15
  * Each harness is ADDITIVE — default.json gates always load first.
16
16
  */
17
17
 
18
18
  const path = require('path');
19
+ const fs = require('fs');
19
20
 
20
21
  const HARNESS_DIR = path.join(__dirname, '..', 'config', 'gates');
22
+ const ROOT_DIR = path.join(__dirname, '..');
21
23
 
22
24
  const HARNESSES = Object.freeze({
23
25
  deploy: path.join(HARNESS_DIR, 'deploy.json'),
24
26
  'code-edit': path.join(HARNESS_DIR, 'code-edit.json'),
25
27
  'db-write': path.join(HARNESS_DIR, 'db-write.json'),
28
+ routine: path.join(HARNESS_DIR, 'routine.json'),
26
29
  });
27
30
 
28
31
  // ---------------------------------------------------------------------------
@@ -48,6 +51,14 @@ const DB_WRITE_PATTERNS = [
48
51
  /\.db\.exec\(|\.db\.prepare\(/i,
49
52
  ];
50
53
 
54
+ const ROUTINE_PATTERNS = [
55
+ /\b(routine|scheduled agent|workspace agent|webhook trigger|post[-\s]?merge|nightly|daily audit)\b/i,
56
+ /\b(reasoning_effort|system prompt|developer message|verbosity|length limits)\b/i,
57
+ /\b(gpt-5\.5|gpt-5\.5-pro|xhigh|ultrathink)\b/i,
58
+ /\b(slack|salesforce|gmail|google drive|notion|jira|linear|atlassian)\b.*\b(send|post|write|update|delete|create)\b/i,
59
+ /\b(context|role|expectations|few[-\s]?shot|zero[-\s]?shot|prompt template|prompt library)\b/i,
60
+ ];
61
+
51
62
  const CODE_EDIT_TOOL_NAMES = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
52
63
 
53
64
  // ---------------------------------------------------------------------------
@@ -82,6 +93,9 @@ function selectHarness(toolName, toolInput) {
82
93
  if (DB_WRITE_PATTERNS.some((p) => p.test(commandText))) {
83
94
  return HARNESSES['db-write'];
84
95
  }
96
+ if (ROUTINE_PATTERNS.some((p) => p.test(commandText))) {
97
+ return HARNESSES.routine;
98
+ }
85
99
  if (DEPLOY_PATTERNS.some((p) => p.test(commandText))) {
86
100
  return HARNESSES['deploy'];
87
101
  }
@@ -113,6 +127,132 @@ function getHarnessPath(name) {
113
127
  return HARNESSES[name] ?? null;
114
128
  }
115
129
 
130
+ function estimateTokenCount(text, charsPerToken = 4) {
131
+ const payload = String(text || '');
132
+ const divisor = Math.max(1, Number(charsPerToken) || 4);
133
+ return Math.ceil(Buffer.byteLength(payload, 'utf8') / divisor);
134
+ }
135
+
136
+ function readIfExists(filePath) {
137
+ try {
138
+ return fs.readFileSync(filePath, 'utf8');
139
+ } catch {
140
+ return '';
141
+ }
142
+ }
143
+
144
+ function readJsonIfExists(filePath) {
145
+ try {
146
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+
152
+ function collectDefaultHarnessAuditInputs(rootDir = ROOT_DIR) {
153
+ const globalDocNames = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
154
+ const globalDocs = globalDocNames.map((name) => {
155
+ const content = readIfExists(path.join(rootDir, name));
156
+ return {
157
+ name,
158
+ chars: Buffer.byteLength(content, 'utf8'),
159
+ estimatedTokens: estimateTokenCount(content),
160
+ exists: content.length > 0,
161
+ };
162
+ });
163
+ const toolIndex = readJsonIfExists(path.join(rootDir, '.well-known', 'mcp', 'tools.json'));
164
+ const tools = Array.isArray(toolIndex && toolIndex.tools) ? toolIndex.tools : [];
165
+
166
+ return {
167
+ globalDocs,
168
+ mcpToolCount: tools.length,
169
+ progressiveToolIndexPresent: tools.some((tool) => typeof tool.schemaUrl === 'string'),
170
+ specializedHarnesses: listHarnesses(),
171
+ };
172
+ }
173
+
174
+ function scoreHarnessAudit(inputs = {}, options = {}) {
175
+ const globalDocs = Array.isArray(inputs.globalDocs) ? inputs.globalDocs : [];
176
+ const totalDocTokens = globalDocs.reduce((sum, doc) => sum + Number(doc.estimatedTokens || 0), 0);
177
+ const totalDocChars = globalDocs.reduce((sum, doc) => sum + Number(doc.chars || 0), 0);
178
+ const docTokenBudget = Number(options.docTokenBudget || 9000);
179
+ const docsOverBudget = totalDocTokens > docTokenBudget;
180
+ const mcpToolCount = Number(inputs.mcpToolCount || 0);
181
+ const progressiveToolIndexPresent = Boolean(inputs.progressiveToolIndexPresent);
182
+ const specializedHarnesses = Array.isArray(inputs.specializedHarnesses) ? inputs.specializedHarnesses : [];
183
+ const hasSpecializedHarnesses = specializedHarnesses.length >= 4;
184
+ const missingDocs = globalDocs.filter((doc) => doc.exists === false).map((doc) => doc.name);
185
+ const observations = [];
186
+ const recommendations = [];
187
+
188
+ let score = 100;
189
+ if (docsOverBudget) {
190
+ const overageRatio = totalDocTokens / docTokenBudget;
191
+ score -= Math.min(35, Math.ceil((overageRatio - 1) * 22));
192
+ observations.push(`Global agent docs use about ${totalDocTokens} tokens against a ${docTokenBudget} token harness budget.`);
193
+ recommendations.push('Move verbose runbooks into skills, guides, or tool help, then leave AGENTS.md/CLAUDE.md as short discovery pointers.');
194
+ } else {
195
+ observations.push(`Global agent docs stay within the ${docTokenBudget} token harness budget.`);
196
+ }
197
+
198
+ if (!progressiveToolIndexPresent && mcpToolCount > 12) {
199
+ score -= 25;
200
+ observations.push(`${mcpToolCount} MCP tools appear preload-only, which can push agents toward instruction bloat.`);
201
+ recommendations.push('Expose a lightweight MCP tool index with per-tool schema URLs so agents fetch schemas only when needed.');
202
+ } else if (progressiveToolIndexPresent) {
203
+ observations.push('Progressive MCP tool discovery is available through schema URLs.');
204
+ }
205
+
206
+ if (!hasSpecializedHarnesses) {
207
+ score -= 18;
208
+ observations.push('Fewer than four specialized gate harnesses are available for risky workflows.');
209
+ recommendations.push('Add workflow-specific harnesses for deploy, code-edit, database-write, and unattended routine actions so default gates stay lean.');
210
+ } else {
211
+ observations.push(`Specialized harnesses are available: ${specializedHarnesses.join(', ')}.`);
212
+ }
213
+
214
+ if (missingDocs.length > 0) {
215
+ score -= Math.min(12, missingDocs.length * 4);
216
+ recommendations.push(`Restore missing global discovery docs or remove stale references: ${missingDocs.join(', ')}.`);
217
+ }
218
+
219
+ if (recommendations.length === 0) {
220
+ recommendations.push('Keep using Research -> Plan -> Implement prompts and delegate only subtasks whose summaries are enough for the main context.');
221
+ } else {
222
+ recommendations.push('Use Research -> Plan -> Implement prompts so implementation starts after the harness has isolated only the needed context.');
223
+ }
224
+
225
+ const normalizedScore = Math.max(0, Math.min(100, score));
226
+ const status = normalizedScore >= 85 ? 'compounding' : normalizedScore >= 65 ? 'watch' : 'bloated';
227
+
228
+ return {
229
+ name: 'thumbgate-harness-optimization-audit',
230
+ status,
231
+ score: normalizedScore,
232
+ roiPriority: normalizedScore < 85 ? 'conversion' : 'retention',
233
+ totals: {
234
+ globalDocChars: totalDocChars,
235
+ globalDocEstimatedTokens: totalDocTokens,
236
+ mcpToolCount,
237
+ specializedHarnessCount: specializedHarnesses.length,
238
+ },
239
+ signals: {
240
+ docsOverBudget,
241
+ progressiveToolIndexPresent,
242
+ hasSpecializedHarnesses,
243
+ missingDocs,
244
+ },
245
+ observations,
246
+ recommendations,
247
+ };
248
+ }
249
+
250
+ function buildHarnessOptimizationAudit(options = {}) {
251
+ const rootDir = options.rootDir || ROOT_DIR;
252
+ const inputs = options.inputs || collectDefaultHarnessAuditInputs(rootDir);
253
+ return scoreHarnessAudit(inputs, options);
254
+ }
255
+
116
256
  // ---------------------------------------------------------------------------
117
257
  // Internal helpers
118
258
  // ---------------------------------------------------------------------------
@@ -140,6 +280,10 @@ module.exports = {
140
280
  selectHarnessName,
141
281
  listHarnesses,
142
282
  getHarnessPath,
283
+ estimateTokenCount,
284
+ collectDefaultHarnessAuditInputs,
285
+ scoreHarnessAudit,
286
+ buildHarnessOptimizationAudit,
143
287
  extractCommandText,
144
288
  HARNESSES,
145
289
  DEPLOY_PATTERNS,
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ function buildHybridSupervisorPlan(options = {}) {
4
+ const sources = options.sources || [
5
+ { id: 'feedback_log', type: 'jsonl', description: 'User thumbs-up/down and correction events.' },
6
+ { id: 'gate_metrics', type: 'sql', description: 'Gate blocks, warnings, pass rates, and timestamps.' },
7
+ { id: 'docs', type: 'vector', description: 'Public docs and operational guides.' },
8
+ ];
9
+
10
+ return {
11
+ pattern: 'multi_step_hybrid_supervisor',
12
+ sources,
13
+ sourceCount: sources.length,
14
+ steps: [
15
+ 'classify query into structured, unstructured, graph, or mixed',
16
+ 'decompose mixed queries into native-source subqueries',
17
+ 'run complementary SQL, graph, and vector searches in parallel',
18
+ 'join or reconcile result sets',
19
+ 'self-correct with a different query path when overlap is empty',
20
+ 'verify final answer against source-specific evidence',
21
+ ],
22
+ gates: [
23
+ 'prefer native source queries over flattening everything into embeddings',
24
+ 'limit initial deployments to 5-10 curated complementary sources',
25
+ 'require plain-language source descriptions at ingestion',
26
+ 'block final answers when structured and unstructured evidence conflict',
27
+ ],
28
+ };
29
+ }
30
+
31
+ function classifyHybridQuery(query = '') {
32
+ const text = String(query).toLowerCase();
33
+ const needsStructured = /\b(count|sum|trend|declin|increase|revenue|sales|rate|over time|sql|table)\b/.test(text);
34
+ const needsUnstructured = /\b(reviews?|feedback|reason|complaints?|docs?|semantic|why|quote|citation)\b/.test(text);
35
+ const needsGraph = /\b(similar|related|path|relationship|because|profile|like you)\b/.test(text);
36
+ if ([needsStructured, needsUnstructured, needsGraph].filter(Boolean).length >= 2) return 'hybrid';
37
+ if (needsStructured) return 'structured';
38
+ if (needsGraph) return 'graph';
39
+ if (needsUnstructured) return 'unstructured';
40
+ return 'general';
41
+ }
42
+
43
+ function evaluateHybridSupervisorRun(run = {}) {
44
+ const issues = [];
45
+ const queryType = classifyHybridQuery(run.query || '');
46
+ if (queryType === 'hybrid' && !run.decomposed) issues.push('hybrid_query_not_decomposed');
47
+ if (queryType === 'hybrid' && !run.parallelNativeQueries) issues.push('parallel_native_queries_required');
48
+ if (!run.sourceDescriptionsPresent) issues.push('missing_source_descriptions');
49
+ if ((run.sourceCount || 0) > 10 && !run.incrementalRollout) issues.push('too_many_sources_without_incremental_rollout');
50
+ if (run.emptyOverlap && !run.selfCorrected) issues.push('self_correction_required');
51
+ if (run.evidenceConflict && !run.escalated) issues.push('conflicting_evidence_requires_escalation');
52
+
53
+ return {
54
+ decision: issues.length ? 'warn' : 'allow',
55
+ issues,
56
+ queryType,
57
+ };
58
+ }
59
+
60
+ module.exports = {
61
+ buildHybridSupervisorPlan,
62
+ classifyHybridQuery,
63
+ evaluateHybridSupervisorRun,
64
+ };
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ function buildInferenceCachePolicy(options = {}) {
4
+ return {
5
+ policyId: 'llm_inference_cache',
6
+ layers: [
7
+ {
8
+ id: 'kv_cache',
9
+ owner: 'model_runtime',
10
+ enabled: true,
11
+ action: 'no app changes; rely on inference runtime',
12
+ },
13
+ {
14
+ id: 'prefix_cache',
15
+ owner: 'agent_harness',
16
+ enabled: options.prefixCache !== false,
17
+ action: 'place stable system prompt, docs, and examples before dynamic fields',
18
+ },
19
+ {
20
+ id: 'semantic_cache',
21
+ owner: 'application',
22
+ enabled: Boolean(options.semanticCache),
23
+ action: 'cache complete input/output pairs when paraphrased repeat volume is high',
24
+ },
25
+ ],
26
+ promptRules: [
27
+ 'static content first',
28
+ 'dynamic user/session/date fields last',
29
+ 'deterministic JSON key order',
30
+ 'no generated timestamps inside cached prefix',
31
+ 'version cache keys with prompt and policy versions',
32
+ ],
33
+ invalidation: {
34
+ prefix: ['system_prompt_version', 'doc_version', 'tool_policy_version'],
35
+ semantic: ['answer_ttl', 'source_doc_version', 'safety_policy_version'],
36
+ },
37
+ };
38
+ }
39
+
40
+ function evaluateCacheCandidate(candidate = {}) {
41
+ const issues = [];
42
+ const repeatedPrefixTokens = Number(candidate.repeatedPrefixTokens || 0);
43
+ const requestsPerDay = Number(candidate.requestsPerDay || 0);
44
+ const semanticRepeatRate = Number(candidate.semanticRepeatRate || 0);
45
+
46
+ if (candidate.dynamicFieldsBeforeStatic) issues.push('dynamic_fields_break_prefix_cache');
47
+ if (!candidate.deterministicSerialization) issues.push('deterministic_serialization_required');
48
+ if (repeatedPrefixTokens >= 1024 && requestsPerDay >= 10 && !candidate.prefixCacheEnabled) {
49
+ issues.push('prefix_cache_high_roi_not_enabled');
50
+ }
51
+ if (candidate.semanticCacheEnabled && semanticRepeatRate < 0.15) {
52
+ issues.push('semantic_cache_overhead_not_justified');
53
+ }
54
+ if (candidate.semanticCacheEnabled && !candidate.ttl) {
55
+ issues.push('semantic_cache_ttl_required');
56
+ }
57
+
58
+ return {
59
+ decision: issues.length ? 'warn' : 'allow',
60
+ issues,
61
+ recommendedLayers: [
62
+ 'kv_cache',
63
+ repeatedPrefixTokens >= 1024 && requestsPerDay >= 10 ? 'prefix_cache' : null,
64
+ semanticRepeatRate >= 0.15 ? 'semantic_cache' : null,
65
+ ].filter(Boolean),
66
+ };
67
+ }
68
+
69
+ module.exports = {
70
+ buildInferenceCachePolicy,
71
+ evaluateCacheCandidate,
72
+ };
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ function estimateDifficulty(input = {}) {
5
+ let score = 0;
6
+ const text = String(input.task || input.prompt || '');
7
+ if (text.length > 1200) score += 20;
8
+ if (/ambiguous|research|architecture|security|production|migration|legal|financial/i.test(text)) score += 25;
9
+ if (Number(input.dollarImpact || 0) >= 1000) score += 25;
10
+ if (Array.isArray(input.files) && input.files.length > 5) score += 15;
11
+ if (input.requiresHumanApproval === true) score += 15;
12
+ return Math.max(0, Math.min(100, score));
13
+ }
14
+
15
+ function planInferenceBudget(input = {}) {
16
+ const difficulty = Number.isFinite(input.difficulty) ? input.difficulty : estimateDifficulty(input);
17
+ const maxCostCents = Number.isFinite(Number(input.maxCostCents)) ? Number(input.maxCostCents) : 50;
18
+ let depth = 'shallow';
19
+ let reasoningEffort = 'low';
20
+ let expertCount = 1;
21
+ let humanHandoff = false;
22
+
23
+ if (difficulty >= 70) {
24
+ depth = 'deep';
25
+ reasoningEffort = 'high';
26
+ expertCount = 4;
27
+ humanHandoff = true;
28
+ } else if (difficulty >= 35) {
29
+ depth = 'standard';
30
+ reasoningEffort = 'medium';
31
+ expertCount = 2;
32
+ }
33
+
34
+ if (maxCostCents < 20 && depth === 'deep') {
35
+ depth = 'standard';
36
+ reasoningEffort = 'medium';
37
+ }
38
+
39
+ return {
40
+ difficulty,
41
+ maxCostCents,
42
+ depth,
43
+ reasoningEffort,
44
+ activeExperts: expertCount,
45
+ humanHandoff,
46
+ telemetry: ['difficulty', 'depth', 'reasoningEffort', 'activeExperts', 'latencyMs', 'costCents', 'outcome'],
47
+ };
48
+ }
49
+
50
+ module.exports = {
51
+ estimateDifficulty,
52
+ planInferenceBudget,
53
+ };
@@ -12,13 +12,18 @@ const {
12
12
  constructContextPack,
13
13
  recordProvenance,
14
14
  } = require('./contextfs');
15
- const { planIntent } = require('./intent-router');
16
15
  const { formatCodeGraphRecallSection } = require('./codegraph-context');
17
16
 
18
17
  const KNOWN_SOURCES = new Set(['github', 'slack', 'linear', 'api', 'cli']);
19
18
  const DEFAULT_SOURCE = 'api';
20
19
  const DEFAULT_SANDBOX_ROOT = path.join(os.tmpdir(), 'thumbgate-internal-agent-sandboxes');
21
20
 
21
+ function loadIntentRouterModule() {
22
+ const modulePath = path.resolve(__dirname, 'intent-router.js');
23
+ if (!fs.existsSync(modulePath)) return null;
24
+ return require(modulePath);
25
+ }
26
+
22
27
  function normalizeText(value) {
23
28
  if (value === undefined || value === null) return '';
24
29
  return String(value).trim();
@@ -422,7 +427,12 @@ function bootstrapInternalAgent(options = {}) {
422
427
  baseRef: null,
423
428
  };
424
429
 
425
- const plan = planIntent({
430
+ const intentRouter = loadIntentRouterModule();
431
+ if (!intentRouter) {
432
+ throw new Error('Internal agent bootstrap requires the ThumbGate private intent router runtime.');
433
+ }
434
+
435
+ const plan = intentRouter.planIntent({
426
436
  intentId: invocation.intentId,
427
437
  context: startupContext.text,
428
438
  mcpProfile: invocation.mcpProfile,
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ function buildKnowledgeLayerPlan(options = {}) {
4
+ const domain = options.domain || 'agent_reliability';
5
+ const graph = options.graph || 'neo4j';
6
+
7
+ return {
8
+ domain,
9
+ graph,
10
+ memoryTiers: [
11
+ {
12
+ id: 'short_term',
13
+ purpose: 'Current session context so the agent does not re-ask answered questions.',
14
+ ttl: 'session',
15
+ },
16
+ {
17
+ id: 'long_term',
18
+ purpose: 'Durable user, product, workflow, and feedback profile facts.',
19
+ ttl: 'durable',
20
+ },
21
+ {
22
+ id: 'reasoning_memory',
23
+ purpose: 'Reusable decision paths that avoid recomputing expensive traversals.',
24
+ ttl: 'versioned',
25
+ },
26
+ ],
27
+ nodeTypes: [
28
+ 'User',
29
+ 'Agent',
30
+ 'Workflow',
31
+ 'Feedback',
32
+ 'Gate',
33
+ 'Decision',
34
+ 'Evidence',
35
+ 'Recommendation',
36
+ 'Outcome',
37
+ ],
38
+ relationshipTypes: [
39
+ 'GAVE_FEEDBACK',
40
+ 'TRIGGERED_GATE',
41
+ 'USED_EVIDENCE',
42
+ 'RECOMMENDED_ACTION',
43
+ 'PRODUCED_OUTCOME',
44
+ 'SIMILAR_TO',
45
+ 'REUSES_REASONING',
46
+ ],
47
+ highRoiUseCases: [
48
+ 'conversion recommendations with explainable evidence paths',
49
+ 'compute savings from reasoning-memory cache hits',
50
+ 'compliance audit trail for why an agent recommended or blocked an action',
51
+ 'closed-loop profile updates from every feedback, purchase, or outcome event',
52
+ ],
53
+ gates: [
54
+ 'do not recommend without an evidence path',
55
+ 'do not reuse reasoning memory when source facts changed',
56
+ 'write audit node for every recommendation and blocked action',
57
+ 'record outcome feedback to update profile and graph edges',
58
+ ],
59
+ };
60
+ }
61
+
62
+ function buildRecommendationEvidencePath(input = {}) {
63
+ const userId = input.userId || 'unknown_user';
64
+ const recommendationId = input.recommendationId || 'rec_pending';
65
+ const evidence = Array.isArray(input.evidence) ? input.evidence : [];
66
+ const similarProfiles = Array.isArray(input.similarProfiles) ? input.similarProfiles : [];
67
+
68
+ return {
69
+ recommendationId,
70
+ path: [
71
+ { type: 'User', id: userId },
72
+ ...similarProfiles.map((id) => ({ type: 'SimilarProfile', id })),
73
+ ...evidence.map((item, index) => ({
74
+ type: item.type || 'Evidence',
75
+ id: item.id || `evidence_${index + 1}`,
76
+ quote: item.quote || null,
77
+ })),
78
+ { type: 'Recommendation', id: recommendationId },
79
+ ],
80
+ explainable: evidence.length > 0,
81
+ };
82
+ }
83
+
84
+ function evaluateKnowledgeLayerRun(run = {}) {
85
+ const issues = [];
86
+ if (!run.userId) issues.push('missing_user_id');
87
+ if (!run.recommendationId) issues.push('missing_recommendation_id');
88
+ if (!run.evidencePath?.explainable) issues.push('missing_explainable_evidence_path');
89
+ if (!run.auditNodeId) issues.push('missing_audit_node_id');
90
+ if (run.reusedReasoning && !run.reasoningVersion) issues.push('missing_reasoning_version');
91
+ if (run.profileUpdate && !run.outcomeEventId) issues.push('missing_outcome_event_id');
92
+
93
+ return {
94
+ decision: issues.length ? 'warn' : 'allow',
95
+ issues,
96
+ roiSignals: [
97
+ run.reusedReasoning ? 'lower_graph_query_and_token_cost' : null,
98
+ run.profileUpdate ? 'closed_loop_personalization' : null,
99
+ run.auditNodeId ? 'compliance_trace_available' : null,
100
+ ].filter(Boolean),
101
+ };
102
+ }
103
+
104
+ module.exports = {
105
+ buildKnowledgeLayerPlan,
106
+ buildRecommendationEvidencePath,
107
+ evaluateKnowledgeLayerRun,
108
+ };