replicas-engine 0.1.411 → 0.1.413
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 +290 -6
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -481,7 +481,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
481
481
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
482
482
|
|
|
483
483
|
// ../shared/src/e2b.ts
|
|
484
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-09-
|
|
484
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-09-v3";
|
|
485
485
|
|
|
486
486
|
// ../shared/src/runtime-env.ts
|
|
487
487
|
function parsePosixEnvFile(content) {
|
|
@@ -2003,6 +2003,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
|
|
|
2003
2003
|
| "Make an automation that runs every weekday at 9am" | \`replicas automation create\` (interactive) or \`--trigger-cron "0 9 * * 1-5"\` |
|
|
2004
2004
|
| "Make an automation that runs when a PR opens on \`acme/web\`" | \`replicas automation create ... --trigger-github pull_request.opened --trigger-github-repos acme/web\` |
|
|
2005
2005
|
| "Make an automation that runs when an MR opens on \`acme/web\`" | \`replicas automation create ... --trigger-gitlab merge_request.opened --trigger-gitlab-repos acme/web\` |
|
|
2006
|
+
| "Ignore Dependabot for this PR automation" | \`replicas automation edit <id> --trigger-github-exclude-users 'dependabot[bot]'\` |
|
|
2006
2007
|
| "What repos are connected?" | \`replicas repos\` |
|
|
2007
2008
|
| "Set up a \`replicas.json\` in this repo" | \`replicas init\` (\`-y\` for YAML) |
|
|
2008
2009
|
|
|
@@ -2089,13 +2090,15 @@ replicas automation create "Review my PRs" \\
|
|
|
2089
2090
|
--prompt "Leave a code review on this PR" \\
|
|
2090
2091
|
--environment <env-name-or-id> \\
|
|
2091
2092
|
--trigger-github pull_request.opened \\
|
|
2092
|
-
--trigger-github-repos acme/web,acme/api
|
|
2093
|
+
--trigger-github-repos acme/web,acme/api \\
|
|
2094
|
+
--trigger-github-exclude-users 'dependabot[bot],renovate[bot]'
|
|
2093
2095
|
|
|
2094
2096
|
replicas automation create "Review my MRs" \\
|
|
2095
2097
|
--prompt "Leave a code review on this MR" \\
|
|
2096
2098
|
--environment <env-name-or-id> \\
|
|
2097
2099
|
--trigger-gitlab merge_request.opened \\
|
|
2098
|
-
--trigger-gitlab-repos acme/web,acme/api
|
|
2100
|
+
--trigger-gitlab-repos acme/web,acme/api \\
|
|
2101
|
+
--trigger-gitlab-exclude-users 'bot-user'
|
|
2099
2102
|
|
|
2100
2103
|
# Disable on creation
|
|
2101
2104
|
replicas automation create ... --disabled
|
|
@@ -2119,6 +2122,8 @@ replicas automation edit <id> \\
|
|
|
2119
2122
|
--trigger-cron "0 4 * * *" # replaces existing triggers
|
|
2120
2123
|
\`\`\`
|
|
2121
2124
|
|
|
2125
|
+
\`replicas automation edit <id> --trigger-github-exclude-users "dependabot[bot],renovate[bot]"\` updates existing GitHub triggers without replacing the rest of the trigger list. The GitLab equivalent is \`--trigger-gitlab-exclude-users\`. Passing an empty string (for example, \`--trigger-github-exclude-users ""\`) clears existing exclusions for that code host. Passing \`--trigger-github\` or \`--trigger-gitlab\` still replaces triggers, and the matching exclude flag is applied to the replacement trigger.
|
|
2126
|
+
|
|
2122
2127
|
\`replicas automation edit <id>\` with no flags drops into interactive mode.
|
|
2123
2128
|
|
|
2124
2129
|
## Repositories
|
|
@@ -2293,6 +2298,27 @@ var REPLICAS_AGENT_ABILITIES = Object.keys(
|
|
|
2293
2298
|
);
|
|
2294
2299
|
|
|
2295
2300
|
// ../shared/src/routes/preferences.ts
|
|
2301
|
+
var EMPTY_AGENT_DEFAULT_SETTINGS = {
|
|
2302
|
+
agent_provider: null,
|
|
2303
|
+
model: null,
|
|
2304
|
+
thinking_level: null,
|
|
2305
|
+
goal_mode: null,
|
|
2306
|
+
fast_mode: null
|
|
2307
|
+
};
|
|
2308
|
+
var DEFAULT_USER_PREFERENCES = {
|
|
2309
|
+
default_agent: "codex",
|
|
2310
|
+
default_model: null,
|
|
2311
|
+
cursor_models: null,
|
|
2312
|
+
close_workspace_on_pr_close: true,
|
|
2313
|
+
default_environment_id: null,
|
|
2314
|
+
respond_to_ci_workflows: true,
|
|
2315
|
+
respond_to_ci_checks: true,
|
|
2316
|
+
respond_to_human_pr_reviews: false,
|
|
2317
|
+
pr_user_attribution: false,
|
|
2318
|
+
auto_draft_prs: false,
|
|
2319
|
+
default_fast_mode: false,
|
|
2320
|
+
agent_defaults: { ...EMPTY_AGENT_DEFAULT_SETTINGS }
|
|
2321
|
+
};
|
|
2296
2322
|
var DEFAULT_REPLICAS_AGENT_ABILITIES = Object.fromEntries(
|
|
2297
2323
|
REPLICAS_AGENT_ABILITIES.map((key) => [key, true])
|
|
2298
2324
|
);
|
|
@@ -2710,6 +2736,11 @@ function extractCommandProtectionCommandText(request, options = {}) {
|
|
|
2710
2736
|
for (const key of COMMAND_PROTECTION_COMMAND_KEYS) {
|
|
2711
2737
|
const value = input[key];
|
|
2712
2738
|
if (typeof value === "string" && value.trim()) return value;
|
|
2739
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value.join(" ");
|
|
2740
|
+
}
|
|
2741
|
+
if (isRecord(input.args)) {
|
|
2742
|
+
const nested = extractCommandProtectionCommandText({ toolInput: input.args }, options);
|
|
2743
|
+
if (nested) return nested;
|
|
2713
2744
|
}
|
|
2714
2745
|
}
|
|
2715
2746
|
if (typeof input === "string") return input;
|
|
@@ -6515,6 +6546,7 @@ var LinearEventForwarder = class {
|
|
|
6515
6546
|
};
|
|
6516
6547
|
|
|
6517
6548
|
// src/services/command-protection-service.ts
|
|
6549
|
+
var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by organization policy: agents may not merge pull requests.";
|
|
6518
6550
|
function asCommandProtectionResponse(value) {
|
|
6519
6551
|
if (typeof value !== "object" || value === null) return null;
|
|
6520
6552
|
const candidate = value;
|
|
@@ -6559,6 +6591,17 @@ async function evaluateCommandProtection(request, signal) {
|
|
|
6559
6591
|
function extractToolCommand(input) {
|
|
6560
6592
|
return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
|
|
6561
6593
|
}
|
|
6594
|
+
function reportCommandProtectionBlock(options) {
|
|
6595
|
+
const message = options.result.reason || DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE;
|
|
6596
|
+
console.warn(`[${options.managerName}] blocked ${options.action} by PR merge policy: ${message}`);
|
|
6597
|
+
options.recordHistoryEvent(options.eventType, {
|
|
6598
|
+
type: "error",
|
|
6599
|
+
message,
|
|
6600
|
+
...options.context,
|
|
6601
|
+
command: options.command
|
|
6602
|
+
}, options.historyFile);
|
|
6603
|
+
return message;
|
|
6604
|
+
}
|
|
6562
6605
|
|
|
6563
6606
|
// src/services/skill-registry-service.ts
|
|
6564
6607
|
import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
@@ -8206,7 +8249,7 @@ var AspClient = class {
|
|
|
8206
8249
|
// src/managers/codex-asp/app-server-process.ts
|
|
8207
8250
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
8208
8251
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
8209
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
8252
|
+
var ENGINE_PACKAGE_VERSION = "0.1.413";
|
|
8210
8253
|
var INITIALIZE_METHOD = "initialize";
|
|
8211
8254
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
8212
8255
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -10322,6 +10365,24 @@ function extractCursorCommandDescription(content) {
|
|
|
10322
10365
|
}
|
|
10323
10366
|
return void 0;
|
|
10324
10367
|
}
|
|
10368
|
+
function cursorToolInputFromDelta(update) {
|
|
10369
|
+
if (update.type !== "tool-call-started") return null;
|
|
10370
|
+
const toolCall = update.toolCall;
|
|
10371
|
+
return {
|
|
10372
|
+
toolName: toolCall.type,
|
|
10373
|
+
toolInput: toolCall,
|
|
10374
|
+
command: extractToolCommand(toolCall),
|
|
10375
|
+
toolUseId: update.callId
|
|
10376
|
+
};
|
|
10377
|
+
}
|
|
10378
|
+
function cursorToolInputFromMessage(event) {
|
|
10379
|
+
return {
|
|
10380
|
+
toolName: event.name,
|
|
10381
|
+
toolInput: event.args ?? {},
|
|
10382
|
+
command: extractToolCommand(event.args),
|
|
10383
|
+
toolUseId: event.call_id
|
|
10384
|
+
};
|
|
10385
|
+
}
|
|
10325
10386
|
async function listCursorCommandsInDirectory(directory) {
|
|
10326
10387
|
let entries;
|
|
10327
10388
|
try {
|
|
@@ -10347,6 +10408,8 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
10347
10408
|
agent = null;
|
|
10348
10409
|
activeRun = null;
|
|
10349
10410
|
activeModel = null;
|
|
10411
|
+
blockedToolCallIds = /* @__PURE__ */ new Set();
|
|
10412
|
+
pendingCursorBlockReason = null;
|
|
10350
10413
|
historyFilePath;
|
|
10351
10414
|
historyFile;
|
|
10352
10415
|
slashCommandsCache = null;
|
|
@@ -10425,12 +10488,22 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
10425
10488
|
}, this.historyFile);
|
|
10426
10489
|
const run = await agent.send(message, {
|
|
10427
10490
|
model: { id: model },
|
|
10428
|
-
mode: request.planMode ? "plan" : "agent"
|
|
10491
|
+
mode: request.planMode ? "plan" : "agent",
|
|
10492
|
+
onDelta: async (args) => {
|
|
10493
|
+
await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
|
|
10494
|
+
}
|
|
10429
10495
|
});
|
|
10430
10496
|
this.activeRun = run;
|
|
10431
10497
|
this.activeModel = model;
|
|
10498
|
+
if (this.pendingCursorBlockReason) {
|
|
10499
|
+
await run.cancel();
|
|
10500
|
+
throw new Error(this.pendingCursorBlockReason);
|
|
10501
|
+
}
|
|
10432
10502
|
for await (const event of run.stream()) {
|
|
10433
10503
|
this.recordCursorEvent(event);
|
|
10504
|
+
if (event.type === "tool_call" && event.status === "running") {
|
|
10505
|
+
await this.handleCursorToolCall(cursorToolInputFromMessage(event));
|
|
10506
|
+
}
|
|
10434
10507
|
if (linearSessionId) {
|
|
10435
10508
|
linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
|
|
10436
10509
|
}
|
|
@@ -10453,10 +10526,45 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
10453
10526
|
} finally {
|
|
10454
10527
|
this.activeRun = null;
|
|
10455
10528
|
this.activeModel = null;
|
|
10529
|
+
this.blockedToolCallIds.clear();
|
|
10530
|
+
this.pendingCursorBlockReason = null;
|
|
10456
10531
|
await this.historyFile.flush();
|
|
10457
10532
|
await this.onTurnComplete();
|
|
10458
10533
|
}
|
|
10459
10534
|
}
|
|
10535
|
+
async handleCursorToolCall(toolCall) {
|
|
10536
|
+
if (!ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE || !toolCall) return;
|
|
10537
|
+
if (this.blockedToolCallIds.has(toolCall.toolUseId)) return;
|
|
10538
|
+
const result = await evaluateCommandProtection({
|
|
10539
|
+
provider: "cursor",
|
|
10540
|
+
source: "cursor_tool_call",
|
|
10541
|
+
toolName: toolCall.toolName,
|
|
10542
|
+
toolInput: toolCall.toolInput,
|
|
10543
|
+
command: toolCall.command,
|
|
10544
|
+
cwd: this.workingDirectory,
|
|
10545
|
+
toolUseId: toolCall.toolUseId
|
|
10546
|
+
});
|
|
10547
|
+
if (result.allowed) return;
|
|
10548
|
+
this.blockedToolCallIds.add(toolCall.toolUseId);
|
|
10549
|
+
const message = reportCommandProtectionBlock({
|
|
10550
|
+
managerName: "CursorManager",
|
|
10551
|
+
action: "tool call",
|
|
10552
|
+
eventType: "cursor-command-protection.blocked",
|
|
10553
|
+
result,
|
|
10554
|
+
command: toolCall.command,
|
|
10555
|
+
context: {
|
|
10556
|
+
toolName: toolCall.toolName,
|
|
10557
|
+
toolUseId: toolCall.toolUseId
|
|
10558
|
+
},
|
|
10559
|
+
historyFile: this.historyFile,
|
|
10560
|
+
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
10561
|
+
});
|
|
10562
|
+
if (this.activeRun) {
|
|
10563
|
+
await this.activeRun.cancel();
|
|
10564
|
+
throw new Error(message);
|
|
10565
|
+
}
|
|
10566
|
+
this.pendingCursorBlockReason = message;
|
|
10567
|
+
}
|
|
10460
10568
|
async toCursorMessage(request) {
|
|
10461
10569
|
if (!request.images || request.images.length === 0) {
|
|
10462
10570
|
return request.message;
|
|
@@ -10552,7 +10660,23 @@ var OPENCODE_SHIM_DIR = dirname6(fileURLToPath(new URL("../../scripts/opencode",
|
|
|
10552
10660
|
var OPENCODE_CONFIG_PATH = join18(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
10553
10661
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
10554
10662
|
var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
|
|
10555
|
-
var OPENCODE_WORKSPACE_PERMISSION =
|
|
10663
|
+
var OPENCODE_WORKSPACE_PERMISSION = {
|
|
10664
|
+
read: "allow",
|
|
10665
|
+
edit: "allow",
|
|
10666
|
+
glob: "allow",
|
|
10667
|
+
grep: "allow",
|
|
10668
|
+
list: "allow",
|
|
10669
|
+
bash: "ask",
|
|
10670
|
+
task: "allow",
|
|
10671
|
+
external_directory: "allow",
|
|
10672
|
+
todowrite: "allow",
|
|
10673
|
+
question: "allow",
|
|
10674
|
+
webfetch: "allow",
|
|
10675
|
+
websearch: "allow",
|
|
10676
|
+
lsp: "allow",
|
|
10677
|
+
doom_loop: "allow",
|
|
10678
|
+
skill: "allow"
|
|
10679
|
+
};
|
|
10556
10680
|
var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
10557
10681
|
var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
|
|
10558
10682
|
low: ["low"],
|
|
@@ -10684,6 +10808,27 @@ function opencodeErrorPayload(error) {
|
|
|
10684
10808
|
function opencodeFetch(input, init) {
|
|
10685
10809
|
return fetch(input, { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER });
|
|
10686
10810
|
}
|
|
10811
|
+
function parseOpencodeToolInputText(text) {
|
|
10812
|
+
try {
|
|
10813
|
+
return JSON.parse(text);
|
|
10814
|
+
} catch {
|
|
10815
|
+
return { command: text };
|
|
10816
|
+
}
|
|
10817
|
+
}
|
|
10818
|
+
function opencodePermissionCommand(payload, toolInput) {
|
|
10819
|
+
const toolCommand = extractToolCommand(toolInput);
|
|
10820
|
+
if (toolCommand) return toolCommand;
|
|
10821
|
+
const payloadCommand = extractToolCommand(payload);
|
|
10822
|
+
if (payloadCommand) return payloadCommand;
|
|
10823
|
+
const resources = Array.isArray(payload.resources) ? payload.resources.filter((item) => typeof item === "string") : [];
|
|
10824
|
+
if (resources.length > 0) return resources.join(" ");
|
|
10825
|
+
const metadata = isRecord4(payload.metadata) ? payload.metadata : null;
|
|
10826
|
+
for (const key of ["command", "cmd", "pattern", "resource"]) {
|
|
10827
|
+
const value = metadata?.[key];
|
|
10828
|
+
if (typeof value === "string" && value.trim()) return value;
|
|
10829
|
+
}
|
|
10830
|
+
return null;
|
|
10831
|
+
}
|
|
10687
10832
|
var OpencodeManager = class extends CodingAgentManager {
|
|
10688
10833
|
client = null;
|
|
10689
10834
|
server = null;
|
|
@@ -10701,6 +10846,11 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10701
10846
|
activeLinearForwarder = null;
|
|
10702
10847
|
forwardedLinearPartKeys = /* @__PURE__ */ new Set();
|
|
10703
10848
|
modelVariants = /* @__PURE__ */ new Map();
|
|
10849
|
+
handledPermissionRequestIds = /* @__PURE__ */ new Set();
|
|
10850
|
+
pendingPermissionRequestIds = /* @__PURE__ */ new Set();
|
|
10851
|
+
handledToolCallIds = /* @__PURE__ */ new Set();
|
|
10852
|
+
pendingToolCallIds = /* @__PURE__ */ new Set();
|
|
10853
|
+
opencodeToolInputs = /* @__PURE__ */ new Map();
|
|
10704
10854
|
slashCommandsCache = null;
|
|
10705
10855
|
slashCommandsRequest = null;
|
|
10706
10856
|
constructor(options) {
|
|
@@ -10905,6 +11055,11 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10905
11055
|
this.activeAbortController = null;
|
|
10906
11056
|
this.activeLinearForwarder = null;
|
|
10907
11057
|
this.forwardedLinearPartKeys.clear();
|
|
11058
|
+
this.handledPermissionRequestIds.clear();
|
|
11059
|
+
this.pendingPermissionRequestIds.clear();
|
|
11060
|
+
this.handledToolCallIds.clear();
|
|
11061
|
+
this.pendingToolCallIds.clear();
|
|
11062
|
+
this.opencodeToolInputs.clear();
|
|
10908
11063
|
await this.historyFile.flush();
|
|
10909
11064
|
await this.onTurnComplete();
|
|
10910
11065
|
}
|
|
@@ -10942,12 +11097,141 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10942
11097
|
if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
|
|
10943
11098
|
if (event.type === "message.part.updated" && isOpencodePart(part)) {
|
|
10944
11099
|
if (this.shouldRecordOpencodePart(part)) this.recordOpencodePart(part);
|
|
11100
|
+
this.handleOpencodePartProtection(part).catch((error) => {
|
|
11101
|
+
console.error("[OpencodeManager] Failed to evaluate Opencode tool part:", error);
|
|
11102
|
+
});
|
|
10945
11103
|
return;
|
|
10946
11104
|
}
|
|
10947
11105
|
if (event.type === "message.updated") this.recordOpencodeMessageRole(payload);
|
|
11106
|
+
if (event.type === "session.next.tool.input.ended") {
|
|
11107
|
+
const callId = typeof payload.callID === "string" ? payload.callID : void 0;
|
|
11108
|
+
if (callId && typeof payload.text === "string") {
|
|
11109
|
+
this.opencodeToolInputs.set(callId, parseOpencodeToolInputText(payload.text));
|
|
11110
|
+
}
|
|
11111
|
+
}
|
|
11112
|
+
if (event.type === "permission.v2.asked" || event.type === "permission.asked") {
|
|
11113
|
+
this.respondToOpencodePermission(event.type, payload).catch((error) => {
|
|
11114
|
+
console.error("[OpencodeManager] Failed to respond to Opencode permission:", error);
|
|
11115
|
+
});
|
|
11116
|
+
}
|
|
11117
|
+
if (event.type === "session.next.tool.called") {
|
|
11118
|
+
this.handleOpencodeToolCallProtection(payload).catch((error) => {
|
|
11119
|
+
console.error("[OpencodeManager] Failed to evaluate Opencode tool call:", error);
|
|
11120
|
+
});
|
|
11121
|
+
}
|
|
10948
11122
|
if (this.recordOpencodeNextPart(event.type, payload)) return;
|
|
10949
11123
|
this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
|
|
10950
11124
|
}
|
|
11125
|
+
async respondToOpencodePermission(type, payload) {
|
|
11126
|
+
if (!this.client) return;
|
|
11127
|
+
const requestId = typeof payload.id === "string" ? payload.id : void 0;
|
|
11128
|
+
const sessionId = typeof payload.sessionID === "string" ? payload.sessionID : this.sessionId ?? void 0;
|
|
11129
|
+
if (!requestId || !sessionId || this.handledPermissionRequestIds.has(requestId) || this.pendingPermissionRequestIds.has(requestId)) return;
|
|
11130
|
+
this.pendingPermissionRequestIds.add(requestId);
|
|
11131
|
+
try {
|
|
11132
|
+
const source = isRecord4(payload.source) ? payload.source : null;
|
|
11133
|
+
const callId = typeof source?.callID === "string" ? source.callID : isRecord4(payload.tool) && typeof payload.tool.callID === "string" ? payload.tool.callID : void 0;
|
|
11134
|
+
const toolInput = callId ? this.opencodeToolInputs.get(callId) ?? payload : payload;
|
|
11135
|
+
const command = opencodePermissionCommand(payload, toolInput);
|
|
11136
|
+
const result = ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE ? await evaluateCommandProtection({
|
|
11137
|
+
provider: "opencode",
|
|
11138
|
+
source: "opencode_permission_request",
|
|
11139
|
+
toolName: typeof payload.action === "string" ? payload.action : typeof payload.permission === "string" ? payload.permission : "permission",
|
|
11140
|
+
toolInput,
|
|
11141
|
+
command,
|
|
11142
|
+
cwd: this.workingDirectory,
|
|
11143
|
+
toolUseId: requestId
|
|
11144
|
+
}) : { allowed: true, enabled: false };
|
|
11145
|
+
const reply = result.allowed ? "once" : "reject";
|
|
11146
|
+
const message = result.allowed ? void 0 : reportCommandProtectionBlock({
|
|
11147
|
+
managerName: "OpencodeManager",
|
|
11148
|
+
action: "permission",
|
|
11149
|
+
eventType: "opencode-command-protection.blocked",
|
|
11150
|
+
result,
|
|
11151
|
+
command,
|
|
11152
|
+
context: {
|
|
11153
|
+
requestId
|
|
11154
|
+
},
|
|
11155
|
+
historyFile: this.historyFile,
|
|
11156
|
+
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
11157
|
+
});
|
|
11158
|
+
if (type === "permission.v2.asked") {
|
|
11159
|
+
await this.client.v2.session.permission.reply({
|
|
11160
|
+
sessionID: sessionId,
|
|
11161
|
+
requestID: requestId,
|
|
11162
|
+
reply,
|
|
11163
|
+
...message ? { message } : {}
|
|
11164
|
+
}, { throwOnError: true });
|
|
11165
|
+
} else {
|
|
11166
|
+
await this.client.permission.reply({
|
|
11167
|
+
requestID: requestId,
|
|
11168
|
+
directory: this.workingDirectory,
|
|
11169
|
+
reply,
|
|
11170
|
+
...message ? { message } : {}
|
|
11171
|
+
}, { throwOnError: true });
|
|
11172
|
+
}
|
|
11173
|
+
this.handledPermissionRequestIds.add(requestId);
|
|
11174
|
+
} finally {
|
|
11175
|
+
this.pendingPermissionRequestIds.delete(requestId);
|
|
11176
|
+
}
|
|
11177
|
+
}
|
|
11178
|
+
async handleOpencodeToolCallProtection(payload) {
|
|
11179
|
+
const callId = typeof payload.callID === "string" ? payload.callID : void 0;
|
|
11180
|
+
if (!callId || this.handledToolCallIds.has(callId)) return;
|
|
11181
|
+
const toolName = typeof payload.tool === "string" ? payload.tool : "tool";
|
|
11182
|
+
const input = isRecord4(payload.input) ? payload.input : {};
|
|
11183
|
+
this.opencodeToolInputs.set(callId, input);
|
|
11184
|
+
const command = extractToolCommand(input);
|
|
11185
|
+
await this.evaluateOpencodeToolProtection(toolName, input, command, callId);
|
|
11186
|
+
}
|
|
11187
|
+
async handleOpencodePartProtection(part) {
|
|
11188
|
+
if (part.type !== "tool") return;
|
|
11189
|
+
const state = part.state;
|
|
11190
|
+
if (state.status !== "running" && state.status !== "pending") return;
|
|
11191
|
+
await this.evaluateOpencodeToolProtection(part.tool, state.input, extractToolCommand(state.input), part.id);
|
|
11192
|
+
}
|
|
11193
|
+
async evaluateOpencodeToolProtection(toolName, toolInput, command, toolUseId) {
|
|
11194
|
+
if (!ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE || this.handledToolCallIds.has(toolUseId) || this.pendingToolCallIds.has(toolUseId)) return;
|
|
11195
|
+
this.pendingToolCallIds.add(toolUseId);
|
|
11196
|
+
try {
|
|
11197
|
+
const result = await evaluateCommandProtection({
|
|
11198
|
+
provider: "opencode",
|
|
11199
|
+
source: "opencode_tool_call",
|
|
11200
|
+
toolName,
|
|
11201
|
+
toolInput,
|
|
11202
|
+
command,
|
|
11203
|
+
cwd: this.workingDirectory,
|
|
11204
|
+
toolUseId
|
|
11205
|
+
});
|
|
11206
|
+
if (result.allowed) {
|
|
11207
|
+
this.handledToolCallIds.add(toolUseId);
|
|
11208
|
+
return;
|
|
11209
|
+
}
|
|
11210
|
+
const message = reportCommandProtectionBlock({
|
|
11211
|
+
managerName: "OpencodeManager",
|
|
11212
|
+
action: "tool call",
|
|
11213
|
+
eventType: "opencode-command-protection.blocked",
|
|
11214
|
+
result,
|
|
11215
|
+
command,
|
|
11216
|
+
context: {
|
|
11217
|
+
toolName,
|
|
11218
|
+
toolUseId
|
|
11219
|
+
},
|
|
11220
|
+
historyFile: this.historyFile,
|
|
11221
|
+
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
11222
|
+
});
|
|
11223
|
+
this.activeAbortController?.abort();
|
|
11224
|
+
if (this.client && this.sessionId) {
|
|
11225
|
+
await this.client.session.abort(
|
|
11226
|
+
{ sessionID: this.sessionId, directory: this.workingDirectory },
|
|
11227
|
+
{ throwOnError: true }
|
|
11228
|
+
);
|
|
11229
|
+
}
|
|
11230
|
+
this.handledToolCallIds.add(toolUseId);
|
|
11231
|
+
} finally {
|
|
11232
|
+
this.pendingToolCallIds.delete(toolUseId);
|
|
11233
|
+
}
|
|
11234
|
+
}
|
|
10951
11235
|
recordOpencodeMessageRole(payload) {
|
|
10952
11236
|
const info = isRecord4(payload.info) ? payload.info : null;
|
|
10953
11237
|
const id = typeof info?.id === "string" ? info.id : void 0;
|