thumbgate 1.29.1 → 1.29.2

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 (35) hide show
  1. package/.claude/commands/dashboard.md +11 -1
  2. package/.claude/commands/thumbgate-dashboard.md +23 -8
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.well-known/mcp/server-card.json +1 -1
  5. package/README.md +61 -1
  6. package/adapters/claude/.mcp.json +2 -2
  7. package/adapters/forge/forge.yaml +3 -3
  8. package/adapters/mcp/server-stdio.js +88 -2
  9. package/adapters/opencode/opencode.json +1 -1
  10. package/commands/dashboard.md +11 -1
  11. package/commands/thumbgate-dashboard.md +23 -8
  12. package/config/agent-outcome-monitor-thresholds.json +63 -0
  13. package/config/evals/agent-outcomes-baseline.json +17 -0
  14. package/config/evals/agent-outcomes-golden.json +412 -0
  15. package/config/evals/prompt-eval-baseline.json +23 -0
  16. package/config/schemas/task-outcome-receipt.schema.json +296 -0
  17. package/openapi/openapi.yaml +235 -0
  18. package/package.json +19 -6
  19. package/public/index.html +4 -2
  20. package/public/numbers.html +2 -2
  21. package/scripts/agent-outcome-eval.js +130 -0
  22. package/scripts/agent-outcome-monitor.js +261 -0
  23. package/scripts/agent-reasoning-traces.js +8 -9
  24. package/scripts/async-job-runner.js +107 -13
  25. package/scripts/durability/step.js +121 -12
  26. package/scripts/gates-engine.js +431 -18
  27. package/scripts/human-escalation.js +265 -0
  28. package/scripts/hybrid-feedback-context.js +93 -50
  29. package/scripts/judge-reward-function.js +30 -18
  30. package/scripts/prompt-eval.js +81 -4
  31. package/scripts/schedule-manager.js +249 -0
  32. package/scripts/task-outcomes.js +425 -0
  33. package/scripts/tool-contract-validator.js +287 -59
  34. package/scripts/tool-registry.js +143 -0
  35. package/src/api/server.js +127 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.29.1",
3
+ "version": "1.29.2",
4
4
  "description": "ThumbGate Pre-Action Checks self-improve from ranked lessons and repeated failures, hard-block detected secret leaks, and block matches in strict mode.",
5
5
  "homepage": "https://thumbgate.ai",
6
6
  "repository": {
@@ -22,6 +22,8 @@
22
22
  "scripts/activation-quickstart.js",
23
23
  "scripts/activation-tracker.js",
24
24
  "scripts/agent-audit-trace.js",
25
+ "scripts/agent-outcome-eval.js",
26
+ "scripts/agent-outcome-monitor.js",
25
27
  "scripts/agent-design-governance.js",
26
28
  "scripts/agent-memory-lifecycle.js",
27
29
  "scripts/agent-readiness-plan.js",
@@ -121,6 +123,7 @@
121
123
  "scripts/hook-stop-anti-claim.js",
122
124
  "scripts/hook-thumbgate-cache-updater.js",
123
125
  "scripts/hosted-config.js",
126
+ "scripts/human-escalation.js",
124
127
  "scripts/hybrid-feedback-context.js",
125
128
  "scripts/imperative-detector.js",
126
129
  "scripts/hybrid-supervisor-agent.js",
@@ -202,6 +205,7 @@
202
205
  "scripts/rubric-engine.js",
203
206
  "scripts/rule-validator.js",
204
207
  "scripts/sales-pipeline.js",
208
+ "scripts/schedule-manager.js",
205
209
  "scripts/secret-scanner.js",
206
210
  "scripts/secret-fixture-tokens.js",
207
211
  "scripts/secret-redaction.js",
@@ -244,6 +248,7 @@
244
248
  "scripts/token-tco.js",
245
249
  "scripts/tool-contract-validator.js",
246
250
  "scripts/tool-registry.js",
251
+ "scripts/task-outcomes.js",
247
252
  "scripts/upstream-contribution-engine.js",
248
253
  "scripts/user-profile.js",
249
254
  "scripts/validate-workflow-contract.js",
@@ -322,6 +327,9 @@
322
327
  "src/"
323
328
  ],
324
329
  "scripts": {
330
+ "canary:snapshot": "node scripts/gate-decision-canary.js --snapshot",
331
+ "canary:check": "node scripts/gate-decision-canary.js --check",
332
+ "test:canary": "node --test tests/gate-decision-canary.test.js",
325
333
  "postinstall": "node bin/postinstall.js || true",
326
334
  "start": "node src/api/server.js",
327
335
  "numbers:generate": "node scripts/generate-numbers-page.js",
@@ -393,6 +401,8 @@
393
401
  "judge:reward:prompt": "node scripts/judge-reward-function.js prompt",
394
402
  "llm:behavior": "node scripts/llm-behavior-monitor.js report",
395
403
  "llm:behavior:json": "node scripts/llm-behavior-monitor.js json",
404
+ "eval:agent-outcomes": "node scripts/agent-outcome-eval.js",
405
+ "monitor:agent-outcomes": "node scripts/agent-outcome-monitor.js",
396
406
  "prompt:plan": "node scripts/prompting-operating-system.js plan",
397
407
  "prompt:plan:json": "node scripts/prompting-operating-system.js json",
398
408
  "credentials:plan": "node scripts/single-use-credential-gate.js plan",
@@ -417,7 +427,7 @@
417
427
  "social:prospect:bluesky": "node scripts/social-bluesky-prospecting.js",
418
428
  "social:prospect:bluesky:dry": "node scripts/social-bluesky-prospecting.js --dry-run",
419
429
  "social:reply-publish:bluesky:dry": "node scripts/social-reply-monitor-bluesky.js --publish-approved --dry-run",
420
- "test": "npm run test:python && npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:grafana-revenue-evidence && npm run test:billing && npm run test:billing-setup && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:post-everywhere-channels && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:release-window && npm run test:check-congruence && npm run test:tool-registry && npm run test:repeat-metric && npm run test:noop-detect && npm run test:action-receipts && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:memory-scope-readiness && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operational-dashboard && npm run test:operator-artifacts && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:mcp-tool-annotations && npm run test:mcp-oauth && npm run test:mcp-oauth-flow && npm run test:plan-gate && npm run test:ai-component-inventory && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:entitlement && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:imperative-detector && npm run test:audit-pr-bot-contamination && npm run test:stripe-bootstrap-saas-catalog && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:statusline-cache-aggregate && npm run test:public-repo-hygiene && npm run test:no-internal-orchestration-leaks && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:lesson-semantic-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:predictive-credible-range && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:lesson-canonical && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:xss-checkout-escape && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-archived-product-guard && npm run test:postgres-guard && npm run test:checkout-bot-guard && npm run test:checkout-pro-confirmation-gate && npm run test:pricing-page-telemetry && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:telemetry-tracked-link-slug && npm run test:prompt-eval && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:numbers-page && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:competitive-positioning-marketing && npm run test:medium-weekly && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth && npm run test:bayes-optimal-gate && npm run test:swarm-coordinator && npm run test:session-report && npm run test:agent-reasoning-traces && npm run test:judge-reward && npm run test:llm-behavior-monitor && npm run test:prompting-os && npm run test:single-use-credential-gate && npm run test:structured-prompt-driven && npm run test:require-evidence-gate && npm run test:rule-validator && npm run test:bluesky-atproto && npm run test:social-reply-monitor-bluesky && npm run test:bluesky-delete-replies && npm run test:architect-kit-memory-bridge && npm run test:sonar-review-hotspots && npm run test:actionable-remediations && npm run test:gemini-embedding-policy && npm run test:agent-design-governance && npm run test:public-core-boundary && npm run test:hook-stop-verify-deploy && npm run test:hook-stop-anti-claim && npm run test:stop-hook-json-contract && npm run test:plausible-server-events && npm run test:activation-tracker && npm run test:activation-onboarding && npm run test:unified-revenue-rollup && npm run test:conversion-rate-stats && npm run test:external-customer-audit && npm run test:telemetry-export && npm run test:stripe-checkout-diagnostic && npm run test:stripe-business-identity-probe && npm run test:revenue-observability-doctor && npm run test:jsonl-window && npm run test:observability-env && npm run test:public-bundle-ratchet && npm run test:pack-runtime-integrity && npm run test:hook-self-protection && npm run test:self-protect-enforcement && npm run test:never-bypass-branch-protection && npm run test:stripe-payment-link-update && npm run test:ci-cd-hygiene-audit && npm run test:verify-marketing-pages-deployed && npm run test:install-email-capture && npm run test:install-shim && npm run test:hook-runtime-subcommands && npm run test:implementation-notes && npm run test:daily-block-cap && npm run test:free-to-paid-conversion-units && npm run test:metrics-real-endpoint && npm run test:cli-trial-and-help && npm run test:cost-cli && npm run test:silent-failure-cluster && npm run test:proof:truth && node --test tests/adaptive-reliability.test.js && npm run test:mcp-oauth-reviewer && npm run test:dfcx-gate && npm run test:dfcx-gate-server && npm run test:vertex-scorer && npm run test:dashboard-chat && npm run test:gitar-integration && npm run test:secret-redaction && npm run test:discoverable-skills && npm run test:discoverable-skill-skills && npm run test:sync-telemetry && npm run test:leak-scanner && npm run test:team-sync && npm run test:eval-rag && npm run test:async-eval-observability && npm run test:letta-adapter && npm run test:policy-engine-adapter && npm run test:tool-contract-validator && npm run test:check-update && npm run test:hermes-gate && npm run test:memory-provider-enforcement-bridge && npm run test:publisher-credential-guards && npm run test:reddit-browser-notification-watch && npm run test:payment-rails && npm run test:service-checkout-price-integrity && npm run test:cursor-marketplace-doctor && npm run test:plugin-hooks-manifest && npm run test:okara-money-promo-automation",
430
+ "test": "npm run test:python && npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:grafana-revenue-evidence && npm run test:billing && npm run test:billing-setup && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:canary && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:post-everywhere-channels && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:release-window && npm run test:check-congruence && npm run test:tool-registry && npm run test:repeat-metric && npm run test:noop-detect && npm run test:action-receipts && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:memory-scope-readiness && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operational-dashboard && npm run test:operator-artifacts && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:mcp-tool-annotations && npm run test:mcp-oauth && npm run test:mcp-oauth-flow && npm run test:plan-gate && npm run test:ai-component-inventory && npm run test:verification-evidence && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:entitlement && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:imperative-detector && npm run test:audit-pr-bot-contamination && npm run test:stripe-bootstrap-saas-catalog && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:statusline-cache-aggregate && npm run test:public-repo-hygiene && npm run test:no-internal-orchestration-leaks && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:lesson-semantic-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:predictive-credible-range && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:lesson-canonical && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:xss-checkout-escape && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-archived-product-guard && npm run test:postgres-guard && npm run test:checkout-bot-guard && npm run test:checkout-pro-confirmation-gate && npm run test:pricing-page-telemetry && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:telemetry-tracked-link-slug && npm run test:prompt-eval && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:numbers-page && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:competitive-positioning-marketing && npm run test:medium-weekly && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth && npm run test:bayes-optimal-gate && npm run test:swarm-coordinator && npm run test:session-report && npm run test:agent-reasoning-traces && npm run test:judge-reward && npm run test:llm-behavior-monitor && npm run test:prompting-os && npm run test:single-use-credential-gate && npm run test:structured-prompt-driven && npm run test:require-evidence-gate && npm run test:rule-validator && npm run test:bluesky-atproto && npm run test:social-reply-monitor-bluesky && npm run test:bluesky-delete-replies && npm run test:architect-kit-memory-bridge && npm run test:sonar-review-hotspots && npm run test:actionable-remediations && npm run test:gemini-embedding-policy && npm run test:agent-design-governance && npm run test:public-core-boundary && npm run test:hook-stop-verify-deploy && npm run test:hook-stop-anti-claim && npm run test:stop-hook-json-contract && npm run test:plausible-server-events && npm run test:activation-tracker && npm run test:activation-onboarding && npm run test:unified-revenue-rollup && npm run test:conversion-rate-stats && npm run test:external-customer-audit && npm run test:telemetry-export && npm run test:stripe-checkout-diagnostic && npm run test:stripe-business-identity-probe && npm run test:revenue-observability-doctor && npm run test:jsonl-window && npm run test:observability-env && npm run test:public-bundle-ratchet && npm run test:pack-runtime-integrity && npm run test:hook-self-protection && npm run test:self-protect-enforcement && npm run test:never-bypass-branch-protection && npm run test:stripe-payment-link-update && npm run test:ci-cd-hygiene-audit && npm run test:verify-marketing-pages-deployed && npm run test:install-email-capture && npm run test:install-shim && npm run test:hook-runtime-subcommands && npm run test:implementation-notes && npm run test:daily-block-cap && npm run test:free-to-paid-conversion-units && npm run test:metrics-real-endpoint && npm run test:cli-trial-and-help && npm run test:cost-cli && npm run test:silent-failure-cluster && npm run test:proof:truth && node --test tests/adaptive-reliability.test.js && npm run test:mcp-oauth-reviewer && npm run test:dfcx-gate && npm run test:dfcx-gate-server && npm run test:vertex-scorer && npm run test:dashboard-chat && npm run test:gitar-integration && npm run test:secret-redaction && npm run test:discoverable-skills && npm run test:discoverable-skill-skills && npm run test:sync-telemetry && npm run test:leak-scanner && npm run test:team-sync && npm run test:eval-rag && npm run test:async-eval-observability && npm run test:letta-adapter && npm run test:policy-engine-adapter && npm run test:tool-contract-validator && npm run test:check-update && npm run test:hermes-gate && npm run test:memory-provider-enforcement-bridge && npm run test:publisher-credential-guards && npm run test:reddit-browser-notification-watch && npm run test:payment-rails && npm run test:service-checkout-price-integrity && npm run test:cursor-marketplace-doctor && npm run test:plugin-hooks-manifest && npm run test:okara-money-promo-automation",
421
431
  "test:python": "python3 -m pytest tests/*.py",
422
432
  "test:check-update": "node --test tests/check-update.test.js",
423
433
  "test:hook-stop-verify-deploy": "node --test tests/hook-stop-verify-deploy.test.js",
@@ -462,7 +472,8 @@
462
472
  "test:spec-gate": "node --test tests/spec-gate.test.js",
463
473
  "test:dashboard-insights": "node --test tests/dashboard-insights.test.js",
464
474
  "test:telemetry-tracked-link-slug": "node --test tests/telemetry-tracked-link-slug.test.js",
465
- "test:prompt-eval": "node --test tests/prompt-eval.test.js",
475
+ "test:prompt-eval": "node --test tests/prompt-eval.test.js tests/verified-agent-outcomes.test.js tests/task-outcome-api.test.js",
476
+ "test:verified-agent-outcomes": "node --test tests/verified-agent-outcomes.test.js tests/task-outcome-api.test.js",
466
477
  "eval:feedback": "node scripts/prompt-eval.js --from-feedback",
467
478
  "eval:feedback-quality": "python3 scripts/feedback_quality_eval.py",
468
479
  "eval:classifier": "python3 scripts/eval_gate_classifier.py",
@@ -513,6 +524,7 @@
513
524
  "test:cloudflare-sandbox": "node --test tests/cloudflare-dynamic-sandbox.test.js tests/cloudflare-sandbox-api.test.js",
514
525
  "test:mcp-config": "node --test tests/mcp-config.test.js tests/mcp-capability-exposure.test.js",
515
526
  "test:mcp-tool-annotations": "node --test tests/mcp-tool-annotations.test.js",
527
+ "test:mcp-policy": "node --test tests/mcp-policy.test.js",
516
528
  "test:mcp-oauth": "node --test tests/mcp-oauth.test.js",
517
529
  "test:mcp-oauth-flow": "node --test tests/mcp-oauth-flow.test.js",
518
530
  "test:plan-gate": "node --test tests/plan-gate.test.js",
@@ -548,7 +560,7 @@
548
560
  "test:proof": "node --test tests/prove-adapters.test.js tests/prove-attribution.test.js tests/prove-cloudflare-sandbox.test.js tests/prove-data-quality.test.js tests/prove-intelligence.test.js tests/prove-lancedb.test.js tests/prove-loop-closure.test.js tests/prove-training-export.test.js tests/prove-local-intelligence.test.js tests/prove-workflow-contract.test.js tests/prove-autoresearch.test.js tests/prove-claim-verification.test.js tests/prove-data-pipeline.test.js tests/prove-evolution.test.js tests/prove-harnesses.test.js tests/prove-packaged-runtime.test.js tests/prove-runtime.test.js tests/prove-seo-gsd.test.js tests/prove-settings.test.js tests/prove-xmemory.test.js && node --test tests/prove-automation.test.js",
549
561
  "test:e2e": "node --test tests/e2e-pipeline.test.js tests/e2e-product-flows.test.js tests/e2e-coverage-contract.test.js tests/interaction-model-e2e.test.js",
550
562
  "test:rlaif": "node --test tests/rlaif-self-audit.test.js tests/dpo-optimizer.test.js tests/meta-policy.test.js tests/agent-reward-model.test.js",
551
- "test:attribution": "node --test tests/feedback-attribution.test.js tests/hybrid-feedback-context.test.js",
563
+ "test:attribution": "node --test tests/feedback-attribution.test.js tests/hybrid-feedback-context.test.js tests/memory-guard-matching.test.js",
552
564
  "test:quality": "node --test tests/validate-feedback.test.js tests/feedback-quality-eval-python.test.js tests/eval-gate-classifier.test.js",
553
565
  "test:intelligence": "node --test tests/intelligence.test.js",
554
566
  "test:training-export": "node --test tests/training-export.test.js tests/databricks-export.test.js",
@@ -567,7 +579,7 @@
567
579
  "test:ops": "node --test tests/qa-scenario-planner.test.js tests/adk-consolidator.test.js tests/anthropic-partner-strategy.test.js tests/auto-promote-gates.test.js tests/auto-wire-hooks.test.js tests/claude-skill.test.js tests/codegraph-context.test.js tests/commercial-signals.test.js tests/decision-journal.test.js tests/delegation-runtime.test.js tests/disagreement-mining.test.js tests/failure-diagnostics.test.js tests/gate-stats.test.js tests/gates-engine-upgrade-cta.test.js tests/git-hook-installer.test.js tests/github-billing.test.js tests/intervention-policy.test.js tests/markdown-escape.test.js tests/mcp-tools-gates.test.js tests/native-messaging-audit.test.js tests/project-bayes-e2e.test.js tests/project-bayes.test.js tests/rate-limiter.test.js tests/schedule-manager.test.js tests/session-handoff.test.js tests/skill-generator.test.js tests/smart-learning.test.js tests/spike-and-sink.test.js tests/stripe-revenue.test.js tests/stripe-webhook-route.test.js tests/stripe-webhook-rotation.test.js tests/train-from-feedback.test.js tests/workflow-hardening-sprint.test.js tests/workflow-sentinel.test.js tests/test-suite-parity.test.js tests/a2ui-engine.test.js tests/webhook-delivery.test.js tests/auto-context-packs.test.js tests/daily-block-cap.test.js tests/auto-promote-regression-gate.test.js",
568
580
  "test:session-analyzer": "node --test tests/session-analyzer.test.js",
569
581
  "test:tessl": "node --test tests/tessl-export.test.js",
570
- "test:gates": "node --test tests/gate-templates.test.js tests/gates-engine.test.js tests/claim-verification.test.js tests/secret-scanner.test.js tests/secret-fixture-safety.test.js tests/prompt-guard.test.js tests/audit-trail.test.js tests/profile-router.test.js tests/workflow-sentinel.test.js tests/docker-sandbox-planner.test.js tests/mcp-tools-suggest-fix.test.js tests/deny-network-egress-pattern.test.js",
582
+ "test:gates": "node --test --test-concurrency=1 tests/gate-templates.test.js tests/gates-engine.test.js tests/claim-verification.test.js tests/secret-scanner.test.js tests/secret-fixture-safety.test.js tests/prompt-guard.test.js tests/audit-trail.test.js tests/profile-router.test.js tests/workflow-sentinel.test.js tests/docker-sandbox-planner.test.js tests/mcp-tools-suggest-fix.test.js tests/deny-network-egress-pattern.test.js tests/git-pathspec-scope.test.js tests/git-global-option-bypass.test.js tests/gate-evasion-matrix.test.js",
571
583
  "test:budget": "node --test tests/budget-guard.test.js tests/budget-enforcer.test.js tests/tokenomics-cost-guard.test.js tests/hook-no-budget-lockout.test.js",
572
584
  "test:workers": "npm --prefix workers ci && npm --prefix workers test",
573
585
  "test:evoskill": "node --test tests/evoskill.test.js",
@@ -745,6 +757,7 @@
745
757
  "gate-eval:ci": "node scripts/gate-eval.js run",
746
758
  "test:ai-engineering-stack-guardrails": "node --test tests/ai-engineering-stack-guardrails.test.js",
747
759
  "test:ai-component-inventory": "node --test tests/ai-component-inventory.test.js",
760
+ "test:verification-evidence": "node --test tests/verification-evidence.test.js",
748
761
  "test:interaction-model": "node --test tests/interaction-model.test.js tests/interaction-model-e2e.test.js",
749
762
  "aws-blocks:guardrails": "node scripts/aws-blocks-guardrails.js",
750
763
  "test:aws-blocks-guardrails": "node --test tests/aws-blocks-guardrails.test.js",
@@ -895,7 +908,7 @@
895
908
  "@google/genai": {
896
909
  "protobufjs": "7.6.5"
897
910
  },
898
- "brace-expansion": "5.0.7",
911
+ "brace-expansion": "5.0.8",
899
912
  "onnxruntime-web": {
900
913
  "protobufjs": "7.6.5"
901
914
  },
package/public/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <meta name="generator" content="ThumbGate">
7
7
  <meta name="author" content="Igor Ganapolsky">
8
- <meta name="thumbgate-version" content="1.29.1">
8
+ <meta name="thumbgate-version" content="1.29.2">
9
9
  __GOOGLE_SITE_VERIFICATION_META__
10
10
  <link rel="icon" type="image/png" href="/thumbgate-icon.png">
11
11
  <link rel="canonical" href="__APP_ORIGIN__/">
@@ -595,6 +595,7 @@
595
595
  </div>
596
596
  <a class="proof-link" href="#proof">See a strict-mode deny example ↓</a>
597
597
  <p class="install-hint"><code>npx thumbgate init</code> free local evaluate · first hard gate usually minutes after install · <a href="/pricing">how we stack up</a></p>
598
+ <p class="install-hint" id="thumbgate-dashboard-command" style="margin-top:10px;">Dashboard: <code>npx thumbgate dashboard --open</code> · <code>/thumbgate-dashboard</code> · bin <code>thumbgate-dashboard</code> if global · <a href="/dashboard#insights">demo</a></p>
598
599
  </div>
599
600
  <div id="offers" class="offer-stack">
600
601
  <form id="buy" class="checkout-card" action="/go/diagnostic-pay" method="POST" data-primary-checkout style="order:-1;border-color:rgba(0,110,82,.35);box-shadow:0 18px 50px rgba(0,110,82,.12);">
@@ -811,11 +812,12 @@ next decision recorded before execution</pre>
811
812
 
812
813
  <footer>
813
814
  <div class="shell footer-inner">
814
- <span>ThumbGate · MIT License · npm v1.29.1</span>
815
+ <span>ThumbGate · MIT License · npm v1.29.2</span>
815
816
  <div class="footer-links">
816
817
  <a href="https://github.com/IgorGanapolsky/ThumbGate" target="_blank" rel="noopener">GitHub</a>
817
818
  <a href="/guide">Technical setup</a>
818
819
  <a href="/dashboard#insights">Live enforcement dashboard</a>
820
+ <a href="#thumbgate-dashboard-command"><code>thumbgate-dashboard</code> command</a>
819
821
  <a href="/guides/developer-machine-supply-chain-guardrails">Developer Machine Supply Chain Guardrails — npm, PyPI, Docker, and CLI compromise paths</a>
820
822
  <a href="/learn">Browse the guide library</a>
821
823
  <a href="/llm-context.md">Machine-readable context</a>
@@ -25,7 +25,7 @@
25
25
  "alternateName": "thumbgate",
26
26
  "applicationCategory": "DeveloperApplication",
27
27
  "operatingSystem": "Cross-platform, Node.js >=18.18.0",
28
- "softwareVersion": "1.29.1",
28
+ "softwareVersion": "1.29.2",
29
29
  "url": "https://thumbgate.ai/numbers",
30
30
  "dateModified": "2026-05-07",
31
31
  "creator": {
@@ -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.29.1</div>
205
+ <div class="freshness">Updated: 2026-05-07 · Version 1.29.2</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>
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const {
7
+ TASK_OUTCOME_SCHEMA,
8
+ calculateTaskOutcomeMetrics,
9
+ normalizeTaskOutcome,
10
+ } = require('./task-outcomes');
11
+ const { validateToolContract } = require('./tool-contract-validator');
12
+
13
+ const ROOT = path.join(__dirname, '..');
14
+ const DEFAULT_SUITE = path.join(ROOT, 'config', 'evals', 'agent-outcomes-golden.json');
15
+ const DEFAULT_BASELINE = path.join(ROOT, 'config', 'evals', 'agent-outcomes-baseline.json');
16
+
17
+ function runCase(testCase = {}) {
18
+ const receipt = normalizeTaskOutcome(testCase.receipt, new Date('2026-07-26T00:00:00.000Z'));
19
+ const validation = validateToolContract(TASK_OUTCOME_SCHEMA, receipt);
20
+ const expectedReasons = [...(testCase.expected?.reasons || [])]
21
+ .sort((left, right) => left.localeCompare(right));
22
+ const actualReasons = [...(receipt.workingReasons || [])]
23
+ .sort((left, right) => left.localeCompare(right));
24
+ const checks = [
25
+ {
26
+ id: 'schema_valid',
27
+ pass: validation.valid,
28
+ detail: validation.valid ? 'receipt schema valid' : validation.errors.join('; '),
29
+ },
30
+ {
31
+ id: 'working_verdict',
32
+ pass: receipt.working === testCase.expected?.working,
33
+ detail: `expected ${testCase.expected?.working}, got ${receipt.working}`,
34
+ },
35
+ {
36
+ id: 'reason_codes',
37
+ pass: JSON.stringify(actualReasons) === JSON.stringify(expectedReasons),
38
+ detail: `expected [${expectedReasons.join(', ')}], got [${actualReasons.join(', ')}]`,
39
+ },
40
+ ];
41
+ const passed = checks.every((check) => check.pass);
42
+ return {
43
+ id: testCase.id,
44
+ status: passed ? 'pass' : 'fail',
45
+ score: Math.round((checks.filter((check) => check.pass).length / checks.length) * 100),
46
+ severity: testCase.severity,
47
+ split: testCase.split,
48
+ checks,
49
+ receipt,
50
+ };
51
+ }
52
+
53
+ function runAgentOutcomeEval(options = {}) {
54
+ const suitePath = path.resolve(options.suitePath || DEFAULT_SUITE);
55
+ const suite = JSON.parse(fs.readFileSync(suitePath, 'utf8'));
56
+ if (!Array.isArray(suite.cases) || suite.cases.length === 0) {
57
+ throw new Error('Agent outcome suite must contain non-empty cases');
58
+ }
59
+ const results = suite.cases.map(runCase);
60
+ const passed = results.filter((result) => result.status === 'pass').length;
61
+ const score = Math.round((results.reduce((sum, result) => sum + result.score, 0) / results.length));
62
+ const minimumCases = Number(suite.successCriteria?.minimumCases || 1);
63
+ const minimumScore = Number(suite.successCriteria?.minAggregateScore || 100);
64
+ const baselinePath = path.resolve(options.baselinePath || DEFAULT_BASELINE);
65
+ const baseline = fs.existsSync(baselinePath)
66
+ ? JSON.parse(fs.readFileSync(baselinePath, 'utf8'))
67
+ : null;
68
+ const regressions = compareBaseline(results, baseline);
69
+ const report = {
70
+ suite: suite.name,
71
+ generatedAt: new Date().toISOString(),
72
+ evidenceStatus: results.length >= minimumCases ? 'measured' : 'insufficient_evidence',
73
+ total: results.length,
74
+ passed,
75
+ failed: results.length - passed,
76
+ score,
77
+ minimumScore,
78
+ minimumCases,
79
+ regressions,
80
+ pass: results.length >= minimumCases
81
+ && score >= minimumScore
82
+ && (!suite.successCriteria?.requireNoRegressions || regressions.length === 0),
83
+ metrics: calculateTaskOutcomeMetrics(results.map((result) => result.receipt)),
84
+ results,
85
+ };
86
+ return report;
87
+ }
88
+
89
+ function compareBaseline(results, baseline) {
90
+ if (!baseline) return [];
91
+ const current = new Map(results.map((result) => [result.id, result]));
92
+ const regressions = [];
93
+ for (const previous of baseline.results || []) {
94
+ const result = current.get(previous.id);
95
+ if (!result) {
96
+ regressions.push({ id: previous.id, reason: 'missing_case' });
97
+ } else if (result.score < previous.score || (previous.status === 'pass' && result.status !== 'pass')) {
98
+ regressions.push({
99
+ id: previous.id,
100
+ reason: 'score_or_status_regression',
101
+ baselineScore: previous.score,
102
+ currentScore: result.score,
103
+ });
104
+ }
105
+ }
106
+ return regressions;
107
+ }
108
+
109
+ function isCliInvocation() {
110
+ return Boolean(process.argv[1]) && path.resolve(process.argv[1]) === __filename;
111
+ }
112
+
113
+ if (isCliInvocation()) {
114
+ const suitePath = process.argv.find((arg) => arg.startsWith('--suite='))?.slice(8);
115
+ const baselinePath = process.argv.find((arg) => arg.startsWith('--baseline='))?.slice(11);
116
+ const outputPath = process.argv.find((arg) => arg.startsWith('--output='))?.slice(9);
117
+ const report = runAgentOutcomeEval({ suitePath, baselinePath });
118
+ if (outputPath) {
119
+ fs.mkdirSync(path.dirname(path.resolve(outputPath)), { recursive: true });
120
+ fs.writeFileSync(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
121
+ }
122
+ console.log(JSON.stringify(report, null, 2));
123
+ process.exitCode = report.pass ? 0 : 1;
124
+ }
125
+
126
+ module.exports = {
127
+ compareBaseline,
128
+ runAgentOutcomeEval,
129
+ runCase,
130
+ };
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const { calculateTaskOutcomeMetrics, readTaskOutcomes } = require('./task-outcomes');
8
+
9
+ const DEFAULT_THRESHOLDS = path.join(__dirname, '..', 'config', 'agent-outcome-monitor-thresholds.json');
10
+ const DEFAULT_HOSTED_ORIGIN = 'https://thumbgate-production.up.railway.app';
11
+ const DEFAULT_MONITOR_PATH = '/v1/task-outcomes/monitor';
12
+ const DEFAULT_SCHEDULE_ID = 'thumbgate-agent-outcome-monitor';
13
+
14
+ function monitorTaskOutcomes(outcomes = [], options = {}) {
15
+ const metrics = calculateTaskOutcomeMetrics(outcomes);
16
+ const thresholds = options.thresholds || JSON.parse(fs.readFileSync(
17
+ path.resolve(options.thresholdsPath || DEFAULT_THRESHOLDS),
18
+ 'utf8',
19
+ ));
20
+ const minimumSamples = Number(thresholds.minimumSamples || 1);
21
+ if (metrics.sampleSize < minimumSamples) {
22
+ return {
23
+ generatedAt: new Date().toISOString(),
24
+ verdict: 'insufficient_evidence',
25
+ sampleSize: metrics.sampleSize,
26
+ minimumSamples,
27
+ alerts: [{
28
+ id: 'minimum-samples',
29
+ severity: 'block',
30
+ message: `Need ${minimumSamples} task outcomes; observed ${metrics.sampleSize}.`,
31
+ }],
32
+ metrics,
33
+ };
34
+ }
35
+
36
+ const values = flattenMetricValues(metrics);
37
+ const alerts = [];
38
+ for (const [id, rule] of Object.entries(thresholds)) {
39
+ if (id === 'minimumSamples') continue;
40
+ const actual = values[id];
41
+ if (actual === null || actual === undefined) {
42
+ alerts.push({
43
+ id: `${id}-missing`,
44
+ severity: rule.severity || 'warn',
45
+ message: `${id} has no measured denominator.`,
46
+ });
47
+ continue;
48
+ }
49
+ if (!passesRule(actual, rule)) {
50
+ alerts.push({
51
+ id: `${id}-threshold`,
52
+ severity: rule.severity || 'warn',
53
+ actual,
54
+ expected: `${rule.operator} ${rule.value}`,
55
+ message: `${id}=${actual} violates ${rule.operator} ${rule.value}.`,
56
+ });
57
+ }
58
+ }
59
+ let verdict = 'healthy';
60
+ if (alerts.some((alert) => alert.severity === 'block')) {
61
+ verdict = 'blocked';
62
+ } else if (alerts.length > 0) {
63
+ verdict = 'watch';
64
+ }
65
+ return {
66
+ generatedAt: new Date().toISOString(),
67
+ verdict,
68
+ sampleSize: metrics.sampleSize,
69
+ minimumSamples,
70
+ alerts,
71
+ metrics,
72
+ };
73
+ }
74
+
75
+ function flattenMetricValues(metrics) {
76
+ return {
77
+ workingRate: metrics.task.workingRate,
78
+ verifiedCompletionRate: metrics.task.verifiedCompletionRate,
79
+ evidenceBackedCompletionRate: metrics.task.evidenceBackedCompletionRate,
80
+ unsupportedClaimRate: metrics.task.unsupportedClaimRate,
81
+ toolContractAccuracy: metrics.tools.contractAccuracy,
82
+ executionSuccessRate: metrics.tools.executionSuccessRate,
83
+ duplicateSideEffectRate: metrics.tools.duplicateSideEffectRate,
84
+ unsafeEscapeRate: metrics.safety.unsafeEscapeRate,
85
+ policyViolationRate: metrics.safety.policyViolationRate,
86
+ safeFalseBlockRate: metrics.safety.safeFalseBlockRate,
87
+ correctEscalationRate: metrics.escalation.correctEscalationRate,
88
+ latencyP95Ms: metrics.efficiency.latencyP95Ms,
89
+ };
90
+ }
91
+
92
+ function passesRule(actual, rule) {
93
+ if (rule.operator === 'gte') return actual >= Number(rule.value);
94
+ if (rule.operator === 'lte') return actual <= Number(rule.value);
95
+ if (rule.operator === 'gt') return actual > Number(rule.value);
96
+ if (rule.operator === 'lt') return actual < Number(rule.value);
97
+ if (rule.operator === 'eq') return actual === Number(rule.value);
98
+ throw new Error(`Unsupported threshold operator '${rule.operator}'`);
99
+ }
100
+
101
+ function isCliInvocation() {
102
+ return Boolean(process.argv[1]) && path.resolve(process.argv[1]) === __filename;
103
+ }
104
+
105
+ function parseArgs(argv = []) {
106
+ return {
107
+ hosted: argv.includes('--hosted'),
108
+ installSchedule: argv.includes('--install-schedule'),
109
+ inputPath: valueForArg(argv, '--input='),
110
+ thresholdsPath: valueForArg(argv, '--thresholds='),
111
+ outputPath: valueForArg(argv, '--output='),
112
+ baseUrl: valueForArg(argv, '--base-url='),
113
+ workingDirectory: valueForArg(argv, '--working-directory='),
114
+ };
115
+ }
116
+
117
+ function valueForArg(argv, prefix) {
118
+ const value = argv.find((arg) => arg.startsWith(prefix));
119
+ return value ? value.slice(prefix.length) : undefined;
120
+ }
121
+
122
+ async function fetchHostedMonitor(options = {}) {
123
+ const env = options.env || process.env;
124
+ const { loadObservabilityEnv } = require('./observability-env');
125
+ loadObservabilityEnv({
126
+ env,
127
+ operatorPath: options.operatorPath,
128
+ observabilityPath: options.observabilityPath,
129
+ applyStripeManagedFiles: false,
130
+ });
131
+
132
+ const apiKey = String(env.THUMBGATE_OPERATOR_KEY || env.THUMBGATE_API_KEY || '').trim();
133
+ if (!apiKey) {
134
+ return {
135
+ generatedAt: new Date().toISOString(),
136
+ verdict: 'not_configured',
137
+ source: 'hosted',
138
+ reason: 'operator_authentication_unavailable',
139
+ };
140
+ }
141
+
142
+ const baseUrl = options.baseUrl
143
+ || env.THUMBGATE_BILLING_API_BASE_URL
144
+ || DEFAULT_HOSTED_ORIGIN;
145
+ const url = new URL(DEFAULT_MONITOR_PATH, baseUrl);
146
+ const controller = new AbortController();
147
+ const timeout = setTimeout(() => controller.abort(), Number(options.timeoutMs || 15000));
148
+ try {
149
+ const response = await (options.fetchImpl || globalThis.fetch)(url, {
150
+ method: 'GET',
151
+ headers: {
152
+ authorization: `Bearer ${apiKey}`,
153
+ accept: 'application/json',
154
+ },
155
+ signal: controller.signal,
156
+ });
157
+ if (!response.ok) {
158
+ return {
159
+ generatedAt: new Date().toISOString(),
160
+ verdict: 'unavailable',
161
+ source: 'hosted',
162
+ httpStatus: response.status,
163
+ reason: 'hosted_monitor_http_error',
164
+ };
165
+ }
166
+ const report = await response.json();
167
+ return {
168
+ ...report,
169
+ source: 'hosted',
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ generatedAt: new Date().toISOString(),
174
+ verdict: 'unavailable',
175
+ source: 'hosted',
176
+ reason: error?.name === 'AbortError' ? 'hosted_monitor_timeout' : 'hosted_monitor_request_failed',
177
+ };
178
+ } finally {
179
+ clearTimeout(timeout);
180
+ }
181
+ }
182
+
183
+ function buildAgentOutcomeMonitorSchedule(options = {}) {
184
+ const workingDirectory = path.resolve(options.workingDirectory || process.cwd());
185
+ const outputPath = path.resolve(
186
+ options.outputPath
187
+ || path.join(os.homedir(), '.thumbgate', 'reports', 'agent-outcome-monitor.json'),
188
+ );
189
+ const args = [
190
+ __filename,
191
+ '--hosted',
192
+ `--output=${outputPath}`,
193
+ ];
194
+ if (options.baseUrl) args.push(`--base-url=${options.baseUrl}`);
195
+ const command = [
196
+ 'const { spawnSync } = require(\'node:child_process\');',
197
+ `const result = spawnSync(process.execPath, ${JSON.stringify(args)}, {`,
198
+ ` cwd: ${JSON.stringify(workingDirectory)},`,
199
+ ' env: process.env,',
200
+ ' stdio: \'inherit\',',
201
+ '});',
202
+ 'if (result.error) throw result.error;',
203
+ 'process.exit(typeof result.status === \'number\' ? result.status : 1);',
204
+ ].join(' ');
205
+ return {
206
+ id: DEFAULT_SCHEDULE_ID,
207
+ name: 'ThumbGate Agent Outcome Monitor',
208
+ description: 'Checks hosted task outcomes daily against fail-closed production thresholds.',
209
+ schedule: options.schedule || 'daily 10:17',
210
+ command,
211
+ workingDirectory,
212
+ };
213
+ }
214
+
215
+ function installAgentOutcomeMonitorSchedule(options, manager) {
216
+ const scheduleManager = manager || require('./schedule-manager');
217
+ return scheduleManager.createSchedule(buildAgentOutcomeMonitorSchedule(options || {}));
218
+ }
219
+
220
+ async function main(argv = process.argv.slice(2)) {
221
+ const options = parseArgs(argv);
222
+ if (options.installSchedule) {
223
+ const result = installAgentOutcomeMonitorSchedule(options);
224
+ console.log(JSON.stringify(result, null, 2));
225
+ process.exitCode = result.success ? 0 : 1;
226
+ return result;
227
+ }
228
+
229
+ const report = options.hosted
230
+ ? await fetchHostedMonitor(options)
231
+ : monitorTaskOutcomes(
232
+ readTaskOutcomes({ inputPath: options.inputPath }),
233
+ { thresholdsPath: options.thresholdsPath },
234
+ );
235
+ if (options.outputPath) {
236
+ const outputPath = path.resolve(options.outputPath);
237
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
238
+ fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
239
+ }
240
+ console.log(JSON.stringify(report, null, 2));
241
+ process.exitCode = report.verdict === 'healthy' ? 0 : 1;
242
+ return report;
243
+ }
244
+
245
+ if (isCliInvocation()) {
246
+ main().catch((error) => {
247
+ console.error(`Agent outcome monitor failed: ${error.message}`);
248
+ process.exitCode = 1;
249
+ });
250
+ }
251
+
252
+ module.exports = {
253
+ buildAgentOutcomeMonitorSchedule,
254
+ fetchHostedMonitor,
255
+ flattenMetricValues,
256
+ installAgentOutcomeMonitorSchedule,
257
+ main,
258
+ monitorTaskOutcomes,
259
+ parseArgs,
260
+ passesRule,
261
+ };