orquesta-agent 0.2.211 → 0.2.213
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/claude-transcript.d.ts +28 -0
- package/dist/claude-transcript.d.ts.map +1 -0
- package/dist/claude-transcript.js +249 -0
- package/dist/claude-transcript.js.map +1 -0
- package/dist/daemon.d.ts +18 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +59 -42
- package/dist/daemon.js.map +1 -1
- package/dist/executor.d.ts +1 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +122 -15
- package/dist/executor.js.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/supabase.d.ts +10 -0
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js +25 -0
- package/dist/supabase.js.map +1 -1
- package/package.json +1 -1
package/dist/executor.js
CHANGED
|
@@ -30,6 +30,7 @@ async function loadPtySpawn() {
|
|
|
30
30
|
import * as fs from 'fs';
|
|
31
31
|
import * as path from 'path';
|
|
32
32
|
import * as os from 'os';
|
|
33
|
+
import { randomUUID } from 'crypto';
|
|
33
34
|
import { fileURLToPath } from 'url';
|
|
34
35
|
import { createRequire } from 'module';
|
|
35
36
|
import * as logger from './logger.js';
|
|
@@ -39,7 +40,8 @@ const nodeRequire = createRequire(import.meta.url);
|
|
|
39
40
|
import { isSandboxAvailable, buildBwrapArgs, shQuote, ensureStrictProjectDirs, encodeClaudeProjectDir } from './sandbox.js';
|
|
40
41
|
import { parseCoordSpec, runCoordination } from './coordination.js';
|
|
41
42
|
import { parseSudosudoInstallSpec, runSudosudoInstall } from './sudosudo.js';
|
|
42
|
-
import { sendOutput, sendComplete, sendError, sendSupervisionRequest, sendExecutionResumed, updatePromptStatus, persistOutputLogs, clearOutputBuffer, sendRequirement, persistRequirement, sendQAInstructions, persistQAInstructions, sendPlanItemsGenerated, sendSessionOutput, sendSessionStarted, sendSessionEnded, sendSessionError, reportAgentError } from './supabase.js';
|
|
43
|
+
import { sendOutput, sendComplete, sendError, sendSupervisionRequest, sendExecutionResumed, updatePromptStatus, persistOutputLogs, clearOutputBuffer, sendRequirement, persistRequirement, sendQAInstructions, persistQAInstructions, sendPlanItemsGenerated, sendSessionOutput, sendSessionStarted, sendSessionEnded, sendSessionError, sendSessionLog, reportAgentError } from './supabase.js';
|
|
44
|
+
import { startTranscriptTail } from './claude-transcript.js';
|
|
43
45
|
import { createThinkingLog, createToolCallLog, createToolResultLog, createOutputLog, createErrorLog, createSystemLog, } from './types/agent-logs.js';
|
|
44
46
|
/**
|
|
45
47
|
* Build args for the `script` PTY wrapper, accounting for BSD vs util-linux differences.
|
|
@@ -668,17 +670,65 @@ export function isOrquestaCliAvailable() {
|
|
|
668
670
|
orquestaCliCache = { available, at: now };
|
|
669
671
|
return available;
|
|
670
672
|
}
|
|
673
|
+
// Resolve a claude binary that ACTUALLY runs — not one that merely exists on
|
|
674
|
+
// PATH. The claude-code npm package's `bin/claude` launcher can be broken: a
|
|
675
|
+
// Windows `claude.exe` shim (ASCII, no +x) landed on a Linux box, so a bare
|
|
676
|
+
// `claude` resolves to it and every spawn dies instantly (exit 126, or exit 0 /
|
|
677
|
+
// no output) while the REAL native binary ships fine as an optional platform
|
|
678
|
+
// subpackage (@anthropic-ai/claude-code-<plat>-<arch>/claude). The old detection
|
|
679
|
+
// just ran `claude --version` and, when it (briefly) passed, reported the CLI as
|
|
680
|
+
// available+authenticated — so the dashboard showed "authenticated" while
|
|
681
|
+
// interactive sessions "exited early". Field report: EHive DLM server,
|
|
682
|
+
// 2026-06-15. Resolve a binary that runs `--version`, preferring PATH, then the
|
|
683
|
+
// platform subpackage; return null if none works (honest "no CLI").
|
|
684
|
+
let claudeBinaryCache = null;
|
|
685
|
+
export function resolveClaudeBinary() {
|
|
686
|
+
const now = Date.now();
|
|
687
|
+
if (claudeBinaryCache && now - claudeBinaryCache.at < CLI_DETECTION_TTL_MS)
|
|
688
|
+
return claudeBinaryCache.path;
|
|
689
|
+
let resolved = null;
|
|
690
|
+
// 1. `claude` on PATH that actually executes (the broken launcher exists but
|
|
691
|
+
// exit-126s, which --version surfaces — so this rejects it).
|
|
692
|
+
try {
|
|
693
|
+
execSync('claude --version', { stdio: 'pipe', timeout: 15000 });
|
|
694
|
+
resolved = 'claude';
|
|
695
|
+
}
|
|
696
|
+
catch { /* missing or broken launcher */ }
|
|
697
|
+
// 2. PATH claude missing/broken → run the native binary claude-code ships as an
|
|
698
|
+
// optional platform subpackage, directly.
|
|
699
|
+
if (!resolved) {
|
|
700
|
+
const exe = process.platform === 'win32' ? 'claude.exe' : 'claude';
|
|
701
|
+
const sub = `claude-code-${process.platform}-${process.arch}`;
|
|
702
|
+
const roots = [];
|
|
703
|
+
try {
|
|
704
|
+
const p = execSync('npm root -g', { stdio: 'pipe', timeout: 5000, encoding: 'utf-8' }).trim();
|
|
705
|
+
if (p)
|
|
706
|
+
roots.push(p);
|
|
707
|
+
}
|
|
708
|
+
catch { /* npm slow/absent */ }
|
|
709
|
+
roots.push(path.join(os.homedir(), '.npm-global', 'lib', 'node_modules'), '/usr/local/lib/node_modules', '/usr/lib/node_modules');
|
|
710
|
+
for (const root of roots) {
|
|
711
|
+
const cand = path.join(root, '@anthropic-ai', 'claude-code', 'node_modules', '@anthropic-ai', sub, exe);
|
|
712
|
+
try {
|
|
713
|
+
fs.accessSync(cand, fs.constants.X_OK);
|
|
714
|
+
execSync(`"${cand}" --version`, { stdio: 'pipe', timeout: 15000 });
|
|
715
|
+
resolved = cand;
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
catch { /* not under this root / not runnable */ }
|
|
719
|
+
}
|
|
720
|
+
if (resolved)
|
|
721
|
+
warnCliOnce(`claude on PATH is broken or wrong-platform; using native binary ${resolved}. Reinstall to fix: npm i -g @anthropic-ai/claude-code`);
|
|
722
|
+
}
|
|
723
|
+
claudeBinaryCache = { path: resolved, at: now };
|
|
724
|
+
return resolved;
|
|
725
|
+
}
|
|
671
726
|
export function isClaudeCliAvailable() {
|
|
672
727
|
const now = Date.now();
|
|
673
728
|
if (claudeCliCache && now - claudeCliCache.at < CLI_DETECTION_TTL_MS) {
|
|
674
729
|
return claudeCliCache.available;
|
|
675
730
|
}
|
|
676
|
-
|
|
677
|
-
try {
|
|
678
|
-
execSync('claude --version', { stdio: 'pipe', timeout: 15000 });
|
|
679
|
-
available = true;
|
|
680
|
-
}
|
|
681
|
-
catch { /* not installed or detection slow */ }
|
|
731
|
+
const available = resolveClaudeBinary() !== null;
|
|
682
732
|
claudeCliCache = { available, at: now };
|
|
683
733
|
return available;
|
|
684
734
|
}
|
|
@@ -797,9 +847,11 @@ export function checkClaudeAuth() {
|
|
|
797
847
|
// Preferred: ask the CLI directly. Claude Code v2.x stores credentials
|
|
798
848
|
// in the OS keychain (libsecret on Linux, Keychain on macOS) and the
|
|
799
849
|
// legacy ~/.claude/.credentials.json file may not exist. `claude auth
|
|
800
|
-
// status --json` resolves auth regardless of storage backend.
|
|
850
|
+
// status --json` resolves auth regardless of storage backend. Use the
|
|
851
|
+
// resolved binary (PATH `claude` may be the broken launcher).
|
|
801
852
|
try {
|
|
802
|
-
const
|
|
853
|
+
const claudeBin = resolveClaudeBinary() || 'claude';
|
|
854
|
+
const out = execSync(`"${claudeBin}" auth status --json`, {
|
|
803
855
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
804
856
|
timeout: 5000,
|
|
805
857
|
encoding: 'utf-8',
|
|
@@ -1687,9 +1739,14 @@ ${userRequestBody}`;
|
|
|
1687
1739
|
}
|
|
1688
1740
|
const cliCommand = selectedCli;
|
|
1689
1741
|
// The string used to actually INVOKE the CLI. kimi usually isn't on PATH
|
|
1690
|
-
// (installed at ~/.kimi-code/bin)
|
|
1691
|
-
//
|
|
1692
|
-
|
|
1742
|
+
// (installed at ~/.kimi-code/bin) and claude's PATH launcher may be broken, so
|
|
1743
|
+
// resolve their real absolute paths; orquesta is invoked by name as before.
|
|
1744
|
+
// (cliCommand stays the TYPE for flag decisions.)
|
|
1745
|
+
const cliBinary = cliCommand === 'kimi'
|
|
1746
|
+
? (resolveKimiBinary() || 'kimi')
|
|
1747
|
+
: cliCommand === 'claude'
|
|
1748
|
+
? (resolveClaudeBinary() || 'claude')
|
|
1749
|
+
: (cliCommand || 'claude');
|
|
1693
1750
|
const cwd = workingDirectory || process.cwd();
|
|
1694
1751
|
logger.info(`CLI Selection: ${cliCommand} (${reason})`);
|
|
1695
1752
|
logger.info(`Spawning: ${cliBinary} -p "${fullContent.slice(0, 50)}..." in ${cwd}`);
|
|
@@ -3187,9 +3244,14 @@ export async function startSession(options) {
|
|
|
3187
3244
|
const { cli: selectedCli } = selectCli();
|
|
3188
3245
|
const cliCommand = selectedCli || 'claude';
|
|
3189
3246
|
// Actual invocation string — kimi usually isn't on PATH (installed at
|
|
3190
|
-
// ~/.kimi-code/bin)
|
|
3191
|
-
//
|
|
3192
|
-
|
|
3247
|
+
// ~/.kimi-code/bin) and claude's PATH launcher may be broken/wrong-platform, so
|
|
3248
|
+
// resolve their real absolute paths. The absolute path also resolves identically
|
|
3249
|
+
// inside bwrap (/ is ro-bound). cliCommand stays the TYPE.
|
|
3250
|
+
const cliBinary = cliCommand === 'kimi'
|
|
3251
|
+
? (resolveKimiBinary() || 'kimi')
|
|
3252
|
+
: cliCommand === 'claude'
|
|
3253
|
+
? (resolveClaudeBinary() || 'claude')
|
|
3254
|
+
: cliCommand;
|
|
3193
3255
|
logger.info(`Interactive session CLI: ${cliCommand}${cliCommand === 'kimi' ? ` (${cliBinary})` : ''}`);
|
|
3194
3256
|
// Best-effort model + cli label reported in session:started so the dashboard
|
|
3195
3257
|
// can tag the interactive prompt (same mapping dispatched prompts use on
|
|
@@ -3323,6 +3385,23 @@ export async function startSession(options) {
|
|
|
3323
3385
|
logger.info(`[Session] Resuming most recent conversation (--continue) for ${cliCommand}`);
|
|
3324
3386
|
}
|
|
3325
3387
|
}
|
|
3388
|
+
// Pin the transcript file name so the structured-log tailer
|
|
3389
|
+
// (claude-transcript.ts) knows exactly which ~/.claude/projects/<cwd>/<id>.jsonl
|
|
3390
|
+
// to follow. claude only:
|
|
3391
|
+
// - --resume <id>: claude appends to that same <id>.jsonl → tail it directly.
|
|
3392
|
+
// - fresh session: mint a uuid + pass --session-id (can't combine with resume).
|
|
3393
|
+
// - plain --continue: claude reattaches its OWN prior id (unknown to us) → null,
|
|
3394
|
+
// and the tailer adopts the newest transcript touched since startup.
|
|
3395
|
+
let claudeTranscriptId = null;
|
|
3396
|
+
if (cliCommand === 'claude') {
|
|
3397
|
+
if (resumeSessionId) {
|
|
3398
|
+
claudeTranscriptId = resumeSessionId;
|
|
3399
|
+
}
|
|
3400
|
+
else if (!resume) {
|
|
3401
|
+
claudeTranscriptId = randomUUID();
|
|
3402
|
+
ptyArgs.push('--session-id', claudeTranscriptId);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3326
3405
|
// Per-project endpoint override (only the orquesta CLI understands --endpoint).
|
|
3327
3406
|
if (cliCommand === 'orquesta' && globalCliEndpoint) {
|
|
3328
3407
|
ptyArgs.push('--endpoint', globalCliEndpoint);
|
|
@@ -3407,6 +3486,7 @@ export async function startSession(options) {
|
|
|
3407
3486
|
pendingPhoneTimer: null,
|
|
3408
3487
|
lastPtyOutputAt: Date.now(),
|
|
3409
3488
|
rogerthatWatcher: null,
|
|
3489
|
+
transcriptTail: null,
|
|
3410
3490
|
};
|
|
3411
3491
|
sessions.set(sessionId, session);
|
|
3412
3492
|
ensureSessionReaper(); // reap this session if it's later abandoned without session:end
|
|
@@ -3414,6 +3494,25 @@ export async function startSession(options) {
|
|
|
3414
3494
|
// the PTY as if the operator had typed it, so they can hold a full
|
|
3415
3495
|
// conversation from the phone without touching the dashboard.
|
|
3416
3496
|
session.rogerthatWatcher = startRogerthatMonitor(sessionId);
|
|
3497
|
+
// Tail claude's on-disk transcript and broadcast STRUCTURED logs (tool_call /
|
|
3498
|
+
// tool_result / output / thinking) so the dashboard persists a rich timeline
|
|
3499
|
+
// instead of scraped terminal chrome. claude only: orquesta-cli self-reports
|
|
3500
|
+
// via its prompt-reporter, and kimi has no structured transcript yet.
|
|
3501
|
+
if (cliCommand === 'claude') {
|
|
3502
|
+
session.transcriptTail = startTranscriptTail({
|
|
3503
|
+
sessionFileId: claudeTranscriptId,
|
|
3504
|
+
startedAtMs: startTime,
|
|
3505
|
+
// The agent has no per-turn promptId (the dashboard owns it), so we tag the
|
|
3506
|
+
// broadcast with none — the dashboard attaches each entry to its active prompt.
|
|
3507
|
+
getPromptId: () => undefined,
|
|
3508
|
+
onLog: (entry) => {
|
|
3509
|
+
if (!sessions.get(sessionId)?.isActive)
|
|
3510
|
+
return;
|
|
3511
|
+
void sendSessionLog(channel, sessionId, entry);
|
|
3512
|
+
},
|
|
3513
|
+
log: (msg) => logger.debug(`[Session ${sessionId}] ${msg}`),
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3417
3516
|
// Send session:started only after Claude produces its first output.
|
|
3418
3517
|
// This ensures the UI doesn't activate the input field before Claude is
|
|
3419
3518
|
// actually ready — early input would otherwise be silently ignored.
|
|
@@ -3499,6 +3598,10 @@ export async function startSession(options) {
|
|
|
3499
3598
|
exitingSession.rogerthatWatcher?.close();
|
|
3500
3599
|
}
|
|
3501
3600
|
catch { /* already closed */ }
|
|
3601
|
+
try {
|
|
3602
|
+
exitingSession.transcriptTail?.stop();
|
|
3603
|
+
}
|
|
3604
|
+
catch { /* already stopped */ }
|
|
3502
3605
|
if (exitingSession.pendingPhoneTimer) {
|
|
3503
3606
|
clearTimeout(exitingSession.pendingPhoneTimer);
|
|
3504
3607
|
exitingSession.pendingPhoneTimer = null;
|
|
@@ -3822,6 +3925,10 @@ export function terminateSession(sessionId) {
|
|
|
3822
3925
|
s.rogerthatWatcher?.close();
|
|
3823
3926
|
}
|
|
3824
3927
|
catch { /* already closed */ }
|
|
3928
|
+
try {
|
|
3929
|
+
s.transcriptTail?.stop();
|
|
3930
|
+
}
|
|
3931
|
+
catch { /* already stopped */ }
|
|
3825
3932
|
if (s.pendingPhoneTimer) {
|
|
3826
3933
|
clearTimeout(s.pendingPhoneTimer);
|
|
3827
3934
|
s.pendingPhoneTimer = null;
|