oh-my-opencode-slim 1.1.0 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -12
- package/dist/cli/index.js +4 -82
- package/dist/cli/skills.d.ts +2 -30
- package/dist/cli/types.d.ts +0 -1
- package/dist/config/constants.d.ts +1 -1
- 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/phase-reminder/index.d.ts +1 -1
- package/dist/hooks/session-goal/index.d.ts +38 -0
- package/dist/hooks/task-session-manager/index.d.ts +9 -0
- package/dist/hooks/todo-continuation/index.d.ts +2 -0
- package/dist/index.js +1015 -397
- package/dist/multiplexer/session-manager.d.ts +3 -1
- package/dist/tui.js +5 -62
- package/dist/utils/background-job-board.d.ts +48 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/task.d.ts +16 -0
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
32
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
33
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
34
|
|
|
35
|
-
// node_modules/@mozilla/readability/Readability.js
|
|
35
|
+
// node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability.js
|
|
36
36
|
var require_Readability = __commonJS((exports, module) => {
|
|
37
37
|
function Readability(doc, options) {
|
|
38
38
|
if (options && options.documentElement) {
|
|
@@ -1603,7 +1603,7 @@ var require_Readability = __commonJS((exports, module) => {
|
|
|
1603
1603
|
}
|
|
1604
1604
|
});
|
|
1605
1605
|
|
|
1606
|
-
// node_modules/@mozilla/readability/Readability-readerable.js
|
|
1606
|
+
// node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability-readerable.js
|
|
1607
1607
|
var require_Readability_readerable = __commonJS((exports, module) => {
|
|
1608
1608
|
var REGEXPS = {
|
|
1609
1609
|
unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
|
|
@@ -1659,7 +1659,7 @@ var require_Readability_readerable = __commonJS((exports, module) => {
|
|
|
1659
1659
|
}
|
|
1660
1660
|
});
|
|
1661
1661
|
|
|
1662
|
-
// node_modules/@mozilla/readability/index.js
|
|
1662
|
+
// node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/index.js
|
|
1663
1663
|
var require_readability = __commonJS((exports, module) => {
|
|
1664
1664
|
var Readability = require_Readability();
|
|
1665
1665
|
var isProbablyReaderable = require_Readability_readerable();
|
|
@@ -1669,7 +1669,7 @@ var require_readability = __commonJS((exports, module) => {
|
|
|
1669
1669
|
};
|
|
1670
1670
|
});
|
|
1671
1671
|
|
|
1672
|
-
// node_modules/@mixmark-io/domino/lib/Event.js
|
|
1672
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Event.js
|
|
1673
1673
|
var require_Event = __commonJS((exports, module) => {
|
|
1674
1674
|
module.exports = Event;
|
|
1675
1675
|
Event.CAPTURING_PHASE = 1;
|
|
@@ -1726,7 +1726,7 @@ var require_Event = __commonJS((exports, module) => {
|
|
|
1726
1726
|
});
|
|
1727
1727
|
});
|
|
1728
1728
|
|
|
1729
|
-
// node_modules/@mixmark-io/domino/lib/UIEvent.js
|
|
1729
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/UIEvent.js
|
|
1730
1730
|
var require_UIEvent = __commonJS((exports, module) => {
|
|
1731
1731
|
var Event = require_Event();
|
|
1732
1732
|
module.exports = UIEvent;
|
|
@@ -1745,7 +1745,7 @@ var require_UIEvent = __commonJS((exports, module) => {
|
|
|
1745
1745
|
});
|
|
1746
1746
|
});
|
|
1747
1747
|
|
|
1748
|
-
// node_modules/@mixmark-io/domino/lib/MouseEvent.js
|
|
1748
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MouseEvent.js
|
|
1749
1749
|
var require_MouseEvent = __commonJS((exports, module) => {
|
|
1750
1750
|
var UIEvent = require_UIEvent();
|
|
1751
1751
|
module.exports = MouseEvent;
|
|
@@ -1803,7 +1803,7 @@ var require_MouseEvent = __commonJS((exports, module) => {
|
|
|
1803
1803
|
});
|
|
1804
1804
|
});
|
|
1805
1805
|
|
|
1806
|
-
// node_modules/@mixmark-io/domino/lib/DOMException.js
|
|
1806
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMException.js
|
|
1807
1807
|
var require_DOMException = __commonJS((exports, module) => {
|
|
1808
1808
|
module.exports = DOMException;
|
|
1809
1809
|
var INDEX_SIZE_ERR = 1;
|
|
@@ -1927,12 +1927,12 @@ var require_DOMException = __commonJS((exports, module) => {
|
|
|
1927
1927
|
var c;
|
|
1928
1928
|
});
|
|
1929
1929
|
|
|
1930
|
-
// node_modules/@mixmark-io/domino/lib/config.js
|
|
1930
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/config.js
|
|
1931
1931
|
var require_config = __commonJS((exports) => {
|
|
1932
1932
|
exports.isApiWritable = !globalThis.__domino_frozen__;
|
|
1933
1933
|
});
|
|
1934
1934
|
|
|
1935
|
-
// node_modules/@mixmark-io/domino/lib/utils.js
|
|
1935
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/utils.js
|
|
1936
1936
|
var require_utils = __commonJS((exports) => {
|
|
1937
1937
|
var DOMException = require_DOMException();
|
|
1938
1938
|
var ERR = DOMException;
|
|
@@ -2045,7 +2045,7 @@ var require_utils = __commonJS((exports) => {
|
|
|
2045
2045
|
};
|
|
2046
2046
|
});
|
|
2047
2047
|
|
|
2048
|
-
// node_modules/@mixmark-io/domino/lib/EventTarget.js
|
|
2048
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/EventTarget.js
|
|
2049
2049
|
var require_EventTarget = __commonJS((exports, module) => {
|
|
2050
2050
|
var Event = require_Event();
|
|
2051
2051
|
var MouseEvent = require_MouseEvent();
|
|
@@ -2233,7 +2233,7 @@ var require_EventTarget = __commonJS((exports, module) => {
|
|
|
2233
2233
|
};
|
|
2234
2234
|
});
|
|
2235
2235
|
|
|
2236
|
-
// node_modules/@mixmark-io/domino/lib/LinkedList.js
|
|
2236
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/LinkedList.js
|
|
2237
2237
|
var require_LinkedList = __commonJS((exports, module) => {
|
|
2238
2238
|
var utils = require_utils();
|
|
2239
2239
|
var LinkedList = module.exports = {
|
|
@@ -2276,7 +2276,7 @@ var require_LinkedList = __commonJS((exports, module) => {
|
|
|
2276
2276
|
};
|
|
2277
2277
|
});
|
|
2278
2278
|
|
|
2279
|
-
// node_modules/@mixmark-io/domino/lib/NodeUtils.js
|
|
2279
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeUtils.js
|
|
2280
2280
|
var require_NodeUtils = __commonJS((exports, module) => {
|
|
2281
2281
|
module.exports = {
|
|
2282
2282
|
serializeOne,
|
|
@@ -2452,7 +2452,7 @@ var require_NodeUtils = __commonJS((exports, module) => {
|
|
|
2452
2452
|
}
|
|
2453
2453
|
});
|
|
2454
2454
|
|
|
2455
|
-
// node_modules/@mixmark-io/domino/lib/Node.js
|
|
2455
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Node.js
|
|
2456
2456
|
var require_Node = __commonJS((exports, module) => {
|
|
2457
2457
|
module.exports = Node;
|
|
2458
2458
|
var EventTarget = require_EventTarget();
|
|
@@ -3013,7 +3013,7 @@ var require_Node = __commonJS((exports, module) => {
|
|
|
3013
3013
|
});
|
|
3014
3014
|
});
|
|
3015
3015
|
|
|
3016
|
-
// node_modules/@mixmark-io/domino/lib/NodeList.es6.js
|
|
3016
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es6.js
|
|
3017
3017
|
var require_NodeList_es6 = __commonJS((exports, module) => {
|
|
3018
3018
|
module.exports = class NodeList extends Array {
|
|
3019
3019
|
constructor(a) {
|
|
@@ -3030,7 +3030,7 @@ var require_NodeList_es6 = __commonJS((exports, module) => {
|
|
|
3030
3030
|
};
|
|
3031
3031
|
});
|
|
3032
3032
|
|
|
3033
|
-
// node_modules/@mixmark-io/domino/lib/NodeList.es5.js
|
|
3033
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es5.js
|
|
3034
3034
|
var require_NodeList_es5 = __commonJS((exports, module) => {
|
|
3035
3035
|
function item(i) {
|
|
3036
3036
|
return this[i] || null;
|
|
@@ -3044,7 +3044,7 @@ var require_NodeList_es5 = __commonJS((exports, module) => {
|
|
|
3044
3044
|
module.exports = NodeList;
|
|
3045
3045
|
});
|
|
3046
3046
|
|
|
3047
|
-
// node_modules/@mixmark-io/domino/lib/NodeList.js
|
|
3047
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.js
|
|
3048
3048
|
var require_NodeList = __commonJS((exports, module) => {
|
|
3049
3049
|
var NodeList;
|
|
3050
3050
|
try {
|
|
@@ -3055,7 +3055,7 @@ var require_NodeList = __commonJS((exports, module) => {
|
|
|
3055
3055
|
module.exports = NodeList;
|
|
3056
3056
|
});
|
|
3057
3057
|
|
|
3058
|
-
// node_modules/@mixmark-io/domino/lib/ContainerNode.js
|
|
3058
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ContainerNode.js
|
|
3059
3059
|
var require_ContainerNode = __commonJS((exports, module) => {
|
|
3060
3060
|
module.exports = ContainerNode;
|
|
3061
3061
|
var Node = require_Node();
|
|
@@ -3123,7 +3123,7 @@ var require_ContainerNode = __commonJS((exports, module) => {
|
|
|
3123
3123
|
});
|
|
3124
3124
|
});
|
|
3125
3125
|
|
|
3126
|
-
// node_modules/@mixmark-io/domino/lib/xmlnames.js
|
|
3126
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/xmlnames.js
|
|
3127
3127
|
var require_xmlnames = __commonJS((exports) => {
|
|
3128
3128
|
exports.isValidName = isValidName;
|
|
3129
3129
|
exports.isValidQName = isValidQName;
|
|
@@ -3172,7 +3172,7 @@ var require_xmlnames = __commonJS((exports) => {
|
|
|
3172
3172
|
}
|
|
3173
3173
|
});
|
|
3174
3174
|
|
|
3175
|
-
// node_modules/@mixmark-io/domino/lib/attributes.js
|
|
3175
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/attributes.js
|
|
3176
3176
|
var require_attributes = __commonJS((exports) => {
|
|
3177
3177
|
var utils = require_utils();
|
|
3178
3178
|
exports.property = function(attr) {
|
|
@@ -3308,7 +3308,7 @@ var require_attributes = __commonJS((exports) => {
|
|
|
3308
3308
|
};
|
|
3309
3309
|
});
|
|
3310
3310
|
|
|
3311
|
-
// node_modules/@mixmark-io/domino/lib/FilteredElementList.js
|
|
3311
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/FilteredElementList.js
|
|
3312
3312
|
var require_FilteredElementList = __commonJS((exports, module) => {
|
|
3313
3313
|
module.exports = FilteredElementList;
|
|
3314
3314
|
var Node = require_Node();
|
|
@@ -3374,7 +3374,7 @@ var require_FilteredElementList = __commonJS((exports, module) => {
|
|
|
3374
3374
|
});
|
|
3375
3375
|
});
|
|
3376
3376
|
|
|
3377
|
-
// node_modules/@mixmark-io/domino/lib/DOMTokenList.js
|
|
3377
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMTokenList.js
|
|
3378
3378
|
var require_DOMTokenList = __commonJS((exports, module) => {
|
|
3379
3379
|
var utils = require_utils();
|
|
3380
3380
|
module.exports = DOMTokenList;
|
|
@@ -3536,7 +3536,7 @@ var require_DOMTokenList = __commonJS((exports, module) => {
|
|
|
3536
3536
|
}
|
|
3537
3537
|
});
|
|
3538
3538
|
|
|
3539
|
-
// node_modules/@mixmark-io/domino/lib/select.js
|
|
3539
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/select.js
|
|
3540
3540
|
var require_select = __commonJS((exports, module) => {
|
|
3541
3541
|
var window2 = Object.create(null, {
|
|
3542
3542
|
location: { get: function() {
|
|
@@ -4279,7 +4279,7 @@ var require_select = __commonJS((exports, module) => {
|
|
|
4279
4279
|
};
|
|
4280
4280
|
});
|
|
4281
4281
|
|
|
4282
|
-
// node_modules/@mixmark-io/domino/lib/ChildNode.js
|
|
4282
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ChildNode.js
|
|
4283
4283
|
var require_ChildNode = __commonJS((exports, module) => {
|
|
4284
4284
|
var Node = require_Node();
|
|
4285
4285
|
var LinkedList = require_LinkedList();
|
|
@@ -4369,7 +4369,7 @@ var require_ChildNode = __commonJS((exports, module) => {
|
|
|
4369
4369
|
module.exports = ChildNode;
|
|
4370
4370
|
});
|
|
4371
4371
|
|
|
4372
|
-
// node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
|
|
4372
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
|
|
4373
4373
|
var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
|
|
4374
4374
|
var Node = require_Node();
|
|
4375
4375
|
var NonDocumentTypeChildNode = {
|
|
@@ -4395,7 +4395,7 @@ var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
|
|
|
4395
4395
|
module.exports = NonDocumentTypeChildNode;
|
|
4396
4396
|
});
|
|
4397
4397
|
|
|
4398
|
-
// node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
|
|
4398
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
|
|
4399
4399
|
var require_NamedNodeMap = __commonJS((exports, module) => {
|
|
4400
4400
|
module.exports = NamedNodeMap;
|
|
4401
4401
|
var utils = require_utils();
|
|
@@ -4432,7 +4432,7 @@ var require_NamedNodeMap = __commonJS((exports, module) => {
|
|
|
4432
4432
|
});
|
|
4433
4433
|
});
|
|
4434
4434
|
|
|
4435
|
-
// node_modules/@mixmark-io/domino/lib/Element.js
|
|
4435
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Element.js
|
|
4436
4436
|
var require_Element = __commonJS((exports, module) => {
|
|
4437
4437
|
module.exports = Element;
|
|
4438
4438
|
var xml = require_xmlnames();
|
|
@@ -5333,7 +5333,7 @@ var require_Element = __commonJS((exports, module) => {
|
|
|
5333
5333
|
}
|
|
5334
5334
|
});
|
|
5335
5335
|
|
|
5336
|
-
// node_modules/@mixmark-io/domino/lib/Leaf.js
|
|
5336
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Leaf.js
|
|
5337
5337
|
var require_Leaf = __commonJS((exports, module) => {
|
|
5338
5338
|
module.exports = Leaf;
|
|
5339
5339
|
var Node = require_Node();
|
|
@@ -5374,7 +5374,7 @@ var require_Leaf = __commonJS((exports, module) => {
|
|
|
5374
5374
|
});
|
|
5375
5375
|
});
|
|
5376
5376
|
|
|
5377
|
-
// node_modules/@mixmark-io/domino/lib/CharacterData.js
|
|
5377
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CharacterData.js
|
|
5378
5378
|
var require_CharacterData = __commonJS((exports, module) => {
|
|
5379
5379
|
module.exports = CharacterData;
|
|
5380
5380
|
var Leaf = require_Leaf();
|
|
@@ -5431,7 +5431,7 @@ var require_CharacterData = __commonJS((exports, module) => {
|
|
|
5431
5431
|
Object.defineProperties(CharacterData.prototype, NonDocumentTypeChildNode);
|
|
5432
5432
|
});
|
|
5433
5433
|
|
|
5434
|
-
// node_modules/@mixmark-io/domino/lib/Text.js
|
|
5434
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Text.js
|
|
5435
5435
|
var require_Text = __commonJS((exports, module) => {
|
|
5436
5436
|
module.exports = Text;
|
|
5437
5437
|
var utils = require_utils();
|
|
@@ -5501,7 +5501,7 @@ var require_Text = __commonJS((exports, module) => {
|
|
|
5501
5501
|
});
|
|
5502
5502
|
});
|
|
5503
5503
|
|
|
5504
|
-
// node_modules/@mixmark-io/domino/lib/Comment.js
|
|
5504
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Comment.js
|
|
5505
5505
|
var require_Comment = __commonJS((exports, module) => {
|
|
5506
5506
|
module.exports = Comment;
|
|
5507
5507
|
var Node = require_Node();
|
|
@@ -5544,7 +5544,7 @@ var require_Comment = __commonJS((exports, module) => {
|
|
|
5544
5544
|
});
|
|
5545
5545
|
});
|
|
5546
5546
|
|
|
5547
|
-
// node_modules/@mixmark-io/domino/lib/DocumentFragment.js
|
|
5547
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentFragment.js
|
|
5548
5548
|
var require_DocumentFragment = __commonJS((exports, module) => {
|
|
5549
5549
|
module.exports = DocumentFragment;
|
|
5550
5550
|
var Node = require_Node();
|
|
@@ -5601,7 +5601,7 @@ var require_DocumentFragment = __commonJS((exports, module) => {
|
|
|
5601
5601
|
});
|
|
5602
5602
|
});
|
|
5603
5603
|
|
|
5604
|
-
// node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
|
|
5604
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
|
|
5605
5605
|
var require_ProcessingInstruction = __commonJS((exports, module) => {
|
|
5606
5606
|
module.exports = ProcessingInstruction;
|
|
5607
5607
|
var Node = require_Node();
|
|
@@ -5650,7 +5650,7 @@ var require_ProcessingInstruction = __commonJS((exports, module) => {
|
|
|
5650
5650
|
});
|
|
5651
5651
|
});
|
|
5652
5652
|
|
|
5653
|
-
// node_modules/@mixmark-io/domino/lib/NodeFilter.js
|
|
5653
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeFilter.js
|
|
5654
5654
|
var require_NodeFilter = __commonJS((exports, module) => {
|
|
5655
5655
|
var NodeFilter = {
|
|
5656
5656
|
FILTER_ACCEPT: 1,
|
|
@@ -5673,7 +5673,7 @@ var require_NodeFilter = __commonJS((exports, module) => {
|
|
|
5673
5673
|
module.exports = NodeFilter.constructor = NodeFilter.prototype = NodeFilter;
|
|
5674
5674
|
});
|
|
5675
5675
|
|
|
5676
|
-
// node_modules/@mixmark-io/domino/lib/NodeTraversal.js
|
|
5676
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeTraversal.js
|
|
5677
5677
|
var require_NodeTraversal = __commonJS((exports, module) => {
|
|
5678
5678
|
var NodeTraversal = module.exports = {
|
|
5679
5679
|
nextSkippingChildren,
|
|
@@ -5737,7 +5737,7 @@ var require_NodeTraversal = __commonJS((exports, module) => {
|
|
|
5737
5737
|
}
|
|
5738
5738
|
});
|
|
5739
5739
|
|
|
5740
|
-
// node_modules/@mixmark-io/domino/lib/TreeWalker.js
|
|
5740
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/TreeWalker.js
|
|
5741
5741
|
var require_TreeWalker = __commonJS((exports, module) => {
|
|
5742
5742
|
module.exports = TreeWalker;
|
|
5743
5743
|
var Node = require_Node();
|
|
@@ -5967,7 +5967,7 @@ var require_TreeWalker = __commonJS((exports, module) => {
|
|
|
5967
5967
|
});
|
|
5968
5968
|
});
|
|
5969
5969
|
|
|
5970
|
-
// node_modules/@mixmark-io/domino/lib/NodeIterator.js
|
|
5970
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeIterator.js
|
|
5971
5971
|
var require_NodeIterator = __commonJS((exports, module) => {
|
|
5972
5972
|
module.exports = NodeIterator;
|
|
5973
5973
|
var NodeFilter = require_NodeFilter();
|
|
@@ -6108,7 +6108,7 @@ var require_NodeIterator = __commonJS((exports, module) => {
|
|
|
6108
6108
|
});
|
|
6109
6109
|
});
|
|
6110
6110
|
|
|
6111
|
-
// node_modules/@mixmark-io/domino/lib/URL.js
|
|
6111
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URL.js
|
|
6112
6112
|
var require_URL = __commonJS((exports, module) => {
|
|
6113
6113
|
module.exports = URL4;
|
|
6114
6114
|
function URL4(url) {
|
|
@@ -6281,7 +6281,7 @@ var require_URL = __commonJS((exports, module) => {
|
|
|
6281
6281
|
};
|
|
6282
6282
|
});
|
|
6283
6283
|
|
|
6284
|
-
// node_modules/@mixmark-io/domino/lib/CustomEvent.js
|
|
6284
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CustomEvent.js
|
|
6285
6285
|
var require_CustomEvent = __commonJS((exports, module) => {
|
|
6286
6286
|
module.exports = CustomEvent;
|
|
6287
6287
|
var Event = require_Event();
|
|
@@ -6293,7 +6293,7 @@ var require_CustomEvent = __commonJS((exports, module) => {
|
|
|
6293
6293
|
});
|
|
6294
6294
|
});
|
|
6295
6295
|
|
|
6296
|
-
// node_modules/@mixmark-io/domino/lib/events.js
|
|
6296
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/events.js
|
|
6297
6297
|
var require_events = __commonJS((exports, module) => {
|
|
6298
6298
|
module.exports = {
|
|
6299
6299
|
Event: require_Event(),
|
|
@@ -6303,7 +6303,7 @@ var require_events = __commonJS((exports, module) => {
|
|
|
6303
6303
|
};
|
|
6304
6304
|
});
|
|
6305
6305
|
|
|
6306
|
-
// node_modules/@mixmark-io/domino/lib/style_parser.js
|
|
6306
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/style_parser.js
|
|
6307
6307
|
var require_style_parser = __commonJS((exports) => {
|
|
6308
6308
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6309
6309
|
exports.hyphenate = exports.parse = undefined;
|
|
@@ -6370,7 +6370,7 @@ var require_style_parser = __commonJS((exports) => {
|
|
|
6370
6370
|
exports.hyphenate = hyphenate;
|
|
6371
6371
|
});
|
|
6372
6372
|
|
|
6373
|
-
// node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
|
|
6373
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
|
|
6374
6374
|
var require_CSSStyleDeclaration = __commonJS((exports, module) => {
|
|
6375
6375
|
var { parse } = require_style_parser();
|
|
6376
6376
|
module.exports = function(elt) {
|
|
@@ -6546,7 +6546,7 @@ var require_CSSStyleDeclaration = __commonJS((exports, module) => {
|
|
|
6546
6546
|
});
|
|
6547
6547
|
});
|
|
6548
6548
|
|
|
6549
|
-
// node_modules/@mixmark-io/domino/lib/URLUtils.js
|
|
6549
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URLUtils.js
|
|
6550
6550
|
var require_URLUtils = __commonJS((exports, module) => {
|
|
6551
6551
|
var URL4 = require_URL();
|
|
6552
6552
|
module.exports = URLUtils;
|
|
@@ -6780,7 +6780,7 @@ var require_URLUtils = __commonJS((exports, module) => {
|
|
|
6780
6780
|
};
|
|
6781
6781
|
});
|
|
6782
6782
|
|
|
6783
|
-
// node_modules/@mixmark-io/domino/lib/defineElement.js
|
|
6783
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/defineElement.js
|
|
6784
6784
|
var require_defineElement = __commonJS((exports, module) => {
|
|
6785
6785
|
var attributes = require_attributes();
|
|
6786
6786
|
var isApiWritable = require_config().isApiWritable;
|
|
@@ -6842,7 +6842,7 @@ var require_defineElement = __commonJS((exports, module) => {
|
|
|
6842
6842
|
}
|
|
6843
6843
|
});
|
|
6844
6844
|
|
|
6845
|
-
// node_modules/@mixmark-io/domino/lib/htmlelts.js
|
|
6845
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/htmlelts.js
|
|
6846
6846
|
var require_htmlelts = __commonJS((exports) => {
|
|
6847
6847
|
var Node = require_Node();
|
|
6848
6848
|
var Element = require_Element();
|
|
@@ -8306,7 +8306,7 @@ var require_htmlelts = __commonJS((exports) => {
|
|
|
8306
8306
|
});
|
|
8307
8307
|
});
|
|
8308
8308
|
|
|
8309
|
-
// node_modules/@mixmark-io/domino/lib/svg.js
|
|
8309
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/svg.js
|
|
8310
8310
|
var require_svg = __commonJS((exports) => {
|
|
8311
8311
|
var Element = require_Element();
|
|
8312
8312
|
var defineElement = require_defineElement();
|
|
@@ -8432,7 +8432,7 @@ var require_svg = __commonJS((exports) => {
|
|
|
8432
8432
|
});
|
|
8433
8433
|
});
|
|
8434
8434
|
|
|
8435
|
-
// node_modules/@mixmark-io/domino/lib/MutationConstants.js
|
|
8435
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MutationConstants.js
|
|
8436
8436
|
var require_MutationConstants = __commonJS((exports, module) => {
|
|
8437
8437
|
module.exports = {
|
|
8438
8438
|
VALUE: 1,
|
|
@@ -8444,7 +8444,7 @@ var require_MutationConstants = __commonJS((exports, module) => {
|
|
|
8444
8444
|
};
|
|
8445
8445
|
});
|
|
8446
8446
|
|
|
8447
|
-
// node_modules/@mixmark-io/domino/lib/Document.js
|
|
8447
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Document.js
|
|
8448
8448
|
var require_Document = __commonJS((exports, module) => {
|
|
8449
8449
|
module.exports = Document;
|
|
8450
8450
|
var Node = require_Node();
|
|
@@ -9156,7 +9156,7 @@ var require_Document = __commonJS((exports, module) => {
|
|
|
9156
9156
|
};
|
|
9157
9157
|
});
|
|
9158
9158
|
|
|
9159
|
-
// node_modules/@mixmark-io/domino/lib/DocumentType.js
|
|
9159
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentType.js
|
|
9160
9160
|
var require_DocumentType = __commonJS((exports, module) => {
|
|
9161
9161
|
module.exports = DocumentType;
|
|
9162
9162
|
var Node = require_Node();
|
|
@@ -9190,7 +9190,7 @@ var require_DocumentType = __commonJS((exports, module) => {
|
|
|
9190
9190
|
Object.defineProperties(DocumentType.prototype, ChildNode);
|
|
9191
9191
|
});
|
|
9192
9192
|
|
|
9193
|
-
// node_modules/@mixmark-io/domino/lib/HTMLParser.js
|
|
9193
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/HTMLParser.js
|
|
9194
9194
|
var require_HTMLParser = __commonJS((exports, module) => {
|
|
9195
9195
|
module.exports = HTMLParser;
|
|
9196
9196
|
var Document = require_Document();
|
|
@@ -17241,7 +17241,7 @@ var require_HTMLParser = __commonJS((exports, module) => {
|
|
|
17241
17241
|
}
|
|
17242
17242
|
});
|
|
17243
17243
|
|
|
17244
|
-
// node_modules/@mixmark-io/domino/lib/DOMImplementation.js
|
|
17244
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMImplementation.js
|
|
17245
17245
|
var require_DOMImplementation = __commonJS((exports, module) => {
|
|
17246
17246
|
module.exports = DOMImplementation;
|
|
17247
17247
|
var Document = require_Document();
|
|
@@ -17315,7 +17315,7 @@ var require_DOMImplementation = __commonJS((exports, module) => {
|
|
|
17315
17315
|
};
|
|
17316
17316
|
});
|
|
17317
17317
|
|
|
17318
|
-
// node_modules/@mixmark-io/domino/lib/Location.js
|
|
17318
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Location.js
|
|
17319
17319
|
var require_Location = __commonJS((exports, module) => {
|
|
17320
17320
|
var URL4 = require_URL();
|
|
17321
17321
|
var URLUtils = require_URLUtils();
|
|
@@ -17351,7 +17351,7 @@ var require_Location = __commonJS((exports, module) => {
|
|
|
17351
17351
|
});
|
|
17352
17352
|
});
|
|
17353
17353
|
|
|
17354
|
-
// node_modules/@mixmark-io/domino/lib/NavigatorID.js
|
|
17354
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NavigatorID.js
|
|
17355
17355
|
var require_NavigatorID = __commonJS((exports, module) => {
|
|
17356
17356
|
var NavigatorID = Object.create(null, {
|
|
17357
17357
|
appCodeName: { value: "Mozilla" },
|
|
@@ -17370,7 +17370,7 @@ var require_NavigatorID = __commonJS((exports, module) => {
|
|
|
17370
17370
|
module.exports = NavigatorID;
|
|
17371
17371
|
});
|
|
17372
17372
|
|
|
17373
|
-
// node_modules/@mixmark-io/domino/lib/WindowTimers.js
|
|
17373
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/WindowTimers.js
|
|
17374
17374
|
var require_WindowTimers = __commonJS((exports, module) => {
|
|
17375
17375
|
var WindowTimers = {
|
|
17376
17376
|
setTimeout,
|
|
@@ -17381,7 +17381,7 @@ var require_WindowTimers = __commonJS((exports, module) => {
|
|
|
17381
17381
|
module.exports = WindowTimers;
|
|
17382
17382
|
});
|
|
17383
17383
|
|
|
17384
|
-
// node_modules/@mixmark-io/domino/lib/impl.js
|
|
17384
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/impl.js
|
|
17385
17385
|
var require_impl = __commonJS((exports, module) => {
|
|
17386
17386
|
var utils = require_utils();
|
|
17387
17387
|
exports = module.exports = {
|
|
@@ -17409,7 +17409,7 @@ var require_impl = __commonJS((exports, module) => {
|
|
|
17409
17409
|
utils.merge(exports, require_svg().elements);
|
|
17410
17410
|
});
|
|
17411
17411
|
|
|
17412
|
-
// node_modules/@mixmark-io/domino/lib/Window.js
|
|
17412
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Window.js
|
|
17413
17413
|
var require_Window = __commonJS((exports, module) => {
|
|
17414
17414
|
var DOMImplementation = require_DOMImplementation();
|
|
17415
17415
|
var EventTarget = require_EventTarget();
|
|
@@ -17464,7 +17464,7 @@ var require_Window = __commonJS((exports, module) => {
|
|
|
17464
17464
|
utils.expose(require_impl(), Window);
|
|
17465
17465
|
});
|
|
17466
17466
|
|
|
17467
|
-
// node_modules/@mixmark-io/domino/lib/index.js
|
|
17467
|
+
// node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/index.js
|
|
17468
17468
|
var require_lib = __commonJS((exports) => {
|
|
17469
17469
|
var DOMImplementation = require_DOMImplementation();
|
|
17470
17470
|
var HTMLParser = require_HTMLParser();
|
|
@@ -17514,7 +17514,7 @@ var require_lib = __commonJS((exports) => {
|
|
|
17514
17514
|
exports.impl = impl;
|
|
17515
17515
|
});
|
|
17516
17516
|
|
|
17517
|
-
// node_modules/turndown/lib/turndown.cjs.js
|
|
17517
|
+
// node_modules/.pnpm/turndown@7.2.4/node_modules/turndown/lib/turndown.cjs.js
|
|
17518
17518
|
var require_turndown_cjs = __commonJS((exports, module) => {
|
|
17519
17519
|
function extend(destination) {
|
|
17520
17520
|
for (var i = 1;i < arguments.length; i++) {
|
|
@@ -18230,19 +18230,6 @@ var CUSTOM_SKILLS = [
|
|
|
18230
18230
|
];
|
|
18231
18231
|
|
|
18232
18232
|
// src/cli/skills.ts
|
|
18233
|
-
var RECOMMENDED_SKILLS = [
|
|
18234
|
-
{
|
|
18235
|
-
name: "agent-browser",
|
|
18236
|
-
repo: "https://github.com/vercel-labs/agent-browser",
|
|
18237
|
-
skillName: "agent-browser",
|
|
18238
|
-
allowedAgents: ["designer"],
|
|
18239
|
-
description: "High-performance browser automation",
|
|
18240
|
-
postInstallCommands: [
|
|
18241
|
-
"npm install -g agent-browser",
|
|
18242
|
-
"agent-browser install"
|
|
18243
|
-
]
|
|
18244
|
-
}
|
|
18245
|
-
];
|
|
18246
18233
|
var PERMISSION_ONLY_SKILLS = [
|
|
18247
18234
|
{
|
|
18248
18235
|
name: "requesting-code-review",
|
|
@@ -18267,12 +18254,6 @@ function getSkillPermissionsForAgent(agentName, skillList) {
|
|
|
18267
18254
|
}
|
|
18268
18255
|
return permissions;
|
|
18269
18256
|
}
|
|
18270
|
-
for (const skill of RECOMMENDED_SKILLS) {
|
|
18271
|
-
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
18272
|
-
if (isAllowed) {
|
|
18273
|
-
permissions[skill.skillName] = "allow";
|
|
18274
|
-
}
|
|
18275
|
-
}
|
|
18276
18257
|
for (const skill of CUSTOM_SKILLS) {
|
|
18277
18258
|
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
18278
18259
|
if (isAllowed) {
|
|
@@ -18322,8 +18303,8 @@ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
18322
18303
|
var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
18323
18304
|
var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
18324
18305
|
var PHASE_REMINDER_TEXT = `!IMPORTANT! Recall the workflow rules:
|
|
18325
|
-
Understand →
|
|
18326
|
-
|
|
18306
|
+
Understand → build a short work graph with independent lanes, dependencies, and advisory ownership → dispatch independent specialists as background tasks → record task/session IDs → continue orchestration → poll task_status for terminal results → reconcile → verify.
|
|
18307
|
+
Only consume outputs or advance dependent work when background results are terminal. !END!`;
|
|
18327
18308
|
var TMUX_SPAWN_DELAY_MS = 500;
|
|
18328
18309
|
var COUNCILLOR_STAGGER_MS = 250;
|
|
18329
18310
|
var DEFAULT_DISABLED_AGENTS = ["observer"];
|
|
@@ -18670,7 +18651,9 @@ function loadConfigFromPath(configPath, options) {
|
|
|
18670
18651
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
18671
18652
|
let rawConfig;
|
|
18672
18653
|
try {
|
|
18673
|
-
|
|
18654
|
+
const stripped = stripJsonComments(content);
|
|
18655
|
+
const interpolated = stripped.replace(/\{env:([^}]+)\}/g, (_, varName) => process.env[varName] ?? "");
|
|
18656
|
+
rawConfig = JSON.parse(interpolated);
|
|
18674
18657
|
} catch (error) {
|
|
18675
18658
|
const message = error instanceof Error ? error.message : String(error);
|
|
18676
18659
|
options?.onWarning?.({
|
|
@@ -18977,6 +18960,7 @@ ${customAppendPrompt}`;
|
|
|
18977
18960
|
}
|
|
18978
18961
|
var AGENT_DESCRIPTIONS = {
|
|
18979
18962
|
explorer: `@explorer
|
|
18963
|
+
- Lane: Codebase discovery and reconnaissance
|
|
18980
18964
|
- Role: Parallel search specialist for discovering unknowns across the codebase
|
|
18981
18965
|
- Permissions: Read files
|
|
18982
18966
|
- Stats: 2x faster codebase search than orchestrator, 1/2 cost of orchestrator
|
|
@@ -18984,38 +18968,43 @@ var AGENT_DESCRIPTIONS = {
|
|
|
18984
18968
|
- **Delegate when:** Need to discover what exists before planning • Parallel searches speed discovery • Need summarized map vs full contents • Broad/uncertain scope
|
|
18985
18969
|
- **Don't delegate when:** Know the path and need actual content • Need full file anyway • Single specific lookup • About to edit the file`,
|
|
18986
18970
|
librarian: `@librarian
|
|
18971
|
+
- Lane: External knowledge and library research
|
|
18987
18972
|
- Role: Authoritative source for current library docs and API references
|
|
18988
18973
|
- Permissions: External docs/search MCPs; no file edits
|
|
18989
18974
|
- Stats: 10x better finding up-to-date library docs than orchestrator, 1/2 cost of orchestrator
|
|
18990
18975
|
- Capabilities: Fetches latest official docs, examples, API signatures, version-specific behavior via grep_app MCP
|
|
18991
18976
|
- **Delegate when:** Libraries with frequent API changes (React, Next.js, AI SDKs) • Complex APIs needing official examples (ORMs, auth) • Version-specific behavior matters • Unfamiliar library • Edge cases or advanced features • Nuanced best practices
|
|
18992
18977
|
- **Don't delegate when:** Standard usage you're confident • Simple stable APIs • General programming knowledge • Info already in conversation • Built-in language features
|
|
18993
|
-
- **Rule of thumb:** "How does this library work?" → @librarian. "How does programming work?" →
|
|
18978
|
+
- **Rule of thumb:** "How does this library work?" → @librarian. "How does programming work?" → answer directly.`,
|
|
18994
18979
|
oracle: `@oracle
|
|
18980
|
+
- Lane: Architecture, risk, debugging strategy, and review
|
|
18995
18981
|
- Role: Strategic advisor for high-stakes decisions and persistent problems, code reviewer
|
|
18996
18982
|
- Permissions: Read files
|
|
18997
18983
|
- Stats: 5x better decision maker, problem solver, investigator than orchestrator, 0.8x speed of orchestrator, same cost.
|
|
18998
18984
|
- Capabilities: Deep architectural reasoning, system-level trade-offs, complex debugging, code review, simplification, maintainability review
|
|
18999
18985
|
- **Delegate when:** Major architectural decisions with long-term impact • Problems persisting after 2+ fix attempts • High-risk multi-system refactors • Costly trade-offs (performance vs maintainability) • Complex debugging with unclear root cause • Security/scalability/data integrity decisions • Genuinely uncertain and cost of wrong choice is high • When a workflow calls for a **reviewer** subagent • Code needs simplification or YAGNI scrutiny
|
|
19000
18986
|
- **Don't delegate when:** Routine decisions you're confident about • First bug fix attempt • Straightforward trade-offs • Tactical "how" vs strategic "should" • Time-sensitive good-enough decisions • Quick research/testing can answer
|
|
19001
|
-
- **Rule of thumb:** Need senior architect review? → @oracle. Need code review or simplification? → @oracle.
|
|
18987
|
+
- **Rule of thumb:** Need senior architect review? → @oracle. Need code review or simplification? → @oracle. Routine coordination or final synthesis? → handle directly.`,
|
|
19002
18988
|
designer: `@designer
|
|
18989
|
+
- Lane: User-facing UI/UX design, polish, and review
|
|
19003
18990
|
- Role: UI/UX specialist for intentional, polished experiences
|
|
19004
18991
|
- Permissions: Read/write files
|
|
19005
18992
|
- Stats: 10x better UI/UX than orchestrator
|
|
19006
18993
|
- Capabilities: Visual relevant edits, interactions, responsive layouts, design systems with aesthetic intent, deep UI/UX knowledge.
|
|
19007
18994
|
- **Delegate when:** User-facing interfaces needing polish • Responsive layouts • UX-critical components (forms, nav, dashboards) • Visual consistency systems • Animations/micro-interactions • Landing/marketing pages • Refining functional→delightful • Reviewing existing UI/UX quality
|
|
19008
18995
|
- **Don't delegate when:** Backend/logic with no visual • Quick prototypes where design doesn't matter yet
|
|
19009
|
-
- **Rule of thumb:** Users see it and polish matters? → @designer. Headless/functional? →
|
|
18996
|
+
- **Rule of thumb:** Users see it and polish matters? → @designer. Headless/functional implementation? → schedule @fixer.`,
|
|
19010
18997
|
fixer: `@fixer
|
|
18998
|
+
- Lane: Bounded implementation and test execution
|
|
19011
18999
|
- Role: Fast execution specialist for well-defined tasks, which empowers orchestrator with parallel, speedy executions
|
|
19012
19000
|
- Permissions: Read/write files
|
|
19013
19001
|
- Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
|
|
19014
19002
|
- Tools/Constraints: Execution-focused—no research, no architectural decisions
|
|
19015
19003
|
- **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers. Parallelization benefits: Task involves multiple folders and multiple files modificaiton, scoping work per folder and spawning parallel @fixers for each folder.
|
|
19016
19004
|
- **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Sequential dependencies
|
|
19017
|
-
- **Rule of thumb:**
|
|
19005
|
+
- **Rule of thumb:** If implementation or tests are needed, schedule @fixer with clear scope. Bigger or lots of edits should be split by ownership and dispatched as parallel background fixer lanes when safe.`,
|
|
19018
19006
|
council: `@council
|
|
19007
|
+
- Lane: High-stakes multi-model decision support
|
|
19019
19008
|
- Role: Multi-LLM consensus engine that runs several councillors, synthesizes their views, and returns a structured council report.
|
|
19020
19009
|
- Permissions: Read files
|
|
19021
19010
|
- Stats: 3x slower than orchestrator, 3x or more cost of orchestrator
|
|
@@ -19024,15 +19013,16 @@ var AGENT_DESCRIPTIONS = {
|
|
|
19024
19013
|
- **Don't delegate when:** Straightforward tasks you're confident about • Speed matters more than confidence • Routine implementation/debugging • A single specialist is clearly the right tool • You only need current docs/search/code review rather than multi-model consensus.
|
|
19025
19014
|
- **How to call:** Send the full question/task and relevant context. Be explicit about what decision, trade-off, or answer the council should resolve. Do not ask council to do routine code edits.
|
|
19026
19015
|
- **Result handling:** Council returns a structured response that may include: synthesized Council Response, individual Councillor Details, and Council Summary/confidence. Preserve that structure when the user asked for council output. Do not pretend the council only returned a final answer. If you need to act on the council result, first briefly state the council's recommendation, then proceed.
|
|
19027
|
-
- **Rule of thumb:** Need second/third opinions from different models? → @council. Need one expert
|
|
19016
|
+
- **Rule of thumb:** Need second/third opinions from different models? → @council. Need one expert lane? → use the specialist. Need final synthesis? → handle directly.`,
|
|
19028
19017
|
observer: `@observer
|
|
19018
|
+
- Lane: Visual/media analysis isolated from orchestrator context
|
|
19029
19019
|
- Role: Visual analysis specialist for images, PDFs, and diagrams
|
|
19030
19020
|
- Permissions: Read files
|
|
19031
19021
|
- Stats: Saves main context tokens — Observer processes raw files, returns structured observations
|
|
19032
19022
|
- Capabilities: Interprets images, screenshots, PDFs, and diagrams via native read tool; extracts UI elements, layouts, text, relationships
|
|
19033
19023
|
- **Delegate when:** Need to analyze a multimedia file• Extract information
|
|
19034
19024
|
- **Don't delegate when:** Plain text files that Read can handle directly • Files that need editing afterward (need literal content from Read)
|
|
19035
|
-
- **Rule of thumb:** Even if your model supports vision, delegate visual analysis to @observer — it isolates large image/PDF bytes from your context window, returning only concise structured text. Need exact file contents for
|
|
19025
|
+
- **Rule of thumb:** Even if your model supports vision, delegate visual analysis to @observer — it isolates large image/PDF bytes from your context window, returning only concise structured text. Need exact file contents for routing? → Read only the minimal context yourself.
|
|
19036
19026
|
- **IMPORTANT:** When delegating to @observer, always include the **full file path** in the prompt so it can read the file. Example: "Analyze the screenshot at /path/to/file.png — describe the UI elements and error messages."`
|
|
19037
19027
|
};
|
|
19038
19028
|
var VALIDATION_ROUTING = [
|
|
@@ -19067,7 +19057,9 @@ function buildOrchestratorPrompt(disabledAgents) {
|
|
|
19067
19057
|
}).join(`
|
|
19068
19058
|
`);
|
|
19069
19059
|
return `<Role>
|
|
19070
|
-
You are
|
|
19060
|
+
You are a workflow manager for coding work. Your job is to plan, schedule, delegate, monitor, reconcile, and verify specialist-agent work. You are not the default implementation worker.
|
|
19061
|
+
|
|
19062
|
+
Optimize for quality, speed, cost, and reliability by dispatching the right specialist lanes, tracking background task state, and integrating terminal results into one coherent outcome.
|
|
19071
19063
|
</Role>
|
|
19072
19064
|
|
|
19073
19065
|
<Agents>
|
|
@@ -19085,22 +19077,31 @@ Parse request: explicit requirements + implicit needs.
|
|
|
19085
19077
|
Evaluate approach by: quality, speed, cost, reliability.
|
|
19086
19078
|
Choose the path that optimizes all four.
|
|
19087
19079
|
|
|
19080
|
+
Classify work into lanes: discovery, external knowledge, implementation, UI/UX, review/risk, visual analysis, and final verification.
|
|
19081
|
+
|
|
19088
19082
|
## 3. Delegation Check
|
|
19089
19083
|
**STOP. Review specialists before acting.**
|
|
19090
19084
|
|
|
19091
|
-
!!! Review available agents and
|
|
19085
|
+
!!! Review available agents and lane rules. Decide what to schedule, what depends on what, and what minimal direct coordination is needed. !!!
|
|
19092
19086
|
|
|
19093
|
-
**
|
|
19087
|
+
**Dispatch efficiency:**
|
|
19094
19088
|
- Reference paths/lines, don't paste files (\`src/app.ts:42\` not full contents)
|
|
19095
19089
|
- Provide context summaries, let specialists read what they need
|
|
19096
19090
|
- Brief user on delegation goal before each call
|
|
19097
|
-
-
|
|
19091
|
+
- Keep direct work limited to clarification, minimal routing context, todos, synthesis, and final checks
|
|
19092
|
+
- For trivial conversational answers or tiny mechanical edits, direct execution is allowed when scheduling overhead would clearly dominate
|
|
19093
|
+
|
|
19094
|
+
## 4. Plan and Parallelize
|
|
19095
|
+
Build a short work graph before dispatching:
|
|
19096
|
+
- Independent lanes that can run now
|
|
19097
|
+
- Dependency-ordered lanes that must wait
|
|
19098
|
+
- Advisory ownership for write-capable lanes
|
|
19099
|
+
- Verification/review lanes that run after implementation
|
|
19098
19100
|
|
|
19099
|
-
|
|
19100
|
-
Can tasks be split into subtasks and run in parallel?
|
|
19101
|
+
Can tasks be split into background specialist work?
|
|
19101
19102
|
${enabledParallelExamples}
|
|
19102
19103
|
|
|
19103
|
-
Balance: respect dependencies, avoid parallelizing what must be sequential.
|
|
19104
|
+
Balance: respect dependencies, avoid parallelizing what must be sequential, and avoid overlapping write ownership.
|
|
19104
19105
|
|
|
19105
19106
|
### Context Isolation
|
|
19106
19107
|
If no specialist delegation is needed, consider \`subtask\` before doing
|
|
@@ -19113,6 +19114,8 @@ compact outcome.
|
|
|
19113
19114
|
Use \`subtask\` for focused investigation, bounded analysis, cleanup, or
|
|
19114
19115
|
verification across files/logs/messages.
|
|
19115
19116
|
|
|
19117
|
+
Prefer native background \`task(..., background: true)\` plus \`task_status\` for independent specialist lanes. Use \`subtask\` only for bounded parent-local context isolation when native background specialist scheduling is not the right fit.
|
|
19118
|
+
|
|
19116
19119
|
Do not use \`subtask\` for tiny tasks, open-ended work, interactive decisions,
|
|
19117
19120
|
work better handled by a named specialist, or cases where the parent must reason
|
|
19118
19121
|
over the details.
|
|
@@ -19121,21 +19124,28 @@ When calling \`subtask\`, give a self-contained prompt with objective,
|
|
|
19121
19124
|
constraints, relevant context, deliverable, and validation. Pass only clearly
|
|
19122
19125
|
relevant files. Wait for the summary, then integrate and verify it.
|
|
19123
19126
|
|
|
19124
|
-
### OpenCode
|
|
19125
|
-
-
|
|
19126
|
-
-
|
|
19127
|
-
-
|
|
19128
|
-
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
|
|
19127
|
+
### OpenCode scheduler model
|
|
19128
|
+
- Delegated specialists should be launched as background tasks whenever work can run independently: use \`task(..., background: true)\`.
|
|
19129
|
+
- A dispatch returns a task/session ID immediately; it does not mean completion.
|
|
19130
|
+
- Track each task ID with specialist, objective, state, and any advisory ownership/dependency labels from the dispatch plan.
|
|
19131
|
+
- Continue orchestration while tasks run: planning, scheduling independent lanes, preparing synthesis, and asking needed user questions.
|
|
19132
|
+
- Poll or wait with \`task_status(wait: true, timeout_ms: ...)\` before consuming outputs or starting dependent work.
|
|
19133
|
+
- Parallel background tasks are allowed only when their write scopes do not conflict.
|
|
19134
|
+
- Final response requires relevant tasks to be terminal and reconciled.
|
|
19135
|
+
|
|
19136
|
+
## 5. Dispatch
|
|
19137
|
+
1. Split work into independent and dependency-ordered lanes
|
|
19138
|
+
2. Plan advisory ownership for write-capable lanes
|
|
19139
|
+
3. Dispatch independent specialists as background tasks
|
|
19140
|
+
4. Record task IDs, state, and advisory ownership/dependency labels
|
|
19141
|
+
5. Continue only independent orchestration while jobs run
|
|
19142
|
+
6. Poll/wait for terminal results with \`task_status(wait: true, timeout_ms: ...)\`
|
|
19143
|
+
7. Reconcile results, resolve conflicts, and gate dependent lanes
|
|
19144
|
+
8. Dispatch follow-up jobs if needed
|
|
19145
|
+
9. Verify final state
|
|
19136
19146
|
|
|
19137
19147
|
### Session Reuse
|
|
19138
|
-
- Smartly reuse an available specialist session -
|
|
19148
|
+
- Smartly reuse an available specialist session - context reuse saves time and tokens
|
|
19139
19149
|
- When too much unrelated, and really needed, start a fresh session with the specialist
|
|
19140
19150
|
- If multiple remembered sessions fit, prefer the most recently used matching session.
|
|
19141
19151
|
- Prefer re-uses over creating new sessions all the time
|
|
@@ -19187,7 +19197,7 @@ When user's approach seems problematic:
|
|
|
19187
19197
|
**Bad:** "Great question! Let me think about the best approach here. I'm going to delegate to @librarian to check the latest Next.js documentation for the App Router, and then I'll implement the solution for you."
|
|
19188
19198
|
|
|
19189
19199
|
**Good:** "Checking Next.js App Router docs via @librarian..."
|
|
19190
|
-
[
|
|
19200
|
+
[continues scheduling or integration]
|
|
19191
19201
|
|
|
19192
19202
|
</Communication>
|
|
19193
19203
|
`;
|
|
@@ -22600,6 +22610,221 @@ function createDisplayNameMentionRewriter(config) {
|
|
|
22600
22610
|
return rewritten;
|
|
22601
22611
|
};
|
|
22602
22612
|
}
|
|
22613
|
+
// src/utils/task.ts
|
|
22614
|
+
function parseTaskIdFromTaskOutput(output) {
|
|
22615
|
+
const lines = output.split(/\r?\n/);
|
|
22616
|
+
for (const line of lines) {
|
|
22617
|
+
const trimmed = line.trim();
|
|
22618
|
+
const match = /^task_id:\s*([^\s()]+)(?:\s*\(.*)?$/.exec(trimmed);
|
|
22619
|
+
if (!match) {
|
|
22620
|
+
continue;
|
|
22621
|
+
}
|
|
22622
|
+
return match[1];
|
|
22623
|
+
}
|
|
22624
|
+
return;
|
|
22625
|
+
}
|
|
22626
|
+
function parseTaskLaunchOutput(output) {
|
|
22627
|
+
const taskID = parseTaskIdFromTaskOutput(output);
|
|
22628
|
+
const state = parseTaskStateFromOutput(output);
|
|
22629
|
+
if (!taskID || state !== "running")
|
|
22630
|
+
return;
|
|
22631
|
+
return {
|
|
22632
|
+
taskID,
|
|
22633
|
+
state,
|
|
22634
|
+
result: parseTaskResultFromOutput(output)
|
|
22635
|
+
};
|
|
22636
|
+
}
|
|
22637
|
+
function parseTaskStatusOutput(output) {
|
|
22638
|
+
const taskID = parseTaskIdFromTaskOutput(output);
|
|
22639
|
+
const state = parseTaskStateFromOutput(output);
|
|
22640
|
+
if (!taskID || !state)
|
|
22641
|
+
return;
|
|
22642
|
+
return {
|
|
22643
|
+
taskID,
|
|
22644
|
+
state,
|
|
22645
|
+
timedOut: state === "running" && /Timed out after \d+ms/i.test(output),
|
|
22646
|
+
result: parseTaskResultFromOutput(output)
|
|
22647
|
+
};
|
|
22648
|
+
}
|
|
22649
|
+
function parseTaskStateFromOutput(output) {
|
|
22650
|
+
for (const line of getTaskHeader(output).split(/\r?\n/)) {
|
|
22651
|
+
const match = /^state:\s*(running|completed|error|cancelled)\s*$/i.exec(line.trim());
|
|
22652
|
+
if (match)
|
|
22653
|
+
return match[1].toLowerCase();
|
|
22654
|
+
}
|
|
22655
|
+
return;
|
|
22656
|
+
}
|
|
22657
|
+
function parseTaskResultFromOutput(output) {
|
|
22658
|
+
const match = /<task_(result|error)>\s*([\s\S]*?)\s*<\/task_\1>/m.exec(output);
|
|
22659
|
+
const result = match?.[2]?.trim();
|
|
22660
|
+
return result || undefined;
|
|
22661
|
+
}
|
|
22662
|
+
function getTaskHeader(output) {
|
|
22663
|
+
const resultIndex = output.search(/<task_(?:result|error)>/);
|
|
22664
|
+
if (resultIndex === -1)
|
|
22665
|
+
return output;
|
|
22666
|
+
return output.slice(0, resultIndex);
|
|
22667
|
+
}
|
|
22668
|
+
|
|
22669
|
+
// src/utils/background-job-board.ts
|
|
22670
|
+
var TERMINAL_STATES = new Set([
|
|
22671
|
+
"completed",
|
|
22672
|
+
"error",
|
|
22673
|
+
"cancelled"
|
|
22674
|
+
]);
|
|
22675
|
+
var AGENT_PREFIX = {
|
|
22676
|
+
council: "cou",
|
|
22677
|
+
designer: "des",
|
|
22678
|
+
explorer: "exp",
|
|
22679
|
+
fixer: "fix",
|
|
22680
|
+
librarian: "lib",
|
|
22681
|
+
observer: "obs",
|
|
22682
|
+
oracle: "ora"
|
|
22683
|
+
};
|
|
22684
|
+
|
|
22685
|
+
class BackgroundJobBoard {
|
|
22686
|
+
jobs = new Map;
|
|
22687
|
+
counters = new Map;
|
|
22688
|
+
registerLaunch(input) {
|
|
22689
|
+
const now = input.now ?? Date.now();
|
|
22690
|
+
const existing = this.jobs.get(input.taskID);
|
|
22691
|
+
if (existing) {
|
|
22692
|
+
const updated = {
|
|
22693
|
+
...existing,
|
|
22694
|
+
agent: input.agent || existing.agent,
|
|
22695
|
+
description: input.description || existing.description,
|
|
22696
|
+
objective: input.objective ?? existing.objective,
|
|
22697
|
+
state: "running",
|
|
22698
|
+
timedOut: false,
|
|
22699
|
+
terminalUnreconciled: false,
|
|
22700
|
+
completedAt: undefined,
|
|
22701
|
+
resultSummary: undefined,
|
|
22702
|
+
updatedAt: now
|
|
22703
|
+
};
|
|
22704
|
+
this.jobs.set(input.taskID, updated);
|
|
22705
|
+
return updated;
|
|
22706
|
+
}
|
|
22707
|
+
const record = {
|
|
22708
|
+
taskID: input.taskID,
|
|
22709
|
+
parentSessionID: input.parentSessionID,
|
|
22710
|
+
agent: input.agent,
|
|
22711
|
+
description: input.description || `background ${input.agent} task`,
|
|
22712
|
+
objective: input.objective,
|
|
22713
|
+
state: "running",
|
|
22714
|
+
timedOut: false,
|
|
22715
|
+
terminalUnreconciled: false,
|
|
22716
|
+
launchedAt: now,
|
|
22717
|
+
updatedAt: now,
|
|
22718
|
+
alias: this.nextAlias(input.parentSessionID, input.agent)
|
|
22719
|
+
};
|
|
22720
|
+
this.jobs.set(input.taskID, record);
|
|
22721
|
+
return record;
|
|
22722
|
+
}
|
|
22723
|
+
updateStatus(input) {
|
|
22724
|
+
const existing = this.jobs.get(input.taskID);
|
|
22725
|
+
if (!existing)
|
|
22726
|
+
return;
|
|
22727
|
+
const now = input.now ?? Date.now();
|
|
22728
|
+
const terminal = TERMINAL_STATES.has(input.state);
|
|
22729
|
+
const updated = {
|
|
22730
|
+
...existing,
|
|
22731
|
+
state: input.state,
|
|
22732
|
+
timedOut: input.timedOut ?? false,
|
|
22733
|
+
terminalUnreconciled: terminal ? true : existing.terminalUnreconciled,
|
|
22734
|
+
updatedAt: now,
|
|
22735
|
+
completedAt: terminal ? existing.completedAt ?? now : existing.completedAt,
|
|
22736
|
+
resultSummary: input.resultSummary ?? existing.resultSummary
|
|
22737
|
+
};
|
|
22738
|
+
this.jobs.set(input.taskID, updated);
|
|
22739
|
+
return updated;
|
|
22740
|
+
}
|
|
22741
|
+
updateFromStatusOutput(output) {
|
|
22742
|
+
const status = parseTaskStatusOutput(output);
|
|
22743
|
+
if (!status)
|
|
22744
|
+
return;
|
|
22745
|
+
return this.updateStatus({
|
|
22746
|
+
taskID: status.taskID,
|
|
22747
|
+
state: status.state,
|
|
22748
|
+
timedOut: status.timedOut,
|
|
22749
|
+
resultSummary: status.result
|
|
22750
|
+
});
|
|
22751
|
+
}
|
|
22752
|
+
markReconciled(taskID, now = Date.now()) {
|
|
22753
|
+
const existing = this.jobs.get(taskID);
|
|
22754
|
+
if (!existing)
|
|
22755
|
+
return;
|
|
22756
|
+
if (!existing.terminalUnreconciled && !TERMINAL_STATES.has(existing.state)) {
|
|
22757
|
+
return;
|
|
22758
|
+
}
|
|
22759
|
+
const updated = {
|
|
22760
|
+
...existing,
|
|
22761
|
+
state: "reconciled",
|
|
22762
|
+
terminalUnreconciled: false,
|
|
22763
|
+
updatedAt: now
|
|
22764
|
+
};
|
|
22765
|
+
this.jobs.set(taskID, updated);
|
|
22766
|
+
return updated;
|
|
22767
|
+
}
|
|
22768
|
+
get(taskID) {
|
|
22769
|
+
return this.jobs.get(taskID);
|
|
22770
|
+
}
|
|
22771
|
+
list(parentSessionID) {
|
|
22772
|
+
const jobs = [...this.jobs.values()];
|
|
22773
|
+
const filtered = parentSessionID ? jobs.filter((job) => job.parentSessionID === parentSessionID) : jobs;
|
|
22774
|
+
return filtered.sort((a, b) => a.launchedAt - b.launchedAt);
|
|
22775
|
+
}
|
|
22776
|
+
hasRunning(parentSessionID) {
|
|
22777
|
+
return this.list(parentSessionID).some((job) => job.state === "running");
|
|
22778
|
+
}
|
|
22779
|
+
hasTerminalUnreconciled(parentSessionID) {
|
|
22780
|
+
return this.list(parentSessionID).some((job) => job.terminalUnreconciled);
|
|
22781
|
+
}
|
|
22782
|
+
formatForPrompt(parentSessionID) {
|
|
22783
|
+
const jobs = this.list(parentSessionID).filter((job) => job.state === "running" || job.terminalUnreconciled);
|
|
22784
|
+
if (jobs.length === 0)
|
|
22785
|
+
return;
|
|
22786
|
+
return [
|
|
22787
|
+
"### Background Job Board",
|
|
22788
|
+
"Use task_status before consuming running jobs. Reconcile terminal jobs before final response.",
|
|
22789
|
+
"",
|
|
22790
|
+
...jobs.map(formatJob)
|
|
22791
|
+
].join(`
|
|
22792
|
+
`);
|
|
22793
|
+
}
|
|
22794
|
+
clearParent(parentSessionID) {
|
|
22795
|
+
for (const job of this.list(parentSessionID)) {
|
|
22796
|
+
this.jobs.delete(job.taskID);
|
|
22797
|
+
}
|
|
22798
|
+
}
|
|
22799
|
+
drop(taskID) {
|
|
22800
|
+
this.jobs.delete(taskID);
|
|
22801
|
+
}
|
|
22802
|
+
nextAlias(parentSessionID, agent) {
|
|
22803
|
+
const prefix2 = AGENT_PREFIX[agent] ?? (agent.slice(0, 3) || "job");
|
|
22804
|
+
const key = `${parentSessionID}:${prefix2}`;
|
|
22805
|
+
const next = (this.counters.get(key) ?? 0) + 1;
|
|
22806
|
+
this.counters.set(key, next);
|
|
22807
|
+
return `${prefix2}-${next}`;
|
|
22808
|
+
}
|
|
22809
|
+
}
|
|
22810
|
+
function formatJob(job) {
|
|
22811
|
+
const status = job.terminalUnreconciled ? `${job.state}, unreconciled` : job.timedOut ? `${job.state}, timed out` : job.state;
|
|
22812
|
+
const lines = [
|
|
22813
|
+
`- ${job.alias} / ${job.taskID} / ${job.agent} / ${status}`,
|
|
22814
|
+
` Objective: ${job.objective || job.description}`
|
|
22815
|
+
];
|
|
22816
|
+
if (job.resultSummary && job.terminalUnreconciled) {
|
|
22817
|
+
lines.push(` Result: ${singleLine(job.resultSummary)}`);
|
|
22818
|
+
}
|
|
22819
|
+
return lines.join(`
|
|
22820
|
+
`);
|
|
22821
|
+
}
|
|
22822
|
+
function singleLine(value) {
|
|
22823
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
22824
|
+
if (normalized.length <= 160)
|
|
22825
|
+
return normalized;
|
|
22826
|
+
return `${normalized.slice(0, 157)}...`;
|
|
22827
|
+
}
|
|
22603
22828
|
// src/utils/internal-initiator.ts
|
|
22604
22829
|
var SLIM_INTERNAL_INITIATOR_MARKER = "<!-- SLIM_INTERNAL_INITIATOR -->";
|
|
22605
22830
|
function isRecord(value) {
|
|
@@ -22851,19 +23076,6 @@ function formatContextFiles(files, options) {
|
|
|
22851
23076
|
const rendered = shown.map((file) => `${file.path} (${file.lineCount} lines)`);
|
|
22852
23077
|
return `${rendered.join(", ")}${rest > 0 ? ` (+${rest} more)` : ""}`;
|
|
22853
23078
|
}
|
|
22854
|
-
// src/utils/task.ts
|
|
22855
|
-
function parseTaskIdFromTaskOutput(output) {
|
|
22856
|
-
const lines = output.split(/\r?\n/);
|
|
22857
|
-
for (const line of lines) {
|
|
22858
|
-
const trimmed = line.trim();
|
|
22859
|
-
const match = /^task_id:\s*([^\s()]+)(?:\s*\(.*)?$/.exec(trimmed);
|
|
22860
|
-
if (!match) {
|
|
22861
|
-
continue;
|
|
22862
|
-
}
|
|
22863
|
-
return match[1];
|
|
22864
|
-
}
|
|
22865
|
-
return;
|
|
22866
|
-
}
|
|
22867
23079
|
// src/utils/zip-extractor.ts
|
|
22868
23080
|
import { spawnSync } from "node:child_process";
|
|
22869
23081
|
import { release } from "node:os";
|
|
@@ -23677,14 +23889,13 @@ function createPhaseReminderHook() {
|
|
|
23677
23889
|
if (originalText.includes(SLIM_INTERNAL_INITIATOR_MARKER)) {
|
|
23678
23890
|
return;
|
|
23679
23891
|
}
|
|
23680
|
-
if (
|
|
23892
|
+
if (lastUserMessage.parts.some((p) => p.text?.includes(PHASE_REMINDER))) {
|
|
23681
23893
|
return;
|
|
23682
23894
|
}
|
|
23683
|
-
lastUserMessage.parts
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
|
|
23687
|
-
${PHASE_REMINDER}`;
|
|
23895
|
+
lastUserMessage.parts.push({
|
|
23896
|
+
type: "text",
|
|
23897
|
+
text: PHASE_REMINDER
|
|
23898
|
+
});
|
|
23688
23899
|
}
|
|
23689
23900
|
};
|
|
23690
23901
|
}
|
|
@@ -23720,8 +23931,338 @@ function createPostFileToolNudgeHook(options = {}) {
|
|
|
23720
23931
|
}
|
|
23721
23932
|
};
|
|
23722
23933
|
}
|
|
23934
|
+
// src/hooks/session-goal/index.ts
|
|
23935
|
+
import * as fs7 from "node:fs/promises";
|
|
23936
|
+
|
|
23937
|
+
// src/interview/document.ts
|
|
23938
|
+
import * as fsSync from "node:fs";
|
|
23939
|
+
import * as fs6 from "node:fs/promises";
|
|
23940
|
+
import * as path9 from "node:path";
|
|
23941
|
+
var DEFAULT_OUTPUT_FOLDER = "interview";
|
|
23942
|
+
function normalizeOutputFolder(outputFolder) {
|
|
23943
|
+
const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
|
|
23944
|
+
return normalized || DEFAULT_OUTPUT_FOLDER;
|
|
23945
|
+
}
|
|
23946
|
+
function createInterviewDirectoryPath(directory, outputFolder) {
|
|
23947
|
+
return path9.join(directory, normalizeOutputFolder(outputFolder));
|
|
23948
|
+
}
|
|
23949
|
+
function createInterviewFilePath(directory, outputFolder, idea) {
|
|
23950
|
+
const fileName = `${slugify(idea) || "interview"}.md`;
|
|
23951
|
+
return path9.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
|
|
23952
|
+
}
|
|
23953
|
+
function relativeInterviewPath(directory, filePath) {
|
|
23954
|
+
return path9.relative(directory, filePath) || path9.basename(filePath);
|
|
23955
|
+
}
|
|
23956
|
+
function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
23957
|
+
const trimmed = value.trim();
|
|
23958
|
+
if (!trimmed) {
|
|
23959
|
+
return null;
|
|
23960
|
+
}
|
|
23961
|
+
const outputDir = createInterviewDirectoryPath(directory, outputFolder);
|
|
23962
|
+
const candidates = new Set;
|
|
23963
|
+
const resolvedRoot = path9.resolve(directory);
|
|
23964
|
+
if (path9.isAbsolute(trimmed)) {
|
|
23965
|
+
candidates.add(trimmed);
|
|
23966
|
+
} else {
|
|
23967
|
+
candidates.add(path9.resolve(directory, trimmed));
|
|
23968
|
+
candidates.add(path9.join(outputDir, trimmed));
|
|
23969
|
+
if (!trimmed.endsWith(".md")) {
|
|
23970
|
+
candidates.add(path9.join(outputDir, `${trimmed}.md`));
|
|
23971
|
+
}
|
|
23972
|
+
}
|
|
23973
|
+
for (const candidate of candidates) {
|
|
23974
|
+
if (path9.extname(candidate) !== ".md") {
|
|
23975
|
+
continue;
|
|
23976
|
+
}
|
|
23977
|
+
const resolved = path9.resolve(candidate);
|
|
23978
|
+
if (!resolved.startsWith(resolvedRoot + path9.sep) && resolved !== resolvedRoot) {
|
|
23979
|
+
continue;
|
|
23980
|
+
}
|
|
23981
|
+
if (fsSync.existsSync(candidate)) {
|
|
23982
|
+
return candidate;
|
|
23983
|
+
}
|
|
23984
|
+
}
|
|
23985
|
+
return null;
|
|
23986
|
+
}
|
|
23987
|
+
function slugify(value) {
|
|
23988
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
23989
|
+
}
|
|
23990
|
+
function extractHistorySection(document) {
|
|
23991
|
+
const marker = `## Q&A history
|
|
23992
|
+
|
|
23993
|
+
`;
|
|
23994
|
+
const index = document.indexOf(marker);
|
|
23995
|
+
return index >= 0 ? document.slice(index + marker.length).trim() : "";
|
|
23996
|
+
}
|
|
23997
|
+
function extractSummarySection(document) {
|
|
23998
|
+
const marker = `## Current spec
|
|
23999
|
+
|
|
24000
|
+
`;
|
|
24001
|
+
const historyMarker = `
|
|
24002
|
+
|
|
24003
|
+
## Q&A history`;
|
|
24004
|
+
const start = document.indexOf(marker);
|
|
24005
|
+
if (start < 0) {
|
|
24006
|
+
return "";
|
|
24007
|
+
}
|
|
24008
|
+
const summaryStart = start + marker.length;
|
|
24009
|
+
const summaryEnd = document.indexOf(historyMarker, summaryStart);
|
|
24010
|
+
return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
|
|
24011
|
+
}
|
|
24012
|
+
function extractTitle(document) {
|
|
24013
|
+
const match = document.match(/^#\s+(.+)$/m);
|
|
24014
|
+
return match?.[1]?.trim() ?? "";
|
|
24015
|
+
}
|
|
24016
|
+
function buildInterviewDocument(idea, summary, history, meta) {
|
|
24017
|
+
const normalizedSummary = summary.trim() || "Waiting for interview answers.";
|
|
24018
|
+
const normalizedHistory = history.trim() || "No answers yet.";
|
|
24019
|
+
const frontmatter = meta?.sessionID ? [
|
|
24020
|
+
"---",
|
|
24021
|
+
`sessionID: ${meta.sessionID}`,
|
|
24022
|
+
`baseMessageCount: ${meta.baseMessageCount ?? 0}`,
|
|
24023
|
+
`updatedAt: ${new Date().toISOString()}`,
|
|
24024
|
+
"---",
|
|
24025
|
+
""
|
|
24026
|
+
].join(`
|
|
24027
|
+
`) : "";
|
|
24028
|
+
return [
|
|
24029
|
+
frontmatter,
|
|
24030
|
+
`# ${idea}`,
|
|
24031
|
+
"",
|
|
24032
|
+
"## Current spec",
|
|
24033
|
+
"",
|
|
24034
|
+
normalizedSummary,
|
|
24035
|
+
"",
|
|
24036
|
+
"## Q&A history",
|
|
24037
|
+
"",
|
|
24038
|
+
normalizedHistory,
|
|
24039
|
+
""
|
|
24040
|
+
].join(`
|
|
24041
|
+
`);
|
|
24042
|
+
}
|
|
24043
|
+
function parseFrontmatter(content) {
|
|
24044
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
24045
|
+
if (!match)
|
|
24046
|
+
return null;
|
|
24047
|
+
const result = {};
|
|
24048
|
+
for (const line of match[1].split(`
|
|
24049
|
+
`)) {
|
|
24050
|
+
const colonIdx = line.indexOf(":");
|
|
24051
|
+
if (colonIdx > 0) {
|
|
24052
|
+
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
24053
|
+
}
|
|
24054
|
+
}
|
|
24055
|
+
return result;
|
|
24056
|
+
}
|
|
24057
|
+
async function ensureInterviewFile(record) {
|
|
24058
|
+
await fs6.mkdir(path9.dirname(record.markdownPath), { recursive: true });
|
|
24059
|
+
try {
|
|
24060
|
+
await fs6.access(record.markdownPath);
|
|
24061
|
+
} catch {
|
|
24062
|
+
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
|
|
24063
|
+
sessionID: record.sessionID,
|
|
24064
|
+
baseMessageCount: record.baseMessageCount
|
|
24065
|
+
}), "utf8");
|
|
24066
|
+
}
|
|
24067
|
+
}
|
|
24068
|
+
async function readInterviewDocument(record) {
|
|
24069
|
+
try {
|
|
24070
|
+
return await fs6.readFile(record.markdownPath, "utf8");
|
|
24071
|
+
} catch {}
|
|
24072
|
+
await ensureInterviewFile(record);
|
|
24073
|
+
return fs6.readFile(record.markdownPath, "utf8");
|
|
24074
|
+
}
|
|
24075
|
+
async function rewriteInterviewDocument(record, summary) {
|
|
24076
|
+
const existing = await readInterviewDocument(record);
|
|
24077
|
+
const history = extractHistorySection(existing);
|
|
24078
|
+
const next = buildInterviewDocument(record.idea, summary, history, {
|
|
24079
|
+
sessionID: record.sessionID,
|
|
24080
|
+
baseMessageCount: record.baseMessageCount
|
|
24081
|
+
});
|
|
24082
|
+
await fs6.writeFile(record.markdownPath, next, "utf8");
|
|
24083
|
+
return next;
|
|
24084
|
+
}
|
|
24085
|
+
async function appendInterviewAnswers(record, questions, answers) {
|
|
24086
|
+
const existing = await readInterviewDocument(record);
|
|
24087
|
+
const summary = extractSummarySection(existing);
|
|
24088
|
+
const history = extractHistorySection(existing);
|
|
24089
|
+
const questionMap = new Map(questions.map((question) => [question.id, question]));
|
|
24090
|
+
const appended = answers.map((answer) => {
|
|
24091
|
+
const question = questionMap.get(answer.questionId);
|
|
24092
|
+
return question ? `Q: ${question.question}
|
|
24093
|
+
A: ${answer.answer.trim()}` : null;
|
|
24094
|
+
}).filter((value) => value !== null).join(`
|
|
24095
|
+
|
|
24096
|
+
`);
|
|
24097
|
+
const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
|
|
24098
|
+
|
|
24099
|
+
`);
|
|
24100
|
+
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
|
|
24101
|
+
sessionID: record.sessionID,
|
|
24102
|
+
baseMessageCount: record.baseMessageCount
|
|
24103
|
+
}), "utf8");
|
|
24104
|
+
}
|
|
24105
|
+
|
|
24106
|
+
// src/hooks/session-goal/index.ts
|
|
24107
|
+
var COMMAND_NAME = "goal";
|
|
24108
|
+
var MAX_GOAL_LENGTH = 4000;
|
|
24109
|
+
function normalizeGoalText(text) {
|
|
24110
|
+
return text.trim().replace(/\s+/g, " ").slice(0, MAX_GOAL_LENGTH);
|
|
24111
|
+
}
|
|
24112
|
+
function trimGoalText(text) {
|
|
24113
|
+
return text.trim().slice(0, MAX_GOAL_LENGTH);
|
|
24114
|
+
}
|
|
24115
|
+
function pushText(output, text) {
|
|
24116
|
+
output.parts.push(createInternalAgentTextPart(text));
|
|
24117
|
+
}
|
|
24118
|
+
function formatGoal(state, inherited) {
|
|
24119
|
+
const tag = inherited ? "parent_goal" : "active_goal";
|
|
24120
|
+
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.";
|
|
24121
|
+
return `<${tag}>
|
|
24122
|
+
Objective: ${state.text}
|
|
24123
|
+
${guidance}
|
|
24124
|
+
</${tag}>`;
|
|
24125
|
+
}
|
|
24126
|
+
async function readInterviewGoal(directory, outputFolder, value) {
|
|
24127
|
+
try {
|
|
24128
|
+
const sourcePath = resolveExistingInterviewPath(directory, outputFolder, value);
|
|
24129
|
+
if (!sourcePath)
|
|
24130
|
+
return null;
|
|
24131
|
+
const content = await fs7.readFile(sourcePath, "utf8");
|
|
24132
|
+
const title = extractTitle(content);
|
|
24133
|
+
const summary = extractSummarySection(content);
|
|
24134
|
+
const text = trimGoalText([title ? `From interview: ${title}` : "", summary].filter(Boolean).join(`
|
|
24135
|
+
|
|
24136
|
+
`));
|
|
24137
|
+
return text ? { text, sourcePath } : null;
|
|
24138
|
+
} catch {
|
|
24139
|
+
return null;
|
|
24140
|
+
}
|
|
24141
|
+
}
|
|
24142
|
+
function resolveGoal(goals, sessionID) {
|
|
24143
|
+
const seen = new Set;
|
|
24144
|
+
let currentSessionID = sessionID;
|
|
24145
|
+
let inherited = false;
|
|
24146
|
+
while (true) {
|
|
24147
|
+
if (seen.has(currentSessionID)) {
|
|
24148
|
+
goals.delete(sessionID);
|
|
24149
|
+
return null;
|
|
24150
|
+
}
|
|
24151
|
+
seen.add(currentSessionID);
|
|
24152
|
+
const goal = goals.get(currentSessionID);
|
|
24153
|
+
if (!goal) {
|
|
24154
|
+
goals.delete(sessionID);
|
|
24155
|
+
return null;
|
|
24156
|
+
}
|
|
24157
|
+
if (!goal.inheritedFrom) {
|
|
24158
|
+
return { goal, inherited };
|
|
24159
|
+
}
|
|
24160
|
+
inherited = true;
|
|
24161
|
+
currentSessionID = goal.inheritedFrom;
|
|
24162
|
+
}
|
|
24163
|
+
}
|
|
24164
|
+
function createSessionGoalHook(ctx, config, options) {
|
|
24165
|
+
const goals = new Map;
|
|
24166
|
+
const outputFolder = config.interview?.outputFolder ?? "interview";
|
|
24167
|
+
return {
|
|
24168
|
+
registerCommand: (opencodeConfig) => {
|
|
24169
|
+
const commandConfig = opencodeConfig.command;
|
|
24170
|
+
if (commandConfig?.[COMMAND_NAME])
|
|
24171
|
+
return;
|
|
24172
|
+
if (!opencodeConfig.command)
|
|
24173
|
+
opencodeConfig.command = {};
|
|
24174
|
+
opencodeConfig.command[COMMAND_NAME] = {
|
|
24175
|
+
template: "Set or show the current session goal",
|
|
24176
|
+
description: "Pin a session objective that keeps todos, delegation, and verification aligned"
|
|
24177
|
+
};
|
|
24178
|
+
},
|
|
24179
|
+
handleCommandExecuteBefore: async (input, output) => {
|
|
24180
|
+
if (input.command !== COMMAND_NAME)
|
|
24181
|
+
return;
|
|
24182
|
+
output.parts.length = 0;
|
|
24183
|
+
const args = input.arguments.trim();
|
|
24184
|
+
if (!args) {
|
|
24185
|
+
const resolved = resolveGoal(goals, input.sessionID);
|
|
24186
|
+
pushText(output, resolved ? `Active goal:
|
|
24187
|
+
${resolved.goal.text}
|
|
24188
|
+
|
|
24189
|
+
Use todos for execution steps. Auto-continuation continues only while todos remain.` : "No active goal. Set one with /goal <objective>.");
|
|
24190
|
+
return;
|
|
24191
|
+
}
|
|
24192
|
+
if (args === "clear") {
|
|
24193
|
+
goals.delete(input.sessionID);
|
|
24194
|
+
pushText(output, "Cleared the active goal for this session.");
|
|
24195
|
+
return;
|
|
24196
|
+
}
|
|
24197
|
+
if (args.startsWith("from ")) {
|
|
24198
|
+
const value = args.slice("from ".length).trim();
|
|
24199
|
+
const interviewGoal = await readInterviewGoal(ctx.directory, outputFolder, value);
|
|
24200
|
+
if (!interviewGoal) {
|
|
24201
|
+
pushText(output, `Could not find a readable interview spec for "${value}".`);
|
|
24202
|
+
return;
|
|
24203
|
+
}
|
|
24204
|
+
goals.set(input.sessionID, {
|
|
24205
|
+
text: interviewGoal.text,
|
|
24206
|
+
source: "interview",
|
|
24207
|
+
sourcePath: interviewGoal.sourcePath,
|
|
24208
|
+
createdAt: Date.now()
|
|
24209
|
+
});
|
|
24210
|
+
pushText(output, `Set active goal from interview:
|
|
24211
|
+
${interviewGoal.text}`);
|
|
24212
|
+
return;
|
|
24213
|
+
}
|
|
24214
|
+
const text = normalizeGoalText(args);
|
|
24215
|
+
goals.set(input.sessionID, {
|
|
24216
|
+
text,
|
|
24217
|
+
source: "manual",
|
|
24218
|
+
createdAt: Date.now()
|
|
24219
|
+
});
|
|
24220
|
+
pushText(output, `Set active goal:
|
|
24221
|
+
${text}`);
|
|
24222
|
+
},
|
|
24223
|
+
handleEvent: (input) => {
|
|
24224
|
+
const event = input.event;
|
|
24225
|
+
if (event.type === "session.created") {
|
|
24226
|
+
const info = event.properties?.info;
|
|
24227
|
+
if (!info?.id || !info.parentID)
|
|
24228
|
+
return;
|
|
24229
|
+
const parentGoal = goals.get(info.parentID);
|
|
24230
|
+
if (!parentGoal)
|
|
24231
|
+
return;
|
|
24232
|
+
goals.set(info.id, {
|
|
24233
|
+
inheritedFrom: info.parentID,
|
|
24234
|
+
createdAt: Date.now(),
|
|
24235
|
+
text: ""
|
|
24236
|
+
});
|
|
24237
|
+
return;
|
|
24238
|
+
}
|
|
24239
|
+
if (event.type === "session.deleted") {
|
|
24240
|
+
const props = event.properties;
|
|
24241
|
+
const sessionID = props?.info?.id ?? props?.sessionID;
|
|
24242
|
+
if (sessionID)
|
|
24243
|
+
goals.delete(sessionID);
|
|
24244
|
+
}
|
|
24245
|
+
},
|
|
24246
|
+
handleSystemTransform: (input, output) => {
|
|
24247
|
+
if (!input.sessionID)
|
|
24248
|
+
return;
|
|
24249
|
+
const resolved = resolveGoal(goals, input.sessionID);
|
|
24250
|
+
if (!resolved)
|
|
24251
|
+
return;
|
|
24252
|
+
const agentName = options?.getAgentName?.(input.sessionID);
|
|
24253
|
+
const { goal, inherited } = resolved;
|
|
24254
|
+
if (!inherited && agentName && agentName !== "orchestrator")
|
|
24255
|
+
return;
|
|
24256
|
+
const block = formatGoal(goal, inherited);
|
|
24257
|
+
if (output.system.some((entry) => entry.includes(block)))
|
|
24258
|
+
return;
|
|
24259
|
+
output.system.push(block);
|
|
24260
|
+
},
|
|
24261
|
+
getGoal: (sessionID) => resolveGoal(goals, sessionID)?.goal
|
|
24262
|
+
};
|
|
24263
|
+
}
|
|
23723
24264
|
// src/hooks/task-session-manager/index.ts
|
|
23724
|
-
import
|
|
24265
|
+
import path10 from "node:path";
|
|
23725
24266
|
var AGENT_NAME_SET = new Set([
|
|
23726
24267
|
"orchestrator",
|
|
23727
24268
|
"oracle",
|
|
@@ -23736,6 +24277,9 @@ var AGENT_NAME_SET = new Set([
|
|
|
23736
24277
|
var MAX_PENDING_TASK_CALLS = 100;
|
|
23737
24278
|
var RESUMABLE_SESSIONS_START = "<resumable_sessions>";
|
|
23738
24279
|
var RESUMABLE_SESSIONS_END = "</resumable_sessions>";
|
|
24280
|
+
var BACKGROUND_COMPLETION_COMPLETED = /^Background task completed: /;
|
|
24281
|
+
var BACKGROUND_COMPLETION_FAILED = /^Background task failed: /;
|
|
24282
|
+
var MAX_PROCESSED_INJECTED_COMPLETIONS = 500;
|
|
23739
24283
|
function isAgentName(value) {
|
|
23740
24284
|
return typeof value === "string" && AGENT_NAME_SET.has(value);
|
|
23741
24285
|
}
|
|
@@ -23746,11 +24290,11 @@ function extractPath(output) {
|
|
|
23746
24290
|
return /<path>([^<]+)<\/path>/.exec(output)?.[1];
|
|
23747
24291
|
}
|
|
23748
24292
|
function normalizePath(root, file) {
|
|
23749
|
-
const
|
|
23750
|
-
if (!
|
|
24293
|
+
const relative2 = path10.relative(root, file);
|
|
24294
|
+
if (!relative2 || relative2.startsWith("..") || path10.isAbsolute(relative2)) {
|
|
23751
24295
|
return file;
|
|
23752
24296
|
}
|
|
23753
|
-
return
|
|
24297
|
+
return relative2;
|
|
23754
24298
|
}
|
|
23755
24299
|
function extractReadFiles(root, output) {
|
|
23756
24300
|
if (typeof output.output !== "string")
|
|
@@ -23779,10 +24323,14 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23779
24323
|
readContextMinLines: options.readContextMinLines,
|
|
23780
24324
|
readContextMaxFiles: options.readContextMaxFiles
|
|
23781
24325
|
});
|
|
24326
|
+
const backgroundJobBoard = options.backgroundJobBoard ?? new BackgroundJobBoard;
|
|
23782
24327
|
const pendingCalls = new Map;
|
|
23783
24328
|
const pendingCallOrder = [];
|
|
23784
24329
|
const contextByTask = new Map;
|
|
23785
24330
|
const pendingManagedTaskIds = new Set;
|
|
24331
|
+
const terminalJobsInjectedByParent = new Map;
|
|
24332
|
+
const processedInjectedCompletions = new Set;
|
|
24333
|
+
const processedInjectedCompletionOrder = [];
|
|
23786
24334
|
let anonymousPendingCallId = 0;
|
|
23787
24335
|
function addTaskContext(taskId, files) {
|
|
23788
24336
|
if (files.length === 0)
|
|
@@ -23826,6 +24374,62 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23826
24374
|
}
|
|
23827
24375
|
}
|
|
23828
24376
|
}
|
|
24377
|
+
function updateBackgroundJobFromOutput(output) {
|
|
24378
|
+
if (typeof output !== "string")
|
|
24379
|
+
return;
|
|
24380
|
+
const status = parseTaskStatusOutput(output);
|
|
24381
|
+
if (!status)
|
|
24382
|
+
return;
|
|
24383
|
+
const updated = backgroundJobBoard.updateStatus({
|
|
24384
|
+
taskID: status.taskID,
|
|
24385
|
+
state: status.state,
|
|
24386
|
+
timedOut: status.timedOut,
|
|
24387
|
+
resultSummary: status.result
|
|
24388
|
+
});
|
|
24389
|
+
if (!updated)
|
|
24390
|
+
return;
|
|
24391
|
+
if (updated.terminalUnreconciled) {
|
|
24392
|
+
pendingManagedTaskIds.delete(updated.taskID);
|
|
24393
|
+
contextByTask.delete(updated.taskID);
|
|
24394
|
+
pruneContext();
|
|
24395
|
+
}
|
|
24396
|
+
return updated;
|
|
24397
|
+
}
|
|
24398
|
+
function updateFromInjectedCompletion(part, message, messageIndex, partIndex) {
|
|
24399
|
+
if (part.type !== "text" || typeof part.text !== "string") {
|
|
24400
|
+
return;
|
|
24401
|
+
}
|
|
24402
|
+
const isCompleted = BACKGROUND_COMPLETION_COMPLETED.test(part.text);
|
|
24403
|
+
const isFailed = BACKGROUND_COMPLETION_FAILED.test(part.text);
|
|
24404
|
+
if (part.synthetic !== true || !isCompleted && !isFailed) {
|
|
24405
|
+
return;
|
|
24406
|
+
}
|
|
24407
|
+
const status = parseTaskStatusOutput(part.text);
|
|
24408
|
+
if (!status)
|
|
24409
|
+
return;
|
|
24410
|
+
if (isCompleted && status.state !== "completed")
|
|
24411
|
+
return;
|
|
24412
|
+
if (isFailed && status.state !== "error")
|
|
24413
|
+
return;
|
|
24414
|
+
const occurrenceId = typeof part.id === "string" ? part.id : typeof message.info.id === "string" ? `${message.info.id}:${partIndex}` : `${message.info.sessionID ?? "unknown"}:${messageIndex}:${partIndex}`;
|
|
24415
|
+
if (processedInjectedCompletions.has(occurrenceId))
|
|
24416
|
+
return;
|
|
24417
|
+
const updated = updateBackgroundJobFromOutput(part.text);
|
|
24418
|
+
if (!updated)
|
|
24419
|
+
return;
|
|
24420
|
+
rememberProcessedInjectedCompletion(occurrenceId);
|
|
24421
|
+
return updated;
|
|
24422
|
+
}
|
|
24423
|
+
function rememberProcessedInjectedCompletion(signature) {
|
|
24424
|
+
processedInjectedCompletions.add(signature);
|
|
24425
|
+
processedInjectedCompletionOrder.push(signature);
|
|
24426
|
+
while (processedInjectedCompletionOrder.length > MAX_PROCESSED_INJECTED_COMPLETIONS) {
|
|
24427
|
+
const evicted = processedInjectedCompletionOrder.shift();
|
|
24428
|
+
if (!evicted)
|
|
24429
|
+
break;
|
|
24430
|
+
processedInjectedCompletions.delete(evicted);
|
|
24431
|
+
}
|
|
24432
|
+
}
|
|
23829
24433
|
function isMissingRememberedSessionError(output) {
|
|
23830
24434
|
const firstLine = output.split(/\r?\n/, 1)[0]?.trim().toLowerCase() ?? "";
|
|
23831
24435
|
return firstLine.startsWith("[error]") && firstLine.includes("session") && (firstLine.includes("not found") || firstLine.includes("no session"));
|
|
@@ -23865,6 +24469,25 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23865
24469
|
return;
|
|
23866
24470
|
return pendingCallOrder.find((callId) => pendingCalls.get(callId)?.parentSessionId === parentSessionId);
|
|
23867
24471
|
}
|
|
24472
|
+
function rememberInjectedTerminalJobs(parentSessionID) {
|
|
24473
|
+
const taskIDs = backgroundJobBoard.list(parentSessionID).filter((job) => job.terminalUnreconciled).map((job) => job.taskID);
|
|
24474
|
+
if (taskIDs.length === 0)
|
|
24475
|
+
return;
|
|
24476
|
+
const existing = terminalJobsInjectedByParent.get(parentSessionID) ?? new Set;
|
|
24477
|
+
for (const taskID of taskIDs) {
|
|
24478
|
+
existing.add(taskID);
|
|
24479
|
+
}
|
|
24480
|
+
terminalJobsInjectedByParent.set(parentSessionID, existing);
|
|
24481
|
+
}
|
|
24482
|
+
function reconcileInjectedTerminalJobs(parentSessionID) {
|
|
24483
|
+
const taskIDs = terminalJobsInjectedByParent.get(parentSessionID);
|
|
24484
|
+
if (!taskIDs)
|
|
24485
|
+
return;
|
|
24486
|
+
for (const taskID of taskIDs) {
|
|
24487
|
+
backgroundJobBoard.markReconciled(taskID);
|
|
24488
|
+
}
|
|
24489
|
+
terminalJobsInjectedByParent.delete(parentSessionID);
|
|
24490
|
+
}
|
|
23868
24491
|
return {
|
|
23869
24492
|
"tool.execute.before": async (input, output) => {
|
|
23870
24493
|
if (input.tool.toLowerCase() !== "task")
|
|
@@ -23914,11 +24537,31 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23914
24537
|
}
|
|
23915
24538
|
return;
|
|
23916
24539
|
}
|
|
24540
|
+
if (input.tool.toLowerCase() === "task_status") {
|
|
24541
|
+
if (!input.sessionID || !options.shouldManageSession(input.sessionID)) {
|
|
24542
|
+
return;
|
|
24543
|
+
}
|
|
24544
|
+
updateBackgroundJobFromOutput(output.output);
|
|
24545
|
+
return;
|
|
24546
|
+
}
|
|
23917
24547
|
if (input.tool.toLowerCase() !== "task")
|
|
23918
24548
|
return;
|
|
23919
24549
|
const pending = takePendingCall(input.callID, input.sessionID);
|
|
23920
24550
|
if (!pending || typeof output.output !== "string")
|
|
23921
24551
|
return;
|
|
24552
|
+
const launch = parseTaskLaunchOutput(output.output);
|
|
24553
|
+
if (launch) {
|
|
24554
|
+
backgroundJobBoard.registerLaunch({
|
|
24555
|
+
taskID: launch.taskID,
|
|
24556
|
+
parentSessionID: pending.parentSessionId,
|
|
24557
|
+
agent: pending.agentType,
|
|
24558
|
+
description: pending.label,
|
|
24559
|
+
objective: pending.label
|
|
24560
|
+
});
|
|
24561
|
+
sessionManager.drop(pending.parentSessionId, pending.agentType, pending.resumedTaskId ?? launch.taskID);
|
|
24562
|
+
pendingManagedTaskIds.add(launch.taskID);
|
|
24563
|
+
return;
|
|
24564
|
+
}
|
|
23922
24565
|
const taskId = parseTaskIdFromTaskOutput(output.output);
|
|
23923
24566
|
if (!taskId) {
|
|
23924
24567
|
if (pending.resumedTaskId && isMissingRememberedSessionError(output.output)) {
|
|
@@ -23941,6 +24584,19 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23941
24584
|
pruneContext();
|
|
23942
24585
|
},
|
|
23943
24586
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
24587
|
+
for (const [messageIndex, message] of output.messages.entries()) {
|
|
24588
|
+
if (message.info.role !== "user")
|
|
24589
|
+
continue;
|
|
24590
|
+
if (message.info.agent && message.info.agent !== "orchestrator") {
|
|
24591
|
+
continue;
|
|
24592
|
+
}
|
|
24593
|
+
if (!message.info.sessionID || !options.shouldManageSession(message.info.sessionID)) {
|
|
24594
|
+
continue;
|
|
24595
|
+
}
|
|
24596
|
+
for (const [partIndex, part] of message.parts.entries()) {
|
|
24597
|
+
updateFromInjectedCompletion(part, message, messageIndex, partIndex);
|
|
24598
|
+
}
|
|
24599
|
+
}
|
|
23944
24600
|
for (let i = output.messages.length - 1;i >= 0; i -= 1) {
|
|
23945
24601
|
const message = output.messages[i];
|
|
23946
24602
|
if (message.info.role !== "user")
|
|
@@ -23950,8 +24606,11 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23950
24606
|
if (!message.info.sessionID || !options.shouldManageSession(message.info.sessionID)) {
|
|
23951
24607
|
return;
|
|
23952
24608
|
}
|
|
23953
|
-
const
|
|
23954
|
-
|
|
24609
|
+
const reminders = [
|
|
24610
|
+
backgroundJobBoard.formatForPrompt(message.info.sessionID),
|
|
24611
|
+
sessionManager.formatForPrompt(message.info.sessionID)
|
|
24612
|
+
].filter((item) => Boolean(item));
|
|
24613
|
+
if (reminders.length === 0)
|
|
23955
24614
|
return;
|
|
23956
24615
|
const textPart = message.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
23957
24616
|
if (!textPart)
|
|
@@ -23960,11 +24619,14 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23960
24619
|
return;
|
|
23961
24620
|
if (textPart.text?.includes(RESUMABLE_SESSIONS_START))
|
|
23962
24621
|
return;
|
|
24622
|
+
rememberInjectedTerminalJobs(message.info.sessionID);
|
|
23963
24623
|
textPart.text = [
|
|
23964
24624
|
textPart.text ?? "",
|
|
23965
24625
|
"",
|
|
23966
24626
|
RESUMABLE_SESSIONS_START,
|
|
23967
|
-
|
|
24627
|
+
reminders.join(`
|
|
24628
|
+
|
|
24629
|
+
`),
|
|
23968
24630
|
RESUMABLE_SESSIONS_END
|
|
23969
24631
|
].join(`
|
|
23970
24632
|
`);
|
|
@@ -23979,13 +24641,30 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
23979
24641
|
}
|
|
23980
24642
|
return;
|
|
23981
24643
|
}
|
|
24644
|
+
if (input.event.type === "session.idle" || input.event.type === "session.status" && input.event.properties?.status?.type === "idle") {
|
|
24645
|
+
const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24646
|
+
if (sessionId2 && options.shouldManageSession(sessionId2)) {
|
|
24647
|
+
reconcileInjectedTerminalJobs(sessionId2);
|
|
24648
|
+
}
|
|
24649
|
+
return;
|
|
24650
|
+
}
|
|
24651
|
+
if (input.event.type === "session.error") {
|
|
24652
|
+
const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24653
|
+
if (sessionId2 && options.shouldManageSession(sessionId2)) {
|
|
24654
|
+
terminalJobsInjectedByParent.delete(sessionId2);
|
|
24655
|
+
}
|
|
24656
|
+
return;
|
|
24657
|
+
}
|
|
23982
24658
|
if (input.event.type !== "session.deleted")
|
|
23983
24659
|
return;
|
|
23984
24660
|
const sessionId = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
23985
24661
|
if (!sessionId)
|
|
23986
24662
|
return;
|
|
23987
24663
|
sessionManager.dropTask(sessionId);
|
|
24664
|
+
backgroundJobBoard.drop(sessionId);
|
|
23988
24665
|
sessionManager.clearParent(sessionId);
|
|
24666
|
+
backgroundJobBoard.clearParent(sessionId);
|
|
24667
|
+
terminalJobsInjectedByParent.delete(sessionId);
|
|
23989
24668
|
contextByTask.delete(sessionId);
|
|
23990
24669
|
pendingManagedTaskIds.delete(sessionId);
|
|
23991
24670
|
pruneContext();
|
|
@@ -24138,7 +24817,7 @@ function createTodoHygiene(options) {
|
|
|
24138
24817
|
|
|
24139
24818
|
// src/hooks/todo-continuation/index.ts
|
|
24140
24819
|
var HOOK_NAME = "todo-continuation";
|
|
24141
|
-
var
|
|
24820
|
+
var COMMAND_NAME2 = "auto-continue";
|
|
24142
24821
|
var TODO_STATE_TIMEOUT_MS = 500;
|
|
24143
24822
|
var CONTINUATION_PROMPT = "[Auto-continue: enabled - there are incomplete todos remaining. Continue with the next uncompleted item. Press Esc to cancel. If you need user input or review for the next item, ask instead of proceeding.]";
|
|
24144
24823
|
var TODO_HYGIENE_INSTRUCTION_OPEN = '<instruction name="todo_hygiene">';
|
|
@@ -24214,6 +24893,7 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24214
24893
|
const cooldownMs = config?.cooldownMs ?? 3000;
|
|
24215
24894
|
const autoEnable = config?.autoEnable ?? false;
|
|
24216
24895
|
const autoEnableThreshold = config?.autoEnableThreshold ?? 4;
|
|
24896
|
+
const backgroundJobBoard = config?.backgroundJobBoard;
|
|
24217
24897
|
const requestSignatureBySession = new Map;
|
|
24218
24898
|
const state = {
|
|
24219
24899
|
enabled: false,
|
|
@@ -24325,8 +25005,10 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24325
25005
|
}
|
|
24326
25006
|
if (requestSignatureBySession.get(lastUserMessage.sessionID) === lastUserMessage.signature) {
|
|
24327
25007
|
const reminder = hygiene.getPendingReminder(lastUserMessage.sessionID);
|
|
24328
|
-
|
|
24329
|
-
|
|
25008
|
+
const guardrail = backgroundGuardrail(lastUserMessage.sessionID);
|
|
25009
|
+
const combinedReminder = [reminder, guardrail].filter((item) => Boolean(item)).join(" ");
|
|
25010
|
+
if (combinedReminder) {
|
|
25011
|
+
appendTodoHygieneInstruction(lastUserMessage.message, combinedReminder);
|
|
24330
25012
|
} else {
|
|
24331
25013
|
stripTodoHygieneInstructionFromMessage(lastUserMessage.message);
|
|
24332
25014
|
}
|
|
@@ -24364,6 +25046,28 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24364
25046
|
function registerOrchestratorSession(sessionID) {
|
|
24365
25047
|
state.orchestratorSessionIds.add(sessionID);
|
|
24366
25048
|
}
|
|
25049
|
+
function backgroundGuardrail(sessionID) {
|
|
25050
|
+
if (!backgroundJobBoard)
|
|
25051
|
+
return;
|
|
25052
|
+
const hasRunning = backgroundJobBoard.hasRunning(sessionID);
|
|
25053
|
+
const hasTerminal = backgroundJobBoard.hasTerminalUnreconciled(sessionID);
|
|
25054
|
+
if (hasRunning && hasTerminal) {
|
|
25055
|
+
return "Background jobs are still unresolved: call task_status for running jobs and reconcile terminal Background Job Board results before dependent work or finalizing.";
|
|
25056
|
+
}
|
|
25057
|
+
if (hasTerminal) {
|
|
25058
|
+
return "Background jobs have terminal results: reconcile the Background Job Board results before finalizing.";
|
|
25059
|
+
}
|
|
25060
|
+
if (hasRunning) {
|
|
25061
|
+
return "Background jobs are still running: call task_status before dependent work or finalizing.";
|
|
25062
|
+
}
|
|
25063
|
+
return;
|
|
25064
|
+
}
|
|
25065
|
+
function continuationPrompt(sessionID) {
|
|
25066
|
+
const guardrail = backgroundGuardrail(sessionID);
|
|
25067
|
+
if (!guardrail)
|
|
25068
|
+
return CONTINUATION_PROMPT;
|
|
25069
|
+
return `${CONTINUATION_PROMPT} ${guardrail}`;
|
|
25070
|
+
}
|
|
24367
25071
|
function handleChatMessage(input) {
|
|
24368
25072
|
if (!input.agent) {
|
|
24369
25073
|
return;
|
|
@@ -24555,7 +25259,9 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24555
25259
|
await ctx.client.session.prompt({
|
|
24556
25260
|
path: { id: sessionID },
|
|
24557
25261
|
body: {
|
|
24558
|
-
parts: [
|
|
25262
|
+
parts: [
|
|
25263
|
+
createInternalAgentTextPart(continuationPrompt(sessionID))
|
|
25264
|
+
]
|
|
24559
25265
|
}
|
|
24560
25266
|
});
|
|
24561
25267
|
state.consecutiveContinuations++;
|
|
@@ -24629,7 +25335,7 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24629
25335
|
}
|
|
24630
25336
|
}
|
|
24631
25337
|
async function handleCommandExecuteBefore(input, output) {
|
|
24632
|
-
if (input.command !==
|
|
25338
|
+
if (input.command !== COMMAND_NAME2) {
|
|
24633
25339
|
return;
|
|
24634
25340
|
}
|
|
24635
25341
|
registerOrchestratorSession(input.sessionID);
|
|
@@ -24648,11 +25354,11 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24648
25354
|
if (!newEnabled) {
|
|
24649
25355
|
cancelPendingTimer(state);
|
|
24650
25356
|
output.parts.push(createInternalAgentTextPart("[Auto-continue: disabled by user command.]"));
|
|
24651
|
-
log(`[${HOOK_NAME}] Disabled via /${
|
|
25357
|
+
log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME2} command`);
|
|
24652
25358
|
return;
|
|
24653
25359
|
}
|
|
24654
25360
|
state.suppressUntil = 0;
|
|
24655
|
-
log(`[${HOOK_NAME}] Enabled via /${
|
|
25361
|
+
log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME2} command`, {
|
|
24656
25362
|
maxContinuations
|
|
24657
25363
|
});
|
|
24658
25364
|
let hasIncompleteTodos = false;
|
|
@@ -24666,7 +25372,7 @@ function createTodoContinuationHook(ctx, config) {
|
|
|
24666
25372
|
});
|
|
24667
25373
|
}
|
|
24668
25374
|
if (hasIncompleteTodos) {
|
|
24669
|
-
output.parts.push(createInternalAgentTextPart(`${
|
|
25375
|
+
output.parts.push(createInternalAgentTextPart(`${continuationPrompt(input.sessionID)} [Auto-continue enabled: up to ${maxContinuations} continuations.]`));
|
|
24670
25376
|
} else {
|
|
24671
25377
|
output.parts.push(createInternalAgentTextPart(`[Auto-continue: enabled for up to ${maxContinuations} continuations. No incomplete todos right now.]`));
|
|
24672
25378
|
}
|
|
@@ -24686,7 +25392,7 @@ import path13 from "node:path";
|
|
|
24686
25392
|
// src/interview/dashboard.ts
|
|
24687
25393
|
import crypto from "node:crypto";
|
|
24688
25394
|
import * as fsSync2 from "node:fs";
|
|
24689
|
-
import
|
|
25395
|
+
import fs8 from "node:fs/promises";
|
|
24690
25396
|
import {
|
|
24691
25397
|
createServer
|
|
24692
25398
|
} from "node:http";
|
|
@@ -24694,175 +25400,6 @@ import os4 from "node:os";
|
|
|
24694
25400
|
import path11 from "node:path";
|
|
24695
25401
|
import { URL as URL2 } from "node:url";
|
|
24696
25402
|
|
|
24697
|
-
// src/interview/document.ts
|
|
24698
|
-
import * as fsSync from "node:fs";
|
|
24699
|
-
import * as fs6 from "node:fs/promises";
|
|
24700
|
-
import * as path10 from "node:path";
|
|
24701
|
-
var DEFAULT_OUTPUT_FOLDER = "interview";
|
|
24702
|
-
function normalizeOutputFolder(outputFolder) {
|
|
24703
|
-
const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
|
|
24704
|
-
return normalized || DEFAULT_OUTPUT_FOLDER;
|
|
24705
|
-
}
|
|
24706
|
-
function createInterviewDirectoryPath(directory, outputFolder) {
|
|
24707
|
-
return path10.join(directory, normalizeOutputFolder(outputFolder));
|
|
24708
|
-
}
|
|
24709
|
-
function createInterviewFilePath(directory, outputFolder, idea) {
|
|
24710
|
-
const fileName = `${slugify(idea) || "interview"}.md`;
|
|
24711
|
-
return path10.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
|
|
24712
|
-
}
|
|
24713
|
-
function relativeInterviewPath(directory, filePath) {
|
|
24714
|
-
return path10.relative(directory, filePath) || path10.basename(filePath);
|
|
24715
|
-
}
|
|
24716
|
-
function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
24717
|
-
const trimmed = value.trim();
|
|
24718
|
-
if (!trimmed) {
|
|
24719
|
-
return null;
|
|
24720
|
-
}
|
|
24721
|
-
const outputDir = createInterviewDirectoryPath(directory, outputFolder);
|
|
24722
|
-
const candidates = new Set;
|
|
24723
|
-
const resolvedRoot = path10.resolve(directory);
|
|
24724
|
-
if (path10.isAbsolute(trimmed)) {
|
|
24725
|
-
candidates.add(trimmed);
|
|
24726
|
-
} else {
|
|
24727
|
-
candidates.add(path10.resolve(directory, trimmed));
|
|
24728
|
-
candidates.add(path10.join(outputDir, trimmed));
|
|
24729
|
-
if (!trimmed.endsWith(".md")) {
|
|
24730
|
-
candidates.add(path10.join(outputDir, `${trimmed}.md`));
|
|
24731
|
-
}
|
|
24732
|
-
}
|
|
24733
|
-
for (const candidate of candidates) {
|
|
24734
|
-
if (path10.extname(candidate) !== ".md") {
|
|
24735
|
-
continue;
|
|
24736
|
-
}
|
|
24737
|
-
const resolved = path10.resolve(candidate);
|
|
24738
|
-
if (!resolved.startsWith(resolvedRoot + path10.sep) && resolved !== resolvedRoot) {
|
|
24739
|
-
continue;
|
|
24740
|
-
}
|
|
24741
|
-
if (fsSync.existsSync(candidate)) {
|
|
24742
|
-
return candidate;
|
|
24743
|
-
}
|
|
24744
|
-
}
|
|
24745
|
-
return null;
|
|
24746
|
-
}
|
|
24747
|
-
function slugify(value) {
|
|
24748
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
24749
|
-
}
|
|
24750
|
-
function extractHistorySection(document) {
|
|
24751
|
-
const marker = `## Q&A history
|
|
24752
|
-
|
|
24753
|
-
`;
|
|
24754
|
-
const index = document.indexOf(marker);
|
|
24755
|
-
return index >= 0 ? document.slice(index + marker.length).trim() : "";
|
|
24756
|
-
}
|
|
24757
|
-
function extractSummarySection(document) {
|
|
24758
|
-
const marker = `## Current spec
|
|
24759
|
-
|
|
24760
|
-
`;
|
|
24761
|
-
const historyMarker = `
|
|
24762
|
-
|
|
24763
|
-
## Q&A history`;
|
|
24764
|
-
const start = document.indexOf(marker);
|
|
24765
|
-
if (start < 0) {
|
|
24766
|
-
return "";
|
|
24767
|
-
}
|
|
24768
|
-
const summaryStart = start + marker.length;
|
|
24769
|
-
const summaryEnd = document.indexOf(historyMarker, summaryStart);
|
|
24770
|
-
return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
|
|
24771
|
-
}
|
|
24772
|
-
function extractTitle(document) {
|
|
24773
|
-
const match = document.match(/^#\s+(.+)$/m);
|
|
24774
|
-
return match?.[1]?.trim() ?? "";
|
|
24775
|
-
}
|
|
24776
|
-
function buildInterviewDocument(idea, summary, history, meta) {
|
|
24777
|
-
const normalizedSummary = summary.trim() || "Waiting for interview answers.";
|
|
24778
|
-
const normalizedHistory = history.trim() || "No answers yet.";
|
|
24779
|
-
const frontmatter = meta?.sessionID ? [
|
|
24780
|
-
"---",
|
|
24781
|
-
`sessionID: ${meta.sessionID}`,
|
|
24782
|
-
`baseMessageCount: ${meta.baseMessageCount ?? 0}`,
|
|
24783
|
-
`updatedAt: ${new Date().toISOString()}`,
|
|
24784
|
-
"---",
|
|
24785
|
-
""
|
|
24786
|
-
].join(`
|
|
24787
|
-
`) : "";
|
|
24788
|
-
return [
|
|
24789
|
-
frontmatter,
|
|
24790
|
-
`# ${idea}`,
|
|
24791
|
-
"",
|
|
24792
|
-
"## Current spec",
|
|
24793
|
-
"",
|
|
24794
|
-
normalizedSummary,
|
|
24795
|
-
"",
|
|
24796
|
-
"## Q&A history",
|
|
24797
|
-
"",
|
|
24798
|
-
normalizedHistory,
|
|
24799
|
-
""
|
|
24800
|
-
].join(`
|
|
24801
|
-
`);
|
|
24802
|
-
}
|
|
24803
|
-
function parseFrontmatter(content) {
|
|
24804
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
24805
|
-
if (!match)
|
|
24806
|
-
return null;
|
|
24807
|
-
const result = {};
|
|
24808
|
-
for (const line of match[1].split(`
|
|
24809
|
-
`)) {
|
|
24810
|
-
const colonIdx = line.indexOf(":");
|
|
24811
|
-
if (colonIdx > 0) {
|
|
24812
|
-
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
24813
|
-
}
|
|
24814
|
-
}
|
|
24815
|
-
return result;
|
|
24816
|
-
}
|
|
24817
|
-
async function ensureInterviewFile(record) {
|
|
24818
|
-
await fs6.mkdir(path10.dirname(record.markdownPath), { recursive: true });
|
|
24819
|
-
try {
|
|
24820
|
-
await fs6.access(record.markdownPath);
|
|
24821
|
-
} catch {
|
|
24822
|
-
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
|
|
24823
|
-
sessionID: record.sessionID,
|
|
24824
|
-
baseMessageCount: record.baseMessageCount
|
|
24825
|
-
}), "utf8");
|
|
24826
|
-
}
|
|
24827
|
-
}
|
|
24828
|
-
async function readInterviewDocument(record) {
|
|
24829
|
-
try {
|
|
24830
|
-
return await fs6.readFile(record.markdownPath, "utf8");
|
|
24831
|
-
} catch {}
|
|
24832
|
-
await ensureInterviewFile(record);
|
|
24833
|
-
return fs6.readFile(record.markdownPath, "utf8");
|
|
24834
|
-
}
|
|
24835
|
-
async function rewriteInterviewDocument(record, summary) {
|
|
24836
|
-
const existing = await readInterviewDocument(record);
|
|
24837
|
-
const history = extractHistorySection(existing);
|
|
24838
|
-
const next = buildInterviewDocument(record.idea, summary, history, {
|
|
24839
|
-
sessionID: record.sessionID,
|
|
24840
|
-
baseMessageCount: record.baseMessageCount
|
|
24841
|
-
});
|
|
24842
|
-
await fs6.writeFile(record.markdownPath, next, "utf8");
|
|
24843
|
-
return next;
|
|
24844
|
-
}
|
|
24845
|
-
async function appendInterviewAnswers(record, questions, answers) {
|
|
24846
|
-
const existing = await readInterviewDocument(record);
|
|
24847
|
-
const summary = extractSummarySection(existing);
|
|
24848
|
-
const history = extractHistorySection(existing);
|
|
24849
|
-
const questionMap = new Map(questions.map((question) => [question.id, question]));
|
|
24850
|
-
const appended = answers.map((answer) => {
|
|
24851
|
-
const question = questionMap.get(answer.questionId);
|
|
24852
|
-
return question ? `Q: ${question.question}
|
|
24853
|
-
A: ${answer.answer.trim()}` : null;
|
|
24854
|
-
}).filter((value) => value !== null).join(`
|
|
24855
|
-
|
|
24856
|
-
`);
|
|
24857
|
-
const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
|
|
24858
|
-
|
|
24859
|
-
`);
|
|
24860
|
-
await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
|
|
24861
|
-
sessionID: record.sessionID,
|
|
24862
|
-
baseMessageCount: record.baseMessageCount
|
|
24863
|
-
}), "utf8");
|
|
24864
|
-
}
|
|
24865
|
-
|
|
24866
25403
|
// src/interview/helpers.ts
|
|
24867
25404
|
function sendJson(response, status, value) {
|
|
24868
25405
|
response.statusCode = status;
|
|
@@ -26507,7 +27044,7 @@ function removeAuthFile(port) {
|
|
|
26507
27044
|
}
|
|
26508
27045
|
async function readDashboardAuthFile(port) {
|
|
26509
27046
|
try {
|
|
26510
|
-
const content = await
|
|
27047
|
+
const content = await fs8.readFile(getAuthFilePath(port), "utf8");
|
|
26511
27048
|
const data = JSON.parse(content);
|
|
26512
27049
|
try {
|
|
26513
27050
|
process.kill(data.pid, 0);
|
|
@@ -26630,7 +27167,7 @@ function createDashboardServer(config) {
|
|
|
26630
27167
|
const interviewDir = path11.join(dir, config.outputFolder);
|
|
26631
27168
|
let entries;
|
|
26632
27169
|
try {
|
|
26633
|
-
entries = await
|
|
27170
|
+
entries = await fs8.readdir(interviewDir);
|
|
26634
27171
|
} catch {
|
|
26635
27172
|
continue;
|
|
26636
27173
|
}
|
|
@@ -26639,7 +27176,7 @@ function createDashboardServer(config) {
|
|
|
26639
27176
|
continue;
|
|
26640
27177
|
let content;
|
|
26641
27178
|
try {
|
|
26642
|
-
content = await
|
|
27179
|
+
content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
|
|
26643
27180
|
} catch {
|
|
26644
27181
|
continue;
|
|
26645
27182
|
}
|
|
@@ -26668,7 +27205,7 @@ function createDashboardServer(config) {
|
|
|
26668
27205
|
const interviewDir = path11.join(dir, config.outputFolder);
|
|
26669
27206
|
let entries;
|
|
26670
27207
|
try {
|
|
26671
|
-
entries = await
|
|
27208
|
+
entries = await fs8.readdir(interviewDir);
|
|
26672
27209
|
} catch {
|
|
26673
27210
|
continue;
|
|
26674
27211
|
}
|
|
@@ -26677,7 +27214,7 @@ function createDashboardServer(config) {
|
|
|
26677
27214
|
continue;
|
|
26678
27215
|
let content;
|
|
26679
27216
|
try {
|
|
26680
|
-
content = await
|
|
27217
|
+
content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
|
|
26681
27218
|
} catch {
|
|
26682
27219
|
continue;
|
|
26683
27220
|
}
|
|
@@ -26961,7 +27498,7 @@ function createDashboardServer(config) {
|
|
|
26961
27498
|
let markdownPath = entry.filePath;
|
|
26962
27499
|
if (entry.filePath) {
|
|
26963
27500
|
try {
|
|
26964
|
-
document = await
|
|
27501
|
+
document = await fs8.readFile(entry.filePath, "utf8");
|
|
26965
27502
|
} catch {}
|
|
26966
27503
|
} else {
|
|
26967
27504
|
const dirs = getKnownDirectories();
|
|
@@ -26969,7 +27506,7 @@ function createDashboardServer(config) {
|
|
|
26969
27506
|
const slug = extractResumeSlug(interviewId);
|
|
26970
27507
|
const candidate = path11.join(dir, config.outputFolder, `${slug}.md`);
|
|
26971
27508
|
try {
|
|
26972
|
-
document = await
|
|
27509
|
+
document = await fs8.readFile(candidate, "utf8");
|
|
26973
27510
|
markdownPath = candidate;
|
|
26974
27511
|
entry.filePath = candidate;
|
|
26975
27512
|
break;
|
|
@@ -27494,7 +28031,7 @@ function createInterviewServer(deps) {
|
|
|
27494
28031
|
|
|
27495
28032
|
// src/interview/service.ts
|
|
27496
28033
|
import { spawn as spawn2 } from "node:child_process";
|
|
27497
|
-
import * as
|
|
28034
|
+
import * as fs9 from "node:fs/promises";
|
|
27498
28035
|
import * as path12 from "node:path";
|
|
27499
28036
|
|
|
27500
28037
|
// src/interview/types.ts
|
|
@@ -27667,7 +28204,7 @@ function buildAnswerPrompt(answers, questions, maxQuestions) {
|
|
|
27667
28204
|
}
|
|
27668
28205
|
|
|
27669
28206
|
// src/interview/service.ts
|
|
27670
|
-
var
|
|
28207
|
+
var COMMAND_NAME3 = "interview";
|
|
27671
28208
|
var DEFAULT_MAX_QUESTIONS = 2;
|
|
27672
28209
|
function isTruthyEnvFlag(value) {
|
|
27673
28210
|
if (!value) {
|
|
@@ -27769,11 +28306,11 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27769
28306
|
const dir = path12.dirname(interview.markdownPath);
|
|
27770
28307
|
const newPath = path12.join(dir, `${newSlug}.md`);
|
|
27771
28308
|
try {
|
|
27772
|
-
await
|
|
28309
|
+
await fs9.access(newPath);
|
|
27773
28310
|
return;
|
|
27774
28311
|
} catch {}
|
|
27775
28312
|
try {
|
|
27776
|
-
await
|
|
28313
|
+
await fs9.rename(interview.markdownPath, newPath);
|
|
27777
28314
|
interview.markdownPath = newPath;
|
|
27778
28315
|
log("[interview] renamed file with assistant title:", {
|
|
27779
28316
|
from: currentFileName,
|
|
@@ -27839,7 +28376,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27839
28376
|
active.status = "abandoned";
|
|
27840
28377
|
}
|
|
27841
28378
|
}
|
|
27842
|
-
const document = await
|
|
28379
|
+
const document = await fs9.readFile(markdownPath, "utf8");
|
|
27843
28380
|
const messages = await loadMessages(sessionID);
|
|
27844
28381
|
const title = extractTitle(document);
|
|
27845
28382
|
const record = {
|
|
@@ -27914,11 +28451,11 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27914
28451
|
}
|
|
27915
28452
|
function registerCommand(opencodeConfig) {
|
|
27916
28453
|
const configCommand = opencodeConfig.command;
|
|
27917
|
-
if (!configCommand?.[
|
|
28454
|
+
if (!configCommand?.[COMMAND_NAME3]) {
|
|
27918
28455
|
if (!opencodeConfig.command) {
|
|
27919
28456
|
opencodeConfig.command = {};
|
|
27920
28457
|
}
|
|
27921
|
-
opencodeConfig.command[
|
|
28458
|
+
opencodeConfig.command[COMMAND_NAME3] = {
|
|
27922
28459
|
template: "Start an interview and write a live markdown spec",
|
|
27923
28460
|
description: "Open a localhost interview UI linked to the current OpenCode session"
|
|
27924
28461
|
};
|
|
@@ -27992,7 +28529,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
27992
28529
|
}
|
|
27993
28530
|
}
|
|
27994
28531
|
async function handleCommandExecuteBefore(input, output) {
|
|
27995
|
-
if (input.command !==
|
|
28532
|
+
if (input.command !== COMMAND_NAME3) {
|
|
27996
28533
|
return;
|
|
27997
28534
|
}
|
|
27998
28535
|
const idea = input.arguments.trim();
|
|
@@ -28011,7 +28548,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
28011
28548
|
const resumePath = resolveExistingInterviewPath(ctx.directory, outputFolder, idea);
|
|
28012
28549
|
if (resumePath) {
|
|
28013
28550
|
const interview2 = await resumeInterview(input.sessionID, resumePath);
|
|
28014
|
-
const document = await
|
|
28551
|
+
const document = await fs9.readFile(interview2.markdownPath, "utf8");
|
|
28015
28552
|
await notifyInterviewUrl(input.sessionID, interview2);
|
|
28016
28553
|
output.parts.push(createInternalAgentTextPart(buildResumePrompt(document, maxQuestions)));
|
|
28017
28554
|
return;
|
|
@@ -28075,7 +28612,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
28075
28612
|
const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path12.resolve(i.markdownPath)));
|
|
28076
28613
|
let entries;
|
|
28077
28614
|
try {
|
|
28078
|
-
entries = await
|
|
28615
|
+
entries = await fs9.readdir(outputDir);
|
|
28079
28616
|
} catch {
|
|
28080
28617
|
return [];
|
|
28081
28618
|
}
|
|
@@ -28088,7 +28625,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
28088
28625
|
continue;
|
|
28089
28626
|
let content;
|
|
28090
28627
|
try {
|
|
28091
|
-
content = await
|
|
28628
|
+
content = await fs9.readFile(fullPath, "utf8");
|
|
28092
28629
|
} catch {
|
|
28093
28630
|
continue;
|
|
28094
28631
|
}
|
|
@@ -29161,29 +29698,46 @@ function startAvailabilityCheck(config) {
|
|
|
29161
29698
|
// src/multiplexer/session-manager.ts
|
|
29162
29699
|
var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
|
|
29163
29700
|
var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
|
|
29164
|
-
|
|
29701
|
+
var SHARED_STATE_KEY = Symbol.for("oh-my-opencode-slim.multiplexer-session-manager.state");
|
|
29702
|
+
function getSharedState() {
|
|
29703
|
+
const globalWithState = globalThis;
|
|
29704
|
+
globalWithState[SHARED_STATE_KEY] ??= {
|
|
29705
|
+
sessions: new Map,
|
|
29706
|
+
knownSessions: new Map,
|
|
29707
|
+
spawningSessions: new Set,
|
|
29708
|
+
closingSessions: new Map
|
|
29709
|
+
};
|
|
29710
|
+
return globalWithState[SHARED_STATE_KEY];
|
|
29711
|
+
}
|
|
29165
29712
|
class MultiplexerSessionManager {
|
|
29166
|
-
|
|
29713
|
+
instanceId = Math.random().toString(36).slice(2, 8);
|
|
29167
29714
|
serverUrl;
|
|
29168
29715
|
directory;
|
|
29169
29716
|
multiplexer = null;
|
|
29170
|
-
sessions
|
|
29171
|
-
knownSessions
|
|
29172
|
-
spawningSessions
|
|
29173
|
-
closingSessions
|
|
29717
|
+
sessions;
|
|
29718
|
+
knownSessions;
|
|
29719
|
+
spawningSessions;
|
|
29720
|
+
closingSessions;
|
|
29174
29721
|
pollInterval;
|
|
29175
29722
|
enabled = false;
|
|
29176
29723
|
constructor(ctx, config) {
|
|
29177
|
-
|
|
29724
|
+
const sharedState = getSharedState();
|
|
29725
|
+
this.sessions = sharedState.sessions;
|
|
29726
|
+
this.knownSessions = sharedState.knownSessions;
|
|
29727
|
+
this.spawningSessions = sharedState.spawningSessions;
|
|
29728
|
+
this.closingSessions = sharedState.closingSessions;
|
|
29178
29729
|
this.directory = ctx.directory;
|
|
29179
29730
|
const defaultPort = process.env.OPENCODE_PORT ?? "4096";
|
|
29180
29731
|
this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
|
|
29181
29732
|
this.multiplexer = getMultiplexer(config);
|
|
29182
29733
|
this.enabled = config.type !== "none" && this.multiplexer !== null && this.multiplexer.isInsideSession();
|
|
29183
29734
|
log("[multiplexer-session-manager] initialized", {
|
|
29735
|
+
instanceId: this.instanceId,
|
|
29184
29736
|
enabled: this.enabled,
|
|
29185
29737
|
type: config.type,
|
|
29186
|
-
serverUrl: this.serverUrl
|
|
29738
|
+
serverUrl: this.serverUrl,
|
|
29739
|
+
trackedSessions: this.sessions.size,
|
|
29740
|
+
knownSessions: this.knownSessions.size
|
|
29187
29741
|
});
|
|
29188
29742
|
}
|
|
29189
29743
|
async onSessionCreated(event) {
|
|
@@ -29201,6 +29755,7 @@ class MultiplexerSessionManager {
|
|
|
29201
29755
|
const directory = info.directory ?? this.directory;
|
|
29202
29756
|
if (this.isTrackedOrSpawning(sessionId)) {
|
|
29203
29757
|
log("[multiplexer-session-manager] session already tracked or spawning", {
|
|
29758
|
+
instanceId: this.instanceId,
|
|
29204
29759
|
sessionId
|
|
29205
29760
|
});
|
|
29206
29761
|
return;
|
|
@@ -29220,6 +29775,7 @@ class MultiplexerSessionManager {
|
|
|
29220
29775
|
const serverRunning = await isServerRunning(this.serverUrl);
|
|
29221
29776
|
if (!serverRunning) {
|
|
29222
29777
|
log("[multiplexer-session-manager] server not running, skipping", {
|
|
29778
|
+
instanceId: this.instanceId,
|
|
29223
29779
|
serverUrl: this.serverUrl
|
|
29224
29780
|
});
|
|
29225
29781
|
return;
|
|
@@ -29230,10 +29786,12 @@ class MultiplexerSessionManager {
|
|
|
29230
29786
|
log("[multiplexer-session-manager] child session created, spawning pane", {
|
|
29231
29787
|
sessionId,
|
|
29232
29788
|
parentId,
|
|
29233
|
-
title
|
|
29789
|
+
title,
|
|
29790
|
+
instanceId: this.instanceId
|
|
29234
29791
|
});
|
|
29235
29792
|
const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl, directory).catch((err) => {
|
|
29236
29793
|
log("[multiplexer-session-manager] failed to spawn pane", {
|
|
29794
|
+
instanceId: this.instanceId,
|
|
29237
29795
|
error: String(err)
|
|
29238
29796
|
});
|
|
29239
29797
|
return { success: false, paneId: undefined };
|
|
@@ -29244,6 +29802,7 @@ class MultiplexerSessionManager {
|
|
|
29244
29802
|
await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale spawned pane failed", {
|
|
29245
29803
|
sessionId,
|
|
29246
29804
|
paneId: paneResult.paneId,
|
|
29805
|
+
instanceId: this.instanceId,
|
|
29247
29806
|
error: String(err)
|
|
29248
29807
|
}));
|
|
29249
29808
|
return;
|
|
@@ -29259,6 +29818,7 @@ class MultiplexerSessionManager {
|
|
|
29259
29818
|
lastSeenAt: now
|
|
29260
29819
|
});
|
|
29261
29820
|
log("[multiplexer-session-manager] pane spawned", {
|
|
29821
|
+
instanceId: this.instanceId,
|
|
29262
29822
|
sessionId,
|
|
29263
29823
|
paneId: paneResult.paneId
|
|
29264
29824
|
});
|
|
@@ -29270,6 +29830,19 @@ class MultiplexerSessionManager {
|
|
|
29270
29830
|
async onSessionStatus(event) {
|
|
29271
29831
|
if (!this.enabled)
|
|
29272
29832
|
return;
|
|
29833
|
+
if (event.type === "session.idle") {
|
|
29834
|
+
const sessionId2 = event.properties?.sessionID;
|
|
29835
|
+
if (!sessionId2)
|
|
29836
|
+
return;
|
|
29837
|
+
log("[multiplexer-session-manager] session idle event received", {
|
|
29838
|
+
instanceId: this.instanceId,
|
|
29839
|
+
sessionId: sessionId2,
|
|
29840
|
+
tracked: this.sessions.has(sessionId2),
|
|
29841
|
+
known: this.knownSessions.has(sessionId2)
|
|
29842
|
+
});
|
|
29843
|
+
await this.closeSession(sessionId2, "idle");
|
|
29844
|
+
return;
|
|
29845
|
+
}
|
|
29273
29846
|
if (event.type !== "session.status")
|
|
29274
29847
|
return;
|
|
29275
29848
|
const sessionId = event.properties?.sessionID;
|
|
@@ -29280,6 +29853,12 @@ class MultiplexerSessionManager {
|
|
|
29280
29853
|
return;
|
|
29281
29854
|
}
|
|
29282
29855
|
if (event.properties?.status?.type === "busy") {
|
|
29856
|
+
log("[multiplexer-session-manager] session busy event received", {
|
|
29857
|
+
instanceId: this.instanceId,
|
|
29858
|
+
sessionId,
|
|
29859
|
+
tracked: this.sessions.has(sessionId),
|
|
29860
|
+
known: this.knownSessions.has(sessionId)
|
|
29861
|
+
});
|
|
29283
29862
|
await this.respawnIfKnown(sessionId);
|
|
29284
29863
|
}
|
|
29285
29864
|
}
|
|
@@ -29292,6 +29871,7 @@ class MultiplexerSessionManager {
|
|
|
29292
29871
|
if (!sessionId)
|
|
29293
29872
|
return;
|
|
29294
29873
|
log("[multiplexer-session-manager] session deleted, closing pane", {
|
|
29874
|
+
instanceId: this.instanceId,
|
|
29295
29875
|
sessionId
|
|
29296
29876
|
});
|
|
29297
29877
|
await this.closeSession(sessionId, "deleted");
|
|
@@ -29300,13 +29880,17 @@ class MultiplexerSessionManager {
|
|
|
29300
29880
|
if (this.pollInterval)
|
|
29301
29881
|
return;
|
|
29302
29882
|
this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
|
|
29303
|
-
log("[multiplexer-session-manager] polling started"
|
|
29883
|
+
log("[multiplexer-session-manager] polling started", {
|
|
29884
|
+
instanceId: this.instanceId
|
|
29885
|
+
});
|
|
29304
29886
|
}
|
|
29305
29887
|
stopPolling() {
|
|
29306
29888
|
if (this.pollInterval) {
|
|
29307
29889
|
clearInterval(this.pollInterval);
|
|
29308
29890
|
this.pollInterval = undefined;
|
|
29309
|
-
log("[multiplexer-session-manager] polling stopped"
|
|
29891
|
+
log("[multiplexer-session-manager] polling stopped", {
|
|
29892
|
+
instanceId: this.instanceId
|
|
29893
|
+
});
|
|
29310
29894
|
}
|
|
29311
29895
|
}
|
|
29312
29896
|
async pollSessions() {
|
|
@@ -29315,8 +29899,7 @@ class MultiplexerSessionManager {
|
|
|
29315
29899
|
return;
|
|
29316
29900
|
}
|
|
29317
29901
|
try {
|
|
29318
|
-
const
|
|
29319
|
-
const allStatuses = statusResult.data ?? {};
|
|
29902
|
+
const allStatuses = await this.fetchSessionStatuses();
|
|
29320
29903
|
const now = Date.now();
|
|
29321
29904
|
const sessionsToClose = [];
|
|
29322
29905
|
for (const [sessionId, tracked] of this.sessions.entries()) {
|
|
@@ -29344,6 +29927,14 @@ class MultiplexerSessionManager {
|
|
|
29344
29927
|
log("[multiplexer-session-manager] poll error", { error: String(err) });
|
|
29345
29928
|
}
|
|
29346
29929
|
}
|
|
29930
|
+
async fetchSessionStatuses() {
|
|
29931
|
+
const url = new URL("/session/status", this.serverUrl);
|
|
29932
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
|
|
29933
|
+
if (!response.ok) {
|
|
29934
|
+
throw new Error(`session status request failed: ${response.status} ${response.statusText}`);
|
|
29935
|
+
}
|
|
29936
|
+
return await response.json();
|
|
29937
|
+
}
|
|
29347
29938
|
async closeSession(sessionId, reason) {
|
|
29348
29939
|
if (reason === "deleted") {
|
|
29349
29940
|
this.knownSessions.delete(sessionId);
|
|
@@ -29352,10 +29943,19 @@ class MultiplexerSessionManager {
|
|
|
29352
29943
|
if (existingClose)
|
|
29353
29944
|
return existingClose;
|
|
29354
29945
|
const tracked = this.sessions.get(sessionId);
|
|
29355
|
-
if (!tracked || !this.multiplexer)
|
|
29946
|
+
if (!tracked || !this.multiplexer) {
|
|
29947
|
+
log("[multiplexer-session-manager] close skipped; session not tracked", {
|
|
29948
|
+
instanceId: this.instanceId,
|
|
29949
|
+
sessionId,
|
|
29950
|
+
reason,
|
|
29951
|
+
tracked: !!tracked,
|
|
29952
|
+
hasMultiplexer: !!this.multiplexer
|
|
29953
|
+
});
|
|
29356
29954
|
return;
|
|
29955
|
+
}
|
|
29357
29956
|
this.sessions.delete(sessionId);
|
|
29358
29957
|
log("[multiplexer-session-manager] closing session pane", {
|
|
29958
|
+
instanceId: this.instanceId,
|
|
29359
29959
|
sessionId,
|
|
29360
29960
|
paneId: tracked.paneId,
|
|
29361
29961
|
reason
|
|
@@ -29363,6 +29963,7 @@ class MultiplexerSessionManager {
|
|
|
29363
29963
|
const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
|
|
29364
29964
|
return;
|
|
29365
29965
|
}).catch((err) => log("[multiplexer-session-manager] failed to close session pane", {
|
|
29966
|
+
instanceId: this.instanceId,
|
|
29366
29967
|
sessionId,
|
|
29367
29968
|
paneId: tracked.paneId,
|
|
29368
29969
|
reason,
|
|
@@ -29391,6 +29992,7 @@ class MultiplexerSessionManager {
|
|
|
29391
29992
|
const serverRunning = await isServerRunning(this.serverUrl);
|
|
29392
29993
|
if (!serverRunning) {
|
|
29393
29994
|
log("[multiplexer-session-manager] server not running, skipping busy respawn", {
|
|
29995
|
+
instanceId: this.instanceId,
|
|
29394
29996
|
serverUrl: this.serverUrl,
|
|
29395
29997
|
sessionId
|
|
29396
29998
|
});
|
|
@@ -29400,12 +30002,14 @@ class MultiplexerSessionManager {
|
|
|
29400
30002
|
return;
|
|
29401
30003
|
}
|
|
29402
30004
|
log("[multiplexer-session-manager] child session busy again, respawning pane", {
|
|
30005
|
+
instanceId: this.instanceId,
|
|
29403
30006
|
sessionId,
|
|
29404
30007
|
parentId: known.parentId,
|
|
29405
30008
|
title: known.title
|
|
29406
30009
|
});
|
|
29407
30010
|
const paneResult = await this.multiplexer.spawnPane(sessionId, known.title, this.serverUrl, known.directory).catch((err) => {
|
|
29408
30011
|
log("[multiplexer-session-manager] failed to respawn pane", {
|
|
30012
|
+
instanceId: this.instanceId,
|
|
29409
30013
|
error: String(err)
|
|
29410
30014
|
});
|
|
29411
30015
|
return { success: false, paneId: undefined };
|
|
@@ -29414,6 +30018,7 @@ class MultiplexerSessionManager {
|
|
|
29414
30018
|
return;
|
|
29415
30019
|
if (!this.knownSessions.has(sessionId) || this.closingSessions.has(sessionId)) {
|
|
29416
30020
|
await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale respawned pane failed", {
|
|
30021
|
+
instanceId: this.instanceId,
|
|
29417
30022
|
sessionId,
|
|
29418
30023
|
paneId: paneResult.paneId,
|
|
29419
30024
|
error: String(err)
|
|
@@ -29431,6 +30036,7 @@ class MultiplexerSessionManager {
|
|
|
29431
30036
|
lastSeenAt: now
|
|
29432
30037
|
});
|
|
29433
30038
|
log("[multiplexer-session-manager] pane respawned on busy", {
|
|
30039
|
+
instanceId: this.instanceId,
|
|
29434
30040
|
sessionId,
|
|
29435
30041
|
paneId: paneResult.paneId
|
|
29436
30042
|
});
|
|
@@ -30109,7 +30715,7 @@ Returns the councillor responses with a summary footer.`,
|
|
|
30109
30715
|
return { council_session };
|
|
30110
30716
|
}
|
|
30111
30717
|
// src/tui-state.ts
|
|
30112
|
-
import * as
|
|
30718
|
+
import * as fs10 from "node:fs";
|
|
30113
30719
|
import * as os5 from "node:os";
|
|
30114
30720
|
import * as path14 from "node:path";
|
|
30115
30721
|
var STATE_DIR = "oh-my-opencode-slim";
|
|
@@ -30139,14 +30745,14 @@ function parseSnapshot(value) {
|
|
|
30139
30745
|
}
|
|
30140
30746
|
function readTuiSnapshot() {
|
|
30141
30747
|
try {
|
|
30142
|
-
return parseSnapshot(
|
|
30748
|
+
return parseSnapshot(fs10.readFileSync(getTuiStatePath(), "utf8"));
|
|
30143
30749
|
} catch {
|
|
30144
30750
|
return emptySnapshot();
|
|
30145
30751
|
}
|
|
30146
30752
|
}
|
|
30147
30753
|
async function readTuiSnapshotAsync() {
|
|
30148
30754
|
try {
|
|
30149
|
-
return parseSnapshot(await
|
|
30755
|
+
return parseSnapshot(await fs10.promises.readFile(getTuiStatePath(), "utf8"));
|
|
30150
30756
|
} catch {
|
|
30151
30757
|
return emptySnapshot();
|
|
30152
30758
|
}
|
|
@@ -30154,8 +30760,8 @@ async function readTuiSnapshotAsync() {
|
|
|
30154
30760
|
function writeTuiSnapshot(snapshot) {
|
|
30155
30761
|
try {
|
|
30156
30762
|
const filePath = getTuiStatePath();
|
|
30157
|
-
|
|
30158
|
-
|
|
30763
|
+
fs10.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
30764
|
+
fs10.writeFileSync(filePath, `${JSON.stringify(snapshot)}
|
|
30159
30765
|
`);
|
|
30160
30766
|
} catch {}
|
|
30161
30767
|
}
|
|
@@ -30177,11 +30783,11 @@ function recordTuiAgentModel(input) {
|
|
|
30177
30783
|
}
|
|
30178
30784
|
|
|
30179
30785
|
// src/tools/preset-manager.ts
|
|
30180
|
-
var
|
|
30786
|
+
var COMMAND_NAME4 = "preset";
|
|
30181
30787
|
function createPresetManager(ctx, config) {
|
|
30182
30788
|
let activePreset = getActiveRuntimePreset() ?? config.preset ?? null;
|
|
30183
30789
|
async function handleCommandExecuteBefore(input, output) {
|
|
30184
|
-
if (input.command !==
|
|
30790
|
+
if (input.command !== COMMAND_NAME4) {
|
|
30185
30791
|
return;
|
|
30186
30792
|
}
|
|
30187
30793
|
output.parts.length = 0;
|
|
@@ -30200,11 +30806,11 @@ function createPresetManager(ctx, config) {
|
|
|
30200
30806
|
}
|
|
30201
30807
|
function registerCommand(opencodeConfig) {
|
|
30202
30808
|
const configCommand = opencodeConfig.command;
|
|
30203
|
-
if (!configCommand?.[
|
|
30809
|
+
if (!configCommand?.[COMMAND_NAME4]) {
|
|
30204
30810
|
if (!opencodeConfig.command) {
|
|
30205
30811
|
opencodeConfig.command = {};
|
|
30206
30812
|
}
|
|
30207
|
-
opencodeConfig.command[
|
|
30813
|
+
opencodeConfig.command[COMMAND_NAME4] = {
|
|
30208
30814
|
template: "List available presets and switch between them",
|
|
30209
30815
|
description: "Switch agent presets at runtime (e.g., /preset cheap, /preset powerful)"
|
|
30210
30816
|
};
|
|
@@ -30416,21 +31022,21 @@ async function saveBinary(binaryDir, data, contentType, filename) {
|
|
|
30416
31022
|
throw new Error("Unable to allocate unique filename for binary content");
|
|
30417
31023
|
}
|
|
30418
31024
|
|
|
30419
|
-
// node_modules/lru-cache/dist/esm/node/index.min.js
|
|
30420
|
-
import { tracingChannel as
|
|
30421
|
-
var S =
|
|
30422
|
-
var W =
|
|
31025
|
+
// node_modules/.pnpm/lru-cache@11.3.6/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
31026
|
+
import { tracingChannel as I, channel as G } from "node:diagnostics_channel";
|
|
31027
|
+
var S = G("lru-cache:metrics");
|
|
31028
|
+
var W = I("lru-cache");
|
|
31029
|
+
var C = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
|
|
30423
31030
|
var D = () => S.hasSubscribers || W.hasSubscribers;
|
|
30424
|
-
var
|
|
30425
|
-
var
|
|
30426
|
-
var C = typeof process == "object" && process ? process : {};
|
|
31031
|
+
var U = new Set;
|
|
31032
|
+
var L = typeof process == "object" && process ? process : {};
|
|
30427
31033
|
var P = (u, e, t, i) => {
|
|
30428
|
-
typeof
|
|
31034
|
+
typeof L.emitWarning == "function" ? L.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
|
|
30429
31035
|
};
|
|
30430
|
-
var H = (u) => !
|
|
30431
|
-
var
|
|
31036
|
+
var H = (u) => !U.has(u);
|
|
31037
|
+
var X = Symbol("type");
|
|
30432
31038
|
var F = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u);
|
|
30433
|
-
var
|
|
31039
|
+
var j = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null;
|
|
30434
31040
|
var O = class extends Array {
|
|
30435
31041
|
constructor(e) {
|
|
30436
31042
|
super(e), this.fill(0);
|
|
@@ -30441,7 +31047,7 @@ var R = class u {
|
|
|
30441
31047
|
length;
|
|
30442
31048
|
static #o = false;
|
|
30443
31049
|
static create(e) {
|
|
30444
|
-
let t =
|
|
31050
|
+
let t = j(e);
|
|
30445
31051
|
if (!t)
|
|
30446
31052
|
return [];
|
|
30447
31053
|
u.#o = true;
|
|
@@ -30460,11 +31066,11 @@ var R = class u {
|
|
|
30460
31066
|
return this.heap[--this.length];
|
|
30461
31067
|
}
|
|
30462
31068
|
};
|
|
30463
|
-
var
|
|
31069
|
+
var M = class u2 {
|
|
30464
31070
|
#o;
|
|
30465
31071
|
#u;
|
|
30466
31072
|
#w;
|
|
30467
|
-
#
|
|
31073
|
+
#x;
|
|
30468
31074
|
#S;
|
|
30469
31075
|
#M;
|
|
30470
31076
|
#U;
|
|
@@ -30535,7 +31141,7 @@ var L = class u2 {
|
|
|
30535
31141
|
return this.#w;
|
|
30536
31142
|
}
|
|
30537
31143
|
get onInsert() {
|
|
30538
|
-
return this.#
|
|
31144
|
+
return this.#x;
|
|
30539
31145
|
}
|
|
30540
31146
|
get disposeAfter() {
|
|
30541
31147
|
return this.#S;
|
|
@@ -30544,9 +31150,9 @@ var L = class u2 {
|
|
|
30544
31150
|
let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z5, perf: x } = e;
|
|
30545
31151
|
if (x !== undefined && typeof x?.now != "function")
|
|
30546
31152
|
throw new TypeError("perf option must have a now() method if specified");
|
|
30547
|
-
if (this.#m = x ??
|
|
31153
|
+
if (this.#m = x ?? C, t !== 0 && !F(t))
|
|
30548
31154
|
throw new TypeError("max option must be a nonnegative integer");
|
|
30549
|
-
let v = t ?
|
|
31155
|
+
let v = t ? j(t) : Array;
|
|
30550
31156
|
if (!v)
|
|
30551
31157
|
throw new Error("invalid max value: " + t);
|
|
30552
31158
|
if (this.#o = t, this.#u = T, this.maxEntrySize = w || this.#u, this.sizeCalculation = y, this.sizeCalculation) {
|
|
@@ -30559,7 +31165,7 @@ var L = class u2 {
|
|
|
30559
31165
|
throw new TypeError("memoMethod must be a function if defined");
|
|
30560
31166
|
if (this.#U = m, a !== undefined && typeof a != "function")
|
|
30561
31167
|
throw new TypeError("fetchMethod must be a function if specified");
|
|
30562
|
-
if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#
|
|
31168
|
+
if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z5, this.maxEntrySize !== 0) {
|
|
30563
31169
|
if (this.#u !== 0 && !F(this.#u))
|
|
30564
31170
|
throw new TypeError("maxSize must be a positive integer if specified");
|
|
30565
31171
|
if (!F(this.maxEntrySize))
|
|
@@ -30575,7 +31181,7 @@ var L = class u2 {
|
|
|
30575
31181
|
throw new TypeError("At least one of max, maxSize, or ttl is required");
|
|
30576
31182
|
if (!this.ttlAutopurge && !this.#o && !this.#u) {
|
|
30577
31183
|
let E = "LRU_CACHE_UNBOUNDED";
|
|
30578
|
-
H(E) && (
|
|
31184
|
+
H(E) && (U.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
|
|
30579
31185
|
}
|
|
30580
31186
|
}
|
|
30581
31187
|
getRemainingTTL(e) {
|
|
@@ -30587,7 +31193,7 @@ var L = class u2 {
|
|
|
30587
31193
|
let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
|
|
30588
31194
|
this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
|
|
30589
31195
|
t[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
|
|
30590
|
-
}, this.#
|
|
31196
|
+
}, this.#D = (r) => {
|
|
30591
31197
|
t[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
|
|
30592
31198
|
};
|
|
30593
31199
|
let s = this.ttlAutopurge ? (r, h) => {
|
|
@@ -30631,7 +31237,7 @@ var L = class u2 {
|
|
|
30631
31237
|
return !!l && !!h && (n || o()) - h > l;
|
|
30632
31238
|
};
|
|
30633
31239
|
}
|
|
30634
|
-
#
|
|
31240
|
+
#D = () => {};
|
|
30635
31241
|
#E = () => {};
|
|
30636
31242
|
#N = () => {};
|
|
30637
31243
|
#p = () => false;
|
|
@@ -30797,7 +31403,7 @@ var L = class u2 {
|
|
|
30797
31403
|
return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
|
|
30798
31404
|
let f = this.#n === 0 ? undefined : this.#s.get(e);
|
|
30799
31405
|
if (f === undefined)
|
|
30800
|
-
f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#
|
|
31406
|
+
f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#x?.(t, e, "add");
|
|
30801
31407
|
else {
|
|
30802
31408
|
this.#L(f);
|
|
30803
31409
|
let g = this.#t[f];
|
|
@@ -30861,7 +31467,7 @@ var L = class u2 {
|
|
|
30861
31467
|
if (this.#p(n))
|
|
30862
31468
|
s && (s.has = "stale", this.#E(s, n));
|
|
30863
31469
|
else
|
|
30864
|
-
return i && this.#
|
|
31470
|
+
return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
|
|
30865
31471
|
} else
|
|
30866
31472
|
s && (s.has = "miss");
|
|
30867
31473
|
return false;
|
|
@@ -30919,7 +31525,7 @@ var L = class u2 {
|
|
|
30919
31525
|
let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
|
|
30920
31526
|
t.status = s, s && t.context && (s.context = t.context);
|
|
30921
31527
|
let n = this.#B(e, t);
|
|
30922
|
-
return s &&
|
|
31528
|
+
return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
|
|
30923
31529
|
}
|
|
30924
31530
|
async#B(e, t = {}) {
|
|
30925
31531
|
let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, ttl: o = this.ttl, noDisposeOnSet: r = this.noDisposeOnSet, size: h = 0, sizeCalculation: l = this.sizeCalculation, noUpdateTTL: c = this.noUpdateTTL, noDeleteOnFetchRejection: f = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection: g = this.allowStaleOnFetchRejection, ignoreFetchAbort: p = this.ignoreFetchAbort, allowStaleOnFetchAbort: T = this.allowStaleOnFetchAbort, context: w, forceRefresh: y = false, status: a, signal: m } = t;
|
|
@@ -30938,7 +31544,7 @@ var L = class u2 {
|
|
|
30938
31544
|
}
|
|
30939
31545
|
let A = this.#p(b);
|
|
30940
31546
|
if (!y && !A)
|
|
30941
|
-
return a && (a.fetch = "hit"), this.#L(b), s && this.#
|
|
31547
|
+
return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
|
|
30942
31548
|
let z5 = this.#P(e, b, _, w), v = z5.__staleWhileFetching !== undefined && i;
|
|
30943
31549
|
return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z5.__staleWhileFetching : z5.__returned = z5;
|
|
30944
31550
|
}
|
|
@@ -30947,7 +31553,7 @@ var L = class u2 {
|
|
|
30947
31553
|
let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
|
|
30948
31554
|
t.status = s, s && t.context && (s.context = t.context);
|
|
30949
31555
|
let n = this.#K(e, t);
|
|
30950
|
-
return s &&
|
|
31556
|
+
return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
|
|
30951
31557
|
}
|
|
30952
31558
|
async#K(e, t = {}) {
|
|
30953
31559
|
let i = await this.#B(e, t);
|
|
@@ -30986,7 +31592,7 @@ var L = class u2 {
|
|
|
30986
31592
|
return;
|
|
30987
31593
|
}
|
|
30988
31594
|
let h = this.#t[r], l = this.#e(h);
|
|
30989
|
-
return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#
|
|
31595
|
+
return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#D(r), l ? h.__staleWhileFetching : h);
|
|
30990
31596
|
}
|
|
30991
31597
|
#$(e, t) {
|
|
30992
31598
|
this.#c[t] = e, this.#a[e] = t;
|
|
@@ -31154,7 +31760,7 @@ function extractStructuredText(root) {
|
|
|
31154
31760
|
]);
|
|
31155
31761
|
const isText = (node) => node.nodeType === node.TEXT_NODE;
|
|
31156
31762
|
const isElement = (node) => node.nodeType === node.ELEMENT_NODE;
|
|
31157
|
-
const
|
|
31763
|
+
const pushText2 = (value) => {
|
|
31158
31764
|
const normalized = value.replace(/\s+/g, " ");
|
|
31159
31765
|
if (!normalized.trim())
|
|
31160
31766
|
return;
|
|
@@ -31180,7 +31786,7 @@ function extractStructuredText(root) {
|
|
|
31180
31786
|
};
|
|
31181
31787
|
const visit = (node) => {
|
|
31182
31788
|
if (isText(node)) {
|
|
31183
|
-
|
|
31789
|
+
pushText2(node.textContent || "");
|
|
31184
31790
|
return;
|
|
31185
31791
|
}
|
|
31186
31792
|
if (!isElement(node))
|
|
@@ -31891,7 +32497,7 @@ async function probeLlmsText(url, timeoutMs, signal, fallbackOrigin) {
|
|
|
31891
32497
|
}
|
|
31892
32498
|
|
|
31893
32499
|
// src/tools/smartfetch/cache.ts
|
|
31894
|
-
var CACHE = new
|
|
32500
|
+
var CACHE = new M({
|
|
31895
32501
|
maxSize: 50 * 1024 * 1024,
|
|
31896
32502
|
ttl: 15 * 60 * 1000,
|
|
31897
32503
|
sizeCalculation: (value) => {
|
|
@@ -31952,7 +32558,7 @@ function isInvalidLlmsResult(fetchResult) {
|
|
|
31952
32558
|
|
|
31953
32559
|
// src/tools/smartfetch/secondary-model.ts
|
|
31954
32560
|
import { existsSync as existsSync10 } from "node:fs";
|
|
31955
|
-
import { readFile as
|
|
32561
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
31956
32562
|
import path17 from "node:path";
|
|
31957
32563
|
function parseModelRef(value) {
|
|
31958
32564
|
if (!value)
|
|
@@ -31989,7 +32595,7 @@ async function readOpenCodeConfigFile(configPath) {
|
|
|
31989
32595
|
if (!configPath)
|
|
31990
32596
|
return;
|
|
31991
32597
|
try {
|
|
31992
|
-
const content = await
|
|
32598
|
+
const content = await readFile5(configPath, "utf8");
|
|
31993
32599
|
return JSON.parse(stripJsonComments(content));
|
|
31994
32600
|
} catch {
|
|
31995
32601
|
return;
|
|
@@ -32639,7 +33245,7 @@ function createWebfetchTool(pluginCtx, options = {}) {
|
|
|
32639
33245
|
});
|
|
32640
33246
|
}
|
|
32641
33247
|
// src/tools/subtask/command.ts
|
|
32642
|
-
var
|
|
33248
|
+
var COMMAND_NAME5 = "subtask";
|
|
32643
33249
|
var SUBTASK_COMMAND_TEMPLATE = `Start a focused subtask worker.
|
|
32644
33250
|
|
|
32645
33251
|
The user's request below is the full scope for the worker. Do not broaden it.
|
|
@@ -32660,11 +33266,11 @@ Only include files that are clearly relevant. If no files are needed, omit files
|
|
|
32660
33266
|
function createSubtaskCommandManager(_ctx, state) {
|
|
32661
33267
|
function registerCommand(opencodeConfig) {
|
|
32662
33268
|
const configCommand = opencodeConfig.command;
|
|
32663
|
-
if (!configCommand?.[
|
|
33269
|
+
if (!configCommand?.[COMMAND_NAME5]) {
|
|
32664
33270
|
if (!opencodeConfig.command) {
|
|
32665
33271
|
opencodeConfig.command = {};
|
|
32666
33272
|
}
|
|
32667
|
-
opencodeConfig.command[
|
|
33273
|
+
opencodeConfig.command[COMMAND_NAME5] = {
|
|
32668
33274
|
description: "Create a focused subtask prompt for a new session",
|
|
32669
33275
|
template: SUBTASK_COMMAND_TEMPLATE
|
|
32670
33276
|
};
|
|
@@ -32691,11 +33297,11 @@ function createSubtaskCommandManager(_ctx, state) {
|
|
|
32691
33297
|
};
|
|
32692
33298
|
}
|
|
32693
33299
|
// src/tools/subtask/files.ts
|
|
32694
|
-
import * as
|
|
33300
|
+
import * as fs12 from "node:fs/promises";
|
|
32695
33301
|
import * as path20 from "node:path";
|
|
32696
33302
|
|
|
32697
33303
|
// src/tools/subtask/vendor.ts
|
|
32698
|
-
import * as
|
|
33304
|
+
import * as fs11 from "node:fs/promises";
|
|
32699
33305
|
import * as path19 from "node:path";
|
|
32700
33306
|
var DEFAULT_READ_LIMIT = 2000;
|
|
32701
33307
|
var MAX_LINE_LENGTH = 2000;
|
|
@@ -32739,7 +33345,7 @@ async function isBinaryFile(filepath) {
|
|
|
32739
33345
|
return true;
|
|
32740
33346
|
}
|
|
32741
33347
|
try {
|
|
32742
|
-
const file = await
|
|
33348
|
+
const file = await fs11.open(filepath, "r");
|
|
32743
33349
|
try {
|
|
32744
33350
|
const buffer = Buffer.alloc(SAMPLE_BYTES);
|
|
32745
33351
|
const result = await file.read(buffer, 0, SAMPLE_BYTES, 0);
|
|
@@ -32825,24 +33431,24 @@ function parseFileReferences(text) {
|
|
|
32825
33431
|
}
|
|
32826
33432
|
async function buildSyntheticFileParts(directory, refs) {
|
|
32827
33433
|
const parts = [];
|
|
32828
|
-
const realDirectory = await
|
|
33434
|
+
const realDirectory = await fs12.realpath(directory);
|
|
32829
33435
|
for (const ref of refs) {
|
|
32830
33436
|
const filepath = path20.resolve(directory, ref);
|
|
32831
33437
|
const relative3 = path20.relative(directory, filepath);
|
|
32832
33438
|
if (relative3.startsWith("..") || path20.isAbsolute(relative3))
|
|
32833
33439
|
continue;
|
|
32834
33440
|
try {
|
|
32835
|
-
const realFilepath = await
|
|
33441
|
+
const realFilepath = await fs12.realpath(filepath);
|
|
32836
33442
|
const realRelative = path20.relative(realDirectory, realFilepath);
|
|
32837
33443
|
if (realRelative.startsWith("..") || path20.isAbsolute(realRelative)) {
|
|
32838
33444
|
continue;
|
|
32839
33445
|
}
|
|
32840
|
-
const stats = await
|
|
33446
|
+
const stats = await fs12.stat(realFilepath);
|
|
32841
33447
|
if (!stats.isFile())
|
|
32842
33448
|
continue;
|
|
32843
33449
|
if (await isBinaryFile(realFilepath))
|
|
32844
33450
|
continue;
|
|
32845
|
-
const content = await
|
|
33451
|
+
const content = await fs12.readFile(realFilepath, "utf-8");
|
|
32846
33452
|
parts.push({
|
|
32847
33453
|
type: "text",
|
|
32848
33454
|
synthetic: true,
|
|
@@ -33204,7 +33810,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33204
33810
|
let jsonErrorRecoveryHook;
|
|
33205
33811
|
let foregroundFallback;
|
|
33206
33812
|
let todoContinuationHook;
|
|
33813
|
+
let sessionGoalHook;
|
|
33207
33814
|
let taskSessionManagerHook;
|
|
33815
|
+
let backgroundJobBoard;
|
|
33208
33816
|
let interviewManager;
|
|
33209
33817
|
let presetManager;
|
|
33210
33818
|
let divoomManager;
|
|
@@ -33290,16 +33898,22 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33290
33898
|
applyPatchHook = createApplyPatchHook(ctx);
|
|
33291
33899
|
jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
33292
33900
|
foregroundFallback = new ForegroundFallbackManager(ctx.client, runtimeChains, config.fallback?.enabled !== false && Object.keys(runtimeChains).length > 0);
|
|
33901
|
+
backgroundJobBoard = new BackgroundJobBoard;
|
|
33293
33902
|
todoContinuationHook = createTodoContinuationHook(ctx, {
|
|
33294
33903
|
maxContinuations: config.todoContinuation?.maxContinuations ?? 5,
|
|
33295
33904
|
cooldownMs: config.todoContinuation?.cooldownMs ?? 3000,
|
|
33296
33905
|
autoEnable: config.todoContinuation?.autoEnable ?? false,
|
|
33297
|
-
autoEnableThreshold: config.todoContinuation?.autoEnableThreshold ?? 4
|
|
33906
|
+
autoEnableThreshold: config.todoContinuation?.autoEnableThreshold ?? 4,
|
|
33907
|
+
backgroundJobBoard
|
|
33908
|
+
});
|
|
33909
|
+
sessionGoalHook = createSessionGoalHook(ctx, config, {
|
|
33910
|
+
getAgentName: (sessionID) => sessionAgentMap.get(sessionID)
|
|
33298
33911
|
});
|
|
33299
33912
|
taskSessionManagerHook = createTaskSessionManagerHook(ctx, {
|
|
33300
33913
|
maxSessionsPerAgent: config.sessionManager?.maxSessionsPerAgent ?? 2,
|
|
33301
33914
|
readContextMinLines: config.sessionManager?.readContextMinLines ?? 10,
|
|
33302
33915
|
readContextMaxFiles: config.sessionManager?.readContextMaxFiles ?? 8,
|
|
33916
|
+
backgroundJobBoard,
|
|
33303
33917
|
shouldManageSession: (sessionID) => sessionAgentMap.get(sessionID) === "orchestrator"
|
|
33304
33918
|
});
|
|
33305
33919
|
interviewManager = createInterviewManager(ctx, config);
|
|
@@ -33550,6 +34164,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33550
34164
|
};
|
|
33551
34165
|
}
|
|
33552
34166
|
interviewManager.registerCommand(opencodeConfig);
|
|
34167
|
+
sessionGoalHook.registerCommand(opencodeConfig);
|
|
33553
34168
|
presetManager.registerCommand(opencodeConfig);
|
|
33554
34169
|
subtaskCommandManager.registerCommand(opencodeConfig);
|
|
33555
34170
|
},
|
|
@@ -33571,12 +34186,13 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33571
34186
|
depthTracker.registerChild(parentSessionId, childSessionId);
|
|
33572
34187
|
}
|
|
33573
34188
|
}
|
|
33574
|
-
await foregroundFallback.handleEvent(input.event);
|
|
33575
|
-
await todoContinuationHook.handleEvent(input);
|
|
33576
|
-
await autoUpdateChecker.event(input);
|
|
33577
34189
|
await multiplexerSessionManager.onSessionCreated(event);
|
|
33578
34190
|
await multiplexerSessionManager.onSessionStatus(event);
|
|
33579
34191
|
await multiplexerSessionManager.onSessionDeleted(event);
|
|
34192
|
+
await foregroundFallback.handleEvent(input.event);
|
|
34193
|
+
await todoContinuationHook.handleEvent(input);
|
|
34194
|
+
sessionGoalHook.handleEvent(input);
|
|
34195
|
+
await autoUpdateChecker.event(input);
|
|
33580
34196
|
await interviewManager.handleEvent(input);
|
|
33581
34197
|
await taskSessionManagerHook.event(input);
|
|
33582
34198
|
subtaskCommandManager.handleEvent(input);
|
|
@@ -33637,6 +34253,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33637
34253
|
await todoContinuationHook.handleCommandExecuteBefore(input, output);
|
|
33638
34254
|
await interviewManager.handleCommandExecuteBefore(input, output);
|
|
33639
34255
|
await presetManager.handleCommandExecuteBefore(input, output);
|
|
34256
|
+
await sessionGoalHook.handleCommandExecuteBefore(input, output);
|
|
33640
34257
|
},
|
|
33641
34258
|
"chat.headers": chatHeadersHook["chat.headers"],
|
|
33642
34259
|
"chat.message": async (input, output) => {
|
|
@@ -33665,6 +34282,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33665
34282
|
${output.system[0]}` : "");
|
|
33666
34283
|
}
|
|
33667
34284
|
}
|
|
34285
|
+
sessionGoalHook.handleSystemTransform(input, output);
|
|
33668
34286
|
collapseSystemInPlace(output.system);
|
|
33669
34287
|
},
|
|
33670
34288
|
"experimental.chat.messages.transform": async (input, output) => {
|