replicas-engine 0.1.412 → 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.
Files changed (2) hide show
  1. package/dist/src/index.js +262 -4
  2. 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-v2";
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) {
@@ -2736,6 +2736,11 @@ function extractCommandProtectionCommandText(request, options = {}) {
2736
2736
  for (const key of COMMAND_PROTECTION_COMMAND_KEYS) {
2737
2737
  const value = input[key];
2738
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;
2739
2744
  }
2740
2745
  }
2741
2746
  if (typeof input === "string") return input;
@@ -6541,6 +6546,7 @@ var LinearEventForwarder = class {
6541
6546
  };
6542
6547
 
6543
6548
  // src/services/command-protection-service.ts
6549
+ var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by organization policy: agents may not merge pull requests.";
6544
6550
  function asCommandProtectionResponse(value) {
6545
6551
  if (typeof value !== "object" || value === null) return null;
6546
6552
  const candidate = value;
@@ -6585,6 +6591,17 @@ async function evaluateCommandProtection(request, signal) {
6585
6591
  function extractToolCommand(input) {
6586
6592
  return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
6587
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
+ }
6588
6605
 
6589
6606
  // src/services/skill-registry-service.ts
6590
6607
  import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
@@ -8232,7 +8249,7 @@ var AspClient = class {
8232
8249
  // src/managers/codex-asp/app-server-process.ts
8233
8250
  var DEFAULT_CODEX_BINARY = "codex";
8234
8251
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
8235
- var ENGINE_PACKAGE_VERSION = "0.1.412";
8252
+ var ENGINE_PACKAGE_VERSION = "0.1.413";
8236
8253
  var INITIALIZE_METHOD = "initialize";
8237
8254
  var INITIALIZED_NOTIFICATION = "initialized";
8238
8255
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -10348,6 +10365,24 @@ function extractCursorCommandDescription(content) {
10348
10365
  }
10349
10366
  return void 0;
10350
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
+ }
10351
10386
  async function listCursorCommandsInDirectory(directory) {
10352
10387
  let entries;
10353
10388
  try {
@@ -10373,6 +10408,8 @@ var CursorManager = class extends CodingAgentManager {
10373
10408
  agent = null;
10374
10409
  activeRun = null;
10375
10410
  activeModel = null;
10411
+ blockedToolCallIds = /* @__PURE__ */ new Set();
10412
+ pendingCursorBlockReason = null;
10376
10413
  historyFilePath;
10377
10414
  historyFile;
10378
10415
  slashCommandsCache = null;
@@ -10451,12 +10488,22 @@ var CursorManager = class extends CodingAgentManager {
10451
10488
  }, this.historyFile);
10452
10489
  const run = await agent.send(message, {
10453
10490
  model: { id: model },
10454
- mode: request.planMode ? "plan" : "agent"
10491
+ mode: request.planMode ? "plan" : "agent",
10492
+ onDelta: async (args) => {
10493
+ await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
10494
+ }
10455
10495
  });
10456
10496
  this.activeRun = run;
10457
10497
  this.activeModel = model;
10498
+ if (this.pendingCursorBlockReason) {
10499
+ await run.cancel();
10500
+ throw new Error(this.pendingCursorBlockReason);
10501
+ }
10458
10502
  for await (const event of run.stream()) {
10459
10503
  this.recordCursorEvent(event);
10504
+ if (event.type === "tool_call" && event.status === "running") {
10505
+ await this.handleCursorToolCall(cursorToolInputFromMessage(event));
10506
+ }
10460
10507
  if (linearSessionId) {
10461
10508
  linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
10462
10509
  }
@@ -10479,10 +10526,45 @@ var CursorManager = class extends CodingAgentManager {
10479
10526
  } finally {
10480
10527
  this.activeRun = null;
10481
10528
  this.activeModel = null;
10529
+ this.blockedToolCallIds.clear();
10530
+ this.pendingCursorBlockReason = null;
10482
10531
  await this.historyFile.flush();
10483
10532
  await this.onTurnComplete();
10484
10533
  }
10485
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
+ }
10486
10568
  async toCursorMessage(request) {
10487
10569
  if (!request.images || request.images.length === 0) {
10488
10570
  return request.message;
@@ -10578,7 +10660,23 @@ var OPENCODE_SHIM_DIR = dirname6(fileURLToPath(new URL("../../scripts/opencode",
10578
10660
  var OPENCODE_CONFIG_PATH = join18(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
10579
10661
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
10580
10662
  var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
10581
- var OPENCODE_WORKSPACE_PERMISSION = "allow";
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
+ };
10582
10680
  var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
10583
10681
  var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
10584
10682
  low: ["low"],
@@ -10710,6 +10808,27 @@ function opencodeErrorPayload(error) {
10710
10808
  function opencodeFetch(input, init) {
10711
10809
  return fetch(input, { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER });
10712
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
+ }
10713
10832
  var OpencodeManager = class extends CodingAgentManager {
10714
10833
  client = null;
10715
10834
  server = null;
@@ -10727,6 +10846,11 @@ var OpencodeManager = class extends CodingAgentManager {
10727
10846
  activeLinearForwarder = null;
10728
10847
  forwardedLinearPartKeys = /* @__PURE__ */ new Set();
10729
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();
10730
10854
  slashCommandsCache = null;
10731
10855
  slashCommandsRequest = null;
10732
10856
  constructor(options) {
@@ -10931,6 +11055,11 @@ var OpencodeManager = class extends CodingAgentManager {
10931
11055
  this.activeAbortController = null;
10932
11056
  this.activeLinearForwarder = null;
10933
11057
  this.forwardedLinearPartKeys.clear();
11058
+ this.handledPermissionRequestIds.clear();
11059
+ this.pendingPermissionRequestIds.clear();
11060
+ this.handledToolCallIds.clear();
11061
+ this.pendingToolCallIds.clear();
11062
+ this.opencodeToolInputs.clear();
10934
11063
  await this.historyFile.flush();
10935
11064
  await this.onTurnComplete();
10936
11065
  }
@@ -10968,12 +11097,141 @@ var OpencodeManager = class extends CodingAgentManager {
10968
11097
  if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
10969
11098
  if (event.type === "message.part.updated" && isOpencodePart(part)) {
10970
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
+ });
10971
11103
  return;
10972
11104
  }
10973
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
+ }
10974
11122
  if (this.recordOpencodeNextPart(event.type, payload)) return;
10975
11123
  this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
10976
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
+ }
10977
11235
  recordOpencodeMessageRole(payload) {
10978
11236
  const info = isRecord4(payload.info) ? payload.info : null;
10979
11237
  const id = typeof info?.id === "string" ? info.id : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.412",
3
+ "version": "0.1.413",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",