thumbgate 1.15.0 → 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 +59 -35
- package/adapters/chatgpt/openapi.yaml +118 -2
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +210 -84
- package/adapters/opencode/opencode.json +1 -1
- package/bench/prompt-eval-suite.json +5 -1
- package/bin/cli.js +157 -8
- package/config/evals/agent-safety-eval.json +338 -22
- package/config/gates/routine.json +43 -0
- package/config/github-about.json +3 -3
- package/config/model-candidates.json +131 -0
- package/openapi/openapi.yaml +118 -2
- package/package.json +55 -48
- package/public/blog.html +7 -7
- package/public/codex-plugin.html +6 -6
- package/public/compare.html +29 -23
- package/public/dashboard.html +82 -10
- package/public/guide.html +28 -28
- package/public/index.html +216 -98
- package/public/learn.html +50 -22
- package/public/lessons.html +1 -1
- package/public/numbers.html +17 -17
- package/public/pro.html +82 -18
- 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-schema.js +18 -2
- package/scripts/code-mode-mcp-plan.js +71 -0
- package/scripts/context-engine.js +1 -2
- package/scripts/context-manager.js +4 -1
- 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 +92 -4
- 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 +16 -4
- 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-inference.js +183 -44
- package/scripts/lesson-search.js +4 -1
- 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/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/private-core-boundary.js +72 -0
- package/scripts/production-agent-readiness.js +40 -0
- package/scripts/prompt-eval.js +564 -32
- package/scripts/prompt-programs.js +93 -0
- package/scripts/provider-action-normalizer.js +585 -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 +232 -55
- 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 +63 -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/src/api/server.js +381 -120
- 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
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* History Distiller — Self-Healing Brain for ThumbGate (Pro feature).
|
|
6
|
-
*
|
|
7
|
-
* When a user gives thumbs-down, this module:
|
|
8
|
-
* 1. Takes the last N conversation messages (chatHistory)
|
|
9
|
-
* 2. Identifies the failed tool call and its context
|
|
10
|
-
* 3. Auto-proposes a lesson: "I noticed X. I've recorded a rule to NEVER do X. Correct?"
|
|
11
|
-
* 4. Creates a prevention rule if the user confirms
|
|
12
|
-
*
|
|
13
|
-
* This closes the gap from "manual guardrail" to "self-healing brain."
|
|
14
|
-
* Strategic value: justifies Pro $19/mo subscription via outcome-based intelligence.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const { createLesson, inferFromSurroundingMessages } = require('./lesson-inference');
|
|
18
|
-
const contextfs = require('./contextfs');
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Chat History Analysis
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const ANTI_PATTERNS = [
|
|
26
|
-
{ pattern: /\b(?:tailwind|tw-)\b/i, label: 'Tailwind CSS', ruleTemplate: 'NEVER use Tailwind CSS in this project' },
|
|
27
|
-
{ pattern: /(?:force[- ]?push|push\s*--force|--force)\b/i, label: 'force push', ruleTemplate: 'NEVER force-push to any branch' },
|
|
28
|
-
{ pattern: /\b(?:rm\s+-rf|delete\s+all)\b/i, label: 'destructive deletion', ruleTemplate: 'NEVER run destructive delete commands without confirmation' },
|
|
29
|
-
{ pattern: /\bskip\s*(?:test|ci|check)/i, label: 'skipping tests', ruleTemplate: 'NEVER skip tests or CI checks' },
|
|
30
|
-
{ pattern: /\b(?:mock(?:ed|ing)?|stub(?:bed|bing)?)\b.*\b(?:database|db)\b/i, label: 'mocking database', ruleTemplate: 'NEVER mock the database — use real test instances' },
|
|
31
|
-
{ pattern: /\b(?:hardcod|hard[- ]cod)/i, label: 'hardcoded values', ruleTemplate: 'NEVER hardcode secrets, URLs, or configuration values' },
|
|
32
|
-
{ pattern: /\b(?:console\.log|print\s+debug)\b/i, label: 'debug logging', ruleTemplate: 'NEVER leave debug console.log/print statements in production code' },
|
|
33
|
-
{ pattern: /\b(?:any\b.*type|:\s*any\b)/i, label: 'TypeScript any', ruleTemplate: 'NEVER use the `any` type — use proper type annotations' },
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Analyze chat history to find the correction pattern.
|
|
38
|
-
* Looks for: user corrected the agent about something → agent did it again → user gave thumbs down.
|
|
39
|
-
*
|
|
40
|
-
* @param {Array} chatHistory - Array of {role, content} messages, most recent last
|
|
41
|
-
* @param {Object} failedToolCall - The tool call that triggered the thumbs-down
|
|
42
|
-
* @returns {{ correction, antiPattern, proposedRule, confidence, evidence }}
|
|
43
|
-
*/
|
|
44
|
-
function analyzeChatHistory(chatHistory, failedToolCall = null) {
|
|
45
|
-
const messages = Array.isArray(chatHistory) ? chatHistory : [];
|
|
46
|
-
if (messages.length === 0) return { correction: null, antiPattern: null, proposedRule: null, confidence: 0, evidence: [] };
|
|
47
|
-
|
|
48
|
-
const evidence = [];
|
|
49
|
-
let bestAntiPattern = null;
|
|
50
|
-
let userCorrection = null;
|
|
51
|
-
|
|
52
|
-
// Scan for user corrections (messages where user said "don't", "stop", "no", "never", "wrong")
|
|
53
|
-
const correctionPatterns = [
|
|
54
|
-
/\b(?:don'?t|do not|stop|never|wrong|no,?\s+(?:that|not|I said))\b/i,
|
|
55
|
-
/\b(?:I (?:told|said|asked) you|I already|we don'?t)\b/i,
|
|
56
|
-
/\b(?:should(?:n'?t| not)|must not|not supposed to)\b/i,
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
for (const msg of messages) {
|
|
60
|
-
if (msg.role !== 'user') continue;
|
|
61
|
-
const text = msg.content || msg.text || '';
|
|
62
|
-
for (const cp of correctionPatterns) {
|
|
63
|
-
if (cp.test(text)) {
|
|
64
|
-
userCorrection = text.slice(0, 200);
|
|
65
|
-
evidence.push({ type: 'user_correction', text: userCorrection });
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Scan assistant messages for anti-patterns
|
|
72
|
-
const assistantMessages = messages.filter((m) => m.role === 'assistant').map((m) => m.content || m.text || '');
|
|
73
|
-
const allAssistantText = assistantMessages.join('\n');
|
|
74
|
-
|
|
75
|
-
// Also check the failed tool call
|
|
76
|
-
const toolCallText = failedToolCall
|
|
77
|
-
? `${failedToolCall.tool || ''} ${failedToolCall.input || ''} ${failedToolCall.output || ''}`
|
|
78
|
-
: '';
|
|
79
|
-
const combinedText = `${allAssistantText}\n${toolCallText}`;
|
|
80
|
-
|
|
81
|
-
for (const ap of ANTI_PATTERNS) {
|
|
82
|
-
if (ap.pattern.test(combinedText)) {
|
|
83
|
-
bestAntiPattern = ap;
|
|
84
|
-
evidence.push({ type: 'anti_pattern', label: ap.label });
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Build proposed rule
|
|
90
|
-
let proposedRule = null;
|
|
91
|
-
let confidence = 0;
|
|
92
|
-
|
|
93
|
-
if (bestAntiPattern && userCorrection) {
|
|
94
|
-
proposedRule = bestAntiPattern.ruleTemplate;
|
|
95
|
-
confidence = 90;
|
|
96
|
-
} else if (bestAntiPattern) {
|
|
97
|
-
proposedRule = bestAntiPattern.ruleTemplate;
|
|
98
|
-
confidence = 60;
|
|
99
|
-
} else if (userCorrection) {
|
|
100
|
-
// Extract the "don't X" part as a rule
|
|
101
|
-
const dontMatch = userCorrection.match(/(?:don'?t|never|stop)\s+(.{5,60})/i);
|
|
102
|
-
if (dontMatch) {
|
|
103
|
-
proposedRule = `NEVER ${dontMatch[1].trim().replace(/[.!?]+$/, '')}`;
|
|
104
|
-
confidence = 50;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
correction: userCorrection,
|
|
110
|
-
antiPattern: bestAntiPattern ? bestAntiPattern.label : null,
|
|
111
|
-
proposedRule,
|
|
112
|
-
confidence,
|
|
113
|
-
evidence,
|
|
114
|
-
messageCount: messages.length,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// History-Aware Distillation (the main entry point)
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Distill a lesson from chat history when thumbs-down is received.
|
|
124
|
-
* This is the "Reflector" agent — it takes conversation context and
|
|
125
|
-
* auto-proposes what went wrong + a prevention rule.
|
|
126
|
-
*
|
|
127
|
-
* @param {Object} opts
|
|
128
|
-
* @param {Array} opts.chatHistory - Last N messages [{role, content}]
|
|
129
|
-
* @param {Object} [opts.failedToolCall] - The tool call that failed
|
|
130
|
-
* @param {string} [opts.feedbackContext] - User's feedback context string
|
|
131
|
-
* @param {string} [opts.signal] - 'negative' or 'positive'
|
|
132
|
-
* @returns {{ lesson, proposedRule, confirmation, autoCreated }}
|
|
133
|
-
*/
|
|
134
|
-
function distillFromHistory({ chatHistory = [], failedToolCall = null, feedbackContext = '', signal = 'negative' } = {}) {
|
|
135
|
-
// Step 1: Analyze chat history for corrections and anti-patterns
|
|
136
|
-
const analysis = analyzeChatHistory(chatHistory, failedToolCall);
|
|
137
|
-
|
|
138
|
-
// Step 2: Infer from surrounding messages (leverage existing module)
|
|
139
|
-
const inference = inferFromSurroundingMessages({
|
|
140
|
-
priorMessages: chatHistory.slice(-10),
|
|
141
|
-
signal,
|
|
142
|
-
feedbackContext,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Step 3: Build the auto-proposed lesson
|
|
146
|
-
let proposedWhatWentWrong;
|
|
147
|
-
let proposedRule = analysis.proposedRule;
|
|
148
|
-
|
|
149
|
-
if (analysis.correction && analysis.antiPattern) {
|
|
150
|
-
proposedWhatWentWrong = `I noticed I used ${analysis.antiPattern} despite your earlier correction: "${analysis.correction.slice(0, 100)}". This has been recorded as a mistake.`;
|
|
151
|
-
} else if (analysis.antiPattern) {
|
|
152
|
-
proposedWhatWentWrong = `I detected a ${analysis.antiPattern} anti-pattern in my output that likely caused the issue.`;
|
|
153
|
-
} else if (analysis.correction) {
|
|
154
|
-
proposedWhatWentWrong = `You previously corrected me: "${analysis.correction.slice(0, 100)}". I may have repeated the same mistake.`;
|
|
155
|
-
} else if (inference.inferredAction) {
|
|
156
|
-
proposedWhatWentWrong = `The ${inference.inferredAction.type} action on ${inference.inferredAction.target} did not produce the expected result.`;
|
|
157
|
-
} else {
|
|
158
|
-
proposedWhatWentWrong = feedbackContext || 'The agent action did not meet expectations.';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Step 4: Build confirmation message
|
|
162
|
-
const confirmation = proposedRule
|
|
163
|
-
? `I've recorded a rule: "${proposedRule}". Correct?`
|
|
164
|
-
: `Lesson captured: "${proposedWhatWentWrong.slice(0, 100)}". Any corrections?`;
|
|
165
|
-
|
|
166
|
-
// Step 5: Auto-create the lesson
|
|
167
|
-
const lesson = createLesson({
|
|
168
|
-
signal,
|
|
169
|
-
inferredLesson: proposedWhatWentWrong,
|
|
170
|
-
triggerMessage: inference.triggerMessage,
|
|
171
|
-
priorSummary: inference.priorSummary,
|
|
172
|
-
confidence: analysis.confidence || inference.confidence,
|
|
173
|
-
tags: analysis.antiPattern ? [analysis.antiPattern] : [],
|
|
174
|
-
metadata: { distilled: true, hasCorrection: !!analysis.correction, hasAntiPattern: !!analysis.antiPattern },
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// Step 6: If high confidence + anti-pattern, auto-install prevention rule
|
|
178
|
-
let ruleInstalled = false;
|
|
179
|
-
if (proposedRule && analysis.confidence >= 60) {
|
|
180
|
-
try {
|
|
181
|
-
contextfs.registerPreventionRules(`# Auto-Distilled Rule\n\n- ${proposedRule}\n\nSource: history-distiller (confidence: ${analysis.confidence}%)`, { source: 'history-distiller', lessonId: lesson.id });
|
|
182
|
-
ruleInstalled = true;
|
|
183
|
-
} catch { /* non-critical */ }
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
lesson,
|
|
188
|
-
proposedWhatWentWrong,
|
|
189
|
-
proposedRule,
|
|
190
|
-
confirmation,
|
|
191
|
-
ruleInstalled,
|
|
192
|
-
analysis,
|
|
193
|
-
inference: { action: inference.inferredAction, confidence: inference.confidence },
|
|
194
|
-
autoCreated: true,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
module.exports = {
|
|
199
|
-
ANTI_PATTERNS, analyzeChatHistory, distillFromHistory,
|
|
200
|
-
};
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawn } = require('child_process');
|
|
6
|
-
|
|
7
|
-
const runner = require('./async-job-runner');
|
|
8
|
-
const { buildHarnessJob } = require('./natural-language-harness');
|
|
9
|
-
const { ensureDir } = require('./fs-utils');
|
|
10
|
-
|
|
11
|
-
const RUNNER_SCRIPT_PATH = path.join(__dirname, 'async-job-runner.js');
|
|
12
|
-
const MANAGED_DPO_EXPORT_SCRIPT_PATH = path.join(__dirname, 'managed-dpo-export.js');
|
|
13
|
-
const BACKGROUND_LAUNCH_MODE = 'background';
|
|
14
|
-
const INLINE_LAUNCH_MODE = 'inline';
|
|
15
|
-
const IDLE_JOB_STATUSES = new Set(['queued', 'paused', 'resume_requested']);
|
|
16
|
-
|
|
17
|
-
function nowIso() {
|
|
18
|
-
return new Date().toISOString();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function shellQuote(value) {
|
|
23
|
-
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function createHostedJobId(prefix = 'job') {
|
|
27
|
-
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getStatePath(jobId) {
|
|
31
|
-
return runner.getJobRuntimePaths(jobId).statePath;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function writeStateFile(jobId, state) {
|
|
35
|
-
const statePath = getStatePath(jobId);
|
|
36
|
-
ensureDir(path.dirname(statePath));
|
|
37
|
-
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
38
|
-
return state;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function updateIdleJobState(jobId, updater) {
|
|
42
|
-
const state = runner.readJobState(jobId);
|
|
43
|
-
if (!state) {
|
|
44
|
-
const error = new Error(`No persisted state found for job ${jobId}`);
|
|
45
|
-
error.statusCode = 404;
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!IDLE_JOB_STATUSES.has(state.status)) {
|
|
50
|
-
const error = new Error(`Job ${jobId} is not idle; current status is ${state.status}`);
|
|
51
|
-
error.statusCode = 409;
|
|
52
|
-
throw error;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return writeStateFile(jobId, updater({ ...state }));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function writeJobFile(jobId, jobSpec) {
|
|
59
|
-
const { jobDir } = runner.getJobRuntimePaths(jobId);
|
|
60
|
-
ensureDir(jobDir);
|
|
61
|
-
const jobFilePath = path.join(jobDir, 'job.json');
|
|
62
|
-
fs.writeFileSync(jobFilePath, JSON.stringify(jobSpec, null, 2) + '\n', 'utf8');
|
|
63
|
-
return jobFilePath;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function runInlineJob(args) {
|
|
67
|
-
if (args.runFile) {
|
|
68
|
-
runner.runJobFromFile(args.runFile);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (args.resumeJobId) {
|
|
73
|
-
runner.resumeJob(args.resumeJobId);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
throw new Error('Unsupported inline hosted job launch');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function launchRunner(args, options = {}) {
|
|
81
|
-
const launchMode = options.launchMode || process.env.THUMBGATE_HOSTED_JOB_LAUNCH_MODE || BACKGROUND_LAUNCH_MODE;
|
|
82
|
-
if (launchMode === INLINE_LAUNCH_MODE) {
|
|
83
|
-
runInlineJob(args);
|
|
84
|
-
return {
|
|
85
|
-
launchMode,
|
|
86
|
-
pid: process.pid,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const runnerArgs = [];
|
|
91
|
-
if (args.runFile) {
|
|
92
|
-
runnerArgs.push(`--run-file=${args.runFile}`);
|
|
93
|
-
} else if (args.resumeJobId) {
|
|
94
|
-
runnerArgs.push(`--resume=${args.resumeJobId}`);
|
|
95
|
-
} else {
|
|
96
|
-
throw new Error('Hosted job launch requires runFile or resumeJobId');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const child = spawn(process.execPath, [RUNNER_SCRIPT_PATH, ...runnerArgs], {
|
|
100
|
-
cwd: options.cwd || process.cwd(),
|
|
101
|
-
env: process.env,
|
|
102
|
-
detached: true,
|
|
103
|
-
stdio: 'ignore',
|
|
104
|
-
});
|
|
105
|
-
child.unref();
|
|
106
|
-
return {
|
|
107
|
-
launchMode,
|
|
108
|
-
pid: child.pid,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function prepareManagedJob(jobSpec, options = {}) {
|
|
113
|
-
const jobId = options.jobId || jobSpec.id || createHostedJobId(options.jobPrefix || 'job');
|
|
114
|
-
const finalSpec = {
|
|
115
|
-
...jobSpec,
|
|
116
|
-
id: jobId,
|
|
117
|
-
};
|
|
118
|
-
const jobFilePath = writeJobFile(jobId, finalSpec);
|
|
119
|
-
const queuedState = runner.queueJob({
|
|
120
|
-
...finalSpec,
|
|
121
|
-
jobFilePath,
|
|
122
|
-
});
|
|
123
|
-
return {
|
|
124
|
-
jobId,
|
|
125
|
-
jobFilePath,
|
|
126
|
-
state: queuedState,
|
|
127
|
-
jobSpec: {
|
|
128
|
-
...finalSpec,
|
|
129
|
-
jobFilePath,
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function launchManagedJob(jobSpec, options = {}) {
|
|
135
|
-
const prepared = prepareManagedJob(jobSpec, options);
|
|
136
|
-
const launch = launchRunner({ runFile: prepared.jobFilePath }, options);
|
|
137
|
-
return {
|
|
138
|
-
jobId: prepared.jobId,
|
|
139
|
-
jobFilePath: prepared.jobFilePath,
|
|
140
|
-
launchMode: launch.launchMode,
|
|
141
|
-
pid: launch.pid || null,
|
|
142
|
-
state: runner.readJobState(prepared.jobId) || prepared.state,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function buildManagedDpoExportJob(params = {}) {
|
|
147
|
-
const command = [
|
|
148
|
-
shellQuote(process.execPath),
|
|
149
|
-
shellQuote(MANAGED_DPO_EXPORT_SCRIPT_PATH),
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
if (params.inputPath) {
|
|
153
|
-
command.push('--inputPath', shellQuote(params.inputPath));
|
|
154
|
-
} else if (params.memoryLogPath) {
|
|
155
|
-
command.push('--memoryLogPath', shellQuote(params.memoryLogPath));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (params.outputPath) {
|
|
159
|
-
command.push('--outputPath', shellQuote(params.outputPath));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
tags: ['hosted-job', 'dpo-export'],
|
|
164
|
-
skill: 'hosted-dpo-export',
|
|
165
|
-
autoImprove: false,
|
|
166
|
-
verificationMode: 'none',
|
|
167
|
-
recordFeedback: false,
|
|
168
|
-
stages: [
|
|
169
|
-
{
|
|
170
|
-
name: 'export_dpo_pairs',
|
|
171
|
-
command: command.join(' '),
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function launchDpoExportJob(params = {}, options = {}) {
|
|
178
|
-
return launchManagedJob(buildManagedDpoExportJob(params), {
|
|
179
|
-
...options,
|
|
180
|
-
jobPrefix: 'dpo_export',
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function launchHarnessJob(identifier, inputs = {}, options = {}) {
|
|
185
|
-
const jobId = options.jobId || createHostedJobId('harness');
|
|
186
|
-
const jobSpec = buildHarnessJob(identifier, inputs, {
|
|
187
|
-
jobId,
|
|
188
|
-
skill: options.skill,
|
|
189
|
-
partnerProfile: options.partnerProfile,
|
|
190
|
-
autoImprove: options.autoImprove,
|
|
191
|
-
});
|
|
192
|
-
return launchManagedJob(jobSpec, {
|
|
193
|
-
...options,
|
|
194
|
-
jobId,
|
|
195
|
-
jobPrefix: 'harness',
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function resumeHostedJob(jobId, options = {}) {
|
|
200
|
-
const state = runner.readJobState(jobId);
|
|
201
|
-
if (!state) {
|
|
202
|
-
const error = new Error(`No persisted state found for job ${jobId}`);
|
|
203
|
-
error.statusCode = 404;
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (['completed', 'failed', 'cancelled'].includes(state.status)) {
|
|
208
|
-
const error = new Error(`Job ${jobId} is already ${state.status}`);
|
|
209
|
-
error.statusCode = 409;
|
|
210
|
-
throw error;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const launch = launchRunner({ resumeJobId: jobId }, options);
|
|
214
|
-
return {
|
|
215
|
-
jobId,
|
|
216
|
-
launchMode: launch.launchMode,
|
|
217
|
-
pid: launch.pid || null,
|
|
218
|
-
state: runner.readJobState(jobId) || state,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function pauseQueuedJob(jobId, metadata = {}) {
|
|
223
|
-
runner.clearJobControl(jobId);
|
|
224
|
-
return updateIdleJobState(jobId, (state) => ({
|
|
225
|
-
...state,
|
|
226
|
-
status: 'paused',
|
|
227
|
-
updatedAt: nowIso(),
|
|
228
|
-
pausedAt: nowIso(),
|
|
229
|
-
stopReason: metadata && metadata.reason ? metadata.reason : 'pause_requested',
|
|
230
|
-
}));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function cancelQueuedJob(jobId, metadata = {}) {
|
|
234
|
-
runner.clearJobControl(jobId);
|
|
235
|
-
return updateIdleJobState(jobId, (state) => ({
|
|
236
|
-
...state,
|
|
237
|
-
status: 'cancelled',
|
|
238
|
-
updatedAt: nowIso(),
|
|
239
|
-
endedAt: nowIso(),
|
|
240
|
-
stopReason: metadata && metadata.reason ? metadata.reason : 'cancel_requested',
|
|
241
|
-
}));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
module.exports = {
|
|
245
|
-
BACKGROUND_LAUNCH_MODE,
|
|
246
|
-
INLINE_LAUNCH_MODE,
|
|
247
|
-
buildManagedDpoExportJob,
|
|
248
|
-
cancelQueuedJob,
|
|
249
|
-
createHostedJobId,
|
|
250
|
-
launchDpoExportJob,
|
|
251
|
-
launchHarnessJob,
|
|
252
|
-
launchManagedJob,
|
|
253
|
-
pauseQueuedJob,
|
|
254
|
-
prepareManagedJob,
|
|
255
|
-
resumeHostedJob,
|
|
256
|
-
};
|