@rallycry/conveyor-agent 7.3.4 → 7.3.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-TP5WEBQE.js → chunk-KO2YQJEV.js} +147 -15
- package/dist/chunk-KO2YQJEV.js.map +1 -0
- package/dist/cli.js +20 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +33 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-TP5WEBQE.js.map +0 -1
|
@@ -641,8 +641,10 @@ var ModeController = class {
|
|
|
641
641
|
// src/runner/lifecycle.ts
|
|
642
642
|
var DEFAULT_LIFECYCLE_CONFIG = {
|
|
643
643
|
idleTimeoutMs: 30 * 60 * 1e3,
|
|
644
|
+
dormantTimeoutMs: 60 * 60 * 1e3,
|
|
644
645
|
heartbeatIntervalMs: 3e4,
|
|
645
|
-
tokenRefreshIntervalMs: 45 * 60 * 1e3
|
|
646
|
+
tokenRefreshIntervalMs: 45 * 60 * 1e3,
|
|
647
|
+
gitFlushIntervalMs: 2 * 60 * 1e3
|
|
646
648
|
};
|
|
647
649
|
var Lifecycle = class {
|
|
648
650
|
config;
|
|
@@ -651,6 +653,8 @@ var Lifecycle = class {
|
|
|
651
653
|
tokenRefreshTimer = null;
|
|
652
654
|
idleTimer = null;
|
|
653
655
|
idleCheckInterval = null;
|
|
656
|
+
dormantTimer = null;
|
|
657
|
+
gitFlushTimer = null;
|
|
654
658
|
constructor(config, callbacks) {
|
|
655
659
|
this.config = config;
|
|
656
660
|
this.callbacks = callbacks;
|
|
@@ -682,6 +686,19 @@ var Lifecycle = class {
|
|
|
682
686
|
this.tokenRefreshTimer = null;
|
|
683
687
|
}
|
|
684
688
|
}
|
|
689
|
+
// ── Periodic git flush ────────────────────────────────────────────
|
|
690
|
+
startGitFlush() {
|
|
691
|
+
this.stopGitFlush();
|
|
692
|
+
this.gitFlushTimer = setInterval(() => {
|
|
693
|
+
this.callbacks.onGitFlush();
|
|
694
|
+
}, this.config.gitFlushIntervalMs);
|
|
695
|
+
}
|
|
696
|
+
stopGitFlush() {
|
|
697
|
+
if (this.gitFlushTimer) {
|
|
698
|
+
clearInterval(this.gitFlushTimer);
|
|
699
|
+
this.gitFlushTimer = null;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
685
702
|
// ── Idle timer ─────────────────────────────────────────────────────
|
|
686
703
|
startIdleTimer() {
|
|
687
704
|
this.clearIdleTimers();
|
|
@@ -692,11 +709,26 @@ var Lifecycle = class {
|
|
|
692
709
|
cancelIdleTimer() {
|
|
693
710
|
this.clearIdleTimers();
|
|
694
711
|
}
|
|
712
|
+
// ── Dormant timer ──────────────────────────────────────────────────
|
|
713
|
+
startDormantTimer() {
|
|
714
|
+
this.cancelDormantTimer();
|
|
715
|
+
this.dormantTimer = setTimeout(() => {
|
|
716
|
+
this.callbacks.onDormantTimeout();
|
|
717
|
+
}, this.config.dormantTimeoutMs);
|
|
718
|
+
}
|
|
719
|
+
cancelDormantTimer() {
|
|
720
|
+
if (this.dormantTimer) {
|
|
721
|
+
clearTimeout(this.dormantTimer);
|
|
722
|
+
this.dormantTimer = null;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
695
725
|
// ── Cleanup ────────────────────────────────────────────────────────
|
|
696
726
|
destroy() {
|
|
697
727
|
this.stopHeartbeat();
|
|
698
728
|
this.stopTokenRefresh();
|
|
729
|
+
this.stopGitFlush();
|
|
699
730
|
this.clearIdleTimers();
|
|
731
|
+
this.cancelDormantTimer();
|
|
700
732
|
}
|
|
701
733
|
// ── Private ────────────────────────────────────────────────────────
|
|
702
734
|
clearIdleTimers() {
|
|
@@ -1773,6 +1805,20 @@ function formatRepoRefs(repoRefs) {
|
|
|
1773
1805
|
}
|
|
1774
1806
|
return parts;
|
|
1775
1807
|
}
|
|
1808
|
+
function formatReferenceProjects(referenceProjects) {
|
|
1809
|
+
const parts = [];
|
|
1810
|
+
parts.push(`
|
|
1811
|
+
## Reference Projects`);
|
|
1812
|
+
parts.push(
|
|
1813
|
+
`These sibling Conveyor projects have been shallow-cloned read-only into \`/workspaces/references/<slug>/\` for inspiration. You MAY grep/read them to compare approaches, but you MUST NOT modify them or commit anything from them into this task's repo.
|
|
1814
|
+
`
|
|
1815
|
+
);
|
|
1816
|
+
for (const ref of referenceProjects) {
|
|
1817
|
+
const repo = ref.githubRepoOwner && ref.githubRepoName ? ` (${ref.githubRepoOwner}/${ref.githubRepoName})` : "";
|
|
1818
|
+
parts.push(`- **${ref.name}**${repo} \u2014 \`/workspaces/references/${ref.slug}/\``);
|
|
1819
|
+
}
|
|
1820
|
+
return parts;
|
|
1821
|
+
}
|
|
1776
1822
|
function formatProjectObjectives(objectives) {
|
|
1777
1823
|
const parts = [];
|
|
1778
1824
|
parts.push(`
|
|
@@ -2686,6 +2732,9 @@ ${truncatePlanForPrompt(context.plan)}`);
|
|
|
2686
2732
|
if (context.repoRefs && context.repoRefs.length > 0) {
|
|
2687
2733
|
parts.push(...formatRepoRefs(context.repoRefs));
|
|
2688
2734
|
}
|
|
2735
|
+
if (context.referenceProjects && context.referenceProjects.length > 0) {
|
|
2736
|
+
parts.push(...formatReferenceProjects(context.referenceProjects));
|
|
2737
|
+
}
|
|
2689
2738
|
const tagSection = await resolveTaskTagContext(context, runnerMode);
|
|
2690
2739
|
if (tagSection) parts.push(tagSection);
|
|
2691
2740
|
if (runnerMode !== "task") {
|
|
@@ -6534,6 +6583,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
6534
6583
|
inputResolver = null;
|
|
6535
6584
|
pendingMessages = [];
|
|
6536
6585
|
prNudgeCount = 0;
|
|
6586
|
+
/** Guards overlapping runs of the periodic git flush. */
|
|
6587
|
+
periodicFlushInFlight = false;
|
|
6537
6588
|
constructor(config, callbacks) {
|
|
6538
6589
|
this.config = config;
|
|
6539
6590
|
this.callbacks = callbacks;
|
|
@@ -6552,7 +6603,17 @@ var SessionRunner = class _SessionRunner {
|
|
|
6552
6603
|
resolver(null);
|
|
6553
6604
|
}
|
|
6554
6605
|
},
|
|
6555
|
-
|
|
6606
|
+
onDormantTimeout: () => {
|
|
6607
|
+
process.stderr.write("[conveyor-agent] Dormant idle timeout reached, shutting down\n");
|
|
6608
|
+
this.stopped = true;
|
|
6609
|
+
if (this.inputResolver) {
|
|
6610
|
+
const resolver = this.inputResolver;
|
|
6611
|
+
this.inputResolver = null;
|
|
6612
|
+
resolver(null);
|
|
6613
|
+
}
|
|
6614
|
+
},
|
|
6615
|
+
onTokenRefresh: () => void this.refreshGithubToken(),
|
|
6616
|
+
onGitFlush: () => void this.periodicGitFlush()
|
|
6556
6617
|
});
|
|
6557
6618
|
}
|
|
6558
6619
|
get state() {
|
|
@@ -6578,6 +6639,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
6578
6639
|
this.wireConnectionCallbacks();
|
|
6579
6640
|
this.lifecycle.startHeartbeat();
|
|
6580
6641
|
this.lifecycle.startTokenRefresh();
|
|
6642
|
+
this.lifecycle.startGitFlush();
|
|
6581
6643
|
const { pendingMessages: serverMessages } = await this.connection.call("connectAgent", {
|
|
6582
6644
|
sessionId: this.sessionId
|
|
6583
6645
|
});
|
|
@@ -6672,9 +6734,15 @@ var SessionRunner = class _SessionRunner {
|
|
|
6672
6734
|
);
|
|
6673
6735
|
this.pendingMessages.length = 0;
|
|
6674
6736
|
if (this._state !== "idle") await this.setState("idle");
|
|
6737
|
+
this.lifecycle.startDormantTimer();
|
|
6675
6738
|
const dormantMsg = await this.waitForMessage();
|
|
6739
|
+
this.lifecycle.cancelDormantTimer();
|
|
6676
6740
|
if (!dormantMsg) break;
|
|
6677
|
-
|
|
6741
|
+
const contentPreview = dormantMsg.content.length > 80 ? `${dormantMsg.content.slice(0, 80)}...` : dormantMsg.content;
|
|
6742
|
+
process.stderr.write(
|
|
6743
|
+
`[conveyor-agent] Received message while dormant, resuming: userId=${dormantMsg.userId}, source=${dormantMsg.source || "unknown"}, content="${contentPreview.replace(/\n/g, "\\n")}"
|
|
6744
|
+
`
|
|
6745
|
+
);
|
|
6678
6746
|
this.completedThisTurn = false;
|
|
6679
6747
|
this.pendingMessages.unshift(dormantMsg);
|
|
6680
6748
|
continue;
|
|
@@ -6783,6 +6851,38 @@ var SessionRunner = class _SessionRunner {
|
|
|
6783
6851
|
}
|
|
6784
6852
|
}
|
|
6785
6853
|
// ── Stop / soft-stop ───────────────────────────────────────────────
|
|
6854
|
+
/** Periodic best-effort WIP commit + push during normal agent execution.
|
|
6855
|
+
* Covers ungraceful pod termination (OOMKilled, node crash/eviction) where
|
|
6856
|
+
* the preStop hook + SIGTERM flush don't get a chance to run. No-ops on a
|
|
6857
|
+
* clean tree. Guarded so two ticks can't overlap. Never throws. */
|
|
6858
|
+
async periodicGitFlush() {
|
|
6859
|
+
if (this.periodicFlushInFlight || this.stopped) return;
|
|
6860
|
+
this.periodicFlushInFlight = true;
|
|
6861
|
+
try {
|
|
6862
|
+
const result = await flushPendingChanges(this.config.workspaceDir, {
|
|
6863
|
+
wipMessage: "WIP: periodic auto-commit",
|
|
6864
|
+
refreshToken: async () => {
|
|
6865
|
+
try {
|
|
6866
|
+
const res = await this.connection.call("refreshGithubToken", {
|
|
6867
|
+
sessionId: this.connection.sessionId
|
|
6868
|
+
});
|
|
6869
|
+
return res.token;
|
|
6870
|
+
} catch {
|
|
6871
|
+
return void 0;
|
|
6872
|
+
}
|
|
6873
|
+
}
|
|
6874
|
+
});
|
|
6875
|
+
if (result.hadWork) {
|
|
6876
|
+
process.stderr.write(
|
|
6877
|
+
`[conveyor-agent] Periodic git flush: committed=${result.committed} pushed=${result.pushed}
|
|
6878
|
+
`
|
|
6879
|
+
);
|
|
6880
|
+
}
|
|
6881
|
+
} catch {
|
|
6882
|
+
} finally {
|
|
6883
|
+
this.periodicFlushInFlight = false;
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6786
6886
|
/** Best-effort WIP commit + push on shutdown so in-flight work isn't lost
|
|
6787
6887
|
* when a claudespace pod is killed. Must be called BEFORE stop() so the
|
|
6788
6888
|
* connection is still alive for token refresh. Never throws. */
|
|
@@ -6944,6 +7044,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
6944
7044
|
onEvent: (event) => {
|
|
6945
7045
|
if (event.type === "completed") {
|
|
6946
7046
|
this.completedThisTurn = true;
|
|
7047
|
+
void this.connection.sendHeartbeat();
|
|
6947
7048
|
}
|
|
6948
7049
|
return this.callbacks.onEvent(event);
|
|
6949
7050
|
}
|
|
@@ -7080,23 +7181,30 @@ var SessionRunner = class _SessionRunner {
|
|
|
7080
7181
|
get finalState() {
|
|
7081
7182
|
return this._finalState;
|
|
7082
7183
|
}
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
mode: this.mode.effectiveMode,
|
|
7086
|
-
runnerMode: this.config.runnerMode ?? "task",
|
|
7087
|
-
sessionId: this.sessionId,
|
|
7088
|
-
// Task context
|
|
7184
|
+
buildTaskContextSnapshot() {
|
|
7185
|
+
return {
|
|
7089
7186
|
isParentTask: this.fullContext?.isParentTask ?? false,
|
|
7090
7187
|
status: this.taskContext?.status,
|
|
7091
7188
|
taskTitle: this.fullContext?.title,
|
|
7092
7189
|
hasExistingPR: !!this.fullContext?.githubPRUrl,
|
|
7093
7190
|
hasExistingSession: !!this.fullContext?.claudeSessionId,
|
|
7094
7191
|
chatHistoryLength: this.fullContext?.chatHistory?.length ?? 0,
|
|
7095
|
-
tagIds: this.fullContext?.taskTagIds ?? []
|
|
7096
|
-
|
|
7192
|
+
tagIds: this.fullContext?.taskTagIds ?? []
|
|
7193
|
+
};
|
|
7194
|
+
}
|
|
7195
|
+
buildInitializationContext() {
|
|
7196
|
+
return {
|
|
7197
|
+
mode: this.mode.effectiveMode,
|
|
7198
|
+
runnerMode: this.config.runnerMode ?? "task",
|
|
7199
|
+
sessionId: this.sessionId,
|
|
7200
|
+
...this.buildTaskContextSnapshot(),
|
|
7097
7201
|
model: this.taskContext?.model,
|
|
7098
|
-
isAuto: this.config.isAuto ?? false
|
|
7202
|
+
isAuto: this.config.isAuto ?? false,
|
|
7203
|
+
subscriptionKeyLabel: process.env.CONVEYOR_SUBSCRIPTION_KEY_LABEL ?? null
|
|
7099
7204
|
};
|
|
7205
|
+
}
|
|
7206
|
+
logInitialization() {
|
|
7207
|
+
const context = this.buildInitializationContext();
|
|
7100
7208
|
process.stderr.write(`[conveyor-agent] Initialized: ${JSON.stringify(context)}
|
|
7101
7209
|
`);
|
|
7102
7210
|
this.connection.sendEvent({ type: "session_manifest", ...context });
|
|
@@ -8586,15 +8694,38 @@ var ProjectRunner = class {
|
|
|
8586
8694
|
import { readFile as readFile2 } from "fs/promises";
|
|
8587
8695
|
import { join as join5 } from "path";
|
|
8588
8696
|
var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
|
|
8697
|
+
var DEVCONTAINER_PORT_DENY_LIST = /* @__PURE__ */ new Set([5432, 6379, 9200]);
|
|
8589
8698
|
async function loadForwardPorts(workspaceDir) {
|
|
8590
8699
|
try {
|
|
8591
8700
|
const raw = await readFile2(join5(workspaceDir, DEVCONTAINER_PATH), "utf-8");
|
|
8592
8701
|
const parsed = JSON.parse(raw);
|
|
8593
|
-
|
|
8702
|
+
const ports = (parsed.forwardPorts ?? []).filter(
|
|
8703
|
+
(p) => typeof p === "number" && !DEVCONTAINER_PORT_DENY_LIST.has(p)
|
|
8704
|
+
);
|
|
8705
|
+
const attributes = {};
|
|
8706
|
+
for (const [key, value] of Object.entries(parsed.portsAttributes ?? {})) {
|
|
8707
|
+
if (!value || typeof value !== "object") continue;
|
|
8708
|
+
const entry = {};
|
|
8709
|
+
if (typeof value.label === "string") entry.label = value.label;
|
|
8710
|
+
if (value.visibility === "public" || value.visibility === "private") {
|
|
8711
|
+
entry.visibility = value.visibility;
|
|
8712
|
+
}
|
|
8713
|
+
attributes[key] = entry;
|
|
8714
|
+
}
|
|
8715
|
+
return { ports, attributes };
|
|
8594
8716
|
} catch {
|
|
8595
|
-
return [];
|
|
8717
|
+
return { ports: [], attributes: {} };
|
|
8596
8718
|
}
|
|
8597
8719
|
}
|
|
8720
|
+
function buildSessionPreviewPorts(result) {
|
|
8721
|
+
return result.ports.filter((port) => !DEVCONTAINER_PORT_DENY_LIST.has(port)).map((port) => {
|
|
8722
|
+
const attr = result.attributes[String(port)];
|
|
8723
|
+
const entry = { port };
|
|
8724
|
+
if (attr?.label) entry.label = attr.label;
|
|
8725
|
+
if (attr?.visibility) entry.visibility = attr.visibility;
|
|
8726
|
+
return entry;
|
|
8727
|
+
});
|
|
8728
|
+
}
|
|
8598
8729
|
function loadConveyorConfig() {
|
|
8599
8730
|
const envSetup = process.env.CONVEYOR_SETUP_COMMAND;
|
|
8600
8731
|
const envStart = process.env.CONVEYOR_START_COMMAND;
|
|
@@ -8634,6 +8765,7 @@ export {
|
|
|
8634
8765
|
runStartCommand,
|
|
8635
8766
|
ProjectRunner,
|
|
8636
8767
|
loadForwardPorts,
|
|
8768
|
+
buildSessionPreviewPorts,
|
|
8637
8769
|
loadConveyorConfig
|
|
8638
8770
|
};
|
|
8639
|
-
//# sourceMappingURL=chunk-
|
|
8771
|
+
//# sourceMappingURL=chunk-KO2YQJEV.js.map
|