@rallycry/conveyor-agent 11.0.4 → 11.0.6
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-SWUVT5B4.js → boot-7XH4FFLO.js} +2 -2
- package/dist/{chunk-UBDSLM44.js → chunk-D57S4UR5.js} +1 -1
- package/dist/{chunk-HYPX6FEX.js → chunk-GUYMLASA.js} +222 -79
- package/dist/{chunk-SLUBOIYE.js → chunk-HFUVAHII.js} +9 -0
- package/dist/{chunk-URYBO32C.js → chunk-UHMCBCQL.js} +1 -1
- package/dist/{chunk-3F4ZZKCA.js → chunk-W6VILPQE.js} +6 -0
- package/dist/cli.js +7 -7
- package/dist/gate-cli.js +4 -2
- package/dist/index.d.ts +63 -0
- package/dist/index.js +3 -3
- package/dist/{serve-boot-B4BIZMNB.js → serve-boot-IFUIGFEH.js} +2 -2
- package/dist/{server-7XH7RYUX.js → server-2Z5V3IPA.js} +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startWorkbenchServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-D57S4UR5.js";
|
|
4
4
|
import {
|
|
5
5
|
TOMBSTONE_MESSAGE,
|
|
6
6
|
isLegacyEntrypointLaunch
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
readAgentVersion
|
|
10
10
|
} from "./chunk-XORJ6SII.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-W6VILPQE.js";
|
|
12
12
|
import {
|
|
13
13
|
GitPrepJob,
|
|
14
14
|
defaultGit,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
buildPromptBytes,
|
|
10
10
|
buildSpawnArgs,
|
|
11
11
|
cleanTerminalOutput,
|
|
12
|
+
ensureSessionTarget,
|
|
12
13
|
inheritedEnv,
|
|
13
14
|
killPtyWithEscalation,
|
|
14
15
|
needsRawReadyGate,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
spawnOptionsFingerprint,
|
|
28
29
|
transcriptSize,
|
|
29
30
|
turnOptionsFrom
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-W6VILPQE.js";
|
|
31
32
|
import {
|
|
32
33
|
AgentConnection,
|
|
33
34
|
CodespacePortVisibility,
|
|
@@ -56,7 +57,7 @@ import {
|
|
|
56
57
|
statWorkspacePath,
|
|
57
58
|
updateRemoteToken,
|
|
58
59
|
verifyGitCredential
|
|
59
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-HFUVAHII.js";
|
|
60
61
|
import {
|
|
61
62
|
registerBootMilestoneSocketFallback,
|
|
62
63
|
reportBootMilestone
|
|
@@ -4639,6 +4640,12 @@ var PtySession = class {
|
|
|
4639
4640
|
// synthetic id so the card flips to answered.
|
|
4640
4641
|
pendingSyntheticQuestionIds = [];
|
|
4641
4642
|
questionResultRemap = /* @__PURE__ */ new Map();
|
|
4643
|
+
// An AskUserQuestion questionnaire is parked on screen (PreToolUse hook
|
|
4644
|
+
// fired, no PostToolUse envelope yet). While set the parked process must
|
|
4645
|
+
// not be reused: feeding follow-up prompt bytes into the live dialog would
|
|
4646
|
+
// type chat text into the questionnaire. Fail-safe — a stale flag only
|
|
4647
|
+
// costs a teardown + respawn (`--resume` recovers the conversation).
|
|
4648
|
+
questionnairePending = false;
|
|
4642
4649
|
// Per-turn state: the prompt to feed and the per-turn options subset. Both
|
|
4643
4650
|
// start from the constructor args (turn 1) and are replaced by beginTurn.
|
|
4644
4651
|
turnPrompt;
|
|
@@ -4731,7 +4738,9 @@ var PtySession = class {
|
|
|
4731
4738
|
*/
|
|
4732
4739
|
canReuse(resume, fingerprint) {
|
|
4733
4740
|
const resumeMatches = resume === void 0 ? this.reportedOpenCodeId !== null : resume === this.sessionUuid;
|
|
4734
|
-
return !this._toreDown && !this.exited && this.activeQueue === null &&
|
|
4741
|
+
return !this._toreDown && !this.exited && this.activeQueue === null && // A parked questionnaire owns the terminal — a fed prompt would type
|
|
4742
|
+
// into the dialog. Force teardown + respawn instead.
|
|
4743
|
+
!this.questionnairePending && resumeMatches && fingerprint === this.spawnFingerprint;
|
|
4735
4744
|
}
|
|
4736
4745
|
get hookSocketPath() {
|
|
4737
4746
|
return this.socket ? this.socket.socketPath : null;
|
|
@@ -4806,6 +4815,7 @@ var PtySession = class {
|
|
|
4806
4815
|
this.deliveredTexts = [];
|
|
4807
4816
|
this.disarmSubmitNudge();
|
|
4808
4817
|
this.disarmPlanDialogAutoAccept();
|
|
4818
|
+
this.questionnairePending = false;
|
|
4809
4819
|
this.closeSyntheticQuestionCards();
|
|
4810
4820
|
}
|
|
4811
4821
|
/**
|
|
@@ -5115,6 +5125,7 @@ var PtySession = class {
|
|
|
5115
5125
|
async teardown() {
|
|
5116
5126
|
if (this._toreDown) return;
|
|
5117
5127
|
this._toreDown = true;
|
|
5128
|
+
this.questionnairePending = false;
|
|
5118
5129
|
this.disarmPlanDialogAutoAccept();
|
|
5119
5130
|
this.disarmSubmitNudge();
|
|
5120
5131
|
this.closeSyntheticQuestionCards();
|
|
@@ -5390,6 +5401,7 @@ var PtySession = class {
|
|
|
5390
5401
|
if (this.pendingPlanApproval && progress.tool_name === "ExitPlanMode") {
|
|
5391
5402
|
this.disarmPlanDialogAutoAccept();
|
|
5392
5403
|
}
|
|
5404
|
+
if (progress.tool_name === "AskUserQuestion") this.questionnairePending = false;
|
|
5393
5405
|
if (this.pendingSubmitNudge) this.disarmSubmitNudge();
|
|
5394
5406
|
this.pushEvent({
|
|
5395
5407
|
type: "tool_progress",
|
|
@@ -5410,6 +5422,7 @@ var PtySession = class {
|
|
|
5410
5422
|
if (request.tool_name === "AskUserQuestion") {
|
|
5411
5423
|
this.disarmSubmitNudge();
|
|
5412
5424
|
this.disarmPlanDialogAutoAccept();
|
|
5425
|
+
this.questionnairePending = true;
|
|
5413
5426
|
const questions = parseUserQuestions(request.tool_input);
|
|
5414
5427
|
this.pushEvent({ type: "user_question", questions });
|
|
5415
5428
|
this.emitSyntheticQuestionCard(questions);
|
|
@@ -5518,6 +5531,7 @@ var PtySession = class {
|
|
|
5518
5531
|
async finalizeOnExit(exitCode) {
|
|
5519
5532
|
this.coalescer?.flush();
|
|
5520
5533
|
this.exited = true;
|
|
5534
|
+
this.questionnairePending = false;
|
|
5521
5535
|
if (this._toreDown) return;
|
|
5522
5536
|
if (this.tailer) {
|
|
5523
5537
|
this.tailer.close();
|
|
@@ -5720,13 +5734,19 @@ var PtyHarness = class _PtyHarness {
|
|
|
5720
5734
|
}
|
|
5721
5735
|
async *executeQuery(opts) {
|
|
5722
5736
|
const want = opts.resume ?? opts.options.resume ?? this.parked?.reportedSessionId ?? void 0;
|
|
5723
|
-
const
|
|
5737
|
+
const options = ensureSessionTarget(opts.options, want);
|
|
5738
|
+
if (options !== opts.options) {
|
|
5739
|
+
_PtyHarness.log.warn("spawn had no session target \u2014 minted one", {
|
|
5740
|
+
sessionId: options.sessionId
|
|
5741
|
+
});
|
|
5742
|
+
}
|
|
5743
|
+
const fingerprint = this.fingerprintOf(options);
|
|
5724
5744
|
let session;
|
|
5725
|
-
if (this.parked?.canReuse(want, fingerprint) && !await this.parkedHomeDied(
|
|
5745
|
+
if (this.parked?.canReuse(want, fingerprint) && !await this.parkedHomeDied(options)) {
|
|
5726
5746
|
session = this.parked;
|
|
5727
5747
|
this.parked = null;
|
|
5728
5748
|
this.cancelEndedTimer();
|
|
5729
|
-
await session.beginTurn(opts.prompt,
|
|
5749
|
+
await session.beginTurn(opts.prompt, options);
|
|
5730
5750
|
} else {
|
|
5731
5751
|
if (this.parked) {
|
|
5732
5752
|
const stale = this.parked;
|
|
@@ -5734,10 +5754,10 @@ var PtyHarness = class _PtyHarness {
|
|
|
5734
5754
|
this.cancelEndedTimer();
|
|
5735
5755
|
await stale.teardown();
|
|
5736
5756
|
}
|
|
5737
|
-
session = await this.spawnSession(opts.prompt,
|
|
5757
|
+
session = await this.spawnSession(opts.prompt, options, want);
|
|
5738
5758
|
}
|
|
5739
5759
|
yield* this.drain(session);
|
|
5740
|
-
yield* this.recoverFailedDelivery(session,
|
|
5760
|
+
yield* this.recoverFailedDelivery(session, options, want);
|
|
5741
5761
|
}
|
|
5742
5762
|
/**
|
|
5743
5763
|
* Did the shared `~/.claude` mount die while this session sat parked?
|
|
@@ -6768,8 +6788,8 @@ function wrapBridgeWithDirectStream(inner, reporter, options = {}) {
|
|
|
6768
6788
|
}
|
|
6769
6789
|
|
|
6770
6790
|
// src/execution/query-executor.ts
|
|
6771
|
-
import { createHash as createHash2 } from "crypto";
|
|
6772
|
-
import { existsSync, readFileSync as readFileSync2, truncateSync } from "fs";
|
|
6791
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
6792
|
+
import { existsSync, readFileSync as readFileSync2, renameSync, truncateSync } from "fs";
|
|
6773
6793
|
|
|
6774
6794
|
// src/execution/chat-instructions.ts
|
|
6775
6795
|
function buildChatInstructions(context, scenario, newMessages) {
|
|
@@ -7678,11 +7698,8 @@ function tagContextIntro(runnerMode) {
|
|
|
7678
7698
|
return `These docs match this task's tags. They auto-load when you edit matching files, and you can Read any of them the moment you need its detail \u2014 do NOT read them all up front.`;
|
|
7679
7699
|
}
|
|
7680
7700
|
function formatResolvedTags(resolved, subProject, mentioned, runnerMode) {
|
|
7681
|
-
const parts = [
|
|
7682
|
-
|
|
7683
|
-
## Reference Guides (load on demand)`,
|
|
7684
|
-
tagContextIntro(runnerMode)
|
|
7685
|
-
];
|
|
7701
|
+
const parts = [`
|
|
7702
|
+
## Reference Guides (load on demand)`, tagContextIntro(runnerMode)];
|
|
7686
7703
|
for (const tag of resolved) {
|
|
7687
7704
|
if (tag.entries.length === 0 && !tag.hasOverview) continue;
|
|
7688
7705
|
const desc = tag.description ? ` \u2014 ${tag.description}` : "";
|
|
@@ -8122,7 +8139,10 @@ function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
|
|
|
8122
8139
|
parts.push(`You were relaunched but no new instructions have been given since your last run.`);
|
|
8123
8140
|
if (agentMode === "auto" || agentMode === "building" || isAuto) {
|
|
8124
8141
|
parts.push(
|
|
8125
|
-
...builderKickoff(
|
|
8142
|
+
...builderKickoff(
|
|
8143
|
+
context,
|
|
8144
|
+
`You are the Builder on this card \u2014 pick up where you left off.`
|
|
8145
|
+
)
|
|
8126
8146
|
);
|
|
8127
8147
|
} else {
|
|
8128
8148
|
parts.push(
|
|
@@ -9405,7 +9425,9 @@ var driveListFilesContract = defineToolContract({
|
|
|
9405
9425
|
fields: {
|
|
9406
9426
|
folderId: f.optional(f.string({ desc: `Folder to list. ${ROOT_DEFAULT}` })),
|
|
9407
9427
|
search: f.optional(f.string({ desc: "Only return names containing this text", max: 200 })),
|
|
9408
|
-
limit: f.optional(
|
|
9428
|
+
limit: f.optional(
|
|
9429
|
+
f.number({ desc: "Max entries (default 100)", int: true, min: 1, max: 200 })
|
|
9430
|
+
)
|
|
9409
9431
|
}
|
|
9410
9432
|
},
|
|
9411
9433
|
mcp: {
|
|
@@ -9414,7 +9436,9 @@ var driveListFilesContract = defineToolContract({
|
|
|
9414
9436
|
projectId: mcpProjectId,
|
|
9415
9437
|
folderId: f.optional(f.string({ desc: `Folder to list. ${ROOT_DEFAULT}` })),
|
|
9416
9438
|
search: f.optional(f.string({ desc: "Only return names containing this text", max: 200 })),
|
|
9417
|
-
limit: f.optional(
|
|
9439
|
+
limit: f.optional(
|
|
9440
|
+
f.number({ desc: "Max entries (default 100)", int: true, min: 1, max: 200 })
|
|
9441
|
+
)
|
|
9418
9442
|
}
|
|
9419
9443
|
}
|
|
9420
9444
|
});
|
|
@@ -9517,7 +9541,12 @@ var listMeetingsContract = defineToolContract({
|
|
|
9517
9541
|
description: `List this project's meetings, newest first \u2014 each with its title, date, source, status, participants, and a short summary preview. Start here when you are asked about "the meeting", "what did we decide", or "what came out of that call". ${SUMMARY_NOTE}`,
|
|
9518
9542
|
fields: {
|
|
9519
9543
|
limit: f.optional(
|
|
9520
|
-
f.number({
|
|
9544
|
+
f.number({
|
|
9545
|
+
desc: "How many to return (default 20, maximum 50).",
|
|
9546
|
+
int: true,
|
|
9547
|
+
min: 1,
|
|
9548
|
+
max: 50
|
|
9549
|
+
})
|
|
9521
9550
|
),
|
|
9522
9551
|
search: f.optional(
|
|
9523
9552
|
f.string({ desc: "Case-insensitive match on the meeting title.", max: 200 })
|
|
@@ -9529,7 +9558,12 @@ var listMeetingsContract = defineToolContract({
|
|
|
9529
9558
|
fields: {
|
|
9530
9559
|
projectId: mcpProjectId,
|
|
9531
9560
|
limit: f.optional(
|
|
9532
|
-
f.number({
|
|
9561
|
+
f.number({
|
|
9562
|
+
desc: "How many to return (default 20, maximum 50).",
|
|
9563
|
+
int: true,
|
|
9564
|
+
min: 1,
|
|
9565
|
+
max: 50
|
|
9566
|
+
})
|
|
9533
9567
|
),
|
|
9534
9568
|
search: f.optional(
|
|
9535
9569
|
f.string({ desc: "Case-insensitive match on the meeting title.", max: 200 })
|
|
@@ -9583,9 +9617,7 @@ var SINCE_MINUTES = f.optional(
|
|
|
9583
9617
|
max: 10080
|
|
9584
9618
|
})
|
|
9585
9619
|
);
|
|
9586
|
-
var START_TIME = f.optional(
|
|
9587
|
-
f.string({ desc: "ISO 8601 lower bound (overrides sinceMinutes)" })
|
|
9588
|
-
);
|
|
9620
|
+
var START_TIME = f.optional(f.string({ desc: "ISO 8601 lower bound (overrides sinceMinutes)" }));
|
|
9589
9621
|
var END_TIME = f.optional(f.string({ desc: "ISO 8601 upper bound (default now)" }));
|
|
9590
9622
|
var LIMIT = f.optional(
|
|
9591
9623
|
f.number({ desc: "Max entries per page (default 50)", int: true, min: 1, max: 200 })
|
|
@@ -9619,7 +9651,10 @@ var gcpFields = {
|
|
|
9619
9651
|
})
|
|
9620
9652
|
),
|
|
9621
9653
|
search: f.optional(
|
|
9622
|
-
f.string({
|
|
9654
|
+
f.string({
|
|
9655
|
+
desc: "Free-text search across all log fields (exact substring, not regex)",
|
|
9656
|
+
max: 256
|
|
9657
|
+
})
|
|
9623
9658
|
),
|
|
9624
9659
|
filter: f.optional(
|
|
9625
9660
|
f.string({
|
|
@@ -11246,7 +11281,9 @@ var CONTEXT_PATH_SHAPE = z18.object({
|
|
|
11246
11281
|
locator: z18.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
|
|
11247
11282
|
'Verified-link tether: text that must keep existing in the file. With locatorType "test" it must appear inside a real it/test/describe TITLE; with "code" anywhere in the file. Validated at write time against the checkout and re-checked by the periodic sweep \u2014 a rename/delete flags the link stale. Locators containing <> are placeholders and never checked.'
|
|
11248
11283
|
),
|
|
11249
|
-
locatorType: z18.enum(["test", "code"]).optional().describe(
|
|
11284
|
+
locatorType: z18.enum(["test", "code"]).optional().describe(
|
|
11285
|
+
"How the locator must match \u2014 required iff locator is set; not valid on folder links"
|
|
11286
|
+
)
|
|
11250
11287
|
}).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
|
|
11251
11288
|
message: "locator and locatorType must be provided together"
|
|
11252
11289
|
}).refine((link) => link.locator === void 0 || link.type !== "folder", {
|
|
@@ -11640,52 +11677,43 @@ function buildDriveCreateFileTool(connection, projectId) {
|
|
|
11640
11677
|
);
|
|
11641
11678
|
}
|
|
11642
11679
|
function buildDriveUpdateFileTool(connection, projectId) {
|
|
11643
|
-
return defineContractTool(
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
} catch (error) {
|
|
11655
|
-
return errText2("Failed to update the Google Drive file", error);
|
|
11656
|
-
}
|
|
11680
|
+
return defineContractTool(driveUpdateFileContract, async ({ fileId, content, mimeType }) => {
|
|
11681
|
+
try {
|
|
11682
|
+
const file = await connection.call("updateProjectDriveFile", {
|
|
11683
|
+
projectId,
|
|
11684
|
+
fileId,
|
|
11685
|
+
content,
|
|
11686
|
+
mimeType
|
|
11687
|
+
});
|
|
11688
|
+
return textResult(`Updated "${file.name}" (${file.id})`);
|
|
11689
|
+
} catch (error) {
|
|
11690
|
+
return errText2("Failed to update the Google Drive file", error);
|
|
11657
11691
|
}
|
|
11658
|
-
);
|
|
11692
|
+
});
|
|
11659
11693
|
}
|
|
11660
11694
|
function buildDriveDeleteFileTool(connection, projectId) {
|
|
11661
|
-
return defineContractTool(
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
} catch (error) {
|
|
11668
|
-
return errText2("Failed to delete the Google Drive file", error);
|
|
11669
|
-
}
|
|
11695
|
+
return defineContractTool(driveDeleteFileContract, async ({ fileId }) => {
|
|
11696
|
+
try {
|
|
11697
|
+
const result = await connection.call("deleteProjectDriveFile", { projectId, fileId });
|
|
11698
|
+
return textResult(`Moved "${result.name}" (${result.id}) to the Google Drive trash`);
|
|
11699
|
+
} catch (error) {
|
|
11700
|
+
return errText2("Failed to delete the Google Drive file", error);
|
|
11670
11701
|
}
|
|
11671
|
-
);
|
|
11702
|
+
});
|
|
11672
11703
|
}
|
|
11673
11704
|
function buildDriveCreateFolderTool(connection, projectId) {
|
|
11674
|
-
return defineContractTool(
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
} catch (error) {
|
|
11685
|
-
return errText2("Failed to create the Google Drive folder", error);
|
|
11686
|
-
}
|
|
11705
|
+
return defineContractTool(driveCreateFolderContract, async ({ name, folderId }) => {
|
|
11706
|
+
try {
|
|
11707
|
+
const folder = await connection.call("createProjectDriveFolder", {
|
|
11708
|
+
projectId,
|
|
11709
|
+
name,
|
|
11710
|
+
folderId
|
|
11711
|
+
});
|
|
11712
|
+
return textResult(`Created folder "${folder.name}" (${folder.id})`);
|
|
11713
|
+
} catch (error) {
|
|
11714
|
+
return errText2("Failed to create the Google Drive folder", error);
|
|
11687
11715
|
}
|
|
11688
|
-
);
|
|
11716
|
+
});
|
|
11689
11717
|
}
|
|
11690
11718
|
function buildDriveTools(connection, projectId) {
|
|
11691
11719
|
return [
|
|
@@ -12524,16 +12552,22 @@ function questionTextFromEvent(event) {
|
|
|
12524
12552
|
}
|
|
12525
12553
|
async function armQuestionPending(host, state, event) {
|
|
12526
12554
|
state.questionPending = true;
|
|
12555
|
+
const questionText = questionTextFromEvent(event);
|
|
12556
|
+
host.callbacks.onUserQuestion?.({
|
|
12557
|
+
kind: "armed",
|
|
12558
|
+
...questionText === void 0 ? {} : { questionText }
|
|
12559
|
+
});
|
|
12527
12560
|
await host.connection.emitStatus(
|
|
12528
12561
|
"waiting_for_input",
|
|
12529
12562
|
AGENT_STATUS_REASON_USER_QUESTION,
|
|
12530
|
-
|
|
12563
|
+
questionText
|
|
12531
12564
|
);
|
|
12532
12565
|
await host.callbacks.onStatusChange("waiting_for_input");
|
|
12533
12566
|
}
|
|
12534
12567
|
async function clearQuestionPending(host, state, options) {
|
|
12535
12568
|
if (!state.questionPending) return;
|
|
12536
12569
|
state.questionPending = false;
|
|
12570
|
+
host.callbacks.onUserQuestion?.({ kind: options.emitRunning ? "answered" : "turn_ended" });
|
|
12537
12571
|
if (options.emitRunning) {
|
|
12538
12572
|
await host.connection.emitStatus("running");
|
|
12539
12573
|
await host.callbacks.onStatusChange("running");
|
|
@@ -13681,6 +13715,29 @@ async function runInitialQuery(host, context, options, resume, promptDelivery) {
|
|
|
13681
13715
|
}
|
|
13682
13716
|
await trackAndRun(host, context, queryOptions, agentQuery);
|
|
13683
13717
|
}
|
|
13718
|
+
function rotateSessionFile(cwd, sessionUuid) {
|
|
13719
|
+
const path = sessionTranscriptPath(cwd, sessionUuid);
|
|
13720
|
+
try {
|
|
13721
|
+
if (!existsSync(path)) return true;
|
|
13722
|
+
renameSync(path, `${path.replace(/\.jsonl$/, "")}.aborted-${Date.now()}.jsonl`);
|
|
13723
|
+
return true;
|
|
13724
|
+
} catch (error) {
|
|
13725
|
+
logger5.warn("Could not rotate an aborted transcript aside", {
|
|
13726
|
+
path,
|
|
13727
|
+
error: error instanceof Error ? error.message : String(error)
|
|
13728
|
+
});
|
|
13729
|
+
return false;
|
|
13730
|
+
}
|
|
13731
|
+
}
|
|
13732
|
+
function freshSessionOptions(host, context, options) {
|
|
13733
|
+
const lineageUuid = taskIdToSessionUuid(
|
|
13734
|
+
sessionLineageKey(context.taskId, host.agentMode, host.config.mode)
|
|
13735
|
+
);
|
|
13736
|
+
if (rotateSessionFile(host.config.workspaceDir, lineageUuid)) {
|
|
13737
|
+
return { ...options, sessionId: lineageUuid };
|
|
13738
|
+
}
|
|
13739
|
+
return { ...options, sessionId: randomUUID2() };
|
|
13740
|
+
}
|
|
13684
13741
|
async function buildRetryQuery(host, context, options, lastErrorWasImage) {
|
|
13685
13742
|
if (lastErrorWasImage) {
|
|
13686
13743
|
host.connection.postChatMessage(
|
|
@@ -13700,10 +13757,7 @@ async function buildRetryQuery(host, context, options, lastErrorWasImage) {
|
|
|
13700
13757
|
);
|
|
13701
13758
|
return host.harness.executeQuery({
|
|
13702
13759
|
prompt: host.createInputStream(retryPrompt),
|
|
13703
|
-
|
|
13704
|
-
// session with that ID, passing it again would error. Let the SDK
|
|
13705
|
-
// auto-generate on retry attempts.
|
|
13706
|
-
options: { ...options, sessionId: void 0 },
|
|
13760
|
+
options: freshSessionOptions(host, context, options),
|
|
13707
13761
|
resume: void 0
|
|
13708
13762
|
});
|
|
13709
13763
|
}
|
|
@@ -13735,7 +13789,7 @@ async function handleAuthError(context, host, options) {
|
|
|
13735
13789
|
);
|
|
13736
13790
|
const freshQuery = host.harness.executeQuery({
|
|
13737
13791
|
prompt: host.createInputStream(freshPrompt),
|
|
13738
|
-
options:
|
|
13792
|
+
options: freshSessionOptions(host, context, options),
|
|
13739
13793
|
resume: void 0
|
|
13740
13794
|
});
|
|
13741
13795
|
return runWithRetry(freshQuery, context, host, options);
|
|
@@ -13756,7 +13810,7 @@ async function handleStaleSession(context, host, options) {
|
|
|
13756
13810
|
);
|
|
13757
13811
|
const freshQuery = host.harness.executeQuery({
|
|
13758
13812
|
prompt: host.createInputStream(freshPrompt),
|
|
13759
|
-
options:
|
|
13813
|
+
options: freshSessionOptions(host, context, options),
|
|
13760
13814
|
resume: void 0
|
|
13761
13815
|
});
|
|
13762
13816
|
return runWithRetry(freshQuery, context, host, options);
|
|
@@ -13856,7 +13910,7 @@ async function handleUsageCapRejection(context, host, options, rateLimitType, re
|
|
|
13856
13910
|
);
|
|
13857
13911
|
const freshQuery = host.harness.executeQuery({
|
|
13858
13912
|
prompt: host.createInputStream(freshPrompt),
|
|
13859
|
-
options:
|
|
13913
|
+
options: freshSessionOptions(host, context, options),
|
|
13860
13914
|
resume: void 0
|
|
13861
13915
|
});
|
|
13862
13916
|
return runWithRetry(freshQuery, context, host, options);
|
|
@@ -14654,10 +14708,8 @@ var BackgroundWorkTracker = class {
|
|
|
14654
14708
|
this.prune(now);
|
|
14655
14709
|
if (this.deadlines.length === 0) return;
|
|
14656
14710
|
this.deadlines.shift();
|
|
14657
|
-
this.log(
|
|
14658
|
-
|
|
14659
|
-
`
|
|
14660
|
-
);
|
|
14711
|
+
this.log(`[conveyor-agent] background work finished; ${this.deadlines.length} outstanding
|
|
14712
|
+
`);
|
|
14661
14713
|
this.onChange?.();
|
|
14662
14714
|
}
|
|
14663
14715
|
/** Is any non-expired background work outstanding? */
|
|
@@ -14707,6 +14759,15 @@ function findLiveChild(sources) {
|
|
|
14707
14759
|
var AUTO_RUN_MODES = /* @__PURE__ */ new Set(["building", "auto", "review", "discovery", "chat"]);
|
|
14708
14760
|
var CHILD_DEFER_RECHECK_MS = 60 * 1e3;
|
|
14709
14761
|
var ORPHAN_WAKE_GRACE_MS = 3 * 60 * 1e3;
|
|
14762
|
+
var QUESTION_GRACE_MS = 10 * 6e4;
|
|
14763
|
+
var QUESTION_HOLD_SLACK_MS = 6e4;
|
|
14764
|
+
function resolveQuestionGraceMs() {
|
|
14765
|
+
const raw = Number(process.env.CONVEYOR_QUESTION_GRACE_MS ?? "");
|
|
14766
|
+
return Number.isFinite(raw) && raw > 0 ? raw : QUESTION_GRACE_MS;
|
|
14767
|
+
}
|
|
14768
|
+
var QUESTION_TYPING_RECENT_MS = 6e4;
|
|
14769
|
+
var QUESTION_TYPING_DEFER_MS = 2 * 6e4;
|
|
14770
|
+
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.';
|
|
14710
14771
|
var SessionRunner = class _SessionRunner {
|
|
14711
14772
|
connection;
|
|
14712
14773
|
mode;
|
|
@@ -14760,6 +14821,18 @@ var SessionRunner = class _SessionRunner {
|
|
|
14760
14821
|
/** Guards the one-shot `agent_live` boot-milestone report — fired on the
|
|
14761
14822
|
* first harness event (createQueryBridge's onEvent hook). */
|
|
14762
14823
|
agentLiveReported = false;
|
|
14824
|
+
/** Epoch ms since a TUI questionnaire has been parked on a human with no
|
|
14825
|
+
* answer; null when none is pending. While set (and within the grace +
|
|
14826
|
+
* slack bound) `resolveLoopStatus` reports `waiting`, which reaches the API
|
|
14827
|
+
* as `active` and keeps the workspace activity clock fresh — the pod must
|
|
14828
|
+
* not be slept out from under an unanswered question. Deliberately KEPT
|
|
14829
|
+
* when the turn ends with the questionnaire still parked (the Shape-B
|
|
14830
|
+
* silent death this hold exists for). */
|
|
14831
|
+
questionPendingSince = null;
|
|
14832
|
+
/** Single-shot unref'd timer driving `handleQuestionGraceExpiry`. The
|
|
14833
|
+
* liveness hold above is bounded by timestamp arithmetic, so a lost timer
|
|
14834
|
+
* still expires the hold — it just skips the un-park injection. */
|
|
14835
|
+
questionGraceTimer = null;
|
|
14763
14836
|
constructor(config, callbacks, deps = {}) {
|
|
14764
14837
|
this.config = config;
|
|
14765
14838
|
this.callbacks = callbacks;
|
|
@@ -14886,8 +14959,10 @@ var SessionRunner = class _SessionRunner {
|
|
|
14886
14959
|
if (this.backgroundWork.hasPending()) holding = "background work outstanding";
|
|
14887
14960
|
else if (isHeavyGateActive()) holding = "a gate process is running";
|
|
14888
14961
|
} catch (err) {
|
|
14889
|
-
process.stderr.write(
|
|
14890
|
-
`
|
|
14962
|
+
process.stderr.write(
|
|
14963
|
+
`[conveyor-agent] Background-work probe failed, not deferring: ${err}
|
|
14964
|
+
`
|
|
14965
|
+
);
|
|
14891
14966
|
return false;
|
|
14892
14967
|
}
|
|
14893
14968
|
if (!holding) {
|
|
@@ -15460,6 +15535,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15460
15535
|
}
|
|
15461
15536
|
/** Inject a message (from connection callback or external source) */
|
|
15462
15537
|
injectMessage(msg) {
|
|
15538
|
+
this.clearQuestionHold();
|
|
15463
15539
|
if (this.inputResolver) {
|
|
15464
15540
|
const resolve = this.inputResolver;
|
|
15465
15541
|
this.inputResolver = null;
|
|
@@ -15667,6 +15743,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15667
15743
|
}
|
|
15668
15744
|
stop() {
|
|
15669
15745
|
this.stopped = true;
|
|
15746
|
+
this.clearQuestionHold();
|
|
15670
15747
|
this.queryBridge?.stop();
|
|
15671
15748
|
void this.queryBridge?.dispose().catch(() => {
|
|
15672
15749
|
});
|
|
@@ -15761,7 +15838,8 @@ var SessionRunner = class _SessionRunner {
|
|
|
15761
15838
|
void this.connection.sendHeartbeat();
|
|
15762
15839
|
}
|
|
15763
15840
|
return this.callbacks.onEvent(event);
|
|
15764
|
-
}
|
|
15841
|
+
},
|
|
15842
|
+
onUserQuestion: (signal) => this.handleUserQuestionSignal(signal)
|
|
15765
15843
|
});
|
|
15766
15844
|
bridge.isParentTask = this.fullContext?.isParentTask ?? false;
|
|
15767
15845
|
bridge.onBackgroundTaskDone = () => this.backgroundWork.noteCompletion();
|
|
@@ -15912,8 +15990,72 @@ ${outcome.failures.join("\n")}
|
|
|
15912
15990
|
*/
|
|
15913
15991
|
resolveLoopStatus() {
|
|
15914
15992
|
if (loopStatusForRunnerStatus(this._state) === "active") return "active";
|
|
15993
|
+
if (this.questionPendingSince !== null && Date.now() - this.questionPendingSince < resolveQuestionGraceMs() + QUESTION_HOLD_SLACK_MS) {
|
|
15994
|
+
return "waiting";
|
|
15995
|
+
}
|
|
15915
15996
|
return this.backgroundWork.hasPending() || isHeavyGateActive() ? "waiting" : "idle";
|
|
15916
15997
|
}
|
|
15998
|
+
// ── Question grace hold ────────────────────────────────────────────
|
|
15999
|
+
/**
|
|
16000
|
+
* Question-pending transitions from the event loop. `armed` starts the
|
|
16001
|
+
* bounded liveness hold + the grace timer; `answered` clears both.
|
|
16002
|
+
* `turn_ended` — the turn's result arrived with the questionnaire still
|
|
16003
|
+
* parked — deliberately KEEPS them: that is the Shape-B silent death
|
|
16004
|
+
* (idle heartbeats stop bumping the activity clock and the reconciler
|
|
16005
|
+
* sleeps the pod minutes later) this hold exists to prevent.
|
|
16006
|
+
*/
|
|
16007
|
+
handleUserQuestionSignal(signal) {
|
|
16008
|
+
if (signal.kind === "armed") {
|
|
16009
|
+
this.questionPendingSince = Date.now();
|
|
16010
|
+
this.armQuestionGraceTimer(resolveQuestionGraceMs());
|
|
16011
|
+
this.refreshLoopStatus();
|
|
16012
|
+
} else if (signal.kind === "answered") {
|
|
16013
|
+
this.clearQuestionHold();
|
|
16014
|
+
}
|
|
16015
|
+
}
|
|
16016
|
+
armQuestionGraceTimer(delayMs) {
|
|
16017
|
+
if (this.questionGraceTimer) clearTimeout(this.questionGraceTimer);
|
|
16018
|
+
this.questionGraceTimer = setTimeout(() => this.handleQuestionGraceExpiry(), delayMs);
|
|
16019
|
+
this.questionGraceTimer.unref();
|
|
16020
|
+
}
|
|
16021
|
+
clearQuestionHold() {
|
|
16022
|
+
if (this.questionGraceTimer) {
|
|
16023
|
+
clearTimeout(this.questionGraceTimer);
|
|
16024
|
+
this.questionGraceTimer = null;
|
|
16025
|
+
}
|
|
16026
|
+
if (this.questionPendingSince === null) return;
|
|
16027
|
+
this.questionPendingSince = null;
|
|
16028
|
+
this.refreshLoopStatus();
|
|
16029
|
+
}
|
|
16030
|
+
/**
|
|
16031
|
+
* The grace expired with the questionnaire still unanswered. Un-park the
|
|
16032
|
+
* agent by injecting the timeout prompt as a superseding system message —
|
|
16033
|
+
* the proven supersede path (mid-turn: queue + queryBridge.stop(), teardown
|
|
16034
|
+
* kills the parked dialog and the respawn `--resume`s cleanly; idle: the
|
|
16035
|
+
* inputResolver picks it up). Never synthetic keypresses: Enter would
|
|
16036
|
+
* fabricate an answer and Esc handling is CLI-version-dependent.
|
|
16037
|
+
*
|
|
16038
|
+
* A human actively typing in the Connected-TUI defers the expiry — they may
|
|
16039
|
+
* be mid-answer, and superseding would yank the dialog out from under them.
|
|
16040
|
+
*/
|
|
16041
|
+
handleQuestionGraceExpiry() {
|
|
16042
|
+
this.questionGraceTimer = null;
|
|
16043
|
+
if (this.questionPendingSince === null || this.stopped) return;
|
|
16044
|
+
const lastInput = this.connection.lastPtyInputAt;
|
|
16045
|
+
if (lastInput !== null && Date.now() - lastInput < QUESTION_TYPING_RECENT_MS) {
|
|
16046
|
+
this.questionPendingSince = Date.now();
|
|
16047
|
+
this.armQuestionGraceTimer(QUESTION_TYPING_DEFER_MS);
|
|
16048
|
+
return;
|
|
16049
|
+
}
|
|
16050
|
+
process.stderr.write(
|
|
16051
|
+
"[conveyor-agent] Question grace expired with no answer \u2014 un-parking the agent\n"
|
|
16052
|
+
);
|
|
16053
|
+
this.injectMessage({
|
|
16054
|
+
content: QUESTION_TIMEOUT_PROMPT,
|
|
16055
|
+
userId: "system",
|
|
16056
|
+
source: "question_timeout"
|
|
16057
|
+
});
|
|
16058
|
+
}
|
|
15917
16059
|
/** Mirror the resolved status into the loop-lag buffer so the starvation-proof
|
|
15918
16060
|
* heartbeat worker reports it too. Returns what it wrote. */
|
|
15919
16061
|
refreshLoopStatus() {
|
|
@@ -15942,6 +16084,7 @@ ${outcome.failures.join("\n")}
|
|
|
15942
16084
|
process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
|
|
15943
16085
|
`);
|
|
15944
16086
|
this.connection.sendEvent({ type: "shutdown", reason: finalState });
|
|
16087
|
+
this.clearQuestionHold();
|
|
15945
16088
|
this.portDiscovery.stop();
|
|
15946
16089
|
this.backgroundWork.clear();
|
|
15947
16090
|
this.loopLag.stop();
|
|
@@ -287,6 +287,10 @@ var AgentConnection = class _AgentConnection {
|
|
|
287
287
|
// PTY relay (S5 terminal). Single-slot callbacks, set per PtySession run.
|
|
288
288
|
ptyInputCallback = null;
|
|
289
289
|
ptyResizeCallback = null;
|
|
290
|
+
/** Epoch ms of the last relayed keystroke, or null before any input. The
|
|
291
|
+
* question-grace expiry reads it to defer un-parking while a human is
|
|
292
|
+
* actively typing into the Connected-TUI (they may be mid-answer). */
|
|
293
|
+
_lastPtyInputAt = null;
|
|
290
294
|
constructor(config) {
|
|
291
295
|
this.config = config;
|
|
292
296
|
}
|
|
@@ -475,6 +479,7 @@ var AgentConnection = class _AgentConnection {
|
|
|
475
479
|
});
|
|
476
480
|
this.socket.on("pty:input", (data) => {
|
|
477
481
|
if (data.sessionId && data.sessionId !== this.config.sessionId) return;
|
|
482
|
+
this._lastPtyInputAt = Date.now();
|
|
478
483
|
this.ptyInputCallback?.(data.data);
|
|
479
484
|
});
|
|
480
485
|
this.socket.on("pty:resize", (data) => {
|
|
@@ -925,6 +930,10 @@ var AgentConnection = class _AgentConnection {
|
|
|
925
930
|
}).catch(() => {
|
|
926
931
|
});
|
|
927
932
|
}
|
|
933
|
+
/** Epoch ms of the last relayed TUI keystroke, or null before any. */
|
|
934
|
+
get lastPtyInputAt() {
|
|
935
|
+
return this._lastPtyInputAt;
|
|
936
|
+
}
|
|
928
937
|
/** Subscribe to relayed keystrokes. Returns an unsubscribe fn. */
|
|
929
938
|
onPtyInput(handler) {
|
|
930
939
|
this.ptyInputCallback = handler;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-W4LZ7R6Z.js";
|
|
5
5
|
|
|
6
6
|
// src/harness/pty/pty-support.ts
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
7
8
|
import { stat } from "fs/promises";
|
|
8
9
|
import { tmpdir } from "os";
|
|
9
10
|
|
|
@@ -153,6 +154,10 @@ function resolvePlanDialogTiming() {
|
|
|
153
154
|
windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
|
|
154
155
|
};
|
|
155
156
|
}
|
|
157
|
+
function ensureSessionTarget(options, want, mintId = randomUUID) {
|
|
158
|
+
if (want || options.sessionId) return options;
|
|
159
|
+
return { ...options, sessionId: mintId() };
|
|
160
|
+
}
|
|
156
161
|
function turnOptionsFrom(options) {
|
|
157
162
|
return {
|
|
158
163
|
canUseTool: options.canUseTool,
|
|
@@ -278,6 +283,7 @@ export {
|
|
|
278
283
|
resolveSubmitNudgeTiming,
|
|
279
284
|
resolveSubmitRedeliveryMaxAttempts,
|
|
280
285
|
resolvePlanDialogTiming,
|
|
286
|
+
ensureSessionTarget,
|
|
281
287
|
turnOptionsFrom,
|
|
282
288
|
killPtyWithEscalation,
|
|
283
289
|
loadPtySpawn,
|
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-UHMCBCQL.js";
|
|
11
11
|
import {
|
|
12
12
|
DEFAULT_SONNET_MODEL,
|
|
13
13
|
PtyHarness,
|
|
@@ -27,13 +27,13 @@ import {
|
|
|
27
27
|
resolveTuiKindFromEnv,
|
|
28
28
|
runUsageProbe,
|
|
29
29
|
sampleKeyUsage
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GUYMLASA.js";
|
|
31
31
|
import "./chunk-XORJ6SII.js";
|
|
32
32
|
import {
|
|
33
33
|
inheritedEnv,
|
|
34
34
|
resolvePtySpawn,
|
|
35
35
|
sessionTempBase
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-W6VILPQE.js";
|
|
37
37
|
import {
|
|
38
38
|
AgentConnection,
|
|
39
39
|
CodespacePortVisibility,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-HFUVAHII.js";
|
|
48
48
|
import "./chunk-GL2DIQEQ.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
50
|
import "./chunk-7P6QZHXZ.js";
|
|
@@ -1201,7 +1201,7 @@ function wireSpawnChildren(mode, connection, supervisors, logger7) {
|
|
|
1201
1201
|
|
|
1202
1202
|
// src/cli.ts
|
|
1203
1203
|
if (process.argv[2] === "boot") {
|
|
1204
|
-
const { runBoot } = await import("./boot-
|
|
1204
|
+
const { runBoot } = await import("./boot-7XH4FFLO.js");
|
|
1205
1205
|
process.exit(await runBoot(process.argv.slice(3)));
|
|
1206
1206
|
}
|
|
1207
1207
|
if (isLegacyEntrypointLaunch(process.env)) {
|
|
@@ -1303,7 +1303,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
1303
1303
|
process.exit(1);
|
|
1304
1304
|
});
|
|
1305
1305
|
if (process.env.CONVEYOR_MODE === "workbench") {
|
|
1306
|
-
const { startWorkbenchServer } = await import("./server-
|
|
1306
|
+
const { startWorkbenchServer } = await import("./server-2Z5V3IPA.js");
|
|
1307
1307
|
const { oomWatchdogOptionsFromEnv } = await import("./oom-watchdog-PAC5OJJG.js");
|
|
1308
1308
|
const { DEFAULT_WORKBENCH_PORT } = await import("./protocol-QBCYO4GI.js");
|
|
1309
1309
|
const port = Number(process.env.CONVEYOR_WORKBENCH_PORT) || DEFAULT_WORKBENCH_PORT;
|
|
@@ -1454,7 +1454,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
|
|
|
1454
1454
|
process.exit(1);
|
|
1455
1455
|
}
|
|
1456
1456
|
if (CONVEYOR_MODE === "serving") {
|
|
1457
|
-
const { runServingSession } = await import("./serve-boot-
|
|
1457
|
+
const { runServingSession } = await import("./serve-boot-IFUIGFEH.js");
|
|
1458
1458
|
exitContext.runnerMode = "serving";
|
|
1459
1459
|
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
|
|
1460
1460
|
const outcome = await runServingSession({
|
package/dist/gate-cli.js
CHANGED
|
@@ -79,7 +79,9 @@ function runGate(argv, options = {}) {
|
|
|
79
79
|
try {
|
|
80
80
|
process.exitCode = await runGate(process.argv.slice(2));
|
|
81
81
|
} catch (error) {
|
|
82
|
-
process.stderr.write(
|
|
83
|
-
`)
|
|
82
|
+
process.stderr.write(
|
|
83
|
+
`conveyor-gate: ${error instanceof Error ? error.message : String(error)}
|
|
84
|
+
`
|
|
85
|
+
);
|
|
84
86
|
process.exitCode = 2;
|
|
85
87
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -131,6 +131,10 @@ declare class AgentConnection {
|
|
|
131
131
|
private earlyProbeUsage;
|
|
132
132
|
private ptyInputCallback;
|
|
133
133
|
private ptyResizeCallback;
|
|
134
|
+
/** Epoch ms of the last relayed keystroke, or null before any input. The
|
|
135
|
+
* question-grace expiry reads it to defer un-parking while a human is
|
|
136
|
+
* actively typing into the Connected-TUI (they may be mid-answer). */
|
|
137
|
+
private _lastPtyInputAt;
|
|
134
138
|
constructor(config: AgentConnectionConfig);
|
|
135
139
|
get sessionId(): string;
|
|
136
140
|
get connected(): boolean;
|
|
@@ -282,6 +286,8 @@ declare class AgentConnection {
|
|
|
282
286
|
* just stays on the relay transport.
|
|
283
287
|
*/
|
|
284
288
|
reportPtyStream(port: number | null): void;
|
|
289
|
+
/** Epoch ms of the last relayed TUI keystroke, or null before any. */
|
|
290
|
+
get lastPtyInputAt(): number | null;
|
|
285
291
|
/** Subscribe to relayed keystrokes. Returns an unsubscribe fn. */
|
|
286
292
|
onPtyInput(handler: (data: string) => void): () => void;
|
|
287
293
|
/** Subscribe to relayed (reconciled) terminal resizes. Returns an unsubscribe fn. */
|
|
@@ -729,6 +735,18 @@ declare class SessionRunner {
|
|
|
729
735
|
/** Guards the one-shot `agent_live` boot-milestone report — fired on the
|
|
730
736
|
* first harness event (createQueryBridge's onEvent hook). */
|
|
731
737
|
private agentLiveReported;
|
|
738
|
+
/** Epoch ms since a TUI questionnaire has been parked on a human with no
|
|
739
|
+
* answer; null when none is pending. While set (and within the grace +
|
|
740
|
+
* slack bound) `resolveLoopStatus` reports `waiting`, which reaches the API
|
|
741
|
+
* as `active` and keeps the workspace activity clock fresh — the pod must
|
|
742
|
+
* not be slept out from under an unanswered question. Deliberately KEPT
|
|
743
|
+
* when the turn ends with the questionnaire still parked (the Shape-B
|
|
744
|
+
* silent death this hold exists for). */
|
|
745
|
+
private questionPendingSince;
|
|
746
|
+
/** Single-shot unref'd timer driving `handleQuestionGraceExpiry`. The
|
|
747
|
+
* liveness hold above is bounded by timestamp arithmetic, so a lost timer
|
|
748
|
+
* still expires the hold — it just skips the un-park injection. */
|
|
749
|
+
private questionGraceTimer;
|
|
732
750
|
constructor(config: SessionRunnerConfig, callbacks: SessionRunnerCallbacks, deps?: SessionRunnerDependencies);
|
|
733
751
|
get state(): AgentRunnerStatus;
|
|
734
752
|
/**
|
|
@@ -1016,6 +1034,29 @@ declare class SessionRunner {
|
|
|
1016
1034
|
* for why an unknown status still fails open to `active`.
|
|
1017
1035
|
*/
|
|
1018
1036
|
private resolveLoopStatus;
|
|
1037
|
+
/**
|
|
1038
|
+
* Question-pending transitions from the event loop. `armed` starts the
|
|
1039
|
+
* bounded liveness hold + the grace timer; `answered` clears both.
|
|
1040
|
+
* `turn_ended` — the turn's result arrived with the questionnaire still
|
|
1041
|
+
* parked — deliberately KEEPS them: that is the Shape-B silent death
|
|
1042
|
+
* (idle heartbeats stop bumping the activity clock and the reconciler
|
|
1043
|
+
* sleeps the pod minutes later) this hold exists to prevent.
|
|
1044
|
+
*/
|
|
1045
|
+
private handleUserQuestionSignal;
|
|
1046
|
+
private armQuestionGraceTimer;
|
|
1047
|
+
private clearQuestionHold;
|
|
1048
|
+
/**
|
|
1049
|
+
* The grace expired with the questionnaire still unanswered. Un-park the
|
|
1050
|
+
* agent by injecting the timeout prompt as a superseding system message —
|
|
1051
|
+
* the proven supersede path (mid-turn: queue + queryBridge.stop(), teardown
|
|
1052
|
+
* kills the parked dialog and the respawn `--resume`s cleanly; idle: the
|
|
1053
|
+
* inputResolver picks it up). Never synthetic keypresses: Enter would
|
|
1054
|
+
* fabricate an answer and Esc handling is CLI-version-dependent.
|
|
1055
|
+
*
|
|
1056
|
+
* A human actively typing in the Connected-TUI defers the expiry — they may
|
|
1057
|
+
* be mid-answer, and superseding would yank the dialog out from under them.
|
|
1058
|
+
*/
|
|
1059
|
+
private handleQuestionGraceExpiry;
|
|
1019
1060
|
/** Mirror the resolved status into the loop-lag buffer so the starvation-proof
|
|
1020
1061
|
* heartbeat worker reports it too. Returns what it wrote. */
|
|
1021
1062
|
private refreshLoopStatus;
|
|
@@ -1055,9 +1096,31 @@ interface AgentRunnerConfig {
|
|
|
1055
1096
|
*/
|
|
1056
1097
|
packExecution?: _project_shared.PackExecution;
|
|
1057
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Question-pending transitions surfaced to the runner by the event loop
|
|
1101
|
+
* (`applyQuestionTransitions` in execution/event-processor.ts):
|
|
1102
|
+
* - `armed` — the PTY hook observed an AskUserQuestion; a questionnaire is
|
|
1103
|
+
* parked on a human.
|
|
1104
|
+
* - `answered` — the questionnaire resolved (PostToolUse envelope, or a new
|
|
1105
|
+
* assistant record without the question block).
|
|
1106
|
+
* - `turn_ended` — the turn's `result` arrived while the questionnaire was
|
|
1107
|
+
* still parked. The dialog is still on screen with nobody answering — the
|
|
1108
|
+
* silent-death shape the question grace hold exists for — so the runner
|
|
1109
|
+
* must KEEP its pending hold rather than clear it.
|
|
1110
|
+
*/
|
|
1111
|
+
type UserQuestionSignal = {
|
|
1112
|
+
kind: "armed";
|
|
1113
|
+
questionText?: string;
|
|
1114
|
+
} | {
|
|
1115
|
+
kind: "answered";
|
|
1116
|
+
} | {
|
|
1117
|
+
kind: "turn_ended";
|
|
1118
|
+
};
|
|
1058
1119
|
interface AgentRunnerCallbacks {
|
|
1059
1120
|
onEvent: (event: Record<string, unknown>) => void | Promise<void>;
|
|
1060
1121
|
onStatusChange: (status: string) => void | Promise<void>;
|
|
1122
|
+
/** Optional — fired on question-pending transitions (see UserQuestionSignal). */
|
|
1123
|
+
onUserQuestion?: (signal: UserQuestionSignal) => void;
|
|
1061
1124
|
}
|
|
1062
1125
|
|
|
1063
1126
|
interface GithubTokenFileSync {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GUYMLASA.js";
|
|
5
5
|
import "./chunk-XORJ6SII.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-W6VILPQE.js";
|
|
7
7
|
import {
|
|
8
8
|
AgentConnection,
|
|
9
9
|
GIT_TIMEOUT_MS,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-HFUVAHII.js";
|
|
21
21
|
import "./chunk-GL2DIQEQ.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UHMCBCQL.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-HFUVAHII.js";
|
|
15
15
|
import "./chunk-GL2DIQEQ.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.6",
|
|
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",
|