thumbgate 1.14.1 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +6 -6
- package/.claude-plugin/plugin.json +3 -3
- package/.well-known/llms.txt +5 -5
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +60 -35
- package/adapters/chatgpt/openapi.yaml +118 -2
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +217 -84
- package/adapters/opencode/opencode.json +1 -1
- package/bench/prompt-eval-suite.json +5 -1
- package/bin/cli.js +211 -8
- package/config/enforcement.json +59 -7
- package/config/evals/agent-safety-eval.json +338 -22
- package/config/gates/default.json +33 -0
- package/config/gates/routine.json +43 -0
- package/config/github-about.json +3 -3
- package/config/mcp-allowlists.json +4 -0
- package/config/merge-quality-checks.json +2 -1
- package/config/model-candidates.json +131 -0
- package/openapi/openapi.yaml +118 -2
- package/package.json +70 -51
- package/public/blog.html +7 -7
- package/public/codex-plugin.html +13 -7
- package/public/compare.html +29 -23
- package/public/dashboard.html +105 -12
- package/public/guide.html +28 -28
- package/public/index.html +233 -97
- package/public/learn.html +87 -20
- package/public/lessons.html +26 -2
- package/public/numbers.html +271 -0
- package/public/pro.html +89 -19
- package/scripts/agent-audit-trace.js +55 -0
- package/scripts/agent-memory-lifecycle.js +96 -0
- package/scripts/agent-readiness-plan.js +118 -0
- package/scripts/agentic-data-pipeline.js +21 -1
- package/scripts/agents-sdk-sandbox-plan.js +57 -0
- package/scripts/ai-org-governance.js +98 -0
- package/scripts/ai-search-distribution.js +43 -0
- package/scripts/artifact-agent-plan.js +81 -0
- package/scripts/billing.js +27 -8
- package/scripts/cli-feedback.js +2 -1
- package/scripts/cli-schema.js +60 -5
- package/scripts/code-mode-mcp-plan.js +71 -0
- package/scripts/commercial-offer.js +1 -1
- package/scripts/context-engine.js +1 -2
- package/scripts/context-manager.js +4 -1
- package/scripts/contextfs.js +214 -32
- package/scripts/dashboard-render-spec.js +1 -1
- package/scripts/dashboard.js +275 -9
- package/scripts/decision-journal.js +13 -3
- package/scripts/document-workflow-governance.js +62 -0
- package/scripts/enterprise-agent-rollout.js +34 -0
- package/scripts/experience-replay-governance.js +69 -0
- package/scripts/export-hf-dataset.js +1 -1
- package/scripts/feedback-loop.js +141 -9
- package/scripts/feedback-to-rules.js +17 -23
- package/scripts/gates-engine.js +4 -6
- package/scripts/growth-campaigns.js +49 -0
- package/scripts/harness-selector.js +145 -1
- package/scripts/hybrid-supervisor-agent.js +64 -0
- package/scripts/inference-cache-policy.js +72 -0
- package/scripts/inference-economics.js +53 -0
- package/scripts/internal-agent-bootstrap.js +12 -2
- package/scripts/knowledge-layer-plan.js +108 -0
- package/scripts/lesson-canonical.js +181 -0
- package/scripts/lesson-db.js +71 -10
- package/scripts/lesson-inference.js +183 -44
- package/scripts/lesson-search.js +4 -1
- package/scripts/lesson-synthesis.js +23 -2
- package/scripts/llm-client.js +157 -26
- package/scripts/mailer/resend-mailer.js +112 -1
- package/scripts/mcp-transport-strategy.js +66 -0
- package/scripts/memory-store-governance.js +60 -0
- package/scripts/meta-agent-loop.js +7 -13
- package/scripts/model-access-eligibility.js +38 -0
- package/scripts/model-migration-readiness.js +55 -0
- package/scripts/native-messaging-audit.js +514 -0
- package/scripts/operational-integrity.js +96 -3
- package/scripts/otel-declarative-config.js +56 -0
- package/scripts/perplexity-client.js +1 -1
- package/scripts/post-training-governance.js +34 -0
- package/scripts/pr-manager.js +47 -7
- package/scripts/private-core-boundary.js +72 -0
- package/scripts/production-agent-readiness.js +40 -0
- package/scripts/profile-router.js +16 -1
- package/scripts/prompt-eval.js +564 -32
- package/scripts/prompt-programs.js +93 -0
- package/scripts/provider-action-normalizer.js +585 -0
- package/scripts/rule-validator.js +285 -0
- package/scripts/scaling-law-claims.js +60 -0
- package/scripts/security-scanner.js +1 -1
- package/scripts/self-distill-agent.js +7 -32
- package/scripts/seo-gsd.js +400 -43
- package/scripts/skill-rag-router.js +53 -0
- package/scripts/spec-gate.js +1 -1
- package/scripts/student-consistent-training.js +73 -0
- package/scripts/synthetic-data-provenance.js +98 -0
- package/scripts/task-context-result.js +81 -0
- package/scripts/telemetry-analytics.js +149 -0
- package/scripts/thompson-sampling.js +2 -2
- package/scripts/token-savings.js +7 -6
- package/scripts/token-tco.js +46 -0
- package/scripts/tool-registry.js +75 -3
- package/scripts/verification-loop.js +10 -1
- package/scripts/verifier-scoring.js +71 -0
- package/scripts/workflow-sentinel.js +284 -28
- package/scripts/workspace-agent-routines.js +118 -0
- package/skills/thumbgate/SKILL.md +1 -1
- package/src/api/server.js +434 -120
- package/.claude-plugin/README.md +0 -170
- package/adapters/README.md +0 -12
- package/scripts/analytics-report.js +0 -328
- package/scripts/autonomous-workflow.js +0 -377
- package/scripts/billing-setup.js +0 -109
- package/scripts/creator-campaigns.js +0 -239
- package/scripts/cross-encoder-reranker.js +0 -235
- package/scripts/daemon-manager.js +0 -108
- package/scripts/decision-trace.js +0 -354
- package/scripts/delegation-runtime.js +0 -896
- package/scripts/dispatch-brief.js +0 -159
- package/scripts/distribution-surfaces.js +0 -110
- package/scripts/feedback-history-distiller.js +0 -382
- package/scripts/funnel-analytics.js +0 -35
- package/scripts/history-distiller.js +0 -200
- package/scripts/hosted-job-launcher.js +0 -256
- package/scripts/intent-router.js +0 -392
- package/scripts/lesson-reranker.js +0 -263
- package/scripts/lesson-retrieval.js +0 -148
- package/scripts/managed-lesson-agent.js +0 -183
- package/scripts/operational-dashboard.js +0 -103
- package/scripts/operational-summary.js +0 -129
- package/scripts/operator-artifacts.js +0 -608
- package/scripts/optimize-context.js +0 -17
- package/scripts/org-dashboard.js +0 -206
- package/scripts/partner-orchestration.js +0 -146
- package/scripts/predictive-insights.js +0 -356
- package/scripts/pulse.js +0 -80
- package/scripts/reflector-agent.js +0 -221
- package/scripts/sales-pipeline.js +0 -681
- package/scripts/session-episode-store.js +0 -329
- package/scripts/session-health-sensor.js +0 -242
- package/scripts/session-report.js +0 -120
- package/scripts/swarm-coordinator.js +0 -81
- package/scripts/tool-kpi-tracker.js +0 -12
- package/scripts/webhook-delivery.js +0 -62
- package/scripts/workflow-sprint-intake.js +0 -475
- package/skills/agent-memory/SKILL.md +0 -97
- package/skills/solve-architecture-autonomy/SKILL.md +0 -17
- package/skills/solve-architecture-autonomy/tool.js +0 -33
- package/skills/thumbgate-feedback/SKILL.md +0 -49
|
@@ -17,6 +17,11 @@ const {
|
|
|
17
17
|
const { buildDockerSandboxPlan } = require('./docker-sandbox-planner');
|
|
18
18
|
const { evaluatePretool } = require('./hybrid-feedback-context');
|
|
19
19
|
const { getInterventionRecommendation } = require('./intervention-policy');
|
|
20
|
+
const {
|
|
21
|
+
buildCostControl,
|
|
22
|
+
buildWorkflowControl,
|
|
23
|
+
normalizeProviderAction,
|
|
24
|
+
} = require('./provider-action-normalizer');
|
|
20
25
|
|
|
21
26
|
const GOVERNANCE_STATE_PATH = path.join(process.env.HOME || '/tmp', '.thumbgate', 'governance-state.json');
|
|
22
27
|
const DEFAULT_PROTECTED_FILE_GLOBS = [
|
|
@@ -33,7 +38,7 @@ const DEFAULT_PROTECTED_FILE_GLOBS = [
|
|
|
33
38
|
'config/gates/**',
|
|
34
39
|
];
|
|
35
40
|
const EDIT_LIKE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit']);
|
|
36
|
-
const HIGH_RISK_BASH_PATTERN = /\b(?:git\s+(?:add|commit|push)|gh\s+pr\s+(?:create|merge)|
|
|
41
|
+
const HIGH_RISK_BASH_PATTERN = /\b(?:git\s+(?:add|commit|push)|gh\s+(?:pr\s+(?:create|merge)|workflow\s+run|release\s+create)|npm\s+publish|yarn\s+publish|pnpm\s+publish|rm\s+-rf)\b/i;
|
|
37
42
|
|
|
38
43
|
const SURFACE_RULES = [
|
|
39
44
|
{ key: 'policy', pattern: /^(?:AGENTS\.md|CLAUDE(?:\.local)?\.md|GEMINI\.md|config\/gates\/|config\/mcp-allowlists\.json|scripts\/tool-registry\.js)/ },
|
|
@@ -392,6 +397,8 @@ function scoreRisk({
|
|
|
392
397
|
blastRadius,
|
|
393
398
|
taskScopeViolation,
|
|
394
399
|
protectedSurface,
|
|
400
|
+
costControl,
|
|
401
|
+
workflowControl,
|
|
395
402
|
}) {
|
|
396
403
|
const drivers = [];
|
|
397
404
|
const commandInfo = classifyCommand(toolInput.command || '');
|
|
@@ -399,8 +406,25 @@ function scoreRisk({
|
|
|
399
406
|
if (isHighRiskAction(toolName, toolInput, affectedFiles)) {
|
|
400
407
|
addDriver(drivers, 'high_risk_action', 0.18, 'Command or edit pattern is classified as high risk.');
|
|
401
408
|
}
|
|
402
|
-
if (commandInfo.isPrCreate
|
|
403
|
-
|
|
409
|
+
if (commandInfo.isPrCreate
|
|
410
|
+
|| commandInfo.isPrMerge
|
|
411
|
+
|| commandInfo.isWorkflowRun
|
|
412
|
+
|| commandInfo.isPublish
|
|
413
|
+
|| commandInfo.isReleaseCreate
|
|
414
|
+
|| commandInfo.isTagCreate) {
|
|
415
|
+
addDriver(drivers, 'governed_command', 0.16, 'Action touches PR, workflow dispatch, release, or publish workflow state.');
|
|
416
|
+
}
|
|
417
|
+
if (commandInfo.isWorkflowRun) {
|
|
418
|
+
addDriver(
|
|
419
|
+
drivers,
|
|
420
|
+
'workflow_dispatch',
|
|
421
|
+
0.2,
|
|
422
|
+
'GitHub Actions workflow dispatch can trigger environment-specific builds or releases.',
|
|
423
|
+
{
|
|
424
|
+
workflowName: commandInfo.workflowName,
|
|
425
|
+
workflowRef: commandInfo.workflowRef,
|
|
426
|
+
}
|
|
427
|
+
);
|
|
404
428
|
}
|
|
405
429
|
if (/\bgit\s+push\b.*(?:--force|-f)\b/i.test(commandInfo.text)) {
|
|
406
430
|
addDriver(drivers, 'force_push', 0.5, 'Force push predicts destructive branch history rewrite.');
|
|
@@ -465,6 +489,56 @@ function scoreRisk({
|
|
|
465
489
|
{ blockers: integrity.blockers.map((blocker) => blocker.code) }
|
|
466
490
|
);
|
|
467
491
|
}
|
|
492
|
+
if (costControl && costControl.mode && costControl.mode !== 'allow') {
|
|
493
|
+
addDriver(
|
|
494
|
+
drivers,
|
|
495
|
+
'cost_control',
|
|
496
|
+
costControl.mode === 'block' ? 0.5 : 0.18,
|
|
497
|
+
costControl.mode === 'block'
|
|
498
|
+
? 'Estimated model usage exceeds the configured per-action budget.'
|
|
499
|
+
: 'Estimated model usage is high enough to require review.',
|
|
500
|
+
{ mode: costControl.mode, reasons: costControl.reasons }
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (workflowControl && workflowControl.workflow && workflowControl.workflow.pattern !== 'single_action') {
|
|
504
|
+
const workflow = workflowControl.workflow;
|
|
505
|
+
if (workflow.pattern === 'agent') {
|
|
506
|
+
addDriver(
|
|
507
|
+
drivers,
|
|
508
|
+
'open_ended_agent',
|
|
509
|
+
workflow.hasInspectionEvidence ? 0.14 : 0.28,
|
|
510
|
+
workflow.hasInspectionEvidence
|
|
511
|
+
? 'Open-ended agent action declares inspection evidence.'
|
|
512
|
+
: 'Open-ended agent action lacks explicit environment-inspection evidence.',
|
|
513
|
+
{ pattern: workflow.pattern, toolCount: workflow.toolCount }
|
|
514
|
+
);
|
|
515
|
+
} else if (workflow.pattern === 'parallelization') {
|
|
516
|
+
addDriver(
|
|
517
|
+
drivers,
|
|
518
|
+
'parallel_workflow',
|
|
519
|
+
workflow.branchCount > 1 ? 0.12 : 0.06,
|
|
520
|
+
'Parallel workflow fan-out increases aggregate cost and review surface.',
|
|
521
|
+
{ branchCount: workflow.branchCount }
|
|
522
|
+
);
|
|
523
|
+
} else {
|
|
524
|
+
addDriver(
|
|
525
|
+
drivers,
|
|
526
|
+
'workflow_pattern',
|
|
527
|
+
0.06,
|
|
528
|
+
`Provider action declared ${workflow.pattern} workflow pattern.`,
|
|
529
|
+
{ pattern: workflow.pattern, stepCount: workflow.stepCount, routeCount: workflow.routeCount }
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
if (workflow.requiresInspection && !workflow.hasInspectionEvidence) {
|
|
533
|
+
addDriver(
|
|
534
|
+
drivers,
|
|
535
|
+
'missing_environment_inspection',
|
|
536
|
+
workflow.pattern === 'agent' ? 0.22 : 0.14,
|
|
537
|
+
'Action has no declared way to observe whether the tool/workflow result succeeded.',
|
|
538
|
+
{ pattern: workflow.pattern }
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
468
542
|
if (memoryGuard && memoryGuard.mode && memoryGuard.mode !== 'allow') {
|
|
469
543
|
addDriver(
|
|
470
544
|
drivers,
|
|
@@ -535,8 +609,28 @@ function buildEvidence({
|
|
|
535
609
|
blastRadius,
|
|
536
610
|
taskScopeViolation,
|
|
537
611
|
protectedSurface,
|
|
612
|
+
normalizedAction,
|
|
613
|
+
costControl,
|
|
614
|
+
workflowControl,
|
|
538
615
|
}) {
|
|
539
616
|
const evidence = [];
|
|
617
|
+
if (normalizedAction && normalizedAction.provider !== 'unknown') {
|
|
618
|
+
evidence.push(
|
|
619
|
+
`Provider action normalized from ${normalizedAction.provider}: ${normalizedAction.actionType} / ${normalizedAction.intent}.`
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
if (costControl && costControl.mode && costControl.mode !== 'allow') {
|
|
623
|
+
evidence.push(`Cost control ${costControl.mode}: ${costControl.reasons.join(' ')}`);
|
|
624
|
+
}
|
|
625
|
+
if (workflowControl && workflowControl.workflow && workflowControl.workflow.pattern !== 'single_action') {
|
|
626
|
+
const workflow = workflowControl.workflow;
|
|
627
|
+
evidence.push(
|
|
628
|
+
`Workflow pattern ${workflow.pattern}: ${workflow.branchCount} branch(es), ${workflow.stepCount} step(s), ${workflow.toolCount} tool(s), inspection evidence ${workflow.hasInspectionEvidence ? 'present' : 'missing'}.`
|
|
629
|
+
);
|
|
630
|
+
if (workflowControl.mode !== 'allow') {
|
|
631
|
+
evidence.push(`Workflow control ${workflowControl.mode}: ${workflowControl.reasons.join(' ')}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
540
634
|
if (memoryGuard && memoryGuard.mode && memoryGuard.mode !== 'allow') {
|
|
541
635
|
evidence.push(`Memory guard predicted ${memoryGuard.mode}: ${memoryGuard.reason}`);
|
|
542
636
|
}
|
|
@@ -595,6 +689,23 @@ function addIntegrityRemediations(push, integrity) {
|
|
|
595
689
|
action: 'Update branch governance with prNumber or prUrl before merging.',
|
|
596
690
|
why: 'Merge actions should be tied to one explicit review surface.',
|
|
597
691
|
},
|
|
692
|
+
{
|
|
693
|
+
codes: [
|
|
694
|
+
'missing_workflow_dispatch_evidence',
|
|
695
|
+
'missing_workflow_environment',
|
|
696
|
+
'missing_workflow_name',
|
|
697
|
+
'workflow_name_mismatch',
|
|
698
|
+
'missing_workflow_ref',
|
|
699
|
+
'workflow_ref_mismatch',
|
|
700
|
+
'missing_workflow_sha',
|
|
701
|
+
'workflow_sha_mismatch',
|
|
702
|
+
'missing_workflow_job',
|
|
703
|
+
],
|
|
704
|
+
id: 'verify_workflow_dispatch',
|
|
705
|
+
title: 'Verify workflow dispatch target',
|
|
706
|
+
action: 'Set branch governance workflowDispatch with environment, workflow, ref, sha, and expected job before running gh workflow run.',
|
|
707
|
+
why: 'Environment-specific build dispatches must prove the workflow file, branch/ref, HEAD SHA, and job name before execution.',
|
|
708
|
+
},
|
|
598
709
|
{
|
|
599
710
|
codes: ['missing_release_version', 'release_version_mismatch'],
|
|
600
711
|
id: 'align_release_version',
|
|
@@ -627,6 +738,8 @@ function buildRemediations({
|
|
|
627
738
|
memoryGuard,
|
|
628
739
|
learnedPolicy,
|
|
629
740
|
executionSurface,
|
|
741
|
+
costControl,
|
|
742
|
+
workflowControl,
|
|
630
743
|
}) {
|
|
631
744
|
const remediations = [];
|
|
632
745
|
const seen = new Set();
|
|
@@ -696,6 +809,30 @@ function buildRemediations({
|
|
|
696
809
|
'Isolated execution limits host damage when a high-risk local action goes wrong.'
|
|
697
810
|
);
|
|
698
811
|
}
|
|
812
|
+
if (costControl && costControl.mode && costControl.mode !== 'allow') {
|
|
813
|
+
push(
|
|
814
|
+
'reduce_model_budget',
|
|
815
|
+
'Reduce model budget before execution',
|
|
816
|
+
'Trim context, lower max output, batch the work, or split the action before retrying.',
|
|
817
|
+
'High token or cost estimates should be reviewed before the model/tool loop continues.'
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (workflowControl && workflowControl.mode && workflowControl.mode !== 'allow') {
|
|
821
|
+
push(
|
|
822
|
+
'add_environment_inspection',
|
|
823
|
+
'Add environment inspection evidence',
|
|
824
|
+
'Declare how the agent will observe results after action: read-before-write, screenshots, API response checks, test commands, or generated-output validation.',
|
|
825
|
+
'Open-ended agents and inspection-sensitive workflows need a concrete feedback signal before they can be trusted in production.'
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
if (workflowControl?.workflow?.pattern === 'agent') {
|
|
829
|
+
push(
|
|
830
|
+
'prefer_workflow_when_possible',
|
|
831
|
+
'Prefer a predefined workflow when possible',
|
|
832
|
+
'If the task shape is known, split it into explicit workflow steps instead of letting an open-ended agent improvise.',
|
|
833
|
+
'Predefined workflows are easier to test, evaluate, budget, and audit than open-ended agents.'
|
|
834
|
+
);
|
|
835
|
+
}
|
|
699
836
|
|
|
700
837
|
return remediations;
|
|
701
838
|
}
|
|
@@ -709,6 +846,9 @@ function buildReasoning(report) {
|
|
|
709
846
|
lines.push(
|
|
710
847
|
`Decision control: ${report.decisionControl.decisionOwner} owns a ${report.decisionControl.reversibility} action via ${report.decisionControl.executionMode}.`
|
|
711
848
|
);
|
|
849
|
+
if (report.decisionControl.deliberation?.required) {
|
|
850
|
+
lines.push(`Deliberation policy: ${report.decisionControl.deliberation.mode} before final approval.`);
|
|
851
|
+
}
|
|
712
852
|
}
|
|
713
853
|
if (report.learnedPolicy && report.learnedPolicy.enabled && report.learnedPolicy.prediction) {
|
|
714
854
|
lines.push(
|
|
@@ -718,6 +858,14 @@ function buildReasoning(report) {
|
|
|
718
858
|
if (report.executionSurface?.shouldSandbox) {
|
|
719
859
|
lines.push(`Execution surface: ${report.executionSurface.summary}`);
|
|
720
860
|
}
|
|
861
|
+
if (report.costControl && report.costControl.mode !== 'allow') {
|
|
862
|
+
lines.push(`Cost control: ${report.costControl.mode} — ${report.costControl.reasons.join(' ')}`);
|
|
863
|
+
}
|
|
864
|
+
if (report.workflowControl && report.workflowControl.workflow.pattern !== 'single_action') {
|
|
865
|
+
lines.push(
|
|
866
|
+
`Workflow control: ${report.workflowControl.mode} for ${report.workflowControl.workflow.pattern} with inspection ${report.workflowControl.workflow.hasInspectionEvidence ? 'present' : 'missing'}.`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
721
869
|
for (const driver of report.drivers.slice(0, 4)) {
|
|
722
870
|
lines.push(`Driver ${driver.key} (+${driver.weight}): ${driver.reason}`);
|
|
723
871
|
}
|
|
@@ -763,6 +911,52 @@ function classifyReversibility({ command, blastRadius, integrity, protectedSurfa
|
|
|
763
911
|
return 'two_way_door';
|
|
764
912
|
}
|
|
765
913
|
|
|
914
|
+
function buildDeliberationPolicy({
|
|
915
|
+
executionMode,
|
|
916
|
+
reversibility,
|
|
917
|
+
risk,
|
|
918
|
+
hasOperationalBlockers,
|
|
919
|
+
}) {
|
|
920
|
+
const riskBand = risk && risk.band ? risk.band : 'very_low';
|
|
921
|
+
const riskScore = risk && typeof risk.score === 'number' ? risk.score : 0;
|
|
922
|
+
const needsConsistencyCheck = executionMode === 'blocked'
|
|
923
|
+
|| reversibility === 'one_way_door'
|
|
924
|
+
|| riskBand === 'very_high'
|
|
925
|
+
|| riskScore >= 0.72
|
|
926
|
+
|| hasOperationalBlockers;
|
|
927
|
+
const required = executionMode !== 'auto_execute' || riskScore >= 0.45 || hasOperationalBlockers;
|
|
928
|
+
const mode = needsConsistencyCheck
|
|
929
|
+
? 'reason_then_consistency_check'
|
|
930
|
+
: required
|
|
931
|
+
? 'reason_then_decide'
|
|
932
|
+
: 'brief_rationale';
|
|
933
|
+
|
|
934
|
+
return {
|
|
935
|
+
required,
|
|
936
|
+
mode,
|
|
937
|
+
minSentences: needsConsistencyCheck ? 4 : required ? 2 : 1,
|
|
938
|
+
summarizeOnly: true,
|
|
939
|
+
instruction: required
|
|
940
|
+
? 'Pause before answering, compare safety, reversibility, prior-failure, and evidence signals, then summarize only the decision evidence.'
|
|
941
|
+
: 'Give a brief evidence summary before approving fast-path execution.',
|
|
942
|
+
consistencyCheck: {
|
|
943
|
+
required: needsConsistencyCheck,
|
|
944
|
+
variants: needsConsistencyCheck
|
|
945
|
+
? [
|
|
946
|
+
'Re-evaluate the same action from the failure-prevention perspective.',
|
|
947
|
+
'Re-evaluate the same action from the reversibility and rollback perspective.',
|
|
948
|
+
'Re-evaluate the same action from the user-intent and evidence perspective.',
|
|
949
|
+
]
|
|
950
|
+
: [],
|
|
951
|
+
requiredAgreement: needsConsistencyCheck ? 'all_variants_same_execution_mode' : 'not_required',
|
|
952
|
+
onDisagreement: 'checkpoint_required',
|
|
953
|
+
rationale: needsConsistencyCheck
|
|
954
|
+
? 'High-risk and one-way-door actions should be stable under paraphrased evaluation before an agent proceeds.'
|
|
955
|
+
: 'Low-risk fast-path actions do not require paraphrase stability checks.',
|
|
956
|
+
},
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
766
960
|
function buildDecisionControl({
|
|
767
961
|
decision,
|
|
768
962
|
risk,
|
|
@@ -770,6 +964,8 @@ function buildDecisionControl({
|
|
|
770
964
|
blastRadius,
|
|
771
965
|
integrity,
|
|
772
966
|
protectedSurface,
|
|
967
|
+
costControl,
|
|
968
|
+
workflowControl,
|
|
773
969
|
}) {
|
|
774
970
|
const reversibility = classifyReversibility({
|
|
775
971
|
command,
|
|
@@ -778,9 +974,15 @@ function buildDecisionControl({
|
|
|
778
974
|
protectedSurface,
|
|
779
975
|
});
|
|
780
976
|
const hasOperationalBlockers = Boolean(integrity && Array.isArray(integrity.blockers) && integrity.blockers.length > 0);
|
|
977
|
+
const hasCostWarning = Boolean(costControl && costControl.mode === 'warn');
|
|
978
|
+
const hasCostBlock = Boolean(costControl && costControl.mode === 'block');
|
|
979
|
+
const hasWorkflowWarning = Boolean(workflowControl && workflowControl.mode === 'warn');
|
|
980
|
+
const hasWorkflowBlock = Boolean(workflowControl && workflowControl.mode === 'block');
|
|
781
981
|
const requiresCheckpoint = decision === 'warn'
|
|
782
|
-
|| (decision === 'allow' && (reversibility !== 'two_way_door' || hasOperationalBlockers));
|
|
982
|
+
|| (decision === 'allow' && (reversibility !== 'two_way_door' || hasOperationalBlockers || hasCostWarning || hasWorkflowWarning));
|
|
783
983
|
const executionMode = decision === 'deny'
|
|
984
|
+
|| hasCostBlock
|
|
985
|
+
|| hasWorkflowBlock
|
|
784
986
|
? 'blocked'
|
|
785
987
|
: requiresCheckpoint
|
|
786
988
|
? 'checkpoint_required'
|
|
@@ -792,12 +994,19 @@ function buildDecisionControl({
|
|
|
792
994
|
? 'shared'
|
|
793
995
|
: 'human'
|
|
794
996
|
: 'agent';
|
|
997
|
+
const deliberation = buildDeliberationPolicy({
|
|
998
|
+
executionMode,
|
|
999
|
+
reversibility,
|
|
1000
|
+
risk,
|
|
1001
|
+
hasOperationalBlockers,
|
|
1002
|
+
});
|
|
795
1003
|
|
|
796
1004
|
return {
|
|
797
1005
|
executionMode,
|
|
798
1006
|
decisionOwner,
|
|
799
1007
|
reversibility,
|
|
800
|
-
|
|
1008
|
+
deliberation,
|
|
1009
|
+
requiresHumanApproval: (executionMode === 'checkpoint_required' && decisionOwner !== 'agent') || hasCostBlock || hasWorkflowBlock,
|
|
801
1010
|
recommendedAction: executionMode === 'blocked'
|
|
802
1011
|
? 'halt'
|
|
803
1012
|
: executionMode === 'checkpoint_required'
|
|
@@ -811,8 +1020,14 @@ function buildDecisionControl({
|
|
|
811
1020
|
};
|
|
812
1021
|
}
|
|
813
1022
|
|
|
814
|
-
function chooseDecision({ riskScore, integrity, memoryGuard, learnedPolicy, blastRadius, command }) {
|
|
1023
|
+
function chooseDecision({ riskScore, integrity, memoryGuard, learnedPolicy, blastRadius, command, costControl, workflowControl }) {
|
|
815
1024
|
const hasOperationalBlockers = Boolean(integrity && Array.isArray(integrity.blockers) && integrity.blockers.length > 0);
|
|
1025
|
+
if (costControl && costControl.mode === 'block') {
|
|
1026
|
+
return 'deny';
|
|
1027
|
+
}
|
|
1028
|
+
if (workflowControl && workflowControl.mode === 'block') {
|
|
1029
|
+
return 'deny';
|
|
1030
|
+
}
|
|
816
1031
|
const destructiveBypass = /\bgit\s+push\b.*(?:--force|-f)\b/i.test(command) || /\bgh\s+pr\s+merge\b.*--admin\b/i.test(command);
|
|
817
1032
|
const learnedPrediction = learnedPolicy && learnedPolicy.enabled ? learnedPolicy.prediction : null;
|
|
818
1033
|
const learnedHardStop = Boolean(
|
|
@@ -859,52 +1074,78 @@ function chooseDecision({ riskScore, integrity, memoryGuard, learnedPolicy, blas
|
|
|
859
1074
|
if (destructiveBypass || learnedHardStop || repeatedHighBlast || (hasOperationalBlockers && riskScore >= 0.72) || riskScore >= 0.86) {
|
|
860
1075
|
return 'deny';
|
|
861
1076
|
}
|
|
862
|
-
if (riskScore >= 0.45 || (learnedWarning && riskScore >= 0.3) || (learnedRecall && riskScore >= 0.34)) {
|
|
1077
|
+
if ((workflowControl && workflowControl.mode === 'warn') || (costControl && costControl.mode === 'warn') || riskScore >= 0.45 || (learnedWarning && riskScore >= 0.3) || (learnedRecall && riskScore >= 0.34)) {
|
|
863
1078
|
return 'warn';
|
|
864
1079
|
}
|
|
865
1080
|
return 'allow';
|
|
866
1081
|
}
|
|
867
1082
|
|
|
868
1083
|
function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
1084
|
+
const normalizedAction = options.normalizedAction || normalizeProviderAction({
|
|
1085
|
+
provider: options.provider,
|
|
1086
|
+
model: options.model,
|
|
1087
|
+
toolName,
|
|
1088
|
+
toolInput,
|
|
1089
|
+
command: toolInput.command,
|
|
1090
|
+
filePath: toolInput.file_path || toolInput.filePath || toolInput.path,
|
|
1091
|
+
changedFiles: toolInput.changed_files || toolInput.changedFiles,
|
|
1092
|
+
usage: options.usage,
|
|
1093
|
+
tokenEstimate: options.tokenEstimate,
|
|
1094
|
+
costUsd: options.costUsd,
|
|
1095
|
+
});
|
|
1096
|
+
const normalizedToolName = normalizedAction.toolName || toolName;
|
|
1097
|
+
const normalizedToolInput = {
|
|
1098
|
+
...toolInput,
|
|
1099
|
+
...normalizedAction.toolInput,
|
|
1100
|
+
};
|
|
1101
|
+
if (normalizedAction.command && !normalizedToolInput.command) {
|
|
1102
|
+
normalizedToolInput.command = normalizedAction.command;
|
|
1103
|
+
}
|
|
1104
|
+
if (normalizedAction.affectedFiles.length > 0 && !normalizedToolInput.changed_files && !normalizedToolInput.changedFiles) {
|
|
1105
|
+
normalizedToolInput.changed_files = normalizedAction.affectedFiles;
|
|
1106
|
+
}
|
|
1107
|
+
const costControl = buildCostControl(normalizedAction, options.budget || toolInput.budget || {});
|
|
1108
|
+
const workflowControl = buildWorkflowControl(normalizedAction, options.workflowPolicy || toolInput.workflowPolicy || options.budget || toolInput.budget || {});
|
|
869
1109
|
const governanceState = options.governanceState || loadGovernanceState();
|
|
870
|
-
const repoPath = options.repoPath ||
|
|
1110
|
+
const repoPath = options.repoPath || normalizedToolInput.repoPath || normalizedToolInput.cwd || process.cwd();
|
|
871
1111
|
const repoRoot = resolveRepoRoot(repoPath) || null;
|
|
872
1112
|
const affectedFiles = Array.isArray(options.affectedFiles)
|
|
873
1113
|
? options.affectedFiles.map((filePath) => normalizePosix(filePath)).filter(Boolean)
|
|
874
|
-
: collectAffectedFiles(
|
|
875
|
-
const highRiskAction = isHighRiskAction(
|
|
1114
|
+
: collectAffectedFiles(normalizedToolName, normalizedToolInput, repoRoot);
|
|
1115
|
+
const highRiskAction = isHighRiskAction(normalizedToolName, normalizedToolInput, affectedFiles);
|
|
876
1116
|
const baseBranch = options.baseBranch
|
|
877
1117
|
|| (governanceState.branchGovernance && governanceState.branchGovernance.baseBranch)
|
|
878
|
-
||
|
|
1118
|
+
|| normalizedToolInput.baseBranch
|
|
879
1119
|
|| DEFAULT_BASE_BRANCH;
|
|
880
1120
|
const integrity = evaluateOperationalIntegrity({
|
|
881
1121
|
repoPath,
|
|
882
1122
|
baseBranch,
|
|
883
|
-
command:
|
|
1123
|
+
command: normalizedToolInput.command,
|
|
884
1124
|
changedFiles: affectedFiles,
|
|
1125
|
+
headSha: options.headSha || toolInput.headSha,
|
|
885
1126
|
requirePrForReleaseSensitive: options.requirePrForReleaseSensitive === true,
|
|
886
1127
|
requireVersionNotBehindBase: options.requireVersionNotBehindBase === true,
|
|
887
1128
|
branchGovernance: governanceState.branchGovernance,
|
|
888
1129
|
});
|
|
889
1130
|
const taskScopeViolation = buildTaskScopeViolation(governanceState.taskScope, affectedFiles);
|
|
890
1131
|
const protectedSurface = buildProtectedSurface(governanceState, affectedFiles);
|
|
891
|
-
const protectedSurfaceForRisk = isProtectedApprovalRelevant(
|
|
1132
|
+
const protectedSurfaceForRisk = isProtectedApprovalRelevant(normalizedToolName, normalizedToolInput)
|
|
892
1133
|
? protectedSurface
|
|
893
1134
|
: {
|
|
894
1135
|
...protectedSurface,
|
|
895
1136
|
protectedFiles: [],
|
|
896
1137
|
unapprovedProtectedFiles: [],
|
|
897
1138
|
};
|
|
898
|
-
const rawMemoryGuard = options.memoryGuard || evaluatePretool(
|
|
899
|
-
toolName,
|
|
900
|
-
command:
|
|
901
|
-
filePath:
|
|
1139
|
+
const rawMemoryGuard = options.memoryGuard || evaluatePretool(normalizedToolName, JSON.stringify({
|
|
1140
|
+
toolName: normalizedToolName,
|
|
1141
|
+
command: normalizedToolInput.command || null,
|
|
1142
|
+
filePath: normalizedToolInput.file_path || normalizedToolInput.filePath || normalizedToolInput.path || null,
|
|
902
1143
|
affectedFiles,
|
|
903
1144
|
}), options.feedbackOptions || {});
|
|
904
1145
|
const memoryGuard = normalizeMemoryGuardForSentinel(rawMemoryGuard, highRiskAction);
|
|
905
1146
|
const learnedPolicy = getInterventionRecommendation({
|
|
906
|
-
toolName,
|
|
907
|
-
command:
|
|
1147
|
+
toolName: normalizedToolName,
|
|
1148
|
+
command: normalizedToolInput.command || '',
|
|
908
1149
|
affectedFiles,
|
|
909
1150
|
integrity,
|
|
910
1151
|
memoryGuard,
|
|
@@ -922,8 +1163,8 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
922
1163
|
protectedSurface: protectedSurfaceForRisk,
|
|
923
1164
|
});
|
|
924
1165
|
const risk = scoreRisk({
|
|
925
|
-
toolName,
|
|
926
|
-
toolInput,
|
|
1166
|
+
toolName: normalizedToolName,
|
|
1167
|
+
toolInput: normalizedToolInput,
|
|
927
1168
|
affectedFiles,
|
|
928
1169
|
integrity,
|
|
929
1170
|
memoryGuard,
|
|
@@ -931,17 +1172,19 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
931
1172
|
blastRadius,
|
|
932
1173
|
taskScopeViolation,
|
|
933
1174
|
protectedSurface: protectedSurfaceForRisk,
|
|
1175
|
+
costControl,
|
|
1176
|
+
workflowControl,
|
|
934
1177
|
});
|
|
935
1178
|
const executionSurface = buildDockerSandboxPlan({
|
|
936
|
-
toolName,
|
|
937
|
-
actionType: getSentinelActionType(
|
|
938
|
-
command:
|
|
1179
|
+
toolName: normalizedToolName,
|
|
1180
|
+
actionType: getSentinelActionType(normalizedToolName),
|
|
1181
|
+
command: normalizedToolInput.command,
|
|
939
1182
|
repoPath,
|
|
940
1183
|
affectedFiles,
|
|
941
1184
|
riskBand: risk.band,
|
|
942
1185
|
riskScore: risk.score,
|
|
943
1186
|
requiresNetwork: Boolean(
|
|
944
|
-
/\b(?:curl|wget|gh\s+pr|git\s+push|npm\s+publish|yarn\s+publish|pnpm\s+publish)\b/i.test(
|
|
1187
|
+
/\b(?:curl|wget|gh\s+pr|git\s+push|npm\s+publish|yarn\s+publish|pnpm\s+publish)\b/i.test(normalizedToolInput.command || '')
|
|
945
1188
|
),
|
|
946
1189
|
});
|
|
947
1190
|
const decision = chooseDecision({
|
|
@@ -953,7 +1196,9 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
953
1196
|
...blastRadius,
|
|
954
1197
|
unapprovedProtectedFiles: protectedSurfaceForRisk.unapprovedProtectedFiles.length,
|
|
955
1198
|
},
|
|
956
|
-
command:
|
|
1199
|
+
command: normalizedToolInput.command || '',
|
|
1200
|
+
costControl,
|
|
1201
|
+
workflowControl,
|
|
957
1202
|
});
|
|
958
1203
|
const evidence = buildEvidence({
|
|
959
1204
|
integrity,
|
|
@@ -962,6 +1207,9 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
962
1207
|
blastRadius,
|
|
963
1208
|
taskScopeViolation,
|
|
964
1209
|
protectedSurface: protectedSurfaceForRisk,
|
|
1210
|
+
normalizedAction,
|
|
1211
|
+
costControl,
|
|
1212
|
+
workflowControl,
|
|
965
1213
|
});
|
|
966
1214
|
const remediations = buildRemediations({
|
|
967
1215
|
integrity,
|
|
@@ -971,6 +1219,8 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
971
1219
|
memoryGuard,
|
|
972
1220
|
learnedPolicy,
|
|
973
1221
|
executionSurface,
|
|
1222
|
+
costControl,
|
|
1223
|
+
workflowControl,
|
|
974
1224
|
});
|
|
975
1225
|
const summary = decision === 'allow'
|
|
976
1226
|
? 'No predictive workflow blockers detected.'
|
|
@@ -979,7 +1229,10 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
979
1229
|
: 'Predicted workflow failure before execution.';
|
|
980
1230
|
const report = {
|
|
981
1231
|
sentinelVersion: 'workflow-sentinel-v2',
|
|
982
|
-
toolName,
|
|
1232
|
+
toolName: normalizedToolName,
|
|
1233
|
+
normalizedAction,
|
|
1234
|
+
costControl,
|
|
1235
|
+
workflowControl,
|
|
983
1236
|
decision,
|
|
984
1237
|
riskScore: risk.score,
|
|
985
1238
|
band: risk.band,
|
|
@@ -1005,13 +1258,15 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
1005
1258
|
report.decisionControl = buildDecisionControl({
|
|
1006
1259
|
decision,
|
|
1007
1260
|
risk,
|
|
1008
|
-
command:
|
|
1261
|
+
command: normalizedToolInput.command || '',
|
|
1009
1262
|
blastRadius: {
|
|
1010
1263
|
...blastRadius,
|
|
1011
1264
|
unapprovedProtectedFiles: protectedSurfaceForRisk.unapprovedProtectedFiles.length,
|
|
1012
1265
|
},
|
|
1013
1266
|
integrity,
|
|
1014
1267
|
protectedSurface: protectedSurfaceForRisk,
|
|
1268
|
+
costControl,
|
|
1269
|
+
workflowControl,
|
|
1015
1270
|
});
|
|
1016
1271
|
report.reasoning = buildReasoning(report);
|
|
1017
1272
|
return report;
|
|
@@ -1019,6 +1274,7 @@ function evaluateWorkflowSentinel(toolName, toolInput = {}, options = {}) {
|
|
|
1019
1274
|
|
|
1020
1275
|
module.exports = {
|
|
1021
1276
|
buildDecisionControl,
|
|
1277
|
+
buildDeliberationPolicy,
|
|
1022
1278
|
DEFAULT_PROTECTED_FILE_GLOBS,
|
|
1023
1279
|
buildBlastRadius,
|
|
1024
1280
|
buildEvidence,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const ROUTINE_TYPES = Object.freeze({
|
|
5
|
+
security_audit: {
|
|
6
|
+
schedule: 'daily',
|
|
7
|
+
trigger: 'schedule',
|
|
8
|
+
approval: 'pull_request_required',
|
|
9
|
+
checks: ['npm test', 'npm run test:coverage', 'npm run self-heal:check'],
|
|
10
|
+
},
|
|
11
|
+
post_merge_hygiene: {
|
|
12
|
+
schedule: 'after_pr_merge',
|
|
13
|
+
trigger: 'webhook',
|
|
14
|
+
approval: 'pull_request_required',
|
|
15
|
+
checks: ['npm run self-heal:check', 'npm run prove:automation'],
|
|
16
|
+
},
|
|
17
|
+
data_table_refresh: {
|
|
18
|
+
schedule: 'daily',
|
|
19
|
+
trigger: 'schedule',
|
|
20
|
+
approval: 'human_approval_for_schema_changes',
|
|
21
|
+
checks: ['npm run test:data-pipeline', 'npm run self-heal:check'],
|
|
22
|
+
},
|
|
23
|
+
portfolio_research: {
|
|
24
|
+
schedule: 'hourly_market_window',
|
|
25
|
+
trigger: 'schedule',
|
|
26
|
+
approval: 'always_ask_before_trade_or_publish',
|
|
27
|
+
checks: ['risk_limit_check', 'source_reconciliation', 'decision_journal_append'],
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function normalizeText(value) {
|
|
32
|
+
if (value === undefined || value === null) return '';
|
|
33
|
+
return String(value).trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeConnector(connector) {
|
|
37
|
+
if (typeof connector === 'string') {
|
|
38
|
+
return {
|
|
39
|
+
name: connector,
|
|
40
|
+
mode: 'read',
|
|
41
|
+
auth: 'user_or_service_account',
|
|
42
|
+
approval: 'always_ask_for_write',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
name: normalizeText(connector?.name) || 'custom',
|
|
47
|
+
mode: normalizeText(connector?.mode) || 'read',
|
|
48
|
+
auth: normalizeText(connector?.auth) || 'user_or_service_account',
|
|
49
|
+
approval: normalizeText(connector?.approval) || 'always_ask_for_write',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildWorkspaceAgentRoutine(input = {}) {
|
|
54
|
+
const type = normalizeText(input.type) || 'post_merge_hygiene';
|
|
55
|
+
const defaults = ROUTINE_TYPES[type] || ROUTINE_TYPES.post_merge_hygiene;
|
|
56
|
+
const name = normalizeText(input.name) || `ThumbGate ${type.replaceAll('_', ' ')}`;
|
|
57
|
+
const connectors = Array.isArray(input.connectors) ? input.connectors.map(normalizeConnector) : [];
|
|
58
|
+
const checks = Array.isArray(input.checks) && input.checks.length > 0 ? input.checks : defaults.checks;
|
|
59
|
+
const routine = {
|
|
60
|
+
name,
|
|
61
|
+
type,
|
|
62
|
+
repository: normalizeText(input.repository) || 'IgorGanapolsky/ThumbGate',
|
|
63
|
+
trigger: normalizeText(input.trigger) || defaults.trigger,
|
|
64
|
+
schedule: normalizeText(input.schedule) || defaults.schedule,
|
|
65
|
+
modelPolicy: normalizeText(input.modelPolicy) || 'use_best_available_for_audit_then_small_model_for_summaries',
|
|
66
|
+
connectors,
|
|
67
|
+
permissionMode: normalizeText(input.permissionMode) || 'least_privilege',
|
|
68
|
+
approvalPolicy: normalizeText(input.approvalPolicy) || defaults.approval,
|
|
69
|
+
branchPolicy: 'feature_branch_only',
|
|
70
|
+
checks,
|
|
71
|
+
evidenceRequired: [
|
|
72
|
+
'branch_name',
|
|
73
|
+
'commit_sha',
|
|
74
|
+
'test_output',
|
|
75
|
+
'decision_journal_entry',
|
|
76
|
+
'pull_request_url_or_no_change_reason',
|
|
77
|
+
],
|
|
78
|
+
blockedActions: [
|
|
79
|
+
'direct_main_write',
|
|
80
|
+
'secret_persistence',
|
|
81
|
+
'credentialed_write_without_approval',
|
|
82
|
+
'schema_migration_without_approval',
|
|
83
|
+
'trade_or_portfolio_action_without_risk_limits',
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
routine,
|
|
89
|
+
prompt: [
|
|
90
|
+
`Run ${routine.name} for ${routine.repository}.`,
|
|
91
|
+
'Use ThumbGate before every risky tool action.',
|
|
92
|
+
'Create a feature branch for any code change.',
|
|
93
|
+
`Run checks: ${routine.checks.join(', ')}.`,
|
|
94
|
+
'Append evidence to the decision journal.',
|
|
95
|
+
'Open a pull request only when changes and proof exist.',
|
|
96
|
+
'Stop and report blockers instead of bypassing checks.',
|
|
97
|
+
].join(' '),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildWorkspaceAgentDirectory(input = {}) {
|
|
102
|
+
const repository = normalizeText(input.repository) || 'IgorGanapolsky/ThumbGate';
|
|
103
|
+
return {
|
|
104
|
+
generatedAt: normalizeText(input.generatedAt) || new Date().toISOString(),
|
|
105
|
+
directory: [
|
|
106
|
+
buildWorkspaceAgentRoutine({ type: 'security_audit', repository }).routine,
|
|
107
|
+
buildWorkspaceAgentRoutine({ type: 'post_merge_hygiene', repository }).routine,
|
|
108
|
+
buildWorkspaceAgentRoutine({ type: 'data_table_refresh', repository }).routine,
|
|
109
|
+
buildWorkspaceAgentRoutine({ type: 'portfolio_research', repository }).routine,
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
ROUTINE_TYPES,
|
|
116
|
+
buildWorkspaceAgentDirectory,
|
|
117
|
+
buildWorkspaceAgentRoutine,
|
|
118
|
+
};
|
|
@@ -94,7 +94,7 @@ Bounded retrieval of relevant feedback history for the current task. The agent g
|
|
|
94
94
|
| Seats | 1 | 1 | Per-seat |
|
|
95
95
|
| Price | $0 | $19/mo | $49/seat/mo |
|
|
96
96
|
|
|
97
|
-
Start a 7-day free trial of Pro: <https://
|
|
97
|
+
Start a 7-day free trial of Pro: <https://thumbgate-production.up.railway.app/go/pro?utm_source=skill>
|
|
98
98
|
|
|
99
99
|
## Compatibility
|
|
100
100
|
|