@rallycry/conveyor-agent 7.3.2 → 7.3.3

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.
@@ -411,6 +411,9 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
411
411
  triggerIdentification() {
412
412
  return this.call("triggerIdentification", { sessionId: this.config.sessionId });
413
413
  }
414
+ getCumulativeSpending() {
415
+ return this.call("getCumulativeSpending", { sessionId: this.config.sessionId });
416
+ }
414
417
  async refreshAuthToken() {
415
418
  const codespaceName = process.env.CODESPACE_NAME || process.env.CLAUDESPACE_NAME;
416
419
  const apiUrl = this.config.apiUrl;
@@ -4811,7 +4814,31 @@ function collectMissingProps(taskProps) {
4811
4814
 
4812
4815
  // src/execution/tool-access.ts
4813
4816
  var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
4814
- var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
4817
+ var DESTRUCTIVE_PATTERNS = [
4818
+ {
4819
+ name: "git push --force (without --force-with-lease)",
4820
+ re: /git\s+push\s+(?:-f\b|--force(?!-with-lease))/
4821
+ },
4822
+ { name: "git push --delete", re: /git\s+push\s+(?:-d\b|--delete\b)/ },
4823
+ { name: "git reset --hard", re: /git\s+reset\s+--hard\b/ },
4824
+ {
4825
+ name: "rm -rf /",
4826
+ re: /rm\s+(?:-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r|--recursive\s+--force)\s+\/(?!\S)/
4827
+ },
4828
+ { name: "sudo rm", re: /\bsudo\s+rm\b/ },
4829
+ { name: "chmod world-writable", re: /\bchmod\s+(?:-R\s+)?[0-7]*7{2,3}\b/ },
4830
+ { name: "dd to device", re: /\bdd\s+.*\bof=\/dev\// },
4831
+ { name: "redirect to block device", re: />\s*\/dev\/(?:sd[a-z]|nvme\d|xvd[a-z])/ },
4832
+ { name: "mkfs filesystem creation", re: /\bmkfs(?:\.|\s)/ },
4833
+ { name: "shutdown/poweroff/halt/reboot", re: /\b(?:shutdown|poweroff|halt|reboot)\b/ },
4834
+ { name: "fork bomb", re: /:\(\)\s*\{\s*:\|\s*:&\s*\}\s*;\s*:/ }
4835
+ ];
4836
+ function matchesDestructive(cmd) {
4837
+ for (const { name, re } of DESTRUCTIVE_PATTERNS) {
4838
+ if (re.test(cmd)) return name;
4839
+ }
4840
+ return null;
4841
+ }
4815
4842
  function isPlanFile(input) {
4816
4843
  const filePath = String(input.file_path ?? input.path ?? "");
4817
4844
  return filePath.includes(".claude/plans/");
@@ -4831,10 +4858,11 @@ function handleDiscoveryToolAccess(toolName, input) {
4831
4858
  function handleBuildingToolAccess(toolName, input) {
4832
4859
  if (toolName === "Bash") {
4833
4860
  const cmd = String(input.command ?? "");
4834
- if (DESTRUCTIVE_CMD_PATTERN.test(cmd)) {
4861
+ const matched = matchesDestructive(cmd);
4862
+ if (matched) {
4835
4863
  return {
4836
4864
  behavior: "deny",
4837
- message: "Destructive operation blocked. Use safer alternatives."
4865
+ message: `Destructive operation blocked (${matched}). Use safer alternatives.`
4838
4866
  };
4839
4867
  }
4840
4868
  }
@@ -5017,6 +5045,65 @@ function buildCanUseTool(host) {
5017
5045
  };
5018
5046
  }
5019
5047
 
5048
+ // src/execution/redactor.ts
5049
+ var REDACTED = "<redacted>";
5050
+ var BEARER_RE = /\b(Bearer\s+)[A-Za-z0-9_\-.]{20,}/g;
5051
+ var VENDOR_KEY_RE = /\b(?:sk-[a-zA-Z0-9_-]{20,}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{22,}|xoxb-[A-Za-z0-9-]+|xai-[A-Za-z0-9-]{20,})\b/g;
5052
+ var AWS_ACCESS_KEY_RE = /\bAKIA[0-9A-Z]{16}\b/g;
5053
+ var JWT_RE = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g;
5054
+ var ENV_SECRET_RE = /^(\s*(?:export\s+)?([A-Z][A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|CREDENTIAL|CREDENTIALS)[A-Z0-9_]*)\s*=\s*)['"]?([^\s'"]+)['"]?/gm;
5055
+ var COOKIE_HEADER_RE = /(Cookie:\s*)[^\r\n]+/gi;
5056
+ var BASIC_AUTH_URL_RE = /(https?:\/\/)([^:@\s/]+):([^@\s]+)@/g;
5057
+ var AWS_SECRET_LINE_RE = /^.*(?:secret|aws_secret).*$/gim;
5058
+ var AWS_SECRET_VALUE_RE = /\b([A-Za-z0-9/+=]{40})\b/g;
5059
+ var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
5060
+ function redact(input) {
5061
+ if (!input) return { output: input, redacted: 0 };
5062
+ let count = 0;
5063
+ let output = input;
5064
+ output = output.replace(SYSTEM_REMINDER_RE, () => {
5065
+ count++;
5066
+ return "<!-- stripped injection -->";
5067
+ });
5068
+ output = output.replace(BEARER_RE, (_match, prefix) => {
5069
+ count++;
5070
+ return `${prefix}${REDACTED}`;
5071
+ });
5072
+ output = output.replace(VENDOR_KEY_RE, () => {
5073
+ count++;
5074
+ return REDACTED;
5075
+ });
5076
+ output = output.replace(AWS_ACCESS_KEY_RE, () => {
5077
+ count++;
5078
+ return REDACTED;
5079
+ });
5080
+ output = output.replace(JWT_RE, () => {
5081
+ count++;
5082
+ return REDACTED;
5083
+ });
5084
+ output = output.replace(
5085
+ AWS_SECRET_LINE_RE,
5086
+ (line) => line.replace(AWS_SECRET_VALUE_RE, (match) => {
5087
+ if (/^[a-f0-9]+$/i.test(match)) return match;
5088
+ count++;
5089
+ return REDACTED;
5090
+ })
5091
+ );
5092
+ output = output.replace(ENV_SECRET_RE, (_match, prefix, _key, _value) => {
5093
+ count++;
5094
+ return `${prefix}${REDACTED}`;
5095
+ });
5096
+ output = output.replace(COOKIE_HEADER_RE, (_match, prefix) => {
5097
+ count++;
5098
+ return `${prefix}${REDACTED}`;
5099
+ });
5100
+ output = output.replace(BASIC_AUTH_URL_RE, (_match, scheme, user) => {
5101
+ count++;
5102
+ return `${scheme}${user}:${REDACTED}@`;
5103
+ });
5104
+ return { output, redacted: count };
5105
+ }
5106
+
5020
5107
  // src/execution/query-executor.ts
5021
5108
  var logger2 = createServiceLogger("QueryExecutor");
5022
5109
  var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
@@ -5029,12 +5116,15 @@ function buildHooks(host) {
5029
5116
  async (input) => {
5030
5117
  if (host.isStopped()) return await Promise.resolve({ continue: false });
5031
5118
  if (input.hook_event_name === "PostToolUse") {
5032
- const output = typeof input.tool_response === "string" ? input.tool_response.slice(0, 500) : JSON.stringify(input.tool_response).slice(0, 500);
5119
+ const raw = typeof input.tool_response === "string" ? input.tool_response : JSON.stringify(input.tool_response);
5120
+ const { output: redacted, redacted: redactedCount } = redact(raw);
5121
+ const output = redacted.slice(0, 500);
5033
5122
  host.connection.sendEvent({
5034
5123
  type: "tool_result",
5035
5124
  tool: input.tool_name,
5036
5125
  output,
5037
- isError: false
5126
+ isError: false,
5127
+ ...redactedCount > 0 ? { redactedCount } : {}
5038
5128
  });
5039
5129
  host.pendingToolOutputs.push({ tool: input.tool_name, output });
5040
5130
  if (input.tool_name === "mcp__conveyor__create_pull_request") {
@@ -5401,6 +5491,22 @@ async function runWithRetry(initialQuery, context, host, options) {
5401
5491
  var CostTracker = class {
5402
5492
  cumulativeCostUsd = 0;
5403
5493
  modelUsage = /* @__PURE__ */ new Map();
5494
+ seeded = false;
5495
+ /**
5496
+ * Rehydrate cumulative spend from the server so `maxBudgetUsd` enforcement
5497
+ * accounts for costs incurred by prior agent runs on the same task. Must be
5498
+ * called before any `addQueryCost` / `addModelUsage` — re-seeding after
5499
+ * costs have accumulated would clobber in-process totals.
5500
+ */
5501
+ seed(totalCostUsd, modelUsage) {
5502
+ if (this.seeded) return;
5503
+ if (this.cumulativeCostUsd > 0 || this.modelUsage.size > 0) return;
5504
+ this.cumulativeCostUsd = totalCostUsd;
5505
+ for (const entry of modelUsage) {
5506
+ this.modelUsage.set(entry.model, { ...entry });
5507
+ }
5508
+ this.seeded = true;
5509
+ }
5404
5510
  /** Add cost from a completed query and return the running total */
5405
5511
  addQueryCost(queryCostUsd) {
5406
5512
  this.cumulativeCostUsd += queryCostUsd;
@@ -5591,6 +5697,10 @@ var QueryBridge = class {
5591
5697
  resume() {
5592
5698
  this._stopped = false;
5593
5699
  }
5700
+ /** Rehydrate CostTracker from server-side cumulative spend on agent boot. */
5701
+ seedCostTracker(totalCostUsd, modelUsage) {
5702
+ this.costTracker.seed(totalCostUsd, modelUsage);
5703
+ }
5594
5704
  /**
5595
5705
  * Execute a Claude SDK query.
5596
5706
  * Without followUpContent: runs initial mode execution (build/plan).
@@ -5856,6 +5966,7 @@ var SessionRunner = class _SessionRunner {
5856
5966
  });
5857
5967
  }
5858
5968
  this.queryBridge = this.createQueryBridge();
5969
+ await this.seedCostTrackerFromServer();
5859
5970
  this.logInitialization();
5860
5971
  const staleMessageCount = this.pendingMessages.length;
5861
5972
  const didExecuteInitialQuery = await this.executeInitialMode();
@@ -6217,6 +6328,36 @@ var SessionRunner = class _SessionRunner {
6217
6328
  }
6218
6329
  });
6219
6330
  }
6331
+ /**
6332
+ * Rehydrate CostTracker from server. Caps at 3s so a slow API call never
6333
+ * blocks agent startup — on timeout/error we fall through with a $0 tracker
6334
+ * (the existing behavior before this rehydration path was added).
6335
+ */
6336
+ async seedCostTrackerFromServer() {
6337
+ if (!this.queryBridge) return;
6338
+ const TIMEOUT_MS = 3e3;
6339
+ try {
6340
+ const timeout = new Promise((resolve2) => {
6341
+ setTimeout(() => resolve2(null), TIMEOUT_MS);
6342
+ });
6343
+ const fetched = await Promise.race([this.connection.getCumulativeSpending(), timeout]);
6344
+ if (fetched) {
6345
+ this.queryBridge.seedCostTracker(fetched.totalCostUsd, fetched.modelUsage);
6346
+ process.stderr.write(
6347
+ `[conveyor-agent] CostTracker seeded: $${fetched.totalCostUsd.toFixed(4)} across ${fetched.modelUsage.length} model(s)
6348
+ `
6349
+ );
6350
+ } else {
6351
+ process.stderr.write(
6352
+ "[conveyor-agent] CostTracker seed timed out after 3s \u2014 starting at $0\n"
6353
+ );
6354
+ }
6355
+ } catch (err) {
6356
+ const msg = err instanceof Error ? err.message : String(err);
6357
+ process.stderr.write(`[conveyor-agent] CostTracker seed failed: ${msg} \u2014 starting at $0
6358
+ `);
6359
+ }
6360
+ }
6220
6361
  /** Proactively refresh the GitHub token before the 1-hour expiry. */
6221
6362
  async refreshGithubToken() {
6222
6363
  try {
@@ -7825,4 +7966,4 @@ export {
7825
7966
  loadForwardPorts,
7826
7967
  loadConveyorConfig
7827
7968
  };
7828
- //# sourceMappingURL=chunk-WDGSLU5S.js.map
7969
+ //# sourceMappingURL=chunk-COJPX2QI.js.map