@rallycry/conveyor-agent 11.0.44 → 11.0.46

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.
@@ -11,14 +11,14 @@ import {
11
11
  readAgentVersion,
12
12
  refreshSkillsAfterCheckout,
13
13
  runPreReadyBinds
14
- } from "./chunk-37J5MMQT.js";
14
+ } from "./chunk-6CKFZ4JG.js";
15
15
  import "./chunk-EKTPDG3G.js";
16
16
  import {
17
17
  GitPrepJob,
18
18
  defaultGit,
19
19
  ensureDir,
20
20
  reportBootMilestone
21
- } from "./chunk-Q4FQOJ7D.js";
21
+ } from "./chunk-VIUZS7V4.js";
22
22
  import "./chunk-W4LZ7R6Z.js";
23
23
  import "./chunk-372R6E4C.js";
24
24
  import "./chunk-SQM2BQ7H.js";
@@ -2,7 +2,7 @@ import {
2
2
  mapChatHistory,
3
3
  readAgentVersion,
4
4
  refreshSkillsAfterCheckout
5
- } from "./chunk-37J5MMQT.js";
5
+ } from "./chunk-6CKFZ4JG.js";
6
6
  import {
7
7
  MAX_BETWEEN_TURN_BUFFER,
8
8
  MAX_DIAGNOSTIC_OUTPUT,
@@ -37,7 +37,6 @@ import {
37
37
  CARD_DESCRIPTION_FIELD_HINT,
38
38
  CONTEXT_LINK_LOCATOR_MAX,
39
39
  CRITICAL_AUTOMATED_SOURCES,
40
- CodespacePortVisibility,
41
40
  DEFAULT_CI_WAIT_TIMEOUT_MINUTES,
42
41
  DEFAULT_CODEX_CODING_MODEL,
43
42
  DEFAULT_LIFECYCLE_CONFIG,
@@ -98,11 +97,10 @@ import {
98
97
  statWorkspacePath,
99
98
  updateRemoteToken,
100
99
  verifyGitCredential
101
- } from "./chunk-KMBMW3J3.js";
100
+ } from "./chunk-SKAFB6DG.js";
102
101
  import {
103
- registerBootMilestoneSocketFallback,
104
102
  reportBootMilestone
105
- } from "./chunk-Q4FQOJ7D.js";
103
+ } from "./chunk-VIUZS7V4.js";
106
104
  import {
107
105
  describeTokenFile,
108
106
  ghHostsExternallyOwned,
@@ -2746,7 +2744,7 @@ function claudeCredentialsPath() {
2746
2744
  return join7(claudeConfigHome(), ".credentials.json");
2747
2745
  }
2748
2746
  function isConveyorCloudEnv(env = process.env) {
2749
- return Boolean(env.CLAUDESPACE_NAME || env.CODESPACE_NAME || env.CODESPACES);
2747
+ return Boolean(env.CLAUDESPACE_NAME);
2750
2748
  }
2751
2749
  async function readRaw(path) {
2752
2750
  try {
@@ -5862,7 +5860,7 @@ function composerOverrides(runMode, env = process.env) {
5862
5860
  }
5863
5861
 
5864
5862
  // src/execution/query-executor.ts
5865
- import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
5863
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
5866
5864
  import {
5867
5865
  closeSync,
5868
5866
  existsSync,
@@ -5970,6 +5968,11 @@ function formatFileSize(bytes) {
5970
5968
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)}KB`;
5971
5969
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
5972
5970
  }
5971
+ function notInlinedRef(file) {
5972
+ const link = file.downloadUrl && isCapabilityServableMime(file.mimeType) ? file.downloadUrl : void 0;
5973
+ if (!file.fileId) return link ?? "read it with get_attachment (see list_task_files)";
5974
+ return `read it with get_attachment("${file.fileId}")${link ? ` or download: ${link}` : ""}`;
5975
+ }
5973
5976
  function formatChatFile(file) {
5974
5977
  const sizeStr = file.fileSize ? `, ${formatFileSize(file.fileSize)}` : "";
5975
5978
  if (file.content && file.contentEncoding === "utf-8") {
@@ -5981,7 +5984,7 @@ function formatChatFile(file) {
5981
5984
  ];
5982
5985
  }
5983
5986
  if (!file.content) {
5984
- return [`[Attached: ${file.fileName} (${file.mimeType}${sizeStr})]: ${file.downloadUrl}`];
5987
+ return [`[Attached: ${file.fileName} (${file.mimeType}${sizeStr})]: ${notInlinedRef(file)}`];
5985
5988
  }
5986
5989
  if (file.content && file.contentEncoding === "base64") {
5987
5990
  const link = file.downloadUrl ? ` or download: ${file.downloadUrl}` : "";
@@ -6018,7 +6021,7 @@ function formatTaskFile(file) {
6018
6021
  ];
6019
6022
  }
6020
6023
  if (!file.content) {
6021
- return [`- **${file.fileName}** (${file.mimeType}): ${file.downloadUrl}`];
6024
+ return [`- **${file.fileName}** (${file.mimeType}): ${notInlinedRef(file)}`];
6022
6025
  }
6023
6026
  return [];
6024
6027
  }
@@ -6367,8 +6370,8 @@ var PACK_RUNNER_FOOTER = [
6367
6370
  function resolveSinglePodPackBranch(context) {
6368
6371
  return context.githubBranch || context.baseBranch;
6369
6372
  }
6370
- function isPackRunnerSession(mode, isAuto, isParentTask) {
6371
- return mode === "pack" || mode === "pm" && !!isAuto && !!isParentTask;
6373
+ function isPackRunnerSession(mode) {
6374
+ return mode === "pack";
6372
6375
  }
6373
6376
  function buildSinglePodPackPrompt(context, setupLog) {
6374
6377
  const packBranch = resolveSinglePodPackBranch(context);
@@ -6937,7 +6940,7 @@ Git:`,
6937
6940
  function buildSystemPrompt(mode, context, config, setupLog, agentMode) {
6938
6941
  const isPm = mode === "pm";
6939
6942
  const isPmActive = isPm && agentMode === "building";
6940
- if (isPackRunnerSession(mode, config.isAuto, context.isParentTask)) {
6943
+ if (isPackRunnerSession(mode)) {
6941
6944
  return buildSinglePodPackPrompt(context, setupLog);
6942
6945
  }
6943
6946
  const parts = isPmActive ? buildActivePreamble(context, config.workspaceDir) : isPm ? buildPmPreamble(context) : buildTaskAgentPreamble(context, config.workspaceDir, config.runtimeTui, agentMode);
@@ -7236,7 +7239,7 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
7236
7239
  return parts;
7237
7240
  }
7238
7241
  async function buildInitialPrompt(mode, context, isAuto, agentMode) {
7239
- const isPackRunner = isPackRunnerSession(mode, isAuto, context.isParentTask);
7242
+ const isPackRunner = isPackRunnerSession(mode);
7240
7243
  if (!isPackRunner) {
7241
7244
  const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode, isAuto);
7242
7245
  if (sessionRelaunch) {
@@ -8091,7 +8094,7 @@ var listTaskFilesContract = defineToolContract({
8091
8094
  var getAttachmentContract = defineToolContract({
8092
8095
  name: "get_attachment",
8093
8096
  agent: {
8094
- 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.",
8097
+ description: "Fetch one task file by ID. Text and images under the inline size caps come back inline. Any other file (PDF, Word, Excel, zip, large logs) is saved into this pod, and the result names the local path: open it with the Read tool or Bash. A document downloadUrl answers 404 without a signed-in project member's session, so do not curl or WebFetch it. Call list_task_files first to discover IDs and sizes. Pass task_id when the file is attached to another card in this project, such as the pack parent.",
8095
8098
  fields: {
8096
8099
  fileId: f.string({ desc: "The file ID to retrieve" }),
8097
8100
  task_id: agentTaskIdOrSlug
@@ -9343,6 +9346,109 @@ var evaluateQuestionsContract = defineToolContract({
9343
9346
  }
9344
9347
  });
9345
9348
  var evaluateContracts = [evaluateQuestionsContract];
9349
+ var listSiteAssetTargetsContract = defineToolContract({
9350
+ name: "list_site_asset_targets",
9351
+ agent: {
9352
+ description: "List the registered static-site media targets this card's project may publish to.",
9353
+ fields: {}
9354
+ },
9355
+ mcp: {
9356
+ description: "List registered static-site media targets for a project.",
9357
+ fields: { projectId: mcpProjectId }
9358
+ }
9359
+ });
9360
+ var uploadSiteAssetsContract = defineToolContract({
9361
+ name: "upload_site_assets",
9362
+ agent: {
9363
+ description: "Upload up to 50 workspace image or video files (25 MiB each) to a registered site using Conveyor identity. Call list_site_asset_targets first for the registered siteId. Returns verified immutable keys and public URLs per file.",
9364
+ fields: {
9365
+ siteId: f.string({ min: 1, max: 100 }),
9366
+ paths: f.array(f.string({ min: 1 }), { min: 1, max: 50 })
9367
+ }
9368
+ },
9369
+ mcp: {
9370
+ description: "Upload up to 50 workspace image or video files (25 MiB each) to a registered site using Conveyor identity. Call list_site_asset_targets first for the registered siteId. Returns verified immutable keys and public URLs per file.",
9371
+ fields: {
9372
+ projectId: mcpProjectId,
9373
+ siteId: f.string({ min: 1, max: 100 }),
9374
+ paths: f.array(f.string({ min: 1 }), { min: 1, max: 50 })
9375
+ }
9376
+ }
9377
+ });
9378
+ var siteAssetsContracts = [
9379
+ listSiteAssetTargetsContract,
9380
+ uploadSiteAssetsContract
9381
+ ];
9382
+ function validateSiteAssetResult(value, expected) {
9383
+ if (!value || typeof value !== "object" || Array.isArray(value))
9384
+ throw new Error("verify returned invalid JSON");
9385
+ const result = value;
9386
+ for (const key of ["key", "publicUrl", "sha256", "sizeBytes", "mimeType"]) {
9387
+ if (result[key] !== expected[key])
9388
+ throw new Error(`verify response ${key} does not match upload capability`);
9389
+ }
9390
+ if (result.status !== "uploaded" && result.status !== "existing")
9391
+ throw new Error("verify response has invalid status");
9392
+ return { ...expected, status: result.status };
9393
+ }
9394
+ async function readReply(response) {
9395
+ if (!response.body) throw new Error("verify returned an empty response");
9396
+ const reader = response.body.getReader();
9397
+ const decoder = new TextDecoder();
9398
+ let text = "";
9399
+ let size = 0;
9400
+ try {
9401
+ while (true) {
9402
+ const { done, value } = await reader.read();
9403
+ if (done) break;
9404
+ size += value.byteLength;
9405
+ if (size > 16384) throw new Error("verify response exceeds 16KiB");
9406
+ text += decoder.decode(value, { stream: true });
9407
+ }
9408
+ text += decoder.decode();
9409
+ } finally {
9410
+ await reader.cancel();
9411
+ reader.releaseLock();
9412
+ }
9413
+ try {
9414
+ return JSON.parse(text);
9415
+ } catch {
9416
+ throw new Error("verify returned invalid JSON");
9417
+ }
9418
+ }
9419
+ async function transferSiteAsset(capability, bytes, expected) {
9420
+ const options = {
9421
+ headers: capability.headers,
9422
+ redirect: "error",
9423
+ signal: AbortSignal.timeout(6e4)
9424
+ };
9425
+ let put;
9426
+ try {
9427
+ put = await fetch(capability.uploadUrl, { ...options, method: "PUT", body: bytes });
9428
+ } catch {
9429
+ throw new Error("Upload transfer failed or timed out; retry this file");
9430
+ }
9431
+ await put.body?.cancel();
9432
+ if (!put.ok) throw new Error(`upload HTTP ${put.status}; retry or check site access`);
9433
+ let verify;
9434
+ try {
9435
+ verify = await fetch(`${new URL(capability.uploadUrl).origin}/_site-assets/verify`, {
9436
+ ...options,
9437
+ method: "POST",
9438
+ signal: AbortSignal.timeout(6e4)
9439
+ });
9440
+ } catch {
9441
+ throw new Error("Upload verification failed or timed out; retry this file");
9442
+ }
9443
+ if (!verify.ok) {
9444
+ await verify.body?.cancel();
9445
+ throw new Error(`verify HTTP ${verify.status}; retry or check site access`);
9446
+ }
9447
+ return {
9448
+ ...validateSiteAssetResult(await readReply(verify), expected),
9449
+ status: put.status === 201 ? "uploaded" : "existing"
9450
+ };
9451
+ }
9346
9452
  var TOOL_CONTRACTS = Object.fromEntries(
9347
9453
  [
9348
9454
  ...tasksContracts,
@@ -9361,7 +9467,8 @@ var TOOL_CONTRACTS = Object.fromEntries(
9361
9467
  ...logsContracts,
9362
9468
  ...ciWaitContracts,
9363
9469
  ...evaluateContracts,
9364
- ...incidentsContracts
9470
+ ...incidentsContracts,
9471
+ ...siteAssetsContracts
9365
9472
  ].map((contract) => [contract.name, contract])
9366
9473
  );
9367
9474
 
@@ -9407,6 +9514,97 @@ function formatCliEvent(e) {
9407
9514
  return formatter ? formatter(e) : JSON.stringify(e);
9408
9515
  }
9409
9516
 
9517
+ // src/tools/attachment-download.ts
9518
+ import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
9519
+ import { createWriteStream } from "fs";
9520
+ import { mkdir as mkdir6, rename, rm as rm4 } from "fs/promises";
9521
+ import { basename as basename2, join as join17 } from "path";
9522
+ import { Readable, Transform } from "stream";
9523
+ import { pipeline } from "stream/promises";
9524
+ var DOWNLOAD_TIMEOUT_MS = 12e4;
9525
+ var AttachmentSaveError = class extends Error {
9526
+ constructor(code) {
9527
+ super(`Attachment save failed: ${code}`);
9528
+ this.code = code;
9529
+ this.name = "AttachmentSaveError";
9530
+ }
9531
+ code;
9532
+ };
9533
+ function attachmentsRoot() {
9534
+ return join17(sessionTempBase(), "conveyor-attachments");
9535
+ }
9536
+ function safeAttachmentId(fileId) {
9537
+ if (/^[A-Za-z0-9_-]{1,64}$/.test(fileId)) return fileId;
9538
+ return createHash4("sha256").update(fileId).digest("hex").slice(0, 32);
9539
+ }
9540
+ function safeAttachmentName(fileName) {
9541
+ const cleaned = basename2(fileName).replace(/[^A-Za-z0-9._-]/g, "_").replace(/^[.-]+/, "").slice(-120);
9542
+ return cleaned || "attachment";
9543
+ }
9544
+ function byteCap(limit) {
9545
+ let seen = 0;
9546
+ const stream = new Transform({
9547
+ transform(chunk, _enc, callback) {
9548
+ seen += chunk.length;
9549
+ if (seen > limit) callback(new AttachmentSaveError("too_large"));
9550
+ else callback(null, chunk);
9551
+ }
9552
+ });
9553
+ return Object.assign(stream, { bytes: () => seen });
9554
+ }
9555
+ function classify(err) {
9556
+ if (err instanceof AttachmentSaveError) return err;
9557
+ if (err instanceof Error) {
9558
+ if (err.name === "TimeoutError" || err.name === "AbortError") {
9559
+ return new AttachmentSaveError("timeout");
9560
+ }
9561
+ if ("syscall" in err && typeof err.syscall === "string" && "path" in err) {
9562
+ return new AttachmentSaveError("write_failed");
9563
+ }
9564
+ }
9565
+ return new AttachmentSaveError("network");
9566
+ }
9567
+ async function fetchAttachment(url) {
9568
+ let res;
9569
+ try {
9570
+ res = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
9571
+ } catch (err) {
9572
+ throw classify(err);
9573
+ }
9574
+ if (!res.ok) throw new AttachmentSaveError(`http_${res.status}`);
9575
+ const declared = Number(res.headers.get("content-length"));
9576
+ if (Number.isFinite(declared) && declared > MAX_FILE_SIZE_BYTES) {
9577
+ throw new AttachmentSaveError("too_large");
9578
+ }
9579
+ if (!res.body) throw new AttachmentSaveError("network");
9580
+ return res;
9581
+ }
9582
+ async function saveAttachmentToDisk(input) {
9583
+ const res = await fetchAttachment(input.url);
9584
+ const dir = join17(attachmentsRoot(), safeAttachmentId(input.fileId));
9585
+ const dest = join17(dir, safeAttachmentName(input.fileName));
9586
+ const part = `${dest}.${randomUUID2()}.part`;
9587
+ try {
9588
+ await mkdir6(dir, { recursive: true, mode: 448 });
9589
+ } catch {
9590
+ throw new AttachmentSaveError("write_failed");
9591
+ }
9592
+ const cap = byteCap(MAX_FILE_SIZE_BYTES);
9593
+ try {
9594
+ await pipeline(
9595
+ Readable.fromWeb(res.body),
9596
+ cap,
9597
+ createWriteStream(part, { mode: 384 })
9598
+ );
9599
+ await rename(part, dest);
9600
+ } catch (err) {
9601
+ await rm4(part, { force: true }).catch(() => {
9602
+ });
9603
+ throw classify(err);
9604
+ }
9605
+ return { path: dest, bytes: cap.bytes() };
9606
+ }
9607
+
9410
9608
  // src/tools/task-context-tools.ts
9411
9609
  function buildReadTaskChatTool(connection) {
9412
9610
  return defineContractTool(
@@ -9595,18 +9793,23 @@ function buildGetAttachmentTool(connection) {
9595
9793
  const file = await connection.call("getTaskFile", {
9596
9794
  sessionId: connection.sessionId,
9597
9795
  fileId,
9598
- ...task_id ? { taskId: task_id } : {}
9796
+ ...task_id ? { taskId: task_id } : {},
9797
+ agentDownload: true
9599
9798
  });
9600
- const { content: rawContent, ...metadata } = file;
9601
- const content = [
9602
- { type: "text", text: JSON.stringify(metadata, null, 2) }
9603
- ];
9799
+ const { content: rawContent, agentDownloadUrl, ...metadata } = file;
9604
9800
  if (rawContent && file.contentEncoding === "base64" && isImageMimeType(file.mimeType)) {
9605
- content.push(imageBlock(rawContent, file.mimeType));
9606
- } else if (rawContent) {
9607
- content[0] = { type: "text", text: JSON.stringify(file, null, 2) };
9801
+ return {
9802
+ content: [
9803
+ { type: "text", text: JSON.stringify(metadata, null, 2) },
9804
+ imageBlock(rawContent, file.mimeType)
9805
+ ]
9806
+ };
9608
9807
  }
9609
- return { content };
9808
+ if (rawContent) {
9809
+ return textResult(JSON.stringify({ ...metadata, content: rawContent }, null, 2));
9810
+ }
9811
+ if (!agentDownloadUrl) return textResult(JSON.stringify(metadata, null, 2));
9812
+ return textResult(await saveForAgent(metadata, agentDownloadUrl));
9610
9813
  } catch (error) {
9611
9814
  return textResult(
9612
9815
  `Failed to get task file: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -9616,6 +9819,26 @@ function buildGetAttachmentTool(connection) {
9616
9819
  { annotations: { readOnlyHint: true } }
9617
9820
  );
9618
9821
  }
9822
+ async function saveForAgent(metadata, url) {
9823
+ const { downloadUrl: _documentLink, ...rest } = metadata;
9824
+ const shown = isCapabilityServableMime(metadata.mimeType) ? metadata : rest;
9825
+ const header = JSON.stringify(shown, null, 2);
9826
+ try {
9827
+ const saved = await saveAttachmentToDisk({
9828
+ url,
9829
+ fileId: metadata.id,
9830
+ fileName: metadata.fileName
9831
+ });
9832
+ return `${header}
9833
+
9834
+ Saved to ${saved.path} (${saved.bytes} bytes). Open it with the Read tool (a PDF over 10 pages needs the pages parameter) or process it with Bash.`;
9835
+ } catch (error) {
9836
+ const code = error instanceof AttachmentSaveError ? error.code : "network";
9837
+ return `${header}
9838
+
9839
+ Could not save the file (${code}). Call get_attachment again; each call mints a fresh 5-minute link.`;
9840
+ }
9841
+ }
9619
9842
  function buildTaskContextTools(connection) {
9620
9843
  return [
9621
9844
  buildReadTaskChatTool(connection),
@@ -10160,7 +10383,7 @@ function buildMutationTools(connection, config) {
10160
10383
  }
10161
10384
 
10162
10385
  // src/tools/attachment-tools.ts
10163
- import { basename as basename2, extname, isAbsolute, join as join17 } from "path";
10386
+ import { basename as basename3, extname, isAbsolute, join as join18 } from "path";
10164
10387
  var MIME_BY_EXT = {
10165
10388
  ".png": "image/png",
10166
10389
  ".jpg": "image/jpeg",
@@ -10239,7 +10462,7 @@ function buildUploadAttachmentTool(connection, config) {
10239
10462
  async ({ path, title, comment, tags, showcase, supersedes }) => {
10240
10463
  const caption = title ?? comment;
10241
10464
  try {
10242
- const filePath = isAbsolute(path) ? path : join17(config.workspaceDir, path);
10465
+ const filePath = isAbsolute(path) ? path : join18(config.workspaceDir, path);
10243
10466
  const mimeType = inferMimeType(filePath);
10244
10467
  const info = await statWorkspacePath(filePath);
10245
10468
  if (!info.isFile) {
@@ -10250,7 +10473,7 @@ function buildUploadAttachmentTool(connection, config) {
10250
10473
  `File is ${info.size} bytes \u2014 exceeds the ${MAX_FILE_SIZE_BYTES} byte (25MB) upload limit.`
10251
10474
  );
10252
10475
  }
10253
- const fileName = basename2(filePath);
10476
+ const fileName = basename3(filePath);
10254
10477
  const { fileId, uploadUrl } = await connection.call("requestFileUpload", {
10255
10478
  sessionId: connection.sessionId,
10256
10479
  fileName,
@@ -10899,7 +11122,7 @@ import { z as z9 } from "zod";
10899
11122
 
10900
11123
  // src/execution/context-path-verifier.ts
10901
11124
  import { readFile as readFile4 } from "fs/promises";
10902
- import { isAbsolute as isAbsolute2, join as join18, normalize } from "path";
11125
+ import { isAbsolute as isAbsolute2, join as join19, normalize } from "path";
10903
11126
  var PROBLEM_TEXT = {
10904
11127
  not_found: "does not exist in the repo",
10905
11128
  expected_folder: "is a file, not a folder \u2014 use type 'file', 'rule', or 'doc'",
@@ -10944,7 +11167,7 @@ async function verifyContextPaths(links, workspaceDir) {
10944
11167
  problems.push({ type: link.type, path: link.path, reason: shape });
10945
11168
  continue;
10946
11169
  }
10947
- const absolutePath = join18(workspaceDir, toRelativePath(link.path));
11170
+ const absolutePath = join19(workspaceDir, toRelativePath(link.path));
10948
11171
  const stat = await statWorkspacePath(absolutePath);
10949
11172
  const wantsDirectory = expectsDirectory(link.type);
10950
11173
  if (!stat.exists) {
@@ -11893,8 +12116,60 @@ function buildEvaluateTools(connection, projectId) {
11893
12116
  ];
11894
12117
  }
11895
12118
 
12119
+ // src/tools/site-asset-tools.ts
12120
+ import crypto from "crypto";
12121
+ import { extname as extname2, isAbsolute as isAbsolute3, join as join20 } from "path";
12122
+ var MAX_SITE_ASSET_BYTES = 25 * 1024 * 1024;
12123
+ function buildSiteAssetTools(connection, config, projectId) {
12124
+ const list = defineContractTool(listSiteAssetTargetsContract, async () => {
12125
+ try {
12126
+ return textResult(
12127
+ JSON.stringify(await connection.call("listSiteAssetTargets", { projectId }), null, 2)
12128
+ );
12129
+ } catch (error) {
12130
+ return textResult(
12131
+ `Failed to list static-site targets: ${error instanceof Error ? error.message : "Unknown error"}`
12132
+ );
12133
+ }
12134
+ });
12135
+ const upload = defineContractTool(uploadSiteAssetsContract, async ({ siteId, paths }) => {
12136
+ const results = [];
12137
+ for (const requestedPath of [...paths].sort()) {
12138
+ const path = isAbsolute3(requestedPath) ? requestedPath : join20(config.workspaceDir, requestedPath);
12139
+ try {
12140
+ const info = await statWorkspacePath(path);
12141
+ if (!info.isFile) throw new Error("not a file");
12142
+ if (info.size > MAX_SITE_ASSET_BYTES) throw new Error("asset exceeds 25MiB limit");
12143
+ const bytes = await readWorkspaceBytes(path);
12144
+ if (bytes.length !== info.size || bytes.length > MAX_SITE_ASSET_BYTES)
12145
+ throw new Error("asset changed during read or exceeds 25MiB limit");
12146
+ const sha256 = crypto.createHash("sha256").update(bytes).digest("hex");
12147
+ const capability = await connection.call("requestSiteAssetUpload", {
12148
+ projectId,
12149
+ siteId,
12150
+ sha256,
12151
+ ext: extname2(path).slice(1).toLowerCase(),
12152
+ sizeBytes: bytes.length
12153
+ });
12154
+ const result = await transferSiteAsset(capability, new Uint8Array(bytes), {
12155
+ key: capability.key,
12156
+ publicUrl: capability.publicUrl,
12157
+ sha256,
12158
+ sizeBytes: bytes.length,
12159
+ mimeType: capability.headers["content-type"]
12160
+ });
12161
+ results.push({ path, ...result });
12162
+ } catch (error) {
12163
+ results.push({ path, error: error instanceof Error ? error.message : "Upload failed" });
12164
+ }
12165
+ }
12166
+ return textResult(JSON.stringify({ results }, null, 2));
12167
+ });
12168
+ return [list, upload];
12169
+ }
12170
+
11896
12171
  // src/tools/connected-tools.ts
11897
- function connectedToolsFor(connection, context) {
12172
+ function connectedToolsFor(connection, context, config) {
11898
12173
  const projectId = context?.projectId;
11899
12174
  if (!projectId) return [];
11900
12175
  return [
@@ -11910,7 +12185,8 @@ function connectedToolsFor(connection, context) {
11910
12185
  ...buildEvaluateTools(connection, projectId),
11911
12186
  ...context.googleAnalyticsConfigured ? [buildAnalyticsSummaryTool(connection, projectId)] : [],
11912
12187
  ...context.gcpLogsConfigured ? [buildQueryGcpLogsTool(connection, projectId)] : [],
11913
- ...context.grafanaLogsConfigured ? [buildQueryGrafanaLogsTool(connection, projectId)] : []
12188
+ ...context.grafanaLogsConfigured ? [buildQueryGrafanaLogsTool(connection, projectId)] : [],
12189
+ ...config && context.siteAssetTargetsConfigured ? buildSiteAssetTools(connection, config, projectId) : []
11914
12190
  ];
11915
12191
  }
11916
12192
 
@@ -12125,7 +12401,7 @@ function buildConveyorTools(connection, config, context, agentMode) {
12125
12401
  const handoffTools = config.mode === "pm" && (effectiveMode === "discovery" || effectiveMode === "auto") ? [buildHandoffTool(connection)] : [];
12126
12402
  const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
12127
12403
  const glossaryTools = glossaryToolsFor(connection, config, context);
12128
- const connectedTools = connectedToolsFor(connection, context);
12404
+ const connectedTools = connectedToolsFor(connection, context, config);
12129
12405
  const isPack = config.mode === "pack" || Boolean(context?.isParentTask);
12130
12406
  return withAlwaysLoad(
12131
12407
  [
@@ -12709,7 +12985,7 @@ function fallbackResetIso(rateLimitType, now = Date.now()) {
12709
12985
  }
12710
12986
 
12711
12987
  // src/execution/tool-access.ts
12712
- import { isAbsolute as isAbsolute3, posix } from "path";
12988
+ import { isAbsolute as isAbsolute4, posix } from "path";
12713
12989
 
12714
12990
  // src/execution/task-property-utils.ts
12715
12991
  function collectMissingProps(taskProps) {
@@ -12855,7 +13131,7 @@ function isPlanFile(input) {
12855
13131
  }
12856
13132
  function isScratchPath(input) {
12857
13133
  const filePath = String(input.file_path ?? input.path ?? "");
12858
- if (!isAbsolute3(filePath)) return false;
13134
+ if (!isAbsolute4(filePath)) return false;
12859
13135
  return posix.normalize(filePath).startsWith("/tmp/");
12860
13136
  }
12861
13137
  var ARTIFACT_DENIED = "Publish deliverables on the card with upload_attachment; the Artifact tool is not available in Conveyor sessions.";
@@ -13186,7 +13462,7 @@ function buildHooks(host) {
13186
13462
  };
13187
13463
  }
13188
13464
  function taskIdToSessionUuid(lineageKey) {
13189
- const hash = createHash4("sha256").update(lineageKey).digest("hex");
13465
+ const hash = createHash5("sha256").update(lineageKey).digest("hex");
13190
13466
  return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-8${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
13191
13467
  }
13192
13468
  function sessionLineageKey(taskId, agentMode, runnerMode) {
@@ -13323,12 +13599,6 @@ function buildQueryOptions(host, context) {
13323
13599
  context,
13324
13600
  {
13325
13601
  ...host.config,
13326
- // ModeController's live value, NOT the boot-frozen `config.isAuto` the
13327
- // spread carries: the pack gate reads this, and the turn prompt below
13328
- // gates on `host.isAuto`. Letting the two read different sources is how
13329
- // a codespace parent card got "you cannot write or edit files" beside
13330
- // "run /conveyor-build".
13331
- isAuto: host.isAuto,
13332
13602
  runtimeTui: process.env.CONVEYOR_TUI
13333
13603
  },
13334
13604
  host.setupLog,
@@ -13800,7 +14070,7 @@ function freshSessionOptions(host, context, options) {
13800
14070
  markSpawnedTranscript(host.config.workspaceDir, lineageUuid);
13801
14071
  return { ...options, sessionId: lineageUuid };
13802
14072
  }
13803
- return { ...options, sessionId: randomUUID2() };
14073
+ return { ...options, sessionId: randomUUID3() };
13804
14074
  }
13805
14075
  async function buildRetryQuery(host, context, options, lastErrorWasImage) {
13806
14076
  if (lastErrorWasImage) {
@@ -15067,11 +15337,9 @@ var SessionRunner = class _SessionRunner {
15067
15337
  this.config = config;
15068
15338
  this.callbacks = callbacks;
15069
15339
  this.connection = new AgentConnection(config.connection);
15070
- const portVisibility = new CodespacePortVisibility();
15071
15340
  this.portDiscovery = deps.portDiscovery ?? new PortDiscovery({
15072
15341
  report: (ports) => this.connection.reportDiscoveredPorts(ports),
15073
- annotate: (ports) => loadAndAnnotateDiscoveredPorts(config.workspaceDir, ports),
15074
- onReported: (ports) => portVisibility.ensureVisible(ports.map(({ port }) => port))
15342
+ annotate: (ports) => loadAndAnnotateDiscoveredPorts(config.workspaceDir, ports)
15075
15343
  });
15076
15344
  const initialMode = config.agentMode ?? (config.runnerMode === "pm" ? config.isAuto ? "auto" : "discovery" : "building");
15077
15345
  this.mode = new ModeController(initialMode, config.runnerMode, config.isAuto);
@@ -15404,7 +15672,6 @@ var SessionRunner = class _SessionRunner {
15404
15672
  await this.setState("connected");
15405
15673
  this.connection.sendEvent({ type: "connected", sessionId: this.sessionId });
15406
15674
  this.wireConnectionCallbacks();
15407
- registerBootMilestoneSocketFallback((key) => this.connection.reportBootMilestone(key));
15408
15675
  this.lifecycle.startHeartbeat();
15409
15676
  this.loopLag.start();
15410
15677
  this.connection.startHeartbeatWorker(this.loopLag.sharedBuffer);
@@ -16189,6 +16456,7 @@ var SessionRunner = class _SessionRunner {
16189
16456
  baseBranch: ctx.baseBranch ?? "",
16190
16457
  projectName: ctx.projectName ?? null,
16191
16458
  projectDescription: ctx.projectDescription ?? null,
16459
+ siteAssetTargetsConfigured: ctx.siteAssetTargetsConfigured ?? false,
16192
16460
  githubPRUrl: ctx.githubPRUrl,
16193
16461
  claudeSessionId: ctx.claudeSessionId ?? null,
16194
16462
  lastSeenMessageId: ctx.lastSeenMessageId ?? null,
@@ -16585,7 +16853,7 @@ ${outcome.failures.join("\n")}
16585
16853
  }
16586
16854
  };
16587
16855
 
16588
- // src/setup/codespace.ts
16856
+ // src/setup/unshallow.ts
16589
16857
  import { execSync } from "child_process";
16590
16858
  function unshallowRepo(workspaceDir) {
16591
16859
  try {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactToken
3
- } from "./chunk-Q4FQOJ7D.js";
3
+ } from "./chunk-VIUZS7V4.js";
4
4
 
5
5
  // src/boot/graphify.ts
6
6
  var USER_HOME_MOUNT = "/mnt/conveyor-users";
@@ -8,10 +8,10 @@ import {
8
8
  readWorkspaceBytes,
9
9
  statWorkspacePath,
10
10
  workspacePathExists
11
- } from "./chunk-KMBMW3J3.js";
11
+ } from "./chunk-SKAFB6DG.js";
12
12
  import {
13
13
  reportBootMilestone
14
- } from "./chunk-Q4FQOJ7D.js";
14
+ } from "./chunk-VIUZS7V4.js";
15
15
  import {
16
16
  runSetupCommand,
17
17
  runStartCommand,