labgate 0.5.37 → 0.5.39
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/README.md +2 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/lib/automation-engine.d.ts +91 -0
- package/dist/lib/automation-engine.js +401 -0
- package/dist/lib/automation-engine.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.js +137 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/container.d.ts +4 -1
- package/dist/lib/container.js +26 -10
- package/dist/lib/container.js.map +1 -1
- package/dist/lib/feedback.d.ts +13 -1
- package/dist/lib/feedback.js +9 -4
- package/dist/lib/feedback.js.map +1 -1
- package/dist/lib/init.js +5 -1
- package/dist/lib/init.js.map +1 -1
- package/dist/lib/results-mcp.js +390 -4
- package/dist/lib/results-mcp.js.map +1 -1
- package/dist/lib/results-store.d.ts +66 -0
- package/dist/lib/results-store.js +324 -6
- package/dist/lib/results-store.js.map +1 -1
- package/dist/lib/ui.d.ts +2 -0
- package/dist/lib/ui.html +16870 -8381
- package/dist/lib/ui.js +1650 -120
- package/dist/lib/ui.js.map +1 -1
- package/dist/lib/web-terminal.d.ts +4 -1
- package/dist/lib/web-terminal.js +33 -3
- package/dist/lib/web-terminal.js.map +1 -1
- package/dist/mcp-bundles/dataset-mcp.bundle.mjs +120 -3
- package/dist/mcp-bundles/display-mcp.bundle.mjs +94 -0
- package/dist/mcp-bundles/explorer-mcp.bundle.mjs +123 -4
- package/dist/mcp-bundles/results-mcp.bundle.mjs +850 -53
- package/dist/mcp-bundles/slurm-mcp.bundle.mjs +94 -0
- package/package.json +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Automation engine: monitors terminal output for "waiting for input" patterns
|
|
4
|
+
* and auto-responds via an external LLM (Anthropic Messages API).
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AutomationEngine = void 0;
|
|
8
|
+
exports.stripAnsi = stripAnsi;
|
|
9
|
+
// ── ANSI stripping ───────────────────────────────────────────
|
|
10
|
+
// Matches ANSI escape sequences (CSI, OSC, single-char escapes, etc.)
|
|
11
|
+
const ANSI_RE = /\x1b(?:\[[0-9;?]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)?|[()][AB012]|[78DEHM=>])/g;
|
|
12
|
+
// Matches common control characters (except newline/tab)
|
|
13
|
+
const CTRL_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
14
|
+
function stripAnsi(text) {
|
|
15
|
+
return text.replace(ANSI_RE, '').replace(CTRL_RE, '');
|
|
16
|
+
}
|
|
17
|
+
// ── Engine ───────────────────────────────────────────────────
|
|
18
|
+
const MAX_BUFFER = 32768; // 32KB rolling buffer
|
|
19
|
+
const MAX_LOG_ENTRIES = 200;
|
|
20
|
+
class AutomationEngine {
|
|
21
|
+
config;
|
|
22
|
+
writeFn;
|
|
23
|
+
broadcastFn;
|
|
24
|
+
broadcastTurnFn;
|
|
25
|
+
enabled;
|
|
26
|
+
paused;
|
|
27
|
+
turnCount;
|
|
28
|
+
turnSequence;
|
|
29
|
+
activityLog;
|
|
30
|
+
turns;
|
|
31
|
+
currentTurn;
|
|
32
|
+
pendingTimer;
|
|
33
|
+
abortController;
|
|
34
|
+
compiledPatterns;
|
|
35
|
+
strippedBuffer;
|
|
36
|
+
lastTriggerTime;
|
|
37
|
+
bridgeId;
|
|
38
|
+
constructor(bridgeId, config, writeFn, broadcastFn, broadcastTurnFn) {
|
|
39
|
+
this.bridgeId = bridgeId;
|
|
40
|
+
this.config = { ...config };
|
|
41
|
+
this.writeFn = writeFn;
|
|
42
|
+
this.broadcastFn = broadcastFn ?? null;
|
|
43
|
+
this.broadcastTurnFn = broadcastTurnFn ?? null;
|
|
44
|
+
this.enabled = true;
|
|
45
|
+
this.paused = false;
|
|
46
|
+
this.turnCount = 0;
|
|
47
|
+
this.turnSequence = 0;
|
|
48
|
+
this.activityLog = [];
|
|
49
|
+
this.turns = [];
|
|
50
|
+
this.currentTurn = null;
|
|
51
|
+
this.pendingTimer = null;
|
|
52
|
+
this.abortController = null;
|
|
53
|
+
this.strippedBuffer = '';
|
|
54
|
+
this.lastTriggerTime = 0;
|
|
55
|
+
this.compiledPatterns = this.compilePatterns(config.trigger_patterns);
|
|
56
|
+
this.addLog({ timestamp: new Date().toISOString(), type: 'enabled' });
|
|
57
|
+
}
|
|
58
|
+
// ── Public API ───────────────────────────────────────────
|
|
59
|
+
/** Feed raw terminal output data into the engine. */
|
|
60
|
+
onTerminalOutput(rawData) {
|
|
61
|
+
if (!this.enabled || this.paused)
|
|
62
|
+
return;
|
|
63
|
+
const stripped = stripAnsi(rawData);
|
|
64
|
+
this.strippedBuffer += stripped;
|
|
65
|
+
if (this.strippedBuffer.length > MAX_BUFFER) {
|
|
66
|
+
this.strippedBuffer = this.strippedBuffer.slice(-MAX_BUFFER);
|
|
67
|
+
}
|
|
68
|
+
// Don't trigger if we already have a pending timer or in-flight request
|
|
69
|
+
if (this.pendingTimer || this.abortController)
|
|
70
|
+
return;
|
|
71
|
+
// Debounce: ignore triggers within 500ms of last trigger to let output settle
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (now - this.lastTriggerTime < 500)
|
|
74
|
+
return;
|
|
75
|
+
const lastLines = this.getLastLines(20);
|
|
76
|
+
const match = this.matchTrigger(lastLines);
|
|
77
|
+
if (!match)
|
|
78
|
+
return;
|
|
79
|
+
this.lastTriggerTime = now;
|
|
80
|
+
// Start delay timer (human override window)
|
|
81
|
+
this.pendingTimer = setTimeout(() => {
|
|
82
|
+
this.pendingTimer = null;
|
|
83
|
+
this.fireTrigger(match.pattern, match.text);
|
|
84
|
+
}, this.config.delay_ms);
|
|
85
|
+
}
|
|
86
|
+
/** Cancel any pending timer and in-flight LLM request. Resets turn counter. */
|
|
87
|
+
cancelPending() {
|
|
88
|
+
if (this.pendingTimer) {
|
|
89
|
+
clearTimeout(this.pendingTimer);
|
|
90
|
+
this.pendingTimer = null;
|
|
91
|
+
}
|
|
92
|
+
if (this.abortController) {
|
|
93
|
+
this.abortController.abort();
|
|
94
|
+
this.abortController = null;
|
|
95
|
+
}
|
|
96
|
+
// Human typed — reset turn counter
|
|
97
|
+
this.turnCount = 0;
|
|
98
|
+
}
|
|
99
|
+
pause() {
|
|
100
|
+
this.paused = true;
|
|
101
|
+
this.cancelPending();
|
|
102
|
+
this.addLog({ timestamp: new Date().toISOString(), type: 'paused' });
|
|
103
|
+
}
|
|
104
|
+
resume() {
|
|
105
|
+
this.paused = false;
|
|
106
|
+
this.turnCount = 0;
|
|
107
|
+
this.addLog({ timestamp: new Date().toISOString(), type: 'resumed' });
|
|
108
|
+
}
|
|
109
|
+
stop() {
|
|
110
|
+
this.enabled = false;
|
|
111
|
+
this.cancelPending();
|
|
112
|
+
this.addLog({ timestamp: new Date().toISOString(), type: 'disabled' });
|
|
113
|
+
}
|
|
114
|
+
getLog() {
|
|
115
|
+
return [...this.activityLog];
|
|
116
|
+
}
|
|
117
|
+
getStatus() {
|
|
118
|
+
return {
|
|
119
|
+
enabled: this.enabled,
|
|
120
|
+
paused: this.paused,
|
|
121
|
+
turnCount: this.turnCount,
|
|
122
|
+
maxTurns: this.config.max_turns,
|
|
123
|
+
inflight: this.abortController !== null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
getConfig() {
|
|
127
|
+
return { ...this.config };
|
|
128
|
+
}
|
|
129
|
+
getTurns() {
|
|
130
|
+
return [...this.turns];
|
|
131
|
+
}
|
|
132
|
+
/** Infer terminal status from the buffer: 'awaiting_input', 'working', or 'idle'. */
|
|
133
|
+
getTerminalStatus() {
|
|
134
|
+
if (this.abortController)
|
|
135
|
+
return 'working'; // LLM call in-flight
|
|
136
|
+
const lastLines = this.getLastLines(20);
|
|
137
|
+
if (!lastLines.trim())
|
|
138
|
+
return 'idle';
|
|
139
|
+
const match = this.matchTrigger(lastLines);
|
|
140
|
+
if (match)
|
|
141
|
+
return 'awaiting_input';
|
|
142
|
+
return 'working';
|
|
143
|
+
}
|
|
144
|
+
updateConfig(partial) {
|
|
145
|
+
Object.assign(this.config, partial);
|
|
146
|
+
if (partial.trigger_patterns) {
|
|
147
|
+
this.compiledPatterns = this.compilePatterns(partial.trigger_patterns);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ── Private ──────────────────────────────────────────────
|
|
151
|
+
compilePatterns(patterns) {
|
|
152
|
+
const compiled = [];
|
|
153
|
+
for (const pat of patterns) {
|
|
154
|
+
try {
|
|
155
|
+
compiled.push(new RegExp(pat, 'm'));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Skip invalid patterns
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return compiled;
|
|
162
|
+
}
|
|
163
|
+
getLastLines(n) {
|
|
164
|
+
const lines = this.strippedBuffer.split('\n');
|
|
165
|
+
return lines.slice(-n).join('\n');
|
|
166
|
+
}
|
|
167
|
+
getContextLines() {
|
|
168
|
+
const lines = this.strippedBuffer.split('\n');
|
|
169
|
+
return lines.slice(-this.config.context_lines).join('\n');
|
|
170
|
+
}
|
|
171
|
+
matchTrigger(text) {
|
|
172
|
+
for (let i = 0; i < this.compiledPatterns.length; i++) {
|
|
173
|
+
const re = this.compiledPatterns[i];
|
|
174
|
+
const m = re.exec(text);
|
|
175
|
+
if (m) {
|
|
176
|
+
return { pattern: this.config.trigger_patterns[i], text: m[0] };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
async fireTrigger(pattern, matchedText) {
|
|
182
|
+
if (!this.enabled || this.paused)
|
|
183
|
+
return;
|
|
184
|
+
// Check turn limit
|
|
185
|
+
if (this.turnCount >= this.config.max_turns) {
|
|
186
|
+
this.pause();
|
|
187
|
+
this.addLog({
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
type: 'paused',
|
|
190
|
+
error: `Auto-paused: max turns (${this.config.max_turns}) reached`,
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Create turn
|
|
195
|
+
this.turnSequence += 1;
|
|
196
|
+
const turn = {
|
|
197
|
+
turnId: `${this.bridgeId}-turn-${this.turnSequence}`,
|
|
198
|
+
turnNumber: this.turnSequence,
|
|
199
|
+
status: 'pending',
|
|
200
|
+
triggeredAt: new Date().toISOString(),
|
|
201
|
+
triggerPattern: pattern,
|
|
202
|
+
matchedText: matchedText.slice(0, 500),
|
|
203
|
+
contextPreview: '',
|
|
204
|
+
contextLineCount: 0,
|
|
205
|
+
};
|
|
206
|
+
this.currentTurn = turn;
|
|
207
|
+
this.turns.push(turn);
|
|
208
|
+
if (this.turns.length > MAX_LOG_ENTRIES) {
|
|
209
|
+
this.turns = this.turns.slice(-MAX_LOG_ENTRIES);
|
|
210
|
+
}
|
|
211
|
+
this.broadcastTurn('started', turn);
|
|
212
|
+
this.addLog({
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
type: 'trigger',
|
|
215
|
+
trigger_pattern: pattern,
|
|
216
|
+
matched_text: matchedText.slice(0, 200),
|
|
217
|
+
});
|
|
218
|
+
const context = this.getContextLines();
|
|
219
|
+
turn.contextLineCount = context.split('\n').length;
|
|
220
|
+
turn.contextPreview = this.makeContextPreview(context);
|
|
221
|
+
const apiKey = this.config.api_key || process.env.ANTHROPIC_API_KEY;
|
|
222
|
+
if (!apiKey) {
|
|
223
|
+
turn.status = 'error';
|
|
224
|
+
turn.error = 'No API key configured (set automation.api_key in config or ANTHROPIC_API_KEY env var)';
|
|
225
|
+
turn.completedAt = new Date().toISOString();
|
|
226
|
+
this.broadcastTurn('completed', turn);
|
|
227
|
+
this.currentTurn = null;
|
|
228
|
+
this.addLog({
|
|
229
|
+
timestamp: new Date().toISOString(),
|
|
230
|
+
type: 'error',
|
|
231
|
+
error: turn.error,
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// Update to calling_llm
|
|
236
|
+
turn.status = 'calling_llm';
|
|
237
|
+
turn.llmStartedAt = new Date().toISOString();
|
|
238
|
+
turn.llmModel = this.config.model;
|
|
239
|
+
this.broadcastTurn('updated', turn);
|
|
240
|
+
try {
|
|
241
|
+
this.abortController = new AbortController();
|
|
242
|
+
const llmStart = Date.now();
|
|
243
|
+
const response = await this.callAnthropicApi(apiKey, context);
|
|
244
|
+
this.abortController = null;
|
|
245
|
+
turn.llmDurationMs = Date.now() - llmStart;
|
|
246
|
+
if (!this.enabled || this.paused) {
|
|
247
|
+
turn.status = 'cancelled';
|
|
248
|
+
turn.completedAt = new Date().toISOString();
|
|
249
|
+
this.broadcastTurn('completed', turn);
|
|
250
|
+
this.currentTurn = null;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (response) {
|
|
254
|
+
// Inject into terminal: write the response + Enter
|
|
255
|
+
this.writeFn(response + '\r');
|
|
256
|
+
this.turnCount++;
|
|
257
|
+
turn.status = 'done';
|
|
258
|
+
turn.fullResponse = response;
|
|
259
|
+
turn.responseSummary = response.slice(0, 100);
|
|
260
|
+
turn.completedAt = new Date().toISOString();
|
|
261
|
+
this.broadcastTurn('completed', turn);
|
|
262
|
+
this.addLog({
|
|
263
|
+
timestamp: new Date().toISOString(),
|
|
264
|
+
type: 'response',
|
|
265
|
+
response_preview: response.slice(0, 200),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
turn.status = 'done';
|
|
270
|
+
turn.responseSummary = '';
|
|
271
|
+
turn.completedAt = new Date().toISOString();
|
|
272
|
+
this.broadcastTurn('completed', turn);
|
|
273
|
+
this.addLog({
|
|
274
|
+
timestamp: new Date().toISOString(),
|
|
275
|
+
type: 'response',
|
|
276
|
+
response_preview: '(empty response)',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
this.abortController = null;
|
|
282
|
+
if (err?.name === 'AbortError') {
|
|
283
|
+
turn.status = 'cancelled';
|
|
284
|
+
turn.completedAt = new Date().toISOString();
|
|
285
|
+
this.broadcastTurn('completed', turn);
|
|
286
|
+
this.currentTurn = null;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const errMsg = err?.message ?? String(err);
|
|
290
|
+
turn.status = 'error';
|
|
291
|
+
turn.error = errMsg;
|
|
292
|
+
turn.completedAt = new Date().toISOString();
|
|
293
|
+
this.broadcastTurn('completed', turn);
|
|
294
|
+
this.addLog({
|
|
295
|
+
timestamp: new Date().toISOString(),
|
|
296
|
+
type: 'error',
|
|
297
|
+
error: errMsg,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
this.currentTurn = null;
|
|
301
|
+
}
|
|
302
|
+
async callAnthropicApi(apiKey, terminalContext) {
|
|
303
|
+
const userMessage = [
|
|
304
|
+
'The terminal output below shows a coding agent session.',
|
|
305
|
+
'The agent appears to be waiting for user input.',
|
|
306
|
+
'Based on your instructions, provide the appropriate response.',
|
|
307
|
+
'Reply with ONLY the text that should be typed into the terminal — no explanation, no markdown, no code fences.',
|
|
308
|
+
'',
|
|
309
|
+
'--- Terminal output (last lines) ---',
|
|
310
|
+
terminalContext,
|
|
311
|
+
].join('\n');
|
|
312
|
+
const body = {
|
|
313
|
+
model: this.config.model,
|
|
314
|
+
max_tokens: this.config.max_tokens,
|
|
315
|
+
system: this.config.system_prompt,
|
|
316
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
317
|
+
stream: true,
|
|
318
|
+
};
|
|
319
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
320
|
+
method: 'POST',
|
|
321
|
+
headers: {
|
|
322
|
+
'x-api-key': apiKey,
|
|
323
|
+
'anthropic-version': '2023-06-01',
|
|
324
|
+
'content-type': 'application/json',
|
|
325
|
+
},
|
|
326
|
+
body: JSON.stringify(body),
|
|
327
|
+
signal: this.abortController?.signal,
|
|
328
|
+
});
|
|
329
|
+
if (!res.ok) {
|
|
330
|
+
const text = await res.text().catch(() => '');
|
|
331
|
+
throw new Error(`Anthropic API error ${res.status}: ${text.slice(0, 200)}`);
|
|
332
|
+
}
|
|
333
|
+
// Parse SSE stream
|
|
334
|
+
return this.parseSSEStream(res);
|
|
335
|
+
}
|
|
336
|
+
async parseSSEStream(res) {
|
|
337
|
+
const reader = res.body?.getReader();
|
|
338
|
+
if (!reader)
|
|
339
|
+
throw new Error('No response body');
|
|
340
|
+
const decoder = new TextDecoder();
|
|
341
|
+
let accumulated = '';
|
|
342
|
+
let sseBuffer = '';
|
|
343
|
+
try {
|
|
344
|
+
while (true) {
|
|
345
|
+
const { done, value } = await reader.read();
|
|
346
|
+
if (done)
|
|
347
|
+
break;
|
|
348
|
+
sseBuffer += decoder.decode(value, { stream: true });
|
|
349
|
+
const lines = sseBuffer.split('\n');
|
|
350
|
+
// Keep the last potentially incomplete line in the buffer
|
|
351
|
+
sseBuffer = lines.pop() ?? '';
|
|
352
|
+
for (const line of lines) {
|
|
353
|
+
if (!line.startsWith('data: '))
|
|
354
|
+
continue;
|
|
355
|
+
const jsonStr = line.slice(6).trim();
|
|
356
|
+
if (jsonStr === '[DONE]')
|
|
357
|
+
continue;
|
|
358
|
+
try {
|
|
359
|
+
const event = JSON.parse(jsonStr);
|
|
360
|
+
if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta') {
|
|
361
|
+
accumulated += event.delta.text;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Skip malformed JSON lines
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
finally {
|
|
371
|
+
reader.releaseLock();
|
|
372
|
+
}
|
|
373
|
+
return accumulated.trim();
|
|
374
|
+
}
|
|
375
|
+
makeContextPreview(context) {
|
|
376
|
+
const lines = context.split('\n');
|
|
377
|
+
if (lines.length <= 6)
|
|
378
|
+
return context;
|
|
379
|
+
return [
|
|
380
|
+
...lines.slice(0, 3),
|
|
381
|
+
`... (${lines.length - 6} more lines) ...`,
|
|
382
|
+
...lines.slice(-3),
|
|
383
|
+
].join('\n');
|
|
384
|
+
}
|
|
385
|
+
broadcastTurn(action, turn) {
|
|
386
|
+
if (this.broadcastTurnFn) {
|
|
387
|
+
this.broadcastTurnFn(action, { ...turn });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
addLog(entry) {
|
|
391
|
+
this.activityLog.push(entry);
|
|
392
|
+
if (this.activityLog.length > MAX_LOG_ENTRIES) {
|
|
393
|
+
this.activityLog = this.activityLog.slice(-MAX_LOG_ENTRIES);
|
|
394
|
+
}
|
|
395
|
+
if (this.broadcastFn) {
|
|
396
|
+
this.broadcastFn(entry);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
exports.AutomationEngine = AutomationEngine;
|
|
401
|
+
//# sourceMappingURL=automation-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation-engine.js","sourceRoot":"","sources":["../../src/lib/automation-engine.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -66,6 +66,27 @@ export interface LabgateConfig {
|
|
|
66
66
|
enabled: boolean;
|
|
67
67
|
log_dir: string;
|
|
68
68
|
};
|
|
69
|
+
/** Plugin enable/disable state (plugin ID → boolean). Core is always enabled. */
|
|
70
|
+
plugins: Record<string, boolean>;
|
|
71
|
+
/** Automation plugin: external LLM monitors terminal and auto-responds */
|
|
72
|
+
automation?: {
|
|
73
|
+
/** Anthropic API key (falls back to ANTHROPIC_API_KEY env var) */
|
|
74
|
+
api_key?: string;
|
|
75
|
+
/** Model to use for automation responses */
|
|
76
|
+
model: string;
|
|
77
|
+
/** System prompt sent to the orchestrating LLM */
|
|
78
|
+
system_prompt: string;
|
|
79
|
+
/** Regex patterns that indicate the terminal is waiting for input */
|
|
80
|
+
trigger_patterns: string[];
|
|
81
|
+
/** Number of recent terminal lines to send as context */
|
|
82
|
+
context_lines: number;
|
|
83
|
+
/** Delay in ms before auto-responding (human override window) */
|
|
84
|
+
delay_ms: number;
|
|
85
|
+
/** Maximum consecutive auto-responses before pausing */
|
|
86
|
+
max_turns: number;
|
|
87
|
+
/** Max tokens for LLM response */
|
|
88
|
+
max_tokens: number;
|
|
89
|
+
};
|
|
69
90
|
/** Headless mode defaults for non-interactive agent runs */
|
|
70
91
|
headless?: {
|
|
71
92
|
/**
|
|
@@ -73,9 +94,21 @@ export interface LabgateConfig {
|
|
|
73
94
|
* per-tool approval prompts are not interactive in this mode.
|
|
74
95
|
*/
|
|
75
96
|
claude_run_with_allowed_permissions?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Show the "Continue in terminal via labgate continue ..." footer hint.
|
|
99
|
+
* Disable this to reduce cross-terminal continuation discoverability.
|
|
100
|
+
*/
|
|
101
|
+
continuation_in_other_terminals?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Enable Git-related UI integration (sidebar DAG and footer branch controls).
|
|
104
|
+
* Disabled by default while this feature remains experimental.
|
|
105
|
+
*/
|
|
106
|
+
git_integration?: boolean;
|
|
76
107
|
};
|
|
77
108
|
}
|
|
78
109
|
export declare const DEFAULT_CONFIG: LabgateConfig;
|
|
110
|
+
export declare const KNOWN_PLUGIN_IDS: readonly string[];
|
|
111
|
+
export declare function isKnownPluginId(pluginId: string): boolean;
|
|
79
112
|
export declare const LABGATE_DIR: string;
|
|
80
113
|
export declare const CONFIG_FILE = "config.json";
|
|
81
114
|
export declare const PRIVATE_DIR_MODE = 448;
|
package/dist/lib/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PRIVATE_FILE_MODE = exports.PRIVATE_DIR_MODE = exports.CONFIG_FILE = exports.LABGATE_DIR = exports.DEFAULT_CONFIG = void 0;
|
|
3
|
+
exports.PRIVATE_FILE_MODE = exports.PRIVATE_DIR_MODE = exports.CONFIG_FILE = exports.LABGATE_DIR = exports.KNOWN_PLUGIN_IDS = exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.isKnownPluginId = isKnownPluginId;
|
|
4
5
|
exports.ensurePrivateDir = ensurePrivateDir;
|
|
5
6
|
exports.ensurePrivateFile = ensurePrivateFile;
|
|
6
7
|
exports.getConfigPath = getConfigPath;
|
|
@@ -88,10 +89,58 @@ exports.DEFAULT_CONFIG = {
|
|
|
88
89
|
enabled: true,
|
|
89
90
|
log_dir: '~/.labgate/logs',
|
|
90
91
|
},
|
|
92
|
+
plugins: {
|
|
93
|
+
files: true,
|
|
94
|
+
datasets: false,
|
|
95
|
+
results: false,
|
|
96
|
+
charting: true,
|
|
97
|
+
molecular: true,
|
|
98
|
+
genomics: true,
|
|
99
|
+
phylogenetics: true,
|
|
100
|
+
network: true,
|
|
101
|
+
slurm: true,
|
|
102
|
+
explorer: true,
|
|
103
|
+
automation: false,
|
|
104
|
+
},
|
|
105
|
+
automation: {
|
|
106
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
107
|
+
system_prompt: 'You are an automation assistant helping guide a coding agent. When the agent asks a question or waits for input, provide a concise, helpful response based on the context.',
|
|
108
|
+
trigger_patterns: [
|
|
109
|
+
'\\?\\s*$',
|
|
110
|
+
'^>\\s*$',
|
|
111
|
+
'Do you want to proceed',
|
|
112
|
+
'Press Enter to continue',
|
|
113
|
+
'Y/n\\]?\\s*$',
|
|
114
|
+
],
|
|
115
|
+
context_lines: 100,
|
|
116
|
+
delay_ms: 3000,
|
|
117
|
+
max_turns: 20,
|
|
118
|
+
max_tokens: 1024,
|
|
119
|
+
},
|
|
91
120
|
headless: {
|
|
92
121
|
claude_run_with_allowed_permissions: true,
|
|
122
|
+
continuation_in_other_terminals: true,
|
|
123
|
+
git_integration: false,
|
|
93
124
|
},
|
|
94
125
|
};
|
|
126
|
+
exports.KNOWN_PLUGIN_IDS = Object.freeze(Object.keys(exports.DEFAULT_CONFIG.plugins));
|
|
127
|
+
const KNOWN_PLUGIN_ID_SET = new Set(exports.KNOWN_PLUGIN_IDS);
|
|
128
|
+
function isKnownPluginId(pluginId) {
|
|
129
|
+
return KNOWN_PLUGIN_ID_SET.has(pluginId);
|
|
130
|
+
}
|
|
131
|
+
function sanitizePluginMap(rawPlugins) {
|
|
132
|
+
const merged = { ...exports.DEFAULT_CONFIG.plugins };
|
|
133
|
+
if (!rawPlugins || typeof rawPlugins !== 'object' || Array.isArray(rawPlugins))
|
|
134
|
+
return merged;
|
|
135
|
+
for (const [pluginId, enabled] of Object.entries(rawPlugins)) {
|
|
136
|
+
if (!isKnownPluginId(pluginId))
|
|
137
|
+
continue;
|
|
138
|
+
if (typeof enabled !== 'boolean')
|
|
139
|
+
continue;
|
|
140
|
+
merged[pluginId] = enabled;
|
|
141
|
+
}
|
|
142
|
+
return merged;
|
|
143
|
+
}
|
|
95
144
|
// ── Paths ─────────────────────────────────────────────────
|
|
96
145
|
exports.LABGATE_DIR = process.env.LABGATE_DIR ?? (0, path_1.join)((0, os_1.homedir)(), '.labgate');
|
|
97
146
|
exports.CONFIG_FILE = 'config.json';
|
|
@@ -192,6 +241,7 @@ function validateConfig(config) {
|
|
|
192
241
|
const blockedPatterns = config.filesystem?.blocked_patterns;
|
|
193
242
|
const extraPaths = config.filesystem?.extra_paths;
|
|
194
243
|
const blacklist = config.commands?.blacklist;
|
|
244
|
+
const plugins = config.plugins;
|
|
195
245
|
const headless = config.headless;
|
|
196
246
|
if (!VALID_RUNTIMES.includes(config.runtime)) {
|
|
197
247
|
errors.push(`Invalid runtime: "${config.runtime}". Must be one of: ${VALID_RUNTIMES.join(', ')}`);
|
|
@@ -217,6 +267,21 @@ function validateConfig(config) {
|
|
|
217
267
|
if (!Array.isArray(blacklist)) {
|
|
218
268
|
errors.push('commands.blacklist must be an array');
|
|
219
269
|
}
|
|
270
|
+
if (plugins !== undefined) {
|
|
271
|
+
if (!plugins || typeof plugins !== 'object' || Array.isArray(plugins)) {
|
|
272
|
+
errors.push('plugins must be an object');
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
for (const [pluginId, enabled] of Object.entries(plugins)) {
|
|
276
|
+
if (!isKnownPluginId(pluginId)) {
|
|
277
|
+
errors.push(`Unknown plugin id "${pluginId}"`);
|
|
278
|
+
}
|
|
279
|
+
else if (typeof enabled !== 'boolean') {
|
|
280
|
+
errors.push(`plugins.${pluginId} must be a boolean`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
220
285
|
if (headless !== undefined) {
|
|
221
286
|
if (!headless || typeof headless !== 'object' || Array.isArray(headless)) {
|
|
222
287
|
errors.push('headless must be an object');
|
|
@@ -225,6 +290,14 @@ function validateConfig(config) {
|
|
|
225
290
|
typeof headless.claude_run_with_allowed_permissions !== 'boolean') {
|
|
226
291
|
errors.push('headless.claude_run_with_allowed_permissions must be a boolean');
|
|
227
292
|
}
|
|
293
|
+
else if (headless.continuation_in_other_terminals !== undefined &&
|
|
294
|
+
typeof headless.continuation_in_other_terminals !== 'boolean') {
|
|
295
|
+
errors.push('headless.continuation_in_other_terminals must be a boolean');
|
|
296
|
+
}
|
|
297
|
+
else if (headless.git_integration !== undefined &&
|
|
298
|
+
typeof headless.git_integration !== 'boolean') {
|
|
299
|
+
errors.push('headless.git_integration must be a boolean');
|
|
300
|
+
}
|
|
228
301
|
}
|
|
229
302
|
const mounts = Array.isArray(extraPaths) ? extraPaths : [];
|
|
230
303
|
for (const mount of mounts) {
|
|
@@ -239,6 +312,52 @@ function validateConfig(config) {
|
|
|
239
312
|
errors.push(`Invalid mount mode "${mount.mode}" for path "${mount.path}". Must be "rw" or "ro"`);
|
|
240
313
|
}
|
|
241
314
|
}
|
|
315
|
+
// ── Automation validation ────────────────────────────────
|
|
316
|
+
const automation = config.automation;
|
|
317
|
+
if (automation !== undefined) {
|
|
318
|
+
if (!automation || typeof automation !== 'object' || Array.isArray(automation)) {
|
|
319
|
+
errors.push('automation must be an object');
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
if (typeof automation.model !== 'string' || !automation.model) {
|
|
323
|
+
errors.push('automation.model must be a non-empty string');
|
|
324
|
+
}
|
|
325
|
+
if (typeof automation.system_prompt !== 'string') {
|
|
326
|
+
errors.push('automation.system_prompt must be a string');
|
|
327
|
+
}
|
|
328
|
+
if (!Array.isArray(automation.trigger_patterns)) {
|
|
329
|
+
errors.push('automation.trigger_patterns must be an array');
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
for (let i = 0; i < automation.trigger_patterns.length; i++) {
|
|
333
|
+
const pat = automation.trigger_patterns[i];
|
|
334
|
+
if (typeof pat !== 'string') {
|
|
335
|
+
errors.push(`automation.trigger_patterns[${i}] must be a string`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
try {
|
|
339
|
+
new RegExp(pat);
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
errors.push(`automation.trigger_patterns[${i}] is not a valid regex: "${pat}"`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (typeof automation.context_lines !== 'number' || automation.context_lines < 1) {
|
|
348
|
+
errors.push('automation.context_lines must be a number >= 1');
|
|
349
|
+
}
|
|
350
|
+
if (typeof automation.delay_ms !== 'number' || automation.delay_ms < 0) {
|
|
351
|
+
errors.push('automation.delay_ms must be a non-negative number');
|
|
352
|
+
}
|
|
353
|
+
if (typeof automation.max_turns !== 'number' || automation.max_turns < 1) {
|
|
354
|
+
errors.push('automation.max_turns must be a number >= 1');
|
|
355
|
+
}
|
|
356
|
+
if (typeof automation.max_tokens !== 'number' || automation.max_tokens < 1) {
|
|
357
|
+
errors.push('automation.max_tokens must be a number >= 1');
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
242
361
|
// ── SLURM validation ─────────────────────────────────────
|
|
243
362
|
if (config.slurm) {
|
|
244
363
|
if (typeof config.slurm.poll_interval_seconds !== 'number' || config.slurm.poll_interval_seconds < 1) {
|
|
@@ -338,11 +457,28 @@ function loadConfig() {
|
|
|
338
457
|
enabled: raw.audit?.enabled ?? exports.DEFAULT_CONFIG.audit.enabled,
|
|
339
458
|
log_dir: raw.audit?.log_dir ?? exports.DEFAULT_CONFIG.audit.log_dir,
|
|
340
459
|
},
|
|
460
|
+
automation: {
|
|
461
|
+
api_key: raw.automation?.api_key ?? exports.DEFAULT_CONFIG.automation?.api_key,
|
|
462
|
+
model: raw.automation?.model ?? exports.DEFAULT_CONFIG.automation.model,
|
|
463
|
+
system_prompt: raw.automation?.system_prompt ?? exports.DEFAULT_CONFIG.automation.system_prompt,
|
|
464
|
+
trigger_patterns: raw.automation?.trigger_patterns ?? [...exports.DEFAULT_CONFIG.automation.trigger_patterns],
|
|
465
|
+
context_lines: raw.automation?.context_lines ?? exports.DEFAULT_CONFIG.automation.context_lines,
|
|
466
|
+
delay_ms: raw.automation?.delay_ms ?? exports.DEFAULT_CONFIG.automation.delay_ms,
|
|
467
|
+
max_turns: raw.automation?.max_turns ?? exports.DEFAULT_CONFIG.automation.max_turns,
|
|
468
|
+
max_tokens: raw.automation?.max_tokens ?? exports.DEFAULT_CONFIG.automation.max_tokens,
|
|
469
|
+
},
|
|
341
470
|
headless: {
|
|
342
471
|
claude_run_with_allowed_permissions: raw.headless?.claude_run_with_allowed_permissions ??
|
|
343
472
|
exports.DEFAULT_CONFIG.headless?.claude_run_with_allowed_permissions ??
|
|
344
473
|
true,
|
|
474
|
+
continuation_in_other_terminals: raw.headless?.continuation_in_other_terminals ??
|
|
475
|
+
exports.DEFAULT_CONFIG.headless?.continuation_in_other_terminals ??
|
|
476
|
+
true,
|
|
477
|
+
git_integration: raw.headless?.git_integration ??
|
|
478
|
+
exports.DEFAULT_CONFIG.headless?.git_integration ??
|
|
479
|
+
false,
|
|
345
480
|
},
|
|
481
|
+
plugins: sanitizePluginMap(raw.plugins),
|
|
346
482
|
};
|
|
347
483
|
const errors = validateConfig(config);
|
|
348
484
|
if (errors.length > 0) {
|