replicas-engine 0.1.451 → 0.1.453

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/src/index.js +279 -69
  2. package/package.json +3 -1
package/dist/src/index.js CHANGED
@@ -43,10 +43,13 @@ function createErrorResult(error) {
43
43
  }
44
44
 
45
45
  // ../shared/src/agent.ts
46
- var VALID_AGENT_PROVIDERS = ["claude", "codex", "cursor", "opencode", "relay"];
46
+ var VALID_AGENT_PROVIDERS = ["claude", "codex", "cursor", "opencode", "pi", "relay"];
47
47
  var VALID_CODING_AGENT_PROVIDERS = VALID_AGENT_PROVIDERS.filter(
48
48
  (provider) => provider !== "relay"
49
49
  );
50
+ function isValidAgentProvider(value) {
51
+ return VALID_AGENT_PROVIDERS.some((p) => p === value);
52
+ }
50
53
  var VALID_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
51
54
  var CODEX_REASONING_EFFORT_BY_THINKING_LEVEL = {
52
55
  low: "low",
@@ -166,6 +169,7 @@ var DEFAULT_CHAT_TITLES = {
166
169
  codex: "Codex",
167
170
  cursor: "Cursor",
168
171
  opencode: "Opencode",
172
+ pi: "Pi",
169
173
  relay: "Relay"
170
174
  };
171
175
  function isDefaultChat(chat) {
@@ -188,6 +192,13 @@ var GPT_5_6_LUNA_MODEL = "gpt-5.6-luna";
188
192
  var DEFAULT_CODEX_MODEL = GPT_5_6_SOL_MODEL;
189
193
  var DEFAULT_CURSOR_MODEL = "composer-2.5";
190
194
  var DEFAULT_OPENCODE_MODEL = "z-ai/glm-5.2";
195
+ var DEFAULT_PI_MODEL = DEFAULT_OPENCODE_MODEL;
196
+ var OPENROUTER_MODELS = [
197
+ DEFAULT_OPENCODE_MODEL,
198
+ "minimax/minimax-m3",
199
+ "xiaomi/mimo-v2.5-pro",
200
+ "moonshotai/kimi-k2.6"
201
+ ];
191
202
  function normalizeClaudeModel(model) {
192
203
  if (model === "opus" || model === CLAUDE_OPUS_1M_MODEL || model === LEGACY_CLAUDE_OPUS_1M_MODEL) {
193
204
  return DEFAULT_CLAUDE_MODEL;
@@ -256,7 +267,8 @@ var AGENT_MODELS = {
256
267
  CLAUDE_HAIKU_4_5_MODEL,
257
268
  "kimi-k2.5"
258
269
  ],
259
- opencode: [DEFAULT_OPENCODE_MODEL, "minimax/minimax-m3", "xiaomi/mimo-v2.5-pro", "moonshotai/kimi-k2.6"],
270
+ opencode: OPENROUTER_MODELS,
271
+ pi: OPENROUTER_MODELS,
260
272
  relay: [CLAUDE_FABLE_5_MODEL, DEFAULT_CLAUDE_MODEL, CLAUDE_SONNET_5_MODEL]
261
273
  };
262
274
  var MODEL_LABELS = {
@@ -516,7 +528,7 @@ var WORKSPACE_SIZES = ["small", "large"];
516
528
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
517
529
 
518
530
  // ../shared/src/e2b.ts
519
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-19-v3";
531
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-19-v5";
520
532
 
521
533
  // ../shared/src/runtime-env.ts
522
534
  function shellQuotePosix(value) {
@@ -672,7 +684,7 @@ var SLASH_COMMANDS = [
672
684
  command: "/plan",
673
685
  description: "Switch to plan mode and optionally send a prompt.",
674
686
  argumentHint: "[prompt]",
675
- providers: ["claude", "codex", "cursor", "opencode", "relay"]
687
+ providers: ["claude", "codex", "cursor", "opencode", "pi", "relay"]
676
688
  },
677
689
  {
678
690
  command: "/fast",
@@ -2730,6 +2742,9 @@ var DESKTOP_NOVNC_PORT = 6080;
2730
2742
 
2731
2743
  // ../shared/src/engine/v1.ts
2732
2744
  var MERGED_MESSAGE_SEPARATOR = "\n\n<!-- replicas:merged -->\n\n";
2745
+ var ENGINE_HEALTH_WAIT_HEADER = "X-Replicas-Health-Wait";
2746
+ var ENGINE_HEALTH_WAIT_QUERY_PARAM = "wait_ms";
2747
+ var ENGINE_HEALTH_MAX_WAIT_MS = 2e3;
2733
2748
  function normalizeCodexAspTranscriptStatus(status, failed = false) {
2734
2749
  if (failed || status === "failed" || status === "declined") return "failed";
2735
2750
  if (status === "completed") return "completed";
@@ -5012,6 +5027,9 @@ function detectCursorAuthMethod() {
5012
5027
  function detectOpencodeAuthMethod() {
5013
5028
  return existsSync3(OPENCODE_AUTH_PATH) || ENGINE_ENV.OPENROUTER_API_KEY ? "api_key" : "none";
5014
5029
  }
5030
+ function detectPiAuthMethod() {
5031
+ return ENGINE_ENV.OPENROUTER_API_KEY ? "api_key" : "none";
5032
+ }
5015
5033
  async function detectGitIdentityConfigured() {
5016
5034
  try {
5017
5035
  const { stdout } = await execFileAsync("git", ["config", "--global", "user.name"]);
@@ -5069,6 +5087,7 @@ function createDefaultDetails() {
5069
5087
  codexAuthMethod: "none",
5070
5088
  cursorAuthMethod: "none",
5071
5089
  opencodeAuthMethod: "none",
5090
+ piAuthMethod: "none",
5072
5091
  lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
5073
5092
  };
5074
5093
  }
@@ -5104,6 +5123,7 @@ var EnvironmentDetailsService = class {
5104
5123
  details.codexAuthMethod = detectCodexAuthMethod();
5105
5124
  details.cursorAuthMethod = detectCursorAuthMethod();
5106
5125
  details.opencodeAuthMethod = detectOpencodeAuthMethod();
5126
+ details.piAuthMethod = detectPiAuthMethod();
5107
5127
  details.gitIdentityConfigured = gitIdentityConfigured;
5108
5128
  const ghConfigured = existsSync3(GH_HOSTS_PATH);
5109
5129
  details.githubAccessConfigured = ghConfigured;
@@ -5854,9 +5874,9 @@ async function registerDesktopPreview() {
5854
5874
 
5855
5875
  // src/services/chat/chat-service.ts
5856
5876
  import { existsSync as existsSync7 } from "fs";
5857
- import { appendFile as appendFile3, copyFile, mkdir as mkdir13, readFile as readFile14, rename as rename2, rm as rm2 } from "fs/promises";
5877
+ import { appendFile as appendFile3, copyFile, mkdir as mkdir14, readFile as readFile14, rename as rename2, rm as rm2 } from "fs/promises";
5858
5878
  import { homedir as homedir15 } from "os";
5859
- import { join as join21 } from "path";
5879
+ import { join as join22 } from "path";
5860
5880
  import { randomUUID as randomUUID5 } from "crypto";
5861
5881
 
5862
5882
  // src/managers/claude-manager.ts
@@ -6576,16 +6596,16 @@ var CodingAgentManager = class {
6576
6596
  });
6577
6597
  }
6578
6598
  recordHistoryEvent(type, payload, historyFile) {
6579
- const eventPayload = {};
6599
+ const eventPayload2 = {};
6580
6600
  if (payload && typeof payload === "object") {
6581
- Object.assign(eventPayload, payload);
6601
+ Object.assign(eventPayload2, payload);
6582
6602
  } else {
6583
- eventPayload.value = payload;
6603
+ eventPayload2.value = payload;
6584
6604
  }
6585
6605
  const event = {
6586
6606
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6587
6607
  type,
6588
- payload: eventPayload
6608
+ payload: eventPayload2
6589
6609
  };
6590
6610
  this.onEvent(event);
6591
6611
  historyFile.append(event);
@@ -8569,7 +8589,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
8569
8589
  var MIN_CODEX_CLI_VERSION = "0.144.0";
8570
8590
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
8571
8591
  var codexCliVersionEnsured = null;
8572
- var ENGINE_PACKAGE_VERSION = "0.1.451";
8592
+ var ENGINE_PACKAGE_VERSION = "0.1.453";
8573
8593
  var INITIALIZE_METHOD = "initialize";
8574
8594
  var INITIALIZED_NOTIFICATION = "initialized";
8575
8595
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -9447,12 +9467,14 @@ var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
9447
9467
  var CODEX_HISTORY_DIR = join15(ENGINE_DIR2, "codex-histories");
9448
9468
  var CURSOR_HISTORY_DIR = join15(ENGINE_DIR2, "cursor-histories");
9449
9469
  var OPENCODE_HISTORY_DIR = join15(ENGINE_DIR2, "opencode-histories");
9470
+ var PI_HISTORY_DIR = join15(ENGINE_DIR2, "pi-histories");
9450
9471
  var HISTORY_DIR_BY_PROVIDER = {
9451
9472
  claude: CLAUDE_HISTORY_DIR,
9452
9473
  relay: RELAY_HISTORY_DIR,
9453
9474
  codex: CODEX_HISTORY_DIR,
9454
9475
  cursor: CURSOR_HISTORY_DIR,
9455
- opencode: OPENCODE_HISTORY_DIR
9476
+ opencode: OPENCODE_HISTORY_DIR,
9477
+ pi: PI_HISTORY_DIR
9456
9478
  };
9457
9479
 
9458
9480
  // src/services/chat/errors.ts
@@ -11744,6 +11766,162 @@ var OpencodeManager = class extends CodingAgentManager {
11744
11766
  }
11745
11767
  };
11746
11768
 
11769
+ // src/managers/pi-manager.ts
11770
+ import { mkdir as mkdir13 } from "fs/promises";
11771
+ import { dirname as dirname7, join as join19 } from "path";
11772
+ import {
11773
+ AuthStorage,
11774
+ createAgentSession,
11775
+ ModelRegistry,
11776
+ SessionManager,
11777
+ DefaultResourceLoader
11778
+ } from "@mariozechner/pi-coding-agent";
11779
+ function eventPayload(event) {
11780
+ return isRecord4(event) ? { ...event } : { value: event };
11781
+ }
11782
+ function registerCommandProtection(cwd) {
11783
+ return (pi) => {
11784
+ pi.on("tool_call", async (event) => {
11785
+ const command = extractToolCommand(event.input);
11786
+ const result = await evaluateCommandProtection({
11787
+ provider: "pi",
11788
+ source: "pi_tool_call",
11789
+ toolName: event.toolName,
11790
+ toolInput: event.input,
11791
+ command,
11792
+ cwd,
11793
+ toolUseId: event.toolCallId
11794
+ });
11795
+ if (!result.allowed) {
11796
+ reportCommandProtectionBlock({ provider: "pi", toolName: event.toolName, command, reason: result.reason });
11797
+ return { block: true, reason: result.reason ?? "Command blocked by Replicas protection." };
11798
+ }
11799
+ return void 0;
11800
+ });
11801
+ };
11802
+ }
11803
+ var PiManager = class extends CodingAgentManager {
11804
+ session = null;
11805
+ unsubscribe = null;
11806
+ activeSessionFile = null;
11807
+ historyFilePath;
11808
+ historyFile;
11809
+ constructor(options) {
11810
+ super(options);
11811
+ this.historyFilePath = options.historyFilePath ?? join19(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
11812
+ this.historyFile = new CodexHistoryFile(this.historyFilePath);
11813
+ this.initializeManager(this.processMessageInternal.bind(this));
11814
+ }
11815
+ async initialize() {
11816
+ await mkdir13(dirname7(this.historyFilePath), { recursive: true });
11817
+ }
11818
+ async interruptActiveTurn() {
11819
+ await this.session?.abort();
11820
+ }
11821
+ async steerRequest(request) {
11822
+ if (!this.session?.isStreaming) return false;
11823
+ await this.session.steer(request.message);
11824
+ return true;
11825
+ }
11826
+ async getHistory() {
11827
+ await this.historyFile.flush();
11828
+ const history = await this.historyFile.load();
11829
+ return { thread_id: this.activeSessionFile ?? this.initialSessionId, events: history.events, goal: null };
11830
+ }
11831
+ async listSlashCommands() {
11832
+ await this.initialized;
11833
+ return mergeSlashCommands(...(this.session?.promptTemplates ?? []).map((template) => createProviderSlashCommand("pi", template.name, template.description)).filter((command) => Boolean(command)));
11834
+ }
11835
+ dispose() {
11836
+ this.unsubscribe?.();
11837
+ this.unsubscribe = null;
11838
+ this.session?.dispose();
11839
+ this.session = null;
11840
+ }
11841
+ async ensureSession(request) {
11842
+ if (this.session) {
11843
+ if (request.model && request.model !== this.session.model?.id) {
11844
+ const model2 = this.session.modelRegistry.find("openrouter", request.model);
11845
+ if (model2) await this.session.setModel(model2);
11846
+ }
11847
+ if (request.thinkingLevel) this.session.setThinkingLevel(request.thinkingLevel);
11848
+ return this.session;
11849
+ }
11850
+ const apiKey = ENGINE_ENV.OPENROUTER_API_KEY;
11851
+ if (!apiKey) throw new Error("OpenRouter API key is not configured for Pi.");
11852
+ const authStorage = AuthStorage.inMemory({ openrouter: { type: "api_key", key: apiKey } });
11853
+ const modelRegistry = ModelRegistry.create(authStorage);
11854
+ const openRouterModels = modelRegistry.getAll().filter((candidate) => candidate.provider === "openrouter");
11855
+ const modelTemplate = openRouterModels[0];
11856
+ if (!modelTemplate) throw new Error("Pi OpenRouter model catalog is empty.");
11857
+ modelRegistry.registerProvider("openrouter", {
11858
+ api: modelTemplate.api,
11859
+ apiKey,
11860
+ baseUrl: modelTemplate.baseUrl,
11861
+ models: [
11862
+ ...openRouterModels,
11863
+ ...OPENROUTER_MODELS.filter((id) => !openRouterModels.some((candidate) => candidate.id === id)).map((id) => ({
11864
+ id,
11865
+ name: id,
11866
+ api: modelTemplate.api,
11867
+ baseUrl: modelTemplate.baseUrl,
11868
+ reasoning: modelTemplate.reasoning,
11869
+ input: modelTemplate.input,
11870
+ cost: modelTemplate.cost,
11871
+ contextWindow: modelTemplate.contextWindow,
11872
+ maxTokens: modelTemplate.maxTokens
11873
+ }))
11874
+ ]
11875
+ });
11876
+ const modelId = request.model ?? DEFAULT_PI_MODEL;
11877
+ const model = modelRegistry.find("openrouter", modelId) ?? modelRegistry.find("openrouter", DEFAULT_PI_MODEL);
11878
+ if (!model) throw new Error(`Pi model is not available through OpenRouter: ${modelId}`);
11879
+ const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
11880
+ const resourceLoader = new DefaultResourceLoader({
11881
+ cwd: this.workingDirectory,
11882
+ agentDir: join19(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
11883
+ extensionFactories: [registerCommandProtection(this.workingDirectory)],
11884
+ appendSystemPrompt: [this.buildCombinedInstructions() ?? ""]
11885
+ });
11886
+ await resourceLoader.reload();
11887
+ const result = await createAgentSession({
11888
+ cwd: this.workingDirectory,
11889
+ authStorage,
11890
+ modelRegistry,
11891
+ model,
11892
+ sessionManager,
11893
+ resourceLoader
11894
+ });
11895
+ this.session = result.session;
11896
+ if (request.thinkingLevel) this.session.setThinkingLevel(request.thinkingLevel);
11897
+ this.activeSessionFile = sessionManager.getSessionFile() ?? null;
11898
+ await this.onSaveSessionId(this.activeSessionFile);
11899
+ this.unsubscribe = this.session.subscribe((event) => this.handleEvent(event));
11900
+ return this.session;
11901
+ }
11902
+ handleEvent(event) {
11903
+ const payload = eventPayload(event);
11904
+ this.recordHistoryEvent(`pi-${event.type}`, payload, this.historyFile);
11905
+ }
11906
+ async processMessageInternal(request) {
11907
+ const session = await this.ensureSession(request);
11908
+ this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
11909
+ try {
11910
+ const images = request.images && request.images.length > 0 ? (await normalizeImages(request.images)).map((image) => ({
11911
+ type: "image",
11912
+ data: image.source.data,
11913
+ mimeType: image.source.media_type
11914
+ })) : void 0;
11915
+ await session.prompt(request.message, images ? { images } : void 0);
11916
+ } catch (error) {
11917
+ this.recordHistoryEvent("pi-error", { message: error instanceof Error ? error.message : String(error) }, this.historyFile);
11918
+ throw error;
11919
+ } finally {
11920
+ await this.onTurnComplete();
11921
+ }
11922
+ }
11923
+ };
11924
+
11747
11925
  // src/managers/relay-tools.ts
11748
11926
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
11749
11927
  import { z } from "zod";
@@ -11753,15 +11931,17 @@ function getAvailableRelayProviders(availability) {
11753
11931
  const codexAvailable = availability.codexAvailable ?? false;
11754
11932
  const cursorAvailable = availability.cursorAvailable ?? false;
11755
11933
  const opencodeAvailable = availability.opencodeAvailable ?? false;
11934
+ const piAvailable = availability.piAvailable ?? false;
11756
11935
  const providers = ["claude"];
11757
11936
  if (codexAvailable) providers.push("codex");
11758
11937
  if (cursorAvailable) providers.push("cursor");
11759
11938
  if (opencodeAvailable) providers.push("opencode");
11939
+ if (piAvailable) providers.push("pi");
11760
11940
  providers.push("relay");
11761
11941
  return providers;
11762
11942
  }
11763
11943
  function getAvailableCodeProviders(availability) {
11764
- return getAvailableRelayProviders(availability).filter((provider) => provider === "codex" || provider === "cursor" || provider === "opencode");
11944
+ return getAvailableRelayProviders(availability).filter((provider) => provider === "codex" || provider === "cursor" || provider === "opencode" || provider === "pi");
11765
11945
  }
11766
11946
 
11767
11947
  // src/managers/relay-tools.ts
@@ -12115,13 +12295,14 @@ function getUsingToolsSection() {
12115
12295
  ];
12116
12296
  return [`# Using your tools`, ...prependBullets(items)].join("\n");
12117
12297
  }
12118
- function getDelegationSection(codexAvailable, cursorAvailable, opencodeAvailable) {
12119
- const providerList = getAvailableRelayProviders({ codexAvailable, cursorAvailable, opencodeAvailable }).join(", ");
12298
+ function getDelegationSection(codexAvailable, cursorAvailable, opencodeAvailable, piAvailable) {
12299
+ const providerList = getAvailableRelayProviders({ codexAvailable, cursorAvailable, opencodeAvailable, piAvailable }).join(", ");
12120
12300
  const spawnDesc = `Create a new subagent with a specific provider (${providerList}), send it a prompt, and wait for its response. Returns the chatId and the agent's final response. You can set a custom timeout via the timeout_minutes parameter (default: 10 minutes).`;
12121
12301
  const claudeModelList = AGENT_MODELS.claude.join(", ");
12122
12302
  const extraAgentLines = [
12123
12303
  codexAvailable ? `Use provider 'codex' for heavy code writing, implementation, and large refactors. Suggested models: ${AGENT_MODELS.codex.join(", ")} (${AGENT_MODELS.codex[0]} is the default).` : null,
12124
12304
  opencodeAvailable ? `Use provider 'opencode' for cheaper routine implementation tasks through OpenRouter-backed open source models. Suggested models: ${AGENT_MODELS.opencode.join(", ")}.` : null,
12305
+ piAvailable ? `Use provider 'pi' for coding tasks through Pi's OpenRouter-backed coding agent. Suggested models: ${AGENT_MODELS.pi.join(", ")}.` : null,
12125
12306
  cursorAvailable ? `Use provider 'cursor' for fast iteration on code changes. Suggested models: ${AGENT_MODELS.cursor.join(", ")}.` : null
12126
12307
  ].filter(Boolean);
12127
12308
  const agentSelectionLines = extraAgentLines.length > 0 ? `${extraAgentLines.join("\n\n")}
@@ -12244,14 +12425,14 @@ function getEnvironmentSection() {
12244
12425
  ].join("\n");
12245
12426
  }
12246
12427
  function buildRelaySystemPrompt(options) {
12247
- const { customInstructions, codexAvailable, cursorAvailable, opencodeAvailable } = options ?? {};
12428
+ const { customInstructions, codexAvailable, cursorAvailable, opencodeAvailable, piAvailable } = options ?? {};
12248
12429
  const sections = [
12249
12430
  getIntroSection(),
12250
12431
  getSystemSection(),
12251
12432
  getDoingTasksSection(),
12252
12433
  getActionsSection(),
12253
12434
  getUsingToolsSection(),
12254
- getDelegationSection(codexAvailable ?? false, cursorAvailable ?? false, opencodeAvailable ?? false),
12435
+ getDelegationSection(codexAvailable ?? false, cursorAvailable ?? false, opencodeAvailable ?? false, piAvailable ?? false),
12255
12436
  getToneAndStyleSection(),
12256
12437
  getOutputEfficiencySection(),
12257
12438
  getEnvironmentSection(),
@@ -12284,12 +12465,13 @@ var RelayManager = class {
12284
12465
  const codexAvailable = options.codexAvailable ?? false;
12285
12466
  const cursorAvailable = options.cursorAvailable ?? false;
12286
12467
  const opencodeAvailable = options.opencodeAvailable ?? false;
12468
+ const piAvailable = options.piAvailable ?? false;
12287
12469
  this.inner = new ClaudeManager({
12288
12470
  ...options,
12289
- systemPromptOverride: (customInstructions) => buildRelaySystemPrompt({ customInstructions, codexAvailable, cursorAvailable, opencodeAvailable }),
12471
+ systemPromptOverride: (customInstructions) => buildRelaySystemPrompt({ customInstructions, codexAvailable, cursorAvailable, opencodeAvailable, piAvailable }),
12290
12472
  tools: RELAY_TOOLS,
12291
12473
  mcpServers: {
12292
- "relay-subagent-tools": createRelayMcpServer(options.chatId, { codexAvailable, cursorAvailable, opencodeAvailable })
12474
+ "relay-subagent-tools": createRelayMcpServer(options.chatId, { codexAvailable, cursorAvailable, opencodeAvailable, piAvailable })
12293
12475
  },
12294
12476
  envOverrides: {
12295
12477
  CLAUDE_CODE_STREAM_CLOSE_TIMEOUT: "900000"
@@ -12381,17 +12563,17 @@ var keepAliveService = new KeepAliveService();
12381
12563
  // src/services/canvas-service.ts
12382
12564
  import { readdir as readdir6, readFile as readFile12, stat as stat3 } from "fs/promises";
12383
12565
  import { homedir as homedir13 } from "os";
12384
- import { join as join19 } from "path";
12566
+ import { join as join20 } from "path";
12385
12567
  var GLOBAL_CANVAS_DIRECTORIES = [
12386
- join19(homedir13(), ".claude", "plans"),
12387
- join19(process.env.XDG_DATA_HOME ?? join19(homedir13(), ".local", "share"), "opencode", "plans"),
12388
- join19(homedir13(), ".replicas", "canvas")
12568
+ join20(homedir13(), ".claude", "plans"),
12569
+ join20(process.env.XDG_DATA_HOME ?? join20(homedir13(), ".local", "share"), "opencode", "plans"),
12570
+ join20(homedir13(), ".replicas", "canvas")
12389
12571
  ];
12390
12572
  async function canvasDirectories() {
12391
12573
  const repositories = await gitService.listRepositories().catch(() => []);
12392
12574
  return [
12393
12575
  ...GLOBAL_CANVAS_DIRECTORIES,
12394
- ...repositories.map((repository) => join19(repository.path, ".opencode", "plans"))
12576
+ ...repositories.map((repository) => join20(repository.path, ".opencode", "plans"))
12395
12577
  ];
12396
12578
  }
12397
12579
  var CanvasService = class {
@@ -12411,7 +12593,7 @@ var CanvasService = class {
12411
12593
  const { kind } = classifyCanvasFilename(entry.name);
12412
12594
  let sizeBytes = 0;
12413
12595
  try {
12414
- const s = await stat3(join19(directory, entry.name));
12596
+ const s = await stat3(join20(directory, entry.name));
12415
12597
  sizeBytes = s.size;
12416
12598
  } catch {
12417
12599
  continue;
@@ -12426,7 +12608,7 @@ var CanvasService = class {
12426
12608
  if (!safe) return null;
12427
12609
  const { kind, mimeType } = classifyCanvasFilename(safe);
12428
12610
  for (const directory of await canvasDirectories()) {
12429
- const filePath = join19(directory, safe);
12611
+ const filePath = join20(directory, safe);
12430
12612
  let sizeBytes = 0;
12431
12613
  let updatedAt = "";
12432
12614
  try {
@@ -12563,13 +12745,13 @@ async function reconcileCanvasItems(filenames) {
12563
12745
 
12564
12746
  // src/services/upload-chat-transcripts.ts
12565
12747
  import { readdir as readdir7, readFile as readFile13 } from "fs/promises";
12566
- import { basename as basename2, join as join20 } from "path";
12748
+ import { basename as basename2, join as join21 } from "path";
12567
12749
  import { homedir as homedir14 } from "os";
12568
- var ENGINE_DIR3 = join20(homedir14(), ".replicas", "engine");
12750
+ var ENGINE_DIR3 = join21(homedir14(), ".replicas", "engine");
12569
12751
  var HISTORY_DIRS = [
12570
- join20(ENGINE_DIR3, "claude-histories"),
12571
- join20(ENGINE_DIR3, "relay-histories"),
12572
- join20(ENGINE_DIR3, "codex-histories")
12752
+ join21(ENGINE_DIR3, "claude-histories"),
12753
+ join21(ENGINE_DIR3, "relay-histories"),
12754
+ join21(ENGINE_DIR3, "codex-histories")
12573
12755
  ];
12574
12756
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
12575
12757
  let flushed = 0;
@@ -12586,7 +12768,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
12586
12768
  if (!entry.endsWith(".jsonl")) continue;
12587
12769
  const chatId = basename2(entry, ".jsonl");
12588
12770
  tasks.push(
12589
- uploadChatTranscript(chatId, join20(dir, entry), chatsById.get(chatId)).then(() => {
12771
+ uploadChatTranscript(chatId, join21(dir, entry), chatsById.get(chatId)).then(() => {
12590
12772
  flushed++;
12591
12773
  }).catch((err) => {
12592
12774
  failed++;
@@ -12665,9 +12847,9 @@ async function flushRepoState() {
12665
12847
  }
12666
12848
 
12667
12849
  // src/services/chat/chat-service.ts
12668
- var CHAT_SENDERS_DIR = join21(ENGINE_DIR2, "chat-senders");
12669
- var CODEX_AUTH_PATH2 = join21(homedir15(), ".codex", "auth.json");
12670
- var OPENCODE_AUTH_PATH2 = join21(homedir15(), ".local", "share", "opencode", "auth.json");
12850
+ var CHAT_SENDERS_DIR = join22(ENGINE_DIR2, "chat-senders");
12851
+ var CODEX_AUTH_PATH2 = join22(homedir15(), ".codex", "auth.json");
12852
+ var OPENCODE_AUTH_PATH2 = join22(homedir15(), ".local", "share", "opencode", "auth.json");
12671
12853
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
12672
12854
  function isChatMessageSender(value) {
12673
12855
  if (!isRecord4(value)) return false;
@@ -12679,6 +12861,9 @@ function isCodexAvailable() {
12679
12861
  function isOpencodeAvailable() {
12680
12862
  return existsSync7(OPENCODE_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENROUTER_API_KEY);
12681
12863
  }
12864
+ function isPiAvailable() {
12865
+ return Boolean(ENGINE_ENV.OPENROUTER_API_KEY);
12866
+ }
12682
12867
  function isCursorAvailable() {
12683
12868
  return Boolean(ENGINE_ENV.CURSOR_API_KEY);
12684
12869
  }
@@ -12727,7 +12912,7 @@ function isPersistedChat(value) {
12727
12912
  return false;
12728
12913
  }
12729
12914
  const candidate = value;
12730
- return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "cursor" || candidate.provider === "opencode" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string") && (candidate.deletedAt === void 0 || candidate.deletedAt === null || typeof candidate.deletedAt === "string");
12915
+ return typeof candidate.id === "string" && typeof candidate.provider === "string" && isValidAgentProvider(candidate.provider) && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string") && (candidate.deletedAt === void 0 || candidate.deletedAt === null || typeof candidate.deletedAt === "string");
12731
12916
  }
12732
12917
  function normalizePersistedChat(chat) {
12733
12918
  const isLegacyCodexSdkChat = chat.provider === "codex" && (chat.codexBackend === "sdk" || chat.codexBackend === void 0 && chat.providerSessionId !== null);
@@ -12779,13 +12964,13 @@ var ChatService = class {
12779
12964
  persistInFlight = false;
12780
12965
  persistQueued = false;
12781
12966
  async initialize() {
12782
- await mkdir13(ENGINE_DIR2, { recursive: true });
12783
- await mkdir13(CLAUDE_HISTORY_DIR, { recursive: true });
12784
- await mkdir13(RELAY_HISTORY_DIR, { recursive: true });
12785
- await mkdir13(CODEX_HISTORY_DIR, { recursive: true });
12786
- await mkdir13(CURSOR_HISTORY_DIR, { recursive: true });
12787
- await mkdir13(OPENCODE_HISTORY_DIR, { recursive: true });
12788
- await mkdir13(CHAT_SENDERS_DIR, { recursive: true });
12967
+ await mkdir14(ENGINE_DIR2, { recursive: true });
12968
+ await mkdir14(CLAUDE_HISTORY_DIR, { recursive: true });
12969
+ await mkdir14(RELAY_HISTORY_DIR, { recursive: true });
12970
+ await mkdir14(CODEX_HISTORY_DIR, { recursive: true });
12971
+ await mkdir14(CURSOR_HISTORY_DIR, { recursive: true });
12972
+ await mkdir14(OPENCODE_HISTORY_DIR, { recursive: true });
12973
+ await mkdir14(CHAT_SENDERS_DIR, { recursive: true });
12789
12974
  const persisted = await this.loadChats();
12790
12975
  for (const chat of persisted) {
12791
12976
  const runtime = this.createRuntimeChat(chat);
@@ -12909,7 +13094,7 @@ var ChatService = class {
12909
13094
  };
12910
13095
  }
12911
13096
  senderFilePath(chatId) {
12912
- return join21(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
13097
+ return join22(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
12913
13098
  }
12914
13099
  async appendSender(chatId, sender) {
12915
13100
  try {
@@ -13103,7 +13288,7 @@ var ChatService = class {
13103
13288
  return descendants;
13104
13289
  }
13105
13290
  async deleteHistoryFile(persisted) {
13106
- await rm2(join21(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
13291
+ await rm2(join22(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
13107
13292
  await rm2(this.senderFilePath(persisted.id), { force: true });
13108
13293
  }
13109
13294
  async getChatHistory(chatId, page = {}) {
@@ -13177,7 +13362,7 @@ var ChatService = class {
13177
13362
  if (persisted.provider === "claude") {
13178
13363
  provider = new ClaudeManager({
13179
13364
  workingDirectory: this.workingDirectory,
13180
- historyFilePath: join21(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13365
+ historyFilePath: join22(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13181
13366
  initialSessionId: persisted.providerSessionId,
13182
13367
  onSaveSessionId: saveSession,
13183
13368
  onTurnComplete: onProviderTurnComplete,
@@ -13186,7 +13371,7 @@ var ChatService = class {
13186
13371
  } else if (persisted.provider === "relay") {
13187
13372
  provider = new RelayManager({
13188
13373
  workingDirectory: this.workingDirectory,
13189
- historyFilePath: join21(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13374
+ historyFilePath: join22(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13190
13375
  initialSessionId: persisted.providerSessionId,
13191
13376
  onSaveSessionId: saveSession,
13192
13377
  onTurnComplete: onProviderTurnComplete,
@@ -13194,12 +13379,13 @@ var ChatService = class {
13194
13379
  chatId: persisted.id,
13195
13380
  codexAvailable: isCodexAvailable(),
13196
13381
  opencodeAvailable: isOpencodeAvailable(),
13382
+ piAvailable: isPiAvailable(),
13197
13383
  cursorAvailable: isCursorAvailable()
13198
13384
  });
13199
13385
  } else if (persisted.provider === "cursor") {
13200
13386
  provider = new CursorManager({
13201
13387
  workingDirectory: this.workingDirectory,
13202
- historyFilePath: join21(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13388
+ historyFilePath: join22(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13203
13389
  initialSessionId: persisted.providerSessionId,
13204
13390
  onSaveSessionId: saveSession,
13205
13391
  onTurnComplete: onProviderTurnComplete,
@@ -13208,7 +13394,16 @@ var ChatService = class {
13208
13394
  } else if (persisted.provider === "opencode") {
13209
13395
  provider = new OpencodeManager({
13210
13396
  workingDirectory: this.workingDirectory,
13211
- historyFilePath: join21(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13397
+ historyFilePath: join22(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13398
+ initialSessionId: persisted.providerSessionId,
13399
+ onSaveSessionId: saveSession,
13400
+ onTurnComplete: onProviderTurnComplete,
13401
+ onEvent: onProviderEvent
13402
+ });
13403
+ } else if (persisted.provider === "pi") {
13404
+ provider = new PiManager({
13405
+ workingDirectory: this.workingDirectory,
13406
+ historyFilePath: join22(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
13212
13407
  initialSessionId: persisted.providerSessionId,
13213
13408
  onSaveSessionId: saveSession,
13214
13409
  onTurnComplete: onProviderTurnComplete,
@@ -13217,7 +13412,7 @@ var ChatService = class {
13217
13412
  } else {
13218
13413
  provider = new CodexAspManager({
13219
13414
  workingDirectory: this.workingDirectory,
13220
- historyFilePath: join21(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13415
+ historyFilePath: join22(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13221
13416
  initialSessionId: persisted.providerSessionId,
13222
13417
  onSaveSessionId: saveSession,
13223
13418
  onTurnComplete: onProviderTurnComplete,
@@ -13357,7 +13552,7 @@ var ChatService = class {
13357
13552
  });
13358
13553
  uploadChatTranscript(
13359
13554
  chatId,
13360
- join21(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13555
+ join22(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13361
13556
  this.toSummary(chat)
13362
13557
  ).catch((err) => {
13363
13558
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -13484,7 +13679,7 @@ var ChatService = class {
13484
13679
  // src/services/repo-file-service.ts
13485
13680
  import { execFile as execFile2 } from "child_process";
13486
13681
  import { readFile as readFile15, realpath, stat as stat4 } from "fs/promises";
13487
- import { join as join22, resolve as resolve2, extname as extname2 } from "path";
13682
+ import { join as join23, resolve as resolve2, extname as extname2 } from "path";
13488
13683
  var CACHE_TTL_MS = 3e4;
13489
13684
  var SEARCH_TIMEOUT_MS = 15e3;
13490
13685
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -13644,7 +13839,7 @@ var RepoFileService = class {
13644
13839
  const repo = repos.find((r) => r.name === repoName);
13645
13840
  if (!repo) return null;
13646
13841
  try {
13647
- const fullPath = await realpath(resolve2(join22(repo.path, filePath)));
13842
+ const fullPath = await realpath(resolve2(join23(repo.path, filePath)));
13648
13843
  const repoRoot = await realpath(repo.path);
13649
13844
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
13650
13845
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
@@ -13752,20 +13947,20 @@ var RepoFileService = class {
13752
13947
  import { Hono } from "hono";
13753
13948
  import { z as z2 } from "zod";
13754
13949
  import { readdir as readdir9, stat as stat5, readFile as readFile18 } from "fs/promises";
13755
- import { join as join25, resolve as resolve3 } from "path";
13950
+ import { join as join26, resolve as resolve3 } from "path";
13756
13951
 
13757
13952
  // src/services/warm-hooks-service.ts
13758
13953
  import { spawn as spawn4 } from "child_process";
13759
13954
  import { readFile as readFile17 } from "fs/promises";
13760
13955
  import { existsSync as existsSync8 } from "fs";
13761
- import { join as join24 } from "path";
13956
+ import { join as join25 } from "path";
13762
13957
 
13763
13958
  // src/services/warm-hook-logs-service.ts
13764
- import { mkdir as mkdir14, readFile as readFile16, writeFile as writeFile6, readdir as readdir8, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
13959
+ import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile6, readdir as readdir8, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
13765
13960
  import { homedir as homedir16 } from "os";
13766
- import { join as join23 } from "path";
13767
- var LOGS_DIR2 = join23(homedir16(), ".replicas", "warm-hook-logs");
13768
- var CURRENT_RUN_LOG = join23(LOGS_DIR2, "current-run.log");
13961
+ import { join as join24 } from "path";
13962
+ var LOGS_DIR2 = join24(homedir16(), ".replicas", "warm-hook-logs");
13963
+ var CURRENT_RUN_LOG = join24(LOGS_DIR2, "current-run.log");
13769
13964
  var GLOBAL_FILENAME = "global.json";
13770
13965
  function withPreview2(stored) {
13771
13966
  const preview = buildHookOutputPreview(stored.output);
@@ -13773,7 +13968,7 @@ function withPreview2(stored) {
13773
13968
  }
13774
13969
  var WarmHookLogsService = class {
13775
13970
  async ensureDir() {
13776
- await mkdir14(LOGS_DIR2, { recursive: true });
13971
+ await mkdir15(LOGS_DIR2, { recursive: true });
13777
13972
  }
13778
13973
  async saveGlobalHookLog(entry) {
13779
13974
  await this.ensureDir();
@@ -13782,7 +13977,7 @@ var WarmHookLogsService = class {
13782
13977
  hookName: "organization",
13783
13978
  ...entry
13784
13979
  };
13785
- await writeFile6(join23(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
13980
+ await writeFile6(join24(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
13786
13981
  `, "utf-8");
13787
13982
  }
13788
13983
  async saveEnvironmentHookLog(entry) {
@@ -13792,7 +13987,7 @@ var WarmHookLogsService = class {
13792
13987
  hookName: "environment",
13793
13988
  ...entry
13794
13989
  };
13795
- await writeFile6(join23(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
13990
+ await writeFile6(join24(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
13796
13991
  `, "utf-8");
13797
13992
  }
13798
13993
  async saveRepoHookLog(repoName, entry) {
@@ -13802,7 +13997,7 @@ var WarmHookLogsService = class {
13802
13997
  hookName: repoName,
13803
13998
  ...entry
13804
13999
  };
13805
- await writeFile6(join23(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
14000
+ await writeFile6(join24(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
13806
14001
  `, "utf-8");
13807
14002
  }
13808
14003
  async getAllLogs() {
@@ -13821,7 +14016,7 @@ var WarmHookLogsService = class {
13821
14016
  continue;
13822
14017
  }
13823
14018
  try {
13824
- const raw = await readFile16(join23(LOGS_DIR2, file), "utf-8");
14019
+ const raw = await readFile16(join24(LOGS_DIR2, file), "utf-8");
13825
14020
  const stored = JSON.parse(raw);
13826
14021
  logs.push(withPreview2(stored));
13827
14022
  } catch {
@@ -13859,7 +14054,7 @@ var WarmHookLogsService = class {
13859
14054
  async getFullOutput(hookType, hookName) {
13860
14055
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
13861
14056
  try {
13862
- const raw = await readFile16(join23(LOGS_DIR2, filename), "utf-8");
14057
+ const raw = await readFile16(join24(LOGS_DIR2, filename), "utf-8");
13863
14058
  const stored = JSON.parse(raw);
13864
14059
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
13865
14060
  return null;
@@ -13878,7 +14073,7 @@ var warmHookLogsService = new WarmHookLogsService();
13878
14073
  // src/services/warm-hooks-service.ts
13879
14074
  async function readRepoWarmHook(repoPath) {
13880
14075
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
13881
- const configPath = join24(repoPath, filename);
14076
+ const configPath = join25(repoPath, filename);
13882
14077
  if (!existsSync8(configPath)) {
13883
14078
  continue;
13884
14079
  }
@@ -14262,7 +14457,7 @@ var setWorkspaceNameSchema = z2.object({
14262
14457
  name: z2.string().min(1).max(48)
14263
14458
  });
14264
14459
  var createChatSchema = z2.object({
14265
- provider: z2.enum(["claude", "codex", "cursor", "opencode", "relay"]),
14460
+ provider: z2.enum(["claude", "codex", "cursor", "opencode", "pi", "relay"]),
14266
14461
  title: z2.string().min(1).optional(),
14267
14462
  parentChatId: z2.string().uuid().optional(),
14268
14463
  clientRequestId: z2.string().min(1).max(128).optional()
@@ -15100,7 +15295,7 @@ data: ${JSON.stringify("Terminal session not found")}
15100
15295
  const logFiles = files.filter((f) => f.endsWith(".log"));
15101
15296
  const sessions = await Promise.all(
15102
15297
  logFiles.map(async (filename) => {
15103
- const filePath = join25(LOG_DIR, filename);
15298
+ const filePath = join26(LOG_DIR, filename);
15104
15299
  const fileStat = await stat5(filePath);
15105
15300
  const sessionId = filename.replace(/\.log$/, "");
15106
15301
  return {
@@ -15219,6 +15414,11 @@ var heartbeatService = new HeartbeatService();
15219
15414
  // src/index.ts
15220
15415
  var startupStartedAt = performance.now();
15221
15416
  var startupTimings = {};
15417
+ var resolveEngineReady = () => {
15418
+ };
15419
+ var engineReadyPromise = new Promise((resolve4) => {
15420
+ resolveEngineReady = resolve4;
15421
+ });
15222
15422
  async function timeStartupStep(name, fn) {
15223
15423
  const startedAt = performance.now();
15224
15424
  try {
@@ -15288,7 +15488,16 @@ var authMiddleware = async (c, next) => {
15288
15488
  await next();
15289
15489
  };
15290
15490
  var chatService = new ChatService(gitService.getWorkspaceRoot());
15291
- app.get("/health", (c) => {
15491
+ app.get("/health", async (c) => {
15492
+ const requestedWaitMs = Number(c.req.query(ENGINE_HEALTH_WAIT_QUERY_PARAM));
15493
+ const waitMs = Number.isFinite(requestedWaitMs) ? Math.min(Math.max(requestedWaitMs, 0), ENGINE_HEALTH_MAX_WAIT_MS) : 0;
15494
+ if (!engineReady && waitMs > 0) {
15495
+ await Promise.race([
15496
+ engineReadyPromise,
15497
+ new Promise((resolve4) => setTimeout(resolve4, waitMs))
15498
+ ]);
15499
+ }
15500
+ c.header(ENGINE_HEALTH_WAIT_HEADER, "1");
15292
15501
  const response = {
15293
15502
  status: engineReady ? "active" : "initializing",
15294
15503
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15477,6 +15686,7 @@ serve(
15477
15686
  await timeStartupStep("github_token_initialize", () => githubTokenManager.start());
15478
15687
  }
15479
15688
  engineReady = true;
15689
+ resolveEngineReady();
15480
15690
  void registerDesktopPreview();
15481
15691
  heartbeatService.start(bootTimeMs);
15482
15692
  if (!IS_WARMING_MODE) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.451",
3
+ "version": "0.1.453",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -35,6 +35,8 @@
35
35
  "@connectrpc/connect-node": "1.7.0",
36
36
  "@cursor/sdk": "1.0.19",
37
37
  "@hono/node-server": "^1.19.5",
38
+ "@mariozechner/pi-ai": "0.73.1",
39
+ "@mariozechner/pi-coding-agent": "0.73.1",
38
40
  "@opencode-ai/sdk": "1.17.9",
39
41
  "hono": "^4.10.3",
40
42
  "opencode-ai": "1.17.9",