@rallycry/conveyor-agent 11.0.5 → 11.0.7
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-7XH4FFLO.js → boot-SQKXI5NY.js} +23 -333
- package/dist/{chunk-URYBO32C.js → chunk-43XITDZP.js} +2 -2
- package/dist/{chunk-SLUBOIYE.js → chunk-APP6Y2D7.js} +11 -1
- package/dist/{chunk-D57S4UR5.js → chunk-IBAIJY7V.js} +17 -1
- package/dist/{chunk-W6VILPQE.js → chunk-JG3VMWRQ.js} +1 -1
- package/dist/{chunk-J2NPWBVZ.js → chunk-NFNB2C35.js} +153 -8
- package/dist/chunk-SR66HQKB.js +464 -0
- package/dist/{chunk-EXQ6AHOY.js → chunk-TOTCEWJD.js} +18 -0
- package/dist/cli.js +9 -9
- package/dist/{client-IG6C5F2G.js → client-6AUPKKMV.js} +1 -1
- package/dist/index.d.ts +76 -0
- package/dist/index.js +5 -5
- package/dist/{serve-boot-B4BIZMNB.js → serve-boot-ESZKFN5M.js} +3 -3
- package/dist/{server-2Z5V3IPA.js → server-2IZBKD3Q.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-XORJ6SII.js +0 -46
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mapChatHistory,
|
|
3
|
-
readAgentVersion
|
|
4
|
-
|
|
3
|
+
readAgentVersion,
|
|
4
|
+
refreshSkillsAfterCheckout
|
|
5
|
+
} from "./chunk-SR66HQKB.js";
|
|
5
6
|
import {
|
|
6
7
|
MAX_BETWEEN_TURN_BUFFER,
|
|
7
8
|
MAX_DIAGNOSTIC_OUTPUT,
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
spawnOptionsFingerprint,
|
|
29
30
|
transcriptSize,
|
|
30
31
|
turnOptionsFrom
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-JG3VMWRQ.js";
|
|
32
33
|
import {
|
|
33
34
|
AgentConnection,
|
|
34
35
|
CodespacePortVisibility,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
forceFreshCooldownNotice,
|
|
45
46
|
forceFreshMintBlocked,
|
|
46
47
|
getCurrentBranch,
|
|
48
|
+
git,
|
|
47
49
|
hasUncommittedChanges,
|
|
48
50
|
hasUnpushedCommits,
|
|
49
51
|
pushToOrigin,
|
|
@@ -57,7 +59,7 @@ import {
|
|
|
57
59
|
statWorkspacePath,
|
|
58
60
|
updateRemoteToken,
|
|
59
61
|
verifyGitCredential
|
|
60
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-APP6Y2D7.js";
|
|
61
63
|
import {
|
|
62
64
|
registerBootMilestoneSocketFallback,
|
|
63
65
|
reportBootMilestone
|
|
@@ -78,7 +80,7 @@ import {
|
|
|
78
80
|
} from "./chunk-IA45XHOA.js";
|
|
79
81
|
import {
|
|
80
82
|
getWorkbenchClient
|
|
81
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-TOTCEWJD.js";
|
|
82
84
|
import {
|
|
83
85
|
workbenchEnabled
|
|
84
86
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -4640,6 +4642,12 @@ var PtySession = class {
|
|
|
4640
4642
|
// synthetic id so the card flips to answered.
|
|
4641
4643
|
pendingSyntheticQuestionIds = [];
|
|
4642
4644
|
questionResultRemap = /* @__PURE__ */ new Map();
|
|
4645
|
+
// An AskUserQuestion questionnaire is parked on screen (PreToolUse hook
|
|
4646
|
+
// fired, no PostToolUse envelope yet). While set the parked process must
|
|
4647
|
+
// not be reused: feeding follow-up prompt bytes into the live dialog would
|
|
4648
|
+
// type chat text into the questionnaire. Fail-safe — a stale flag only
|
|
4649
|
+
// costs a teardown + respawn (`--resume` recovers the conversation).
|
|
4650
|
+
questionnairePending = false;
|
|
4643
4651
|
// Per-turn state: the prompt to feed and the per-turn options subset. Both
|
|
4644
4652
|
// start from the constructor args (turn 1) and are replaced by beginTurn.
|
|
4645
4653
|
turnPrompt;
|
|
@@ -4732,7 +4740,9 @@ var PtySession = class {
|
|
|
4732
4740
|
*/
|
|
4733
4741
|
canReuse(resume, fingerprint) {
|
|
4734
4742
|
const resumeMatches = resume === void 0 ? this.reportedOpenCodeId !== null : resume === this.sessionUuid;
|
|
4735
|
-
return !this._toreDown && !this.exited && this.activeQueue === null &&
|
|
4743
|
+
return !this._toreDown && !this.exited && this.activeQueue === null && // A parked questionnaire owns the terminal — a fed prompt would type
|
|
4744
|
+
// into the dialog. Force teardown + respawn instead.
|
|
4745
|
+
!this.questionnairePending && resumeMatches && fingerprint === this.spawnFingerprint;
|
|
4736
4746
|
}
|
|
4737
4747
|
get hookSocketPath() {
|
|
4738
4748
|
return this.socket ? this.socket.socketPath : null;
|
|
@@ -4807,6 +4817,7 @@ var PtySession = class {
|
|
|
4807
4817
|
this.deliveredTexts = [];
|
|
4808
4818
|
this.disarmSubmitNudge();
|
|
4809
4819
|
this.disarmPlanDialogAutoAccept();
|
|
4820
|
+
this.questionnairePending = false;
|
|
4810
4821
|
this.closeSyntheticQuestionCards();
|
|
4811
4822
|
}
|
|
4812
4823
|
/**
|
|
@@ -5116,6 +5127,7 @@ var PtySession = class {
|
|
|
5116
5127
|
async teardown() {
|
|
5117
5128
|
if (this._toreDown) return;
|
|
5118
5129
|
this._toreDown = true;
|
|
5130
|
+
this.questionnairePending = false;
|
|
5119
5131
|
this.disarmPlanDialogAutoAccept();
|
|
5120
5132
|
this.disarmSubmitNudge();
|
|
5121
5133
|
this.closeSyntheticQuestionCards();
|
|
@@ -5391,6 +5403,7 @@ var PtySession = class {
|
|
|
5391
5403
|
if (this.pendingPlanApproval && progress.tool_name === "ExitPlanMode") {
|
|
5392
5404
|
this.disarmPlanDialogAutoAccept();
|
|
5393
5405
|
}
|
|
5406
|
+
if (progress.tool_name === "AskUserQuestion") this.questionnairePending = false;
|
|
5394
5407
|
if (this.pendingSubmitNudge) this.disarmSubmitNudge();
|
|
5395
5408
|
this.pushEvent({
|
|
5396
5409
|
type: "tool_progress",
|
|
@@ -5411,6 +5424,7 @@ var PtySession = class {
|
|
|
5411
5424
|
if (request.tool_name === "AskUserQuestion") {
|
|
5412
5425
|
this.disarmSubmitNudge();
|
|
5413
5426
|
this.disarmPlanDialogAutoAccept();
|
|
5427
|
+
this.questionnairePending = true;
|
|
5414
5428
|
const questions = parseUserQuestions(request.tool_input);
|
|
5415
5429
|
this.pushEvent({ type: "user_question", questions });
|
|
5416
5430
|
this.emitSyntheticQuestionCard(questions);
|
|
@@ -5519,6 +5533,7 @@ var PtySession = class {
|
|
|
5519
5533
|
async finalizeOnExit(exitCode) {
|
|
5520
5534
|
this.coalescer?.flush();
|
|
5521
5535
|
this.exited = true;
|
|
5536
|
+
this.questionnairePending = false;
|
|
5522
5537
|
if (this._toreDown) return;
|
|
5523
5538
|
if (this.tailer) {
|
|
5524
5539
|
this.tailer.close();
|
|
@@ -12539,16 +12554,22 @@ function questionTextFromEvent(event) {
|
|
|
12539
12554
|
}
|
|
12540
12555
|
async function armQuestionPending(host, state, event) {
|
|
12541
12556
|
state.questionPending = true;
|
|
12557
|
+
const questionText = questionTextFromEvent(event);
|
|
12558
|
+
host.callbacks.onUserQuestion?.({
|
|
12559
|
+
kind: "armed",
|
|
12560
|
+
...questionText === void 0 ? {} : { questionText }
|
|
12561
|
+
});
|
|
12542
12562
|
await host.connection.emitStatus(
|
|
12543
12563
|
"waiting_for_input",
|
|
12544
12564
|
AGENT_STATUS_REASON_USER_QUESTION,
|
|
12545
|
-
|
|
12565
|
+
questionText
|
|
12546
12566
|
);
|
|
12547
12567
|
await host.callbacks.onStatusChange("waiting_for_input");
|
|
12548
12568
|
}
|
|
12549
12569
|
async function clearQuestionPending(host, state, options) {
|
|
12550
12570
|
if (!state.questionPending) return;
|
|
12551
12571
|
state.questionPending = false;
|
|
12572
|
+
host.callbacks.onUserQuestion?.({ kind: options.emitRunning ? "answered" : "turn_ended" });
|
|
12552
12573
|
if (options.emitRunning) {
|
|
12553
12574
|
await host.connection.emitStatus("running");
|
|
12554
12575
|
await host.callbacks.onStatusChange("running");
|
|
@@ -14740,6 +14761,15 @@ function findLiveChild(sources) {
|
|
|
14740
14761
|
var AUTO_RUN_MODES = /* @__PURE__ */ new Set(["building", "auto", "review", "discovery", "chat"]);
|
|
14741
14762
|
var CHILD_DEFER_RECHECK_MS = 60 * 1e3;
|
|
14742
14763
|
var ORPHAN_WAKE_GRACE_MS = 3 * 60 * 1e3;
|
|
14764
|
+
var QUESTION_GRACE_MS = 10 * 6e4;
|
|
14765
|
+
var QUESTION_HOLD_SLACK_MS = 6e4;
|
|
14766
|
+
function resolveQuestionGraceMs() {
|
|
14767
|
+
const raw = Number(process.env.CONVEYOR_QUESTION_GRACE_MS ?? "");
|
|
14768
|
+
return Number.isFinite(raw) && raw > 0 ? raw : QUESTION_GRACE_MS;
|
|
14769
|
+
}
|
|
14770
|
+
var QUESTION_TYPING_RECENT_MS = 6e4;
|
|
14771
|
+
var QUESTION_TYPING_DEFER_MS = 2 * 6e4;
|
|
14772
|
+
var QUESTION_TIMEOUT_PROMPT = 'No one answered your clarifying questions within 10 minutes. Do this now: (1) Save your findings-so-far as the card plan (`update_task`). (2) Post the unanswered questions to chat with `post_to_chat` using `milestone: "blocked"`, stating the assumption you would make for each. (3) If you can safely proceed on your best-judgment answers, state the assumptions and continue; otherwise end your turn \u2014 answers posted in chat will be delivered when you resume.';
|
|
14743
14773
|
var SessionRunner = class _SessionRunner {
|
|
14744
14774
|
connection;
|
|
14745
14775
|
mode;
|
|
@@ -14793,6 +14823,18 @@ var SessionRunner = class _SessionRunner {
|
|
|
14793
14823
|
/** Guards the one-shot `agent_live` boot-milestone report — fired on the
|
|
14794
14824
|
* first harness event (createQueryBridge's onEvent hook). */
|
|
14795
14825
|
agentLiveReported = false;
|
|
14826
|
+
/** Epoch ms since a TUI questionnaire has been parked on a human with no
|
|
14827
|
+
* answer; null when none is pending. While set (and within the grace +
|
|
14828
|
+
* slack bound) `resolveLoopStatus` reports `waiting`, which reaches the API
|
|
14829
|
+
* as `active` and keeps the workspace activity clock fresh — the pod must
|
|
14830
|
+
* not be slept out from under an unanswered question. Deliberately KEPT
|
|
14831
|
+
* when the turn ends with the questionnaire still parked (the Shape-B
|
|
14832
|
+
* silent death this hold exists for). */
|
|
14833
|
+
questionPendingSince = null;
|
|
14834
|
+
/** Single-shot unref'd timer driving `handleQuestionGraceExpiry`. The
|
|
14835
|
+
* liveness hold above is bounded by timestamp arithmetic, so a lost timer
|
|
14836
|
+
* still expires the hold — it just skips the un-park injection. */
|
|
14837
|
+
questionGraceTimer = null;
|
|
14796
14838
|
constructor(config, callbacks, deps = {}) {
|
|
14797
14839
|
this.config = config;
|
|
14798
14840
|
this.callbacks = callbacks;
|
|
@@ -15148,6 +15190,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15148
15190
|
process.stderr.write("[conveyor-agent] WARNING: task-branch checkout failed\n");
|
|
15149
15191
|
}
|
|
15150
15192
|
if (ok) void reportBootMilestone({ key: "branch_ready" });
|
|
15193
|
+
await this.refreshSkillsForCheckout();
|
|
15151
15194
|
}
|
|
15152
15195
|
if (!this.stopped) {
|
|
15153
15196
|
this.lifecycle.startGitFlush();
|
|
@@ -15207,6 +15250,40 @@ var SessionRunner = class _SessionRunner {
|
|
|
15207
15250
|
await this.run();
|
|
15208
15251
|
}
|
|
15209
15252
|
// ── Core loop ──────────────────────────────────────────────────────
|
|
15253
|
+
/**
|
|
15254
|
+
* Re-run the skill floor against the tree `ensureOnTaskBranch` just produced.
|
|
15255
|
+
*
|
|
15256
|
+
* Split-mode pods keep the repo in the WORKBENCH container, so this routes
|
|
15257
|
+
* over the launcher: running it in-process here would materialize skills into
|
|
15258
|
+
* the AGENT container's own copy of /workspaces/repo — still on the base
|
|
15259
|
+
* branch, already complete — and look like it worked while Claude spawned on
|
|
15260
|
+
* the untouched real tree. Codespaces/local (no workbench) run it directly.
|
|
15261
|
+
*
|
|
15262
|
+
* Best-effort by design: a failure warns and boot continues, exactly like the
|
|
15263
|
+
* pre-ready binds it repeats.
|
|
15264
|
+
*/
|
|
15265
|
+
async refreshSkillsForCheckout() {
|
|
15266
|
+
const write = (msg) => void process.stderr.write(`[conveyor-agent] ${msg}
|
|
15267
|
+
`);
|
|
15268
|
+
try {
|
|
15269
|
+
const result = workbenchEnabled() ? await getWorkbenchClient().refreshSkills() : await refreshSkillsAfterCheckout(
|
|
15270
|
+
this.config.workspaceDir,
|
|
15271
|
+
async (args, opts) => ({
|
|
15272
|
+
stdout: await git(opts?.cwd ?? this.config.workspaceDir, args, opts?.timeoutMs)
|
|
15273
|
+
}),
|
|
15274
|
+
{ info: write, warn: write, error: write }
|
|
15275
|
+
);
|
|
15276
|
+
const { filled, shadowed, stale } = result;
|
|
15277
|
+
if (filled.length + shadowed.length + stale.length === 0) return;
|
|
15278
|
+
write(
|
|
15279
|
+
`post-checkout skill refresh: filled=[${filled.join(", ")}] shadowed=[${shadowed.join(", ")}] unusable=[${stale.join(", ")}]`
|
|
15280
|
+
);
|
|
15281
|
+
} catch (err) {
|
|
15282
|
+
write(
|
|
15283
|
+
`WARNING: post-checkout skill refresh failed: ${err instanceof Error ? err.message : String(err)}`
|
|
15284
|
+
);
|
|
15285
|
+
}
|
|
15286
|
+
}
|
|
15210
15287
|
// oxlint-disable-next-line complexity -- dormant idle paths add branches but are tightly coupled to the loop
|
|
15211
15288
|
async coreLoop() {
|
|
15212
15289
|
while (!this.stopped) {
|
|
@@ -15495,6 +15572,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15495
15572
|
}
|
|
15496
15573
|
/** Inject a message (from connection callback or external source) */
|
|
15497
15574
|
injectMessage(msg) {
|
|
15575
|
+
this.clearQuestionHold();
|
|
15498
15576
|
if (this.inputResolver) {
|
|
15499
15577
|
const resolve = this.inputResolver;
|
|
15500
15578
|
this.inputResolver = null;
|
|
@@ -15702,6 +15780,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15702
15780
|
}
|
|
15703
15781
|
stop() {
|
|
15704
15782
|
this.stopped = true;
|
|
15783
|
+
this.clearQuestionHold();
|
|
15705
15784
|
this.queryBridge?.stop();
|
|
15706
15785
|
void this.queryBridge?.dispose().catch(() => {
|
|
15707
15786
|
});
|
|
@@ -15796,7 +15875,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
15796
15875
|
void this.connection.sendHeartbeat();
|
|
15797
15876
|
}
|
|
15798
15877
|
return this.callbacks.onEvent(event);
|
|
15799
|
-
}
|
|
15878
|
+
},
|
|
15879
|
+
onUserQuestion: (signal) => this.handleUserQuestionSignal(signal)
|
|
15800
15880
|
});
|
|
15801
15881
|
bridge.isParentTask = this.fullContext?.isParentTask ?? false;
|
|
15802
15882
|
bridge.onBackgroundTaskDone = () => this.backgroundWork.noteCompletion();
|
|
@@ -15947,8 +16027,72 @@ ${outcome.failures.join("\n")}
|
|
|
15947
16027
|
*/
|
|
15948
16028
|
resolveLoopStatus() {
|
|
15949
16029
|
if (loopStatusForRunnerStatus(this._state) === "active") return "active";
|
|
16030
|
+
if (this.questionPendingSince !== null && Date.now() - this.questionPendingSince < resolveQuestionGraceMs() + QUESTION_HOLD_SLACK_MS) {
|
|
16031
|
+
return "waiting";
|
|
16032
|
+
}
|
|
15950
16033
|
return this.backgroundWork.hasPending() || isHeavyGateActive() ? "waiting" : "idle";
|
|
15951
16034
|
}
|
|
16035
|
+
// ── Question grace hold ────────────────────────────────────────────
|
|
16036
|
+
/**
|
|
16037
|
+
* Question-pending transitions from the event loop. `armed` starts the
|
|
16038
|
+
* bounded liveness hold + the grace timer; `answered` clears both.
|
|
16039
|
+
* `turn_ended` — the turn's result arrived with the questionnaire still
|
|
16040
|
+
* parked — deliberately KEEPS them: that is the Shape-B silent death
|
|
16041
|
+
* (idle heartbeats stop bumping the activity clock and the reconciler
|
|
16042
|
+
* sleeps the pod minutes later) this hold exists to prevent.
|
|
16043
|
+
*/
|
|
16044
|
+
handleUserQuestionSignal(signal) {
|
|
16045
|
+
if (signal.kind === "armed") {
|
|
16046
|
+
this.questionPendingSince = Date.now();
|
|
16047
|
+
this.armQuestionGraceTimer(resolveQuestionGraceMs());
|
|
16048
|
+
this.refreshLoopStatus();
|
|
16049
|
+
} else if (signal.kind === "answered") {
|
|
16050
|
+
this.clearQuestionHold();
|
|
16051
|
+
}
|
|
16052
|
+
}
|
|
16053
|
+
armQuestionGraceTimer(delayMs) {
|
|
16054
|
+
if (this.questionGraceTimer) clearTimeout(this.questionGraceTimer);
|
|
16055
|
+
this.questionGraceTimer = setTimeout(() => this.handleQuestionGraceExpiry(), delayMs);
|
|
16056
|
+
this.questionGraceTimer.unref();
|
|
16057
|
+
}
|
|
16058
|
+
clearQuestionHold() {
|
|
16059
|
+
if (this.questionGraceTimer) {
|
|
16060
|
+
clearTimeout(this.questionGraceTimer);
|
|
16061
|
+
this.questionGraceTimer = null;
|
|
16062
|
+
}
|
|
16063
|
+
if (this.questionPendingSince === null) return;
|
|
16064
|
+
this.questionPendingSince = null;
|
|
16065
|
+
this.refreshLoopStatus();
|
|
16066
|
+
}
|
|
16067
|
+
/**
|
|
16068
|
+
* The grace expired with the questionnaire still unanswered. Un-park the
|
|
16069
|
+
* agent by injecting the timeout prompt as a superseding system message —
|
|
16070
|
+
* the proven supersede path (mid-turn: queue + queryBridge.stop(), teardown
|
|
16071
|
+
* kills the parked dialog and the respawn `--resume`s cleanly; idle: the
|
|
16072
|
+
* inputResolver picks it up). Never synthetic keypresses: Enter would
|
|
16073
|
+
* fabricate an answer and Esc handling is CLI-version-dependent.
|
|
16074
|
+
*
|
|
16075
|
+
* A human actively typing in the Connected-TUI defers the expiry — they may
|
|
16076
|
+
* be mid-answer, and superseding would yank the dialog out from under them.
|
|
16077
|
+
*/
|
|
16078
|
+
handleQuestionGraceExpiry() {
|
|
16079
|
+
this.questionGraceTimer = null;
|
|
16080
|
+
if (this.questionPendingSince === null || this.stopped) return;
|
|
16081
|
+
const lastInput = this.connection.lastPtyInputAt;
|
|
16082
|
+
if (lastInput !== null && Date.now() - lastInput < QUESTION_TYPING_RECENT_MS) {
|
|
16083
|
+
this.questionPendingSince = Date.now();
|
|
16084
|
+
this.armQuestionGraceTimer(QUESTION_TYPING_DEFER_MS);
|
|
16085
|
+
return;
|
|
16086
|
+
}
|
|
16087
|
+
process.stderr.write(
|
|
16088
|
+
"[conveyor-agent] Question grace expired with no answer \u2014 un-parking the agent\n"
|
|
16089
|
+
);
|
|
16090
|
+
this.injectMessage({
|
|
16091
|
+
content: QUESTION_TIMEOUT_PROMPT,
|
|
16092
|
+
userId: "system",
|
|
16093
|
+
source: "question_timeout"
|
|
16094
|
+
});
|
|
16095
|
+
}
|
|
15952
16096
|
/** Mirror the resolved status into the loop-lag buffer so the starvation-proof
|
|
15953
16097
|
* heartbeat worker reports it too. Returns what it wrote. */
|
|
15954
16098
|
refreshLoopStatus() {
|
|
@@ -15977,6 +16121,7 @@ ${outcome.failures.join("\n")}
|
|
|
15977
16121
|
process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
|
|
15978
16122
|
`);
|
|
15979
16123
|
this.connection.sendEvent({ type: "shutdown", reason: finalState });
|
|
16124
|
+
this.clearQuestionHold();
|
|
15980
16125
|
this.portDiscovery.stop();
|
|
15981
16126
|
this.backgroundWork.clear();
|
|
15982
16127
|
this.loopLag.stop();
|