@rallycry/conveyor-agent 10.2.1 → 10.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-7TQO4ZF4.js +60 -0
- package/dist/chunk-7TQO4ZF4.js.map +1 -0
- package/dist/{chunk-YGLTLUGA.js → chunk-DEMRCBJN.js} +994 -862
- package/dist/chunk-DEMRCBJN.js.map +1 -0
- package/dist/cli.js +319 -49
- package/dist/cli.js.map +1 -1
- package/dist/heartbeat-worker.d.ts +2 -0
- package/dist/heartbeat-worker.js +47 -0
- package/dist/heartbeat-worker.js.map +1 -0
- package/dist/index.d.ts +74 -42
- package/dist/index.js +19 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-YGLTLUGA.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _project_shared from '@project/shared';
|
|
2
|
-
import { RunnerMode, AgentSessionServiceMethods, AgentMode, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus, BootstrapBundle } from '@project/shared';
|
|
2
|
+
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus, BootstrapBundle } from '@project/shared';
|
|
3
3
|
export * from '@project/shared';
|
|
4
4
|
import { ChildProcess } from 'node:child_process';
|
|
5
5
|
|
|
@@ -32,6 +32,20 @@ interface ApiKeyUpdateData {
|
|
|
32
32
|
apiKey: string;
|
|
33
33
|
isSubscription?: boolean;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Server push asking the builder pod to spawn a same-pod review child: a
|
|
37
|
+
* second conveyor-agent process bound to a fresh review WorkspaceSession
|
|
38
|
+
* (sessionId + sessionJwt). No taskId on the wire — the child inherits the
|
|
39
|
+
* parent's CONVEYOR_TASK_ID and the server derives it from the JWT (SEC-9).
|
|
40
|
+
*/
|
|
41
|
+
interface SpawnReviewData {
|
|
42
|
+
sessionId: string;
|
|
43
|
+
sessionJwt: string;
|
|
44
|
+
mode: "code-review";
|
|
45
|
+
branch?: string | null;
|
|
46
|
+
prNumber?: number | null;
|
|
47
|
+
checkoutRef?: string | null;
|
|
48
|
+
}
|
|
35
49
|
declare class AgentConnection {
|
|
36
50
|
private socket;
|
|
37
51
|
private readonly config;
|
|
@@ -59,6 +73,8 @@ declare class AgentConnection {
|
|
|
59
73
|
private finalizeSnapshotCallback;
|
|
60
74
|
private runStartCommandCallback;
|
|
61
75
|
private earlyPullBranches;
|
|
76
|
+
private spawnReviewCallback;
|
|
77
|
+
private earlySpawnReviews;
|
|
62
78
|
private ptyInputCallback;
|
|
63
79
|
private ptyResizeCallback;
|
|
64
80
|
constructor(config: AgentConnectionConfig);
|
|
@@ -108,6 +124,14 @@ declare class AgentConnection {
|
|
|
108
124
|
onPullBranch(callback: (data: {
|
|
109
125
|
branch: string;
|
|
110
126
|
}) => void): void;
|
|
127
|
+
onSpawnReview(callback: (data: SpawnReviewData) => void): void;
|
|
128
|
+
/**
|
|
129
|
+
* Report that a same-pod review child failed to spawn (fire-and-forget).
|
|
130
|
+
* The server Ends the orphaned review session and falls back to a dedicated
|
|
131
|
+
* review pod. sessionId is OUR (builder) session — the SEC-9 guard runs on
|
|
132
|
+
* it; the review session is identified separately.
|
|
133
|
+
*/
|
|
134
|
+
reportReviewSpawnFailure(reviewSessionId: string, error?: string): void;
|
|
111
135
|
onFinalizeSnapshot(callback: () => void): void;
|
|
112
136
|
onRunStartCommand(callback: () => void): void;
|
|
113
137
|
/**
|
|
@@ -119,6 +143,12 @@ declare class AgentConnection {
|
|
|
119
143
|
cols: number;
|
|
120
144
|
rows: number;
|
|
121
145
|
}): void;
|
|
146
|
+
/**
|
|
147
|
+
* Forward one compact chat-proxy event derived from the transcript JSONL to
|
|
148
|
+
* the relay (fire-and-forget). Feeds the experimental chat PTY proxy ring.
|
|
149
|
+
* Old servers that don't know the method reject harmlessly.
|
|
150
|
+
*/
|
|
151
|
+
sendPtyChatEvent(event: PtyChatEventPayload): void;
|
|
122
152
|
/**
|
|
123
153
|
* Report that the interactive CLI process for this session has died and no
|
|
124
154
|
* respawn is imminent (fire-and-forget). The server clears the scrollback
|
|
@@ -140,7 +170,10 @@ declare class AgentConnection {
|
|
|
140
170
|
duplicate: true;
|
|
141
171
|
matchedMessagePreview: string;
|
|
142
172
|
};
|
|
143
|
-
sendHeartbeat(): void;
|
|
173
|
+
sendHeartbeat(loopLagMs?: number): void;
|
|
174
|
+
private heartbeatWorker;
|
|
175
|
+
startHeartbeatWorker(sharedBuffer: SharedArrayBuffer, intervalMs?: number): void;
|
|
176
|
+
stopHeartbeatWorker(): void;
|
|
144
177
|
emitModeChanged(agentMode?: AgentMode | null): void;
|
|
145
178
|
updateTaskFields(fields: {
|
|
146
179
|
plan?: string;
|
|
@@ -385,8 +418,14 @@ declare class SessionRunner {
|
|
|
385
418
|
private agentSpokeThisTurn;
|
|
386
419
|
/** Guards overlapping runs of the periodic git flush. */
|
|
387
420
|
private periodicFlushInFlight;
|
|
421
|
+
/** Consecutive flush ticks skipped because a heavy gate was running. */
|
|
422
|
+
private gateSkipCount;
|
|
423
|
+
/** Max consecutive gate-deferred ticks (~2 min each) before flushing anyway. */
|
|
424
|
+
private static readonly MAX_GATE_SKIPS;
|
|
388
425
|
/** Runtime preview-port poller (v3 dynamic port discovery). */
|
|
389
426
|
private portDiscovery;
|
|
427
|
+
/** Main event-loop lag measurement, shared with the heartbeat worker. */
|
|
428
|
+
private readonly loopLag;
|
|
390
429
|
constructor(config: SessionRunnerConfig, callbacks: SessionRunnerCallbacks);
|
|
391
430
|
get state(): AgentRunnerStatus;
|
|
392
431
|
get sessionId(): string;
|
|
@@ -484,16 +523,27 @@ declare class SessionRunner {
|
|
|
484
523
|
stop(): void;
|
|
485
524
|
softStop(): void;
|
|
486
525
|
private static readonly MAX_STUCK_NUDGES;
|
|
487
|
-
/** The
|
|
488
|
-
*
|
|
526
|
+
/** The build-phase nudge (In Progress, no PR). Offers BOTH escape routes so
|
|
527
|
+
* the agent never just goes idle. */
|
|
489
528
|
private static readonly STUCK_PROMPT;
|
|
529
|
+
/** The planning-phase nudge (still Planning, identification/plan unfinished).
|
|
530
|
+
* Same two-escape-route shape, aimed at finishing discovery instead of a PR. */
|
|
531
|
+
private static readonly STUCK_PROMPT_PLANNING;
|
|
490
532
|
/**
|
|
491
|
-
*
|
|
492
|
-
* post-turn, so the TUI is no longer
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
533
|
+
* Classify how an auto task is "stuck" after its turn ended (call sites run
|
|
534
|
+
* post-turn, so the TUI is no longer working), or null if it isn't. Shared
|
|
535
|
+
* guards: only auto, not rate-limited, under the nudge cap, and the agent
|
|
536
|
+
* stayed silent this turn — if it posted to chat (explained itself / asked for
|
|
537
|
+
* help) it's waiting on a human, not silently stuck.
|
|
538
|
+
*
|
|
539
|
+
* - "pr": In Progress with no open PR (build finished without shipping).
|
|
540
|
+
* - "planning": still Planning without (identified + saved plan). A fresh auto
|
|
541
|
+
* agent that finishes its plan turn WITHOUT ExitPlanMode would otherwise idle
|
|
542
|
+
* → clean-exit → its session Ends → the workspace is reaped "agent_gone"
|
|
543
|
+
* before a plan ever landed. Nudging (and, on exhaustion, going dormant)
|
|
544
|
+
* keeps the session Active so the pod is never prematurely slept.
|
|
496
545
|
*/
|
|
546
|
+
private autoStuckKind;
|
|
497
547
|
private isAutoStuck;
|
|
498
548
|
/**
|
|
499
549
|
* Stop driving the agent and route the core loop into dormant idle —
|
|
@@ -504,15 +554,18 @@ declare class SessionRunner {
|
|
|
504
554
|
*/
|
|
505
555
|
private enterDormantForHumanTakeover;
|
|
506
556
|
private refreshTaskContext;
|
|
557
|
+
/** Prompt + chat-message label for a stuck-nudge of the given kind. */
|
|
558
|
+
private stuckNudgeContent;
|
|
507
559
|
private maybeHandleStuckAuto;
|
|
508
560
|
private buildFullContext;
|
|
509
561
|
private createQueryBridge;
|
|
510
562
|
private wireConnectionCallbacks;
|
|
511
563
|
/** Eager finalize snapshot triggered by the reconciler's sleep signal
|
|
512
|
-
* (session:finalizeSnapshot). Runs
|
|
564
|
+
* (session:finalizeSnapshot). Runs a full workspace capture and uploads it
|
|
513
565
|
* NOW so the sleep confirms on this snapshot instead of waiting for the
|
|
514
|
-
* ~2min periodic flush
|
|
515
|
-
*
|
|
566
|
+
* ~2min periodic flush. Sidecar DB state is intentionally NOT captured — it
|
|
567
|
+
* is no longer durable across sleep/wake (#2623); only the agent's
|
|
568
|
+
* /workspace rides the snapshot. Best-effort: on failure the
|
|
516
569
|
* periodic/shutdown path stays the fallback. No-op on non-v3 pods. */
|
|
517
570
|
private finalizeSnapshotNow;
|
|
518
571
|
/** Proactively refresh the GitHub token before the 1-hour expiry. */
|
|
@@ -545,29 +598,16 @@ interface AgentRunnerCallbacks {
|
|
|
545
598
|
onStatusChange: (status: string) => void | Promise<void>;
|
|
546
599
|
}
|
|
547
600
|
|
|
548
|
-
declare class PlanSync {
|
|
549
|
-
private planFileSnapshot;
|
|
550
|
-
private lockedPlanFile;
|
|
551
|
-
private workspaceDir;
|
|
552
|
-
private connection;
|
|
553
|
-
constructor(workspaceDir: string, connection: AgentConnection);
|
|
554
|
-
updateWorkspaceDir(workspaceDir: string): void;
|
|
555
|
-
private getPlanDirs;
|
|
556
|
-
snapshotPlanFiles(): void;
|
|
557
|
-
private findNewestPlanFile;
|
|
558
|
-
syncPlanFile(): Promise<void>;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
601
|
/** Returns true if the working tree has uncommitted changes (staged or unstaged). */
|
|
562
|
-
declare function hasUncommittedChanges(cwd: string): boolean
|
|
602
|
+
declare function hasUncommittedChanges(cwd: string): Promise<boolean>;
|
|
563
603
|
/** Returns the current branch name, or null if on detached HEAD. */
|
|
564
|
-
declare function getCurrentBranch(cwd: string): string | null
|
|
604
|
+
declare function getCurrentBranch(cwd: string): Promise<string | null>;
|
|
565
605
|
/** Returns true if there are committed changes that haven't been pushed to origin. */
|
|
566
|
-
declare function hasUnpushedCommits(cwd: string): boolean
|
|
606
|
+
declare function hasUnpushedCommits(cwd: string): Promise<boolean>;
|
|
567
607
|
/** Stage all changes and create a commit. Returns the commit hash if successful. */
|
|
568
|
-
declare function stageAndCommit(cwd: string, message: string): string | null
|
|
608
|
+
declare function stageAndCommit(cwd: string, message: string): Promise<string | null>;
|
|
569
609
|
/** Update the git remote URL with a fresh token. */
|
|
570
|
-
declare function updateRemoteToken(cwd: string, token: string): void
|
|
610
|
+
declare function updateRemoteToken(cwd: string, token: string): Promise<void>;
|
|
571
611
|
/** Best-effort flush of pending work: pushes any real (intentional) commits to
|
|
572
612
|
* the task branch, and captures uncommitted changes as a WIP snapshot on the
|
|
573
613
|
* `conveyor-wip/<branch>` ref — never as commits on the branch itself. When
|
|
@@ -584,18 +624,18 @@ declare function flushPendingChanges(cwd: string, opts?: {
|
|
|
584
624
|
* attempting the push. On auth failure, refreshes again and retries. */
|
|
585
625
|
declare function pushToOrigin(cwd: string, refreshToken?: () => Promise<string | undefined>, skipVerify?: boolean): Promise<boolean>;
|
|
586
626
|
|
|
587
|
-
declare function ensureWorktree(projectDir: string, taskId: string, branch?: string): string
|
|
627
|
+
declare function ensureWorktree(projectDir: string, taskId: string, branch?: string): Promise<string>;
|
|
588
628
|
/**
|
|
589
629
|
* Detach any task worktree that has `branch` checked out, releasing the branch lock
|
|
590
630
|
* so the main project directory can check it out. Only touches worktrees inside
|
|
591
631
|
* `.worktrees/` — never the main project directory.
|
|
592
632
|
*/
|
|
593
|
-
declare function detachWorktreeBranch(projectDir: string, branch: string): void
|
|
633
|
+
declare function detachWorktreeBranch(projectDir: string, branch: string): Promise<void>;
|
|
594
634
|
/**
|
|
595
635
|
* Force-remove is intentional: this runs after task completion or explicit stop.
|
|
596
636
|
* Any uncommitted changes at this point are scratch artifacts, not valuable work.
|
|
597
637
|
*/
|
|
598
|
-
declare function removeWorktree(projectDir: string, taskId: string): void
|
|
638
|
+
declare function removeWorktree(projectDir: string, taskId: string): Promise<void>;
|
|
599
639
|
|
|
600
640
|
interface CaptureContext {
|
|
601
641
|
cwd: string;
|
|
@@ -603,9 +643,6 @@ interface CaptureContext {
|
|
|
603
643
|
snapshotUploadUrl?: string;
|
|
604
644
|
refreshToken?: () => Promise<string | undefined>;
|
|
605
645
|
wipMessage?: string;
|
|
606
|
-
/** Path to a pre-built postgres dump OUTSIDE cwd (see pg-dump.ts) that the
|
|
607
|
-
* GCS tar should carry under its fixed in-archive name. */
|
|
608
|
-
pgDumpPath?: string;
|
|
609
646
|
/** Force the GCS PUT even when the tar is empty (sizeBytes === 0). Used by the
|
|
610
647
|
* sleep FINALIZE path so `snapshotAt` is stamped and the reconciler's
|
|
611
648
|
* teardown confirm lands immediately — an empty workspace is a valid (empty)
|
|
@@ -620,8 +657,6 @@ interface SnapshotArtifact {
|
|
|
620
657
|
sizeBytes: number;
|
|
621
658
|
gcsUploaded: boolean;
|
|
622
659
|
wipPushed: boolean;
|
|
623
|
-
/** Whether this capture bundled a postgres dump into the GCS artifact. */
|
|
624
|
-
pgDumped: boolean;
|
|
625
660
|
}
|
|
626
661
|
interface RestoreResult {
|
|
627
662
|
source: "gcs" | "wip" | "clean";
|
|
@@ -632,7 +667,6 @@ interface GcsUploadResult {
|
|
|
632
667
|
fileCount: number;
|
|
633
668
|
deletionCount: number;
|
|
634
669
|
sizeBytes: number;
|
|
635
|
-
pgDumped: boolean;
|
|
636
670
|
}
|
|
637
671
|
/**
|
|
638
672
|
* The GCS sink on its own: assemble the snapshot tar (the single capture
|
|
@@ -643,7 +677,6 @@ interface GcsUploadResult {
|
|
|
643
677
|
* after any earlier in-flight one has settled.
|
|
644
678
|
*/
|
|
645
679
|
declare function uploadSnapshotToGcs(cwd: string, uploadUrl: string | undefined, opts?: {
|
|
646
|
-
pgDumpPath?: string;
|
|
647
680
|
forceUpload?: boolean;
|
|
648
681
|
}): Promise<GcsUploadResult>;
|
|
649
682
|
declare function captureSnapshot(ctx: CaptureContext): Promise<SnapshotArtifact>;
|
|
@@ -668,7 +701,6 @@ declare function restoreOnBoot(bundle: Pick<BootstrapBundle, "snapshotUrl"> & {
|
|
|
668
701
|
interface ConveyorConfig {
|
|
669
702
|
setupCommand?: string;
|
|
670
703
|
startCommand?: string;
|
|
671
|
-
previewPort?: number;
|
|
672
704
|
}
|
|
673
705
|
interface ForwardPortsResult {
|
|
674
706
|
ports: number[];
|
|
@@ -688,4 +720,4 @@ declare function runStartCommand(cmd: string, cwd: string, onOutput: (stream: "s
|
|
|
688
720
|
/** Fetch full git history if the repo was cloned with --depth=1. */
|
|
689
721
|
declare function unshallowRepo(workspaceDir: string): void;
|
|
690
722
|
|
|
691
|
-
export { AgentConnection, type AgentConnectionConfig, type AgentRunnerCallbacks, type AgentRunnerConfig, type ApiKeyUpdateData, type CaptureContext, type ConveyorConfig, type GcsUploadResult, type IncomingMessage, type LifecycleCallbacks, type LifecycleConfig, type ModeAction, type ModeTaskContext,
|
|
723
|
+
export { AgentConnection, type AgentConnectionConfig, type AgentRunnerCallbacks, type AgentRunnerConfig, type ApiKeyUpdateData, type CaptureContext, type ConveyorConfig, type GcsUploadResult, type IncomingMessage, type LifecycleCallbacks, type LifecycleConfig, type ModeAction, type ModeTaskContext, type RestoreResult, SessionRunner, type SessionRunnerCallbacks, type SessionRunnerConfig, type SetModeData, type SnapshotArtifact, captureSnapshot, detachWorktreeBranch, ensureWorktree, finalizeForSleep, flushPendingChanges, getCurrentBranch, hasUncommittedChanges, hasUnpushedCommits, loadConveyorConfig, loadForwardPorts, pushToOrigin, removeWorktree, restoreOnBoot, runAuthTokenCommand, runSetupCommand, runStartCommand, stageAndCommit, startPeriodic as startWorkPreservation, stop as stopWorkPreservation, unshallowRepo, updateRemoteToken, uploadSnapshotToGcs };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentConnection,
|
|
3
|
-
PlanSync,
|
|
4
3
|
SessionRunner,
|
|
5
4
|
captureSnapshot,
|
|
6
5
|
finalizeForSleep,
|
|
@@ -21,46 +20,45 @@ import {
|
|
|
21
20
|
unshallowRepo,
|
|
22
21
|
updateRemoteToken,
|
|
23
22
|
uploadSnapshotToGcs
|
|
24
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-DEMRCBJN.js";
|
|
24
|
+
import "./chunk-7TQO4ZF4.js";
|
|
25
25
|
|
|
26
26
|
// src/runner/worktree.ts
|
|
27
|
-
import {
|
|
27
|
+
import { execFile } from "child_process";
|
|
28
28
|
import { existsSync } from "fs";
|
|
29
29
|
import { join } from "path";
|
|
30
|
+
import { promisify } from "util";
|
|
31
|
+
var execFileAsync = promisify(execFile);
|
|
32
|
+
var GIT_TIMEOUT_MS = 6e4;
|
|
33
|
+
async function git(cwd, args) {
|
|
34
|
+
const { stdout } = await execFileAsync("git", args, { cwd, timeout: GIT_TIMEOUT_MS });
|
|
35
|
+
return stdout.toString();
|
|
36
|
+
}
|
|
30
37
|
var WORKTREE_DIR = ".worktrees";
|
|
31
|
-
function ensureWorktree(projectDir, taskId, branch) {
|
|
38
|
+
async function ensureWorktree(projectDir, taskId, branch) {
|
|
32
39
|
if (projectDir.includes(`/${WORKTREE_DIR}/`)) {
|
|
33
40
|
return projectDir;
|
|
34
41
|
}
|
|
35
42
|
const worktreePath = join(projectDir, WORKTREE_DIR, taskId);
|
|
36
43
|
if (existsSync(worktreePath)) {
|
|
37
44
|
if (branch) {
|
|
38
|
-
if (hasUncommittedChanges(worktreePath)) {
|
|
45
|
+
if (await hasUncommittedChanges(worktreePath)) {
|
|
39
46
|
return worktreePath;
|
|
40
47
|
}
|
|
41
48
|
try {
|
|
42
|
-
|
|
43
|
-
cwd: worktreePath,
|
|
44
|
-
stdio: "ignore"
|
|
45
|
-
});
|
|
49
|
+
await git(worktreePath, ["checkout", "--detach", `origin/${branch}`]);
|
|
46
50
|
} catch {
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
return worktreePath;
|
|
50
54
|
}
|
|
51
55
|
const ref = branch ? `origin/${branch}` : "HEAD";
|
|
52
|
-
|
|
53
|
-
cwd: projectDir,
|
|
54
|
-
stdio: "ignore"
|
|
55
|
-
});
|
|
56
|
+
await git(projectDir, ["worktree", "add", "--detach", worktreePath, ref]);
|
|
56
57
|
return worktreePath;
|
|
57
58
|
}
|
|
58
|
-
function detachWorktreeBranch(projectDir, branch) {
|
|
59
|
+
async function detachWorktreeBranch(projectDir, branch) {
|
|
59
60
|
try {
|
|
60
|
-
const output =
|
|
61
|
-
cwd: projectDir,
|
|
62
|
-
encoding: "utf-8"
|
|
63
|
-
});
|
|
61
|
+
const output = await git(projectDir, ["worktree", "list", "--porcelain"]);
|
|
64
62
|
const entries = output.split("\n\n");
|
|
65
63
|
for (const entry of entries) {
|
|
66
64
|
const lines = entry.trim().split("\n");
|
|
@@ -70,27 +68,23 @@ function detachWorktreeBranch(projectDir, branch) {
|
|
|
70
68
|
const worktreePath = worktreeLine.replace("worktree ", "");
|
|
71
69
|
if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;
|
|
72
70
|
try {
|
|
73
|
-
|
|
71
|
+
await git(worktreePath, ["checkout", "--detach"]);
|
|
74
72
|
} catch {
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
} catch {
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
|
-
function removeWorktree(projectDir, taskId) {
|
|
78
|
+
async function removeWorktree(projectDir, taskId) {
|
|
81
79
|
const worktreePath = join(projectDir, WORKTREE_DIR, taskId);
|
|
82
80
|
if (!existsSync(worktreePath)) return;
|
|
83
81
|
try {
|
|
84
|
-
|
|
85
|
-
cwd: projectDir,
|
|
86
|
-
stdio: "ignore"
|
|
87
|
-
});
|
|
82
|
+
await git(projectDir, ["worktree", "remove", worktreePath, "--force"]);
|
|
88
83
|
} catch {
|
|
89
84
|
}
|
|
90
85
|
}
|
|
91
86
|
export {
|
|
92
87
|
AgentConnection,
|
|
93
|
-
PlanSync,
|
|
94
88
|
SessionRunner,
|
|
95
89
|
captureSnapshot,
|
|
96
90
|
detachWorktreeBranch,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner/worktree.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/runner/worktree.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { hasUncommittedChanges } from \"./git-utils.js\";\n\n// Async git (execFile, no shell) — synchronous child processes freeze the\n// agent's event loop and starve the socket heartbeat; see git-utils.ts.\nconst execFileAsync = promisify(execFile);\nconst GIT_TIMEOUT_MS = 60_000;\n\nasync function git(cwd: string, args: string[]): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd, timeout: GIT_TIMEOUT_MS });\n return stdout.toString();\n}\n\nconst WORKTREE_DIR = \".worktrees\";\n\nexport async function ensureWorktree(\n projectDir: string,\n taskId: string,\n branch?: string,\n): Promise<string> {\n if (projectDir.includes(`/${WORKTREE_DIR}/`)) {\n return projectDir;\n }\n\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n\n if (existsSync(worktreePath)) {\n if (branch) {\n if (await hasUncommittedChanges(worktreePath)) {\n return worktreePath;\n }\n try {\n await git(worktreePath, [\"checkout\", \"--detach\", `origin/${branch}`]);\n } catch {\n /* branch doesn't exist on remote yet */\n }\n }\n return worktreePath;\n }\n\n const ref = branch ? `origin/${branch}` : \"HEAD\";\n await git(projectDir, [\"worktree\", \"add\", \"--detach\", worktreePath, ref]);\n\n return worktreePath;\n}\n\n/**\n * Detach any task worktree that has `branch` checked out, releasing the branch lock\n * so the main project directory can check it out. Only touches worktrees inside\n * `.worktrees/` — never the main project directory.\n */\nexport async function detachWorktreeBranch(projectDir: string, branch: string): Promise<void> {\n try {\n const output = await git(projectDir, [\"worktree\", \"list\", \"--porcelain\"]);\n const entries = output.split(\"\\n\\n\");\n for (const entry of entries) {\n const lines = entry.trim().split(\"\\n\");\n const worktreeLine = lines.find((l) => l.startsWith(\"worktree \"));\n const branchLine = lines.find((l) => l.startsWith(\"branch \"));\n if (!worktreeLine || branchLine !== `branch refs/heads/${branch}`) continue;\n\n const worktreePath = worktreeLine.replace(\"worktree \", \"\");\n if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;\n\n try {\n await git(worktreePath, [\"checkout\", \"--detach\"]);\n } catch {\n /* best effort */\n }\n }\n } catch {\n /* best effort */\n }\n}\n\n/**\n * Force-remove is intentional: this runs after task completion or explicit stop.\n * Any uncommitted changes at this point are scratch artifacts, not valuable work.\n */\nexport async function removeWorktree(projectDir: string, taskId: string): Promise<void> {\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n if (!existsSync(worktreePath)) return;\n try {\n await git(projectDir, [\"worktree\", \"remove\", worktreePath, \"--force\"]);\n } catch {\n /* best effort */\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAK1B,IAAM,gBAAgB,UAAU,QAAQ;AACxC,IAAM,iBAAiB;AAEvB,eAAe,IAAI,KAAa,MAAiC;AAC/D,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,MAAM,EAAE,KAAK,SAAS,eAAe,CAAC;AACpF,SAAO,OAAO,SAAS;AACzB;AAEA,IAAM,eAAe;AAErB,eAAsB,eACpB,YACA,QACA,QACiB;AACjB,MAAI,WAAW,SAAS,IAAI,YAAY,GAAG,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,KAAK,YAAY,cAAc,MAAM;AAE1D,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI,QAAQ;AACV,UAAI,MAAM,sBAAsB,YAAY,GAAG;AAC7C,eAAO;AAAA,MACT;AACA,UAAI;AACF,cAAM,IAAI,cAAc,CAAC,YAAY,YAAY,UAAU,MAAM,EAAE,CAAC;AAAA,MACtE,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,SAAS,UAAU,MAAM,KAAK;AAC1C,QAAM,IAAI,YAAY,CAAC,YAAY,OAAO,YAAY,cAAc,GAAG,CAAC;AAExE,SAAO;AACT;AAOA,eAAsB,qBAAqB,YAAoB,QAA+B;AAC5F,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,YAAY,CAAC,YAAY,QAAQ,aAAa,CAAC;AACxE,UAAM,UAAU,OAAO,MAAM,MAAM;AACnC,eAAW,SAAS,SAAS;AAC3B,YAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,IAAI;AACrC,YAAM,eAAe,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,WAAW,CAAC;AAChE,YAAM,aAAa,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAC5D,UAAI,CAAC,gBAAgB,eAAe,qBAAqB,MAAM,GAAI;AAEnE,YAAM,eAAe,aAAa,QAAQ,aAAa,EAAE;AACzD,UAAI,CAAC,aAAa,SAAS,IAAI,YAAY,GAAG,EAAG;AAEjD,UAAI;AACF,cAAM,IAAI,cAAc,CAAC,YAAY,UAAU,CAAC;AAAA,MAClD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAMA,eAAsB,eAAe,YAAoB,QAA+B;AACtF,QAAM,eAAe,KAAK,YAAY,cAAc,MAAM;AAC1D,MAAI,CAAC,WAAW,YAAY,EAAG;AAC/B,MAAI;AACF,UAAM,IAAI,YAAY,CAAC,YAAY,UAAU,cAAc,SAAS,CAAC;AAAA,EACvE,QAAQ;AAAA,EAER;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "10.2.
|
|
3
|
+
"version": "10.2.4",
|
|
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",
|