@rallycry/conveyor-agent 11.0.43 → 11.0.45

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.
@@ -8,7 +8,7 @@ import {
8
8
  readWorkspaceBytes,
9
9
  statWorkspacePath,
10
10
  workspacePathExists
11
- } from "./chunk-4SX3FFVU.js";
11
+ } from "./chunk-IBLFLCFK.js";
12
12
  import {
13
13
  reportBootMilestone
14
14
  } from "./chunk-Q4FQOJ7D.js";
@@ -98,7 +98,7 @@ import {
98
98
  statWorkspacePath,
99
99
  updateRemoteToken,
100
100
  verifyGitCredential
101
- } from "./chunk-4SX3FFVU.js";
101
+ } from "./chunk-IBLFLCFK.js";
102
102
  import {
103
103
  registerBootMilestoneSocketFallback,
104
104
  reportBootMilestone
@@ -9343,6 +9343,109 @@ var evaluateQuestionsContract = defineToolContract({
9343
9343
  }
9344
9344
  });
9345
9345
  var evaluateContracts = [evaluateQuestionsContract];
9346
+ var listSiteAssetTargetsContract = defineToolContract({
9347
+ name: "list_site_asset_targets",
9348
+ agent: {
9349
+ description: "List the registered static-site media targets this card's project may publish to.",
9350
+ fields: {}
9351
+ },
9352
+ mcp: {
9353
+ description: "List registered static-site media targets for a project.",
9354
+ fields: { projectId: mcpProjectId }
9355
+ }
9356
+ });
9357
+ var uploadSiteAssetsContract = defineToolContract({
9358
+ name: "upload_site_assets",
9359
+ agent: {
9360
+ 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.",
9361
+ fields: {
9362
+ siteId: f.string({ min: 1, max: 100 }),
9363
+ paths: f.array(f.string({ min: 1 }), { min: 1, max: 50 })
9364
+ }
9365
+ },
9366
+ mcp: {
9367
+ 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.",
9368
+ fields: {
9369
+ projectId: mcpProjectId,
9370
+ siteId: f.string({ min: 1, max: 100 }),
9371
+ paths: f.array(f.string({ min: 1 }), { min: 1, max: 50 })
9372
+ }
9373
+ }
9374
+ });
9375
+ var siteAssetsContracts = [
9376
+ listSiteAssetTargetsContract,
9377
+ uploadSiteAssetsContract
9378
+ ];
9379
+ function validateSiteAssetResult(value, expected) {
9380
+ if (!value || typeof value !== "object" || Array.isArray(value))
9381
+ throw new Error("verify returned invalid JSON");
9382
+ const result = value;
9383
+ for (const key of ["key", "publicUrl", "sha256", "sizeBytes", "mimeType"]) {
9384
+ if (result[key] !== expected[key])
9385
+ throw new Error(`verify response ${key} does not match upload capability`);
9386
+ }
9387
+ if (result.status !== "uploaded" && result.status !== "existing")
9388
+ throw new Error("verify response has invalid status");
9389
+ return { ...expected, status: result.status };
9390
+ }
9391
+ async function readReply(response) {
9392
+ if (!response.body) throw new Error("verify returned an empty response");
9393
+ const reader = response.body.getReader();
9394
+ const decoder = new TextDecoder();
9395
+ let text = "";
9396
+ let size = 0;
9397
+ try {
9398
+ while (true) {
9399
+ const { done, value } = await reader.read();
9400
+ if (done) break;
9401
+ size += value.byteLength;
9402
+ if (size > 16384) throw new Error("verify response exceeds 16KiB");
9403
+ text += decoder.decode(value, { stream: true });
9404
+ }
9405
+ text += decoder.decode();
9406
+ } finally {
9407
+ await reader.cancel();
9408
+ reader.releaseLock();
9409
+ }
9410
+ try {
9411
+ return JSON.parse(text);
9412
+ } catch {
9413
+ throw new Error("verify returned invalid JSON");
9414
+ }
9415
+ }
9416
+ async function transferSiteAsset(capability, bytes, expected) {
9417
+ const options = {
9418
+ headers: capability.headers,
9419
+ redirect: "error",
9420
+ signal: AbortSignal.timeout(6e4)
9421
+ };
9422
+ let put;
9423
+ try {
9424
+ put = await fetch(capability.uploadUrl, { ...options, method: "PUT", body: bytes });
9425
+ } catch {
9426
+ throw new Error("Upload transfer failed or timed out; retry this file");
9427
+ }
9428
+ await put.body?.cancel();
9429
+ if (!put.ok) throw new Error(`upload HTTP ${put.status}; retry or check site access`);
9430
+ let verify;
9431
+ try {
9432
+ verify = await fetch(`${new URL(capability.uploadUrl).origin}/_site-assets/verify`, {
9433
+ ...options,
9434
+ method: "POST",
9435
+ signal: AbortSignal.timeout(6e4)
9436
+ });
9437
+ } catch {
9438
+ throw new Error("Upload verification failed or timed out; retry this file");
9439
+ }
9440
+ if (!verify.ok) {
9441
+ await verify.body?.cancel();
9442
+ throw new Error(`verify HTTP ${verify.status}; retry or check site access`);
9443
+ }
9444
+ return {
9445
+ ...validateSiteAssetResult(await readReply(verify), expected),
9446
+ status: put.status === 201 ? "uploaded" : "existing"
9447
+ };
9448
+ }
9346
9449
  var TOOL_CONTRACTS = Object.fromEntries(
9347
9450
  [
9348
9451
  ...tasksContracts,
@@ -9361,7 +9464,8 @@ var TOOL_CONTRACTS = Object.fromEntries(
9361
9464
  ...logsContracts,
9362
9465
  ...ciWaitContracts,
9363
9466
  ...evaluateContracts,
9364
- ...incidentsContracts
9467
+ ...incidentsContracts,
9468
+ ...siteAssetsContracts
9365
9469
  ].map((contract) => [contract.name, contract])
9366
9470
  );
9367
9471
 
@@ -11893,8 +11997,60 @@ function buildEvaluateTools(connection, projectId) {
11893
11997
  ];
11894
11998
  }
11895
11999
 
12000
+ // src/tools/site-asset-tools.ts
12001
+ import crypto from "crypto";
12002
+ import { extname as extname2, isAbsolute as isAbsolute3, join as join19 } from "path";
12003
+ var MAX_SITE_ASSET_BYTES = 25 * 1024 * 1024;
12004
+ function buildSiteAssetTools(connection, config, projectId) {
12005
+ const list = defineContractTool(listSiteAssetTargetsContract, async () => {
12006
+ try {
12007
+ return textResult(
12008
+ JSON.stringify(await connection.call("listSiteAssetTargets", { projectId }), null, 2)
12009
+ );
12010
+ } catch (error) {
12011
+ return textResult(
12012
+ `Failed to list static-site targets: ${error instanceof Error ? error.message : "Unknown error"}`
12013
+ );
12014
+ }
12015
+ });
12016
+ const upload = defineContractTool(uploadSiteAssetsContract, async ({ siteId, paths }) => {
12017
+ const results = [];
12018
+ for (const requestedPath of [...paths].sort()) {
12019
+ const path = isAbsolute3(requestedPath) ? requestedPath : join19(config.workspaceDir, requestedPath);
12020
+ try {
12021
+ const info = await statWorkspacePath(path);
12022
+ if (!info.isFile) throw new Error("not a file");
12023
+ if (info.size > MAX_SITE_ASSET_BYTES) throw new Error("asset exceeds 25MiB limit");
12024
+ const bytes = await readWorkspaceBytes(path);
12025
+ if (bytes.length !== info.size || bytes.length > MAX_SITE_ASSET_BYTES)
12026
+ throw new Error("asset changed during read or exceeds 25MiB limit");
12027
+ const sha256 = crypto.createHash("sha256").update(bytes).digest("hex");
12028
+ const capability = await connection.call("requestSiteAssetUpload", {
12029
+ projectId,
12030
+ siteId,
12031
+ sha256,
12032
+ ext: extname2(path).slice(1).toLowerCase(),
12033
+ sizeBytes: bytes.length
12034
+ });
12035
+ const result = await transferSiteAsset(capability, new Uint8Array(bytes), {
12036
+ key: capability.key,
12037
+ publicUrl: capability.publicUrl,
12038
+ sha256,
12039
+ sizeBytes: bytes.length,
12040
+ mimeType: capability.headers["content-type"]
12041
+ });
12042
+ results.push({ path, ...result });
12043
+ } catch (error) {
12044
+ results.push({ path, error: error instanceof Error ? error.message : "Upload failed" });
12045
+ }
12046
+ }
12047
+ return textResult(JSON.stringify({ results }, null, 2));
12048
+ });
12049
+ return [list, upload];
12050
+ }
12051
+
11896
12052
  // src/tools/connected-tools.ts
11897
- function connectedToolsFor(connection, context) {
12053
+ function connectedToolsFor(connection, context, config) {
11898
12054
  const projectId = context?.projectId;
11899
12055
  if (!projectId) return [];
11900
12056
  return [
@@ -11910,7 +12066,8 @@ function connectedToolsFor(connection, context) {
11910
12066
  ...buildEvaluateTools(connection, projectId),
11911
12067
  ...context.googleAnalyticsConfigured ? [buildAnalyticsSummaryTool(connection, projectId)] : [],
11912
12068
  ...context.gcpLogsConfigured ? [buildQueryGcpLogsTool(connection, projectId)] : [],
11913
- ...context.grafanaLogsConfigured ? [buildQueryGrafanaLogsTool(connection, projectId)] : []
12069
+ ...context.grafanaLogsConfigured ? [buildQueryGrafanaLogsTool(connection, projectId)] : [],
12070
+ ...config && context.siteAssetTargetsConfigured ? buildSiteAssetTools(connection, config, projectId) : []
11914
12071
  ];
11915
12072
  }
11916
12073
 
@@ -12125,7 +12282,7 @@ function buildConveyorTools(connection, config, context, agentMode) {
12125
12282
  const handoffTools = config.mode === "pm" && (effectiveMode === "discovery" || effectiveMode === "auto") ? [buildHandoffTool(connection)] : [];
12126
12283
  const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
12127
12284
  const glossaryTools = glossaryToolsFor(connection, config, context);
12128
- const connectedTools = connectedToolsFor(connection, context);
12285
+ const connectedTools = connectedToolsFor(connection, context, config);
12129
12286
  const isPack = config.mode === "pack" || Boolean(context?.isParentTask);
12130
12287
  return withAlwaysLoad(
12131
12288
  [
@@ -12368,6 +12525,7 @@ function handleRateLimitEvent(event, host) {
12368
12525
  const resetsAt = epochSecondsToISO(rate_limit_info.resetsAt);
12369
12526
  const utilization = rate_limit_info.utilization ?? (status === "rejected" ? 1 : void 0);
12370
12527
  if (utilization !== void 0 && rate_limit_info.rateLimitType) {
12528
+ const codingAgentKeyId = process.env.CONVEYOR_CODING_AGENT_KEY_ID;
12371
12529
  host.connection.sendEvent({
12372
12530
  type: "rate_limit_update",
12373
12531
  rateLimitType: rate_limit_info.rateLimitType,
@@ -12376,7 +12534,8 @@ function handleRateLimitEvent(event, host) {
12376
12534
  // The server parses this into the key's sessionResetsAt / weeklyResetsAt;
12377
12535
  // Codex reports a reset instant with every gauge, so allowed events carry
12378
12536
  // it too, not only rejections.
12379
- ...resetsAt ? { resetsAt } : {}
12537
+ ...resetsAt ? { resetsAt } : {},
12538
+ ...codingAgentKeyId ? { codingAgentKeyId } : {}
12380
12539
  });
12381
12540
  }
12382
12541
  if (status === "rejected") {
@@ -12707,7 +12866,7 @@ function fallbackResetIso(rateLimitType, now = Date.now()) {
12707
12866
  }
12708
12867
 
12709
12868
  // src/execution/tool-access.ts
12710
- import { isAbsolute as isAbsolute3, posix } from "path";
12869
+ import { isAbsolute as isAbsolute4, posix } from "path";
12711
12870
 
12712
12871
  // src/execution/task-property-utils.ts
12713
12872
  function collectMissingProps(taskProps) {
@@ -12853,7 +13012,7 @@ function isPlanFile(input) {
12853
13012
  }
12854
13013
  function isScratchPath(input) {
12855
13014
  const filePath = String(input.file_path ?? input.path ?? "");
12856
- if (!isAbsolute3(filePath)) return false;
13015
+ if (!isAbsolute4(filePath)) return false;
12857
13016
  return posix.normalize(filePath).startsWith("/tmp/");
12858
13017
  }
12859
13018
  var ARTIFACT_DENIED = "Publish deliverables on the card with upload_attachment; the Artifact tool is not available in Conveyor sessions.";
@@ -16187,6 +16346,7 @@ var SessionRunner = class _SessionRunner {
16187
16346
  baseBranch: ctx.baseBranch ?? "",
16188
16347
  projectName: ctx.projectName ?? null,
16189
16348
  projectDescription: ctx.projectDescription ?? null,
16349
+ siteAssetTargetsConfigured: ctx.siteAssetTargetsConfigured ?? false,
16190
16350
  githubPRUrl: ctx.githubPRUrl,
16191
16351
  claudeSessionId: ctx.claudeSessionId ?? null,
16192
16352
  lastSeenMessageId: ctx.lastSeenMessageId ?? null,
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  WorkspaceCommandSupervisor,
8
8
  startWorkspaceCommandsAfterConnect,
9
9
  stopWorkspaceCommands
10
- } from "./chunk-FHYK2NIT.js";
10
+ } from "./chunk-IIYEASRQ.js";
11
11
  import {
12
12
  PtyHarness,
13
13
  SessionRunner,
@@ -27,7 +27,7 @@ import {
27
27
  runUsageProbe,
28
28
  sampleKeyUsage,
29
29
  usesNativeUsageReporting
30
- } from "./chunk-XDIMT2ZM.js";
30
+ } from "./chunk-NHKEFOCV.js";
31
31
  import "./chunk-37J5MMQT.js";
32
32
  import {
33
33
  inheritedEnv,
@@ -47,7 +47,7 @@ import {
47
47
  fetchBootstrap,
48
48
  loadAndAnnotateDiscoveredPorts,
49
49
  loadConveyorConfig
50
- } from "./chunk-4SX3FFVU.js";
50
+ } from "./chunk-IBLFLCFK.js";
51
51
  import "./chunk-Q4FQOJ7D.js";
52
52
  import "./chunk-W4LZ7R6Z.js";
53
53
  import "./chunk-372R6E4C.js";
@@ -1470,7 +1470,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
1470
1470
  process.exit(1);
1471
1471
  }
1472
1472
  if (CONVEYOR_MODE === "serving") {
1473
- const { runServingSession } = await import("./serve-boot-4KWT7W2I.js");
1473
+ const { runServingSession } = await import("./serve-boot-G4LZGJ6W.js");
1474
1474
  exitContext.runnerMode = "serving";
1475
1475
  exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
1476
1476
  const outcome = await runServingSession({
package/dist/index.d.ts CHANGED
@@ -374,6 +374,12 @@ declare class AgentConnection {
374
374
  * limited and hand back the best remaining key's credential env (or a
375
375
  * requeue confirmation when none is left). Awaited: the caller swaps
376
376
  * credentials and resumes on success, so it needs the real response.
377
+ *
378
+ * Names the key this pod is running so the server stamps THAT key: the
379
+ * session's own attribution can lag the credential a wake handed out, and
380
+ * stamping it blindly is how one key's cap landed on another. Read from the
381
+ * env at call time on purpose — `applyCycledKeyEnv` rewrites the var after a
382
+ * successful cycle, so a second cap names the replacement.
377
383
  */
378
384
  cycleCodingAgentKey(rateLimitType: string, resetsAt?: string): Promise<{
379
385
  cycled: true;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SessionRunner,
3
3
  unshallowRepo
4
- } from "./chunk-XDIMT2ZM.js";
4
+ } from "./chunk-NHKEFOCV.js";
5
5
  import "./chunk-37J5MMQT.js";
6
6
  import "./chunk-EKTPDG3G.js";
7
7
  import {
@@ -17,7 +17,7 @@ import {
17
17
  stageAndCommit,
18
18
  updateRemoteToken,
19
19
  workspacePathExists
20
- } from "./chunk-4SX3FFVU.js";
20
+ } from "./chunk-IBLFLCFK.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-FHYK2NIT.js";
3
+ } from "./chunk-IIYEASRQ.js";
4
4
  import {
5
5
  AgentConnection,
6
6
  CodespacePortVisibility,
@@ -12,7 +12,7 @@ import {
12
12
  ensureOnTaskBranch,
13
13
  loadAndAnnotateDiscoveredPorts,
14
14
  loadConveyorConfig
15
- } from "./chunk-4SX3FFVU.js";
15
+ } from "./chunk-IBLFLCFK.js";
16
16
  import "./chunk-Q4FQOJ7D.js";
17
17
  import "./chunk-W4LZ7R6Z.js";
18
18
  import "./chunk-IA45XHOA.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-agent",
3
- "version": "11.0.43",
3
+ "version": "11.0.45",
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",