@prismatic-io/lux 0.0.2-preview.24 → 0.0.2-preview.26
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/lib/answerers/claude-code/index.d.ts +3 -3
- package/lib/answerers/claude-code/index.d.ts.map +1 -1
- package/lib/answerers/claude-code/index.js +3 -2
- package/lib/answerers/claude-code/index.js.map +1 -1
- package/lib/answerers/persona/index.d.ts +3 -3
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/answerers/persona/index.js +3 -2
- package/lib/answerers/persona/index.js.map +1 -1
- package/lib/answerers/terminal/index.d.ts +3 -3
- package/lib/answerers/terminal/index.d.ts.map +1 -1
- package/lib/answerers/terminal/index.js +3 -2
- package/lib/answerers/terminal/index.js.map +1 -1
- package/lib/assertions/core/command-exits-zero.d.ts +1 -1
- package/lib/assertions/core/command-exits-zero.d.ts.map +1 -1
- package/lib/assertions/core/command-exits-zero.js +4 -3
- package/lib/assertions/core/command-exits-zero.js.map +1 -1
- package/lib/assertions/rubric/index.d.ts +2 -2
- package/lib/assertions/rubric/index.d.ts.map +1 -1
- package/lib/assertions/rubric/index.js +4 -3
- package/lib/assertions/rubric/index.js.map +1 -1
- package/lib/authoring.d.ts +1 -0
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +1 -0
- package/lib/authoring.js.map +1 -1
- package/lib/cli/output-schemas.d.ts +2 -2
- package/lib/core/duration.d.ts +5 -0
- package/lib/core/duration.d.ts.map +1 -0
- package/lib/core/duration.js +49 -0
- package/lib/core/duration.js.map +1 -0
- package/lib/core/experiment.d.ts +2 -2
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +2 -1
- package/lib/core/experiment.js.map +1 -1
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +1 -0
- package/lib/core/index.js.map +1 -1
- package/lib/drivers/antigravity/config.d.ts +3 -3
- package/lib/drivers/antigravity/config.d.ts.map +1 -1
- package/lib/drivers/antigravity/config.js +2 -1
- package/lib/drivers/antigravity/config.js.map +1 -1
- package/lib/drivers/antigravity/index.d.ts +4 -4
- package/lib/drivers/antigravity/index.js +1 -1
- package/lib/drivers/antigravity/index.js.map +1 -1
- package/lib/drivers/claude-code/deferred-process.d.ts +17 -0
- package/lib/drivers/claude-code/deferred-process.d.ts.map +1 -0
- package/lib/drivers/claude-code/deferred-process.js +108 -0
- package/lib/drivers/claude-code/deferred-process.js.map +1 -0
- package/lib/drivers/claude-code/index.d.ts +14 -7
- package/lib/drivers/claude-code/index.d.ts.map +1 -1
- package/lib/drivers/claude-code/index.js +48 -44
- package/lib/drivers/claude-code/index.js.map +1 -1
- package/lib/drivers/codex/config.d.ts +3 -3
- package/lib/drivers/codex/config.d.ts.map +1 -1
- package/lib/drivers/codex/config.js +2 -1
- package/lib/drivers/codex/config.js.map +1 -1
- package/lib/drivers/codex/index.d.ts +4 -4
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +4 -3
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/drivers/copilot/config.d.ts +6 -6
- package/lib/drivers/copilot/config.d.ts.map +1 -1
- package/lib/drivers/copilot/config.js +3 -2
- package/lib/drivers/copilot/config.js.map +1 -1
- package/lib/drivers/copilot/index.d.ts +8 -8
- package/lib/drivers/copilot/index.d.ts.map +1 -1
- package/lib/drivers/copilot/index.js +3 -2
- package/lib/drivers/copilot/index.js.map +1 -1
- package/lib/drivers/cursor/config.d.ts +2 -2
- package/lib/drivers/cursor/config.d.ts.map +1 -1
- package/lib/drivers/cursor/config.js +3 -2
- package/lib/drivers/cursor/config.js.map +1 -1
- package/lib/drivers/cursor/index.d.ts +4 -4
- package/lib/drivers/cursor/index.d.ts.map +1 -1
- package/lib/drivers/cursor/index.js +3 -2
- package/lib/drivers/cursor/index.js.map +1 -1
- package/lib/drivers/grok-build/config.d.ts +9 -9
- package/lib/drivers/grok-build/config.d.ts.map +1 -1
- package/lib/drivers/grok-build/config.js +4 -3
- package/lib/drivers/grok-build/config.js.map +1 -1
- package/lib/drivers/grok-build/index.d.ts +12 -12
- package/lib/drivers/grok-build/index.d.ts.map +1 -1
- package/lib/drivers/grok-build/index.js +5 -4
- package/lib/drivers/grok-build/index.js.map +1 -1
- package/lib/drivers/mcp/index.d.ts +6 -6
- package/lib/drivers/mcp/index.d.ts.map +1 -1
- package/lib/drivers/mcp/index.js +9 -8
- package/lib/drivers/mcp/index.js.map +1 -1
- package/lib/drivers/subprocess/index.d.ts +3 -3
- package/lib/drivers/subprocess/index.d.ts.map +1 -1
- package/lib/drivers/subprocess/index.js +4 -3
- package/lib/drivers/subprocess/index.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/optimization/reflective.d.ts +1 -1
- package/lib/optimization/reflective.d.ts.map +1 -1
- package/lib/optimization/reflective.js +3 -2
- package/lib/optimization/reflective.js.map +1 -1
- package/lib/orchestrator/candidates.d.ts.map +1 -1
- package/lib/orchestrator/candidates.js +3 -2
- package/lib/orchestrator/candidates.js.map +1 -1
- package/lib/orchestrator/experiment-contracts.d.ts +1 -1
- package/lib/orchestrator/experiment-loader.d.ts +1 -1
- package/lib/orchestrator/experiment-report.d.ts +1 -1
- package/lib/orchestrator/experiment-runs.d.ts +1 -1
- package/lib/orchestrator/grading.js +1 -1
- package/lib/orchestrator/grading.js.map +1 -1
- package/lib/orchestrator/rubric-loader.js +1 -1
- package/lib/orchestrator/rubric-loader.js.map +1 -1
- package/package.json +1 -1
- package/skills/lux-answerer/SKILL.md +2 -2
- package/src/answerers/claude-code/index.ts +3 -2
- package/src/answerers/persona/index.ts +3 -2
- package/src/answerers/terminal/index.ts +3 -2
- package/src/assertions/core/command-exits-zero.ts +4 -3
- package/src/assertions/rubric/index.ts +4 -3
- package/src/authoring.ts +1 -0
- package/src/core/duration.ts +52 -0
- package/src/core/experiment.ts +2 -1
- package/src/core/index.ts +1 -0
- package/src/drivers/antigravity/config.ts +2 -1
- package/src/drivers/antigravity/index.ts +1 -1
- package/src/drivers/claude-code/deferred-process.ts +118 -0
- package/src/drivers/claude-code/index.ts +56 -54
- package/src/drivers/codex/config.ts +2 -1
- package/src/drivers/codex/index.ts +4 -3
- package/src/drivers/copilot/config.ts +3 -2
- package/src/drivers/copilot/index.ts +4 -3
- package/src/drivers/cursor/config.ts +3 -2
- package/src/drivers/cursor/index.ts +4 -3
- package/src/drivers/grok-build/README.md +2 -2
- package/src/drivers/grok-build/config.ts +4 -3
- package/src/drivers/grok-build/index.ts +6 -5
- package/src/drivers/mcp/index.ts +9 -8
- package/src/drivers/subprocess/index.ts +4 -3
- package/src/index.ts +1 -0
- package/src/optimization/reflective.ts +3 -2
- package/src/orchestrator/candidates.ts +3 -2
- package/src/orchestrator/grading.ts +1 -1
- package/src/orchestrator/rubric-loader.ts +1 -1
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { temporaryDirectory } from "tempy";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { DurationSchema, durationMs } from "../../core/duration.js";
|
|
6
7
|
import {
|
|
7
8
|
type AgentDriver,
|
|
8
9
|
type Answer,
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
type DriverEventMap,
|
|
14
15
|
defineDriver,
|
|
15
16
|
discoverCliVersion,
|
|
16
|
-
exec,
|
|
17
17
|
type ParsedDriverEvent,
|
|
18
18
|
type ReadyState,
|
|
19
19
|
type StartContext,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { prepareClaudeExperimentConfig } from "../shared/experiment-policy.js";
|
|
30
30
|
import { copyRegularFileNoFollow } from "../shared/secure-copy.js";
|
|
31
31
|
import { subprocessDriver } from "../subprocess/index.js";
|
|
32
|
+
import { runDeferredProcess } from "./deferred-process.js";
|
|
32
33
|
import { ClaudeCodeLineSchema, makeParseEvent } from "./parse-events.js";
|
|
33
34
|
|
|
34
35
|
const LUX_BOOKKEEPING_FILES = new Set([
|
|
@@ -107,13 +108,22 @@ export const ClaudeCodeDriverConfigSchema = z
|
|
|
107
108
|
* when a weaker model asks in text instead of calling `askToolName`.
|
|
108
109
|
*/
|
|
109
110
|
textQuestionFallback: z.boolean().default(false),
|
|
110
|
-
/**
|
|
111
|
-
|
|
111
|
+
/** Maximum silence between recognized protocol events; excludes answerer wait time. */
|
|
112
|
+
idleTimeout: DurationSchema.default("10 minutes"),
|
|
113
|
+
/** Optional elapsed-time cap per defer-resume CLI invocation, regardless of activity. */
|
|
114
|
+
turnTimeout: DurationSchema.optional(),
|
|
112
115
|
/** Cap on outstanding interrupts before the driver aborts. */
|
|
113
116
|
maxInterrupts: z.number().positive().default(20),
|
|
114
117
|
})
|
|
115
118
|
.strict()
|
|
116
119
|
.superRefine((config, ctx) => {
|
|
120
|
+
if (config.turnTimeout !== undefined && config.interactionMode !== "defer-resume") {
|
|
121
|
+
ctx.addIssue({
|
|
122
|
+
code: "custom",
|
|
123
|
+
path: ["turnTimeout"],
|
|
124
|
+
message: "turnTimeout requires defer-resume mode",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
117
127
|
if (config.interactionMode === "defer-resume") {
|
|
118
128
|
const reserved = config.extraArgs?.find((arg) =>
|
|
119
129
|
["--input-format", "--no-session-persistence", "--resume", "-r", "--settings"].some(
|
|
@@ -522,6 +532,7 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
522
532
|
private deferredInterrupts = 0;
|
|
523
533
|
private deferredTerminal = false;
|
|
524
534
|
private deferredTurn: Promise<void> | null = null;
|
|
535
|
+
private deferredAbort = new AbortController();
|
|
525
536
|
|
|
526
537
|
constructor(config: ClaudeCodeDriverConfig) {
|
|
527
538
|
this.config = config;
|
|
@@ -586,7 +597,7 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
586
597
|
parseEvent: makeParseEvent(this.config, cliVersion),
|
|
587
598
|
formatAnswer,
|
|
588
599
|
formatPrompt,
|
|
589
|
-
|
|
600
|
+
idleTimeout: this.config.idleTimeout,
|
|
590
601
|
maxInterrupts: this.config.maxInterrupts,
|
|
591
602
|
});
|
|
592
603
|
for (const queued of this.pendingAttach) queued.attach(inner);
|
|
@@ -620,7 +631,7 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
620
631
|
// A terminal protocol event can arrive before the CLI and its descendants
|
|
621
632
|
// have exited. Quiesce the whole process tree before walking/copying files
|
|
622
633
|
// so it cannot swap a checked regular file into a symlink.
|
|
623
|
-
await this.
|
|
634
|
+
await this.quiesce();
|
|
624
635
|
const artifacts = await walkArtifacts(this.collectDir);
|
|
625
636
|
if (this.isolatedWorkspace && this.artifactsDir) {
|
|
626
637
|
await Promise.all(
|
|
@@ -652,6 +663,7 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
652
663
|
|
|
653
664
|
async quiesce(): Promise<void> {
|
|
654
665
|
if (this.config.interactionMode === "defer-resume") {
|
|
666
|
+
this.deferredAbort.abort();
|
|
655
667
|
await this.deferredTurn;
|
|
656
668
|
return;
|
|
657
669
|
}
|
|
@@ -661,6 +673,7 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
661
673
|
async close(): Promise<void> {
|
|
662
674
|
try {
|
|
663
675
|
if (this.config.interactionMode === "defer-resume") {
|
|
676
|
+
await this.quiesce();
|
|
664
677
|
this.emitter.removeAllListeners();
|
|
665
678
|
if (this.deferredHookPath) {
|
|
666
679
|
await rm(this.deferredHookPath, { force: true });
|
|
@@ -728,58 +741,47 @@ class ClaudeCodeDriverImpl implements AgentDriver {
|
|
|
728
741
|
} else if (resumeWithAnswer && answer?.kind === "ask") {
|
|
729
742
|
input = { input: answer.text };
|
|
730
743
|
}
|
|
731
|
-
const
|
|
732
|
-
this.config.command,
|
|
733
|
-
buildDeferredArgs(this.config, hookPath, this.deferredSessionId ?? undefined),
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
env
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
...(answer ? { LUX_CLAUDE_DEFERRED_ANSWER: JSON.stringify(answer) } : {}),
|
|
742
|
-
},
|
|
743
|
-
...input,
|
|
744
|
-
timeoutMs: this.config.idleTimeoutMs,
|
|
745
|
-
signal: ctx.abortSignal,
|
|
744
|
+
const terminal = await runDeferredProcess({
|
|
745
|
+
command: this.config.command,
|
|
746
|
+
args: buildDeferredArgs(this.config, hookPath, this.deferredSessionId ?? undefined),
|
|
747
|
+
cwd,
|
|
748
|
+
env: {
|
|
749
|
+
...process.env,
|
|
750
|
+
LUX_RUN_DIR: ctx.runDir,
|
|
751
|
+
LUX_ARTIFACTS_DIR: ctx.artifactsDir,
|
|
752
|
+
...this.config.env,
|
|
753
|
+
...(answer ? { LUX_CLAUDE_DEFERRED_ANSWER: JSON.stringify(answer) } : {}),
|
|
746
754
|
},
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
755
|
+
...input,
|
|
756
|
+
idleTimeoutMs: durationMs(this.config.idleTimeout),
|
|
757
|
+
...(this.config.turnTimeout === undefined
|
|
758
|
+
? {}
|
|
759
|
+
: { turnTimeoutMs: durationMs(this.config.turnTimeout) }),
|
|
760
|
+
signal: AbortSignal.any([ctx.abortSignal, this.deferredAbort.signal]),
|
|
761
|
+
parse: (line) => {
|
|
762
|
+
try {
|
|
763
|
+
const envelope = ClaudeCodeLineSchema.safeParse(JSON.parse(line));
|
|
764
|
+
if (
|
|
765
|
+
envelope.success &&
|
|
766
|
+
(envelope.data.type === "system" || envelope.data.type === "result") &&
|
|
767
|
+
envelope.data.session_id
|
|
768
|
+
) {
|
|
769
|
+
this.deferredSessionId = envelope.data.session_id;
|
|
770
|
+
}
|
|
771
|
+
} catch {
|
|
772
|
+
// The parser below deliberately ignores unmodeled output.
|
|
763
773
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
if (result.timedOut) {
|
|
774
|
-
throw new Error(`no event for ${this.config.idleTimeoutMs}ms`);
|
|
775
|
-
}
|
|
776
|
-
if (result.signal) throw new Error(`claude exited from signal ${result.signal}`);
|
|
777
|
-
if (result.exitCode !== 0 && !this.deferredTerminal) {
|
|
778
|
-
throw new Error(`claude exited ${result.exitCode}`);
|
|
779
|
-
}
|
|
780
|
-
if (!resuming && this.deferredReady) {
|
|
781
|
-
throw new Error("claude exited before ready");
|
|
774
|
+
return parse(line);
|
|
775
|
+
},
|
|
776
|
+
onEvent: (event) => this.dispatchDeferred(event),
|
|
777
|
+
});
|
|
778
|
+
if (this.deferredReady) {
|
|
779
|
+
throw new Error(
|
|
780
|
+
terminal?.tag === "error" ? terminal.error.reason : "claude exited before ready",
|
|
781
|
+
);
|
|
782
782
|
}
|
|
783
|
+
if (terminal) await this.dispatchDeferred(terminal);
|
|
784
|
+
if (!terminal) throw new Error("claude exited before a terminal result");
|
|
783
785
|
}
|
|
784
786
|
|
|
785
787
|
private async dispatchDeferred(event: ParsedDriverEvent): Promise<void> {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DurationSchema } from "../../core/duration.js";
|
|
2
3
|
import { applyCatalogEffort, codexCatalog, DEFAULT_CODEX_MODEL } from "../../core/index.js";
|
|
3
4
|
|
|
4
5
|
export type CodexConfigValue =
|
|
@@ -51,7 +52,7 @@ export const CodexDriverConfigSchema = z
|
|
|
51
52
|
isolation: IsolationSchema.optional(),
|
|
52
53
|
maxInterrupts: z.number().positive().default(20),
|
|
53
54
|
maxLineBytes: z.number().positive().default(10_000_000),
|
|
54
|
-
|
|
55
|
+
idleTimeout: DurationSchema.default("10 minutes"),
|
|
55
56
|
})
|
|
56
57
|
.strict()
|
|
57
58
|
.superRefine((config, ctx) => {
|
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline";
|
|
5
5
|
import { temporaryDirectory } from "tempy";
|
|
6
|
+
import { durationMs } from "../../core/duration.js";
|
|
6
7
|
import {
|
|
7
8
|
type AgentDriver,
|
|
8
9
|
type Answer,
|
|
@@ -654,8 +655,8 @@ class CodexAppServerDriverImpl implements AgentDriver {
|
|
|
654
655
|
this.idleTimer = setTimeout(() => {
|
|
655
656
|
this.idleTimer = null;
|
|
656
657
|
if (this.interrupts.size > 0) return;
|
|
657
|
-
this.fail(`no event for ${this.config.
|
|
658
|
-
}, this.config.
|
|
658
|
+
this.fail(`no event for ${durationMs(this.config.idleTimeout)}ms`, "idle-timeout");
|
|
659
|
+
}, durationMs(this.config.idleTimeout));
|
|
659
660
|
}
|
|
660
661
|
|
|
661
662
|
private clearIdleTimer(): void {
|
|
@@ -718,7 +719,7 @@ class CodexExecDriverImpl implements AgentDriver {
|
|
|
718
719
|
parseEvent: makeParseEvent(this.config, cliVersion),
|
|
719
720
|
formatPrompt: (prompt) => prompt,
|
|
720
721
|
closeStdinAfterPrompt: true,
|
|
721
|
-
|
|
722
|
+
idleTimeout: this.config.idleTimeout,
|
|
722
723
|
maxLineBytes: this.config.maxLineBytes,
|
|
723
724
|
maxInterrupts: 1,
|
|
724
725
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DurationSchema } from "../../core/duration.js";
|
|
2
3
|
import { applyCatalogEffort, copilotCatalog } from "../../core/index.js";
|
|
3
4
|
|
|
4
5
|
export const CopilotDriverConfigSchema = z
|
|
@@ -23,8 +24,8 @@ export const CopilotDriverConfigSchema = z
|
|
|
23
24
|
extraArgs: z.array(z.string()).optional(),
|
|
24
25
|
cwd: z.string().optional(),
|
|
25
26
|
env: z.record(z.string(), z.string()).optional(),
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
startupTimeout: DurationSchema.default("30 seconds"),
|
|
28
|
+
idleTimeout: DurationSchema.default("10 minutes"),
|
|
28
29
|
maxInterrupts: z.number().int().positive().default(20),
|
|
29
30
|
maxLineBytes: z.number().int().positive().default(10_000_000),
|
|
30
31
|
maxQueuedBytes: z.number().int().positive().default(20_000_000),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { durationMs } from "../../core/duration.js";
|
|
2
3
|
import {
|
|
3
4
|
type AgentDriver,
|
|
4
5
|
type Answer,
|
|
@@ -130,7 +131,7 @@ class CopilotDriver implements AgentDriver {
|
|
|
130
131
|
|
|
131
132
|
private request(method: string, params: RpcRecord): Promise<RpcRecord> {
|
|
132
133
|
if (!this.transport) throw new Error("Copilot ACP has not started");
|
|
133
|
-
return this.transport.request(method, params, this.config.
|
|
134
|
+
return this.transport.request(method, params, durationMs(this.config.startupTimeout));
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
private async run(prompt: string): Promise<void> {
|
|
@@ -209,10 +210,10 @@ class CopilotDriver implements AgentDriver {
|
|
|
209
210
|
this.idleTimer = setTimeout(
|
|
210
211
|
() =>
|
|
211
212
|
this.fail(
|
|
212
|
-
new Error(`Copilot emitted no event for ${this.config.
|
|
213
|
+
new Error(`Copilot emitted no event for ${durationMs(this.config.idleTimeout)}ms`),
|
|
213
214
|
"idle-timeout",
|
|
214
215
|
),
|
|
215
|
-
this.config.
|
|
216
|
+
durationMs(this.config.idleTimeout),
|
|
216
217
|
);
|
|
217
218
|
}
|
|
218
219
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DurationSchema } from "../../core/duration.js";
|
|
2
3
|
import { cursorCatalog } from "../../core/index.js";
|
|
3
4
|
|
|
4
5
|
export const CursorDriverConfigSchema = z
|
|
@@ -9,8 +10,8 @@ export const CursorDriverConfigSchema = z
|
|
|
9
10
|
mode: z.enum(["agent", "plan", "ask"]).default("agent"),
|
|
10
11
|
cwd: z.string().optional(),
|
|
11
12
|
env: z.record(z.string(), z.string()).optional(),
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
startupTimeout: DurationSchema.default("30 seconds"),
|
|
14
|
+
idleTimeout: DurationSchema.default("10 minutes"),
|
|
14
15
|
maxInterrupts: z.number().int().positive().default(20),
|
|
15
16
|
maxLineBytes: z.number().int().positive().default(10_000_000),
|
|
16
17
|
maxQueuedBytes: z.number().int().positive().default(20_000_000),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { durationMs } from "../../core/duration.js";
|
|
2
3
|
import {
|
|
3
4
|
type AgentDriver,
|
|
4
5
|
type Answer,
|
|
@@ -136,7 +137,7 @@ class CursorDriver implements AgentDriver {
|
|
|
136
137
|
|
|
137
138
|
private request(method: string, params: RpcRecord): Promise<RpcRecord> {
|
|
138
139
|
if (!this.transport) throw new Error("Cursor ACP has not started");
|
|
139
|
-
return this.transport.request(method, params, this.config.
|
|
140
|
+
return this.transport.request(method, params, durationMs(this.config.startupTimeout));
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
private async run(prompt: string): Promise<void> {
|
|
@@ -237,10 +238,10 @@ class CursorDriver implements AgentDriver {
|
|
|
237
238
|
this.idleTimer = setTimeout(
|
|
238
239
|
() =>
|
|
239
240
|
this.fail(
|
|
240
|
-
new Error(`Cursor emitted no event for ${this.config.
|
|
241
|
+
new Error(`Cursor emitted no event for ${durationMs(this.config.idleTimeout)}ms`),
|
|
241
242
|
"idle-timeout",
|
|
242
243
|
),
|
|
243
|
-
this.config.
|
|
244
|
+
durationMs(this.config.idleTimeout),
|
|
244
245
|
);
|
|
245
246
|
}
|
|
246
247
|
|
|
@@ -64,8 +64,8 @@ text and invalid fields still deny the operation. If the agent offers no matchin
|
|
|
64
64
|
one-operation option, Lux cancels and reports an error rather than widening the
|
|
65
65
|
grant. Native auto decisions are handled inside Grok and are not Lux interrupts.
|
|
66
66
|
|
|
67
|
-
Permission reviews have a separate `
|
|
68
|
-
default), while ordinary inactivity uses `
|
|
67
|
+
Permission reviews have a separate `permissionTimeout` (150 seconds by
|
|
68
|
+
default), while ordinary inactivity uses `idleTimeout` (10 minutes). Startup
|
|
69
69
|
RPCs time out after 30 seconds. Cancelled turns, process failures, unanswered
|
|
70
70
|
reviews, and turns ending with pending approvals cannot count as successful runs.
|
|
71
71
|
A completed turn can still contain denied tool calls: assert the expected files
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DurationSchema } from "../../core/duration.js";
|
|
2
3
|
import { applyCatalogEffort, grokBuildCatalog } from "../../core/index.js";
|
|
3
4
|
|
|
4
5
|
export const GrokBuildDriverConfigSchema = z
|
|
@@ -11,9 +12,9 @@ export const GrokBuildDriverConfigSchema = z
|
|
|
11
12
|
sandbox: z.enum(["workspace", "read-only", "strict"]).default("workspace"),
|
|
12
13
|
cwd: z.string().optional(),
|
|
13
14
|
env: z.record(z.string(), z.string()).optional(),
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
startupTimeout: DurationSchema.default("30 seconds"),
|
|
16
|
+
idleTimeout: DurationSchema.default("10 minutes"),
|
|
17
|
+
permissionTimeout: DurationSchema.default("150 seconds"),
|
|
17
18
|
maxInterrupts: z.number().int().positive().default(20),
|
|
18
19
|
maxLineBytes: z.number().int().positive().default(10_000_000),
|
|
19
20
|
maxQueuedBytes: z.number().int().positive().default(20_000_000),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { durationMs } from "../../core/duration.js";
|
|
2
3
|
import {
|
|
3
4
|
type AgentDriver,
|
|
4
5
|
type Answer,
|
|
@@ -143,7 +144,7 @@ class GrokBuildDriver implements AgentDriver {
|
|
|
143
144
|
|
|
144
145
|
private request(method: string, params: RpcRecord): Promise<RpcRecord> {
|
|
145
146
|
if (!this.transport) throw new Error("Grok.build ACP has not started");
|
|
146
|
-
return this.transport.request(method, params, this.config.
|
|
147
|
+
return this.transport.request(method, params, durationMs(this.config.startupTimeout));
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
private async run(prompt: string): Promise<void> {
|
|
@@ -198,10 +199,10 @@ class GrokBuildDriver implements AgentDriver {
|
|
|
198
199
|
this.interrupts.delete(interruptId);
|
|
199
200
|
this.fail(
|
|
200
201
|
new Error(
|
|
201
|
-
`Grok.build permission review timed out after ${this.config.
|
|
202
|
+
`Grok.build permission review timed out after ${durationMs(this.config.permissionTimeout)}ms`,
|
|
202
203
|
),
|
|
203
204
|
);
|
|
204
|
-
}, this.config.
|
|
205
|
+
}, durationMs(this.config.permissionTimeout));
|
|
205
206
|
this.interrupts.set(interruptId, { id, params, timer });
|
|
206
207
|
clearTimeout(this.idleTimer);
|
|
207
208
|
void this.emitter
|
|
@@ -243,10 +244,10 @@ class GrokBuildDriver implements AgentDriver {
|
|
|
243
244
|
this.idleTimer = setTimeout(
|
|
244
245
|
() =>
|
|
245
246
|
this.fail(
|
|
246
|
-
new Error(`Grok.build emitted no event for ${this.config.
|
|
247
|
+
new Error(`Grok.build emitted no event for ${durationMs(this.config.idleTimeout)}ms`),
|
|
247
248
|
"idle-timeout",
|
|
248
249
|
),
|
|
249
|
-
this.config.
|
|
250
|
+
durationMs(this.config.idleTimeout),
|
|
250
251
|
);
|
|
251
252
|
}
|
|
252
253
|
|
package/src/drivers/mcp/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import { getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { DurationSchema, durationMs as parseDuration } from "../../core/duration.js";
|
|
4
5
|
import {
|
|
5
6
|
type AgentDriver,
|
|
6
7
|
type Answer,
|
|
@@ -72,9 +73,9 @@ export const McpProbeDriverConfigSchema = z
|
|
|
72
73
|
/** The steps to run. Defaults to a single `tools/list` capture. */
|
|
73
74
|
probe: z.array(ProbeStepSchema).default([{ op: "list-tools" }]),
|
|
74
75
|
/** Timeout for the initialize handshake. */
|
|
75
|
-
|
|
76
|
+
startupTimeout: DurationSchema.default("30 seconds"),
|
|
76
77
|
/** Timeout for each `tools/list` / `tools/call` request. */
|
|
77
|
-
|
|
78
|
+
stepTimeout: DurationSchema.default("1 minute"),
|
|
78
79
|
/** Safety cap for a paginated `tools/list` operation. */
|
|
79
80
|
maxListPages: z.number().int().positive().default(100),
|
|
80
81
|
})
|
|
@@ -211,8 +212,8 @@ class McpProbeDriverImpl implements AgentDriver {
|
|
|
211
212
|
|
|
212
213
|
try {
|
|
213
214
|
await client.connect(transport, {
|
|
214
|
-
timeout: this.config.
|
|
215
|
-
maxTotalTimeout: this.config.
|
|
215
|
+
timeout: parseDuration(this.config.startupTimeout),
|
|
216
|
+
maxTotalTimeout: parseDuration(this.config.startupTimeout),
|
|
216
217
|
signal: ctx.abortSignal,
|
|
217
218
|
});
|
|
218
219
|
} catch (err) {
|
|
@@ -261,8 +262,8 @@ class McpProbeDriverImpl implements AgentDriver {
|
|
|
261
262
|
throw new Error(`tools/list exceeded ${this.config.maxListPages} pages`);
|
|
262
263
|
}
|
|
263
264
|
const result = await client.listTools(cursor ? { cursor } : undefined, {
|
|
264
|
-
timeout: this.config.
|
|
265
|
-
maxTotalTimeout: this.config.
|
|
265
|
+
timeout: parseDuration(this.config.stepTimeout),
|
|
266
|
+
maxTotalTimeout: parseDuration(this.config.stepTimeout),
|
|
266
267
|
...(this.abortSignal ? { signal: this.abortSignal } : {}),
|
|
267
268
|
});
|
|
268
269
|
tools.push(...(result.tools as ListedTool[]));
|
|
@@ -285,8 +286,8 @@ class McpProbeDriverImpl implements AgentDriver {
|
|
|
285
286
|
{ name: step.tool, arguments: step.args },
|
|
286
287
|
undefined,
|
|
287
288
|
{
|
|
288
|
-
timeout: this.config.
|
|
289
|
-
maxTotalTimeout: this.config.
|
|
289
|
+
timeout: parseDuration(this.config.stepTimeout),
|
|
290
|
+
maxTotalTimeout: parseDuration(this.config.stepTimeout),
|
|
290
291
|
...(this.abortSignal ? { signal: this.abortSignal } : {}),
|
|
291
292
|
},
|
|
292
293
|
);
|
|
@@ -2,6 +2,7 @@ import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { DurationSchema, durationMs } from "../../core/duration.js";
|
|
5
6
|
import {
|
|
6
7
|
type AgentDriver,
|
|
7
8
|
type Answer,
|
|
@@ -36,7 +37,7 @@ export const SubprocessDriverConfigSchema = z
|
|
|
36
37
|
/** Inherit the host environment. Experiment mode always forces this off. */
|
|
37
38
|
inheritEnv: z.boolean().optional(),
|
|
38
39
|
protocol: z.enum(["lux-jsonl", "custom"]).default("lux-jsonl"),
|
|
39
|
-
|
|
40
|
+
idleTimeout: DurationSchema.default("2 minutes"),
|
|
40
41
|
maxInterrupts: z.number().positive().default(20),
|
|
41
42
|
/**
|
|
42
43
|
* Maximum files inventoried from LUX_ARTIFACTS_DIR. Keep the conservative
|
|
@@ -485,7 +486,7 @@ class SubprocessDriverImpl implements AgentDriver {
|
|
|
485
486
|
private bumpIdleTimer(): void {
|
|
486
487
|
this.clearIdleTimer();
|
|
487
488
|
this.idleTimer = setTimeout(() => {
|
|
488
|
-
const reason = `no event for ${this.config.
|
|
489
|
+
const reason = `no event for ${durationMs(this.config.idleTimeout)}ms`;
|
|
489
490
|
// Idle before ready means start() is still pending; reject it (and kill
|
|
490
491
|
// the child) or the orchestrator waits forever.
|
|
491
492
|
const preReady = this.readyRejected !== null;
|
|
@@ -497,7 +498,7 @@ class SubprocessDriverImpl implements AgentDriver {
|
|
|
497
498
|
// Kill immediately: the run is lost and the child would keep doing
|
|
498
499
|
// billable work until close().
|
|
499
500
|
this.terminate();
|
|
500
|
-
}, this.config.
|
|
501
|
+
}, durationMs(this.config.idleTimeout));
|
|
501
502
|
}
|
|
502
503
|
|
|
503
504
|
private clearIdleTimer(): void {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { DurationSchema, durationMs } from "./core/duration.js";
|
|
1
2
|
// Public surface of @prismatic-io/lux. The package root exposes authoring
|
|
2
3
|
// contracts, extension points, built-ins, and supported programmatic
|
|
3
4
|
// lifecycles. Explicit exports keep implementation and test helpers private.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DurationSchema, durationMs } from "../core/duration.js";
|
|
2
3
|
import {
|
|
3
4
|
type CandidateEvaluation,
|
|
4
5
|
type CandidateProposal,
|
|
@@ -16,7 +17,7 @@ export const ReflectiveOptimizerConfigSchema = z
|
|
|
16
17
|
model: z.string().optional(),
|
|
17
18
|
command: z.string().optional(),
|
|
18
19
|
reasoningEffort: ReasoningEffortSchema.optional(),
|
|
19
|
-
|
|
20
|
+
timeout: DurationSchema.default("3 minutes"),
|
|
20
21
|
maxComponentChars: z.number().int().positive().default(80_000),
|
|
21
22
|
maxAlternativeChars: z.number().int().positive().default(40_000),
|
|
22
23
|
maxFeedbackChars: z.number().int().positive().default(50_000),
|
|
@@ -273,7 +274,7 @@ const reflectiveOptimizer = (config: ReflectiveOptimizerConfig): Optimizer => ({
|
|
|
273
274
|
model,
|
|
274
275
|
command: config.command,
|
|
275
276
|
reasoningEffort: config.reasoningEffort,
|
|
276
|
-
timeoutMs: config.
|
|
277
|
+
timeoutMs: durationMs(config.timeout),
|
|
277
278
|
system: systemPrompt(input.campaign.evaluation.kind),
|
|
278
279
|
signal: input.abortSignal,
|
|
279
280
|
prompt: builtPrompt.prompt,
|
|
@@ -5,6 +5,7 @@ import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep
|
|
|
5
5
|
import { temporaryDirectory } from "tempy";
|
|
6
6
|
import { glob } from "tinyglobby";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
+
import { durationMs } from "../core/duration.js";
|
|
8
9
|
import { exec } from "../core/exec.js";
|
|
9
10
|
import {
|
|
10
11
|
type Candidate,
|
|
@@ -226,7 +227,7 @@ export const validateCandidate = async (
|
|
|
226
227
|
try {
|
|
227
228
|
return await exec(validation.command, validation.args, {
|
|
228
229
|
cwd: materializedRoot,
|
|
229
|
-
timeoutMs: validation.
|
|
230
|
+
timeoutMs: durationMs(validation.timeout),
|
|
230
231
|
env,
|
|
231
232
|
...(signal ? { signal } : {}),
|
|
232
233
|
});
|
|
@@ -236,7 +237,7 @@ export const validateCandidate = async (
|
|
|
236
237
|
})().finally(() => rm(validationHome, { recursive: true, force: true }));
|
|
237
238
|
if (result.timedOut || result.exitCode !== 0) {
|
|
238
239
|
const reason = result.timedOut
|
|
239
|
-
? `timed out after ${validation.
|
|
240
|
+
? `timed out after ${durationMs(validation.timeout)}ms`
|
|
240
241
|
: `exited ${result.exitCode}`;
|
|
241
242
|
throw new Error(
|
|
242
243
|
`candidate validation '${validation.command}' ${reason}: ${result.stderr.slice(0, 500)}`,
|
|
@@ -213,7 +213,7 @@ export const gradeRun = async (
|
|
|
213
213
|
if (validatedConfigs && index >= validatedConfigs.length) {
|
|
214
214
|
throw new Error(`validated assertion plan is missing assertions[${index}]`);
|
|
215
215
|
}
|
|
216
|
-
// Pass the validated config so schema defaults (e.g.
|
|
216
|
+
// Pass the validated config so schema defaults (e.g. timeout)
|
|
217
217
|
// reach evaluate(); the raw assertion object has none applied.
|
|
218
218
|
const result = AssertionResultSchema.parse(
|
|
219
219
|
await impl.evaluate(validated as typeof a, run, {
|