@rallycry/conveyor-agent 10.2.2 → 10.2.5
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/chunk-7TQO4ZF4.js +60 -0
- package/dist/chunk-7TQO4ZF4.js.map +1 -0
- package/dist/{chunk-53ZLFIAI.js → chunk-CFELRV35.js} +1405 -1233
- package/dist/chunk-CFELRV35.js.map +1 -0
- package/dist/cli.js +209 -10
- package/dist/cli.js.map +1 -1
- package/dist/heartbeat-worker.d.ts +2 -0
- package/dist/heartbeat-worker.js +47 -0
- package/dist/heartbeat-worker.js.map +1 -0
- package/dist/index.d.ts +50 -42
- package/dist/index.js +19 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/entrypoint.sh +93 -4
- package/dist/chunk-53ZLFIAI.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
AgentConnection,
|
|
4
|
+
ClaudeTuiAdapter,
|
|
4
5
|
DEFAULT_LIFECYCLE_CONFIG,
|
|
5
6
|
DEFAULT_SONNET_MODEL,
|
|
6
7
|
Lifecycle,
|
|
7
8
|
PtyHarness,
|
|
8
9
|
SessionRunner,
|
|
10
|
+
TUI_KINDS,
|
|
9
11
|
applyBootstrapToEnv,
|
|
10
12
|
awaitGitReady,
|
|
13
|
+
buildPromptBytes,
|
|
11
14
|
buildSessionPreviewPorts,
|
|
15
|
+
cleanTerminalOutput,
|
|
12
16
|
createServiceLogger,
|
|
13
17
|
fetchBootstrap,
|
|
18
|
+
inheritedEnv,
|
|
14
19
|
loadConveyorConfig,
|
|
15
20
|
loadForwardPorts,
|
|
16
21
|
resolveSessionStart,
|
|
17
22
|
runSetupCommand,
|
|
18
23
|
runStartCommand,
|
|
19
24
|
sampleKeyUsage
|
|
20
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CFELRV35.js";
|
|
26
|
+
import "./chunk-7TQO4ZF4.js";
|
|
21
27
|
|
|
22
28
|
// src/cli.ts
|
|
23
29
|
import { readFileSync } from "fs";
|
|
24
|
-
import { join, dirname as dirname2 } from "path";
|
|
30
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
25
31
|
import { fileURLToPath } from "url";
|
|
26
32
|
|
|
27
33
|
// src/setup/sidecars.ts
|
|
@@ -169,6 +175,29 @@ async function checkSessionTaskIdentity(params) {
|
|
|
169
175
|
return "match";
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
// src/utils/agent-exit.ts
|
|
179
|
+
function exitLogLevel(reason) {
|
|
180
|
+
return reason === "clean" ? "info" : "warn";
|
|
181
|
+
}
|
|
182
|
+
function buildAgentExitLog(args) {
|
|
183
|
+
const log = {
|
|
184
|
+
event: "agent_exit",
|
|
185
|
+
reason: args.reason,
|
|
186
|
+
exitCode: args.exitCode,
|
|
187
|
+
uptimeSec: Math.round(args.uptimeSec * 10) / 10
|
|
188
|
+
};
|
|
189
|
+
if (args.signal !== void 0) log.signal = args.signal;
|
|
190
|
+
if (args.finalState !== void 0) log.finalState = args.finalState;
|
|
191
|
+
const id = args.identity;
|
|
192
|
+
if (id) {
|
|
193
|
+
if (id.sessionId !== void 0) log.sessionId = id.sessionId;
|
|
194
|
+
if (id.taskId !== void 0) log.taskId = id.taskId;
|
|
195
|
+
if (id.workspaceId !== void 0) log.workspaceId = id.workspaceId;
|
|
196
|
+
if (id.runnerMode !== void 0) log.runnerMode = id.runnerMode;
|
|
197
|
+
}
|
|
198
|
+
return log;
|
|
199
|
+
}
|
|
200
|
+
|
|
172
201
|
// src/setup/project-identity.ts
|
|
173
202
|
function resolveProjectRunnerIdentity(env) {
|
|
174
203
|
if (env.CONVEYOR_TASK_ID) return null;
|
|
@@ -317,11 +346,118 @@ var ProjectSessionRunner = class {
|
|
|
317
346
|
}
|
|
318
347
|
};
|
|
319
348
|
|
|
349
|
+
// src/harness/pty/adapters/types.ts
|
|
350
|
+
import { accessSync, constants, statSync } from "fs";
|
|
351
|
+
import { join } from "path";
|
|
352
|
+
var TuiUnavailableError = class extends Error {
|
|
353
|
+
constructor(tui, message) {
|
|
354
|
+
super(message);
|
|
355
|
+
this.tui = tui;
|
|
356
|
+
this.name = "TuiUnavailableError";
|
|
357
|
+
}
|
|
358
|
+
tui;
|
|
359
|
+
};
|
|
360
|
+
function isExecutable(path) {
|
|
361
|
+
try {
|
|
362
|
+
if (!statSync(path).isFile()) return false;
|
|
363
|
+
accessSync(path, constants.X_OK);
|
|
364
|
+
return true;
|
|
365
|
+
} catch {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function findOnPath(binary, env = process.env) {
|
|
370
|
+
if (binary.includes("/")) {
|
|
371
|
+
return isExecutable(binary) ? binary : null;
|
|
372
|
+
}
|
|
373
|
+
for (const dir of (env.PATH ?? "").split(":")) {
|
|
374
|
+
if (!dir) continue;
|
|
375
|
+
const candidate = join(dir, binary);
|
|
376
|
+
if (isExecutable(candidate)) return candidate;
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/harness/pty/adapters/opencode.ts
|
|
382
|
+
var PROVIDER_KEY_ENV = {
|
|
383
|
+
openai: "OPENAI_API_KEY",
|
|
384
|
+
anthropic: "ANTHROPIC_API_KEY"
|
|
385
|
+
};
|
|
386
|
+
var OpenCodeTuiAdapter = class {
|
|
387
|
+
constructor(env = process.env) {
|
|
388
|
+
this.env = env;
|
|
389
|
+
}
|
|
390
|
+
env;
|
|
391
|
+
id = "opencode";
|
|
392
|
+
capabilities = {
|
|
393
|
+
resume: false,
|
|
394
|
+
structuredEvents: false,
|
|
395
|
+
prefill: false,
|
|
396
|
+
passiveTurns: false
|
|
397
|
+
};
|
|
398
|
+
resolveBinary(env = this.env) {
|
|
399
|
+
const override = env.CONVEYOR_OPENCODE_BIN;
|
|
400
|
+
const found = override ? findOnPath(override, env) : findOnPath("opencode", env);
|
|
401
|
+
if (!found) {
|
|
402
|
+
throw new TuiUnavailableError(
|
|
403
|
+
"opencode",
|
|
404
|
+
"The opencode CLI is not available in this environment. It must be baked into the pod image (see Dockerfile.base) \u2014 re-run Build Image for this project, or set CONVEYOR_OPENCODE_BIN to its absolute path."
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
return found;
|
|
408
|
+
}
|
|
409
|
+
buildSpawn(input) {
|
|
410
|
+
const env = { ...inheritedEnv() };
|
|
411
|
+
delete env.CONVEYOR_AGENT_KEY;
|
|
412
|
+
const key = this.env.CONVEYOR_AGENT_KEY;
|
|
413
|
+
const provider = this.env.CONVEYOR_AGENT_PROVIDER ?? "openai";
|
|
414
|
+
const keyEnvVar = PROVIDER_KEY_ENV[provider];
|
|
415
|
+
if (key && keyEnvVar) env[keyEnvVar] = key;
|
|
416
|
+
const model = this.env.CONVEYOR_AGENT_MODEL ?? input.options.model;
|
|
417
|
+
const args = [];
|
|
418
|
+
if (model) args.push("--model", model.includes("/") ? model : `${provider}/${model}`);
|
|
419
|
+
return { file: this.resolveBinary(), args, env };
|
|
420
|
+
}
|
|
421
|
+
async prepareEnvironment() {
|
|
422
|
+
}
|
|
423
|
+
spawnFingerprint(input) {
|
|
424
|
+
return JSON.stringify(["opencode", input.model, input.cwd]);
|
|
425
|
+
}
|
|
426
|
+
encodePromptBytes(text) {
|
|
427
|
+
return buildPromptBytes(text);
|
|
428
|
+
}
|
|
429
|
+
buildExitErrors(exitCode, rawOutput) {
|
|
430
|
+
const errors = [`opencode exited (code ${exitCode}) without a result`];
|
|
431
|
+
const tail = cleanTerminalOutput(rawOutput);
|
|
432
|
+
if (tail) errors.push(`Last terminal output before exit:
|
|
433
|
+
${tail}`);
|
|
434
|
+
return errors;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// src/harness/pty/adapters/index.ts
|
|
439
|
+
function resolveTuiAdapter(kind = "claude-code") {
|
|
440
|
+
switch (kind) {
|
|
441
|
+
case "claude-code":
|
|
442
|
+
return new ClaudeTuiAdapter();
|
|
443
|
+
case "opencode":
|
|
444
|
+
return new OpenCodeTuiAdapter();
|
|
445
|
+
default:
|
|
446
|
+
throw new Error(`Unknown TUI kind: ${kind}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
320
450
|
// src/runner/adhoc-session-runner.ts
|
|
321
451
|
var ADHOC_SYSTEM_NOTE = "You are running in an ad-hoc Conveyor scratch pod \u2014 an interactive terminal on the project's repository checked out at its default branch. There is no task or plan; help the human with whatever they ask directly.";
|
|
452
|
+
function resolveAdhocTui(env) {
|
|
453
|
+
const raw = env.CONVEYOR_TUI ?? "claude-code";
|
|
454
|
+
if (TUI_KINDS.includes(raw)) return raw;
|
|
455
|
+
throw new Error(`Unknown TUI "${raw}" in CONVEYOR_TUI (expected: ${TUI_KINDS.join(", ")})`);
|
|
456
|
+
}
|
|
322
457
|
function buildAdhocPtyBridge(connection) {
|
|
323
458
|
return {
|
|
324
459
|
sendOutput: (data, dims) => connection.sendPtyOutput(data, dims),
|
|
460
|
+
sendChatEvent: (event) => connection.sendPtyChatEvent(event),
|
|
325
461
|
onInput: (handler) => connection.onPtyInput(handler),
|
|
326
462
|
onResize: (handler) => connection.onPtyResize(handler)
|
|
327
463
|
};
|
|
@@ -359,7 +495,10 @@ var AdhocSessionRunner = class {
|
|
|
359
495
|
this.config = config;
|
|
360
496
|
this.callbacks = callbacks;
|
|
361
497
|
this.connection = deps?.connection ?? new AgentConnection(config.connection);
|
|
362
|
-
this.harness = deps?.harness ?? new PtyHarness(
|
|
498
|
+
this.harness = deps?.harness ?? new PtyHarness(
|
|
499
|
+
buildAdhocPtyBridge(this.connection),
|
|
500
|
+
resolveTuiAdapter(resolveAdhocTui(process.env))
|
|
501
|
+
);
|
|
363
502
|
this.lifecycle = new Lifecycle(
|
|
364
503
|
// No git flush: the WIP snapshot machinery is branch/task-shaped; the human
|
|
365
504
|
// commits/pushes explicitly from the interactive shell.
|
|
@@ -419,7 +558,7 @@ var AdhocSessionRunner = class {
|
|
|
419
558
|
* loop blocks (keeping the pod alive) until the human exits or stop() aborts.
|
|
420
559
|
*/
|
|
421
560
|
async runInteractiveTui() {
|
|
422
|
-
const model = this.config.model ?? process.env.CONVEYOR_ADHOC_MODEL ?? DEFAULT_SONNET_MODEL;
|
|
561
|
+
const model = this.config.model ?? process.env.CONVEYOR_AGENT_MODEL ?? process.env.CONVEYOR_ADHOC_MODEL ?? DEFAULT_SONNET_MODEL;
|
|
423
562
|
const session = resolveSessionStart(this.config.workspaceId, this.config.workspaceDir);
|
|
424
563
|
const options = buildAdhocQueryOptions(
|
|
425
564
|
this.config.workspaceDir,
|
|
@@ -428,8 +567,11 @@ var AdhocSessionRunner = class {
|
|
|
428
567
|
session
|
|
429
568
|
);
|
|
430
569
|
try {
|
|
431
|
-
for await (const
|
|
570
|
+
for await (const event of this.harness.executeQuery({ prompt: "", options })) {
|
|
432
571
|
if (this.stopped) break;
|
|
572
|
+
if (event.type === "result" && event.subtype === "error") {
|
|
573
|
+
throw new Error(event.errors.join("\n"));
|
|
574
|
+
}
|
|
433
575
|
}
|
|
434
576
|
} catch (error) {
|
|
435
577
|
if (!this.stopped) throw error;
|
|
@@ -628,12 +770,27 @@ var ReviewChildSupervisor = class {
|
|
|
628
770
|
// src/cli.ts
|
|
629
771
|
if (process.argv.includes("--version")) {
|
|
630
772
|
const __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
631
|
-
const pkgPath =
|
|
773
|
+
const pkgPath = join2(__dirname, "..", "package.json");
|
|
632
774
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
633
775
|
process.stdout.write(pkg.version + "\n");
|
|
634
776
|
process.exit(0);
|
|
635
777
|
}
|
|
636
778
|
var logger2 = createServiceLogger("CLI");
|
|
779
|
+
var exitContext = {};
|
|
780
|
+
var exitLogged = false;
|
|
781
|
+
function logAgentExit(reason, opts) {
|
|
782
|
+
if (exitLogged) return;
|
|
783
|
+
exitLogged = true;
|
|
784
|
+
const payload = buildAgentExitLog({
|
|
785
|
+
reason,
|
|
786
|
+
exitCode: opts.exitCode,
|
|
787
|
+
signal: opts.signal,
|
|
788
|
+
finalState: opts.finalState,
|
|
789
|
+
uptimeSec: process.uptime(),
|
|
790
|
+
identity: exitContext
|
|
791
|
+
});
|
|
792
|
+
logger2[exitLogLevel(reason)]("agent_exit", payload);
|
|
793
|
+
}
|
|
637
794
|
async function bootstrapFromCodespace(apiUrl, instanceName) {
|
|
638
795
|
const bootstrapToken = process.env.CONVEYOR_BOOTSTRAP_TOKEN;
|
|
639
796
|
const apiUrlFromEnv = Boolean(process.env.CONVEYOR_API_URL);
|
|
@@ -680,6 +837,7 @@ process.on("uncaughtException", (err) => {
|
|
|
680
837
|
return;
|
|
681
838
|
}
|
|
682
839
|
logger2.error("Uncaught exception", { error: err.message, code: err.code });
|
|
840
|
+
logAgentExit("uncaught_exception", { exitCode: 1 });
|
|
683
841
|
process.exit(1);
|
|
684
842
|
});
|
|
685
843
|
process.on("unhandledRejection", (reason) => {
|
|
@@ -689,6 +847,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
689
847
|
return;
|
|
690
848
|
}
|
|
691
849
|
logger2.error("Unhandled rejection", { error: err.message });
|
|
850
|
+
logAgentExit("unhandled_rejection", { exitCode: 1 });
|
|
692
851
|
process.exit(1);
|
|
693
852
|
});
|
|
694
853
|
var DEFAULT_CONVEYOR_API_URL = "https://api.conveyor.rallycryapp.com";
|
|
@@ -708,6 +867,10 @@ var CONVEYOR_WORKSPACE = process.env.CONVEYOR_WORKSPACE ?? process.cwd();
|
|
|
708
867
|
var CONVEYOR_MODE = process.env.CONVEYOR_MODE ?? "task";
|
|
709
868
|
var CONVEYOR_AGENT_MODE = process.env.CONVEYOR_AGENT_MODE || void 0;
|
|
710
869
|
var CONVEYOR_IS_AUTO = CONVEYOR_AGENT_MODE ? CONVEYOR_AGENT_MODE === "auto" : process.env.CONVEYOR_IS_AUTO === "true";
|
|
870
|
+
exitContext.taskId = CONVEYOR_TASK_ID;
|
|
871
|
+
exitContext.workspaceId = process.env.CONVEYOR_WORKSPACE_ID;
|
|
872
|
+
exitContext.runnerMode = CONVEYOR_MODE;
|
|
873
|
+
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? CONVEYOR_TASK_ID;
|
|
711
874
|
var projectIdentity = resolveProjectRunnerIdentity(process.env);
|
|
712
875
|
if (!CONVEYOR_TASK_ID && projectIdentity && CONVEYOR_MODE === "adhoc") {
|
|
713
876
|
logger2.info("Starting ad-hoc agent", { projectId: projectIdentity.projectId });
|
|
@@ -730,10 +893,18 @@ if (!CONVEYOR_TASK_ID && projectIdentity && CONVEYOR_MODE === "adhoc") {
|
|
|
730
893
|
}
|
|
731
894
|
}
|
|
732
895
|
);
|
|
896
|
+
exitContext.sessionId = projectIdentity.sessionId;
|
|
897
|
+
exitContext.workspaceId = projectIdentity.workspaceId;
|
|
898
|
+
exitContext.runnerMode = "adhoc";
|
|
733
899
|
process.on("SIGTERM", () => adhocRunner.stop());
|
|
734
900
|
process.on("SIGINT", () => adhocRunner.stop());
|
|
735
901
|
await adhocRunner.run();
|
|
736
|
-
|
|
902
|
+
const adhocError = adhocRunner.finalState === "error";
|
|
903
|
+
logAgentExit(adhocError ? "error" : "clean", {
|
|
904
|
+
exitCode: adhocError ? 1 : 0,
|
|
905
|
+
finalState: adhocRunner.finalState ?? void 0
|
|
906
|
+
});
|
|
907
|
+
process.exit(adhocError ? 1 : 0);
|
|
737
908
|
}
|
|
738
909
|
if (!CONVEYOR_TASK_ID && projectIdentity) {
|
|
739
910
|
logger2.info("Starting project agent", { projectId: projectIdentity.projectId });
|
|
@@ -755,10 +926,18 @@ if (!CONVEYOR_TASK_ID && projectIdentity) {
|
|
|
755
926
|
}
|
|
756
927
|
}
|
|
757
928
|
);
|
|
929
|
+
exitContext.sessionId = projectIdentity.sessionId;
|
|
930
|
+
exitContext.workspaceId = projectIdentity.workspaceId;
|
|
931
|
+
exitContext.runnerMode = "pm";
|
|
758
932
|
process.on("SIGTERM", () => projectRunner.stop());
|
|
759
933
|
process.on("SIGINT", () => projectRunner.stop());
|
|
760
934
|
await projectRunner.run();
|
|
761
|
-
|
|
935
|
+
const projectError = projectRunner.finalState === "error";
|
|
936
|
+
logAgentExit(projectError ? "error" : "clean", {
|
|
937
|
+
exitCode: projectError ? 1 : 0,
|
|
938
|
+
finalState: projectRunner.finalState ?? void 0
|
|
939
|
+
});
|
|
940
|
+
process.exit(projectError ? 1 : 0);
|
|
762
941
|
}
|
|
763
942
|
if (!CONVEYOR_TASK_TOKEN || !CONVEYOR_TASK_ID) {
|
|
764
943
|
logger2.error("Missing required environment variables");
|
|
@@ -813,8 +992,10 @@ var runner = new SessionRunner(
|
|
|
813
992
|
}
|
|
814
993
|
);
|
|
815
994
|
var reviewChildren = new ReviewChildSupervisor(runner.connection, CONVEYOR_WORKSPACE);
|
|
995
|
+
var shutdownSignal;
|
|
816
996
|
var shutdownAgent = (signal) => {
|
|
817
997
|
logger2.info(`Received ${signal}, flushing git and stopping agent`);
|
|
998
|
+
shutdownSignal = signal;
|
|
818
999
|
void (async () => {
|
|
819
1000
|
try {
|
|
820
1001
|
await reviewChildren.stopAll();
|
|
@@ -828,6 +1009,17 @@ var shutdownAgent = (signal) => {
|
|
|
828
1009
|
})();
|
|
829
1010
|
setTimeout(() => {
|
|
830
1011
|
logger2.warn(`Forcing exit after ${signal} timeout`);
|
|
1012
|
+
logger2.warn(
|
|
1013
|
+
"agent_exit",
|
|
1014
|
+
buildAgentExitLog({
|
|
1015
|
+
reason: "force_timeout",
|
|
1016
|
+
exitCode: 1,
|
|
1017
|
+
signal,
|
|
1018
|
+
finalState: runner.finalState ?? void 0,
|
|
1019
|
+
uptimeSec: process.uptime(),
|
|
1020
|
+
identity: exitContext
|
|
1021
|
+
})
|
|
1022
|
+
);
|
|
831
1023
|
process.exit(1);
|
|
832
1024
|
}, 45e3).unref();
|
|
833
1025
|
};
|
|
@@ -964,16 +1156,23 @@ if (conveyorConfig) {
|
|
|
964
1156
|
runner.connection.sendEvent({
|
|
965
1157
|
type: "setup_complete",
|
|
966
1158
|
startCommandRunning,
|
|
967
|
-
previewPort: conveyorConfig.previewPort,
|
|
968
1159
|
...previewPorts.length > 0 ? { previewPorts } : {}
|
|
969
1160
|
});
|
|
970
1161
|
})();
|
|
971
1162
|
}
|
|
972
1163
|
runner.run().then(() => {
|
|
973
|
-
|
|
1164
|
+
const errored = runner.finalState === "error";
|
|
1165
|
+
const reason = shutdownSignal ? "signal" : errored ? "error" : "clean";
|
|
1166
|
+
logAgentExit(reason, {
|
|
1167
|
+
exitCode: errored ? 1 : 0,
|
|
1168
|
+
signal: shutdownSignal,
|
|
1169
|
+
finalState: runner.finalState ?? void 0
|
|
1170
|
+
});
|
|
1171
|
+
process.exit(errored ? 1 : 0);
|
|
974
1172
|
}).catch((error) => {
|
|
975
1173
|
const msg = error instanceof Error ? error.message : String(error);
|
|
976
1174
|
logger2.error("Agent runner failed", { error: msg });
|
|
1175
|
+
logAgentExit("error", { exitCode: 1, finalState: runner.finalState ?? void 0 });
|
|
977
1176
|
process.exit(1);
|
|
978
1177
|
});
|
|
979
1178
|
//# sourceMappingURL=cli.js.map
|