thumbgate 1.27.17 → 1.27.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +85 -0
- package/.claude-plugin/plugin.json +3 -4
- package/.well-known/llms.txt +12 -33
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +30 -221
- package/adapters/chatgpt/openapi.yaml +1695 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/gemini/function-declarations.json +0 -1
- package/adapters/mcp/server-stdio.js +11 -263
- package/adapters/opencode/opencode.json +1 -1
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +122 -1421
- package/bin/postinstall.js +27 -11
- package/config/gate-templates.json +0 -216
- package/config/gates/claim-verification.json +0 -12
- package/config/gates/default.json +2 -31
- package/config/github-about.json +2 -2
- package/config/mcp-allowlists.json +13 -23
- package/config/merge-quality-checks.json +1 -0
- package/config/model-candidates.json +6 -121
- package/openapi/openapi.yaml +0 -12
- package/package.json +99 -229
- package/public/blog.html +4 -4
- package/public/codex-plugin.html +20 -72
- package/public/compare.html +8 -31
- package/public/dashboard.html +166 -930
- package/public/federal.html +2 -2
- package/public/guide.html +9 -33
- package/public/index.html +116 -495
- package/public/learn.html +18 -191
- package/public/lessons.html +10 -168
- package/public/numbers.html +7 -7
- package/public/pro.html +11 -34
- package/scripts/agent-memory-lifecycle.js +0 -211
- package/scripts/agent-readiness.js +3 -167
- package/scripts/agent-reward-model.js +1 -53
- package/scripts/auto-promote-gates.js +10 -82
- package/scripts/auto-wire-hooks.js +0 -14
- package/scripts/billing.js +1 -93
- package/scripts/build-metadata.js +10 -50
- package/scripts/cli-feedback.js +6 -18
- package/scripts/cli-schema.js +0 -90
- package/scripts/cli-telemetry.js +1 -6
- package/scripts/commercial-offer.js +2 -82
- package/scripts/context-manager.js +6 -74
- package/scripts/dashboard.js +2 -68
- package/scripts/durability/step.js +3 -3
- package/scripts/export-databricks-bundle.js +1 -5
- package/scripts/export-dpo-pairs.js +2 -7
- package/scripts/feedback-loop.js +1 -145
- package/scripts/feedback-quality.js +0 -87
- package/scripts/filesystem-search.js +10 -35
- package/scripts/gate-stats.js +0 -89
- package/scripts/gates-engine.js +85 -1105
- package/scripts/gemini-embedding-policy.js +1 -2
- package/scripts/hook-runtime.js +14 -20
- package/scripts/hook-thumbgate-cache-updater.js +2 -18
- package/scripts/hybrid-feedback-context.js +7 -142
- package/scripts/lesson-inference.js +3 -8
- package/scripts/lesson-search.js +1 -17
- package/scripts/license.js +1 -0
- package/scripts/llm-client.js +9 -390
- package/scripts/local-model-profile.js +8 -15
- package/scripts/memory-scope-readiness.js +0 -159
- package/scripts/meta-agent-loop.js +0 -36
- package/scripts/operational-integrity.js +5 -39
- package/scripts/oss-pr-opportunity-scout.js +5 -35
- package/scripts/plausible-server-events.js +6 -9
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/proxy-pointer-rag-guardrails.js +1 -42
- package/scripts/rate-limiter.js +13 -64
- package/scripts/secret-scanner.js +5 -44
- package/scripts/security-scanner.js +10 -260
- package/scripts/self-distill-agent.js +1 -3
- package/scripts/seo-gsd.js +2 -916
- package/scripts/statusline-cache-path.js +2 -17
- package/scripts/statusline-local-stats.js +1 -9
- package/scripts/statusline-meta.js +2 -28
- package/scripts/statusline.sh +4 -20
- package/scripts/telemetry-analytics.js +0 -357
- package/scripts/thompson-sampling.js +10 -31
- package/scripts/thumbgate-bench.js +1 -16
- package/scripts/thumbgate-search.js +19 -85
- package/scripts/tool-registry.js +1 -169
- package/scripts/vector-store.js +0 -45
- package/scripts/workflow-sentinel.js +53 -286
- package/scripts/workspace-evolver.js +2 -62
- package/src/api/server.js +290 -2578
- package/.claude/commands/dashboard.md +0 -15
- package/.claude/commands/thumbgate-blocked.md +0 -27
- package/.claude/commands/thumbgate-dashboard.md +0 -15
- package/.claude/commands/thumbgate-doctor.md +0 -30
- package/.claude/commands/thumbgate-guard.md +0 -36
- package/.claude/commands/thumbgate-protect.md +0 -30
- package/.claude/commands/thumbgate-rules.md +0 -30
- package/.well-known/agentic-verify.txt +0 -1
- package/adapters/gcp/dfcx-webhook-gate.js +0 -295
- package/adapters/letta/README.md +0 -41
- package/adapters/letta/thumbgate-letta-adapter.js +0 -133
- package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
- package/bench/observability-eval-suite.json +0 -26
- package/bin/dashboard-cli.js +0 -7
- package/commands/dashboard.md +0 -15
- package/commands/thumbgate-dashboard.md +0 -15
- package/config/builtin-lessons.json +0 -23
- package/config/gate-classifier-routing.json +0 -98
- package/config/post-deploy-marketing-pages.json +0 -61
- package/public/about.html +0 -162
- package/public/agent-manager.html +0 -179
- package/public/agents-cost-savings.html +0 -153
- package/public/ai-malpractice-prevention.html +0 -818
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
- package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
- package/public/assets/brand/thumbgate-mark-team.svg +0 -26
- package/public/assets/brand/thumbgate-mark.svg +0 -21
- package/public/assets/brand/thumbgate-wordmark.svg +0 -20
- package/public/assets/claude-thumbgate-statusbar.svg +0 -8
- package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
- package/public/assets/legal-intake-control-flow.svg +0 -66
- package/public/brand/thumbgate-mark.svg +0 -19
- package/public/brand/thumbgate-og.svg +0 -16
- package/public/chatgpt-app.html +0 -330
- package/public/codex-enterprise.html +0 -123
- package/public/js/buyer-intent.js +0 -672
- package/public/pricing.html +0 -399
- package/scripts/action-receipts.js +0 -324
- package/scripts/activation-quickstart.js +0 -187
- package/scripts/agent-operations-planner.js +0 -621
- package/scripts/ai-component-inventory.js +0 -367
- package/scripts/async-eval-observability.js +0 -236
- package/scripts/audit.js +0 -65
- package/scripts/aws-blocks-guardrails.js +0 -228
- package/scripts/classifier-routing.js +0 -130
- package/scripts/dashboard-chat.js +0 -332
- package/scripts/feedback-aggregate.js +0 -281
- package/scripts/feedback-sanitizer.js +0 -105
- package/scripts/hob-pack.js +0 -591
- package/scripts/hook-stop-anti-claim.js +0 -301
- package/scripts/install-shim.js +0 -87
- package/scripts/mcp-oauth.js +0 -293
- package/scripts/noop-detect.js +0 -285
- package/scripts/omlx-smoke.js +0 -192
- package/scripts/parallel-workflow-orchestrator.js +0 -293
- package/scripts/plan-gate.js +0 -243
- package/scripts/plausible-domain-config.js +0 -99
- package/scripts/qa-scenario-planner.js +0 -136
- package/scripts/repeat-metric.js +0 -137
- package/scripts/secret-fixture-tokens.js +0 -61
- package/scripts/secret-redaction.js +0 -166
- package/scripts/self-harness-optimizer.js +0 -141
- package/scripts/self-healing-check.js +0 -193
- package/scripts/silent-failure-cluster.js +0 -531
- package/scripts/statusline-cache-read.js +0 -57
- package/scripts/sync-telemetry-from-prod.js +0 -374
- package/scripts/tool-contract-validator.js +0 -76
- package/scripts/trajectory-scorer.js +0 -63
- package/scripts/verify-marketing-pages-deployed.js +0 -195
- package/scripts/visitor-journey.js +0 -172
package/public/numbers.html
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
<meta property="og:title" content="ThumbGate — The Numbers">
|
|
11
11
|
<meta property="og:description" content="Generated first-party operational snapshot: configured gates, recorded interventions, and explicit zero-evidence caveats.">
|
|
12
12
|
<meta property="og:type" content="website">
|
|
13
|
-
<meta property="og:url" content="https://thumbgate.
|
|
13
|
+
<meta property="og:url" content="https://thumbgate-production.up.railway.app/numbers">
|
|
14
14
|
<meta name="twitter:card" content="summary_large_image">
|
|
15
|
-
<link rel="canonical" href="https://thumbgate.
|
|
15
|
+
<link rel="canonical" href="https://thumbgate-production.up.railway.app/numbers">
|
|
16
16
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
17
17
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
18
|
-
<script defer data-domain="thumbgate.
|
|
18
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
19
19
|
|
|
20
20
|
<script type="application/ld+json">
|
|
21
21
|
{
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"alternateName": "thumbgate",
|
|
26
26
|
"applicationCategory": "DeveloperApplication",
|
|
27
27
|
"operatingSystem": "Cross-platform, Node.js >=18.18.0",
|
|
28
|
-
"softwareVersion": "1.
|
|
29
|
-
"url": "https://thumbgate.
|
|
28
|
+
"softwareVersion": "1.20.0",
|
|
29
|
+
"url": "https://thumbgate-production.up.railway.app/numbers",
|
|
30
30
|
"dateModified": "2026-05-07",
|
|
31
31
|
"creator": {
|
|
32
32
|
"@type": "Person",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@type": "Dataset",
|
|
47
47
|
"name": "ThumbGate Operational Snapshot",
|
|
48
48
|
"description": "First-party operational snapshot from the ThumbGate pre-action check runtime: configured checks, recorded block/warn events, estimated token savings from recorded blocks, and Bayes error rate when the sample supports it.",
|
|
49
|
-
"url": "https://thumbgate.
|
|
49
|
+
"url": "https://thumbgate-production.up.railway.app/numbers",
|
|
50
50
|
"license": "https://opensource.org/licenses/MIT",
|
|
51
51
|
"creator": {
|
|
52
52
|
"@type": "Person",
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
<main class="container">
|
|
203
203
|
<h1>The Numbers</h1>
|
|
204
204
|
<p class="subtitle">Generated first-party operational snapshot from the ThumbGate runtime. This is not customer traction, install volume, revenue, or proof that a configured gate has fired.</p>
|
|
205
|
-
<div class="freshness">Updated: 2026-05-07 · Version 1.
|
|
205
|
+
<div class="freshness">Updated: 2026-05-07 · Version 1.20.0</div>
|
|
206
206
|
<div class="truth-note"><strong>Read this first:</strong> configured checks are inventory. Recorded blocks and warnings are usage evidence. This snapshot currently reports 0 recorded hard-block event(s) and 0 recorded warning event(s).</div>
|
|
207
207
|
|
|
208
208
|
<h2>Gate enforcement</h2>
|
package/public/pro.html
CHANGED
|
@@ -11,12 +11,11 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
11
11
|
<meta property="og:type" content="website">
|
|
12
12
|
<meta property="og:url" content="__APP_ORIGIN__/pro">
|
|
13
13
|
<link rel="canonical" href="__APP_ORIGIN__/pro">
|
|
14
|
-
<link rel="alternate" type="text/markdown" title="ThumbGate LLM context" href="__APP_ORIGIN__/llm-context.md">
|
|
15
14
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
16
15
|
<link rel="apple-touch-icon" href="/assets/brand/thumbgate-mark.svg">
|
|
17
16
|
<meta property="og:image" content="/og.png">
|
|
18
17
|
|
|
19
|
-
<script defer data-domain="thumbgate.
|
|
18
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
20
19
|
__GA_BOOTSTRAP__
|
|
21
20
|
|
|
22
21
|
<script>
|
|
@@ -37,7 +36,7 @@ __GA_BOOTSTRAP__
|
|
|
37
36
|
<script type="application/ld+json">
|
|
38
37
|
{
|
|
39
38
|
"@context": "https://schema.org",
|
|
40
|
-
"@type": "FAQPage", "mainEntity": [{ "@type": "Question", "name": "How is Pro different from the free install?", "acceptedAnswer": { "@type": "Answer", "text": "Free keeps local recall, checks, and MCP. Pro adds the personal dashboard, DPO export, auto-connect, and founder support." } }, { "@type": "Question", "name": "Does Pro require a cloud account?", "acceptedAnswer": { "@type": "Answer", "text": "No. Pro stays local-first;
|
|
39
|
+
"@type": "FAQPage", "mainEntity": [{ "@type": "Question", "name": "How is Pro different from the free install?", "acceptedAnswer": { "@type": "Answer", "text": "Free keeps local recall, checks, and MCP. Pro adds the personal dashboard, DPO export, auto-connect, and founder support." } }, { "@type": "Question", "name": "Does Pro require a cloud account?", "acceptedAnswer": { "@type": "Answer", "text": "No. Pro stays local-first; Team is the hosted rollout lane for shared lessons, org visibility, and reviews." } }, { "@type": "Question", "name": "What happens after checkout?", "acceptedAnswer": { "@type": "Answer", "text": "You activate Pro, connect the local dashboard, and inspect blocked actions, lessons, and exports." } }, { "@type": "Question", "name": "When should I choose Team instead of Pro?", "acceptedAnswer": { "@type": "Answer", "text": "Choose Team when one correction needs to protect multiple developers or agents across shared repositories." } }]
|
|
41
40
|
}
|
|
42
41
|
</script>
|
|
43
42
|
|
|
@@ -712,7 +711,7 @@ __GA_BOOTSTRAP__
|
|
|
712
711
|
<body>
|
|
713
712
|
<nav>
|
|
714
713
|
<div class="container">
|
|
715
|
-
<a href="/" class="nav-logo"><img src="/assets/brand/thumbgate-mark-inline
|
|
714
|
+
<a href="/" class="nav-logo"><img src="/assets/brand/thumbgate-mark-inline.svg" alt="ThumbGate" class="logo-mark" width="28" height="28"><span class="logo-text">ThumbGate Pro</span></a>
|
|
716
715
|
<div class="nav-links">
|
|
717
716
|
<a href="#why-pay">Why Pro</a>
|
|
718
717
|
<a href="#proof">Proof</a>
|
|
@@ -731,7 +730,7 @@ __GA_BOOTSTRAP__
|
|
|
731
730
|
<h1>Buy the operator loop that proves your AI agent stopped repeating the mistake.</h1>
|
|
732
731
|
<p style="font-size:13px;opacity:0.8;margin-bottom:0.5rem;">Updated: <time datetime="2026-04-20">2026-04-20</time> · by <a href="https://github.com/IgorGanapolsky" style="color:inherit;">Igor Ganapolsky</a></p>
|
|
733
732
|
<p>ThumbGate Pro is for one operator who already hit a repeated AI-agent failure and now needs proof: what was blocked, why it was blocked, and what changed before the next risky run.</p>
|
|
734
|
-
<p>Start Pro when you want the local dashboard, DPO export, and a single proof lane for the repeated mistake you need to stop.
|
|
733
|
+
<p>Start Pro when you want the local dashboard, DPO export, and a single proof lane for the repeated mistake you need to stop. Team diagnostics and custom services are handled through intake, not this buyer path.</p>
|
|
735
734
|
<div class="hero-proof">
|
|
736
735
|
<div class="proof-pill">Personal local dashboard</div>
|
|
737
736
|
<div class="proof-pill">DPO export from real corrections</div>
|
|
@@ -816,28 +815,6 @@ __GA_BOOTSTRAP__
|
|
|
816
815
|
</div>
|
|
817
816
|
</section>
|
|
818
817
|
|
|
819
|
-
<section class="section" id="deterministic-loop">
|
|
820
|
-
<div class="container">
|
|
821
|
-
<div class="section-label">Why Pro now</div>
|
|
822
|
-
<h2 class="section-title">Black-box thumbs do not prove prevention. Pro gives the operator an audit loop.</h2>
|
|
823
|
-
<p class="section-intro">Native rating buttons can tell a vendor that an answer felt wrong. ThumbGate Pro gives you the operational record: the correction, the lesson, the rule, the blocked tool call, and the export path.</p>
|
|
824
|
-
<div class="grid-3">
|
|
825
|
-
<div class="feature-card">
|
|
826
|
-
<h3>Inspectable memory</h3>
|
|
827
|
-
<p>Search the exact lesson that came from a thumbs-down and see whether it is still active, warning-only, or blocking.</p>
|
|
828
|
-
</div>
|
|
829
|
-
<div class="feature-card">
|
|
830
|
-
<h3>Deterministic checks</h3>
|
|
831
|
-
<p>The enforcement layer evaluates tool name, arguments, working directory, command shape, confidence, and required evidence before the action runs.</p>
|
|
832
|
-
</div>
|
|
833
|
-
<div class="feature-card">
|
|
834
|
-
<h3>Exportable proof</h3>
|
|
835
|
-
<p>Take the same correction history into JSONL, DPO export, review packets, and team rollout conversations instead of trusting hidden memory.</p>
|
|
836
|
-
</div>
|
|
837
|
-
</div>
|
|
838
|
-
</div>
|
|
839
|
-
</section>
|
|
840
|
-
|
|
841
818
|
<section class="section" id="why-pay">
|
|
842
819
|
<div class="container">
|
|
843
820
|
<div class="section-label">Why operators pay</div>
|
|
@@ -924,11 +901,11 @@ __GA_BOOTSTRAP__
|
|
|
924
901
|
|
|
925
902
|
<div class="pricing-sidebar">
|
|
926
903
|
<div class="team-card">
|
|
927
|
-
<div class="section-label" style="text-align:left;margin-bottom:8px;">When
|
|
904
|
+
<div class="section-label" style="text-align:left;margin-bottom:8px;">When Team is better</div>
|
|
928
905
|
<h3>Need shared enforcement?</h3>
|
|
929
|
-
<p>Choose
|
|
906
|
+
<p>Choose Team when one correction must protect multiple developers or agents across shared repositories, CI, approval policies, and audit trails. Team is $49/seat/mo with a 3-seat minimum after qualification.</p>
|
|
930
907
|
<div class="hero-actions" style="margin-top:18px;">
|
|
931
|
-
<a class="btn-secondary" href="/#workflow-sprint-intake">Book
|
|
908
|
+
<a class="btn-secondary" href="/#workflow-sprint-intake">Book a Team Pilot Call</a>
|
|
932
909
|
</div>
|
|
933
910
|
</div>
|
|
934
911
|
<div class="team-card">
|
|
@@ -951,15 +928,15 @@ __GA_BOOTSTRAP__
|
|
|
951
928
|
</div>
|
|
952
929
|
<div class="faq-item">
|
|
953
930
|
<button class="faq-q" type="button" onclick="toggleFaq(this)" onkeydown="handleFaqKeydown(event)" aria-expanded="false">Does Pro require a cloud account?</button>
|
|
954
|
-
<div class="faq-a">No. Pro is still local-first for the individual operator lane.
|
|
931
|
+
<div class="faq-a">No. Pro is still local-first for the individual operator lane. Team is the hosted rollout lane for shared lessons, org visibility, and hosted review views.</div>
|
|
955
932
|
</div>
|
|
956
933
|
<div class="faq-item">
|
|
957
934
|
<button class="faq-q" type="button" onclick="toggleFaq(this)" onkeydown="handleFaqKeydown(event)" aria-expanded="false">What happens after checkout?</button>
|
|
958
935
|
<div class="faq-a">You activate Pro, connect the personal local dashboard, and your running agents can appear automatically so you can inspect blocked actions, active lessons, and exportable DPO pairs without adding a separate cloud dashboard dependency.</div>
|
|
959
936
|
</div>
|
|
960
937
|
<div class="faq-item">
|
|
961
|
-
<button class="faq-q" type="button" onclick="toggleFaq(this)" onkeydown="handleFaqKeydown(event)" aria-expanded="false">When should I choose
|
|
962
|
-
<div class="faq-a">Choose
|
|
938
|
+
<button class="faq-q" type="button" onclick="toggleFaq(this)" onkeydown="handleFaqKeydown(event)" aria-expanded="false">When should I choose Team instead of Pro?</button>
|
|
939
|
+
<div class="faq-a">Choose Team when one thumbs-down should protect multiple people or agents across shared repositories, or when you need shared hosted lessons, org dashboard visibility, and a workflow hardening pilot with rollout review views.</div>
|
|
963
940
|
</div>
|
|
964
941
|
</div>
|
|
965
942
|
</div>
|
|
@@ -987,7 +964,7 @@ __GA_BOOTSTRAP__
|
|
|
987
964
|
<a href="__VERIFICATION_URL__" target="_blank" rel="noopener">Verification Evidence</a>
|
|
988
965
|
<a href="https://github.com/IgorGanapolsky/ThumbGate" target="_blank" rel="noopener">GitHub</a>
|
|
989
966
|
</div>
|
|
990
|
-
<div class="footer-copy">ThumbGate Pro for individual operators.
|
|
967
|
+
<div class="footer-copy">ThumbGate Pro for individual operators. Team stays intake-first.</div>
|
|
991
968
|
</div>
|
|
992
969
|
</footer>
|
|
993
970
|
|
|
@@ -2,40 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const MEMORY_TYPES = new Set(['episodic', 'semantic', 'procedural', 'preference', 'working']);
|
|
5
|
-
const MEMORY_SCOPES = new Set(['task', 'session', 'user', 'project', 'org']);
|
|
6
|
-
const HIGH_RISK_TERMS = new Set([
|
|
7
|
-
'billing',
|
|
8
|
-
'checkout',
|
|
9
|
-
'compliance',
|
|
10
|
-
'credential',
|
|
11
|
-
'data-loss',
|
|
12
|
-
'deploy',
|
|
13
|
-
'deployment',
|
|
14
|
-
'git',
|
|
15
|
-
'payment',
|
|
16
|
-
'production',
|
|
17
|
-
'release',
|
|
18
|
-
'secret',
|
|
19
|
-
'security',
|
|
20
|
-
'stripe',
|
|
21
|
-
'verification',
|
|
22
|
-
]);
|
|
23
|
-
const KNOWN_ENTITY_PATTERNS = [
|
|
24
|
-
['Claude Code', /\bclaude\s+code\b/i, 'agent'],
|
|
25
|
-
['Codex', /\bcodex\b/i, 'agent'],
|
|
26
|
-
['Cursor', /\bcursor\b/i, 'agent'],
|
|
27
|
-
['Gemini CLI', /\bgemini\s+cli\b/i, 'agent'],
|
|
28
|
-
['MCP', /\bmcp\b/i, 'protocol'],
|
|
29
|
-
['Stripe', /\bstripe\b/i, 'service'],
|
|
30
|
-
['GitHub', /\bgithub\b|\bgh\s+/i, 'service'],
|
|
31
|
-
['Railway', /\brailway\b/i, 'service'],
|
|
32
|
-
['Plausible', /\bplausible\b/i, 'service'],
|
|
33
|
-
['PostHog', /\bposthog\b/i, 'service'],
|
|
34
|
-
['SQLite', /\bsqlite\b|\bfts5\b/i, 'storage'],
|
|
35
|
-
['LanceDB', /\blancedb\b/i, 'storage'],
|
|
36
|
-
['Docker', /\bdocker\b/i, 'runtime'],
|
|
37
|
-
['npm', /\bnpm\b|\bnpx\b/i, 'runtime'],
|
|
38
|
-
];
|
|
39
5
|
|
|
40
6
|
function normalizeText(value) {
|
|
41
7
|
if (value === undefined || value === null) return '';
|
|
@@ -47,178 +13,6 @@ function normalizeMemoryType(value) {
|
|
|
47
13
|
return MEMORY_TYPES.has(normalized) ? normalized : 'episodic';
|
|
48
14
|
}
|
|
49
15
|
|
|
50
|
-
function tokenize(value) {
|
|
51
|
-
return normalizeText(value)
|
|
52
|
-
.toLowerCase()
|
|
53
|
-
.split(/[^a-z0-9_.:/-]+/)
|
|
54
|
-
.filter(Boolean);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function uniqueByName(entities) {
|
|
58
|
-
const seen = new Set();
|
|
59
|
-
return entities.filter((entity) => {
|
|
60
|
-
const key = normalizeText(entity.name).toLowerCase();
|
|
61
|
-
if (!key || seen.has(key)) return false;
|
|
62
|
-
seen.add(key);
|
|
63
|
-
return true;
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function collectMemoryText(memory = {}) {
|
|
68
|
-
return [
|
|
69
|
-
memory.title,
|
|
70
|
-
memory.content,
|
|
71
|
-
memory.context,
|
|
72
|
-
memory.whatWentWrong,
|
|
73
|
-
memory.whatToChange,
|
|
74
|
-
memory.whatWorked,
|
|
75
|
-
memory.domain,
|
|
76
|
-
memory.skill,
|
|
77
|
-
Array.isArray(memory.tags) ? memory.tags.join(' ') : memory.tags,
|
|
78
|
-
].filter(Boolean).join(' ');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function extractMemoryEntities(memory = {}) {
|
|
82
|
-
const text = collectMemoryText(memory);
|
|
83
|
-
const entities = [];
|
|
84
|
-
|
|
85
|
-
for (const [name, pattern, type] of KNOWN_ENTITY_PATTERNS) {
|
|
86
|
-
if (pattern.test(text)) entities.push({ name, type });
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const commandMatches = text.match(/`([^`]+)`/g) || [];
|
|
90
|
-
for (const match of commandMatches) {
|
|
91
|
-
const command = match.slice(1, -1).trim();
|
|
92
|
-
if (/^(git|npm|npx|node|gh|curl|docker|python|pytest|stripe)\b/i.test(command)) {
|
|
93
|
-
entities.push({ name: command, type: 'command' });
|
|
94
|
-
} else if (/[./-]/.test(command)) {
|
|
95
|
-
entities.push({ name: command, type: 'path' });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const pathMatches = text.match(/\b(?:[a-z0-9_-]+\/)+[a-z0-9_.-]+\b/gi) || [];
|
|
100
|
-
for (const filePath of pathMatches.slice(0, 8)) {
|
|
101
|
-
entities.push({ name: filePath, type: 'path' });
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return uniqueByName(entities).slice(0, 16);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function inferMemoryScope(memory = {}) {
|
|
108
|
-
const explicit = normalizeText(memory.scope || memory.memoryScope).toLowerCase();
|
|
109
|
-
if (MEMORY_SCOPES.has(explicit)) return explicit;
|
|
110
|
-
|
|
111
|
-
const text = collectMemoryText(memory).toLowerCase();
|
|
112
|
-
const tags = new Set(Array.isArray(memory.tags) ? memory.tags.map((tag) => normalizeText(tag).toLowerCase()) : []);
|
|
113
|
-
|
|
114
|
-
if (tags.has('preference') || /\b(prefer|style|tone|my preference|user preference)\b/.test(text)) return 'user';
|
|
115
|
-
if (tags.has('org') || tags.has('team') || /\b(enterprise|seat|team|shared|org|compliance|policy|approval)\b/.test(text)) return 'org';
|
|
116
|
-
if (tags.has('repo') || tags.has('project') || tags.has('release') || tags.has('deployment')
|
|
117
|
-
|| /\b(repo|repository|branch|ci|pull request|github|deploy|production|release|publish)\b/.test(text)) return 'project';
|
|
118
|
-
if (tags.has('session') || /\b(this session|current session|today|right now)\b/.test(text)) return 'session';
|
|
119
|
-
return 'task';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function scoreMemoryDecay(memory = {}, options = {}) {
|
|
123
|
-
const nowMs = options.now ? new Date(options.now).getTime() : Date.now();
|
|
124
|
-
const timestampMs = memory.timestamp ? new Date(memory.timestamp).getTime() : NaN;
|
|
125
|
-
const ageDays = Number.isFinite(timestampMs)
|
|
126
|
-
? Math.max(0, (nowMs - timestampMs) / (1000 * 60 * 60 * 24))
|
|
127
|
-
: null;
|
|
128
|
-
const textTokens = new Set(tokenize(collectMemoryText(memory)));
|
|
129
|
-
const tags = Array.isArray(memory.tags) ? memory.tags.map((tag) => normalizeText(tag).toLowerCase()) : [];
|
|
130
|
-
const highRisk = tags.some((tag) => HIGH_RISK_TERMS.has(tag))
|
|
131
|
-
|| [...textTokens].some((token) => HIGH_RISK_TERMS.has(token))
|
|
132
|
-
|| ['critical', 'high'].includes(normalizeText(memory.importance).toLowerCase());
|
|
133
|
-
|
|
134
|
-
if (highRisk) {
|
|
135
|
-
return {
|
|
136
|
-
state: 'sticky',
|
|
137
|
-
ageDays,
|
|
138
|
-
score: 1,
|
|
139
|
-
reason: 'high-risk memories stay retrievable until explicitly retired',
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
if (ageDays === null) {
|
|
143
|
-
return {
|
|
144
|
-
state: 'review',
|
|
145
|
-
ageDays,
|
|
146
|
-
score: 0.6,
|
|
147
|
-
reason: 'memory has no timestamp, so it needs review before durable promotion',
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
if (ageDays > 180) {
|
|
151
|
-
return {
|
|
152
|
-
state: 'archive_candidate',
|
|
153
|
-
ageDays,
|
|
154
|
-
score: 0.2,
|
|
155
|
-
reason: 'old low-risk memory should be consolidated or archived',
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (ageDays > 60) {
|
|
159
|
-
return {
|
|
160
|
-
state: 'review',
|
|
161
|
-
ageDays,
|
|
162
|
-
score: 0.55,
|
|
163
|
-
reason: 'older low-risk memory should be refreshed before it dominates recall',
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
state: 'active',
|
|
168
|
-
ageDays,
|
|
169
|
-
score: 0.85,
|
|
170
|
-
reason: 'recent memory remains eligible for recall',
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function scoreHybridMemoryMatch(query, memory = {}, options = {}) {
|
|
175
|
-
const queryTokens = new Set(tokenize(query));
|
|
176
|
-
const memoryTokens = new Set(tokenize(collectMemoryText(memory)));
|
|
177
|
-
const queryText = normalizeText(query).toLowerCase();
|
|
178
|
-
const memoryText = collectMemoryText(memory).toLowerCase();
|
|
179
|
-
const memoryEntities = extractMemoryEntities(memory);
|
|
180
|
-
const queryEntityNames = extractMemoryEntities({ content: query }).map((entity) => entity.name.toLowerCase());
|
|
181
|
-
|
|
182
|
-
let lexicalMatches = 0;
|
|
183
|
-
for (const token of queryTokens) {
|
|
184
|
-
if (memoryTokens.has(token)) lexicalMatches++;
|
|
185
|
-
}
|
|
186
|
-
const lexicalScore = queryTokens.size > 0 ? lexicalMatches / queryTokens.size : 0;
|
|
187
|
-
const phraseScore = queryText && memoryText.includes(queryText) ? 0.35 : 0;
|
|
188
|
-
const entityMatches = memoryEntities.filter((entity) => queryEntityNames.includes(entity.name.toLowerCase()));
|
|
189
|
-
const entityScore = queryEntityNames.length > 0 ? entityMatches.length / queryEntityNames.length : 0;
|
|
190
|
-
const decay = scoreMemoryDecay(memory, options);
|
|
191
|
-
const lifecycleScore = decay.state === 'archive_candidate' ? -0.15 : decay.state === 'sticky' ? 0.12 : 0;
|
|
192
|
-
const score = lexicalScore + phraseScore + (entityScore * 0.45) + lifecycleScore;
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
score: Number(Math.max(0, score).toFixed(4)),
|
|
196
|
-
lexicalScore: Number(lexicalScore.toFixed(4)),
|
|
197
|
-
entityScore: Number(entityScore.toFixed(4)),
|
|
198
|
-
matchedEntities: entityMatches,
|
|
199
|
-
decayState: decay.state,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function buildMemoryLifecycleView(memory = {}, options = {}) {
|
|
204
|
-
const scope = inferMemoryScope(memory);
|
|
205
|
-
const entities = extractMemoryEntities(memory);
|
|
206
|
-
const decay = scoreMemoryDecay(memory, options);
|
|
207
|
-
const retrieval = scoreHybridMemoryMatch(options.query || '', memory, options);
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
scope,
|
|
211
|
-
entities,
|
|
212
|
-
decay,
|
|
213
|
-
retrievalHints: {
|
|
214
|
-
hybridScore: retrieval.score,
|
|
215
|
-
lexicalScore: retrieval.lexicalScore,
|
|
216
|
-
entityScore: retrieval.entityScore,
|
|
217
|
-
matchedEntities: retrieval.matchedEntities,
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
16
|
function buildMemoryLifecyclePolicy(input = {}) {
|
|
223
17
|
return {
|
|
224
18
|
generatedAt: normalizeText(input.generatedAt) || new Date().toISOString(),
|
|
@@ -297,11 +91,6 @@ function evaluateMemoryPromotion(memory = {}, policy = buildMemoryLifecyclePolic
|
|
|
297
91
|
|
|
298
92
|
module.exports = {
|
|
299
93
|
buildMemoryLifecyclePolicy,
|
|
300
|
-
buildMemoryLifecycleView,
|
|
301
94
|
evaluateMemoryPromotion,
|
|
302
|
-
extractMemoryEntities,
|
|
303
|
-
inferMemoryScope,
|
|
304
95
|
normalizeMemoryType,
|
|
305
|
-
scoreHybridMemoryMatch,
|
|
306
|
-
scoreMemoryDecay,
|
|
307
96
|
};
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
5
|
const path = require('path');
|
|
7
6
|
const {
|
|
8
7
|
getActiveMcpProfile,
|
|
@@ -95,26 +94,9 @@ function detectRuntimeIsolation() {
|
|
|
95
94
|
};
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
function
|
|
99
|
-
try {
|
|
100
|
-
let curr = path.resolve(startDir);
|
|
101
|
-
while (true) {
|
|
102
|
-
const indicators = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md', '.mcp.json', '.git'];
|
|
103
|
-
if (indicators.some((f) => fs.existsSync(path.join(curr, f)))) {
|
|
104
|
-
return curr;
|
|
105
|
-
}
|
|
106
|
-
const parent = path.dirname(curr);
|
|
107
|
-
if (parent === curr) break;
|
|
108
|
-
curr = parent;
|
|
109
|
-
}
|
|
110
|
-
} catch (_) { /* fallback to startDir */ }
|
|
111
|
-
return startDir;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function collectBootstrapFiles(projectRoot) {
|
|
115
|
-
const effectiveRoot = projectRoot || findProjectRoot();
|
|
97
|
+
function collectBootstrapFiles(projectRoot = PROJECT_ROOT) {
|
|
116
98
|
const files = BOOTSTRAP_FILES.map((file) => {
|
|
117
|
-
const absolutePath = path.join(
|
|
99
|
+
const absolutePath = path.join(projectRoot, file.path);
|
|
118
100
|
return {
|
|
119
101
|
id: file.id,
|
|
120
102
|
path: file.path,
|
|
@@ -136,7 +118,7 @@ function collectBootstrapFiles(projectRoot) {
|
|
|
136
118
|
missingRequired,
|
|
137
119
|
recommendation: missingRequired.length === 0
|
|
138
120
|
? 'Bootstrap context is present.'
|
|
139
|
-
: `Add missing bootstrap files
|
|
121
|
+
: `Add missing bootstrap files: ${missingRequired.join(', ')}`,
|
|
140
122
|
};
|
|
141
123
|
}
|
|
142
124
|
|
|
@@ -166,141 +148,6 @@ function summarizePermissionTier(profileName = getActiveMcpProfile()) {
|
|
|
166
148
|
};
|
|
167
149
|
}
|
|
168
150
|
|
|
169
|
-
function flattenHookCommands(value, commands = []) {
|
|
170
|
-
if (!value) return commands;
|
|
171
|
-
if (typeof value === 'string') {
|
|
172
|
-
commands.push(value);
|
|
173
|
-
return commands;
|
|
174
|
-
}
|
|
175
|
-
if (Array.isArray(value)) {
|
|
176
|
-
value.forEach((item) => flattenHookCommands(item, commands));
|
|
177
|
-
return commands;
|
|
178
|
-
}
|
|
179
|
-
if (typeof value === 'object') {
|
|
180
|
-
if (typeof value.command === 'string') commands.push(value.command);
|
|
181
|
-
Object.entries(value)
|
|
182
|
-
.filter(([key]) => key !== 'command')
|
|
183
|
-
.forEach(([, item]) => flattenHookCommands(item, commands));
|
|
184
|
-
}
|
|
185
|
-
return commands;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function readJsonIfExists(filePath) {
|
|
189
|
-
try {
|
|
190
|
-
if (!fs.existsSync(filePath)) return null;
|
|
191
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
192
|
-
} catch (_) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function detectHookAutoCapture(projectRoot) {
|
|
198
|
-
const effectiveRoot = projectRoot || findProjectRoot();
|
|
199
|
-
const settingsFiles = ['.claude/settings.json', '.claude/settings.local.json']
|
|
200
|
-
.map((file) => path.join(effectiveRoot, file))
|
|
201
|
-
.filter((file) => fs.existsSync(file));
|
|
202
|
-
const commands = settingsFiles.flatMap((file) => {
|
|
203
|
-
const parsed = readJsonIfExists(file);
|
|
204
|
-
return flattenHookCommands(parsed && parsed.hooks && parsed.hooks.UserPromptSubmit);
|
|
205
|
-
});
|
|
206
|
-
const wiredCommands = commands.filter((command) => /hook-auto-capture(\.sh)?\b/.test(command));
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
ready: wiredCommands.length > 0,
|
|
210
|
-
settingsFiles: settingsFiles.map((file) => path.relative(effectiveRoot, file)),
|
|
211
|
-
wiredCommands,
|
|
212
|
-
recommendation: wiredCommands.length > 0
|
|
213
|
-
? 'UserPromptSubmit auto-capture hook is wired for typed thumbs feedback.'
|
|
214
|
-
: 'Run `npx thumbgate init --wire-hooks` so typed thumbs up/down feedback is captured automatically.',
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function probeFeedbackCapture(projectRoot) {
|
|
219
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-doctor-feedback-'));
|
|
220
|
-
const previousFeedbackDir = process.env.THUMBGATE_FEEDBACK_DIR;
|
|
221
|
-
const previousProjectDir = process.env.THUMBGATE_PROJECT_DIR;
|
|
222
|
-
const previousNoNudge = process.env.THUMBGATE_NO_NUDGE;
|
|
223
|
-
|
|
224
|
-
process.env.THUMBGATE_FEEDBACK_DIR = tempDir;
|
|
225
|
-
// Keep this probe isolated. Direct project scope intentionally overrides
|
|
226
|
-
// THUMBGATE_FEEDBACK_DIR in the runtime resolver, which is correct for real
|
|
227
|
-
// project writes but wrong for a doctor self-test.
|
|
228
|
-
delete process.env.THUMBGATE_PROJECT_DIR;
|
|
229
|
-
process.env.THUMBGATE_NO_NUDGE = '1';
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
const {
|
|
233
|
-
captureFeedback,
|
|
234
|
-
getFeedbackPaths,
|
|
235
|
-
readJSONL,
|
|
236
|
-
} = require('./feedback-loop');
|
|
237
|
-
const result = captureFeedback({
|
|
238
|
-
signal: 'down',
|
|
239
|
-
context: 'doctor feedback loop probe: prove thumbs feedback persists to durable logs',
|
|
240
|
-
whatWentWrong: 'A runtime claimed thumbs feedback worked without verifying durable capture',
|
|
241
|
-
whatToChange: 'Run thumbgate doctor or feedback-self-test before claiming the loop is wired',
|
|
242
|
-
tags: 'doctor,feedback-loop-probe',
|
|
243
|
-
});
|
|
244
|
-
const paths = getFeedbackPaths();
|
|
245
|
-
const feedbackId = result.feedbackEvent && result.feedbackEvent.id;
|
|
246
|
-
const memoryId = result.memoryRecord && result.memoryRecord.id;
|
|
247
|
-
const feedbackStored = Boolean(feedbackId && readJSONL(paths.FEEDBACK_LOG_PATH).some((row) => row.id === feedbackId));
|
|
248
|
-
const memoryStored = Boolean(memoryId && readJSONL(paths.MEMORY_LOG_PATH).some((row) => row.id === memoryId));
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
ready: Boolean(result.accepted && feedbackStored && memoryStored),
|
|
252
|
-
accepted: Boolean(result.accepted),
|
|
253
|
-
feedbackStored,
|
|
254
|
-
memoryStored,
|
|
255
|
-
feedbackId: feedbackId || null,
|
|
256
|
-
memoryId: memoryId || null,
|
|
257
|
-
mode: 'isolated-temp-store',
|
|
258
|
-
feedbackDir: paths.FEEDBACK_DIR,
|
|
259
|
-
recommendation: (result.accepted && feedbackStored && memoryStored)
|
|
260
|
-
? 'Feedback capture writes durable feedback and memory records.'
|
|
261
|
-
: 'Feedback capture did not persist both feedback and memory records; run `npx thumbgate feedback-self-test --json`.',
|
|
262
|
-
};
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return {
|
|
265
|
-
ready: false,
|
|
266
|
-
accepted: false,
|
|
267
|
-
feedbackStored: false,
|
|
268
|
-
memoryStored: false,
|
|
269
|
-
feedbackId: null,
|
|
270
|
-
memoryId: null,
|
|
271
|
-
mode: 'isolated-temp-store',
|
|
272
|
-
feedbackDir: tempDir,
|
|
273
|
-
error: error && error.message ? error.message : String(error),
|
|
274
|
-
recommendation: 'Feedback capture probe failed; run `npx thumbgate feedback-self-test --json` for details.',
|
|
275
|
-
};
|
|
276
|
-
} finally {
|
|
277
|
-
if (previousFeedbackDir === undefined) delete process.env.THUMBGATE_FEEDBACK_DIR;
|
|
278
|
-
else process.env.THUMBGATE_FEEDBACK_DIR = previousFeedbackDir;
|
|
279
|
-
if (previousProjectDir === undefined) delete process.env.THUMBGATE_PROJECT_DIR;
|
|
280
|
-
else process.env.THUMBGATE_PROJECT_DIR = previousProjectDir;
|
|
281
|
-
if (previousNoNudge === undefined) delete process.env.THUMBGATE_NO_NUDGE;
|
|
282
|
-
else process.env.THUMBGATE_NO_NUDGE = previousNoNudge;
|
|
283
|
-
try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch (_) { /* ignore cleanup failure */ }
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function assessFeedbackLoop(projectRoot) {
|
|
288
|
-
const hookAutoCapture = detectHookAutoCapture(projectRoot);
|
|
289
|
-
const captureProbe = probeFeedbackCapture(projectRoot);
|
|
290
|
-
const ready = hookAutoCapture.ready && captureProbe.ready;
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
ready,
|
|
294
|
-
automatic: hookAutoCapture.ready,
|
|
295
|
-
durableCapture: captureProbe.ready,
|
|
296
|
-
hookAutoCapture,
|
|
297
|
-
captureProbe,
|
|
298
|
-
recommendation: ready
|
|
299
|
-
? 'Typed thumbs feedback is automatically detected and durable capture is verified.'
|
|
300
|
-
: 'Install hooks and verify capture before claiming thumbs feedback improves future runs.',
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
151
|
function generateAgentReadinessReport({
|
|
305
152
|
projectRoot = PROJECT_ROOT,
|
|
306
153
|
mcpProfile = null,
|
|
@@ -308,13 +155,11 @@ function generateAgentReadinessReport({
|
|
|
308
155
|
const runtime = detectRuntimeIsolation();
|
|
309
156
|
const bootstrap = collectBootstrapFiles(projectRoot);
|
|
310
157
|
const permissions = summarizePermissionTier(mcpProfile || getActiveMcpProfile());
|
|
311
|
-
const feedbackLoop = assessFeedbackLoop(projectRoot);
|
|
312
158
|
|
|
313
159
|
const warnings = [];
|
|
314
160
|
if (!runtime.isolated) warnings.push(runtime.recommendation);
|
|
315
161
|
if (!bootstrap.ready) warnings.push(bootstrap.recommendation);
|
|
316
162
|
if (!permissions.ready) warnings.push(permissions.recommendation);
|
|
317
|
-
if (!feedbackLoop.ready) warnings.push(feedbackLoop.recommendation);
|
|
318
163
|
|
|
319
164
|
return {
|
|
320
165
|
generatedAt: new Date().toISOString(),
|
|
@@ -323,12 +168,10 @@ function generateAgentReadinessReport({
|
|
|
323
168
|
runtime,
|
|
324
169
|
bootstrap,
|
|
325
170
|
permissions,
|
|
326
|
-
feedbackLoop,
|
|
327
171
|
articleAlignment: {
|
|
328
172
|
runtimeIsolation: runtime.isolated,
|
|
329
173
|
contextConditioning: bootstrap.ready,
|
|
330
174
|
permissionEnvelope: permissions.ready,
|
|
331
|
-
feedbackLoop: feedbackLoop.ready,
|
|
332
175
|
},
|
|
333
176
|
warnings,
|
|
334
177
|
};
|
|
@@ -348,10 +191,6 @@ function reportToText(report) {
|
|
|
348
191
|
lines.push(`Permissions: ${report.permissions.profile} (${report.permissions.tier})`);
|
|
349
192
|
lines.push(` Write-capable tools: ${report.permissions.writeCapableTools.length}`);
|
|
350
193
|
lines.push(` Recommendation: ${report.permissions.recommendation}`);
|
|
351
|
-
lines.push(`Feedback loop: ${report.feedbackLoop.ready ? 'ready' : 'needs_attention'}`);
|
|
352
|
-
lines.push(` Auto-capture hook: ${report.feedbackLoop.automatic ? 'wired' : 'missing'}`);
|
|
353
|
-
lines.push(` Durable capture: ${report.feedbackLoop.durableCapture ? 'verified' : 'failed'}`);
|
|
354
|
-
lines.push(` Recommendation: ${report.feedbackLoop.recommendation}`);
|
|
355
194
|
|
|
356
195
|
if (report.warnings.length > 0) {
|
|
357
196
|
lines.push('');
|
|
@@ -370,9 +209,6 @@ module.exports = {
|
|
|
370
209
|
detectRuntimeIsolation,
|
|
371
210
|
collectBootstrapFiles,
|
|
372
211
|
summarizePermissionTier,
|
|
373
|
-
detectHookAutoCapture,
|
|
374
|
-
probeFeedbackCapture,
|
|
375
|
-
assessFeedbackLoop,
|
|
376
212
|
generateAgentReadinessReport,
|
|
377
213
|
reportToText,
|
|
378
214
|
};
|