thumbgate 1.34.3 → 1.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
  2. package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
  3. package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
  4. package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
  5. package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
  6. package/.claude-plugin/plugin.json +1 -1
  7. package/.well-known/llms.txt +1 -0
  8. package/.well-known/mcp/server-card.json +1 -1
  9. package/CONTRIBUTING.md +95 -0
  10. package/README.md +195 -632
  11. package/THIRD_PARTY_NOTICES.md +89 -0
  12. package/adapters/claude/.mcp.json +2 -2
  13. package/adapters/forge/forge.yaml +3 -3
  14. package/adapters/future-agi/.mcp.json +8 -0
  15. package/adapters/future-agi/FUTURE_AGI.md +23 -0
  16. package/adapters/future-agi/config.toml +3 -0
  17. package/adapters/future-agi/future-agi-bridge.js +9 -0
  18. package/adapters/future-agi/opencode.json +8 -0
  19. package/adapters/herdr/herdr-plugin.toml +18 -0
  20. package/adapters/mcp/server-stdio.js +238 -25
  21. package/adapters/opencode/opencode.json +1 -1
  22. package/adapters/workos/WORKOS.md +52 -0
  23. package/bin/cli.js +373 -5
  24. package/bin/futureagi-bridge +9 -0
  25. package/config/gate-templates.json +653 -4
  26. package/config/gates/actor-critic-audit.json +34 -0
  27. package/config/gates/default.json +21 -2
  28. package/config/gates/five-walls-governance.json +34 -0
  29. package/config/gates/future-agi-guardrails.json +34 -0
  30. package/config/gates/radware-threat-defense-2026.json +61 -0
  31. package/config/gates/simatree-data-governance.json +33 -0
  32. package/config/mcp-allowlists.json +4 -0
  33. package/config/merge-quality-checks.json +10 -1
  34. package/config/model-candidates.json +382 -24
  35. package/config/model-tiers.json +18 -0
  36. package/config/post-deploy-marketing-pages.json +10 -0
  37. package/config/progressive/01-wire-only.json +11 -0
  38. package/config/progressive/02-dashboard-empty-ok.json +10 -0
  39. package/config/progressive/03-one-lesson.json +10 -0
  40. package/config/progressive/04-warn-fires.json +11 -0
  41. package/config/progressive/05-strict-optional.json +11 -0
  42. package/config/progressive/README.md +15 -0
  43. package/config/schemas/broker-execution-receipt.schema.json +139 -0
  44. package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
  45. package/conformance/provider-attestation/vectors.json +320 -0
  46. package/docs/specs/provider-execution-attestation-v1.md +69 -0
  47. package/openapi/openapi.yaml +15 -0
  48. package/package.json +401 -147
  49. package/public/about.html +2 -2
  50. package/public/ai-malpractice-prevention.html +7 -7
  51. package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
  52. package/public/blog/a-receipt-is-not-world-state.html +388 -0
  53. package/public/blog/git-at-agent-scale.html +374 -0
  54. package/public/blog/no-llm-in-the-gate.html +133 -0
  55. package/public/blog.html +80 -0
  56. package/public/case-studies.html +16 -1
  57. package/public/compare.html +28 -0
  58. package/public/diagnostic.html +216 -7
  59. package/public/docs/connectors.html +39 -0
  60. package/public/federal.html +2 -2
  61. package/public/founders.html +639 -0
  62. package/public/index.html +87 -9
  63. package/public/install.html +8 -8
  64. package/public/learn.html +39 -0
  65. package/public/numbers.html +2 -2
  66. package/public/peter.html +310 -0
  67. package/public/platform-partners.html +119 -0
  68. package/public/pricing.html +24 -3
  69. package/public/privacy.html +117 -0
  70. package/public/pro.html +17 -0
  71. package/public/support.html +62 -0
  72. package/public/terms.html +130 -0
  73. package/public/third-party-notices.html +95 -0
  74. package/public/yt.html +351 -0
  75. package/scripts/action-receipts.js +133 -3
  76. package/scripts/adaptive-governance-arena.js +349 -0
  77. package/scripts/admin-override.js +205 -0
  78. package/scripts/agent-action-inventory.js +869 -0
  79. package/scripts/agent-audit-trace.js +42 -2
  80. package/scripts/agent-egress-policy.js +1117 -0
  81. package/scripts/agent-memory-lifecycle.js +141 -2
  82. package/scripts/agent-operations-planner.js +441 -1
  83. package/scripts/agent-readiness.js +68 -0
  84. package/scripts/agent-security-central.js +647 -0
  85. package/scripts/allowlist-bridge-honesty.js +417 -0
  86. package/scripts/async-job-runner.js +102 -11
  87. package/scripts/audit-trail.js +212 -0
  88. package/scripts/auto-promote-gates.js +178 -27
  89. package/scripts/billing.js +1 -1
  90. package/scripts/broker-execution-receipts.js +719 -0
  91. package/scripts/budget-aware-gates-proof.js +423 -0
  92. package/scripts/claude-feedback-sync.js +29 -3
  93. package/scripts/claw-harness-production.js +237 -0
  94. package/scripts/cli-progress.js +111 -0
  95. package/scripts/cli-schema.js +163 -1
  96. package/scripts/codex-runbook-flywheel.js +318 -0
  97. package/scripts/context-footprint.js +186 -0
  98. package/scripts/contextfs.js +143 -61
  99. package/scripts/dashboard-limits.js +27 -0
  100. package/scripts/dashboard.js +279 -9
  101. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  102. package/scripts/docker-sandbox-planner.js +18 -0
  103. package/scripts/double-blind-eval-protocol.js +252 -0
  104. package/scripts/edotenv-rl-gateway.js +259 -0
  105. package/scripts/ensure-production-search-corpus.js +162 -0
  106. package/scripts/eval-holdout.js +311 -0
  107. package/scripts/feedback-aggregate.js +21 -2
  108. package/scripts/feedback-loop.js +87 -5
  109. package/scripts/feedback-quality.js +9 -0
  110. package/scripts/file-ledger-lock.js +4 -1
  111. package/scripts/financial-control-plane.js +41 -1
  112. package/scripts/find-dormant-requires.js +118 -0
  113. package/scripts/fs-utils.js +84 -8
  114. package/scripts/gate-stats.js +2 -2
  115. package/scripts/gates-engine.js +859 -58
  116. package/scripts/generate-case-study-outreach.js +24 -15
  117. package/scripts/git-at-scale.js +628 -0
  118. package/scripts/governance-conflict-audit.js +1650 -0
  119. package/scripts/governance-difficulty-curriculum.js +328 -0
  120. package/scripts/graphrag-retrieval.js +275 -0
  121. package/scripts/gurobi-optimizer.js +324 -0
  122. package/scripts/gurobi_optimizer.py +485 -0
  123. package/scripts/harness-selector.js +82 -1
  124. package/scripts/hidden-entry-points.js +284 -0
  125. package/scripts/human-escalation.js +199 -1
  126. package/scripts/hybrid-feedback-context.js +152 -19
  127. package/scripts/intent-governed-execution.js +602 -0
  128. package/scripts/intervention-policy.js +123 -20
  129. package/scripts/jit-harness-compose.js +628 -0
  130. package/scripts/jsonl-watcher.js +10 -0
  131. package/scripts/lesson-embedding-index.js +95 -12
  132. package/scripts/lesson-retrieval.js +105 -19
  133. package/scripts/local-model-profile.js +19 -2
  134. package/scripts/mailer/resend-mailer.js +1 -1
  135. package/scripts/matryoshka-embedding.js +235 -0
  136. package/scripts/mcp-oauth.js +42 -4
  137. package/scripts/mcp-session-handles.js +1016 -0
  138. package/scripts/mcp-wiring-doctor.js +314 -0
  139. package/scripts/memory-firewall.js +115 -2
  140. package/scripts/memory-scope-readiness.js +299 -0
  141. package/scripts/memory-vs-rag-route.js +161 -0
  142. package/scripts/model-tier-router.js +148 -21
  143. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  144. package/scripts/openui-catalog-compose-honesty.js +593 -0
  145. package/scripts/operational-integrity.js +19 -1
  146. package/scripts/override-audit.js +213 -0
  147. package/scripts/package-manager-honesty-doctor.js +458 -0
  148. package/scripts/pr-manager.js +63 -1
  149. package/scripts/prove-herdr-adapter.js +52 -0
  150. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  151. package/scripts/prove-workos.js +73 -0
  152. package/scripts/provider-attestation-conformance.js +192 -0
  153. package/scripts/provider-receipt-contract.js +136 -0
  154. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  155. package/scripts/radware-threat-defense.js +280 -0
  156. package/scripts/rag-embedding-identity.js +221 -0
  157. package/scripts/rag-precision-guardrails.js +112 -2
  158. package/scripts/remote-feedback-capture.js +159 -0
  159. package/scripts/research-agent-harness.js +256 -0
  160. package/scripts/rsi-safety-hillclimb.js +200 -0
  161. package/scripts/rule-sprawl.js +188 -0
  162. package/scripts/schedule-manager.js +147 -0
  163. package/scripts/self-heal.js +8 -0
  164. package/scripts/session-lease.js +415 -0
  165. package/scripts/simatree-data-governance.js +347 -0
  166. package/scripts/slo-alert-engine.js +172 -7
  167. package/scripts/solver-parity.js +539 -0
  168. package/scripts/stealth-memory-injection-gate.js +333 -0
  169. package/scripts/switchyard-router.js +366 -0
  170. package/scripts/telemetry-analytics.js +84 -27
  171. package/scripts/temporal-decay-weighting.js +138 -0
  172. package/scripts/test-all.js +165 -0
  173. package/scripts/token-savings.js +42 -0
  174. package/scripts/tool-kpi-tracker.js +108 -5
  175. package/scripts/tool-registry.js +193 -5
  176. package/scripts/universal-claim-evaluator.js +14 -2
  177. package/scripts/vector-store.js +279 -9
  178. package/scripts/workflow-notebook.js +391 -0
  179. package/scripts/workflow-sentinel.js +111 -12
  180. package/scripts/workos-production-guard.js +260 -0
  181. package/scripts/workspace-search-route.js +515 -0
  182. package/server.json +2 -2
  183. package/src/agent-identity-boundary.js +76 -0
  184. package/src/agent-retrieval-cache.js +155 -0
  185. package/src/alert-noise-ledger.js +502 -0
  186. package/src/api/server.js +802 -185
  187. package/src/git-fast-cache.js +220 -0
  188. package/src/git-wal-sync.js +156 -0
  189. package/src/hash-anchored-edit.js +82 -0
  190. package/src/hermes-platform-protocol.js +475 -0
  191. package/src/hermes-sync-plane.js +241 -0
  192. package/src/index.js +30 -1
  193. package/src/iso42001-compliance-guard.js +97 -0
  194. package/src/latency-budget.js +244 -0
  195. package/src/mcp-writeguard.js +316 -0
  196. package/src/miminions-adapter.js +106 -0
  197. package/src/pipeline-compass.js +104 -0
  198. package/src/ppl-alert-pipeline.js +284 -0
  199. package/src/rendezvous-router.js +90 -0
  200. package/src/security-questionnaire.js +195 -0
@@ -0,0 +1,628 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * JIT harness compose doctor — JIT-Agent FORMAT steal.
6
+ *
7
+ * Maps arXiv:2608.25593 four-module harness (memory, planning, action,
8
+ * capability) onto EXISTING ThumbGate rails:
9
+ * memory → lesson-retrieval / feedback / contextfs
10
+ * planning → bin/agent-loop Plan + GSD / harness-selector task class
11
+ * action → PreToolUse gates + selected gate harness + switchyard steps
12
+ * capability → subagent-profiles + MCP profile + skills + model pool
13
+ *
14
+ * Does NOT train/download JIT-Agent-27B, emit free-form harness programs,
15
+ * or clone HarnessFactory / bingreeky/JIT.
16
+ *
17
+ * Source inspiration (not affiliated):
18
+ * https://arxiv.org/abs/2608.25593
19
+ * https://github.com/bingreeky/JIT
20
+ */
21
+
22
+ const fs = require('node:fs');
23
+ const path = require('node:path');
24
+
25
+ const {
26
+ selectHarnessName,
27
+ listHarnesses,
28
+ HARNESSES,
29
+ } = require('./harness-selector');
30
+ const {
31
+ routeAgentSteps,
32
+ buildAlwaysOnAgentPlan,
33
+ DEFAULT_POOL,
34
+ } = require('./switchyard-router');
35
+ const {
36
+ listSubagentProfiles,
37
+ getSubagentProfile,
38
+ validateSubagentProfiles,
39
+ } = require('./subagent-profiles');
40
+
41
+ const SOURCE_URL = 'https://arxiv.org/abs/2608.25593';
42
+ const SOURCE_REPO = 'https://github.com/bingreeky/JIT';
43
+ const MODULES = Object.freeze(['memory', 'planning', 'action', 'capability']);
44
+
45
+ const MODULE_RAILS = Object.freeze({
46
+ memory: {
47
+ label: 'memory management',
48
+ rails: [
49
+ 'lesson-retrieval (BM25F / hybrid RRF)',
50
+ '.claude/memory/feedback',
51
+ 'contextfs packs',
52
+ 'capture-feedback.js',
53
+ ],
54
+ when: 'Prior mistakes, scoped lessons, and session context packs.',
55
+ },
56
+ planning: {
57
+ label: 'planning strategy',
58
+ rails: [
59
+ 'bin/agent-loop Plan stage',
60
+ 'GSD (Capture→Clarify→Organize→Execute→Review)',
61
+ 'harness-selector task class',
62
+ 'implementation-notes ledger',
63
+ ],
64
+ when: 'Multi-step work needs an explicit plan before tool calls.',
65
+ },
66
+ action: {
67
+ label: 'action protocol',
68
+ rails: [
69
+ 'PreToolUse / gates-engine',
70
+ 'config/gates/*.json harnesses',
71
+ 'switchyard-router step roles',
72
+ 'session-lease + worktree isolation',
73
+ ],
74
+ when: 'Every tool call must pass deterministic gates before execution.',
75
+ },
76
+ capability: {
77
+ label: 'tool / skill orchestration',
78
+ rails: [
79
+ 'config/subagent-profiles.json',
80
+ 'config/mcp-allowlists.json profiles',
81
+ '.agents/skills + skills/thumbgate',
82
+ 'switchyard model pool / model-candidates',
83
+ ],
84
+ when: 'Bound which MCP profile, skills, and models this task may use.',
85
+ },
86
+ });
87
+
88
+ const TASK_CLASSES = Object.freeze({
89
+ code_edit: {
90
+ label: 'code edit',
91
+ memory: 'scoped_lessons',
92
+ planning: 'agent_loop_plan',
93
+ actionHarness: 'code-edit',
94
+ subagentProfile: 'pr_workflow',
95
+ switchyardRoles: ['code', 'gate'],
96
+ },
97
+ review: {
98
+ label: 'review / risk analysis',
99
+ memory: 'scoped_lessons',
100
+ planning: 'gsd_clarify',
101
+ actionHarness: null,
102
+ subagentProfile: 'review_workflow',
103
+ switchyardRoles: ['reason', 'summarize', 'gate'],
104
+ },
105
+ deploy: {
106
+ label: 'deploy / publish',
107
+ memory: 'deploy_lessons',
108
+ planning: 'agent_loop_plan',
109
+ actionHarness: 'deploy',
110
+ subagentProfile: 'secure_runtime',
111
+ switchyardRoles: ['gate', 'code'],
112
+ },
113
+ research: {
114
+ label: 'research / deep search',
115
+ memory: 'broad_hybrid',
116
+ planning: 'gsd_full',
117
+ actionHarness: null,
118
+ subagentProfile: 'review_workflow',
119
+ switchyardRoles: ['reason', 'summarize'],
120
+ },
121
+ secure: {
122
+ label: 'secure / constrained runtime',
123
+ memory: 'scoped_lessons',
124
+ planning: 'single_shot',
125
+ actionHarness: 'five-walls-governance',
126
+ subagentProfile: 'secure_runtime',
127
+ switchyardRoles: ['gate', 'private'],
128
+ },
129
+ routine: {
130
+ label: 'routine / scheduled agent',
131
+ memory: 'scoped_lessons',
132
+ planning: 'single_shot',
133
+ actionHarness: 'routine',
134
+ subagentProfile: 'pr_workflow',
135
+ switchyardRoles: ['intent', 'gate', 'bulk'],
136
+ },
137
+ db_write: {
138
+ label: 'database write',
139
+ memory: 'scoped_lessons',
140
+ planning: 'agent_loop_plan',
141
+ actionHarness: 'db-write',
142
+ subagentProfile: 'secure_runtime',
143
+ switchyardRoles: ['gate', 'code'],
144
+ },
145
+ default: {
146
+ label: 'general task',
147
+ memory: 'hybrid_default',
148
+ planning: 'agent_loop_plan',
149
+ actionHarness: null,
150
+ subagentProfile: 'pr_workflow',
151
+ switchyardRoles: ['intent', 'gate', 'code'],
152
+ },
153
+ });
154
+
155
+ const MEMORY_CHOICES = Object.freeze({
156
+ scoped_lessons: {
157
+ id: 'scoped_lessons',
158
+ rails: ['lesson-retrieval with entity/project/session scope', 'feedback promotion'],
159
+ why: 'Prefer matchable lessons over raw transcript dumps.',
160
+ },
161
+ deploy_lessons: {
162
+ id: 'deploy_lessons',
163
+ rails: ['lesson-retrieval tagged deploy/railway', 'prevention-rules.md'],
164
+ why: 'Deploy mistakes are high-cost; pull prior deploy denials first.',
165
+ },
166
+ broad_hybrid: {
167
+ id: 'broad_hybrid',
168
+ rails: ['pragmatic-hybrid-search RRF', 'graphify when architectural'],
169
+ why: 'Research tasks need lexical + optional dense + graph hops.',
170
+ },
171
+ hybrid_default: {
172
+ id: 'hybrid_default',
173
+ rails: ['lesson-retrieval reciprocalRankFusion', 'contextfs'],
174
+ why: 'Default: fuse lessons without shipping transcript blobs.',
175
+ },
176
+ });
177
+
178
+ const PLANNING_CHOICES = Object.freeze({
179
+ agent_loop_plan: {
180
+ id: 'agent_loop_plan',
181
+ rails: ['bin/agent-loop Plan→Observe→Act→Evaluate→Learn'],
182
+ why: 'Multi-step coding stays inside the Recollect→Plan loop.',
183
+ },
184
+ gsd_clarify: {
185
+ id: 'gsd_clarify',
186
+ rails: ['GSD Clarify + Organize before Execute'],
187
+ why: 'Review work needs acceptance criteria before patches.',
188
+ },
189
+ gsd_full: {
190
+ id: 'gsd_full',
191
+ rails: ['GSD Capture→Clarify→Organize→Execute→Review'],
192
+ why: 'Research / long-horizon tasks need the full GSD spine.',
193
+ },
194
+ single_shot: {
195
+ id: 'single_shot',
196
+ rails: ['single bounded plan + evidence receipt'],
197
+ why: 'Routine or locked profiles should not sprawl into multi-agent fan-out.',
198
+ },
199
+ });
200
+
201
+ function normalizeBoolean(value) {
202
+ if (value === true || value === 1) return true;
203
+ if (value === false || value === 0 || value == null) return false;
204
+ const s = String(value).trim().toLowerCase();
205
+ return s === '1' || s === 'true' || s === 'yes' || s === 'on';
206
+ }
207
+
208
+ function detectTrainOrCloneAttempt(text) {
209
+ const t = String(text || '');
210
+ const hits = [];
211
+ if (/\bjit-?agent-?27b\b/i.test(t) || /\bserve_meta_model\b/i.test(t)) {
212
+ hits.push('jit_model_serve');
213
+ }
214
+ if (/\bhuggingface\.co\/JIT-Agent\b/i.test(t) || /\bpip install.*\bjit\b/i.test(t)) {
215
+ hits.push('jit_model_download');
216
+ }
217
+ if (/\bHarnessFactory\b/.test(t) && /\bclone\b/i.test(t)) {
218
+ hits.push('harness_factory_clone');
219
+ }
220
+ if (/\bemit\b.*\bfree-?form\b.*\bharness\b/i.test(t) || /\bgenerate executable harness code\b/i.test(t)) {
221
+ hits.push('freeform_harness_emit');
222
+ }
223
+ return hits;
224
+ }
225
+
226
+ function classifyTask(taskText, options = {}) {
227
+ const q = String(taskText || '').trim();
228
+ const reasons = [];
229
+ if (options.taskClass && TASK_CLASSES[options.taskClass]) {
230
+ return {
231
+ taskClass: options.taskClass,
232
+ confidence: 1,
233
+ reasons: [`explicit taskClass=${options.taskClass}`],
234
+ gateHarness: TASK_CLASSES[options.taskClass].actionHarness,
235
+ };
236
+ }
237
+ if (!q) {
238
+ return {
239
+ taskClass: 'default',
240
+ confidence: 0.4,
241
+ reasons: ['empty task → default four-module compose'],
242
+ gateHarness: null,
243
+ };
244
+ }
245
+
246
+ const toolName = options.toolName || (/\b(edit|write|patch|refactor)\b/i.test(q) ? 'Edit' : 'Bash');
247
+ const gateHarness = selectHarnessName(toolName, { command: q, content: q });
248
+ if (gateHarness && HARNESSES[gateHarness]) {
249
+ reasons.push(`harness-selector → ${gateHarness}`);
250
+ }
251
+
252
+ if (gateHarness === 'deploy' || /\b(deploy|railway|npm publish|docker push)\b/i.test(q)) {
253
+ reasons.push('deploy / publish vocabulary');
254
+ return { taskClass: 'deploy', confidence: 0.9, reasons, gateHarness: gateHarness || 'deploy' };
255
+ }
256
+ if (gateHarness === 'db-write' || /\b(DROP TABLE|TRUNCATE|DELETE FROM|ALTER TABLE)\b/i.test(q)) {
257
+ reasons.push('database-write vocabulary');
258
+ return { taskClass: 'db_write', confidence: 0.9, reasons, gateHarness: 'db-write' };
259
+ }
260
+ if (gateHarness === 'routine' || /\b(nightly|scheduled|cron|routine agent)\b/i.test(q)) {
261
+ reasons.push('routine / scheduled vocabulary');
262
+ return { taskClass: 'routine', confidence: 0.85, reasons, gateHarness: 'routine' };
263
+ }
264
+ if (
265
+ gateHarness === 'five-walls-governance'
266
+ || /\b(secure_runtime|locked mcp|five.?walls|hard_deny|secret|pii)\b/i.test(q)
267
+ ) {
268
+ reasons.push('secure / constrained vocabulary');
269
+ return {
270
+ taskClass: 'secure',
271
+ confidence: 0.88,
272
+ reasons,
273
+ gateHarness: gateHarness || 'five-walls-governance',
274
+ };
275
+ }
276
+ if (/\b(review|code review|risk analysis|audit PR|read-only review)\b/i.test(q)) {
277
+ reasons.push('review vocabulary');
278
+ return { taskClass: 'review', confidence: 0.86, reasons, gateHarness };
279
+ }
280
+ if (/\b(research|deep search|investigate|survey|literature|arxiv)\b/i.test(q)) {
281
+ reasons.push('research vocabulary');
282
+ return { taskClass: 'research', confidence: 0.84, reasons, gateHarness };
283
+ }
284
+ if (gateHarness === 'code-edit' || /\b(implement|fix|refactor|patch|edit file|write code)\b/i.test(q)) {
285
+ reasons.push('code-edit vocabulary');
286
+ return { taskClass: 'code_edit', confidence: 0.87, reasons, gateHarness: gateHarness || 'code-edit' };
287
+ }
288
+
289
+ reasons.push('no strong class → default');
290
+ return { taskClass: 'default', confidence: 0.55, reasons, gateHarness };
291
+ }
292
+
293
+ function composeModules(classification, options = {}) {
294
+ const preset = TASK_CLASSES[classification.taskClass] || TASK_CLASSES.default;
295
+ const memory = MEMORY_CHOICES[preset.memory] || MEMORY_CHOICES.hybrid_default;
296
+ const planning = PLANNING_CHOICES[preset.planning] || PLANNING_CHOICES.agent_loop_plan;
297
+
298
+ let actionHarness = options.harness || classification.gateHarness || preset.actionHarness;
299
+ if (actionHarness && !HARNESSES[actionHarness]) {
300
+ actionHarness = preset.actionHarness;
301
+ }
302
+
303
+ const availableProfiles = listSubagentProfiles();
304
+ let subagentProfile = options.profile || preset.subagentProfile;
305
+ if (!availableProfiles.includes(subagentProfile)) {
306
+ subagentProfile = availableProfiles.includes('pr_workflow')
307
+ ? 'pr_workflow'
308
+ : availableProfiles[0] || null;
309
+ }
310
+
311
+ let profile = null;
312
+ if (subagentProfile) {
313
+ try {
314
+ profile = getSubagentProfile(subagentProfile);
315
+ } catch {
316
+ profile = null;
317
+ }
318
+ }
319
+
320
+ const roles = Array.isArray(options.roles) && options.roles.length
321
+ ? options.roles
322
+ : preset.switchyardRoles;
323
+ const plan = {
324
+ steps: roles.map((role, index) => ({
325
+ id: `m${index + 1}-${role}`,
326
+ type: role,
327
+ tags: [classification.taskClass, 'jit-compose'],
328
+ riskLevel: classification.taskClass === 'secure' || classification.taskClass === 'deploy'
329
+ ? 'high'
330
+ : 'medium',
331
+ sensitive: classification.taskClass === 'secure',
332
+ })),
333
+ };
334
+ const routed = routeAgentSteps(plan);
335
+
336
+ return {
337
+ memory: {
338
+ module: 'memory',
339
+ choice: memory.id,
340
+ rails: memory.rails,
341
+ why: memory.why,
342
+ catalog: MODULE_RAILS.memory,
343
+ },
344
+ planning: {
345
+ module: 'planning',
346
+ choice: planning.id,
347
+ rails: planning.rails,
348
+ why: planning.why,
349
+ catalog: MODULE_RAILS.planning,
350
+ },
351
+ action: {
352
+ module: 'action',
353
+ choice: actionHarness || 'default-gates-only',
354
+ gateHarness: actionHarness,
355
+ rails: [
356
+ ...(actionHarness ? [`config/gates/${actionHarness}.json`] : ['default.json + auto-promoted gates']),
357
+ 'PreToolUse / gates-engine',
358
+ 'switchyard step roles: ' + roles.join(', '),
359
+ ],
360
+ why: actionHarness
361
+ ? `Load specialized ${actionHarness} harness additively on top of default gates.`
362
+ : 'No specialized harness; default gates still apply.',
363
+ catalog: MODULE_RAILS.action,
364
+ switchyard: {
365
+ distinctModels: routed.distinctModels,
366
+ steps: routed.steps.map((s) => ({
367
+ id: s.id,
368
+ role: s.role,
369
+ modelId: s.modelId,
370
+ reason: s.reason,
371
+ })),
372
+ },
373
+ },
374
+ capability: {
375
+ module: 'capability',
376
+ choice: subagentProfile || 'none',
377
+ subagentProfile,
378
+ mcpProfile: profile?.mcpProfile || null,
379
+ skills: profile?.skills || [],
380
+ context: profile?.context || null,
381
+ rails: [
382
+ subagentProfile ? `subagent-profiles:${subagentProfile}` : 'no subagent profile',
383
+ profile?.mcpProfile ? `mcp-allowlists:${profile.mcpProfile}` : 'mcp default',
384
+ 'skills registry + model pool',
385
+ ],
386
+ why: profile
387
+ ? `Bound tools/skills via ${subagentProfile} (mcp=${profile.mcpProfile}).`
388
+ : 'No matching subagent profile; keep MCP allowlist fail-closed.',
389
+ catalog: MODULE_RAILS.capability,
390
+ modelPoolSample: DEFAULT_POOL.slice(0, 3).map((m) => m.id),
391
+ },
392
+ };
393
+ }
394
+
395
+ function probeRails(rootDir) {
396
+ const root = rootDir || path.resolve(__dirname, '..');
397
+ const checks = [
398
+ { id: 'agent_loop', path: path.join(root, 'bin', 'agent-loop') },
399
+ { id: 'harness_selector', path: path.join(root, 'scripts', 'harness-selector.js') },
400
+ { id: 'switchyard_router', path: path.join(root, 'scripts', 'switchyard-router.js') },
401
+ { id: 'subagent_profiles', path: path.join(root, 'config', 'subagent-profiles.json') },
402
+ { id: 'mcp_allowlists', path: path.join(root, 'config', 'mcp-allowlists.json') },
403
+ { id: 'gates_dir', path: path.join(root, 'config', 'gates') },
404
+ ];
405
+ return checks.map((c) => ({
406
+ id: c.id,
407
+ path: c.path,
408
+ exists: fs.existsSync(c.path),
409
+ }));
410
+ }
411
+
412
+ function buildJitHarnessComposeReport(options = {}) {
413
+ const root = options.root
414
+ ? path.resolve(String(options.root))
415
+ : path.resolve(__dirname, '..');
416
+ const task = options.task || options.query || options._ || '';
417
+ const findings = [];
418
+ const cloneHits = detectTrainOrCloneAttempt(task);
419
+ for (const hit of options.argv || []) {
420
+ cloneHits.push(...detectTrainOrCloneAttempt(hit));
421
+ }
422
+ const uniqueCloneHits = [...new Set(cloneHits)];
423
+ if (uniqueCloneHits.length) {
424
+ findings.push({
425
+ severity: 'fail',
426
+ id: 'jit_clone_refused',
427
+ message: `Refusing JIT model/HarnessFactory clone path (${uniqueCloneHits.join(', ')}). Compose existing rails only.`,
428
+ });
429
+ }
430
+
431
+ const classification = classifyTask(task, {
432
+ taskClass: options.taskClass || options.class || null,
433
+ toolName: options.toolName || options.tool || null,
434
+ });
435
+ const modules = composeModules(classification, {
436
+ harness: options.harness || null,
437
+ profile: options.profile || null,
438
+ roles: options.roles
439
+ ? String(options.roles).split(',').map((s) => s.trim()).filter(Boolean)
440
+ : null,
441
+ });
442
+
443
+ const probes = probeRails(root);
444
+ const missing = probes.filter((p) => !p.exists);
445
+ for (const m of missing) {
446
+ findings.push({
447
+ severity: 'warn',
448
+ id: `missing_rail_${m.id}`,
449
+ message: `Expected rail missing: ${m.path}`,
450
+ });
451
+ }
452
+
453
+ let profileValidation = null;
454
+ try {
455
+ profileValidation = validateSubagentProfiles();
456
+ if (!profileValidation.valid) {
457
+ findings.push({
458
+ severity: 'warn',
459
+ id: 'subagent_profile_invalid',
460
+ message: profileValidation.issues.slice(0, 5).join('; '),
461
+ });
462
+ }
463
+ } catch (err) {
464
+ findings.push({
465
+ severity: 'warn',
466
+ id: 'subagent_profile_load_error',
467
+ message: err.message,
468
+ });
469
+ }
470
+
471
+ const alwaysOn = buildAlwaysOnAgentPlan({ includeBulk: false });
472
+ const mapOnly = normalizeBoolean(options.map || options['map-only']);
473
+
474
+ let status = 'ready';
475
+ if (findings.some((f) => f.severity === 'fail')) status = 'fail';
476
+ else if (findings.some((f) => f.severity === 'warn')) status = 'ready_with_warnings';
477
+
478
+ const report = {
479
+ name: 'thumbgate-jit-harness-compose',
480
+ status,
481
+ task: task || null,
482
+ taskClass: classification.taskClass,
483
+ taskLabel: (TASK_CLASSES[classification.taskClass] || TASK_CLASSES.default).label,
484
+ confidence: classification.confidence,
485
+ reasons: classification.reasons,
486
+ modules,
487
+ moduleOrder: MODULES,
488
+ availableGateHarnesses: listHarnesses(),
489
+ availableSubagentProfiles: listSubagentProfiles(),
490
+ railProbes: probes,
491
+ profileValidation,
492
+ alwaysOnPlanSteps: alwaysOn.steps?.length || 0,
493
+ compareNotClone: true,
494
+ never: [
495
+ 'train or download JIT-Agent-27B',
496
+ 'clone bingreeky/JIT HarnessFactory',
497
+ 'emit free-form harness programs',
498
+ 'claim ThumbGate is JIT-Agent',
499
+ ],
500
+ source: SOURCE_URL,
501
+ sourceRepo: SOURCE_REPO,
502
+ disclaimer: 'FORMAT steal only. Not affiliated with JIT-Agent / bingreeky/JIT. Composes existing ThumbGate rails; does not train a harness model.',
503
+ findings,
504
+ };
505
+
506
+ if (mapOnly) {
507
+ report.map = MODULES.map((id) => ({ id, ...MODULE_RAILS[id] }));
508
+ report.taskClasses = Object.keys(TASK_CLASSES);
509
+ }
510
+
511
+ return report;
512
+ }
513
+
514
+ function formatJitHarnessComposeReport(report) {
515
+ const lines = [
516
+ 'ThumbGate JIT Harness Compose (JIT-Agent FORMAT steal)',
517
+ `Status : ${report.status}`,
518
+ `Task : ${report.task || '(none)'}`,
519
+ `Class : ${report.taskClass} (${report.taskLabel}) confidence=${report.confidence}`,
520
+ `Reasons : ${(report.reasons || []).join('; ')}`,
521
+ 'Modules :',
522
+ ];
523
+ for (const id of report.moduleOrder || MODULES) {
524
+ const m = report.modules?.[id];
525
+ if (!m) continue;
526
+ lines.push(` [${id}] choice=${m.choice}`);
527
+ lines.push(` rails=${(m.rails || []).join(' · ')}`);
528
+ lines.push(` why=${m.why}`);
529
+ }
530
+ if (report.modules?.action?.switchyard?.distinctModels?.length) {
531
+ lines.push(`Models : ${report.modules.action.switchyard.distinctModels.join(', ')}`);
532
+ }
533
+ if (report.modules?.capability?.subagentProfile) {
534
+ lines.push(
535
+ `Capability profile: ${report.modules.capability.subagentProfile}`
536
+ + ` mcp=${report.modules.capability.mcpProfile}`
537
+ + ` skills=${(report.modules.capability.skills || []).join(',') || '(none)'}`
538
+ );
539
+ }
540
+ if (report.findings?.length) {
541
+ lines.push('Findings:');
542
+ for (const f of report.findings) {
543
+ lines.push(` [${f.severity}] ${f.id}: ${f.message}`);
544
+ }
545
+ }
546
+ lines.push(`Never : ${(report.never || []).join('; ')}`);
547
+ lines.push(`Source : ${report.source}`);
548
+ lines.push(report.disclaimer);
549
+ return `${lines.join('\n')}\n`;
550
+ }
551
+
552
+ function printHelp() {
553
+ process.stdout.write(`Usage: node scripts/jit-harness-compose.js [options]
554
+
555
+ JIT-Agent FORMAT steal — compose memory/planning/action/capability onto
556
+ existing ThumbGate rails. Does not train or download JIT-Agent.
557
+
558
+ Options:
559
+ --task=<text> Task to classify / compose
560
+ --class=<taskClass> Force task class (${Object.keys(TASK_CLASSES).join('|')})
561
+ --harness=<name> Force gate harness
562
+ --profile=<name> Force subagent profile
563
+ --roles=a,b,c Switchyard roles override
564
+ --toolName=Bash|Edit Hint for harness-selector
565
+ --map-only Print four-module rail map
566
+ --json
567
+ --strict Exit 1 unless status=ready
568
+ --root=<dir>
569
+ `);
570
+ }
571
+
572
+ function main(argv = process.argv.slice(2)) {
573
+ if (argv.includes('--help') || argv.includes('-h')) {
574
+ printHelp();
575
+ return 0;
576
+ }
577
+ const options = { argv };
578
+ for (const arg of argv) {
579
+ if (arg === '--json') options.json = true;
580
+ else if (arg === '--strict') options.strict = true;
581
+ else if (arg === '--map' || arg === '--map-only') options.map = true;
582
+ else if (arg.startsWith('--task=')) options.task = arg.slice('--task='.length);
583
+ else if (arg.startsWith('--query=')) options.task = arg.slice('--query='.length);
584
+ else if (arg.startsWith('--class=')) options.taskClass = arg.slice('--class='.length);
585
+ else if (arg.startsWith('--taskClass=')) options.taskClass = arg.slice('--taskClass='.length);
586
+ else if (arg.startsWith('--harness=')) options.harness = arg.slice('--harness='.length);
587
+ else if (arg.startsWith('--profile=')) options.profile = arg.slice('--profile='.length);
588
+ else if (arg.startsWith('--roles=')) options.roles = arg.slice('--roles='.length);
589
+ else if (arg.startsWith('--toolName=')) options.toolName = arg.slice('--toolName='.length);
590
+ else if (arg.startsWith('--tool=')) options.toolName = arg.slice('--tool='.length);
591
+ else if (arg.startsWith('--root=')) options.root = arg.slice('--root='.length);
592
+ else if (!arg.startsWith('-') && !options.task) options.task = arg;
593
+ }
594
+
595
+ const report = buildJitHarnessComposeReport(options);
596
+ if (options.json) {
597
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
598
+ } else {
599
+ process.stdout.write(formatJitHarnessComposeReport(report));
600
+ }
601
+ if (options.strict && report.status !== 'ready') return 1;
602
+ if (report.status === 'fail') return 1;
603
+ return 0;
604
+ }
605
+
606
+ if (require.main === module || (
607
+ process.argv[1]
608
+ && path.resolve(process.argv[1]) === path.resolve(__filename)
609
+ )) {
610
+ process.exitCode = main();
611
+ }
612
+
613
+ module.exports = {
614
+ MODULES,
615
+ MODULE_RAILS,
616
+ TASK_CLASSES,
617
+ MEMORY_CHOICES,
618
+ PLANNING_CHOICES,
619
+ classifyTask,
620
+ composeModules,
621
+ detectTrainOrCloneAttempt,
622
+ probeRails,
623
+ buildJitHarnessComposeReport,
624
+ formatJitHarnessComposeReport,
625
+ main,
626
+ SOURCE_URL,
627
+ SOURCE_REPO,
628
+ };
@@ -62,6 +62,9 @@ function shouldIngest(entry, sourceFilter) {
62
62
 
63
63
  function ingestEntry(entry) {
64
64
  const signal = entry.signal === 'positive' ? 'up' : entry.signal === 'negative' ? 'down' : entry.signal;
65
+ const suppliedSource = entry.memorySource && typeof entry.memorySource === 'object'
66
+ ? entry.memorySource
67
+ : {};
65
68
 
66
69
  const result = captureFeedback({
67
70
  signal,
@@ -72,6 +75,13 @@ function ingestEntry(entry) {
72
75
  tags: [...(entry.tags || []), WATCHER_SOURCE_TAG, `bridged-from:${entry.source}`],
73
76
  skill: entry.skill || undefined,
74
77
  reviewOrigin: entry.reviewOrigin || 'imported',
78
+ memorySource: {
79
+ type: suppliedSource.type || entry.source,
80
+ identifier: suppliedSource.identifier || entry.id || entry.source,
81
+ // JSONL watcher entries are externally authored. Never let the payload
82
+ // self-assert trusted provenance across this boundary.
83
+ trust: 'untrusted',
84
+ },
75
85
  });
76
86
 
77
87
  return result;