replicas-engine 0.1.269 → 0.1.271
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 +240 -6
- 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-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-06-v2";
|
|
290
290
|
|
|
291
291
|
// ../shared/src/runtime-env.ts
|
|
292
292
|
function parsePosixEnvFile(content) {
|
|
@@ -2039,7 +2039,7 @@ function classifyCanvasFilename(filename) {
|
|
|
2039
2039
|
// ../shared/src/engine/v1.ts
|
|
2040
2040
|
var MERGED_MESSAGE_SEPARATOR = "\n\n<!-- replicas:merged -->\n\n";
|
|
2041
2041
|
function normalizeCodexAspTranscriptStatus(status, failed = false) {
|
|
2042
|
-
if (failed || status === "failed") return "failed";
|
|
2042
|
+
if (failed || status === "failed" || status === "declined") return "failed";
|
|
2043
2043
|
if (status === "completed") return "completed";
|
|
2044
2044
|
return "in_progress";
|
|
2045
2045
|
}
|
|
@@ -2198,6 +2198,60 @@ var CLAUDE_AUTH_ENV_KEYS_BY_METHOD = {
|
|
|
2198
2198
|
]
|
|
2199
2199
|
};
|
|
2200
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
|
+
|
|
2201
2255
|
// ../shared/src/routes/workspaces.ts
|
|
2202
2256
|
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
2203
2257
|
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
@@ -2558,6 +2612,7 @@ function loadEngineEnv() {
|
|
|
2558
2612
|
AWS_REGION: readEnv("AWS_REGION"),
|
|
2559
2613
|
REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
|
|
2560
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",
|
|
2561
2616
|
REPLICAS_ENV_SYSTEM_PROMPT: readEnv("REPLICAS_ENV_SYSTEM_PROMPT"),
|
|
2562
2617
|
REPLICAS_ENV_START_HOOK: readEnv("REPLICAS_ENV_START_HOOK"),
|
|
2563
2618
|
REPLICAS_DISABLE_AUTO_START_HOOKS: readEnv("REPLICAS_DISABLE_AUTO_START_HOOKS")?.toLowerCase() === "true"
|
|
@@ -5631,6 +5686,52 @@ var LinearEventForwarder = class {
|
|
|
5631
5686
|
}
|
|
5632
5687
|
};
|
|
5633
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
|
+
|
|
5634
5735
|
// src/managers/claude-manager.ts
|
|
5635
5736
|
var PromptStream = class {
|
|
5636
5737
|
queue = [];
|
|
@@ -5705,6 +5806,18 @@ var ALWAYS_DISALLOWED_TOOLS = [
|
|
|
5705
5806
|
"PushNotification"
|
|
5706
5807
|
];
|
|
5707
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
|
+
]);
|
|
5708
5821
|
var TOOL_INPUT_HANDLERS = {
|
|
5709
5822
|
ExitPlanMode: {
|
|
5710
5823
|
getRequest: () => ({
|
|
@@ -5924,6 +6037,29 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
5924
6037
|
return result;
|
|
5925
6038
|
};
|
|
5926
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
|
+
}
|
|
5927
6063
|
async processMessageInternal(request) {
|
|
5928
6064
|
const MAX_AUTH_RETRIES = 2;
|
|
5929
6065
|
let lastError;
|
|
@@ -6158,7 +6294,16 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6158
6294
|
env: queryEnv,
|
|
6159
6295
|
model: resolvedModel,
|
|
6160
6296
|
...thinkingLevel ? { effort: thinkingLevel } : {},
|
|
6161
|
-
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
|
+
} : {}
|
|
6162
6307
|
}
|
|
6163
6308
|
});
|
|
6164
6309
|
this.activeQuery = response;
|
|
@@ -6582,7 +6727,7 @@ var AspClient = class {
|
|
|
6582
6727
|
// src/managers/codex-asp/app-server-process.ts
|
|
6583
6728
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6584
6729
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6585
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6730
|
+
var ENGINE_PACKAGE_VERSION = "0.1.271";
|
|
6586
6731
|
var INITIALIZE_METHOD = "initialize";
|
|
6587
6732
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6588
6733
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -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;
|