@khalilgharbaoui/opencode-claude-code-plugin 0.24.0 → 0.26.0

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/index.js CHANGED
@@ -386,6 +386,134 @@ function mapTool(name, input, opts) {
386
386
  return { name, input, executed: true };
387
387
  }
388
388
 
389
+ // src/host-tools.ts
390
+ function defined(input) {
391
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
392
+ }
393
+ var V2_TOOLS = {
394
+ bash: {
395
+ name: "shell",
396
+ // V2's shell takes no `description`; `workdir`, `timeout` and
397
+ // `background` keep their names.
398
+ input: (input) => defined({
399
+ command: input.command,
400
+ workdir: input.workdir,
401
+ timeout: input.timeout,
402
+ background: input.background
403
+ })
404
+ },
405
+ edit: {
406
+ name: "edit",
407
+ input: (input) => defined({
408
+ path: input.filePath ?? input.path,
409
+ oldString: input.oldString,
410
+ newString: input.newString,
411
+ replaceAll: input.replaceAll
412
+ })
413
+ },
414
+ write: {
415
+ name: "write",
416
+ input: (input) => defined({ path: input.filePath ?? input.path, content: input.content })
417
+ },
418
+ read: {
419
+ name: "read",
420
+ input: (input) => defined({ path: input.filePath ?? input.path, offset: input.offset, limit: input.limit })
421
+ },
422
+ glob: { name: "glob" },
423
+ grep: { name: "grep" },
424
+ webfetch: { name: "webfetch" },
425
+ websearch: { name: "websearch" },
426
+ question: { name: "question" },
427
+ task: {
428
+ name: "subagent",
429
+ input: (input) => defined({
430
+ agent: input.subagent_type ?? input.agent,
431
+ description: input.description,
432
+ prompt: input.prompt,
433
+ sessionID: input.task_id ?? input.sessionID,
434
+ background: input.background
435
+ })
436
+ },
437
+ todowrite: null,
438
+ plan_enter: null,
439
+ plan_exit: null,
440
+ notebookedit: null
441
+ };
442
+ function parseInput(raw) {
443
+ if (typeof raw !== "string") return raw ?? {};
444
+ try {
445
+ const parsed = JSON.parse(raw);
446
+ return parsed && typeof parsed === "object" ? parsed : {};
447
+ } catch {
448
+ return {};
449
+ }
450
+ }
451
+ function createHostToolPartTranslator(dialect = "v1") {
452
+ if (dialect !== "v2") return (part) => part;
453
+ const dropped = /* @__PURE__ */ new Set();
454
+ const translated = /* @__PURE__ */ new Map();
455
+ const classify2 = (id, name) => {
456
+ if (dropped.has(id)) return null;
457
+ const known = translated.get(id);
458
+ if (known) return known;
459
+ if (typeof name !== "string") return void 0;
460
+ const tool = V2_TOOLS[name];
461
+ if (tool === null) {
462
+ dropped.add(id);
463
+ return null;
464
+ }
465
+ if (tool) translated.set(id, tool);
466
+ return tool;
467
+ };
468
+ return (part) => {
469
+ switch (part.type) {
470
+ case "tool-input-start": {
471
+ const tool = classify2(String(part.id), part.toolName);
472
+ if (tool === null) return null;
473
+ return tool ? { ...part, toolName: tool.name } : part;
474
+ }
475
+ case "tool-input-delta": {
476
+ const tool = classify2(String(part.id), void 0);
477
+ if (tool === null) return null;
478
+ return tool?.input ? null : part;
479
+ }
480
+ case "tool-input-end": {
481
+ return classify2(String(part.id), void 0) === null ? null : part;
482
+ }
483
+ case "tool-call": {
484
+ const tool = classify2(String(part.toolCallId), part.toolName);
485
+ if (tool === null) return null;
486
+ if (!tool) return part;
487
+ const input = tool.input ? tool.input(parseInput(part.input)) : parseInput(part.input);
488
+ return {
489
+ ...part,
490
+ toolName: tool.name,
491
+ input: typeof part.input === "string" ? JSON.stringify(input) : input
492
+ };
493
+ }
494
+ case "tool-result": {
495
+ const tool = classify2(String(part.toolCallId), part.toolName);
496
+ if (tool === null) return null;
497
+ return tool ? { ...part, toolName: tool.name } : part;
498
+ }
499
+ default:
500
+ return part;
501
+ }
502
+ };
503
+ }
504
+ function translateStreamForHost(stream, dialect = "v1") {
505
+ if (dialect !== "v2") return stream;
506
+ const translate = createHostToolPartTranslator(dialect);
507
+ return stream.pipeThrough(
508
+ new TransformStream({
509
+ transform(part, controller) {
510
+ const next = translate(part);
511
+ if (next) controller.enqueue(next);
512
+ }
513
+ })
514
+ );
515
+ }
516
+
389
517
  // src/accounts.ts
390
518
  import { chmod, lstat, mkdir, readlink, symlink, writeFile } from "fs/promises";
391
519
  import path from "path";
@@ -596,7 +724,9 @@ function rateLimitKey(info) {
596
724
  ].join("|");
597
725
  }
598
726
  function isRateLimitRejected(info) {
599
- return info.status === "rejected" || info.overageStatus === "rejected";
727
+ if (info.status === "rejected") return true;
728
+ if (info.status === "allowed" || info.status === "allowed_warning") return false;
729
+ return info.overageStatus === "rejected";
600
730
  }
601
731
  function describeRateLimit(info) {
602
732
  if (!info.status && !info.overageStatus) return null;
@@ -3254,6 +3384,7 @@ var BtwHandledError = class extends Error {
3254
3384
  super(message);
3255
3385
  }
3256
3386
  };
3387
+ var BTW_COMMAND_DESCRIPTION = "Ask a side question in the live Claude Code session without changing its context";
3257
3388
  var BTW_NO_SESSION_MESSAGE = "/btw needs a live Claude Code session in this conversation. Send a normal message with a Claude Code model first, then ask again.";
3258
3389
  var BTW_INLINE_HANDLED_MESSAGE = "/btw was answered inside the running turn; nothing to add to this conversation.";
3259
3390
  var BTW_TURN_TOO_LONG_MESSAGE = "/btw gave up waiting for this turn to end. Ask again once it is over.";
@@ -3781,14 +3912,14 @@ function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers) {
3781
3912
  excludeServers
3782
3913
  });
3783
3914
  }
3784
- function mergeOpencodeMcp(cwd, runtimeStatus) {
3915
+ function opencodeConfigLayers(cwd) {
3785
3916
  const worktree = detectWorktree(cwd);
3786
- let merged = {};
3787
- merged = mergeMcp(merged, extractMcpBlock(loadGlobalConfig()));
3917
+ const layers = [];
3918
+ layers.push(loadGlobalConfig());
3788
3919
  const explicitConfig = process.env.OPENCODE_CONFIG;
3789
3920
  if (explicitConfig && fileExists(explicitConfig)) {
3790
3921
  const parsed = readAndParse(explicitConfig);
3791
- if (parsed) merged = mergeMcp(merged, extractMcpBlock(parsed));
3922
+ if (parsed) layers.push(parsed);
3792
3923
  }
3793
3924
  const projectFiles = walkUp({
3794
3925
  start: cwd,
@@ -3806,10 +3937,22 @@ function mergeOpencodeMcp(cwd, runtimeStatus) {
3806
3937
  }
3807
3938
  }
3808
3939
  for (const dir of projectDirs.slice().reverse()) {
3809
- merged = mergeMcp(merged, extractMcpBlock(loadProjectFilesInDir(dir)));
3940
+ layers.push(loadProjectFilesInDir(dir));
3810
3941
  }
3811
3942
  for (const dir of dotOpencodeDirs(cwd, worktree)) {
3812
- merged = mergeMcp(merged, extractMcpBlock(loadProjectFilesInDir(dir)));
3943
+ layers.push(loadProjectFilesInDir(dir));
3944
+ }
3945
+ return layers;
3946
+ }
3947
+ function loadMergedOpencodeConfig(cwd) {
3948
+ let merged = {};
3949
+ for (const layer of opencodeConfigLayers(cwd)) merged = deepMerge(merged, layer);
3950
+ return merged;
3951
+ }
3952
+ function mergeOpencodeMcp(cwd, runtimeStatus) {
3953
+ let merged = {};
3954
+ for (const layer of opencodeConfigLayers(cwd)) {
3955
+ merged = mergeMcp(merged, extractMcpBlock(layer));
3813
3956
  }
3814
3957
  if (runtimeStatus) {
3815
3958
  for (const name of Object.keys(merged)) {
@@ -4129,6 +4272,7 @@ function logStartupDiagnostics(providers, opencodeVersion) {
4129
4272
 
4130
4273
  // src/doctor.ts
4131
4274
  var DOCTOR_COMMAND = "claude-code-doctor";
4275
+ var DOCTOR_COMMAND_DESCRIPTION = "Report what the Claude Code plugin sees: versions, cwd, live processes, pending proxy calls";
4132
4276
  var DOCTOR_MARKER = "\u258C **claude-code doctor**";
4133
4277
  function isRecord3(value) {
4134
4278
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -4856,6 +5000,8 @@ var opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 };
4856
5000
  var fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
4857
5001
  var fable51Cost = { input: 10, output: 50, cacheRead: 0.25, cacheWrite: 12.5 };
4858
5002
  var opusFastCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
5003
+ var opus55Cost = { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 };
5004
+ var opus55FastCost = { input: 8, output: 40, cacheRead: 0.4, cacheWrite: 10 };
4859
5005
  function toConfigModel(model) {
4860
5006
  const inputMods = [];
4861
5007
  const outputMods = [];
@@ -4982,10 +5128,12 @@ var defaultModels = {
4982
5128
  // `--settings {"fastMode":true}` on the spawn. Retired `-fast` model strings
4983
5129
  // (`claude-opus-4-6-fast`) are a different thing and are not registered here.
4984
5130
  //
4985
- // Only Opus 4.8 and Opus 5 qualify: the CLI gates fast mode on the resolved
4986
- // model name containing `opus-4-8` or `opus-5`, so registering a fast entry
4987
- // for any other model would produce a picker option that silently runs at
4988
- // standard speed while displaying the 10x price.
5131
+ // Only Opus 4.8, Opus 5 and Opus 5.5 qualify: the CLI gates fast mode on
5132
+ // the resolved model name containing `opus-4-8` or `opus-5` (which
5133
+ // `claude-opus-5-5` satisfies too, and 2.1.280's model catalog lists
5134
+ // `fast_mode` for it outright), so registering a fast entry for any other
5135
+ // model would produce a picker option that silently runs at standard speed
5136
+ // while displaying the fast price.
4989
5137
  "claude-opus-4-8-fast": defineModel({
4990
5138
  id: "claude-opus-4-8-fast",
4991
5139
  name: "Claude Opus 4.8 Fast",
@@ -5019,6 +5167,33 @@ var defaultModels = {
5019
5167
  multiplier: 10,
5020
5168
  releaseDate: "2026-07-24"
5021
5169
  }),
5170
+ // Opus 5.5 needs Claude Code 2.1.280+: the API rejects it from an older CLI
5171
+ // with a 400 naming that floor, which the plugin surfaces as a turn error.
5172
+ // Thinking cannot be switched off for it (the CLI's catalog marks it
5173
+ // `rejects_disabled_thinking`); the plugin never asks for that, so nothing
5174
+ // here has to special-case it.
5175
+ "claude-opus-5-5": defineModel({
5176
+ id: "claude-opus-5-5",
5177
+ name: "Claude Opus 5.5",
5178
+ family: "opus",
5179
+ reasoning: true,
5180
+ context: 1e6,
5181
+ output: 128e3,
5182
+ cost: opus55Cost,
5183
+ multiplier: 4,
5184
+ releaseDate: "2026-09-22"
5185
+ }),
5186
+ "claude-opus-5-5-fast": defineModel({
5187
+ id: "claude-opus-5-5-fast",
5188
+ name: "Claude Opus 5.5 Fast",
5189
+ family: "opus",
5190
+ reasoning: true,
5191
+ context: 1e6,
5192
+ output: 128e3,
5193
+ cost: opus55FastCost,
5194
+ multiplier: 8,
5195
+ releaseDate: "2026-09-22"
5196
+ }),
5022
5197
  "claude-fable-5": defineModel({
5023
5198
  id: "claude-fable-5",
5024
5199
  name: "Claude Fable 5",
@@ -5309,6 +5484,33 @@ function registerBundledSkillPath(config) {
5309
5484
  paths.push(dir);
5310
5485
  return true;
5311
5486
  }
5487
+ function readBundledSkillInfos() {
5488
+ return discoverBundledSkills().flatMap((skill) => {
5489
+ const file = path7.join(skill.dir, "SKILL.md");
5490
+ let text;
5491
+ try {
5492
+ text = fs5.readFileSync(file, "utf8");
5493
+ } catch {
5494
+ return [];
5495
+ }
5496
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
5497
+ const frontmatter = match?.[1] ?? "";
5498
+ const field = (key) => {
5499
+ const line = new RegExp(`^${key}:\\s*(.+)$`, "m").exec(frontmatter)?.[1]?.trim();
5500
+ return line ? line.replace(/^(["'])(.*)\1$/, "$2") : void 0;
5501
+ };
5502
+ const description = field("description");
5503
+ return [
5504
+ {
5505
+ id: skill.name,
5506
+ name: field("name") ?? skill.name,
5507
+ ...description ? { description } : {},
5508
+ path: file,
5509
+ content: match ? text.slice(match[0].length) : text
5510
+ }
5511
+ ];
5512
+ });
5513
+ }
5312
5514
  function linkSkill(source, target) {
5313
5515
  try {
5314
5516
  fs5.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
@@ -5958,7 +6160,7 @@ function spawnInteractiveProcess(opts) {
5958
6160
  }
5959
6161
 
5960
6162
  // src/claude-code-language-model.ts
5961
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
6163
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
5962
6164
  import { unlink as unlink3 } from "fs/promises";
5963
6165
  import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
5964
6166
  import { randomUUID as randomUUID5 } from "crypto";
@@ -5987,6 +6189,22 @@ function resolveSessionAffinity(headers, providerOptions, providerKey) {
5987
6189
  }
5988
6190
  return "default";
5989
6191
  }
6192
+ function resolveOpencodeAgent(headers, providerOptions, providerKey) {
6193
+ if (providerOptions) {
6194
+ const bag = providerOptions[providerKey] ?? providerOptions["claude-code"];
6195
+ const agent = bag?.opencodeAgent;
6196
+ if (typeof agent === "string") return agent;
6197
+ }
6198
+ if (headers) {
6199
+ for (const key of Object.keys(headers)) {
6200
+ if (key.toLowerCase() === "x-opencode-agent") {
6201
+ const value = headers[key];
6202
+ if (typeof value === "string" && value.length > 0) return value;
6203
+ }
6204
+ }
6205
+ }
6206
+ return void 0;
6207
+ }
5990
6208
  var KNOWN_DELTA_TYPES = /* @__PURE__ */ new Set([
5991
6209
  "thinking_delta",
5992
6210
  "text_delta",
@@ -6186,9 +6404,9 @@ ${body}`;
6186
6404
  }
6187
6405
  });
6188
6406
  }
6189
- function readPromptFileIfPresent(path9) {
6407
+ function readPromptFileIfPresent(path10) {
6190
6408
  try {
6191
- const content = readFileSync4(path9, "utf8").trim();
6409
+ const content = readFileSync5(path10, "utf8").trim();
6192
6410
  return content || void 0;
6193
6411
  } catch {
6194
6412
  return void 0;
@@ -6303,10 +6521,10 @@ ${options.compressionSummary.trim()}`
6303
6521
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
6304
6522
  const content = parts.join("\n\n");
6305
6523
  if (!content) return void 0;
6306
- const path9 = join8(tmpdir2(), `opencode-cc-sys-${randomUUID5()}.md`);
6524
+ const path10 = join8(tmpdir2(), `opencode-cc-sys-${randomUUID5()}.md`);
6307
6525
  try {
6308
- writeFileSync4(path9, content, "utf8");
6309
- return path9;
6526
+ writeFileSync4(path10, content, "utf8");
6527
+ return path10;
6310
6528
  } catch (err) {
6311
6529
  log.warn("failed to write system prompt file", { error: String(err) });
6312
6530
  return void 0;
@@ -6338,14 +6556,14 @@ function reportFastModeState(msg, requested) {
6338
6556
  const reason = msg.fast_mode_disabled_reason;
6339
6557
  if (state === "cooldown") {
6340
6558
  log.notice(
6341
- "fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the 10x shown in the model picker.",
6559
+ "fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the fast price shown in the model picker.",
6342
6560
  { state, reason: reason ?? null }
6343
6561
  );
6344
6562
  return;
6345
6563
  }
6346
6564
  const key = reason ?? "unknown";
6347
6565
  const explanation = reason ? FAST_MODE_REASONS[reason] : void 0;
6348
- const message = `fast mode was requested but is off${explanation ? `: ${explanation}` : reason ? ` (${reason})` : ""}. Turns run at standard speed and are billed at standard Opus rates, not the 10x shown in the model picker. Switch to the non-fast model id to make the picker's price accurate.`;
6566
+ const message = `fast mode was requested but is off${explanation ? `: ${explanation}` : reason ? ` (${reason})` : ""}. Turns run at standard speed and are billed at standard Opus rates, not the fast price shown in the model picker. Switch to the non-fast model id to make the picker's price accurate.`;
6349
6567
  if (warnedFastModeReasons.has(key)) {
6350
6568
  log.debug(message, { state, reason: reason ?? null });
6351
6569
  return;
@@ -6392,6 +6610,19 @@ var ClaudeCodeLanguageModel = class {
6392
6610
  raw: reason
6393
6611
  };
6394
6612
  }
6613
+ /**
6614
+ * Whether this call only names the session, which gets the synthetic stub
6615
+ * rather than a `claude` spawn. opencode 1.x sends a title request with no
6616
+ * tools, and that is the whole test there. opencode 2 sends its tool set
6617
+ * along with it (measured on 2.0.11: `scope: "tools"`, agent `title`), so
6618
+ * every new V2 session paid for a second `claude` process just to title
6619
+ * itself; for a V2 model the request kind, carried as the `title` agent,
6620
+ * decides instead.
6621
+ */
6622
+ isTitleRequest(scope, options) {
6623
+ if (scope === "no-tools") return true;
6624
+ return this.config.hostApi === "v2" && this.getOpencodeAgent(options) === "title";
6625
+ }
6395
6626
  requestScope(options) {
6396
6627
  const tools = options?.tools;
6397
6628
  if (Array.isArray(tools)) return "tools";
@@ -6803,15 +7034,15 @@ var ClaudeCodeLanguageModel = class {
6803
7034
  ];
6804
7035
  return valid.includes(effort) ? effort : void 0;
6805
7036
  }
6806
- getOpencodeAgent(providerOptions) {
6807
- if (!providerOptions) return void 0;
6808
- const ownKey = this.config.provider;
6809
- const bag = providerOptions[ownKey] ?? providerOptions["claude-code"];
6810
- const agent = bag?.opencodeAgent;
6811
- return typeof agent === "string" ? agent : void 0;
7037
+ getOpencodeAgent(options) {
7038
+ return resolveOpencodeAgent(
7039
+ options?.headers,
7040
+ options.providerOptions,
7041
+ this.config.provider
7042
+ );
6812
7043
  }
6813
7044
  isCompactionCall(options) {
6814
- return this.getOpencodeAgent(options.providerOptions) === "compaction";
7045
+ return this.getOpencodeAgent(options) === "compaction";
6815
7046
  }
6816
7047
  /**
6817
7048
  * Pick the model used to handle /compact. Precedence:
@@ -6947,6 +7178,18 @@ var ClaudeCodeLanguageModel = class {
6947
7178
  };
6948
7179
  }
6949
7180
  async doGenerate(options) {
7181
+ const result = await this.doGenerateForHost(options);
7182
+ if (this.config.hostApi !== "v2") return result;
7183
+ const translate = createHostToolPartTranslator("v2");
7184
+ return {
7185
+ ...result,
7186
+ content: result.content.flatMap((part) => {
7187
+ const next = translate(part);
7188
+ return next ? [next] : [];
7189
+ })
7190
+ };
7191
+ }
7192
+ async doGenerateForHost(options) {
6950
7193
  if (!this.isCompactionCall(options) && this.requestScope(options) !== "no-tools" && parseSideQuestion(options.prompt)) {
6951
7194
  return this.doGenerateViaStream(options);
6952
7195
  }
@@ -6955,16 +7198,16 @@ var ClaudeCodeLanguageModel = class {
6955
7198
  const affinity = this.sessionAffinity(options);
6956
7199
  const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity);
6957
7200
  const effectiveModelId = resolveAgentModel(
6958
- this.getOpencodeAgent(options.providerOptions),
7201
+ this.getOpencodeAgent(options),
6959
7202
  this.modelId
6960
7203
  );
6961
7204
  const reasoningEffort = resolveAgentEffort(
6962
- this.getOpencodeAgent(options.providerOptions),
7205
+ this.getOpencodeAgent(options),
6963
7206
  this.getReasoningEffort(options.providerOptions)
6964
7207
  );
6965
7208
  const baseKey = sessionKey(
6966
7209
  cwd,
6967
- `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options.providerOptions) ?? null])}`
7210
+ `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`
6968
7211
  );
6969
7212
  const sk = effortSessionKey(baseKey, reasoningEffort);
6970
7213
  const compactionMode = this.isCompactionCall(options);
@@ -6974,10 +7217,10 @@ var ClaudeCodeLanguageModel = class {
6974
7217
  if (compactionMode) {
6975
7218
  return this.doGenerateViaStream(options);
6976
7219
  }
6977
- if (scope === "no-tools") {
7220
+ if (this.isTitleRequest(scope, options)) {
6978
7221
  log.info("doGenerate no-tools title stub", {
6979
7222
  compactionMode,
6980
- opencodeAgent: this.getOpencodeAgent(options.providerOptions),
7223
+ opencodeAgent: this.getOpencodeAgent(options),
6981
7224
  providerOptionsKeys: options.providerOptions ? Object.keys(options.providerOptions) : []
6982
7225
  });
6983
7226
  const text = this.synthesizeTitle(options.prompt);
@@ -7377,7 +7620,19 @@ ${plan}
7377
7620
  warnings
7378
7621
  };
7379
7622
  }
7623
+ /**
7624
+ * Tool parts leave in opencode 1.x's vocabulary; on opencode 2.x they are
7625
+ * renamed at this one edge (src/host-tools.ts). On V1 the stream is
7626
+ * returned untouched.
7627
+ */
7380
7628
  async doStream(options) {
7629
+ const result = await this.doStreamForHost(options);
7630
+ return {
7631
+ ...result,
7632
+ stream: translateStreamForHost(result.stream, this.config.hostApi ?? "v1")
7633
+ };
7634
+ }
7635
+ async doStreamForHost(options) {
7381
7636
  const warnings = [];
7382
7637
  const skipPermissions = this.config.skipPermissions !== false;
7383
7638
  const scope = this.requestScope(options);
@@ -7385,16 +7640,16 @@ ${plan}
7385
7640
  const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity);
7386
7641
  const compactionMode = this.isCompactionCall(options);
7387
7642
  const effectiveModelId = compactionMode ? this.resolveCompactionModel() : resolveAgentModel(
7388
- this.getOpencodeAgent(options.providerOptions),
7643
+ this.getOpencodeAgent(options),
7389
7644
  this.modelId
7390
7645
  );
7391
7646
  const reasoningEffort = compactionMode ? void 0 : resolveAgentEffort(
7392
- this.getOpencodeAgent(options.providerOptions),
7647
+ this.getOpencodeAgent(options),
7393
7648
  this.getReasoningEffort(options.providerOptions)
7394
7649
  );
7395
7650
  const baseKey = sessionKey(
7396
7651
  cwd,
7397
- `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options.providerOptions) ?? null])}`
7652
+ `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`
7398
7653
  );
7399
7654
  const sk = compactionMode ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`) : effortSessionKey(baseKey, reasoningEffort);
7400
7655
  const toUsage = this.toUsage.bind(this);
@@ -7495,10 +7750,10 @@ ${plan}
7495
7750
  });
7496
7751
  return { stream: stream2, request: { body: { text: aside.question } } };
7497
7752
  }
7498
- if (scope === "no-tools" && !compactionMode) {
7753
+ if (this.isTitleRequest(scope, options) && !compactionMode) {
7499
7754
  log.info("doStream no-tools title stub", {
7500
7755
  compactionMode,
7501
- opencodeAgent: this.getOpencodeAgent(options.providerOptions),
7756
+ opencodeAgent: this.getOpencodeAgent(options),
7502
7757
  providerOptionsKeys: options.providerOptions ? Object.keys(options.providerOptions) : []
7503
7758
  });
7504
7759
  const text = this.synthesizeTitle(options.prompt);
@@ -7688,7 +7943,7 @@ ${plan}
7688
7943
  proxyTools: resolvedProxy?.map((t) => t.name) ?? null,
7689
7944
  compactionMode,
7690
7945
  scope,
7691
- opencodeAgent: this.getOpencodeAgent(options.providerOptions),
7946
+ opencodeAgent: this.getOpencodeAgent(options),
7692
7947
  providerOptionsKeys: options.providerOptions ? Object.keys(options.providerOptions) : []
7693
7948
  });
7694
7949
  const stream = new ReadableStream({
@@ -8350,7 +8605,7 @@ ${plan}
8350
8605
  });
8351
8606
  }
8352
8607
  activeProcess?.pendingProxyCompletions?.clear();
8353
- if (accountLimitHit && failoverAskActive) {
8608
+ if (accountLimitHit && failoverAskActive && msg.is_error === true) {
8354
8609
  const call = createAccountFailoverQuestionCall(sk, {
8355
8610
  sourceAccount,
8356
8611
  candidates: failoverAccounts,
@@ -9456,7 +9711,7 @@ ${plan}
9456
9711
  // src/cleanup-stale.ts
9457
9712
  import {
9458
9713
  existsSync as existsSync4,
9459
- readFileSync as readFileSync5,
9714
+ readFileSync as readFileSync6,
9460
9715
  realpathSync,
9461
9716
  rmSync as rmSync3,
9462
9717
  writeFileSync as writeFileSync5
@@ -9483,7 +9738,7 @@ function userIntendsToUseUnscoped() {
9483
9738
  const cfg = userOpencodeJsonPath();
9484
9739
  if (!existsSync4(cfg)) return false;
9485
9740
  try {
9486
- const json = JSON.parse(readFileSync5(cfg, "utf8"));
9741
+ const json = JSON.parse(readFileSync6(cfg, "utf8"));
9487
9742
  const plugins = json.plugin;
9488
9743
  if (!Array.isArray(plugins)) return false;
9489
9744
  return plugins.some(
@@ -9532,7 +9787,7 @@ function cleanupOne(cacheRoot, ourDir) {
9532
9787
  if (!existsSync4(pkgJsonPath)) return;
9533
9788
  let pkg = {};
9534
9789
  try {
9535
- pkg = JSON.parse(readFileSync5(pkgJsonPath, "utf8"));
9790
+ pkg = JSON.parse(readFileSync6(pkgJsonPath, "utf8"));
9536
9791
  } catch {
9537
9792
  return;
9538
9793
  }
@@ -9551,7 +9806,7 @@ function cleanupOne(cacheRoot, ourDir) {
9551
9806
  const cachePkgJson = join9(cacheRoot, "package.json");
9552
9807
  if (!existsSync4(cachePkgJson)) return;
9553
9808
  try {
9554
- const cfg = JSON.parse(readFileSync5(cachePkgJson, "utf8"));
9809
+ const cfg = JSON.parse(readFileSync6(cachePkgJson, "utf8"));
9555
9810
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
9556
9811
  delete cfg.dependencies[STALE_PACKAGE_NAME];
9557
9812
  writeFileSync5(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
@@ -9564,6 +9819,414 @@ function cleanupOne(cacheRoot, ourDir) {
9564
9819
  }
9565
9820
  }
9566
9821
 
9822
+ // src/v2-client.ts
9823
+ import path9 from "path";
9824
+ function formatAgentTypeList(agents) {
9825
+ const lines = agents.flatMap((agent) => {
9826
+ if (agent.hidden === true || agent.mode === "primary") return [];
9827
+ const name = typeof agent.id === "string" ? agent.id : agent.name;
9828
+ if (typeof name !== "string" || name.length === 0) return [];
9829
+ const blurb = typeof agent.description === "string" ? agent.description.trim() : "";
9830
+ return [`- ${name}: ${blurb || "(no description)"}`];
9831
+ });
9832
+ if (lines.length === 0) return void 0;
9833
+ return `Available agent types and the tools they have access to:
9834
+ ${lines.join("\n")}`;
9835
+ }
9836
+ function describe(value) {
9837
+ if (typeof value === "string") return value;
9838
+ if (typeof value === "function") {
9839
+ try {
9840
+ const result = value();
9841
+ return typeof result === "string" ? result : "";
9842
+ } catch {
9843
+ return "";
9844
+ }
9845
+ }
9846
+ return "";
9847
+ }
9848
+ function toV1Session(info) {
9849
+ if (!info || typeof info !== "object") return void 0;
9850
+ const session = info;
9851
+ const root = session.location?.directory;
9852
+ const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path9.resolve(root, session.subpath) : root : void 0;
9853
+ return { ...info, directory, parentID: session.parentID };
9854
+ }
9855
+ function createV1ClientShim(ctx) {
9856
+ const client = {};
9857
+ const mcpList = ctx.mcp?.list;
9858
+ if (typeof mcpList === "function") {
9859
+ client.mcp = {
9860
+ // V1: `{ data: { [server]: { status } } }`. V2: `{ data: [{ name, status: { status } }] }`.
9861
+ status: async () => {
9862
+ const result = await mcpList.call(ctx.mcp);
9863
+ const data = {};
9864
+ for (const server2 of result?.data ?? []) {
9865
+ const status = server2?.status?.status;
9866
+ if (typeof server2?.name === "string" && typeof status === "string") {
9867
+ data[server2.name] = { status };
9868
+ }
9869
+ }
9870
+ return { data };
9871
+ }
9872
+ };
9873
+ }
9874
+ const toolList = ctx.tool?.list;
9875
+ if (typeof toolList === "function") {
9876
+ client.tool = {
9877
+ // V1: `{ data: [{ id, description, parameters }] }`. V2 returns the tools
9878
+ // themselves, with an Effect schema for input rather than JSON Schema, so
9879
+ // `parameters` is empty; only `proxyOpencodeTools` reads it.
9880
+ list: async () => {
9881
+ const tools = await toolList.call(ctx.tool);
9882
+ const data = (tools ?? []).flatMap((tool) => {
9883
+ const id = typeof tool?.id === "string" ? tool.id : tool?.name;
9884
+ return typeof id === "string" ? [{ id, description: describe(tool.description), parameters: {} }] : [];
9885
+ });
9886
+ const subagent = data.find((tool) => tool.id === "subagent");
9887
+ if (subagent && !data.some((tool) => tool.id === "task")) {
9888
+ let agentList;
9889
+ const agentApi = ctx.agent?.list;
9890
+ if (typeof agentApi === "function") {
9891
+ try {
9892
+ agentList = formatAgentTypeList((await agentApi.call(ctx.agent))?.data ?? []);
9893
+ } catch {
9894
+ agentList = void 0;
9895
+ }
9896
+ }
9897
+ const description = agentList ? `${subagent.description}
9898
+
9899
+ ${agentList}` : subagent.description;
9900
+ data.push({ ...subagent, id: "task", description });
9901
+ }
9902
+ return { data };
9903
+ }
9904
+ };
9905
+ }
9906
+ const sessionGet = ctx.session?.get;
9907
+ if (typeof sessionGet === "function") {
9908
+ client.session = {
9909
+ get: async (options) => ({
9910
+ data: toV1Session(await sessionGet.call(ctx.session, { sessionID: options.path.id }))
9911
+ })
9912
+ };
9913
+ }
9914
+ return client;
9915
+ }
9916
+
9917
+ // src/v2.ts
9918
+ var V2_PLUGIN_PACKAGE = "aisdk:@khalilgharbaoui/opencode-claude-code-plugin";
9919
+ var SESSION_AFFINITY_HEADER = "x-session-affinity";
9920
+ var OPENCODE_AGENT_HEADER = "x-opencode-agent";
9921
+ var PLUGIN_ONLY_SETTINGS = ["accounts", "defaultSubagentModel"];
9922
+ var OPENCODE_SDK_OPTION_KEYS = ["fetch", "headers", "body", "name"];
9923
+ function isClaudeCodeProviderId(providerID) {
9924
+ if (typeof providerID !== "string") return false;
9925
+ return providerID === BASE_PROVIDER_ID || providerID.startsWith(`${BASE_PROVIDER_ID}-`);
9926
+ }
9927
+ function agentForRequest(kind, agent) {
9928
+ if (kind === "compaction") return "compaction";
9929
+ if (kind === "title") return "title";
9930
+ return agent;
9931
+ }
9932
+ function releasedAt(date) {
9933
+ const ms = date ? Date.parse(date) : Number.NaN;
9934
+ return Number.isFinite(ms) ? ms : 0;
9935
+ }
9936
+ function modalities(flags) {
9937
+ return Object.entries(flags).filter(([, enabled]) => enabled === true).map(([name]) => name);
9938
+ }
9939
+ function toV2Model(model, providerID, modelId) {
9940
+ return {
9941
+ id: modelId,
9942
+ modelID: modelId,
9943
+ providerID,
9944
+ name: model.name,
9945
+ family: model.family,
9946
+ capabilities: {
9947
+ tools: model.capabilities.toolcall,
9948
+ input: modalities(model.capabilities.input),
9949
+ output: modalities(model.capabilities.output)
9950
+ },
9951
+ variants: Object.entries(model.variants ?? {}).map(([id, settings]) => ({
9952
+ id,
9953
+ settings: { ...settings }
9954
+ })),
9955
+ time: { released: releasedAt(model.release_date) },
9956
+ cost: [
9957
+ {
9958
+ input: model.cost.input,
9959
+ output: model.cost.output,
9960
+ cache: { read: model.cost.cache.read, write: model.cost.cache.write }
9961
+ }
9962
+ ],
9963
+ status: model.status ?? "active",
9964
+ enabled: true,
9965
+ limit: { context: model.limit.context, output: model.limit.output },
9966
+ package: V2_PLUGIN_PACKAGE
9967
+ };
9968
+ }
9969
+ function v2ModelsForProvider(providerID, modelSuffix) {
9970
+ return Object.entries(defaultModels).map(
9971
+ ([id, model]) => toV2Model(model, providerID, modelSuffix ? `${id}@${modelSuffix}` : id)
9972
+ );
9973
+ }
9974
+ function stripPluginOnly(settings) {
9975
+ const result = { ...settings ?? {} };
9976
+ for (const key of PLUGIN_ONLY_SETTINGS) delete result[key];
9977
+ return result;
9978
+ }
9979
+ function planV2Providers(seedSettings, defaultProxyTools) {
9980
+ const base = {
9981
+ cliPath: "claude",
9982
+ proxyTools: [...defaultProxyTools],
9983
+ ...stripPluginOnly(seedSettings)
9984
+ };
9985
+ const accounts = resolveAccounts(seedSettings?.accounts);
9986
+ if (!accounts) {
9987
+ return [
9988
+ {
9989
+ info: {
9990
+ id: BASE_PROVIDER_ID,
9991
+ name: "Claude Code",
9992
+ activation: "enabled",
9993
+ package: V2_PLUGIN_PACKAGE,
9994
+ settings: { ...base, providerID: BASE_PROVIDER_ID }
9995
+ },
9996
+ models: v2ModelsForProvider(BASE_PROVIDER_ID)
9997
+ }
9998
+ ];
9999
+ }
10000
+ return accounts.map((account) => {
10001
+ const providerID = accountProviderId(account);
10002
+ return {
10003
+ info: {
10004
+ id: providerID,
10005
+ name: accountDisplayName(account),
10006
+ activation: "enabled",
10007
+ package: V2_PLUGIN_PACKAGE,
10008
+ settings: {
10009
+ ...base,
10010
+ account,
10011
+ // The resolved list, so this account's language model can offer the
10012
+ // others when it runs out of usage (src/account-failover.ts).
10013
+ failoverAccounts: accounts,
10014
+ providerID,
10015
+ // Kept separately because the wrapper replaces `cliPath`, and a
10016
+ // failover builds another account's wrapper on the same base.
10017
+ baseCliPath: String(base.cliPath ?? "claude")
10018
+ }
10019
+ },
10020
+ models: v2ModelsForProvider(providerID, accountModelSuffix(account))
10021
+ };
10022
+ });
10023
+ }
10024
+ async function resolveSdkSettings(providerID, planned, eventOptions) {
10025
+ const fromEvent = { ...eventOptions };
10026
+ for (const key of OPENCODE_SDK_OPTION_KEYS) delete fromEvent[key];
10027
+ const merged = {
10028
+ ...stripPluginOnly({ ...planned ?? {}, ...fromEvent }),
10029
+ providerID,
10030
+ // The only place a model is told it serves opencode 2.
10031
+ hostApi: "v2"
10032
+ };
10033
+ const account = typeof merged.account === "string" ? merged.account : void 0;
10034
+ if (account) {
10035
+ const base = String(merged.baseCliPath ?? merged.cliPath ?? "claude");
10036
+ const runtime = await ensureAccountRuntime(account, base);
10037
+ Object.assign(merged, runtime, { baseCliPath: base });
10038
+ }
10039
+ return merged;
10040
+ }
10041
+ function plainObject(value) {
10042
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
10043
+ }
10044
+ function configuredSeedSettings(config, pluginOptions) {
10045
+ const provider = plainObject(plainObject(config.provider)?.[BASE_PROVIDER_ID]);
10046
+ return {
10047
+ ...plainObject(provider?.options) ?? {},
10048
+ ...plainObject(provider?.settings) ?? {},
10049
+ ...pluginOptions ?? {}
10050
+ };
10051
+ }
10052
+ function isOpencodeV2Context(ctx) {
10053
+ const context = plainObject(ctx);
10054
+ if (!context) return false;
10055
+ const version = plainObject(context.app)?.version;
10056
+ if (typeof version === "string") {
10057
+ const major = Number.parseInt(version.replace(/^v/, ""), 10);
10058
+ if (Number.isFinite(major) && major < 2) return false;
10059
+ }
10060
+ const hasHook = (domain, name) => typeof plainObject(domain)?.[name] === "function";
10061
+ return hasHook(context.provider, "transform") && hasHook(context.aisdk, "hook") && hasHook(context.session, "hook");
10062
+ }
10063
+ function commandPromptText(command, args) {
10064
+ const rest = (args ?? "").trim();
10065
+ return rest ? `/${command} ${rest}` : `/${command}`;
10066
+ }
10067
+ function deletedSessionIdV2(event) {
10068
+ if (!event || event.type !== "session.deleted") return void 0;
10069
+ const id = event.data?.sessionID;
10070
+ return typeof id === "string" && id.length > 0 ? id : void 0;
10071
+ }
10072
+ async function releaseDeletedSessions(subscribe, signal) {
10073
+ try {
10074
+ for await (const event of subscribe({ signal })) {
10075
+ if (signal.aborted) break;
10076
+ const sessionID = deletedSessionIdV2(event);
10077
+ if (!sessionID) continue;
10078
+ const released = deleteActiveProcessesForSession(sessionID);
10079
+ if (released.length > 0) {
10080
+ log.info("released claude state for deleted session", { sessionID, released });
10081
+ }
10082
+ }
10083
+ } catch (err) {
10084
+ if (!signal.aborted) {
10085
+ log.warn("opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction", {
10086
+ error: err instanceof Error ? err.message : String(err)
10087
+ });
10088
+ }
10089
+ }
10090
+ }
10091
+ function createV2Setup(deps) {
10092
+ return async (ctx) => {
10093
+ if (!isOpencodeV2Context(ctx)) {
10094
+ log.debug("setup called outside opencode 2; leaving it to server()", {
10095
+ version: ctx?.app?.version
10096
+ });
10097
+ return () => void 0;
10098
+ }
10099
+ ensureProcessExitCleanup();
10100
+ setOpencodeClient(createV1ClientShim(ctx));
10101
+ const directory = ctx.location?.directory;
10102
+ setOpencodeProjectDirectory(isUsableDirectory(directory) ? directory : void 0);
10103
+ if (deps.buildAgentRegistry) {
10104
+ try {
10105
+ const config = deps.loadConfig(directory ?? process.cwd());
10106
+ await deps.buildAgentRegistry({
10107
+ ...config,
10108
+ provider: {
10109
+ ...plainObject(config.provider) ?? {},
10110
+ [BASE_PROVIDER_ID]: {
10111
+ options: configuredSeedSettings(config, plainObject(ctx.options))
10112
+ }
10113
+ }
10114
+ });
10115
+ } catch (err) {
10116
+ log.warn("failed to build the agent registry for opencode 2", {
10117
+ error: err instanceof Error ? err.message : String(err)
10118
+ });
10119
+ }
10120
+ }
10121
+ const planned = /* @__PURE__ */ new Map();
10122
+ const registrations = [];
10123
+ registrations.push(
10124
+ await ctx.provider.transform((editor) => {
10125
+ const seed = editor.get(BASE_PROVIDER_ID);
10126
+ const seedSettings = {
10127
+ ...seed?.provider.settings ?? {},
10128
+ ...configuredSeedSettings(
10129
+ deps.loadConfig(directory ?? process.cwd()),
10130
+ plainObject(ctx.options)
10131
+ )
10132
+ };
10133
+ const plans = planV2Providers(seedSettings, deps.defaultProxyTools);
10134
+ planned.clear();
10135
+ for (const plan of plans) {
10136
+ planned.set(plan.info.id, plan.info.settings ?? {});
10137
+ if (editor.get(plan.info.id)) {
10138
+ editor.update(plan.info.id, (provider) => {
10139
+ provider.name = plan.info.name;
10140
+ provider.activation = plan.info.activation;
10141
+ provider.package = plan.info.package;
10142
+ provider.settings = plan.info.settings;
10143
+ });
10144
+ editor.models.set(plan.info.id, plan.models);
10145
+ } else {
10146
+ editor.add({ info: plan.info, models: plan.models });
10147
+ }
10148
+ }
10149
+ if (seed && !plans.some((plan) => plan.info.id === BASE_PROVIDER_ID)) {
10150
+ editor.remove(BASE_PROVIDER_ID);
10151
+ }
10152
+ logStartupDiagnostics(
10153
+ Object.fromEntries(
10154
+ plans.map((plan) => [plan.info.id, { name: plan.info.name, options: plan.info.settings }])
10155
+ ),
10156
+ ctx.app?.version
10157
+ );
10158
+ })
10159
+ );
10160
+ registrations.push(
10161
+ await ctx.aisdk.hook("sdk", async (event) => {
10162
+ const providerID = event.model.providerID;
10163
+ if (!isClaudeCodeProviderId(providerID)) return;
10164
+ const settings = await resolveSdkSettings(providerID, planned.get(providerID), event.options);
10165
+ event.sdk = deps.createProvider(settings);
10166
+ log.debug("v2 sdk created", { providerID, cliPath: settings.cliPath });
10167
+ })
10168
+ );
10169
+ registrations.push(
10170
+ await ctx.session.hook("model.request", (event) => {
10171
+ if (!isClaudeCodeProviderId(event.model.providerID)) return;
10172
+ event.headers[SESSION_AFFINITY_HEADER] = event.sessionID;
10173
+ event.headers[OPENCODE_AGENT_HEADER] = agentForRequest(event.kind, event.agent);
10174
+ })
10175
+ );
10176
+ const prompt = ctx.session.prompt;
10177
+ if (ctx.command?.transform && typeof prompt === "function") {
10178
+ const send = (input, command, delivery) => prompt.call(ctx.session, {
10179
+ ...input.prompt,
10180
+ sessionID: input.sessionID,
10181
+ text: commandPromptText(command, input.prompt.text),
10182
+ delivery
10183
+ }).then(() => void 0);
10184
+ registrations.push(
10185
+ await ctx.command.transform((editor) => {
10186
+ editor.add({
10187
+ name: DOCTOR_COMMAND,
10188
+ description: DOCTOR_COMMAND_DESCRIPTION,
10189
+ execute: (input) => send(input, DOCTOR_COMMAND, input.delivery)
10190
+ });
10191
+ editor.add({
10192
+ name: "btw",
10193
+ description: BTW_COMMAND_DESCRIPTION,
10194
+ execute: (input) => send(input, "btw", "queue")
10195
+ });
10196
+ })
10197
+ );
10198
+ }
10199
+ const bundledSkills = readBundledSkillInfos();
10200
+ if (typeof ctx.skill?.transform === "function" && bundledSkills.length > 0) {
10201
+ registrations.push(
10202
+ await ctx.skill.transform((editor) => {
10203
+ const added = [];
10204
+ for (const skill of bundledSkills) {
10205
+ if (editor.get(skill.id)) continue;
10206
+ editor.add(skill);
10207
+ added.push(skill.id);
10208
+ }
10209
+ log.debug("v2 bundled skills registered", { added });
10210
+ })
10211
+ );
10212
+ }
10213
+ const events = new AbortController();
10214
+ if (typeof ctx.event?.subscribe === "function") {
10215
+ void releaseDeletedSessions(ctx.event.subscribe.bind(ctx.event), events.signal);
10216
+ }
10217
+ log.info("claude-code plugin set up for opencode 2", {
10218
+ opencode: ctx.app?.version,
10219
+ directory
10220
+ });
10221
+ return async () => {
10222
+ events.abort();
10223
+ for (const registration of registrations.splice(0)) {
10224
+ await registration.dispose().catch(() => void 0);
10225
+ }
10226
+ };
10227
+ };
10228
+ }
10229
+
9567
10230
  // src/index.ts
9568
10231
  function pickOpencodeDirectory(input) {
9569
10232
  if (!input || typeof input !== "object") return void 0;
@@ -9586,7 +10249,7 @@ function registerSideQuestionCommand(config) {
9586
10249
  if (config.command.btw) return false;
9587
10250
  config.command.btw = {
9588
10251
  template: "/btw $ARGUMENTS",
9589
- description: "Ask a side question in the live Claude Code session without changing its context"
10252
+ description: BTW_COMMAND_DESCRIPTION
9590
10253
  };
9591
10254
  return true;
9592
10255
  }
@@ -9595,7 +10258,7 @@ function registerDoctorCommand(config) {
9595
10258
  if (config.command[DOCTOR_COMMAND]) return false;
9596
10259
  config.command[DOCTOR_COMMAND] = {
9597
10260
  template: `/${DOCTOR_COMMAND} $ARGUMENTS`,
9598
- description: "Report what the Claude Code plugin sees: versions, cwd, live processes, pending proxy calls"
10261
+ description: DOCTOR_COMMAND_DESCRIPTION
9599
10262
  };
9600
10263
  return true;
9601
10264
  }
@@ -9644,6 +10307,7 @@ function createClaudeCode(settings = {}) {
9644
10307
  return new ClaudeCodeLanguageModel(modelId, {
9645
10308
  provider: providerName,
9646
10309
  cliPath,
10310
+ hostApi: settings.hostApi,
9647
10311
  cwd: settings.cwd,
9648
10312
  account: settings.account,
9649
10313
  configDir: settings.configDir,
@@ -9973,13 +10637,20 @@ var server = async (input) => {
9973
10637
  };
9974
10638
  var index_default = {
9975
10639
  id: "@khalilgharbaoui/opencode-claude-code-plugin",
9976
- server
10640
+ server,
10641
+ setup: createV2Setup({
10642
+ createProvider: createClaudeCode,
10643
+ defaultProxyTools: DEFAULT_PROXY_TOOL_NAMES,
10644
+ loadConfig: loadMergedOpencodeConfig,
10645
+ buildAgentRegistry: (config) => buildAgentRegistry(config)
10646
+ })
9977
10647
  };
9978
10648
  export {
9979
10649
  ClaudeCodeLanguageModel,
9980
10650
  DEFAULT_PROXY_TOOL_NAMES,
9981
10651
  _resetPlanModeWarningForTests,
9982
10652
  bridgeOpencodeMcp,
10653
+ buildAgentRegistry,
9983
10654
  claudeCodeProviders,
9984
10655
  configModelsForProvider,
9985
10656
  createClaudeCode,