@rallycry/conveyor-agent 11.0.28 → 11.0.30
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-DMTBXIA4.js → chunk-2BHXJ37W.js} +7 -2
- package/dist/{chunk-GHNYP67H.js → chunk-7KG7CDVU.js} +146 -24
- package/dist/{chunk-ND5INHM4.js → chunk-HYXAXQHN.js} +112 -12
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +28 -6
- package/dist/index.js +2 -2
- package/dist/{serve-boot-CYVW7Z27.js → serve-boot-F2G7ZXFF.js} +2 -2
- package/package.json +1 -1
- package/skills/conveyor-workflows/SKILL.md +11 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readWorkspaceBytes,
|
|
9
9
|
statWorkspacePath,
|
|
10
10
|
workspacePathExists
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7KG7CDVU.js";
|
|
12
12
|
import {
|
|
13
13
|
reportBootMilestone
|
|
14
14
|
} from "./chunk-Q4FQOJ7D.js";
|
|
@@ -583,6 +583,10 @@ var WorkspaceCommandSupervisor = class {
|
|
|
583
583
|
this.reportBootMilestoneFn("start_command_launched");
|
|
584
584
|
const forwardPorts = await this.loadForwardPortsFn(this.workspaceDir);
|
|
585
585
|
if (this.stopped) return;
|
|
586
|
+
this.reportSetupComplete(startCommandRunning, forwardPorts);
|
|
587
|
+
}
|
|
588
|
+
/** Send `setup_complete` with the devcontainer preview hints and links. */
|
|
589
|
+
reportSetupComplete(startCommandRunning, forwardPorts) {
|
|
586
590
|
const previewPorts = buildSessionPreviewPorts(forwardPorts);
|
|
587
591
|
this.connection.sendEvent({
|
|
588
592
|
type: "setup_complete",
|
|
@@ -590,7 +594,8 @@ var WorkspaceCommandSupervisor = class {
|
|
|
590
594
|
// Reported separately so the server can tell "nothing to run" apart from
|
|
591
595
|
// "the dev server failed to launch" — both report startCommandRunning:false.
|
|
592
596
|
startCommandConfigured: Boolean(this.config?.startCommand),
|
|
593
|
-
...previewPorts.length > 0 ? { previewPorts } : {}
|
|
597
|
+
...previewPorts.length > 0 ? { previewPorts } : {},
|
|
598
|
+
...forwardPorts.links.length > 0 ? { previewLinks: forwardPorts.links } : {}
|
|
594
599
|
});
|
|
595
600
|
}
|
|
596
601
|
/** Run the dependency sync, forwarding its output as setup output so it lands
|
|
@@ -240,10 +240,43 @@ function syncBundleGithubToken(token) {
|
|
|
240
240
|
syncGithubTokenFiles(token);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// src/connection/event-batch.ts
|
|
244
|
+
var MAX_EVENT_BATCH_BYTES = 256 * 1024;
|
|
245
|
+
var CLIPPED_FIELD_CHARS = 16 * 1024;
|
|
246
|
+
function sizeOf(event) {
|
|
247
|
+
return JSON.stringify(event).length;
|
|
248
|
+
}
|
|
249
|
+
function fitEvent(event) {
|
|
250
|
+
const size = sizeOf(event);
|
|
251
|
+
if (size <= MAX_EVENT_BATCH_BYTES) return { event, size };
|
|
252
|
+
const clipped = { ...event };
|
|
253
|
+
for (const [key, value] of Object.entries(clipped)) {
|
|
254
|
+
if (typeof value === "string" && value.length > CLIPPED_FIELD_CHARS) {
|
|
255
|
+
const dropped = value.length - CLIPPED_FIELD_CHARS;
|
|
256
|
+
clipped[key] = `${value.slice(0, CLIPPED_FIELD_CHARS)}
|
|
257
|
+
[conveyor-agent] truncated ${dropped} chars
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const clippedSize = sizeOf(clipped);
|
|
262
|
+
return clippedSize <= MAX_EVENT_BATCH_BYTES ? { event: clipped, size: clippedSize } : null;
|
|
263
|
+
}
|
|
264
|
+
function batchLength(entries) {
|
|
265
|
+
let total = 0;
|
|
266
|
+
for (let i = 0; i < entries.length; i++) {
|
|
267
|
+
const entry = entries[i];
|
|
268
|
+
const size = entry.size ?? sizeOf(entry.event);
|
|
269
|
+
total += size + 1;
|
|
270
|
+
if (total > MAX_EVENT_BATCH_BYTES && i > 0) return i;
|
|
271
|
+
}
|
|
272
|
+
return entries.length;
|
|
273
|
+
}
|
|
274
|
+
|
|
243
275
|
// src/connection/agent-connection.ts
|
|
244
276
|
var logger = createServiceLogger("agent-connection");
|
|
245
277
|
var EVENT_BATCH_MS = 500;
|
|
246
278
|
var MAX_EVENT_BUFFER = 5e3;
|
|
279
|
+
var PTY_INPUT_REPORT_INTERVAL_MS = 3e4;
|
|
247
280
|
var TOKEN_REFRESH_INTERVAL_MS = 45 * 60 * 1e3;
|
|
248
281
|
var AgentConnection = class _AgentConnection {
|
|
249
282
|
socket = null;
|
|
@@ -291,6 +324,9 @@ var AgentConnection = class _AgentConnection {
|
|
|
291
324
|
* question-grace expiry reads it to defer un-parking while a human is
|
|
292
325
|
* actively typing into the Connected-TUI (they may be mid-answer). */
|
|
293
326
|
_lastPtyInputAt = null;
|
|
327
|
+
/** Runner-side subscribers to keystrokes from EITHER transport. */
|
|
328
|
+
ptyInputActivityListeners = /* @__PURE__ */ new Set();
|
|
329
|
+
lastReportedDirectInputAt = Number.NEGATIVE_INFINITY;
|
|
294
330
|
constructor(config) {
|
|
295
331
|
this.config = config;
|
|
296
332
|
}
|
|
@@ -479,7 +515,7 @@ var AgentConnection = class _AgentConnection {
|
|
|
479
515
|
});
|
|
480
516
|
this.socket.on("pty:input", (data) => {
|
|
481
517
|
if (data.sessionId && data.sessionId !== this.config.sessionId) return;
|
|
482
|
-
this.
|
|
518
|
+
this.notePtyInput("relay");
|
|
483
519
|
this.ptyInputCallback?.(data.data);
|
|
484
520
|
});
|
|
485
521
|
this.socket.on("pty:resize", (data) => {
|
|
@@ -946,7 +982,32 @@ var AgentConnection = class _AgentConnection {
|
|
|
946
982
|
}).catch(() => {
|
|
947
983
|
});
|
|
948
984
|
}
|
|
949
|
-
/**
|
|
985
|
+
/**
|
|
986
|
+
* A human typed into the TUI. Stamps `lastPtyInputAt` and notifies the
|
|
987
|
+
* runner's input-activity listeners for both transports. Direct-stream
|
|
988
|
+
* keystrokes never cross the API, so for those this also tells the server
|
|
989
|
+
* (`reportPtyInput`, throttled, fire-and-forget) — the relay path's server
|
|
990
|
+
* already bumps in `relayPtyInput`. An old API that doesn't know the method
|
|
991
|
+
* rejects harmlessly.
|
|
992
|
+
*/
|
|
993
|
+
notePtyInput(origin = "direct") {
|
|
994
|
+
const now = Date.now();
|
|
995
|
+
this._lastPtyInputAt = now;
|
|
996
|
+
for (const listener of this.ptyInputActivityListeners) listener();
|
|
997
|
+
if (origin !== "direct" || !this.socket) return;
|
|
998
|
+
if (now - this.lastReportedDirectInputAt < PTY_INPUT_REPORT_INTERVAL_MS) return;
|
|
999
|
+
this.lastReportedDirectInputAt = now;
|
|
1000
|
+
void this.call("reportPtyInput", { sessionId: this.config.sessionId }).catch(() => {
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/** Subscribe to keystroke activity from either transport. Returns an unsubscribe fn. */
|
|
1004
|
+
onPtyInputActivity(listener) {
|
|
1005
|
+
this.ptyInputActivityListeners.add(listener);
|
|
1006
|
+
return () => {
|
|
1007
|
+
this.ptyInputActivityListeners.delete(listener);
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
/** Epoch ms of the last TUI keystroke (relay or direct), or null before any. */
|
|
950
1011
|
get lastPtyInputAt() {
|
|
951
1012
|
return this._lastPtyInputAt;
|
|
952
1013
|
}
|
|
@@ -1402,7 +1463,15 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
|
|
|
1402
1463
|
// ── Event buffering ────────────────────────────────────────────────
|
|
1403
1464
|
sendEvent(event) {
|
|
1404
1465
|
if (!this.socket) return;
|
|
1405
|
-
|
|
1466
|
+
const fitted = fitEvent(event);
|
|
1467
|
+
if (!fitted) {
|
|
1468
|
+
process.stderr.write(
|
|
1469
|
+
`[conveyor-agent] dropped oversized ${event.type} event (over the emit batch limit)
|
|
1470
|
+
`
|
|
1471
|
+
);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
this.enqueueEvents([fitted], false);
|
|
1406
1475
|
}
|
|
1407
1476
|
/** Append (or, on `toFront`, prepend for a failed-flush re-queue) events to
|
|
1408
1477
|
* the buffer, then cap + arm the flush timer. Single owner of the overflow
|
|
@@ -1429,14 +1498,15 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
|
|
|
1429
1498
|
clearTimeout(this.flushTimer);
|
|
1430
1499
|
this.flushTimer = null;
|
|
1431
1500
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1501
|
+
while (this.socket && this.eventBuffer.length > 0) {
|
|
1502
|
+
const entries = this.eventBuffer.splice(0, batchLength(this.eventBuffer));
|
|
1503
|
+
const events = entries.map((entry) => entry.event);
|
|
1504
|
+
try {
|
|
1505
|
+
await this.call("emitAgentEvent", { sessionId: this.config.sessionId, events });
|
|
1506
|
+
} catch {
|
|
1507
|
+
this.requeueFailedEvents(entries);
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1440
1510
|
}
|
|
1441
1511
|
}
|
|
1442
1512
|
/** Put a failed flush's events back at the FRONT of the buffer, preserving
|
|
@@ -1446,7 +1516,7 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
|
|
|
1446
1516
|
}
|
|
1447
1517
|
};
|
|
1448
1518
|
|
|
1449
|
-
// ../shared/dist/chunk-
|
|
1519
|
+
// ../shared/dist/chunk-3D2OVVIS.js
|
|
1450
1520
|
import { z } from "zod";
|
|
1451
1521
|
import { z as z2 } from "zod";
|
|
1452
1522
|
var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
|
|
@@ -1455,6 +1525,15 @@ var DEFAULT_HAIKU_MODEL = "claude-haiku-4-5-20251001";
|
|
|
1455
1525
|
var FABLE_MODEL = "claude-fable-5-1";
|
|
1456
1526
|
var PREVIOUS_SONNET_MODEL = "claude-sonnet-4-6";
|
|
1457
1527
|
var PREVIOUS_OPUS_MODEL = "claude-opus-5";
|
|
1528
|
+
function formatModelId(provider, model) {
|
|
1529
|
+
return `${provider}/${model}`;
|
|
1530
|
+
}
|
|
1531
|
+
var JEV_MODEL = "jev-1.13.0";
|
|
1532
|
+
var JEV_MODEL_ID = formatModelId("typesafe", JEV_MODEL);
|
|
1533
|
+
var JEV_ZEN_MODEL = "jev-1.13";
|
|
1534
|
+
var JEV_ZEN_MODEL_ID = formatModelId("zen", JEV_ZEN_MODEL);
|
|
1535
|
+
var JEV_INPUT_PRICE_PER_TOKEN = 0.042 / 1e6;
|
|
1536
|
+
var JEV_STATE_MAX_CHARS = 1e5;
|
|
1458
1537
|
var PTY_STREAM_PORT_BASE = 7420;
|
|
1459
1538
|
var PTY_STREAM_PORT_ATTEMPTS = 8;
|
|
1460
1539
|
function encodePtyStreamFrame(frame) {
|
|
@@ -1508,6 +1587,11 @@ var PREVIEW_PORT_DENY_LIST = [
|
|
|
1508
1587
|
9200,
|
|
1509
1588
|
...Array.from({ length: PTY_STREAM_PORT_ATTEMPTS }, (_, i) => PTY_STREAM_PORT_BASE + i)
|
|
1510
1589
|
];
|
|
1590
|
+
function isAllowablePreviewPort(port) {
|
|
1591
|
+
if (typeof port !== "number" || !Number.isInteger(port)) return false;
|
|
1592
|
+
if (port < 1 || port > 65535) return false;
|
|
1593
|
+
return !PREVIEW_PORT_DENY_LIST.includes(port);
|
|
1594
|
+
}
|
|
1511
1595
|
function normalizeCheckpointPath(value) {
|
|
1512
1596
|
let normalized = value.trim().replace(/\/{2,}/g, "/");
|
|
1513
1597
|
normalized = normalized.split("/").filter((segment) => segment !== ".").join("/");
|
|
@@ -1973,7 +2057,9 @@ var KnownAgentEventSchema = z22.discriminatedUnion("type", [
|
|
|
1973
2057
|
startCommandRunning: z22.boolean().optional(),
|
|
1974
2058
|
startCommandConfigured: z22.boolean().optional(),
|
|
1975
2059
|
// Sanitized server-side by sanitizeSessionPreviewPorts — stays unknown.
|
|
1976
|
-
previewPorts: z22.unknown().optional()
|
|
2060
|
+
previewPorts: z22.unknown().optional(),
|
|
2061
|
+
// Sanitized server-side by sanitizeSessionPreviewLinks — stays unknown.
|
|
2062
|
+
previewLinks: z22.unknown().optional()
|
|
1977
2063
|
}).passthrough(),
|
|
1978
2064
|
z22.object({ type: z22.literal("setup_error"), message: z22.string() }).passthrough(),
|
|
1979
2065
|
z22.object({ type: z22.literal("start_command_started") }).passthrough(),
|
|
@@ -2451,6 +2537,9 @@ var ReportPtyStreamRequestSchema = z32.object({
|
|
|
2451
2537
|
sessionId: z32.string(),
|
|
2452
2538
|
port: z32.number().int().positive().max(65535).nullable()
|
|
2453
2539
|
});
|
|
2540
|
+
var ReportPtyInputRequestSchema = z32.object({
|
|
2541
|
+
sessionId: z32.string()
|
|
2542
|
+
});
|
|
2454
2543
|
var GetPtyStreamEndpointRequestSchema = z32.object({
|
|
2455
2544
|
sessionId: z32.string()
|
|
2456
2545
|
});
|
|
@@ -3275,6 +3364,30 @@ var CRITICAL_AUTOMATED_SOURCES = /* @__PURE__ */ new Set([
|
|
|
3275
3364
|
// clear the completion guard.
|
|
3276
3365
|
"ci_result"
|
|
3277
3366
|
]);
|
|
3367
|
+
var MAX_PREVIEW_LINKS = 8;
|
|
3368
|
+
var MAX_PREVIEW_LINK_LABEL = 64;
|
|
3369
|
+
var MAX_PREVIEW_LINK_PATH = 512;
|
|
3370
|
+
function isSafePreviewPath(path) {
|
|
3371
|
+
if (!path.startsWith("/") || path.startsWith("//")) return false;
|
|
3372
|
+
if (path.length > MAX_PREVIEW_LINK_PATH) return false;
|
|
3373
|
+
return !/[\s\\\u0000-\u001f\u007f]/.test(path);
|
|
3374
|
+
}
|
|
3375
|
+
function sanitizeSessionPreviewLinks(raw) {
|
|
3376
|
+
if (!Array.isArray(raw)) return [];
|
|
3377
|
+
const out = [];
|
|
3378
|
+
for (const entry of raw) {
|
|
3379
|
+
if (out.length >= MAX_PREVIEW_LINKS) break;
|
|
3380
|
+
if (!entry || typeof entry !== "object") continue;
|
|
3381
|
+
const { label, port, path } = entry;
|
|
3382
|
+
if (typeof label !== "string") continue;
|
|
3383
|
+
const trimmed = label.trim().slice(0, MAX_PREVIEW_LINK_LABEL);
|
|
3384
|
+
if (trimmed.length === 0 || !isAllowablePreviewPort(port)) continue;
|
|
3385
|
+
const resolvedPath = path === void 0 ? "/" : path;
|
|
3386
|
+
if (typeof resolvedPath !== "string" || !isSafePreviewPath(resolvedPath)) continue;
|
|
3387
|
+
out.push({ label: trimmed, port, path: resolvedPath });
|
|
3388
|
+
}
|
|
3389
|
+
return out;
|
|
3390
|
+
}
|
|
3278
3391
|
var DECISION_STATUS_LABELS = ["Open", "Decided", "Cancelled"];
|
|
3279
3392
|
var DECISION_OPTION_KINDS = ["current", "planned", "proposed"];
|
|
3280
3393
|
var DECISION_VOTE_SOURCES = ["web", "slack", "discord", "agent"];
|
|
@@ -3448,13 +3561,6 @@ var UpdateProjectDecisionRequestSchema = z10.object({
|
|
|
3448
3561
|
status: z10.enum(["Cancelled", "Open"]).optional(),
|
|
3449
3562
|
requestingUserId: z10.string().optional()
|
|
3450
3563
|
});
|
|
3451
|
-
function formatModelId(provider, model) {
|
|
3452
|
-
return `${provider}/${model}`;
|
|
3453
|
-
}
|
|
3454
|
-
var JEV_MODEL = "jev-1.13.0";
|
|
3455
|
-
var JEV_MODEL_ID = formatModelId("typesafe", JEV_MODEL);
|
|
3456
|
-
var JEV_INPUT_PRICE_PER_TOKEN = 0.042 / 1e6;
|
|
3457
|
-
var JEV_STATE_MAX_CHARS = 1e5;
|
|
3458
3564
|
var EVALUATE_MAX_QUESTIONS = 32;
|
|
3459
3565
|
var EVALUATE_MAX_ITEMS = 100;
|
|
3460
3566
|
var EVALUATE_ITEM_MAX_CHARS = 8e3;
|
|
@@ -4624,21 +4730,30 @@ async function loadForwardPorts(workspaceDir) {
|
|
|
4624
4730
|
if (value.visibility === "public" || value.visibility === "private") {
|
|
4625
4731
|
entry.visibility = value.visibility;
|
|
4626
4732
|
}
|
|
4733
|
+
if (value.onAutoForward === "ignore") entry.hidden = true;
|
|
4627
4734
|
attributes[key] = entry;
|
|
4628
4735
|
}
|
|
4629
|
-
|
|
4736
|
+
const links = sanitizeSessionPreviewLinks(parsed.customizations?.conveyor?.previewLinks);
|
|
4737
|
+
return { ports, attributes, links };
|
|
4630
4738
|
} catch {
|
|
4631
|
-
return { ports: [], attributes: {} };
|
|
4739
|
+
return { ports: [], attributes: {}, links: [] };
|
|
4632
4740
|
}
|
|
4633
4741
|
}
|
|
4634
4742
|
function buildSessionPreviewPorts(result) {
|
|
4635
|
-
|
|
4743
|
+
const toEntry = (port) => {
|
|
4636
4744
|
const attr = result.attributes[String(port)];
|
|
4637
4745
|
const entry = { port };
|
|
4638
4746
|
if (attr?.label) entry.label = attr.label;
|
|
4639
4747
|
if (attr?.visibility) entry.visibility = attr.visibility;
|
|
4748
|
+
if (attr?.hidden) entry.hidden = true;
|
|
4640
4749
|
return entry;
|
|
4641
|
-
}
|
|
4750
|
+
};
|
|
4751
|
+
const forwarded = result.ports.filter((port) => !DEVCONTAINER_PORT_DENY_LIST.has(port));
|
|
4752
|
+
const listed = new Set(forwarded);
|
|
4753
|
+
const hiddenOnly = Object.entries(result.attributes).filter(([, attr]) => attr.hidden).map(([key]) => Number(key)).filter(
|
|
4754
|
+
(port) => Number.isInteger(port) && port >= 1 && port <= 65535 && !listed.has(port) && !DEVCONTAINER_PORT_DENY_LIST.has(port)
|
|
4755
|
+
);
|
|
4756
|
+
return [...forwarded, ...hiddenOnly].map(toEntry);
|
|
4642
4757
|
}
|
|
4643
4758
|
function loadConveyorConfig() {
|
|
4644
4759
|
const envStart = process.env.CONVEYOR_START_COMMAND;
|
|
@@ -4741,12 +4856,19 @@ var Lifecycle = class {
|
|
|
4741
4856
|
this.clearIdleTimers();
|
|
4742
4857
|
const delay2 = Math.max(0, overrideMs ?? this.config.idleTimeoutMs);
|
|
4743
4858
|
this.idleTimer = setTimeout(() => {
|
|
4859
|
+
this.idleTimer = null;
|
|
4744
4860
|
this.callbacks.onIdleTimeout();
|
|
4745
4861
|
}, delay2);
|
|
4746
4862
|
}
|
|
4747
4863
|
cancelIdleTimer() {
|
|
4748
4864
|
this.clearIdleTimers();
|
|
4749
4865
|
}
|
|
4866
|
+
/** Restart an ARMED idle timer with the full window; a no-op when none is
|
|
4867
|
+
* armed. Human activity uses this, so a keystroke during a running turn
|
|
4868
|
+
* (timer cancelled) can never arm one. */
|
|
4869
|
+
touchIdleTimer() {
|
|
4870
|
+
if (this.idleTimer) this.startIdleTimer();
|
|
4871
|
+
}
|
|
4750
4872
|
// ── Dormant timer ──────────────────────────────────────────────────
|
|
4751
4873
|
/** Start (or restart) the dormant timer.
|
|
4752
4874
|
* @param overrideMs Optional custom delay in ms. When provided, the timer
|
|
@@ -95,7 +95,7 @@ import {
|
|
|
95
95
|
statWorkspacePath,
|
|
96
96
|
updateRemoteToken,
|
|
97
97
|
verifyGitCredential
|
|
98
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-7KG7CDVU.js";
|
|
99
99
|
import {
|
|
100
100
|
registerBootMilestoneSocketFallback,
|
|
101
101
|
reportBootMilestone
|
|
@@ -550,6 +550,8 @@ var MAX_TRACKED_MESSAGES = 200;
|
|
|
550
550
|
var CHAT_TEXT_MAX = 16e3;
|
|
551
551
|
var CHAT_TOOL_INPUT_MAX = 1900;
|
|
552
552
|
var CHAT_TOOL_OUTPUT_MAX = 1900;
|
|
553
|
+
var OPENCODE_LIVENESS_THROTTLE_MS = 5e3;
|
|
554
|
+
var MAX_TRACKED_CHILD_SESSIONS = 100;
|
|
553
555
|
function truncate(text, max) {
|
|
554
556
|
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
555
557
|
}
|
|
@@ -585,6 +587,9 @@ var OpenCodeEventSource = class {
|
|
|
585
587
|
relayedTextParts = /* @__PURE__ */ new Set();
|
|
586
588
|
relayedToolUses = /* @__PURE__ */ new Set();
|
|
587
589
|
relayedToolResults = /* @__PURE__ */ new Set();
|
|
590
|
+
/** Session ids opencode reported with a `parentID` — subagent sessions. */
|
|
591
|
+
childSessions = /* @__PURE__ */ new Set();
|
|
592
|
+
lastLivenessAt = Number.NEGATIVE_INFINITY;
|
|
588
593
|
/** The opencode-assigned session id, once any event has carried it. */
|
|
589
594
|
get sessionId() {
|
|
590
595
|
return this.latchedSessionId;
|
|
@@ -598,11 +603,21 @@ var OpenCodeEventSource = class {
|
|
|
598
603
|
handleRecord(raw) {
|
|
599
604
|
if (typeof raw !== "object" || raw === null) return;
|
|
600
605
|
const event = raw;
|
|
606
|
+
this.trackChildSession(event);
|
|
601
607
|
this.latchSessionId(event);
|
|
608
|
+
if (this.isForeignSession(event)) {
|
|
609
|
+
if (this.active) this.emitLiveness();
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
602
612
|
const info = busInfoOf(event);
|
|
603
613
|
if (info?.id && info.role) this.trackRole(info.id, info.role);
|
|
604
614
|
if (isBusyEvent(event)) {
|
|
605
615
|
if (!this.active) this.beginBusTurn();
|
|
616
|
+
this.emitLiveness();
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (isRetryEvent(event)) {
|
|
620
|
+
if (this.active) this.emitLiveness();
|
|
606
621
|
return;
|
|
607
622
|
}
|
|
608
623
|
if (isIdleEvent(event)) {
|
|
@@ -629,15 +644,58 @@ var OpenCodeEventSource = class {
|
|
|
629
644
|
this.relayAssistantPart(part);
|
|
630
645
|
accumulateUsage({ part }, this.usage);
|
|
631
646
|
const mapped = mapOpenCodeEvent({ part });
|
|
632
|
-
if (!mapped)
|
|
647
|
+
if (!mapped) {
|
|
648
|
+
this.emitLiveness();
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
633
651
|
this.emit(mapped);
|
|
634
652
|
if (mapped.type === "assistant") {
|
|
635
653
|
const block = mapped.message.content[0];
|
|
636
654
|
if (block?.type === "text" && block.text) this.assistantText += block.text;
|
|
637
655
|
}
|
|
638
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Local-only proof the turn is live. Throttled per turn; `beginBusTurn`
|
|
659
|
+
* resets the floor so every turn's `busy` gets through.
|
|
660
|
+
*/
|
|
661
|
+
emitLiveness() {
|
|
662
|
+
const now = Date.now();
|
|
663
|
+
if (now - this.lastLivenessAt < OPENCODE_LIVENESS_THROTTLE_MS) return;
|
|
664
|
+
this.lastLivenessAt = now;
|
|
665
|
+
this.emit({ type: "tool_progress", tool_name: "OpenCode", elapsed_time_seconds: 0 });
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* A record from another session. Known children (a `parentID` was seen)
|
|
669
|
+
* always are; an unknown session counts as foreign only mid-turn, where
|
|
670
|
+
* the only other session on the bus is a subagent. Outside a turn it is
|
|
671
|
+
* processed as before, and one that goes `busy` becomes the new root — a
|
|
672
|
+
* human's `/new` in the parked TUI opens a passive turn and later spawns
|
|
673
|
+
* resume into it.
|
|
674
|
+
*/
|
|
675
|
+
isForeignSession(event) {
|
|
676
|
+
const id = busSessionIdOf(event);
|
|
677
|
+
if (!id || !this.latchedSessionId || id === this.latchedSessionId) return false;
|
|
678
|
+
if (this.childSessions.has(id) || this.active) return true;
|
|
679
|
+
if (isBusyEvent(event)) {
|
|
680
|
+
this.latchedSessionId = id;
|
|
681
|
+
this.onSessionId?.(id);
|
|
682
|
+
}
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
trackChildSession(event) {
|
|
686
|
+
if (event.type !== "session.created" && event.type !== "session.updated") return;
|
|
687
|
+
const info = event.properties?.info;
|
|
688
|
+
if (typeof info?.id !== "string" || typeof info.parentID !== "string") return;
|
|
689
|
+
if (!info.parentID) return;
|
|
690
|
+
this.childSessions.add(info.id);
|
|
691
|
+
if (this.childSessions.size > MAX_TRACKED_CHILD_SESSIONS) {
|
|
692
|
+
const oldest = this.childSessions.values().next().value;
|
|
693
|
+
if (oldest !== void 0) this.childSessions.delete(oldest);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
639
696
|
beginBusTurn() {
|
|
640
697
|
this.active = true;
|
|
698
|
+
this.lastLivenessAt = Number.NEGATIVE_INFINITY;
|
|
641
699
|
this.usage = { inputTokens: 0, outputTokens: 0, totalCostUsd: 0 };
|
|
642
700
|
this.assistantText = "";
|
|
643
701
|
this.relayedTextParts.clear();
|
|
@@ -705,7 +763,7 @@ var OpenCodeEventSource = class {
|
|
|
705
763
|
latchSessionId(event) {
|
|
706
764
|
if (this.latchedSessionId) return;
|
|
707
765
|
const id = busSessionIdOf(event);
|
|
708
|
-
if (!id) return;
|
|
766
|
+
if (!id || this.childSessions.has(id)) return;
|
|
709
767
|
this.latchedSessionId = id;
|
|
710
768
|
this.onSessionId?.(id);
|
|
711
769
|
}
|
|
@@ -717,6 +775,9 @@ var OpenCodeEventSource = class {
|
|
|
717
775
|
}
|
|
718
776
|
}
|
|
719
777
|
};
|
|
778
|
+
function isRetryEvent(event) {
|
|
779
|
+
return event.type === "session.status" && event.properties?.status?.type === "retry";
|
|
780
|
+
}
|
|
720
781
|
function describeSessionError(event) {
|
|
721
782
|
const error = event.properties?.error;
|
|
722
783
|
if (error && typeof error.message === "string") return error.message;
|
|
@@ -1008,6 +1069,7 @@ var JsonlTailer = class {
|
|
|
1008
1069
|
|
|
1009
1070
|
// src/harness/codex/event-source.ts
|
|
1010
1071
|
var USAGE_CAP_PATTERN = /usage_limit_(?:reached|exceeded)|rate_limit_reached|UsageLimitReached|hit your usage limit/i;
|
|
1072
|
+
var CODEX_LIVENESS_THROTTLE_MS = 5e3;
|
|
1011
1073
|
var WEEKLY_WINDOW_MINUTES = 24 * 60;
|
|
1012
1074
|
function isUsageCap(error) {
|
|
1013
1075
|
return [error.codex_error_info, error.code, error.type, error.message].some(
|
|
@@ -1024,6 +1086,9 @@ var CodexEventSource = class {
|
|
|
1024
1086
|
onSession;
|
|
1025
1087
|
chat;
|
|
1026
1088
|
sessionId = null;
|
|
1089
|
+
/** UserPromptSubmit seen and no completion/interrupt since. */
|
|
1090
|
+
turnOpen = false;
|
|
1091
|
+
lastLivenessAt = Number.NEGATIVE_INFINITY;
|
|
1027
1092
|
completed = /* @__PURE__ */ new Set();
|
|
1028
1093
|
transcript = null;
|
|
1029
1094
|
/** Last reported reading per rate-limit window, keyed by `five_hour` / `seven_day`. */
|
|
@@ -1037,13 +1102,18 @@ var CodexEventSource = class {
|
|
|
1037
1102
|
this.sessionId = id;
|
|
1038
1103
|
this.onSession(id);
|
|
1039
1104
|
}
|
|
1040
|
-
if (id !== this.sessionId)
|
|
1105
|
+
if (id !== this.sessionId) {
|
|
1106
|
+
this.emitLiveness();
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1041
1109
|
if (e.hook_event_name === "SessionStart") this.watchTranscript(e);
|
|
1042
1110
|
if (e.type === "agent-turn-complete") {
|
|
1043
1111
|
this.finish(e);
|
|
1044
1112
|
return;
|
|
1045
1113
|
}
|
|
1046
1114
|
if (e.hook_event_name === "UserPromptSubmit") {
|
|
1115
|
+
this.turnOpen = true;
|
|
1116
|
+
this.lastLivenessAt = Date.now();
|
|
1047
1117
|
this.emit({ type: "tool_progress", tool_name: "Codex", elapsed_time_seconds: 0 });
|
|
1048
1118
|
if (typeof e.prompt === "string")
|
|
1049
1119
|
this.chat({ kind: "user_text", text: e.prompt.slice(0, 16e3) });
|
|
@@ -1056,6 +1126,7 @@ var CodexEventSource = class {
|
|
|
1056
1126
|
elapsed_time_seconds: 0
|
|
1057
1127
|
});
|
|
1058
1128
|
} else if (e.hook_event_name === "Interrupt") {
|
|
1129
|
+
this.turnOpen = false;
|
|
1059
1130
|
this.emit({ type: "result", subtype: "error", errors: ["Codex turn interrupted"] });
|
|
1060
1131
|
this.chat({ kind: "turn_end" });
|
|
1061
1132
|
}
|
|
@@ -1065,6 +1136,14 @@ var CodexEventSource = class {
|
|
|
1065
1136
|
await this.transcript?.flush();
|
|
1066
1137
|
this.transcript = null;
|
|
1067
1138
|
}
|
|
1139
|
+
/** Throttled local-only liveness; silent unless a turn is open. */
|
|
1140
|
+
emitLiveness() {
|
|
1141
|
+
if (!this.turnOpen) return;
|
|
1142
|
+
const now = Date.now();
|
|
1143
|
+
if (now - this.lastLivenessAt < CODEX_LIVENESS_THROTTLE_MS) return;
|
|
1144
|
+
this.lastLivenessAt = now;
|
|
1145
|
+
this.emit({ type: "tool_progress", tool_name: "Codex", elapsed_time_seconds: 0 });
|
|
1146
|
+
}
|
|
1068
1147
|
watchTranscript(e) {
|
|
1069
1148
|
if (this.transcript || typeof e.transcript_path !== "string" || typeof e.transcript_offset !== "number" || e.transcript_offset < 0)
|
|
1070
1149
|
return;
|
|
@@ -1079,9 +1158,10 @@ var CodexEventSource = class {
|
|
|
1079
1158
|
handleTranscriptRecord(raw) {
|
|
1080
1159
|
if (!raw || typeof raw !== "object") return;
|
|
1081
1160
|
const record = raw;
|
|
1082
|
-
if (
|
|
1083
|
-
return;
|
|
1161
|
+
if (!record.payload || typeof record.payload !== "object") return;
|
|
1084
1162
|
const event = record.payload;
|
|
1163
|
+
if (event.type !== "task_complete") this.emitLiveness();
|
|
1164
|
+
if (record.type !== "event_msg") return;
|
|
1085
1165
|
if (event.type === "token_count") {
|
|
1086
1166
|
this.handleTokenCount(event);
|
|
1087
1167
|
return;
|
|
@@ -1090,6 +1170,7 @@ var CodexEventSource = class {
|
|
|
1090
1170
|
const turn = event.turn_id;
|
|
1091
1171
|
if (typeof turn !== "string" || this.completed.has(turn)) return;
|
|
1092
1172
|
this.rememberTurn(turn);
|
|
1173
|
+
this.turnOpen = false;
|
|
1093
1174
|
const error = event.error;
|
|
1094
1175
|
if (isUsageCap(error)) this.emitUsageCap();
|
|
1095
1176
|
this.emit({
|
|
@@ -1178,6 +1259,7 @@ var CodexEventSource = class {
|
|
|
1178
1259
|
const turn = e["turn-id"];
|
|
1179
1260
|
if (typeof turn !== "string" || this.completed.has(turn)) return;
|
|
1180
1261
|
this.rememberTurn(turn);
|
|
1262
|
+
this.turnOpen = false;
|
|
1181
1263
|
const text = typeof e["last-assistant-message"] === "string" ? e["last-assistant-message"] : "";
|
|
1182
1264
|
if (text) {
|
|
1183
1265
|
this.emit({
|
|
@@ -2643,6 +2725,7 @@ var ClaudeTuiAdapter = class {
|
|
|
2643
2725
|
};
|
|
2644
2726
|
|
|
2645
2727
|
// src/harness/pty/session.ts
|
|
2728
|
+
var SUBMIT_PROVING_PROGRESS_ADAPTERS = /* @__PURE__ */ new Set(["codex", "opencode"]);
|
|
2646
2729
|
var PtySession = class {
|
|
2647
2730
|
constructor(prompt, options, resume, bridge, adapter = new ClaudeTuiAdapter()) {
|
|
2648
2731
|
this.options = options;
|
|
@@ -3702,7 +3785,7 @@ var PtySession = class {
|
|
|
3702
3785
|
if (this.pendingPlanApproval && event.type === "result") {
|
|
3703
3786
|
this.disarmPlanDialogAutoAccept();
|
|
3704
3787
|
}
|
|
3705
|
-
if (this.pendingSubmitNudge && (event.type === "assistant" || event.type === "result" || this.adapter.id
|
|
3788
|
+
if (this.pendingSubmitNudge && (event.type === "assistant" || event.type === "result" || SUBMIT_PROVING_PROGRESS_ADAPTERS.has(this.adapter.id) && event.type === "tool_progress")) {
|
|
3706
3789
|
this.disarmSubmitNudge();
|
|
3707
3790
|
}
|
|
3708
3791
|
this.synthesizeRateLimitFromBanner(event);
|
|
@@ -4919,6 +5002,13 @@ var CodexTuiAdapter = class {
|
|
|
4919
5002
|
// The pod image owns CLI upgrades. An interactive update would try to
|
|
4920
5003
|
// replace the root-owned global package from the conveyor user account.
|
|
4921
5004
|
check_for_update_on_startup: false,
|
|
5005
|
+
// Acknowledge 0.157.0's model promotions so unattended sessions start.
|
|
5006
|
+
// This dismisses the notices; --model below still selects the saved model.
|
|
5007
|
+
"notice.model_migrations": {
|
|
5008
|
+
"gpt-5.6-terra": "gpt-6-sol",
|
|
5009
|
+
"gpt-5.6-sol": "gpt-6-sol",
|
|
5010
|
+
"gpt-5.6-luna": "gpt-6-luna"
|
|
5011
|
+
},
|
|
4922
5012
|
sqlite_home: codexSqliteHome(this.env),
|
|
4923
5013
|
approval_policy: "never",
|
|
4924
5014
|
// Always full access, plan mode included. Codex's own read-only mode is
|
|
@@ -5217,7 +5307,10 @@ var DirectStreamController = class {
|
|
|
5217
5307
|
this.starting = true;
|
|
5218
5308
|
const created = new PtyStreamServer({
|
|
5219
5309
|
sessionId: this.reporter.sessionId,
|
|
5220
|
-
onInput: (data) =>
|
|
5310
|
+
onInput: (data) => {
|
|
5311
|
+
this.reporter.notePtyInput();
|
|
5312
|
+
this.inputHandler?.(data);
|
|
5313
|
+
},
|
|
5221
5314
|
onResize: (cols, rows) => {
|
|
5222
5315
|
this.directDims = { cols, rows };
|
|
5223
5316
|
this.applyDims();
|
|
@@ -8597,7 +8690,7 @@ var waitForChecksContract = defineToolContract({
|
|
|
8597
8690
|
var ciWaitContracts = [waitForChecksContract];
|
|
8598
8691
|
var DESCRIPTION = [
|
|
8599
8692
|
"Ask typed questions about text and get probabilities back, never text: yes/no (noul \u2192 probability of yes), pick-one (choice \u2192 the chosen option, per-option probabilities, and a confidence), or a graded scale (score \u2192 the level, per-level probabilities, and a confidence).",
|
|
8600
|
-
"Requires
|
|
8693
|
+
"Requires Jev to be switched on under Project Settings \u2192 AI Provider Keys (a TypeSafe key, or an OpenCode Zen key); without it the tool returns an error saying so.",
|
|
8601
8694
|
"The state is data, not a guardrail: adversarial text inside it can move the answers, so never use a result to authorize an action.",
|
|
8602
8695
|
"Ask one simple judgment per question and split compound questions; accuracy falls when the state carries irrelevant text.",
|
|
8603
8696
|
"Pass items to judge up to 100 separate texts with the same questions in one call; answers come back in item order.",
|
|
@@ -12926,13 +13019,17 @@ async function runPassiveTurn(host, context) {
|
|
|
12926
13019
|
host.activeQuery = null;
|
|
12927
13020
|
}
|
|
12928
13021
|
}
|
|
13022
|
+
function parkedTuiWatchOptions(harness, cwd) {
|
|
13023
|
+
if (harness.emitsStructuredEvents === false) return { disableWedgeAbort: true };
|
|
13024
|
+
if (harness.ownsClaudeConfigHome === false) return {};
|
|
13025
|
+
return { probeMountDead: () => isConfigHomeMountDead(cwd) };
|
|
13026
|
+
}
|
|
12929
13027
|
async function trackAndRun(host, context, options, agentQuery) {
|
|
12930
13028
|
if (host.harnessKind === "pty" && options.promptDelivery !== "prefill") {
|
|
12931
|
-
const rawRelay = host.harness.emitsStructuredEvents === false;
|
|
12932
13029
|
agentQuery = watchForParkedTui(
|
|
12933
13030
|
agentQuery,
|
|
12934
13031
|
host,
|
|
12935
|
-
|
|
13032
|
+
parkedTuiWatchOptions(host.harness, options.cwd)
|
|
12936
13033
|
);
|
|
12937
13034
|
}
|
|
12938
13035
|
host.activeQuery = agentQuery;
|
|
@@ -14320,6 +14417,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
14320
14417
|
onGitFlush: () => void this.periodicGitFlush(),
|
|
14321
14418
|
onUsageSample: () => void this.sampleAndReportKeyUsage()
|
|
14322
14419
|
});
|
|
14420
|
+
this.connection.onPtyInputActivity(() => this.lifecycle.touchIdleTimer());
|
|
14323
14421
|
}
|
|
14324
14422
|
get state() {
|
|
14325
14423
|
return this._state;
|
|
@@ -15656,7 +15754,9 @@ ${outcome.failures.join("\n")}
|
|
|
15656
15754
|
* turn: a background launch, or an armed `ScheduleWakeup`. */
|
|
15657
15755
|
noteToolUseForBackgroundWork(event) {
|
|
15658
15756
|
if (event.type !== "tool_use" || typeof event.tool !== "string") return;
|
|
15659
|
-
this.
|
|
15757
|
+
if (resolveCardTui(this.config.runnerMode) === "claude-code") {
|
|
15758
|
+
this.backgroundWork.noteToolUse(event.tool, event.input);
|
|
15759
|
+
}
|
|
15660
15760
|
this.scheduledWakeup.noteToolUse(event.tool, event.input);
|
|
15661
15761
|
}
|
|
15662
15762
|
async setState(status) {
|
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-2BHXJ37W.js";
|
|
11
11
|
import {
|
|
12
12
|
PtyHarness,
|
|
13
13
|
SessionRunner,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runUsageProbe,
|
|
26
26
|
sampleKeyUsage,
|
|
27
27
|
usesNativeUsageReporting
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-HYXAXQHN.js";
|
|
29
29
|
import "./chunk-37J5MMQT.js";
|
|
30
30
|
import {
|
|
31
31
|
inheritedEnv,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-7KG7CDVU.js";
|
|
48
48
|
import "./chunk-Q4FQOJ7D.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
50
|
import "./chunk-372R6E4C.js";
|
|
@@ -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-F2G7ZXFF.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/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _project_shared from '@project/shared';
|
|
2
|
-
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus } from '@project/shared';
|
|
2
|
+
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus, SessionPreviewLink } from '@project/shared';
|
|
3
3
|
export * from '@project/shared';
|
|
4
4
|
import { ChildProcess } from 'node:child_process';
|
|
5
5
|
|
|
@@ -135,6 +135,9 @@ declare class AgentConnection {
|
|
|
135
135
|
* question-grace expiry reads it to defer un-parking while a human is
|
|
136
136
|
* actively typing into the Connected-TUI (they may be mid-answer). */
|
|
137
137
|
private _lastPtyInputAt;
|
|
138
|
+
/** Runner-side subscribers to keystrokes from EITHER transport. */
|
|
139
|
+
private readonly ptyInputActivityListeners;
|
|
140
|
+
private lastReportedDirectInputAt;
|
|
138
141
|
constructor(config: AgentConnectionConfig);
|
|
139
142
|
get sessionId(): string;
|
|
140
143
|
get connected(): boolean;
|
|
@@ -294,7 +297,18 @@ declare class AgentConnection {
|
|
|
294
297
|
* just stays on the relay transport.
|
|
295
298
|
*/
|
|
296
299
|
reportPtyStream(port: number | null): void;
|
|
297
|
-
/**
|
|
300
|
+
/**
|
|
301
|
+
* A human typed into the TUI. Stamps `lastPtyInputAt` and notifies the
|
|
302
|
+
* runner's input-activity listeners for both transports. Direct-stream
|
|
303
|
+
* keystrokes never cross the API, so for those this also tells the server
|
|
304
|
+
* (`reportPtyInput`, throttled, fire-and-forget) — the relay path's server
|
|
305
|
+
* already bumps in `relayPtyInput`. An old API that doesn't know the method
|
|
306
|
+
* rejects harmlessly.
|
|
307
|
+
*/
|
|
308
|
+
notePtyInput(origin?: "relay" | "direct"): void;
|
|
309
|
+
/** Subscribe to keystroke activity from either transport. Returns an unsubscribe fn. */
|
|
310
|
+
onPtyInputActivity(listener: () => void): () => void;
|
|
311
|
+
/** Epoch ms of the last TUI keystroke (relay or direct), or null before any. */
|
|
298
312
|
get lastPtyInputAt(): number | null;
|
|
299
313
|
/** Subscribe to relayed keystrokes. Returns an unsubscribe fn. */
|
|
300
314
|
onPtyInput(handler: (data: string) => void): () => void;
|
|
@@ -620,6 +634,10 @@ declare class Lifecycle {
|
|
|
620
634
|
* window every time it defers. */
|
|
621
635
|
startIdleTimer(overrideMs?: number): void;
|
|
622
636
|
cancelIdleTimer(): void;
|
|
637
|
+
/** Restart an ARMED idle timer with the full window; a no-op when none is
|
|
638
|
+
* armed. Human activity uses this, so a keystroke during a running turn
|
|
639
|
+
* (timer cancelled) can never arm one. */
|
|
640
|
+
touchIdleTimer(): void;
|
|
623
641
|
/** Start (or restart) the dormant timer.
|
|
624
642
|
* @param overrideMs Optional custom delay in ms. When provided, the timer
|
|
625
643
|
* fires after exactly that delay instead of `dormantTimeoutMs`. SessionRunner
|
|
@@ -1294,12 +1312,16 @@ declare function removeWorktree(projectDir: string, taskId: string): Promise<voi
|
|
|
1294
1312
|
interface ConveyorConfig {
|
|
1295
1313
|
startCommand?: string;
|
|
1296
1314
|
}
|
|
1315
|
+
type PortAttributes = {
|
|
1316
|
+
label?: string;
|
|
1317
|
+
visibility?: "public" | "private";
|
|
1318
|
+
hidden?: boolean;
|
|
1319
|
+
};
|
|
1297
1320
|
interface ForwardPortsResult {
|
|
1298
1321
|
ports: number[];
|
|
1299
|
-
attributes: Record<string,
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
}>;
|
|
1322
|
+
attributes: Record<string, PortAttributes>;
|
|
1323
|
+
/** `customizations.conveyor.previewLinks`, sanitized. */
|
|
1324
|
+
links: SessionPreviewLink[];
|
|
1303
1325
|
}
|
|
1304
1326
|
declare function loadForwardPorts(workspaceDir: string): Promise<ForwardPortsResult>;
|
|
1305
1327
|
/** Load config from env vars (project-level settings injected via bootstrap).
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HYXAXQHN.js";
|
|
5
5
|
import "./chunk-37J5MMQT.js";
|
|
6
6
|
import "./chunk-WCJ6HRVU.js";
|
|
7
7
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-7KG7CDVU.js";
|
|
21
21
|
import "./chunk-Q4FQOJ7D.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2BHXJ37W.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentConnection,
|
|
6
6
|
CodespacePortVisibility,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createServiceLogger,
|
|
12
12
|
ensureOnTaskBranch,
|
|
13
13
|
loadConveyorConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-7KG7CDVU.js";
|
|
15
15
|
import "./chunk-Q4FQOJ7D.js";
|
|
16
16
|
import "./chunk-W4LZ7R6Z.js";
|
|
17
17
|
import "./chunk-IA45XHOA.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.30",
|
|
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",
|
|
@@ -151,6 +151,17 @@ labels. Each tag carries a `description` (≤255 — the summary), an `overview`
|
|
|
151
151
|
work. The loop idles on `conveyor-wait`, a CLI in `@rallycry/conveyor-mcp`
|
|
152
152
|
that blocks until a card becomes claimable, so an idle loop wakes on a board
|
|
153
153
|
event instead of a timer.
|
|
154
|
+
- **From an app, with a project token (no MCP, no user)**: an external service
|
|
155
|
+
holding an incident-scoped project token files AND starts a card in one
|
|
156
|
+
call — `POST /api/incidents/report` with `type: "task"`, a `plan`, and
|
|
157
|
+
`start: true` (optionally `tags: [<glossary names>]`; an unknown name is a
|
|
158
|
+
400). The build starts through the same path as `start_task`, as the token's
|
|
159
|
+
owner, and the response carries `{ taskId, slug, url, status, buildStarted,
|
|
160
|
+
buildError? }`. A failed start still returns 201 with `buildStarted: false`
|
|
161
|
+
and leaves the card for a human — do not retry the report, that files a
|
|
162
|
+
second card. Poll `GET /api/incidents/tasks/<taskId>` with the same token for
|
|
163
|
+
`{ status, githubPRUrl, githubPRNumber, agentRunnerStatus }`. `start` is
|
|
164
|
+
rate-limited to 30 per token per 10 minutes.
|
|
154
165
|
- **Reserved-branch trap**: a card with an assigned agent may have a reserved
|
|
155
166
|
`githubBranch`. Check `get_task` before pushing: if set, push to THAT
|
|
156
167
|
branch; a PR from any other branch gets auto-closed and unlinked.
|