@rallycry/conveyor-agent 11.0.26 → 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.
@@ -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-BZZ4B37Q.js";
95
+ } from "./chunk-PBSFLQA7.js";
95
96
  import {
96
97
  registerBootMilestoneSocketFallback,
97
98
  reportBootMilestone
@@ -5440,6 +5441,20 @@ function formatChatFile(file) {
5440
5441
  }
5441
5442
  return [`[Attached: ${file.fileName} (${file.mimeType}${sizeStr})]`];
5442
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
+ }
5443
5458
  function formatTaskFile(file) {
5444
5459
  if (file.content && file.contentEncoding === "utf-8") {
5445
5460
  return [`
@@ -5792,7 +5807,7 @@ var PACK_RUNNER_FOOTER = [
5792
5807
  ``,
5793
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.`,
5794
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.`,
5795
- `Pass task_id only to message a DIFFERENT task's chat (e.g. a child task).`,
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.`,
5796
5811
  `Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
5797
5812
  ``,
5798
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.)`,
@@ -6395,7 +6410,7 @@ ${context.agentInstructions}`);
6395
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.`,
6396
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.`,
6397
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.`,
6398
- `Pass task_id only to message a DIFFERENT task's chat (e.g. a parent task via get_task).`,
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.`,
6399
6414
  `Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
6400
6415
  `
6401
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.`,
@@ -6570,6 +6585,9 @@ ${truncatePlanForPrompt(context.plan)}`);
6570
6585
  parts.push(...formatTaskFile(file));
6571
6586
  }
6572
6587
  }
6588
+ if (context.parentTaskFiles && context.parentTaskFiles.files.length > 0) {
6589
+ parts.push(...formatParentTaskFiles(context.parentTaskFiles));
6590
+ }
6573
6591
  if (context.repoRefs && context.repoRefs.length > 0) {
6574
6592
  parts.push(...formatRepoRefs(context.repoRefs));
6575
6593
  }
@@ -7463,11 +7481,18 @@ var subtasksContracts = [
7463
7481
  setTaskParentContract
7464
7482
  ];
7465
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
+ );
7466
7489
  var listTaskFilesContract = defineToolContract({
7467
7490
  name: "list_task_files",
7468
7491
  agent: {
7469
- 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.",
7470
- 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
+ }
7471
7496
  },
7472
7497
  mcp: {
7473
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.",
@@ -7480,13 +7505,14 @@ var listTaskFilesContract = defineToolContract({
7480
7505
  var getAttachmentContract = defineToolContract({
7481
7506
  name: "get_attachment",
7482
7507
  agent: {
7483
- 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.",
7484
7509
  fields: {
7485
- fileId: f.string({ desc: "The file ID to retrieve" })
7510
+ fileId: f.string({ desc: "The file ID to retrieve" }),
7511
+ task_id: agentTaskIdOrSlug
7486
7512
  }
7487
7513
  },
7488
7514
  mcp: {
7489
- 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, or fetch `downloadUrl` for the whole file. Call list_task_files first to discover IDs and sizes.",
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.",
7490
7516
  fields: {
7491
7517
  projectId: mcpProjectId,
7492
7518
  taskId: mcpTaskIdOrSlug,
@@ -7553,10 +7579,29 @@ var uploadAttachmentContract = defineToolContract({
7553
7579
  }
7554
7580
  }
7555
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
+ });
7556
7600
  var attachmentsContracts = [
7557
7601
  listTaskFilesContract,
7558
7602
  getAttachmentContract,
7559
- uploadAttachmentContract
7603
+ uploadAttachmentContract,
7604
+ deleteAttachmentContract
7560
7605
  ];
7561
7606
  var createSuggestionContract = defineToolContract({
7562
7607
  name: "create_suggestion",
@@ -7879,7 +7924,7 @@ var createPullRequestContract = defineToolContract({
7879
7924
  fields: {
7880
7925
  title: f.string({ desc: "The PR title" }),
7881
7926
  body: f.string({
7882
- 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 (![caption](url) 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 (![caption](url) 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."
7883
7928
  }),
7884
7929
  branch: f.optional(
7885
7930
  f.string({
@@ -8736,17 +8781,20 @@ function buildGetExecutionLogsTool(connection) {
8736
8781
  function buildListTaskFilesTool(connection) {
8737
8782
  return defineContractTool(
8738
8783
  listTaskFilesContract,
8739
- async () => {
8784
+ async ({ task_id }) => {
8740
8785
  try {
8741
8786
  const files = await connection.call("getTaskFiles", {
8742
- sessionId: connection.sessionId
8787
+ sessionId: connection.sessionId,
8788
+ ...task_id ? { taskId: task_id } : {}
8743
8789
  });
8744
8790
  const metadata = files.map((file) => {
8745
8791
  const { content: _c, ...rest } = file;
8746
8792
  return rest;
8747
8793
  });
8794
+ const heading = task_id ? `Files on card ${task_id}:
8795
+ ` : "";
8748
8796
  const content = [
8749
- { type: "text", text: JSON.stringify(metadata, null, 2) }
8797
+ { type: "text", text: heading + JSON.stringify(metadata, null, 2) }
8750
8798
  ];
8751
8799
  for (const file of files) {
8752
8800
  if (file.content && file.contentEncoding === "base64" && isImageMimeType(file.mimeType)) {
@@ -8764,11 +8812,12 @@ function buildListTaskFilesTool(connection) {
8764
8812
  function buildGetAttachmentTool(connection) {
8765
8813
  return defineContractTool(
8766
8814
  getAttachmentContract,
8767
- async ({ fileId }) => {
8815
+ async ({ fileId, task_id }) => {
8768
8816
  try {
8769
8817
  const file = await connection.call("getTaskFile", {
8770
8818
  sessionId: connection.sessionId,
8771
- fileId
8819
+ fileId,
8820
+ ...task_id ? { taskId: task_id } : {}
8772
8821
  });
8773
8822
  const { content: rawContent, ...metadata } = file;
8774
8823
  const content = [
@@ -9355,6 +9404,9 @@ var MIME_BY_EXT = {
9355
9404
  ".yaml": "application/yaml",
9356
9405
  ".yml": "application/yaml",
9357
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",
9358
9410
  ".zip": "application/zip",
9359
9411
  ".gz": "application/gzip",
9360
9412
  ".mp4": "video/mp4",
@@ -9377,11 +9429,21 @@ var EMBEDDABLE_IN_MARKDOWN = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", "
9377
9429
  function describeEmbed(fileName, downloadUrl) {
9378
9430
  if (!downloadUrl) return "";
9379
9431
  const isImage = EMBEDDABLE_IN_MARKDOWN.has(extname(fileName).toLowerCase());
9380
- const snippet = isImage ? `![${fileName}](${downloadUrl})` : `[${fileName}](${downloadUrl})`;
9381
- return `
9432
+ if (isImage) {
9433
+ return `
9382
9434
 
9383
9435
  Paste this into the PR description to show it inline:
9384
- ${snippet}`;
9436
+ ![${fileName}](${downloadUrl})`;
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}`;
9385
9447
  }
9386
9448
  function buildUploadAttachmentTool(connection, config) {
9387
9449
  return defineContractTool(uploadAttachmentContract, async ({ path, title, comment, tags }) => {
@@ -9434,6 +9496,23 @@ function buildUploadAttachmentTool(connection, config) {
9434
9496
  }
9435
9497
  });
9436
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
+ }
9437
9516
 
9438
9517
  // src/tools/checklist-tools.ts
9439
9518
  function buildListManualTestsTool(connection) {
@@ -9630,7 +9709,8 @@ function buildCommonTools(connection, config) {
9630
9709
  buildApproveManualTestTool(connection),
9631
9710
  buildRejectManualTestTool(connection),
9632
9711
  ...buildMutationTools(connection, config),
9633
- buildUploadAttachmentTool(connection, config)
9712
+ buildUploadAttachmentTool(connection, config),
9713
+ buildDeleteAttachmentTool(connection)
9634
9714
  ];
9635
9715
  }
9636
9716
 
@@ -15292,6 +15372,7 @@ var SessionRunner = class _SessionRunner {
15292
15372
  claudeSessionId: ctx.claudeSessionId ?? null,
15293
15373
  lastSeenMessageId: ctx.lastSeenMessageId ?? null,
15294
15374
  isParentTask: ctx.isParentTask ?? false,
15375
+ parentTaskFiles: ctx.parentTaskFiles ?? null,
15295
15376
  storyPoints: ctx.storyPoints ?? void 0,
15296
15377
  agents: ctx.agents ?? void 0,
15297
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,
@@ -8,7 +8,7 @@ import {
8
8
  readWorkspaceBytes,
9
9
  statWorkspacePath,
10
10
  workspacePathExists
11
- } from "./chunk-BZZ4B37Q.js";
11
+ } from "./chunk-PBSFLQA7.js";
12
12
  import {
13
13
  reportBootMilestone
14
14
  } from "./chunk-Q4FQOJ7D.js";
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  WorkspaceCommandSupervisor,
8
8
  startWorkspaceCommandsAfterConnect,
9
9
  stopWorkspaceCommands
10
- } from "./chunk-7WO6NTOS.js";
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-4L22UNZD.js";
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-BZZ4B37Q.js";
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-T7MNWASE.js");
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-4L22UNZD.js";
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-BZZ4B37Q.js";
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-7WO6NTOS.js";
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-BZZ4B37Q.js";
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.26",
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. Consult
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