replicas-engine 0.1.268 → 0.1.270
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/src/index.js +247 -13
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -286,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
286
286
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
287
287
|
|
|
288
288
|
// ../shared/src/e2b.ts
|
|
289
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-05-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-05-v7";
|
|
290
290
|
|
|
291
291
|
// ../shared/src/runtime-env.ts
|
|
292
292
|
function parsePosixEnvFile(content) {
|
|
@@ -1967,6 +1967,13 @@ function isClaudeAuthErrorText(text) {
|
|
|
1967
1967
|
return lower.includes("failed to authenticate") || lower.includes("authentication_error") || lower.includes("authentication_failed") || lower.includes("authentication failed") || lower.includes("invalid authentication credentials") || lower.includes("not logged in") || lower.includes("please run /login") || lower.includes("credit balance is too low") || lower.includes("401") && lower.includes("authentic");
|
|
1968
1968
|
}
|
|
1969
1969
|
|
|
1970
|
+
// ../shared/src/codex-auth.ts
|
|
1971
|
+
function isCodexAuthError(error) {
|
|
1972
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1973
|
+
const lower = msg.toLowerCase();
|
|
1974
|
+
return lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid api key") || lower.includes("incorrect api key") || lower.includes("api key") && lower.includes("invalid") || lower.includes("401") || lower.includes('codexerrorinfo="unauthorized"') || lower.includes("codexerrorinfo=unauthorized") || lower.includes("failed to refresh token") || lower.includes("your session has ended");
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1970
1977
|
// ../shared/src/engine/environment.ts
|
|
1971
1978
|
var DESKTOP_NOVNC_PORT = 6080;
|
|
1972
1979
|
|
|
@@ -2032,7 +2039,7 @@ function classifyCanvasFilename(filename) {
|
|
|
2032
2039
|
// ../shared/src/engine/v1.ts
|
|
2033
2040
|
var MERGED_MESSAGE_SEPARATOR = "\n\n<!-- replicas:merged -->\n\n";
|
|
2034
2041
|
function normalizeCodexAspTranscriptStatus(status, failed = false) {
|
|
2035
|
-
if (failed || status === "failed") return "failed";
|
|
2042
|
+
if (failed || status === "failed" || status === "declined") return "failed";
|
|
2036
2043
|
if (status === "completed") return "completed";
|
|
2037
2044
|
return "in_progress";
|
|
2038
2045
|
}
|
|
@@ -2191,6 +2198,60 @@ var CLAUDE_AUTH_ENV_KEYS_BY_METHOD = {
|
|
|
2191
2198
|
]
|
|
2192
2199
|
};
|
|
2193
2200
|
|
|
2201
|
+
// ../shared/src/routes/command-protection.ts
|
|
2202
|
+
var COMMAND_PROTECTION_COMMAND_KEYS = ["command", "cmd", "script", "query", "endpoint", "path", "url", "mutation"];
|
|
2203
|
+
function stringifyCommandProtectionInput(value) {
|
|
2204
|
+
if (typeof value === "string") return value;
|
|
2205
|
+
if (value == null) return "";
|
|
2206
|
+
try {
|
|
2207
|
+
return JSON.stringify(value);
|
|
2208
|
+
} catch {
|
|
2209
|
+
return String(value);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function normalizeCommandProtectionText(text) {
|
|
2213
|
+
return text.replace(/\\\n/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
2214
|
+
}
|
|
2215
|
+
function extractCommandProtectionCommandText(request, options = {}) {
|
|
2216
|
+
if (request.command && request.command.trim()) return request.command;
|
|
2217
|
+
const input = request.toolInput;
|
|
2218
|
+
if (isRecord(input)) {
|
|
2219
|
+
for (const key of COMMAND_PROTECTION_COMMAND_KEYS) {
|
|
2220
|
+
const value = input[key];
|
|
2221
|
+
if (typeof value === "string" && value.trim()) return value;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
if (typeof input === "string") return input;
|
|
2225
|
+
return options.stringifyFallback ? stringifyCommandProtectionInput(input) : null;
|
|
2226
|
+
}
|
|
2227
|
+
function findPrMergeSignals(request) {
|
|
2228
|
+
const command = normalizeCommandProtectionText(extractCommandProtectionCommandText(request, { stringifyFallback: true }) ?? "");
|
|
2229
|
+
const input = normalizeCommandProtectionText(stringifyCommandProtectionInput(request.toolInput));
|
|
2230
|
+
const combined = `${request.toolName.toLowerCase()} ${command} ${input}`;
|
|
2231
|
+
const checks = [
|
|
2232
|
+
["gh-pr-merge", /\bgh\s+pr\s+merge\b/],
|
|
2233
|
+
["gh-api-pulls-merge", /\bgh\s+api\b[\s\S]*\/pulls\/(?:\d+|[^/\s]+)\/merge\b/],
|
|
2234
|
+
["github-rest-pulls-merge", /api\.github\.com\/repos\/[^/\s]+\/[^/\s]+\/pulls\/(?:\d+|[^/\s]+)\/merge\b/],
|
|
2235
|
+
["github-rest-ref-update", /\b(?:gh\s+api|curl|wget|python3?)\b[\s\S]*(?:\/repos\/|repos\/)[^/\s]+\/[^/\s]+\/git\/refs\/heads\/(?:main|master|trunk|develop)\b[\s\S]*\b(?:patch|put|sha|oid|force)\b/],
|
|
2236
|
+
["github-graphql-merge-pr", /\bmergepullrequest\b/],
|
|
2237
|
+
["github-graphql-enable-auto-merge", /\benablepullrequestautomerge\b/],
|
|
2238
|
+
["github-graphql-update-ref", /\bupdateref\b/],
|
|
2239
|
+
["git-merge-default-branch", /\bgit\s+(?:checkout|switch)\s+(?:main|master|trunk|develop)\b[\s\S]*\bgit\s+merge\b/],
|
|
2240
|
+
["git-merge-standalone", /\bgit\s+merge\s+(?:origin\/)?(main|master|trunk|develop)\b/],
|
|
2241
|
+
["git-push-main", /\bgit\s+push\b[\s\S]*\b(origin\s+)?(main|master|trunk|develop)\b/],
|
|
2242
|
+
["git-push-refspec-default-branch", /\bgit\s+push\b[\s\S]*\S+:(?:refs\/heads\/)?(main|master|trunk|develop)\b/],
|
|
2243
|
+
["git-cherry-pick-default-branch", /\bgit\s+(?:checkout|switch)\s+(?:main|master|trunk|develop)\b[\s\S]*\bgit\s+cherry-pick\b[\s\S]*\bgit\s+push\b/],
|
|
2244
|
+
["git-push-all", /\bgit\s+push\s+(-[^\s]+\s+)*(--all|--mirror)\b/],
|
|
2245
|
+
["git-push-bare", /\bgit\s+push\s*(?:["{}\[\]]|$)/],
|
|
2246
|
+
["hub-merge", /\bhub\s+merge\b/],
|
|
2247
|
+
["obfuscated-gh-pr-merge", /printf\s+['"]\\x67\\x68['"][\s\S]*\bpr\b[\s\S]*printf\s+['"]m\\x65rge['"]/],
|
|
2248
|
+
["mcp-merge-tool", /\bmcp__[^\s"]*merge[^\s"]*\b/],
|
|
2249
|
+
["tool-merge-name", /\b(pr|pull[_-]?request).{0,40}\bmerge\b|\bmerge.{0,40}(pr|pull[_-]?request)\b/],
|
|
2250
|
+
["merge-word", /(?:^|[^a-z0-9])merge(?:$|[^a-z0-9])/]
|
|
2251
|
+
];
|
|
2252
|
+
return checks.flatMap(([name, pattern]) => pattern.test(combined) ? [name] : []);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2194
2255
|
// ../shared/src/routes/workspaces.ts
|
|
2195
2256
|
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
2196
2257
|
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
@@ -2551,6 +2612,7 @@ function loadEngineEnv() {
|
|
|
2551
2612
|
AWS_REGION: readEnv("AWS_REGION"),
|
|
2552
2613
|
REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
|
|
2553
2614
|
REPLICAS_CODEX_AUTH_METHOD: parseCodexAuthMethod(readEnv("REPLICAS_CODEX_AUTH_METHOD")),
|
|
2615
|
+
REPLICAS_DISABLE_GH_PR_MERGE: readEnv("REPLICAS_DISABLE_GH_PR_MERGE")?.toLowerCase() === "true",
|
|
2554
2616
|
REPLICAS_ENV_SYSTEM_PROMPT: readEnv("REPLICAS_ENV_SYSTEM_PROMPT"),
|
|
2555
2617
|
REPLICAS_ENV_START_HOOK: readEnv("REPLICAS_ENV_START_HOOK"),
|
|
2556
2618
|
REPLICAS_DISABLE_AUTO_START_HOOKS: readEnv("REPLICAS_DISABLE_AUTO_START_HOOKS")?.toLowerCase() === "true"
|
|
@@ -5624,6 +5686,52 @@ var LinearEventForwarder = class {
|
|
|
5624
5686
|
}
|
|
5625
5687
|
};
|
|
5626
5688
|
|
|
5689
|
+
// src/services/command-protection-service.ts
|
|
5690
|
+
function asCommandProtectionResponse(value) {
|
|
5691
|
+
if (typeof value !== "object" || value === null) return null;
|
|
5692
|
+
const candidate = value;
|
|
5693
|
+
return typeof candidate.allowed === "boolean" && typeof candidate.enabled === "boolean" ? candidate : null;
|
|
5694
|
+
}
|
|
5695
|
+
function failClosed(reason, matchedSignals) {
|
|
5696
|
+
return {
|
|
5697
|
+
allowed: false,
|
|
5698
|
+
enabled: true,
|
|
5699
|
+
reason,
|
|
5700
|
+
matchedSignals
|
|
5701
|
+
};
|
|
5702
|
+
}
|
|
5703
|
+
async function evaluateCommandProtection(request, signal) {
|
|
5704
|
+
const matchedSignals = findPrMergeSignals(request);
|
|
5705
|
+
if (matchedSignals.length === 0) {
|
|
5706
|
+
return { allowed: true, enabled: true };
|
|
5707
|
+
}
|
|
5708
|
+
let response;
|
|
5709
|
+
try {
|
|
5710
|
+
response = await monolithRequest("/v1/engine/command-protection/evaluate", {
|
|
5711
|
+
body: request,
|
|
5712
|
+
signal
|
|
5713
|
+
});
|
|
5714
|
+
} catch (error) {
|
|
5715
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5716
|
+
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
5717
|
+
}
|
|
5718
|
+
if (!response.ok) {
|
|
5719
|
+
const detail = await response.text().catch(() => "");
|
|
5720
|
+
return failClosed(`Command protection failed closed: ${response.status} ${detail}`, matchedSignals);
|
|
5721
|
+
}
|
|
5722
|
+
let payload;
|
|
5723
|
+
try {
|
|
5724
|
+
payload = await response.json();
|
|
5725
|
+
} catch (error) {
|
|
5726
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5727
|
+
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
5728
|
+
}
|
|
5729
|
+
return asCommandProtectionResponse(payload) ?? failClosed("Command protection failed closed: invalid response from policy service", matchedSignals);
|
|
5730
|
+
}
|
|
5731
|
+
function extractToolCommand(input) {
|
|
5732
|
+
return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
|
|
5733
|
+
}
|
|
5734
|
+
|
|
5627
5735
|
// src/managers/claude-manager.ts
|
|
5628
5736
|
var PromptStream = class {
|
|
5629
5737
|
queue = [];
|
|
@@ -5698,6 +5806,18 @@ var ALWAYS_DISALLOWED_TOOLS = [
|
|
|
5698
5806
|
"PushNotification"
|
|
5699
5807
|
];
|
|
5700
5808
|
var INTERACTIVE_TOOL_NAMES = ["AskUserQuestion", "ExitPlanMode"];
|
|
5809
|
+
var COMMAND_PROTECTION_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
5810
|
+
"Read",
|
|
5811
|
+
"Write",
|
|
5812
|
+
"Edit",
|
|
5813
|
+
"Glob",
|
|
5814
|
+
"Grep",
|
|
5815
|
+
"NotebookEdit",
|
|
5816
|
+
"TodoRead",
|
|
5817
|
+
"TodoWrite",
|
|
5818
|
+
"WebFetch",
|
|
5819
|
+
"LS"
|
|
5820
|
+
]);
|
|
5701
5821
|
var TOOL_INPUT_HANDLERS = {
|
|
5702
5822
|
ExitPlanMode: {
|
|
5703
5823
|
getRequest: () => ({
|
|
@@ -5917,6 +6037,29 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
5917
6037
|
return result;
|
|
5918
6038
|
};
|
|
5919
6039
|
}
|
|
6040
|
+
buildCommandProtectionHook() {
|
|
6041
|
+
return async (input, toolUseID, options) => {
|
|
6042
|
+
if (input.hook_event_name !== "PreToolUse") return {};
|
|
6043
|
+
if (COMMAND_PROTECTION_SAFE_TOOLS.has(input.tool_name)) return {};
|
|
6044
|
+
const result = await evaluateCommandProtection({
|
|
6045
|
+
provider: "claude",
|
|
6046
|
+
source: "claude_pre_tool_use",
|
|
6047
|
+
toolName: input.tool_name,
|
|
6048
|
+
toolInput: input.tool_input,
|
|
6049
|
+
command: extractToolCommand(input.tool_input),
|
|
6050
|
+
cwd: input.cwd,
|
|
6051
|
+
toolUseId: toolUseID ?? input.tool_use_id
|
|
6052
|
+
}, options.signal);
|
|
6053
|
+
if (result.allowed) return {};
|
|
6054
|
+
return {
|
|
6055
|
+
hookSpecificOutput: {
|
|
6056
|
+
hookEventName: "PreToolUse",
|
|
6057
|
+
permissionDecision: "deny",
|
|
6058
|
+
permissionDecisionReason: result.reason || "Blocked by organization policy: agents may not merge pull requests."
|
|
6059
|
+
}
|
|
6060
|
+
};
|
|
6061
|
+
};
|
|
6062
|
+
}
|
|
5920
6063
|
async processMessageInternal(request) {
|
|
5921
6064
|
const MAX_AUTH_RETRIES = 2;
|
|
5922
6065
|
let lastError;
|
|
@@ -6151,7 +6294,16 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6151
6294
|
env: queryEnv,
|
|
6152
6295
|
model: resolvedModel,
|
|
6153
6296
|
...thinkingLevel ? { effort: thinkingLevel } : {},
|
|
6154
|
-
canUseTool: this.buildCanUseTool()
|
|
6297
|
+
canUseTool: this.buildCanUseTool(),
|
|
6298
|
+
...ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE ? {
|
|
6299
|
+
hooks: {
|
|
6300
|
+
PreToolUse: [{
|
|
6301
|
+
matcher: "*",
|
|
6302
|
+
hooks: [this.buildCommandProtectionHook()],
|
|
6303
|
+
timeout: 30
|
|
6304
|
+
}]
|
|
6305
|
+
}
|
|
6306
|
+
} : {}
|
|
6155
6307
|
}
|
|
6156
6308
|
});
|
|
6157
6309
|
this.activeQuery = response;
|
|
@@ -6575,7 +6727,7 @@ var AspClient = class {
|
|
|
6575
6727
|
// src/managers/codex-asp/app-server-process.ts
|
|
6576
6728
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6577
6729
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6578
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6730
|
+
var ENGINE_PACKAGE_VERSION = "0.1.270";
|
|
6579
6731
|
var INITIALIZE_METHOD = "initialize";
|
|
6580
6732
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6581
6733
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6834,13 +6986,6 @@ var CodexQuotaStatusTracker = class {
|
|
|
6834
6986
|
}
|
|
6835
6987
|
};
|
|
6836
6988
|
|
|
6837
|
-
// src/utils/codex-auth.ts
|
|
6838
|
-
function isCodexAuthError(error) {
|
|
6839
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
6840
|
-
const lower = msg.toLowerCase();
|
|
6841
|
-
return lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid api key") || lower.includes("incorrect api key") || lower.includes("api key") && lower.includes("invalid") || lower.includes("401") || lower.includes('codexerrorinfo="unauthorized"') || lower.includes("codexerrorinfo=unauthorized");
|
|
6842
|
-
}
|
|
6843
|
-
|
|
6844
6989
|
// src/managers/codex-asp/mappers.ts
|
|
6845
6990
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
6846
6991
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
@@ -6855,6 +7000,15 @@ var TURN_START_METHOD = "turn/start";
|
|
|
6855
7000
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
6856
7001
|
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
6857
7002
|
var MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS;
|
|
7003
|
+
function codexApprovalPolicyOverrides() {
|
|
7004
|
+
if (!ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE) {
|
|
7005
|
+
return {};
|
|
7006
|
+
}
|
|
7007
|
+
return {
|
|
7008
|
+
approvalPolicy: "untrusted",
|
|
7009
|
+
approvalsReviewer: "user"
|
|
7010
|
+
};
|
|
7011
|
+
}
|
|
6858
7012
|
function toReasoningEffort(thinkingLevel) {
|
|
6859
7013
|
return codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
6860
7014
|
}
|
|
@@ -7250,6 +7404,7 @@ async function buildThreadStartParams(workingDirectory, request, developerInstru
|
|
|
7250
7404
|
cwd: workingDirectory,
|
|
7251
7405
|
runtimeWorkspaceRoots: additionalDirectories,
|
|
7252
7406
|
sandbox: "danger-full-access",
|
|
7407
|
+
...codexApprovalPolicyOverrides(),
|
|
7253
7408
|
developerInstructions: developerInstructions ?? null,
|
|
7254
7409
|
config: { web_search: "live" },
|
|
7255
7410
|
experimentalRawEvents: false,
|
|
@@ -7264,6 +7419,7 @@ async function buildThreadResumeParams(workingDirectory, threadId, request, deve
|
|
|
7264
7419
|
cwd: workingDirectory,
|
|
7265
7420
|
runtimeWorkspaceRoots: additionalDirectories,
|
|
7266
7421
|
sandbox: "danger-full-access",
|
|
7422
|
+
...codexApprovalPolicyOverrides(),
|
|
7267
7423
|
developerInstructions: developerInstructions ?? null,
|
|
7268
7424
|
config: { web_search: "live" },
|
|
7269
7425
|
excludeTurns: false,
|
|
@@ -7296,6 +7452,7 @@ async function buildTurnStartParams(threadId, request, developerInstructions) {
|
|
|
7296
7452
|
threadId,
|
|
7297
7453
|
input,
|
|
7298
7454
|
model,
|
|
7455
|
+
...codexApprovalPolicyOverrides(),
|
|
7299
7456
|
...effort ? { effort } : {},
|
|
7300
7457
|
...developerInstructions ? {
|
|
7301
7458
|
collaborationMode: {
|
|
@@ -7808,12 +7965,75 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7808
7965
|
const requestId = serverRequest.id;
|
|
7809
7966
|
const method = serverRequest.method;
|
|
7810
7967
|
switch (serverRequest.method) {
|
|
7811
|
-
case "item/commandExecution/requestApproval":
|
|
7968
|
+
case "item/commandExecution/requestApproval": {
|
|
7969
|
+
if (ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE) {
|
|
7970
|
+
const result = await evaluateCommandProtection({
|
|
7971
|
+
provider: "codex",
|
|
7972
|
+
source: "codex_approval_request",
|
|
7973
|
+
toolName: "commandExecution",
|
|
7974
|
+
toolInput: {
|
|
7975
|
+
command: serverRequest.params.command,
|
|
7976
|
+
commandActions: serverRequest.params.commandActions ?? null
|
|
7977
|
+
},
|
|
7978
|
+
command: serverRequest.params.command,
|
|
7979
|
+
cwd: serverRequest.params.cwd ?? null,
|
|
7980
|
+
toolUseId: serverRequest.params.itemId ?? null
|
|
7981
|
+
});
|
|
7982
|
+
if (!result.allowed) {
|
|
7983
|
+
console.warn(`[CodexAspManager] blocked command approval by PR merge policy: ${result.reason ?? "unknown reason"}`);
|
|
7984
|
+
this.recordBlockedCommand(
|
|
7985
|
+
serverRequest.params.threadId,
|
|
7986
|
+
serverRequest.params.turnId,
|
|
7987
|
+
serverRequest.params.itemId,
|
|
7988
|
+
serverRequest.params.command ?? "",
|
|
7989
|
+
result.reason,
|
|
7990
|
+
serverRequest.params.startedAtMs
|
|
7991
|
+
);
|
|
7992
|
+
host.client.respond(requestId, { decision: "decline" });
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
}
|
|
7996
|
+
console.warn("[CodexAspManager] approval requested while sandbox is danger-full-access");
|
|
7997
|
+
host.client.respond(requestId, { decision: "accept" });
|
|
7998
|
+
return;
|
|
7999
|
+
}
|
|
7812
8000
|
case "item/fileChange/requestApproval":
|
|
7813
8001
|
console.warn("[CodexAspManager] approval requested while sandbox is danger-full-access");
|
|
7814
8002
|
host.client.respond(requestId, { decision: "accept" });
|
|
7815
8003
|
return;
|
|
7816
|
-
case "execCommandApproval":
|
|
8004
|
+
case "execCommandApproval": {
|
|
8005
|
+
if (ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE) {
|
|
8006
|
+
const command = serverRequest.params.command.join(" ");
|
|
8007
|
+
const result = await evaluateCommandProtection({
|
|
8008
|
+
provider: "codex",
|
|
8009
|
+
source: "codex_approval_request",
|
|
8010
|
+
toolName: "execCommand",
|
|
8011
|
+
toolInput: {
|
|
8012
|
+
command: serverRequest.params.command,
|
|
8013
|
+
parsedCmd: serverRequest.params.parsedCmd ?? null
|
|
8014
|
+
},
|
|
8015
|
+
command,
|
|
8016
|
+
cwd: serverRequest.params.cwd ?? null,
|
|
8017
|
+
toolUseId: serverRequest.params.callId ?? null
|
|
8018
|
+
});
|
|
8019
|
+
if (!result.allowed) {
|
|
8020
|
+
console.warn(`[CodexAspManager] blocked legacy command approval by PR merge policy: ${result.reason ?? "unknown reason"}`);
|
|
8021
|
+
this.recordBlockedCommand(
|
|
8022
|
+
this.currentThreadId ?? serverRequest.params.conversationId,
|
|
8023
|
+
this.activeTurnId ?? `legacy-${requestId}`,
|
|
8024
|
+
serverRequest.params.callId,
|
|
8025
|
+
command,
|
|
8026
|
+
result.reason,
|
|
8027
|
+
Date.now()
|
|
8028
|
+
);
|
|
8029
|
+
host.client.respond(requestId, { decision: "denied" });
|
|
8030
|
+
return;
|
|
8031
|
+
}
|
|
8032
|
+
}
|
|
8033
|
+
console.warn("[CodexAspManager] legacy approval requested while sandbox is danger-full-access");
|
|
8034
|
+
host.client.respond(requestId, { decision: "approved" });
|
|
8035
|
+
return;
|
|
8036
|
+
}
|
|
7817
8037
|
case "applyPatchApproval":
|
|
7818
8038
|
console.warn("[CodexAspManager] legacy approval requested while sandbox is danger-full-access");
|
|
7819
8039
|
host.client.respond(requestId, { decision: "approved" });
|
|
@@ -7861,6 +8081,20 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7861
8081
|
nextTranscriptSequence() {
|
|
7862
8082
|
return this.codexAspSequence++;
|
|
7863
8083
|
}
|
|
8084
|
+
recordBlockedCommand(threadId, turnId, itemId, command, reason, startedAtMs) {
|
|
8085
|
+
const output = reason?.includes("Blocked by organization policy") ? reason : `Blocked by organization policy: ${reason || "agents may not merge pull requests."}`;
|
|
8086
|
+
const timestamp = timestampFromMilliseconds(startedAtMs);
|
|
8087
|
+
const blockedItem = {
|
|
8088
|
+
type: "commandExecution",
|
|
8089
|
+
id: itemId,
|
|
8090
|
+
command,
|
|
8091
|
+
aggregatedOutput: output,
|
|
8092
|
+
exitCode: null,
|
|
8093
|
+
status: "declined"
|
|
8094
|
+
};
|
|
8095
|
+
this.upsertTranscriptItem(threadId, turnId, blockedItem, timestamp, "failed", "completed");
|
|
8096
|
+
this.emitTranscriptUpdated(threadId, { immediate: true });
|
|
8097
|
+
}
|
|
7864
8098
|
syncTranscriptSequence(transcript) {
|
|
7865
8099
|
if (!transcript) return;
|
|
7866
8100
|
let next = 0;
|