@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.
- package/dist/{chunk-WDGSLU5S.js → chunk-COJPX2QI.js} +147 -6
- package/dist/chunk-COJPX2QI.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WDGSLU5S.js.map +0 -1
|
@@ -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
|
|
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
|
-
|
|
4861
|
+
const matched = matchesDestructive(cmd);
|
|
4862
|
+
if (matched) {
|
|
4835
4863
|
return {
|
|
4836
4864
|
behavior: "deny",
|
|
4837
|
-
message:
|
|
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
|
|
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-
|
|
7969
|
+
//# sourceMappingURL=chunk-COJPX2QI.js.map
|