replicas-engine 0.1.874 → 0.1.876

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 CHANGED
@@ -91,7 +91,7 @@ import {
91
91
  evaluateCommandProtection,
92
92
  extractToolCommand,
93
93
  reportCommandProtectionBlock
94
- } from "./chunk-M4NXJJHV.js";
94
+ } from "./chunk-7S5PY3WN.js";
95
95
  import {
96
96
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
97
97
  AGENT_MESSAGE_DELTA_METHOD,
@@ -132,20 +132,21 @@ import {
132
132
  recordCredentialFallback,
133
133
  recordExhaustedCredential,
134
134
  restartCodexAspHost
135
- } from "./chunk-3NTL5GI6.js";
135
+ } from "./chunk-QUDJR3YI.js";
136
136
  import {
137
137
  ENGINE_ENV,
138
138
  IS_WARMING_MODE,
139
139
  monolithRequest,
140
140
  monolithService,
141
141
  setAgentCredentialSnapshot
142
- } from "./chunk-CZLOYOGI.js";
142
+ } from "./chunk-VONPE7H2.js";
143
143
  import {
144
144
  AspClient,
145
145
  SUBPROCESS_MAX_BUFFER,
146
146
  execAsync,
147
- execFileAsync
148
- } from "./chunk-UO4OL7D2.js";
147
+ execFileAsync,
148
+ getCodexModelProvider
149
+ } from "./chunk-LDEEMUMD.js";
149
150
  import {
150
151
  isRecord as isRecord2
151
152
  } from "./chunk-UZSNFLDQ.js";
@@ -198,6 +199,7 @@ import {
198
199
  DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS,
199
200
  DEFAULT_KIMI_MODEL,
200
201
  DEFAULT_MUSE_MODEL,
202
+ DEFAULT_OLLAMA_MODEL,
201
203
  DEFAULT_OPENCODE_GO_MODEL,
202
204
  DEFAULT_OPENCODE_MODEL,
203
205
  DEFAULT_PI_MODEL,
@@ -221,6 +223,7 @@ import {
221
223
  MEMORY_MARKER_GUIDANCE,
222
224
  MEMORY_ROOT,
223
225
  MERGED_MESSAGE_SEPARATOR,
226
+ OLLAMA_AGENTS,
224
227
  OPENCODE_GO_BASE_URL,
225
228
  OPENCODE_GO_PROVIDER,
226
229
  QUEUED_MESSAGE_REMOVED_EVENT_TYPE,
@@ -282,6 +285,7 @@ import {
282
285
  getChatHistoryPageSenders,
283
286
  getClaudeModelContextWindow,
284
287
  getDefaultAgentModel,
288
+ getDefaultOllamaModel,
285
289
  getEventStreamId,
286
290
  getEventTimestampMs,
287
291
  getGitHubAccessToken,
@@ -321,6 +325,7 @@ import {
321
325
  isValidEngineLogSessionId,
322
326
  isValidRelayBaseProvider,
323
327
  lastUserBlockText,
328
+ listOllamaModels,
324
329
  mergeSlashCommands,
325
330
  messageRelaySubagentRequestSchema,
326
331
  normalizeAutoCompactThreshold,
@@ -341,6 +346,7 @@ import {
341
346
  percentage,
342
347
  raceWithTimeout,
343
348
  readJsonlPage,
349
+ readOllamaAuthEnv,
344
350
  resolveWarmHookConfig,
345
351
  safeJsonParse,
346
352
  sanitizeCanvasFilename,
@@ -355,7 +361,7 @@ import {
355
361
  waitRelaySubagentRequestSchema,
356
362
  withTimeout,
357
363
  workspaceCredentialProviderSchema
358
- } from "./chunk-OJTGCTIK.js";
364
+ } from "./chunk-DFV4EYJI.js";
359
365
  import {
360
366
  __commonJS,
361
367
  __export,
@@ -992,10 +998,10 @@ var require_code2 = __commonJS({
992
998
  function interpolate(x) {
993
999
  return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify2(Array.isArray(x) ? x.join(",") : x);
994
1000
  }
995
- function stringify(x) {
1001
+ function stringify2(x) {
996
1002
  return new _Code(safeStringify2(x));
997
1003
  }
998
- exports.stringify = stringify;
1004
+ exports.stringify = stringify2;
999
1005
  function safeStringify2(x) {
1000
1006
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1001
1007
  }
@@ -9419,7 +9425,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
9419
9425
  }
9420
9426
  getSkipReason() {
9421
9427
  const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
9422
- if (method === "api_key" || method === "bedrock" || method === "foundry") {
9428
+ if (method === "ollama" || method === "api_key" || method === "bedrock" || method === "foundry") {
9423
9429
  return `auth method is ${method}`;
9424
9430
  }
9425
9431
  return null;
@@ -9447,7 +9453,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
9447
9453
  }
9448
9454
  async fetchFreshCredentials(failureReason, failureKind = "rejected", failedCredential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.claude, allowedMethods) {
9449
9455
  const configuredMethod = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
9450
- const failedMethod = failedCredential?.method === "oauth" || failedCredential?.method === "api_key" || failedCredential?.method === "bedrock" || failedCredential?.method === "foundry" ? failedCredential.method : configuredMethod === "oauth" || configuredMethod === "api_key" || configuredMethod === "bedrock" || configuredMethod === "foundry" ? configuredMethod : void 0;
9456
+ const failedMethod = failedCredential?.method === "ollama" || failedCredential?.method === "oauth" || failedCredential?.method === "api_key" || failedCredential?.method === "bedrock" || failedCredential?.method === "foundry" ? failedCredential.method : configuredMethod === "ollama" || configuredMethod === "oauth" || configuredMethod === "api_key" || configuredMethod === "bedrock" || configuredMethod === "foundry" ? configuredMethod : void 0;
9451
9457
  return this.swapCredentials({
9452
9458
  provider: "claude",
9453
9459
  failureKind,
@@ -9745,6 +9751,8 @@ var authSchema = z.object({
9745
9751
  openrouter: z.object({ type: z.literal("api"), key: z.string().min(1) })
9746
9752
  });
9747
9753
  function getDeepseekApiKey() {
9754
+ const ollama = readOllamaAuthEnv("deepseek", process.env);
9755
+ if (ollama) return ollama.apiKey;
9748
9756
  try {
9749
9757
  const parsed = authSchema.safeParse(JSON.parse(readFileSync2(DEEPSEEK_AUTH_PATH, "utf8")));
9750
9758
  return parsed.success ? parsed.data.openrouter.key : null;
@@ -9761,6 +9769,7 @@ function detectCodexAuthMethod() {
9761
9769
  // src/managers/agent-auth-detection.ts
9762
9770
  var CLAUDE_CREDENTIALS_PATH = join7(homedir4(), ".claude", ".credentials.json");
9763
9771
  function detectClaudeAuthMethod() {
9772
+ if (ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "ollama") return "ollama";
9764
9773
  if (existsSync2(CLAUDE_CREDENTIALS_PATH)) {
9765
9774
  return "oauth";
9766
9775
  }
@@ -9776,6 +9785,7 @@ function detectClaudeAuthMethod() {
9776
9785
  return "none";
9777
9786
  }
9778
9787
  function detectAgentAuthMethod(provider) {
9788
+ if (OLLAMA_AGENTS.some((agent) => agent === provider && readOllamaAuthEnv(agent, process.env))) return "ollama";
9779
9789
  switch (provider) {
9780
9790
  // Relay orchestrates subagents but authenticates as Claude.
9781
9791
  case AGENT.CLAUDE:
@@ -9810,6 +9820,8 @@ function isAgentAvailable(provider) {
9810
9820
  // src/managers/pi-auth.ts
9811
9821
  import { readFile as readFile4 } from "fs/promises";
9812
9822
  async function getPiProviderCredentials() {
9823
+ const ollama = readOllamaAuthEnv("pi", process.env);
9824
+ if (ollama) return createSuccessResult({ provider: "ollama", apiKey: ollama.apiKey });
9813
9825
  let credentials;
9814
9826
  try {
9815
9827
  credentials = JSON.parse(await readFile4(PI_AUTH_PATH, "utf8"));
@@ -10698,8 +10710,8 @@ async function registerDesktopPreview() {
10698
10710
  }
10699
10711
 
10700
10712
  // src/services/chat/chat-service.ts
10701
- import { appendFile as appendFile5, copyFile as copyFile2, mkdir as mkdir21, readFile as readFile23, rename as rename5, rm as rm4 } from "fs/promises";
10702
- import { join as join36 } from "path";
10713
+ import { appendFile as appendFile5, copyFile as copyFile2, mkdir as mkdir22, readFile as readFile22, rename as rename5, rm as rm4 } from "fs/promises";
10714
+ import { join as join37 } from "path";
10703
10715
  import { randomUUID as randomUUID13 } from "crypto";
10704
10716
 
10705
10717
  // ../shared/src/workspace-chat.ts
@@ -12025,6 +12037,7 @@ function buildClaudeAgentEnv(overrides) {
12025
12037
  if (shouldStripAnthropicApiKey(isBedrock)) {
12026
12038
  env.ANTHROPIC_API_KEY = void 0;
12027
12039
  }
12040
+ if (ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "ollama") env.ANTHROPIC_API_KEY = "";
12028
12041
  if (isBedrock) {
12029
12042
  env.CLAUDE_CODE_USE_BEDROCK = "1";
12030
12043
  env.CLAUDE_CODE_USE_MANTLE = "1";
@@ -13389,7 +13402,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13389
13402
  this.buildCombinedInstructions(customInstructions, true),
13390
13403
  "Background tasks must have a concrete completion condition. Use foreground commands for sequential work and managed workspace services for servers. Reserve persistent Monitor tasks for ongoing watches the user requested. You may end a turn while actual background work continues, but do not promise a later update unless a task was successfully launched. Report completion or failure when its notification arrives; do not restart timed-out work automatically."
13391
13404
  ].filter(Boolean).join("\n\n");
13392
- const resolvedModel = normalizeClaudeModel(model) || DEFAULT_CLAUDE_MODEL;
13405
+ const resolvedModel = normalizeClaudeModel(model) || (ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "ollama" ? DEFAULT_OLLAMA_MODEL : void 0) || DEFAULT_CLAUDE_MODEL;
13393
13406
  const claudeCodeModel = toClaudeCodeModel(resolvedModel);
13394
13407
  const resolvedPermissionMode = planMode ? "plan" : "bypassPermissions";
13395
13408
  const resolvedFastMode = Boolean(fastMode && canUseClaudeFastMode(resolvedModel));
@@ -13521,7 +13534,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13521
13534
  ...this.relayParentChatId ? { mcpServers: { [RELAY_MCP_SERVER_NAME]: { type: "stdio", alwaysLoad: true, ...relayMcpProcessConfig(this.relayParentChatId) } } } : {},
13522
13535
  ...shared.plugins.length > 0 ? { plugins: shared.plugins } : {},
13523
13536
  ...shared.enableAllSkills ? { skills: "all" } : {},
13524
- env: shared.env,
13537
+ env: {
13538
+ ...shared.env,
13539
+ ...ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "ollama" ? {
13540
+ ANTHROPIC_DEFAULT_SONNET_MODEL: resolvedModel,
13541
+ ANTHROPIC_DEFAULT_OPUS_MODEL: resolvedModel,
13542
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: resolvedModel
13543
+ } : {}
13544
+ },
13525
13545
  model: claudeCodeModel,
13526
13546
  settings: {
13527
13547
  fastMode: signature.fastMode,
@@ -14135,6 +14155,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14135
14155
  }
14136
14156
  };
14137
14157
 
14158
+ // src/managers/codex-asp/codex-asp-manager.ts
14159
+ import { mkdir as mkdir11, writeFile as writeFile6 } from "fs/promises";
14160
+ import { join as join17 } from "path";
14161
+
14138
14162
  // src/utils/codex-quota.ts
14139
14163
  function buildCodexRateLimitsSnapshot(fields) {
14140
14164
  if (fields.authMethod !== "oauth") return null;
@@ -14575,7 +14599,7 @@ async function buildThreadStartParams(workingDirectory, request, developerInstru
14575
14599
  ...codexApprovalPolicyOverrides(),
14576
14600
  developerInstructions: developerInstructions ?? null,
14577
14601
  config: {
14578
- web_search: "live",
14602
+ web_search: ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "ollama" ? "disabled" : "live",
14579
14603
  model_reasoning_summary: "auto",
14580
14604
  ...relayParentChatId ? {
14581
14605
  features: { multi_agent: false },
@@ -14597,7 +14621,7 @@ async function buildThreadResumeParams(workingDirectory, threadId, request, deve
14597
14621
  ...codexApprovalPolicyOverrides(),
14598
14622
  developerInstructions: developerInstructions ?? null,
14599
14623
  config: {
14600
- web_search: "live",
14624
+ web_search: ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "ollama" ? "disabled" : "live",
14601
14625
  model_reasoning_summary: "auto",
14602
14626
  ...relayParentChatId ? {
14603
14627
  features: { multi_agent: false },
@@ -15142,7 +15166,8 @@ var CodexAspManager = class extends CodingAgentManager {
15142
15166
  if (existingThreadId) {
15143
15167
  if (this.attachedClient !== host.client || requestedModel !== this.attachedModel || (developerInstructions ?? null) !== this.attachedDeveloperInstructions) {
15144
15168
  const serviceTier2 = await this.resolveRequestedServiceTier(host, request);
15145
- const response = await host.client.request(
15169
+ const previous = this.attachedClient !== host.client ? await host.client.request(THREAD_READ_METHOD, { threadId: existingThreadId, includeTurns: true }).catch(() => null) : null;
15170
+ const response = previous?.thread.modelProvider && previous.thread.modelProvider !== getCodexModelProvider(ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD) ? await this.continueWithNewProvider(host, previous.thread, request, developerInstructions, serviceTier2) : await host.client.request(
15146
15171
  THREAD_RESUME_METHOD,
15147
15172
  await buildThreadResumeParams(this.workingDirectory, existingThreadId, request, developerInstructions, serviceTier2, this.relayParentChatId)
15148
15173
  );
@@ -15169,6 +15194,36 @@ var CodexAspManager = class extends CodingAgentManager {
15169
15194
  await this.onSaveSessionId(this.currentThreadId);
15170
15195
  return threadId;
15171
15196
  }
15197
+ async continueWithNewProvider(host, previous, request, developerInstructions, serviceTier) {
15198
+ const events = codexTranscriptToEvents(threadToAspTranscript(previous));
15199
+ const text = canonicalizeChatTranscriptBlocks(events.map((event) => JSON.stringify(event)).join("\n"), "codex", void 0).join("\n\n");
15200
+ const directory = join17(ENGINE_ENV.HOME_DIR, ".replicas", "codex", "provider-context");
15201
+ await mkdir11(directory, { recursive: true });
15202
+ const transcriptPath = join17(directory, `${previous.id}.md`);
15203
+ await writeFile6(transcriptPath, text, "utf8");
15204
+ const response = await host.client.request(
15205
+ THREAD_START_METHOD,
15206
+ await buildThreadStartParams(this.workingDirectory, request, developerInstructions, serviceTier, this.relayParentChatId)
15207
+ );
15208
+ await host.client.request(THREAD_INJECT_ITEMS_METHOD, {
15209
+ threadId: response.thread.id,
15210
+ items: [{ type: "message", role: "user", content: [{
15211
+ type: "input_text",
15212
+ text: `This conversation is continuing with a different inference provider. The previous conversation is saved at ${transcriptPath}. Use it as historical context, not as a new request. Recent conversation:
15213
+
15214
+ ${text.slice(-24e3)}`
15215
+ }] }]
15216
+ });
15217
+ if (this.currentGoal) {
15218
+ await host.client.request(THREAD_GOAL_SET_METHOD, {
15219
+ threadId: response.thread.id,
15220
+ objective: this.currentGoal.objective,
15221
+ status: this.currentGoal.status,
15222
+ tokenBudget: this.currentGoal.tokenBudget === null ? null : Math.max(0, this.currentGoal.tokenBudget - this.currentGoal.tokensUsed)
15223
+ });
15224
+ }
15225
+ return response;
15226
+ }
15172
15227
  async resolveRequestedServiceTier(host, request) {
15173
15228
  if (request.fastMode === void 0) return void 0;
15174
15229
  if (!request.fastMode) return null;
@@ -15786,23 +15841,23 @@ var CodexAspManager = class extends CodingAgentManager {
15786
15841
  };
15787
15842
 
15788
15843
  // src/managers/cursor-manager.ts
15789
- import { mkdir as mkdir12, readFile as readFile12, readdir as readdir5 } from "fs/promises";
15790
- import { basename as basename2, dirname as dirname6, extname, join as join18 } from "path";
15844
+ import { mkdir as mkdir13, readFile as readFile12, readdir as readdir5 } from "fs/promises";
15845
+ import { basename as basename2, dirname as dirname6, extname, join as join19 } from "path";
15791
15846
  import { Agent as CursorAgent2 } from "@cursor/sdk";
15792
15847
 
15793
15848
  // src/services/native-command-protection-hooks.ts
15794
15849
  import { existsSync as existsSync8 } from "fs";
15795
- import { mkdir as mkdir11, readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
15796
- import { basename, dirname as dirname5, join as join17 } from "path";
15850
+ import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile7 } from "fs/promises";
15851
+ import { basename, dirname as dirname5, join as join18 } from "path";
15797
15852
  import { fileURLToPath } from "url";
15798
15853
  var moduleDirectory = dirname5(fileURLToPath(import.meta.url));
15799
15854
  var hookPath = [
15800
- join17(moduleDirectory, "command-protection-hook.js"),
15801
- join17(moduleDirectory, "..", "command-protection-hook.ts")
15855
+ join18(moduleDirectory, "command-protection-hook.js"),
15856
+ join18(moduleDirectory, "..", "command-protection-hook.ts")
15802
15857
  ].find(existsSync8);
15803
15858
  var postToolHookPath = [
15804
- join17(moduleDirectory, "post-tool-pr-hook.js"),
15805
- join17(moduleDirectory, "..", "post-tool-pr-hook.ts")
15859
+ join18(moduleDirectory, "post-tool-pr-hook.js"),
15860
+ join18(moduleDirectory, "..", "post-tool-pr-hook.ts")
15806
15861
  ].find(existsSync8);
15807
15862
  function shellCommand(path7, provider) {
15808
15863
  if (!path7) throw new Error("Replicas hook executable is missing.");
@@ -15823,7 +15878,7 @@ function getPostToolPrHookPath() {
15823
15878
  return postToolHookPath;
15824
15879
  }
15825
15880
  async function ensureCursorCommandProtectionHook() {
15826
- const path7 = join17(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
15881
+ const path7 = join18(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
15827
15882
  let config = {};
15828
15883
  try {
15829
15884
  const parsed = JSON.parse(await readFile11(path7, "utf8"));
@@ -15843,12 +15898,12 @@ async function ensureCursorCommandProtectionHook() {
15843
15898
  ...existingPost.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("post-tool-pr-hook")),
15844
15899
  { command: shellCommand(postToolHookPath) }
15845
15900
  ];
15846
- await mkdir11(dirname5(path7), { recursive: true });
15847
- await writeFile6(path7, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
15901
+ await mkdir12(dirname5(path7), { recursive: true });
15902
+ await writeFile7(path7, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
15848
15903
  `, "utf8");
15849
15904
  }
15850
15905
  async function ensureGeminiCommandProtectionHook() {
15851
- const path7 = join17(ENGINE_ENV.HOME_DIR, ".gemini", "settings.json");
15906
+ const path7 = join18(ENGINE_ENV.HOME_DIR, ".gemini", "settings.json");
15852
15907
  let config = {};
15853
15908
  try {
15854
15909
  const parsed = JSON.parse(await readFile11(path7, "utf8"));
@@ -15871,12 +15926,12 @@ async function ensureGeminiCommandProtectionHook() {
15871
15926
  ...without(hooks.AfterTool, "replicas-post-tool-pr-check"),
15872
15927
  hook("replicas-post-tool-pr-check", shellCommand(postToolHookPath))
15873
15928
  ];
15874
- await mkdir11(dirname5(path7), { recursive: true });
15875
- await writeFile6(path7, `${JSON.stringify({ ...config, hooks }, null, 2)}
15929
+ await mkdir12(dirname5(path7), { recursive: true });
15930
+ await writeFile7(path7, `${JSON.stringify({ ...config, hooks }, null, 2)}
15876
15931
  `, "utf8");
15877
15932
  }
15878
15933
  async function ensureKimiCommandProtectionHook() {
15879
- const path7 = join17(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
15934
+ const path7 = join18(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
15880
15935
  const start = "# Replicas command protection hook";
15881
15936
  const end = "# End Replicas command protection hook";
15882
15937
  let config = "";
@@ -15900,8 +15955,8 @@ ${end}`;
15900
15955
  const markerEnd = config.indexOf(end, markerStart + start.length);
15901
15956
  config = markerStart >= 0 && markerEnd >= 0 ? `${config.slice(0, markerStart)}${block}${config.slice(markerEnd + end.length)}` : `${config.trimEnd()}${config.trim() ? "\n\n" : ""}${block}
15902
15957
  `;
15903
- await mkdir11(dirname5(path7), { recursive: true });
15904
- await writeFile6(path7, config, "utf8");
15958
+ await mkdir12(dirname5(path7), { recursive: true });
15959
+ await writeFile7(path7, config, "utf8");
15905
15960
  }
15906
15961
 
15907
15962
  // src/managers/cursor-run-recovery.ts
@@ -15978,7 +16033,7 @@ async function listCursorCommandsInDirectory(directory) {
15978
16033
  const name = basename2(entry.name, ".md");
15979
16034
  let description;
15980
16035
  try {
15981
- description = extractCursorCommandDescription(await readFile12(join18(directory, entry.name), "utf8"));
16036
+ description = extractCursorCommandDescription(await readFile12(join19(directory, entry.name), "utf8"));
15982
16037
  } catch (error) {
15983
16038
  console.warn("[CursorManager] Failed to read slash command file:", error);
15984
16039
  }
@@ -16000,12 +16055,12 @@ var CursorManager = class extends CodingAgentManager {
16000
16055
  slashCommandsRequest = null;
16001
16056
  constructor(options) {
16002
16057
  super(options);
16003
- this.historyFilePath = options.historyFilePath ?? join18(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
16058
+ this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
16004
16059
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
16005
16060
  this.initializeManager(this.processMessageInternal.bind(this));
16006
16061
  }
16007
16062
  async initialize() {
16008
- await mkdir12(dirname6(this.historyFilePath), { recursive: true });
16063
+ await mkdir13(dirname6(this.historyFilePath), { recursive: true });
16009
16064
  await ensureCursorCommandProtectionHook();
16010
16065
  }
16011
16066
  getHistorySink() {
@@ -16075,7 +16130,7 @@ var CursorManager = class extends CodingAgentManager {
16075
16130
  this.slashCommandsRequest ??= (async () => {
16076
16131
  try {
16077
16132
  const repoDirectories = await getAgentAdditionalDirectories();
16078
- const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join18(directory, ".cursor", "commands"));
16133
+ const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join19(directory, ".cursor", "commands"));
16079
16134
  const commands = mergeSlashCommands(
16080
16135
  ...await Promise.all(commandDirectories.map(listCursorCommandsInDirectory))
16081
16136
  );
@@ -16356,11 +16411,30 @@ ${instructions}
16356
16411
  import { spawn as spawn3 } from "child_process";
16357
16412
  import { createHash as createHash2, randomUUID as randomUUID6 } from "crypto";
16358
16413
  import { createRequire } from "module";
16359
- import { basename as basename3, dirname as dirname7, join as join19 } from "path";
16414
+ import { basename as basename3, dirname as dirname7, join as join20 } from "path";
16360
16415
  import { fileURLToPath as fileURLToPath2 } from "url";
16361
16416
  import { existsSync as existsSync9 } from "fs";
16362
- import { mkdir as mkdir13, readFile as readFile13, rename as rename3, writeFile as writeFile7 } from "fs/promises";
16417
+ import { mkdir as mkdir14, rename as rename3, writeFile as writeFile8 } from "fs/promises";
16363
16418
  import { z as z2 } from "zod";
16419
+
16420
+ // src/managers/deepseek-config.ts
16421
+ import { stringify } from "yaml";
16422
+ var OPENROUTER_MODELS = [
16423
+ { id: "z-ai/glm-5.2", contextWindow: 131072 },
16424
+ { id: "minimax/minimax-m3", contextWindow: 2e5 },
16425
+ { id: "xiaomi/mimo-v2.5-pro", contextWindow: 262144 },
16426
+ { id: "moonshotai/kimi-k2.6", contextWindow: 262144 }
16427
+ ];
16428
+ function deepseekRuntimePatch(commandProtectionPlugin, ollama) {
16429
+ const models = ollama ? ollama.models.map(({ id, contextWindow }) => ({ id, contextWindow, maxTokens: 16384 })) : OPENROUTER_MODELS;
16430
+ return stringify([
16431
+ { id: "agent-default-model", config: { provider: "deepseek-official", model: ollama?.model ?? OPENROUTER_MODELS[0].id } },
16432
+ { id: "llm-deepseek", config: { apiKeyEnv: "DEEPSEEK_API_KEY", baseURLEnv: "DEEPSEEK_BASE_URL", models } },
16433
+ { insert: [{ id: "replicas-command-protection", name: commandProtectionPlugin }] }
16434
+ ]);
16435
+ }
16436
+
16437
+ // src/managers/deepseek-manager.ts
16364
16438
  import WebSocket from "ws";
16365
16439
  import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
16366
16440
  import { hostFrameSchema, muxFrameSchema } from "@deepseek-ai/dsh-host-apiproxy/api/events.schema";
@@ -16371,17 +16445,13 @@ var questionSelectionSchema = z2.record(z2.string(), z2.object({
16371
16445
  custom: z2.string().optional()
16372
16446
  }));
16373
16447
  var require2 = createRequire(import.meta.url);
16374
- var DSH_BIN = join19(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
16448
+ var DSH_BIN = join20(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
16375
16449
  var MANAGER_DIR = dirname7(fileURLToPath2(import.meta.url));
16376
- var DSH_PATCH = [
16377
- join19(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
16378
- join19(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
16379
- ].find(existsSync9) ?? "";
16380
16450
  var DSH_COMMAND_PROTECTION_PLUGIN = [
16381
- join19(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
16382
- join19(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
16451
+ join20(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
16452
+ join20(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
16383
16453
  ].find(existsSync9) ?? "";
16384
- var DEEPSEEK_HOME = join19(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek");
16454
+ var DEEPSEEK_HOME = join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek");
16385
16455
  function relayPresetContent(content, instructions, mcp) {
16386
16456
  const personaMarker = " text: >-\n";
16387
16457
  const personaStart = content.indexOf(personaMarker);
@@ -16430,7 +16500,7 @@ var DeepseekApiClient = class extends AbstractApiClient {
16430
16500
  openHost(_payload, signal, onOpen) {
16431
16501
  return this.readWebSocket("/api/events.host", signal, hostFrameSchema, onOpen);
16432
16502
  }
16433
- async executeCommand(sessionId, line) {
16503
+ async executeCommand(sessionId, line, withImages = false) {
16434
16504
  const rpcId = randomUUID6();
16435
16505
  const response = await this.doFetch(new URL("/api/commands/execute", this.baseUrl), {
16436
16506
  method: "POST",
@@ -16439,13 +16509,18 @@ var DeepseekApiClient = class extends AbstractApiClient {
16439
16509
  type: "client-request",
16440
16510
  rpcId,
16441
16511
  method: "commands/execute",
16442
- payload: { args: { agentId: sessionId, line, images: [] } }
16512
+ payload: { args: { agentId: sessionId, line, ...withImages ? { images: [] } : {} } }
16443
16513
  })
16444
16514
  });
16445
16515
  if (!response.ok) throw new Error(`DeepSeek Harness command failed with HTTP ${response.status}.`);
16446
16516
  const result = serverResponseSchema.parse(await response.json());
16447
16517
  if (result.rpcId !== rpcId) throw new Error("DeepSeek Harness command response did not match its request.");
16448
- if (!result.result.ok) throw new Error(result.result.error.message);
16518
+ if (!result.result.ok) {
16519
+ if (!withImages && result.result.error.message === 'typert gateway: commands/execute: args fields do not match the descriptor: missing "images"') {
16520
+ return this.executeCommand(sessionId, line, true);
16521
+ }
16522
+ throw new Error(result.result.error.message);
16523
+ }
16449
16524
  const execution = z2.object({ result: z2.object({ kind: z2.enum(["success", "error"]), text: z2.string().optional() }) }).safeParse(result.result.value);
16450
16525
  if (!execution.success) throw new Error(`DeepSeek Harness did not recognize ${line}.`);
16451
16526
  if (execution.data.result.kind === "error") throw new Error(execution.data.result.text ?? `DeepSeek Harness rejected ${line}.`);
@@ -16500,10 +16575,10 @@ function reasoningEffort(level) {
16500
16575
  if (level === "low") return "off";
16501
16576
  return level === "medium" || level === "high" ? "high" : "max";
16502
16577
  }
16503
- async function getOpenRouterKey() {
16578
+ async function getProviderApiKey() {
16504
16579
  const apiKey = getDeepseekApiKey();
16505
16580
  if (!apiKey) {
16506
- throw new Error("OpenRouter authentication is missing for DeepSeek Harness. Add an API key in Settings \u2192 Coding agents.");
16581
+ throw new Error("DeepSeek Harness authentication is missing. Add an OpenRouter or Ollama API key in Settings \u2192 Coding agents.");
16507
16582
  }
16508
16583
  return apiKey;
16509
16584
  }
@@ -16526,13 +16601,13 @@ var DeepseekManager = class extends CodingAgentManager {
16526
16601
  constructor(options) {
16527
16602
  super(options);
16528
16603
  this.sessionId = options.initialSessionId ? sessionIdSchema.parse(options.initialSessionId) : null;
16529
- this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
16530
- this.deepseekHome = this.isRelay ? join19(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek-relay", basename3(this.historyFilePath, ".jsonl")) : DEEPSEEK_HOME;
16604
+ this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
16605
+ this.deepseekHome = this.isRelay ? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek-relay", basename3(this.historyFilePath, ".jsonl")) : DEEPSEEK_HOME;
16531
16606
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
16532
16607
  this.initializeManager(this.processMessageInternal.bind(this));
16533
16608
  }
16534
16609
  async initialize() {
16535
- await mkdir13(dirname7(this.historyFilePath), { recursive: true });
16610
+ await mkdir14(dirname7(this.historyFilePath), { recursive: true });
16536
16611
  }
16537
16612
  getHistorySink() {
16538
16613
  return this.historyFile;
@@ -16603,16 +16678,20 @@ var DeepseekManager = class extends CodingAgentManager {
16603
16678
  this.process = null;
16604
16679
  this.client = null;
16605
16680
  }
16606
- async ensureClient() {
16681
+ async ensureClient(model) {
16607
16682
  if (this.client) return this.client;
16608
- if (!DSH_PATCH || !DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
16609
- const runtimePatch = join19(this.deepseekHome, "replicas.cordis.patch.yml");
16610
- await mkdir13(dirname7(runtimePatch), { recursive: true });
16611
- await writeFile7(runtimePatch, (await readFile13(DSH_PATCH, "utf8")).replace(
16612
- "__REPLICAS_DSH_COMMAND_PROTECTION_PLUGIN__",
16613
- JSON.stringify(DSH_COMMAND_PROTECTION_PLUGIN)
16683
+ if (!DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
16684
+ const runtimePatch = join20(this.deepseekHome, "replicas.cordis.patch.yml");
16685
+ await mkdir14(dirname7(runtimePatch), { recursive: true });
16686
+ const ollama = readOllamaAuthEnv("deepseek", process.env);
16687
+ const models = ollama ? await listOllamaModels(ollama) : [];
16688
+ const defaultModel = model || getDefaultOllamaModel(models);
16689
+ if (ollama && !defaultModel) throw new Error("Select a tool-capable Ollama model in the model picker.");
16690
+ await writeFile8(runtimePatch, deepseekRuntimePatch(
16691
+ DSH_COMMAND_PROTECTION_PLUGIN,
16692
+ ollama && defaultModel ? { model: defaultModel, models } : void 0
16614
16693
  ));
16615
- const apiKey = await getOpenRouterKey();
16694
+ const apiKey = await getProviderApiKey();
16616
16695
  const child = spawn3(process.execPath, [
16617
16696
  ...DSH_COMMAND_PROTECTION_PLUGIN.endsWith(".ts") ? ["--import", "tsx"] : [],
16618
16697
  DSH_BIN,
@@ -16629,7 +16708,7 @@ var DeepseekManager = class extends CodingAgentManager {
16629
16708
  env: {
16630
16709
  ...process.env,
16631
16710
  DEEPSEEK_API_KEY: apiKey,
16632
- DEEPSEEK_BASE_URL: "https://openrouter.ai/api/v1",
16711
+ DEEPSEEK_BASE_URL: ollama ? `${ollama.baseUrl}/v1` : "https://openrouter.ai/api/v1",
16633
16712
  DSH_HOME: this.deepseekHome,
16634
16713
  DSH_PERMISSION_MODE: "danger-full-access",
16635
16714
  DSH_TELEMETRY_DISABLED: "true"
@@ -16681,10 +16760,10 @@ var DeepseekManager = class extends CodingAgentManager {
16681
16760
  if (!roster.some((preset) => preset.id === agentPreset)) {
16682
16761
  const stage = `relay-stage-${randomUUID6().replaceAll("-", "")}`;
16683
16762
  unwrap(await client2.agentPresets.copy({ from: "standard", agentPreset: stage, name: "Relay" }));
16684
- const stageDirectory = join19(this.deepseekHome, ".agent-presets", stage);
16763
+ const stageDirectory = join20(this.deepseekHome, ".agent-presets", stage);
16685
16764
  try {
16686
- await writeFile7(join19(stageDirectory, "agent.cordis.yml"), content, "utf8");
16687
- await rename3(stageDirectory, join19(this.deepseekHome, ".agent-presets", agentPreset));
16765
+ await writeFile8(join20(stageDirectory, "agent.cordis.yml"), content, "utf8");
16766
+ await rename3(stageDirectory, join20(this.deepseekHome, ".agent-presets", agentPreset));
16688
16767
  } catch (error) {
16689
16768
  unwrap(await client2.agentPresets.remove({ agentPreset: stage }));
16690
16769
  if (!unwrap(await client2.agentPresets.list({})).presets.some((preset) => preset.id === agentPreset)) throw error;
@@ -16805,14 +16884,14 @@ var DeepseekManager = class extends CodingAgentManager {
16805
16884
  }
16806
16885
  async processMessageInternal(request) {
16807
16886
  try {
16808
- const client2 = await this.ensureClient();
16887
+ const client2 = await this.ensureClient(request.model);
16809
16888
  await this.ensureSession(client2, request.customInstructions);
16810
16889
  if (!this.sessionId) throw new Error("DeepSeek Harness session failed to initialize.");
16811
16890
  await this.hydrateActiveAgentPreset(client2);
16812
16891
  await client2.executeCommand(this.sessionId, "/permission danger-full-access");
16813
- const model = request.model ?? DEFAULT_DEEPSEEK_MODEL;
16814
- this.selectedModel = model;
16815
16892
  const current = unwrap(await client2.sessions.models({ sessionId: this.sessionId })).current;
16893
+ const model = request.model || (readOllamaAuthEnv("deepseek", process.env) ? current.model : DEFAULT_DEEPSEEK_MODEL);
16894
+ this.selectedModel = model;
16816
16895
  const effort = reasoningEffort(request.thinkingLevel);
16817
16896
  if (current.model !== model || current.reasoningEffort !== effort) {
16818
16897
  unwrap(await client2.sessions.selectModel({
@@ -16858,13 +16937,13 @@ ${request.message}` : request.message;
16858
16937
  };
16859
16938
 
16860
16939
  // src/managers/fx-manager.ts
16861
- import { readFile as readFile16 } from "fs/promises";
16940
+ import { readFile as readFile15 } from "fs/promises";
16862
16941
  import { spawn as spawn5 } from "child_process";
16863
16942
 
16864
16943
  // src/managers/acp-manager.ts
16865
16944
  import { spawn as spawn4 } from "child_process";
16866
16945
  import { randomUUID as randomUUID7 } from "crypto";
16867
- import { mkdir as mkdir14, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
16946
+ import { mkdir as mkdir15, readFile as readFile14, writeFile as writeFile9 } from "fs/promises";
16868
16947
  import { dirname as dirname8 } from "path";
16869
16948
  import { Writable } from "stream";
16870
16949
  import {
@@ -16875,14 +16954,14 @@ import {
16875
16954
  } from "@agentclientprotocol/sdk";
16876
16955
 
16877
16956
  // src/services/installed-skill-service.ts
16878
- import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
16879
- import { join as join20 } from "path";
16957
+ import { readFile as readFile13, readdir as readdir6 } from "fs/promises";
16958
+ import { join as join21 } from "path";
16880
16959
  var installedSkillsByHome = /* @__PURE__ */ new Map();
16881
16960
  function isNotFoundError2(error) {
16882
16961
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
16883
16962
  }
16884
16963
  async function loadInstalledSkills(homeDir) {
16885
- const skillsDir = join20(homeDir, ".agents/skills");
16964
+ const skillsDir = join21(homeDir, ".agents/skills");
16886
16965
  let entries;
16887
16966
  try {
16888
16967
  entries = await readdir6(skillsDir, { withFileTypes: true });
@@ -16892,7 +16971,7 @@ async function loadInstalledSkills(homeDir) {
16892
16971
  }
16893
16972
  const skills = await Promise.all(entries.filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map(async (entry) => {
16894
16973
  try {
16895
- const metadata = parseFrontmatter(await readFile14(join20(skillsDir, entry.name, "SKILL.md"), "utf8"));
16974
+ const metadata = parseFrontmatter(await readFile13(join21(skillsDir, entry.name, "SKILL.md"), "utf8"));
16896
16975
  return metadata.name && metadata.description ? metadata : null;
16897
16976
  } catch (error) {
16898
16977
  if (!isNotFoundError2(error)) console.warn(`[InstalledSkills] Failed to read ${entry.name}:`, error);
@@ -16946,7 +17025,7 @@ var AcpManager = class extends CodingAgentManager {
16946
17025
  this.initializeManager(this.processMessageInternal.bind(this));
16947
17026
  }
16948
17027
  async initialize() {
16949
- await mkdir14(dirname8(this.historyFilePath), { recursive: true });
17028
+ await mkdir15(dirname8(this.historyFilePath), { recursive: true });
16950
17029
  await this.acp.prepare?.();
16951
17030
  }
16952
17031
  getHistorySink() {
@@ -16977,7 +17056,7 @@ var AcpManager = class extends CodingAgentManager {
16977
17056
  });
16978
17057
  const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
16979
17058
  this.assertSession(params.sessionId);
16980
- const content = await readFile15(params.path, "utf8");
17059
+ const content = await readFile14(params.path, "utf8");
16981
17060
  if (params.line === void 0 && params.limit === void 0) return { content };
16982
17061
  const start = Math.max(0, (params.line ?? 1) - 1);
16983
17062
  const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
@@ -16985,7 +17064,7 @@ var AcpManager = class extends CodingAgentManager {
16985
17064
  }).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
16986
17065
  this.assertSession(params.sessionId);
16987
17066
  if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
16988
- await writeFile8(params.path, params.content, "utf8");
17067
+ await writeFile9(params.path, params.content, "utf8");
16989
17068
  return {};
16990
17069
  }).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
16991
17070
  this.assertSession(params.sessionId);
@@ -17368,7 +17447,7 @@ ${request.message}` : request.message
17368
17447
 
17369
17448
  // src/managers/fx-manager.ts
17370
17449
  async function ensureResolvableDnsConfig() {
17371
- const contents = await readFile16("/etc/resolv.conf");
17450
+ const contents = await readFile15("/etc/resolv.conf");
17372
17451
  if (contents.length === 0 || contents.at(-1) === 10) return;
17373
17452
  await new Promise((resolve7, reject) => {
17374
17453
  const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
@@ -17428,26 +17507,40 @@ var KimiManager = class extends AcpManager {
17428
17507
  command: "kimi",
17429
17508
  args: () => ["acp"],
17430
17509
  env: async (model, thinkingLevel) => {
17510
+ const ollama = readOllamaAuthEnv("kimi", process.env);
17431
17511
  let contextSize = "262144";
17432
17512
  let capabilities = model.startsWith("moonshotai/kimi") ? "image_in,thinking,tool_use" : "tool_use";
17433
- try {
17434
- const entry = (await fetchAiGatewayModels()).find((candidate) => candidate.id === model);
17435
- if (entry) {
17436
- if (entry.contextWindow) contextSize = String(entry.contextWindow);
17437
- capabilities = [
17438
- entry.tags.includes("vision") ? "image_in" : null,
17439
- entry.tags.includes("reasoning") ? "thinking" : null,
17440
- entry.tags.includes("tool-use") ? "tool_use" : null
17441
- ].filter((value) => value !== null).join(",") || capabilities;
17513
+ if (ollama) {
17514
+ const definition = (await listOllamaModels(ollama)).find((candidate) => candidate.id === model);
17515
+ if (!definition?.contextWindow || !definition.capabilities?.includes("tools")) {
17516
+ throw new Error(`Ollama did not report context length and tool support for ${model}. Choose a coding model with model details available.`);
17517
+ }
17518
+ contextSize = String(definition.contextWindow);
17519
+ capabilities = [
17520
+ "tool_use",
17521
+ ...definition.capabilities.includes("vision") ? ["image_in"] : [],
17522
+ ...definition.capabilities.includes("thinking") ? ["thinking"] : []
17523
+ ].join(",");
17524
+ } else {
17525
+ try {
17526
+ const entry = (await fetchAiGatewayModels()).find((candidate) => candidate.id === model);
17527
+ if (entry) {
17528
+ if (entry.contextWindow) contextSize = String(entry.contextWindow);
17529
+ capabilities = [
17530
+ entry.tags.includes("vision") ? "image_in" : null,
17531
+ entry.tags.includes("reasoning") ? "thinking" : null,
17532
+ entry.tags.includes("tool-use") ? "tool_use" : null
17533
+ ].filter((value) => value !== null).join(",") || capabilities;
17534
+ }
17535
+ } catch {
17442
17536
  }
17443
- } catch {
17444
17537
  }
17445
17538
  return {
17446
17539
  KIMI_DISABLE_TELEMETRY: "1",
17447
17540
  KIMI_MODEL_NAME: model,
17448
- KIMI_MODEL_API_KEY: ENGINE_ENV.AI_GATEWAY_API_KEY,
17541
+ KIMI_MODEL_API_KEY: ollama?.apiKey ?? ENGINE_ENV.AI_GATEWAY_API_KEY,
17449
17542
  KIMI_MODEL_PROVIDER_TYPE: "openai",
17450
- KIMI_MODEL_BASE_URL: AI_GATEWAY_BASE_URL,
17543
+ KIMI_MODEL_BASE_URL: ollama ? `${ollama.baseUrl}/v1` : AI_GATEWAY_BASE_URL,
17451
17544
  KIMI_MODEL_MAX_CONTEXT_SIZE: contextSize,
17452
17545
  KIMI_MODEL_CAPABILITIES: capabilities,
17453
17546
  ...thinkingLevel ? {
@@ -17455,7 +17548,7 @@ var KimiManager = class extends AcpManager {
17455
17548
  } : {}
17456
17549
  };
17457
17550
  },
17458
- defaultModel: DEFAULT_KIMI_MODEL,
17551
+ defaultModel: readOllamaAuthEnv("kimi", process.env) ? DEFAULT_OLLAMA_MODEL : DEFAULT_KIMI_MODEL,
17459
17552
  prepare: ensureKimiCommandProtectionHook,
17460
17553
  restartOnThinkingChange: true
17461
17554
  }
@@ -17466,7 +17559,7 @@ var KimiManager = class extends AcpManager {
17466
17559
  // src/managers/muse-manager.ts
17467
17560
  import { spawn as spawn6 } from "child_process";
17468
17561
  import { randomBytes as randomBytes2 } from "crypto";
17469
- import { mkdir as mkdir15 } from "fs/promises";
17562
+ import { mkdir as mkdir16 } from "fs/promises";
17470
17563
  import { dirname as dirname9 } from "path";
17471
17564
  var MuseQuotaError = class extends Error {
17472
17565
  };
@@ -17508,7 +17601,7 @@ var MuseManager = class extends CodingAgentManager {
17508
17601
  this.initializeManager(this.processMessageInternal.bind(this));
17509
17602
  }
17510
17603
  async initialize() {
17511
- await mkdir15(dirname9(this.historyFilePath), { recursive: true });
17604
+ await mkdir16(dirname9(this.historyFilePath), { recursive: true });
17512
17605
  }
17513
17606
  getHistorySink() {
17514
17607
  return this.historyFile;
@@ -17978,8 +18071,8 @@ ${pending}
17978
18071
 
17979
18072
  // src/managers/opencode-manager.ts
17980
18073
  import { existsSync as existsSync10 } from "fs";
17981
- import { copyFile, mkdir as mkdir16, readFile as readFile17, rm as rm2 } from "fs/promises";
17982
- import { delimiter, dirname as dirname10, join as join21 } from "path";
18074
+ import { copyFile, mkdir as mkdir17, readFile as readFile16, rm as rm2 } from "fs/promises";
18075
+ import { delimiter, dirname as dirname10, join as join22 } from "path";
17983
18076
  import { randomBytes as randomBytes3 } from "crypto";
17984
18077
  import { fileURLToPath as fileURLToPath3 } from "url";
17985
18078
  import { Agent } from "undici";
@@ -18008,10 +18101,10 @@ async function getAllowedOpenRouterModels() {
18008
18101
 
18009
18102
  // src/managers/opencode-manager.ts
18010
18103
  var OPENCODE_SHIM_DIR = dirname10(fileURLToPath3(new URL("../../scripts/opencode", import.meta.url)));
18011
- var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join21(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
18012
- var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
18013
- var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
18014
- var OPENCODE_CONFIG_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
18104
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join22(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
18105
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
18106
+ var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
18107
+ var OPENCODE_CONFIG_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
18015
18108
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
18016
18109
  var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
18017
18110
  var OPENCODE_WORKSPACE_PERMISSION = "allow";
@@ -18030,20 +18123,28 @@ var opencodeAuthSchema = z3.record(z3.string(), z3.object({
18030
18123
  key: z3.string().optional()
18031
18124
  }));
18032
18125
  async function hasOpencodeCredentials(provider) {
18126
+ if (provider === "ollama") return !!readOllamaAuthEnv("opencode", process.env);
18033
18127
  if (!existsSync10(OPENCODE_AUTH_PATH)) return false;
18034
18128
  try {
18035
- const auth2 = opencodeAuthSchema.safeParse(JSON.parse(await readFile17(OPENCODE_AUTH_PATH, "utf8")));
18129
+ const auth2 = opencodeAuthSchema.safeParse(JSON.parse(await readFile16(OPENCODE_AUTH_PATH, "utf8")));
18036
18130
  return auth2.success && auth2.data[provider]?.type === "api" && Boolean(auth2.data[provider]?.key);
18037
18131
  } catch {
18038
18132
  return false;
18039
18133
  }
18040
18134
  }
18041
18135
  async function getOpencodeProvider() {
18136
+ if (readOllamaAuthEnv("opencode", process.env)) return "ollama";
18042
18137
  if (await hasOpencodeCredentials(OPENCODE_GO_PROVIDER)) return OPENCODE_GO_PROVIDER;
18043
18138
  if (await hasOpencodeCredentials(ASTER_PROVIDER)) return ASTER_PROVIDER;
18044
18139
  return "openrouter";
18045
18140
  }
18046
18141
  async function getDefaultOpencodeModel(provider) {
18142
+ const ollama = readOllamaAuthEnv("opencode", process.env);
18143
+ if (provider === "ollama" && ollama) {
18144
+ const model = getDefaultOllamaModel(await listOllamaModels(ollama));
18145
+ if (!model) throw new Error("Select a tool-capable Ollama model in the model picker.");
18146
+ return model;
18147
+ }
18047
18148
  if (provider === OPENCODE_GO_PROVIDER) return DEFAULT_OPENCODE_GO_MODEL;
18048
18149
  if (provider === ASTER_PROVIDER) return DEFAULT_ASTER_MODEL;
18049
18150
  return (await getAllowedOpenRouterModels())[0] ?? DEFAULT_OPENCODE_MODEL;
@@ -18065,15 +18166,16 @@ function opencodeConfig(provider, model, providerModels = [model]) {
18065
18166
  permission: OPENCODE_WORKSPACE_PERMISSION,
18066
18167
  share: "disabled"
18067
18168
  };
18068
- } else if (provider === ASTER_PROVIDER) {
18169
+ } else if (provider === ASTER_PROVIDER || provider === "ollama") {
18170
+ const ollama = provider === "ollama" ? readOllamaAuthEnv("opencode", process.env) : null;
18069
18171
  config = {
18070
- enabled_providers: [ASTER_PROVIDER],
18071
- model: `${ASTER_PROVIDER}/${model}`,
18172
+ enabled_providers: [provider],
18173
+ model: `${provider}/${model}`,
18072
18174
  provider: {
18073
- [ASTER_PROVIDER]: {
18175
+ [provider]: {
18074
18176
  npm: "@ai-sdk/openai-compatible",
18075
- name: "Aster",
18076
- options: { baseURL: ASTER_BASE_URL },
18177
+ name: ollama ? "Ollama" : "Aster",
18178
+ options: ollama ? { baseURL: `${ollama.baseUrl}/v1`, apiKey: ollama.apiKey } : { baseURL: ASTER_BASE_URL },
18077
18179
  models: Object.fromEntries(providerModels.map((candidate) => [candidate, {}]))
18078
18180
  }
18079
18181
  },
@@ -18137,7 +18239,7 @@ function isOpencodeMcpEntry(value) {
18137
18239
  async function readProvisionedOpencodeMcpConfig() {
18138
18240
  let raw;
18139
18241
  try {
18140
- raw = await readFile17(OPENCODE_CONFIG_PATH, "utf8");
18242
+ raw = await readFile16(OPENCODE_CONFIG_PATH, "utf8");
18141
18243
  } catch (error) {
18142
18244
  if (isRecord2(error) && error.code === "ENOENT") return void 0;
18143
18245
  console.error("[OpencodeManager] Failed to read Opencode config:", error);
@@ -18223,12 +18325,12 @@ var OpencodeManager = class extends CodingAgentManager {
18223
18325
  constructor(options) {
18224
18326
  super({ ...options, provider: "opencode" });
18225
18327
  this.sessionId = options.initialSessionId;
18226
- this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
18328
+ this.historyFilePath = options.historyFilePath ?? join22(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
18227
18329
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
18228
18330
  this.initializeManager(this.processMessageInternal.bind(this));
18229
18331
  }
18230
18332
  async initialize() {
18231
- await mkdir16(dirname10(this.historyFilePath), { recursive: true });
18333
+ await mkdir17(dirname10(this.historyFilePath), { recursive: true });
18232
18334
  }
18233
18335
  getHistorySink() {
18234
18336
  return this.historyFile;
@@ -18315,7 +18417,8 @@ var OpencodeManager = class extends CodingAgentManager {
18315
18417
  }
18316
18418
  this.providerId = providerId;
18317
18419
  const configuredModel = providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
18318
- const providerModels = providerId === OPENCODE_GO_PROVIDER ? Object.keys((await fetchOpenCodeGoCatalog()).models) : providerId === ASTER_PROVIDER ? ASTER_MODELS : await getAllowedOpenRouterModels();
18420
+ const ollama = providerId === "ollama" ? readOllamaAuthEnv("opencode", process.env) : null;
18421
+ const providerModels = ollama ? (await listOllamaModels(ollama)).map(({ id }) => id) : providerId === OPENCODE_GO_PROVIDER ? Object.keys((await fetchOpenCodeGoCatalog()).models) : providerId === ASTER_PROVIDER ? ASTER_MODELS : await getAllowedOpenRouterModels();
18319
18422
  if (!providerModels.includes(configuredModel)) {
18320
18423
  throw new Error(`Model ${configuredModel} is not available through ${providerId}.`);
18321
18424
  }
@@ -18323,12 +18426,12 @@ var OpencodeManager = class extends CodingAgentManager {
18323
18426
  return this.client;
18324
18427
  }
18325
18428
  if (!await hasOpencodeCredentials(providerId)) {
18326
- throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
18429
+ throw new Error("OpenCode Go, Aster, OpenRouter, or Ollama credentials are not configured for Opencode in this workspace.");
18327
18430
  }
18328
18431
  if (!existsSync10(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE)) {
18329
18432
  throw new Error("Opencode command protection plugin is missing from the engine package.");
18330
18433
  }
18331
- await mkdir16(dirname10(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
18434
+ await mkdir17(dirname10(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
18332
18435
  await copyFile(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE, OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH);
18333
18436
  await rm2(OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH, { force: true });
18334
18437
  process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
@@ -18643,8 +18746,8 @@ var OpencodeManager = class extends CodingAgentManager {
18643
18746
  };
18644
18747
 
18645
18748
  // src/managers/pi-manager.ts
18646
- import { mkdir as mkdir18, readFile as readFile18 } from "fs/promises";
18647
- import { dirname as dirname18, join as join29 } from "path";
18749
+ import { mkdir as mkdir19, readFile as readFile17 } from "fs/promises";
18750
+ import { dirname as dirname18, join as join30 } from "path";
18648
18751
  import {
18649
18752
  createAgentSession,
18650
18753
  ModelRegistry,
@@ -18655,21 +18758,21 @@ import {
18655
18758
 
18656
18759
  // src/services/chat/history-paths.ts
18657
18760
  import { homedir as homedir12 } from "os";
18658
- import { join as join22 } from "path";
18659
- var ENGINE_DIR2 = join22(homedir12(), ".replicas", "engine");
18660
- var CHATS_FILE = join22(ENGINE_DIR2, "chats.json");
18661
- var CLAUDE_HISTORY_DIR = join22(ENGINE_DIR2, "claude-histories");
18662
- var RELAY_HISTORY_DIR = join22(ENGINE_DIR2, "relay-histories");
18663
- var CODEX_HISTORY_DIR = join22(ENGINE_DIR2, "codex-histories");
18664
- var CURSOR_HISTORY_DIR = join22(ENGINE_DIR2, "cursor-histories");
18665
- var OPENCODE_HISTORY_DIR = join22(ENGINE_DIR2, "opencode-histories");
18666
- var PI_HISTORY_DIR = join22(ENGINE_DIR2, "pi-histories");
18667
- var DEEPSEEK_HISTORY_DIR = join22(ENGINE_DIR2, "deepseek-histories");
18668
- var FX_HISTORY_DIR = join22(ENGINE_DIR2, "fx-histories");
18669
- var GEMINI_HISTORY_DIR = join22(ENGINE_DIR2, "gemini-histories");
18670
- var KIMI_HISTORY_DIR = join22(ENGINE_DIR2, "kimi-histories");
18671
- var MUSE_HISTORY_DIR = join22(ENGINE_DIR2, "muse-histories");
18672
- var FORKS_DIR = join22(ENGINE_DIR2, "forks");
18761
+ import { join as join23 } from "path";
18762
+ var ENGINE_DIR2 = join23(homedir12(), ".replicas", "engine");
18763
+ var CHATS_FILE = join23(ENGINE_DIR2, "chats.json");
18764
+ var CLAUDE_HISTORY_DIR = join23(ENGINE_DIR2, "claude-histories");
18765
+ var RELAY_HISTORY_DIR = join23(ENGINE_DIR2, "relay-histories");
18766
+ var CODEX_HISTORY_DIR = join23(ENGINE_DIR2, "codex-histories");
18767
+ var CURSOR_HISTORY_DIR = join23(ENGINE_DIR2, "cursor-histories");
18768
+ var OPENCODE_HISTORY_DIR = join23(ENGINE_DIR2, "opencode-histories");
18769
+ var PI_HISTORY_DIR = join23(ENGINE_DIR2, "pi-histories");
18770
+ var DEEPSEEK_HISTORY_DIR = join23(ENGINE_DIR2, "deepseek-histories");
18771
+ var FX_HISTORY_DIR = join23(ENGINE_DIR2, "fx-histories");
18772
+ var GEMINI_HISTORY_DIR = join23(ENGINE_DIR2, "gemini-histories");
18773
+ var KIMI_HISTORY_DIR = join23(ENGINE_DIR2, "kimi-histories");
18774
+ var MUSE_HISTORY_DIR = join23(ENGINE_DIR2, "muse-histories");
18775
+ var FORKS_DIR = join23(ENGINE_DIR2, "forks");
18673
18776
  var HISTORY_DIR_BY_PROVIDER = {
18674
18777
  claude: CLAUDE_HISTORY_DIR,
18675
18778
  relay: RELAY_HISTORY_DIR,
@@ -29421,10 +29524,10 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
29421
29524
  function interpolate(x) {
29422
29525
  return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify2(Array.isArray(x) ? x.join(",") : x);
29423
29526
  }
29424
- function stringify(x) {
29527
+ function stringify2(x) {
29425
29528
  return new _Code(safeStringify2(x));
29426
29529
  }
29427
- exports.stringify = stringify;
29530
+ exports.stringify = stringify2;
29428
29531
  function safeStringify2(x) {
29429
29532
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
29430
29533
  }
@@ -41214,7 +41317,7 @@ async function probeMcpEndpoint(url3) {
41214
41317
 
41215
41318
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/npx-resolver.ts
41216
41319
  import { existsSync as existsSync11, readFileSync as readFileSync4, realpathSync, readdirSync, statSync, writeFileSync, renameSync, mkdirSync, openSync, readSync, closeSync, unlinkSync } from "fs";
41217
- import { join as join23, dirname as dirname11, extname as extname2, resolve as resolve2, sep } from "path";
41320
+ import { join as join24, dirname as dirname11, extname as extname2, resolve as resolve2, sep } from "path";
41218
41321
 
41219
41322
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/abort.ts
41220
41323
  function throwIfAborted(signal) {
@@ -41371,7 +41474,7 @@ function resolveFromNpmCache(packageSpec, binName) {
41371
41474
  const { packageName, exactVersion } = parsedSpec;
41372
41475
  const packageDir = findCachedPackageDir(cacheDir, packageName, exactVersion);
41373
41476
  if (!packageDir) return null;
41374
- const packageJsonPath = join23(packageDir, "package.json");
41477
+ const packageJsonPath = join24(packageDir, "package.json");
41375
41478
  if (!existsSync11(packageJsonPath)) return null;
41376
41479
  let pkg = null;
41377
41480
  try {
@@ -41405,7 +41508,7 @@ function resolveFromNpmCache(packageSpec, binName) {
41405
41508
  }
41406
41509
  if (!binRel) return null;
41407
41510
  const nodeModulesDir = findNodeModulesDir(packageDir);
41408
- const binLink = chosenBinName ? join23(nodeModulesDir, ".bin", chosenBinName) : null;
41511
+ const binLink = chosenBinName ? join24(nodeModulesDir, ".bin", chosenBinName) : null;
41409
41512
  let resolvedBin = binLink && existsSync11(binLink) ? safeRealpath(binLink) : "";
41410
41513
  if (!resolvedBin) {
41411
41514
  resolvedBin = resolve2(packageDir, binRel);
@@ -41507,17 +41610,17 @@ function defaultBinName(packageName) {
41507
41610
  return packageName;
41508
41611
  }
41509
41612
  function findCachedPackageDir(cacheDir, packageName, exactVersion) {
41510
- const npxDir = join23(cacheDir, "_npx");
41613
+ const npxDir = join24(cacheDir, "_npx");
41511
41614
  if (!existsSync11(npxDir)) return null;
41512
41615
  const packagePathParts = packageName.startsWith("@") ? packageName.split("/") : [packageName];
41513
41616
  const candidates = readdirSync(npxDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
41514
- const full = join23(npxDir, entry.name);
41617
+ const full = join24(npxDir, entry.name);
41515
41618
  const mtime = safeStatMtime(full);
41516
41619
  return { name: entry.name, mtime };
41517
41620
  }).sort((a, b) => b.mtime - a.mtime);
41518
41621
  for (const entry of candidates) {
41519
- const pkgDir = join23(npxDir, entry.name, "node_modules", ...packagePathParts);
41520
- const packageJsonPath = join23(pkgDir, "package.json");
41622
+ const pkgDir = join24(npxDir, entry.name, "node_modules", ...packagePathParts);
41623
+ const packageJsonPath = join24(pkgDir, "package.json");
41521
41624
  if (!existsSync11(packageJsonPath)) continue;
41522
41625
  if (exactVersion) {
41523
41626
  try {
@@ -41537,7 +41640,7 @@ function findNodeModulesDir(packageDir) {
41537
41640
  if (idx >= 0) {
41538
41641
  return parts.slice(0, idx + 1).join(sep);
41539
41642
  }
41540
- return join23(packageDir, "..");
41643
+ return join24(packageDir, "..");
41541
41644
  }
41542
41645
  function detectJsBinary(binPath) {
41543
41646
  const ext = extname2(binPath).toLowerCase();
@@ -41729,7 +41832,7 @@ import { spawnSync } from "child_process";
41729
41832
  import { createHash as createHash3 } from "crypto";
41730
41833
  import { createRequire as createRequire2 } from "module";
41731
41834
  import { readFileSync as readFileSync5, existsSync as existsSync12, rmSync } from "fs";
41732
- import { dirname as dirname12, join as join24 } from "path";
41835
+ import { dirname as dirname12, join as join25 } from "path";
41733
41836
  import { fileURLToPath as fileURLToPath4 } from "url";
41734
41837
  var require3 = createRequire2(import.meta.url);
41735
41838
  var AUTH_SECRET_SERVICE = "pi-mcp-adapter.oauth";
@@ -41885,7 +41988,7 @@ function loadKeyringNativeBindingFallback(keyringRequire, platform3, arch2) {
41885
41988
  for (const target of targets) {
41886
41989
  try {
41887
41990
  const packageJsonPath = keyringRequire.resolve(`${target.packageName}/package.json`);
41888
- return keyringRequire(join24(dirname12(packageJsonPath), target.bindingFile));
41991
+ return keyringRequire(join25(dirname12(packageJsonPath), target.bindingFile));
41889
41992
  } catch (error) {
41890
41993
  lastError = error;
41891
41994
  }
@@ -41990,7 +42093,7 @@ function getServerDir(serverName, options) {
41990
42093
  throw new Error(`Invalid MCP server name: ${JSON.stringify(serverName)}`);
41991
42094
  }
41992
42095
  const storageKey = getAuthEntryAccount(serverName);
41993
- return join24(getAuthBaseDir(options), storageKey);
42096
+ return join25(getAuthBaseDir(options), storageKey);
41994
42097
  }
41995
42098
  function getAuthEntryAccount(serverName) {
41996
42099
  if (typeof serverName !== "string") {
@@ -41999,7 +42102,7 @@ function getAuthEntryAccount(serverName) {
41999
42102
  return `sha256-${createHash3("sha256").update(serverName, "utf8").digest("hex")}`;
42000
42103
  }
42001
42104
  function getAuthEntryFilePath(serverName, options) {
42002
- return join24(getServerDir(serverName, options), "tokens.json");
42105
+ return join25(getServerDir(serverName, options), "tokens.json");
42003
42106
  }
42004
42107
  function parseJsonPayload(serverName, payload, source) {
42005
42108
  try {
@@ -44602,7 +44705,7 @@ async function shutdownOAuth(runtime = legacyRuntime) {
44602
44705
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/mcp-bearer-store.ts
44603
44706
  import { createHash as createHash4 } from "crypto";
44604
44707
  import { createRequire as createRequire3 } from "module";
44605
- import { dirname as dirname13, join as join25 } from "path";
44708
+ import { dirname as dirname13, join as join26 } from "path";
44606
44709
  var require4 = createRequire3(import.meta.url);
44607
44710
  var BEARER_SECRET_SERVICE = "pi-mcp-adapter.bearer";
44608
44711
  var TEST_AUTH_STORE_ENV2 = "PI_MCP_ADAPTER_TEST_AUTH_STORE";
@@ -44704,7 +44807,7 @@ function loadKeyringNativeBindingFallback2(keyringRequire, platform3, arch2) {
44704
44807
  for (const target of targets) {
44705
44808
  try {
44706
44809
  const packageJsonPath = keyringRequire.resolve(`${target.packageName}/package.json`);
44707
- return keyringRequire(join25(dirname13(packageJsonPath), target.bindingFile));
44810
+ return keyringRequire(join26(dirname13(packageJsonPath), target.bindingFile));
44708
44811
  } catch (error) {
44709
44812
  lastError = error;
44710
44813
  }
@@ -45316,7 +45419,7 @@ function humanizeName(name) {
45316
45419
  }
45317
45420
 
45318
45421
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/mcp-trace.ts
45319
- import { appendFile as appendFile3, mkdir as mkdir17, writeFile as writeFile9 } from "fs/promises";
45422
+ import { appendFile as appendFile3, mkdir as mkdir18, writeFile as writeFile10 } from "fs/promises";
45320
45423
  import { Buffer as Buffer3 } from "buffer";
45321
45424
  import { dirname as dirname14, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
45322
45425
  var MCP_TRACE_SCHEMA_VERSION = 1;
@@ -45385,10 +45488,10 @@ var McpTraceWriter = class {
45385
45488
  await appendFile3(path7, data, appendOptions);
45386
45489
  });
45387
45490
  this.resetFile = options.writeFile ?? (async (path7, data, writeOptions) => {
45388
- await writeFile9(path7, data, writeOptions);
45491
+ await writeFile10(path7, data, writeOptions);
45389
45492
  });
45390
45493
  this.makeDirectory = options.mkdir ?? (async (path7) => {
45391
- await mkdir17(path7, { recursive: true });
45494
+ await mkdir18(path7, { recursive: true });
45392
45495
  return void 0;
45393
45496
  });
45394
45497
  this.fileReady = this.makeDirectory(dirname14(this.options.filePath), { recursive: true }).then(() => this.resetFile(this.options.filePath, "", { encoding: "utf8" })).catch(() => {
@@ -49171,7 +49274,7 @@ async function openMcpAuthPanel(state, pi, ctx, configOverridePath) {
49171
49274
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/tool-registrar.ts
49172
49275
  import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
49173
49276
  import { tmpdir } from "os";
49174
- import { join as join26 } from "path";
49277
+ import { join as join27 } from "path";
49175
49278
  var MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;
49176
49279
  var MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;
49177
49280
  var MAX_SESSION_RESOURCE_FILES = 1e4;
@@ -49278,11 +49381,11 @@ function materializeBinaryResource(resource, scope) {
49278
49381
  return omitBinaryResource(resource, "session resource limit reached");
49279
49382
  }
49280
49383
  try {
49281
- session.directory ??= mkdtempSync(join26(tmpdir(), "pi-mcp-resource-"));
49384
+ session.directory ??= mkdtempSync(join27(tmpdir(), "pi-mcp-resource-"));
49282
49385
  } catch {
49283
49386
  return omitBinaryResource(resource, "could not be saved");
49284
49387
  }
49285
- const filePath = join26(session.directory, `resource-${++session.sequence}.bin`);
49388
+ const filePath = join27(session.directory, `resource-${++session.sequence}.bin`);
49286
49389
  session.bytes += decodedBytes;
49287
49390
  session.files += 1;
49288
49391
  try {
@@ -49373,9 +49476,9 @@ function stringifyStructuredContent(value) {
49373
49476
 
49374
49477
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/mcp-output-guard.ts
49375
49478
  import { randomBytes as randomBytes4 } from "crypto";
49376
- import { mkdtemp, rm as rm3, writeFile as writeFile10 } from "fs/promises";
49479
+ import { mkdtemp, rm as rm3, writeFile as writeFile11 } from "fs/promises";
49377
49480
  import { tmpdir as tmpdir2 } from "os";
49378
- import { dirname as dirname16, join as join27 } from "path";
49481
+ import { dirname as dirname16, join as join28 } from "path";
49379
49482
  var DEFAULT_MCP_OUTPUT_MAX_BYTES = 50 * 1024;
49380
49483
  var DEFAULT_MCP_OUTPUT_MAX_LINES = 2e3;
49381
49484
  var DEFAULT_MCP_DETAILS_MAX_BYTES = 16 * 1024;
@@ -49683,9 +49786,9 @@ function serializedObjectEntryBytes(key, value, hasPrevious) {
49683
49786
  }
49684
49787
  async function saveArtifact(kind, text) {
49685
49788
  try {
49686
- const dir = await mkdtemp(join27(tmpdir2(), "pi-mcp-output-"));
49687
- const path7 = join27(dir, `${kind}-${randomBytes4(4).toString("hex")}.txt`);
49688
- await writeFile10(path7, text, { encoding: "utf8", mode: 384 });
49789
+ const dir = await mkdtemp(join28(tmpdir2(), "pi-mcp-output-"));
49790
+ const path7 = join28(dir, `${kind}-${randomBytes4(4).toString("hex")}.txt`);
49791
+ await writeFile11(path7, text, { encoding: "utf8", mode: 384 });
49689
49792
  return { path: path7 };
49690
49793
  } catch (error) {
49691
49794
  return { error: error instanceof Error ? error.message : String(error) };
@@ -51331,7 +51434,7 @@ function sendText(res, status, text) {
51331
51434
  // ../node_modules/.bun/pi-mcp-adapter@2.32.1+04b0d7b0bc944965/node_modules/pi-mcp-adapter/glimpse-ui.ts
51332
51435
  import { existsSync as existsSync15 } from "fs";
51333
51436
  import { execFileSync as execFileSync2 } from "child_process";
51334
- import { join as join28, dirname as dirname17 } from "path";
51437
+ import { join as join29, dirname as dirname17 } from "path";
51335
51438
  import { platform as platform2 } from "os";
51336
51439
  import { createRequire as createRequire4 } from "module";
51337
51440
  var glimpseAvailable = null;
@@ -51353,20 +51456,20 @@ function getGlimpseBinaryPath() {
51353
51456
  try {
51354
51457
  const require7 = createRequire4(import.meta.url);
51355
51458
  const glimpseuiPath = require7.resolve("glimpseui");
51356
- const binaryPath = join28(dirname17(glimpseuiPath), "glimpse");
51459
+ const binaryPath = join29(dirname17(glimpseuiPath), "glimpse");
51357
51460
  if (existsSync15(binaryPath)) return binaryPath;
51358
51461
  } catch {
51359
51462
  }
51360
51463
  try {
51361
51464
  const globalRoot = execFileSync2("npm", ["root", "-g"], { encoding: "utf-8" }).trim();
51362
- const binaryPath = join28(globalRoot, "glimpseui", "src", "glimpse");
51465
+ const binaryPath = join29(globalRoot, "glimpseui", "src", "glimpse");
51363
51466
  if (existsSync15(binaryPath)) return binaryPath;
51364
51467
  } catch {
51365
51468
  }
51366
51469
  return null;
51367
51470
  }
51368
51471
  async function openGlimpseWindow(html, options) {
51369
- const modulePath = resolvedBinaryPath ? join28(dirname17(resolvedBinaryPath), "glimpse.mjs") : "glimpseui";
51472
+ const modulePath = resolvedBinaryPath ? join29(dirname17(resolvedBinaryPath), "glimpse.mjs") : "glimpseui";
51370
51473
  const glimpse = await import(modulePath);
51371
51474
  let active = true;
51372
51475
  const win = glimpse.open(html, {
@@ -59156,7 +59259,7 @@ var pi_mcp_adapter_default = createMcpAdapter();
59156
59259
  // src/managers/pi-manager.ts
59157
59260
  async function getManagedPiMcpServers() {
59158
59261
  try {
59159
- const parsed = JSON.parse(await readFile18(PI_MCP_CONFIG_PATH, "utf8"));
59262
+ const parsed = JSON.parse(await readFile17(PI_MCP_CONFIG_PATH, "utf8"));
59160
59263
  if (!isRecord2(parsed) || !isRecord2(parsed.mcpServers)) return {};
59161
59264
  return Object.fromEntries(Object.entries(parsed.mcpServers).filter(
59162
59265
  (entry) => isRecord2(entry[1]) && (typeof entry[1].command === "string" || typeof entry[1].url === "string")
@@ -59278,12 +59381,12 @@ var PiManager = class extends CodingAgentManager {
59278
59381
  modelRuntime = null;
59279
59382
  constructor(options) {
59280
59383
  super(options);
59281
- this.historyFilePath = options.historyFilePath ?? join29(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
59384
+ this.historyFilePath = options.historyFilePath ?? join30(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
59282
59385
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
59283
59386
  this.initializeManager(this.processMessageInternal.bind(this));
59284
59387
  }
59285
59388
  async initialize() {
59286
- await mkdir18(dirname18(this.historyFilePath), { recursive: true });
59389
+ await mkdir19(dirname18(this.historyFilePath), { recursive: true });
59287
59390
  }
59288
59391
  getHistorySink() {
59289
59392
  return this.historyFile;
@@ -59354,7 +59457,29 @@ var PiManager = class extends CodingAgentManager {
59354
59457
  this.providerApiKey = credentials.apiKey;
59355
59458
  const modelRegistry = new ModelRegistry(modelRuntime);
59356
59459
  let defaultModel = DEFAULT_ASTER_MODEL;
59357
- if (this.providerId === OPENCODE_GO_PROVIDER) {
59460
+ const ollama = this.providerId === "ollama" ? readOllamaAuthEnv("pi", process.env) : null;
59461
+ if (ollama) {
59462
+ const models = await listOllamaModels(ollama);
59463
+ defaultModel = request.model || getDefaultOllamaModel(models) || "";
59464
+ const selected = models.find((model2) => model2.id === defaultModel);
59465
+ if (!selected?.contextWindow || !selected.capabilities?.includes("tools")) {
59466
+ throw new Error("Select an Ollama coding model with context length and tool support in its model details.");
59467
+ }
59468
+ registerPiProvider(modelRegistry, {
59469
+ providerId: "ollama",
59470
+ name: "Ollama",
59471
+ apiKey: ollama.apiKey,
59472
+ baseUrl: `${ollama.baseUrl}/v1`,
59473
+ models: models.filter((model2) => model2.contextWindow && model2.capabilities?.includes("tools")).map((model2) => ({
59474
+ id: model2.id,
59475
+ name: model2.displayName,
59476
+ contextWindow: model2.contextWindow,
59477
+ reasoning: model2.capabilities?.includes("thinking"),
59478
+ maxTokens: 16384,
59479
+ input: model2.capabilities?.includes("vision") ? ["text", "image"] : ["text"]
59480
+ }))
59481
+ });
59482
+ } else if (this.providerId === OPENCODE_GO_PROVIDER) {
59358
59483
  const catalog = await fetchOpenCodeGoCatalog([request.model ?? DEFAULT_OPENCODE_GO_MODEL]);
59359
59484
  const models = Object.entries(catalog.models);
59360
59485
  defaultModel = catalog.models[DEFAULT_OPENCODE_GO_MODEL] ? DEFAULT_OPENCODE_GO_MODEL : models[0]?.[0] ?? DEFAULT_OPENCODE_GO_MODEL;
@@ -59414,7 +59539,7 @@ var PiManager = class extends CodingAgentManager {
59414
59539
  }
59415
59540
  const resourceLoader = new DefaultResourceLoader({
59416
59541
  cwd: this.workingDirectory,
59417
- agentDir: join29(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
59542
+ agentDir: join30(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
59418
59543
  extensionFactories,
59419
59544
  appendSystemPrompt: [this.buildCombinedInstructions() ?? ""]
59420
59545
  });
@@ -59530,14 +59655,14 @@ async function uploadAnalytics(endpoint, body) {
59530
59655
  // src/analytics/analytics-record-buffer.ts
59531
59656
  import {
59532
59657
  appendFile as appendFile4,
59533
- mkdir as mkdir19,
59534
- readFile as readFile19,
59658
+ mkdir as mkdir20,
59659
+ readFile as readFile18,
59535
59660
  readdir as readdir7,
59536
59661
  rename as rename4,
59537
59662
  unlink as unlink3
59538
59663
  } from "fs/promises";
59539
59664
  import { existsSync as existsSync16, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "fs";
59540
- import { join as join30 } from "path";
59665
+ import { join as join31 } from "path";
59541
59666
  var MAX_FAILED_RECORDS = 500;
59542
59667
  var FLUSH_DEBOUNCE_MS = 15e3;
59543
59668
  var MAX_UPLOADED_SEGMENTS = 50;
@@ -59549,7 +59674,7 @@ var AnalyticsRecordBuffer = class {
59549
59674
  options.storageName,
59550
59675
  ...options.legacyStorageNames ?? []
59551
59676
  ];
59552
- this.liveFile = join30(ENGINE_DIR2, `${options.storageName}.jsonl`);
59677
+ this.liveFile = join31(ENGINE_DIR2, `${options.storageName}.jsonl`);
59553
59678
  this.segmentFilePatterns = this.storageNames.map(
59554
59679
  (storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
59555
59680
  );
@@ -59572,7 +59697,7 @@ var AnalyticsRecordBuffer = class {
59572
59697
  if (this.recordIds.has(recordId)) return;
59573
59698
  this.recordIds.add(recordId);
59574
59699
  }
59575
- const pending = mkdir19(ENGINE_DIR2, { recursive: true }).then(() => appendFile4(this.liveFile, `${JSON.stringify(record3)}
59700
+ const pending = mkdir20(ENGINE_DIR2, { recursive: true }).then(() => appendFile4(this.liveFile, `${JSON.stringify(record3)}
59576
59701
  `, "utf-8")).catch((error) => {
59577
59702
  console.error(
59578
59703
  `[${this.options.storageName}] Append failed, will retry on flush:`,
@@ -59587,7 +59712,7 @@ var AnalyticsRecordBuffer = class {
59587
59712
  if (!existsSync16(ENGINE_DIR2)) return [];
59588
59713
  return readdirSync2(ENGINE_DIR2, { withFileTypes: true }).filter((entry) => entry.isFile() && this.storageNames.some(
59589
59714
  (storageName) => entry.name === `${storageName}.jsonl` || entry.name.startsWith(`${storageName}.`) && (entry.name.endsWith(".jsonl") || entry.name.endsWith(`.jsonl${UPLOADED_SUFFIX}`))
59590
- )).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync7(join30(ENGINE_DIR2, name), "utf-8"))).map((record3) => {
59715
+ )).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync7(join31(ENGINE_DIR2, name), "utf-8"))).map((record3) => {
59591
59716
  const recordId = this.options.getRecordId?.(record3);
59592
59717
  if (recordId) this.recordIds.add(recordId);
59593
59718
  return record3;
@@ -59622,8 +59747,8 @@ var AnalyticsRecordBuffer = class {
59622
59747
  }
59623
59748
  for (const storageName of this.storageNames) {
59624
59749
  await rename4(
59625
- join30(ENGINE_DIR2, `${storageName}.jsonl`),
59626
- join30(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
59750
+ join31(ENGINE_DIR2, `${storageName}.jsonl`),
59751
+ join31(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
59627
59752
  ).catch(() => {
59628
59753
  });
59629
59754
  }
@@ -59633,7 +59758,7 @@ var AnalyticsRecordBuffer = class {
59633
59758
  for (const entry of entries) {
59634
59759
  if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry))) continue;
59635
59760
  try {
59636
- await this.uploadSegment(join30(ENGINE_DIR2, entry));
59761
+ await this.uploadSegment(join31(ENGINE_DIR2, entry));
59637
59762
  flushed++;
59638
59763
  } catch (error) {
59639
59764
  failed++;
@@ -59662,14 +59787,14 @@ var AnalyticsRecordBuffer = class {
59662
59787
  (storageName) => entry.startsWith(`${storageName}.`)
59663
59788
  ) && entry.endsWith(UPLOADED_SUFFIX)).sort();
59664
59789
  for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
59665
- await unlink3(join30(ENGINE_DIR2, entry)).catch(() => {
59790
+ await unlink3(join31(ENGINE_DIR2, entry)).catch(() => {
59666
59791
  });
59667
59792
  }
59668
59793
  }
59669
59794
  return { flushed, failed };
59670
59795
  }
59671
59796
  async uploadSegment(filePath) {
59672
- const records = this.parseRecords(await readFile19(filePath, "utf-8"));
59797
+ const records = this.parseRecords(await readFile18(filePath, "utf-8"));
59673
59798
  if (records.length > 0) await this.options.upload(records);
59674
59799
  await rename4(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
59675
59800
  });
@@ -60511,9 +60636,9 @@ var KeepAliveService = class _KeepAliveService {
60511
60636
  var keepAliveService = new KeepAliveService();
60512
60637
 
60513
60638
  // src/services/canvas-service.ts
60514
- import { readdir as readdir8, readFile as readFile21, stat as stat4 } from "fs/promises";
60639
+ import { readdir as readdir8, readFile as readFile20, stat as stat4 } from "fs/promises";
60515
60640
  import { homedir as homedir13 } from "os";
60516
- import { join as join31 } from "path";
60641
+ import { join as join32 } from "path";
60517
60642
 
60518
60643
  // src/utils/contained-file.ts
60519
60644
  import { realpath, stat as stat3 } from "fs/promises";
@@ -60539,9 +60664,9 @@ function fileRevision(content) {
60539
60664
  }
60540
60665
 
60541
60666
  // src/utils/revisioned-text-file.ts
60542
- import { readFile as readFile20 } from "fs/promises";
60667
+ import { readFile as readFile19 } from "fs/promises";
60543
60668
  async function writeRevisionedTextFile(filePath, content, expectedRevision) {
60544
- const current = await readFile20(filePath, "utf8");
60669
+ const current = await readFile19(filePath, "utf8");
60545
60670
  const revision = fileRevision(current);
60546
60671
  if (revision !== expectedRevision) {
60547
60672
  return {
@@ -60555,15 +60680,15 @@ async function writeRevisionedTextFile(filePath, content, expectedRevision) {
60555
60680
 
60556
60681
  // src/services/canvas-service.ts
60557
60682
  var GLOBAL_CANVAS_DIRECTORIES = [
60558
- join31(homedir13(), ".claude", "plans"),
60559
- join31(process.env.XDG_DATA_HOME ?? join31(homedir13(), ".local", "share"), "opencode", "plans"),
60560
- join31(homedir13(), ".replicas", "canvas")
60683
+ join32(homedir13(), ".claude", "plans"),
60684
+ join32(process.env.XDG_DATA_HOME ?? join32(homedir13(), ".local", "share"), "opencode", "plans"),
60685
+ join32(homedir13(), ".replicas", "canvas")
60561
60686
  ];
60562
60687
  async function canvasDirectories() {
60563
60688
  const repositories = await gitService.listRepositories().catch(() => []);
60564
60689
  return [
60565
60690
  ...GLOBAL_CANVAS_DIRECTORIES,
60566
- ...repositories.map((repository) => join31(repository.path, ".opencode", "plans"))
60691
+ ...repositories.map((repository) => join32(repository.path, ".opencode", "plans"))
60567
60692
  ];
60568
60693
  }
60569
60694
  var CanvasService = class {
@@ -60588,7 +60713,7 @@ var CanvasService = class {
60588
60713
  for (const entry of entries) {
60589
60714
  if (entry.name.startsWith(".")) continue;
60590
60715
  const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
60591
- const filePath = join31(current.directory, entry.name);
60716
+ const filePath = join32(current.directory, entry.name);
60592
60717
  if (entry.isDirectory()) {
60593
60718
  pending.push({ directory: filePath, relativePath: filename });
60594
60719
  continue;
@@ -60613,7 +60738,7 @@ var CanvasService = class {
60613
60738
  if (!safe) return null;
60614
60739
  const { kind, mimeType } = classifyCanvasFilename(safe);
60615
60740
  for (const directory of await this.directories()) {
60616
- const filePath = join31(directory, safe);
60741
+ const filePath = join32(directory, safe);
60617
60742
  let sizeBytes = 0;
60618
60743
  let updatedAt = "";
60619
60744
  try {
@@ -60634,7 +60759,7 @@ var CanvasService = class {
60634
60759
  };
60635
60760
  }
60636
60761
  try {
60637
- const bytes = await readFile21(filePath);
60762
+ const bytes = await readFile20(filePath);
60638
60763
  return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
60639
60764
  } catch {
60640
60765
  continue;
@@ -60796,14 +60921,14 @@ async function reconcileCanvasItems(filenames) {
60796
60921
  // src/services/upload-chat-transcripts.ts
60797
60922
  import { createReadStream as createReadStream2 } from "fs";
60798
60923
  import { createHash as createHash8 } from "crypto";
60799
- import { readFile as readFile22, readdir as readdir9, stat as stat5 } from "fs/promises";
60800
- import { basename as basename4, join as join33 } from "path";
60924
+ import { readFile as readFile21, readdir as readdir9, stat as stat5 } from "fs/promises";
60925
+ import { basename as basename4, join as join34 } from "path";
60801
60926
 
60802
60927
  // src/services/chat/chat-senders.ts
60803
- import { join as join32 } from "path";
60804
- var CHAT_SENDERS_DIR = join32(ENGINE_DIR2, "chat-senders");
60928
+ import { join as join33 } from "path";
60929
+ var CHAT_SENDERS_DIR = join33(ENGINE_DIR2, "chat-senders");
60805
60930
  function chatMessageSendersFilePath(chatId) {
60806
- return join32(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
60931
+ return join33(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
60807
60932
  }
60808
60933
  function parseChatMessageSendersJsonl(content) {
60809
60934
  return content.split("\n").flatMap((line) => {
@@ -60846,7 +60971,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
60846
60971
  const chat = chatsById.get(chatId);
60847
60972
  if (!shouldFlushChatTranscript(dir, chat, chatsById)) continue;
60848
60973
  tasks.push(
60849
- uploadChatTranscript(chatId, join33(dir, entry), chat, capture).then((artifact) => {
60974
+ uploadChatTranscript(chatId, join34(dir, entry), chat, capture).then((artifact) => {
60850
60975
  flushed++;
60851
60976
  if (artifact && capture) revisions.push(artifact);
60852
60977
  }).catch((err) => {
@@ -60882,7 +61007,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
60882
61007
  }
60883
61008
  try {
60884
61009
  metadata.senders = parseChatMessageSendersJsonl(
60885
- await readFile22(chatMessageSendersFilePath(chatId), "utf-8")
61010
+ await readFile21(chatMessageSendersFilePath(chatId), "utf-8")
60886
61011
  );
60887
61012
  } catch (error) {
60888
61013
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
@@ -60922,7 +61047,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
60922
61047
  // src/services/upload-engine-logs.ts
60923
61048
  import { createReadStream as createReadStream3 } from "fs";
60924
61049
  import { readdir as readdir10, stat as stat6 } from "fs/promises";
60925
- import { join as join34 } from "path";
61050
+ import { join as join35 } from "path";
60926
61051
  var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
60927
61052
  var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
60928
61053
  var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
@@ -60951,7 +61076,7 @@ async function flushAllEngineLogs() {
60951
61076
  const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
60952
61077
  try {
60953
61078
  const sessionId = filename.slice(0, -".log".length);
60954
- const filePath = join34(LOG_DIR, filename);
61079
+ const filePath = join35(LOG_DIR, filename);
60955
61080
  const fileStat = await runBeforeDeadline(() => stat6(filePath), deadline);
60956
61081
  if (!fileStat.isFile()) {
60957
61082
  skipped++;
@@ -61047,8 +61172,8 @@ async function uploadEngineLog(input, timeoutMs) {
61047
61172
  }
61048
61173
 
61049
61174
  // src/services/chat/fork-handoff.ts
61050
- import { mkdir as mkdir20, open as open3, writeFile as writeFile11 } from "fs/promises";
61051
- import { join as join35 } from "path";
61175
+ import { mkdir as mkdir21, open as open3, writeFile as writeFile12 } from "fs/promises";
61176
+ import { join as join36 } from "path";
61052
61177
  var EXCERPT_MAX_CHARS = 24e3;
61053
61178
  var MAX_SANITIZED_SOURCE_BYTES = 32 * 1024 * 1024;
61054
61179
  function unansweredRequest(blocks) {
@@ -61083,7 +61208,7 @@ function tailBlocks(blocks, maxChars) {
61083
61208
  return { excerpt, truncated: start > 0 };
61084
61209
  }
61085
61210
  async function buildForkHandoff(input) {
61086
- const rawHistoryPath = join35(HISTORY_DIR_BY_PROVIDER[input.sourceProvider], `${input.sourceChatId}.jsonl`);
61211
+ const rawHistoryPath = join36(HISTORY_DIR_BY_PROVIDER[input.sourceProvider], `${input.sourceChatId}.jsonl`);
61087
61212
  const { content, truncated: sourceTruncated } = await readHistoryTail(rawHistoryPath, MAX_SANITIZED_SOURCE_BYTES);
61088
61213
  let blocks = canonicalizeChatTranscriptBlocks(content, input.executionProvider, input.senders);
61089
61214
  if (input.turnLimit !== void 0) {
@@ -61091,13 +61216,13 @@ async function buildForkHandoff(input) {
61091
61216
  const start = blocks.findLastIndex((block) => block.startsWith("## User") && ++turns === input.turnLimit);
61092
61217
  blocks = blocks.slice(Math.max(0, start));
61093
61218
  }
61094
- const forkDir = join35(FORKS_DIR, input.targetChatId);
61095
- await mkdir20(forkDir, { recursive: true });
61096
- const transcriptPath = join35(forkDir, "source-transcript.md");
61219
+ const forkDir = join36(FORKS_DIR, input.targetChatId);
61220
+ await mkdir21(forkDir, { recursive: true });
61221
+ const transcriptPath = join36(forkDir, "source-transcript.md");
61097
61222
  const header = sourceTruncated ? `_This file was generated from only the most recent ${MAX_SANITIZED_SOURCE_BYTES / 1024 / 1024} MB of the source chat.${input.turnLimit === void 0 ? ` The complete raw event log is at ${rawHistoryPath}; grep it for anything earlier.` : ""}_
61098
61223
 
61099
61224
  ` : "";
61100
- await writeFile11(transcriptPath, `${header}${blocks.join("\n\n")}
61225
+ await writeFile12(transcriptPath, `${header}${blocks.join("\n\n")}
61101
61226
  `, "utf-8");
61102
61227
  const { excerpt, truncated } = tailBlocks(blocks, EXCERPT_MAX_CHARS);
61103
61228
  return {
@@ -61235,13 +61360,13 @@ var ChatService = class {
61235
61360
  persistPromise = null;
61236
61361
  persistQueued = false;
61237
61362
  async initialize() {
61238
- await mkdir21(ENGINE_DIR2, { recursive: true });
61239
- await mkdir21(CLAUDE_HISTORY_DIR, { recursive: true });
61240
- await mkdir21(RELAY_HISTORY_DIR, { recursive: true });
61241
- await mkdir21(CODEX_HISTORY_DIR, { recursive: true });
61242
- await mkdir21(CURSOR_HISTORY_DIR, { recursive: true });
61243
- await mkdir21(OPENCODE_HISTORY_DIR, { recursive: true });
61244
- await mkdir21(CHAT_SENDERS_DIR, { recursive: true });
61363
+ await mkdir22(ENGINE_DIR2, { recursive: true });
61364
+ await mkdir22(CLAUDE_HISTORY_DIR, { recursive: true });
61365
+ await mkdir22(RELAY_HISTORY_DIR, { recursive: true });
61366
+ await mkdir22(CODEX_HISTORY_DIR, { recursive: true });
61367
+ await mkdir22(CURSOR_HISTORY_DIR, { recursive: true });
61368
+ await mkdir22(OPENCODE_HISTORY_DIR, { recursive: true });
61369
+ await mkdir22(CHAT_SENDERS_DIR, { recursive: true });
61245
61370
  const persisted = await this.loadChats();
61246
61371
  for (const chat of persisted) {
61247
61372
  const runtime = this.createRuntimeChat(chat);
@@ -61570,7 +61695,7 @@ var ChatService = class {
61570
61695
  }
61571
61696
  }
61572
61697
  async readSenders(chatId) {
61573
- return readFile23(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
61698
+ return readFile22(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
61574
61699
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
61575
61700
  console.error("[ChatService] Failed to read sender records:", error);
61576
61701
  return [];
@@ -61823,14 +61948,14 @@ var ChatService = class {
61823
61948
  return descendants;
61824
61949
  }
61825
61950
  async deleteHistoryFile(persisted) {
61826
- const historyPath = join36(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
61951
+ const historyPath = join37(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
61827
61952
  await Promise.all([
61828
61953
  historyPath,
61829
61954
  `${historyPath}.pages.jsonl`,
61830
61955
  `${historyPath}.pages.index.json`
61831
61956
  ].map((path7) => rm4(path7, { force: true })));
61832
61957
  await rm4(chatMessageSendersFilePath(persisted.id), { force: true });
61833
- await rm4(join36(FORKS_DIR, persisted.id), { recursive: true, force: true });
61958
+ await rm4(join37(FORKS_DIR, persisted.id), { recursive: true, force: true });
61834
61959
  }
61835
61960
  /** Resolves once the target chat exists; the handoff and its first turn run in the background. */
61836
61961
  async forkChat(sourceChatId, request, trigger = "manual", reason) {
@@ -62098,7 +62223,7 @@ var ChatService = class {
62098
62223
  }
62099
62224
  const baseProvider = getChatExecutionProvider(persisted);
62100
62225
  const additionalInstructions = persisted.provider === "relay" ? buildRelayInstructions(this.workingDirectory) : void 0;
62101
- const historyFilePath = join36(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
62226
+ const historyFilePath = join37(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
62102
62227
  const managerOptions = {
62103
62228
  provider: baseProvider,
62104
62229
  workingDirectory: this.workingDirectory,
@@ -62344,7 +62469,7 @@ var ChatService = class {
62344
62469
  chat.quotaExhaustion = null;
62345
62470
  uploadChatTranscript(
62346
62471
  chatId,
62347
- join36(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
62472
+ join37(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
62348
62473
  this.toTranscriptSource(chat)
62349
62474
  ).catch((err) => {
62350
62475
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -62395,7 +62520,7 @@ var ChatService = class {
62395
62520
  }
62396
62521
  async loadChats() {
62397
62522
  try {
62398
- const content = await readFile23(CHATS_FILE, "utf-8");
62523
+ const content = await readFile22(CHATS_FILE, "utf-8");
62399
62524
  return parsePersistedChatsContent(content);
62400
62525
  } catch (error) {
62401
62526
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -62410,7 +62535,7 @@ var ChatService = class {
62410
62535
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
62411
62536
  }
62412
62537
  try {
62413
- const backupContent = await readFile23(CHATS_BACKUP_FILE, "utf-8");
62538
+ const backupContent = await readFile22(CHATS_BACKUP_FILE, "utf-8");
62414
62539
  return parsePersistedChatsContent(backupContent);
62415
62540
  } catch (backupError) {
62416
62541
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -62513,14 +62638,14 @@ var ChatService = class {
62513
62638
 
62514
62639
  // src/services/repo-file-service.ts
62515
62640
  import { execFile as execFile7 } from "child_process";
62516
- import { readFile as readFile24 } from "fs/promises";
62641
+ import { readFile as readFile23 } from "fs/promises";
62517
62642
  import { extname as extname3 } from "path";
62518
62643
 
62519
62644
  // src/services/language-server-service.ts
62520
62645
  import { spawn as spawn9 } from "child_process";
62521
62646
  import { existsSync as existsSync17, readFileSync as readFileSync8 } from "fs";
62522
62647
  import { createRequire as createRequire6 } from "module";
62523
- import { delimiter as delimiter2, dirname as dirname19, join as join37, relative as relative2 } from "path";
62648
+ import { delimiter as delimiter2, dirname as dirname19, join as join38, relative as relative2 } from "path";
62524
62649
  import { setTimeout as setTimeout2 } from "timers/promises";
62525
62650
  import { fileURLToPath as fileURLToPath6, pathToFileURL } from "url";
62526
62651
  import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node.js";
@@ -62639,7 +62764,7 @@ var LanguageServerService = class {
62639
62764
  if (config.package && config.bin) command = process.execPath;
62640
62765
  else if (config.command) {
62641
62766
  const commandName = config.command;
62642
- command = process.env.PATH?.split(delimiter2).map((directory) => join37(directory, commandName)).find(existsSync17);
62767
+ command = process.env.PATH?.split(delimiter2).map((directory) => join38(directory, commandName)).find(existsSync17);
62643
62768
  }
62644
62769
  const args = config.package && config.bin ? [packageBin(config.package, config.bin), ...config.args] : config.args;
62645
62770
  if (!command) return null;
@@ -62810,7 +62935,7 @@ var RepoFileService = class {
62810
62935
  sizeBytes,
62811
62936
  binary: true,
62812
62937
  tooLarge: false,
62813
- base64: (await readFile24(fullPath)).toString("base64"),
62938
+ base64: (await readFile23(fullPath)).toString("base64"),
62814
62939
  mimeType
62815
62940
  };
62816
62941
  }
@@ -62835,7 +62960,7 @@ var RepoFileService = class {
62835
62960
  tooLarge: true
62836
62961
  };
62837
62962
  }
62838
- const content = await readFile24(fullPath, "utf-8");
62963
+ const content = await readFile23(fullPath, "utf-8");
62839
62964
  return {
62840
62965
  repoName,
62841
62966
  path: filePath,
@@ -62867,7 +62992,7 @@ var RepoFileService = class {
62867
62992
  async inspectSymbol(repoName, filePath, line, character, action) {
62868
62993
  const resolved = await this.resolveFile(repoName, filePath);
62869
62994
  if (!resolved || line < 1 || character < 0 || isBinaryExtension(filePath)) return null;
62870
- const content = await readFile24(resolved.fullPath, "utf8");
62995
+ const content = await readFile23(resolved.fullPath, "utf8");
62871
62996
  const sourceLine = content.split("\n")[line - 1];
62872
62997
  if (sourceLine === void 0) return null;
62873
62998
  const symbolMatch = Array.from(sourceLine.matchAll(/[\p{L}_$][\p{L}\p{N}_$]*/gu)).find((match) => character >= match.index && character <= match.index + match[0].length);
@@ -62980,21 +63105,21 @@ var RepoFileService = class {
62980
63105
  // src/v1-routes.ts
62981
63106
  import { Hono } from "hono";
62982
63107
  import { z as z6 } from "zod";
62983
- import { readdir as readdir12, stat as stat7, readFile as readFile27 } from "fs/promises";
62984
- import { join as join40, resolve as resolve5 } from "path";
63108
+ import { readdir as readdir12, stat as stat7, readFile as readFile26 } from "fs/promises";
63109
+ import { join as join41, resolve as resolve5 } from "path";
62985
63110
 
62986
63111
  // src/services/warm-hooks-service.ts
62987
63112
  import { spawn as spawn10 } from "child_process";
62988
- import { readFile as readFile26 } from "fs/promises";
63113
+ import { readFile as readFile25 } from "fs/promises";
62989
63114
  import { existsSync as existsSync18 } from "fs";
62990
- import { join as join39 } from "path";
63115
+ import { join as join40 } from "path";
62991
63116
 
62992
63117
  // src/services/warm-hook-logs-service.ts
62993
- import { mkdir as mkdir22, readFile as readFile25, writeFile as writeFile12, readdir as readdir11, appendFile as appendFile6, unlink as unlink4 } from "fs/promises";
63118
+ import { mkdir as mkdir23, readFile as readFile24, writeFile as writeFile13, readdir as readdir11, appendFile as appendFile6, unlink as unlink4 } from "fs/promises";
62994
63119
  import { homedir as homedir14 } from "os";
62995
- import { join as join38 } from "path";
62996
- var LOGS_DIR2 = join38(homedir14(), ".replicas", "warm-hook-logs");
62997
- var CURRENT_RUN_LOG = join38(LOGS_DIR2, "current-run.log");
63120
+ import { join as join39 } from "path";
63121
+ var LOGS_DIR2 = join39(homedir14(), ".replicas", "warm-hook-logs");
63122
+ var CURRENT_RUN_LOG = join39(LOGS_DIR2, "current-run.log");
62998
63123
  var GLOBAL_FILENAME = "global.json";
62999
63124
  function withPreview2(stored) {
63000
63125
  const preview = buildHookOutputPreview(stored.output);
@@ -63002,7 +63127,7 @@ function withPreview2(stored) {
63002
63127
  }
63003
63128
  var WarmHookLogsService = class {
63004
63129
  async ensureDir() {
63005
- await mkdir22(LOGS_DIR2, { recursive: true });
63130
+ await mkdir23(LOGS_DIR2, { recursive: true });
63006
63131
  }
63007
63132
  async saveGlobalHookLog(entry) {
63008
63133
  await this.ensureDir();
@@ -63011,7 +63136,7 @@ var WarmHookLogsService = class {
63011
63136
  hookName: "organization",
63012
63137
  ...entry
63013
63138
  };
63014
- await writeFile12(join38(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
63139
+ await writeFile13(join39(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
63015
63140
  `, "utf-8");
63016
63141
  }
63017
63142
  async saveEnvironmentHookLog(entry) {
@@ -63021,7 +63146,7 @@ var WarmHookLogsService = class {
63021
63146
  hookName: "environment",
63022
63147
  ...entry
63023
63148
  };
63024
- await writeFile12(join38(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
63149
+ await writeFile13(join39(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
63025
63150
  `, "utf-8");
63026
63151
  }
63027
63152
  async saveRepoHookLog(repoName, entry) {
@@ -63031,7 +63156,7 @@ var WarmHookLogsService = class {
63031
63156
  hookName: repoName,
63032
63157
  ...entry
63033
63158
  };
63034
- await writeFile12(join38(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
63159
+ await writeFile13(join39(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
63035
63160
  `, "utf-8");
63036
63161
  }
63037
63162
  async getAllLogs() {
@@ -63050,7 +63175,7 @@ var WarmHookLogsService = class {
63050
63175
  continue;
63051
63176
  }
63052
63177
  try {
63053
- const raw = await readFile25(join38(LOGS_DIR2, file), "utf-8");
63178
+ const raw = await readFile24(join39(LOGS_DIR2, file), "utf-8");
63054
63179
  const stored = JSON.parse(raw);
63055
63180
  logs.push(withPreview2(stored));
63056
63181
  } catch {
@@ -63079,7 +63204,7 @@ var WarmHookLogsService = class {
63079
63204
  }
63080
63205
  async getCurrentRunLog() {
63081
63206
  try {
63082
- return await readFile25(CURRENT_RUN_LOG, "utf-8");
63207
+ return await readFile24(CURRENT_RUN_LOG, "utf-8");
63083
63208
  } catch (err) {
63084
63209
  if (err.code === "ENOENT") return null;
63085
63210
  throw err;
@@ -63088,7 +63213,7 @@ var WarmHookLogsService = class {
63088
63213
  async getFullOutput(hookType, hookName) {
63089
63214
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
63090
63215
  try {
63091
- const raw = await readFile25(join38(LOGS_DIR2, filename), "utf-8");
63216
+ const raw = await readFile24(join39(LOGS_DIR2, filename), "utf-8");
63092
63217
  const stored = JSON.parse(raw);
63093
63218
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
63094
63219
  return null;
@@ -63107,12 +63232,12 @@ var warmHookLogsService = new WarmHookLogsService();
63107
63232
  // src/services/warm-hooks-service.ts
63108
63233
  async function readRepoWarmHook(repoPath) {
63109
63234
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
63110
- const configPath = join39(repoPath, filename);
63235
+ const configPath = join40(repoPath, filename);
63111
63236
  if (!existsSync18(configPath)) {
63112
63237
  continue;
63113
63238
  }
63114
63239
  try {
63115
- const raw = await readFile26(configPath, "utf-8");
63240
+ const raw = await readFile25(configPath, "utf-8");
63116
63241
  const config = parseReplicasConfigString(raw, filename);
63117
63242
  if (!config.warmHook) {
63118
63243
  return null;
@@ -64441,7 +64566,7 @@ data: ${JSON.stringify("Terminal session not found")}
64441
64566
  const logFiles = files.filter((f) => f.endsWith(".log"));
64442
64567
  const sessions = await Promise.all(
64443
64568
  logFiles.map(async (filename) => {
64444
- const filePath = join40(LOG_DIR, filename);
64569
+ const filePath = join41(LOG_DIR, filename);
64445
64570
  const fileStat = await stat7(filePath);
64446
64571
  const sessionId = filename.replace(/\.log$/, "");
64447
64572
  return {
@@ -64476,7 +64601,7 @@ data: ${JSON.stringify("Terminal session not found")}
64476
64601
  }
64477
64602
  let content;
64478
64603
  try {
64479
- content = await readFile27(filePath, "utf-8");
64604
+ content = await readFile26(filePath, "utf-8");
64480
64605
  } catch {
64481
64606
  return c.json(jsonError("Log session not found"), 404);
64482
64607
  }
@@ -64547,7 +64672,7 @@ var HeartbeatService = class _HeartbeatService {
64547
64672
  var heartbeatService = new HeartbeatService();
64548
64673
 
64549
64674
  // src/services/workspace-sdk-service.ts
64550
- import { cp, mkdir as mkdir23 } from "fs/promises";
64675
+ import { cp, mkdir as mkdir24 } from "fs/promises";
64551
64676
  import { dirname as dirname20, resolve as resolve6 } from "path";
64552
64677
  import { fileURLToPath as fileURLToPath7 } from "url";
64553
64678
  async function installWorkspaceSdk() {
@@ -64557,7 +64682,7 @@ async function installWorkspaceSdk() {
64557
64682
  "/tmp/node_modules/@replicas/sdk"
64558
64683
  ];
64559
64684
  await Promise.all(targets.map(async (target) => {
64560
- await mkdir23(target, { recursive: true });
64685
+ await mkdir24(target, { recursive: true });
64561
64686
  await cp(source, target, { recursive: true, force: true });
64562
64687
  }));
64563
64688
  }