replicas-engine 0.1.1279 → 0.1.1281

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.
@@ -8,12 +8,12 @@ import {
8
8
  listRunningCodexModels,
9
9
  restartCodexAspHost,
10
10
  restartCodexAspHostIfRunning
11
- } from "./chunk-VCCUBYU3.js";
12
- import "./chunk-HUUEA35W.js";
13
- import "./chunk-GJN2TDJ7.js";
14
- import "./chunk-CQR3GSM4.js";
11
+ } from "./chunk-JPOHWGGI.js";
12
+ import "./chunk-4J7EZ245.js";
13
+ import "./chunk-5OYGVDKL.js";
14
+ import "./chunk-3MSBE24K.js";
15
15
  import "./chunk-UZSNFLDQ.js";
16
- import "./chunk-47JJX56J.js";
16
+ import "./chunk-5YU4KQIU.js";
17
17
  import "./chunk-VEQXQN22.js";
18
18
  export {
19
19
  CODEX_ULTRAFAST_SERVICE_TIER,
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  HOOK_EXEC_MAX_BUFFER_BYTES
6
- } from "./chunk-47JJX56J.js";
6
+ } from "./chunk-5YU4KQIU.js";
7
7
 
8
8
  // src/utils/exec.ts
9
9
  import { exec, execFile } from "child_process";
@@ -4,7 +4,7 @@ const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  SUBPROCESS_MAX_BUFFER,
6
6
  execFileAsync
7
- } from "./chunk-CQR3GSM4.js";
7
+ } from "./chunk-3MSBE24K.js";
8
8
  import {
9
9
  isRecord as isRecord2
10
10
  } from "./chunk-UZSNFLDQ.js";
@@ -22,7 +22,7 @@ import {
22
22
  provisionedDeviceSchema,
23
23
  readReplicasRuntimeEnv,
24
24
  serializePosixEnvFile
25
- } from "./chunk-47JJX56J.js";
25
+ } from "./chunk-5YU4KQIU.js";
26
26
 
27
27
  // src/runtime-env-loader.ts
28
28
  import { readFileSync } from "fs";
@@ -3,10 +3,10 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  execFileAsync
6
- } from "./chunk-CQR3GSM4.js";
6
+ } from "./chunk-3MSBE24K.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-47JJX56J.js";
9
+ } from "./chunk-5YU4KQIU.js";
10
10
 
11
11
  // src/utils/codex-agent-env.ts
12
12
  function buildCodexAgentEnv(source = process.env) {
@@ -240,7 +240,7 @@ function getCodexModelProvider(method) {
240
240
  if (method === "foundry") return "azure";
241
241
  return method === "ollama" || method === "openai-compatible" ? "replicas_openai_compatible" : "openai";
242
242
  }
243
- var ENGINE_PACKAGE_VERSION = "0.1.1279";
243
+ var ENGINE_PACKAGE_VERSION = "0.1.1280";
244
244
  var INITIALIZE_METHOD = "initialize";
245
245
  var INITIALIZED_NOTIFICATION = "initialized";
246
246
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -167,14 +167,20 @@ async function fetchModelsDevCatalog() {
167
167
 
168
168
  // ../shared/src/credentials/opencode-go.ts
169
169
  var OPENCODE_GO_PROVIDER = "opencode-go";
170
+ var OPENCODE_ZEN_PROVIDER = "opencode";
170
171
  var OPENCODE_GO_BASE_URL = "https://opencode.ai/zen/go/v1";
171
172
  var DEFAULT_OPENCODE_GO_MODEL = "glm-5.3";
172
- async function fetchOpenCodeGoCatalog(fallbackModelIds = [DEFAULT_OPENCODE_GO_MODEL]) {
173
+ function isOpenCodeZenModel(model2) {
174
+ return model2.startsWith(`${OPENCODE_ZEN_PROVIDER}/`);
175
+ }
176
+ async function fetchOpenCodeGoCatalog(agent, fallbackModelIds = [DEFAULT_OPENCODE_GO_MODEL]) {
173
177
  try {
174
- const provider2 = (await fetchModelsDevCatalog())[OPENCODE_GO_PROVIDER];
178
+ const catalog = await fetchModelsDevCatalog();
179
+ const provider2 = catalog[OPENCODE_GO_PROVIDER];
175
180
  if (!provider2) throw new Error("OpenCode Go model catalog is unavailable");
181
+ const zenFreeModels = agent === "opencode" ? Object.entries(catalog[OPENCODE_ZEN_PROVIDER]?.models ?? {}).filter(([, definition]) => definition.cost?.input === 0).map(([id, definition]) => [`${OPENCODE_ZEN_PROVIDER}/${id}`, definition]) : [];
176
182
  const models = Object.fromEntries(
177
- Object.entries(provider2.models).filter(([, definition]) => definition.status !== "deprecated" && definition.status !== "alpha").map(([id, definition]) => [id, {
183
+ [...Object.entries(provider2.models), ...zenFreeModels].filter(([, definition]) => definition.status !== "deprecated" && definition.status !== "alpha").map(([id, definition]) => [id, {
178
184
  id: definition.id ?? id,
179
185
  name: definition.name,
180
186
  api: definition.provider?.npm === "@ai-sdk/anthropic" ? "anthropic-messages" : definition.provider?.npm === "@ai-sdk/openai" ? "openai-responses" : void 0,
@@ -187,7 +193,7 @@ async function fetchOpenCodeGoCatalog(fallbackModelIds = [DEFAULT_OPENCODE_GO_MO
187
193
  } catch {
188
194
  return {
189
195
  authoritative: false,
190
- models: Object.fromEntries(fallbackModelIds.map((id) => [id, {
196
+ models: Object.fromEntries(fallbackModelIds.filter((id) => agent === "opencode" || !isOpenCodeZenModel(id)).map((id) => [id, {
191
197
  id,
192
198
  name: id === DEFAULT_OPENCODE_GO_MODEL ? "GLM 5.3" : id
193
199
  }]))
@@ -4496,6 +4502,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
4496
4502
  | "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\` |
4497
4503
  | "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\` |
4498
4504
  | "Ignore Dependabot for this PR automation" | \`replicas automation edit <id> --trigger-github-exclude-users 'dependabot[bot]'\` |
4505
+ | "Also run this PR automation on every push" | \`replicas automation edit <id> --keep-triggers --trigger-github pull_request.synchronize --trigger-github-repos acme/web\` |
4499
4506
  | "What repos are connected?" | \`replicas repos\` |
4500
4507
  | "Set up a \`replicas.json\` in this repo" | \`replicas init\` (\`-y\` for YAML) |
4501
4508
 
@@ -4657,7 +4664,7 @@ replicas automation edit <id> \\
4657
4664
  --trigger-cron "0 4 * * *" # replaces existing triggers
4658
4665
  \`\`\`
4659
4666
 
4660
- \`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 (repeat it to keep several events), and the matching exclude flag is applied to the replacement triggers.
4667
+ \`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-cron\`, \`--trigger-github\` or \`--trigger-gitlab\` replaces all triggers (repeat a flag for several events); add \`--keep-triggers\` to add them to the existing triggers instead. The repo, exclude and reviewer flags apply only to the events you pass.
4661
4668
 
4662
4669
  \`replicas automation edit <id> --move-to-org\` turns a personal automation into a shared one the whole team can see. It can't be undone.
4663
4670
 
@@ -10557,8 +10564,10 @@ export {
10557
10564
  detectAgentQuotaLimit,
10558
10565
  listOpenAICompatibleModels,
10559
10566
  resolveOpenAICompatibleModelsUrl,
10567
+ openAICompatibleAuthEnvKeys,
10560
10568
  readOpenAICompatibleAuthEnv,
10561
10569
  OLLAMA_AGENTS,
10570
+ ollamaAuthEnvKeys,
10562
10571
  readOllamaAuthEnv,
10563
10572
  DEFAULT_OLLAMA_MODEL,
10564
10573
  getDefaultOllamaModel,
@@ -10586,8 +10595,10 @@ export {
10586
10595
  DEFAULT_ASTER_MODEL,
10587
10596
  ASTER_MODEL_LABELS,
10588
10597
  OPENCODE_GO_PROVIDER,
10598
+ OPENCODE_ZEN_PROVIDER,
10589
10599
  OPENCODE_GO_BASE_URL,
10590
10600
  DEFAULT_OPENCODE_GO_MODEL,
10601
+ isOpenCodeZenModel,
10591
10602
  fetchOpenCodeGoCatalog,
10592
10603
  getChatKind,
10593
10604
  isGitHubUrl,
@@ -10,15 +10,15 @@ import {
10
10
  resolveDiffBase,
11
11
  runGitCommand,
12
12
  setAgentCredentialSnapshot
13
- } from "./chunk-HUUEA35W.js";
13
+ } from "./chunk-4J7EZ245.js";
14
14
  import {
15
15
  AppServerProcess,
16
16
  buildCodexAgentEnv
17
- } from "./chunk-GJN2TDJ7.js";
17
+ } from "./chunk-5OYGVDKL.js";
18
18
  import {
19
19
  SUBPROCESS_MAX_BUFFER,
20
20
  execFileAsync
21
- } from "./chunk-CQR3GSM4.js";
21
+ } from "./chunk-3MSBE24K.js";
22
22
  import {
23
23
  isRecord
24
24
  } from "./chunk-UZSNFLDQ.js";
@@ -55,7 +55,7 @@ import {
55
55
  resolveOpenAICompatibleModelsUrl,
56
56
  splitMothershipInstructions,
57
57
  withTimeout
58
- } from "./chunk-47JJX56J.js";
58
+ } from "./chunk-5YU4KQIU.js";
59
59
 
60
60
  // src/managers/codex-token-manager.ts
61
61
  import { promises as fs } from "fs";
@@ -291,7 +291,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
291
291
  await this.applyCredentialUpdate(++this.credentialGeneration, async () => {
292
292
  await this.applyCredentialsResponse(data);
293
293
  setAgentCredentialSnapshot("codex", data);
294
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-CYLNJ6JB.js");
294
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-VCIWMQXN.js");
295
295
  await restartCodexAspHostIfRunning2();
296
296
  });
297
297
  await this.start(true);
@@ -313,7 +313,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
313
313
  const accountChanged = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.codex?.credentialId !== data.credentialId;
314
314
  await this.applyCredentialsResponse(data);
315
315
  if (restartOnChange && (accountChanged || CODEX_AUTH_ENV_KEYS.some((key, index) => process.env[key] !== previousEnv[index]))) {
316
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-CYLNJ6JB.js");
316
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-VCIWMQXN.js");
317
317
  await restartCodexAspHostIfRunning2();
318
318
  }
319
319
  setAgentCredentialSnapshot("codex", data);
@@ -3,17 +3,19 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  AspClient
6
- } from "./chunk-GJN2TDJ7.js";
6
+ } from "./chunk-5OYGVDKL.js";
7
7
  import {
8
8
  ASTER_BASE_URL,
9
9
  ASTER_PROVIDER,
10
10
  CREDENTIAL_METHOD,
11
11
  OPENCODE_GO_PROVIDER,
12
+ OPENCODE_ZEN_PROVIDER,
12
13
  codexReasoningEffortForThinkingLevel,
14
+ isOpenCodeZenModel,
13
15
  isRecord,
14
16
  readOllamaAuthEnv,
15
17
  readOpenAICompatibleAuthEnv
16
- } from "./chunk-47JJX56J.js";
18
+ } from "./chunk-5YU4KQIU.js";
17
19
 
18
20
  // src/managers/codex-asp/notification-dispatch.ts
19
21
  var TURN_STARTED_METHOD = "turn/started";
@@ -109,17 +111,23 @@ async function getOpencodeProvider() {
109
111
  return "openrouter";
110
112
  }
111
113
  function getOpenCodeGoModel(model) {
112
- return model.replace(/^[^/]+\//, "");
114
+ return isOpenCodeZenModel(model) ? model : model.replace(/^[^/]+\//, "");
115
+ }
116
+ function opencodeModelRef(provider, model) {
117
+ if (provider !== OPENCODE_GO_PROVIDER) return { providerID: provider, modelID: model };
118
+ return isOpenCodeZenModel(model) ? { providerID: OPENCODE_ZEN_PROVIDER, modelID: model.slice(OPENCODE_ZEN_PROVIDER.length + 1) } : { providerID: OPENCODE_GO_PROVIDER, modelID: getOpenCodeGoModel(model) };
113
119
  }
114
120
  function opencodeConfig(provider, model, providerModels = [model]) {
115
121
  let config;
116
122
  if (provider === OPENCODE_GO_PROVIDER) {
123
+ const { providerID, modelID } = opencodeModelRef(provider, model);
117
124
  config = {
118
- enabled_providers: [OPENCODE_GO_PROVIDER],
119
- model: `${OPENCODE_GO_PROVIDER}/${model}`,
125
+ // Without a key OpenCode's own provider serves only its free Zen models.
126
+ enabled_providers: [OPENCODE_GO_PROVIDER, OPENCODE_ZEN_PROVIDER],
127
+ model: `${providerID}/${modelID}`,
120
128
  provider: {
121
129
  [OPENCODE_GO_PROVIDER]: {
122
- models: Object.fromEntries(providerModels.map((candidate) => [candidate, {}]))
130
+ models: Object.fromEntries(providerModels.filter((candidate) => !isOpenCodeZenModel(candidate)).map((candidate) => [candidate, {}]))
123
131
  }
124
132
  },
125
133
  permission: OPENCODE_WORKSPACE_PERMISSION,
@@ -235,6 +243,7 @@ export {
235
243
  hasOpencodeCredentials,
236
244
  getOpencodeProvider,
237
245
  getOpenCodeGoModel,
246
+ opencodeModelRef,
238
247
  opencodeConfig,
239
248
  uuidV7,
240
249
  spawnMuseMsp,
@@ -5,7 +5,7 @@ import {
5
5
  extractCommandProtectionCommandText,
6
6
  isRecord,
7
7
  parseCodeHostPrUrl
8
- } from "./chunk-47JJX56J.js";
8
+ } from "./chunk-5YU4KQIU.js";
9
9
 
10
10
  // src/services/published-pull-request-urls.ts
11
11
  function publishedPullRequestUrls(result) {
@@ -5,14 +5,14 @@ import {
5
5
  getOriginDefaultBranch,
6
6
  monolithRequest,
7
7
  runGitCommand
8
- } from "./chunk-HUUEA35W.js";
8
+ } from "./chunk-4J7EZ245.js";
9
9
  import {
10
10
  extractCommandProtectionCommandText,
11
11
  findCommandProtectionGitLookups,
12
12
  findGitCommitSignals,
13
13
  findPrMergeSignals,
14
14
  parseCommandProtectionRequest
15
- } from "./chunk-47JJX56J.js";
15
+ } from "./chunk-5YU4KQIU.js";
16
16
 
17
17
  // src/services/command-protection-service.ts
18
18
  import { readFile, stat } from "fs/promises";
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  evaluateCommandProtection
6
- } from "./chunk-5WJNG5IX.js";
7
- import "./chunk-HUUEA35W.js";
8
- import "./chunk-CQR3GSM4.js";
6
+ } from "./chunk-VI33T336.js";
7
+ import "./chunk-4J7EZ245.js";
8
+ import "./chunk-3MSBE24K.js";
9
9
  import {
10
10
  isRecord
11
11
  } from "./chunk-UZSNFLDQ.js";
12
- import "./chunk-47JJX56J.js";
12
+ import "./chunk-5YU4KQIU.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/command-protection-hook.ts
@@ -9,16 +9,17 @@ import {
9
9
  initializeMuse,
10
10
  museTurnStartParams,
11
11
  opencodeConfig,
12
+ opencodeModelRef,
12
13
  opencodeThinkingVariant,
13
14
  recoverCompletedTurn,
14
15
  spawnMuseMsp,
15
16
  startMuseSession
16
- } from "./chunk-32B2T6C3.js";
17
+ } from "./chunk-OYPH6PPR.js";
17
18
  import {
18
19
  AppServerProcess,
19
20
  buildCodexAgentEnv
20
- } from "./chunk-GJN2TDJ7.js";
21
- import "./chunk-CQR3GSM4.js";
21
+ } from "./chunk-5OYGVDKL.js";
22
+ import "./chunk-3MSBE24K.js";
22
23
  import {
23
24
  AGENT,
24
25
  OPENCODE_GO_PROVIDER,
@@ -28,7 +29,7 @@ import {
28
29
  headlessAgentUsagePath,
29
30
  isRecord,
30
31
  withTimeout
31
- } from "./chunk-47JJX56J.js";
32
+ } from "./chunk-5YU4KQIU.js";
32
33
  import "./chunk-VEQXQN22.js";
33
34
 
34
35
  // src/headless-agent.ts
@@ -175,6 +176,7 @@ async function runCodex(request) {
175
176
  async function runOpencode(request) {
176
177
  const provider = await getOpencodeProvider();
177
178
  const model = provider === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(request.model) : request.model;
179
+ const { providerID, modelID } = opencodeModelRef(provider, model);
178
180
  const server = await createOpencodeServer({ port: 0, timeout: 3e4, config: opencodeConfig(provider, model) });
179
181
  try {
180
182
  const password = process.env.OPENCODE_SERVER_PASSWORD;
@@ -182,12 +184,12 @@ async function runOpencode(request) {
182
184
  baseUrl: server.url,
183
185
  ...password ? { headers: { Authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}` } } : {}
184
186
  });
185
- const variant = opencodeThinkingVariant((await fetchOpencodeModelVariants(client, request.workingDirectory, provider)).get(model), request.thinkingLevel);
186
- const session = await client.session.create({ directory: request.workingDirectory, model: { providerID: provider, id: model } }, { throwOnError: true });
187
+ const variant = opencodeThinkingVariant((await fetchOpencodeModelVariants(client, request.workingDirectory, providerID)).get(modelID), request.thinkingLevel);
188
+ const session = await client.session.create({ directory: request.workingDirectory, model: { providerID, id: modelID } }, { throwOnError: true });
187
189
  const result = await client.session.prompt({
188
190
  sessionID: session.data.id,
189
191
  directory: request.workingDirectory,
190
- model: { providerID: provider, modelID: model },
192
+ model: { providerID, modelID },
191
193
  ...variant ? { variant } : {},
192
194
  format: { type: "json_schema", schema: request.outputSchema, retryCount: 2 },
193
195
  parts: [{ type: "text", text: request.prompt }]
package/dist/src/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  evaluateCommandProtection,
28
28
  extractToolCommand,
29
29
  reportCommandProtectionBlock
30
- } from "./chunk-5WJNG5IX.js";
30
+ } from "./chunk-VI33T336.js";
31
31
  import {
32
32
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
33
33
  AGENT_MESSAGE_DELTA_METHOD,
@@ -59,15 +59,16 @@ import {
59
59
  initializeMuse,
60
60
  museTurnStartParams,
61
61
  opencodeConfig,
62
+ opencodeModelRef,
62
63
  opencodeThinkingVariant,
63
64
  recoverCompletedTurn,
64
65
  spawnMuseMsp,
65
66
  startMuseSession,
66
67
  uuidV7
67
- } from "./chunk-32B2T6C3.js";
68
+ } from "./chunk-OYPH6PPR.js";
68
69
  import {
69
70
  publishedPullRequestUrls
70
- } from "./chunk-YD76ZL7D.js";
71
+ } from "./chunk-THAFUTL3.js";
71
72
  import {
72
73
  ACCOUNT_RATE_LIMITS_READ_METHOD,
73
74
  AsyncLock,
@@ -140,7 +141,7 @@ import {
140
141
  timestampFromSeconds,
141
142
  upsertCredentialFileLines,
142
143
  writeSecureCredentialFile
143
- } from "./chunk-VCCUBYU3.js";
144
+ } from "./chunk-JPOHWGGI.js";
144
145
  import {
145
146
  ENGINE_ENV,
146
147
  monolithRequest,
@@ -150,15 +151,15 @@ import {
150
151
  resolveDiffBase,
151
152
  setAgentCredentialSnapshot,
152
153
  setLinearSessionId
153
- } from "./chunk-HUUEA35W.js";
154
+ } from "./chunk-4J7EZ245.js";
154
155
  import {
155
156
  getCodexModelProvider
156
- } from "./chunk-GJN2TDJ7.js";
157
+ } from "./chunk-5OYGVDKL.js";
157
158
  import {
158
159
  SUBPROCESS_MAX_BUFFER,
159
160
  execAsync,
160
161
  execFileAsync
161
- } from "./chunk-CQR3GSM4.js";
162
+ } from "./chunk-3MSBE24K.js";
162
163
  import {
163
164
  isRecord as isRecord2
164
165
  } from "./chunk-UZSNFLDQ.js";
@@ -364,6 +365,8 @@ import {
364
365
  normalizeClaudeModel,
365
366
  normalizeCodexAspTranscriptStatus,
366
367
  normalizeContentBlocks,
368
+ ollamaAuthEnvKeys,
369
+ openAICompatibleAuthEnvKeys,
367
370
  paginateChatHistory,
368
371
  paginateEngineLogContent,
369
372
  parseAgentEventJsonlLine,
@@ -406,7 +409,7 @@ import {
406
409
  workspaceTokenRequestSchema,
407
410
  workspaceTokenResponseSchema,
408
411
  wrapInTag
409
- } from "./chunk-47JJX56J.js";
412
+ } from "./chunk-5YU4KQIU.js";
410
413
  import {
411
414
  cloneMcpConfig,
412
415
  ensureCompatibilityImports,
@@ -11229,11 +11232,57 @@ var MuseTokenManager = class extends BaseRefreshManager {
11229
11232
  };
11230
11233
  var museTokenManager = new MuseTokenManager();
11231
11234
 
11235
+ // src/managers/opencode-token-manager.ts
11236
+ var OPENCODE_AUTH_ENV_KEYS = [...ollamaAuthEnvKeys("opencode"), ...openAICompatibleAuthEnvKeys("opencode")];
11237
+ var OpencodeTokenManager = class extends BaseRefreshManager {
11238
+ constructor() {
11239
+ super("OpencodeTokenManager");
11240
+ }
11241
+ doRefresh() {
11242
+ return Promise.resolve();
11243
+ }
11244
+ async fetchFreshCredentials(failureReason, failureKind = "exhausted", failedCredential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.opencode, allowedMethods, resetsAt) {
11245
+ const failedMethod = AGENT_CREDENTIAL_METHODS_IN_ORDER.opencode.find(({ method }) => method === failedCredential?.method)?.method;
11246
+ if (!failedMethod || allowedMethods && !allowedMethods.includes(failedMethod)) {
11247
+ return createErrorResult({ message: "OpenCode only switches between keys of the same provider", code: "no_credentials" });
11248
+ }
11249
+ return this.swapCredentials({
11250
+ provider: "opencode",
11251
+ failureKind,
11252
+ allowedMethods: [failedMethod],
11253
+ refresh: async (exclusions) => {
11254
+ const request = {
11255
+ failedMethod,
11256
+ ...failedCredential ? { failedCredential } : {},
11257
+ failureReason,
11258
+ failureKind,
11259
+ ...resetsAt ? { resetsAt } : {},
11260
+ ...exclusions
11261
+ };
11262
+ const response = await monolithRequest("/v1/engine/opencode/refresh-credentials", { body: request, signal: AbortSignal.timeout(12e4) });
11263
+ if (!response.ok) {
11264
+ throw new Error(`Credentials refresh failed: ${response.status} ${await response.text()}`);
11265
+ }
11266
+ const data = await response.json();
11267
+ for (const file of data.auth.files ?? []) {
11268
+ await writeSecureCredentialFile(file.path, file.contents, { ensureParentDir: true });
11269
+ }
11270
+ for (const key of OPENCODE_AUTH_ENV_KEYS) delete process.env[key];
11271
+ Object.assign(process.env, data.auth.env);
11272
+ setAgentCredentialSnapshot("opencode", data);
11273
+ },
11274
+ isOauthNow: () => false
11275
+ });
11276
+ }
11277
+ };
11278
+ var opencodeTokenManager = new OpencodeTokenManager();
11279
+
11232
11280
  // src/managers/auth-fallback.ts
11233
11281
  var SWAPPABLE_AGENTS = {
11234
11282
  claude: claudeTokenManager,
11235
11283
  codex: codexTokenManager,
11236
- muse: museTokenManager
11284
+ muse: museTokenManager,
11285
+ opencode: opencodeTokenManager
11237
11286
  };
11238
11287
  var QUOTA_STOP_NEXT_STEP = {
11239
11288
  failed: "The next credential in the fallback chain could not be used. Check it in Settings \u2192 Fallbacks and try again.",
@@ -16796,9 +16845,9 @@ var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join15(OPENCODE_SHIM_DIR, "openc
16796
16845
  var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join15(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
16797
16846
  var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join15(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
16798
16847
  var OPENCODE_CONFIG_PATH = join15(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
16799
- var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
16800
16848
  var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
16801
16849
  var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
16850
+ var OPENCODE_SWITCHED_CREDENTIAL_PROMPT = "Your previous turn stopped at a usage limit. Continue from where you left off: first check what is already done, and do not repeat commands, edits, or messages you already completed.";
16802
16851
  async function getDefaultOpencodeModel(provider) {
16803
16852
  const ollama = readOllamaAuthEnv("opencode", process.env);
16804
16853
  if (provider === "ollama" && ollama) {
@@ -16911,13 +16960,16 @@ function opencodeErrorPayload(error) {
16911
16960
  ...status !== void 0 ? { status } : {}
16912
16961
  };
16913
16962
  }
16914
- function opencodeFetch(input, init) {
16915
- const requestInit = { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER };
16916
- return fetch(input, requestInit);
16963
+ function opencodeFetch(dispatcher) {
16964
+ return (input, init) => {
16965
+ const requestInit = { ...init, dispatcher };
16966
+ return fetch(input, requestInit);
16967
+ };
16917
16968
  }
16918
16969
  var OpencodeManager = class extends CodingAgentManager {
16919
16970
  client = null;
16920
16971
  server = null;
16972
+ dispatcher = null;
16921
16973
  sessionId = null;
16922
16974
  historyFilePath;
16923
16975
  historyFile;
@@ -16935,6 +16987,9 @@ var OpencodeManager = class extends CodingAgentManager {
16935
16987
  activeLinearForwarder = null;
16936
16988
  forwardedLinearPartKeys = /* @__PURE__ */ new Set();
16937
16989
  handledAccountRateLimit = false;
16990
+ credentialSwitch = null;
16991
+ /** OpenCode reads its key when the server starts, so a server started on another credential has to restart. */
16992
+ serverCredential;
16938
16993
  modelVariants = /* @__PURE__ */ new Map();
16939
16994
  slashCommandsCache = null;
16940
16995
  slashCommandsRequest = null;
@@ -17025,18 +17080,16 @@ var OpencodeManager = class extends CodingAgentManager {
17025
17080
  try {
17026
17081
  if (reuseExisting && this.client) return this.client;
17027
17082
  const subagentMcpParentChatId = this.subagentMcpParentChatId();
17028
- if (this.client && (this.providerId !== providerId || this.serverSubagentMcpParentChatId !== subagentMcpParentChatId)) {
17029
- this.eventAbortController?.abort();
17030
- this.server?.close();
17083
+ if (this.client && (this.providerId !== providerId || this.serverSubagentMcpParentChatId !== subagentMcpParentChatId || this.serverCredential !== ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.opencode)) {
17084
+ this.stopServer();
17031
17085
  this.client = null;
17032
- this.server = null;
17033
17086
  this.configuredModels.clear();
17034
17087
  }
17035
17088
  this.providerId = providerId;
17036
17089
  const configuredModel = providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
17037
17090
  const ollama = providerId === "ollama" ? readOllamaAuthEnv("opencode", process.env) : null;
17038
17091
  const compatible = providerId === CREDENTIAL_METHOD.OPENAI_COMPATIBLE ? readOpenAICompatibleAuthEnv("opencode", process.env) : null;
17039
- const providerModels = ollama ? (await listOllamaModels(ollama)).map(({ id }) => id) : compatible ? (await listOpenAICompatibleModels(compatible)).map(({ id }) => id) : providerId === OPENCODE_GO_PROVIDER ? Object.keys((await fetchOpenCodeGoCatalog()).models) : providerId === ASTER_PROVIDER ? ASTER_MODELS : await getAllowedOpenRouterModels();
17092
+ const providerModels = ollama ? (await listOllamaModels(ollama)).map(({ id }) => id) : compatible ? (await listOpenAICompatibleModels(compatible)).map(({ id }) => id) : providerId === OPENCODE_GO_PROVIDER ? Object.keys((await fetchOpenCodeGoCatalog("opencode")).models) : providerId === ASTER_PROVIDER ? ASTER_MODELS : await getAllowedOpenRouterModels();
17040
17093
  if (!providerModels.includes(configuredModel)) {
17041
17094
  throw new Error(`Model ${configuredModel} is not available through ${providerId}.`);
17042
17095
  }
@@ -17055,8 +17108,7 @@ var OpencodeManager = class extends CodingAgentManager {
17055
17108
  process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
17056
17109
  process.env.REPLICAS_POST_TOOL_PR_HOOK_PATH = getPostToolPrHookPath();
17057
17110
  process.env.REPLICAS_COMMAND_PROTECTION_RUNTIME_PATH = process.execPath;
17058
- this.eventAbortController?.abort();
17059
- this.server?.close();
17111
+ this.stopServer();
17060
17112
  const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
17061
17113
  if (!pathEntries.includes(OPENCODE_SHIM_DIR)) {
17062
17114
  process.env.PATH = [OPENCODE_SHIM_DIR, ...pathEntries].join(delimiter);
@@ -17079,6 +17131,7 @@ var OpencodeManager = class extends CodingAgentManager {
17079
17131
  };
17080
17132
  }
17081
17133
  if (this.originChatId) process.env.REPLICAS_CHAT_ID = this.originChatId;
17134
+ const credential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.opencode;
17082
17135
  const serverStart = createOpencodeServer({
17083
17136
  port: 0,
17084
17137
  timeout: OPENCODE_SERVER_STARTUP_TIMEOUT_MS,
@@ -17086,16 +17139,19 @@ var OpencodeManager = class extends CodingAgentManager {
17086
17139
  });
17087
17140
  delete process.env.REPLICAS_CHAT_ID;
17088
17141
  const server3 = await serverStart;
17142
+ const dispatcher = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
17089
17143
  const client = createOpencodeClient({
17090
17144
  baseUrl: server3.url,
17091
- fetch: opencodeFetch,
17145
+ fetch: opencodeFetch(dispatcher),
17092
17146
  headers: {
17093
17147
  Authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
17094
17148
  }
17095
17149
  });
17096
17150
  this.client = client;
17097
17151
  this.server = server3;
17152
+ this.dispatcher = dispatcher;
17098
17153
  this.serverSubagentMcpParentChatId = subagentMcpParentChatId;
17154
+ this.serverCredential = credential;
17099
17155
  this.configuredModels = new Set(providerModels);
17100
17156
  const eventController = new AbortController();
17101
17157
  this.eventAbortController = eventController;
@@ -17117,7 +17173,14 @@ var OpencodeManager = class extends CodingAgentManager {
17117
17173
  finishUpdate();
17118
17174
  }
17119
17175
  }
17120
- async ensureSession(client, model, agent, variant) {
17176
+ stopServer() {
17177
+ this.eventAbortController?.abort();
17178
+ this.server?.close();
17179
+ void this.dispatcher?.destroy();
17180
+ this.server = null;
17181
+ this.dispatcher = null;
17182
+ }
17183
+ async ensureSession(client, providerID, model, agent, variant) {
17121
17184
  if (this.sessionId) {
17122
17185
  try {
17123
17186
  await client.session.get(
@@ -17132,79 +17195,97 @@ var OpencodeManager = class extends CodingAgentManager {
17132
17195
  const result = await client.session.create({
17133
17196
  directory: this.workingDirectory,
17134
17197
  agent,
17135
- model: { providerID: this.providerId, id: model, ...variant ? { variant } : {} }
17198
+ model: { providerID, id: model, ...variant ? { variant } : {} }
17136
17199
  }, { throwOnError: true });
17137
17200
  const session = result.data;
17138
17201
  this.sessionId = session.id;
17139
17202
  await this.onSaveSessionId(session.id);
17140
17203
  return session.id;
17141
17204
  }
17142
- async getModelVariants(client, model) {
17143
- const cached = this.modelVariants.get(model);
17205
+ async getModelVariants(client, providerID, model) {
17206
+ const key = `${providerID}/${model}`;
17207
+ const cached = this.modelVariants.get(key);
17144
17208
  if (cached) return cached;
17145
17209
  try {
17146
- for (const [id, variants] of await fetchOpencodeModelVariants(client, this.workingDirectory, this.providerId)) {
17147
- this.modelVariants.set(id, variants);
17210
+ for (const [id, variants] of await fetchOpencodeModelVariants(client, this.workingDirectory, providerID)) {
17211
+ this.modelVariants.set(`${providerID}/${id}`, variants);
17148
17212
  }
17149
17213
  } catch {
17150
- this.modelVariants.set(model, /* @__PURE__ */ new Set());
17214
+ this.modelVariants.set(key, /* @__PURE__ */ new Set());
17151
17215
  }
17152
- return this.modelVariants.get(model) ?? /* @__PURE__ */ new Set();
17216
+ return this.modelVariants.get(key) ?? /* @__PURE__ */ new Set();
17153
17217
  }
17154
- async getThinkingVariant(client, model, thinkingLevel) {
17218
+ async getThinkingVariant(client, providerID, model, thinkingLevel) {
17155
17219
  if (!thinkingLevel) return void 0;
17156
- return opencodeThinkingVariant(await this.getModelVariants(client, model), thinkingLevel);
17220
+ return opencodeThinkingVariant(await this.getModelVariants(client, providerID, model), thinkingLevel);
17157
17221
  }
17158
- async processMessageInternal(request) {
17159
- const provider = await getOpencodeProvider();
17160
- const controller = new AbortController();
17222
+ async processMessageInternal(request, signal) {
17161
17223
  const linearForwarder = new LinearEventForwarder(() => this.linearSessionId);
17162
- this.handledAccountRateLimit = false;
17163
- this.activeAbortController = controller;
17164
17224
  this.activeLinearForwarder = linearForwarder;
17165
17225
  this.forwardedLinearPartKeys.clear();
17226
+ this.credentialSwitch = null;
17227
+ try {
17228
+ let retry = false;
17229
+ while (await this.promptTurn(request, linearForwarder, signal, retry)) retry = true;
17230
+ } finally {
17231
+ this.activeAbortController = null;
17232
+ this.activeLinearForwarder = null;
17233
+ this.forwardedLinearPartKeys.clear();
17234
+ await this.historyFile.flush();
17235
+ await this.onTurnComplete();
17236
+ }
17237
+ }
17238
+ async promptTurn(request, linearForwarder, signal, retry) {
17239
+ const controller = new AbortController();
17240
+ this.handledAccountRateLimit = false;
17241
+ this.activeAbortController = controller;
17242
+ if (signal.aborted) controller.abort();
17166
17243
  try {
17244
+ controller.signal.throwIfAborted();
17245
+ const provider = await getOpencodeProvider();
17167
17246
  const model = request.model ?? await getDefaultOpencodeModel(provider);
17168
17247
  const client = await this.ensureClient(model, provider);
17169
- const providerModel = this.providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
17248
+ const { providerID, modelID } = opencodeModelRef(this.providerId, model);
17170
17249
  const agent = request.planMode ? "plan" : "build";
17171
- const variant = await this.getThinkingVariant(client, providerModel, request.thinkingLevel);
17172
- const sessionId = await this.ensureSession(client, providerModel, agent, variant);
17250
+ const variant = await this.getThinkingVariant(client, providerID, modelID, request.thinkingLevel);
17251
+ const sessionId = await this.ensureSession(client, providerID, modelID, agent, variant);
17173
17252
  const system = this.buildCombinedInstructions(request.customInstructions);
17174
- this.recordUserMessageHistoryEvent(request, this.historyFile);
17253
+ if (!retry) this.recordUserMessageHistoryEvent(request, this.historyFile);
17175
17254
  const result = await client.session.prompt({
17176
17255
  sessionID: sessionId,
17177
17256
  directory: this.workingDirectory,
17178
17257
  agent,
17179
- model: { providerID: this.providerId, modelID: providerModel },
17258
+ model: { providerID, modelID },
17180
17259
  ...variant ? { variant } : {},
17181
17260
  ...system ? { system } : {},
17182
17261
  ...this.subagentMcpParentChatId() ? { tools: { task: false } } : {},
17183
- parts: [{ type: "text", text: request.message }]
17262
+ parts: [{ type: "text", text: retry ? OPENCODE_SWITCHED_CREDENTIAL_PROMPT : request.message }]
17184
17263
  }, { signal: controller.signal, throwOnError: true });
17185
17264
  this.recordHistoryEvent("opencode-message.updated", { info: result.data.info }, this.historyFile);
17186
17265
  for (const part of result.data.parts) this.recordOpencodePart(part);
17187
17266
  linearForwarder.flushThoughtAsResponse();
17188
17267
  this.recordHistoryEvent("opencode-session.idle", { sessionID: sessionId }, this.historyFile);
17189
17268
  } catch (error) {
17269
+ const credentialSwitch = this.credentialSwitch;
17270
+ if (credentialSwitch) {
17271
+ this.credentialSwitch = null;
17272
+ await credentialSwitch.catch(() => {
17273
+ });
17274
+ if (!signal.aborted) return true;
17275
+ }
17190
17276
  if (controller.signal.aborted) {
17191
17277
  this.recordHistoryEvent("opencode-session.idle", { sessionID: this.sessionId }, this.historyFile);
17192
17278
  } else {
17193
17279
  console.error("[OpencodeManager] Turn failed:", error);
17194
17280
  this.recordHistoryEvent("opencode-error", opencodeErrorPayload(error), this.historyFile);
17195
17281
  }
17196
- } finally {
17197
- this.activeAbortController = null;
17198
- this.activeLinearForwarder = null;
17199
- this.forwardedLinearPartKeys.clear();
17200
- await this.historyFile.flush();
17201
- await this.onTurnComplete();
17202
17282
  }
17283
+ return false;
17203
17284
  }
17204
17285
  async subscribeToEvents(client, controller) {
17205
17286
  let streamError;
17206
- const result = await client.v2.event.subscribe(
17207
- { location: { directory: this.workingDirectory } },
17287
+ const result = await client.event.subscribe(
17288
+ { directory: this.workingDirectory },
17208
17289
  {
17209
17290
  signal: controller.signal,
17210
17291
  sseMaxRetryAttempts: 1,
@@ -17225,6 +17306,7 @@ var OpencodeManager = class extends CodingAgentManager {
17225
17306
  if (!controller.signal.aborted && streamError) throw streamError;
17226
17307
  }
17227
17308
  recordOpencodeEvent(event) {
17309
+ if (!/^(session|message)\./.test(event.type)) return;
17228
17310
  const payload = opencodeEventPayload(event);
17229
17311
  if (!payload) return;
17230
17312
  const sessionID = typeof payload.sessionID === "string" ? payload.sessionID : void 0;
@@ -17251,8 +17333,15 @@ var OpencodeManager = class extends CodingAgentManager {
17251
17333
  if (status?.type !== "retry" || action?.reason !== "account_rate_limit" || this.handledAccountRateLimit) return;
17252
17334
  this.handledAccountRateLimit = true;
17253
17335
  const message = typeof status.message === "string" ? status.message : typeof action.message === "string" ? action.message : "OpenCode Go usage limit reached.";
17336
+ const controller = this.activeAbortController;
17337
+ const fallback = await this.fallBackCredential("rate_limit", message, this.serverCredential);
17338
+ if (!controller || controller.signal.aborted || controller !== this.activeAbortController) return;
17339
+ if (fallback.outcome === "switched") {
17340
+ this.credentialSwitch = this.abortActiveTurn();
17341
+ await this.credentialSwitch;
17342
+ return;
17343
+ }
17254
17344
  this.recordHistoryEvent("opencode-error", { message, code: "account_rate_limit" }, this.historyFile);
17255
- const fallback = await this.fallBackCredential("rate_limit", message);
17256
17345
  this.emitQuotaExhausted({
17257
17346
  provider: "opencode",
17258
17347
  reason: "rate_limit",
@@ -58088,7 +58177,7 @@ var PiManager = class extends CodingAgentManager {
58088
58177
  }))
58089
58178
  });
58090
58179
  } else if (this.providerId === OPENCODE_GO_PROVIDER) {
58091
- const catalog = await fetchOpenCodeGoCatalog([request.model ?? DEFAULT_OPENCODE_GO_MODEL]);
58180
+ const catalog = await fetchOpenCodeGoCatalog("pi", [request.model ?? DEFAULT_OPENCODE_GO_MODEL]);
58092
58181
  const models = Object.entries(catalog.models);
58093
58182
  defaultModel = catalog.models[DEFAULT_OPENCODE_GO_MODEL] ? DEFAULT_OPENCODE_GO_MODEL : models[0]?.[0] ?? DEFAULT_OPENCODE_GO_MODEL;
58094
58183
  registerPiProvider(modelRegistry, {
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  publishedPullRequestUrls
6
- } from "./chunk-YD76ZL7D.js";
6
+ } from "./chunk-THAFUTL3.js";
7
7
  import {
8
8
  isRecord
9
9
  } from "./chunk-UZSNFLDQ.js";
10
10
  import {
11
11
  shouldRefreshPullRequests
12
- } from "./chunk-47JJX56J.js";
12
+ } from "./chunk-5YU4KQIU.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/services/post-tool-pr-notifier.ts
@@ -11,7 +11,7 @@ import {
11
11
  spawnSubagentRequestSchema,
12
12
  subagentResponseSchema,
13
13
  waitSubagentRequestSchema
14
- } from "./chunk-47JJX56J.js";
14
+ } from "./chunk-5YU4KQIU.js";
15
15
  import "./chunk-VEQXQN22.js";
16
16
 
17
17
  // src/subagent-mcp.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.1279",
3
+ "version": "0.1.1281",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -49,11 +49,11 @@
49
49
  "@earendil-works/pi-server": "0.85.0",
50
50
  "@hono/node-server": "^1.19.5",
51
51
  "@modelcontextprotocol/sdk": "^1.29.0",
52
- "@opencode-ai/sdk": "1.17.9",
52
+ "@opencode-ai/sdk": "1.19.4",
53
53
  "bash-language-server": "^5.6.0",
54
54
  "dockerfile-language-server-nodejs": "^0.14.1",
55
55
  "hono": "^4.10.3",
56
- "opencode-ai": "1.17.9",
56
+ "opencode-ai": "1.19.4",
57
57
  "pi-mcp-adapter": "2.32.1",
58
58
  "pyright": "^1.1.413",
59
59
  "typescript": "^5.9.3",