oh-my-opencode-slim 1.0.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/dist/cli/config-io.d.ts +1 -0
- package/dist/cli/index.js +170 -52
- package/dist/goal/index.d.ts +3 -0
- package/dist/goal/manager.d.ts +41 -0
- package/dist/goal/prompts.d.ts +4 -0
- package/dist/goal/store.d.ts +15 -0
- package/dist/goal/types.d.ts +28 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/session-goal/index.d.ts +38 -0
- package/dist/index.js +1135 -369
- package/dist/multiplexer/session-manager.d.ts +3 -1
- package/dist/tools/fork/command.d.ts +28 -0
- package/dist/tools/fork/files.d.ts +33 -0
- package/dist/tools/fork/index.d.ts +10 -0
- package/dist/tools/fork/state.d.ts +7 -0
- package/dist/tools/fork/tools.d.ts +23 -0
- package/dist/tools/fork/vendor.d.ts +28 -0
- package/dist/tools/handoff/command.d.ts +29 -0
- package/dist/tools/handoff/files.d.ts +33 -0
- package/dist/tools/handoff/index.d.ts +10 -0
- package/dist/tools/handoff/state.d.ts +7 -0
- package/dist/tools/handoff/tools.d.ts +23 -0
- package/dist/tools/handoff/vendor.d.ts +28 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/subtask/command.d.ts +30 -0
- package/dist/tools/subtask/files.d.ts +34 -0
- package/dist/tools/subtask/index.d.ts +11 -0
- package/dist/tools/subtask/state.d.ts +7 -0
- package/dist/tools/subtask/tools.d.ts +23 -0
- package/dist/tools/subtask/vendor.d.ts +27 -0
- package/dist/tui.js +56 -0
- package/dist/utils/session.d.ts +2 -1
- package/package.json +1 -1
- package/src/skills/clonedeps/README.md +23 -0
- package/src/skills/clonedeps/SKILL.md +237 -0
- package/src/skills/clonedeps/codemap.md +41 -0
- package/src/skills/codemap.md +8 -5
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++) {
|
|
@@ -18220,6 +18220,12 @@ var CUSTOM_SKILLS = [
|
|
|
18220
18220
|
description: "Repository understanding and hierarchical codemap generation",
|
|
18221
18221
|
allowedAgents: ["orchestrator"],
|
|
18222
18222
|
sourcePath: "src/skills/codemap"
|
|
18223
|
+
},
|
|
18224
|
+
{
|
|
18225
|
+
name: "clonedeps",
|
|
18226
|
+
description: "Clone important dependency source for local inspection",
|
|
18227
|
+
allowedAgents: ["orchestrator"],
|
|
18228
|
+
sourcePath: "src/skills/clonedeps"
|
|
18223
18229
|
}
|
|
18224
18230
|
];
|
|
18225
18231
|
|
|
@@ -18393,6 +18399,56 @@ var CouncilConfigSchema = z.object({
|
|
|
18393
18399
|
import * as fs from "node:fs";
|
|
18394
18400
|
import * as path from "node:path";
|
|
18395
18401
|
|
|
18402
|
+
// src/utils/compat.ts
|
|
18403
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
18404
|
+
import { writeFile as fsWriteFile } from "node:fs/promises";
|
|
18405
|
+
var isBun = typeof globalThis.Bun !== "undefined";
|
|
18406
|
+
function collectStream(stream) {
|
|
18407
|
+
if (!stream)
|
|
18408
|
+
return () => Promise.resolve("");
|
|
18409
|
+
const chunks = [];
|
|
18410
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
18411
|
+
return () => new Promise((resolve, reject) => {
|
|
18412
|
+
if (!stream.readable) {
|
|
18413
|
+
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
18414
|
+
return;
|
|
18415
|
+
}
|
|
18416
|
+
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
18417
|
+
stream.on("error", reject);
|
|
18418
|
+
});
|
|
18419
|
+
}
|
|
18420
|
+
function crossSpawn(command, options) {
|
|
18421
|
+
const [cmd, ...args] = command;
|
|
18422
|
+
const proc = nodeSpawn(cmd, args, {
|
|
18423
|
+
stdio: [
|
|
18424
|
+
options?.stdin ?? "ignore",
|
|
18425
|
+
options?.stdout ?? "pipe",
|
|
18426
|
+
options?.stderr ?? "pipe"
|
|
18427
|
+
],
|
|
18428
|
+
cwd: options?.cwd,
|
|
18429
|
+
env: options?.env
|
|
18430
|
+
});
|
|
18431
|
+
const stdoutCollector = collectStream(proc.stdout);
|
|
18432
|
+
const stderrCollector = collectStream(proc.stderr);
|
|
18433
|
+
const exited = new Promise((resolve, reject) => {
|
|
18434
|
+
proc.on("error", reject);
|
|
18435
|
+
proc.on("close", (code) => resolve(code ?? 1));
|
|
18436
|
+
});
|
|
18437
|
+
return {
|
|
18438
|
+
proc,
|
|
18439
|
+
stdout: stdoutCollector,
|
|
18440
|
+
stderr: stderrCollector,
|
|
18441
|
+
exited,
|
|
18442
|
+
kill: (signal) => proc.kill(signal),
|
|
18443
|
+
get exitCode() {
|
|
18444
|
+
return proc.exitCode;
|
|
18445
|
+
}
|
|
18446
|
+
};
|
|
18447
|
+
}
|
|
18448
|
+
async function crossWrite(path, data) {
|
|
18449
|
+
await fsWriteFile(path, Buffer.from(data));
|
|
18450
|
+
}
|
|
18451
|
+
|
|
18396
18452
|
// src/config/agent-mcps.ts
|
|
18397
18453
|
var DEFAULT_AGENT_MCPS = {
|
|
18398
18454
|
orchestrator: ["*", "!context7"],
|
|
@@ -18842,13 +18898,16 @@ function parseModelReference(model) {
|
|
|
18842
18898
|
modelID: model.slice(slashIndex + 1)
|
|
18843
18899
|
};
|
|
18844
18900
|
}
|
|
18845
|
-
async function promptWithTimeout(client, args, timeoutMs) {
|
|
18901
|
+
async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
18902
|
+
if (signal?.aborted)
|
|
18903
|
+
throw new Error("Prompt cancelled");
|
|
18846
18904
|
if (timeoutMs <= 0) {
|
|
18847
18905
|
await client.session.prompt(args);
|
|
18848
18906
|
return;
|
|
18849
18907
|
}
|
|
18850
18908
|
const sessionId = args.path.id;
|
|
18851
18909
|
let timer;
|
|
18910
|
+
let onAbort;
|
|
18852
18911
|
try {
|
|
18853
18912
|
const promptPromise = client.session.prompt(args);
|
|
18854
18913
|
promptPromise.catch(() => {});
|
|
@@ -18858,6 +18917,16 @@ async function promptWithTimeout(client, args, timeoutMs) {
|
|
|
18858
18917
|
timer = setTimeout(() => {
|
|
18859
18918
|
reject(new OperationTimeoutError(`Prompt timed out after ${timeoutMs}ms`));
|
|
18860
18919
|
}, timeoutMs);
|
|
18920
|
+
}),
|
|
18921
|
+
new Promise((_, reject) => {
|
|
18922
|
+
if (!signal)
|
|
18923
|
+
return;
|
|
18924
|
+
if (signal.aborted) {
|
|
18925
|
+
reject(new Error("Prompt cancelled"));
|
|
18926
|
+
return;
|
|
18927
|
+
}
|
|
18928
|
+
onAbort = () => reject(new Error("Prompt cancelled"));
|
|
18929
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
18861
18930
|
})
|
|
18862
18931
|
]);
|
|
18863
18932
|
} catch (error) {
|
|
@@ -18869,12 +18938,15 @@ async function promptWithTimeout(client, args, timeoutMs) {
|
|
|
18869
18938
|
throw error;
|
|
18870
18939
|
} finally {
|
|
18871
18940
|
clearTimeout(timer);
|
|
18941
|
+
if (onAbort)
|
|
18942
|
+
signal?.removeEventListener("abort", onAbort);
|
|
18872
18943
|
}
|
|
18873
18944
|
}
|
|
18874
18945
|
async function extractSessionResult(client, sessionId, options) {
|
|
18875
18946
|
const includeReasoning = options?.includeReasoning ?? true;
|
|
18876
18947
|
const messagesResult = await client.session.messages({
|
|
18877
|
-
path: { id: sessionId }
|
|
18948
|
+
path: { id: sessionId },
|
|
18949
|
+
...options?.directory ? { query: { directory: options.directory } } : {}
|
|
18878
18950
|
});
|
|
18879
18951
|
const messages = messagesResult.data ?? [];
|
|
18880
18952
|
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
@@ -19030,6 +19102,25 @@ ${enabledParallelExamples}
|
|
|
19030
19102
|
|
|
19031
19103
|
Balance: respect dependencies, avoid parallelizing what must be sequential.
|
|
19032
19104
|
|
|
19105
|
+
### Context Isolation
|
|
19106
|
+
If no specialist delegation is needed, consider \`subtask\` before doing
|
|
19107
|
+
context-heavy work directly.
|
|
19108
|
+
|
|
19109
|
+
Ask whether the parent context needs the details or only the result. Use
|
|
19110
|
+
\`subtask\` when the work is bounded, context-heavy, and the parent only needs a
|
|
19111
|
+
compact outcome.
|
|
19112
|
+
|
|
19113
|
+
Use \`subtask\` for focused investigation, bounded analysis, cleanup, or
|
|
19114
|
+
verification across files/logs/messages.
|
|
19115
|
+
|
|
19116
|
+
Do not use \`subtask\` for tiny tasks, open-ended work, interactive decisions,
|
|
19117
|
+
work better handled by a named specialist, or cases where the parent must reason
|
|
19118
|
+
over the details.
|
|
19119
|
+
|
|
19120
|
+
When calling \`subtask\`, give a self-contained prompt with objective,
|
|
19121
|
+
constraints, relevant context, deliverable, and validation. Pass only clearly
|
|
19122
|
+
relevant files. Wait for the summary, then integrate and verify it.
|
|
19123
|
+
|
|
19033
19124
|
### OpenCode subagent execution model
|
|
19034
19125
|
- A delegated specialist runs in a separate child session.
|
|
19035
19126
|
- Delegation is blocking for the parent at that point: send work out, then continue that line after results return.
|
|
@@ -21966,6 +22057,15 @@ var APPLY_PATCH_RESCUE_OPTIONS = {
|
|
|
21966
22057
|
prefixSuffix: true,
|
|
21967
22058
|
lcsRescue: true
|
|
21968
22059
|
};
|
|
22060
|
+
function replacePatchArgs(output, args, patchText) {
|
|
22061
|
+
const nextArgs = { ...args, patchText };
|
|
22062
|
+
try {
|
|
22063
|
+
output.args = nextArgs;
|
|
22064
|
+
} catch {
|
|
22065
|
+
return false;
|
|
22066
|
+
}
|
|
22067
|
+
return output.args?.patchText === patchText;
|
|
22068
|
+
}
|
|
21969
22069
|
function createApplyPatchHook(ctx) {
|
|
21970
22070
|
function logHookStatus(state, data) {
|
|
21971
22071
|
log(`apply-patch hook ${state}`, data);
|
|
@@ -21985,8 +22085,16 @@ function createApplyPatchHook(ctx) {
|
|
|
21985
22085
|
try {
|
|
21986
22086
|
const result = await rewritePatch(root, patchText, APPLY_PATCH_RESCUE_OPTIONS, worktree);
|
|
21987
22087
|
if (result.changed) {
|
|
21988
|
-
args
|
|
21989
|
-
|
|
22088
|
+
if (replacePatchArgs(output, args, result.patchText)) {
|
|
22089
|
+
logHookStatus("rewrite");
|
|
22090
|
+
} else {
|
|
22091
|
+
logHookStatus("skipped", {
|
|
22092
|
+
reason: "readonly output args",
|
|
22093
|
+
failOpen: true,
|
|
22094
|
+
rescueOptions: APPLY_PATCH_RESCUE_OPTIONS,
|
|
22095
|
+
rewriteStage: "before-native"
|
|
22096
|
+
});
|
|
22097
|
+
}
|
|
21990
22098
|
return;
|
|
21991
22099
|
}
|
|
21992
22100
|
logHookStatus("unchanged");
|
|
@@ -22018,56 +22126,6 @@ function createApplyPatchHook(ctx) {
|
|
|
22018
22126
|
}
|
|
22019
22127
|
};
|
|
22020
22128
|
}
|
|
22021
|
-
// src/utils/compat.ts
|
|
22022
|
-
import { spawn as nodeSpawn } from "node:child_process";
|
|
22023
|
-
import { writeFile as fsWriteFile } from "node:fs/promises";
|
|
22024
|
-
var isBun = typeof globalThis.Bun !== "undefined";
|
|
22025
|
-
function collectStream(stream) {
|
|
22026
|
-
if (!stream)
|
|
22027
|
-
return () => Promise.resolve("");
|
|
22028
|
-
const chunks = [];
|
|
22029
|
-
stream.on("data", (chunk) => chunks.push(chunk));
|
|
22030
|
-
return () => new Promise((resolve, reject) => {
|
|
22031
|
-
if (!stream.readable) {
|
|
22032
|
-
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
22033
|
-
return;
|
|
22034
|
-
}
|
|
22035
|
-
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
22036
|
-
stream.on("error", reject);
|
|
22037
|
-
});
|
|
22038
|
-
}
|
|
22039
|
-
function crossSpawn(command, options) {
|
|
22040
|
-
const [cmd, ...args] = command;
|
|
22041
|
-
const proc = nodeSpawn(cmd, args, {
|
|
22042
|
-
stdio: [
|
|
22043
|
-
options?.stdin ?? "ignore",
|
|
22044
|
-
options?.stdout ?? "pipe",
|
|
22045
|
-
options?.stderr ?? "pipe"
|
|
22046
|
-
],
|
|
22047
|
-
cwd: options?.cwd,
|
|
22048
|
-
env: options?.env
|
|
22049
|
-
});
|
|
22050
|
-
const stdoutCollector = collectStream(proc.stdout);
|
|
22051
|
-
const stderrCollector = collectStream(proc.stderr);
|
|
22052
|
-
const exited = new Promise((resolve, reject) => {
|
|
22053
|
-
proc.on("error", reject);
|
|
22054
|
-
proc.on("close", (code) => resolve(code ?? 1));
|
|
22055
|
-
});
|
|
22056
|
-
return {
|
|
22057
|
-
proc,
|
|
22058
|
-
stdout: stdoutCollector,
|
|
22059
|
-
stderr: stderrCollector,
|
|
22060
|
-
exited,
|
|
22061
|
-
kill: (signal) => proc.kill(signal),
|
|
22062
|
-
get exitCode() {
|
|
22063
|
-
return proc.exitCode;
|
|
22064
|
-
}
|
|
22065
|
-
};
|
|
22066
|
-
}
|
|
22067
|
-
async function crossWrite(path6, data) {
|
|
22068
|
-
await fsWriteFile(path6, Buffer.from(data));
|
|
22069
|
-
}
|
|
22070
|
-
|
|
22071
22129
|
// src/hooks/auto-update-checker/cache.ts
|
|
22072
22130
|
import * as fs5 from "node:fs";
|
|
22073
22131
|
import * as path8 from "node:path";
|
|
@@ -23662,8 +23720,338 @@ function createPostFileToolNudgeHook(options = {}) {
|
|
|
23662
23720
|
}
|
|
23663
23721
|
};
|
|
23664
23722
|
}
|
|
23723
|
+
// src/hooks/session-goal/index.ts
|
|
23724
|
+
import * as fs7 from "node:fs/promises";
|
|
23725
|
+
|
|
23726
|
+
// src/interview/document.ts
|
|
23727
|
+
import * as fsSync from "node:fs";
|
|
23728
|
+
import * as fs6 from "node:fs/promises";
|
|
23729
|
+
import * as path9 from "node:path";
|
|
23730
|
+
var DEFAULT_OUTPUT_FOLDER = "interview";
|
|
23731
|
+
function normalizeOutputFolder(outputFolder) {
|
|
23732
|
+
const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
|
|
23733
|
+
return normalized || DEFAULT_OUTPUT_FOLDER;
|
|
23734
|
+
}
|
|
23735
|
+
function createInterviewDirectoryPath(directory, outputFolder) {
|
|
23736
|
+
return path9.join(directory, normalizeOutputFolder(outputFolder));
|
|
23737
|
+
}
|
|
23738
|
+
function createInterviewFilePath(directory, outputFolder, idea) {
|
|
23739
|
+
const fileName = `${slugify(idea) || "interview"}.md`;
|
|
23740
|
+
return path9.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
|
|
23741
|
+
}
|
|
23742
|
+
function relativeInterviewPath(directory, filePath) {
|
|
23743
|
+
return path9.relative(directory, filePath) || path9.basename(filePath);
|
|
23744
|
+
}
|
|
23745
|
+
function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
23746
|
+
const trimmed = value.trim();
|
|
23747
|
+
if (!trimmed) {
|
|
23748
|
+
return null;
|
|
23749
|
+
}
|
|
23750
|
+
const outputDir = createInterviewDirectoryPath(directory, outputFolder);
|
|
23751
|
+
const candidates = new Set;
|
|
23752
|
+
const resolvedRoot = path9.resolve(directory);
|
|
23753
|
+
if (path9.isAbsolute(trimmed)) {
|
|
23754
|
+
candidates.add(trimmed);
|
|
23755
|
+
} else {
|
|
23756
|
+
candidates.add(path9.resolve(directory, trimmed));
|
|
23757
|
+
candidates.add(path9.join(outputDir, trimmed));
|
|
23758
|
+
if (!trimmed.endsWith(".md")) {
|
|
23759
|
+
candidates.add(path9.join(outputDir, `${trimmed}.md`));
|
|
23760
|
+
}
|
|
23761
|
+
}
|
|
23762
|
+
for (const candidate of candidates) {
|
|
23763
|
+
if (path9.extname(candidate) !== ".md") {
|
|
23764
|
+
continue;
|
|
23765
|
+
}
|
|
23766
|
+
const resolved = path9.resolve(candidate);
|
|
23767
|
+
if (!resolved.startsWith(resolvedRoot + path9.sep) && resolved !== resolvedRoot) {
|
|
23768
|
+
continue;
|
|
23769
|
+
}
|
|
23770
|
+
if (fsSync.existsSync(candidate)) {
|
|
23771
|
+
return candidate;
|
|
23772
|
+
}
|
|
23773
|
+
}
|
|
23774
|
+
return null;
|
|
23775
|
+
}
|
|
23776
|
+
function slugify(value) {
|
|
23777
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
23778
|
+
}
|
|
23779
|
+
function extractHistorySection(document) {
|
|
23780
|
+
const marker = `## Q&A history
|
|
23781
|
+
|
|
23782
|
+
`;
|
|
23783
|
+
const index = document.indexOf(marker);
|
|
23784
|
+
return index >= 0 ? document.slice(index + marker.length).trim() : "";
|
|
23785
|
+
}
|
|
23786
|
+
function extractSummarySection(document) {
|
|
23787
|
+
const marker = `## Current spec
|
|
23788
|
+
|
|
23789
|
+
`;
|
|
23790
|
+
const historyMarker = `
|
|
23791
|
+
|
|
23792
|
+
## Q&A history`;
|
|
23793
|
+
const start = document.indexOf(marker);
|
|
23794
|
+
if (start < 0) {
|
|
23795
|
+
return "";
|
|
23796
|
+
}
|
|
23797
|
+
const summaryStart = start + marker.length;
|
|
23798
|
+
const summaryEnd = document.indexOf(historyMarker, summaryStart);
|
|
23799
|
+
return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
|
|
23800
|
+
}
|
|
23801
|
+
function extractTitle(document) {
|
|
23802
|
+
const match = document.match(/^#\s+(.+)$/m);
|
|
23803
|
+
return match?.[1]?.trim() ?? "";
|
|
23804
|
+
}
|
|
23805
|
+
function buildInterviewDocument(idea, summary, history, meta) {
|
|
23806
|
+
const normalizedSummary = summary.trim() || "Waiting for interview answers.";
|
|
23807
|
+
const normalizedHistory = history.trim() || "No answers yet.";
|
|
23808
|
+
const frontmatter = meta?.sessionID ? [
|
|
23809
|
+
"---",
|
|
23810
|
+
`sessionID: ${meta.sessionID}`,
|
|
23811
|
+
`baseMessageCount: ${meta.baseMessageCount ?? 0}`,
|
|
23812
|
+
`updatedAt: ${new Date().toISOString()}`,
|
|
23813
|
+
"---",
|
|
23814
|
+
""
|
|
23815
|
+
].join(`
|
|
23816
|
+
`) : "";
|
|
23817
|
+
return [
|
|
23818
|
+
frontmatter,
|
|
23819
|
+
`# ${idea}`,
|
|
23820
|
+
"",
|
|
23821
|
+
"## Current spec",
|
|
23822
|
+
"",
|
|
23823
|
+
normalizedSummary,
|
|
23824
|
+
"",
|
|
23825
|
+
"## Q&A history",
|
|
23826
|
+
"",
|
|
23827
|
+
normalizedHistory,
|
|
23828
|
+
""
|
|
23829
|
+
].join(`
|
|
23830
|
+
`);
|
|
23831
|
+
}
|
|
23832
|
+
function parseFrontmatter(content) {
|
|
23833
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
23834
|
+
if (!match)
|
|
23835
|
+
return null;
|
|
23836
|
+
const result = {};
|
|
23837
|
+
for (const line of match[1].split(`
|
|
23838
|
+
`)) {
|
|
23839
|
+
const colonIdx = line.indexOf(":");
|
|
23840
|
+
if (colonIdx > 0) {
|
|
23841
|
+
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
23842
|
+
}
|
|
23843
|
+
}
|
|
23844
|
+
return result;
|
|
23845
|
+
}
|
|
23846
|
+
async function ensureInterviewFile(record) {
|
|
23847
|
+
await fs6.mkdir(path9.dirname(record.markdownPath), { recursive: true });
|
|
23848
|
+
try {
|
|
23849
|
+
await fs6.access(record.markdownPath);
|
|
23850
|
+
} catch {
|
|
23851
|
+
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
|
|
23852
|
+
sessionID: record.sessionID,
|
|
23853
|
+
baseMessageCount: record.baseMessageCount
|
|
23854
|
+
}), "utf8");
|
|
23855
|
+
}
|
|
23856
|
+
}
|
|
23857
|
+
async function readInterviewDocument(record) {
|
|
23858
|
+
try {
|
|
23859
|
+
return await fs6.readFile(record.markdownPath, "utf8");
|
|
23860
|
+
} catch {}
|
|
23861
|
+
await ensureInterviewFile(record);
|
|
23862
|
+
return fs6.readFile(record.markdownPath, "utf8");
|
|
23863
|
+
}
|
|
23864
|
+
async function rewriteInterviewDocument(record, summary) {
|
|
23865
|
+
const existing = await readInterviewDocument(record);
|
|
23866
|
+
const history = extractHistorySection(existing);
|
|
23867
|
+
const next = buildInterviewDocument(record.idea, summary, history, {
|
|
23868
|
+
sessionID: record.sessionID,
|
|
23869
|
+
baseMessageCount: record.baseMessageCount
|
|
23870
|
+
});
|
|
23871
|
+
await fs6.writeFile(record.markdownPath, next, "utf8");
|
|
23872
|
+
return next;
|
|
23873
|
+
}
|
|
23874
|
+
async function appendInterviewAnswers(record, questions, answers) {
|
|
23875
|
+
const existing = await readInterviewDocument(record);
|
|
23876
|
+
const summary = extractSummarySection(existing);
|
|
23877
|
+
const history = extractHistorySection(existing);
|
|
23878
|
+
const questionMap = new Map(questions.map((question) => [question.id, question]));
|
|
23879
|
+
const appended = answers.map((answer) => {
|
|
23880
|
+
const question = questionMap.get(answer.questionId);
|
|
23881
|
+
return question ? `Q: ${question.question}
|
|
23882
|
+
A: ${answer.answer.trim()}` : null;
|
|
23883
|
+
}).filter((value) => value !== null).join(`
|
|
23884
|
+
|
|
23885
|
+
`);
|
|
23886
|
+
const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
|
|
23887
|
+
|
|
23888
|
+
`);
|
|
23889
|
+
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
|
|
23890
|
+
sessionID: record.sessionID,
|
|
23891
|
+
baseMessageCount: record.baseMessageCount
|
|
23892
|
+
}), "utf8");
|
|
23893
|
+
}
|
|
23894
|
+
|
|
23895
|
+
// src/hooks/session-goal/index.ts
|
|
23896
|
+
var COMMAND_NAME = "goal";
|
|
23897
|
+
var MAX_GOAL_LENGTH = 4000;
|
|
23898
|
+
function normalizeGoalText(text) {
|
|
23899
|
+
return text.trim().replace(/\s+/g, " ").slice(0, MAX_GOAL_LENGTH);
|
|
23900
|
+
}
|
|
23901
|
+
function trimGoalText(text) {
|
|
23902
|
+
return text.trim().slice(0, MAX_GOAL_LENGTH);
|
|
23903
|
+
}
|
|
23904
|
+
function pushText(output, text) {
|
|
23905
|
+
output.parts.push(createInternalAgentTextPart(text));
|
|
23906
|
+
}
|
|
23907
|
+
function formatGoal(state, inherited) {
|
|
23908
|
+
const tag = inherited ? "parent_goal" : "active_goal";
|
|
23909
|
+
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.";
|
|
23910
|
+
return `<${tag}>
|
|
23911
|
+
Objective: ${state.text}
|
|
23912
|
+
${guidance}
|
|
23913
|
+
</${tag}>`;
|
|
23914
|
+
}
|
|
23915
|
+
async function readInterviewGoal(directory, outputFolder, value) {
|
|
23916
|
+
try {
|
|
23917
|
+
const sourcePath = resolveExistingInterviewPath(directory, outputFolder, value);
|
|
23918
|
+
if (!sourcePath)
|
|
23919
|
+
return null;
|
|
23920
|
+
const content = await fs7.readFile(sourcePath, "utf8");
|
|
23921
|
+
const title = extractTitle(content);
|
|
23922
|
+
const summary = extractSummarySection(content);
|
|
23923
|
+
const text = trimGoalText([title ? `From interview: ${title}` : "", summary].filter(Boolean).join(`
|
|
23924
|
+
|
|
23925
|
+
`));
|
|
23926
|
+
return text ? { text, sourcePath } : null;
|
|
23927
|
+
} catch {
|
|
23928
|
+
return null;
|
|
23929
|
+
}
|
|
23930
|
+
}
|
|
23931
|
+
function resolveGoal(goals, sessionID) {
|
|
23932
|
+
const seen = new Set;
|
|
23933
|
+
let currentSessionID = sessionID;
|
|
23934
|
+
let inherited = false;
|
|
23935
|
+
while (true) {
|
|
23936
|
+
if (seen.has(currentSessionID)) {
|
|
23937
|
+
goals.delete(sessionID);
|
|
23938
|
+
return null;
|
|
23939
|
+
}
|
|
23940
|
+
seen.add(currentSessionID);
|
|
23941
|
+
const goal = goals.get(currentSessionID);
|
|
23942
|
+
if (!goal) {
|
|
23943
|
+
goals.delete(sessionID);
|
|
23944
|
+
return null;
|
|
23945
|
+
}
|
|
23946
|
+
if (!goal.inheritedFrom) {
|
|
23947
|
+
return { goal, inherited };
|
|
23948
|
+
}
|
|
23949
|
+
inherited = true;
|
|
23950
|
+
currentSessionID = goal.inheritedFrom;
|
|
23951
|
+
}
|
|
23952
|
+
}
|
|
23953
|
+
function createSessionGoalHook(ctx, config, options) {
|
|
23954
|
+
const goals = new Map;
|
|
23955
|
+
const outputFolder = config.interview?.outputFolder ?? "interview";
|
|
23956
|
+
return {
|
|
23957
|
+
registerCommand: (opencodeConfig) => {
|
|
23958
|
+
const commandConfig = opencodeConfig.command;
|
|
23959
|
+
if (commandConfig?.[COMMAND_NAME])
|
|
23960
|
+
return;
|
|
23961
|
+
if (!opencodeConfig.command)
|
|
23962
|
+
opencodeConfig.command = {};
|
|
23963
|
+
opencodeConfig.command[COMMAND_NAME] = {
|
|
23964
|
+
template: "Set or show the current session goal",
|
|
23965
|
+
description: "Pin a session objective that keeps todos, delegation, and verification aligned"
|
|
23966
|
+
};
|
|
23967
|
+
},
|
|
23968
|
+
handleCommandExecuteBefore: async (input, output) => {
|
|
23969
|
+
if (input.command !== COMMAND_NAME)
|
|
23970
|
+
return;
|
|
23971
|
+
output.parts.length = 0;
|
|
23972
|
+
const args = input.arguments.trim();
|
|
23973
|
+
if (!args) {
|
|
23974
|
+
const resolved = resolveGoal(goals, input.sessionID);
|
|
23975
|
+
pushText(output, resolved ? `Active goal:
|
|
23976
|
+
${resolved.goal.text}
|
|
23977
|
+
|
|
23978
|
+
Use todos for execution steps. Auto-continuation continues only while todos remain.` : "No active goal. Set one with /goal <objective>.");
|
|
23979
|
+
return;
|
|
23980
|
+
}
|
|
23981
|
+
if (args === "clear") {
|
|
23982
|
+
goals.delete(input.sessionID);
|
|
23983
|
+
pushText(output, "Cleared the active goal for this session.");
|
|
23984
|
+
return;
|
|
23985
|
+
}
|
|
23986
|
+
if (args.startsWith("from ")) {
|
|
23987
|
+
const value = args.slice("from ".length).trim();
|
|
23988
|
+
const interviewGoal = await readInterviewGoal(ctx.directory, outputFolder, value);
|
|
23989
|
+
if (!interviewGoal) {
|
|
23990
|
+
pushText(output, `Could not find a readable interview spec for "${value}".`);
|
|
23991
|
+
return;
|
|
23992
|
+
}
|
|
23993
|
+
goals.set(input.sessionID, {
|
|
23994
|
+
text: interviewGoal.text,
|
|
23995
|
+
source: "interview",
|
|
23996
|
+
sourcePath: interviewGoal.sourcePath,
|
|
23997
|
+
createdAt: Date.now()
|
|
23998
|
+
});
|
|
23999
|
+
pushText(output, `Set active goal from interview:
|
|
24000
|
+
${interviewGoal.text}`);
|
|
24001
|
+
return;
|
|
24002
|
+
}
|
|
24003
|
+
const text = normalizeGoalText(args);
|
|
24004
|
+
goals.set(input.sessionID, {
|
|
24005
|
+
text,
|
|
24006
|
+
source: "manual",
|
|
24007
|
+
createdAt: Date.now()
|
|
24008
|
+
});
|
|
24009
|
+
pushText(output, `Set active goal:
|
|
24010
|
+
${text}`);
|
|
24011
|
+
},
|
|
24012
|
+
handleEvent: (input) => {
|
|
24013
|
+
const event = input.event;
|
|
24014
|
+
if (event.type === "session.created") {
|
|
24015
|
+
const info = event.properties?.info;
|
|
24016
|
+
if (!info?.id || !info.parentID)
|
|
24017
|
+
return;
|
|
24018
|
+
const parentGoal = goals.get(info.parentID);
|
|
24019
|
+
if (!parentGoal)
|
|
24020
|
+
return;
|
|
24021
|
+
goals.set(info.id, {
|
|
24022
|
+
inheritedFrom: info.parentID,
|
|
24023
|
+
createdAt: Date.now(),
|
|
24024
|
+
text: ""
|
|
24025
|
+
});
|
|
24026
|
+
return;
|
|
24027
|
+
}
|
|
24028
|
+
if (event.type === "session.deleted") {
|
|
24029
|
+
const props = event.properties;
|
|
24030
|
+
const sessionID = props?.info?.id ?? props?.sessionID;
|
|
24031
|
+
if (sessionID)
|
|
24032
|
+
goals.delete(sessionID);
|
|
24033
|
+
}
|
|
24034
|
+
},
|
|
24035
|
+
handleSystemTransform: (input, output) => {
|
|
24036
|
+
if (!input.sessionID)
|
|
24037
|
+
return;
|
|
24038
|
+
const resolved = resolveGoal(goals, input.sessionID);
|
|
24039
|
+
if (!resolved)
|
|
24040
|
+
return;
|
|
24041
|
+
const agentName = options?.getAgentName?.(input.sessionID);
|
|
24042
|
+
const { goal, inherited } = resolved;
|
|
24043
|
+
if (!inherited && agentName && agentName !== "orchestrator")
|
|
24044
|
+
return;
|
|
24045
|
+
const block = formatGoal(goal, inherited);
|
|
24046
|
+
if (output.system.some((entry) => entry.includes(block)))
|
|
24047
|
+
return;
|
|
24048
|
+
output.system.push(block);
|
|
24049
|
+
},
|
|
24050
|
+
getGoal: (sessionID) => resolveGoal(goals, sessionID)?.goal
|
|
24051
|
+
};
|
|
24052
|
+
}
|
|
23665
24053
|
// src/hooks/task-session-manager/index.ts
|
|
23666
|
-
import
|
|
24054
|
+
import path10 from "node:path";
|
|
23667
24055
|
var AGENT_NAME_SET = new Set([
|
|
23668
24056
|
"orchestrator",
|
|
23669
24057
|
"oracle",
|
|
@@ -23688,11 +24076,11 @@ function extractPath(output) {
|
|
|
23688
24076
|
return /<path>([^<]+)<\/path>/.exec(output)?.[1];
|
|
23689
24077
|
}
|
|
23690
24078
|
function normalizePath(root, file) {
|
|
23691
|
-
const
|
|
23692
|
-
if (!
|
|
24079
|
+
const relative2 = path10.relative(root, file);
|
|
24080
|
+
if (!relative2 || relative2.startsWith("..") || path10.isAbsolute(relative2)) {
|
|
23693
24081
|
return file;
|
|
23694
24082
|
}
|
|
23695
|
-
return
|
|
24083
|
+
return relative2;
|
|
23696
24084
|
}
|
|
23697
24085
|
function extractReadFiles(root, output) {
|
|
23698
24086
|
if (typeof output.output !== "string")
|
|
@@ -24080,7 +24468,7 @@ function createTodoHygiene(options) {
|
|
|
24080
24468
|
|
|
24081
24469
|
// src/hooks/todo-continuation/index.ts
|
|
24082
24470
|
var HOOK_NAME = "todo-continuation";
|
|
24083
|
-
var
|
|
24471
|
+
var COMMAND_NAME2 = "auto-continue";
|
|
24084
24472
|
var TODO_STATE_TIMEOUT_MS = 500;
|
|
24085
24473
|
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.]";
|
|
24086
24474
|
var TODO_HYGIENE_INSTRUCTION_OPEN = '<instruction name="todo_hygiene">';
|
|
@@ -24571,7 +24959,7 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24571
24959
|
}
|
|
24572
24960
|
}
|
|
24573
24961
|
async function handleCommandExecuteBefore(input, output) {
|
|
24574
|
-
if (input.command !==
|
|
24962
|
+
if (input.command !== COMMAND_NAME2) {
|
|
24575
24963
|
return;
|
|
24576
24964
|
}
|
|
24577
24965
|
registerOrchestratorSession(input.sessionID);
|
|
@@ -24590,11 +24978,11 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24590
24978
|
if (!newEnabled) {
|
|
24591
24979
|
cancelPendingTimer(state);
|
|
24592
24980
|
output.parts.push(createInternalAgentTextPart("[Auto-continue: disabled by user command.]"));
|
|
24593
|
-
log(`[${HOOK_NAME}] Disabled via /${
|
|
24981
|
+
log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME2} command`);
|
|
24594
24982
|
return;
|
|
24595
24983
|
}
|
|
24596
24984
|
state.suppressUntil = 0;
|
|
24597
|
-
log(`[${HOOK_NAME}] Enabled via /${
|
|
24985
|
+
log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME2} command`, {
|
|
24598
24986
|
maxContinuations
|
|
24599
24987
|
});
|
|
24600
24988
|
let hasIncompleteTodos = false;
|
|
@@ -24628,7 +25016,7 @@ import path13 from "node:path";
|
|
|
24628
25016
|
// src/interview/dashboard.ts
|
|
24629
25017
|
import crypto from "node:crypto";
|
|
24630
25018
|
import * as fsSync2 from "node:fs";
|
|
24631
|
-
import
|
|
25019
|
+
import fs8 from "node:fs/promises";
|
|
24632
25020
|
import {
|
|
24633
25021
|
createServer
|
|
24634
25022
|
} from "node:http";
|
|
@@ -24636,175 +25024,6 @@ import os4 from "node:os";
|
|
|
24636
25024
|
import path11 from "node:path";
|
|
24637
25025
|
import { URL as URL2 } from "node:url";
|
|
24638
25026
|
|
|
24639
|
-
// src/interview/document.ts
|
|
24640
|
-
import * as fsSync from "node:fs";
|
|
24641
|
-
import * as fs6 from "node:fs/promises";
|
|
24642
|
-
import * as path10 from "node:path";
|
|
24643
|
-
var DEFAULT_OUTPUT_FOLDER = "interview";
|
|
24644
|
-
function normalizeOutputFolder(outputFolder) {
|
|
24645
|
-
const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
|
|
24646
|
-
return normalized || DEFAULT_OUTPUT_FOLDER;
|
|
24647
|
-
}
|
|
24648
|
-
function createInterviewDirectoryPath(directory, outputFolder) {
|
|
24649
|
-
return path10.join(directory, normalizeOutputFolder(outputFolder));
|
|
24650
|
-
}
|
|
24651
|
-
function createInterviewFilePath(directory, outputFolder, idea) {
|
|
24652
|
-
const fileName = `${slugify(idea) || "interview"}.md`;
|
|
24653
|
-
return path10.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
|
|
24654
|
-
}
|
|
24655
|
-
function relativeInterviewPath(directory, filePath) {
|
|
24656
|
-
return path10.relative(directory, filePath) || path10.basename(filePath);
|
|
24657
|
-
}
|
|
24658
|
-
function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
24659
|
-
const trimmed = value.trim();
|
|
24660
|
-
if (!trimmed) {
|
|
24661
|
-
return null;
|
|
24662
|
-
}
|
|
24663
|
-
const outputDir = createInterviewDirectoryPath(directory, outputFolder);
|
|
24664
|
-
const candidates = new Set;
|
|
24665
|
-
const resolvedRoot = path10.resolve(directory);
|
|
24666
|
-
if (path10.isAbsolute(trimmed)) {
|
|
24667
|
-
candidates.add(trimmed);
|
|
24668
|
-
} else {
|
|
24669
|
-
candidates.add(path10.resolve(directory, trimmed));
|
|
24670
|
-
candidates.add(path10.join(outputDir, trimmed));
|
|
24671
|
-
if (!trimmed.endsWith(".md")) {
|
|
24672
|
-
candidates.add(path10.join(outputDir, `${trimmed}.md`));
|
|
24673
|
-
}
|
|
24674
|
-
}
|
|
24675
|
-
for (const candidate of candidates) {
|
|
24676
|
-
if (path10.extname(candidate) !== ".md") {
|
|
24677
|
-
continue;
|
|
24678
|
-
}
|
|
24679
|
-
const resolved = path10.resolve(candidate);
|
|
24680
|
-
if (!resolved.startsWith(resolvedRoot + path10.sep) && resolved !== resolvedRoot) {
|
|
24681
|
-
continue;
|
|
24682
|
-
}
|
|
24683
|
-
if (fsSync.existsSync(candidate)) {
|
|
24684
|
-
return candidate;
|
|
24685
|
-
}
|
|
24686
|
-
}
|
|
24687
|
-
return null;
|
|
24688
|
-
}
|
|
24689
|
-
function slugify(value) {
|
|
24690
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
24691
|
-
}
|
|
24692
|
-
function extractHistorySection(document) {
|
|
24693
|
-
const marker = `## Q&A history
|
|
24694
|
-
|
|
24695
|
-
`;
|
|
24696
|
-
const index = document.indexOf(marker);
|
|
24697
|
-
return index >= 0 ? document.slice(index + marker.length).trim() : "";
|
|
24698
|
-
}
|
|
24699
|
-
function extractSummarySection(document) {
|
|
24700
|
-
const marker = `## Current spec
|
|
24701
|
-
|
|
24702
|
-
`;
|
|
24703
|
-
const historyMarker = `
|
|
24704
|
-
|
|
24705
|
-
## Q&A history`;
|
|
24706
|
-
const start = document.indexOf(marker);
|
|
24707
|
-
if (start < 0) {
|
|
24708
|
-
return "";
|
|
24709
|
-
}
|
|
24710
|
-
const summaryStart = start + marker.length;
|
|
24711
|
-
const summaryEnd = document.indexOf(historyMarker, summaryStart);
|
|
24712
|
-
return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
|
|
24713
|
-
}
|
|
24714
|
-
function extractTitle(document) {
|
|
24715
|
-
const match = document.match(/^#\s+(.+)$/m);
|
|
24716
|
-
return match?.[1]?.trim() ?? "";
|
|
24717
|
-
}
|
|
24718
|
-
function buildInterviewDocument(idea, summary, history, meta) {
|
|
24719
|
-
const normalizedSummary = summary.trim() || "Waiting for interview answers.";
|
|
24720
|
-
const normalizedHistory = history.trim() || "No answers yet.";
|
|
24721
|
-
const frontmatter = meta?.sessionID ? [
|
|
24722
|
-
"---",
|
|
24723
|
-
`sessionID: ${meta.sessionID}`,
|
|
24724
|
-
`baseMessageCount: ${meta.baseMessageCount ?? 0}`,
|
|
24725
|
-
`updatedAt: ${new Date().toISOString()}`,
|
|
24726
|
-
"---",
|
|
24727
|
-
""
|
|
24728
|
-
].join(`
|
|
24729
|
-
`) : "";
|
|
24730
|
-
return [
|
|
24731
|
-
frontmatter,
|
|
24732
|
-
`# ${idea}`,
|
|
24733
|
-
"",
|
|
24734
|
-
"## Current spec",
|
|
24735
|
-
"",
|
|
24736
|
-
normalizedSummary,
|
|
24737
|
-
"",
|
|
24738
|
-
"## Q&A history",
|
|
24739
|
-
"",
|
|
24740
|
-
normalizedHistory,
|
|
24741
|
-
""
|
|
24742
|
-
].join(`
|
|
24743
|
-
`);
|
|
24744
|
-
}
|
|
24745
|
-
function parseFrontmatter(content) {
|
|
24746
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
24747
|
-
if (!match)
|
|
24748
|
-
return null;
|
|
24749
|
-
const result = {};
|
|
24750
|
-
for (const line of match[1].split(`
|
|
24751
|
-
`)) {
|
|
24752
|
-
const colonIdx = line.indexOf(":");
|
|
24753
|
-
if (colonIdx > 0) {
|
|
24754
|
-
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
24755
|
-
}
|
|
24756
|
-
}
|
|
24757
|
-
return result;
|
|
24758
|
-
}
|
|
24759
|
-
async function ensureInterviewFile(record) {
|
|
24760
|
-
await fs6.mkdir(path10.dirname(record.markdownPath), { recursive: true });
|
|
24761
|
-
try {
|
|
24762
|
-
await fs6.access(record.markdownPath);
|
|
24763
|
-
} catch {
|
|
24764
|
-
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
|
|
24765
|
-
sessionID: record.sessionID,
|
|
24766
|
-
baseMessageCount: record.baseMessageCount
|
|
24767
|
-
}), "utf8");
|
|
24768
|
-
}
|
|
24769
|
-
}
|
|
24770
|
-
async function readInterviewDocument(record) {
|
|
24771
|
-
try {
|
|
24772
|
-
return await fs6.readFile(record.markdownPath, "utf8");
|
|
24773
|
-
} catch {}
|
|
24774
|
-
await ensureInterviewFile(record);
|
|
24775
|
-
return fs6.readFile(record.markdownPath, "utf8");
|
|
24776
|
-
}
|
|
24777
|
-
async function rewriteInterviewDocument(record, summary) {
|
|
24778
|
-
const existing = await readInterviewDocument(record);
|
|
24779
|
-
const history = extractHistorySection(existing);
|
|
24780
|
-
const next = buildInterviewDocument(record.idea, summary, history, {
|
|
24781
|
-
sessionID: record.sessionID,
|
|
24782
|
-
baseMessageCount: record.baseMessageCount
|
|
24783
|
-
});
|
|
24784
|
-
await fs6.writeFile(record.markdownPath, next, "utf8");
|
|
24785
|
-
return next;
|
|
24786
|
-
}
|
|
24787
|
-
async function appendInterviewAnswers(record, questions, answers) {
|
|
24788
|
-
const existing = await readInterviewDocument(record);
|
|
24789
|
-
const summary = extractSummarySection(existing);
|
|
24790
|
-
const history = extractHistorySection(existing);
|
|
24791
|
-
const questionMap = new Map(questions.map((question) => [question.id, question]));
|
|
24792
|
-
const appended = answers.map((answer) => {
|
|
24793
|
-
const question = questionMap.get(answer.questionId);
|
|
24794
|
-
return question ? `Q: ${question.question}
|
|
24795
|
-
A: ${answer.answer.trim()}` : null;
|
|
24796
|
-
}).filter((value) => value !== null).join(`
|
|
24797
|
-
|
|
24798
|
-
`);
|
|
24799
|
-
const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
|
|
24800
|
-
|
|
24801
|
-
`);
|
|
24802
|
-
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
|
|
24803
|
-
sessionID: record.sessionID,
|
|
24804
|
-
baseMessageCount: record.baseMessageCount
|
|
24805
|
-
}), "utf8");
|
|
24806
|
-
}
|
|
24807
|
-
|
|
24808
25027
|
// src/interview/helpers.ts
|
|
24809
25028
|
function sendJson(response, status, value) {
|
|
24810
25029
|
response.statusCode = status;
|
|
@@ -26449,7 +26668,7 @@ function removeAuthFile(port) {
|
|
|
26449
26668
|
}
|
|
26450
26669
|
async function readDashboardAuthFile(port) {
|
|
26451
26670
|
try {
|
|
26452
|
-
const content = await
|
|
26671
|
+
const content = await fs8.readFile(getAuthFilePath(port), "utf8");
|
|
26453
26672
|
const data = JSON.parse(content);
|
|
26454
26673
|
try {
|
|
26455
26674
|
process.kill(data.pid, 0);
|
|
@@ -26572,7 +26791,7 @@ function createDashboardServer(config) {
|
|
|
26572
26791
|
const interviewDir = path11.join(dir, config.outputFolder);
|
|
26573
26792
|
let entries;
|
|
26574
26793
|
try {
|
|
26575
|
-
entries = await
|
|
26794
|
+
entries = await fs8.readdir(interviewDir);
|
|
26576
26795
|
} catch {
|
|
26577
26796
|
continue;
|
|
26578
26797
|
}
|
|
@@ -26581,7 +26800,7 @@ function createDashboardServer(config) {
|
|
|
26581
26800
|
continue;
|
|
26582
26801
|
let content;
|
|
26583
26802
|
try {
|
|
26584
|
-
content = await
|
|
26803
|
+
content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
|
|
26585
26804
|
} catch {
|
|
26586
26805
|
continue;
|
|
26587
26806
|
}
|
|
@@ -26610,7 +26829,7 @@ function createDashboardServer(config) {
|
|
|
26610
26829
|
const interviewDir = path11.join(dir, config.outputFolder);
|
|
26611
26830
|
let entries;
|
|
26612
26831
|
try {
|
|
26613
|
-
entries = await
|
|
26832
|
+
entries = await fs8.readdir(interviewDir);
|
|
26614
26833
|
} catch {
|
|
26615
26834
|
continue;
|
|
26616
26835
|
}
|
|
@@ -26619,7 +26838,7 @@ function createDashboardServer(config) {
|
|
|
26619
26838
|
continue;
|
|
26620
26839
|
let content;
|
|
26621
26840
|
try {
|
|
26622
|
-
content = await
|
|
26841
|
+
content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
|
|
26623
26842
|
} catch {
|
|
26624
26843
|
continue;
|
|
26625
26844
|
}
|
|
@@ -26903,7 +27122,7 @@ function createDashboardServer(config) {
|
|
|
26903
27122
|
let markdownPath = entry.filePath;
|
|
26904
27123
|
if (entry.filePath) {
|
|
26905
27124
|
try {
|
|
26906
|
-
document = await
|
|
27125
|
+
document = await fs8.readFile(entry.filePath, "utf8");
|
|
26907
27126
|
} catch {}
|
|
26908
27127
|
} else {
|
|
26909
27128
|
const dirs = getKnownDirectories();
|
|
@@ -26911,7 +27130,7 @@ function createDashboardServer(config) {
|
|
|
26911
27130
|
const slug = extractResumeSlug(interviewId);
|
|
26912
27131
|
const candidate = path11.join(dir, config.outputFolder, `${slug}.md`);
|
|
26913
27132
|
try {
|
|
26914
|
-
document = await
|
|
27133
|
+
document = await fs8.readFile(candidate, "utf8");
|
|
26915
27134
|
markdownPath = candidate;
|
|
26916
27135
|
entry.filePath = candidate;
|
|
26917
27136
|
break;
|
|
@@ -27436,7 +27655,7 @@ function createInterviewServer(deps) {
|
|
|
27436
27655
|
|
|
27437
27656
|
// src/interview/service.ts
|
|
27438
27657
|
import { spawn as spawn2 } from "node:child_process";
|
|
27439
|
-
import * as
|
|
27658
|
+
import * as fs9 from "node:fs/promises";
|
|
27440
27659
|
import * as path12 from "node:path";
|
|
27441
27660
|
|
|
27442
27661
|
// src/interview/types.ts
|
|
@@ -27609,7 +27828,7 @@ function buildAnswerPrompt(answers, questions, maxQuestions) {
|
|
|
27609
27828
|
}
|
|
27610
27829
|
|
|
27611
27830
|
// src/interview/service.ts
|
|
27612
|
-
var
|
|
27831
|
+
var COMMAND_NAME3 = "interview";
|
|
27613
27832
|
var DEFAULT_MAX_QUESTIONS = 2;
|
|
27614
27833
|
function isTruthyEnvFlag(value) {
|
|
27615
27834
|
if (!value) {
|
|
@@ -27711,11 +27930,11 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27711
27930
|
const dir = path12.dirname(interview.markdownPath);
|
|
27712
27931
|
const newPath = path12.join(dir, `${newSlug}.md`);
|
|
27713
27932
|
try {
|
|
27714
|
-
await
|
|
27933
|
+
await fs9.access(newPath);
|
|
27715
27934
|
return;
|
|
27716
27935
|
} catch {}
|
|
27717
27936
|
try {
|
|
27718
|
-
await
|
|
27937
|
+
await fs9.rename(interview.markdownPath, newPath);
|
|
27719
27938
|
interview.markdownPath = newPath;
|
|
27720
27939
|
log("[interview] renamed file with assistant title:", {
|
|
27721
27940
|
from: currentFileName,
|
|
@@ -27781,7 +28000,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27781
28000
|
active.status = "abandoned";
|
|
27782
28001
|
}
|
|
27783
28002
|
}
|
|
27784
|
-
const document = await
|
|
28003
|
+
const document = await fs9.readFile(markdownPath, "utf8");
|
|
27785
28004
|
const messages = await loadMessages(sessionID);
|
|
27786
28005
|
const title = extractTitle(document);
|
|
27787
28006
|
const record = {
|
|
@@ -27856,11 +28075,11 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27856
28075
|
}
|
|
27857
28076
|
function registerCommand(opencodeConfig) {
|
|
27858
28077
|
const configCommand = opencodeConfig.command;
|
|
27859
|
-
if (!configCommand?.[
|
|
28078
|
+
if (!configCommand?.[COMMAND_NAME3]) {
|
|
27860
28079
|
if (!opencodeConfig.command) {
|
|
27861
28080
|
opencodeConfig.command = {};
|
|
27862
28081
|
}
|
|
27863
|
-
opencodeConfig.command[
|
|
28082
|
+
opencodeConfig.command[COMMAND_NAME3] = {
|
|
27864
28083
|
template: "Start an interview and write a live markdown spec",
|
|
27865
28084
|
description: "Open a localhost interview UI linked to the current OpenCode session"
|
|
27866
28085
|
};
|
|
@@ -27934,7 +28153,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27934
28153
|
}
|
|
27935
28154
|
}
|
|
27936
28155
|
async function handleCommandExecuteBefore(input, output) {
|
|
27937
|
-
if (input.command !==
|
|
28156
|
+
if (input.command !== COMMAND_NAME3) {
|
|
27938
28157
|
return;
|
|
27939
28158
|
}
|
|
27940
28159
|
const idea = input.arguments.trim();
|
|
@@ -27953,7 +28172,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27953
28172
|
const resumePath = resolveExistingInterviewPath(ctx.directory, outputFolder, idea);
|
|
27954
28173
|
if (resumePath) {
|
|
27955
28174
|
const interview2 = await resumeInterview(input.sessionID, resumePath);
|
|
27956
|
-
const document = await
|
|
28175
|
+
const document = await fs9.readFile(interview2.markdownPath, "utf8");
|
|
27957
28176
|
await notifyInterviewUrl(input.sessionID, interview2);
|
|
27958
28177
|
output.parts.push(createInternalAgentTextPart(buildResumePrompt(document, maxQuestions)));
|
|
27959
28178
|
return;
|
|
@@ -28017,7 +28236,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
28017
28236
|
const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path12.resolve(i.markdownPath)));
|
|
28018
28237
|
let entries;
|
|
28019
28238
|
try {
|
|
28020
|
-
entries = await
|
|
28239
|
+
entries = await fs9.readdir(outputDir);
|
|
28021
28240
|
} catch {
|
|
28022
28241
|
return [];
|
|
28023
28242
|
}
|
|
@@ -28030,7 +28249,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
28030
28249
|
continue;
|
|
28031
28250
|
let content;
|
|
28032
28251
|
try {
|
|
28033
|
-
content = await
|
|
28252
|
+
content = await fs9.readFile(fullPath, "utf8");
|
|
28034
28253
|
} catch {
|
|
28035
28254
|
continue;
|
|
28036
28255
|
}
|
|
@@ -28644,7 +28863,11 @@ class TmuxMultiplexer {
|
|
|
28644
28863
|
this.storedLayout = layout;
|
|
28645
28864
|
this.storedMainPaneSize = mainPaneSize;
|
|
28646
28865
|
try {
|
|
28647
|
-
const layoutResult = await this.runTmux(tmux, [
|
|
28866
|
+
const layoutResult = await this.runTmux(tmux, [
|
|
28867
|
+
"select-layout",
|
|
28868
|
+
...this.targetArgs(),
|
|
28869
|
+
layout
|
|
28870
|
+
]);
|
|
28648
28871
|
if (layoutResult !== 0)
|
|
28649
28872
|
return;
|
|
28650
28873
|
if (layout === "main-horizontal" || layout === "main-vertical") {
|
|
@@ -28657,7 +28880,11 @@ class TmuxMultiplexer {
|
|
|
28657
28880
|
]);
|
|
28658
28881
|
if (sizeResult !== 0)
|
|
28659
28882
|
return;
|
|
28660
|
-
const reapplyResult = await this.runTmux(tmux, [
|
|
28883
|
+
const reapplyResult = await this.runTmux(tmux, [
|
|
28884
|
+
"select-layout",
|
|
28885
|
+
...this.targetArgs(),
|
|
28886
|
+
layout
|
|
28887
|
+
]);
|
|
28661
28888
|
if (reapplyResult !== 0)
|
|
28662
28889
|
return;
|
|
28663
28890
|
}
|
|
@@ -29095,29 +29322,46 @@ function startAvailabilityCheck(config) {
|
|
|
29095
29322
|
// src/multiplexer/session-manager.ts
|
|
29096
29323
|
var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
|
|
29097
29324
|
var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
|
|
29098
|
-
|
|
29325
|
+
var SHARED_STATE_KEY = Symbol.for("oh-my-opencode-slim.multiplexer-session-manager.state");
|
|
29326
|
+
function getSharedState() {
|
|
29327
|
+
const globalWithState = globalThis;
|
|
29328
|
+
globalWithState[SHARED_STATE_KEY] ??= {
|
|
29329
|
+
sessions: new Map,
|
|
29330
|
+
knownSessions: new Map,
|
|
29331
|
+
spawningSessions: new Set,
|
|
29332
|
+
closingSessions: new Map
|
|
29333
|
+
};
|
|
29334
|
+
return globalWithState[SHARED_STATE_KEY];
|
|
29335
|
+
}
|
|
29099
29336
|
class MultiplexerSessionManager {
|
|
29100
|
-
|
|
29337
|
+
instanceId = Math.random().toString(36).slice(2, 8);
|
|
29101
29338
|
serverUrl;
|
|
29102
29339
|
directory;
|
|
29103
29340
|
multiplexer = null;
|
|
29104
|
-
sessions
|
|
29105
|
-
knownSessions
|
|
29106
|
-
spawningSessions
|
|
29107
|
-
closingSessions
|
|
29341
|
+
sessions;
|
|
29342
|
+
knownSessions;
|
|
29343
|
+
spawningSessions;
|
|
29344
|
+
closingSessions;
|
|
29108
29345
|
pollInterval;
|
|
29109
29346
|
enabled = false;
|
|
29110
29347
|
constructor(ctx, config) {
|
|
29111
|
-
|
|
29348
|
+
const sharedState = getSharedState();
|
|
29349
|
+
this.sessions = sharedState.sessions;
|
|
29350
|
+
this.knownSessions = sharedState.knownSessions;
|
|
29351
|
+
this.spawningSessions = sharedState.spawningSessions;
|
|
29352
|
+
this.closingSessions = sharedState.closingSessions;
|
|
29112
29353
|
this.directory = ctx.directory;
|
|
29113
29354
|
const defaultPort = process.env.OPENCODE_PORT ?? "4096";
|
|
29114
29355
|
this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
|
|
29115
29356
|
this.multiplexer = getMultiplexer(config);
|
|
29116
29357
|
this.enabled = config.type !== "none" && this.multiplexer !== null && this.multiplexer.isInsideSession();
|
|
29117
29358
|
log("[multiplexer-session-manager] initialized", {
|
|
29359
|
+
instanceId: this.instanceId,
|
|
29118
29360
|
enabled: this.enabled,
|
|
29119
29361
|
type: config.type,
|
|
29120
|
-
serverUrl: this.serverUrl
|
|
29362
|
+
serverUrl: this.serverUrl,
|
|
29363
|
+
trackedSessions: this.sessions.size,
|
|
29364
|
+
knownSessions: this.knownSessions.size
|
|
29121
29365
|
});
|
|
29122
29366
|
}
|
|
29123
29367
|
async onSessionCreated(event) {
|
|
@@ -29135,6 +29379,7 @@ class MultiplexerSessionManager {
|
|
|
29135
29379
|
const directory = info.directory ?? this.directory;
|
|
29136
29380
|
if (this.isTrackedOrSpawning(sessionId)) {
|
|
29137
29381
|
log("[multiplexer-session-manager] session already tracked or spawning", {
|
|
29382
|
+
instanceId: this.instanceId,
|
|
29138
29383
|
sessionId
|
|
29139
29384
|
});
|
|
29140
29385
|
return;
|
|
@@ -29154,6 +29399,7 @@ class MultiplexerSessionManager {
|
|
|
29154
29399
|
const serverRunning = await isServerRunning(this.serverUrl);
|
|
29155
29400
|
if (!serverRunning) {
|
|
29156
29401
|
log("[multiplexer-session-manager] server not running, skipping", {
|
|
29402
|
+
instanceId: this.instanceId,
|
|
29157
29403
|
serverUrl: this.serverUrl
|
|
29158
29404
|
});
|
|
29159
29405
|
return;
|
|
@@ -29164,10 +29410,12 @@ class MultiplexerSessionManager {
|
|
|
29164
29410
|
log("[multiplexer-session-manager] child session created, spawning pane", {
|
|
29165
29411
|
sessionId,
|
|
29166
29412
|
parentId,
|
|
29167
|
-
title
|
|
29413
|
+
title,
|
|
29414
|
+
instanceId: this.instanceId
|
|
29168
29415
|
});
|
|
29169
29416
|
const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl, directory).catch((err) => {
|
|
29170
29417
|
log("[multiplexer-session-manager] failed to spawn pane", {
|
|
29418
|
+
instanceId: this.instanceId,
|
|
29171
29419
|
error: String(err)
|
|
29172
29420
|
});
|
|
29173
29421
|
return { success: false, paneId: undefined };
|
|
@@ -29178,6 +29426,7 @@ class MultiplexerSessionManager {
|
|
|
29178
29426
|
await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale spawned pane failed", {
|
|
29179
29427
|
sessionId,
|
|
29180
29428
|
paneId: paneResult.paneId,
|
|
29429
|
+
instanceId: this.instanceId,
|
|
29181
29430
|
error: String(err)
|
|
29182
29431
|
}));
|
|
29183
29432
|
return;
|
|
@@ -29193,6 +29442,7 @@ class MultiplexerSessionManager {
|
|
|
29193
29442
|
lastSeenAt: now
|
|
29194
29443
|
});
|
|
29195
29444
|
log("[multiplexer-session-manager] pane spawned", {
|
|
29445
|
+
instanceId: this.instanceId,
|
|
29196
29446
|
sessionId,
|
|
29197
29447
|
paneId: paneResult.paneId
|
|
29198
29448
|
});
|
|
@@ -29204,6 +29454,19 @@ class MultiplexerSessionManager {
|
|
|
29204
29454
|
async onSessionStatus(event) {
|
|
29205
29455
|
if (!this.enabled)
|
|
29206
29456
|
return;
|
|
29457
|
+
if (event.type === "session.idle") {
|
|
29458
|
+
const sessionId2 = event.properties?.sessionID;
|
|
29459
|
+
if (!sessionId2)
|
|
29460
|
+
return;
|
|
29461
|
+
log("[multiplexer-session-manager] session idle event received", {
|
|
29462
|
+
instanceId: this.instanceId,
|
|
29463
|
+
sessionId: sessionId2,
|
|
29464
|
+
tracked: this.sessions.has(sessionId2),
|
|
29465
|
+
known: this.knownSessions.has(sessionId2)
|
|
29466
|
+
});
|
|
29467
|
+
await this.closeSession(sessionId2, "idle");
|
|
29468
|
+
return;
|
|
29469
|
+
}
|
|
29207
29470
|
if (event.type !== "session.status")
|
|
29208
29471
|
return;
|
|
29209
29472
|
const sessionId = event.properties?.sessionID;
|
|
@@ -29214,6 +29477,12 @@ class MultiplexerSessionManager {
|
|
|
29214
29477
|
return;
|
|
29215
29478
|
}
|
|
29216
29479
|
if (event.properties?.status?.type === "busy") {
|
|
29480
|
+
log("[multiplexer-session-manager] session busy event received", {
|
|
29481
|
+
instanceId: this.instanceId,
|
|
29482
|
+
sessionId,
|
|
29483
|
+
tracked: this.sessions.has(sessionId),
|
|
29484
|
+
known: this.knownSessions.has(sessionId)
|
|
29485
|
+
});
|
|
29217
29486
|
await this.respawnIfKnown(sessionId);
|
|
29218
29487
|
}
|
|
29219
29488
|
}
|
|
@@ -29226,6 +29495,7 @@ class MultiplexerSessionManager {
|
|
|
29226
29495
|
if (!sessionId)
|
|
29227
29496
|
return;
|
|
29228
29497
|
log("[multiplexer-session-manager] session deleted, closing pane", {
|
|
29498
|
+
instanceId: this.instanceId,
|
|
29229
29499
|
sessionId
|
|
29230
29500
|
});
|
|
29231
29501
|
await this.closeSession(sessionId, "deleted");
|
|
@@ -29234,13 +29504,17 @@ class MultiplexerSessionManager {
|
|
|
29234
29504
|
if (this.pollInterval)
|
|
29235
29505
|
return;
|
|
29236
29506
|
this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
|
|
29237
|
-
log("[multiplexer-session-manager] polling started"
|
|
29507
|
+
log("[multiplexer-session-manager] polling started", {
|
|
29508
|
+
instanceId: this.instanceId
|
|
29509
|
+
});
|
|
29238
29510
|
}
|
|
29239
29511
|
stopPolling() {
|
|
29240
29512
|
if (this.pollInterval) {
|
|
29241
29513
|
clearInterval(this.pollInterval);
|
|
29242
29514
|
this.pollInterval = undefined;
|
|
29243
|
-
log("[multiplexer-session-manager] polling stopped"
|
|
29515
|
+
log("[multiplexer-session-manager] polling stopped", {
|
|
29516
|
+
instanceId: this.instanceId
|
|
29517
|
+
});
|
|
29244
29518
|
}
|
|
29245
29519
|
}
|
|
29246
29520
|
async pollSessions() {
|
|
@@ -29249,8 +29523,7 @@ class MultiplexerSessionManager {
|
|
|
29249
29523
|
return;
|
|
29250
29524
|
}
|
|
29251
29525
|
try {
|
|
29252
|
-
const
|
|
29253
|
-
const allStatuses = statusResult.data ?? {};
|
|
29526
|
+
const allStatuses = await this.fetchSessionStatuses();
|
|
29254
29527
|
const now = Date.now();
|
|
29255
29528
|
const sessionsToClose = [];
|
|
29256
29529
|
for (const [sessionId, tracked] of this.sessions.entries()) {
|
|
@@ -29278,6 +29551,14 @@ class MultiplexerSessionManager {
|
|
|
29278
29551
|
log("[multiplexer-session-manager] poll error", { error: String(err) });
|
|
29279
29552
|
}
|
|
29280
29553
|
}
|
|
29554
|
+
async fetchSessionStatuses() {
|
|
29555
|
+
const url = new URL("/session/status", this.serverUrl);
|
|
29556
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
|
|
29557
|
+
if (!response.ok) {
|
|
29558
|
+
throw new Error(`session status request failed: ${response.status} ${response.statusText}`);
|
|
29559
|
+
}
|
|
29560
|
+
return await response.json();
|
|
29561
|
+
}
|
|
29281
29562
|
async closeSession(sessionId, reason) {
|
|
29282
29563
|
if (reason === "deleted") {
|
|
29283
29564
|
this.knownSessions.delete(sessionId);
|
|
@@ -29286,10 +29567,19 @@ class MultiplexerSessionManager {
|
|
|
29286
29567
|
if (existingClose)
|
|
29287
29568
|
return existingClose;
|
|
29288
29569
|
const tracked = this.sessions.get(sessionId);
|
|
29289
|
-
if (!tracked || !this.multiplexer)
|
|
29570
|
+
if (!tracked || !this.multiplexer) {
|
|
29571
|
+
log("[multiplexer-session-manager] close skipped; session not tracked", {
|
|
29572
|
+
instanceId: this.instanceId,
|
|
29573
|
+
sessionId,
|
|
29574
|
+
reason,
|
|
29575
|
+
tracked: !!tracked,
|
|
29576
|
+
hasMultiplexer: !!this.multiplexer
|
|
29577
|
+
});
|
|
29290
29578
|
return;
|
|
29579
|
+
}
|
|
29291
29580
|
this.sessions.delete(sessionId);
|
|
29292
29581
|
log("[multiplexer-session-manager] closing session pane", {
|
|
29582
|
+
instanceId: this.instanceId,
|
|
29293
29583
|
sessionId,
|
|
29294
29584
|
paneId: tracked.paneId,
|
|
29295
29585
|
reason
|
|
@@ -29297,6 +29587,7 @@ class MultiplexerSessionManager {
|
|
|
29297
29587
|
const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
|
|
29298
29588
|
return;
|
|
29299
29589
|
}).catch((err) => log("[multiplexer-session-manager] failed to close session pane", {
|
|
29590
|
+
instanceId: this.instanceId,
|
|
29300
29591
|
sessionId,
|
|
29301
29592
|
paneId: tracked.paneId,
|
|
29302
29593
|
reason,
|
|
@@ -29325,6 +29616,7 @@ class MultiplexerSessionManager {
|
|
|
29325
29616
|
const serverRunning = await isServerRunning(this.serverUrl);
|
|
29326
29617
|
if (!serverRunning) {
|
|
29327
29618
|
log("[multiplexer-session-manager] server not running, skipping busy respawn", {
|
|
29619
|
+
instanceId: this.instanceId,
|
|
29328
29620
|
serverUrl: this.serverUrl,
|
|
29329
29621
|
sessionId
|
|
29330
29622
|
});
|
|
@@ -29334,12 +29626,14 @@ class MultiplexerSessionManager {
|
|
|
29334
29626
|
return;
|
|
29335
29627
|
}
|
|
29336
29628
|
log("[multiplexer-session-manager] child session busy again, respawning pane", {
|
|
29629
|
+
instanceId: this.instanceId,
|
|
29337
29630
|
sessionId,
|
|
29338
29631
|
parentId: known.parentId,
|
|
29339
29632
|
title: known.title
|
|
29340
29633
|
});
|
|
29341
29634
|
const paneResult = await this.multiplexer.spawnPane(sessionId, known.title, this.serverUrl, known.directory).catch((err) => {
|
|
29342
29635
|
log("[multiplexer-session-manager] failed to respawn pane", {
|
|
29636
|
+
instanceId: this.instanceId,
|
|
29343
29637
|
error: String(err)
|
|
29344
29638
|
});
|
|
29345
29639
|
return { success: false, paneId: undefined };
|
|
@@ -29348,6 +29642,7 @@ class MultiplexerSessionManager {
|
|
|
29348
29642
|
return;
|
|
29349
29643
|
if (!this.knownSessions.has(sessionId) || this.closingSessions.has(sessionId)) {
|
|
29350
29644
|
await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale respawned pane failed", {
|
|
29645
|
+
instanceId: this.instanceId,
|
|
29351
29646
|
sessionId,
|
|
29352
29647
|
paneId: paneResult.paneId,
|
|
29353
29648
|
error: String(err)
|
|
@@ -29365,6 +29660,7 @@ class MultiplexerSessionManager {
|
|
|
29365
29660
|
lastSeenAt: now
|
|
29366
29661
|
});
|
|
29367
29662
|
log("[multiplexer-session-manager] pane respawned on busy", {
|
|
29663
|
+
instanceId: this.instanceId,
|
|
29368
29664
|
sessionId,
|
|
29369
29665
|
paneId: paneResult.paneId
|
|
29370
29666
|
});
|
|
@@ -30043,7 +30339,7 @@ Returns the councillor responses with a summary footer.`,
|
|
|
30043
30339
|
return { council_session };
|
|
30044
30340
|
}
|
|
30045
30341
|
// src/tui-state.ts
|
|
30046
|
-
import * as
|
|
30342
|
+
import * as fs10 from "node:fs";
|
|
30047
30343
|
import * as os5 from "node:os";
|
|
30048
30344
|
import * as path14 from "node:path";
|
|
30049
30345
|
var STATE_DIR = "oh-my-opencode-slim";
|
|
@@ -30073,14 +30369,14 @@ function parseSnapshot(value) {
|
|
|
30073
30369
|
}
|
|
30074
30370
|
function readTuiSnapshot() {
|
|
30075
30371
|
try {
|
|
30076
|
-
return parseSnapshot(
|
|
30372
|
+
return parseSnapshot(fs10.readFileSync(getTuiStatePath(), "utf8"));
|
|
30077
30373
|
} catch {
|
|
30078
30374
|
return emptySnapshot();
|
|
30079
30375
|
}
|
|
30080
30376
|
}
|
|
30081
30377
|
async function readTuiSnapshotAsync() {
|
|
30082
30378
|
try {
|
|
30083
|
-
return parseSnapshot(await
|
|
30379
|
+
return parseSnapshot(await fs10.promises.readFile(getTuiStatePath(), "utf8"));
|
|
30084
30380
|
} catch {
|
|
30085
30381
|
return emptySnapshot();
|
|
30086
30382
|
}
|
|
@@ -30088,8 +30384,8 @@ async function readTuiSnapshotAsync() {
|
|
|
30088
30384
|
function writeTuiSnapshot(snapshot) {
|
|
30089
30385
|
try {
|
|
30090
30386
|
const filePath = getTuiStatePath();
|
|
30091
|
-
|
|
30092
|
-
|
|
30387
|
+
fs10.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
30388
|
+
fs10.writeFileSync(filePath, `${JSON.stringify(snapshot)}
|
|
30093
30389
|
`);
|
|
30094
30390
|
} catch {}
|
|
30095
30391
|
}
|
|
@@ -30111,11 +30407,11 @@ function recordTuiAgentModel(input) {
|
|
|
30111
30407
|
}
|
|
30112
30408
|
|
|
30113
30409
|
// src/tools/preset-manager.ts
|
|
30114
|
-
var
|
|
30410
|
+
var COMMAND_NAME4 = "preset";
|
|
30115
30411
|
function createPresetManager(ctx, config) {
|
|
30116
30412
|
let activePreset = getActiveRuntimePreset() ?? config.preset ?? null;
|
|
30117
30413
|
async function handleCommandExecuteBefore(input, output) {
|
|
30118
|
-
if (input.command !==
|
|
30414
|
+
if (input.command !== COMMAND_NAME4) {
|
|
30119
30415
|
return;
|
|
30120
30416
|
}
|
|
30121
30417
|
output.parts.length = 0;
|
|
@@ -30134,11 +30430,11 @@ function createPresetManager(ctx, config) {
|
|
|
30134
30430
|
}
|
|
30135
30431
|
function registerCommand(opencodeConfig) {
|
|
30136
30432
|
const configCommand = opencodeConfig.command;
|
|
30137
|
-
if (!configCommand?.[
|
|
30433
|
+
if (!configCommand?.[COMMAND_NAME4]) {
|
|
30138
30434
|
if (!opencodeConfig.command) {
|
|
30139
30435
|
opencodeConfig.command = {};
|
|
30140
30436
|
}
|
|
30141
|
-
opencodeConfig.command[
|
|
30437
|
+
opencodeConfig.command[COMMAND_NAME4] = {
|
|
30142
30438
|
template: "List available presets and switch between them",
|
|
30143
30439
|
description: "Switch agent presets at runtime (e.g., /preset cheap, /preset powerful)"
|
|
30144
30440
|
};
|
|
@@ -30350,21 +30646,21 @@ async function saveBinary(binaryDir, data, contentType, filename) {
|
|
|
30350
30646
|
throw new Error("Unable to allocate unique filename for binary content");
|
|
30351
30647
|
}
|
|
30352
30648
|
|
|
30353
|
-
// node_modules/lru-cache/dist/esm/node/index.min.js
|
|
30354
|
-
import { tracingChannel as
|
|
30355
|
-
var S =
|
|
30356
|
-
var W =
|
|
30649
|
+
// node_modules/.pnpm/lru-cache@11.3.6/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
30650
|
+
import { tracingChannel as I, channel as G } from "node:diagnostics_channel";
|
|
30651
|
+
var S = G("lru-cache:metrics");
|
|
30652
|
+
var W = I("lru-cache");
|
|
30653
|
+
var C = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
|
|
30357
30654
|
var D = () => S.hasSubscribers || W.hasSubscribers;
|
|
30358
|
-
var
|
|
30359
|
-
var
|
|
30360
|
-
var C = typeof process == "object" && process ? process : {};
|
|
30655
|
+
var U = new Set;
|
|
30656
|
+
var L = typeof process == "object" && process ? process : {};
|
|
30361
30657
|
var P = (u, e, t, i) => {
|
|
30362
|
-
typeof
|
|
30658
|
+
typeof L.emitWarning == "function" ? L.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
|
|
30363
30659
|
};
|
|
30364
|
-
var H = (u) => !
|
|
30365
|
-
var
|
|
30660
|
+
var H = (u) => !U.has(u);
|
|
30661
|
+
var X = Symbol("type");
|
|
30366
30662
|
var F = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u);
|
|
30367
|
-
var
|
|
30663
|
+
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;
|
|
30368
30664
|
var O = class extends Array {
|
|
30369
30665
|
constructor(e) {
|
|
30370
30666
|
super(e), this.fill(0);
|
|
@@ -30375,7 +30671,7 @@ var R = class u {
|
|
|
30375
30671
|
length;
|
|
30376
30672
|
static #o = false;
|
|
30377
30673
|
static create(e) {
|
|
30378
|
-
let t =
|
|
30674
|
+
let t = j(e);
|
|
30379
30675
|
if (!t)
|
|
30380
30676
|
return [];
|
|
30381
30677
|
u.#o = true;
|
|
@@ -30394,11 +30690,11 @@ var R = class u {
|
|
|
30394
30690
|
return this.heap[--this.length];
|
|
30395
30691
|
}
|
|
30396
30692
|
};
|
|
30397
|
-
var
|
|
30693
|
+
var M = class u2 {
|
|
30398
30694
|
#o;
|
|
30399
30695
|
#u;
|
|
30400
30696
|
#w;
|
|
30401
|
-
#
|
|
30697
|
+
#x;
|
|
30402
30698
|
#S;
|
|
30403
30699
|
#M;
|
|
30404
30700
|
#U;
|
|
@@ -30469,7 +30765,7 @@ var L = class u2 {
|
|
|
30469
30765
|
return this.#w;
|
|
30470
30766
|
}
|
|
30471
30767
|
get onInsert() {
|
|
30472
|
-
return this.#
|
|
30768
|
+
return this.#x;
|
|
30473
30769
|
}
|
|
30474
30770
|
get disposeAfter() {
|
|
30475
30771
|
return this.#S;
|
|
@@ -30478,9 +30774,9 @@ var L = class u2 {
|
|
|
30478
30774
|
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;
|
|
30479
30775
|
if (x !== undefined && typeof x?.now != "function")
|
|
30480
30776
|
throw new TypeError("perf option must have a now() method if specified");
|
|
30481
|
-
if (this.#m = x ??
|
|
30777
|
+
if (this.#m = x ?? C, t !== 0 && !F(t))
|
|
30482
30778
|
throw new TypeError("max option must be a nonnegative integer");
|
|
30483
|
-
let v = t ?
|
|
30779
|
+
let v = t ? j(t) : Array;
|
|
30484
30780
|
if (!v)
|
|
30485
30781
|
throw new Error("invalid max value: " + t);
|
|
30486
30782
|
if (this.#o = t, this.#u = T, this.maxEntrySize = w || this.#u, this.sizeCalculation = y, this.sizeCalculation) {
|
|
@@ -30493,7 +30789,7 @@ var L = class u2 {
|
|
|
30493
30789
|
throw new TypeError("memoMethod must be a function if defined");
|
|
30494
30790
|
if (this.#U = m, a !== undefined && typeof a != "function")
|
|
30495
30791
|
throw new TypeError("fetchMethod must be a function if specified");
|
|
30496
|
-
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.#
|
|
30792
|
+
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) {
|
|
30497
30793
|
if (this.#u !== 0 && !F(this.#u))
|
|
30498
30794
|
throw new TypeError("maxSize must be a positive integer if specified");
|
|
30499
30795
|
if (!F(this.maxEntrySize))
|
|
@@ -30509,7 +30805,7 @@ var L = class u2 {
|
|
|
30509
30805
|
throw new TypeError("At least one of max, maxSize, or ttl is required");
|
|
30510
30806
|
if (!this.ttlAutopurge && !this.#o && !this.#u) {
|
|
30511
30807
|
let E = "LRU_CACHE_UNBOUNDED";
|
|
30512
|
-
H(E) && (
|
|
30808
|
+
H(E) && (U.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
|
|
30513
30809
|
}
|
|
30514
30810
|
}
|
|
30515
30811
|
getRemainingTTL(e) {
|
|
@@ -30521,7 +30817,7 @@ var L = class u2 {
|
|
|
30521
30817
|
let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
|
|
30522
30818
|
this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
|
|
30523
30819
|
t[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
|
|
30524
|
-
}, this.#
|
|
30820
|
+
}, this.#D = (r) => {
|
|
30525
30821
|
t[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
|
|
30526
30822
|
};
|
|
30527
30823
|
let s = this.ttlAutopurge ? (r, h) => {
|
|
@@ -30565,7 +30861,7 @@ var L = class u2 {
|
|
|
30565
30861
|
return !!l && !!h && (n || o()) - h > l;
|
|
30566
30862
|
};
|
|
30567
30863
|
}
|
|
30568
|
-
#
|
|
30864
|
+
#D = () => {};
|
|
30569
30865
|
#E = () => {};
|
|
30570
30866
|
#N = () => {};
|
|
30571
30867
|
#p = () => false;
|
|
@@ -30731,7 +31027,7 @@ var L = class u2 {
|
|
|
30731
31027
|
return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
|
|
30732
31028
|
let f = this.#n === 0 ? undefined : this.#s.get(e);
|
|
30733
31029
|
if (f === undefined)
|
|
30734
|
-
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.#
|
|
31030
|
+
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");
|
|
30735
31031
|
else {
|
|
30736
31032
|
this.#L(f);
|
|
30737
31033
|
let g = this.#t[f];
|
|
@@ -30795,7 +31091,7 @@ var L = class u2 {
|
|
|
30795
31091
|
if (this.#p(n))
|
|
30796
31092
|
s && (s.has = "stale", this.#E(s, n));
|
|
30797
31093
|
else
|
|
30798
|
-
return i && this.#
|
|
31094
|
+
return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
|
|
30799
31095
|
} else
|
|
30800
31096
|
s && (s.has = "miss");
|
|
30801
31097
|
return false;
|
|
@@ -30853,7 +31149,7 @@ var L = class u2 {
|
|
|
30853
31149
|
let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
|
|
30854
31150
|
t.status = s, s && t.context && (s.context = t.context);
|
|
30855
31151
|
let n = this.#B(e, t);
|
|
30856
|
-
return s &&
|
|
31152
|
+
return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
|
|
30857
31153
|
}
|
|
30858
31154
|
async#B(e, t = {}) {
|
|
30859
31155
|
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;
|
|
@@ -30872,7 +31168,7 @@ var L = class u2 {
|
|
|
30872
31168
|
}
|
|
30873
31169
|
let A = this.#p(b);
|
|
30874
31170
|
if (!y && !A)
|
|
30875
|
-
return a && (a.fetch = "hit"), this.#L(b), s && this.#
|
|
31171
|
+
return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
|
|
30876
31172
|
let z5 = this.#P(e, b, _, w), v = z5.__staleWhileFetching !== undefined && i;
|
|
30877
31173
|
return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z5.__staleWhileFetching : z5.__returned = z5;
|
|
30878
31174
|
}
|
|
@@ -30881,7 +31177,7 @@ var L = class u2 {
|
|
|
30881
31177
|
let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
|
|
30882
31178
|
t.status = s, s && t.context && (s.context = t.context);
|
|
30883
31179
|
let n = this.#K(e, t);
|
|
30884
|
-
return s &&
|
|
31180
|
+
return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
|
|
30885
31181
|
}
|
|
30886
31182
|
async#K(e, t = {}) {
|
|
30887
31183
|
let i = await this.#B(e, t);
|
|
@@ -30920,7 +31216,7 @@ var L = class u2 {
|
|
|
30920
31216
|
return;
|
|
30921
31217
|
}
|
|
30922
31218
|
let h = this.#t[r], l = this.#e(h);
|
|
30923
|
-
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.#
|
|
31219
|
+
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);
|
|
30924
31220
|
}
|
|
30925
31221
|
#$(e, t) {
|
|
30926
31222
|
this.#c[t] = e, this.#a[e] = t;
|
|
@@ -31088,7 +31384,7 @@ function extractStructuredText(root) {
|
|
|
31088
31384
|
]);
|
|
31089
31385
|
const isText = (node) => node.nodeType === node.TEXT_NODE;
|
|
31090
31386
|
const isElement = (node) => node.nodeType === node.ELEMENT_NODE;
|
|
31091
|
-
const
|
|
31387
|
+
const pushText2 = (value) => {
|
|
31092
31388
|
const normalized = value.replace(/\s+/g, " ");
|
|
31093
31389
|
if (!normalized.trim())
|
|
31094
31390
|
return;
|
|
@@ -31114,7 +31410,7 @@ function extractStructuredText(root) {
|
|
|
31114
31410
|
};
|
|
31115
31411
|
const visit = (node) => {
|
|
31116
31412
|
if (isText(node)) {
|
|
31117
|
-
|
|
31413
|
+
pushText2(node.textContent || "");
|
|
31118
31414
|
return;
|
|
31119
31415
|
}
|
|
31120
31416
|
if (!isElement(node))
|
|
@@ -31825,7 +32121,7 @@ async function probeLlmsText(url, timeoutMs, signal, fallbackOrigin) {
|
|
|
31825
32121
|
}
|
|
31826
32122
|
|
|
31827
32123
|
// src/tools/smartfetch/cache.ts
|
|
31828
|
-
var CACHE = new
|
|
32124
|
+
var CACHE = new M({
|
|
31829
32125
|
maxSize: 50 * 1024 * 1024,
|
|
31830
32126
|
ttl: 15 * 60 * 1000,
|
|
31831
32127
|
sizeCalculation: (value) => {
|
|
@@ -31886,7 +32182,7 @@ function isInvalidLlmsResult(fetchResult) {
|
|
|
31886
32182
|
|
|
31887
32183
|
// src/tools/smartfetch/secondary-model.ts
|
|
31888
32184
|
import { existsSync as existsSync10 } from "node:fs";
|
|
31889
|
-
import { readFile as
|
|
32185
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
31890
32186
|
import path17 from "node:path";
|
|
31891
32187
|
function parseModelRef(value) {
|
|
31892
32188
|
if (!value)
|
|
@@ -31923,7 +32219,7 @@ async function readOpenCodeConfigFile(configPath) {
|
|
|
31923
32219
|
if (!configPath)
|
|
31924
32220
|
return;
|
|
31925
32221
|
try {
|
|
31926
|
-
const content = await
|
|
32222
|
+
const content = await readFile5(configPath, "utf8");
|
|
31927
32223
|
return JSON.parse(stripJsonComments(content));
|
|
31928
32224
|
} catch {
|
|
31929
32225
|
return;
|
|
@@ -32572,6 +32868,460 @@ function createWebfetchTool(pluginCtx, options = {}) {
|
|
|
32572
32868
|
}
|
|
32573
32869
|
});
|
|
32574
32870
|
}
|
|
32871
|
+
// src/tools/subtask/command.ts
|
|
32872
|
+
var COMMAND_NAME5 = "subtask";
|
|
32873
|
+
var SUBTASK_COMMAND_TEMPLATE = `Start a focused subtask worker.
|
|
32874
|
+
|
|
32875
|
+
The user's request below is the full scope for the worker. Do not broaden it.
|
|
32876
|
+
Create a self-contained worker prompt that includes:
|
|
32877
|
+
- the exact objective
|
|
32878
|
+
- relevant context from this conversation
|
|
32879
|
+
- specific files/paths that matter
|
|
32880
|
+
- expected deliverables
|
|
32881
|
+
- validation the worker should run, if applicable
|
|
32882
|
+
|
|
32883
|
+
USER REQUEST:
|
|
32884
|
+
$ARGUMENTS
|
|
32885
|
+
|
|
32886
|
+
Then call the subtask tool:
|
|
32887
|
+
\`subtask(prompt="...", files=["src/foo.ts", "docs/bar.md"])\`
|
|
32888
|
+
|
|
32889
|
+
Only include files that are clearly relevant. If no files are needed, omit files.`;
|
|
32890
|
+
function createSubtaskCommandManager(_ctx, state) {
|
|
32891
|
+
function registerCommand(opencodeConfig) {
|
|
32892
|
+
const configCommand = opencodeConfig.command;
|
|
32893
|
+
if (!configCommand?.[COMMAND_NAME5]) {
|
|
32894
|
+
if (!opencodeConfig.command) {
|
|
32895
|
+
opencodeConfig.command = {};
|
|
32896
|
+
}
|
|
32897
|
+
opencodeConfig.command[COMMAND_NAME5] = {
|
|
32898
|
+
description: "Create a focused subtask prompt for a new session",
|
|
32899
|
+
template: SUBTASK_COMMAND_TEMPLATE
|
|
32900
|
+
};
|
|
32901
|
+
}
|
|
32902
|
+
}
|
|
32903
|
+
return {
|
|
32904
|
+
registerCommand,
|
|
32905
|
+
handleEvent(input) {
|
|
32906
|
+
if (input.event.type === "session.created") {
|
|
32907
|
+
const info = input.event.properties?.info;
|
|
32908
|
+
if (!info?.id || !info.parentID)
|
|
32909
|
+
return;
|
|
32910
|
+
const source = state.sourceFor(info.parentID);
|
|
32911
|
+
if (source)
|
|
32912
|
+
state.markSession(info.id, source);
|
|
32913
|
+
return;
|
|
32914
|
+
}
|
|
32915
|
+
if (input.event.type !== "session.deleted")
|
|
32916
|
+
return;
|
|
32917
|
+
const sessionID = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
32918
|
+
if (sessionID)
|
|
32919
|
+
state.unmarkSession(sessionID);
|
|
32920
|
+
}
|
|
32921
|
+
};
|
|
32922
|
+
}
|
|
32923
|
+
// src/tools/subtask/files.ts
|
|
32924
|
+
import * as fs12 from "node:fs/promises";
|
|
32925
|
+
import * as path20 from "node:path";
|
|
32926
|
+
|
|
32927
|
+
// src/tools/subtask/vendor.ts
|
|
32928
|
+
import * as fs11 from "node:fs/promises";
|
|
32929
|
+
import * as path19 from "node:path";
|
|
32930
|
+
var DEFAULT_READ_LIMIT = 2000;
|
|
32931
|
+
var MAX_LINE_LENGTH = 2000;
|
|
32932
|
+
var MAX_BYTES = 50 * 1024;
|
|
32933
|
+
var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
|
|
32934
|
+
var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
|
|
32935
|
+
var SAMPLE_BYTES = 4096;
|
|
32936
|
+
var BINARY_EXTENSIONS = new Set([
|
|
32937
|
+
".zip",
|
|
32938
|
+
".tar",
|
|
32939
|
+
".gz",
|
|
32940
|
+
".exe",
|
|
32941
|
+
".dll",
|
|
32942
|
+
".so",
|
|
32943
|
+
".class",
|
|
32944
|
+
".jar",
|
|
32945
|
+
".war",
|
|
32946
|
+
".7z",
|
|
32947
|
+
".doc",
|
|
32948
|
+
".docx",
|
|
32949
|
+
".xls",
|
|
32950
|
+
".xlsx",
|
|
32951
|
+
".ppt",
|
|
32952
|
+
".pptx",
|
|
32953
|
+
".odt",
|
|
32954
|
+
".ods",
|
|
32955
|
+
".odp",
|
|
32956
|
+
".bin",
|
|
32957
|
+
".dat",
|
|
32958
|
+
".obj",
|
|
32959
|
+
".o",
|
|
32960
|
+
".a",
|
|
32961
|
+
".lib",
|
|
32962
|
+
".wasm",
|
|
32963
|
+
".pyc",
|
|
32964
|
+
".pyo"
|
|
32965
|
+
]);
|
|
32966
|
+
async function isBinaryFile(filepath) {
|
|
32967
|
+
const ext = path19.extname(filepath).toLowerCase();
|
|
32968
|
+
if (BINARY_EXTENSIONS.has(ext)) {
|
|
32969
|
+
return true;
|
|
32970
|
+
}
|
|
32971
|
+
try {
|
|
32972
|
+
const file = await fs11.open(filepath, "r");
|
|
32973
|
+
try {
|
|
32974
|
+
const buffer = Buffer.alloc(SAMPLE_BYTES);
|
|
32975
|
+
const result = await file.read(buffer, 0, SAMPLE_BYTES, 0);
|
|
32976
|
+
if (result.bytesRead === 0)
|
|
32977
|
+
return false;
|
|
32978
|
+
const bytes = buffer.subarray(0, result.bytesRead);
|
|
32979
|
+
let nonPrintableCount = 0;
|
|
32980
|
+
for (let i = 0;i < bytes.length; i++) {
|
|
32981
|
+
const byte = bytes[i];
|
|
32982
|
+
if (byte === undefined)
|
|
32983
|
+
continue;
|
|
32984
|
+
if (byte === 0)
|
|
32985
|
+
return true;
|
|
32986
|
+
if (byte < 9 || byte > 13 && byte < 32) {
|
|
32987
|
+
nonPrintableCount++;
|
|
32988
|
+
}
|
|
32989
|
+
}
|
|
32990
|
+
return nonPrintableCount / bytes.length > 0.3;
|
|
32991
|
+
} finally {
|
|
32992
|
+
await file.close();
|
|
32993
|
+
}
|
|
32994
|
+
} catch {
|
|
32995
|
+
return false;
|
|
32996
|
+
}
|
|
32997
|
+
}
|
|
32998
|
+
function formatFileContent(_filepath, content) {
|
|
32999
|
+
const cappedContent = Buffer.byteLength(content, "utf8") > MAX_BYTES;
|
|
33000
|
+
const contentToFormat = cappedContent ? content.slice(0, MAX_BYTES) : content;
|
|
33001
|
+
const lines = contentToFormat.split(`
|
|
33002
|
+
`);
|
|
33003
|
+
const limit = DEFAULT_READ_LIMIT;
|
|
33004
|
+
const offset = 0;
|
|
33005
|
+
const raw = lines.slice(offset, offset + limit).map((line) => {
|
|
33006
|
+
return line.length > MAX_LINE_LENGTH ? `${line.substring(0, MAX_LINE_LENGTH)}${MAX_LINE_SUFFIX}` : line;
|
|
33007
|
+
});
|
|
33008
|
+
const formatted = raw.map((line, index) => {
|
|
33009
|
+
return `${index + offset + 1}: ${line}`;
|
|
33010
|
+
});
|
|
33011
|
+
let output = [
|
|
33012
|
+
`<path>${_filepath}</path>`,
|
|
33013
|
+
"<type>file</type>",
|
|
33014
|
+
`<content>
|
|
33015
|
+
`
|
|
33016
|
+
].join(`
|
|
33017
|
+
`);
|
|
33018
|
+
output += formatted.join(`
|
|
33019
|
+
`);
|
|
33020
|
+
const totalLines = lines.length;
|
|
33021
|
+
const lastReadLine = offset + formatted.length;
|
|
33022
|
+
const hasMoreLines = totalLines > lastReadLine;
|
|
33023
|
+
if (cappedContent) {
|
|
33024
|
+
output += `
|
|
33025
|
+
|
|
33026
|
+
(Output capped at ${MAX_BYTES_LABEL}. Showing lines 1-${lastReadLine}. Use offset=${lastReadLine + 1} to continue.)`;
|
|
33027
|
+
} else if (hasMoreLines) {
|
|
33028
|
+
output += `
|
|
33029
|
+
|
|
33030
|
+
(Showing lines 1-${lastReadLine} of ${totalLines}. Use offset=${lastReadLine + 1} to continue.)`;
|
|
33031
|
+
} else {
|
|
33032
|
+
output += `
|
|
33033
|
+
|
|
33034
|
+
(End of file - total ${totalLines} lines)`;
|
|
33035
|
+
}
|
|
33036
|
+
output += `
|
|
33037
|
+
</content>`;
|
|
33038
|
+
return output;
|
|
33039
|
+
}
|
|
33040
|
+
|
|
33041
|
+
// src/tools/subtask/files.ts
|
|
33042
|
+
var FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g;
|
|
33043
|
+
var TRAILING_PATH_PUNCTUATION = /[!?:;]+$/;
|
|
33044
|
+
function cleanFileReference(ref) {
|
|
33045
|
+
return ref.replace(/^@/, "").replace(TRAILING_PATH_PUNCTUATION, "");
|
|
33046
|
+
}
|
|
33047
|
+
function parseFileReferences(text) {
|
|
33048
|
+
const fileRefs = new Set;
|
|
33049
|
+
for (const match of text.matchAll(FILE_REGEX)) {
|
|
33050
|
+
if (match[1]) {
|
|
33051
|
+
fileRefs.add(cleanFileReference(match[1]));
|
|
33052
|
+
}
|
|
33053
|
+
}
|
|
33054
|
+
return fileRefs;
|
|
33055
|
+
}
|
|
33056
|
+
async function buildSyntheticFileParts(directory, refs) {
|
|
33057
|
+
const parts = [];
|
|
33058
|
+
const realDirectory = await fs12.realpath(directory);
|
|
33059
|
+
for (const ref of refs) {
|
|
33060
|
+
const filepath = path20.resolve(directory, ref);
|
|
33061
|
+
const relative3 = path20.relative(directory, filepath);
|
|
33062
|
+
if (relative3.startsWith("..") || path20.isAbsolute(relative3))
|
|
33063
|
+
continue;
|
|
33064
|
+
try {
|
|
33065
|
+
const realFilepath = await fs12.realpath(filepath);
|
|
33066
|
+
const realRelative = path20.relative(realDirectory, realFilepath);
|
|
33067
|
+
if (realRelative.startsWith("..") || path20.isAbsolute(realRelative)) {
|
|
33068
|
+
continue;
|
|
33069
|
+
}
|
|
33070
|
+
const stats = await fs12.stat(realFilepath);
|
|
33071
|
+
if (!stats.isFile())
|
|
33072
|
+
continue;
|
|
33073
|
+
if (await isBinaryFile(realFilepath))
|
|
33074
|
+
continue;
|
|
33075
|
+
const content = await fs12.readFile(realFilepath, "utf-8");
|
|
33076
|
+
parts.push({
|
|
33077
|
+
type: "text",
|
|
33078
|
+
synthetic: true,
|
|
33079
|
+
text: `Called the Read tool with the following input: ${JSON.stringify({ filePath: realFilepath })}`
|
|
33080
|
+
});
|
|
33081
|
+
parts.push({
|
|
33082
|
+
type: "text",
|
|
33083
|
+
synthetic: true,
|
|
33084
|
+
text: formatFileContent(realFilepath, content)
|
|
33085
|
+
});
|
|
33086
|
+
} catch {}
|
|
33087
|
+
}
|
|
33088
|
+
return parts;
|
|
33089
|
+
}
|
|
33090
|
+
// src/tools/subtask/state.ts
|
|
33091
|
+
function createSubtaskState() {
|
|
33092
|
+
const sourceBySession = new Map;
|
|
33093
|
+
return {
|
|
33094
|
+
markSession(sessionID, sourceSessionID) {
|
|
33095
|
+
sourceBySession.set(sessionID, sourceSessionID);
|
|
33096
|
+
},
|
|
33097
|
+
unmarkSession(sessionID) {
|
|
33098
|
+
sourceBySession.delete(sessionID);
|
|
33099
|
+
},
|
|
33100
|
+
isSubtaskSession(sessionID) {
|
|
33101
|
+
return sourceBySession.has(sessionID);
|
|
33102
|
+
},
|
|
33103
|
+
sourceFor(sessionID) {
|
|
33104
|
+
return sourceBySession.get(sessionID);
|
|
33105
|
+
}
|
|
33106
|
+
};
|
|
33107
|
+
}
|
|
33108
|
+
// src/tools/subtask/tools.ts
|
|
33109
|
+
import { tool as tool5 } from "@opencode-ai/plugin";
|
|
33110
|
+
var SUBTASK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
33111
|
+
var SUBTASK_SUMMARY_TAG_REGEX = /<\/?subtask_summary>/g;
|
|
33112
|
+
function normalizeSubtaskSummary(text) {
|
|
33113
|
+
return text.replace(SUBTASK_SUMMARY_TAG_REGEX, "").trim();
|
|
33114
|
+
}
|
|
33115
|
+
function getAbortSignal(context) {
|
|
33116
|
+
if (!context || typeof context !== "object" || !("abort" in context)) {
|
|
33117
|
+
return;
|
|
33118
|
+
}
|
|
33119
|
+
const signal = context.abort;
|
|
33120
|
+
return signal && typeof signal === "object" && "addEventListener" in signal && "removeEventListener" in signal && "aborted" in signal ? signal : undefined;
|
|
33121
|
+
}
|
|
33122
|
+
function createSubtaskTool(ctx, state, depthTracker) {
|
|
33123
|
+
const client = ctx.client;
|
|
33124
|
+
return tool5({
|
|
33125
|
+
description: "Run a child worker session and return its completion summary to the caller",
|
|
33126
|
+
args: {
|
|
33127
|
+
prompt: tool5.schema.string().describe("The generated subtask prompt"),
|
|
33128
|
+
files: tool5.schema.array(tool5.schema.string()).optional().describe("Array of file paths to load into the new session's context")
|
|
33129
|
+
},
|
|
33130
|
+
async execute(args, context) {
|
|
33131
|
+
const directory = context && typeof context === "object" && "directory" in context && typeof context.directory === "string" ? context.directory : ctx.directory;
|
|
33132
|
+
const sessionID = context && typeof context === "object" && "sessionID" in context ? context.sessionID : "unknown";
|
|
33133
|
+
const abortSignal = getAbortSignal(context);
|
|
33134
|
+
if (state.isSubtaskSession(sessionID)) {
|
|
33135
|
+
return "Nested subtask is disabled: this session is already a subtask worker. Finish this worker and return its summary to the parent session instead.";
|
|
33136
|
+
}
|
|
33137
|
+
if (sessionID !== "unknown" && depthTracker && depthTracker.getDepth(sessionID) + 1 > depthTracker.maxDepth) {
|
|
33138
|
+
return `Subtask worker blocked: max subagent depth ${depthTracker.maxDepth} would be exceeded.`;
|
|
33139
|
+
}
|
|
33140
|
+
const sessionReference = `You are a subtask worker spawned by parent session ${sessionID}.
|
|
33141
|
+
|
|
33142
|
+
Your job is bounded: complete only the task below. Do not expand scope.
|
|
33143
|
+
If needed context is missing, use read_session to inspect the parent session.
|
|
33144
|
+
Do not spawn another subtask.`;
|
|
33145
|
+
const files = new Set([
|
|
33146
|
+
...parseFileReferences(args.prompt),
|
|
33147
|
+
...(args.files ?? []).map(cleanFileReference)
|
|
33148
|
+
]);
|
|
33149
|
+
const fileRefs = files.size > 0 ? [...files].map((f) => `@${f}`).join(" ") : "";
|
|
33150
|
+
const fullPrompt = fileRefs ? `${sessionReference}
|
|
33151
|
+
|
|
33152
|
+
TASK:
|
|
33153
|
+
${args.prompt}
|
|
33154
|
+
|
|
33155
|
+
FILES PROVIDED:
|
|
33156
|
+
${fileRefs}` : `${sessionReference}
|
|
33157
|
+
|
|
33158
|
+
TASK:
|
|
33159
|
+
${args.prompt}`;
|
|
33160
|
+
let childSessionID;
|
|
33161
|
+
try {
|
|
33162
|
+
const session2 = await client.session.create({
|
|
33163
|
+
responseStyle: "data",
|
|
33164
|
+
throwOnError: true,
|
|
33165
|
+
query: { directory },
|
|
33166
|
+
body: {
|
|
33167
|
+
parentID: sessionID === "unknown" ? undefined : sessionID,
|
|
33168
|
+
title: `Subtask worker from ${sessionID}`
|
|
33169
|
+
}
|
|
33170
|
+
});
|
|
33171
|
+
childSessionID = session2?.data?.id ?? session2?.id;
|
|
33172
|
+
if (!childSessionID) {
|
|
33173
|
+
throw new Error("Subtask worker session did not return an id");
|
|
33174
|
+
}
|
|
33175
|
+
if (sessionID !== "unknown" && depthTracker) {
|
|
33176
|
+
const registered = depthTracker.registerChild(sessionID, childSessionID);
|
|
33177
|
+
if (!registered) {
|
|
33178
|
+
throw new Error("Subtask worker blocked: max subagent depth exceeded");
|
|
33179
|
+
}
|
|
33180
|
+
}
|
|
33181
|
+
state.markSession(childSessionID, sessionID);
|
|
33182
|
+
await promptWithTimeout(client, {
|
|
33183
|
+
responseStyle: "data",
|
|
33184
|
+
throwOnError: true,
|
|
33185
|
+
query: { directory },
|
|
33186
|
+
path: { id: childSessionID },
|
|
33187
|
+
body: {
|
|
33188
|
+
agent: "orchestrator",
|
|
33189
|
+
parts: [
|
|
33190
|
+
{
|
|
33191
|
+
type: "text",
|
|
33192
|
+
text: `${fullPrompt}
|
|
33193
|
+
|
|
33194
|
+
Instructions:
|
|
33195
|
+
1. Understand the task and relevant file context.
|
|
33196
|
+
2. Make only necessary changes.
|
|
33197
|
+
3. Run the most relevant validation checks when practical.
|
|
33198
|
+
4. Stop when the requested task is done.
|
|
33199
|
+
|
|
33200
|
+
Return your final response in this format:
|
|
33201
|
+
|
|
33202
|
+
<subtask_summary>
|
|
33203
|
+
Status: completed | blocked | partial
|
|
33204
|
+
|
|
33205
|
+
What changed:
|
|
33206
|
+
- ...
|
|
33207
|
+
|
|
33208
|
+
Files touched:
|
|
33209
|
+
- ...
|
|
33210
|
+
|
|
33211
|
+
Validation:
|
|
33212
|
+
- ...
|
|
33213
|
+
|
|
33214
|
+
Risks / follow-up:
|
|
33215
|
+
- ...
|
|
33216
|
+
</subtask_summary>`
|
|
33217
|
+
},
|
|
33218
|
+
...await buildSyntheticFileParts(directory, files)
|
|
33219
|
+
]
|
|
33220
|
+
}
|
|
33221
|
+
}, SUBTASK_TIMEOUT_MS, abortSignal);
|
|
33222
|
+
const extraction = await extractSessionResult(client, childSessionID, {
|
|
33223
|
+
directory,
|
|
33224
|
+
includeReasoning: false
|
|
33225
|
+
});
|
|
33226
|
+
if (extraction.empty) {
|
|
33227
|
+
throw new Error("Subtask worker returned no summary");
|
|
33228
|
+
}
|
|
33229
|
+
const summary = normalizeSubtaskSummary(extraction.text);
|
|
33230
|
+
return [
|
|
33231
|
+
`task_id: ${childSessionID}`,
|
|
33232
|
+
"",
|
|
33233
|
+
"<subtask_summary>",
|
|
33234
|
+
summary,
|
|
33235
|
+
"</subtask_summary>"
|
|
33236
|
+
].join(`
|
|
33237
|
+
`);
|
|
33238
|
+
} finally {
|
|
33239
|
+
if (childSessionID) {
|
|
33240
|
+
try {
|
|
33241
|
+
await client.session.abort({
|
|
33242
|
+
path: { id: childSessionID },
|
|
33243
|
+
query: { directory }
|
|
33244
|
+
});
|
|
33245
|
+
state.unmarkSession(childSessionID);
|
|
33246
|
+
} catch {}
|
|
33247
|
+
}
|
|
33248
|
+
}
|
|
33249
|
+
}
|
|
33250
|
+
});
|
|
33251
|
+
}
|
|
33252
|
+
function formatTranscript(messages, limit) {
|
|
33253
|
+
const lines = [];
|
|
33254
|
+
for (const msg of messages) {
|
|
33255
|
+
const role = msg.info?.role;
|
|
33256
|
+
const parts = msg.parts;
|
|
33257
|
+
if (role === "user") {
|
|
33258
|
+
lines.push("## User");
|
|
33259
|
+
for (const part of parts) {
|
|
33260
|
+
if (part.type === "text" && !part.ignored && typeof part.text === "string") {
|
|
33261
|
+
lines.push(part.text);
|
|
33262
|
+
}
|
|
33263
|
+
if (part.type === "file") {
|
|
33264
|
+
lines.push(`[Attached: ${part.filename || "file"}]`);
|
|
33265
|
+
}
|
|
33266
|
+
}
|
|
33267
|
+
lines.push("");
|
|
33268
|
+
}
|
|
33269
|
+
if (role === "assistant") {
|
|
33270
|
+
lines.push("## Assistant");
|
|
33271
|
+
for (const part of parts) {
|
|
33272
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
33273
|
+
lines.push(part.text);
|
|
33274
|
+
}
|
|
33275
|
+
if (part.type === "tool" && part.state?.status === "completed" && part.tool) {
|
|
33276
|
+
lines.push(`[Tool: ${part.tool}] ${part.state.title ?? ""}`);
|
|
33277
|
+
}
|
|
33278
|
+
}
|
|
33279
|
+
lines.push("");
|
|
33280
|
+
}
|
|
33281
|
+
}
|
|
33282
|
+
const output = lines.join(`
|
|
33283
|
+
`).trim();
|
|
33284
|
+
if (messages.length >= (limit ?? 100)) {
|
|
33285
|
+
return output + `
|
|
33286
|
+
|
|
33287
|
+
(Showing ${messages.length} most recent messages. Use a higher 'limit' to see more.)`;
|
|
33288
|
+
}
|
|
33289
|
+
return `${output}
|
|
33290
|
+
|
|
33291
|
+
(End of session - ${messages.length} messages)`;
|
|
33292
|
+
}
|
|
33293
|
+
function createReadSessionTool(client, state) {
|
|
33294
|
+
return tool5({
|
|
33295
|
+
description: "Read the conversation transcript from a previous session. Use this when you need specific information from the source session that wasn't included in the subtask summary.",
|
|
33296
|
+
args: {
|
|
33297
|
+
sessionID: tool5.schema.string().describe("The full session ID (e.g., sess_01jxyz...)"),
|
|
33298
|
+
limit: tool5.schema.number().optional().describe("Maximum number of messages to read (defaults to 100, max 500)")
|
|
33299
|
+
},
|
|
33300
|
+
async execute(args, context) {
|
|
33301
|
+
const limit = Math.min(args.limit ?? 100, 500);
|
|
33302
|
+
const directory = context && typeof context === "object" && "directory" in context && typeof context.directory === "string" ? context.directory : undefined;
|
|
33303
|
+
const callerSessionID = context && typeof context === "object" && "sessionID" in context ? context.sessionID : undefined;
|
|
33304
|
+
if (!callerSessionID || !state.isSubtaskSession(callerSessionID)) {
|
|
33305
|
+
return "read_session is only available from subtask worker sessions.";
|
|
33306
|
+
}
|
|
33307
|
+
if (state.sourceFor(callerSessionID) !== args.sessionID) {
|
|
33308
|
+
return "read_session can only read the source session for this subtask worker.";
|
|
33309
|
+
}
|
|
33310
|
+
try {
|
|
33311
|
+
const response = await client.session.messages({
|
|
33312
|
+
path: { id: args.sessionID },
|
|
33313
|
+
query: { limit, ...directory ? { directory } : {} }
|
|
33314
|
+
});
|
|
33315
|
+
if (!response.data || response.data.length === 0) {
|
|
33316
|
+
return "Session has no messages or does not exist.";
|
|
33317
|
+
}
|
|
33318
|
+
return formatTranscript(response.data, limit);
|
|
33319
|
+
} catch (error) {
|
|
33320
|
+
return `Could not read session ${args.sessionID}: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
33321
|
+
}
|
|
33322
|
+
}
|
|
33323
|
+
});
|
|
33324
|
+
}
|
|
32575
33325
|
// src/utils/subagent-depth.ts
|
|
32576
33326
|
class SubagentDepthTracker {
|
|
32577
33327
|
depthBySession = new Map;
|
|
@@ -32684,6 +33434,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32684
33434
|
let jsonErrorRecoveryHook;
|
|
32685
33435
|
let foregroundFallback;
|
|
32686
33436
|
let todoContinuationHook;
|
|
33437
|
+
let sessionGoalHook;
|
|
32687
33438
|
let taskSessionManagerHook;
|
|
32688
33439
|
let interviewManager;
|
|
32689
33440
|
let presetManager;
|
|
@@ -32691,6 +33442,8 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32691
33442
|
let councilTools;
|
|
32692
33443
|
let webfetch;
|
|
32693
33444
|
let rewriteDisplayNameMentions;
|
|
33445
|
+
let subtaskCommandManager;
|
|
33446
|
+
let subtaskState;
|
|
32694
33447
|
let toolCount = 0;
|
|
32695
33448
|
try {
|
|
32696
33449
|
config = loadPluginConfig(ctx.directory);
|
|
@@ -32774,6 +33527,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32774
33527
|
autoEnable: config.todoContinuation?.autoEnable ?? false,
|
|
32775
33528
|
autoEnableThreshold: config.todoContinuation?.autoEnableThreshold ?? 4
|
|
32776
33529
|
});
|
|
33530
|
+
sessionGoalHook = createSessionGoalHook(ctx, config, {
|
|
33531
|
+
getAgentName: (sessionID) => sessionAgentMap.get(sessionID)
|
|
33532
|
+
});
|
|
32777
33533
|
taskSessionManagerHook = createTaskSessionManagerHook(ctx, {
|
|
32778
33534
|
maxSessionsPerAgent: config.sessionManager?.maxSessionsPerAgent ?? 2,
|
|
32779
33535
|
readContextMinLines: config.sessionManager?.readContextMinLines ?? 10,
|
|
@@ -32783,7 +33539,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32783
33539
|
interviewManager = createInterviewManager(ctx, config);
|
|
32784
33540
|
presetManager = createPresetManager(ctx, config);
|
|
32785
33541
|
divoomManager = createDivoomManager(config.divoom);
|
|
32786
|
-
|
|
33542
|
+
subtaskState = createSubtaskState();
|
|
33543
|
+
subtaskCommandManager = createSubtaskCommandManager(ctx, subtaskState);
|
|
33544
|
+
toolCount = Object.keys(councilTools).length + Object.keys(todoContinuationHook.tool).length + 1 + 2 + 2;
|
|
32787
33545
|
} catch (err) {
|
|
32788
33546
|
log("[plugin] FATAL: init failed", String(err));
|
|
32789
33547
|
await appLog(ctx, "error", `INIT FAILED: ${String(err)}. Report at github.com/alvinunreal/oh-my-opencode-slim/issues/310`);
|
|
@@ -32828,7 +33586,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32828
33586
|
webfetch,
|
|
32829
33587
|
...todoContinuationHook.tool,
|
|
32830
33588
|
ast_grep_search,
|
|
32831
|
-
ast_grep_replace
|
|
33589
|
+
ast_grep_replace,
|
|
33590
|
+
subtask: createSubtaskTool(ctx, subtaskState, depthTracker),
|
|
33591
|
+
read_session: createReadSessionTool(ctx.client, subtaskState)
|
|
32832
33592
|
},
|
|
32833
33593
|
mcp: mcps,
|
|
32834
33594
|
config: async (opencodeConfig) => {
|
|
@@ -33024,7 +33784,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33024
33784
|
};
|
|
33025
33785
|
}
|
|
33026
33786
|
interviewManager.registerCommand(opencodeConfig);
|
|
33787
|
+
sessionGoalHook.registerCommand(opencodeConfig);
|
|
33027
33788
|
presetManager.registerCommand(opencodeConfig);
|
|
33789
|
+
subtaskCommandManager.registerCommand(opencodeConfig);
|
|
33028
33790
|
},
|
|
33029
33791
|
event: async (input) => {
|
|
33030
33792
|
const event = input.event;
|
|
@@ -33044,14 +33806,16 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33044
33806
|
depthTracker.registerChild(parentSessionId, childSessionId);
|
|
33045
33807
|
}
|
|
33046
33808
|
}
|
|
33047
|
-
await foregroundFallback.handleEvent(input.event);
|
|
33048
|
-
await todoContinuationHook.handleEvent(input);
|
|
33049
|
-
await autoUpdateChecker.event(input);
|
|
33050
33809
|
await multiplexerSessionManager.onSessionCreated(event);
|
|
33051
33810
|
await multiplexerSessionManager.onSessionStatus(event);
|
|
33052
33811
|
await multiplexerSessionManager.onSessionDeleted(event);
|
|
33812
|
+
await foregroundFallback.handleEvent(input.event);
|
|
33813
|
+
await todoContinuationHook.handleEvent(input);
|
|
33814
|
+
sessionGoalHook.handleEvent(input);
|
|
33815
|
+
await autoUpdateChecker.event(input);
|
|
33053
33816
|
await interviewManager.handleEvent(input);
|
|
33054
33817
|
await taskSessionManagerHook.event(input);
|
|
33818
|
+
subtaskCommandManager.handleEvent(input);
|
|
33055
33819
|
if (event.type === "permission.asked" || event.type === "question.asked") {
|
|
33056
33820
|
const props = event.properties;
|
|
33057
33821
|
divoomManager.onUserInputRequired({
|
|
@@ -33109,6 +33873,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33109
33873
|
await todoContinuationHook.handleCommandExecuteBefore(input, output);
|
|
33110
33874
|
await interviewManager.handleCommandExecuteBefore(input, output);
|
|
33111
33875
|
await presetManager.handleCommandExecuteBefore(input, output);
|
|
33876
|
+
await sessionGoalHook.handleCommandExecuteBefore(input, output);
|
|
33112
33877
|
},
|
|
33113
33878
|
"chat.headers": chatHeadersHook["chat.headers"],
|
|
33114
33879
|
"chat.message": async (input, output) => {
|
|
@@ -33137,6 +33902,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33137
33902
|
${output.system[0]}` : "");
|
|
33138
33903
|
}
|
|
33139
33904
|
}
|
|
33905
|
+
sessionGoalHook.handleSystemTransform(input, output);
|
|
33140
33906
|
collapseSystemInPlace(output.system);
|
|
33141
33907
|
},
|
|
33142
33908
|
"experimental.chat.messages.transform": async (input, output) => {
|