@rallycry/conveyor-agent 10.13.71 → 11.0.0
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-PKHZUDAC.js → boot-PKQ2I66D.js} +56 -5
- package/dist/chunk-3F4ZZKCA.js +291 -0
- package/dist/{chunk-QU53HND5.js → chunk-GL2DIQEQ.js} +116 -47
- package/dist/chunk-JQVAWRVL.js +697 -0
- package/dist/chunk-N4WSUTGV.js +2564 -0
- package/dist/{chunk-7MMECTTJ.js → chunk-PEEGCZAR.js} +2257 -3701
- package/dist/{chunk-DOB2XE2I.js → chunk-UBDSLM44.js} +4 -2
- package/dist/{chunk-GJXAAPJ6.js → chunk-W4LZ7R6Z.js} +117 -400
- package/dist/chunk-XORJ6SII.js +46 -0
- package/dist/cli.js +304 -804
- package/dist/index.d.ts +170 -3
- package/dist/index.js +9 -5
- package/dist/serve-boot-YPAUKENG.js +225 -0
- package/dist/{server-CC7KUJOK.js → server-7XH7RYUX.js} +3 -2
- package/package.json +5 -4
- package/skills/conveyor-build/SKILL.md +262 -0
- package/skills/conveyor-build/references/pack-path.md +224 -0
- package/skills/conveyor-build/references/task-path.md +67 -0
- package/skills/conveyor-consensus/SKILL.md +99 -0
- package/skills/conveyor-consensus/references/doc-template.html +204 -0
- package/skills/conveyor-local-loop/SKILL.md +258 -0
- package/skills/conveyor-meeting-review/SKILL.md +72 -0
- package/skills/conveyor-plan/SKILL.md +177 -0
- package/skills/conveyor-plan/references/plan-format.md +134 -0
- package/skills/conveyor-review/SKILL.md +161 -0
- package/skills/conveyor-start/SKILL.md +106 -0
- package/skills/conveyor-triage/SKILL.md +174 -0
- package/skills/conveyor-workflows/SKILL.md +195 -0
- package/skills/conveyor-workflows/references/mcp-setup.md +43 -0
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, PackExecution, AgentRunnerStatus } from '@project/shared';
|
|
3
3
|
export * from '@project/shared';
|
|
4
4
|
import { ChildProcess } from 'node:child_process';
|
|
5
5
|
|
|
@@ -56,6 +56,31 @@ interface SpawnReviewData {
|
|
|
56
56
|
prNumber?: number | null;
|
|
57
57
|
checkoutRef?: string | null;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Server push asking a PLANNER pod to spawn the Builder child: a second
|
|
61
|
+
* conveyor-agent process bound to a fresh `mode:"build"` WorkspaceSession. The
|
|
62
|
+
* lifecycle handoff — a human pressing Build, or a Yolomatic plan landing —
|
|
63
|
+
* without cold-booting a second pod, and without flipping the planner's own
|
|
64
|
+
* session into a builder in place.
|
|
65
|
+
*
|
|
66
|
+
* Same wire discipline as SpawnReviewData: no taskId (the child inherits the
|
|
67
|
+
* parent's CONVEYOR_TASK_ID and the server derives it from the JWT), and the
|
|
68
|
+
* runner mode rides the JWT claim rather than this field — `mode` here is what
|
|
69
|
+
* the supervisor exports as CONVEYOR_MODE for the child process.
|
|
70
|
+
*/
|
|
71
|
+
interface SpawnBuilderData {
|
|
72
|
+
sessionId: string;
|
|
73
|
+
sessionJwt: string;
|
|
74
|
+
/**
|
|
75
|
+
* Runner mode for the child — `pack` when the card has children, so the
|
|
76
|
+
* coordinator gets the single-pod pack prompt and toolset. Optional so a pod
|
|
77
|
+
* running against an older server (which sends none) still boots as `task`.
|
|
78
|
+
*/
|
|
79
|
+
mode?: "task" | "pack";
|
|
80
|
+
branch?: string | null;
|
|
81
|
+
/** The agent whose model/settings/instructions the Builder runs under. */
|
|
82
|
+
agentName?: string | null;
|
|
83
|
+
}
|
|
59
84
|
/**
|
|
60
85
|
* Server push asking the builder pod to spawn an extra interactive child on
|
|
61
86
|
* this pod: a second Claude TUI (mode "adhoc") or a raw login shell (mode
|
|
@@ -98,6 +123,8 @@ declare class AgentConnection {
|
|
|
98
123
|
private earlyPullBranches;
|
|
99
124
|
private spawnReviewCallback;
|
|
100
125
|
private earlySpawnReviews;
|
|
126
|
+
private spawnBuilderCallback;
|
|
127
|
+
private earlySpawnBuilders;
|
|
101
128
|
private spawnTuiCallback;
|
|
102
129
|
private earlySpawnTuis;
|
|
103
130
|
private probeUsageCallback;
|
|
@@ -168,6 +195,9 @@ declare class AgentConnection {
|
|
|
168
195
|
branch: string;
|
|
169
196
|
}) => void): void;
|
|
170
197
|
onSpawnReview(callback: (data: SpawnReviewData) => void): void;
|
|
198
|
+
/** Mirror of onSpawnReview for the Builder handoff. Drains the early buffer
|
|
199
|
+
* so a Build pressed during boot is not lost. */
|
|
200
|
+
onSpawnBuilder(callback: (data: SpawnBuilderData) => void): void;
|
|
171
201
|
/**
|
|
172
202
|
* Report that a same-pod review child failed to spawn (fire-and-forget).
|
|
173
203
|
* The server Ends the orphaned review session and falls back to a dedicated
|
|
@@ -175,6 +205,15 @@ declare class AgentConnection {
|
|
|
175
205
|
* it; the review session is identified separately.
|
|
176
206
|
*/
|
|
177
207
|
reportReviewSpawnFailure(reviewSessionId: string, error?: string): void;
|
|
208
|
+
/**
|
|
209
|
+
* Report that this (planner) pod could not spawn the Builder child, so the
|
|
210
|
+
* server can End the orphaned build session and reopen the card instead of
|
|
211
|
+
* leaving it InProgress with a Builder tab that never appears.
|
|
212
|
+
*
|
|
213
|
+
* sessionId is OUR (planner) session — the task-identity guard runs on it;
|
|
214
|
+
* the build session is identified separately.
|
|
215
|
+
*/
|
|
216
|
+
reportBuilderSpawnFailure(buildSessionId: string, error?: string): void;
|
|
178
217
|
/**
|
|
179
218
|
* Report that this pod's git credential is dead and refreshing did not fix
|
|
180
219
|
* it (fire-and-forget).
|
|
@@ -412,14 +451,66 @@ declare class ModeController {
|
|
|
412
451
|
private _pendingModeRestart;
|
|
413
452
|
private _runnerMode;
|
|
414
453
|
private _isAuto;
|
|
454
|
+
/**
|
|
455
|
+
* Which planner this is. Only read when `_runnerMode` is `plan`; see
|
|
456
|
+
* `effectiveMode` for the full reasoning, and `latchPlanFlavor` for why this
|
|
457
|
+
* latches toward `chat` and never away from it.
|
|
458
|
+
*/
|
|
459
|
+
private _planFlavor;
|
|
415
460
|
constructor(initialMode: AgentMode, runnerMode?: RunnerMode, isAuto?: boolean);
|
|
461
|
+
/**
|
|
462
|
+
* Promote this planner to the chat flavor, once, and never demote it.
|
|
463
|
+
*
|
|
464
|
+
* It cannot be a constructor-only assignment, because the constructor usually
|
|
465
|
+
* has nothing to go on. A GKE pod's bootstrap bundle carries no agentMode, so
|
|
466
|
+
* `CONVEYOR_AGENT_MODE` is unset and `initialMode` falls back to `"building"`
|
|
467
|
+
* for EVERY pod — which is the whole reason `applyServerMode` exists. Pinning
|
|
468
|
+
* the flavor at construction alone would therefore make every chat card in the
|
|
469
|
+
* cloud a discovery planner and silently re-open the defect this replaced.
|
|
470
|
+
*
|
|
471
|
+
* One-way, and both directions matter:
|
|
472
|
+
*
|
|
473
|
+
* - Only the literal card mode `chat` promotes. The server reports `chat` only
|
|
474
|
+
* for a card whose agentMode IS `chat`, so a discovery card can never be
|
|
475
|
+
* talked into the build-capable flavor. The original hazard — `auto` stamped
|
|
476
|
+
* onto a plan-less auto card's planner — is untouched, because `auto` is not
|
|
477
|
+
* `chat`.
|
|
478
|
+
* - Nothing demotes. The Build press overwrites `Task.agentMode` to
|
|
479
|
+
* `auto`/`building` before the server sees it, so a later stamp would
|
|
480
|
+
* otherwise strip a live conversation's prompt and file deliverables
|
|
481
|
+
* mid-turn.
|
|
482
|
+
*
|
|
483
|
+
* The asymmetry with `auto` is principled rather than convenient: an auto
|
|
484
|
+
* card's mode and its planner phase legitimately differ, while a chat card's
|
|
485
|
+
* mode IS what its session is, for the session's whole life.
|
|
486
|
+
*/
|
|
487
|
+
private latchPlanFlavor;
|
|
416
488
|
get mode(): AgentMode;
|
|
417
489
|
get isAuto(): boolean;
|
|
418
490
|
get hasExitedPlanMode(): boolean;
|
|
419
491
|
set hasExitedPlanMode(val: boolean);
|
|
420
492
|
get pendingModeRestart(): boolean;
|
|
421
493
|
set pendingModeRestart(val: boolean);
|
|
422
|
-
/**
|
|
494
|
+
/**
|
|
495
|
+
* Effective mode accounting for PM/task defaults.
|
|
496
|
+
*
|
|
497
|
+
* A `plan` runner is pinned to ONE mode for its whole life, whatever the
|
|
498
|
+
* card's own agentMode later says. That pin is what makes the planner
|
|
499
|
+
* read-only, non-build-capable, and prompted for planning — all three derive
|
|
500
|
+
* from here — and it has to be immune to `applyServerMode`, which runs AFTER
|
|
501
|
+
* boot and would otherwise stamp `auto` onto the planner of a plan-less auto
|
|
502
|
+
* card, handing it the build prompt and `--dangerously-skip-permissions`
|
|
503
|
+
* inside a session whose whole purpose is not to have them.
|
|
504
|
+
*
|
|
505
|
+
* There are TWO planner flavors, and which one this session is was decided at
|
|
506
|
+
* boot (`_planFlavor`) — never re-read from the mutable `_mode`. A chat card
|
|
507
|
+
* boots a plan session too (it is a Planner-session flavor, not a builder in
|
|
508
|
+
* disguise), and it must keep `buildChatPrompt`, its file deliverables, and
|
|
509
|
+
* the build-capable tool handler. Reading the flavor from `_mode` would put
|
|
510
|
+
* that choice back under server control and re-open the exact hole the
|
|
511
|
+
* paragraph above closes, in the other direction: a stamp of `chat` onto a
|
|
512
|
+
* discovery planner would hand it build capability.
|
|
513
|
+
*/
|
|
423
514
|
get effectiveMode(): AgentMode;
|
|
424
515
|
get isReadOnly(): boolean;
|
|
425
516
|
get isAutoPlanning(): boolean;
|
|
@@ -507,7 +598,13 @@ declare class Lifecycle {
|
|
|
507
598
|
stopGitFlush(): void;
|
|
508
599
|
startUsageSample(): void;
|
|
509
600
|
stopUsageSample(): void;
|
|
510
|
-
|
|
601
|
+
/** Start (or restart) the idle timer.
|
|
602
|
+
* @param overrideMs Optional custom delay in ms, mirroring
|
|
603
|
+
* `startDormantTimer`. SessionRunner passes a short delay when it DEFERS a
|
|
604
|
+
* shutdown because a spawned child is still working: the pod must re-check
|
|
605
|
+
* soon after that child exits, rather than granting itself a fresh full idle
|
|
606
|
+
* window every time it defers. */
|
|
607
|
+
startIdleTimer(overrideMs?: number): void;
|
|
511
608
|
cancelIdleTimer(): void;
|
|
512
609
|
/** Start (or restart) the dormant timer.
|
|
513
610
|
* @param overrideMs Optional custom delay in ms. When provided, the timer
|
|
@@ -522,10 +619,43 @@ declare class Lifecycle {
|
|
|
522
619
|
private clearIdleTimers;
|
|
523
620
|
}
|
|
524
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Who else is working on this pod?
|
|
624
|
+
*
|
|
625
|
+
* The Planner is the pod's MAIN process, so its idle/dormant timeouts end the
|
|
626
|
+
* whole pod: `stopped = true` → `shutdown("finished")` → `process.exit(0)` →
|
|
627
|
+
* the supervisor logs "agent exited cleanly, shutting down pod". Nothing used
|
|
628
|
+
* to ask whether a spawned child was still working. A handed-off build that
|
|
629
|
+
* outlived the Planner's idle window therefore died mid-run — the work survived
|
|
630
|
+
* only as far as the last `conveyor-wip` flush, the janitor parked the
|
|
631
|
+
* workspace, and the card reverted to Open.
|
|
632
|
+
*
|
|
633
|
+
* This is the seam that lets `SessionRunner` ask without importing the
|
|
634
|
+
* supervisors: `cli.ts` owns them and hands the runner a probe. Keeping the
|
|
635
|
+
* dependency pointing that way also keeps the runner testable with a fake.
|
|
636
|
+
*
|
|
637
|
+
* Ordering is deliberate — `builder` first — so the deferral log names the
|
|
638
|
+
* session a human is most likely looking for when they ask why a pod is still
|
|
639
|
+
* up.
|
|
640
|
+
*/
|
|
641
|
+
/** A child supervisor, reduced to the one question the shutdown path asks. */
|
|
642
|
+
interface ChildSessionSource {
|
|
643
|
+
/**
|
|
644
|
+
* Names the child kind in the deferral log. `tui` rather than `session` —
|
|
645
|
+
* these come from `session:spawnTui`, but "session session <id>" stutters in
|
|
646
|
+
* the one place this string is read.
|
|
647
|
+
*/
|
|
648
|
+
readonly kind: "builder" | "review" | "tui";
|
|
649
|
+
/** Sessions this supervisor currently hosts. Empty when it hosts none. */
|
|
650
|
+
activeSessionIds(): string[];
|
|
651
|
+
}
|
|
652
|
+
|
|
525
653
|
interface SessionRunnerConfig {
|
|
526
654
|
connection: AgentConnectionConfig;
|
|
527
655
|
agentMode?: AgentMode;
|
|
528
656
|
runnerMode?: RunnerMode;
|
|
657
|
+
/** Only read when `runnerMode` is "pack". Defaults to single-pod. */
|
|
658
|
+
packExecution?: PackExecution;
|
|
529
659
|
isAuto?: boolean;
|
|
530
660
|
workspaceDir: string;
|
|
531
661
|
model?: string;
|
|
@@ -601,6 +731,33 @@ declare class SessionRunner {
|
|
|
601
731
|
private agentLiveReported;
|
|
602
732
|
constructor(config: SessionRunnerConfig, callbacks: SessionRunnerCallbacks, deps?: SessionRunnerDependencies);
|
|
603
733
|
get state(): AgentRunnerStatus;
|
|
734
|
+
/**
|
|
735
|
+
* Supervisors whose live children keep this pod alive. Empty until `cli.ts`
|
|
736
|
+
* wires them, which is deliberate: the supervisors are constructed AFTER the
|
|
737
|
+
* runner, and an empty list simply means "no children to protect" — the
|
|
738
|
+
* pre-existing shutdown behavior.
|
|
739
|
+
*/
|
|
740
|
+
private liveChildSources;
|
|
741
|
+
/** Called by `cli.ts` once the child supervisors exist. */
|
|
742
|
+
setLiveChildSources(sources: readonly ChildSessionSource[]): void;
|
|
743
|
+
/**
|
|
744
|
+
* Suppress an idle/dormant shutdown while a spawned child is still working.
|
|
745
|
+
*
|
|
746
|
+
* The Planner is the pod's main process, so its timeouts take the pod — and
|
|
747
|
+
* the Builder's in-flight work — down with it. Returns true when the shutdown
|
|
748
|
+
* was deferred and the caller must not proceed.
|
|
749
|
+
*
|
|
750
|
+
* Re-arms with a SHORT delay rather than a fresh full window: the pod should
|
|
751
|
+
* converge on shutdown soon after the last child exits, not one more idle
|
|
752
|
+
* window later. Bounded by the configured timeout so a test with a 50ms idle
|
|
753
|
+
* window re-checks in 50ms rather than a minute.
|
|
754
|
+
*
|
|
755
|
+
* Fail-open by construction — if the probe throws, or no sources are wired,
|
|
756
|
+
* the shutdown proceeds exactly as before. The opposite bias (a pod that
|
|
757
|
+
* cannot die) is the more expensive mistake here only in money; killing a
|
|
758
|
+
* live build costs work.
|
|
759
|
+
*/
|
|
760
|
+
private deferShutdownForLiveChild;
|
|
604
761
|
get sessionId(): string;
|
|
605
762
|
get isStopped(): boolean;
|
|
606
763
|
/** Wire the boot supervisor handle post-construction — cli.ts constructs it
|
|
@@ -815,6 +972,16 @@ interface AgentRunnerConfig {
|
|
|
815
972
|
mode?: _project_shared.RunnerMode;
|
|
816
973
|
isAuto?: boolean;
|
|
817
974
|
agentSettings?: _project_shared.AgentSettings;
|
|
975
|
+
/**
|
|
976
|
+
* How a `mode: "pack"` session executes its children.
|
|
977
|
+
*
|
|
978
|
+
* "single-pod" (the default): this session implements every child itself,
|
|
979
|
+
* serially. "fan-out": the legacy coordinator — it fires one pod per child
|
|
980
|
+
* and writes no code. Carried as a sub-flag rather than a new `RunnerMode`
|
|
981
|
+
* so `resolveModeEnv`, the JWT mode claim, and every `mode === "pack"`
|
|
982
|
+
* reader keep working unchanged.
|
|
983
|
+
*/
|
|
984
|
+
packExecution?: _project_shared.PackExecution;
|
|
818
985
|
}
|
|
819
986
|
interface AgentRunnerCallbacks {
|
|
820
987
|
onEvent: (event: Record<string, unknown>) => void | Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SessionRunner,
|
|
3
|
+
unshallowRepo
|
|
4
|
+
} from "./chunk-PEEGCZAR.js";
|
|
5
|
+
import "./chunk-XORJ6SII.js";
|
|
1
6
|
import {
|
|
2
7
|
AgentConnection,
|
|
3
8
|
GIT_TIMEOUT_MS,
|
|
4
|
-
SessionRunner,
|
|
5
9
|
flushPendingChanges,
|
|
6
10
|
getCurrentBranch,
|
|
7
11
|
hasUncommittedChanges,
|
|
@@ -10,11 +14,10 @@ import {
|
|
|
10
14
|
loadForwardPorts,
|
|
11
15
|
pushToOrigin,
|
|
12
16
|
stageAndCommit,
|
|
13
|
-
unshallowRepo,
|
|
14
17
|
updateRemoteToken,
|
|
15
18
|
workspacePathExists
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-N4WSUTGV.js";
|
|
20
|
+
import "./chunk-GL2DIQEQ.js";
|
|
18
21
|
import "./chunk-IA45XHOA.js";
|
|
19
22
|
import {
|
|
20
23
|
getWorkbenchClient
|
|
@@ -22,11 +25,12 @@ import {
|
|
|
22
25
|
import {
|
|
23
26
|
workbenchEnabled
|
|
24
27
|
} from "./chunk-KMB3BU4S.js";
|
|
28
|
+
import "./chunk-3F4ZZKCA.js";
|
|
25
29
|
import {
|
|
26
30
|
runAuthTokenCommand,
|
|
27
31
|
runSetupCommand,
|
|
28
32
|
runStartCommand
|
|
29
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-W4LZ7R6Z.js";
|
|
30
34
|
import "./chunk-6Q6LQBWO.js";
|
|
31
35
|
|
|
32
36
|
// src/runner/worktree.ts
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WorkspaceCommandSupervisor
|
|
3
|
+
} from "./chunk-JQVAWRVL.js";
|
|
4
|
+
import {
|
|
5
|
+
AgentConnection,
|
|
6
|
+
CodespacePortVisibility,
|
|
7
|
+
DEFAULT_LIFECYCLE_CONFIG,
|
|
8
|
+
Lifecycle,
|
|
9
|
+
PortDiscovery,
|
|
10
|
+
awaitGitReady,
|
|
11
|
+
createServiceLogger,
|
|
12
|
+
ensureOnTaskBranch,
|
|
13
|
+
loadConveyorConfig
|
|
14
|
+
} from "./chunk-N4WSUTGV.js";
|
|
15
|
+
import "./chunk-GL2DIQEQ.js";
|
|
16
|
+
import "./chunk-IA45XHOA.js";
|
|
17
|
+
import "./chunk-EXQ6AHOY.js";
|
|
18
|
+
import "./chunk-KMB3BU4S.js";
|
|
19
|
+
import "./chunk-W4LZ7R6Z.js";
|
|
20
|
+
import "./chunk-6Q6LQBWO.js";
|
|
21
|
+
|
|
22
|
+
// src/runner/serve-session-runner.ts
|
|
23
|
+
var ServeSessionRunner = class {
|
|
24
|
+
connection;
|
|
25
|
+
lifecycle;
|
|
26
|
+
config;
|
|
27
|
+
callbacks;
|
|
28
|
+
portDiscovery;
|
|
29
|
+
commandSupervisor;
|
|
30
|
+
git;
|
|
31
|
+
stopped = false;
|
|
32
|
+
stopResolver = null;
|
|
33
|
+
_finalState = null;
|
|
34
|
+
constructor(config, callbacks = {}, deps) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.callbacks = callbacks;
|
|
37
|
+
this.connection = deps?.connection ?? new AgentConnection(config.connection);
|
|
38
|
+
const portVisibility = new CodespacePortVisibility();
|
|
39
|
+
this.portDiscovery = deps?.portDiscovery ?? new PortDiscovery({
|
|
40
|
+
report: (ports) => this.connection.reportDiscoveredPorts(ports),
|
|
41
|
+
onReported: (ports) => portVisibility.ensureVisible(ports.map(({ port }) => port))
|
|
42
|
+
});
|
|
43
|
+
this.commandSupervisor = deps?.commandSupervisor ?? new WorkspaceCommandSupervisor({
|
|
44
|
+
config: loadConveyorConfig(),
|
|
45
|
+
workspaceDir: config.workspaceDir,
|
|
46
|
+
connection: this.connection
|
|
47
|
+
});
|
|
48
|
+
this.git = deps?.git ?? {
|
|
49
|
+
awaitGitReady: (opts) => awaitGitReady(opts),
|
|
50
|
+
checkoutBranch: (cwd, branch) => ensureOnTaskBranch(cwd, branch)
|
|
51
|
+
};
|
|
52
|
+
this.lifecycle = new Lifecycle(
|
|
53
|
+
// No git flush and no usage sample: nothing here writes to the checkout,
|
|
54
|
+
// and the serving bundle selects no coding-agent key to measure.
|
|
55
|
+
{
|
|
56
|
+
...DEFAULT_LIFECYCLE_CONFIG,
|
|
57
|
+
gitFlushIntervalMs: 0,
|
|
58
|
+
usageSampleIntervalMs: 0,
|
|
59
|
+
...config.lifecycle
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
onHeartbeat: () => this.connection.sendHeartbeat(),
|
|
63
|
+
// Never armed (see the file header) — supplied because Lifecycle
|
|
64
|
+
// requires the full callback set.
|
|
65
|
+
onIdleTimeout: () => this.requestStop(),
|
|
66
|
+
onDormantTimeout: () => this.requestStop(),
|
|
67
|
+
onTokenRefresh: () => void this.connection.refreshTaskTokenFromBootstrap().catch(() => {
|
|
68
|
+
}),
|
|
69
|
+
onGitFlush: () => {
|
|
70
|
+
},
|
|
71
|
+
onUsageSample: () => {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
get finalState() {
|
|
77
|
+
return this._finalState;
|
|
78
|
+
}
|
|
79
|
+
get isStopped() {
|
|
80
|
+
return this.stopped;
|
|
81
|
+
}
|
|
82
|
+
/** Connect, register the session, launch the start command, then hold open. */
|
|
83
|
+
async run() {
|
|
84
|
+
try {
|
|
85
|
+
await this.connection.connect();
|
|
86
|
+
this.connection.sendEvent({
|
|
87
|
+
type: "connected",
|
|
88
|
+
sessionId: this.config.connection.sessionId
|
|
89
|
+
});
|
|
90
|
+
this.connection.onStop(() => this.requestStop());
|
|
91
|
+
this.lifecycle.startHeartbeat();
|
|
92
|
+
this.lifecycle.startTokenRefresh();
|
|
93
|
+
await this.connection.call("connectAgent", {
|
|
94
|
+
sessionId: this.config.connection.sessionId
|
|
95
|
+
});
|
|
96
|
+
await this.portDiscovery.start().catch(() => {
|
|
97
|
+
});
|
|
98
|
+
if (this.stopped) {
|
|
99
|
+
this._finalState = "finished";
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const gitState = await this.git.awaitGitReady({
|
|
103
|
+
onLog: (m) => process.stderr.write(`[conveyor-agent] ${m}
|
|
104
|
+
`)
|
|
105
|
+
});
|
|
106
|
+
if (this.stopped) {
|
|
107
|
+
this._finalState = "finished";
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (gitState === "failed" || gitState === "timeout") {
|
|
111
|
+
throw new Error(
|
|
112
|
+
gitState === "failed" ? "Workspace git preparation failed (see pod logs)" : "Workspace git preparation timed out (see pod logs)"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const onBranch = await this.git.checkoutBranch(this.config.workspaceDir, this.config.branch);
|
|
116
|
+
if (this.stopped) {
|
|
117
|
+
this._finalState = "finished";
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!onBranch) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Task-branch checkout failed for "${this.config.branch}"; refusing to serve base-branch code`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
this.commandSupervisor.start();
|
|
126
|
+
this.commandSupervisor.notifyLoopReady?.();
|
|
127
|
+
await this.connection.emitStatus("idle");
|
|
128
|
+
this.callbacks.onEvent?.({ type: "serve_runner_started", taskId: this.config.taskId });
|
|
129
|
+
await this.waitUntilStopped();
|
|
130
|
+
this._finalState = "finished";
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
+
process.stderr.write(`[conveyor-agent] Serve runner failed: ${message}
|
|
134
|
+
`);
|
|
135
|
+
this.connection.sendEvent({ type: "error", message });
|
|
136
|
+
this._finalState = "error";
|
|
137
|
+
} finally {
|
|
138
|
+
await this.shutdown();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** External stop (SIGTERM/SIGINT or server session:stop). */
|
|
142
|
+
stop() {
|
|
143
|
+
this.requestStop();
|
|
144
|
+
}
|
|
145
|
+
requestStop() {
|
|
146
|
+
if (this.stopped) return;
|
|
147
|
+
this.stopped = true;
|
|
148
|
+
this.portDiscovery.stop();
|
|
149
|
+
if (this.stopResolver) {
|
|
150
|
+
const resolve = this.stopResolver;
|
|
151
|
+
this.stopResolver = null;
|
|
152
|
+
resolve();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
waitUntilStopped() {
|
|
156
|
+
if (this.stopped) return Promise.resolve();
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
this.stopResolver = resolve;
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async shutdown() {
|
|
162
|
+
this.stopped = true;
|
|
163
|
+
this.portDiscovery.stop();
|
|
164
|
+
this.lifecycle.destroy();
|
|
165
|
+
await this.commandSupervisor.stop().catch(() => {
|
|
166
|
+
});
|
|
167
|
+
this.connection.sendEvent({ type: "shutdown", reason: this._finalState ?? "finished" });
|
|
168
|
+
this.connection.disconnect();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// src/runner/serve-boot.ts
|
|
173
|
+
var logger = createServiceLogger("ServeBoot");
|
|
174
|
+
function outcomeFor(runner) {
|
|
175
|
+
const errored = runner.finalState === "error";
|
|
176
|
+
return {
|
|
177
|
+
exitCode: errored ? 1 : 0,
|
|
178
|
+
reason: errored ? "error" : "clean",
|
|
179
|
+
finalState: runner.finalState ?? void 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async function runServingSession(params) {
|
|
183
|
+
if (!params.sessionId) {
|
|
184
|
+
logger.error("Serving mode requires CONVEYOR_SESSION_ID");
|
|
185
|
+
return { exitCode: 1, reason: "error", finalState: "error" };
|
|
186
|
+
}
|
|
187
|
+
if (!params.branch) {
|
|
188
|
+
logger.error("Serving mode requires BRANCH (the card's branch to serve)");
|
|
189
|
+
return { exitCode: 1, reason: "error", finalState: "error" };
|
|
190
|
+
}
|
|
191
|
+
logger.info("Starting preview-only serving runner", {
|
|
192
|
+
taskId: params.taskId,
|
|
193
|
+
sessionId: params.sessionId,
|
|
194
|
+
branch: params.branch
|
|
195
|
+
});
|
|
196
|
+
const runner = new ServeSessionRunner(
|
|
197
|
+
{
|
|
198
|
+
connection: {
|
|
199
|
+
apiUrl: params.apiUrl,
|
|
200
|
+
taskToken: params.taskToken,
|
|
201
|
+
sessionId: params.sessionId,
|
|
202
|
+
runnerMode: "serving"
|
|
203
|
+
},
|
|
204
|
+
taskId: params.taskId,
|
|
205
|
+
workspaceDir: params.workspaceDir,
|
|
206
|
+
branch: params.branch
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
onEvent: (event) => {
|
|
210
|
+
logger.info("Serve runner event", { eventType: event.type });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
const stop = () => {
|
|
215
|
+
params.onSignal?.();
|
|
216
|
+
runner.stop();
|
|
217
|
+
};
|
|
218
|
+
process.on("SIGTERM", stop);
|
|
219
|
+
process.on("SIGINT", stop);
|
|
220
|
+
await runner.run();
|
|
221
|
+
return outcomeFor(runner);
|
|
222
|
+
}
|
|
223
|
+
export {
|
|
224
|
+
runServingSession
|
|
225
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startWorkbenchServer
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-UBDSLM44.js";
|
|
4
|
+
import "./chunk-3F4ZZKCA.js";
|
|
5
|
+
import "./chunk-W4LZ7R6Z.js";
|
|
5
6
|
import "./chunk-6Q6LQBWO.js";
|
|
6
7
|
import "./chunk-6W6UZ4SJ.js";
|
|
7
8
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
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",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
-
"runtime"
|
|
18
|
+
"runtime",
|
|
19
|
+
"skills"
|
|
19
20
|
],
|
|
20
21
|
"type": "module",
|
|
21
22
|
"main": "./dist/index.js",
|
|
@@ -36,8 +37,8 @@
|
|
|
36
37
|
"typecheck": "tsgo --noEmit"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
40
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
40
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.246",
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
41
42
|
"node-pty": "^1.0.0",
|
|
42
43
|
"socket.io-client": "^4.8.3",
|
|
43
44
|
"tar": "^7.5.21",
|