agent-worker 0.4.0 → 0.5.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/dist/backends-B8rYTNqn.mjs +3 -0
- package/dist/{backends-DenGdkrj.mjs → backends-CEYiMUgC.mjs} +355 -53
- package/dist/cli/index.mjs +10 -7
- package/dist/index.d.mts +13 -11
- package/dist/index.mjs +2 -2
- package/dist/{skills-VyC7eQyK.mjs → skills-iya7NbH7.mjs} +1 -1
- package/dist/{workflow-CaRCNEh6.mjs → workflow-K1Zd655A.mjs} +18 -10
- package/package.json +1 -1
- package/dist/backends-D3MAlJBX.mjs +0 -3
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { C as getModelForBackend, S as SDK_MODEL_ALIASES, _ as execWithIdleTimeout, a as createMockBackend, b as CODEX_MODEL_MAP, c as CodexBackend, d as codexAdapter, f as createStreamParser, g as IdleTimeoutError, h as formatEvent, i as MockAIBackend, l as ClaudeCodeBackend, m as extractCodexResult, n as createBackend, o as SdkBackend, p as extractClaudeResult, r as listBackends, s as CursorBackend, t as checkBackends, u as claudeAdapter, v as BACKEND_DEFAULT_MODELS, x as CURSOR_MODEL_MAP, y as CLAUDE_MODEL_MAP } from "./backends-CEYiMUgC.mjs";
|
|
2
|
+
|
|
3
|
+
export { listBackends };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { gateway, generateText } from "ai";
|
|
2
|
-
import {
|
|
2
|
+
import { execa } from "execa";
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { stringify } from "yaml";
|
|
@@ -297,6 +297,271 @@ function parseModel(model) {
|
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/backends/idle-timeout.ts
|
|
302
|
+
/**
|
|
303
|
+
* Idle timeout for CLI subprocess execution
|
|
304
|
+
*
|
|
305
|
+
* Unlike a hard timeout (kill after N ms total), an idle timeout only fires
|
|
306
|
+
* when the process produces no stdout/stderr output for the configured duration.
|
|
307
|
+
* This allows long-running agent tasks to continue as long as they're actively
|
|
308
|
+
* producing output (tool calls, analysis, etc.).
|
|
309
|
+
*/
|
|
310
|
+
/**
|
|
311
|
+
* Execute a command with idle timeout.
|
|
312
|
+
*
|
|
313
|
+
* The timeout resets every time the process writes to stdout or stderr.
|
|
314
|
+
* If the process goes silent for longer than `timeout` ms, it's killed.
|
|
315
|
+
*/
|
|
316
|
+
/** Minimum idle timeout to prevent accidental instant kills */
|
|
317
|
+
const MIN_TIMEOUT_MS = 1e3;
|
|
318
|
+
async function execWithIdleTimeout(options) {
|
|
319
|
+
const { command, args, cwd, onStdout } = options;
|
|
320
|
+
const timeout = Math.max(options.timeout, MIN_TIMEOUT_MS);
|
|
321
|
+
let idleTimedOut = false;
|
|
322
|
+
let timer;
|
|
323
|
+
let stdout = "";
|
|
324
|
+
let stderr = "";
|
|
325
|
+
const subprocess = execa(command, args, {
|
|
326
|
+
cwd,
|
|
327
|
+
stdin: "ignore",
|
|
328
|
+
buffer: false
|
|
329
|
+
});
|
|
330
|
+
const resetTimer = () => {
|
|
331
|
+
clearTimeout(timer);
|
|
332
|
+
timer = setTimeout(() => {
|
|
333
|
+
idleTimedOut = true;
|
|
334
|
+
subprocess.kill();
|
|
335
|
+
}, timeout);
|
|
336
|
+
};
|
|
337
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
338
|
+
const text = chunk.toString();
|
|
339
|
+
stdout += text;
|
|
340
|
+
if (onStdout) onStdout(text);
|
|
341
|
+
resetTimer();
|
|
342
|
+
});
|
|
343
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
344
|
+
stderr += chunk.toString();
|
|
345
|
+
resetTimer();
|
|
346
|
+
});
|
|
347
|
+
resetTimer();
|
|
348
|
+
try {
|
|
349
|
+
await subprocess;
|
|
350
|
+
clearTimeout(timer);
|
|
351
|
+
return {
|
|
352
|
+
stdout: stdout.trimEnd(),
|
|
353
|
+
stderr: stderr.trimEnd()
|
|
354
|
+
};
|
|
355
|
+
} catch (error) {
|
|
356
|
+
clearTimeout(timer);
|
|
357
|
+
if (idleTimedOut) throw new IdleTimeoutError(timeout, stdout, stderr);
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Error thrown when a process is killed due to idle timeout
|
|
363
|
+
*/
|
|
364
|
+
var IdleTimeoutError = class extends Error {
|
|
365
|
+
timeout;
|
|
366
|
+
stdout;
|
|
367
|
+
stderr;
|
|
368
|
+
constructor(timeout, stdout, stderr) {
|
|
369
|
+
super(`Process idle timed out after ${timeout}ms of inactivity`);
|
|
370
|
+
this.name = "IdleTimeoutError";
|
|
371
|
+
this.timeout = timeout;
|
|
372
|
+
this.stdout = stdout;
|
|
373
|
+
this.stderr = stderr;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
//#endregion
|
|
378
|
+
//#region src/backends/stream-json.ts
|
|
379
|
+
/**
|
|
380
|
+
* Format a standard StreamEvent into a human-readable progress message.
|
|
381
|
+
* Returns null if the event doesn't need display.
|
|
382
|
+
*
|
|
383
|
+
* This function only knows about StreamEvent — it never touches
|
|
384
|
+
* backend-specific raw JSON. Format-specific conversion is handled
|
|
385
|
+
* by the EventAdapter.
|
|
386
|
+
*/
|
|
387
|
+
function formatEvent(event, backendName) {
|
|
388
|
+
switch (event.kind) {
|
|
389
|
+
case "init": {
|
|
390
|
+
const details = [];
|
|
391
|
+
if (event.model) details.push(`model: ${event.model}`);
|
|
392
|
+
if (event.sessionId) details.push(`session: ${event.sessionId}`);
|
|
393
|
+
return `${backendName} initialized${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
|
|
394
|
+
}
|
|
395
|
+
case "tool_call": {
|
|
396
|
+
const truncated = event.args.length > 100 ? event.args.slice(0, 100) + "..." : event.args;
|
|
397
|
+
return `CALL ${event.name}(${truncated})`;
|
|
398
|
+
}
|
|
399
|
+
case "completed": {
|
|
400
|
+
const parts = [backendName, "completed"];
|
|
401
|
+
const details = [];
|
|
402
|
+
if (event.durationMs) details.push(`${(event.durationMs / 1e3).toFixed(1)}s`);
|
|
403
|
+
if (event.costUsd) details.push(`$${event.costUsd.toFixed(4)}`);
|
|
404
|
+
if (event.usage) details.push(`${event.usage.input} in, ${event.usage.output} out`);
|
|
405
|
+
if (details.length > 0) parts.push(`(${details.join(", ")})`);
|
|
406
|
+
return parts.join(" ");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Adapter for Claude/Cursor stream-json format.
|
|
412
|
+
*
|
|
413
|
+
* Events:
|
|
414
|
+
* { type: "system", subtype: "init", model: "..." }
|
|
415
|
+
* { type: "assistant", message: { content: [{ type: "tool_use", name, input }] } }
|
|
416
|
+
* { type: "result", duration_ms: N, total_cost_usd: N }
|
|
417
|
+
*/
|
|
418
|
+
const claudeAdapter = (raw) => {
|
|
419
|
+
const type = raw.type;
|
|
420
|
+
if (type === "system" && raw.subtype === "init") return {
|
|
421
|
+
kind: "init",
|
|
422
|
+
model: raw.model || void 0,
|
|
423
|
+
sessionId: raw.session_id
|
|
424
|
+
};
|
|
425
|
+
if (type === "assistant") {
|
|
426
|
+
const message = raw.message;
|
|
427
|
+
if (!message?.content) return null;
|
|
428
|
+
const toolCalls = message.content.filter((c) => c.type === "tool_use");
|
|
429
|
+
if (toolCalls.length > 0) {
|
|
430
|
+
const tc = toolCalls[0];
|
|
431
|
+
return {
|
|
432
|
+
kind: "tool_call",
|
|
433
|
+
name: tc.name || "unknown",
|
|
434
|
+
args: formatToolInput(tc.input)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
if (type === "result") return {
|
|
440
|
+
kind: "completed",
|
|
441
|
+
durationMs: raw.duration_ms,
|
|
442
|
+
costUsd: raw.total_cost_usd
|
|
443
|
+
};
|
|
444
|
+
return null;
|
|
445
|
+
};
|
|
446
|
+
/**
|
|
447
|
+
* Extract final result from Claude/Cursor stream-json output.
|
|
448
|
+
*
|
|
449
|
+
* Priority:
|
|
450
|
+
* 1. type=result with result field
|
|
451
|
+
* 2. Last assistant message with text content
|
|
452
|
+
* 3. Raw stdout fallback
|
|
453
|
+
*/
|
|
454
|
+
function extractClaudeResult(stdout) {
|
|
455
|
+
const lines = stdout.trim().split("\n");
|
|
456
|
+
for (let i = lines.length - 1; i >= 0; i--) try {
|
|
457
|
+
const event = JSON.parse(lines[i]);
|
|
458
|
+
if (event.type === "result" && event.result) return { content: event.result };
|
|
459
|
+
} catch {}
|
|
460
|
+
for (let i = lines.length - 1; i >= 0; i--) try {
|
|
461
|
+
const event = JSON.parse(lines[i]);
|
|
462
|
+
if (event.type === "assistant" && event.message?.content) {
|
|
463
|
+
const textParts = event.message.content.filter((c) => c.type === "text").map((c) => c.text);
|
|
464
|
+
if (textParts.length > 0) return { content: textParts.join("\n") };
|
|
465
|
+
}
|
|
466
|
+
} catch {}
|
|
467
|
+
return { content: stdout.trim() };
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Adapter for Codex --json format.
|
|
471
|
+
*
|
|
472
|
+
* Events:
|
|
473
|
+
* { type: "thread.started", thread_id: "..." }
|
|
474
|
+
* { type: "item.completed", item: { type: "function_call", name, arguments } }
|
|
475
|
+
* { type: "item.completed", item: { type: "agent_message", text } } → skipped (result only)
|
|
476
|
+
* { type: "turn.completed", usage: { input_tokens, output_tokens } }
|
|
477
|
+
*/
|
|
478
|
+
const codexAdapter = (raw) => {
|
|
479
|
+
const type = raw.type;
|
|
480
|
+
if (type === "thread.started") {
|
|
481
|
+
const threadId = raw.thread_id;
|
|
482
|
+
return {
|
|
483
|
+
kind: "init",
|
|
484
|
+
sessionId: threadId ? `${threadId.slice(0, 8)}...` : void 0
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
if (type === "item.completed") {
|
|
488
|
+
const item = raw.item;
|
|
489
|
+
if (!item) return null;
|
|
490
|
+
if (item.type === "function_call") return {
|
|
491
|
+
kind: "tool_call",
|
|
492
|
+
name: item.name || "unknown",
|
|
493
|
+
args: item.arguments ?? ""
|
|
494
|
+
};
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
if (type === "turn.completed") {
|
|
498
|
+
const usage = raw.usage;
|
|
499
|
+
return {
|
|
500
|
+
kind: "completed",
|
|
501
|
+
usage: usage ? {
|
|
502
|
+
input: usage.input_tokens ?? 0,
|
|
503
|
+
output: usage.output_tokens ?? 0
|
|
504
|
+
} : void 0
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
};
|
|
509
|
+
/**
|
|
510
|
+
* Extract final result from Codex --json output.
|
|
511
|
+
*
|
|
512
|
+
* Priority:
|
|
513
|
+
* 1. Last item.completed with item.type=agent_message
|
|
514
|
+
* 2. Raw stdout fallback
|
|
515
|
+
*/
|
|
516
|
+
function extractCodexResult(stdout) {
|
|
517
|
+
const lines = stdout.trim().split("\n");
|
|
518
|
+
for (let i = lines.length - 1; i >= 0; i--) try {
|
|
519
|
+
const event = JSON.parse(lines[i]);
|
|
520
|
+
if (event.type === "item.completed" && event.item?.type === "agent_message" && event.item?.text) return { content: event.item.text };
|
|
521
|
+
} catch {}
|
|
522
|
+
return { content: stdout.trim() };
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Create a line-buffered stream parser.
|
|
526
|
+
*
|
|
527
|
+
* Accumulates stdout chunks, parses each line through the given adapter,
|
|
528
|
+
* and emits formatted progress messages via debugLog.
|
|
529
|
+
*
|
|
530
|
+
* @param debugLog - Callback for progress messages
|
|
531
|
+
* @param backendName - Display name (e.g., "Cursor", "Claude", "Codex")
|
|
532
|
+
* @param adapter - Format-specific adapter to convert raw JSON → StreamEvent
|
|
533
|
+
*/
|
|
534
|
+
function createStreamParser(debugLog, backendName, adapter) {
|
|
535
|
+
let lineBuf = "";
|
|
536
|
+
return (chunk) => {
|
|
537
|
+
lineBuf += chunk;
|
|
538
|
+
const lines = lineBuf.split("\n");
|
|
539
|
+
lineBuf = lines.pop() ?? "";
|
|
540
|
+
for (const line of lines) {
|
|
541
|
+
if (!line.trim()) continue;
|
|
542
|
+
try {
|
|
543
|
+
const event = adapter(JSON.parse(line));
|
|
544
|
+
if (event) {
|
|
545
|
+
const progress = formatEvent(event, backendName);
|
|
546
|
+
if (progress) debugLog(progress);
|
|
547
|
+
}
|
|
548
|
+
} catch {}
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Format tool call input for display (truncated JSON string)
|
|
554
|
+
*/
|
|
555
|
+
function formatToolInput(input) {
|
|
556
|
+
if (!input || typeof input !== "object") return "";
|
|
557
|
+
try {
|
|
558
|
+
const str = JSON.stringify(input);
|
|
559
|
+
return str.length > 100 ? str.slice(0, 100) + "..." : str;
|
|
560
|
+
} catch {
|
|
561
|
+
return "";
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
300
565
|
//#endregion
|
|
301
566
|
//#region src/backends/claude-code.ts
|
|
302
567
|
/**
|
|
@@ -332,13 +597,19 @@ var ClaudeCodeBackend = class {
|
|
|
332
597
|
async send(message, options) {
|
|
333
598
|
const args = this.buildArgs(message, options);
|
|
334
599
|
const cwd = this.options.workspace || this.options.cwd;
|
|
600
|
+
const debugLog = this.options.debugLog;
|
|
601
|
+
const outputFormat = this.options.outputFormat ?? "stream-json";
|
|
602
|
+
const timeout = this.options.timeout ?? 3e5;
|
|
335
603
|
try {
|
|
336
|
-
const { stdout } = await
|
|
604
|
+
const { stdout } = await execWithIdleTimeout({
|
|
605
|
+
command: "claude",
|
|
606
|
+
args,
|
|
337
607
|
cwd,
|
|
338
|
-
|
|
339
|
-
|
|
608
|
+
timeout,
|
|
609
|
+
onStdout: outputFormat === "stream-json" && debugLog ? createStreamParser(debugLog, "Claude", claudeAdapter) : void 0
|
|
340
610
|
});
|
|
341
|
-
if (
|
|
611
|
+
if (outputFormat === "stream-json") return extractClaudeResult(stdout);
|
|
612
|
+
if (outputFormat === "json") try {
|
|
342
613
|
const parsed = JSON.parse(stdout);
|
|
343
614
|
return {
|
|
344
615
|
content: parsed.content || parsed.result || stdout,
|
|
@@ -350,7 +621,11 @@ var ClaudeCodeBackend = class {
|
|
|
350
621
|
}
|
|
351
622
|
return { content: stdout.trim() };
|
|
352
623
|
} catch (error) {
|
|
353
|
-
if (error instanceof
|
|
624
|
+
if (error instanceof IdleTimeoutError) throw new Error(`claude timed out after ${timeout}ms of inactivity`);
|
|
625
|
+
if (error && typeof error === "object" && "exitCode" in error) {
|
|
626
|
+
const execError = error;
|
|
627
|
+
throw new Error(`claude failed (exit ${execError.exitCode}): ${execError.stderr || execError.shortMessage}`);
|
|
628
|
+
}
|
|
354
629
|
throw error;
|
|
355
630
|
}
|
|
356
631
|
}
|
|
@@ -383,7 +658,8 @@ var ClaudeCodeBackend = class {
|
|
|
383
658
|
args.push("--append-system-prompt", system);
|
|
384
659
|
}
|
|
385
660
|
if (this.options.allowedTools?.length) args.push("--allowed-tools", this.options.allowedTools.join(","));
|
|
386
|
-
|
|
661
|
+
const outputFormat = this.options.outputFormat ?? "stream-json";
|
|
662
|
+
args.push("--output-format", outputFormat);
|
|
387
663
|
if (this.options.continue) args.push("--continue");
|
|
388
664
|
if (this.options.resume) args.push("--resume", this.options.resume);
|
|
389
665
|
if (this.options.mcpConfigPath) args.push("--mcp-config", this.options.mcpConfigPath);
|
|
@@ -401,7 +677,7 @@ var ClaudeCodeBackend = class {
|
|
|
401
677
|
//#region src/backends/codex.ts
|
|
402
678
|
/**
|
|
403
679
|
* OpenAI Codex CLI backend
|
|
404
|
-
* Uses `codex exec` for non-interactive mode
|
|
680
|
+
* Uses `codex exec` for non-interactive mode with JSON event output
|
|
405
681
|
*
|
|
406
682
|
* MCP Configuration:
|
|
407
683
|
* Codex uses project-level MCP config. Use setWorkspace() to set up
|
|
@@ -432,28 +708,23 @@ var CodexBackend = class {
|
|
|
432
708
|
async send(message, _options) {
|
|
433
709
|
const args = this.buildArgs(message);
|
|
434
710
|
const cwd = this.options.workspace || this.options.cwd;
|
|
711
|
+
const debugLog = this.options.debugLog;
|
|
712
|
+
const timeout = this.options.timeout ?? 3e5;
|
|
435
713
|
try {
|
|
436
|
-
const { stdout } = await
|
|
714
|
+
const { stdout } = await execWithIdleTimeout({
|
|
715
|
+
command: "codex",
|
|
716
|
+
args,
|
|
437
717
|
cwd,
|
|
438
|
-
|
|
439
|
-
|
|
718
|
+
timeout,
|
|
719
|
+
onStdout: debugLog ? createStreamParser(debugLog, "Codex", codexAdapter) : void 0
|
|
440
720
|
});
|
|
441
|
-
|
|
442
|
-
const lines = stdout.trim().split("\n");
|
|
443
|
-
const lastLine = lines[lines.length - 1];
|
|
444
|
-
if (!lastLine) return { content: stdout.trim() };
|
|
445
|
-
const lastEvent = JSON.parse(lastLine);
|
|
446
|
-
return {
|
|
447
|
-
content: lastEvent.message || lastEvent.content || stdout,
|
|
448
|
-
toolCalls: lastEvent.toolCalls,
|
|
449
|
-
usage: lastEvent.usage
|
|
450
|
-
};
|
|
451
|
-
} catch {
|
|
452
|
-
return { content: stdout.trim() };
|
|
453
|
-
}
|
|
454
|
-
return { content: stdout.trim() };
|
|
721
|
+
return extractCodexResult(stdout);
|
|
455
722
|
} catch (error) {
|
|
456
|
-
if (error instanceof
|
|
723
|
+
if (error instanceof IdleTimeoutError) throw new Error(`codex timed out after ${timeout}ms of inactivity`);
|
|
724
|
+
if (error && typeof error === "object" && "exitCode" in error) {
|
|
725
|
+
const execError = error;
|
|
726
|
+
throw new Error(`codex failed (exit ${execError.exitCode}): ${execError.stderr || execError.shortMessage}`);
|
|
727
|
+
}
|
|
457
728
|
throw error;
|
|
458
729
|
}
|
|
459
730
|
}
|
|
@@ -477,13 +748,12 @@ var CodexBackend = class {
|
|
|
477
748
|
buildArgs(message) {
|
|
478
749
|
const args = [
|
|
479
750
|
"exec",
|
|
480
|
-
"--
|
|
751
|
+
"--full-auto",
|
|
752
|
+
"--json",
|
|
753
|
+
"--skip-git-repo-check",
|
|
481
754
|
message
|
|
482
755
|
];
|
|
483
756
|
if (this.options.model) args.push("--model", this.options.model);
|
|
484
|
-
if (this.options.json) args.push("--json");
|
|
485
|
-
if (this.options.skipGitRepoCheck) args.push("--skip-git-repo-check");
|
|
486
|
-
if (this.options.approvalMode) args.push("--approval-mode", this.options.approvalMode);
|
|
487
757
|
if (this.options.resume) args.push("--resume", this.options.resume);
|
|
488
758
|
return args;
|
|
489
759
|
}
|
|
@@ -493,7 +763,8 @@ var CodexBackend = class {
|
|
|
493
763
|
//#region src/backends/cursor.ts
|
|
494
764
|
/**
|
|
495
765
|
* Cursor CLI backend
|
|
496
|
-
* Uses `cursor
|
|
766
|
+
* Uses `cursor agent -p` (preferred) or `cursor-agent -p` (fallback)
|
|
767
|
+
* for non-interactive mode with stream-json output
|
|
497
768
|
*
|
|
498
769
|
* MCP Configuration:
|
|
499
770
|
* Cursor uses project-level MCP config via .cursor/mcp.json in the workspace.
|
|
@@ -504,9 +775,11 @@ var CodexBackend = class {
|
|
|
504
775
|
var CursorBackend = class {
|
|
505
776
|
type = "cursor";
|
|
506
777
|
options;
|
|
778
|
+
/** Resolved command: "cursor" (subcommand style) or "cursor-agent" (standalone) */
|
|
779
|
+
resolvedCommand = null;
|
|
507
780
|
constructor(options = {}) {
|
|
508
781
|
this.options = {
|
|
509
|
-
timeout:
|
|
782
|
+
timeout: 3e5,
|
|
510
783
|
...options
|
|
511
784
|
};
|
|
512
785
|
}
|
|
@@ -521,32 +794,30 @@ var CursorBackend = class {
|
|
|
521
794
|
writeFileSync(join(cursorDir, "mcp.json"), JSON.stringify(mcpConfig, null, 2));
|
|
522
795
|
}
|
|
523
796
|
async send(message, _options) {
|
|
524
|
-
const { command, args } = this.buildCommand(message);
|
|
797
|
+
const { command, args } = await this.buildCommand(message);
|
|
525
798
|
const cwd = this.options.workspace || this.options.cwd;
|
|
799
|
+
const debugLog = this.options.debugLog;
|
|
800
|
+
const timeout = this.options.timeout ?? 3e5;
|
|
526
801
|
try {
|
|
527
|
-
const { stdout } = await
|
|
802
|
+
const { stdout } = await execWithIdleTimeout({
|
|
803
|
+
command,
|
|
804
|
+
args,
|
|
528
805
|
cwd,
|
|
529
|
-
|
|
530
|
-
|
|
806
|
+
timeout,
|
|
807
|
+
onStdout: debugLog ? createStreamParser(debugLog, "Cursor", claudeAdapter) : void 0
|
|
531
808
|
});
|
|
532
|
-
return
|
|
809
|
+
return extractClaudeResult(stdout);
|
|
533
810
|
} catch (error) {
|
|
534
|
-
if (error instanceof
|
|
535
|
-
|
|
536
|
-
|
|
811
|
+
if (error instanceof IdleTimeoutError) throw new Error(`cursor agent timed out after ${timeout}ms of inactivity`);
|
|
812
|
+
if (error && typeof error === "object" && "exitCode" in error) {
|
|
813
|
+
const execError = error;
|
|
814
|
+
throw new Error(`cursor agent failed (exit ${execError.exitCode}): ${execError.stderr || execError.shortMessage}`);
|
|
537
815
|
}
|
|
538
816
|
throw error;
|
|
539
817
|
}
|
|
540
818
|
}
|
|
541
819
|
async isAvailable() {
|
|
542
|
-
|
|
543
|
-
await execa(cmd, ["--version"], {
|
|
544
|
-
stdin: "ignore",
|
|
545
|
-
timeout: 2e3
|
|
546
|
-
});
|
|
547
|
-
return true;
|
|
548
|
-
} catch {}
|
|
549
|
-
return false;
|
|
820
|
+
return await this.resolveCommand() !== null;
|
|
550
821
|
}
|
|
551
822
|
getInfo() {
|
|
552
823
|
return {
|
|
@@ -554,17 +825,48 @@ var CursorBackend = class {
|
|
|
554
825
|
model: this.options.model
|
|
555
826
|
};
|
|
556
827
|
}
|
|
557
|
-
|
|
558
|
-
|
|
828
|
+
/**
|
|
829
|
+
* Resolve which cursor command is available.
|
|
830
|
+
* Prefers `cursor agent` (subcommand), falls back to `cursor-agent` (standalone).
|
|
831
|
+
* Result is cached after first resolution.
|
|
832
|
+
*/
|
|
833
|
+
async resolveCommand() {
|
|
834
|
+
if (this.resolvedCommand !== null) return this.resolvedCommand;
|
|
835
|
+
try {
|
|
836
|
+
await execa("cursor", ["agent", "--version"], {
|
|
837
|
+
stdin: "ignore",
|
|
838
|
+
timeout: 2e3
|
|
839
|
+
});
|
|
840
|
+
this.resolvedCommand = "cursor";
|
|
841
|
+
return "cursor";
|
|
842
|
+
} catch {}
|
|
843
|
+
try {
|
|
844
|
+
await execa("cursor-agent", ["--version"], {
|
|
845
|
+
stdin: "ignore",
|
|
846
|
+
timeout: 2e3
|
|
847
|
+
});
|
|
848
|
+
this.resolvedCommand = "cursor-agent";
|
|
849
|
+
return "cursor-agent";
|
|
850
|
+
} catch {}
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
async buildCommand(message) {
|
|
854
|
+
const cmd = await this.resolveCommand();
|
|
855
|
+
const agentArgs = [
|
|
559
856
|
"-p",
|
|
560
857
|
"--force",
|
|
561
858
|
"--approve-mcps",
|
|
859
|
+
"--output-format=stream-json",
|
|
562
860
|
message
|
|
563
861
|
];
|
|
564
|
-
if (this.options.model)
|
|
862
|
+
if (this.options.model) agentArgs.push("--model", this.options.model);
|
|
863
|
+
if (cmd === "cursor") return {
|
|
864
|
+
command: "cursor",
|
|
865
|
+
args: ["agent", ...agentArgs]
|
|
866
|
+
};
|
|
565
867
|
return {
|
|
566
868
|
command: "cursor-agent",
|
|
567
|
-
args
|
|
869
|
+
args: agentArgs
|
|
568
870
|
};
|
|
569
871
|
}
|
|
570
872
|
};
|
|
@@ -732,4 +1034,4 @@ async function listBackends() {
|
|
|
732
1034
|
}
|
|
733
1035
|
|
|
734
1036
|
//#endregion
|
|
735
|
-
export { FRONTIER_MODELS as _, createMockBackend as a,
|
|
1037
|
+
export { getModelForBackend as C, createModel as D, SUPPORTED_PROVIDERS as E, createModelAsync as O, SDK_MODEL_ALIASES as S, FRONTIER_MODELS as T, execWithIdleTimeout as _, createMockBackend as a, CODEX_MODEL_MAP as b, CodexBackend as c, codexAdapter as d, createStreamParser as f, IdleTimeoutError as g, formatEvent as h, MockAIBackend as i, getDefaultModel as k, ClaudeCodeBackend as l, extractCodexResult as m, createBackend as n, SdkBackend as o, extractClaudeResult as p, listBackends as r, CursorBackend as s, checkBackends as t, claudeAdapter as u, BACKEND_DEFAULT_MODELS as v, parseModel as w, CURSOR_MODEL_MAP as x, CLAUDE_MODEL_MAP as y };
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as createSkillsTool, c as createFeedbackTool, l as AgentSession, o as SkillsProvider, s as FEEDBACK_PROMPT, t as SkillImporter } from "../skills-
|
|
2
|
+
import { T as FRONTIER_MODELS, k as getDefaultModel, n as createBackend } from "../backends-CEYiMUgC.mjs";
|
|
3
|
+
import { a as createSkillsTool, c as createFeedbackTool, l as AgentSession, o as SkillsProvider, s as FEEDBACK_PROMPT, t as SkillImporter } from "../skills-iya7NbH7.mjs";
|
|
4
4
|
import { jsonSchema, tool } from "ai";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
@@ -2094,7 +2094,7 @@ Examples:
|
|
|
2094
2094
|
|
|
2095
2095
|
Note: Workflow name is inferred from YAML 'name' field or filename
|
|
2096
2096
|
`).action(async (file, options) => {
|
|
2097
|
-
const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-
|
|
2097
|
+
const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-K1Zd655A.mjs");
|
|
2098
2098
|
const tag = options.tag || DEFAULT_TAG;
|
|
2099
2099
|
const parsedWorkflow = await parseWorkflowFile(file, { tag });
|
|
2100
2100
|
const workflowName = parsedWorkflow.name;
|
|
@@ -2144,7 +2144,7 @@ Examples:
|
|
|
2144
2144
|
|
|
2145
2145
|
Note: Workflow name is inferred from YAML 'name' field or filename
|
|
2146
2146
|
`).action(async (file, options) => {
|
|
2147
|
-
const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-
|
|
2147
|
+
const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-K1Zd655A.mjs");
|
|
2148
2148
|
const tag = options.tag || DEFAULT_TAG;
|
|
2149
2149
|
const parsedWorkflow = await parseWorkflowFile(file, { tag });
|
|
2150
2150
|
const workflowName = parsedWorkflow.name;
|
|
@@ -2339,7 +2339,7 @@ function registerInfoCommands(program) {
|
|
|
2339
2339
|
console.log(`\nDefault: ${defaultModel} (when no model specified)`);
|
|
2340
2340
|
});
|
|
2341
2341
|
program.command("backends").description("Check available backends (SDK, CLI tools)").action(async () => {
|
|
2342
|
-
const { listBackends } = await import("../backends-
|
|
2342
|
+
const { listBackends } = await import("../backends-B8rYTNqn.mjs");
|
|
2343
2343
|
const backends = await listBackends();
|
|
2344
2344
|
console.log("Backend Status:\n");
|
|
2345
2345
|
for (const backend of backends) {
|
|
@@ -2489,8 +2489,11 @@ Note: Requires agent to be created with --feedback flag
|
|
|
2489
2489
|
//#region src/cli/index.ts
|
|
2490
2490
|
globalThis.AI_SDK_LOG_WARNINGS = false;
|
|
2491
2491
|
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
2492
|
-
process.stderr.write = function(chunk, ...
|
|
2493
|
-
if (process.argv.includes("--debug") || process.argv.includes("-d"))
|
|
2492
|
+
process.stderr.write = function(chunk, ...rest) {
|
|
2493
|
+
if (process.argv.includes("--debug") || process.argv.includes("-d")) {
|
|
2494
|
+
const message = typeof chunk === "string" ? chunk : chunk.toString();
|
|
2495
|
+
return originalStderrWrite.call(process.stderr, message, ...rest);
|
|
2496
|
+
}
|
|
2494
2497
|
return true;
|
|
2495
2498
|
};
|
|
2496
2499
|
const program = new Command();
|
package/dist/index.d.mts
CHANGED
|
@@ -467,7 +467,7 @@ interface ClaudeCodeOptions {
|
|
|
467
467
|
cwd?: string;
|
|
468
468
|
/** Workspace directory for agent isolation */
|
|
469
469
|
workspace?: string;
|
|
470
|
-
/**
|
|
470
|
+
/** Idle timeout in milliseconds — kills process if no output for this duration */
|
|
471
471
|
timeout?: number;
|
|
472
472
|
/** MCP config file path (for workflow context) */
|
|
473
473
|
mcpConfigPath?: string;
|
|
@@ -505,19 +505,13 @@ declare class ClaudeCodeBackend implements Backend {
|
|
|
505
505
|
interface CodexOptions {
|
|
506
506
|
/** Model to use (e.g., 'gpt-5.2-codex') */
|
|
507
507
|
model?: string;
|
|
508
|
-
/** Output as JSON events */
|
|
509
|
-
json?: boolean;
|
|
510
508
|
/** Working directory (defaults to workspace if set) */
|
|
511
509
|
cwd?: string;
|
|
512
510
|
/** Workspace directory for agent isolation */
|
|
513
511
|
workspace?: string;
|
|
514
|
-
/** Skip git repo check */
|
|
515
|
-
skipGitRepoCheck?: boolean;
|
|
516
|
-
/** Approval mode: 'suggest' | 'auto-edit' | 'full-auto' */
|
|
517
|
-
approvalMode?: "suggest" | "auto-edit" | "full-auto";
|
|
518
512
|
/** Resume a previous session */
|
|
519
513
|
resume?: string;
|
|
520
|
-
/**
|
|
514
|
+
/** Idle timeout in milliseconds — kills process if no output for this duration */
|
|
521
515
|
timeout?: number;
|
|
522
516
|
/** Debug log function (for workflow diagnostics) */
|
|
523
517
|
debugLog?: (message: string) => void;
|
|
@@ -553,7 +547,7 @@ interface CursorOptions {
|
|
|
553
547
|
cwd?: string;
|
|
554
548
|
/** Workspace directory for agent isolation (contains .cursor/mcp.json) */
|
|
555
549
|
workspace?: string;
|
|
556
|
-
/**
|
|
550
|
+
/** Idle timeout in milliseconds — kills process if no output for this duration */
|
|
557
551
|
timeout?: number;
|
|
558
552
|
/** Debug log function (for workflow diagnostics) */
|
|
559
553
|
debugLog?: (message: string) => void;
|
|
@@ -561,6 +555,8 @@ interface CursorOptions {
|
|
|
561
555
|
declare class CursorBackend implements Backend {
|
|
562
556
|
readonly type: "cursor";
|
|
563
557
|
private options;
|
|
558
|
+
/** Resolved command: "cursor" (subcommand style) or "cursor-agent" (standalone) */
|
|
559
|
+
private resolvedCommand;
|
|
564
560
|
constructor(options?: CursorOptions);
|
|
565
561
|
/**
|
|
566
562
|
* Set up workspace directory with MCP config
|
|
@@ -578,10 +574,16 @@ declare class CursorBackend implements Backend {
|
|
|
578
574
|
version?: string;
|
|
579
575
|
model?: string;
|
|
580
576
|
};
|
|
581
|
-
|
|
577
|
+
/**
|
|
578
|
+
* Resolve which cursor command is available.
|
|
579
|
+
* Prefers `cursor agent` (subcommand), falls back to `cursor-agent` (standalone).
|
|
580
|
+
* Result is cached after first resolution.
|
|
581
|
+
*/
|
|
582
|
+
private resolveCommand;
|
|
583
|
+
protected buildCommand(message: string): Promise<{
|
|
582
584
|
command: string;
|
|
583
585
|
args: string[];
|
|
584
|
-
}
|
|
586
|
+
}>;
|
|
585
587
|
}
|
|
586
588
|
//#endregion
|
|
587
589
|
//#region src/backends/sdk.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as createSkillsTool, c as createFeedbackTool, i as parseImportSpec, l as AgentSession, n as buildGitUrl, o as SkillsProvider, r as getSpecDisplayName, s as FEEDBACK_PROMPT, t as SkillImporter } from "./skills-
|
|
1
|
+
import { D as createModel, E as SUPPORTED_PROVIDERS, O as createModelAsync, T as FRONTIER_MODELS, a as createMockBackend, c as CodexBackend, i as MockAIBackend, l as ClaudeCodeBackend, n as createBackend, o as SdkBackend, r as listBackends, s as CursorBackend, t as checkBackends } from "./backends-CEYiMUgC.mjs";
|
|
2
|
+
import { a as createSkillsTool, c as createFeedbackTool, i as parseImportSpec, l as AgentSession, n as buildGitUrl, o as SkillsProvider, r as getSpecDisplayName, s as FEEDBACK_PROMPT, t as SkillImporter } from "./skills-iya7NbH7.mjs";
|
|
3
3
|
import { jsonSchema, tool } from "ai";
|
|
4
4
|
import { createBashTool } from "bash-tool";
|
|
5
5
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { O as createModelAsync } from "./backends-CEYiMUgC.mjs";
|
|
2
2
|
import { ToolLoopAgent, jsonSchema, stepCountIs, tool } from "ai";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { join, normalize } from "node:path";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { O as createModelAsync, a as createMockBackend, n as createBackend, w as parseModel } from "./backends-CEYiMUgC.mjs";
|
|
2
2
|
import { c as CONTEXT_DEFAULTS, i as resolveContextDir, n as createFileContextProvider, t as FileContextProvider } from "./cli/index.mjs";
|
|
3
3
|
import { a as createMemoryContextProvider, t as createContextMCPServer } from "./mcp-server-DtIApaBD.mjs";
|
|
4
4
|
import { generateText, jsonSchema, stepCountIs, tool } from "ai";
|
|
@@ -860,6 +860,7 @@ function shouldContinue(state) {
|
|
|
860
860
|
*/
|
|
861
861
|
function createAgentController(config) {
|
|
862
862
|
const { name, agent, contextProvider, mcpUrl, workspaceDir, projectDir, backend, onRunComplete, log = () => {}, feedback } = config;
|
|
863
|
+
const infoLog = config.infoLog ?? log;
|
|
863
864
|
const errorLog = config.errorLog ?? log;
|
|
864
865
|
const pollInterval = config.pollInterval ?? CONTROLLER_DEFAULTS.pollInterval;
|
|
865
866
|
const retryConfig = {
|
|
@@ -895,7 +896,7 @@ function createAgentController(config) {
|
|
|
895
896
|
continue;
|
|
896
897
|
}
|
|
897
898
|
const senders = inbox.map((m) => m.entry.from);
|
|
898
|
-
|
|
899
|
+
infoLog(`Inbox: ${inbox.length} message(s) from [${senders.join(", ")}]`);
|
|
899
900
|
for (const msg of inbox) {
|
|
900
901
|
const preview = msg.entry.content.length > 120 ? msg.entry.content.slice(0, 120) + "..." : msg.entry.content;
|
|
901
902
|
log(` from @${msg.entry.from}: ${preview}`);
|
|
@@ -907,7 +908,7 @@ function createAgentController(config) {
|
|
|
907
908
|
while (attempt < retryConfig.maxAttempts && shouldContinue(state)) {
|
|
908
909
|
attempt++;
|
|
909
910
|
state = "running";
|
|
910
|
-
|
|
911
|
+
infoLog(`Running (attempt ${attempt}/${retryConfig.maxAttempts})`);
|
|
911
912
|
lastResult = await runAgent(backend, {
|
|
912
913
|
name,
|
|
913
914
|
agent,
|
|
@@ -922,9 +923,9 @@ function createAgentController(config) {
|
|
|
922
923
|
projectDir,
|
|
923
924
|
retryAttempt: attempt,
|
|
924
925
|
feedback
|
|
925
|
-
}, log);
|
|
926
|
+
}, log, infoLog);
|
|
926
927
|
if (lastResult.success) {
|
|
927
|
-
|
|
928
|
+
infoLog(`DONE ${lastResult.steps ? `${lastResult.steps} steps, ${lastResult.toolCalls} tool calls, ${lastResult.duration}ms` : `${lastResult.duration}ms`}`);
|
|
928
929
|
if (lastResult.content) await contextProvider.appendChannel(name, lastResult.content);
|
|
929
930
|
await contextProvider.ackInbox(name, latestId);
|
|
930
931
|
break;
|
|
@@ -954,7 +955,7 @@ function createAgentController(config) {
|
|
|
954
955
|
async start() {
|
|
955
956
|
if (state !== "stopped") throw new Error(`Controller ${name} is already running`);
|
|
956
957
|
state = "idle";
|
|
957
|
-
|
|
958
|
+
infoLog(`Starting`);
|
|
958
959
|
runLoop().catch((error) => {
|
|
959
960
|
errorLog(`ERROR ${error instanceof Error ? error.message : String(error)}`);
|
|
960
961
|
state = "stopped";
|
|
@@ -994,7 +995,8 @@ function createAgentController(config) {
|
|
|
994
995
|
* SDK and mock backends get special runners with MCP tool bridge + bash,
|
|
995
996
|
* because they can't manage tools on their own (unlike CLI backends).
|
|
996
997
|
*/
|
|
997
|
-
async function runAgent(backend, ctx, log) {
|
|
998
|
+
async function runAgent(backend, ctx, log, infoLog) {
|
|
999
|
+
const info = infoLog ?? log;
|
|
998
1000
|
if (backend.type === "mock") return runMockAgent(ctx, (msg) => log(msg));
|
|
999
1001
|
if (backend.type === "sdk") return runSdkAgent(ctx, (msg) => log(msg));
|
|
1000
1002
|
const startTime = Date.now();
|
|
@@ -1004,7 +1006,7 @@ async function runAgent(backend, ctx, log) {
|
|
|
1004
1006
|
backend.setWorkspace(ctx.workspaceDir, mcpConfig);
|
|
1005
1007
|
}
|
|
1006
1008
|
const prompt = buildAgentPrompt(ctx);
|
|
1007
|
-
|
|
1009
|
+
info(`Prompt (${prompt.length} chars) → ${backend.type} backend`);
|
|
1008
1010
|
await backend.send(prompt, { system: ctx.agent.resolvedSystemPrompt });
|
|
1009
1011
|
return {
|
|
1010
1012
|
success: true,
|
|
@@ -1044,9 +1046,13 @@ async function checkWorkflowIdle(controllers, provider, debounceMs = CONTROLLER_
|
|
|
1044
1046
|
*/
|
|
1045
1047
|
function getBackendByType(backendType, options) {
|
|
1046
1048
|
if (backendType === "mock") return createMockBackend(options?.debugLog);
|
|
1049
|
+
const backendOptions = {};
|
|
1050
|
+
if (options?.timeout) backendOptions.timeout = options.timeout;
|
|
1051
|
+
if (options?.debugLog) backendOptions.debugLog = options.debugLog;
|
|
1047
1052
|
return createBackend({
|
|
1048
1053
|
type: backendType,
|
|
1049
|
-
model: options?.model
|
|
1054
|
+
model: options?.model,
|
|
1055
|
+
...Object.keys(backendOptions).length > 0 ? { options: backendOptions } : {}
|
|
1050
1056
|
});
|
|
1051
1057
|
}
|
|
1052
1058
|
/**
|
|
@@ -1658,7 +1664,8 @@ async function runWorkflowWithControllers(config) {
|
|
|
1658
1664
|
if (createBackend) backend = createBackend(agentName, agentDef);
|
|
1659
1665
|
else if (agentDef.backend) backend = getBackendByType(agentDef.backend, {
|
|
1660
1666
|
model: agentDef.model,
|
|
1661
|
-
debugLog: backendDebugLog
|
|
1667
|
+
debugLog: backendDebugLog,
|
|
1668
|
+
timeout: agentDef.timeout
|
|
1662
1669
|
});
|
|
1663
1670
|
else if (agentDef.model) backend = getBackendForModel(agentDef.model, { debugLog: backendDebugLog });
|
|
1664
1671
|
else throw new Error(`Agent "${agentName}" requires either a backend or model field`);
|
|
@@ -1676,6 +1683,7 @@ async function runWorkflowWithControllers(config) {
|
|
|
1676
1683
|
backend,
|
|
1677
1684
|
pollInterval,
|
|
1678
1685
|
log: (msg) => controllerLogger.debug(msg),
|
|
1686
|
+
infoLog: (msg) => controllerLogger.info(msg),
|
|
1679
1687
|
errorLog: (msg) => controllerLogger.error(msg),
|
|
1680
1688
|
feedback: feedbackEnabled
|
|
1681
1689
|
});
|
package/package.json
CHANGED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { a as createMockBackend, c as CodexBackend, d as CLAUDE_MODEL_MAP, f as CODEX_MODEL_MAP, h as getModelForBackend, i as MockAIBackend, l as ClaudeCodeBackend, m as SDK_MODEL_ALIASES, n as createBackend, o as SdkBackend, p as CURSOR_MODEL_MAP, r as listBackends, s as CursorBackend, t as checkBackends, u as BACKEND_DEFAULT_MODELS } from "./backends-DenGdkrj.mjs";
|
|
2
|
-
|
|
3
|
-
export { listBackends };
|