@rallycry/conveyor-agent 11.0.25 → 11.0.27
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-RLROQPPT.js → chunk-7HK6OIV4.js} +164 -25
- package/dist/{chunk-BZZ4B37Q.js → chunk-PBSFLQA7.js} +47 -1
- package/dist/{chunk-7WO6NTOS.js → chunk-SI7WNWZZ.js} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{serve-boot-T7MNWASE.js → serve-boot-WGUFEWYJ.js} +2 -2
- package/package.json +1 -1
- package/skills/conveyor-build/SKILL.md +9 -2
- package/skills/conveyor-plan/SKILL.md +4 -0
- package/skills/conveyor-triage/SKILL.md +3 -1
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
hasTaskPlan,
|
|
75
75
|
hasUncommittedChanges,
|
|
76
76
|
hasUnpushedCommits,
|
|
77
|
+
isCapabilityServableMime,
|
|
77
78
|
isCodexReasoningEffort,
|
|
78
79
|
isPlaceholderLocator,
|
|
79
80
|
locatorMatchesContent,
|
|
@@ -91,7 +92,7 @@ import {
|
|
|
91
92
|
statWorkspacePath,
|
|
92
93
|
updateRemoteToken,
|
|
93
94
|
verifyGitCredential
|
|
94
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-PBSFLQA7.js";
|
|
95
96
|
import {
|
|
96
97
|
registerBootMilestoneSocketFallback,
|
|
97
98
|
reportBootMilestone
|
|
@@ -5312,7 +5313,16 @@ function wrapBridgeWithDirectStream(inner, reporter, options = {}) {
|
|
|
5312
5313
|
|
|
5313
5314
|
// src/execution/query-executor.ts
|
|
5314
5315
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
5315
|
-
import {
|
|
5316
|
+
import {
|
|
5317
|
+
closeSync,
|
|
5318
|
+
existsSync,
|
|
5319
|
+
openSync,
|
|
5320
|
+
readFileSync as readFileSync3,
|
|
5321
|
+
readSync,
|
|
5322
|
+
renameSync as renameSync2,
|
|
5323
|
+
statSync as statSync2,
|
|
5324
|
+
truncateSync
|
|
5325
|
+
} from "fs";
|
|
5316
5326
|
|
|
5317
5327
|
// src/execution/chat-instructions.ts
|
|
5318
5328
|
function buildChatInstructions(context, scenario, newMessages) {
|
|
@@ -5431,6 +5441,20 @@ function formatChatFile(file) {
|
|
|
5431
5441
|
}
|
|
5432
5442
|
return [`[Attached: ${file.fileName} (${file.mimeType}${sizeStr})]`];
|
|
5433
5443
|
}
|
|
5444
|
+
function formatParentTaskFiles(parent) {
|
|
5445
|
+
const lines = [
|
|
5446
|
+
`
|
|
5447
|
+
## Files on the parent card ${parent.slug}`,
|
|
5448
|
+
`These are attached to the parent card "${parent.title}", not to this one. Read one with get_attachment(fileId, task_id: "${parent.slug}"); list_task_files(task_id: "${parent.slug}") shows the current set.`
|
|
5449
|
+
];
|
|
5450
|
+
for (const file of parent.files) {
|
|
5451
|
+
const size = formatFileSize(file.fileSize);
|
|
5452
|
+
lines.push(
|
|
5453
|
+
`- **${file.fileName}** (${file.mimeType}${size ? `, ${size}` : ""}) fileId: ${file.fileId}`
|
|
5454
|
+
);
|
|
5455
|
+
}
|
|
5456
|
+
return lines;
|
|
5457
|
+
}
|
|
5434
5458
|
function formatTaskFile(file) {
|
|
5435
5459
|
if (file.content && file.contentEncoding === "utf-8") {
|
|
5436
5460
|
return [`
|
|
@@ -5783,7 +5807,7 @@ var PACK_RUNNER_FOOTER = [
|
|
|
5783
5807
|
``,
|
|
5784
5808
|
`Your turn output appears ONLY in the live agent terminal \u2014 it is NOT posted to the task chat. The team does NOT see your replies unless you post them.`,
|
|
5785
5809
|
`Use post_to_chat (omit task_id \u2192 this task's chat) to report which child you are on, status as you work the pack, and any blocker or escalation the team needs to act on.`,
|
|
5786
|
-
`Pass task_id only to
|
|
5810
|
+
`Pass task_id only to reach a DIFFERENT card: a child task's chat on post_to_chat and read_task_chat, or a child's files on list_task_files and get_attachment.`,
|
|
5787
5811
|
`Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
|
|
5788
5812
|
``,
|
|
5789
5813
|
`If a Conveyor tool call fails or reports the MCP server is unavailable/disconnected, this is almost always a transient socket reconnect \u2014 RETRY the same call; it will succeed once the connection re-establishes. A tool error is NOT a reason to go idle or stop the pack. (When a turn may end is the pack path's call \u2014 see its goal and finish line.)`,
|
|
@@ -6386,7 +6410,7 @@ ${context.agentInstructions}`);
|
|
|
6386
6410
|
Your turn output appears ONLY in the live agent terminal \u2014 it is NOT posted to the task chat. The team does NOT see your replies unless you post them.`,
|
|
6387
6411
|
`Use post_to_chat (omit task_id \u2192 your own task's chat) to share meaningful status as you work, a summary when you finish, and any blocker or question you need a human to answer.`,
|
|
6388
6412
|
`A question you need answered goes through AskUserQuestion \u2014 that is what pages a person. post_to_chat with milestone: "blocked" records that you are parked and does not page anyone.`,
|
|
6389
|
-
`Pass task_id only to
|
|
6413
|
+
`Pass task_id only to reach a DIFFERENT card: a child task's chat on post_to_chat and read_task_chat, or any card in this project's files on list_task_files and get_attachment (a pack parent's source documents, for example). Omit it for your own card.`,
|
|
6390
6414
|
`Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
|
|
6391
6415
|
`
|
|
6392
6416
|
If a Conveyor tool call fails or reports the MCP server is unavailable/disconnected, this is almost always a transient socket reconnect \u2014 simply RETRY the same call and it will succeed once the connection re-establishes. Do NOT stop work, go idle, or treat a tool error as a reason to end your turn. Keep making progress on the code; only post_to_chat to ask for a human if a tool keeps failing across several retries over a sustained period.`,
|
|
@@ -6561,6 +6585,9 @@ ${truncatePlanForPrompt(context.plan)}`);
|
|
|
6561
6585
|
parts.push(...formatTaskFile(file));
|
|
6562
6586
|
}
|
|
6563
6587
|
}
|
|
6588
|
+
if (context.parentTaskFiles && context.parentTaskFiles.files.length > 0) {
|
|
6589
|
+
parts.push(...formatParentTaskFiles(context.parentTaskFiles));
|
|
6590
|
+
}
|
|
6564
6591
|
if (context.repoRefs && context.repoRefs.length > 0) {
|
|
6565
6592
|
parts.push(...formatRepoRefs(context.repoRefs));
|
|
6566
6593
|
}
|
|
@@ -7454,11 +7481,18 @@ var subtasksContracts = [
|
|
|
7454
7481
|
setTaskParentContract
|
|
7455
7482
|
];
|
|
7456
7483
|
var mcpTaskIdOrSlug = f.string({ desc: "The task ID or slug" });
|
|
7484
|
+
var agentTaskIdOrSlug = f.optional(
|
|
7485
|
+
f.string({
|
|
7486
|
+
desc: "Omit for the current card; pass a parent, sibling, or any other card in this project (id or slug) to read its files instead."
|
|
7487
|
+
})
|
|
7488
|
+
);
|
|
7457
7489
|
var listTaskFilesContract = defineToolContract({
|
|
7458
7490
|
name: "list_task_files",
|
|
7459
7491
|
agent: {
|
|
7460
|
-
description: "List all files attached to this task with metadata. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment.",
|
|
7461
|
-
fields: {
|
|
7492
|
+
description: "List all files attached to this task with metadata. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment. Pass task_id to list another card's files in this project, for example the pack parent's source documents.",
|
|
7493
|
+
fields: {
|
|
7494
|
+
task_id: agentTaskIdOrSlug
|
|
7495
|
+
}
|
|
7462
7496
|
},
|
|
7463
7497
|
mcp: {
|
|
7464
7498
|
description: "List all files attached to a task with metadata (no contents \u2014 fast and small). Pass projectId to target a specific project; otherwise the configured default project is used. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment.",
|
|
@@ -7471,13 +7505,14 @@ var listTaskFilesContract = defineToolContract({
|
|
|
7471
7505
|
var getAttachmentContract = defineToolContract({
|
|
7472
7506
|
name: "get_attachment",
|
|
7473
7507
|
agent: {
|
|
7474
|
-
description: "Fetch one task file's content plus metadata by file ID. Call list_task_files first to discover IDs and check sizes \u2014 large binaries may be truncated by the service's size limit.",
|
|
7508
|
+
description: "Fetch one task file's content plus metadata by file ID. This is the way to read attachment bytes: a document `downloadUrl` (anything that is not an image or video) answers 404 without a signed-in project member's session, so do not curl or WebFetch it from the pod. Call list_task_files first to discover IDs and check sizes \u2014 large binaries may be truncated by the service's size limit. Pass task_id when the file is attached to another card in this project, such as the pack parent.",
|
|
7475
7509
|
fields: {
|
|
7476
|
-
fileId: f.string({ desc: "The file ID to retrieve" })
|
|
7510
|
+
fileId: f.string({ desc: "The file ID to retrieve" }),
|
|
7511
|
+
task_id: agentTaskIdOrSlug
|
|
7477
7512
|
}
|
|
7478
7513
|
},
|
|
7479
7514
|
mcp: {
|
|
7480
|
-
description: "Fetch one task file's content plus metadata by file ID (accepts task id or slug). Pass projectId to target a specific project; otherwise the configured default project is used. Images are returned as viewable image blocks. Large text files (logs, JSON) are returned in pages \u2014 use `offset`/`maxBytes` to read more
|
|
7515
|
+
description: "Fetch one task file's content plus metadata by file ID (accepts task id or slug). Pass projectId to target a specific project; otherwise the configured default project is used. Images are returned as viewable image blocks. Large text files (logs, JSON) are returned in pages \u2014 use `offset`/`maxBytes` to read more. This tool is the way to read bytes: a document `downloadUrl` (anything that is not an image or video) answers 404 unless the request carries a signed-in project member's session, so do not curl or fetch it. Call list_task_files first to discover IDs and sizes.",
|
|
7481
7516
|
fields: {
|
|
7482
7517
|
projectId: mcpProjectId,
|
|
7483
7518
|
taskId: mcpTaskIdOrSlug,
|
|
@@ -7544,10 +7579,29 @@ var uploadAttachmentContract = defineToolContract({
|
|
|
7544
7579
|
}
|
|
7545
7580
|
}
|
|
7546
7581
|
});
|
|
7582
|
+
var DELETE_ATTACHMENT_WARNING = "Permanent, with no trash and no undo (unlike drive_delete_file): the file and its bytes are gone, and the chat message that announced it keeps its text but loses the attachment. Use it to remove a superseded upload once its replacement is attached. `fileId` comes from list_task_files.";
|
|
7583
|
+
var deleteAttachmentContract = defineToolContract({
|
|
7584
|
+
name: "delete_attachment",
|
|
7585
|
+
agent: {
|
|
7586
|
+
description: `Delete one attachment from this task. ${DELETE_ATTACHMENT_WARNING} A pod can only delete files on its own card.`,
|
|
7587
|
+
fields: {
|
|
7588
|
+
fileId: f.string({ desc: "The file ID to delete" })
|
|
7589
|
+
}
|
|
7590
|
+
},
|
|
7591
|
+
mcp: {
|
|
7592
|
+
description: `Delete one attachment from a task (accepts task id or slug). ${DELETE_ATTACHMENT_WARNING} Requires Moderate on the project, the same bar as the web Files delete button. Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
7593
|
+
fields: {
|
|
7594
|
+
projectId: mcpProjectId,
|
|
7595
|
+
taskId: mcpTaskIdOrSlug,
|
|
7596
|
+
fileId: f.string({ desc: "The file ID to delete" })
|
|
7597
|
+
}
|
|
7598
|
+
}
|
|
7599
|
+
});
|
|
7547
7600
|
var attachmentsContracts = [
|
|
7548
7601
|
listTaskFilesContract,
|
|
7549
7602
|
getAttachmentContract,
|
|
7550
|
-
uploadAttachmentContract
|
|
7603
|
+
uploadAttachmentContract,
|
|
7604
|
+
deleteAttachmentContract
|
|
7551
7605
|
];
|
|
7552
7606
|
var createSuggestionContract = defineToolContract({
|
|
7553
7607
|
name: "create_suggestion",
|
|
@@ -7870,7 +7924,7 @@ var createPullRequestContract = defineToolContract({
|
|
|
7870
7924
|
fields: {
|
|
7871
7925
|
title: f.string({ desc: "The PR title" }),
|
|
7872
7926
|
body: f.string({
|
|
7873
|
-
desc: "The PR description/body in markdown. If the diff changes anything a user sees rendered, the body MUST show the visual proof inline, not just point at the card: upload the capture with upload_attachment, then embed the downloadUrl it returns under a '## Screenshots' heading ( for images, [caption](url) for video). Attach to the card as well \u2014 the card is the durable home, the PR body is what the reviewer reads."
|
|
7927
|
+
desc: "The PR description/body in markdown. If the diff changes anything a user sees rendered, the body MUST show the visual proof inline, not just point at the card: upload the capture with upload_attachment, then embed the downloadUrl it returns under a '## Screenshots' heading ( for images, [caption](url) for video). Only image and video URLs belong in a PR body: a document downloadUrl (csv, pdf, md, \u2026) needs a signed-in project member and shows 404 to a reviewer on GitHub, so point at the card instead. Attach to the card as well \u2014 the card is the durable home, the PR body is what the reviewer reads."
|
|
7874
7928
|
}),
|
|
7875
7929
|
branch: f.optional(
|
|
7876
7930
|
f.string({
|
|
@@ -8727,17 +8781,20 @@ function buildGetExecutionLogsTool(connection) {
|
|
|
8727
8781
|
function buildListTaskFilesTool(connection) {
|
|
8728
8782
|
return defineContractTool(
|
|
8729
8783
|
listTaskFilesContract,
|
|
8730
|
-
async () => {
|
|
8784
|
+
async ({ task_id }) => {
|
|
8731
8785
|
try {
|
|
8732
8786
|
const files = await connection.call("getTaskFiles", {
|
|
8733
|
-
sessionId: connection.sessionId
|
|
8787
|
+
sessionId: connection.sessionId,
|
|
8788
|
+
...task_id ? { taskId: task_id } : {}
|
|
8734
8789
|
});
|
|
8735
8790
|
const metadata = files.map((file) => {
|
|
8736
8791
|
const { content: _c, ...rest } = file;
|
|
8737
8792
|
return rest;
|
|
8738
8793
|
});
|
|
8794
|
+
const heading = task_id ? `Files on card ${task_id}:
|
|
8795
|
+
` : "";
|
|
8739
8796
|
const content = [
|
|
8740
|
-
{ type: "text", text: JSON.stringify(metadata, null, 2) }
|
|
8797
|
+
{ type: "text", text: heading + JSON.stringify(metadata, null, 2) }
|
|
8741
8798
|
];
|
|
8742
8799
|
for (const file of files) {
|
|
8743
8800
|
if (file.content && file.contentEncoding === "base64" && isImageMimeType(file.mimeType)) {
|
|
@@ -8755,11 +8812,12 @@ function buildListTaskFilesTool(connection) {
|
|
|
8755
8812
|
function buildGetAttachmentTool(connection) {
|
|
8756
8813
|
return defineContractTool(
|
|
8757
8814
|
getAttachmentContract,
|
|
8758
|
-
async ({ fileId }) => {
|
|
8815
|
+
async ({ fileId, task_id }) => {
|
|
8759
8816
|
try {
|
|
8760
8817
|
const file = await connection.call("getTaskFile", {
|
|
8761
8818
|
sessionId: connection.sessionId,
|
|
8762
|
-
fileId
|
|
8819
|
+
fileId,
|
|
8820
|
+
...task_id ? { taskId: task_id } : {}
|
|
8763
8821
|
});
|
|
8764
8822
|
const { content: rawContent, ...metadata } = file;
|
|
8765
8823
|
const content = [
|
|
@@ -9346,6 +9404,9 @@ var MIME_BY_EXT = {
|
|
|
9346
9404
|
".yaml": "application/yaml",
|
|
9347
9405
|
".yml": "application/yaml",
|
|
9348
9406
|
".pdf": "application/pdf",
|
|
9407
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
9408
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
9409
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
9349
9410
|
".zip": "application/zip",
|
|
9350
9411
|
".gz": "application/gzip",
|
|
9351
9412
|
".mp4": "video/mp4",
|
|
@@ -9368,11 +9429,21 @@ var EMBEDDABLE_IN_MARKDOWN = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", "
|
|
|
9368
9429
|
function describeEmbed(fileName, downloadUrl) {
|
|
9369
9430
|
if (!downloadUrl) return "";
|
|
9370
9431
|
const isImage = EMBEDDABLE_IN_MARKDOWN.has(extname(fileName).toLowerCase());
|
|
9371
|
-
|
|
9372
|
-
|
|
9432
|
+
if (isImage) {
|
|
9433
|
+
return `
|
|
9373
9434
|
|
|
9374
9435
|
Paste this into the PR description to show it inline:
|
|
9375
|
-
${
|
|
9436
|
+
`;
|
|
9437
|
+
}
|
|
9438
|
+
if (isCapabilityServableMime(inferMimeType(fileName))) {
|
|
9439
|
+
return `
|
|
9440
|
+
|
|
9441
|
+
Paste this into the PR description to link it:
|
|
9442
|
+
[${fileName}](${downloadUrl})`;
|
|
9443
|
+
}
|
|
9444
|
+
return `
|
|
9445
|
+
|
|
9446
|
+
downloadUrl (signed-in project members only; agents read it with get_attachment): ${downloadUrl}`;
|
|
9376
9447
|
}
|
|
9377
9448
|
function buildUploadAttachmentTool(connection, config) {
|
|
9378
9449
|
return defineContractTool(uploadAttachmentContract, async ({ path, title, comment, tags }) => {
|
|
@@ -9425,6 +9496,23 @@ function buildUploadAttachmentTool(connection, config) {
|
|
|
9425
9496
|
}
|
|
9426
9497
|
});
|
|
9427
9498
|
}
|
|
9499
|
+
function buildDeleteAttachmentTool(connection) {
|
|
9500
|
+
return defineContractTool(deleteAttachmentContract, async ({ fileId }) => {
|
|
9501
|
+
try {
|
|
9502
|
+
const result = await connection.call("deleteTaskFile", {
|
|
9503
|
+
sessionId: connection.sessionId,
|
|
9504
|
+
fileId
|
|
9505
|
+
});
|
|
9506
|
+
return textResult(
|
|
9507
|
+
`Deleted ${result.fileName} (${result.id}) permanently. Any chat message that announced it keeps its text without the attachment.`
|
|
9508
|
+
);
|
|
9509
|
+
} catch (error) {
|
|
9510
|
+
return textResult(
|
|
9511
|
+
`Failed to delete attachment: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
9512
|
+
);
|
|
9513
|
+
}
|
|
9514
|
+
});
|
|
9515
|
+
}
|
|
9428
9516
|
|
|
9429
9517
|
// src/tools/checklist-tools.ts
|
|
9430
9518
|
function buildListManualTestsTool(connection) {
|
|
@@ -9621,7 +9709,8 @@ function buildCommonTools(connection, config) {
|
|
|
9621
9709
|
buildApproveManualTestTool(connection),
|
|
9622
9710
|
buildRejectManualTestTool(connection),
|
|
9623
9711
|
...buildMutationTools(connection, config),
|
|
9624
|
-
buildUploadAttachmentTool(connection, config)
|
|
9712
|
+
buildUploadAttachmentTool(connection, config),
|
|
9713
|
+
buildDeleteAttachmentTool(connection)
|
|
9625
9714
|
];
|
|
9626
9715
|
}
|
|
9627
9716
|
|
|
@@ -12325,7 +12414,15 @@ function hasExistingSessionFile(taskId, cwd, lineage) {
|
|
|
12325
12414
|
if (process.env.CONVEYOR_TUI === "codex") {
|
|
12326
12415
|
return !!readCodexLineage(taskIdToSessionUuid(key), cwd);
|
|
12327
12416
|
}
|
|
12328
|
-
|
|
12417
|
+
const uuid = taskIdToSessionUuid(key);
|
|
12418
|
+
return sessionFileExists(uuid, cwd) && isResumableTranscript(sessionTranscriptPath(cwd, uuid));
|
|
12419
|
+
}
|
|
12420
|
+
var spawnedTranscripts = /* @__PURE__ */ new Set();
|
|
12421
|
+
function markSpawnedTranscript(cwd, sessionUuid) {
|
|
12422
|
+
spawnedTranscripts.add(sessionTranscriptPath(cwd, sessionUuid));
|
|
12423
|
+
}
|
|
12424
|
+
function isResumableTranscript(path) {
|
|
12425
|
+
return spawnedTranscripts.has(path) || transcriptHasConversation(path);
|
|
12329
12426
|
}
|
|
12330
12427
|
function resolveSessionStart(lineageKey, cwd, tui = process.env.CONVEYOR_TUI) {
|
|
12331
12428
|
const sessionUuid = taskIdToSessionUuid(lineageKey);
|
|
@@ -12334,11 +12431,46 @@ function resolveSessionStart(lineageKey, cwd, tui = process.env.CONVEYOR_TUI) {
|
|
|
12334
12431
|
return { sessionId: sessionUuid, ...resume ? { resume } : {} };
|
|
12335
12432
|
}
|
|
12336
12433
|
if (sessionFileExists(sessionUuid, cwd)) {
|
|
12337
|
-
|
|
12338
|
-
|
|
12434
|
+
const path = sessionTranscriptPath(cwd, sessionUuid);
|
|
12435
|
+
repairTornSessionFile(path);
|
|
12436
|
+
if (isResumableTranscript(path)) return { resume: sessionUuid };
|
|
12437
|
+
rotateSessionFile(cwd, sessionUuid);
|
|
12438
|
+
logger5.warn("Rotated a transcript with no conversation aside before resume", { path });
|
|
12339
12439
|
}
|
|
12440
|
+
markSpawnedTranscript(cwd, sessionUuid);
|
|
12340
12441
|
return { sessionId: sessionUuid };
|
|
12341
12442
|
}
|
|
12443
|
+
var CONVERSATION_PROBE_BYTES = 256 * 1024;
|
|
12444
|
+
function transcriptHasConversation(path) {
|
|
12445
|
+
let fd;
|
|
12446
|
+
try {
|
|
12447
|
+
const size = statSync2(path).size;
|
|
12448
|
+
const length = Math.min(size, CONVERSATION_PROBE_BYTES);
|
|
12449
|
+
const buffer = Buffer.alloc(length);
|
|
12450
|
+
fd = openSync(path, "r");
|
|
12451
|
+
const read = readSync(fd, buffer, 0, length, 0);
|
|
12452
|
+
const text = buffer.subarray(0, read).toString("utf8");
|
|
12453
|
+
const complete = text.slice(0, text.lastIndexOf("\n") + 1);
|
|
12454
|
+
for (const line of complete.split("\n")) {
|
|
12455
|
+
if (!line.trim()) continue;
|
|
12456
|
+
try {
|
|
12457
|
+
const type = JSON.parse(line).type;
|
|
12458
|
+
if (type === "user" || type === "assistant") return true;
|
|
12459
|
+
} catch {
|
|
12460
|
+
}
|
|
12461
|
+
}
|
|
12462
|
+
return size > CONVERSATION_PROBE_BYTES;
|
|
12463
|
+
} catch {
|
|
12464
|
+
return true;
|
|
12465
|
+
} finally {
|
|
12466
|
+
if (fd !== void 0) {
|
|
12467
|
+
try {
|
|
12468
|
+
closeSync(fd);
|
|
12469
|
+
} catch {
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
12472
|
+
}
|
|
12473
|
+
}
|
|
12342
12474
|
function repairTornSessionFile(path) {
|
|
12343
12475
|
try {
|
|
12344
12476
|
if (!existsSync(path)) return false;
|
|
@@ -12867,6 +12999,7 @@ function freshSessionOptions(host, context, options) {
|
|
|
12867
12999
|
sessionLineageKey(context.taskId, host.agentMode, host.config.mode)
|
|
12868
13000
|
);
|
|
12869
13001
|
if (rotateSessionFile(host.config.workspaceDir, lineageUuid)) {
|
|
13002
|
+
markSpawnedTranscript(host.config.workspaceDir, lineageUuid);
|
|
12870
13003
|
return { ...options, sessionId: lineageUuid };
|
|
12871
13004
|
}
|
|
12872
13005
|
return { ...options, sessionId: randomUUID2() };
|
|
@@ -12918,6 +13051,11 @@ async function handleAuthError(context, host, options) {
|
|
|
12918
13051
|
async function handleStaleSession(context, host, options) {
|
|
12919
13052
|
context.claudeSessionId = null;
|
|
12920
13053
|
host.connection.storeSessionId("");
|
|
13054
|
+
logger5.warn("Resume target had no resumable conversation; rotating it aside and starting fresh", {
|
|
13055
|
+
taskId: context.taskId,
|
|
13056
|
+
runnerMode: host.config.mode,
|
|
13057
|
+
agentMode: host.agentMode
|
|
13058
|
+
});
|
|
12921
13059
|
const freshPrompt = buildMultimodalPrompt(
|
|
12922
13060
|
await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode),
|
|
12923
13061
|
context,
|
|
@@ -13025,7 +13163,7 @@ async function handleUsageCapRejection(context, host, options, rateLimitType, re
|
|
|
13025
13163
|
return runWithRetry(freshQuery, context, host, options);
|
|
13026
13164
|
}
|
|
13027
13165
|
function handleRetryError(error, context, host, options, prevImageError) {
|
|
13028
|
-
if (isStaleOrExitedSession(error, context)
|
|
13166
|
+
if (isStaleOrExitedSession(error, context)) {
|
|
13029
13167
|
return handleStaleSession(context, host, options);
|
|
13030
13168
|
}
|
|
13031
13169
|
if (isAuthError(getErrorMessage(error))) {
|
|
@@ -13051,7 +13189,7 @@ function handleProcessResult(result, context, host, options) {
|
|
|
13051
13189
|
)
|
|
13052
13190
|
};
|
|
13053
13191
|
}
|
|
13054
|
-
if (result.staleSession
|
|
13192
|
+
if (result.staleSession) {
|
|
13055
13193
|
return {
|
|
13056
13194
|
action: "return_promise",
|
|
13057
13195
|
promise: handleStaleSession(context, host, options)
|
|
@@ -15234,6 +15372,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
15234
15372
|
claudeSessionId: ctx.claudeSessionId ?? null,
|
|
15235
15373
|
lastSeenMessageId: ctx.lastSeenMessageId ?? null,
|
|
15236
15374
|
isParentTask: ctx.isParentTask ?? false,
|
|
15375
|
+
parentTaskFiles: ctx.parentTaskFiles ?? null,
|
|
15237
15376
|
storyPoints: ctx.storyPoints ?? void 0,
|
|
15238
15377
|
agents: ctx.agents ?? void 0,
|
|
15239
15378
|
projectTags: ctx.projectTags ?? void 0,
|
|
@@ -1785,8 +1785,41 @@ var IDENTIFIED_WORK_STATUSES = ["Open", ...ACTIVE_WORK_STATUSES];
|
|
|
1785
1785
|
var DEFAULT_TASK_STATUS_COLOR = "#a8a29e";
|
|
1786
1786
|
var DEFAULT_TASK_STATUS_COLOR_INT = Number.parseInt(DEFAULT_TASK_STATUS_COLOR.slice(1), 16);
|
|
1787
1787
|
var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
|
|
1788
|
+
function isCapabilityServableMime(mimeType) {
|
|
1789
|
+
const base = mimeType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
1790
|
+
return base.startsWith("image/") || base.startsWith("video/");
|
|
1791
|
+
}
|
|
1788
1792
|
var MAX_FILE_TAGS = 5;
|
|
1789
1793
|
var MAX_FILE_TAG_LENGTH = 100;
|
|
1794
|
+
var ALLOWED_FILE_MIME_TYPES = [
|
|
1795
|
+
"image/png",
|
|
1796
|
+
"image/jpeg",
|
|
1797
|
+
"image/gif",
|
|
1798
|
+
"image/webp",
|
|
1799
|
+
"application/json",
|
|
1800
|
+
"text/csv",
|
|
1801
|
+
"text/plain",
|
|
1802
|
+
"text/markdown",
|
|
1803
|
+
"text/vnd.mermaid",
|
|
1804
|
+
"application/pdf",
|
|
1805
|
+
// Reports (conveyor-consensus and friends) are single-file, no-JS HTML. Safe
|
|
1806
|
+
// to store because the DOWNLOAD route serves these under a sandbox CSP with
|
|
1807
|
+
// an opaque origin — see SANDBOXED_MIME in services/file/routes.ts.
|
|
1808
|
+
"text/html",
|
|
1809
|
+
// Office documents: the OOXML trio only (legacy .doc/.xls/.ppt stay out).
|
|
1810
|
+
// Served inline like PDFs; browsers download them and nothing renders
|
|
1811
|
+
// in-origin, so they need no SANDBOXED_MIME entry.
|
|
1812
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1813
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1814
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
1815
|
+
];
|
|
1816
|
+
var ALLOWED_FILE_ACCEPT = [
|
|
1817
|
+
...ALLOWED_FILE_MIME_TYPES,
|
|
1818
|
+
// Extensions too: a mime-only `accept` filters the OS picker by its own mime
|
|
1819
|
+
// registry, which greys out .md/.mmd (and Office files on a machine without
|
|
1820
|
+
// Office) even though the upload would succeed.
|
|
1821
|
+
".png,.jpg,.jpeg,.gif,.webp,.json,.csv,.txt,.md,.mmd,.pdf,.html,.docx,.xlsx,.pptx"
|
|
1822
|
+
].join(",");
|
|
1790
1823
|
var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
|
|
1791
1824
|
var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
|
|
1792
1825
|
var IDLE_HEARTBEAT_MS = 90 * 1e3;
|
|
@@ -1992,9 +2025,15 @@ var GetChatMessagesRequestSchema = z3.object({
|
|
|
1992
2025
|
taskId: z3.string().optional()
|
|
1993
2026
|
});
|
|
1994
2027
|
var GetTaskFilesRequestSchema = z3.object({
|
|
1995
|
-
sessionId: z3.string()
|
|
2028
|
+
sessionId: z3.string(),
|
|
2029
|
+
taskId: z3.string().optional()
|
|
1996
2030
|
});
|
|
1997
2031
|
var GetTaskFileRequestSchema = z3.object({
|
|
2032
|
+
sessionId: z3.string(),
|
|
2033
|
+
fileId: z3.string(),
|
|
2034
|
+
taskId: z3.string().optional()
|
|
2035
|
+
});
|
|
2036
|
+
var DeleteTaskFileRequestSchema = z3.object({
|
|
1998
2037
|
sessionId: z3.string(),
|
|
1999
2038
|
fileId: z3.string()
|
|
2000
2039
|
});
|
|
@@ -2924,6 +2963,12 @@ var GetProjectAttachmentRequestSchema = z5.object({
|
|
|
2924
2963
|
/** Max bytes of text content to return from `offset`. Server default applies. */
|
|
2925
2964
|
maxBytes: z5.number().int().positive().optional()
|
|
2926
2965
|
});
|
|
2966
|
+
var DeleteProjectTaskFileRequestSchema = z5.object({
|
|
2967
|
+
projectId: z5.string(),
|
|
2968
|
+
taskId: z5.string(),
|
|
2969
|
+
fileId: z5.string(),
|
|
2970
|
+
requestingUserId: z5.string().optional()
|
|
2971
|
+
});
|
|
2927
2972
|
var RequestProjectFileUploadRequestSchema = z5.object({
|
|
2928
2973
|
projectId: z5.string(),
|
|
2929
2974
|
taskId: z5.string(),
|
|
@@ -5061,6 +5106,7 @@ export {
|
|
|
5061
5106
|
EXTERNAL_AGENT_MESSAGE_SOURCE,
|
|
5062
5107
|
TUI_KINDS,
|
|
5063
5108
|
MAX_FILE_SIZE_BYTES,
|
|
5109
|
+
isCapabilityServableMime,
|
|
5064
5110
|
RUNNER_MODES,
|
|
5065
5111
|
ReviewGuideContentSchema,
|
|
5066
5112
|
CONTEXT_LINK_LOCATOR_MAX,
|
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-SI7WNWZZ.js";
|
|
11
11
|
import {
|
|
12
12
|
PtyHarness,
|
|
13
13
|
SessionRunner,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runUsageProbe,
|
|
26
26
|
sampleKeyUsage,
|
|
27
27
|
usesNativeUsageReporting
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-7HK6OIV4.js";
|
|
29
29
|
import "./chunk-37J5MMQT.js";
|
|
30
30
|
import {
|
|
31
31
|
inheritedEnv,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-PBSFLQA7.js";
|
|
48
48
|
import "./chunk-Q4FQOJ7D.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
50
|
import "./chunk-372R6E4C.js";
|
|
@@ -1458,7 +1458,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
|
|
|
1458
1458
|
process.exit(1);
|
|
1459
1459
|
}
|
|
1460
1460
|
if (CONVEYOR_MODE === "serving") {
|
|
1461
|
-
const { runServingSession } = await import("./serve-boot-
|
|
1461
|
+
const { runServingSession } = await import("./serve-boot-WGUFEWYJ.js");
|
|
1462
1462
|
exitContext.runnerMode = "serving";
|
|
1463
1463
|
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
|
|
1464
1464
|
const outcome = await runServingSession({
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7HK6OIV4.js";
|
|
5
5
|
import "./chunk-37J5MMQT.js";
|
|
6
6
|
import "./chunk-WCJ6HRVU.js";
|
|
7
7
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-PBSFLQA7.js";
|
|
21
21
|
import "./chunk-Q4FQOJ7D.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SI7WNWZZ.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-PBSFLQA7.js";
|
|
15
15
|
import "./chunk-Q4FQOJ7D.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.27",
|
|
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",
|
|
@@ -111,7 +111,11 @@ task binding and no user account.
|
|
|
111
111
|
1. Resolve the card from the argument — slug, id, or URL. **None given → ask
|
|
112
112
|
which card.** Never infer one from the branch you happen to be on.
|
|
113
113
|
2. `mcp__conveyor__get_task` for the full plan and
|
|
114
|
-
`mcp__conveyor__read_task_chat` for addenda and user answers.
|
|
114
|
+
`mcp__conveyor__read_task_chat` for addenda and user answers. On a pack
|
|
115
|
+
child the source documents usually sit on the parent card:
|
|
116
|
+
`mcp__conveyor__list_task_files` with `task_id` set to the parent (in a pod;
|
|
117
|
+
the local surface spells it `taskId`) lists them, and
|
|
118
|
+
`mcp__conveyor__get_attachment` with the same `task_id` reads one. Consult
|
|
115
119
|
`mcp__conveyor__get_tag` on the card's tags before diving in — a tag's
|
|
116
120
|
overview and linked files are the fast path into the subsystem.
|
|
117
121
|
3. Route on the card's shape:
|
|
@@ -249,7 +253,10 @@ Instead: post the answer, config, or findings with
|
|
|
249
253
|
`mcp__conveyor__upload_attachment` (any file type, up to 25MB), and complete
|
|
250
254
|
the card directly with `force_update_task_status("Complete")` — there is no PR
|
|
251
255
|
or review step for a no-code task. Never publish a deliverable as an off-card
|
|
252
|
-
link; the card is where it belongs.
|
|
256
|
+
link; the card is where it belongs. When a revision supersedes an earlier
|
|
257
|
+
upload, remove the old copy with `mcp__conveyor__delete_attachment` (permanent,
|
|
258
|
+
`fileId` from `list_task_files`) so the Files list shows one current version
|
|
259
|
+
of each deliverable.
|
|
253
260
|
|
|
254
261
|
When unsure, check the diff: a real diff means open a PR, no diff means finish
|
|
255
262
|
in chat and mark it Complete.
|
|
@@ -100,6 +100,10 @@ Scale to the idea's size; a one-file tweak needs minutes, not a survey.
|
|
|
100
100
|
already in your context. **Stop when you can cite a specific `file.ts:line`
|
|
101
101
|
and a symbol name for every step your plan will contain** — that is the
|
|
102
102
|
threshold, and more reading past it buys nothing.
|
|
103
|
+
- **Attachments**: `mcp__conveyor__list_task_files` on the card, and on its
|
|
104
|
+
parent when the card is a pack child (in a pod pass `task_id`; the local
|
|
105
|
+
surface takes `taskId`). Source documents, screenshots and worksheets usually
|
|
106
|
+
live there, and a plan written without reading them plans the wrong work.
|
|
103
107
|
- **Prior art**: `mcp__conveyor__search_tasks` on 2-3 keyword variants
|
|
104
108
|
(`typeFilters` to include incidents/suggestions when relevant), then
|
|
105
109
|
`mcp__conveyor__get_task` on the closest hits. You're looking for duplicates
|
|
@@ -80,7 +80,9 @@ Find the code path the report implicates and, where you can, reproduce it. A
|
|
|
80
80
|
diagnosis grounded only in reading is a hypothesis; a reproduction makes it a
|
|
81
81
|
finding. Attach evidence to the card with `mcp__conveyor__upload_attachment` —
|
|
82
82
|
a screenshot, a log excerpt, a short recording — so the Builder inherits proof
|
|
83
|
-
rather than your assertion.
|
|
83
|
+
rather than your assertion. A re-captured version replaces the old one:
|
|
84
|
+
`mcp__conveyor__delete_attachment` (permanent, `fileId` from
|
|
85
|
+
`list_task_files`) keeps the card's Files list to what is current.
|
|
84
86
|
|
|
85
87
|
## 5. Decide
|
|
86
88
|
|