oh-my-opencode-slim 1.1.0 → 1.1.2

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 (59) hide show
  1. package/README.ja-JP.md +638 -0
  2. package/README.ko-KR.md +634 -0
  3. package/README.md +48 -12
  4. package/README.zh-CN.md +627 -0
  5. package/dist/cli/background-subagents.d.ts +13 -0
  6. package/dist/cli/index.js +65 -92
  7. package/dist/cli/skills.d.ts +2 -30
  8. package/dist/cli/types.d.ts +0 -1
  9. package/dist/config/fallback-chains.d.ts +1 -0
  10. package/dist/config/schema.d.ts +7 -0
  11. package/dist/hooks/auto-update-checker/checker.d.ts +7 -1
  12. package/dist/hooks/auto-update-checker/constants.d.ts +1 -0
  13. package/dist/hooks/auto-update-checker/types.d.ts +10 -0
  14. package/dist/hooks/deepwork/index.d.ts +13 -0
  15. package/dist/hooks/index.d.ts +1 -0
  16. package/dist/hooks/session-goal/index.d.ts +38 -0
  17. package/dist/index.js +852 -435
  18. package/dist/multiplexer/session-manager.d.ts +3 -1
  19. package/dist/tools/ast-grep/tools.d.ts +1 -1
  20. package/dist/tools/cancel-task.d.ts +16 -0
  21. package/dist/tools/preset-manager.d.ts +6 -7
  22. package/dist/tools/subtask/tools.d.ts +6 -1
  23. package/dist/tui.js +17 -62
  24. package/dist/utils/background-job-board.d.ts +90 -0
  25. package/oh-my-opencode-slim.schema.json +11 -0
  26. package/package.json +6 -3
  27. package/dist/agents/council-master.d.ts +0 -2
  28. package/dist/background/background-manager.d.ts +0 -203
  29. package/dist/background/index.d.ts +0 -3
  30. package/dist/background/multiplexer-session-manager.d.ts +0 -70
  31. package/dist/background/subagent-depth.d.ts +0 -35
  32. package/dist/cli/divoom.d.ts +0 -23
  33. package/dist/integrations/divoom/index.d.ts +0 -3
  34. package/dist/integrations/divoom/status-manager.d.ts +0 -31
  35. package/dist/integrations/divoom/swift-helper-source.d.ts +0 -1
  36. package/dist/integrations/divoom/swift-transport.d.ts +0 -26
  37. package/dist/integrations/divoom/types.d.ts +0 -41
  38. package/dist/tools/background.d.ts +0 -13
  39. package/dist/tools/fork/command.d.ts +0 -28
  40. package/dist/tools/fork/files.d.ts +0 -33
  41. package/dist/tools/fork/index.d.ts +0 -10
  42. package/dist/tools/fork/state.d.ts +0 -7
  43. package/dist/tools/fork/tools.d.ts +0 -23
  44. package/dist/tools/fork/vendor.d.ts +0 -28
  45. package/dist/tools/handoff/command.d.ts +0 -29
  46. package/dist/tools/handoff/files.d.ts +0 -33
  47. package/dist/tools/handoff/index.d.ts +0 -10
  48. package/dist/tools/handoff/state.d.ts +0 -7
  49. package/dist/tools/handoff/tools.d.ts +0 -23
  50. package/dist/tools/handoff/vendor.d.ts +0 -28
  51. package/dist/tools/lsp/client.d.ts +0 -81
  52. package/dist/tools/lsp/config-store.d.ts +0 -29
  53. package/dist/tools/lsp/config.d.ts +0 -5
  54. package/dist/tools/lsp/constants.d.ts +0 -24
  55. package/dist/tools/lsp/index.d.ts +0 -4
  56. package/dist/tools/lsp/tools.d.ts +0 -5
  57. package/dist/tools/lsp/types.d.ts +0 -45
  58. package/dist/tools/lsp/utils.d.ts +0 -34
  59. package/dist/utils/tmux-debug-log.d.ts +0 -2
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var __toESM = (mod, isNodeMode, target) => {
32
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
33
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
34
 
35
- // node_modules/@mozilla/readability/Readability.js
35
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability.js
36
36
  var require_Readability = __commonJS((exports, module) => {
37
37
  function Readability(doc, options) {
38
38
  if (options && options.documentElement) {
@@ -1603,7 +1603,7 @@ var require_Readability = __commonJS((exports, module) => {
1603
1603
  }
1604
1604
  });
1605
1605
 
1606
- // node_modules/@mozilla/readability/Readability-readerable.js
1606
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability-readerable.js
1607
1607
  var require_Readability_readerable = __commonJS((exports, module) => {
1608
1608
  var REGEXPS = {
1609
1609
  unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
@@ -1659,7 +1659,7 @@ var require_Readability_readerable = __commonJS((exports, module) => {
1659
1659
  }
1660
1660
  });
1661
1661
 
1662
- // node_modules/@mozilla/readability/index.js
1662
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/index.js
1663
1663
  var require_readability = __commonJS((exports, module) => {
1664
1664
  var Readability = require_Readability();
1665
1665
  var isProbablyReaderable = require_Readability_readerable();
@@ -1669,7 +1669,7 @@ var require_readability = __commonJS((exports, module) => {
1669
1669
  };
1670
1670
  });
1671
1671
 
1672
- // node_modules/@mixmark-io/domino/lib/Event.js
1672
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Event.js
1673
1673
  var require_Event = __commonJS((exports, module) => {
1674
1674
  module.exports = Event;
1675
1675
  Event.CAPTURING_PHASE = 1;
@@ -1726,7 +1726,7 @@ var require_Event = __commonJS((exports, module) => {
1726
1726
  });
1727
1727
  });
1728
1728
 
1729
- // node_modules/@mixmark-io/domino/lib/UIEvent.js
1729
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/UIEvent.js
1730
1730
  var require_UIEvent = __commonJS((exports, module) => {
1731
1731
  var Event = require_Event();
1732
1732
  module.exports = UIEvent;
@@ -1745,7 +1745,7 @@ var require_UIEvent = __commonJS((exports, module) => {
1745
1745
  });
1746
1746
  });
1747
1747
 
1748
- // node_modules/@mixmark-io/domino/lib/MouseEvent.js
1748
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MouseEvent.js
1749
1749
  var require_MouseEvent = __commonJS((exports, module) => {
1750
1750
  var UIEvent = require_UIEvent();
1751
1751
  module.exports = MouseEvent;
@@ -1803,7 +1803,7 @@ var require_MouseEvent = __commonJS((exports, module) => {
1803
1803
  });
1804
1804
  });
1805
1805
 
1806
- // node_modules/@mixmark-io/domino/lib/DOMException.js
1806
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMException.js
1807
1807
  var require_DOMException = __commonJS((exports, module) => {
1808
1808
  module.exports = DOMException;
1809
1809
  var INDEX_SIZE_ERR = 1;
@@ -1927,12 +1927,12 @@ var require_DOMException = __commonJS((exports, module) => {
1927
1927
  var c;
1928
1928
  });
1929
1929
 
1930
- // node_modules/@mixmark-io/domino/lib/config.js
1930
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/config.js
1931
1931
  var require_config = __commonJS((exports) => {
1932
1932
  exports.isApiWritable = !globalThis.__domino_frozen__;
1933
1933
  });
1934
1934
 
1935
- // node_modules/@mixmark-io/domino/lib/utils.js
1935
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/utils.js
1936
1936
  var require_utils = __commonJS((exports) => {
1937
1937
  var DOMException = require_DOMException();
1938
1938
  var ERR = DOMException;
@@ -2045,7 +2045,7 @@ var require_utils = __commonJS((exports) => {
2045
2045
  };
2046
2046
  });
2047
2047
 
2048
- // node_modules/@mixmark-io/domino/lib/EventTarget.js
2048
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/EventTarget.js
2049
2049
  var require_EventTarget = __commonJS((exports, module) => {
2050
2050
  var Event = require_Event();
2051
2051
  var MouseEvent = require_MouseEvent();
@@ -2233,7 +2233,7 @@ var require_EventTarget = __commonJS((exports, module) => {
2233
2233
  };
2234
2234
  });
2235
2235
 
2236
- // node_modules/@mixmark-io/domino/lib/LinkedList.js
2236
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/LinkedList.js
2237
2237
  var require_LinkedList = __commonJS((exports, module) => {
2238
2238
  var utils = require_utils();
2239
2239
  var LinkedList = module.exports = {
@@ -2276,7 +2276,7 @@ var require_LinkedList = __commonJS((exports, module) => {
2276
2276
  };
2277
2277
  });
2278
2278
 
2279
- // node_modules/@mixmark-io/domino/lib/NodeUtils.js
2279
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeUtils.js
2280
2280
  var require_NodeUtils = __commonJS((exports, module) => {
2281
2281
  module.exports = {
2282
2282
  serializeOne,
@@ -2452,7 +2452,7 @@ var require_NodeUtils = __commonJS((exports, module) => {
2452
2452
  }
2453
2453
  });
2454
2454
 
2455
- // node_modules/@mixmark-io/domino/lib/Node.js
2455
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Node.js
2456
2456
  var require_Node = __commonJS((exports, module) => {
2457
2457
  module.exports = Node;
2458
2458
  var EventTarget = require_EventTarget();
@@ -3013,7 +3013,7 @@ var require_Node = __commonJS((exports, module) => {
3013
3013
  });
3014
3014
  });
3015
3015
 
3016
- // node_modules/@mixmark-io/domino/lib/NodeList.es6.js
3016
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es6.js
3017
3017
  var require_NodeList_es6 = __commonJS((exports, module) => {
3018
3018
  module.exports = class NodeList extends Array {
3019
3019
  constructor(a) {
@@ -3030,7 +3030,7 @@ var require_NodeList_es6 = __commonJS((exports, module) => {
3030
3030
  };
3031
3031
  });
3032
3032
 
3033
- // node_modules/@mixmark-io/domino/lib/NodeList.es5.js
3033
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es5.js
3034
3034
  var require_NodeList_es5 = __commonJS((exports, module) => {
3035
3035
  function item(i) {
3036
3036
  return this[i] || null;
@@ -3044,7 +3044,7 @@ var require_NodeList_es5 = __commonJS((exports, module) => {
3044
3044
  module.exports = NodeList;
3045
3045
  });
3046
3046
 
3047
- // node_modules/@mixmark-io/domino/lib/NodeList.js
3047
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.js
3048
3048
  var require_NodeList = __commonJS((exports, module) => {
3049
3049
  var NodeList;
3050
3050
  try {
@@ -3055,7 +3055,7 @@ var require_NodeList = __commonJS((exports, module) => {
3055
3055
  module.exports = NodeList;
3056
3056
  });
3057
3057
 
3058
- // node_modules/@mixmark-io/domino/lib/ContainerNode.js
3058
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ContainerNode.js
3059
3059
  var require_ContainerNode = __commonJS((exports, module) => {
3060
3060
  module.exports = ContainerNode;
3061
3061
  var Node = require_Node();
@@ -3123,7 +3123,7 @@ var require_ContainerNode = __commonJS((exports, module) => {
3123
3123
  });
3124
3124
  });
3125
3125
 
3126
- // node_modules/@mixmark-io/domino/lib/xmlnames.js
3126
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/xmlnames.js
3127
3127
  var require_xmlnames = __commonJS((exports) => {
3128
3128
  exports.isValidName = isValidName;
3129
3129
  exports.isValidQName = isValidQName;
@@ -3172,7 +3172,7 @@ var require_xmlnames = __commonJS((exports) => {
3172
3172
  }
3173
3173
  });
3174
3174
 
3175
- // node_modules/@mixmark-io/domino/lib/attributes.js
3175
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/attributes.js
3176
3176
  var require_attributes = __commonJS((exports) => {
3177
3177
  var utils = require_utils();
3178
3178
  exports.property = function(attr) {
@@ -3308,7 +3308,7 @@ var require_attributes = __commonJS((exports) => {
3308
3308
  };
3309
3309
  });
3310
3310
 
3311
- // node_modules/@mixmark-io/domino/lib/FilteredElementList.js
3311
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/FilteredElementList.js
3312
3312
  var require_FilteredElementList = __commonJS((exports, module) => {
3313
3313
  module.exports = FilteredElementList;
3314
3314
  var Node = require_Node();
@@ -3374,7 +3374,7 @@ var require_FilteredElementList = __commonJS((exports, module) => {
3374
3374
  });
3375
3375
  });
3376
3376
 
3377
- // node_modules/@mixmark-io/domino/lib/DOMTokenList.js
3377
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMTokenList.js
3378
3378
  var require_DOMTokenList = __commonJS((exports, module) => {
3379
3379
  var utils = require_utils();
3380
3380
  module.exports = DOMTokenList;
@@ -3536,7 +3536,7 @@ var require_DOMTokenList = __commonJS((exports, module) => {
3536
3536
  }
3537
3537
  });
3538
3538
 
3539
- // node_modules/@mixmark-io/domino/lib/select.js
3539
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/select.js
3540
3540
  var require_select = __commonJS((exports, module) => {
3541
3541
  var window2 = Object.create(null, {
3542
3542
  location: { get: function() {
@@ -4279,7 +4279,7 @@ var require_select = __commonJS((exports, module) => {
4279
4279
  };
4280
4280
  });
4281
4281
 
4282
- // node_modules/@mixmark-io/domino/lib/ChildNode.js
4282
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ChildNode.js
4283
4283
  var require_ChildNode = __commonJS((exports, module) => {
4284
4284
  var Node = require_Node();
4285
4285
  var LinkedList = require_LinkedList();
@@ -4369,7 +4369,7 @@ var require_ChildNode = __commonJS((exports, module) => {
4369
4369
  module.exports = ChildNode;
4370
4370
  });
4371
4371
 
4372
- // node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
4372
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
4373
4373
  var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
4374
4374
  var Node = require_Node();
4375
4375
  var NonDocumentTypeChildNode = {
@@ -4395,7 +4395,7 @@ var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
4395
4395
  module.exports = NonDocumentTypeChildNode;
4396
4396
  });
4397
4397
 
4398
- // node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
4398
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
4399
4399
  var require_NamedNodeMap = __commonJS((exports, module) => {
4400
4400
  module.exports = NamedNodeMap;
4401
4401
  var utils = require_utils();
@@ -4432,7 +4432,7 @@ var require_NamedNodeMap = __commonJS((exports, module) => {
4432
4432
  });
4433
4433
  });
4434
4434
 
4435
- // node_modules/@mixmark-io/domino/lib/Element.js
4435
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Element.js
4436
4436
  var require_Element = __commonJS((exports, module) => {
4437
4437
  module.exports = Element;
4438
4438
  var xml = require_xmlnames();
@@ -5333,7 +5333,7 @@ var require_Element = __commonJS((exports, module) => {
5333
5333
  }
5334
5334
  });
5335
5335
 
5336
- // node_modules/@mixmark-io/domino/lib/Leaf.js
5336
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Leaf.js
5337
5337
  var require_Leaf = __commonJS((exports, module) => {
5338
5338
  module.exports = Leaf;
5339
5339
  var Node = require_Node();
@@ -5374,7 +5374,7 @@ var require_Leaf = __commonJS((exports, module) => {
5374
5374
  });
5375
5375
  });
5376
5376
 
5377
- // node_modules/@mixmark-io/domino/lib/CharacterData.js
5377
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CharacterData.js
5378
5378
  var require_CharacterData = __commonJS((exports, module) => {
5379
5379
  module.exports = CharacterData;
5380
5380
  var Leaf = require_Leaf();
@@ -5431,7 +5431,7 @@ var require_CharacterData = __commonJS((exports, module) => {
5431
5431
  Object.defineProperties(CharacterData.prototype, NonDocumentTypeChildNode);
5432
5432
  });
5433
5433
 
5434
- // node_modules/@mixmark-io/domino/lib/Text.js
5434
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Text.js
5435
5435
  var require_Text = __commonJS((exports, module) => {
5436
5436
  module.exports = Text;
5437
5437
  var utils = require_utils();
@@ -5501,7 +5501,7 @@ var require_Text = __commonJS((exports, module) => {
5501
5501
  });
5502
5502
  });
5503
5503
 
5504
- // node_modules/@mixmark-io/domino/lib/Comment.js
5504
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Comment.js
5505
5505
  var require_Comment = __commonJS((exports, module) => {
5506
5506
  module.exports = Comment;
5507
5507
  var Node = require_Node();
@@ -5544,7 +5544,7 @@ var require_Comment = __commonJS((exports, module) => {
5544
5544
  });
5545
5545
  });
5546
5546
 
5547
- // node_modules/@mixmark-io/domino/lib/DocumentFragment.js
5547
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentFragment.js
5548
5548
  var require_DocumentFragment = __commonJS((exports, module) => {
5549
5549
  module.exports = DocumentFragment;
5550
5550
  var Node = require_Node();
@@ -5601,7 +5601,7 @@ var require_DocumentFragment = __commonJS((exports, module) => {
5601
5601
  });
5602
5602
  });
5603
5603
 
5604
- // node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
5604
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
5605
5605
  var require_ProcessingInstruction = __commonJS((exports, module) => {
5606
5606
  module.exports = ProcessingInstruction;
5607
5607
  var Node = require_Node();
@@ -5650,7 +5650,7 @@ var require_ProcessingInstruction = __commonJS((exports, module) => {
5650
5650
  });
5651
5651
  });
5652
5652
 
5653
- // node_modules/@mixmark-io/domino/lib/NodeFilter.js
5653
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeFilter.js
5654
5654
  var require_NodeFilter = __commonJS((exports, module) => {
5655
5655
  var NodeFilter = {
5656
5656
  FILTER_ACCEPT: 1,
@@ -5673,7 +5673,7 @@ var require_NodeFilter = __commonJS((exports, module) => {
5673
5673
  module.exports = NodeFilter.constructor = NodeFilter.prototype = NodeFilter;
5674
5674
  });
5675
5675
 
5676
- // node_modules/@mixmark-io/domino/lib/NodeTraversal.js
5676
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeTraversal.js
5677
5677
  var require_NodeTraversal = __commonJS((exports, module) => {
5678
5678
  var NodeTraversal = module.exports = {
5679
5679
  nextSkippingChildren,
@@ -5737,7 +5737,7 @@ var require_NodeTraversal = __commonJS((exports, module) => {
5737
5737
  }
5738
5738
  });
5739
5739
 
5740
- // node_modules/@mixmark-io/domino/lib/TreeWalker.js
5740
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/TreeWalker.js
5741
5741
  var require_TreeWalker = __commonJS((exports, module) => {
5742
5742
  module.exports = TreeWalker;
5743
5743
  var Node = require_Node();
@@ -5967,7 +5967,7 @@ var require_TreeWalker = __commonJS((exports, module) => {
5967
5967
  });
5968
5968
  });
5969
5969
 
5970
- // node_modules/@mixmark-io/domino/lib/NodeIterator.js
5970
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeIterator.js
5971
5971
  var require_NodeIterator = __commonJS((exports, module) => {
5972
5972
  module.exports = NodeIterator;
5973
5973
  var NodeFilter = require_NodeFilter();
@@ -6108,7 +6108,7 @@ var require_NodeIterator = __commonJS((exports, module) => {
6108
6108
  });
6109
6109
  });
6110
6110
 
6111
- // node_modules/@mixmark-io/domino/lib/URL.js
6111
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URL.js
6112
6112
  var require_URL = __commonJS((exports, module) => {
6113
6113
  module.exports = URL4;
6114
6114
  function URL4(url) {
@@ -6281,7 +6281,7 @@ var require_URL = __commonJS((exports, module) => {
6281
6281
  };
6282
6282
  });
6283
6283
 
6284
- // node_modules/@mixmark-io/domino/lib/CustomEvent.js
6284
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CustomEvent.js
6285
6285
  var require_CustomEvent = __commonJS((exports, module) => {
6286
6286
  module.exports = CustomEvent;
6287
6287
  var Event = require_Event();
@@ -6293,7 +6293,7 @@ var require_CustomEvent = __commonJS((exports, module) => {
6293
6293
  });
6294
6294
  });
6295
6295
 
6296
- // node_modules/@mixmark-io/domino/lib/events.js
6296
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/events.js
6297
6297
  var require_events = __commonJS((exports, module) => {
6298
6298
  module.exports = {
6299
6299
  Event: require_Event(),
@@ -6303,7 +6303,7 @@ var require_events = __commonJS((exports, module) => {
6303
6303
  };
6304
6304
  });
6305
6305
 
6306
- // node_modules/@mixmark-io/domino/lib/style_parser.js
6306
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/style_parser.js
6307
6307
  var require_style_parser = __commonJS((exports) => {
6308
6308
  Object.defineProperty(exports, "__esModule", { value: true });
6309
6309
  exports.hyphenate = exports.parse = undefined;
@@ -6370,7 +6370,7 @@ var require_style_parser = __commonJS((exports) => {
6370
6370
  exports.hyphenate = hyphenate;
6371
6371
  });
6372
6372
 
6373
- // node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
6373
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
6374
6374
  var require_CSSStyleDeclaration = __commonJS((exports, module) => {
6375
6375
  var { parse } = require_style_parser();
6376
6376
  module.exports = function(elt) {
@@ -6546,7 +6546,7 @@ var require_CSSStyleDeclaration = __commonJS((exports, module) => {
6546
6546
  });
6547
6547
  });
6548
6548
 
6549
- // node_modules/@mixmark-io/domino/lib/URLUtils.js
6549
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URLUtils.js
6550
6550
  var require_URLUtils = __commonJS((exports, module) => {
6551
6551
  var URL4 = require_URL();
6552
6552
  module.exports = URLUtils;
@@ -6780,7 +6780,7 @@ var require_URLUtils = __commonJS((exports, module) => {
6780
6780
  };
6781
6781
  });
6782
6782
 
6783
- // node_modules/@mixmark-io/domino/lib/defineElement.js
6783
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/defineElement.js
6784
6784
  var require_defineElement = __commonJS((exports, module) => {
6785
6785
  var attributes = require_attributes();
6786
6786
  var isApiWritable = require_config().isApiWritable;
@@ -6842,7 +6842,7 @@ var require_defineElement = __commonJS((exports, module) => {
6842
6842
  }
6843
6843
  });
6844
6844
 
6845
- // node_modules/@mixmark-io/domino/lib/htmlelts.js
6845
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/htmlelts.js
6846
6846
  var require_htmlelts = __commonJS((exports) => {
6847
6847
  var Node = require_Node();
6848
6848
  var Element = require_Element();
@@ -8306,7 +8306,7 @@ var require_htmlelts = __commonJS((exports) => {
8306
8306
  });
8307
8307
  });
8308
8308
 
8309
- // node_modules/@mixmark-io/domino/lib/svg.js
8309
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/svg.js
8310
8310
  var require_svg = __commonJS((exports) => {
8311
8311
  var Element = require_Element();
8312
8312
  var defineElement = require_defineElement();
@@ -8432,7 +8432,7 @@ var require_svg = __commonJS((exports) => {
8432
8432
  });
8433
8433
  });
8434
8434
 
8435
- // node_modules/@mixmark-io/domino/lib/MutationConstants.js
8435
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MutationConstants.js
8436
8436
  var require_MutationConstants = __commonJS((exports, module) => {
8437
8437
  module.exports = {
8438
8438
  VALUE: 1,
@@ -8444,7 +8444,7 @@ var require_MutationConstants = __commonJS((exports, module) => {
8444
8444
  };
8445
8445
  });
8446
8446
 
8447
- // node_modules/@mixmark-io/domino/lib/Document.js
8447
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Document.js
8448
8448
  var require_Document = __commonJS((exports, module) => {
8449
8449
  module.exports = Document;
8450
8450
  var Node = require_Node();
@@ -9156,7 +9156,7 @@ var require_Document = __commonJS((exports, module) => {
9156
9156
  };
9157
9157
  });
9158
9158
 
9159
- // node_modules/@mixmark-io/domino/lib/DocumentType.js
9159
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentType.js
9160
9160
  var require_DocumentType = __commonJS((exports, module) => {
9161
9161
  module.exports = DocumentType;
9162
9162
  var Node = require_Node();
@@ -9190,7 +9190,7 @@ var require_DocumentType = __commonJS((exports, module) => {
9190
9190
  Object.defineProperties(DocumentType.prototype, ChildNode);
9191
9191
  });
9192
9192
 
9193
- // node_modules/@mixmark-io/domino/lib/HTMLParser.js
9193
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/HTMLParser.js
9194
9194
  var require_HTMLParser = __commonJS((exports, module) => {
9195
9195
  module.exports = HTMLParser;
9196
9196
  var Document = require_Document();
@@ -17241,7 +17241,7 @@ var require_HTMLParser = __commonJS((exports, module) => {
17241
17241
  }
17242
17242
  });
17243
17243
 
17244
- // node_modules/@mixmark-io/domino/lib/DOMImplementation.js
17244
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMImplementation.js
17245
17245
  var require_DOMImplementation = __commonJS((exports, module) => {
17246
17246
  module.exports = DOMImplementation;
17247
17247
  var Document = require_Document();
@@ -17315,7 +17315,7 @@ var require_DOMImplementation = __commonJS((exports, module) => {
17315
17315
  };
17316
17316
  });
17317
17317
 
17318
- // node_modules/@mixmark-io/domino/lib/Location.js
17318
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Location.js
17319
17319
  var require_Location = __commonJS((exports, module) => {
17320
17320
  var URL4 = require_URL();
17321
17321
  var URLUtils = require_URLUtils();
@@ -17351,7 +17351,7 @@ var require_Location = __commonJS((exports, module) => {
17351
17351
  });
17352
17352
  });
17353
17353
 
17354
- // node_modules/@mixmark-io/domino/lib/NavigatorID.js
17354
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NavigatorID.js
17355
17355
  var require_NavigatorID = __commonJS((exports, module) => {
17356
17356
  var NavigatorID = Object.create(null, {
17357
17357
  appCodeName: { value: "Mozilla" },
@@ -17370,7 +17370,7 @@ var require_NavigatorID = __commonJS((exports, module) => {
17370
17370
  module.exports = NavigatorID;
17371
17371
  });
17372
17372
 
17373
- // node_modules/@mixmark-io/domino/lib/WindowTimers.js
17373
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/WindowTimers.js
17374
17374
  var require_WindowTimers = __commonJS((exports, module) => {
17375
17375
  var WindowTimers = {
17376
17376
  setTimeout,
@@ -17381,7 +17381,7 @@ var require_WindowTimers = __commonJS((exports, module) => {
17381
17381
  module.exports = WindowTimers;
17382
17382
  });
17383
17383
 
17384
- // node_modules/@mixmark-io/domino/lib/impl.js
17384
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/impl.js
17385
17385
  var require_impl = __commonJS((exports, module) => {
17386
17386
  var utils = require_utils();
17387
17387
  exports = module.exports = {
@@ -17409,7 +17409,7 @@ var require_impl = __commonJS((exports, module) => {
17409
17409
  utils.merge(exports, require_svg().elements);
17410
17410
  });
17411
17411
 
17412
- // node_modules/@mixmark-io/domino/lib/Window.js
17412
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Window.js
17413
17413
  var require_Window = __commonJS((exports, module) => {
17414
17414
  var DOMImplementation = require_DOMImplementation();
17415
17415
  var EventTarget = require_EventTarget();
@@ -17464,7 +17464,7 @@ var require_Window = __commonJS((exports, module) => {
17464
17464
  utils.expose(require_impl(), Window);
17465
17465
  });
17466
17466
 
17467
- // node_modules/@mixmark-io/domino/lib/index.js
17467
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/index.js
17468
17468
  var require_lib = __commonJS((exports) => {
17469
17469
  var DOMImplementation = require_DOMImplementation();
17470
17470
  var HTMLParser = require_HTMLParser();
@@ -17514,7 +17514,7 @@ var require_lib = __commonJS((exports) => {
17514
17514
  exports.impl = impl;
17515
17515
  });
17516
17516
 
17517
- // node_modules/turndown/lib/turndown.cjs.js
17517
+ // node_modules/.pnpm/turndown@7.2.4/node_modules/turndown/lib/turndown.cjs.js
17518
17518
  var require_turndown_cjs = __commonJS((exports, module) => {
17519
17519
  function extend(destination) {
17520
17520
  for (var i = 1;i < arguments.length; i++) {
@@ -18196,6 +18196,13 @@ function getCustomOpenCodeConfigDir() {
18196
18196
  const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
18197
18197
  return configDir || undefined;
18198
18198
  }
18199
+ function getConfigDir() {
18200
+ const customConfigDir = getCustomOpenCodeConfigDir();
18201
+ if (customConfigDir) {
18202
+ return customConfigDir;
18203
+ }
18204
+ return getDefaultOpenCodeConfigDir();
18205
+ }
18199
18206
  function getConfigSearchDirs() {
18200
18207
  const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
18201
18208
  return dirs.filter((dir, index) => {
@@ -18203,7 +18210,7 @@ function getConfigSearchDirs() {
18203
18210
  });
18204
18211
  }
18205
18212
  function getOpenCodeConfigPaths() {
18206
- const configDir = getDefaultOpenCodeConfigDir();
18213
+ const configDir = getConfigDir();
18207
18214
  return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
18208
18215
  }
18209
18216
 
@@ -18230,19 +18237,6 @@ var CUSTOM_SKILLS = [
18230
18237
  ];
18231
18238
 
18232
18239
  // src/cli/skills.ts
18233
- var RECOMMENDED_SKILLS = [
18234
- {
18235
- name: "agent-browser",
18236
- repo: "https://github.com/vercel-labs/agent-browser",
18237
- skillName: "agent-browser",
18238
- allowedAgents: ["designer"],
18239
- description: "High-performance browser automation",
18240
- postInstallCommands: [
18241
- "npm install -g agent-browser",
18242
- "agent-browser install"
18243
- ]
18244
- }
18245
- ];
18246
18240
  var PERMISSION_ONLY_SKILLS = [
18247
18241
  {
18248
18242
  name: "requesting-code-review",
@@ -18267,12 +18261,6 @@ function getSkillPermissionsForAgent(agentName, skillList) {
18267
18261
  }
18268
18262
  return permissions;
18269
18263
  }
18270
- for (const skill of RECOMMENDED_SKILLS) {
18271
- const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
18272
- if (isAllowed) {
18273
- permissions[skill.skillName] = "allow";
18274
- }
18275
- }
18276
18264
  for (const skill of CUSTOM_SKILLS) {
18277
18265
  const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
18278
18266
  if (isAllowed) {
@@ -18603,6 +18591,9 @@ var TodoContinuationConfigSchema = z2.object({
18603
18591
  autoEnable: z2.boolean().default(false).describe("Automatically enable auto-continue when the orchestrator session has enough todos"),
18604
18592
  autoEnableThreshold: z2.number().int().min(1).max(50).default(4).describe("Number of todos that triggers auto-enable (only used when autoEnable is true)")
18605
18593
  });
18594
+ var SubtaskConfigSchema = z2.object({
18595
+ timeoutMs: z2.number().int().min(0).max(24 * 60 * 60 * 1000).optional().describe("Subtask worker timeout in ms. 0 disables the timeout. Defaults to 300000 (5 minutes).")
18596
+ });
18606
18597
  var FailoverConfigSchema = z2.object({
18607
18598
  enabled: z2.boolean().default(true),
18608
18599
  timeoutMs: z2.number().min(0).default(15000),
@@ -18650,6 +18641,7 @@ var PluginConfigSchema = z2.object({
18650
18641
  sessionManager: SessionManagerConfigSchema.optional(),
18651
18642
  divoom: DivoomConfigSchema.optional(),
18652
18643
  todoContinuation: TodoContinuationConfigSchema.optional(),
18644
+ subtask: SubtaskConfigSchema.optional(),
18653
18645
  fallback: FailoverConfigSchema.optional(),
18654
18646
  council: CouncilConfigSchema.optional()
18655
18647
  }).superRefine((value, ctx) => {
@@ -18670,7 +18662,9 @@ function loadConfigFromPath(configPath, options) {
18670
18662
  const content = fs.readFileSync(configPath, "utf-8");
18671
18663
  let rawConfig;
18672
18664
  try {
18673
- rawConfig = JSON.parse(stripJsonComments(content));
18665
+ const stripped = stripJsonComments(content);
18666
+ const interpolated = stripped.replace(/\{env:([^}]+)\}/g, (_, varName) => process.env[varName] ?? "");
18667
+ rawConfig = JSON.parse(interpolated);
18674
18668
  } catch (error) {
18675
18669
  const message = error instanceof Error ? error.message : String(error);
18676
18670
  options?.onWarning?.({
@@ -18749,7 +18743,8 @@ function mergePluginConfigs(base, override) {
18749
18743
  sessionManager: deepMerge(base.sessionManager, override.sessionManager),
18750
18744
  divoom: deepMerge(base.divoom, override.divoom),
18751
18745
  fallback: deepMerge(base.fallback, override.fallback),
18752
- council: deepMerge(base.council, override.council)
18746
+ council: deepMerge(base.council, override.council),
18747
+ subtask: deepMerge(base.subtask, override.subtask)
18753
18748
  };
18754
18749
  }
18755
18750
  function deepMerge(base, override) {
@@ -18901,34 +18896,32 @@ function parseModelReference(model) {
18901
18896
  async function promptWithTimeout(client, args, timeoutMs, signal) {
18902
18897
  if (signal?.aborted)
18903
18898
  throw new Error("Prompt cancelled");
18904
- if (timeoutMs <= 0) {
18905
- await client.session.prompt(args);
18906
- return;
18907
- }
18908
18899
  const sessionId = args.path.id;
18900
+ const hasTimeout = timeoutMs > 0;
18909
18901
  let timer;
18910
18902
  let onAbort;
18911
18903
  try {
18912
18904
  const promptPromise = client.session.prompt(args);
18913
18905
  promptPromise.catch(() => {});
18914
- await Promise.race([
18915
- promptPromise,
18916
- new Promise((_, reject) => {
18906
+ const racers = [promptPromise];
18907
+ if (hasTimeout) {
18908
+ racers.push(new Promise((_, reject) => {
18917
18909
  timer = setTimeout(() => {
18918
18910
  reject(new OperationTimeoutError(`Prompt timed out after ${timeoutMs}ms`));
18919
18911
  }, timeoutMs);
18920
- }),
18921
- new Promise((_, reject) => {
18922
- if (!signal)
18923
- return;
18912
+ }));
18913
+ }
18914
+ if (signal) {
18915
+ racers.push(new Promise((_, reject) => {
18924
18916
  if (signal.aborted) {
18925
18917
  reject(new Error("Prompt cancelled"));
18926
18918
  return;
18927
18919
  }
18928
18920
  onAbort = () => reject(new Error("Prompt cancelled"));
18929
18921
  signal.addEventListener("abort", onAbort, { once: true });
18930
- })
18931
- ]);
18922
+ }));
18923
+ }
18924
+ await Promise.race(racers);
18932
18925
  } catch (error) {
18933
18926
  if (error instanceof OperationTimeoutError) {
18934
18927
  try {
@@ -19012,7 +19005,7 @@ var AGENT_DESCRIPTIONS = {
19012
19005
  - Permissions: Read/write files
19013
19006
  - Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
19014
19007
  - Tools/Constraints: Execution-focused—no research, no architectural decisions
19015
- - **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.
19008
+ - **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 modification, scoping work per folder and spawning parallel @fixers for each folder.
19016
19009
  - **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
19017
19010
  - **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.`,
19018
19011
  council: `@council
@@ -19135,7 +19128,7 @@ relevant files. Wait for the summary, then integrate and verify it.
19135
19128
  5. Adjust if needed
19136
19129
 
19137
19130
  ### Session Reuse
19138
- - Smartly reuse an available specialist session - constext reuse saves time and tokens
19131
+ - Smartly reuse an available specialist session - context reuse saves time and tokens
19139
19132
  - When too much unrelated, and really needed, start a fresh session with the specialist
19140
19133
  - If multiple remembered sessions fit, prefer the most recently used matching session.
19141
19134
  - Prefer re-uses over creating new sessions all the time
@@ -19982,6 +19975,35 @@ function getDisabledAgents(config) {
19982
19975
  return disabled;
19983
19976
  }
19984
19977
 
19978
+ // src/config/fallback-chains.ts
19979
+ function normalizeFallbackChainsForPreset(chains, presetName) {
19980
+ const normalized = {};
19981
+ for (const [rawKey, chainModels] of Object.entries(chains)) {
19982
+ if (!chainModels?.length)
19983
+ continue;
19984
+ const separatorIndex = rawKey.indexOf(":");
19985
+ const hasPresetScope = separatorIndex !== -1;
19986
+ const scopedPreset = hasPresetScope ? rawKey.slice(0, separatorIndex) : "";
19987
+ const agentName = hasPresetScope ? rawKey.slice(separatorIndex + 1) : rawKey;
19988
+ if (!agentName)
19989
+ continue;
19990
+ if (hasPresetScope && scopedPreset !== presetName)
19991
+ continue;
19992
+ const existing = normalized[agentName] ?? [];
19993
+ const seen = new Set(existing);
19994
+ for (const chainModel of chainModels) {
19995
+ if (seen.has(chainModel))
19996
+ continue;
19997
+ seen.add(chainModel);
19998
+ existing.push(chainModel);
19999
+ }
20000
+ if (existing.length > 0) {
20001
+ normalized[agentName] = existing;
20002
+ }
20003
+ }
20004
+ return normalized;
20005
+ }
20006
+
19985
20007
  // src/config/runtime-preset.ts
19986
20008
  var activeRuntimePreset = null;
19987
20009
  function setActiveRuntimePreset(name) {
@@ -19998,10 +20020,6 @@ function setActiveRuntimePresetWithPrevious(name) {
19998
20020
  previousRuntimePreset = activeRuntimePreset;
19999
20021
  activeRuntimePreset = name;
20000
20022
  }
20001
- function rollbackRuntimePreset(previous) {
20002
- activeRuntimePreset = previous;
20003
- previousRuntimePreset = null;
20004
- }
20005
20023
 
20006
20024
  // src/utils/logger.ts
20007
20025
  import * as fs2 from "node:fs";
@@ -20014,7 +20032,7 @@ var RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
20014
20032
  var logFile = null;
20015
20033
  var writeChain = Promise.resolve();
20016
20034
  function getLogDir() {
20017
- return process.env.OPENCODE_LOG_DIR ?? path2.join(os.homedir(), ".local/share/opencode");
20035
+ return process.env.OPENCODE_LOG_DIR ?? path2.join(os.homedir(), ".local/share/opencode/log");
20018
20036
  }
20019
20037
  function cleanupOldLogs(logDir) {
20020
20038
  try {
@@ -22139,6 +22157,7 @@ import * as os3 from "node:os";
22139
22157
  import * as path6 from "node:path";
22140
22158
  var PACKAGE_NAME = "oh-my-opencode-slim";
22141
22159
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
22160
+ var NPM_PACKAGE_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
22142
22161
  var NPM_FETCH_TIMEOUT = 5000;
22143
22162
  function getCacheDir() {
22144
22163
  if (process.platform === "win32") {
@@ -22165,6 +22184,83 @@ function isPrereleaseVersion(version) {
22165
22184
  function isDistTag(version) {
22166
22185
  return !/^\d/.test(version);
22167
22186
  }
22187
+ function parseVersion(version) {
22188
+ const normalized = version.trim().replace(/^[~^=<>\s]+/, "");
22189
+ const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?/);
22190
+ if (!match)
22191
+ return null;
22192
+ return {
22193
+ major: Number(match[1]),
22194
+ minor: Number(match[2]),
22195
+ patch: Number(match[3]),
22196
+ prerelease: match[4] ?? null
22197
+ };
22198
+ }
22199
+ function compareVersions(a, b) {
22200
+ const parsedA = parseVersion(a);
22201
+ const parsedB = parseVersion(b);
22202
+ if (!parsedA || !parsedB)
22203
+ return a.localeCompare(b);
22204
+ const parts = [
22205
+ "major",
22206
+ "minor",
22207
+ "patch"
22208
+ ];
22209
+ for (const part of parts) {
22210
+ if (parsedA[part] !== parsedB[part]) {
22211
+ return parsedA[part] - parsedB[part];
22212
+ }
22213
+ }
22214
+ if (parsedA.prerelease === parsedB.prerelease)
22215
+ return 0;
22216
+ if (!parsedA.prerelease)
22217
+ return 1;
22218
+ if (!parsedB.prerelease)
22219
+ return -1;
22220
+ return comparePrerelease(parsedA.prerelease, parsedB.prerelease);
22221
+ }
22222
+ function comparePrerelease(a, b) {
22223
+ const segmentsA = a.split(".");
22224
+ const segmentsB = b.split(".");
22225
+ const length = Math.max(segmentsA.length, segmentsB.length);
22226
+ for (let i = 0;i < length; i++) {
22227
+ const segmentA = segmentsA[i];
22228
+ const segmentB = segmentsB[i];
22229
+ if (segmentA === segmentB)
22230
+ continue;
22231
+ if (segmentA === undefined)
22232
+ return -1;
22233
+ if (segmentB === undefined)
22234
+ return 1;
22235
+ const numberA = Number(segmentA);
22236
+ const numberB = Number(segmentB);
22237
+ const numericA = Number.isInteger(numberA);
22238
+ const numericB = Number.isInteger(numberB);
22239
+ if (numericA && numericB)
22240
+ return numberA - numberB;
22241
+ if (numericA)
22242
+ return -1;
22243
+ if (numericB)
22244
+ return 1;
22245
+ const comparison = segmentA.localeCompare(segmentB);
22246
+ if (comparison !== 0)
22247
+ return comparison;
22248
+ }
22249
+ return 0;
22250
+ }
22251
+ function getPrereleaseChannel(version) {
22252
+ if (!version.prerelease)
22253
+ return null;
22254
+ return version.prerelease.split(".")[0] ?? null;
22255
+ }
22256
+ function isVersionInChannel(version, channel) {
22257
+ const parsed = parseVersion(version);
22258
+ if (!parsed)
22259
+ return false;
22260
+ if (channel === "latest")
22261
+ return parsed.prerelease === null;
22262
+ return getPrereleaseChannel(parsed) === channel;
22263
+ }
22168
22264
  function extractChannel(version) {
22169
22265
  if (!version)
22170
22266
  return "latest";
@@ -22312,6 +22408,10 @@ function getCachedVersion() {
22312
22408
  return null;
22313
22409
  }
22314
22410
  async function getLatestVersion(channel = "latest") {
22411
+ const distTags = await fetchDistTags();
22412
+ return distTags?.[channel] ?? distTags?.latest ?? null;
22413
+ }
22414
+ async function fetchDistTags() {
22315
22415
  const controller = new AbortController;
22316
22416
  const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
22317
22417
  try {
@@ -22322,13 +22422,84 @@ async function getLatestVersion(channel = "latest") {
22322
22422
  if (!response.ok)
22323
22423
  return null;
22324
22424
  const data = await response.json();
22325
- return data[channel] ?? data.latest ?? null;
22425
+ return data;
22326
22426
  } catch {
22327
22427
  return null;
22328
22428
  } finally {
22329
22429
  clearTimeout(timeoutId);
22330
22430
  }
22331
22431
  }
22432
+ async function getLatestCompatibleVersion(currentVersion, channel = "latest") {
22433
+ const current = parseVersion(currentVersion);
22434
+ if (!current) {
22435
+ const latestVersion = await getLatestVersion(channel);
22436
+ return {
22437
+ latestVersion: null,
22438
+ latestMajorVersion: latestVersion,
22439
+ blockedByMajor: latestVersion !== null,
22440
+ unsafeReason: latestVersion ? "unparseable-current-version" : undefined
22441
+ };
22442
+ }
22443
+ const controller = new AbortController;
22444
+ const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
22445
+ try {
22446
+ const response = await fetch(NPM_PACKAGE_URL, {
22447
+ signal: controller.signal,
22448
+ headers: { Accept: "application/json" }
22449
+ });
22450
+ if (!response.ok)
22451
+ return await getCompatibleFromDistTags(current, channel);
22452
+ const data = await response.json();
22453
+ const distTags = data["dist-tags"] ?? { latest: "" };
22454
+ const taggedVersion = distTags[channel] ?? distTags.latest ?? null;
22455
+ const latestMajorVersion = getBlockingMajorVersion(current, [
22456
+ taggedVersion,
22457
+ distTags.latest
22458
+ ]);
22459
+ const blockedByMajor = latestMajorVersion !== null;
22460
+ const versions = Object.keys(data.versions ?? {}).filter((version) => {
22461
+ const parsed = parseVersion(version);
22462
+ return parsed?.major === current.major && isVersionInChannel(version, channel);
22463
+ }).sort(compareVersions);
22464
+ const latestVersion = versions.at(-1) ?? null;
22465
+ return { latestVersion, latestMajorVersion, blockedByMajor };
22466
+ } catch {
22467
+ return await getCompatibleFromDistTags(current, channel);
22468
+ } finally {
22469
+ clearTimeout(timeoutId);
22470
+ }
22471
+ }
22472
+ async function getCompatibleFromDistTags(current, channel) {
22473
+ const distTags = await fetchDistTags();
22474
+ if (!distTags) {
22475
+ return {
22476
+ latestVersion: null,
22477
+ latestMajorVersion: null,
22478
+ blockedByMajor: false
22479
+ };
22480
+ }
22481
+ const latestVersion = distTags[channel] ?? distTags.latest ?? null;
22482
+ const latestMajorVersion = getBlockingMajorVersion(current, [
22483
+ latestVersion,
22484
+ distTags.latest
22485
+ ]);
22486
+ const blockedByMajor = latestMajorVersion !== null;
22487
+ const parsedLatest = latestVersion ? parseVersion(latestVersion) : null;
22488
+ const compatibleLatestVersion = parsedLatest?.major === current.major && latestVersion && isVersionInChannel(latestVersion, channel) ? latestVersion : null;
22489
+ return {
22490
+ latestVersion: compatibleLatestVersion,
22491
+ latestMajorVersion,
22492
+ blockedByMajor
22493
+ };
22494
+ }
22495
+ function getBlockingMajorVersion(current, candidates) {
22496
+ for (const candidate of candidates) {
22497
+ const parsed = candidate ? parseVersion(candidate) : null;
22498
+ if (parsed && parsed.major > current.major)
22499
+ return candidate ?? null;
22500
+ }
22501
+ return null;
22502
+ }
22332
22503
 
22333
22504
  // src/hooks/auto-update-checker/cache.ts
22334
22505
  function removeFromBunLock(installDir, packageName) {
@@ -22472,7 +22643,20 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate) {
22472
22643
  return;
22473
22644
  }
22474
22645
  const channel = extractChannel(pluginInfo.pinnedVersion ?? currentVersion);
22475
- const latestVersion = await getLatestVersion(channel);
22646
+ const latestInfo = await getLatestCompatibleVersion(currentVersion, channel);
22647
+ if (latestInfo.unsafeReason === "unparseable-current-version") {
22648
+ log(`[auto-update-checker] Current version is not semver; skipping auto-update: ${currentVersion}`);
22649
+ if (latestInfo.latestMajorVersion) {
22650
+ showToast(ctx, `OMO-Slim ${latestInfo.latestMajorVersion}`, `v${latestInfo.latestMajorVersion} available. Auto-update skipped because the current version could not be compared safely.`, "info", 8000);
22651
+ }
22652
+ return;
22653
+ }
22654
+ if (latestInfo.blockedByMajor && latestInfo.latestMajorVersion) {
22655
+ showMajorUpgradeToast(ctx, latestInfo.latestMajorVersion);
22656
+ log(`[auto-update-checker] Major update available; skipping auto-update: ${latestInfo.latestMajorVersion}`);
22657
+ return;
22658
+ }
22659
+ const latestVersion = latestInfo.latestVersion;
22476
22660
  if (!latestVersion) {
22477
22661
  log("[auto-update-checker] Failed to fetch latest version for channel:", channel);
22478
22662
  return;
@@ -22509,6 +22693,10 @@ Restart OpenCode to apply.`, "success", 8000);
22509
22693
  log("[auto-update-checker] bun install failed; update not installed");
22510
22694
  }
22511
22695
  }
22696
+ function showMajorUpgradeToast(ctx, version) {
22697
+ showToast(ctx, `oh-my-opencode-slim v${version} is available.`, `It requires OpenCode background subagents.
22698
+ Run: bunx oh-my-opencode-slim@latest install --background-subagents=yes`, "info", 12000);
22699
+ }
22512
22700
  async function runBunInstallSafe(installDir) {
22513
22701
  try {
22514
22702
  const proc = crossSpawn(["bun", "install"], {
@@ -23194,7 +23382,7 @@ var RATE_LIMIT_PATTERNS = [
23194
23382
  /usage limit/i,
23195
23383
  /overloaded/i,
23196
23384
  /resource.?exhausted/i,
23197
- /insufficient.?quota/i,
23385
+ /insufficient.?(quota|balance)/i,
23198
23386
  /high concurrency/i,
23199
23387
  /reduce concurrency/i
23200
23388
  ];
@@ -23270,7 +23458,7 @@ class ForegroundFallbackManager {
23270
23458
  if (!props?.sessionID || props.status?.type !== "retry")
23271
23459
  break;
23272
23460
  const msg = props.status.message?.toLowerCase() ?? "";
23273
- if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
23461
+ if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("insufficient") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
23274
23462
  await this.tryFallback(props.sessionID);
23275
23463
  }
23276
23464
  break;
@@ -23677,14 +23865,13 @@ function createPhaseReminderHook() {
23677
23865
  if (originalText.includes(SLIM_INTERNAL_INITIATOR_MARKER)) {
23678
23866
  return;
23679
23867
  }
23680
- if (originalText.includes(PHASE_REMINDER)) {
23868
+ if (lastUserMessage.parts.some((p) => p.text?.includes(PHASE_REMINDER))) {
23681
23869
  return;
23682
23870
  }
23683
- lastUserMessage.parts[textPartIndex].text = `${originalText}
23684
-
23685
- ---
23686
-
23687
- ${PHASE_REMINDER}`;
23871
+ lastUserMessage.parts.push({
23872
+ type: "text",
23873
+ text: PHASE_REMINDER
23874
+ });
23688
23875
  }
23689
23876
  };
23690
23877
  }
@@ -23720,8 +23907,338 @@ function createPostFileToolNudgeHook(options = {}) {
23720
23907
  }
23721
23908
  };
23722
23909
  }
23910
+ // src/hooks/session-goal/index.ts
23911
+ import * as fs7 from "node:fs/promises";
23912
+
23913
+ // src/interview/document.ts
23914
+ import * as fsSync from "node:fs";
23915
+ import * as fs6 from "node:fs/promises";
23916
+ import * as path9 from "node:path";
23917
+ var DEFAULT_OUTPUT_FOLDER = "interview";
23918
+ function normalizeOutputFolder(outputFolder) {
23919
+ const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
23920
+ return normalized || DEFAULT_OUTPUT_FOLDER;
23921
+ }
23922
+ function createInterviewDirectoryPath(directory, outputFolder) {
23923
+ return path9.join(directory, normalizeOutputFolder(outputFolder));
23924
+ }
23925
+ function createInterviewFilePath(directory, outputFolder, idea) {
23926
+ const fileName = `${slugify(idea) || "interview"}.md`;
23927
+ return path9.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
23928
+ }
23929
+ function relativeInterviewPath(directory, filePath) {
23930
+ return path9.relative(directory, filePath) || path9.basename(filePath);
23931
+ }
23932
+ function resolveExistingInterviewPath(directory, outputFolder, value) {
23933
+ const trimmed = value.trim();
23934
+ if (!trimmed) {
23935
+ return null;
23936
+ }
23937
+ const outputDir = createInterviewDirectoryPath(directory, outputFolder);
23938
+ const candidates = new Set;
23939
+ const resolvedRoot = path9.resolve(directory);
23940
+ if (path9.isAbsolute(trimmed)) {
23941
+ candidates.add(trimmed);
23942
+ } else {
23943
+ candidates.add(path9.resolve(directory, trimmed));
23944
+ candidates.add(path9.join(outputDir, trimmed));
23945
+ if (!trimmed.endsWith(".md")) {
23946
+ candidates.add(path9.join(outputDir, `${trimmed}.md`));
23947
+ }
23948
+ }
23949
+ for (const candidate of candidates) {
23950
+ if (path9.extname(candidate) !== ".md") {
23951
+ continue;
23952
+ }
23953
+ const resolved = path9.resolve(candidate);
23954
+ if (!resolved.startsWith(resolvedRoot + path9.sep) && resolved !== resolvedRoot) {
23955
+ continue;
23956
+ }
23957
+ if (fsSync.existsSync(candidate)) {
23958
+ return candidate;
23959
+ }
23960
+ }
23961
+ return null;
23962
+ }
23963
+ function slugify(value) {
23964
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
23965
+ }
23966
+ function extractHistorySection(document) {
23967
+ const marker = `## Q&A history
23968
+
23969
+ `;
23970
+ const index = document.indexOf(marker);
23971
+ return index >= 0 ? document.slice(index + marker.length).trim() : "";
23972
+ }
23973
+ function extractSummarySection(document) {
23974
+ const marker = `## Current spec
23975
+
23976
+ `;
23977
+ const historyMarker = `
23978
+
23979
+ ## Q&A history`;
23980
+ const start = document.indexOf(marker);
23981
+ if (start < 0) {
23982
+ return "";
23983
+ }
23984
+ const summaryStart = start + marker.length;
23985
+ const summaryEnd = document.indexOf(historyMarker, summaryStart);
23986
+ return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
23987
+ }
23988
+ function extractTitle(document) {
23989
+ const match = document.match(/^#\s+(.+)$/m);
23990
+ return match?.[1]?.trim() ?? "";
23991
+ }
23992
+ function buildInterviewDocument(idea, summary, history, meta) {
23993
+ const normalizedSummary = summary.trim() || "Waiting for interview answers.";
23994
+ const normalizedHistory = history.trim() || "No answers yet.";
23995
+ const frontmatter = meta?.sessionID ? [
23996
+ "---",
23997
+ `sessionID: ${meta.sessionID}`,
23998
+ `baseMessageCount: ${meta.baseMessageCount ?? 0}`,
23999
+ `updatedAt: ${new Date().toISOString()}`,
24000
+ "---",
24001
+ ""
24002
+ ].join(`
24003
+ `) : "";
24004
+ return [
24005
+ frontmatter,
24006
+ `# ${idea}`,
24007
+ "",
24008
+ "## Current spec",
24009
+ "",
24010
+ normalizedSummary,
24011
+ "",
24012
+ "## Q&A history",
24013
+ "",
24014
+ normalizedHistory,
24015
+ ""
24016
+ ].join(`
24017
+ `);
24018
+ }
24019
+ function parseFrontmatter(content) {
24020
+ const match = content.match(/^---\n([\s\S]*?)\n---\n/);
24021
+ if (!match)
24022
+ return null;
24023
+ const result = {};
24024
+ for (const line of match[1].split(`
24025
+ `)) {
24026
+ const colonIdx = line.indexOf(":");
24027
+ if (colonIdx > 0) {
24028
+ result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
24029
+ }
24030
+ }
24031
+ return result;
24032
+ }
24033
+ async function ensureInterviewFile(record) {
24034
+ await fs6.mkdir(path9.dirname(record.markdownPath), { recursive: true });
24035
+ try {
24036
+ await fs6.access(record.markdownPath);
24037
+ } catch {
24038
+ await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
24039
+ sessionID: record.sessionID,
24040
+ baseMessageCount: record.baseMessageCount
24041
+ }), "utf8");
24042
+ }
24043
+ }
24044
+ async function readInterviewDocument(record) {
24045
+ try {
24046
+ return await fs6.readFile(record.markdownPath, "utf8");
24047
+ } catch {}
24048
+ await ensureInterviewFile(record);
24049
+ return fs6.readFile(record.markdownPath, "utf8");
24050
+ }
24051
+ async function rewriteInterviewDocument(record, summary) {
24052
+ const existing = await readInterviewDocument(record);
24053
+ const history = extractHistorySection(existing);
24054
+ const next = buildInterviewDocument(record.idea, summary, history, {
24055
+ sessionID: record.sessionID,
24056
+ baseMessageCount: record.baseMessageCount
24057
+ });
24058
+ await fs6.writeFile(record.markdownPath, next, "utf8");
24059
+ return next;
24060
+ }
24061
+ async function appendInterviewAnswers(record, questions, answers) {
24062
+ const existing = await readInterviewDocument(record);
24063
+ const summary = extractSummarySection(existing);
24064
+ const history = extractHistorySection(existing);
24065
+ const questionMap = new Map(questions.map((question) => [question.id, question]));
24066
+ const appended = answers.map((answer) => {
24067
+ const question = questionMap.get(answer.questionId);
24068
+ return question ? `Q: ${question.question}
24069
+ A: ${answer.answer.trim()}` : null;
24070
+ }).filter((value) => value !== null).join(`
24071
+
24072
+ `);
24073
+ const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
24074
+
24075
+ `);
24076
+ await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
24077
+ sessionID: record.sessionID,
24078
+ baseMessageCount: record.baseMessageCount
24079
+ }), "utf8");
24080
+ }
24081
+
24082
+ // src/hooks/session-goal/index.ts
24083
+ var COMMAND_NAME = "goal";
24084
+ var MAX_GOAL_LENGTH = 4000;
24085
+ function normalizeGoalText(text) {
24086
+ return text.trim().replace(/\s+/g, " ").slice(0, MAX_GOAL_LENGTH);
24087
+ }
24088
+ function trimGoalText(text) {
24089
+ return text.trim().slice(0, MAX_GOAL_LENGTH);
24090
+ }
24091
+ function pushText(output, text) {
24092
+ output.parts.push(createInternalAgentTextPart(text));
24093
+ }
24094
+ function formatGoal(state, inherited) {
24095
+ const tag = inherited ? "parent_goal" : "active_goal";
24096
+ const guidance = inherited ? "This is context only. Your delegated prompt remains the bounded task." : "Use todos as the execution ledger. Keep planning, delegation, edits, and verification aligned to this goal. Do not broaden scope unless the user changes the goal.";
24097
+ return `<${tag}>
24098
+ Objective: ${state.text}
24099
+ ${guidance}
24100
+ </${tag}>`;
24101
+ }
24102
+ async function readInterviewGoal(directory, outputFolder, value) {
24103
+ try {
24104
+ const sourcePath = resolveExistingInterviewPath(directory, outputFolder, value);
24105
+ if (!sourcePath)
24106
+ return null;
24107
+ const content = await fs7.readFile(sourcePath, "utf8");
24108
+ const title = extractTitle(content);
24109
+ const summary = extractSummarySection(content);
24110
+ const text = trimGoalText([title ? `From interview: ${title}` : "", summary].filter(Boolean).join(`
24111
+
24112
+ `));
24113
+ return text ? { text, sourcePath } : null;
24114
+ } catch {
24115
+ return null;
24116
+ }
24117
+ }
24118
+ function resolveGoal(goals, sessionID) {
24119
+ const seen = new Set;
24120
+ let currentSessionID = sessionID;
24121
+ let inherited = false;
24122
+ while (true) {
24123
+ if (seen.has(currentSessionID)) {
24124
+ goals.delete(sessionID);
24125
+ return null;
24126
+ }
24127
+ seen.add(currentSessionID);
24128
+ const goal = goals.get(currentSessionID);
24129
+ if (!goal) {
24130
+ goals.delete(sessionID);
24131
+ return null;
24132
+ }
24133
+ if (!goal.inheritedFrom) {
24134
+ return { goal, inherited };
24135
+ }
24136
+ inherited = true;
24137
+ currentSessionID = goal.inheritedFrom;
24138
+ }
24139
+ }
24140
+ function createSessionGoalHook(ctx, config, options) {
24141
+ const goals = new Map;
24142
+ const outputFolder = config.interview?.outputFolder ?? "interview";
24143
+ return {
24144
+ registerCommand: (opencodeConfig) => {
24145
+ const commandConfig = opencodeConfig.command;
24146
+ if (commandConfig?.[COMMAND_NAME])
24147
+ return;
24148
+ if (!opencodeConfig.command)
24149
+ opencodeConfig.command = {};
24150
+ opencodeConfig.command[COMMAND_NAME] = {
24151
+ template: "Set or show the current session goal",
24152
+ description: "Pin a session objective that keeps todos, delegation, and verification aligned"
24153
+ };
24154
+ },
24155
+ handleCommandExecuteBefore: async (input, output) => {
24156
+ if (input.command !== COMMAND_NAME)
24157
+ return;
24158
+ output.parts.length = 0;
24159
+ const args = input.arguments.trim();
24160
+ if (!args) {
24161
+ const resolved = resolveGoal(goals, input.sessionID);
24162
+ pushText(output, resolved ? `Active goal:
24163
+ ${resolved.goal.text}
24164
+
24165
+ Use todos for execution steps. Auto-continuation continues only while todos remain.` : "No active goal. Set one with /goal <objective>.");
24166
+ return;
24167
+ }
24168
+ if (args === "clear") {
24169
+ goals.delete(input.sessionID);
24170
+ pushText(output, "Cleared the active goal for this session.");
24171
+ return;
24172
+ }
24173
+ if (args.startsWith("from ")) {
24174
+ const value = args.slice("from ".length).trim();
24175
+ const interviewGoal = await readInterviewGoal(ctx.directory, outputFolder, value);
24176
+ if (!interviewGoal) {
24177
+ pushText(output, `Could not find a readable interview spec for "${value}".`);
24178
+ return;
24179
+ }
24180
+ goals.set(input.sessionID, {
24181
+ text: interviewGoal.text,
24182
+ source: "interview",
24183
+ sourcePath: interviewGoal.sourcePath,
24184
+ createdAt: Date.now()
24185
+ });
24186
+ pushText(output, `Set active goal from interview:
24187
+ ${interviewGoal.text}`);
24188
+ return;
24189
+ }
24190
+ const text = normalizeGoalText(args);
24191
+ goals.set(input.sessionID, {
24192
+ text,
24193
+ source: "manual",
24194
+ createdAt: Date.now()
24195
+ });
24196
+ pushText(output, `Set active goal:
24197
+ ${text}`);
24198
+ },
24199
+ handleEvent: (input) => {
24200
+ const event = input.event;
24201
+ if (event.type === "session.created") {
24202
+ const info = event.properties?.info;
24203
+ if (!info?.id || !info.parentID)
24204
+ return;
24205
+ const parentGoal = goals.get(info.parentID);
24206
+ if (!parentGoal)
24207
+ return;
24208
+ goals.set(info.id, {
24209
+ inheritedFrom: info.parentID,
24210
+ createdAt: Date.now(),
24211
+ text: ""
24212
+ });
24213
+ return;
24214
+ }
24215
+ if (event.type === "session.deleted") {
24216
+ const props = event.properties;
24217
+ const sessionID = props?.info?.id ?? props?.sessionID;
24218
+ if (sessionID)
24219
+ goals.delete(sessionID);
24220
+ }
24221
+ },
24222
+ handleSystemTransform: (input, output) => {
24223
+ if (!input.sessionID)
24224
+ return;
24225
+ const resolved = resolveGoal(goals, input.sessionID);
24226
+ if (!resolved)
24227
+ return;
24228
+ const agentName = options?.getAgentName?.(input.sessionID);
24229
+ const { goal, inherited } = resolved;
24230
+ if (!inherited && agentName && agentName !== "orchestrator")
24231
+ return;
24232
+ const block = formatGoal(goal, inherited);
24233
+ if (output.system.some((entry) => entry.includes(block)))
24234
+ return;
24235
+ output.system.push(block);
24236
+ },
24237
+ getGoal: (sessionID) => resolveGoal(goals, sessionID)?.goal
24238
+ };
24239
+ }
23723
24240
  // src/hooks/task-session-manager/index.ts
23724
- import path9 from "node:path";
24241
+ import path10 from "node:path";
23725
24242
  var AGENT_NAME_SET = new Set([
23726
24243
  "orchestrator",
23727
24244
  "oracle",
@@ -23746,11 +24263,11 @@ function extractPath(output) {
23746
24263
  return /<path>([^<]+)<\/path>/.exec(output)?.[1];
23747
24264
  }
23748
24265
  function normalizePath(root, file) {
23749
- const relative = path9.relative(root, file);
23750
- if (!relative || relative.startsWith("..") || path9.isAbsolute(relative)) {
24266
+ const relative2 = path10.relative(root, file);
24267
+ if (!relative2 || relative2.startsWith("..") || path10.isAbsolute(relative2)) {
23751
24268
  return file;
23752
24269
  }
23753
- return relative;
24270
+ return relative2;
23754
24271
  }
23755
24272
  function extractReadFiles(root, output) {
23756
24273
  if (typeof output.output !== "string")
@@ -23999,7 +24516,7 @@ function createTaskSessionManagerHook(_ctx, options) {
23999
24516
  };
24000
24517
  }
24001
24518
  // src/hooks/todo-continuation/index.ts
24002
- import { tool } from "@opencode-ai/plugin/tool";
24519
+ import { tool } from "@opencode-ai/plugin";
24003
24520
 
24004
24521
  // src/hooks/todo-continuation/todo-hygiene.ts
24005
24522
  var TODO_HYGIENE_REMINDER = "If the active task changed or finished, update the todo list to match the current work state.";
@@ -24138,7 +24655,7 @@ function createTodoHygiene(options) {
24138
24655
 
24139
24656
  // src/hooks/todo-continuation/index.ts
24140
24657
  var HOOK_NAME = "todo-continuation";
24141
- var COMMAND_NAME = "auto-continue";
24658
+ var COMMAND_NAME2 = "auto-continue";
24142
24659
  var TODO_STATE_TIMEOUT_MS = 500;
24143
24660
  var CONTINUATION_PROMPT = "[Auto-continue: enabled - there are incomplete todos remaining. Continue with the next uncompleted item. Press Esc to cancel. If you need user input or review for the next item, ask instead of proceeding.]";
24144
24661
  var TODO_HYGIENE_INSTRUCTION_OPEN = '<instruction name="todo_hygiene">';
@@ -24629,7 +25146,7 @@ function createTodoContinuationHook(ctx, config) {
24629
25146
  }
24630
25147
  }
24631
25148
  async function handleCommandExecuteBefore(input, output) {
24632
- if (input.command !== COMMAND_NAME) {
25149
+ if (input.command !== COMMAND_NAME2) {
24633
25150
  return;
24634
25151
  }
24635
25152
  registerOrchestratorSession(input.sessionID);
@@ -24648,11 +25165,11 @@ function createTodoContinuationHook(ctx, config) {
24648
25165
  if (!newEnabled) {
24649
25166
  cancelPendingTimer(state);
24650
25167
  output.parts.push(createInternalAgentTextPart("[Auto-continue: disabled by user command.]"));
24651
- log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME} command`);
25168
+ log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME2} command`);
24652
25169
  return;
24653
25170
  }
24654
25171
  state.suppressUntil = 0;
24655
- log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME} command`, {
25172
+ log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME2} command`, {
24656
25173
  maxContinuations
24657
25174
  });
24658
25175
  let hasIncompleteTodos = false;
@@ -24686,7 +25203,7 @@ import path13 from "node:path";
24686
25203
  // src/interview/dashboard.ts
24687
25204
  import crypto from "node:crypto";
24688
25205
  import * as fsSync2 from "node:fs";
24689
- import fs7 from "node:fs/promises";
25206
+ import fs8 from "node:fs/promises";
24690
25207
  import {
24691
25208
  createServer
24692
25209
  } from "node:http";
@@ -24694,175 +25211,6 @@ import os4 from "node:os";
24694
25211
  import path11 from "node:path";
24695
25212
  import { URL as URL2 } from "node:url";
24696
25213
 
24697
- // src/interview/document.ts
24698
- import * as fsSync from "node:fs";
24699
- import * as fs6 from "node:fs/promises";
24700
- import * as path10 from "node:path";
24701
- var DEFAULT_OUTPUT_FOLDER = "interview";
24702
- function normalizeOutputFolder(outputFolder) {
24703
- const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
24704
- return normalized || DEFAULT_OUTPUT_FOLDER;
24705
- }
24706
- function createInterviewDirectoryPath(directory, outputFolder) {
24707
- return path10.join(directory, normalizeOutputFolder(outputFolder));
24708
- }
24709
- function createInterviewFilePath(directory, outputFolder, idea) {
24710
- const fileName = `${slugify(idea) || "interview"}.md`;
24711
- return path10.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
24712
- }
24713
- function relativeInterviewPath(directory, filePath) {
24714
- return path10.relative(directory, filePath) || path10.basename(filePath);
24715
- }
24716
- function resolveExistingInterviewPath(directory, outputFolder, value) {
24717
- const trimmed = value.trim();
24718
- if (!trimmed) {
24719
- return null;
24720
- }
24721
- const outputDir = createInterviewDirectoryPath(directory, outputFolder);
24722
- const candidates = new Set;
24723
- const resolvedRoot = path10.resolve(directory);
24724
- if (path10.isAbsolute(trimmed)) {
24725
- candidates.add(trimmed);
24726
- } else {
24727
- candidates.add(path10.resolve(directory, trimmed));
24728
- candidates.add(path10.join(outputDir, trimmed));
24729
- if (!trimmed.endsWith(".md")) {
24730
- candidates.add(path10.join(outputDir, `${trimmed}.md`));
24731
- }
24732
- }
24733
- for (const candidate of candidates) {
24734
- if (path10.extname(candidate) !== ".md") {
24735
- continue;
24736
- }
24737
- const resolved = path10.resolve(candidate);
24738
- if (!resolved.startsWith(resolvedRoot + path10.sep) && resolved !== resolvedRoot) {
24739
- continue;
24740
- }
24741
- if (fsSync.existsSync(candidate)) {
24742
- return candidate;
24743
- }
24744
- }
24745
- return null;
24746
- }
24747
- function slugify(value) {
24748
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
24749
- }
24750
- function extractHistorySection(document) {
24751
- const marker = `## Q&A history
24752
-
24753
- `;
24754
- const index = document.indexOf(marker);
24755
- return index >= 0 ? document.slice(index + marker.length).trim() : "";
24756
- }
24757
- function extractSummarySection(document) {
24758
- const marker = `## Current spec
24759
-
24760
- `;
24761
- const historyMarker = `
24762
-
24763
- ## Q&A history`;
24764
- const start = document.indexOf(marker);
24765
- if (start < 0) {
24766
- return "";
24767
- }
24768
- const summaryStart = start + marker.length;
24769
- const summaryEnd = document.indexOf(historyMarker, summaryStart);
24770
- return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
24771
- }
24772
- function extractTitle(document) {
24773
- const match = document.match(/^#\s+(.+)$/m);
24774
- return match?.[1]?.trim() ?? "";
24775
- }
24776
- function buildInterviewDocument(idea, summary, history, meta) {
24777
- const normalizedSummary = summary.trim() || "Waiting for interview answers.";
24778
- const normalizedHistory = history.trim() || "No answers yet.";
24779
- const frontmatter = meta?.sessionID ? [
24780
- "---",
24781
- `sessionID: ${meta.sessionID}`,
24782
- `baseMessageCount: ${meta.baseMessageCount ?? 0}`,
24783
- `updatedAt: ${new Date().toISOString()}`,
24784
- "---",
24785
- ""
24786
- ].join(`
24787
- `) : "";
24788
- return [
24789
- frontmatter,
24790
- `# ${idea}`,
24791
- "",
24792
- "## Current spec",
24793
- "",
24794
- normalizedSummary,
24795
- "",
24796
- "## Q&A history",
24797
- "",
24798
- normalizedHistory,
24799
- ""
24800
- ].join(`
24801
- `);
24802
- }
24803
- function parseFrontmatter(content) {
24804
- const match = content.match(/^---\n([\s\S]*?)\n---\n/);
24805
- if (!match)
24806
- return null;
24807
- const result = {};
24808
- for (const line of match[1].split(`
24809
- `)) {
24810
- const colonIdx = line.indexOf(":");
24811
- if (colonIdx > 0) {
24812
- result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
24813
- }
24814
- }
24815
- return result;
24816
- }
24817
- async function ensureInterviewFile(record) {
24818
- await fs6.mkdir(path10.dirname(record.markdownPath), { recursive: true });
24819
- try {
24820
- await fs6.access(record.markdownPath);
24821
- } catch {
24822
- await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
24823
- sessionID: record.sessionID,
24824
- baseMessageCount: record.baseMessageCount
24825
- }), "utf8");
24826
- }
24827
- }
24828
- async function readInterviewDocument(record) {
24829
- try {
24830
- return await fs6.readFile(record.markdownPath, "utf8");
24831
- } catch {}
24832
- await ensureInterviewFile(record);
24833
- return fs6.readFile(record.markdownPath, "utf8");
24834
- }
24835
- async function rewriteInterviewDocument(record, summary) {
24836
- const existing = await readInterviewDocument(record);
24837
- const history = extractHistorySection(existing);
24838
- const next = buildInterviewDocument(record.idea, summary, history, {
24839
- sessionID: record.sessionID,
24840
- baseMessageCount: record.baseMessageCount
24841
- });
24842
- await fs6.writeFile(record.markdownPath, next, "utf8");
24843
- return next;
24844
- }
24845
- async function appendInterviewAnswers(record, questions, answers) {
24846
- const existing = await readInterviewDocument(record);
24847
- const summary = extractSummarySection(existing);
24848
- const history = extractHistorySection(existing);
24849
- const questionMap = new Map(questions.map((question) => [question.id, question]));
24850
- const appended = answers.map((answer) => {
24851
- const question = questionMap.get(answer.questionId);
24852
- return question ? `Q: ${question.question}
24853
- A: ${answer.answer.trim()}` : null;
24854
- }).filter((value) => value !== null).join(`
24855
-
24856
- `);
24857
- const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
24858
-
24859
- `);
24860
- await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
24861
- sessionID: record.sessionID,
24862
- baseMessageCount: record.baseMessageCount
24863
- }), "utf8");
24864
- }
24865
-
24866
25214
  // src/interview/helpers.ts
24867
25215
  function sendJson(response, status, value) {
24868
25216
  response.statusCode = status;
@@ -26507,7 +26855,7 @@ function removeAuthFile(port) {
26507
26855
  }
26508
26856
  async function readDashboardAuthFile(port) {
26509
26857
  try {
26510
- const content = await fs7.readFile(getAuthFilePath(port), "utf8");
26858
+ const content = await fs8.readFile(getAuthFilePath(port), "utf8");
26511
26859
  const data = JSON.parse(content);
26512
26860
  try {
26513
26861
  process.kill(data.pid, 0);
@@ -26630,7 +26978,7 @@ function createDashboardServer(config) {
26630
26978
  const interviewDir = path11.join(dir, config.outputFolder);
26631
26979
  let entries;
26632
26980
  try {
26633
- entries = await fs7.readdir(interviewDir);
26981
+ entries = await fs8.readdir(interviewDir);
26634
26982
  } catch {
26635
26983
  continue;
26636
26984
  }
@@ -26639,7 +26987,7 @@ function createDashboardServer(config) {
26639
26987
  continue;
26640
26988
  let content;
26641
26989
  try {
26642
- content = await fs7.readFile(path11.join(interviewDir, entry), "utf8");
26990
+ content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
26643
26991
  } catch {
26644
26992
  continue;
26645
26993
  }
@@ -26668,7 +27016,7 @@ function createDashboardServer(config) {
26668
27016
  const interviewDir = path11.join(dir, config.outputFolder);
26669
27017
  let entries;
26670
27018
  try {
26671
- entries = await fs7.readdir(interviewDir);
27019
+ entries = await fs8.readdir(interviewDir);
26672
27020
  } catch {
26673
27021
  continue;
26674
27022
  }
@@ -26677,7 +27025,7 @@ function createDashboardServer(config) {
26677
27025
  continue;
26678
27026
  let content;
26679
27027
  try {
26680
- content = await fs7.readFile(path11.join(interviewDir, entry), "utf8");
27028
+ content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
26681
27029
  } catch {
26682
27030
  continue;
26683
27031
  }
@@ -26961,7 +27309,7 @@ function createDashboardServer(config) {
26961
27309
  let markdownPath = entry.filePath;
26962
27310
  if (entry.filePath) {
26963
27311
  try {
26964
- document = await fs7.readFile(entry.filePath, "utf8");
27312
+ document = await fs8.readFile(entry.filePath, "utf8");
26965
27313
  } catch {}
26966
27314
  } else {
26967
27315
  const dirs = getKnownDirectories();
@@ -26969,7 +27317,7 @@ function createDashboardServer(config) {
26969
27317
  const slug = extractResumeSlug(interviewId);
26970
27318
  const candidate = path11.join(dir, config.outputFolder, `${slug}.md`);
26971
27319
  try {
26972
- document = await fs7.readFile(candidate, "utf8");
27320
+ document = await fs8.readFile(candidate, "utf8");
26973
27321
  markdownPath = candidate;
26974
27322
  entry.filePath = candidate;
26975
27323
  break;
@@ -27494,7 +27842,7 @@ function createInterviewServer(deps) {
27494
27842
 
27495
27843
  // src/interview/service.ts
27496
27844
  import { spawn as spawn2 } from "node:child_process";
27497
- import * as fs8 from "node:fs/promises";
27845
+ import * as fs9 from "node:fs/promises";
27498
27846
  import * as path12 from "node:path";
27499
27847
 
27500
27848
  // src/interview/types.ts
@@ -27667,7 +28015,7 @@ function buildAnswerPrompt(answers, questions, maxQuestions) {
27667
28015
  }
27668
28016
 
27669
28017
  // src/interview/service.ts
27670
- var COMMAND_NAME2 = "interview";
28018
+ var COMMAND_NAME3 = "interview";
27671
28019
  var DEFAULT_MAX_QUESTIONS = 2;
27672
28020
  function isTruthyEnvFlag(value) {
27673
28021
  if (!value) {
@@ -27769,11 +28117,11 @@ function createInterviewService(ctx, config, deps) {
27769
28117
  const dir = path12.dirname(interview.markdownPath);
27770
28118
  const newPath = path12.join(dir, `${newSlug}.md`);
27771
28119
  try {
27772
- await fs8.access(newPath);
28120
+ await fs9.access(newPath);
27773
28121
  return;
27774
28122
  } catch {}
27775
28123
  try {
27776
- await fs8.rename(interview.markdownPath, newPath);
28124
+ await fs9.rename(interview.markdownPath, newPath);
27777
28125
  interview.markdownPath = newPath;
27778
28126
  log("[interview] renamed file with assistant title:", {
27779
28127
  from: currentFileName,
@@ -27839,7 +28187,7 @@ function createInterviewService(ctx, config, deps) {
27839
28187
  active.status = "abandoned";
27840
28188
  }
27841
28189
  }
27842
- const document = await fs8.readFile(markdownPath, "utf8");
28190
+ const document = await fs9.readFile(markdownPath, "utf8");
27843
28191
  const messages = await loadMessages(sessionID);
27844
28192
  const title = extractTitle(document);
27845
28193
  const record = {
@@ -27914,11 +28262,11 @@ function createInterviewService(ctx, config, deps) {
27914
28262
  }
27915
28263
  function registerCommand(opencodeConfig) {
27916
28264
  const configCommand = opencodeConfig.command;
27917
- if (!configCommand?.[COMMAND_NAME2]) {
28265
+ if (!configCommand?.[COMMAND_NAME3]) {
27918
28266
  if (!opencodeConfig.command) {
27919
28267
  opencodeConfig.command = {};
27920
28268
  }
27921
- opencodeConfig.command[COMMAND_NAME2] = {
28269
+ opencodeConfig.command[COMMAND_NAME3] = {
27922
28270
  template: "Start an interview and write a live markdown spec",
27923
28271
  description: "Open a localhost interview UI linked to the current OpenCode session"
27924
28272
  };
@@ -27992,7 +28340,7 @@ function createInterviewService(ctx, config, deps) {
27992
28340
  }
27993
28341
  }
27994
28342
  async function handleCommandExecuteBefore(input, output) {
27995
- if (input.command !== COMMAND_NAME2) {
28343
+ if (input.command !== COMMAND_NAME3) {
27996
28344
  return;
27997
28345
  }
27998
28346
  const idea = input.arguments.trim();
@@ -28011,7 +28359,7 @@ function createInterviewService(ctx, config, deps) {
28011
28359
  const resumePath = resolveExistingInterviewPath(ctx.directory, outputFolder, idea);
28012
28360
  if (resumePath) {
28013
28361
  const interview2 = await resumeInterview(input.sessionID, resumePath);
28014
- const document = await fs8.readFile(interview2.markdownPath, "utf8");
28362
+ const document = await fs9.readFile(interview2.markdownPath, "utf8");
28015
28363
  await notifyInterviewUrl(input.sessionID, interview2);
28016
28364
  output.parts.push(createInternalAgentTextPart(buildResumePrompt(document, maxQuestions)));
28017
28365
  return;
@@ -28075,7 +28423,7 @@ function createInterviewService(ctx, config, deps) {
28075
28423
  const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path12.resolve(i.markdownPath)));
28076
28424
  let entries;
28077
28425
  try {
28078
- entries = await fs8.readdir(outputDir);
28426
+ entries = await fs9.readdir(outputDir);
28079
28427
  } catch {
28080
28428
  return [];
28081
28429
  }
@@ -28088,7 +28436,7 @@ function createInterviewService(ctx, config, deps) {
28088
28436
  continue;
28089
28437
  let content;
28090
28438
  try {
28091
- content = await fs8.readFile(fullPath, "utf8");
28439
+ content = await fs9.readFile(fullPath, "utf8");
28092
28440
  } catch {
28093
28441
  continue;
28094
28442
  }
@@ -29161,29 +29509,46 @@ function startAvailabilityCheck(config) {
29161
29509
  // src/multiplexer/session-manager.ts
29162
29510
  var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
29163
29511
  var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
29164
-
29512
+ var SHARED_STATE_KEY = Symbol.for("oh-my-opencode-slim.multiplexer-session-manager.state");
29513
+ function getSharedState() {
29514
+ const globalWithState = globalThis;
29515
+ globalWithState[SHARED_STATE_KEY] ??= {
29516
+ sessions: new Map,
29517
+ knownSessions: new Map,
29518
+ spawningSessions: new Set,
29519
+ closingSessions: new Map
29520
+ };
29521
+ return globalWithState[SHARED_STATE_KEY];
29522
+ }
29165
29523
  class MultiplexerSessionManager {
29166
- client;
29524
+ instanceId = Math.random().toString(36).slice(2, 8);
29167
29525
  serverUrl;
29168
29526
  directory;
29169
29527
  multiplexer = null;
29170
- sessions = new Map;
29171
- knownSessions = new Map;
29172
- spawningSessions = new Set;
29173
- closingSessions = new Map;
29528
+ sessions;
29529
+ knownSessions;
29530
+ spawningSessions;
29531
+ closingSessions;
29174
29532
  pollInterval;
29175
29533
  enabled = false;
29176
29534
  constructor(ctx, config) {
29177
- this.client = ctx.client;
29535
+ const sharedState = getSharedState();
29536
+ this.sessions = sharedState.sessions;
29537
+ this.knownSessions = sharedState.knownSessions;
29538
+ this.spawningSessions = sharedState.spawningSessions;
29539
+ this.closingSessions = sharedState.closingSessions;
29178
29540
  this.directory = ctx.directory;
29179
29541
  const defaultPort = process.env.OPENCODE_PORT ?? "4096";
29180
29542
  this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
29181
29543
  this.multiplexer = getMultiplexer(config);
29182
29544
  this.enabled = config.type !== "none" && this.multiplexer !== null && this.multiplexer.isInsideSession();
29183
29545
  log("[multiplexer-session-manager] initialized", {
29546
+ instanceId: this.instanceId,
29184
29547
  enabled: this.enabled,
29185
29548
  type: config.type,
29186
- serverUrl: this.serverUrl
29549
+ serverUrl: this.serverUrl,
29550
+ trackedSessions: this.sessions.size,
29551
+ knownSessions: this.knownSessions.size
29187
29552
  });
29188
29553
  }
29189
29554
  async onSessionCreated(event) {
@@ -29201,6 +29566,7 @@ class MultiplexerSessionManager {
29201
29566
  const directory = info.directory ?? this.directory;
29202
29567
  if (this.isTrackedOrSpawning(sessionId)) {
29203
29568
  log("[multiplexer-session-manager] session already tracked or spawning", {
29569
+ instanceId: this.instanceId,
29204
29570
  sessionId
29205
29571
  });
29206
29572
  return;
@@ -29220,6 +29586,7 @@ class MultiplexerSessionManager {
29220
29586
  const serverRunning = await isServerRunning(this.serverUrl);
29221
29587
  if (!serverRunning) {
29222
29588
  log("[multiplexer-session-manager] server not running, skipping", {
29589
+ instanceId: this.instanceId,
29223
29590
  serverUrl: this.serverUrl
29224
29591
  });
29225
29592
  return;
@@ -29230,10 +29597,12 @@ class MultiplexerSessionManager {
29230
29597
  log("[multiplexer-session-manager] child session created, spawning pane", {
29231
29598
  sessionId,
29232
29599
  parentId,
29233
- title
29600
+ title,
29601
+ instanceId: this.instanceId
29234
29602
  });
29235
29603
  const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl, directory).catch((err) => {
29236
29604
  log("[multiplexer-session-manager] failed to spawn pane", {
29605
+ instanceId: this.instanceId,
29237
29606
  error: String(err)
29238
29607
  });
29239
29608
  return { success: false, paneId: undefined };
@@ -29244,6 +29613,7 @@ class MultiplexerSessionManager {
29244
29613
  await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale spawned pane failed", {
29245
29614
  sessionId,
29246
29615
  paneId: paneResult.paneId,
29616
+ instanceId: this.instanceId,
29247
29617
  error: String(err)
29248
29618
  }));
29249
29619
  return;
@@ -29256,9 +29626,11 @@ class MultiplexerSessionManager {
29256
29626
  title,
29257
29627
  directory,
29258
29628
  createdAt: now,
29259
- lastSeenAt: now
29629
+ lastSeenAt: now,
29630
+ seenInStatus: false
29260
29631
  });
29261
29632
  log("[multiplexer-session-manager] pane spawned", {
29633
+ instanceId: this.instanceId,
29262
29634
  sessionId,
29263
29635
  paneId: paneResult.paneId
29264
29636
  });
@@ -29270,6 +29642,19 @@ class MultiplexerSessionManager {
29270
29642
  async onSessionStatus(event) {
29271
29643
  if (!this.enabled)
29272
29644
  return;
29645
+ if (event.type === "session.idle") {
29646
+ const sessionId2 = event.properties?.sessionID;
29647
+ if (!sessionId2)
29648
+ return;
29649
+ log("[multiplexer-session-manager] session idle event received", {
29650
+ instanceId: this.instanceId,
29651
+ sessionId: sessionId2,
29652
+ tracked: this.sessions.has(sessionId2),
29653
+ known: this.knownSessions.has(sessionId2)
29654
+ });
29655
+ await this.closeSession(sessionId2, "idle");
29656
+ return;
29657
+ }
29273
29658
  if (event.type !== "session.status")
29274
29659
  return;
29275
29660
  const sessionId = event.properties?.sessionID;
@@ -29280,6 +29665,12 @@ class MultiplexerSessionManager {
29280
29665
  return;
29281
29666
  }
29282
29667
  if (event.properties?.status?.type === "busy") {
29668
+ log("[multiplexer-session-manager] session busy event received", {
29669
+ instanceId: this.instanceId,
29670
+ sessionId,
29671
+ tracked: this.sessions.has(sessionId),
29672
+ known: this.knownSessions.has(sessionId)
29673
+ });
29283
29674
  await this.respawnIfKnown(sessionId);
29284
29675
  }
29285
29676
  }
@@ -29292,6 +29683,7 @@ class MultiplexerSessionManager {
29292
29683
  if (!sessionId)
29293
29684
  return;
29294
29685
  log("[multiplexer-session-manager] session deleted, closing pane", {
29686
+ instanceId: this.instanceId,
29295
29687
  sessionId
29296
29688
  });
29297
29689
  await this.closeSession(sessionId, "deleted");
@@ -29300,13 +29692,17 @@ class MultiplexerSessionManager {
29300
29692
  if (this.pollInterval)
29301
29693
  return;
29302
29694
  this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
29303
- log("[multiplexer-session-manager] polling started");
29695
+ log("[multiplexer-session-manager] polling started", {
29696
+ instanceId: this.instanceId
29697
+ });
29304
29698
  }
29305
29699
  stopPolling() {
29306
29700
  if (this.pollInterval) {
29307
29701
  clearInterval(this.pollInterval);
29308
29702
  this.pollInterval = undefined;
29309
- log("[multiplexer-session-manager] polling stopped");
29703
+ log("[multiplexer-session-manager] polling stopped", {
29704
+ instanceId: this.instanceId
29705
+ });
29310
29706
  }
29311
29707
  }
29312
29708
  async pollSessions() {
@@ -29315,8 +29711,7 @@ class MultiplexerSessionManager {
29315
29711
  return;
29316
29712
  }
29317
29713
  try {
29318
- const statusResult = await this.client.session.status();
29319
- const allStatuses = statusResult.data ?? {};
29714
+ const allStatuses = await this.fetchSessionStatuses();
29320
29715
  const now = Date.now();
29321
29716
  const sessionsToClose = [];
29322
29717
  for (const [sessionId, tracked] of this.sessions.entries()) {
@@ -29324,11 +29719,12 @@ class MultiplexerSessionManager {
29324
29719
  const isIdle = status?.type === "idle";
29325
29720
  if (status) {
29326
29721
  tracked.lastSeenAt = now;
29722
+ tracked.seenInStatus = true;
29327
29723
  tracked.missingSince = undefined;
29328
29724
  } else if (!tracked.missingSince) {
29329
29725
  tracked.missingSince = now;
29330
29726
  }
29331
- const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
29727
+ const missingTooLong = tracked.seenInStatus && !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
29332
29728
  const isTimedOut = now - tracked.createdAt > SESSION_TIMEOUT_MS;
29333
29729
  if (isIdle || missingTooLong || isTimedOut) {
29334
29730
  sessionsToClose.push({
@@ -29344,6 +29740,14 @@ class MultiplexerSessionManager {
29344
29740
  log("[multiplexer-session-manager] poll error", { error: String(err) });
29345
29741
  }
29346
29742
  }
29743
+ async fetchSessionStatuses() {
29744
+ const url = new URL("/session/status", this.serverUrl);
29745
+ const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
29746
+ if (!response.ok) {
29747
+ throw new Error(`session status request failed: ${response.status} ${response.statusText}`);
29748
+ }
29749
+ return await response.json();
29750
+ }
29347
29751
  async closeSession(sessionId, reason) {
29348
29752
  if (reason === "deleted") {
29349
29753
  this.knownSessions.delete(sessionId);
@@ -29352,10 +29756,19 @@ class MultiplexerSessionManager {
29352
29756
  if (existingClose)
29353
29757
  return existingClose;
29354
29758
  const tracked = this.sessions.get(sessionId);
29355
- if (!tracked || !this.multiplexer)
29759
+ if (!tracked || !this.multiplexer) {
29760
+ log("[multiplexer-session-manager] close skipped; session not tracked", {
29761
+ instanceId: this.instanceId,
29762
+ sessionId,
29763
+ reason,
29764
+ tracked: !!tracked,
29765
+ hasMultiplexer: !!this.multiplexer
29766
+ });
29356
29767
  return;
29768
+ }
29357
29769
  this.sessions.delete(sessionId);
29358
29770
  log("[multiplexer-session-manager] closing session pane", {
29771
+ instanceId: this.instanceId,
29359
29772
  sessionId,
29360
29773
  paneId: tracked.paneId,
29361
29774
  reason
@@ -29363,6 +29776,7 @@ class MultiplexerSessionManager {
29363
29776
  const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
29364
29777
  return;
29365
29778
  }).catch((err) => log("[multiplexer-session-manager] failed to close session pane", {
29779
+ instanceId: this.instanceId,
29366
29780
  sessionId,
29367
29781
  paneId: tracked.paneId,
29368
29782
  reason,
@@ -29391,6 +29805,7 @@ class MultiplexerSessionManager {
29391
29805
  const serverRunning = await isServerRunning(this.serverUrl);
29392
29806
  if (!serverRunning) {
29393
29807
  log("[multiplexer-session-manager] server not running, skipping busy respawn", {
29808
+ instanceId: this.instanceId,
29394
29809
  serverUrl: this.serverUrl,
29395
29810
  sessionId
29396
29811
  });
@@ -29400,12 +29815,14 @@ class MultiplexerSessionManager {
29400
29815
  return;
29401
29816
  }
29402
29817
  log("[multiplexer-session-manager] child session busy again, respawning pane", {
29818
+ instanceId: this.instanceId,
29403
29819
  sessionId,
29404
29820
  parentId: known.parentId,
29405
29821
  title: known.title
29406
29822
  });
29407
29823
  const paneResult = await this.multiplexer.spawnPane(sessionId, known.title, this.serverUrl, known.directory).catch((err) => {
29408
29824
  log("[multiplexer-session-manager] failed to respawn pane", {
29825
+ instanceId: this.instanceId,
29409
29826
  error: String(err)
29410
29827
  });
29411
29828
  return { success: false, paneId: undefined };
@@ -29414,6 +29831,7 @@ class MultiplexerSessionManager {
29414
29831
  return;
29415
29832
  if (!this.knownSessions.has(sessionId) || this.closingSessions.has(sessionId)) {
29416
29833
  await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale respawned pane failed", {
29834
+ instanceId: this.instanceId,
29417
29835
  sessionId,
29418
29836
  paneId: paneResult.paneId,
29419
29837
  error: String(err)
@@ -29428,9 +29846,11 @@ class MultiplexerSessionManager {
29428
29846
  title: known.title,
29429
29847
  directory: known.directory,
29430
29848
  createdAt: now,
29431
- lastSeenAt: now
29849
+ lastSeenAt: now,
29850
+ seenInStatus: false
29432
29851
  });
29433
29852
  log("[multiplexer-session-manager] pane respawned on busy", {
29853
+ instanceId: this.instanceId,
29434
29854
  sessionId,
29435
29855
  paneId: paneResult.paneId
29436
29856
  });
@@ -29497,7 +29917,7 @@ async function isServerRunning(serverUrl, timeoutMs = 3000, maxAttempts = 2) {
29497
29917
  return false;
29498
29918
  }
29499
29919
  // src/tools/ast-grep/tools.ts
29500
- import { tool as tool2 } from "@opencode-ai/plugin/tool";
29920
+ import { tool as tool2 } from "@opencode-ai/plugin";
29501
29921
 
29502
29922
  // src/tools/ast-grep/cli.ts
29503
29923
  import { existsSync as existsSync9 } from "node:fs";
@@ -30108,8 +30528,11 @@ Returns the councillor responses with a summary footer.`,
30108
30528
  });
30109
30529
  return { council_session };
30110
30530
  }
30531
+ // src/tools/preset-manager.ts
30532
+ import * as fs11 from "node:fs";
30533
+
30111
30534
  // src/tui-state.ts
30112
- import * as fs9 from "node:fs";
30535
+ import * as fs10 from "node:fs";
30113
30536
  import * as os5 from "node:os";
30114
30537
  import * as path14 from "node:path";
30115
30538
  var STATE_DIR = "oh-my-opencode-slim";
@@ -30139,14 +30562,14 @@ function parseSnapshot(value) {
30139
30562
  }
30140
30563
  function readTuiSnapshot() {
30141
30564
  try {
30142
- return parseSnapshot(fs9.readFileSync(getTuiStatePath(), "utf8"));
30565
+ return parseSnapshot(fs10.readFileSync(getTuiStatePath(), "utf8"));
30143
30566
  } catch {
30144
30567
  return emptySnapshot();
30145
30568
  }
30146
30569
  }
30147
30570
  async function readTuiSnapshotAsync() {
30148
30571
  try {
30149
- return parseSnapshot(await fs9.promises.readFile(getTuiStatePath(), "utf8"));
30572
+ return parseSnapshot(await fs10.promises.readFile(getTuiStatePath(), "utf8"));
30150
30573
  } catch {
30151
30574
  return emptySnapshot();
30152
30575
  }
@@ -30154,8 +30577,8 @@ async function readTuiSnapshotAsync() {
30154
30577
  function writeTuiSnapshot(snapshot) {
30155
30578
  try {
30156
30579
  const filePath = getTuiStatePath();
30157
- fs9.mkdirSync(path14.dirname(filePath), { recursive: true });
30158
- fs9.writeFileSync(filePath, `${JSON.stringify(snapshot)}
30580
+ fs10.mkdirSync(path14.dirname(filePath), { recursive: true });
30581
+ fs10.writeFileSync(filePath, `${JSON.stringify(snapshot)}
30159
30582
  `);
30160
30583
  } catch {}
30161
30584
  }
@@ -30177,11 +30600,11 @@ function recordTuiAgentModel(input) {
30177
30600
  }
30178
30601
 
30179
30602
  // src/tools/preset-manager.ts
30180
- var COMMAND_NAME3 = "preset";
30603
+ var COMMAND_NAME4 = "preset";
30181
30604
  function createPresetManager(ctx, config) {
30182
30605
  let activePreset = getActiveRuntimePreset() ?? config.preset ?? null;
30183
30606
  async function handleCommandExecuteBefore(input, output) {
30184
- if (input.command !== COMMAND_NAME3) {
30607
+ if (input.command !== COMMAND_NAME4) {
30185
30608
  return;
30186
30609
  }
30187
30610
  output.parts.length = 0;
@@ -30200,11 +30623,11 @@ function createPresetManager(ctx, config) {
30200
30623
  }
30201
30624
  function registerCommand(opencodeConfig) {
30202
30625
  const configCommand = opencodeConfig.command;
30203
- if (!configCommand?.[COMMAND_NAME3]) {
30626
+ if (!configCommand?.[COMMAND_NAME4]) {
30204
30627
  if (!opencodeConfig.command) {
30205
30628
  opencodeConfig.command = {};
30206
30629
  }
30207
- opencodeConfig.command[COMMAND_NAME3] = {
30630
+ opencodeConfig.command[COMMAND_NAME4] = {
30208
30631
  template: "List available presets and switch between them",
30209
30632
  description: "Switch agent presets at runtime (e.g., /preset cheap, /preset powerful)"
30210
30633
  };
@@ -30226,64 +30649,47 @@ function createPresetManager(ctx, config) {
30226
30649
  agentUpdates[resolvedName] = agentConfig;
30227
30650
  }
30228
30651
  }
30229
- const currentRuntimePreset = getActiveRuntimePreset();
30230
- const resetUpdates = {};
30231
- if (currentRuntimePreset && config.presets?.[currentRuntimePreset]) {
30232
- const oldPreset = config.presets[currentRuntimePreset];
30233
- for (const rawName of Object.keys(oldPreset)) {
30234
- const resolvedOld = AGENT_ALIASES[rawName] ?? rawName;
30235
- if (resolvedOld in agentUpdates)
30236
- continue;
30237
- const baseline = config.agents?.[resolvedOld];
30238
- if (baseline) {
30239
- resetUpdates[resolvedOld] = mapOverrideToAgentConfig(baseline);
30240
- }
30241
- }
30242
- }
30243
30652
  const hasAgentUpdates = Object.keys(agentUpdates).length > 0;
30244
- const allUpdates = { ...resetUpdates, ...agentUpdates };
30245
30653
  if (!hasAgentUpdates) {
30246
30654
  output.parts.push(createInternalAgentTextPart(`Preset "${presetName}" is empty (no agent overrides defined).`));
30247
30655
  return;
30248
30656
  }
30249
- const previousPreset = activePreset;
30250
30657
  setActiveRuntimePresetWithPrevious(presetName);
30251
30658
  try {
30252
- await ctx.client.config.update({
30253
- body: { agent: allUpdates }
30254
- });
30255
- const snapshot = readTuiSnapshot();
30256
- const agentModels = { ...snapshot.agentModels };
30257
- for (const [agentName, agentConfig] of Object.entries(allUpdates)) {
30258
- if (typeof agentConfig.model === "string") {
30259
- agentModels[agentName] = agentConfig.model;
30260
- }
30261
- }
30262
- recordTuiAgentModels({ agentModels });
30263
- activePreset = presetName;
30264
- const summaryParts = [];
30265
- for (const [name, cfg] of Object.entries(agentUpdates)) {
30266
- const parts = [name];
30267
- if (cfg.model)
30268
- parts.push(`model: ${cfg.model}`);
30269
- if (cfg.variant)
30270
- parts.push(`variant: ${cfg.variant}`);
30271
- if (cfg.temperature !== undefined)
30272
- parts.push(`temp: ${cfg.temperature}`);
30273
- if (cfg.options)
30274
- parts.push("options: yes");
30275
- summaryParts.push(parts.join(" → "));
30276
- }
30277
- if (Object.keys(resetUpdates).length > 0) {
30278
- summaryParts.push(`Reset to baseline: ${Object.keys(resetUpdates).join(", ")}`);
30279
- }
30280
- output.parts.push(createInternalAgentTextPart(`Switched to preset "${presetName}":
30659
+ const { userConfigPath } = findPluginConfigPaths(ctx.directory);
30660
+ if (userConfigPath) {
30661
+ const raw = fs11.readFileSync(userConfigPath, "utf-8");
30662
+ const persisted = JSON.parse(stripJsonComments(raw));
30663
+ persisted.preset = presetName;
30664
+ fs11.writeFileSync(userConfigPath, `${JSON.stringify(persisted, null, 2)}
30665
+ `);
30666
+ }
30667
+ } catch {}
30668
+ const snapshot = readTuiSnapshot();
30669
+ const agentModels = { ...snapshot.agentModels };
30670
+ for (const [agentName, agentConfig] of Object.entries(agentUpdates)) {
30671
+ if (typeof agentConfig.model === "string") {
30672
+ agentModels[agentName] = agentConfig.model;
30673
+ }
30674
+ }
30675
+ recordTuiAgentModels({ agentModels });
30676
+ activePreset = presetName;
30677
+ const summaryParts = [];
30678
+ for (const [name, cfg] of Object.entries(agentUpdates)) {
30679
+ const parts = [name];
30680
+ if (cfg.model)
30681
+ parts.push(`model: ${cfg.model}`);
30682
+ if (cfg.variant)
30683
+ parts.push(`variant: ${cfg.variant}`);
30684
+ if (cfg.temperature !== undefined)
30685
+ parts.push(`temp: ${cfg.temperature}`);
30686
+ if (cfg.options)
30687
+ parts.push("options: yes");
30688
+ summaryParts.push(parts.join(" → "));
30689
+ }
30690
+ output.parts.push(createInternalAgentTextPart(`Saved preset "${presetName}". Restart or reload OpenCode to apply it to agent configuration. The current session was not reloaded to avoid interrupting the active conversation.
30281
30691
  ${summaryParts.join(`
30282
30692
  `)}`));
30283
- } catch (err) {
30284
- rollbackRuntimePreset(previousPreset);
30285
- output.parts.push(createInternalAgentTextPart(`Failed to switch preset "${presetName}": ${String(err)}`));
30286
- }
30287
30693
  }
30288
30694
  function mapOverrideToAgentConfig(override) {
30289
30695
  const agentConfig = {};
@@ -30416,21 +30822,21 @@ async function saveBinary(binaryDir, data, contentType, filename) {
30416
30822
  throw new Error("Unable to allocate unique filename for binary content");
30417
30823
  }
30418
30824
 
30419
- // node_modules/lru-cache/dist/esm/node/index.min.js
30420
- import { tracingChannel as j, channel as I } from "node:diagnostics_channel";
30421
- var S = I("lru-cache:metrics");
30422
- var W = j("lru-cache");
30825
+ // node_modules/.pnpm/lru-cache@11.3.6/node_modules/lru-cache/dist/esm/node/index.min.js
30826
+ import { tracingChannel as I, channel as G } from "node:diagnostics_channel";
30827
+ var S = G("lru-cache:metrics");
30828
+ var W = I("lru-cache");
30829
+ var C = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
30423
30830
  var D = () => S.hasSubscribers || W.hasSubscribers;
30424
- var G = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
30425
- var M = new Set;
30426
- var C = typeof process == "object" && process ? process : {};
30831
+ var U = new Set;
30832
+ var L = typeof process == "object" && process ? process : {};
30427
30833
  var P = (u, e, t, i) => {
30428
- typeof C.emitWarning == "function" ? C.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
30834
+ typeof L.emitWarning == "function" ? L.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
30429
30835
  };
30430
- var H = (u) => !M.has(u);
30431
- var $ = Symbol("type");
30836
+ var H = (u) => !U.has(u);
30837
+ var X = Symbol("type");
30432
30838
  var F = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u);
30433
- var U = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null;
30839
+ var j = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null;
30434
30840
  var O = class extends Array {
30435
30841
  constructor(e) {
30436
30842
  super(e), this.fill(0);
@@ -30441,7 +30847,7 @@ var R = class u {
30441
30847
  length;
30442
30848
  static #o = false;
30443
30849
  static create(e) {
30444
- let t = U(e);
30850
+ let t = j(e);
30445
30851
  if (!t)
30446
30852
  return [];
30447
30853
  u.#o = true;
@@ -30460,11 +30866,11 @@ var R = class u {
30460
30866
  return this.heap[--this.length];
30461
30867
  }
30462
30868
  };
30463
- var L = class u2 {
30869
+ var M = class u2 {
30464
30870
  #o;
30465
30871
  #u;
30466
30872
  #w;
30467
- #D;
30873
+ #x;
30468
30874
  #S;
30469
30875
  #M;
30470
30876
  #U;
@@ -30535,7 +30941,7 @@ var L = class u2 {
30535
30941
  return this.#w;
30536
30942
  }
30537
30943
  get onInsert() {
30538
- return this.#D;
30944
+ return this.#x;
30539
30945
  }
30540
30946
  get disposeAfter() {
30541
30947
  return this.#S;
@@ -30544,9 +30950,9 @@ var L = class u2 {
30544
30950
  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: z5, perf: x } = e;
30545
30951
  if (x !== undefined && typeof x?.now != "function")
30546
30952
  throw new TypeError("perf option must have a now() method if specified");
30547
- if (this.#m = x ?? G, t !== 0 && !F(t))
30953
+ if (this.#m = x ?? C, t !== 0 && !F(t))
30548
30954
  throw new TypeError("max option must be a nonnegative integer");
30549
- let v = t ? U(t) : Array;
30955
+ let v = t ? j(t) : Array;
30550
30956
  if (!v)
30551
30957
  throw new Error("invalid max value: " + t);
30552
30958
  if (this.#o = t, this.#u = T, this.maxEntrySize = w || this.#u, this.sizeCalculation = y, this.sizeCalculation) {
@@ -30559,7 +30965,7 @@ var L = class u2 {
30559
30965
  throw new TypeError("memoMethod must be a function if defined");
30560
30966
  if (this.#U = m, a !== undefined && typeof a != "function")
30561
30967
  throw new TypeError("fetchMethod must be a function if specified");
30562
- 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 = !!z5, this.maxEntrySize !== 0) {
30968
+ 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 = !!z5, this.maxEntrySize !== 0) {
30563
30969
  if (this.#u !== 0 && !F(this.#u))
30564
30970
  throw new TypeError("maxSize must be a positive integer if specified");
30565
30971
  if (!F(this.maxEntrySize))
@@ -30575,7 +30981,7 @@ var L = class u2 {
30575
30981
  throw new TypeError("At least one of max, maxSize, or ttl is required");
30576
30982
  if (!this.ttlAutopurge && !this.#o && !this.#u) {
30577
30983
  let E = "LRU_CACHE_UNBOUNDED";
30578
- H(E) && (M.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
30984
+ H(E) && (U.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
30579
30985
  }
30580
30986
  }
30581
30987
  getRemainingTTL(e) {
@@ -30587,7 +30993,7 @@ var L = class u2 {
30587
30993
  let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
30588
30994
  this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
30589
30995
  t[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
30590
- }, this.#x = (r) => {
30996
+ }, this.#D = (r) => {
30591
30997
  t[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
30592
30998
  };
30593
30999
  let s = this.ttlAutopurge ? (r, h) => {
@@ -30631,7 +31037,7 @@ var L = class u2 {
30631
31037
  return !!l && !!h && (n || o()) - h > l;
30632
31038
  };
30633
31039
  }
30634
- #x = () => {};
31040
+ #D = () => {};
30635
31041
  #E = () => {};
30636
31042
  #N = () => {};
30637
31043
  #p = () => false;
@@ -30797,7 +31203,7 @@ var L = class u2 {
30797
31203
  return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
30798
31204
  let f = this.#n === 0 ? undefined : this.#s.get(e);
30799
31205
  if (f === undefined)
30800
- 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");
31206
+ 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");
30801
31207
  else {
30802
31208
  this.#L(f);
30803
31209
  let g = this.#t[f];
@@ -30861,7 +31267,7 @@ var L = class u2 {
30861
31267
  if (this.#p(n))
30862
31268
  s && (s.has = "stale", this.#E(s, n));
30863
31269
  else
30864
- return i && this.#x(n), s && (s.has = "hit", this.#E(s, n)), true;
31270
+ return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
30865
31271
  } else
30866
31272
  s && (s.has = "miss");
30867
31273
  return false;
@@ -30919,7 +31325,7 @@ var L = class u2 {
30919
31325
  let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
30920
31326
  t.status = s, s && t.context && (s.context = t.context);
30921
31327
  let n = this.#B(e, t);
30922
- return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
31328
+ return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
30923
31329
  }
30924
31330
  async#B(e, t = {}) {
30925
31331
  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;
@@ -30938,7 +31344,7 @@ var L = class u2 {
30938
31344
  }
30939
31345
  let A = this.#p(b);
30940
31346
  if (!y && !A)
30941
- return a && (a.fetch = "hit"), this.#L(b), s && this.#x(b), a && this.#E(a, b), d;
31347
+ return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
30942
31348
  let z5 = this.#P(e, b, _, w), v = z5.__staleWhileFetching !== undefined && i;
30943
31349
  return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z5.__staleWhileFetching : z5.__returned = z5;
30944
31350
  }
@@ -30947,7 +31353,7 @@ var L = class u2 {
30947
31353
  let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
30948
31354
  t.status = s, s && t.context && (s.context = t.context);
30949
31355
  let n = this.#K(e, t);
30950
- return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
31356
+ return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
30951
31357
  }
30952
31358
  async#K(e, t = {}) {
30953
31359
  let i = await this.#B(e, t);
@@ -30986,7 +31392,7 @@ var L = class u2 {
30986
31392
  return;
30987
31393
  }
30988
31394
  let h = this.#t[r], l = this.#e(h);
30989
- 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);
31395
+ 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);
30990
31396
  }
30991
31397
  #$(e, t) {
30992
31398
  this.#c[t] = e, this.#a[e] = t;
@@ -31154,7 +31560,7 @@ function extractStructuredText(root) {
31154
31560
  ]);
31155
31561
  const isText = (node) => node.nodeType === node.TEXT_NODE;
31156
31562
  const isElement = (node) => node.nodeType === node.ELEMENT_NODE;
31157
- const pushText = (value) => {
31563
+ const pushText2 = (value) => {
31158
31564
  const normalized = value.replace(/\s+/g, " ");
31159
31565
  if (!normalized.trim())
31160
31566
  return;
@@ -31180,7 +31586,7 @@ function extractStructuredText(root) {
31180
31586
  };
31181
31587
  const visit = (node) => {
31182
31588
  if (isText(node)) {
31183
- pushText(node.textContent || "");
31589
+ pushText2(node.textContent || "");
31184
31590
  return;
31185
31591
  }
31186
31592
  if (!isElement(node))
@@ -31891,7 +32297,7 @@ async function probeLlmsText(url, timeoutMs, signal, fallbackOrigin) {
31891
32297
  }
31892
32298
 
31893
32299
  // src/tools/smartfetch/cache.ts
31894
- var CACHE = new L({
32300
+ var CACHE = new M({
31895
32301
  maxSize: 50 * 1024 * 1024,
31896
32302
  ttl: 15 * 60 * 1000,
31897
32303
  sizeCalculation: (value) => {
@@ -31952,7 +32358,7 @@ function isInvalidLlmsResult(fetchResult) {
31952
32358
 
31953
32359
  // src/tools/smartfetch/secondary-model.ts
31954
32360
  import { existsSync as existsSync10 } from "node:fs";
31955
- import { readFile as readFile4 } from "node:fs/promises";
32361
+ import { readFile as readFile5 } from "node:fs/promises";
31956
32362
  import path17 from "node:path";
31957
32363
  function parseModelRef(value) {
31958
32364
  if (!value)
@@ -31989,7 +32395,7 @@ async function readOpenCodeConfigFile(configPath) {
31989
32395
  if (!configPath)
31990
32396
  return;
31991
32397
  try {
31992
- const content = await readFile4(configPath, "utf8");
32398
+ const content = await readFile5(configPath, "utf8");
31993
32399
  return JSON.parse(stripJsonComments(content));
31994
32400
  } catch {
31995
32401
  return;
@@ -32639,7 +33045,7 @@ function createWebfetchTool(pluginCtx, options = {}) {
32639
33045
  });
32640
33046
  }
32641
33047
  // src/tools/subtask/command.ts
32642
- var COMMAND_NAME4 = "subtask";
33048
+ var COMMAND_NAME5 = "subtask";
32643
33049
  var SUBTASK_COMMAND_TEMPLATE = `Start a focused subtask worker.
32644
33050
 
32645
33051
  The user's request below is the full scope for the worker. Do not broaden it.
@@ -32660,11 +33066,11 @@ Only include files that are clearly relevant. If no files are needed, omit files
32660
33066
  function createSubtaskCommandManager(_ctx, state) {
32661
33067
  function registerCommand(opencodeConfig) {
32662
33068
  const configCommand = opencodeConfig.command;
32663
- if (!configCommand?.[COMMAND_NAME4]) {
33069
+ if (!configCommand?.[COMMAND_NAME5]) {
32664
33070
  if (!opencodeConfig.command) {
32665
33071
  opencodeConfig.command = {};
32666
33072
  }
32667
- opencodeConfig.command[COMMAND_NAME4] = {
33073
+ opencodeConfig.command[COMMAND_NAME5] = {
32668
33074
  description: "Create a focused subtask prompt for a new session",
32669
33075
  template: SUBTASK_COMMAND_TEMPLATE
32670
33076
  };
@@ -32691,11 +33097,11 @@ function createSubtaskCommandManager(_ctx, state) {
32691
33097
  };
32692
33098
  }
32693
33099
  // src/tools/subtask/files.ts
32694
- import * as fs11 from "node:fs/promises";
33100
+ import * as fs13 from "node:fs/promises";
32695
33101
  import * as path20 from "node:path";
32696
33102
 
32697
33103
  // src/tools/subtask/vendor.ts
32698
- import * as fs10 from "node:fs/promises";
33104
+ import * as fs12 from "node:fs/promises";
32699
33105
  import * as path19 from "node:path";
32700
33106
  var DEFAULT_READ_LIMIT = 2000;
32701
33107
  var MAX_LINE_LENGTH = 2000;
@@ -32739,7 +33145,7 @@ async function isBinaryFile(filepath) {
32739
33145
  return true;
32740
33146
  }
32741
33147
  try {
32742
- const file = await fs10.open(filepath, "r");
33148
+ const file = await fs12.open(filepath, "r");
32743
33149
  try {
32744
33150
  const buffer = Buffer.alloc(SAMPLE_BYTES);
32745
33151
  const result = await file.read(buffer, 0, SAMPLE_BYTES, 0);
@@ -32825,24 +33231,24 @@ function parseFileReferences(text) {
32825
33231
  }
32826
33232
  async function buildSyntheticFileParts(directory, refs) {
32827
33233
  const parts = [];
32828
- const realDirectory = await fs11.realpath(directory);
33234
+ const realDirectory = await fs13.realpath(directory);
32829
33235
  for (const ref of refs) {
32830
33236
  const filepath = path20.resolve(directory, ref);
32831
33237
  const relative3 = path20.relative(directory, filepath);
32832
33238
  if (relative3.startsWith("..") || path20.isAbsolute(relative3))
32833
33239
  continue;
32834
33240
  try {
32835
- const realFilepath = await fs11.realpath(filepath);
33241
+ const realFilepath = await fs13.realpath(filepath);
32836
33242
  const realRelative = path20.relative(realDirectory, realFilepath);
32837
33243
  if (realRelative.startsWith("..") || path20.isAbsolute(realRelative)) {
32838
33244
  continue;
32839
33245
  }
32840
- const stats = await fs11.stat(realFilepath);
33246
+ const stats = await fs13.stat(realFilepath);
32841
33247
  if (!stats.isFile())
32842
33248
  continue;
32843
33249
  if (await isBinaryFile(realFilepath))
32844
33250
  continue;
32845
- const content = await fs11.readFile(realFilepath, "utf-8");
33251
+ const content = await fs13.readFile(realFilepath, "utf-8");
32846
33252
  parts.push({
32847
33253
  type: "text",
32848
33254
  synthetic: true,
@@ -32877,7 +33283,7 @@ function createSubtaskState() {
32877
33283
  }
32878
33284
  // src/tools/subtask/tools.ts
32879
33285
  import { tool as tool5 } from "@opencode-ai/plugin";
32880
- var SUBTASK_TIMEOUT_MS = 5 * 60 * 1000;
33286
+ var DEFAULT_SUBTASK_TIMEOUT_MS = 5 * 60 * 1000;
32881
33287
  var SUBTASK_SUMMARY_TAG_REGEX = /<\/?subtask_summary>/g;
32882
33288
  function normalizeSubtaskSummary(text) {
32883
33289
  return text.replace(SUBTASK_SUMMARY_TAG_REGEX, "").trim();
@@ -32889,8 +33295,9 @@ function getAbortSignal(context) {
32889
33295
  const signal = context.abort;
32890
33296
  return signal && typeof signal === "object" && "addEventListener" in signal && "removeEventListener" in signal && "aborted" in signal ? signal : undefined;
32891
33297
  }
32892
- function createSubtaskTool(ctx, state, depthTracker) {
33298
+ function createSubtaskTool(ctx, state, depthTracker, options = {}) {
32893
33299
  const client = ctx.client;
33300
+ const timeoutMs = options.timeoutMs ?? DEFAULT_SUBTASK_TIMEOUT_MS;
32894
33301
  return tool5({
32895
33302
  description: "Run a child worker session and return its completion summary to the caller",
32896
33303
  args: {
@@ -32988,7 +33395,7 @@ Risks / follow-up:
32988
33395
  ...await buildSyntheticFileParts(directory, files)
32989
33396
  ]
32990
33397
  }
32991
- }, SUBTASK_TIMEOUT_MS, abortSignal);
33398
+ }, timeoutMs, abortSignal);
32992
33399
  const extraction = await extractSessionResult(client, childSessionID, {
32993
33400
  directory,
32994
33401
  includeReasoning: false
@@ -33204,6 +33611,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33204
33611
  let jsonErrorRecoveryHook;
33205
33612
  let foregroundFallback;
33206
33613
  let todoContinuationHook;
33614
+ let sessionGoalHook;
33207
33615
  let taskSessionManagerHook;
33208
33616
  let interviewManager;
33209
33617
  let presetManager;
@@ -33240,11 +33648,10 @@ var OhMyOpenCodeLite = async (ctx) => {
33240
33648
  runtimeChains[agentDef.name] = agentDef._modelArray.map((m) => m.id);
33241
33649
  }
33242
33650
  }
33651
+ const activePresetForFallback = getActiveRuntimePreset() ?? config.preset ?? null;
33243
33652
  if (config.fallback?.enabled !== false) {
33244
- const chains = config.fallback?.chains ?? {};
33653
+ const chains = normalizeFallbackChainsForPreset(config.fallback?.chains ?? {}, activePresetForFallback);
33245
33654
  for (const [agentName, chainModels] of Object.entries(chains)) {
33246
- if (!chainModels?.length)
33247
- continue;
33248
33655
  const existing = runtimeChains[agentName] ?? [];
33249
33656
  const seen = new Set(existing);
33250
33657
  for (const m of chainModels) {
@@ -33296,6 +33703,9 @@ var OhMyOpenCodeLite = async (ctx) => {
33296
33703
  autoEnable: config.todoContinuation?.autoEnable ?? false,
33297
33704
  autoEnableThreshold: config.todoContinuation?.autoEnableThreshold ?? 4
33298
33705
  });
33706
+ sessionGoalHook = createSessionGoalHook(ctx, config, {
33707
+ getAgentName: (sessionID) => sessionAgentMap.get(sessionID)
33708
+ });
33299
33709
  taskSessionManagerHook = createTaskSessionManagerHook(ctx, {
33300
33710
  maxSessionsPerAgent: config.sessionManager?.maxSessionsPerAgent ?? 2,
33301
33711
  readContextMinLines: config.sessionManager?.readContextMinLines ?? 10,
@@ -33353,7 +33763,9 @@ var OhMyOpenCodeLite = async (ctx) => {
33353
33763
  ...todoContinuationHook.tool,
33354
33764
  ast_grep_search,
33355
33765
  ast_grep_replace,
33356
- subtask: createSubtaskTool(ctx, subtaskState, depthTracker),
33766
+ subtask: createSubtaskTool(ctx, subtaskState, depthTracker, {
33767
+ timeoutMs: config.subtask?.timeoutMs
33768
+ }),
33357
33769
  read_session: createReadSessionTool(ctx.client, subtaskState)
33358
33770
  },
33359
33771
  mcp: mcps,
@@ -33379,8 +33791,9 @@ var OhMyOpenCodeLite = async (ctx) => {
33379
33791
  }
33380
33792
  }
33381
33793
  const configAgent = opencodeConfig.agent;
33794
+ const activePresetForFallback = getActiveRuntimePreset() ?? config.preset ?? null;
33382
33795
  const fallbackChainsEnabled = config.fallback?.enabled !== false;
33383
- const fallbackChains = fallbackChainsEnabled ? config.fallback?.chains ?? {} : {};
33796
+ const fallbackChains = fallbackChainsEnabled ? normalizeFallbackChainsForPreset(config.fallback?.chains ?? {}, activePresetForFallback) : {};
33384
33797
  const effectiveArrays = {};
33385
33798
  for (const [agentName, models] of Object.entries(modelArrayMap)) {
33386
33799
  effectiveArrays[agentName] = [...models];
@@ -33550,6 +33963,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33550
33963
  };
33551
33964
  }
33552
33965
  interviewManager.registerCommand(opencodeConfig);
33966
+ sessionGoalHook.registerCommand(opencodeConfig);
33553
33967
  presetManager.registerCommand(opencodeConfig);
33554
33968
  subtaskCommandManager.registerCommand(opencodeConfig);
33555
33969
  },
@@ -33571,12 +33985,13 @@ var OhMyOpenCodeLite = async (ctx) => {
33571
33985
  depthTracker.registerChild(parentSessionId, childSessionId);
33572
33986
  }
33573
33987
  }
33574
- await foregroundFallback.handleEvent(input.event);
33575
- await todoContinuationHook.handleEvent(input);
33576
- await autoUpdateChecker.event(input);
33577
33988
  await multiplexerSessionManager.onSessionCreated(event);
33578
33989
  await multiplexerSessionManager.onSessionStatus(event);
33579
33990
  await multiplexerSessionManager.onSessionDeleted(event);
33991
+ await foregroundFallback.handleEvent(input.event);
33992
+ await todoContinuationHook.handleEvent(input);
33993
+ sessionGoalHook.handleEvent(input);
33994
+ await autoUpdateChecker.event(input);
33580
33995
  await interviewManager.handleEvent(input);
33581
33996
  await taskSessionManagerHook.event(input);
33582
33997
  subtaskCommandManager.handleEvent(input);
@@ -33637,6 +34052,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33637
34052
  await todoContinuationHook.handleCommandExecuteBefore(input, output);
33638
34053
  await interviewManager.handleCommandExecuteBefore(input, output);
33639
34054
  await presetManager.handleCommandExecuteBefore(input, output);
34055
+ await sessionGoalHook.handleCommandExecuteBefore(input, output);
33640
34056
  },
33641
34057
  "chat.headers": chatHeadersHook["chat.headers"],
33642
34058
  "chat.message": async (input, output) => {
@@ -33665,6 +34081,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33665
34081
  ${output.system[0]}` : "");
33666
34082
  }
33667
34083
  }
34084
+ sessionGoalHook.handleSystemTransform(input, output);
33668
34085
  collapseSystemInPlace(output.system);
33669
34086
  },
33670
34087
  "experimental.chat.messages.transform": async (input, output) => {