replicas-engine 0.1.545 → 0.1.546

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 +85 -36
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -702,7 +702,7 @@ var WORKSPACE_SIZES = ["small", "large"];
702
702
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
703
703
 
704
704
  // ../shared/src/e2b.ts
705
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-08-02-v1";
705
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-08-02-v2";
706
706
 
707
707
  // ../shared/src/runtime-env.ts
708
708
  function shellQuotePosix(value) {
@@ -2772,6 +2772,7 @@ var DEFAULT_USER_PREFERENCES = {
2772
2772
  default_agent: "codex",
2773
2773
  default_model: null,
2774
2774
  cursor_models: null,
2775
+ openrouter_models: null,
2775
2776
  close_workspace_on_pr_close: true,
2776
2777
  close_workspace_on_pr_merge: true,
2777
2778
  default_environment_id: null,
@@ -5258,6 +5259,18 @@ var MonolithService = class {
5258
5259
  return null;
5259
5260
  }
5260
5261
  }
5262
+ async getOpenRouterModels() {
5263
+ try {
5264
+ const response = await monolithRequest("/v1/engine/openrouter-models", { method: "GET" });
5265
+ if (!response.ok) {
5266
+ return null;
5267
+ }
5268
+ const body = await response.json();
5269
+ return isRecord(body) && Array.isArray(body.models) && body.models.every((model) => typeof model === "string") ? body.models : null;
5270
+ } catch {
5271
+ return null;
5272
+ }
5273
+ }
5261
5274
  async sendEvent(event) {
5262
5275
  if (!ENGINE_ENV.WORKSPACE_ID) {
5263
5276
  return;
@@ -10263,7 +10276,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
10263
10276
  var MIN_CODEX_CLI_VERSION = "0.144.6";
10264
10277
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
10265
10278
  var codexCliVersionEnsured = null;
10266
- var ENGINE_PACKAGE_VERSION = "0.1.545";
10279
+ var ENGINE_PACKAGE_VERSION = "0.1.546";
10267
10280
  var INITIALIZE_METHOD = "initialize";
10268
10281
  var INITIALIZED_NOTIFICATION = "initialized";
10269
10282
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -12887,6 +12900,25 @@ import {
12887
12900
  createOpencodeClient,
12888
12901
  createOpencodeServer
12889
12902
  } from "@opencode-ai/sdk/v2";
12903
+
12904
+ // src/services/openrouter-models-service.ts
12905
+ var OPENROUTER_MODELS_CACHE_MS = 6e4;
12906
+ var OPENROUTER_MODELS_STALE_GRACE_MS = 10 * 6e4;
12907
+ var cache = null;
12908
+ async function getAllowedOpenRouterModels() {
12909
+ const now = Date.now();
12910
+ if (cache && now - cache.fetchedAt < OPENROUTER_MODELS_CACHE_MS) return cache.models;
12911
+ const models = await monolithService.getOpenRouterModels();
12912
+ if (models?.length) {
12913
+ cache = { models, fetchedAt: now };
12914
+ return models;
12915
+ }
12916
+ if (cache && now - cache.fetchedAt < OPENROUTER_MODELS_STALE_GRACE_MS) return cache.models;
12917
+ cache = null;
12918
+ throw new Error("Could not load the organization's allowed OpenRouter models; refusing to run an unverified model.");
12919
+ }
12920
+
12921
+ // src/managers/opencode-manager.ts
12890
12922
  var OPENCODE_SHIM_DIR = dirname6(fileURLToPath(new URL("../../scripts/opencode", import.meta.url)));
12891
12923
  var OPENCODE_CONFIG_PATH = join20(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
12892
12924
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
@@ -12936,10 +12968,10 @@ async function getOpencodeProvider() {
12936
12968
  if (await hasOpencodeCredentials(ASTER_PROVIDER)) return ASTER_PROVIDER;
12937
12969
  return "openrouter";
12938
12970
  }
12939
- function getDefaultOpencodeModel(provider) {
12971
+ async function getDefaultOpencodeModel(provider) {
12940
12972
  if (provider === OPENCODE_GO_PROVIDER) return DEFAULT_OPENCODE_GO_MODEL;
12941
12973
  if (provider === ASTER_PROVIDER) return DEFAULT_ASTER_MODEL;
12942
- return DEFAULT_OPENCODE_MODEL;
12974
+ return (await getAllowedOpenRouterModels())[0] ?? DEFAULT_OPENCODE_MODEL;
12943
12975
  }
12944
12976
  function getOpenCodeGoModel(model) {
12945
12977
  return model.replace(/^[^/]+\//, "");
@@ -12986,7 +13018,7 @@ function opencodeConfig(provider, model) {
12986
13018
  return config;
12987
13019
  }
12988
13020
  function getConfiguredOpencodeModels(model, provider) {
12989
- return [.../* @__PURE__ */ new Set([model, ...provider === ASTER_PROVIDER ? ASTER_MODELS : OPENROUTER_MODELS])];
13021
+ return [.../* @__PURE__ */ new Set([model, ...provider === ASTER_PROVIDER ? ASTER_MODELS : []])];
12990
13022
  }
12991
13023
  function opencodeCommandToSlashCommand(command) {
12992
13024
  return createProviderSlashCommand("opencode", command.name, command.description);
@@ -13177,7 +13209,7 @@ var OpencodeManager = class extends CodingAgentManager {
13177
13209
  this.slashCommandsRequest ??= (async () => {
13178
13210
  try {
13179
13211
  const provider = await getOpencodeProvider();
13180
- const client = await this.ensureClient(getDefaultOpencodeModel(provider), provider);
13212
+ const client = await this.ensureClient(await getDefaultOpencodeModel(provider), provider);
13181
13213
  const directories = [this.workingDirectory, ...await getAgentAdditionalDirectories()];
13182
13214
  const perDirectory = await Promise.all(directories.map(async (directory) => {
13183
13215
  try {
@@ -13217,7 +13249,7 @@ var OpencodeManager = class extends CodingAgentManager {
13217
13249
  }
13218
13250
  this.providerId = providerId;
13219
13251
  const configuredModel = providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
13220
- const providerModels = providerId === ASTER_PROVIDER ? ASTER_MODELS : providerId === "openrouter" ? OPENROUTER_MODELS : null;
13252
+ const providerModels = providerId === ASTER_PROVIDER ? ASTER_MODELS : providerId === "openrouter" ? await getAllowedOpenRouterModels() : null;
13221
13253
  if (providerModels && !providerModels.includes(configuredModel)) {
13222
13254
  throw new Error(`Model ${configuredModel} is not available through ${providerId}.`);
13223
13255
  }
@@ -13317,7 +13349,6 @@ var OpencodeManager = class extends CodingAgentManager {
13317
13349
  }
13318
13350
  async processMessageInternal(request) {
13319
13351
  const provider = await getOpencodeProvider();
13320
- const model = request.model ?? getDefaultOpencodeModel(provider);
13321
13352
  const controller = new AbortController();
13322
13353
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
13323
13354
  const linearForwarder = new LinearEventForwarder(linearSessionId);
@@ -13325,6 +13356,7 @@ var OpencodeManager = class extends CodingAgentManager {
13325
13356
  this.activeLinearForwarder = linearForwarder;
13326
13357
  this.forwardedLinearPartKeys.clear();
13327
13358
  try {
13359
+ const model = request.model ?? await getDefaultOpencodeModel(provider);
13328
13360
  const client = await this.ensureClient(model, provider);
13329
13361
  const providerModel = this.providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
13330
13362
  const agent = request.planMode ? "plan" : "build";
@@ -13643,6 +13675,30 @@ import {
13643
13675
  SessionManager,
13644
13676
  DefaultResourceLoader
13645
13677
  } from "@earendil-works/pi-coding-agent";
13678
+ function registerOpenRouterModels(modelRegistry, apiKey, modelIds) {
13679
+ const openRouterModels = modelRegistry.getAll().filter((model) => model.provider === "openrouter");
13680
+ const modelTemplate = openRouterModels[0];
13681
+ if (!modelTemplate) throw new Error("Pi OpenAI-compatible model template is unavailable.");
13682
+ modelRegistry.registerProvider("openrouter", {
13683
+ api: modelTemplate.api,
13684
+ apiKey,
13685
+ baseUrl: modelTemplate.baseUrl,
13686
+ models: [
13687
+ ...openRouterModels,
13688
+ ...modelIds.filter((id) => !openRouterModels.some((model) => model.id === id)).map((id) => ({
13689
+ id,
13690
+ name: id,
13691
+ api: modelTemplate.api,
13692
+ baseUrl: modelTemplate.baseUrl,
13693
+ reasoning: modelTemplate.reasoning,
13694
+ input: modelTemplate.input,
13695
+ cost: modelTemplate.cost,
13696
+ contextWindow: modelTemplate.contextWindow,
13697
+ maxTokens: modelTemplate.maxTokens
13698
+ }))
13699
+ ]
13700
+ });
13701
+ }
13646
13702
  function eventPayload(event) {
13647
13703
  return isRecord4(event) ? { ...event } : { value: event };
13648
13704
  }
@@ -13674,6 +13730,7 @@ var PiManager = class extends CodingAgentManager {
13674
13730
  historyFilePath;
13675
13731
  historyFile;
13676
13732
  providerId = "openrouter";
13733
+ providerApiKey = null;
13677
13734
  constructor(options) {
13678
13735
  super(options);
13679
13736
  this.historyFilePath = options.historyFilePath ?? join21(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
@@ -13718,8 +13775,16 @@ var PiManager = class extends CodingAgentManager {
13718
13775
  }
13719
13776
  async ensureSession(request) {
13720
13777
  if (this.session) {
13778
+ const turnModel = request.model ?? this.session.model?.id;
13779
+ if (this.providerId === "openrouter" && turnModel && !(await getAllowedOpenRouterModels()).includes(turnModel)) {
13780
+ throw new Error(`Pi model is not available through ${this.providerId}: ${turnModel}`);
13781
+ }
13721
13782
  if (request.model && request.model !== this.session.model?.id) {
13722
- const model2 = this.session.modelRegistry.find(this.providerId, request.model);
13783
+ let model2 = this.session.modelRegistry.find(this.providerId, request.model);
13784
+ if (!model2 && this.providerId === "openrouter" && this.providerApiKey) {
13785
+ registerOpenRouterModels(this.session.modelRegistry, this.providerApiKey, [request.model]);
13786
+ model2 = this.session.modelRegistry.find(this.providerId, request.model);
13787
+ }
13723
13788
  if (!model2) throw new Error(`Pi model is not available through ${this.providerId}: ${request.model}`);
13724
13789
  await this.session.setModel(model2);
13725
13790
  }
@@ -13732,10 +13797,8 @@ var PiManager = class extends CodingAgentManager {
13732
13797
  const credentials = this.getProviderCredentials(authStorage);
13733
13798
  if (!credentials) throw new Error("Aster or OpenRouter API key is not configured for Pi.");
13734
13799
  this.providerId = credentials.provider;
13800
+ this.providerApiKey = credentials.apiKey;
13735
13801
  const modelRegistry = ModelRegistry.create(authStorage);
13736
- const openRouterModels = modelRegistry.getAll().filter((candidate) => candidate.provider === "openrouter");
13737
- const modelTemplate = openRouterModels[0];
13738
- if (!modelTemplate) throw new Error("Pi OpenAI-compatible model template is unavailable.");
13739
13802
  if (this.providerId === ASTER_PROVIDER) {
13740
13803
  modelRegistry.registerProvider(ASTER_PROVIDER, {
13741
13804
  name: "Aster",
@@ -13754,30 +13817,16 @@ var PiManager = class extends CodingAgentManager {
13754
13817
  maxTokens: 65536
13755
13818
  }))
13756
13819
  });
13757
- } else {
13758
- modelRegistry.registerProvider("openrouter", {
13759
- api: modelTemplate.api,
13760
- apiKey: credentials.apiKey,
13761
- baseUrl: modelTemplate.baseUrl,
13762
- models: [
13763
- ...openRouterModels,
13764
- ...OPENROUTER_MODELS.filter((id) => !openRouterModels.some((candidate) => candidate.id === id)).map((id) => ({
13765
- id,
13766
- name: id,
13767
- api: modelTemplate.api,
13768
- baseUrl: modelTemplate.baseUrl,
13769
- reasoning: modelTemplate.reasoning,
13770
- input: modelTemplate.input,
13771
- cost: modelTemplate.cost,
13772
- contextWindow: modelTemplate.contextWindow,
13773
- maxTokens: modelTemplate.maxTokens
13774
- }))
13775
- ]
13776
- });
13777
13820
  }
13778
- const defaultModel = this.providerId === ASTER_PROVIDER ? DEFAULT_ASTER_MODEL : DEFAULT_PI_MODEL;
13821
+ let defaultModel = DEFAULT_ASTER_MODEL;
13822
+ let allowedModels = null;
13823
+ if (this.providerId !== ASTER_PROVIDER) {
13824
+ allowedModels = await getAllowedOpenRouterModels();
13825
+ registerOpenRouterModels(modelRegistry, credentials.apiKey, allowedModels);
13826
+ defaultModel = allowedModels[0] ?? DEFAULT_PI_MODEL;
13827
+ }
13779
13828
  const modelId = request.model ?? defaultModel;
13780
- const model = modelRegistry.find(this.providerId, modelId);
13829
+ const model = allowedModels && !allowedModels.includes(modelId) ? null : modelRegistry.find(this.providerId, modelId);
13781
13830
  if (!model) throw new Error(`Pi model is not available through ${this.providerId}: ${modelId}`);
13782
13831
  const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
13783
13832
  const resourceLoader = new DefaultResourceLoader({
@@ -13816,9 +13865,9 @@ var PiManager = class extends CodingAgentManager {
13816
13865
  this.recordHistoryEvent(`pi-${event.type}`, payload, this.historyFile);
13817
13866
  }
13818
13867
  async processMessageInternal(request) {
13819
- const session = await this.ensureSession(request);
13820
- this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
13821
13868
  try {
13869
+ const session = await this.ensureSession(request);
13870
+ this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
13822
13871
  const images = request.images && request.images.length > 0 ? (await normalizeImages(request.images)).map((image) => ({
13823
13872
  type: "image",
13824
13873
  data: image.source.data,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.545",
3
+ "version": "0.1.546",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",