oh-my-opencode-slim 0.9.14 → 0.9.15

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 (44) hide show
  1. package/README.md +116 -56
  2. package/dist/agents/orchestrator.d.ts +2 -1
  3. package/dist/background/background-manager.d.ts +9 -1
  4. package/dist/background/multiplexer-session-manager.d.ts +2 -0
  5. package/dist/cli/index.js +49 -50
  6. package/dist/cli/types.d.ts +0 -1
  7. package/dist/config/schema.d.ts +5 -0
  8. package/dist/index.js +584 -310
  9. package/dist/multiplexer/factory.d.ts +5 -1
  10. package/dist/multiplexer/tmux/index.d.ts +3 -1
  11. package/dist/multiplexer/types.d.ts +2 -2
  12. package/dist/multiplexer/zellij/index.d.ts +1 -1
  13. package/dist/utils/agent-variant.d.ts +15 -1
  14. package/oh-my-opencode-slim.schema.json +12 -0
  15. package/package.json +3 -1
  16. package/src/skills/simplify/README.md +19 -0
  17. package/src/skills/simplify/SKILL.md +138 -0
  18. package/dist/background/tmux-session-manager.d.ts +0 -63
  19. package/dist/cli/chutes-selection.d.ts +0 -3
  20. package/dist/cli/dynamic-model-selection.d.ts +0 -14
  21. package/dist/cli/external-rankings.d.ts +0 -8
  22. package/dist/cli/model-selection.d.ts +0 -30
  23. package/dist/cli/opencode-models.d.ts +0 -18
  24. package/dist/cli/opencode-selection.d.ts +0 -3
  25. package/dist/cli/precedence-resolver.d.ts +0 -16
  26. package/dist/cli/scoring-v2/engine.d.ts +0 -4
  27. package/dist/cli/scoring-v2/features.d.ts +0 -3
  28. package/dist/cli/scoring-v2/index.d.ts +0 -4
  29. package/dist/cli/scoring-v2/types.d.ts +0 -17
  30. package/dist/cli/scoring-v2/weights.d.ts +0 -2
  31. package/dist/hooks/post-read-nudge/index.d.ts +0 -18
  32. package/dist/interview/store.d.ts +0 -9
  33. package/dist/tools/grep/cli.d.ts +0 -3
  34. package/dist/tools/grep/constants.d.ts +0 -18
  35. package/dist/tools/grep/downloader.d.ts +0 -3
  36. package/dist/tools/grep/index.d.ts +0 -5
  37. package/dist/tools/grep/tools.d.ts +0 -2
  38. package/dist/tools/grep/types.d.ts +0 -35
  39. package/dist/tools/grep/utils.d.ts +0 -2
  40. package/dist/tools/quota/api.d.ts +0 -5
  41. package/dist/tools/quota/command.d.ts +0 -1
  42. package/dist/tools/quota/index.d.ts +0 -21
  43. package/dist/tools/quota/types.d.ts +0 -41
  44. package/dist/utils/tmux.d.ts +0 -32
package/dist/index.js CHANGED
@@ -210,9 +210,9 @@ var init_orchestrator = __esm(() => {
210
210
  - Role: Fast execution specialist for well-defined tasks, which empowers orchestrator with parallel, speedy executions
211
211
  - Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
212
212
  - Tools/Constraints: Execution-focused—no research, no architectural decisions
213
- - **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers
213
+ - **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers. Parallelization benefits: Task involves multiple folders and multiple files modificaiton, scoping work per folder and spawning parallel @fixers for each folder.
214
214
  - **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Sequential dependencies
215
- - **Rule of thumb:** Explaining > doing? → yourself. Test file modifications and bounded implementation work usually go to @fixer. Orchestrator paths selection is vastly improved by Fixer. eg it can reduce overall speed if Orchestrator splits what's usually a single task into multiple subtasks and parallelize it with fixer.`,
215
+ - **Rule of thumb:** Explaining > doing? → yourself. Test file modifications and bounded implementation work usually go to @fixer. Bigger or lots of edits, splitting makes sense, parallelized by spawning @fixers per certain scope.`,
216
216
  council: `@council
217
217
  - Role: Multi-LLM consensus engine for high-confidence answers
218
218
  - Stats: 3x slower than orchestrator, 3x or more cost of orchestrator
@@ -3255,7 +3255,7 @@ var require_main = __commonJS((exports) => {
3255
3255
  exports.createMessageConnection = exports.createServerSocketTransport = exports.createClientSocketTransport = exports.createServerPipeTransport = exports.createClientPipeTransport = exports.generateRandomPipeName = exports.StreamMessageWriter = exports.StreamMessageReader = exports.SocketMessageWriter = exports.SocketMessageReader = exports.PortMessageWriter = exports.PortMessageReader = exports.IPCMessageWriter = exports.IPCMessageReader = undefined;
3256
3256
  var ril_1 = require_ril();
3257
3257
  ril_1.default.install();
3258
- var path12 = __require("path");
3258
+ var path13 = __require("path");
3259
3259
  var os4 = __require("os");
3260
3260
  var crypto_1 = __require("crypto");
3261
3261
  var net_1 = __require("net");
@@ -3397,9 +3397,9 @@ var require_main = __commonJS((exports) => {
3397
3397
  }
3398
3398
  let result;
3399
3399
  if (XDG_RUNTIME_DIR) {
3400
- result = path12.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
3400
+ result = path13.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
3401
3401
  } else {
3402
- result = path12.join(os4.tmpdir(), `vscode-${randomSuffix}.sock`);
3402
+ result = path13.join(os4.tmpdir(), `vscode-${randomSuffix}.sock`);
3403
3403
  }
3404
3404
  const limit = safeIpcPathLengths.get(process.platform);
3405
3405
  if (limit !== undefined && result.length > limit) {
@@ -3629,7 +3629,7 @@ var require_index_min = __commonJS((exports) => {
3629
3629
  // node_modules/which/lib/index.js
3630
3630
  var require_lib = __commonJS((exports, module) => {
3631
3631
  var { isexe, sync: isexeSync } = require_index_min();
3632
- var { join: join12, delimiter, sep: sep2, posix } = __require("path");
3632
+ var { join: join13, delimiter, sep: sep2, posix } = __require("path");
3633
3633
  var isWindows = process.platform === "win32";
3634
3634
  var rSlash = new RegExp(`[${posix.sep}${sep2 === posix.sep ? "" : sep2}]`.replace(/(\\)/g, "\\$1"));
3635
3635
  var rRel = new RegExp(`^\\.${rSlash.source}`);
@@ -3656,7 +3656,7 @@ var require_lib = __commonJS((exports, module) => {
3656
3656
  var getPathPart = (raw, cmd) => {
3657
3657
  const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
3658
3658
  const prefix2 = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
3659
- return prefix2 + join12(pathPart, cmd);
3659
+ return prefix2 + join13(pathPart, cmd);
3660
3660
  };
3661
3661
  var which = async (cmd, opt = {}) => {
3662
3662
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
@@ -3762,8 +3762,8 @@ var require_Readability = __commonJS((exports, module) => {
3762
3762
  args.unshift("Reader: (Readability)");
3763
3763
  console.log(...args);
3764
3764
  } else if (typeof dump !== "undefined") {
3765
- var msg = Array.prototype.map.call(arguments, function(x2) {
3766
- return x2 && x2.nodeName ? logNode(x2) : x2;
3765
+ var msg = Array.prototype.map.call(arguments, function(x) {
3766
+ return x && x.nodeName ? logNode(x) : x;
3767
3767
  }).join(" ");
3768
3768
  dump("Reader: (Readability) " + msg + `
3769
3769
  `);
@@ -5181,8 +5181,8 @@ var require_Readability = __commonJS((exports, module) => {
5181
5181
  };
5182
5182
  var haveToRemove = shouldRemoveNode();
5183
5183
  if (isList && haveToRemove) {
5184
- for (var x2 = 0;x2 < node.children.length; x2++) {
5185
- let child = node.children[x2];
5184
+ for (var x = 0;x < node.children.length; x++) {
5185
+ let child = node.children[x];
5186
5186
  if (child.children.length > 1) {
5187
5187
  return haveToRemove;
5188
5188
  }
@@ -8378,7 +8378,7 @@ var require_Element = __commonJS((exports, module) => {
8378
8378
  getElementsByName: { value: function getElementsByName(name) {
8379
8379
  return new FilteredElementList(this, elementNameFilter(String(name)));
8380
8380
  } },
8381
- clone: { value: function clone() {
8381
+ clone: { value: function clone2() {
8382
8382
  var e;
8383
8383
  if (this.namespaceURI !== NAMESPACE.HTML || this.prefix || !this.ownerDocument.isHTML) {
8384
8384
  e = this.ownerDocument.createElementNS(this.namespaceURI, this.prefix !== null ? this.prefix + ":" + this.localName : this.localName);
@@ -9173,7 +9173,7 @@ var require_Text = __commonJS((exports, module) => {
9173
9173
  return result;
9174
9174
  } },
9175
9175
  replaceWholeText: { value: utils.nyi },
9176
- clone: { value: function clone() {
9176
+ clone: { value: function clone2() {
9177
9177
  return new Text(this.ownerDocument, this._data);
9178
9178
  } }
9179
9179
  });
@@ -9216,7 +9216,7 @@ var require_Comment = __commonJS((exports, module) => {
9216
9216
  nodeValue.set.call(this, v === null ? "" : String(v));
9217
9217
  }
9218
9218
  },
9219
- clone: { value: function clone() {
9219
+ clone: { value: function clone2() {
9220
9220
  return new Comment(this.ownerDocument, this._data);
9221
9221
  } }
9222
9222
  });
@@ -9258,7 +9258,7 @@ var require_DocumentFragment = __commonJS((exports, module) => {
9258
9258
  var nodes = select(selector, context);
9259
9259
  return nodes.item ? nodes : new NodeList(nodes);
9260
9260
  } },
9261
- clone: { value: function clone() {
9261
+ clone: { value: function clone2() {
9262
9262
  return new DocumentFragment(this.ownerDocument);
9263
9263
  } },
9264
9264
  isEqual: { value: function isEqual(n) {
@@ -9319,7 +9319,7 @@ var require_ProcessingInstruction = __commonJS((exports, module) => {
9319
9319
  nodeValue.set.call(this, v === null ? "" : String(v));
9320
9320
  }
9321
9321
  },
9322
- clone: { value: function clone() {
9322
+ clone: { value: function clone2() {
9323
9323
  return new ProcessingInstruction(this.ownerDocument, this.target, this._data);
9324
9324
  } },
9325
9325
  isEqual: { value: function isEqual(n) {
@@ -9924,33 +9924,33 @@ var require_URL = __commonJS((exports, module) => {
9924
9924
  else
9925
9925
  return basepath.substring(0, lastslash + 1) + refpath;
9926
9926
  }
9927
- function remove_dot_segments(path13) {
9928
- if (!path13)
9929
- return path13;
9927
+ function remove_dot_segments(path14) {
9928
+ if (!path14)
9929
+ return path14;
9930
9930
  var output = "";
9931
- while (path13.length > 0) {
9932
- if (path13 === "." || path13 === "..") {
9933
- path13 = "";
9931
+ while (path14.length > 0) {
9932
+ if (path14 === "." || path14 === "..") {
9933
+ path14 = "";
9934
9934
  break;
9935
9935
  }
9936
- var twochars = path13.substring(0, 2);
9937
- var threechars = path13.substring(0, 3);
9938
- var fourchars = path13.substring(0, 4);
9936
+ var twochars = path14.substring(0, 2);
9937
+ var threechars = path14.substring(0, 3);
9938
+ var fourchars = path14.substring(0, 4);
9939
9939
  if (threechars === "../") {
9940
- path13 = path13.substring(3);
9940
+ path14 = path14.substring(3);
9941
9941
  } else if (twochars === "./") {
9942
- path13 = path13.substring(2);
9942
+ path14 = path14.substring(2);
9943
9943
  } else if (threechars === "/./") {
9944
- path13 = "/" + path13.substring(3);
9945
- } else if (twochars === "/." && path13.length === 2) {
9946
- path13 = "/";
9947
- } else if (fourchars === "/../" || threechars === "/.." && path13.length === 3) {
9948
- path13 = "/" + path13.substring(4);
9944
+ path14 = "/" + path14.substring(3);
9945
+ } else if (twochars === "/." && path14.length === 2) {
9946
+ path14 = "/";
9947
+ } else if (fourchars === "/../" || threechars === "/.." && path14.length === 3) {
9948
+ path14 = "/" + path14.substring(4);
9949
9949
  output = output.replace(/\/?[^\/]*$/, "");
9950
9950
  } else {
9951
- var segment = path13.match(/(\/?([^\/]*))/)[0];
9951
+ var segment = path14.match(/(\/?([^\/]*))/)[0];
9952
9952
  output += segment;
9953
- path13 = path13.substring(segment.length);
9953
+ path14 = path14.substring(segment.length);
9954
9954
  }
9955
9955
  }
9956
9956
  return output;
@@ -10593,7 +10593,7 @@ var require_htmlelts = __commonJS((exports) => {
10593
10593
  var HTMLElement = exports.HTMLElement = define({
10594
10594
  superclass: Element,
10595
10595
  name: "HTMLElement",
10596
- ctor: function HTMLElement(doc2, localName, prefix2) {
10596
+ ctor: function HTMLElement2(doc2, localName, prefix2) {
10597
10597
  Element.call(this, doc2, localName, utils.NAMESPACE.HTML, prefix2);
10598
10598
  },
10599
10599
  props: {
@@ -10730,7 +10730,7 @@ var require_htmlelts = __commonJS((exports) => {
10730
10730
  });
10731
10731
  var HTMLUnknownElement = define({
10732
10732
  name: "HTMLUnknownElement",
10733
- ctor: function HTMLUnknownElement(doc2, localName, prefix2) {
10733
+ ctor: function HTMLUnknownElement2(doc2, localName, prefix2) {
10734
10734
  HTMLElement.call(this, doc2, localName, prefix2);
10735
10735
  }
10736
10736
  });
@@ -10945,7 +10945,7 @@ var require_htmlelts = __commonJS((exports) => {
10945
10945
  define({
10946
10946
  tag: "form",
10947
10947
  name: "HTMLFormElement",
10948
- ctor: function HTMLFormElement(doc2, localName, prefix2) {
10948
+ ctor: function HTMLFormElement2(doc2, localName, prefix2) {
10949
10949
  HTMLElement.call(this, doc2, localName, prefix2);
10950
10950
  },
10951
10951
  attributes: {
@@ -12002,7 +12002,7 @@ var require_svg = __commonJS((exports) => {
12002
12002
  var SVGElement = define({
12003
12003
  superclass: Element,
12004
12004
  name: "SVGElement",
12005
- ctor: function SVGElement(doc2, localName, prefix2) {
12005
+ ctor: function SVGElement2(doc2, localName, prefix2) {
12006
12006
  Element.call(this, doc2, localName, utils.NAMESPACE.SVG, prefix2);
12007
12007
  },
12008
12008
  props: {
@@ -12421,7 +12421,7 @@ var require_Document = __commonJS((exports, module) => {
12421
12421
  contentType: { get: function contentType() {
12422
12422
  return this._contentType;
12423
12423
  } },
12424
- URL: { get: function URL() {
12424
+ URL: { get: function URL5() {
12425
12425
  return this._address;
12426
12426
  } },
12427
12427
  domain: { get: utils.nyi, set: utils.nyi },
@@ -12549,7 +12549,7 @@ var require_Document = __commonJS((exports, module) => {
12549
12549
  this.defaultView._dispatchEvent(new Event("load"), true);
12550
12550
  }
12551
12551
  } },
12552
- clone: { value: function clone() {
12552
+ clone: { value: function clone2() {
12553
12553
  var d = new Document(this.isHTML, this._address);
12554
12554
  d._quirks = this._quirks;
12555
12555
  d._contentType = this._contentType;
@@ -12858,7 +12858,7 @@ var require_DocumentType = __commonJS((exports, module) => {
12858
12858
  },
12859
12859
  set: function() {}
12860
12860
  },
12861
- clone: { value: function clone() {
12861
+ clone: { value: function clone2() {
12862
12862
  return new DocumentType(this.ownerDocument, this.name, this.publicId, this.systemId);
12863
12863
  } },
12864
12864
  isEqual: { value: function isEqual(n) {
@@ -16259,7 +16259,7 @@ var require_HTMLParser = __commonJS((exports, module) => {
16259
16259
  parser(EOF);
16260
16260
  doc2.modclock = 1;
16261
16261
  }
16262
- var insertToken = htmlparser.insertToken = function insertToken(t, value, arg3, arg4) {
16262
+ var insertToken = htmlparser.insertToken = function insertToken2(t, value, arg3, arg4) {
16263
16263
  flushText();
16264
16264
  var current = stack.top;
16265
16265
  if (!current || current.namespaceURI === NAMESPACE.HTML) {
@@ -21828,14 +21828,14 @@ var require_turndown_cjs = __commonJS((exports, module) => {
21828
21828
  } else if (node.nodeType === 1) {
21829
21829
  replacement = replacementForNode.call(self, node);
21830
21830
  }
21831
- return join14(output, replacement);
21831
+ return join15(output, replacement);
21832
21832
  }, "");
21833
21833
  }
21834
21834
  function postProcess(output) {
21835
21835
  var self = this;
21836
21836
  this.rules.forEach(function(rule) {
21837
21837
  if (typeof rule.append === "function") {
21838
- output = join14(output, rule.append(self.options));
21838
+ output = join15(output, rule.append(self.options));
21839
21839
  }
21840
21840
  });
21841
21841
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -21848,7 +21848,7 @@ var require_turndown_cjs = __commonJS((exports, module) => {
21848
21848
  content = content.trim();
21849
21849
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
21850
21850
  }
21851
- function join14(output, replacement) {
21851
+ function join15(output, replacement) {
21852
21852
  var s1 = trimTrailingNewlines(output);
21853
21853
  var s2 = trimLeadingNewlines(replacement);
21854
21854
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -21887,6 +21887,12 @@ function getOpenCodeConfigPaths() {
21887
21887
 
21888
21888
  // src/cli/custom-skills.ts
21889
21889
  var CUSTOM_SKILLS = [
21890
+ {
21891
+ name: "simplify",
21892
+ description: "Code simplification and readability-focused refactoring",
21893
+ allowedAgents: ["oracle"],
21894
+ sourcePath: "src/skills/simplify"
21895
+ },
21890
21896
  {
21891
21897
  name: "cartography",
21892
21898
  description: "Repository understanding and hierarchical codemap generation",
@@ -21897,13 +21903,6 @@ var CUSTOM_SKILLS = [
21897
21903
 
21898
21904
  // src/cli/skills.ts
21899
21905
  var RECOMMENDED_SKILLS = [
21900
- {
21901
- name: "simplify",
21902
- repo: "https://github.com/brianlovin/claude-config",
21903
- skillName: "simplify",
21904
- allowedAgents: ["oracle"],
21905
- description: "YAGNI code simplification expert"
21906
- },
21907
21906
  {
21908
21907
  name: "agent-browser",
21909
21908
  repo: "https://github.com/vercel-labs/agent-browser",
@@ -35624,7 +35623,7 @@ import * as path from "node:path";
35624
35623
 
35625
35624
  // src/config/agent-mcps.ts
35626
35625
  var DEFAULT_AGENT_MCPS = {
35627
- orchestrator: ["*"],
35626
+ orchestrator: ["*", "!context7"],
35628
35627
  designer: [],
35629
35628
  oracle: [],
35630
35629
  librarian: ["websearch", "context7", "grep_app"],
@@ -35718,7 +35717,8 @@ var AgentOverrideConfigSchema = exports_external.object({
35718
35717
  variant: exports_external.string().optional().catch(undefined),
35719
35718
  skills: exports_external.array(exports_external.string()).optional(),
35720
35719
  mcps: exports_external.array(exports_external.string()).optional(),
35721
- options: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
35720
+ options: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
35721
+ displayName: exports_external.string().min(1).optional()
35722
35722
  });
35723
35723
  var MultiplexerTypeSchema = exports_external.enum(["auto", "tmux", "zellij", "none"]);
35724
35724
  var MultiplexerLayoutSchema = exports_external.enum([
@@ -35772,6 +35772,7 @@ var PluginConfigSchema = exports_external.object({
35772
35772
  setDefaultAgent: exports_external.boolean().optional(),
35773
35773
  scoringEngineVersion: exports_external.enum(["v1", "v2-shadow", "v2"]).optional(),
35774
35774
  balanceProviderUsage: exports_external.boolean().optional(),
35775
+ showStartupToast: exports_external.boolean().optional().describe("Show the startup activation toast when OpenCode starts. Defaults to true."),
35775
35776
  manualPlan: ManualPlanSchema.optional(),
35776
35777
  presets: exports_external.record(exports_external.string(), PresetSchema).optional(),
35777
35778
  agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
@@ -36494,6 +36495,10 @@ ${customAppendPrompt}`;
36494
36495
 
36495
36496
  // src/agents/index.ts
36496
36497
  init_orchestrator();
36498
+ function normalizeDisplayName(displayName) {
36499
+ const trimmed = displayName.trim();
36500
+ return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
36501
+ }
36497
36502
  function applyOverrides(agent, override) {
36498
36503
  if (override.model) {
36499
36504
  if (Array.isArray(override.model)) {
@@ -36513,6 +36518,20 @@ function applyOverrides(agent, override) {
36513
36518
  ...override.options
36514
36519
  };
36515
36520
  }
36521
+ if (override.displayName) {
36522
+ agent.displayName = override.displayName;
36523
+ }
36524
+ }
36525
+ function injectDisplayNames(orchestrator, nameMap) {
36526
+ if (nameMap.size === 0)
36527
+ return;
36528
+ let prompt = orchestrator.config.prompt;
36529
+ if (!prompt)
36530
+ return;
36531
+ for (const [internalName, displayName] of nameMap) {
36532
+ prompt = prompt.replace(new RegExp(`@${internalName}\\b`, "g"), `@${normalizeDisplayName(displayName)}`);
36533
+ }
36534
+ orchestrator.config.prompt = prompt;
36516
36535
  }
36517
36536
  function applyDefaultPermissions(agent, configuredSkills) {
36518
36537
  const existing = agent.config.permission ?? {};
@@ -36580,28 +36599,66 @@ function createAgents(config2) {
36580
36599
  if (orchestratorOverride) {
36581
36600
  applyOverrides(orchestrator, orchestratorOverride);
36582
36601
  }
36602
+ const displayNameMap = new Map;
36603
+ if (orchestrator.displayName) {
36604
+ displayNameMap.set("orchestrator", orchestrator.displayName);
36605
+ }
36606
+ for (const agent of allSubAgents) {
36607
+ if (agent.displayName) {
36608
+ displayNameMap.set(agent.name, agent.displayName);
36609
+ }
36610
+ }
36611
+ const usedDisplayNames = new Set;
36612
+ for (const [, displayName] of displayNameMap) {
36613
+ const normalizedDisplayName = normalizeDisplayName(displayName);
36614
+ if (usedDisplayNames.has(normalizedDisplayName)) {
36615
+ throw new Error(`Duplicate displayName '${normalizedDisplayName}' assigned to multiple agents`);
36616
+ }
36617
+ usedDisplayNames.add(normalizedDisplayName);
36618
+ }
36619
+ for (const displayName of usedDisplayNames) {
36620
+ if (ALL_AGENT_NAMES.includes(displayName)) {
36621
+ throw new Error(`displayName '${displayName}' conflicts with internal agent name`);
36622
+ }
36623
+ }
36624
+ injectDisplayNames(orchestrator, displayNameMap);
36583
36625
  return [orchestrator, ...allSubAgents];
36584
36626
  }
36585
36627
  function getAgentConfigs(config2) {
36586
36628
  const agents = createAgents(config2);
36587
- return Object.fromEntries(agents.map((a) => {
36588
- const sdkConfig = {
36589
- ...a.config,
36590
- description: a.description,
36591
- mcps: getAgentMcpList(a.name, config2)
36592
- };
36593
- if (a.name === "council") {
36629
+ const applyClassification = (name, sdkConfig) => {
36630
+ if (name === "council") {
36594
36631
  sdkConfig.mode = "all";
36595
- } else if (a.name === "councillor" || a.name === "council-master") {
36632
+ } else if (name === "councillor" || name === "council-master") {
36596
36633
  sdkConfig.mode = "subagent";
36597
36634
  sdkConfig.hidden = true;
36598
- } else if (isSubagent(a.name)) {
36635
+ } else if (isSubagent(name)) {
36599
36636
  sdkConfig.mode = "subagent";
36600
- } else if (a.name === "orchestrator") {
36637
+ } else if (name === "orchestrator") {
36601
36638
  sdkConfig.mode = "primary";
36602
36639
  }
36603
- return [a.name, sdkConfig];
36604
- }));
36640
+ };
36641
+ const isInternalOnly = (name) => name === "councillor" || name === "council-master";
36642
+ const entries = [];
36643
+ for (const a of agents) {
36644
+ const sdkConfig = {
36645
+ ...a.config,
36646
+ description: a.description,
36647
+ mcps: getAgentMcpList(a.name, config2)
36648
+ };
36649
+ if (a.displayName) {
36650
+ sdkConfig.displayName = a.displayName;
36651
+ }
36652
+ applyClassification(a.name, sdkConfig);
36653
+ const normalizedDisplayName = a.displayName ? normalizeDisplayName(a.displayName) : undefined;
36654
+ if (normalizedDisplayName && !isInternalOnly(a.name)) {
36655
+ entries.push([normalizedDisplayName, sdkConfig]);
36656
+ entries.push([a.name, { ...sdkConfig, hidden: true }]);
36657
+ continue;
36658
+ }
36659
+ entries.push([a.name, sdkConfig]);
36660
+ }
36661
+ return Object.fromEntries(entries);
36605
36662
  }
36606
36663
  function getDisabledAgents(config2) {
36607
36664
  const userDisabled = config2?.disabled_agents;
@@ -36615,6 +36672,10 @@ function getDisabledAgents(config2) {
36615
36672
  return disabled;
36616
36673
  }
36617
36674
 
36675
+ // src/background/background-manager.ts
36676
+ import * as fs3 from "node:fs";
36677
+ import * as path3 from "node:path";
36678
+
36618
36679
  // src/utils/logger.ts
36619
36680
  import * as fs2 from "node:fs";
36620
36681
  import * as os from "node:os";
@@ -36642,6 +36703,22 @@ function cleanupOldLogs(logDir) {
36642
36703
  }
36643
36704
  }
36644
36705
  } catch {}
36706
+ try {
36707
+ const bgTaskDir = path2.join(logDir, "bg-tasks");
36708
+ const taskFiles = fs2.readdirSync(bgTaskDir);
36709
+ const now = Date.now();
36710
+ for (const entry of taskFiles) {
36711
+ if (!entry.endsWith(".json"))
36712
+ continue;
36713
+ const filePath = path2.join(bgTaskDir, entry);
36714
+ try {
36715
+ const stat = fs2.statSync(filePath);
36716
+ if (now - stat.mtimeMs > RETENTION_MS) {
36717
+ fs2.unlinkSync(filePath);
36718
+ }
36719
+ } catch {}
36720
+ }
36721
+ } catch {}
36645
36722
  }
36646
36723
  function initLogger(sessionId) {
36647
36724
  const dir = getLogDir();
@@ -36727,6 +36804,7 @@ class TmuxMultiplexer {
36727
36804
  hasChecked = false;
36728
36805
  storedLayout;
36729
36806
  storedMainPaneSize;
36807
+ targetPane = process.env.TMUX_PANE;
36730
36808
  constructor(layout = "main-vertical", mainPaneSize = 60) {
36731
36809
  this.storedLayout = layout;
36732
36810
  this.storedMainPaneSize = mainPaneSize;
@@ -36742,14 +36820,25 @@ class TmuxMultiplexer {
36742
36820
  isInsideSession() {
36743
36821
  return !!process.env.TMUX;
36744
36822
  }
36745
- async spawnPane(sessionId, description, serverUrl) {
36823
+ async spawnPane(sessionId, description, serverUrl, directory) {
36746
36824
  const tmux = await this.getBinary();
36747
36825
  if (!tmux) {
36748
36826
  log("[tmux] spawnPane: tmux binary not found");
36749
36827
  return { success: false };
36750
36828
  }
36751
36829
  try {
36752
- const opencodeCmd = `opencode attach ${serverUrl} --session ${sessionId}`;
36830
+ const quotedDirectory = quoteShellArg(directory);
36831
+ const quotedUrl = quoteShellArg(serverUrl);
36832
+ const quotedSessionId = quoteShellArg(sessionId);
36833
+ const opencodeCmd = [
36834
+ "opencode",
36835
+ "attach",
36836
+ quotedUrl,
36837
+ "--session",
36838
+ quotedSessionId,
36839
+ "--dir",
36840
+ quotedDirectory
36841
+ ].join(" ");
36753
36842
  const args = [
36754
36843
  "split-window",
36755
36844
  "-h",
@@ -36757,6 +36846,7 @@ class TmuxMultiplexer {
36757
36846
  "-P",
36758
36847
  "-F",
36759
36848
  "#{pane_id}",
36849
+ ...this.targetArgs(),
36760
36850
  opencodeCmd
36761
36851
  ];
36762
36852
  log("[tmux] spawnPane: executing", { tmux, args });
@@ -36830,19 +36920,25 @@ class TmuxMultiplexer {
36830
36920
  this.storedLayout = layout;
36831
36921
  this.storedMainPaneSize = mainPaneSize;
36832
36922
  try {
36833
- const layoutProc = crossSpawn([tmux, "select-layout", layout], {
36923
+ const layoutProc = crossSpawn([tmux, "select-layout", ...this.targetArgs(), layout], {
36834
36924
  stdout: "pipe",
36835
36925
  stderr: "pipe"
36836
36926
  });
36837
36927
  await layoutProc.exited;
36838
36928
  if (layout === "main-horizontal" || layout === "main-vertical") {
36839
36929
  const sizeOption = layout === "main-horizontal" ? "main-pane-height" : "main-pane-width";
36840
- const sizeProc = crossSpawn([tmux, "set-window-option", sizeOption, `${mainPaneSize}%`], {
36930
+ const sizeProc = crossSpawn([
36931
+ tmux,
36932
+ "set-window-option",
36933
+ ...this.targetArgs(),
36934
+ sizeOption,
36935
+ `${mainPaneSize}%`
36936
+ ], {
36841
36937
  stdout: "pipe",
36842
36938
  stderr: "pipe"
36843
36939
  });
36844
36940
  await sizeProc.exited;
36845
- const reapplyProc = crossSpawn([tmux, "select-layout", layout], {
36941
+ const reapplyProc = crossSpawn([tmux, "select-layout", ...this.targetArgs(), layout], {
36846
36942
  stdout: "pipe",
36847
36943
  stderr: "pipe"
36848
36944
  });
@@ -36857,6 +36953,9 @@ class TmuxMultiplexer {
36857
36953
  await this.isAvailable();
36858
36954
  return this.binaryPath;
36859
36955
  }
36956
+ targetArgs() {
36957
+ return this.targetPane ? ["-t", this.targetPane] : [];
36958
+ }
36860
36959
  async findBinary() {
36861
36960
  const isWindows = process.platform === "win32";
36862
36961
  const cmd = isWindows ? "where" : "which";
@@ -36894,6 +36993,9 @@ class TmuxMultiplexer {
36894
36993
  }
36895
36994
  }
36896
36995
  }
36996
+ function quoteShellArg(value) {
36997
+ return `'${value.replace(/'/g, `'\\''`)}'`;
36998
+ }
36897
36999
 
36898
37000
  // src/multiplexer/zellij/index.ts
36899
37001
  class ZellijMultiplexer {
@@ -36915,7 +37017,7 @@ class ZellijMultiplexer {
36915
37017
  isInsideSession() {
36916
37018
  return !!process.env.ZELLIJ;
36917
37019
  }
36918
- async spawnPane(sessionId, description, serverUrl) {
37020
+ async spawnPane(sessionId, description, serverUrl, directory) {
36919
37021
  const zellij = await this.getBinary();
36920
37022
  if (!zellij)
36921
37023
  return { success: false };
@@ -36928,19 +37030,19 @@ class ZellijMultiplexer {
36928
37030
  this.firstPaneId = result.firstPaneId;
36929
37031
  }
36930
37032
  if (!this.firstPaneUsed && this.firstPaneId) {
36931
- const success2 = await this.runInPane(zellij, this.firstPaneId, sessionId, serverUrl, description);
37033
+ const success2 = await this.runInPane(zellij, this.firstPaneId, sessionId, serverUrl, directory, description);
36932
37034
  if (success2) {
36933
37035
  this.firstPaneUsed = true;
36934
37036
  return { success: true, paneId: this.firstPaneId };
36935
37037
  }
36936
37038
  }
36937
- return await this.createPaneInAgentTab(zellij, sessionId, serverUrl, description);
37039
+ return await this.createPaneInAgentTab(zellij, sessionId, serverUrl, directory, description);
36938
37040
  } catch {
36939
37041
  return { success: false };
36940
37042
  }
36941
37043
  }
36942
- async createPaneInAgentTab(zellij, sessionId, serverUrl, description) {
36943
- const opencodeCmd = `opencode attach ${serverUrl} --session ${sessionId}`;
37044
+ async createPaneInAgentTab(zellij, sessionId, serverUrl, directory, description) {
37045
+ const opencodeCmd = buildOpencodeAttachCommand(sessionId, serverUrl, directory);
36944
37046
  const paneName = description.slice(0, 30).replace(/"/g, "\\\"");
36945
37047
  const currentTabId = await this.getCurrentTabId(zellij);
36946
37048
  const inAgentTab = currentTabId === this.agentTabId;
@@ -36953,7 +37055,7 @@ class ZellijMultiplexer {
36953
37055
  "--close-on-exit",
36954
37056
  "--",
36955
37057
  "sh",
36956
- "-c",
37058
+ "-lc",
36957
37059
  opencodeCmd
36958
37060
  ];
36959
37061
  const proc2 = crossSpawn([zellij, ...args2], {
@@ -36984,7 +37086,7 @@ class ZellijMultiplexer {
36984
37086
  "--close-on-exit",
36985
37087
  "--",
36986
37088
  "sh",
36987
- "-c",
37089
+ "-lc",
36988
37090
  opencodeCmd
36989
37091
  ];
36990
37092
  const proc = crossSpawn([zellij, ...args], {
@@ -37005,15 +37107,15 @@ class ZellijMultiplexer {
37005
37107
  }
37006
37108
  return { success: false };
37007
37109
  }
37008
- async runInPane(zellij, paneId, sessionId, serverUrl, description) {
37110
+ async runInPane(zellij, paneId, sessionId, serverUrl, directory, description) {
37009
37111
  try {
37010
- const opencodeCmd = `opencode attach ${serverUrl} --session ${sessionId}`;
37112
+ const opencodeCmd = buildOpencodeAttachCommand(sessionId, serverUrl, directory);
37011
37113
  await crossSpawn([zellij, "action", "focus-pane", "--pane-id", paneId], {
37012
37114
  stdout: "ignore",
37013
37115
  stderr: "ignore"
37014
37116
  }).exited;
37015
37117
  await crossSpawn([zellij, "action", "rename-pane", "--name", description.slice(0, 30)], { stdout: "ignore", stderr: "ignore" }).exited;
37016
- await crossSpawn([zellij, "action", "write-chars", opencodeCmd], {
37118
+ await crossSpawn([zellij, "action", "write-chars", buildShellLaunchCommand(opencodeCmd)], {
37017
37119
  stdout: "ignore",
37018
37120
  stderr: "ignore"
37019
37121
  }).exited;
@@ -37192,18 +37294,30 @@ class ZellijMultiplexer {
37192
37294
  }
37193
37295
  }
37194
37296
  }
37297
+ function buildOpencodeAttachCommand(sessionId, serverUrl, directory) {
37298
+ return [
37299
+ "opencode",
37300
+ "attach",
37301
+ quoteShellArg2(serverUrl),
37302
+ "--session",
37303
+ quoteShellArg2(sessionId),
37304
+ "--dir",
37305
+ quoteShellArg2(directory)
37306
+ ].join(" ");
37307
+ }
37308
+ function buildShellLaunchCommand(command) {
37309
+ return ["sh", "-lc", quoteShellArg2(command)].join(" ");
37310
+ }
37311
+ function quoteShellArg2(value) {
37312
+ return `'${value.replace(/'/g, `'\\''`)}'`;
37313
+ }
37195
37314
 
37196
37315
  // src/multiplexer/factory.ts
37197
- var multiplexerCache = new Map;
37198
37316
  function getMultiplexer(config2) {
37199
37317
  const { type } = config2;
37200
37318
  if (type === "none") {
37201
37319
  return null;
37202
37320
  }
37203
- const cached2 = multiplexerCache.get(type);
37204
- if (cached2) {
37205
- return cached2;
37206
- }
37207
37321
  let multiplexer;
37208
37322
  let actualType;
37209
37323
  switch (type) {
@@ -37232,7 +37346,6 @@ function getMultiplexer(config2) {
37232
37346
  log(`[multiplexer] Unknown type: ${type}`);
37233
37347
  return null;
37234
37348
  }
37235
- multiplexerCache.set(actualType, multiplexer);
37236
37349
  log(`[multiplexer] Created ${actualType} instance`);
37237
37350
  return multiplexer;
37238
37351
  }
@@ -37269,8 +37382,8 @@ function normalizeAgentName(agentName) {
37269
37382
  return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
37270
37383
  }
37271
37384
  function resolveAgentVariant(config2, agentName) {
37272
- const normalized = normalizeAgentName(agentName);
37273
- const rawVariant = config2?.agents?.[normalized]?.variant;
37385
+ const normalized = resolveRuntimeAgentName(config2, agentName);
37386
+ const rawVariant = getAgentOverride(config2, normalized)?.variant;
37274
37387
  if (typeof rawVariant !== "string") {
37275
37388
  return;
37276
37389
  }
@@ -37281,6 +37394,46 @@ function resolveAgentVariant(config2, agentName) {
37281
37394
  log(`[variant] resolved variant="${trimmed}" for agent "${normalized}"`);
37282
37395
  return trimmed;
37283
37396
  }
37397
+ function resolveRuntimeAgentName(config2, agentName) {
37398
+ const normalized = normalizeAgentName(agentName);
37399
+ if (!normalized) {
37400
+ return normalized;
37401
+ }
37402
+ if (ALL_AGENT_NAMES.includes(normalized)) {
37403
+ return normalized;
37404
+ }
37405
+ for (const internalName of ALL_AGENT_NAMES) {
37406
+ const displayName = getAgentOverride(config2, internalName)?.displayName;
37407
+ if (!displayName) {
37408
+ continue;
37409
+ }
37410
+ if (normalizeAgentName(displayName) === normalized) {
37411
+ return internalName;
37412
+ }
37413
+ }
37414
+ return normalized;
37415
+ }
37416
+ function escapeRegExp(value) {
37417
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
37418
+ }
37419
+ function rewriteDisplayNameMentions(config2, text) {
37420
+ if (!text.includes("@")) {
37421
+ return text;
37422
+ }
37423
+ let rewritten = text;
37424
+ for (const internalName of ALL_AGENT_NAMES) {
37425
+ const displayName = getAgentOverride(config2, internalName)?.displayName;
37426
+ if (!displayName) {
37427
+ continue;
37428
+ }
37429
+ const normalizedDisplayName = normalizeAgentName(displayName);
37430
+ if (!normalizedDisplayName || normalizedDisplayName === internalName) {
37431
+ continue;
37432
+ }
37433
+ rewritten = rewritten.replace(new RegExp(`(^|[^\\w.])@${escapeRegExp(normalizedDisplayName)}\\b`, "g"), `$1@${internalName}`);
37434
+ }
37435
+ return rewritten;
37436
+ }
37284
37437
  function applyAgentVariant(variant, body) {
37285
37438
  if (!variant) {
37286
37439
  return body;
@@ -37433,6 +37586,51 @@ class SubagentDepthTracker {
37433
37586
  }
37434
37587
 
37435
37588
  // src/background/background-manager.ts
37589
+ function persistTask(task) {
37590
+ try {
37591
+ const dir = path3.join(getLogDir(), "bg-tasks");
37592
+ fs3.mkdirSync(dir, { recursive: true });
37593
+ const data = {
37594
+ id: task.id,
37595
+ sessionId: task.sessionId,
37596
+ parentSessionId: task.parentSessionId,
37597
+ description: task.description,
37598
+ agent: task.agent,
37599
+ prompt: task.prompt,
37600
+ config: task.config,
37601
+ status: task.status,
37602
+ result: task.result,
37603
+ error: task.error,
37604
+ startedAt: task.startedAt.toISOString(),
37605
+ completedAt: task.completedAt?.toISOString()
37606
+ };
37607
+ fs3.writeFileSync(path3.join(dir, `${task.id}.json`), JSON.stringify(data), "utf-8");
37608
+ } catch (e) {
37609
+ log(`[background-manager] failed to persist task ${task.id}: ${e}`);
37610
+ }
37611
+ }
37612
+ function loadPersistedTask(taskId) {
37613
+ try {
37614
+ const file2 = path3.join(getLogDir(), "bg-tasks", `${taskId}.json`);
37615
+ const data = JSON.parse(fs3.readFileSync(file2, "utf-8"));
37616
+ return {
37617
+ id: data.id,
37618
+ sessionId: data.sessionId,
37619
+ parentSessionId: data.parentSessionId,
37620
+ description: data.description,
37621
+ agent: data.agent,
37622
+ status: data.status,
37623
+ result: data.result,
37624
+ error: data.error,
37625
+ startedAt: new Date(data.startedAt),
37626
+ completedAt: data.completedAt ? new Date(data.completedAt) : undefined,
37627
+ prompt: data.prompt,
37628
+ config: data.config
37629
+ };
37630
+ } catch {
37631
+ return null;
37632
+ }
37633
+ }
37436
37634
  function generateTaskId() {
37437
37635
  return `bg_${Math.random().toString(36).substring(2, 10)}`;
37438
37636
  }
@@ -37467,6 +37665,9 @@ class BackgroundTaskManager {
37467
37665
  getSubagentRules(agentName) {
37468
37666
  return SUBAGENT_DELEGATION_RULES[agentName] ?? ["explorer"];
37469
37667
  }
37668
+ getSessionAgent(sessionId) {
37669
+ return this.agentBySessionId.get(sessionId) ?? "orchestrator";
37670
+ }
37470
37671
  isAgentAllowed(parentSessionId, requestedAgent) {
37471
37672
  if (this.disabledAgents.has(requestedAgent)) {
37472
37673
  return false;
@@ -37483,11 +37684,12 @@ class BackgroundTaskManager {
37483
37684
  return allowedSubagents.filter((name) => !this.disabledAgents.has(name));
37484
37685
  }
37485
37686
  launch(opts) {
37687
+ const resolvedAgent = resolveRuntimeAgentName(this.config, opts.agent);
37486
37688
  const task = {
37487
37689
  id: generateTaskId(),
37488
37690
  sessionId: undefined,
37489
37691
  description: opts.description,
37490
- agent: opts.agent,
37692
+ agent: resolvedAgent,
37491
37693
  status: "pending",
37492
37694
  startedAt: new Date,
37493
37695
  config: {
@@ -37499,7 +37701,7 @@ class BackgroundTaskManager {
37499
37701
  this.tasks.set(task.id, task);
37500
37702
  this.enqueueStart(task);
37501
37703
  log(`[background-manager] task launched: ${task.id}`, {
37502
- agent: opts.agent,
37704
+ agent: resolvedAgent,
37503
37705
  description: opts.description
37504
37706
  });
37505
37707
  return task;
@@ -37750,18 +37952,29 @@ class BackgroundTaskManager {
37750
37952
  log(`[background-manager] task ${status}: ${task.id}`, {
37751
37953
  description: task.description
37752
37954
  });
37955
+ persistTask(task);
37753
37956
  }
37754
37957
  async sendCompletionNotification(task) {
37958
+ const parentAgent = this.getSessionAgent(task.parentSessionId);
37755
37959
  const message = task.status === "completed" ? `[Background task "${task.description}" completed]` : `[Background task "${task.description}" failed: ${task.error}]`;
37756
37960
  await this.client.session.prompt({
37757
37961
  path: { id: task.parentSessionId },
37758
37962
  body: {
37963
+ agent: parentAgent,
37759
37964
  parts: [createInternalAgentTextPart(message)]
37760
37965
  }
37761
37966
  });
37762
37967
  }
37763
37968
  getResult(taskId) {
37764
- return this.tasks.get(taskId) ?? null;
37969
+ const inMemory = this.tasks.get(taskId);
37970
+ if (inMemory)
37971
+ return inMemory;
37972
+ const fromDisk = loadPersistedTask(taskId);
37973
+ if (fromDisk) {
37974
+ this.tasks.set(taskId, fromDisk);
37975
+ log(`[background-manager] restored task from disk: ${taskId}`);
37976
+ }
37977
+ return fromDisk;
37765
37978
  }
37766
37979
  async waitForCompletion(taskId, timeout = 0) {
37767
37980
  const task = this.tasks.get(taskId);
@@ -37836,12 +38049,14 @@ var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
37836
38049
  class MultiplexerSessionManager {
37837
38050
  client;
37838
38051
  serverUrl;
38052
+ directory;
37839
38053
  multiplexer = null;
37840
38054
  sessions = new Map;
37841
38055
  pollInterval;
37842
38056
  enabled = false;
37843
38057
  constructor(ctx, config2) {
37844
38058
  this.client = ctx.client;
38059
+ this.directory = ctx.directory;
37845
38060
  const defaultPort = process.env.OPENCODE_PORT ?? "4096";
37846
38061
  this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
37847
38062
  this.multiplexer = getMultiplexer(config2);
@@ -37864,6 +38079,7 @@ class MultiplexerSessionManager {
37864
38079
  const sessionId = info.id;
37865
38080
  const parentId = info.parentID;
37866
38081
  const title = info.title ?? "Subagent";
38082
+ const directory = info.directory ?? this.directory;
37867
38083
  if (this.sessions.has(sessionId)) {
37868
38084
  log("[multiplexer-session-manager] session already tracked", {
37869
38085
  sessionId
@@ -37882,7 +38098,7 @@ class MultiplexerSessionManager {
37882
38098
  parentId,
37883
38099
  title
37884
38100
  });
37885
- const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl).catch((err) => {
38101
+ const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl, directory).catch((err) => {
37886
38102
  log("[multiplexer-session-manager] failed to spawn pane", {
37887
38103
  error: String(err)
37888
38104
  });
@@ -38392,8 +38608,8 @@ function ensureApplyPatchError(error48, context) {
38392
38608
  }
38393
38609
 
38394
38610
  // src/hooks/apply-patch/execution-context.ts
38395
- import * as fs3 from "node:fs/promises";
38396
- import path3 from "node:path";
38611
+ import * as fs4 from "node:fs/promises";
38612
+ import path4 from "node:path";
38397
38613
 
38398
38614
  // src/hooks/apply-patch/codec.ts
38399
38615
  function normalizeLineEndings(text) {
@@ -39179,28 +39395,28 @@ function isMissingPathError(error48) {
39179
39395
  }
39180
39396
  async function real(target) {
39181
39397
  const parts = [];
39182
- let current = path3.resolve(target);
39398
+ let current = path4.resolve(target);
39183
39399
  while (true) {
39184
- const exact = await fs3.realpath(current).catch((error48) => {
39400
+ const exact = await fs4.realpath(current).catch((error48) => {
39185
39401
  if (isMissingPathError(error48)) {
39186
39402
  return null;
39187
39403
  }
39188
39404
  throw createApplyPatchInternalError(`Failed to resolve real path: ${current}`, error48);
39189
39405
  });
39190
39406
  if (exact) {
39191
- return parts.length === 0 ? exact : path3.join(exact, ...parts.reverse());
39407
+ return parts.length === 0 ? exact : path4.join(exact, ...parts.reverse());
39192
39408
  }
39193
- const parent = path3.dirname(current);
39409
+ const parent = path4.dirname(current);
39194
39410
  if (parent === current) {
39195
- return parts.length === 0 ? current : path3.join(current, ...parts.reverse());
39411
+ return parts.length === 0 ? current : path4.join(current, ...parts.reverse());
39196
39412
  }
39197
- parts.push(path3.basename(current));
39413
+ parts.push(path4.basename(current));
39198
39414
  current = parent;
39199
39415
  }
39200
39416
  }
39201
39417
  function inside(root, target) {
39202
- const rel = path3.relative(root, target);
39203
- return rel === "" || !rel.startsWith("..") && !path3.isAbsolute(rel);
39418
+ const rel = path4.relative(root, target);
39419
+ return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
39204
39420
  }
39205
39421
  function createPathGuardContext(root, worktree) {
39206
39422
  return {
@@ -39210,7 +39426,7 @@ function createPathGuardContext(root, worktree) {
39210
39426
  };
39211
39427
  }
39212
39428
  async function realCached(ctx, target) {
39213
- const resolvedTarget = path3.resolve(target);
39429
+ const resolvedTarget = path4.resolve(target);
39214
39430
  let pending = ctx.realCache.get(resolvedTarget);
39215
39431
  if (!pending) {
39216
39432
  pending = real(resolvedTarget);
@@ -39241,7 +39457,7 @@ function createFileCacheContext() {
39241
39457
  async function statCached(ctx, filePath) {
39242
39458
  let pending = ctx.stats.get(filePath);
39243
39459
  if (!pending) {
39244
- const nextPending = fs3.stat(filePath).catch((error48) => {
39460
+ const nextPending = fs4.stat(filePath).catch((error48) => {
39245
39461
  if (isMissingPathError(error48)) {
39246
39462
  return null;
39247
39463
  }
@@ -39261,22 +39477,22 @@ async function assertRegularFile(ctx, filePath, verb) {
39261
39477
  function collectPatchTargets(root, hunks) {
39262
39478
  const targets = new Set;
39263
39479
  for (const hunk of hunks) {
39264
- targets.add(path3.resolve(root, hunk.path));
39480
+ targets.add(path4.resolve(root, hunk.path));
39265
39481
  if (hunk.type === "update" && hunk.move_path) {
39266
- targets.add(path3.resolve(root, hunk.move_path));
39482
+ targets.add(path4.resolve(root, hunk.move_path));
39267
39483
  }
39268
39484
  }
39269
39485
  return [...targets];
39270
39486
  }
39271
39487
  function toRelativePatchPath(root, target) {
39272
- const relative = path3.relative(root, target);
39488
+ const relative = path4.relative(root, target);
39273
39489
  return (relative.length === 0 ? "." : relative).replaceAll("\\", "/");
39274
39490
  }
39275
39491
  function normalizePatchPath(root, value) {
39276
- return path3.isAbsolute(value) ? toRelativePatchPath(root, path3.resolve(value)) : value;
39492
+ return path4.isAbsolute(value) ? toRelativePatchPath(root, path4.resolve(value)) : value;
39277
39493
  }
39278
39494
  function normalizePatchPaths(root, hunks) {
39279
- const resolvedRoot = path3.resolve(root);
39495
+ const resolvedRoot = path4.resolve(root);
39280
39496
  const normalized = [];
39281
39497
  let changed = false;
39282
39498
  for (const hunk of hunks) {
@@ -39327,7 +39543,7 @@ function parseValidatedPatch(patchText) {
39327
39543
  }
39328
39544
  async function readPreparedFileText(filePath, verb) {
39329
39545
  try {
39330
- return await fs3.readFile(filePath, "utf-8");
39546
+ return await fs4.readFile(filePath, "utf-8");
39331
39547
  } catch (error48) {
39332
39548
  if (isMissingPathError(error48)) {
39333
39549
  throw createApplyPatchVerificationError(`Failed to read file to ${verb}: ${filePath}`);
@@ -39400,7 +39616,7 @@ function stageAddedText(contents) {
39400
39616
  `;
39401
39617
  }
39402
39618
  // src/hooks/apply-patch/rewrite.ts
39403
- import path4 from "node:path";
39619
+ import path5 from "node:path";
39404
39620
  function normalizeTextLineEndings(text) {
39405
39621
  return text.replace(/\r\n/g, `
39406
39622
  `).replace(/\r/g, `
@@ -39572,7 +39788,7 @@ async function rewritePatch(root, patchText, cfg, worktree) {
39572
39788
  const dependencyGroups = new Map;
39573
39789
  for (const hunk of hunks) {
39574
39790
  if (hunk.type === "add") {
39575
- const filePath2 = path4.resolve(root, hunk.path);
39791
+ const filePath2 = path5.resolve(root, hunk.path);
39576
39792
  await assertPreparedPathMissing(filePath2, "add");
39577
39793
  rewritten.push(hunk);
39578
39794
  clearDependencyGroup(filePath2);
@@ -39594,20 +39810,20 @@ async function rewritePatch(root, patchText, cfg, worktree) {
39594
39810
  continue;
39595
39811
  }
39596
39812
  if (hunk.type === "delete") {
39597
- const filePath2 = path4.resolve(root, hunk.path);
39813
+ const filePath2 = path5.resolve(root, hunk.path);
39598
39814
  await getPreparedFileState(filePath2, "delete");
39599
39815
  clearDependencyGroup(filePath2);
39600
39816
  rewritten.push(hunk);
39601
39817
  staged.set(filePath2, { exists: false, derived: true });
39602
39818
  continue;
39603
39819
  }
39604
- const filePath = path4.resolve(root, hunk.path);
39820
+ const filePath = path5.resolve(root, hunk.path);
39605
39821
  const currentDependency = dependencyGroups.get(filePath);
39606
39822
  const current = await getPreparedFileState(filePath, "update");
39607
39823
  if (!current.exists) {
39608
39824
  throw createApplyPatchVerificationError(`Failed to read file to update: ${filePath}`);
39609
39825
  }
39610
- const movePath = hunk.move_path ? path4.resolve(root, hunk.move_path) : undefined;
39826
+ const movePath = hunk.move_path ? path5.resolve(root, hunk.move_path) : undefined;
39611
39827
  if (movePath && movePath !== filePath) {
39612
39828
  await assertPreparedPathMissing(movePath, "move");
39613
39829
  }
@@ -39771,27 +39987,27 @@ function createApplyPatchHook(ctx) {
39771
39987
  };
39772
39988
  }
39773
39989
  // src/hooks/auto-update-checker/cache.ts
39990
+ import * as fs6 from "node:fs";
39991
+ import * as path8 from "node:path";
39992
+ // src/hooks/auto-update-checker/checker.ts
39774
39993
  import * as fs5 from "node:fs";
39775
39994
  import * as path7 from "node:path";
39776
- // src/hooks/auto-update-checker/checker.ts
39777
- import * as fs4 from "node:fs";
39778
- import * as path6 from "node:path";
39779
39995
  import { fileURLToPath } from "node:url";
39780
39996
 
39781
39997
  // src/hooks/auto-update-checker/constants.ts
39782
39998
  import * as os2 from "node:os";
39783
- import * as path5 from "node:path";
39999
+ import * as path6 from "node:path";
39784
40000
  var PACKAGE_NAME = "oh-my-opencode-slim";
39785
40001
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
39786
40002
  var NPM_FETCH_TIMEOUT = 5000;
39787
40003
  function getCacheDir() {
39788
40004
  if (process.platform === "win32") {
39789
- return path5.join(process.env.LOCALAPPDATA ?? os2.homedir(), "opencode");
40005
+ return path6.join(process.env.LOCALAPPDATA ?? os2.homedir(), "opencode");
39790
40006
  }
39791
- return path5.join(os2.homedir(), ".cache", "opencode");
40007
+ return path6.join(os2.homedir(), ".cache", "opencode");
39792
40008
  }
39793
40009
  var CACHE_DIR = getCacheDir();
39794
- var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
40010
+ var INSTALLED_PACKAGE_JSON = path6.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
39795
40011
  var configPaths = getOpenCodeConfigPaths();
39796
40012
  var USER_OPENCODE_CONFIG = configPaths[0];
39797
40013
  var USER_OPENCODE_CONFIG_JSONC = configPaths[1];
@@ -39820,8 +40036,8 @@ function extractChannel(version2) {
39820
40036
  }
39821
40037
  function getConfigPaths(directory) {
39822
40038
  return [
39823
- path6.join(directory, ".opencode", "opencode.json"),
39824
- path6.join(directory, ".opencode", "opencode.jsonc"),
40039
+ path7.join(directory, ".opencode", "opencode.json"),
40040
+ path7.join(directory, ".opencode", "opencode.jsonc"),
39825
40041
  USER_OPENCODE_CONFIG,
39826
40042
  USER_OPENCODE_CONFIG_JSONC
39827
40043
  ];
@@ -39829,9 +40045,9 @@ function getConfigPaths(directory) {
39829
40045
  function getLocalDevPath(directory) {
39830
40046
  for (const configPath of getConfigPaths(directory)) {
39831
40047
  try {
39832
- if (!fs4.existsSync(configPath))
40048
+ if (!fs5.existsSync(configPath))
39833
40049
  continue;
39834
- const content = fs4.readFileSync(configPath, "utf-8");
40050
+ const content = fs5.readFileSync(configPath, "utf-8");
39835
40051
  const config2 = JSON.parse(stripJsonComments(content));
39836
40052
  const plugins = config2.plugin ?? [];
39837
40053
  for (const entry of plugins) {
@@ -39849,19 +40065,19 @@ function getLocalDevPath(directory) {
39849
40065
  }
39850
40066
  function findPackageJsonUp(startPath) {
39851
40067
  try {
39852
- const stat2 = fs4.statSync(startPath);
39853
- let dir = stat2.isDirectory() ? startPath : path6.dirname(startPath);
40068
+ const stat2 = fs5.statSync(startPath);
40069
+ let dir = stat2.isDirectory() ? startPath : path7.dirname(startPath);
39854
40070
  for (let i = 0;i < 10; i++) {
39855
- const pkgPath = path6.join(dir, "package.json");
39856
- if (fs4.existsSync(pkgPath)) {
40071
+ const pkgPath = path7.join(dir, "package.json");
40072
+ if (fs5.existsSync(pkgPath)) {
39857
40073
  try {
39858
- const content = fs4.readFileSync(pkgPath, "utf-8");
40074
+ const content = fs5.readFileSync(pkgPath, "utf-8");
39859
40075
  const pkg = JSON.parse(content);
39860
40076
  if (pkg.name === PACKAGE_NAME)
39861
40077
  return pkgPath;
39862
40078
  } catch {}
39863
40079
  }
39864
- const parent = path6.dirname(dir);
40080
+ const parent = path7.dirname(dir);
39865
40081
  if (parent === dir)
39866
40082
  break;
39867
40083
  dir = parent;
@@ -39877,7 +40093,7 @@ function getLocalDevVersion(directory) {
39877
40093
  const pkgPath = findPackageJsonUp(localPath);
39878
40094
  if (!pkgPath)
39879
40095
  return null;
39880
- const content = fs4.readFileSync(pkgPath, "utf-8");
40096
+ const content = fs5.readFileSync(pkgPath, "utf-8");
39881
40097
  const pkg = JSON.parse(content);
39882
40098
  return pkg.version ?? null;
39883
40099
  } catch {
@@ -39886,7 +40102,7 @@ function getLocalDevVersion(directory) {
39886
40102
  }
39887
40103
  function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
39888
40104
  try {
39889
- const currentDir = path6.dirname(fileURLToPath(currentModuleUrl));
40105
+ const currentDir = path7.dirname(fileURLToPath(currentModuleUrl));
39890
40106
  return findPackageJsonUp(currentDir);
39891
40107
  } catch (err) {
39892
40108
  log("[auto-update-checker] Failed to resolve runtime package path:", err);
@@ -39896,9 +40112,9 @@ function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
39896
40112
  function findPluginEntry(directory) {
39897
40113
  for (const configPath of getConfigPaths(directory)) {
39898
40114
  try {
39899
- if (!fs4.existsSync(configPath))
40115
+ if (!fs5.existsSync(configPath))
39900
40116
  continue;
39901
- const content = fs4.readFileSync(configPath, "utf-8");
40117
+ const content = fs5.readFileSync(configPath, "utf-8");
39902
40118
  const config2 = JSON.parse(stripJsonComments(content));
39903
40119
  const plugins = config2.plugin ?? [];
39904
40120
  for (const entry of plugins) {
@@ -39926,8 +40142,8 @@ function getCachedVersion() {
39926
40142
  return cachedPackageVersion;
39927
40143
  try {
39928
40144
  const runtimePackageJsonPath = getCurrentRuntimePackageJsonPath();
39929
- if (runtimePackageJsonPath && fs4.existsSync(runtimePackageJsonPath)) {
39930
- const content = fs4.readFileSync(runtimePackageJsonPath, "utf-8");
40145
+ if (runtimePackageJsonPath && fs5.existsSync(runtimePackageJsonPath)) {
40146
+ const content = fs5.readFileSync(runtimePackageJsonPath, "utf-8");
39931
40147
  const pkg = JSON.parse(content);
39932
40148
  if (pkg.version) {
39933
40149
  cachedPackageVersion = pkg.version;
@@ -39936,8 +40152,8 @@ function getCachedVersion() {
39936
40152
  }
39937
40153
  } catch {}
39938
40154
  try {
39939
- if (fs4.existsSync(INSTALLED_PACKAGE_JSON)) {
39940
- const content = fs4.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
40155
+ if (fs5.existsSync(INSTALLED_PACKAGE_JSON)) {
40156
+ const content = fs5.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
39941
40157
  const pkg = JSON.parse(content);
39942
40158
  if (pkg.version) {
39943
40159
  cachedPackageVersion = pkg.version;
@@ -39970,11 +40186,11 @@ async function getLatestVersion(channel = "latest") {
39970
40186
 
39971
40187
  // src/hooks/auto-update-checker/cache.ts
39972
40188
  function removeFromBunLock(installDir, packageName) {
39973
- const lockPath = path7.join(installDir, "bun.lock");
39974
- if (!fs5.existsSync(lockPath))
40189
+ const lockPath = path8.join(installDir, "bun.lock");
40190
+ if (!fs6.existsSync(lockPath))
39975
40191
  return false;
39976
40192
  try {
39977
- const content = fs5.readFileSync(lockPath, "utf-8");
40193
+ const content = fs6.readFileSync(lockPath, "utf-8");
39978
40194
  let lock;
39979
40195
  try {
39980
40196
  lock = JSON.parse(stripJsonComments(content));
@@ -39991,7 +40207,7 @@ function removeFromBunLock(installDir, packageName) {
39991
40207
  modified = true;
39992
40208
  }
39993
40209
  if (modified) {
39994
- fs5.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
40210
+ fs6.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
39995
40211
  log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
39996
40212
  }
39997
40213
  return modified;
@@ -40001,10 +40217,10 @@ function removeFromBunLock(installDir, packageName) {
40001
40217
  }
40002
40218
  }
40003
40219
  function ensureDependencyVersion(packageJsonPath, packageName, version2) {
40004
- if (!fs5.existsSync(packageJsonPath))
40220
+ if (!fs6.existsSync(packageJsonPath))
40005
40221
  return false;
40006
40222
  try {
40007
- const content = fs5.readFileSync(packageJsonPath, "utf-8");
40223
+ const content = fs6.readFileSync(packageJsonPath, "utf-8");
40008
40224
  const pkgJson = JSON.parse(stripJsonComments(content));
40009
40225
  const dependencies = { ...pkgJson.dependencies ?? {} };
40010
40226
  if (dependencies[packageName] === version2) {
@@ -40012,7 +40228,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
40012
40228
  }
40013
40229
  dependencies[packageName] = version2;
40014
40230
  pkgJson.dependencies = dependencies;
40015
- fs5.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
40231
+ fs6.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
40016
40232
  log(`[auto-update-checker] Updated dependency in package.json: ${packageName} → ${version2}`);
40017
40233
  return true;
40018
40234
  } catch (err) {
@@ -40021,28 +40237,28 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
40021
40237
  }
40022
40238
  }
40023
40239
  function removeInstalledPackage(installDir, packageName) {
40024
- const pkgDir = path7.join(installDir, "node_modules", packageName);
40025
- if (!fs5.existsSync(pkgDir))
40240
+ const pkgDir = path8.join(installDir, "node_modules", packageName);
40241
+ if (!fs6.existsSync(pkgDir))
40026
40242
  return false;
40027
- fs5.rmSync(pkgDir, { recursive: true, force: true });
40243
+ fs6.rmSync(pkgDir, { recursive: true, force: true });
40028
40244
  log(`[auto-update-checker] Package removed: ${pkgDir}`);
40029
40245
  return true;
40030
40246
  }
40031
40247
  function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
40032
40248
  if (runtimePackageJsonPath) {
40033
- const packageDir = path7.dirname(runtimePackageJsonPath);
40034
- const nodeModulesDir = path7.dirname(packageDir);
40035
- if (path7.basename(packageDir) === PACKAGE_NAME && path7.basename(nodeModulesDir) === "node_modules") {
40036
- const installDir = path7.dirname(nodeModulesDir);
40037
- const packageJsonPath = path7.join(installDir, "package.json");
40038
- if (fs5.existsSync(packageJsonPath)) {
40249
+ const packageDir = path8.dirname(runtimePackageJsonPath);
40250
+ const nodeModulesDir = path8.dirname(packageDir);
40251
+ if (path8.basename(packageDir) === PACKAGE_NAME && path8.basename(nodeModulesDir) === "node_modules") {
40252
+ const installDir = path8.dirname(nodeModulesDir);
40253
+ const packageJsonPath = path8.join(installDir, "package.json");
40254
+ if (fs6.existsSync(packageJsonPath)) {
40039
40255
  return { installDir, packageJsonPath };
40040
40256
  }
40041
40257
  }
40042
40258
  return null;
40043
40259
  }
40044
- const legacyPackageJsonPath = path7.join(CACHE_DIR, "package.json");
40045
- if (fs5.existsSync(legacyPackageJsonPath)) {
40260
+ const legacyPackageJsonPath = path8.join(CACHE_DIR, "package.json");
40261
+ if (fs6.existsSync(legacyPackageJsonPath)) {
40046
40262
  return { installDir: CACHE_DIR, packageJsonPath: legacyPackageJsonPath };
40047
40263
  }
40048
40264
  return null;
@@ -40627,14 +40843,15 @@ class ForegroundFallbackManager {
40627
40843
  import { createHash } from "node:crypto";
40628
40844
  import {
40629
40845
  existsSync as existsSync4,
40630
- mkdirSync as mkdirSync2,
40846
+ mkdirSync as mkdirSync3,
40631
40847
  readdirSync as readdirSync2,
40848
+ rmdirSync,
40632
40849
  statSync as statSync3,
40633
40850
  unlinkSync as unlinkSync2,
40634
- writeFileSync as writeFileSync3
40851
+ writeFileSync as writeFileSync4
40635
40852
  } from "node:fs";
40636
- import { join as join7 } from "node:path";
40637
- var lastCleanup = 0;
40853
+ import { basename as basename2, extname, join as join8 } from "node:path";
40854
+ var lastCleanupByDir = new Map;
40638
40855
  var CLEANUP_INTERVAL = 10 * 60 * 1000;
40639
40856
  function isImagePart(p) {
40640
40857
  if (p.type === "image")
@@ -40668,41 +40885,84 @@ function extFromMime(mime) {
40668
40885
  };
40669
40886
  return map2[mime] ?? ".png";
40670
40887
  }
40888
+ function sanitizeFilename(name) {
40889
+ return name.replace(/[^a-zA-Z0-9._-]/g, "_");
40890
+ }
40891
+ function cleanupOldImages(dir, saveDir) {
40892
+ const now = Date.now();
40893
+ const lastCleanup = lastCleanupByDir.get(dir) ?? 0;
40894
+ if (now - lastCleanup < CLEANUP_INTERVAL)
40895
+ return;
40896
+ lastCleanupByDir.set(dir, now);
40897
+ try {
40898
+ const maxAge = 60 * 60 * 1000;
40899
+ for (const f of readdirSync2(dir)) {
40900
+ const fp = join8(dir, f);
40901
+ try {
40902
+ if (now - statSync3(fp).mtimeMs > maxAge)
40903
+ unlinkSync2(fp);
40904
+ } catch {}
40905
+ }
40906
+ if (dir !== saveDir) {
40907
+ try {
40908
+ rmdirSync(dir);
40909
+ lastCleanupByDir.delete(dir);
40910
+ } catch {}
40911
+ }
40912
+ } catch {}
40913
+ }
40914
+ function writeUniqueFile(dir, name, data, log2) {
40915
+ const ext = extname(name);
40916
+ const base = basename2(name, ext) || name;
40917
+ let candidate = join8(dir, name);
40918
+ let counter = 0;
40919
+ const MAX_ATTEMPTS = 1000;
40920
+ for (let attempt = 0;attempt < MAX_ATTEMPTS; attempt++) {
40921
+ try {
40922
+ writeFileSync4(candidate, data, { flag: "wx" });
40923
+ return candidate;
40924
+ } catch (e) {
40925
+ if (e instanceof Error && e.code === "EEXIST") {
40926
+ counter += 1;
40927
+ candidate = join8(dir, `${base}-${counter}${ext}`);
40928
+ continue;
40929
+ }
40930
+ log2(`[image-hook] failed to save image: ${e}`);
40931
+ return null;
40932
+ }
40933
+ }
40934
+ log2(`[image-hook] failed to save image: max attempts (${MAX_ATTEMPTS}) reached`);
40935
+ return null;
40936
+ }
40671
40937
  function processImageAttachments(args) {
40672
40938
  const { messages, workDir, disabledAgents, log: log2 } = args;
40673
40939
  const observerEnabled = !disabledAgents.has("observer");
40674
40940
  if (!observerEnabled)
40675
40941
  return;
40676
- const saveDir = join7(workDir, ".opencode", "images");
40677
- const gitignorePath = join7(workDir, ".opencode", ".gitignore");
40942
+ const saveDir = join8(workDir, ".opencode", "images");
40943
+ const gitignorePath = join8(workDir, ".opencode", ".gitignore");
40678
40944
  try {
40679
- mkdirSync2(saveDir, { recursive: true });
40945
+ mkdirSync3(saveDir, { recursive: true });
40680
40946
  if (!existsSync4(gitignorePath))
40681
- writeFileSync3(gitignorePath, `*
40947
+ writeFileSync4(gitignorePath, `*
40682
40948
  `);
40683
40949
  } catch (e) {
40684
40950
  log2(`[image-hook] failed to create image directory: ${e}`);
40685
40951
  }
40686
- const now = Date.now();
40687
- if (now - lastCleanup > CLEANUP_INTERVAL) {
40688
- lastCleanup = now;
40689
- try {
40690
- const maxAge = 60 * 60 * 1000;
40691
- for (const f of readdirSync2(saveDir)) {
40692
- const fp = join7(saveDir, f);
40693
- try {
40694
- if (now - statSync3(fp).mtimeMs > maxAge)
40695
- unlinkSync2(fp);
40696
- } catch {}
40697
- }
40698
- } catch {}
40699
- }
40700
40952
  for (const msg of messages) {
40701
40953
  if (msg.info.role !== "user")
40702
40954
  continue;
40703
40955
  const imageParts = msg.parts.filter(isImagePart);
40704
40956
  if (imageParts.length === 0)
40705
40957
  continue;
40958
+ const sessionSubdir = msg.info.sessionID ? sanitizeFilename(msg.info.sessionID) : undefined;
40959
+ const targetDir = sessionSubdir ? join8(saveDir, sessionSubdir) : saveDir;
40960
+ try {
40961
+ mkdirSync3(targetDir, { recursive: true });
40962
+ } catch (e) {
40963
+ log2(`[image-hook] failed to create target image directory: ${e}`);
40964
+ }
40965
+ cleanupOldImages(targetDir, saveDir);
40706
40966
  const savedPaths = [];
40707
40967
  for (const p of imageParts) {
40708
40968
  const url2 = p.url;
@@ -40711,14 +40971,13 @@ function processImageAttachments(args) {
40711
40971
  const decoded = decodeDataUrl(url2);
40712
40972
  if (decoded) {
40713
40973
  const hash2 = createHash("sha1").update(decoded.data).digest("hex").slice(0, 8);
40714
- const name = filename ?? `image-${hash2}${extFromMime(decoded.mime)}`;
40715
- const filePath = join7(saveDir, name);
40716
- try {
40717
- writeFileSync3(filePath, decoded.data);
40974
+ const sanitizedFilename = filename ? sanitizeFilename(filename) : undefined;
40975
+ const baseName = sanitizedFilename ? sanitizedFilename.replace(/\.[^.]+$/, "") || "image" : "image";
40976
+ const ext = sanitizedFilename ? extname(sanitizedFilename) || extFromMime(decoded.mime) : extFromMime(decoded.mime);
40977
+ const name = `${baseName}-${hash2}${ext}`;
40978
+ const filePath = writeUniqueFile(targetDir, name, decoded.data, log2);
40979
+ if (filePath)
40718
40980
  savedPaths.push(filePath);
40719
- } catch (e) {
40720
- log2(`[image-hook] failed to save image: ${e}`);
40721
- }
40722
40981
  }
40723
40982
  }
40724
40983
  }
@@ -41525,37 +41784,37 @@ function createTodoContinuationHook(ctx, config2) {
41525
41784
  };
41526
41785
  }
41527
41786
  // src/interview/manager.ts
41528
- import path11 from "node:path";
41787
+ import path12 from "node:path";
41529
41788
 
41530
41789
  // src/interview/dashboard.ts
41531
41790
  import crypto from "node:crypto";
41532
41791
  import * as fsSync2 from "node:fs";
41533
- import fs7 from "node:fs/promises";
41792
+ import fs8 from "node:fs/promises";
41534
41793
  import {
41535
41794
  createServer
41536
41795
  } from "node:http";
41537
41796
  import os3 from "node:os";
41538
- import path9 from "node:path";
41797
+ import path10 from "node:path";
41539
41798
  import { URL as URL2 } from "node:url";
41540
41799
 
41541
41800
  // src/interview/document.ts
41542
41801
  import * as fsSync from "node:fs";
41543
- import * as fs6 from "node:fs/promises";
41544
- import * as path8 from "node:path";
41802
+ import * as fs7 from "node:fs/promises";
41803
+ import * as path9 from "node:path";
41545
41804
  var DEFAULT_OUTPUT_FOLDER = "interview";
41546
41805
  function normalizeOutputFolder(outputFolder) {
41547
41806
  const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
41548
41807
  return normalized || DEFAULT_OUTPUT_FOLDER;
41549
41808
  }
41550
41809
  function createInterviewDirectoryPath(directory, outputFolder) {
41551
- return path8.join(directory, normalizeOutputFolder(outputFolder));
41810
+ return path9.join(directory, normalizeOutputFolder(outputFolder));
41552
41811
  }
41553
41812
  function createInterviewFilePath(directory, outputFolder, idea) {
41554
41813
  const fileName = `${slugify2(idea) || "interview"}.md`;
41555
- return path8.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
41814
+ return path9.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
41556
41815
  }
41557
41816
  function relativeInterviewPath(directory, filePath) {
41558
- return path8.relative(directory, filePath) || path8.basename(filePath);
41817
+ return path9.relative(directory, filePath) || path9.basename(filePath);
41559
41818
  }
41560
41819
  function resolveExistingInterviewPath(directory, outputFolder, value) {
41561
41820
  const trimmed = value.trim();
@@ -41564,22 +41823,22 @@ function resolveExistingInterviewPath(directory, outputFolder, value) {
41564
41823
  }
41565
41824
  const outputDir = createInterviewDirectoryPath(directory, outputFolder);
41566
41825
  const candidates = new Set;
41567
- const resolvedRoot = path8.resolve(directory);
41568
- if (path8.isAbsolute(trimmed)) {
41826
+ const resolvedRoot = path9.resolve(directory);
41827
+ if (path9.isAbsolute(trimmed)) {
41569
41828
  candidates.add(trimmed);
41570
41829
  } else {
41571
- candidates.add(path8.resolve(directory, trimmed));
41572
- candidates.add(path8.join(outputDir, trimmed));
41830
+ candidates.add(path9.resolve(directory, trimmed));
41831
+ candidates.add(path9.join(outputDir, trimmed));
41573
41832
  if (!trimmed.endsWith(".md")) {
41574
- candidates.add(path8.join(outputDir, `${trimmed}.md`));
41833
+ candidates.add(path9.join(outputDir, `${trimmed}.md`));
41575
41834
  }
41576
41835
  }
41577
41836
  for (const candidate of candidates) {
41578
- if (path8.extname(candidate) !== ".md") {
41837
+ if (path9.extname(candidate) !== ".md") {
41579
41838
  continue;
41580
41839
  }
41581
- const resolved = path8.resolve(candidate);
41582
- if (!resolved.startsWith(resolvedRoot + path8.sep) && resolved !== resolvedRoot) {
41840
+ const resolved = path9.resolve(candidate);
41841
+ if (!resolved.startsWith(resolvedRoot + path9.sep) && resolved !== resolvedRoot) {
41583
41842
  continue;
41584
41843
  }
41585
41844
  if (fsSync.existsSync(candidate)) {
@@ -41659,11 +41918,11 @@ function parseFrontmatter(content) {
41659
41918
  return result;
41660
41919
  }
41661
41920
  async function ensureInterviewFile(record2) {
41662
- await fs6.mkdir(path8.dirname(record2.markdownPath), { recursive: true });
41921
+ await fs7.mkdir(path9.dirname(record2.markdownPath), { recursive: true });
41663
41922
  try {
41664
- await fs6.access(record2.markdownPath);
41923
+ await fs7.access(record2.markdownPath);
41665
41924
  } catch {
41666
- await fs6.writeFile(record2.markdownPath, buildInterviewDocument(record2.idea, "", "", {
41925
+ await fs7.writeFile(record2.markdownPath, buildInterviewDocument(record2.idea, "", "", {
41667
41926
  sessionID: record2.sessionID,
41668
41927
  baseMessageCount: record2.baseMessageCount
41669
41928
  }), "utf8");
@@ -41671,10 +41930,10 @@ async function ensureInterviewFile(record2) {
41671
41930
  }
41672
41931
  async function readInterviewDocument(record2) {
41673
41932
  try {
41674
- return await fs6.readFile(record2.markdownPath, "utf8");
41933
+ return await fs7.readFile(record2.markdownPath, "utf8");
41675
41934
  } catch {}
41676
41935
  await ensureInterviewFile(record2);
41677
- return fs6.readFile(record2.markdownPath, "utf8");
41936
+ return fs7.readFile(record2.markdownPath, "utf8");
41678
41937
  }
41679
41938
  async function rewriteInterviewDocument(record2, summary) {
41680
41939
  const existing = await readInterviewDocument(record2);
@@ -41683,7 +41942,7 @@ async function rewriteInterviewDocument(record2, summary) {
41683
41942
  sessionID: record2.sessionID,
41684
41943
  baseMessageCount: record2.baseMessageCount
41685
41944
  });
41686
- await fs6.writeFile(record2.markdownPath, next, "utf8");
41945
+ await fs7.writeFile(record2.markdownPath, next, "utf8");
41687
41946
  return next;
41688
41947
  }
41689
41948
  async function appendInterviewAnswers(record2, questions, answers) {
@@ -41701,7 +41960,7 @@ A: ${answer.answer.trim()}` : null;
41701
41960
  const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
41702
41961
 
41703
41962
  `);
41704
- await fs6.writeFile(record2.markdownPath, buildInterviewDocument(record2.idea, summary, nextHistory, {
41963
+ await fs7.writeFile(record2.markdownPath, buildInterviewDocument(record2.idea, summary, nextHistory, {
41705
41964
  sessionID: record2.sessionID,
41706
41965
  baseMessageCount: record2.baseMessageCount
41707
41966
  }), "utf8");
@@ -43181,12 +43440,12 @@ function renderInterviewPage(interviewId, resumeSlug) {
43181
43440
 
43182
43441
  // src/interview/dashboard.ts
43183
43442
  function getAuthFilePath(port) {
43184
- const dataHome = process.env.XDG_DATA_HOME || path9.join(os3.homedir(), ".local", "share");
43185
- return path9.join(dataHome, "opencode", `.dashboard-${port}.json`);
43443
+ const dataHome = process.env.XDG_DATA_HOME || path10.join(os3.homedir(), ".local", "share");
43444
+ return path10.join(dataHome, "opencode", `.dashboard-${port}.json`);
43186
43445
  }
43187
43446
  function writeAuthFile(port, token) {
43188
43447
  const filePath = getAuthFilePath(port);
43189
- const dir = path9.dirname(filePath);
43448
+ const dir = path10.dirname(filePath);
43190
43449
  try {
43191
43450
  fsSync2.mkdirSync(dir, { recursive: true });
43192
43451
  } catch {}
@@ -43203,7 +43462,7 @@ function removeAuthFile(port) {
43203
43462
  }
43204
43463
  async function readDashboardAuthFile(port) {
43205
43464
  try {
43206
- const content = await fs7.readFile(getAuthFilePath(port), "utf8");
43465
+ const content = await fs8.readFile(getAuthFilePath(port), "utf8");
43207
43466
  const data = JSON.parse(content);
43208
43467
  try {
43209
43468
  process.kill(data.pid, 0);
@@ -43323,10 +43582,10 @@ function createDashboardServer(config2) {
43323
43582
  const directories = getKnownDirectories();
43324
43583
  const items = [];
43325
43584
  for (const dir of directories) {
43326
- const interviewDir = path9.join(dir, config2.outputFolder);
43585
+ const interviewDir = path10.join(dir, config2.outputFolder);
43327
43586
  let entries;
43328
43587
  try {
43329
- entries = await fs7.readdir(interviewDir);
43588
+ entries = await fs8.readdir(interviewDir);
43330
43589
  } catch {
43331
43590
  continue;
43332
43591
  }
@@ -43335,7 +43594,7 @@ function createDashboardServer(config2) {
43335
43594
  continue;
43336
43595
  let content;
43337
43596
  try {
43338
- content = await fs7.readFile(path9.join(interviewDir, entry), "utf8");
43597
+ content = await fs8.readFile(path10.join(interviewDir, entry), "utf8");
43339
43598
  } catch {
43340
43599
  continue;
43341
43600
  }
@@ -43361,10 +43620,10 @@ function createDashboardServer(config2) {
43361
43620
  const directories = getKnownDirectories();
43362
43621
  let rebuilt = 0;
43363
43622
  for (const dir of directories) {
43364
- const interviewDir = path9.join(dir, config2.outputFolder);
43623
+ const interviewDir = path10.join(dir, config2.outputFolder);
43365
43624
  let entries;
43366
43625
  try {
43367
- entries = await fs7.readdir(interviewDir);
43626
+ entries = await fs8.readdir(interviewDir);
43368
43627
  } catch {
43369
43628
  continue;
43370
43629
  }
@@ -43373,7 +43632,7 @@ function createDashboardServer(config2) {
43373
43632
  continue;
43374
43633
  let content;
43375
43634
  try {
43376
- content = await fs7.readFile(path9.join(interviewDir, entry), "utf8");
43635
+ content = await fs8.readFile(path10.join(interviewDir, entry), "utf8");
43377
43636
  } catch {
43378
43637
  continue;
43379
43638
  }
@@ -43399,7 +43658,7 @@ function createDashboardServer(config2) {
43399
43658
  questions: [],
43400
43659
  pendingAnswers: null,
43401
43660
  lastUpdatedAt: fm.updatedAt ? new Date(fm.updatedAt).getTime() : Date.now(),
43402
- filePath: path9.join(interviewDir, entry),
43661
+ filePath: path10.join(interviewDir, entry),
43403
43662
  nudgeAction: null
43404
43663
  });
43405
43664
  if (!sessions.has(fm.sessionID)) {
@@ -43657,15 +43916,15 @@ function createDashboardServer(config2) {
43657
43916
  let markdownPath = entry.filePath;
43658
43917
  if (entry.filePath) {
43659
43918
  try {
43660
- document = await fs7.readFile(entry.filePath, "utf8");
43919
+ document = await fs8.readFile(entry.filePath, "utf8");
43661
43920
  } catch {}
43662
43921
  } else {
43663
43922
  const dirs = getKnownDirectories();
43664
43923
  for (const dir of dirs) {
43665
43924
  const slug = extractResumeSlug(interviewId);
43666
- const candidate = path9.join(dir, config2.outputFolder, `${slug}.md`);
43925
+ const candidate = path10.join(dir, config2.outputFolder, `${slug}.md`);
43667
43926
  try {
43668
- document = await fs7.readFile(candidate, "utf8");
43927
+ document = await fs8.readFile(candidate, "utf8");
43669
43928
  markdownPath = candidate;
43670
43929
  entry.filePath = candidate;
43671
43930
  break;
@@ -44190,8 +44449,8 @@ function createInterviewServer(deps) {
44190
44449
 
44191
44450
  // src/interview/service.ts
44192
44451
  import { spawn } from "node:child_process";
44193
- import * as fs8 from "node:fs/promises";
44194
- import * as path10 from "node:path";
44452
+ import * as fs9 from "node:fs/promises";
44453
+ import * as path11 from "node:path";
44195
44454
 
44196
44455
  // src/interview/types.ts
44197
44456
  var RawQuestionSchema = exports_external.object({
@@ -44444,18 +44703,18 @@ function createInterviewService(ctx, config2, deps) {
44444
44703
  if (!newSlug) {
44445
44704
  return;
44446
44705
  }
44447
- const currentFileName = path10.basename(interview.markdownPath, ".md");
44706
+ const currentFileName = path11.basename(interview.markdownPath, ".md");
44448
44707
  if (currentFileName === newSlug) {
44449
44708
  return;
44450
44709
  }
44451
- const dir = path10.dirname(interview.markdownPath);
44452
- const newPath = path10.join(dir, `${newSlug}.md`);
44710
+ const dir = path11.dirname(interview.markdownPath);
44711
+ const newPath = path11.join(dir, `${newSlug}.md`);
44453
44712
  try {
44454
- await fs8.access(newPath);
44713
+ await fs9.access(newPath);
44455
44714
  return;
44456
44715
  } catch {}
44457
44716
  try {
44458
- await fs8.rename(interview.markdownPath, newPath);
44717
+ await fs9.rename(interview.markdownPath, newPath);
44459
44718
  interview.markdownPath = newPath;
44460
44719
  log("[interview] renamed file with assistant title:", {
44461
44720
  from: currentFileName,
@@ -44521,13 +44780,13 @@ function createInterviewService(ctx, config2, deps) {
44521
44780
  active.status = "abandoned";
44522
44781
  }
44523
44782
  }
44524
- const document = await fs8.readFile(markdownPath, "utf8");
44783
+ const document = await fs9.readFile(markdownPath, "utf8");
44525
44784
  const messages = await loadMessages(sessionID);
44526
44785
  const title = extractTitle(document);
44527
44786
  const record2 = {
44528
- id: `${Date.now()}-${++idCounter}-${slugify2(path10.basename(markdownPath, ".md")) || "interview"}`,
44787
+ id: `${Date.now()}-${++idCounter}-${slugify2(path11.basename(markdownPath, ".md")) || "interview"}`,
44529
44788
  sessionID,
44530
- idea: title || path10.basename(markdownPath, ".md"),
44789
+ idea: title || path11.basename(markdownPath, ".md"),
44531
44790
  markdownPath,
44532
44791
  createdAt: nowIso(),
44533
44792
  status: "active",
@@ -44691,7 +44950,7 @@ function createInterviewService(ctx, config2, deps) {
44691
44950
  const resumePath = resolveExistingInterviewPath(ctx.directory, outputFolder, idea);
44692
44951
  if (resumePath) {
44693
44952
  const interview2 = await resumeInterview(input.sessionID, resumePath);
44694
- const document = await fs8.readFile(interview2.markdownPath, "utf8");
44953
+ const document = await fs9.readFile(interview2.markdownPath, "utf8");
44695
44954
  await notifyInterviewUrl(input.sessionID, interview2);
44696
44955
  output.parts.push(createInternalAgentTextPart(buildResumePrompt(document, maxQuestions)));
44697
44956
  return;
@@ -44741,10 +45000,10 @@ function createInterviewService(ctx, config2, deps) {
44741
45000
  return fileCache.items;
44742
45001
  }
44743
45002
  const outputDir = createInterviewDirectoryPath(ctx.directory, outputFolder);
44744
- const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path10.resolve(i.markdownPath)));
45003
+ const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path11.resolve(i.markdownPath)));
44745
45004
  let entries;
44746
45005
  try {
44747
- entries = await fs8.readdir(outputDir);
45006
+ entries = await fs9.readdir(outputDir);
44748
45007
  } catch {
44749
45008
  return [];
44750
45009
  }
@@ -44752,12 +45011,12 @@ function createInterviewService(ctx, config2, deps) {
44752
45011
  for (const entry of entries) {
44753
45012
  if (!entry.endsWith(".md"))
44754
45013
  continue;
44755
- const fullPath = path10.join(outputDir, entry);
44756
- if (activePaths.has(path10.resolve(fullPath)))
45014
+ const fullPath = path11.join(outputDir, entry);
45015
+ if (activePaths.has(path11.resolve(fullPath)))
44757
45016
  continue;
44758
45017
  let content;
44759
45018
  try {
44760
- content = await fs8.readFile(fullPath, "utf8");
45019
+ content = await fs9.readFile(fullPath, "utf8");
44761
45020
  } catch {
44762
45021
  continue;
44763
45022
  }
@@ -44850,7 +45109,7 @@ function createInterviewManager(ctx, config2) {
44850
45109
  const outputFolder = interviewConfig?.outputFolder ?? "interview";
44851
45110
  if (!dashboardEnabled) {
44852
45111
  const service2 = createInterviewService(ctx, interviewConfig);
44853
- const resolvedOutputPath = path11.join(ctx.directory, outputFolder);
45112
+ const resolvedOutputPath = path12.join(ctx.directory, outputFolder);
44854
45113
  const server = createInterviewServer({
44855
45114
  getState: async (interviewId) => service2.getInterviewState(interviewId),
44856
45115
  listInterviewFiles: async () => service2.listInterviewFiles(),
@@ -44955,7 +45214,7 @@ function createInterviewManager(ctx, config2) {
44955
45214
  listInterviews: () => service.listInterviews(),
44956
45215
  submitAnswers: async (interviewId, answers) => service.submitAnswers(interviewId, answers),
44957
45216
  handleNudgeAction: async (interviewId, action) => service.handleNudgeAction(interviewId, action),
44958
- outputFolder: path11.join(ctx.directory, outputFolder),
45217
+ outputFolder: path12.join(ctx.directory, outputFolder),
44959
45218
  port: 0
44960
45219
  });
44961
45220
  service.setBaseUrlResolver(() => perSessionServer.ensureStarted());
@@ -45231,13 +45490,13 @@ import { existsSync as existsSync8 } from "node:fs";
45231
45490
  // src/tools/ast-grep/constants.ts
45232
45491
  import { existsSync as existsSync7, statSync as statSync4 } from "node:fs";
45233
45492
  import { createRequire as createRequire3 } from "node:module";
45234
- import { dirname as dirname6, join as join11 } from "node:path";
45493
+ import { dirname as dirname6, join as join12 } from "node:path";
45235
45494
 
45236
45495
  // src/tools/ast-grep/downloader.ts
45237
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "node:fs";
45496
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "node:fs";
45238
45497
  import { createRequire as createRequire2 } from "node:module";
45239
45498
  import { homedir as homedir4 } from "node:os";
45240
- import { join as join10 } from "node:path";
45499
+ import { join as join11 } from "node:path";
45241
45500
  var REPO = "ast-grep/ast-grep";
45242
45501
  var DEFAULT_VERSION = "0.40.0";
45243
45502
  function getAstGrepVersion() {
@@ -45261,18 +45520,18 @@ var PLATFORM_MAP = {
45261
45520
  function getCacheDir2() {
45262
45521
  if (process.platform === "win32") {
45263
45522
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
45264
- const base2 = localAppData || join10(homedir4(), "AppData", "Local");
45265
- return join10(base2, "oh-my-opencode-slim", "bin");
45523
+ const base2 = localAppData || join11(homedir4(), "AppData", "Local");
45524
+ return join11(base2, "oh-my-opencode-slim", "bin");
45266
45525
  }
45267
45526
  const xdgCache = process.env.XDG_CACHE_HOME;
45268
- const base = xdgCache || join10(homedir4(), ".cache");
45269
- return join10(base, "oh-my-opencode-slim", "bin");
45527
+ const base = xdgCache || join11(homedir4(), ".cache");
45528
+ return join11(base, "oh-my-opencode-slim", "bin");
45270
45529
  }
45271
45530
  function getBinaryName() {
45272
45531
  return process.platform === "win32" ? "sg.exe" : "sg";
45273
45532
  }
45274
45533
  function getCachedBinaryPath() {
45275
- const binaryPath = join10(getCacheDir2(), getBinaryName());
45534
+ const binaryPath = join11(getCacheDir2(), getBinaryName());
45276
45535
  return existsSync6(binaryPath) ? binaryPath : null;
45277
45536
  }
45278
45537
  async function downloadAstGrep(version2 = DEFAULT_VERSION) {
@@ -45284,7 +45543,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
45284
45543
  }
45285
45544
  const cacheDir = getCacheDir2();
45286
45545
  const binaryName = getBinaryName();
45287
- const binaryPath = join10(cacheDir, binaryName);
45546
+ const binaryPath = join11(cacheDir, binaryName);
45288
45547
  if (existsSync6(binaryPath)) {
45289
45548
  return binaryPath;
45290
45549
  }
@@ -45294,13 +45553,13 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
45294
45553
  console.log(`[oh-my-opencode-slim] Downloading ast-grep binary...`);
45295
45554
  try {
45296
45555
  if (!existsSync6(cacheDir)) {
45297
- mkdirSync4(cacheDir, { recursive: true });
45556
+ mkdirSync5(cacheDir, { recursive: true });
45298
45557
  }
45299
45558
  const response = await fetch(downloadUrl, { redirect: "follow" });
45300
45559
  if (!response.ok) {
45301
45560
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
45302
45561
  }
45303
- const archivePath = join10(cacheDir, assetName);
45562
+ const archivePath = join11(cacheDir, assetName);
45304
45563
  const arrayBuffer = await response.arrayBuffer();
45305
45564
  await crossWrite(archivePath, arrayBuffer);
45306
45565
  await extractZip(archivePath, cacheDir);
@@ -45389,7 +45648,7 @@ function findSgCliPathSync() {
45389
45648
  const require2 = createRequire3(import.meta.url);
45390
45649
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
45391
45650
  const cliDir = dirname6(cliPkgPath);
45392
- const sgPath = join11(cliDir, binaryName);
45651
+ const sgPath = join12(cliDir, binaryName);
45393
45652
  if (existsSync7(sgPath) && isValidBinary(sgPath)) {
45394
45653
  return sgPath;
45395
45654
  }
@@ -45401,7 +45660,7 @@ function findSgCliPathSync() {
45401
45660
  const pkgPath = require2.resolve(`${platformPkg}/package.json`);
45402
45661
  const pkgDir = dirname6(pkgPath);
45403
45662
  const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
45404
- const binaryPath = join11(pkgDir, astGrepName);
45663
+ const binaryPath = join12(pkgDir, astGrepName);
45405
45664
  if (existsSync7(binaryPath) && isValidBinary(binaryPath)) {
45406
45665
  return binaryPath;
45407
45666
  }
@@ -45409,9 +45668,9 @@ function findSgCliPathSync() {
45409
45668
  }
45410
45669
  if (process.platform === "darwin") {
45411
45670
  const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
45412
- for (const path12 of homebrewPaths) {
45413
- if (existsSync7(path12) && isValidBinary(path12)) {
45414
- return path12;
45671
+ for (const path13 of homebrewPaths) {
45672
+ if (existsSync7(path13) && isValidBinary(path13)) {
45673
+ return path13;
45415
45674
  }
45416
45675
  }
45417
45676
  }
@@ -45428,8 +45687,8 @@ function getSgCliPath() {
45428
45687
  }
45429
45688
  return "sg";
45430
45689
  }
45431
- function setSgCliPath(path12) {
45432
- resolvedCliPath = path12;
45690
+ function setSgCliPath(path13) {
45691
+ resolvedCliPath = path13;
45433
45692
  }
45434
45693
  var DEFAULT_TIMEOUT_MS2 = 300000;
45435
45694
  var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
@@ -45794,7 +46053,7 @@ Key behaviors:
45794
46053
  if (!toolContext || typeof toolContext !== "object" || !("sessionID" in toolContext)) {
45795
46054
  throw new Error("Invalid toolContext: missing sessionID");
45796
46055
  }
45797
- const agent = String(args.agent);
46056
+ const agent = resolveRuntimeAgentName(_pluginConfig, String(args.agent));
45798
46057
  const prompt = String(args.prompt);
45799
46058
  const description = String(args.description);
45800
46059
  const parentSessionId = toolContext.sessionID;
@@ -45947,15 +46206,15 @@ Returns the synthesized result with councillor summary.`,
45947
46206
  // src/tools/lsp/client.ts
45948
46207
  var import_node = __toESM(require_main(), 1);
45949
46208
  import { spawn as nodeSpawn2 } from "node:child_process";
45950
- import { readFileSync as readFileSync4 } from "node:fs";
45951
- import { extname as extname2, resolve as resolve5 } from "node:path";
46209
+ import { readFileSync as readFileSync5 } from "node:fs";
46210
+ import { extname as extname3, resolve as resolve5 } from "node:path";
45952
46211
  import { pathToFileURL } from "node:url";
45953
46212
 
45954
46213
  // src/tools/lsp/config.ts
45955
46214
  var import_which = __toESM(require_lib(), 1);
45956
46215
  import { existsSync as existsSync10 } from "node:fs";
45957
46216
  import { homedir as homedir5 } from "node:os";
45958
- import { dirname as dirname8, join as join12, resolve as resolve4 } from "node:path";
46217
+ import { dirname as dirname8, join as join13, resolve as resolve4 } from "node:path";
45959
46218
 
45960
46219
  // src/tools/lsp/config-store.ts
45961
46220
  var userConfig = new Map;
@@ -46634,7 +46893,7 @@ function resolveServerCommand(command, cwd) {
46634
46893
  }
46635
46894
  const isWindows = process.platform === "win32";
46636
46895
  const ext = isWindows ? ".exe" : "";
46637
- const opencodeBin = join12(homedir5(), ".config", "opencode", "bin");
46896
+ const opencodeBin = join13(homedir5(), ".config", "opencode", "bin");
46638
46897
  const searchPath = (process.env.PATH ?? "") + (isWindows ? ";" : ":") + opencodeBin;
46639
46898
  const result = import_which.default.sync(cmd, {
46640
46899
  path: searchPath,
@@ -46645,7 +46904,7 @@ function resolveServerCommand(command, cwd) {
46645
46904
  return [result, ...args];
46646
46905
  }
46647
46906
  const localBinRoot = cwd ?? process.cwd();
46648
- const localBin = join12(localBinRoot, "node_modules", ".bin", cmd);
46907
+ const localBin = join13(localBinRoot, "node_modules", ".bin", cmd);
46649
46908
  if (existsSync10(localBin)) {
46650
46909
  return [localBin, ...args];
46651
46910
  }
@@ -47066,8 +47325,8 @@ stderr: ${stderr}` : ""));
47066
47325
  async ensureDocumentSynced(filePath) {
47067
47326
  const absPath = resolve5(filePath);
47068
47327
  const uri = pathToFileURL(absPath).href;
47069
- const text = readFileSync4(absPath, "utf-8");
47070
- const ext = extname2(absPath);
47328
+ const text = readFileSync5(absPath, "utf-8");
47329
+ const ext = extname3(absPath);
47071
47330
  const languageId = getLanguageId(ext);
47072
47331
  const existing = this.documents.get(uri);
47073
47332
  if (!existing) {
@@ -47221,12 +47480,12 @@ import { tool as tool5 } from "@opencode-ai/plugin/tool";
47221
47480
  // src/tools/lsp/utils.ts
47222
47481
  import {
47223
47482
  existsSync as existsSync11,
47224
- readFileSync as readFileSync5,
47483
+ readFileSync as readFileSync6,
47225
47484
  statSync as statSync6,
47226
47485
  unlinkSync as unlinkSync5,
47227
- writeFileSync as writeFileSync5
47486
+ writeFileSync as writeFileSync6
47228
47487
  } from "node:fs";
47229
- import { dirname as dirname9, extname as extname3, join as join13, resolve as resolve6 } from "node:path";
47488
+ import { dirname as dirname9, extname as extname4, join as join14, resolve as resolve6 } from "node:path";
47230
47489
  import { fileURLToPath as fileURLToPath2 } from "node:url";
47231
47490
  function findServerProjectRoot(filePath, server) {
47232
47491
  if (server.root) {
@@ -47252,7 +47511,7 @@ function formatServerLookupError(result) {
47252
47511
  }
47253
47512
  async function withLspClient(filePath, fn) {
47254
47513
  const absPath = resolve6(filePath);
47255
- const ext = extname3(absPath);
47514
+ const ext = extname4(absPath);
47256
47515
  const result = findServerForExtension(ext, absPath);
47257
47516
  if (result.status !== "found") {
47258
47517
  log("[lsp] withLspClient: server not found", {
@@ -47339,7 +47598,7 @@ function filterDiagnosticsBySeverity(diagnostics, severityFilter) {
47339
47598
  }
47340
47599
  function applyTextEditsToFile(filePath, edits) {
47341
47600
  try {
47342
- const content = readFileSync5(filePath, "utf-8");
47601
+ const content = readFileSync6(filePath, "utf-8");
47343
47602
  const lines = content.split(`
47344
47603
  `);
47345
47604
  const sortedEdits = [...edits].sort((a, b) => {
@@ -47364,7 +47623,7 @@ function applyTextEditsToFile(filePath, edits) {
47364
47623
  `));
47365
47624
  }
47366
47625
  }
47367
- writeFileSync5(filePath, lines.join(`
47626
+ writeFileSync6(filePath, lines.join(`
47368
47627
  `), "utf-8");
47369
47628
  return { success: true, editCount: edits.length };
47370
47629
  } catch (err) {
@@ -47412,7 +47671,7 @@ function applyWorkspaceEdit(edit) {
47412
47671
  if (change.kind === "create") {
47413
47672
  try {
47414
47673
  const filePath = uriToPath(change.uri);
47415
- writeFileSync5(filePath, "", "utf-8");
47674
+ writeFileSync6(filePath, "", "utf-8");
47416
47675
  result.filesModified.push(filePath);
47417
47676
  } catch (err) {
47418
47677
  result.success = false;
@@ -47422,8 +47681,8 @@ function applyWorkspaceEdit(edit) {
47422
47681
  try {
47423
47682
  const oldPath = uriToPath(change.oldUri);
47424
47683
  const newPath = uriToPath(change.newUri);
47425
- const content = readFileSync5(oldPath, "utf-8");
47426
- writeFileSync5(newPath, content, "utf-8");
47684
+ const content = readFileSync6(oldPath, "utf-8");
47685
+ writeFileSync6(newPath, content, "utf-8");
47427
47686
  unlinkSync5(oldPath);
47428
47687
  result.filesModified.push(newPath);
47429
47688
  } catch (err) {
@@ -47625,14 +47884,14 @@ var BINARY_PREFIXES = [
47625
47884
  var WEBFETCH_DESCRIPTION = "Fetch a URL with better extraction for static/docs pages. Supports llms.txt probing, content-focused HTML extraction, metadata, redirects, and an optional prompt processed by a cheap secondary model.";
47626
47885
  // src/tools/smartfetch/tool.ts
47627
47886
  import os4 from "node:os";
47628
- import path15 from "node:path";
47887
+ import path16 from "node:path";
47629
47888
  import {
47630
47889
  tool as tool6
47631
47890
  } from "@opencode-ai/plugin";
47632
47891
 
47633
47892
  // src/tools/smartfetch/binary.ts
47634
47893
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
47635
- import path12 from "node:path";
47894
+ import path13 from "node:path";
47636
47895
  function extensionForMime(contentType) {
47637
47896
  const mime = contentType.split(";")[0]?.trim().toLowerCase();
47638
47897
  const map2 = {
@@ -47653,10 +47912,10 @@ function buildBinaryResultMessage(fetchResult, savedPath) {
47653
47912
  async function saveBinary(binaryDir, data, contentType, filename) {
47654
47913
  await mkdir2(binaryDir, { recursive: true });
47655
47914
  const initialName = filename || `webfetch-${Date.now()}.${extensionForMime(contentType)}`;
47656
- const parsed = path12.parse(initialName);
47915
+ const parsed = path13.parse(initialName);
47657
47916
  for (let attempt = 0;attempt < 1000; attempt++) {
47658
47917
  const candidateName = attempt === 0 ? initialName : `${parsed.name}-${attempt}${parsed.ext || `.${extensionForMime(contentType)}`}`;
47659
- const file2 = path12.join(binaryDir, candidateName);
47918
+ const file2 = path13.join(binaryDir, candidateName);
47660
47919
  try {
47661
47920
  await writeFile2(file2, data, { flag: "wx" });
47662
47921
  return file2;
@@ -47674,7 +47933,7 @@ async function saveBinary(binaryDir, data, contentType, filename) {
47674
47933
  import { tracingChannel as j, channel as I } from "node:diagnostics_channel";
47675
47934
  var S = I("lru-cache:metrics");
47676
47935
  var W = j("lru-cache");
47677
- var x = () => S.hasSubscribers || W.hasSubscribers;
47936
+ var D = () => S.hasSubscribers || W.hasSubscribers;
47678
47937
  var G = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
47679
47938
  var M = new Set;
47680
47939
  var C = typeof process == "object" && process ? process : {};
@@ -47718,7 +47977,7 @@ var L = class u2 {
47718
47977
  #o;
47719
47978
  #u;
47720
47979
  #w;
47721
- #x;
47980
+ #D;
47722
47981
  #S;
47723
47982
  #M;
47724
47983
  #U;
@@ -47789,16 +48048,16 @@ var L = class u2 {
47789
48048
  return this.#w;
47790
48049
  }
47791
48050
  get onInsert() {
47792
- return this.#x;
48051
+ return this.#D;
47793
48052
  }
47794
48053
  get disposeAfter() {
47795
48054
  return this.#S;
47796
48055
  }
47797
48056
  constructor(e) {
47798
- let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z4, perf: D } = e;
47799
- if (D !== undefined && typeof D?.now != "function")
48057
+ let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z4, perf: x } = e;
48058
+ if (x !== undefined && typeof x?.now != "function")
47800
48059
  throw new TypeError("perf option must have a now() method if specified");
47801
- if (this.#m = D ?? G, t !== 0 && !F(t))
48060
+ if (this.#m = x ?? G, t !== 0 && !F(t))
47802
48061
  throw new TypeError("max option must be a nonnegative integer");
47803
48062
  let v = t ? U(t) : Array;
47804
48063
  if (!v)
@@ -47813,7 +48072,7 @@ var L = class u2 {
47813
48072
  throw new TypeError("memoMethod must be a function if defined");
47814
48073
  if (this.#U = m, a !== undefined && typeof a != "function")
47815
48074
  throw new TypeError("fetchMethod must be a function if specified");
47816
- if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z4, this.maxEntrySize !== 0) {
48075
+ if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#D = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#D, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z4, this.maxEntrySize !== 0) {
47817
48076
  if (this.#u !== 0 && !F(this.#u))
47818
48077
  throw new TypeError("maxSize must be a positive integer if specified");
47819
48078
  if (!F(this.maxEntrySize))
@@ -47841,7 +48100,7 @@ var L = class u2 {
47841
48100
  let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
47842
48101
  this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
47843
48102
  t[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
47844
- }, this.#D = (r) => {
48103
+ }, this.#x = (r) => {
47845
48104
  t[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
47846
48105
  };
47847
48106
  let s = this.ttlAutopurge ? (r, h) => {
@@ -47885,7 +48144,7 @@ var L = class u2 {
47885
48144
  return !!l && !!h && (n || o()) - h > l;
47886
48145
  };
47887
48146
  }
47888
- #D = () => {};
48147
+ #x = () => {};
47889
48148
  #E = () => {};
47890
48149
  #N = () => {};
47891
48150
  #p = () => false;
@@ -48051,7 +48310,7 @@ var L = class u2 {
48051
48310
  return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
48052
48311
  let f = this.#n === 0 ? undefined : this.#s.get(e);
48053
48312
  if (f === undefined)
48054
- f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#x?.(t, e, "add");
48313
+ f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#D?.(t, e, "add");
48055
48314
  else {
48056
48315
  this.#L(f);
48057
48316
  let g = this.#t[f];
@@ -48115,13 +48374,13 @@ var L = class u2 {
48115
48374
  if (this.#p(n))
48116
48375
  s && (s.has = "stale", this.#E(s, n));
48117
48376
  else
48118
- return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
48377
+ return i && this.#x(n), s && (s.has = "hit", this.#E(s, n)), true;
48119
48378
  } else
48120
48379
  s && (s.has = "miss");
48121
48380
  return false;
48122
48381
  }
48123
48382
  peek(e, t = {}) {
48124
- let { status: i = x() ? {} : undefined } = t;
48383
+ let { status: i = D() ? {} : undefined } = t;
48125
48384
  i && (i.op = "peek", i.key = e), t.status = i;
48126
48385
  let s = this.#J(e, t);
48127
48386
  return S.hasSubscribers && S.publish(i), s;
@@ -48170,10 +48429,10 @@ var L = class u2 {
48170
48429
  return !!t && t instanceof Promise && t.hasOwnProperty("__staleWhileFetching") && t.__abortController instanceof AbortController;
48171
48430
  }
48172
48431
  fetch(e, t = {}) {
48173
- let i = W.hasSubscribers, { status: s = x() ? {} : undefined } = t;
48432
+ let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
48174
48433
  t.status = s, s && t.context && (s.context = t.context);
48175
48434
  let n = this.#B(e, t);
48176
- return s && x() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
48435
+ return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
48177
48436
  }
48178
48437
  async#B(e, t = {}) {
48179
48438
  let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, ttl: o = this.ttl, noDisposeOnSet: r = this.noDisposeOnSet, size: h = 0, sizeCalculation: l = this.sizeCalculation, noUpdateTTL: c = this.noUpdateTTL, noDeleteOnFetchRejection: f = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection: g = this.allowStaleOnFetchRejection, ignoreFetchAbort: p = this.ignoreFetchAbort, allowStaleOnFetchAbort: T = this.allowStaleOnFetchAbort, context: w, forceRefresh: y = false, status: a, signal: m } = t;
@@ -48192,16 +48451,16 @@ var L = class u2 {
48192
48451
  }
48193
48452
  let A = this.#p(b);
48194
48453
  if (!y && !A)
48195
- return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
48454
+ return a && (a.fetch = "hit"), this.#L(b), s && this.#x(b), a && this.#E(a, b), d;
48196
48455
  let z4 = this.#P(e, b, _, w), v = z4.__staleWhileFetching !== undefined && i;
48197
48456
  return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z4.__staleWhileFetching : z4.__returned = z4;
48198
48457
  }
48199
48458
  }
48200
48459
  forceFetch(e, t = {}) {
48201
- let i = W.hasSubscribers, { status: s = x() ? {} : undefined } = t;
48460
+ let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
48202
48461
  t.status = s, s && t.context && (s.context = t.context);
48203
48462
  let n = this.#K(e, t);
48204
- return s && x() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
48463
+ return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
48205
48464
  }
48206
48465
  async#K(e, t = {}) {
48207
48466
  let i = await this.#B(e, t);
@@ -48240,7 +48499,7 @@ var L = class u2 {
48240
48499
  return;
48241
48500
  }
48242
48501
  let h = this.#t[r], l = this.#e(h);
48243
- return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#D(r), l ? h.__staleWhileFetching : h);
48502
+ return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#x(r), l ? h.__staleWhileFetching : h);
48244
48503
  }
48245
48504
  #$(e, t) {
48246
48505
  this.#c[t] = e, this.#a[e] = t;
@@ -48310,7 +48569,7 @@ var L = class u2 {
48310
48569
  };
48311
48570
 
48312
48571
  // src/tools/smartfetch/network.ts
48313
- import path13 from "node:path";
48572
+ import path14 from "node:path";
48314
48573
 
48315
48574
  // src/tools/smartfetch/utils.ts
48316
48575
  var import_readability = __toESM(require_readability(), 1);
@@ -49035,12 +49294,12 @@ function inferFilenameFromUrl(url2) {
49035
49294
  function truncateFilename(name, maxLength = 180) {
49036
49295
  if (name.length <= maxLength)
49037
49296
  return name;
49038
- const parsed = path13.parse(name);
49297
+ const parsed = path14.parse(name);
49039
49298
  const ext = parsed.ext || "";
49040
49299
  const baseLimit = Math.max(1, maxLength - ext.length);
49041
49300
  return `${parsed.name.slice(0, baseLimit)}${ext}`;
49042
49301
  }
49043
- function sanitizeFilename(name) {
49302
+ function sanitizeFilename2(name) {
49044
49303
  let sanitized = Array.from(name, (char) => {
49045
49304
  const code = char.charCodeAt(0);
49046
49305
  if (code < 32 || '<>:"/\\|?*'.includes(char))
@@ -49065,7 +49324,7 @@ function extractHeaderMetadata(headers, finalUrl) {
49065
49324
  etag: headers.get("etag") || undefined,
49066
49325
  lastModified: headers.get("last-modified") || undefined,
49067
49326
  contentLength: parseContentLength(headers),
49068
- filename: filename ? sanitizeFilename(filename) : undefined
49327
+ filename: filename ? sanitizeFilename2(filename) : undefined
49069
49328
  };
49070
49329
  }
49071
49330
  function buildConditionalHeaders(cached2) {
@@ -49207,7 +49466,7 @@ function isInvalidLlmsResult(fetchResult) {
49207
49466
  // src/tools/smartfetch/secondary-model.ts
49208
49467
  import { existsSync as existsSync12 } from "node:fs";
49209
49468
  import { readFile as readFile4 } from "node:fs/promises";
49210
- import path14 from "node:path";
49469
+ import path15 from "node:path";
49211
49470
  function parseModelRef(value) {
49212
49471
  if (!value)
49213
49472
  return;
@@ -49233,7 +49492,7 @@ function pickAgentModelRef(value) {
49233
49492
  }
49234
49493
  function findPreferredOpenCodeConfigPath(baseDir) {
49235
49494
  for (const file2 of ["opencode.jsonc", "opencode.json"]) {
49236
- const fullPath = path14.join(baseDir, file2);
49495
+ const fullPath = path15.join(baseDir, file2);
49237
49496
  if (existsSync12(fullPath))
49238
49497
  return fullPath;
49239
49498
  }
@@ -49250,7 +49509,7 @@ async function readOpenCodeConfigFile(configPath) {
49250
49509
  }
49251
49510
  }
49252
49511
  async function readEffectiveOpenCodeConfig(directory) {
49253
- const projectDir = path14.join(directory, ".opencode");
49512
+ const projectDir = path15.join(directory, ".opencode");
49254
49513
  const userDirs = getConfigSearchDirs();
49255
49514
  const projectPath = findPreferredOpenCodeConfigPath(projectDir);
49256
49515
  const userPath = userDirs.map((configDir) => findPreferredOpenCodeConfigPath(configDir)).find(Boolean);
@@ -49411,7 +49670,7 @@ async function runSecondaryModelWithFallback(client, directory, models, prompt,
49411
49670
  // src/tools/smartfetch/tool.ts
49412
49671
  var z4 = tool6.schema;
49413
49672
  function createWebfetchTool(pluginCtx, options = {}) {
49414
- const binaryDir = options.binaryDir || path15.join(os4.tmpdir(), "opencode-smartfetch");
49673
+ const binaryDir = options.binaryDir || path16.join(os4.tmpdir(), "opencode-smartfetch");
49415
49674
  return tool6({
49416
49675
  description: WEBFETCH_DESCRIPTION,
49417
49676
  args: {
@@ -49949,7 +50208,7 @@ var OhMyOpenCodeLite = async (ctx) => {
49949
50208
  const webfetch = createWebfetchTool(ctx);
49950
50209
  const multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig);
49951
50210
  const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
49952
- showStartupToast: true,
50211
+ showStartupToast: config2.showStartupToast ?? true,
49953
50212
  autoUpdate: true
49954
50213
  });
49955
50214
  const phaseReminderHook = createPhaseReminderHook();
@@ -50124,7 +50383,11 @@ var OhMyOpenCodeLite = async (ctx) => {
50124
50383
  },
50125
50384
  "chat.headers": chatHeadersHook["chat.headers"],
50126
50385
  "chat.message": async (input, output) => {
50127
- const agent = input.agent ?? output?.message?.agent;
50386
+ const rawAgent = input.agent ?? output?.message?.agent;
50387
+ const agent = rawAgent ? resolveRuntimeAgentName(config2, rawAgent) : undefined;
50388
+ if (agent && output?.message && typeof output.message.agent === "string") {
50389
+ output.message.agent = agent;
50390
+ }
50128
50391
  if (agent) {
50129
50392
  sessionAgentMap.set(input.sessionID, agent);
50130
50393
  }
@@ -50151,6 +50414,17 @@ ${output.system[0]}` : "");
50151
50414
  },
50152
50415
  "experimental.chat.messages.transform": async (input, output) => {
50153
50416
  const typedOutput = output;
50417
+ for (const message of typedOutput.messages) {
50418
+ if (message.info.role !== "user") {
50419
+ continue;
50420
+ }
50421
+ for (const part of message.parts) {
50422
+ if (part.type !== "text" || typeof part.text !== "string") {
50423
+ continue;
50424
+ }
50425
+ part.text = rewriteDisplayNameMentions(config2, part.text);
50426
+ }
50427
+ }
50154
50428
  processImageAttachments({
50155
50429
  messages: typedOutput.messages,
50156
50430
  workDir: ctx.directory,