orquesta-agent 0.2.114 → 0.2.117
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/dist/executor.d.ts +3 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +141 -19
- package/dist/executor.js.map +1 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts +56 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +147 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/supabase.d.ts +4 -0
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js +8 -0
- package/dist/supabase.js.map +1 -1
- package/dist/ui/manager.d.ts.map +1 -1
- package/dist/ui/manager.js +6 -0
- package/dist/ui/manager.js.map +1 -1
- package/dist/ui/public/app.js +16 -0
- package/dist/ui/public/index.html +7 -0
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/ui/server.js +16 -1
- package/dist/ui/server.js.map +1 -1
- package/dist/ui/types.d.ts +6 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/executor.d.ts
CHANGED
|
@@ -45,6 +45,9 @@ export interface AuthConfig {
|
|
|
45
45
|
}
|
|
46
46
|
export declare function configureAuth(config: AuthConfig): void;
|
|
47
47
|
export declare function setPermissionMode(mode: PermissionMode): void;
|
|
48
|
+
export declare function setSandboxConfig(enabled: boolean, extraPaths?: string[]): void;
|
|
49
|
+
/** Reported to the dashboard so the UI can show Active / Unavailable / Off. */
|
|
50
|
+
export declare function getSandboxStatus(): 'active' | 'unavailable' | 'off';
|
|
48
51
|
export type CliPreference = 'auto' | 'orquesta' | 'claude';
|
|
49
52
|
export declare function setCliPreference(preference: CliPreference): void;
|
|
50
53
|
export declare function isOrquestaCliAvailable(): boolean;
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
CHANGED
|
@@ -17,6 +17,7 @@ import * as fs from 'fs';
|
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
import * as os from 'os';
|
|
19
19
|
import * as logger from './logger.js';
|
|
20
|
+
import { isSandboxAvailable, buildBwrapArgs, shQuote } from './sandbox.js';
|
|
20
21
|
import { sendOutput, sendComplete, sendError, sendSupervisionRequest, sendExecutionResumed, updatePromptStatus, persistOutputLogs, clearOutputBuffer, sendRequirement, persistRequirement, sendQAInstructions, persistQAInstructions, sendPlanItemsGenerated, sendSessionOutput, sendSessionStarted, sendSessionEnded, sendSessionError } from './supabase.js';
|
|
21
22
|
import { createThinkingLog, createToolCallLog, createToolResultLog, createOutputLog, createErrorLog, createSystemLog, } from './types/agent-logs.js';
|
|
22
23
|
/**
|
|
@@ -301,6 +302,66 @@ export function setPermissionMode(mode) {
|
|
|
301
302
|
globalPermissionMode = mode;
|
|
302
303
|
logger.info(`Permission mode set to: ${mode}`);
|
|
303
304
|
}
|
|
305
|
+
// Filesystem sandbox (bubblewrap). When enabled, every spawned CLI is confined
|
|
306
|
+
// to its working directory for writes; the rest of the host is read-only.
|
|
307
|
+
let globalSandbox = false;
|
|
308
|
+
let globalSandboxExtraPaths = [];
|
|
309
|
+
let sandboxUnavailableWarned = false;
|
|
310
|
+
// Set global sandbox config
|
|
311
|
+
export function setSandboxConfig(enabled, extraPaths = []) {
|
|
312
|
+
globalSandbox = enabled;
|
|
313
|
+
globalSandboxExtraPaths = Array.isArray(extraPaths) ? extraPaths : [];
|
|
314
|
+
if (enabled) {
|
|
315
|
+
logger.info(`Sandbox enabled${globalSandboxExtraPaths.length ? ` (extra rw: ${globalSandboxExtraPaths.join(', ')})` : ''}`);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
logger.info('Sandbox disabled');
|
|
319
|
+
}
|
|
320
|
+
sandboxUnavailableWarned = false;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Whether the sandbox should actually wrap spawns right now: enabled by config
|
|
324
|
+
* AND bwrap usable on this host. Logs a one-time warning (per config change)
|
|
325
|
+
* when enabled but unavailable, then falls back to running unsandboxed.
|
|
326
|
+
*/
|
|
327
|
+
function sandboxActive() {
|
|
328
|
+
if (!globalSandbox)
|
|
329
|
+
return false;
|
|
330
|
+
if (isSandboxAvailable())
|
|
331
|
+
return true;
|
|
332
|
+
if (!sandboxUnavailableWarned) {
|
|
333
|
+
logger.warn('Sandbox enabled but bwrap not found — running without isolation. Install bubblewrap (apt install bubblewrap).');
|
|
334
|
+
sandboxUnavailableWarned = true;
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
/** Reported to the dashboard so the UI can show Active / Unavailable / Off. */
|
|
339
|
+
export function getSandboxStatus() {
|
|
340
|
+
if (!globalSandbox)
|
|
341
|
+
return 'off';
|
|
342
|
+
return isSandboxAvailable() ? 'active' : 'unavailable';
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Wrap an argv-style spawn (file + args) in bwrap when the sandbox is active.
|
|
346
|
+
* Returns the file/args to actually pass to spawn()/ptySpawn().
|
|
347
|
+
*/
|
|
348
|
+
function sandboxArgv(file, args, cwd) {
|
|
349
|
+
if (!sandboxActive())
|
|
350
|
+
return { file, args };
|
|
351
|
+
const bwrap = buildBwrapArgs({ workingDir: cwd, extraWritablePaths: globalSandboxExtraPaths });
|
|
352
|
+
return { file: 'bwrap', args: [...bwrap, file, ...args] };
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Prefix a shell command string with the bwrap invocation when the sandbox is
|
|
356
|
+
* active (for the execSync `script -q -c "…" /dev/null` call sites). Returns ''
|
|
357
|
+
* when inactive, so callers just template `${prefix}script …`.
|
|
358
|
+
*/
|
|
359
|
+
function sandboxShellPrefix(cwd) {
|
|
360
|
+
if (!sandboxActive())
|
|
361
|
+
return '';
|
|
362
|
+
const bwrap = buildBwrapArgs({ workingDir: cwd, extraWritablePaths: globalSandboxExtraPaths });
|
|
363
|
+
return `bwrap ${bwrap.map(shQuote).join(' ')} `;
|
|
364
|
+
}
|
|
304
365
|
let globalCliPreference = 'auto';
|
|
305
366
|
// Set CLI preference
|
|
306
367
|
export function setCliPreference(preference) {
|
|
@@ -1256,7 +1317,13 @@ ${userRequestBody}`;
|
|
|
1256
1317
|
});
|
|
1257
1318
|
}
|
|
1258
1319
|
else {
|
|
1259
|
-
|
|
1320
|
+
// Sandbox wraps the `script` invocation in bwrap when active (Linux only);
|
|
1321
|
+
// no-op otherwise.
|
|
1322
|
+
const { file: spawnFile, args: spawnArgs } = sandboxArgv('script', scriptWrapArgs(command), cwd);
|
|
1323
|
+
if (spawnFile === 'bwrap') {
|
|
1324
|
+
logger.info(`Sandbox active — confining writes to ${cwd}`);
|
|
1325
|
+
}
|
|
1326
|
+
claude = spawn(spawnFile, spawnArgs, {
|
|
1260
1327
|
cwd,
|
|
1261
1328
|
env,
|
|
1262
1329
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -1979,7 +2046,7 @@ Return ONLY valid JSON in this exact format (no markdown, no explanation):
|
|
|
1979
2046
|
const command = `${cliCommand} -p '${escapedContent}' --dangerously-skip-permissions --output-format text`;
|
|
1980
2047
|
const { execSync } = await import('child_process');
|
|
1981
2048
|
try {
|
|
1982
|
-
const output = execSync(
|
|
2049
|
+
const output = execSync(`${sandboxShellPrefix(cwd)}script -q -c "${command.replace(/"/g, '\\"')}" /dev/null`, {
|
|
1983
2050
|
cwd,
|
|
1984
2051
|
env,
|
|
1985
2052
|
timeout: 60000, // 60 second timeout for evaluation
|
|
@@ -2193,7 +2260,7 @@ export async function generatePlanItems(options) {
|
|
|
2193
2260
|
const command = `${cliCommand} -p '${escapedContent}' --dangerously-skip-permissions --output-format text`;
|
|
2194
2261
|
logger.debug(`Running Claude CLI...`);
|
|
2195
2262
|
try {
|
|
2196
|
-
const output = execSync(
|
|
2263
|
+
const output = execSync(`${sandboxShellPrefix(cwd)}script -q -c "${command.replace(/"/g, '\\"')}" /dev/null`, {
|
|
2197
2264
|
cwd,
|
|
2198
2265
|
env,
|
|
2199
2266
|
timeout: 120000, // 2 minute timeout for generation
|
|
@@ -2503,13 +2570,41 @@ export async function startSession(options) {
|
|
|
2503
2570
|
// fetchCoordinationContextBlock has its own 1.5s timeout and returns '' on failure,
|
|
2504
2571
|
// so even if the Orquesta API is slow it won't block session startup by much.
|
|
2505
2572
|
const liveCoordination = await fetchCoordinationContextBlock();
|
|
2506
|
-
|
|
2573
|
+
// Read local CLAUDE.md / AGENT.md from the working directory so the interactive
|
|
2574
|
+
// session starts with project conventions loaded. This mirrors what orquesta-cli's
|
|
2575
|
+
// buildSystemPrompt does, but guarantees the content is present even when the
|
|
2576
|
+
// CLI's own file discovery fails or the cache is stale.
|
|
2577
|
+
const CANDIDATE_MD_FILES = ['CLAUDE.md', '.claude/CLAUDE.md', 'AGENT.md', 'agent.md'];
|
|
2578
|
+
let projectMdContent = '';
|
|
2579
|
+
for (const rel of CANDIDATE_MD_FILES) {
|
|
2580
|
+
const abs = path.join(cwd, rel);
|
|
2581
|
+
try {
|
|
2582
|
+
const stat = fs.statSync(abs);
|
|
2583
|
+
if (stat.isFile()) {
|
|
2584
|
+
let content = fs.readFileSync(abs, 'utf-8');
|
|
2585
|
+
if (Buffer.byteLength(content, 'utf-8') > 32_000) {
|
|
2586
|
+
content = content.slice(0, 32_000) + '\n\n[...truncated for context window]';
|
|
2587
|
+
}
|
|
2588
|
+
projectMdContent = `\n\n## PROJECT CONTEXT\n(from ${rel} — treat as authoritative project conventions)\n\n${content}\n`;
|
|
2589
|
+
break;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
catch { /* file not found — try next */ }
|
|
2593
|
+
}
|
|
2594
|
+
const systemPromptBlock = `${BROWSER_TOOLS_PROMPT_BLOCK}\n\n${ORQUESTA_CAPABILITIES_PROMPT_BLOCK}${liveCoordination}${projectMdContent}`;
|
|
2507
2595
|
const ptyArgs = [
|
|
2508
2596
|
'--dangerously-skip-permissions',
|
|
2509
2597
|
'--append-system-prompt', systemPromptBlock,
|
|
2510
2598
|
];
|
|
2511
2599
|
try {
|
|
2512
|
-
|
|
2600
|
+
// Sandbox the interactive session too. node-pty allocates the pty on the
|
|
2601
|
+
// host side and bwrap (when active) inherits the slave as its stdio; bwrap's
|
|
2602
|
+
// own `--dev /dev` then exposes a working tty to the CLI inside the namespace.
|
|
2603
|
+
const { file: ptyFile, args: ptyArgsFinal } = sandboxArgv(cliCommand, ptyArgs, cwd);
|
|
2604
|
+
if (ptyFile === 'bwrap') {
|
|
2605
|
+
logger.info(`Sandbox active for interactive session — confining writes to ${cwd}`);
|
|
2606
|
+
}
|
|
2607
|
+
ptyProcess = ptySpawn(ptyFile, ptyArgsFinal, {
|
|
2513
2608
|
name: 'xterm-256color',
|
|
2514
2609
|
cols,
|
|
2515
2610
|
rows,
|
|
@@ -2710,28 +2805,55 @@ function writeSessionText(sessionId, input) {
|
|
|
2710
2805
|
if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
|
|
2711
2806
|
return;
|
|
2712
2807
|
const text = input.endsWith('\r') ? input.slice(0, -1) : input;
|
|
2808
|
+
const isOrquesta = activeSession.cliType === 'orquesta';
|
|
2713
2809
|
// Bracketed paste for non-trivial inputs. Claude's TUI uses it to treat a long
|
|
2714
2810
|
// block as a coherent paste. But orquesta-cli's Ink stack SWALLOWS the
|
|
2715
2811
|
// \x1b[200~…\x1b[201~ sequence at its own stdin layer — the text never reaches
|
|
2716
2812
|
// its input handler, so a long message silently fails to appear/submit. For
|
|
2717
2813
|
// orquesta-cli, send the raw text instead (its input handles multi-char chunks
|
|
2718
2814
|
// fine; the separate \r below submits).
|
|
2719
|
-
const usePasteMarkers = text.length > 200 &&
|
|
2815
|
+
const usePasteMarkers = text.length > 200 && !isOrquesta;
|
|
2720
2816
|
const payload = usePasteMarkers ? `\x1b[200~${text}\x1b[201~` : text;
|
|
2721
2817
|
activeSession.ptyProcess.write(payload);
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2818
|
+
if (isOrquesta) {
|
|
2819
|
+
// orquesta-cli (Ink) drops stdin events while it is rendering / processing.
|
|
2820
|
+
// If we fire \r during an active turn the Enter is queued and only surfaces
|
|
2821
|
+
// when the turn finishes, so the user sees their message "appear late".
|
|
2822
|
+
// Fix: wait until the PTY has been quiet for a short window (300 ms) before
|
|
2823
|
+
// sending \r. While the LLM is streaming or a tool is running there is
|
|
2824
|
+
// constant output; the quiet window only happens when Ink is idle and ready
|
|
2825
|
+
// to read stdin. We poll every 100 ms with a hard ceiling so we never hang.
|
|
2826
|
+
const IDLE_THRESHOLD_MS = 300;
|
|
2827
|
+
const POLL_INTERVAL_MS = 100;
|
|
2828
|
+
const MAX_WAIT_MS = 5000;
|
|
2829
|
+
const startAt = Date.now();
|
|
2830
|
+
const trySubmit = () => {
|
|
2831
|
+
if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
|
|
2832
|
+
return;
|
|
2833
|
+
const elapsed = Date.now() - startAt;
|
|
2834
|
+
const idleFor = Date.now() - (activeSession.lastPtyOutputAt ?? 0);
|
|
2835
|
+
if (idleFor >= IDLE_THRESHOLD_MS || elapsed >= MAX_WAIT_MS) {
|
|
2836
|
+
activeSession.ptyProcess.write('\r');
|
|
2837
|
+
logger.info(`Session input submitted (${text.length} chars, idle=${idleFor}ms, elapsed=${elapsed}ms)`);
|
|
2838
|
+
}
|
|
2839
|
+
else {
|
|
2840
|
+
setTimeout(trySubmit, POLL_INTERVAL_MS);
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
setTimeout(trySubmit, POLL_INTERVAL_MS);
|
|
2844
|
+
}
|
|
2845
|
+
else {
|
|
2846
|
+
// Claude CLI — scale the wait: base 50ms + ~1ms per 20 chars + 80ms per newline.
|
|
2847
|
+
// Multi-line pastes need more time before the \r submit. Capped at 800ms.
|
|
2848
|
+
const newlineCount = (text.match(/\n/g) || []).length;
|
|
2849
|
+
const delayMs = Math.min(800, 50 + Math.floor(text.length / 20) + newlineCount * 80);
|
|
2850
|
+
setTimeout(() => {
|
|
2851
|
+
if (activeSession?.sessionId === sessionId && activeSession.isActive) {
|
|
2852
|
+
activeSession.ptyProcess.write('\r');
|
|
2853
|
+
}
|
|
2854
|
+
}, delayMs);
|
|
2855
|
+
}
|
|
2856
|
+
logger.info(`Session input written (${text.length} chars${usePasteMarkers ? ', bracketed' : ''}): ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`);
|
|
2735
2857
|
}
|
|
2736
2858
|
/**
|
|
2737
2859
|
* End the active interactive session.
|