@rallycry/conveyor-agent 11.0.16 → 11.0.18
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/{boot-CCRZMY2P.js → boot-4ZNOCKFZ.js} +4 -2
- package/dist/chunk-372R6E4C.js +147 -0
- package/dist/{chunk-7TSQXIN3.js → chunk-EIHZRSYI.js} +16 -9
- package/dist/{chunk-X2MKHJBZ.js → chunk-GZRZGBIK.js} +2 -2
- package/dist/{chunk-B54XFOXL.js → chunk-SQM2BQ7H.js} +32 -0
- package/dist/{chunk-QLMNQSJL.js → chunk-W5INK3NE.js} +1 -1
- package/dist/{chunk-M4SD2ESQ.js → chunk-WS7QRB37.js} +1 -1
- package/dist/{chunk-GAUVITY7.js → chunk-ZFHN7GO7.js} +77 -22
- package/dist/cli.js +9 -9
- package/dist/{client-DVVHMIOO.js → client-VIFYMBT2.js} +1 -1
- package/dist/gate-cli.js +4 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.js +5 -5
- package/dist/{serve-boot-VJR6J5PL.js → serve-boot-RU5BYIK4.js} +3 -3
- package/dist/{server-WM23BC5C.js → server-L5CQ5EJB.js} +5 -2
- package/package.json +1 -1
- package/dist/chunk-7P6QZHXZ.js +0 -76
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startWorkbenchServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EIHZRSYI.js";
|
|
4
4
|
import {
|
|
5
5
|
TOMBSTONE_MESSAGE,
|
|
6
6
|
isLegacyEntrypointLaunch
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
refreshSkillsAfterCheckout,
|
|
13
13
|
runPreReadyBinds
|
|
14
14
|
} from "./chunk-SR66HQKB.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-W5INK3NE.js";
|
|
16
16
|
import {
|
|
17
17
|
GitPrepJob,
|
|
18
18
|
defaultGit,
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
reportBootMilestone
|
|
21
21
|
} from "./chunk-GL2DIQEQ.js";
|
|
22
22
|
import "./chunk-W4LZ7R6Z.js";
|
|
23
|
+
import "./chunk-372R6E4C.js";
|
|
24
|
+
import "./chunk-SQM2BQ7H.js";
|
|
23
25
|
import {
|
|
24
26
|
workbenchPort
|
|
25
27
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getWorkbenchClient
|
|
3
|
+
} from "./chunk-SQM2BQ7H.js";
|
|
4
|
+
import {
|
|
5
|
+
sharedDir,
|
|
6
|
+
workbenchEnabled
|
|
7
|
+
} from "./chunk-KMB3BU4S.js";
|
|
8
|
+
|
|
9
|
+
// src/runner/heavy-gate.ts
|
|
10
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
var GATE_KEYS = ["heavy", "test", "typecheck", "build"];
|
|
13
|
+
var GATES_DIR_NAME = "gates";
|
|
14
|
+
function runDir() {
|
|
15
|
+
return process.env.CONVEYOR_RUN_DIR ?? "/tmp/conveyor-run";
|
|
16
|
+
}
|
|
17
|
+
function gatesDir() {
|
|
18
|
+
return path.join(sharedDir() ?? runDir(), GATES_DIR_NAME);
|
|
19
|
+
}
|
|
20
|
+
function pidAlive(pid) {
|
|
21
|
+
if (process.platform === "linux" && pidIsZombie(pid)) return false;
|
|
22
|
+
try {
|
|
23
|
+
process.kill(pid, 0);
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function pidIsZombie(pid) {
|
|
30
|
+
try {
|
|
31
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
32
|
+
const closingParen = stat.lastIndexOf(")");
|
|
33
|
+
return closingParen >= 0 && stat.slice(closingParen + 1).trimStart().startsWith("Z");
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function parsePid(raw) {
|
|
39
|
+
const pid = Number.parseInt(raw.trim(), 10);
|
|
40
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
41
|
+
}
|
|
42
|
+
function singletonPidFiles() {
|
|
43
|
+
return GATE_KEYS.map((key) => path.join(runDir(), `${key}.pid`));
|
|
44
|
+
}
|
|
45
|
+
var splitGenericGateStatus = null;
|
|
46
|
+
var splitGenericGateStatusRefresh = null;
|
|
47
|
+
function getLocalGenericGateStatus() {
|
|
48
|
+
const activeLabels = [];
|
|
49
|
+
const abandonedReceipts = [];
|
|
50
|
+
let entries;
|
|
51
|
+
try {
|
|
52
|
+
entries = readdirSync(gatesDir());
|
|
53
|
+
} catch {
|
|
54
|
+
return { activeLabels, abandonedReceipts };
|
|
55
|
+
}
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (!entry.endsWith(".pid")) continue;
|
|
58
|
+
const pidPath = path.join(gatesDir(), entry);
|
|
59
|
+
const label = entry.slice(0, -".pid".length);
|
|
60
|
+
try {
|
|
61
|
+
const pid = parsePid(readFileSync(pidPath, "utf8"));
|
|
62
|
+
if (pid === null) continue;
|
|
63
|
+
if (pidAlive(pid)) {
|
|
64
|
+
activeLabels.push(label);
|
|
65
|
+
} else if (!existsSync(path.join(gatesDir(), `${label}.exit`))) {
|
|
66
|
+
abandonedReceipts.push({ path: pidPath, label, pid, mtimeMs: statSync(pidPath).mtimeMs });
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
abandonedReceipts.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
72
|
+
return { activeLabels, abandonedReceipts };
|
|
73
|
+
}
|
|
74
|
+
function genericGateStatus() {
|
|
75
|
+
if (!workbenchEnabled()) return getLocalGenericGateStatus();
|
|
76
|
+
return splitGenericGateStatus ?? { activeLabels: ["unknown"], abandonedReceipts: [] };
|
|
77
|
+
}
|
|
78
|
+
function refreshGenericGateStatus() {
|
|
79
|
+
if (!workbenchEnabled()) return Promise.resolve(getLocalGenericGateStatus());
|
|
80
|
+
if (splitGenericGateStatusRefresh) return splitGenericGateStatusRefresh;
|
|
81
|
+
splitGenericGateStatusRefresh = getWorkbenchClient().gateStatus().then((status) => {
|
|
82
|
+
splitGenericGateStatus = {
|
|
83
|
+
activeLabels: status.activeLabels,
|
|
84
|
+
abandonedReceipts: status.abandonedReceipts.map((receipt) => ({
|
|
85
|
+
...receipt,
|
|
86
|
+
path: path.join(gatesDir(), `${receipt.label}.pid`)
|
|
87
|
+
}))
|
|
88
|
+
};
|
|
89
|
+
return splitGenericGateStatus;
|
|
90
|
+
}).catch(() => {
|
|
91
|
+
splitGenericGateStatus = null;
|
|
92
|
+
return genericGateStatus();
|
|
93
|
+
}).finally(() => {
|
|
94
|
+
splitGenericGateStatusRefresh = null;
|
|
95
|
+
});
|
|
96
|
+
return splitGenericGateStatusRefresh;
|
|
97
|
+
}
|
|
98
|
+
function listAbandonedGateReceipts() {
|
|
99
|
+
return genericGateStatus().abandonedReceipts;
|
|
100
|
+
}
|
|
101
|
+
function listGateExitSentinels() {
|
|
102
|
+
const sentinels = [];
|
|
103
|
+
let entries;
|
|
104
|
+
try {
|
|
105
|
+
entries = readdirSync(gatesDir());
|
|
106
|
+
} catch {
|
|
107
|
+
return sentinels;
|
|
108
|
+
}
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
if (!entry.endsWith(".exit")) continue;
|
|
111
|
+
const file = path.join(gatesDir(), entry);
|
|
112
|
+
try {
|
|
113
|
+
const mtimeMs = statSync(file).mtimeMs;
|
|
114
|
+
let code = null;
|
|
115
|
+
try {
|
|
116
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
117
|
+
const value = parsed?.code;
|
|
118
|
+
if (typeof value === "number") code = value;
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
sentinels.push({ path: file, label: entry.slice(0, -".exit".length), code, mtimeMs });
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
sentinels.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
126
|
+
return sentinels;
|
|
127
|
+
}
|
|
128
|
+
function isHeavyGateActive() {
|
|
129
|
+
if (genericGateStatus().activeLabels.length > 0) return true;
|
|
130
|
+
for (const file of singletonPidFiles()) {
|
|
131
|
+
try {
|
|
132
|
+
const pid = parsePid(readFileSync(file, "utf8"));
|
|
133
|
+
if (pid !== null && pidAlive(pid)) return true;
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
gatesDir,
|
|
142
|
+
getLocalGenericGateStatus,
|
|
143
|
+
refreshGenericGateStatus,
|
|
144
|
+
listAbandonedGateReceipts,
|
|
145
|
+
listGateExitSentinels,
|
|
146
|
+
isHeavyGateActive
|
|
147
|
+
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadPtySpawn
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-W5INK3NE.js";
|
|
4
4
|
import {
|
|
5
5
|
terminateProcessGroup
|
|
6
6
|
} from "./chunk-W4LZ7R6Z.js";
|
|
7
|
+
import {
|
|
8
|
+
getLocalGenericGateStatus
|
|
9
|
+
} from "./chunk-372R6E4C.js";
|
|
7
10
|
import {
|
|
8
11
|
FrameReader,
|
|
9
12
|
timingSafeTokenEqual,
|
|
@@ -112,21 +115,25 @@ async function runRefreshSkills(socket, opts) {
|
|
|
112
115
|
}
|
|
113
116
|
socket.end();
|
|
114
117
|
}
|
|
118
|
+
function immediateResponse(req, opts) {
|
|
119
|
+
if (req.op === "ping") return { t: "pong", version: opts.version };
|
|
120
|
+
if (req.op === "gitStatus") return opts.getGitStatus?.() ?? { t: "gitStatus", state: "ready" };
|
|
121
|
+
if (req.op === "gateStatus") return { t: "gateStatus", ...getLocalGenericGateStatus() };
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
115
124
|
function dispatch(socket, req, sink, opts, liveOps) {
|
|
116
125
|
if (!req || typeof req !== "object" || !timingSafeTokenEqual(req.token ?? "", opts.token)) {
|
|
117
126
|
writeFrame(socket, { t: "error", message: "unauthorized", code: "unauthorized" });
|
|
118
127
|
socket.destroy();
|
|
119
128
|
return;
|
|
120
129
|
}
|
|
130
|
+
const immediate = immediateResponse(req, opts);
|
|
131
|
+
if (immediate) {
|
|
132
|
+
writeFrame(socket, immediate);
|
|
133
|
+
socket.end();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
121
136
|
switch (req.op) {
|
|
122
|
-
case "ping":
|
|
123
|
-
writeFrame(socket, { t: "pong", version: opts.version });
|
|
124
|
-
socket.end();
|
|
125
|
-
return;
|
|
126
|
-
case "gitStatus":
|
|
127
|
-
writeFrame(socket, opts.getGitStatus?.() ?? { t: "gitStatus", state: "ready" });
|
|
128
|
-
socket.end();
|
|
129
|
-
return;
|
|
130
137
|
case "refreshSkills":
|
|
131
138
|
void runRefreshSkills(socket, opts);
|
|
132
139
|
return;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
readWorkspaceBytes,
|
|
6
6
|
statWorkspacePath,
|
|
7
7
|
workspacePathExists
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WS7QRB37.js";
|
|
9
9
|
import {
|
|
10
10
|
reportBootMilestone
|
|
11
11
|
} from "./chunk-GL2DIQEQ.js";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-W4LZ7R6Z.js";
|
|
17
17
|
import {
|
|
18
18
|
getWorkbenchClient
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-SQM2BQ7H.js";
|
|
20
20
|
import {
|
|
21
21
|
workbenchEnabled
|
|
22
22
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -37,7 +37,31 @@ var RemoteProcessHandle = class extends EventEmitter {
|
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
// src/workbench/gate-status.ts
|
|
41
|
+
function readGateStatus(open, timeoutMs) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
let status = null;
|
|
44
|
+
let timer = null;
|
|
45
|
+
const socket = open(
|
|
46
|
+
(frame) => {
|
|
47
|
+
if (frame.t === "gateStatus") status = frame;
|
|
48
|
+
},
|
|
49
|
+
(error) => {
|
|
50
|
+
if (timer) clearTimeout(timer);
|
|
51
|
+
if (status) resolve(status);
|
|
52
|
+
else reject(error ?? new WorkbenchError("connection closed before gateStatus"));
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
timer = setTimeout(
|
|
56
|
+
() => socket.destroy(new WorkbenchError("gateStatus timed out", "workbench_timeout")),
|
|
57
|
+
timeoutMs
|
|
58
|
+
);
|
|
59
|
+
timer.unref();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
40
63
|
// src/workbench/client.ts
|
|
64
|
+
var DEFAULT_GATE_STATUS_TIMEOUT_MS = 5e3;
|
|
41
65
|
var WORKBENCH_COMMAND_ENV_DENYLIST = /* @__PURE__ */ new Set([
|
|
42
66
|
"POD_BOOTSTRAP_TOKEN",
|
|
43
67
|
"CONVEYOR_BOOTSTRAP_TOKEN",
|
|
@@ -107,10 +131,12 @@ var WorkbenchClient = class {
|
|
|
107
131
|
port;
|
|
108
132
|
host;
|
|
109
133
|
token;
|
|
134
|
+
gateStatusTimeoutMs;
|
|
110
135
|
constructor(options = {}) {
|
|
111
136
|
this.port = options.port ?? workbenchPort() ?? DEFAULT_WORKBENCH_PORT;
|
|
112
137
|
this.host = options.host ?? "127.0.0.1";
|
|
113
138
|
this.token = options.token ?? workbenchToken();
|
|
139
|
+
this.gateStatusTimeoutMs = options.gateStatusTimeoutMs ?? DEFAULT_GATE_STATUS_TIMEOUT_MS;
|
|
114
140
|
}
|
|
115
141
|
/** Open a connection, send the request, route response frames. */
|
|
116
142
|
open(request, onFrame, onClose) {
|
|
@@ -168,6 +194,12 @@ var WorkbenchClient = class {
|
|
|
168
194
|
);
|
|
169
195
|
});
|
|
170
196
|
}
|
|
197
|
+
gateStatus() {
|
|
198
|
+
return readGateStatus(
|
|
199
|
+
(onFrame, onClose) => this.open({ op: "gateStatus" }, onFrame, onClose),
|
|
200
|
+
this.gateStatusTimeoutMs
|
|
201
|
+
);
|
|
202
|
+
}
|
|
171
203
|
/** Ask the workbench — which owns the repo filesystem in split mode — to
|
|
172
204
|
* re-run the skill floor after the agent's authoritative task-branch
|
|
173
205
|
* checkout. A daemon without a provider answers an empty result. */
|
|
@@ -200,7 +200,7 @@ async function loadPtySpawn() {
|
|
|
200
200
|
async function resolvePtySpawn() {
|
|
201
201
|
const { workbenchEnabled } = await import("./mode-ZJSOSLGU.js");
|
|
202
202
|
if (!workbenchEnabled()) return loadPtySpawn();
|
|
203
|
-
const { getWorkbenchClient } = await import("./client-
|
|
203
|
+
const { getWorkbenchClient } = await import("./client-VIFYMBT2.js");
|
|
204
204
|
return (file, args, options) => getWorkbenchClient().spawnPty(file, args, options);
|
|
205
205
|
}
|
|
206
206
|
function sessionTempBase() {
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
spawnOptionsFingerprint,
|
|
30
30
|
transcriptSize,
|
|
31
31
|
turnOptionsFrom
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-W5INK3NE.js";
|
|
33
33
|
import {
|
|
34
34
|
AgentConnection,
|
|
35
35
|
CodespacePortVisibility,
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
statWorkspacePath,
|
|
60
60
|
updateRemoteToken,
|
|
61
61
|
verifyGitCredential
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-WS7QRB37.js";
|
|
63
63
|
import {
|
|
64
64
|
registerBootMilestoneSocketFallback,
|
|
65
65
|
reportBootMilestone
|
|
@@ -72,15 +72,17 @@ import {
|
|
|
72
72
|
} from "./chunk-W4LZ7R6Z.js";
|
|
73
73
|
import {
|
|
74
74
|
isHeavyGateActive,
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
listAbandonedGateReceipts,
|
|
76
|
+
listGateExitSentinels,
|
|
77
|
+
refreshGenericGateStatus
|
|
78
|
+
} from "./chunk-372R6E4C.js";
|
|
77
79
|
import {
|
|
78
80
|
LoopLagMonitor,
|
|
79
81
|
loopStatusForRunnerStatus
|
|
80
82
|
} from "./chunk-IA45XHOA.js";
|
|
81
83
|
import {
|
|
82
84
|
getWorkbenchClient
|
|
83
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-SQM2BQ7H.js";
|
|
84
86
|
import {
|
|
85
87
|
workbenchEnabled
|
|
86
88
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -2181,6 +2183,10 @@ var CATALOG = {
|
|
|
2181
2183
|
// rejects (media_type_header_exception), breaking search/audit indexing
|
|
2182
2184
|
// in pods. 512m heap matches the project compose sizing.
|
|
2183
2185
|
image: "docker.elastic.co/elasticsearch/elasticsearch:9.4.0",
|
|
2186
|
+
mirror: {
|
|
2187
|
+
src: "docker.elastic.co/elasticsearch/elasticsearch:9.4.0",
|
|
2188
|
+
dest: "mirror-elasticsearch:9.4.0"
|
|
2189
|
+
},
|
|
2184
2190
|
ports: [9200],
|
|
2185
2191
|
// Baked service images are `docker commit`s of a recently-running ES, so
|
|
2186
2192
|
// they carry a stale data-dir node.lock; ES 9 hard-fails on it at boot
|
|
@@ -8517,7 +8523,7 @@ Workflow:`,
|
|
|
8517
8523
|
`- If you toggled into active mode temporarily, mention when you're done so the team can switch you back to planning mode.`
|
|
8518
8524
|
].filter(Boolean);
|
|
8519
8525
|
}
|
|
8520
|
-
function buildTaskAgentPreamble(context, workspaceDir) {
|
|
8526
|
+
function buildTaskAgentPreamble(context, workspaceDir, runtimeTui) {
|
|
8521
8527
|
const managedStack = repoHasScript(workspaceDir, "web:rebuild");
|
|
8522
8528
|
const stackLines = managedStack ? [
|
|
8523
8529
|
`- The web app is served on port 3050, the API on port 7090.`,
|
|
@@ -8543,8 +8549,8 @@ Working rules:`,
|
|
|
8543
8549
|
`- Read a file before your first Write/Edit to it, and batch multiple changes to the same file into a single call instead of many sequential edits.`,
|
|
8544
8550
|
`- To learn what calls a symbol or where it lives, query the prebuilt code graph before grepping: \`graphify query "<SymbolName>"\` from the repo root. Query a SYMBOL, never a sentence \u2014 \`graphify query "resolveTaskBaseBranch"\` returns the definition plus every call site, while "how does a task get its base branch" seeds unrelated start nodes and returns test files and loggers. Don't know the symbol yet? Grep for the name first, then query it: grep finds names, the graph finds relationships. \`No matching nodes found\` means "not in this graph" (it is prebuilt, so very recent code is absent), NOT "not in the codebase" \u2014 fall back to \`git grep\`. Skip all of this if \`graphify-out/graph.json\` is not present.`,
|
|
8545
8551
|
`- When a build/lint/test run fails, capture its output to a file once and grep the file \u2014 never re-run the suite just to re-filter the same output.`,
|
|
8546
|
-
`- Waiting on long-running commands: if a gate finishes in under ~2 minutes, run it in the foreground with a timeout. For a longer one, launch it with run_in_background and STOP; a completion notification arrives when it finishes, and the workspace stays awake for as long as background work is outstanding, so a backgrounded gate will not be killed by an idle sleep. For the final pre-PR gate a bounded foreground run (\`timeout 590 <gate>\` with Bash \`timeout: 600000\`) is still preferred as defense in depth \u2014 it survives a pod resume, which a background job does not. Never busy-wait with sleep/pgrep/tail loops, and never re-run the suite to escape a wait that looks stalled.`,
|
|
8547
|
-
`- Ending your turn with NO tool call is the correct way to wait, and it is safe: the pod stays alive and the next notification re-invokes you. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn. Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 an external CI run, a deploy, a remote queue \u2014 never as insurance against a background job's own notification, which does fire.`,
|
|
8552
|
+
runtimeTui === "codex" ? `- Waiting on long-running commands: retain and resume the command session until it returns an exit result. Finish each required gate before ending the task. Never pretend a completion notification will resume you, and never start another gate while the current one is running.` : `- Waiting on long-running commands: if a gate finishes in under ~2 minutes, run it in the foreground with a timeout. For a longer one, launch it with run_in_background and STOP; a completion notification arrives when it finishes, and the workspace stays awake for as long as background work is outstanding, so a backgrounded gate will not be killed by an idle sleep. For the final pre-PR gate a bounded foreground run (\`timeout 590 <gate>\` with Bash \`timeout: 600000\`) is still preferred as defense in depth \u2014 it survives a pod resume, which a background job does not. Never busy-wait with sleep/pgrep/tail loops, and never re-run the suite to escape a wait that looks stalled.`,
|
|
8553
|
+
runtimeTui === "codex" ? `- Do not end your turn to wait on a required gate. Resume its command session, inspect its exit result, and then continue the checklist.` : `- Ending your turn with NO tool call is the correct way to wait, and it is safe: the pod stays alive and the next notification re-invokes you. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn. Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 an external CI run, a deploy, a remote queue \u2014 never as insurance against a background job's own notification, which does fire.`,
|
|
8548
8554
|
`
|
|
8549
8555
|
Git:`,
|
|
8550
8556
|
`- Stay on \`${context.githubBranch}\` for the whole task: do not check out another branch and do not create one. It was cut from \`${context.baseBranch}\`, and PRs target that automatically.`,
|
|
@@ -8561,7 +8567,7 @@ function buildSystemPrompt(mode, context, config, setupLog, agentMode) {
|
|
|
8561
8567
|
if (isPackRunner) {
|
|
8562
8568
|
return buildPackPrompt(mode, context, config, setupLog);
|
|
8563
8569
|
}
|
|
8564
|
-
const parts = isPmActive ? buildActivePreamble(context, config.workspaceDir) : isPm ? buildPmPreamble(context) : buildTaskAgentPreamble(context, config.workspaceDir);
|
|
8570
|
+
const parts = isPmActive ? buildActivePreamble(context, config.workspaceDir) : isPm ? buildPmPreamble(context) : buildTaskAgentPreamble(context, config.workspaceDir, config.runtimeTui);
|
|
8565
8571
|
if (setupLog.length > 0) {
|
|
8566
8572
|
parts.push(
|
|
8567
8573
|
`
|
|
@@ -13656,10 +13662,14 @@ var ToolLoopTracker = class {
|
|
|
13656
13662
|
};
|
|
13657
13663
|
function buildRepeatLoopMessage(repeatCount, heavyGateActive) {
|
|
13658
13664
|
const head = `Conveyor blocked this call: you have run the exact same command ${repeatCount} times in a row with nothing different in between. Repeating it again will return the same result.`;
|
|
13659
|
-
const
|
|
13665
|
+
const codex = process.env.CONVEYOR_TUI === "codex";
|
|
13666
|
+
const advice = codex ? heavyGateActive ? `A build gate is running. Resume its command session for the exit result, then continue the required gates.` : `If a command is still running, resume its session for the result. Otherwise change your approach or post to chat.` : heavyGateActive ? `A build gate (test/typecheck/build) is running on this pod right now. Do NOT poll its log. End your turn \u2014 the completion notification re-invokes you when the gate finishes.` : `If you are waiting on a background job, end your turn instead of polling; the completion notification re-invokes you. If you are stuck, change your approach: read a different file, run a different command, or post to chat and ask the team.`;
|
|
13660
13667
|
return `${head} ${advice} Call a different tool now.`;
|
|
13661
13668
|
}
|
|
13662
13669
|
function buildNoOpKeepAliveMessage(noOpCount) {
|
|
13670
|
+
if (process.env.CONVEYOR_TUI === "codex") {
|
|
13671
|
+
return `Conveyor blocked this call: it is a no-op keep-alive (${noOpCount} this session). If a required command is running, resume its session for the exit result. Otherwise do real work or end the turn.`;
|
|
13672
|
+
}
|
|
13663
13673
|
return `Conveyor blocked this call: it is a no-op keep-alive (${noOpCount} this session). You do not need to emit tool calls to stay alive. Ending your turn with NO tool call is safe and expected while waiting: the pod stays up and the completion notification re-invokes you. If nothing is running, do real work or end the turn. For a long wait with no notification source, use Monitor or ScheduleWakeup instead of filler commands.`;
|
|
13664
13674
|
}
|
|
13665
13675
|
function buildRepeatLoopChatMessage(repeatCount, forceStopped) {
|
|
@@ -14110,7 +14120,7 @@ function buildQueryOptions(host, context) {
|
|
|
14110
14120
|
const systemPromptText = buildSystemPrompt(
|
|
14111
14121
|
host.config.mode,
|
|
14112
14122
|
context,
|
|
14113
|
-
{ ...host.config, isAuto: host.isAuto },
|
|
14123
|
+
{ ...host.config, isAuto: host.isAuto, runtimeTui: process.env.CONVEYOR_TUI },
|
|
14114
14124
|
host.setupLog,
|
|
14115
14125
|
mode
|
|
14116
14126
|
);
|
|
@@ -15684,7 +15694,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15684
15694
|
onHeartbeat: () => {
|
|
15685
15695
|
const loopStatus = this.refreshLoopStatus();
|
|
15686
15696
|
this.connection.sendHeartbeat(this.loopLag.takeMaxLagMs(), loopStatus);
|
|
15687
|
-
this.
|
|
15697
|
+
void this.refreshGateStatusAndMaybeWake();
|
|
15688
15698
|
},
|
|
15689
15699
|
onIdleTimeout: () => {
|
|
15690
15700
|
if (this.deferShutdownForLiveChild("idle")) return;
|
|
@@ -15825,6 +15835,10 @@ var SessionRunner = class _SessionRunner {
|
|
|
15825
15835
|
/** One-shot latch: at most one watchdog wake per idle episode. Reset when a
|
|
15826
15836
|
* turn runs. */
|
|
15827
15837
|
backgroundWakeFiredThisIdle = false;
|
|
15838
|
+
/** Start of the last active episode. A gate can be launched shortly before
|
|
15839
|
+
* the agent declares completion, so idle-start freshness would reject its
|
|
15840
|
+
* terminal receipt as stale. */
|
|
15841
|
+
activeEpisodeStartedAt = Date.now();
|
|
15828
15842
|
/** When the current idle episode began — an exit sentinel older than this
|
|
15829
15843
|
* appeared while a turn could still have consumed it, so it never wakes. */
|
|
15830
15844
|
idleEpisodeStartedAt = 0;
|
|
@@ -15839,22 +15853,30 @@ var SessionRunner = class _SessionRunner {
|
|
|
15839
15853
|
* evidence of orphaned background work — a tracked launch with no live gate
|
|
15840
15854
|
* process continuously past `ORPHAN_WAKE_GRACE_MS`, or an unconsumed
|
|
15841
15855
|
* `gates/<label>.exit` sentinel that appeared this idle episode — never on
|
|
15842
|
-
* "the agent seems stuck". A `completed` agent
|
|
15843
|
-
*
|
|
15856
|
+
* "the agent seems stuck". A `completed` agent remains parked unless a
|
|
15857
|
+
* current-episode generic receipt proves a required gate died without an
|
|
15858
|
+
* exit record, and a live pid always suppresses the wake.
|
|
15844
15859
|
*/
|
|
15845
15860
|
maybeWakeForOrphanedBackgroundWork() {
|
|
15846
15861
|
try {
|
|
15847
|
-
if (this._state !== "idle" || this.stopped
|
|
15862
|
+
if (this._state !== "idle" || this.stopped) {
|
|
15848
15863
|
this.orphanGraceStartedAt = null;
|
|
15849
15864
|
return;
|
|
15850
15865
|
}
|
|
15851
15866
|
if (this.backgroundWakeFiredThisIdle) return;
|
|
15852
15867
|
if (!this.inputResolver) return;
|
|
15868
|
+
const abandoned = listAbandonedGateReceipts().find(
|
|
15869
|
+
(receipt) => receipt.mtimeMs >= this.activeEpisodeStartedAt
|
|
15870
|
+
);
|
|
15871
|
+
if (this.completedThisTurn && !abandoned) {
|
|
15872
|
+
this.orphanGraceStartedAt = null;
|
|
15873
|
+
return;
|
|
15874
|
+
}
|
|
15853
15875
|
if (isHeavyGateActive()) {
|
|
15854
15876
|
this.orphanGraceStartedAt = null;
|
|
15855
15877
|
return;
|
|
15856
15878
|
}
|
|
15857
|
-
const evidence = this.findOrphanEvidence(Date.now());
|
|
15879
|
+
const evidence = abandoned ? this.abandonedReceiptEvidence(abandoned) : this.findOrphanEvidence(Date.now());
|
|
15858
15880
|
if (!evidence) return;
|
|
15859
15881
|
process.stderr.write(
|
|
15860
15882
|
`[conveyor-agent] Background-work watchdog: waking agent \u2014 ${evidence.description}
|
|
@@ -15863,11 +15885,16 @@ var SessionRunner = class _SessionRunner {
|
|
|
15863
15885
|
this.backgroundWakeFiredThisIdle = true;
|
|
15864
15886
|
this.orphanGraceStartedAt = null;
|
|
15865
15887
|
if (evidence.sentinel) rmSync(evidence.sentinel.path, { force: true });
|
|
15888
|
+
if (evidence.abandonedReceipt) rmSync(evidence.abandonedReceipt.path, { force: true });
|
|
15866
15889
|
this.backgroundWork.clear();
|
|
15890
|
+
if (evidence.abandonedReceipt) {
|
|
15891
|
+
this.completedThisTurn = false;
|
|
15892
|
+
this.dormantDeadline = null;
|
|
15893
|
+
}
|
|
15867
15894
|
const resolver = this.inputResolver;
|
|
15868
15895
|
this.inputResolver = null;
|
|
15869
15896
|
resolver({
|
|
15870
|
-
content: `A
|
|
15897
|
+
content: `A required gate you started appears to have finished or died without delivering its completion notification (${evidence.description}). Read the job's output \u2014 its log file, or the corresponding file under $CONVEYOR_SHARED_DIR/gates on split pods \u2014 to determine the real result, then continue your plan. Do not assume the job succeeded; verify its output first. If it died, rerun it in the foreground without shell '&' and retain the command session until its exit result.`,
|
|
15871
15898
|
userId: "system",
|
|
15872
15899
|
source: "background_work_check"
|
|
15873
15900
|
});
|
|
@@ -15898,6 +15925,25 @@ var SessionRunner = class _SessionRunner {
|
|
|
15898
15925
|
description: `${this.backgroundWork.pendingCount()} tracked background launch(es) with no live gate process for ${Math.round(ORPHAN_WAKE_GRACE_MS / 6e4)}+ minutes`
|
|
15899
15926
|
};
|
|
15900
15927
|
}
|
|
15928
|
+
abandonedReceiptEvidence(receipt) {
|
|
15929
|
+
return {
|
|
15930
|
+
abandonedReceipt: receipt,
|
|
15931
|
+
description: `gate '${receipt.label}' stopped before writing an exit receipt (pid ${receipt.pid})`
|
|
15932
|
+
};
|
|
15933
|
+
}
|
|
15934
|
+
/** Refresh PID liveness in the owning workbench namespace before checking
|
|
15935
|
+
* terminal receipts. The local path is immediate; socket failures preserve
|
|
15936
|
+
* the conservative cached state and never interrupt the heartbeat. */
|
|
15937
|
+
async refreshGateStatusAndMaybeWake() {
|
|
15938
|
+
try {
|
|
15939
|
+
await refreshGenericGateStatus();
|
|
15940
|
+
this.refreshLoopStatus();
|
|
15941
|
+
this.maybeWakeForOrphanedBackgroundWork();
|
|
15942
|
+
} catch (err) {
|
|
15943
|
+
process.stderr.write(`[conveyor-agent] Gate status refresh failed: ${err}
|
|
15944
|
+
`);
|
|
15945
|
+
}
|
|
15946
|
+
}
|
|
15901
15947
|
get sessionId() {
|
|
15902
15948
|
return this.connection.sessionId;
|
|
15903
15949
|
}
|
|
@@ -16709,9 +16755,12 @@ var SessionRunner = class _SessionRunner {
|
|
|
16709
16755
|
};
|
|
16710
16756
|
const bridge = new QueryBridge(this.connection, this.mode, runnerConfig, {
|
|
16711
16757
|
onStatusChange: (status) => {
|
|
16712
|
-
if (status === "running"
|
|
16713
|
-
this.
|
|
16714
|
-
this.
|
|
16758
|
+
if (status === "running") {
|
|
16759
|
+
this.beginRunningEpisode();
|
|
16760
|
+
if (this._state === "waiting_for_input") {
|
|
16761
|
+
this._state = "running";
|
|
16762
|
+
this.lifecycle.cancelIdleTimer();
|
|
16763
|
+
}
|
|
16715
16764
|
}
|
|
16716
16765
|
return this.callbacks.onStatusChange(status);
|
|
16717
16766
|
},
|
|
@@ -16959,8 +17008,7 @@ ${outcome.failures.join("\n")}
|
|
|
16959
17008
|
}
|
|
16960
17009
|
async setState(status) {
|
|
16961
17010
|
if (status === "running") {
|
|
16962
|
-
this.
|
|
16963
|
-
this.backgroundWakeFiredThisIdle = false;
|
|
17011
|
+
this.beginRunningEpisode();
|
|
16964
17012
|
}
|
|
16965
17013
|
if (status === "idle" && this._state !== "idle") this.idleEpisodeStartedAt = Date.now();
|
|
16966
17014
|
this._state = status;
|
|
@@ -16968,6 +17016,13 @@ ${outcome.failures.join("\n")}
|
|
|
16968
17016
|
await this.connection.emitStatus(status);
|
|
16969
17017
|
await this.callbacks.onStatusChange(status);
|
|
16970
17018
|
}
|
|
17019
|
+
/** Mark a fresh turn without emitting a duplicate status. QueryExecutor's
|
|
17020
|
+
* prefilled PTY callback already emitted `running` before it reaches here. */
|
|
17021
|
+
beginRunningEpisode() {
|
|
17022
|
+
this.activeEpisodeStartedAt = Date.now();
|
|
17023
|
+
this.backgroundDeferStartedAt = null;
|
|
17024
|
+
this.backgroundWakeFiredThisIdle = false;
|
|
17025
|
+
}
|
|
16971
17026
|
async shutdown(finalState) {
|
|
16972
17027
|
process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
|
|
16973
17028
|
`);
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
WorkspaceCommandSupervisor,
|
|
8
8
|
startWorkspaceCommandsAfterConnect,
|
|
9
9
|
stopWorkspaceCommands
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GZRZGBIK.js";
|
|
11
11
|
import {
|
|
12
12
|
DEFAULT_SONNET_MODEL,
|
|
13
13
|
PtyHarness,
|
|
@@ -27,13 +27,13 @@ import {
|
|
|
27
27
|
resolveTuiKindFromEnv,
|
|
28
28
|
runUsageProbe,
|
|
29
29
|
sampleKeyUsage
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ZFHN7GO7.js";
|
|
31
31
|
import "./chunk-SR66HQKB.js";
|
|
32
32
|
import {
|
|
33
33
|
inheritedEnv,
|
|
34
34
|
resolvePtySpawn,
|
|
35
35
|
sessionTempBase
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-W5INK3NE.js";
|
|
37
37
|
import {
|
|
38
38
|
AgentConnection,
|
|
39
39
|
CodespacePortVisibility,
|
|
@@ -44,12 +44,12 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-WS7QRB37.js";
|
|
48
48
|
import "./chunk-GL2DIQEQ.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
|
-
import "./chunk-
|
|
50
|
+
import "./chunk-372R6E4C.js";
|
|
51
51
|
import "./chunk-IA45XHOA.js";
|
|
52
|
-
import "./chunk-
|
|
52
|
+
import "./chunk-SQM2BQ7H.js";
|
|
53
53
|
import "./chunk-KMB3BU4S.js";
|
|
54
54
|
import "./chunk-6Q6LQBWO.js";
|
|
55
55
|
|
|
@@ -1205,7 +1205,7 @@ function wireSpawnChildren(mode, connection, supervisors, logger7) {
|
|
|
1205
1205
|
|
|
1206
1206
|
// src/cli.ts
|
|
1207
1207
|
if (process.argv[2] === "boot") {
|
|
1208
|
-
const { runBoot } = await import("./boot-
|
|
1208
|
+
const { runBoot } = await import("./boot-4ZNOCKFZ.js");
|
|
1209
1209
|
process.exit(await runBoot(process.argv.slice(3)));
|
|
1210
1210
|
}
|
|
1211
1211
|
if (isLegacyEntrypointLaunch(process.env)) {
|
|
@@ -1307,7 +1307,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
1307
1307
|
process.exit(1);
|
|
1308
1308
|
});
|
|
1309
1309
|
if (process.env.CONVEYOR_MODE === "workbench") {
|
|
1310
|
-
const { startWorkbenchServer } = await import("./server-
|
|
1310
|
+
const { startWorkbenchServer } = await import("./server-L5CQ5EJB.js");
|
|
1311
1311
|
const { oomWatchdogOptionsFromEnv } = await import("./oom-watchdog-PAC5OJJG.js");
|
|
1312
1312
|
const { DEFAULT_WORKBENCH_PORT } = await import("./protocol-QBCYO4GI.js");
|
|
1313
1313
|
const port = Number(process.env.CONVEYOR_WORKBENCH_PORT) || DEFAULT_WORKBENCH_PORT;
|
|
@@ -1458,7 +1458,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
|
|
|
1458
1458
|
process.exit(1);
|
|
1459
1459
|
}
|
|
1460
1460
|
if (CONVEYOR_MODE === "serving") {
|
|
1461
|
-
const { runServingSession } = await import("./serve-boot-
|
|
1461
|
+
const { runServingSession } = await import("./serve-boot-RU5BYIK4.js");
|
|
1462
1462
|
exitContext.runnerMode = "serving";
|
|
1463
1463
|
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
|
|
1464
1464
|
const outcome = await runServingSession({
|
package/dist/gate-cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
gatesDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-372R6E4C.js";
|
|
5
|
+
import "./chunk-SQM2BQ7H.js";
|
|
6
|
+
import "./chunk-KMB3BU4S.js";
|
|
7
|
+
import "./chunk-6Q6LQBWO.js";
|
|
5
8
|
|
|
6
9
|
// src/runner/gate-wrapper.ts
|
|
7
10
|
import { mkdirSync, rmSync, writeFileSync } from "fs";
|
package/dist/index.d.ts
CHANGED
|
@@ -810,6 +810,10 @@ declare class SessionRunner {
|
|
|
810
810
|
/** One-shot latch: at most one watchdog wake per idle episode. Reset when a
|
|
811
811
|
* turn runs. */
|
|
812
812
|
private backgroundWakeFiredThisIdle;
|
|
813
|
+
/** Start of the last active episode. A gate can be launched shortly before
|
|
814
|
+
* the agent declares completion, so idle-start freshness would reject its
|
|
815
|
+
* terminal receipt as stale. */
|
|
816
|
+
private activeEpisodeStartedAt;
|
|
813
817
|
/** When the current idle episode began — an exit sentinel older than this
|
|
814
818
|
* appeared while a turn could still have consumed it, so it never wakes. */
|
|
815
819
|
private idleEpisodeStartedAt;
|
|
@@ -824,13 +828,19 @@ declare class SessionRunner {
|
|
|
824
828
|
* evidence of orphaned background work — a tracked launch with no live gate
|
|
825
829
|
* process continuously past `ORPHAN_WAKE_GRACE_MS`, or an unconsumed
|
|
826
830
|
* `gates/<label>.exit` sentinel that appeared this idle episode — never on
|
|
827
|
-
* "the agent seems stuck". A `completed` agent
|
|
828
|
-
*
|
|
831
|
+
* "the agent seems stuck". A `completed` agent remains parked unless a
|
|
832
|
+
* current-episode generic receipt proves a required gate died without an
|
|
833
|
+
* exit record, and a live pid always suppresses the wake.
|
|
829
834
|
*/
|
|
830
835
|
private maybeWakeForOrphanedBackgroundWork;
|
|
831
836
|
/** Orphan evidence that has held past the grace, or null. Maintains the
|
|
832
837
|
* grace clock for the tracked-work condition as a side effect. */
|
|
833
838
|
private findOrphanEvidence;
|
|
839
|
+
private abandonedReceiptEvidence;
|
|
840
|
+
/** Refresh PID liveness in the owning workbench namespace before checking
|
|
841
|
+
* terminal receipts. The local path is immediate; socket failures preserve
|
|
842
|
+
* the conservative cached state and never interrupt the heartbeat. */
|
|
843
|
+
private refreshGateStatusAndMaybeWake;
|
|
834
844
|
get sessionId(): string;
|
|
835
845
|
get isStopped(): boolean;
|
|
836
846
|
/** Wire the boot supervisor handle post-construction — cli.ts constructs it
|
|
@@ -1097,6 +1107,9 @@ declare class SessionRunner {
|
|
|
1097
1107
|
* the tool starts work that outlives the turn. */
|
|
1098
1108
|
private noteToolUseForBackgroundWork;
|
|
1099
1109
|
private setState;
|
|
1110
|
+
/** Mark a fresh turn without emitting a duplicate status. QueryExecutor's
|
|
1111
|
+
* prefilled PTY callback already emitted `running` before it reaches here. */
|
|
1112
|
+
private beginRunningEpisode;
|
|
1100
1113
|
private shutdown;
|
|
1101
1114
|
private _finalState;
|
|
1102
1115
|
/** The final status after run() completes. Use to determine exit code. */
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZFHN7GO7.js";
|
|
5
5
|
import "./chunk-SR66HQKB.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-W5INK3NE.js";
|
|
7
7
|
import {
|
|
8
8
|
AgentConnection,
|
|
9
9
|
GIT_TIMEOUT_MS,
|
|
@@ -17,18 +17,18 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-WS7QRB37.js";
|
|
21
21
|
import "./chunk-GL2DIQEQ.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
24
24
|
runSetupCommand,
|
|
25
25
|
runStartCommand
|
|
26
26
|
} from "./chunk-W4LZ7R6Z.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-372R6E4C.js";
|
|
28
28
|
import "./chunk-IA45XHOA.js";
|
|
29
29
|
import {
|
|
30
30
|
getWorkbenchClient
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-SQM2BQ7H.js";
|
|
32
32
|
import {
|
|
33
33
|
workbenchEnabled
|
|
34
34
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GZRZGBIK.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentConnection,
|
|
6
6
|
CodespacePortVisibility,
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
createServiceLogger,
|
|
12
12
|
ensureOnTaskBranch,
|
|
13
13
|
loadConveyorConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WS7QRB37.js";
|
|
15
15
|
import "./chunk-GL2DIQEQ.js";
|
|
16
16
|
import "./chunk-W4LZ7R6Z.js";
|
|
17
17
|
import "./chunk-IA45XHOA.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-SQM2BQ7H.js";
|
|
19
19
|
import "./chunk-KMB3BU4S.js";
|
|
20
20
|
import "./chunk-6Q6LQBWO.js";
|
|
21
21
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startWorkbenchServer
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-EIHZRSYI.js";
|
|
4
|
+
import "./chunk-W5INK3NE.js";
|
|
5
5
|
import "./chunk-W4LZ7R6Z.js";
|
|
6
|
+
import "./chunk-372R6E4C.js";
|
|
7
|
+
import "./chunk-SQM2BQ7H.js";
|
|
8
|
+
import "./chunk-KMB3BU4S.js";
|
|
6
9
|
import "./chunk-6Q6LQBWO.js";
|
|
7
10
|
import "./chunk-6W6UZ4SJ.js";
|
|
8
11
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.18",
|
|
4
4
|
"description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
package/dist/chunk-7P6QZHXZ.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// src/runner/heavy-gate.ts
|
|
2
|
-
import { readdirSync, readFileSync, statSync } from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
var GATE_KEYS = ["heavy", "test", "typecheck", "build"];
|
|
5
|
-
var GATES_DIR_NAME = "gates";
|
|
6
|
-
function runDir() {
|
|
7
|
-
return process.env.CONVEYOR_RUN_DIR ?? "/tmp/conveyor-run";
|
|
8
|
-
}
|
|
9
|
-
function gatesDir() {
|
|
10
|
-
return path.join(runDir(), GATES_DIR_NAME);
|
|
11
|
-
}
|
|
12
|
-
function pidAlive(pid) {
|
|
13
|
-
try {
|
|
14
|
-
process.kill(pid, 0);
|
|
15
|
-
return true;
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function parsePid(raw) {
|
|
21
|
-
const pid = Number.parseInt(raw.trim(), 10);
|
|
22
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
23
|
-
}
|
|
24
|
-
function gatePidFiles() {
|
|
25
|
-
const files = GATE_KEYS.map((key) => path.join(runDir(), `${key}.pid`));
|
|
26
|
-
try {
|
|
27
|
-
for (const entry of readdirSync(gatesDir())) {
|
|
28
|
-
if (entry.endsWith(".pid")) files.push(path.join(gatesDir(), entry));
|
|
29
|
-
}
|
|
30
|
-
} catch {
|
|
31
|
-
}
|
|
32
|
-
return files;
|
|
33
|
-
}
|
|
34
|
-
function listGateExitSentinels() {
|
|
35
|
-
const sentinels = [];
|
|
36
|
-
let entries;
|
|
37
|
-
try {
|
|
38
|
-
entries = readdirSync(gatesDir());
|
|
39
|
-
} catch {
|
|
40
|
-
return sentinels;
|
|
41
|
-
}
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
if (!entry.endsWith(".exit")) continue;
|
|
44
|
-
const file = path.join(gatesDir(), entry);
|
|
45
|
-
try {
|
|
46
|
-
const mtimeMs = statSync(file).mtimeMs;
|
|
47
|
-
let code = null;
|
|
48
|
-
try {
|
|
49
|
-
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
50
|
-
const value = parsed?.code;
|
|
51
|
-
if (typeof value === "number") code = value;
|
|
52
|
-
} catch {
|
|
53
|
-
}
|
|
54
|
-
sentinels.push({ path: file, label: entry.slice(0, -".exit".length), code, mtimeMs });
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
sentinels.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
59
|
-
return sentinels;
|
|
60
|
-
}
|
|
61
|
-
function isHeavyGateActive() {
|
|
62
|
-
for (const file of gatePidFiles()) {
|
|
63
|
-
try {
|
|
64
|
-
const pid = parsePid(readFileSync(file, "utf8"));
|
|
65
|
-
if (pid !== null && pidAlive(pid)) return true;
|
|
66
|
-
} catch {
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export {
|
|
73
|
-
gatesDir,
|
|
74
|
-
listGateExitSentinels,
|
|
75
|
-
isHeavyGateActive
|
|
76
|
-
};
|