@heretek-ai/epistemic-swarm 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js CHANGED
@@ -94,51 +94,107 @@ var FactoryPanel = (props) => {
94
94
  return _el$8;
95
95
  })(), _$createComponent(Show, {
96
96
  get when() {
97
- return data().halt;
97
+ return data().tree;
98
98
  },
99
99
  get children() {
100
- var _el$0 = _$createElement("text"), _el$1 = _$createTextNode(`HALTED: `);
100
+ var _el$0 = _$createElement("text"), _el$1 = _$createTextNode(`Design tree (round `), _el$10 = _$createTextNode(`): `), _el$11 = _$createTextNode(` settled \xB7 `), _el$12 = _$createTextNode(` open \xB7 `), _el$14 = _$createTextNode(` deferred`);
101
101
  _$insertNode(_el$0, _el$1);
102
- _$insert(_el$0, () => data().halt, null);
102
+ _$insertNode(_el$0, _el$10);
103
+ _$insertNode(_el$0, _el$11);
104
+ _$insertNode(_el$0, _el$12);
105
+ _$insertNode(_el$0, _el$14);
106
+ _$insert(_el$0, () => data().tree.round, _el$10);
107
+ _$insert(_el$0, () => data().tree.settled, _el$11);
108
+ _$insert(_el$0, () => data().tree.open, _el$12);
109
+ _$insert(_el$0, () => data().tree.deferred, _el$14);
110
+ _$insert(_el$0, (() => {
111
+ var _c$3 = _$memo(() => !!data().tree.facts);
112
+ return () => _c$3() ? ` (${data().tree.facts} for research)` : "";
113
+ })(), null);
103
114
  return _el$0;
104
115
  }
116
+ }), _$createComponent(Show, {
117
+ get when() {
118
+ return data().halt;
119
+ },
120
+ get children() {
121
+ var _el$15 = _$createElement("text"), _el$16 = _$createTextNode(`HALTED: `);
122
+ _$insertNode(_el$15, _el$16);
123
+ _$insert(_el$15, () => data().halt, null);
124
+ return _el$15;
125
+ }
105
126
  }), _$createComponent(Show, {
106
127
  get when() {
107
128
  return data().release;
108
129
  },
109
130
  get children() {
110
- var _el$10 = _$createElement("text"), _el$11 = _$createTextNode(`PR: `);
111
- _$insertNode(_el$10, _el$11);
112
- _$insert(_el$10, () => data().release, null);
113
- return _el$10;
131
+ var _el$17 = _$createElement("text"), _el$18 = _$createTextNode(`PR: `);
132
+ _$insertNode(_el$17, _el$18);
133
+ _$insert(_el$17, () => data().release, null);
134
+ return _el$17;
114
135
  }
115
136
  }), _$createComponent(Show, {
116
137
  get when() {
117
138
  return data().pending.length;
118
139
  },
119
140
  get children() {
120
- var _el$12 = _$createElement("text"), _el$13 = _$createTextNode(`Waiting on human: `);
121
- _$insertNode(_el$12, _el$13);
122
- _$insert(_el$12, () => data().pending.join(", "), null);
123
- return _el$12;
141
+ var _el$19 = _$createElement("text"), _el$20 = _$createTextNode(`Waiting on human: `);
142
+ _$insertNode(_el$19, _el$20);
143
+ _$insert(_el$19, () => data().pending.join(", "), null);
144
+ return _el$19;
145
+ }
146
+ }), _$createComponent(Show, {
147
+ get when() {
148
+ return data().audits?.length;
149
+ },
150
+ get children() {
151
+ return [(() => {
152
+ var _el$21 = _$createElement("text");
153
+ _$insertNode(_el$21, _$createTextNode(`Audits:`));
154
+ return _el$21;
155
+ })(), _$createComponent(For, {
156
+ get each() {
157
+ return data().audits;
158
+ },
159
+ children: (audit) => (() => {
160
+ var _el$25 = _$createElement("text"), _el$26 = _$createTextNode(` `), _el$27 = _$createTextNode(` \xB7 `), _el$28 = _$createTextNode(` \xB7 `), _el$29 = _$createTextNode(` r`), _el$30 = _$createTextNode(` \xB7 t:`), _el$31 = _$createTextNode(` a:`);
161
+ _$insertNode(_el$25, _el$26);
162
+ _$insertNode(_el$25, _el$27);
163
+ _$insertNode(_el$25, _el$28);
164
+ _$insertNode(_el$25, _el$29);
165
+ _$insertNode(_el$25, _el$30);
166
+ _$insertNode(_el$25, _el$31);
167
+ _$insert(_el$25, () => audit.id, _el$27);
168
+ _$insert(_el$25, () => audit.target, _el$28);
169
+ _$insert(_el$25, () => audit.status, _el$29);
170
+ _$insert(_el$25, () => audit.round, _el$30);
171
+ _$insert(_el$25, () => audit.thesis, _el$31);
172
+ _$insert(_el$25, () => audit.antithesis, null);
173
+ _$insert(_el$25, (() => {
174
+ var _c$4 = _$memo(() => !!audit.tiebreak);
175
+ return () => _c$4() ? ` x:${audit.tiebreak}` : "";
176
+ })(), null);
177
+ return _el$25;
178
+ })()
179
+ })];
124
180
  }
125
181
  }), (() => {
126
- var _el$14 = _$createElement("text");
127
- _$insertNode(_el$14, _$createTextNode(`Phases:`));
128
- return _el$14;
182
+ var _el$23 = _$createElement("text");
183
+ _$insertNode(_el$23, _$createTextNode(`Phases:`));
184
+ return _el$23;
129
185
  })(), _$createComponent(For, {
130
186
  get each() {
131
187
  return data().phases;
132
188
  },
133
189
  children: (phase) => (() => {
134
- var _el$16 = _$createElement("text"), _el$17 = _$createTextNode(` \xB7 `), _el$18 = _$createTextNode(` \xB7 failures `);
135
- _$insertNode(_el$16, _el$17);
136
- _$insertNode(_el$16, _el$18);
137
- _$insert(_el$16, () => phase.id === data().activePhase ? "\u25B6 " : " ", _el$17);
138
- _$insert(_el$16, () => phase.id, _el$17);
139
- _$insert(_el$16, () => phase.status, _el$18);
140
- _$insert(_el$16, () => phase.failures, null);
141
- return _el$16;
190
+ var _el$32 = _$createElement("text"), _el$33 = _$createTextNode(` \xB7 `), _el$34 = _$createTextNode(` \xB7 failures `);
191
+ _$insertNode(_el$32, _el$33);
192
+ _$insertNode(_el$32, _el$34);
193
+ _$insert(_el$32, () => phase.id === data().activePhase ? "\u25B6 " : " ", _el$33);
194
+ _$insert(_el$32, () => phase.id, _el$33);
195
+ _$insert(_el$32, () => phase.status, _el$34);
196
+ _$insert(_el$32, () => phase.failures, null);
197
+ return _el$32;
142
198
  })()
143
199
  })]
144
200
  })];
@@ -160,43 +216,43 @@ var LspPanel = (props) => {
160
216
  },
161
217
  get fallback() {
162
218
  return (() => {
163
- var _el$19 = _$createElement("text");
164
- _$insert(_el$19, () => state.error() ? "" : "Loading language servers\u2026");
165
- return _el$19;
219
+ var _el$35 = _$createElement("text");
220
+ _$insert(_el$35, () => state.error() ? "" : "Loading language servers\u2026");
221
+ return _el$35;
166
222
  })();
167
223
  },
168
224
  children: (data) => [(() => {
169
- var _el$20 = _$createElement("text");
170
- _$insert(_el$20, () => data().enabled ? "LSP is on." : "LSP is disabled (lsp: false).");
171
- return _el$20;
225
+ var _el$36 = _$createElement("text");
226
+ _$insert(_el$36, () => data().enabled ? "LSP is on." : "LSP is disabled (lsp: false).");
227
+ return _el$36;
172
228
  })(), _$createComponent(For, {
173
229
  get each() {
174
230
  return data().servers;
175
231
  },
176
232
  children: (server) => (() => {
177
- var _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` `), _el$23 = _$createTextNode(` [`), _el$24 = _$createTextNode(`]`);
178
- _$insertNode(_el$21, _el$22);
179
- _$insertNode(_el$21, _el$23);
180
- _$insertNode(_el$21, _el$24);
181
- _$insert(_el$21, () => server.available ? "\u25CF" : "\u25CB", _el$22);
182
- _$insert(_el$21, () => server.id, _el$23);
183
- _$insert(_el$21, () => server.extensions.join(" "), _el$24);
184
- _$insert(_el$21, (() => {
185
- var _c$3 = _$memo(() => !!server.running);
186
- return () => _c$3() ? ` \xB7 ${server.running} running` : "";
233
+ var _el$37 = _$createElement("text"), _el$38 = _$createTextNode(` `), _el$39 = _$createTextNode(` [`), _el$40 = _$createTextNode(`]`);
234
+ _$insertNode(_el$37, _el$38);
235
+ _$insertNode(_el$37, _el$39);
236
+ _$insertNode(_el$37, _el$40);
237
+ _$insert(_el$37, () => server.available ? "\u25CF" : "\u25CB", _el$38);
238
+ _$insert(_el$37, () => server.id, _el$39);
239
+ _$insert(_el$37, () => server.extensions.join(" "), _el$40);
240
+ _$insert(_el$37, (() => {
241
+ var _c$5 = _$memo(() => !!server.running);
242
+ return () => _c$5() ? ` \xB7 ${server.running} running` : "";
187
243
  })(), null);
188
- _$insert(_el$21, () => server.available ? "" : " \u2014 not installed", null);
189
- return _el$21;
244
+ _$insert(_el$37, () => server.available ? "" : " \u2014 not installed", null);
245
+ return _el$37;
190
246
  })()
191
247
  }), _$createComponent(For, {
192
248
  get each() {
193
249
  return data().diagnostics;
194
250
  },
195
251
  children: (diagnostic) => (() => {
196
- var _el$25 = _$createElement("text"), _el$26 = _$createTextNode(`! `);
197
- _$insertNode(_el$25, _el$26);
198
- _$insert(_el$25, diagnostic, null);
199
- return _el$25;
252
+ var _el$41 = _$createElement("text"), _el$42 = _$createTextNode(`! `);
253
+ _$insertNode(_el$41, _el$42);
254
+ _$insert(_el$41, diagnostic, null);
255
+ return _el$41;
200
256
  })()
201
257
  })]
202
258
  })];
@@ -218,30 +274,30 @@ var HooksPanel = (props) => {
218
274
  },
219
275
  get fallback() {
220
276
  return (() => {
221
- var _el$27 = _$createElement("text");
222
- _$insert(_el$27, () => state.error() ? "" : "Loading hooks\u2026");
223
- return _el$27;
277
+ var _el$43 = _$createElement("text");
278
+ _$insert(_el$43, () => state.error() ? "" : "Loading hooks\u2026");
279
+ return _el$43;
224
280
  })();
225
281
  },
226
282
  children: (data) => [(() => {
227
- var _el$28 = _$createElement("text"), _el$29 = _$createTextNode(` handler(s) \xB7 `), _el$30 = _$createTextNode(` from the project \xB7 `);
228
- _$insertNode(_el$28, _el$29);
229
- _$insertNode(_el$28, _el$30);
230
- _$insert(_el$28, () => data().handlers, _el$29);
231
- _$insert(_el$28, () => data().projectHandlers, _el$30);
232
- _$insert(_el$28, (() => {
233
- var _c$4 = _$memo(() => data().projectHandlers === 0);
234
- return () => _c$4() ? "nothing to trust" : data().trusted ? "trusted" : "NOT trusted";
283
+ var _el$44 = _$createElement("text"), _el$45 = _$createTextNode(` handler(s) \xB7 `), _el$46 = _$createTextNode(` from the project \xB7 `);
284
+ _$insertNode(_el$44, _el$45);
285
+ _$insertNode(_el$44, _el$46);
286
+ _$insert(_el$44, () => data().handlers, _el$45);
287
+ _$insert(_el$44, () => data().projectHandlers, _el$46);
288
+ _$insert(_el$44, (() => {
289
+ var _c$6 = _$memo(() => data().projectHandlers === 0);
290
+ return () => _c$6() ? "nothing to trust" : data().trusted ? "trusted" : "NOT trusted";
235
291
  })(), null);
236
- return _el$28;
292
+ return _el$44;
237
293
  })(), _$createComponent(For, {
238
294
  get each() {
239
295
  return data().projectLines;
240
296
  },
241
297
  children: (line) => (() => {
242
- var _el$34 = _$createElement("text");
243
- _$insert(_el$34, line);
244
- return _el$34;
298
+ var _el$50 = _$createElement("text");
299
+ _$insert(_el$50, line);
300
+ return _el$50;
245
301
  })()
246
302
  }), _$createComponent(Show, {
247
303
  get when() {
@@ -249,24 +305,24 @@ var HooksPanel = (props) => {
249
305
  },
250
306
  get children() {
251
307
  return [(() => {
252
- var _el$32 = _$createElement("text");
253
- _$insertNode(_el$32, _$createTextNode(`Capability loss (advisory rows):`));
254
- return _el$32;
308
+ var _el$48 = _$createElement("text");
309
+ _$insertNode(_el$48, _$createTextNode(`Capability loss (advisory rows):`));
310
+ return _el$48;
255
311
  })(), _$createComponent(For, {
256
312
  get each() {
257
313
  return data().loss;
258
314
  },
259
315
  children: (item) => (() => {
260
- var _el$35 = _$createElement("text"), _el$36 = _$createTextNode(`! `), _el$37 = _$createTextNode(` \xB7 `), _el$38 = _$createTextNode(` \u2014 `), _el$39 = _$createTextNode(`: `);
261
- _$insertNode(_el$35, _el$36);
262
- _$insertNode(_el$35, _el$37);
263
- _$insertNode(_el$35, _el$38);
264
- _$insertNode(_el$35, _el$39);
265
- _$insert(_el$35, () => item.event, _el$37);
266
- _$insert(_el$35, () => item.handler, _el$38);
267
- _$insert(_el$35, () => item.support, _el$39);
268
- _$insert(_el$35, () => item.reason, null);
269
- return _el$35;
316
+ var _el$51 = _$createElement("text"), _el$52 = _$createTextNode(`! `), _el$53 = _$createTextNode(` \xB7 `), _el$54 = _$createTextNode(` \u2014 `), _el$55 = _$createTextNode(`: `);
317
+ _$insertNode(_el$51, _el$52);
318
+ _$insertNode(_el$51, _el$53);
319
+ _$insertNode(_el$51, _el$54);
320
+ _$insertNode(_el$51, _el$55);
321
+ _$insert(_el$51, () => item.event, _el$53);
322
+ _$insert(_el$51, () => item.handler, _el$54);
323
+ _$insert(_el$51, () => item.support, _el$55);
324
+ _$insert(_el$51, () => item.reason, null);
325
+ return _el$51;
270
326
  })()
271
327
  })];
272
328
  }
@@ -275,20 +331,20 @@ var HooksPanel = (props) => {
275
331
  return data().diagnostics;
276
332
  },
277
333
  children: (item) => (() => {
278
- var _el$40 = _$createElement("text"), _el$41 = _$createTextNode(`! `);
279
- _$insertNode(_el$40, _el$41);
280
- _$insert(_el$40, item, null);
281
- return _el$40;
334
+ var _el$56 = _$createElement("text"), _el$57 = _$createTextNode(`! `);
335
+ _$insertNode(_el$56, _el$57);
336
+ _$insert(_el$56, item, null);
337
+ return _el$56;
282
338
  })()
283
339
  }), _$createComponent(For, {
284
340
  get each() {
285
341
  return data().recent;
286
342
  },
287
343
  children: (item) => (() => {
288
- var _el$42 = _$createElement("text"), _el$43 = _$createTextNode(`recent error: `);
289
- _$insertNode(_el$42, _el$43);
290
- _$insert(_el$42, item, null);
291
- return _el$42;
344
+ var _el$58 = _$createElement("text"), _el$59 = _$createTextNode(`recent error: `);
345
+ _$insertNode(_el$58, _el$59);
346
+ _$insert(_el$58, item, null);
347
+ return _el$58;
292
348
  })()
293
349
  })]
294
350
  })];
@@ -310,9 +366,9 @@ var BrainstormPanel = (props) => {
310
366
  },
311
367
  get fallback() {
312
368
  return (() => {
313
- var _el$44 = _$createElement("text");
314
- _$insert(_el$44, () => state.error() ? "" : "Loading brainstorm\u2026");
315
- return _el$44;
369
+ var _el$60 = _$createElement("text");
370
+ _$insert(_el$60, () => state.error() ? "" : "Loading brainstorm\u2026");
371
+ return _el$60;
316
372
  })();
317
373
  },
318
374
  children: (data) => _$createComponent(Show, {
@@ -321,41 +377,41 @@ var BrainstormPanel = (props) => {
321
377
  },
322
378
  get fallback() {
323
379
  return (() => {
324
- var _el$59 = _$createElement("text");
325
- _$insertNode(_el$59, _$createTextNode(`No brainstorm in .factory/brainstorm yet. Run /brainstorm.`));
326
- return _el$59;
380
+ var _el$75 = _$createElement("text");
381
+ _$insertNode(_el$75, _$createTextNode(`No brainstorm in .factory/brainstorm yet. Run /brainstorm.`));
382
+ return _el$75;
327
383
  })();
328
384
  },
329
385
  get children() {
330
386
  return [(() => {
331
- var _el$45 = _$createElement("text"), _el$46 = _$createTextNode(`Brief: `);
332
- _$insertNode(_el$45, _el$46);
333
- _$insert(_el$45, () => data().brief, null);
334
- return _el$45;
387
+ var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(`Brief: `);
388
+ _$insertNode(_el$61, _el$62);
389
+ _$insert(_el$61, () => data().brief, null);
390
+ return _el$61;
335
391
  })(), (() => {
336
- var _el$47 = _$createElement("text"), _el$48 = _$createTextNode(` idea(s) \xB7 `), _el$49 = _$createTextNode(` duplicate(s) \xB7 `), _el$50 = _$createTextNode(` scored \xB7 `);
337
- _$insertNode(_el$47, _el$48);
338
- _$insertNode(_el$47, _el$49);
339
- _$insertNode(_el$47, _el$50);
340
- _$insert(_el$47, () => data().ideas, _el$48);
341
- _$insert(_el$47, () => data().duplicates, _el$49);
342
- _$insert(_el$47, () => data().scored, _el$50);
343
- _$insert(_el$47, () => data().complete ? "complete" : "in progress", null);
344
- return _el$47;
392
+ var _el$63 = _$createElement("text"), _el$64 = _$createTextNode(` idea(s) \xB7 `), _el$65 = _$createTextNode(` duplicate(s) \xB7 `), _el$66 = _$createTextNode(` scored \xB7 `);
393
+ _$insertNode(_el$63, _el$64);
394
+ _$insertNode(_el$63, _el$65);
395
+ _$insertNode(_el$63, _el$66);
396
+ _$insert(_el$63, () => data().ideas, _el$64);
397
+ _$insert(_el$63, () => data().duplicates, _el$65);
398
+ _$insert(_el$63, () => data().scored, _el$66);
399
+ _$insert(_el$63, () => data().complete ? "complete" : "in progress", null);
400
+ return _el$63;
345
401
  })(), (() => {
346
- var _el$52 = _$createElement("text"), _el$53 = _$createTextNode(`Coverage: `);
347
- _$insertNode(_el$52, _el$53);
348
- _$insert(_el$52, () => data().lenses.map((lens) => `${lens} ${data().coverage[lens] ?? 0}`).join(" \xB7 "), null);
349
- return _el$52;
402
+ var _el$68 = _$createElement("text"), _el$69 = _$createTextNode(`Coverage: `);
403
+ _$insertNode(_el$68, _el$69);
404
+ _$insert(_el$68, () => data().lenses.map((lens) => `${lens} ${data().coverage[lens] ?? 0}`).join(" \xB7 "), null);
405
+ return _el$68;
350
406
  })(), _$createComponent(Show, {
351
407
  get when() {
352
408
  return data().gaps.length;
353
409
  },
354
410
  get children() {
355
- var _el$55 = _$createElement("text"), _el$56 = _$createTextNode(`Gaps: `);
356
- _$insertNode(_el$55, _el$56);
357
- _$insert(_el$55, () => data().gaps.join(", "), null);
358
- return _el$55;
411
+ var _el$71 = _$createElement("text"), _el$72 = _$createTextNode(`Gaps: `);
412
+ _$insertNode(_el$71, _el$72);
413
+ _$insert(_el$71, () => data().gaps.join(", "), null);
414
+ return _el$71;
359
415
  }
360
416
  }), _$createComponent(Show, {
361
417
  get when() {
@@ -363,24 +419,24 @@ var BrainstormPanel = (props) => {
363
419
  },
364
420
  get children() {
365
421
  return [(() => {
366
- var _el$57 = _$createElement("text");
367
- _$insertNode(_el$57, _$createTextNode(`Shortlist:`));
368
- return _el$57;
422
+ var _el$73 = _$createElement("text");
423
+ _$insertNode(_el$73, _$createTextNode(`Shortlist:`));
424
+ return _el$73;
369
425
  })(), _$createComponent(For, {
370
426
  get each() {
371
427
  return data().shortlist;
372
428
  },
373
429
  children: (entry) => (() => {
374
- var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createTextNode(` (`), _el$64 = _$createTextNode(`/20) \u2014 `);
375
- _$insertNode(_el$61, _el$62);
376
- _$insertNode(_el$61, _el$63);
377
- _$insertNode(_el$61, _el$64);
378
- _$insert(_el$61, () => entry.outlier ? "\u25C7" : "\u25C6", _el$62);
379
- _$insert(_el$61, () => entry.id, _el$63);
380
- _$insert(_el$61, () => entry.total, _el$64);
381
- _$insert(_el$61, () => entry.title, null);
382
- _$insert(_el$61, () => entry.outlier ? " (forced outlier)" : "", null);
383
- return _el$61;
430
+ var _el$77 = _$createElement("text"), _el$78 = _$createTextNode(` `), _el$79 = _$createTextNode(` (`), _el$80 = _$createTextNode(`/20) \u2014 `);
431
+ _$insertNode(_el$77, _el$78);
432
+ _$insertNode(_el$77, _el$79);
433
+ _$insertNode(_el$77, _el$80);
434
+ _$insert(_el$77, () => entry.outlier ? "\u25C7" : "\u25C6", _el$78);
435
+ _$insert(_el$77, () => entry.id, _el$79);
436
+ _$insert(_el$77, () => entry.total, _el$80);
437
+ _$insert(_el$77, () => entry.title, null);
438
+ _$insert(_el$77, () => entry.outlier ? " (forced outlier)" : "", null);
439
+ return _el$77;
384
440
  })()
385
441
  })];
386
442
  }
@@ -511,7 +567,12 @@ var EsRpc = define({
511
567
  "estimated"
512
568
  ]),
513
569
  phases: { type: "array" },
514
- pending: strArray
570
+ pending: strArray,
571
+ audits: { type: "array" },
572
+ tree: obj(Object.fromEntries(["round", "total", "settled", "open", "deferred", "facts", "frontier"].map((key) => [
573
+ key,
574
+ { type: "number" }
575
+ ])), ["round", "total", "settled", "open", "deferred", "facts", "frontier"])
515
576
  }, ["stage", "spend", "phases", "pending"])
516
577
  },
517
578
  lspState: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heretek-ai/epistemic-swarm",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Epistemic Swarm: an AI build factory for OpenCode v2 — grill, research, spec, build, QA and release, gated mechanically.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -41,7 +41,7 @@
41
41
  "test": "bun test"
42
42
  },
43
43
  "dependencies": {
44
- "@heretek-ai/es-core": "1.0.4",
44
+ "@heretek-ai/es-core": "1.1.0",
45
45
  "@opencode/plugin": "2.0.24"
46
46
  },
47
47
  "devDependencies": {
package/src/agents.ts CHANGED
@@ -12,6 +12,9 @@ export interface CompiledAgent {
12
12
  readonly model?: { providerID: string; id: string }
13
13
  }
14
14
 
15
+ /** The host's own web tools, denied to seats whose web access is "cached". */
16
+ export const HOST_WEB_TOOLS = ["websearch", "webfetch"] as const
17
+
15
18
  export function permissionRules(spec: AgentSpec, mcpServers: readonly string[]): CompiledAgent["permissions"] {
16
19
  const rules: CompiledAgent["permissions"] = [
17
20
  { action: "es_*", resource: "*", effect: "deny" },
@@ -32,6 +35,9 @@ export function permissionRules(spec: AgentSpec, mcpServers: readonly string[]):
32
35
  // this only grants autonomous seats access to a scratch directory.
33
36
  rules.push({ action: "external_directory", resource: "/tmp/*", effect: "allow" }) // NOSONAR
34
37
  }
38
+ if (spec.web === "cached")
39
+ // Search as policy: web facts only through the cached, citable es_research_* tools.
40
+ for (const action of HOST_WEB_TOOLS) rules.push({ action, resource: "*", effect: "deny" })
35
41
  if (spec.writes.length === 0) rules.push({ action: "edit", resource: "*", effect: "deny" })
36
42
  if (spec.lsp === "none") rules.push({ action: "lsp", resource: "*", effect: "deny" })
37
43
  if (spec.lsp !== "full") rules.push({ action: "lsp_rename", resource: "*", effect: "deny" })
package/src/continue.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // work remains (not halted, not waiting on a human approval or the grill), we
3
3
  // re-prompt it to continue. Progress-aware: three idle turns without any state
4
4
  // change stop the loop so a confused agent cannot spin on budget.
5
- import { factorySummary, pendingApprovals } from "@heretek-ai/es-core"
5
+ import { pendingApprovals } from "@heretek-ai/es-core"
6
6
  import type { Runtime } from "./runtime.ts"
7
7
 
8
8
  const ACTIVE = new Set(["RESEARCH", "SPEC", "BUILD", "QA", "RELEASE"])
@@ -34,7 +34,7 @@ export function createFactoryContinuation(
34
34
  if (count >= STALL_LIMIT) return
35
35
  await ctx.session.prompt({
36
36
  sessionID,
37
- text: `${factorySummary(state)}\nThe factory run is not finished. Continue from the current stage (no human is needed unless an approval is due).`,
37
+ text: `${await runtime.factory.summary(state)}\nThe factory run is not finished. Continue from the current stage (no human is needed unless an approval is due).`,
38
38
  })
39
39
  }
40
40
  }
package/src/panels.tsx CHANGED
@@ -68,6 +68,13 @@ const FactoryPanel = (props: PanelProps) => {
68
68
  {data().spend.ceilingUSD ? ` / $${data().spend.ceilingUSD}` : ""}
69
69
  {data().spend.estimated ? " (estimated)" : ""}
70
70
  </text>
71
+ <Show when={data().tree}>
72
+ <text>
73
+ Design tree (round {data().tree.round}): {data().tree.settled} settled · {data().tree.open} open ·{" "}
74
+ {data().tree.deferred} deferred
75
+ {data().tree.facts ? ` (${data().tree.facts} for research)` : ""}
76
+ </text>
77
+ </Show>
71
78
  <Show when={data().halt}>
72
79
  <text>HALTED: {data().halt}</text>
73
80
  </Show>
@@ -77,6 +84,18 @@ const FactoryPanel = (props: PanelProps) => {
77
84
  <Show when={data().pending.length}>
78
85
  <text>Waiting on human: {data().pending.join(", ")}</text>
79
86
  </Show>
87
+ <Show when={data().audits?.length}>
88
+ <text>Audits:</text>
89
+ <For each={data().audits}>
90
+ {(audit: any) => (
91
+ <text>
92
+ {" "}
93
+ {audit.id} · {audit.target} · {audit.status} r{audit.round} · t:{audit.thesis} a:{audit.antithesis}
94
+ {audit.tiebreak ? ` x:${audit.tiebreak}` : ""}
95
+ </text>
96
+ )}
97
+ </For>
98
+ </Show>
80
99
  <text>Phases:</text>
81
100
  <For each={data().phases}>
82
101
  {(phase: any) => (
package/src/policy.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  // read-only shells under bubblewrap, and get fast gate feedback after edits.
5
5
  import path from "node:path"
6
6
  import {
7
+ agentSpec,
7
8
  bwrapAvailable,
8
9
  checkStopFile,
9
10
  evaluateRead,
@@ -19,6 +20,7 @@ import {
19
20
  treeFingerprint,
20
21
  } from "@heretek-ai/es-core"
21
22
  import { Error as ToolError } from "@opencode/plugin/promise/tool"
23
+ import { HOST_WEB_TOOLS } from "./agents.ts"
22
24
  import type { Runtime } from "./runtime.ts"
23
25
 
24
26
  const WRITE_TOOLS = new Set(["write", "edit"])
@@ -58,11 +60,17 @@ export function createPolicyHooks(runtime: Runtime) {
58
60
  if (stop.stopped)
59
61
  deny(`The factory is stopped (.factory/STOP: ${stop.reason}). A human must remove it and resume.`)
60
62
  }
61
- if (seat && !event.tool.startsWith("es_")) {
63
+ // A halted run (spend ceiling, runtime cap, drift, STOP) stops every seat
64
+ // tool except es_status, so no seat keeps spending through its own es_* tools.
65
+ if (seat && event.tool !== "es_status") {
62
66
  const state = await runtime.factory.read().catch(() => undefined)
63
67
  if (state?.stage === "HALTED")
64
68
  deny(`The factory is halted: ${state.halt?.reason}. Stop and report; a human must resume.`)
65
69
  }
70
+ if (agentSpec(event.agent)?.web === "cached" && (HOST_WEB_TOOLS as readonly string[]).includes(event.tool))
71
+ deny(
72
+ `Seat "${event.agent}" gets web facts only through es_research_search and es_research_fetch (cached and citable); ${event.tool} is not available to it.`,
73
+ )
66
74
  const context = await runtime.policy()
67
75
  if (WRITE_TOOLS.has(event.tool) && typeof input.path === "string") {
68
76
  const decision = evaluateWrite(context, event.agent, input.path)
package/src/research.ts CHANGED
@@ -8,14 +8,16 @@ export function registerWebsearch(
8
8
  editor: { add(definition: any): void; default: { get(): unknown; set(id: string): void } },
9
9
  runtime: Runtime,
10
10
  ) {
11
- const provider = selectProvider(runtime.options.searchProvider)
11
+ const searxng = runtime.config.research?.searxngUrl
12
+ const env = searxng ? { ...process.env, SEARXNG_URL: searxng } : process.env
13
+ const provider = selectProvider(runtime.options.searchProvider, env)
12
14
  if (!provider) return
13
15
  const cache = researchCache(runtime.root)
14
16
  editor.add({
15
17
  id: "epistemic-swarm",
16
18
  name: `Epistemic Swarm (${provider.id}, cached)`,
17
19
  execute: async (input: { query: string; limit?: number }, context: { signal: AbortSignal }) => {
18
- const results = await provider.search(input.query, { limit: input.limit ?? 8, signal: context.signal })
20
+ const results = await provider.search(input.query, { limit: input.limit ?? 8, signal: context.signal, env })
19
21
  const out = []
20
22
  for (const result of results) {
21
23
  const cached = await cache.put({
package/src/rpc-def.ts CHANGED
@@ -81,6 +81,16 @@ export const EsRpc = Rpc.define({
81
81
  ]),
82
82
  phases: { type: "array" },
83
83
  pending: strArray,
84
+ audits: { type: "array" },
85
+ tree: obj(
86
+ Object.fromEntries(
87
+ ["round", "total", "settled", "open", "deferred", "facts", "frontier"].map((key) => [
88
+ key,
89
+ { type: "number" },
90
+ ]),
91
+ ),
92
+ ["round", "total", "settled", "open", "deferred", "facts", "frontier"],
93
+ ),
84
94
  },
85
95
  ["stage", "spend", "phases", "pending"],
86
96
  ),
package/src/rpc.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  import { randomBytes } from "node:crypto"
6
6
  import {
7
7
  type ApprovalStage,
8
+ allAudits,
8
9
  approvalSubject,
9
10
  capabilityLoss,
10
11
  commandSetHash,
12
+ describeTarget,
11
13
  factoryLayout,
12
14
  factorySummary,
13
15
  type HookEngine,
@@ -15,12 +17,14 @@ import {
15
17
  installServer,
16
18
  isTrusted,
17
19
  loadGatesConfig,
20
+ readFrontier,
18
21
  readIdeas,
19
22
  readPlan,
20
23
  readResult,
21
24
  readScores,
22
25
  recordApproval,
23
26
  recordConsent,
27
+ treeCounts,
24
28
  trustProject,
25
29
  verifyControl,
26
30
  writeJson,
@@ -75,14 +79,16 @@ export function createRpcHandlers(runtime: Runtime, notify: () => Promise<void>,
75
79
  status: async () => {
76
80
  const state = await runtime.factory.read()
77
81
  return {
78
- summary: factorySummary(state),
82
+ summary: await runtime.factory.summary(state),
79
83
  stage: state?.stage ?? "NONE",
80
84
  pending: (await pendingApprovals(runtime.root)).map((item) => item.stage),
81
85
  }
82
86
  },
83
87
  factoryState: async () => {
84
88
  const state = await runtime.factory.read()
89
+ const frontier = await readFrontier(runtime.root).catch(() => undefined)
85
90
  return {
91
+ ...(frontier ? { tree: treeCounts(frontier) } : {}),
86
92
  stage: state?.stage ?? "NONE",
87
93
  ...(state?.runId ? { runId: state.runId } : {}),
88
94
  ...(state?.halt ? { halt: state.halt.reason } : {}),
@@ -101,6 +107,15 @@ export function createRpcHandlers(runtime: Runtime, notify: () => Promise<void>,
101
107
  qa: Object.fromEntries(Object.entries(phase.qa).filter(([, verdict]) => verdict !== undefined)),
102
108
  })),
103
109
  pending: (await pendingApprovals(runtime.root)).map((item) => item.stage),
110
+ audits: (state ? allAudits(state) : []).map((audit) => ({
111
+ id: audit.id,
112
+ target: describeTarget(audit.target),
113
+ status: audit.status,
114
+ round: audit.round,
115
+ thesis: audit.thesis?.verdict ?? "-",
116
+ antithesis: audit.antithesis?.verdict ?? "-",
117
+ ...(audit.tiebreak ? { tiebreak: audit.tiebreak.verdict } : {}),
118
+ })),
104
119
  }
105
120
  },
106
121
  lspState: async () => {
package/src/runtime.ts CHANGED
@@ -73,6 +73,8 @@ export async function createRuntime(root: string, raw: RawPluginOptions): Promis
73
73
  gates: gateRunner({ stateDir }),
74
74
  ...(options.pr === "off" ? {} : { pr: ghPrOpener }),
75
75
  stateDir,
76
+ researchDepth: config.research.depth,
77
+ ...(config.domainPack ? { domainPack: config.domainPack } : {}),
76
78
  })
77
79
  return {
78
80
  root,
package/src/server.ts CHANGED
@@ -4,11 +4,12 @@
4
4
  import { readdir, readFile } from "node:fs/promises"
5
5
  import path from "node:path"
6
6
  import {
7
+ describeTarget,
7
8
  factoryLayout,
8
- factorySummary,
9
9
  formatReport,
10
10
  git,
11
11
  loadSkills,
12
+ parseAuditTarget,
12
13
  readJson,
13
14
  researchSourcesDir,
14
15
  runGates,
@@ -102,7 +103,7 @@ export default Plugin.define({
102
103
  const notify = async () => {
103
104
  const state = await runtime.factory.read()
104
105
  await registration?.events
105
- .emit("changed", { stage: state?.stage ?? "NONE", summary: factorySummary(state) })
106
+ .emit("changed", { stage: state?.stage ?? "NONE", summary: await runtime.factory.summary(state) })
106
107
  .catch(() => undefined)
107
108
  }
108
109
  registration = await ctx.rpc.register(EsRpc, createRpcHandlers(runtime, notify, bridge.engine) as any)
@@ -166,16 +167,75 @@ export default Plugin.define({
166
167
  } as any)
167
168
  },
168
169
  })
170
+ editor.add({
171
+ name: "scout",
172
+ description: "Find and vet open-source candidates for a feature: licenses, maintenance, CVEs (Epistemic Swarm)",
173
+ execute: async ({ sessionID, prompt, delivery }) => {
174
+ await ctx.session.switchAgent({ sessionID, agent: "scout" } as any)
175
+ await ctx.session.prompt({
176
+ ...prompt,
177
+ sessionID,
178
+ text: prompt.text?.trim()
179
+ ? `Scout: ${prompt.text}`
180
+ : "Ask me for the feature to scout and our license posture, then plan the candidates.",
181
+ delivery,
182
+ } as any)
183
+ },
184
+ })
185
+ editor.add({
186
+ name: "audit",
187
+ description: "Open a code audit of the active phase or a path and run the auditor pair (Epistemic Swarm)",
188
+ execute: async ({ sessionID, prompt, delivery }) => {
189
+ const target = prompt.text?.trim()
190
+ const say = async (text: string) => {
191
+ await ctx.session.synthetic({ sessionID, text } as any)
192
+ }
193
+ if (!target) return say("Usage: /audit <active phase id | path in the project>")
194
+ const state = await runtime.factory.read().catch(() => undefined)
195
+ if (state?.spendCeilingUSD === undefined)
196
+ return say(
197
+ "Audits run inside a factory run with a spend ceiling. Start one at a terminal with `es audit <target> --max-usd N`, or run /grill first.",
198
+ )
199
+ let opened: Awaited<ReturnType<typeof runtime.factory.openAudit>>
200
+ try {
201
+ // The human typed /audit: opening it is their action.
202
+ opened = await runtime.factory.openAudit(
203
+ "human:tui",
204
+ parseAuditTarget(
205
+ target,
206
+ state.phases.map((phase) => phase.id),
207
+ ),
208
+ )
209
+ } catch (error) {
210
+ return say(`Could not open the audit: ${error instanceof Error ? error.message : String(error)}`)
211
+ }
212
+ const { audit } = opened
213
+ await ctx.session.switchAgent({ sessionID, agent: "factory" } as any)
214
+ await ctx.session.prompt({
215
+ ...prompt,
216
+ sessionID,
217
+ text: `${await runtime.factory.summary()}\nThe human opened code audit ${audit.id} (round ${audit.round}) on ${describeTarget(audit.target)}. Launch es-auditor-thesis and es-auditor-antithesis in parallel with the audit id and target; if they split, have es-manager break the tie (es_tiebreak with audit). Report the verdicts and the report path when the audit settles.`,
218
+ delivery,
219
+ } as any)
220
+ },
221
+ })
169
222
  editor.add({
170
223
  name: "factory",
171
224
  description: "Drive the build factory from its current stage (Epistemic Swarm)",
172
225
  execute: async ({ sessionID, prompt, delivery }) => {
173
- await ctx.session.switchAgent({ sessionID, agent: "factory" } as any)
174
226
  const state = await runtime.factory.read()
227
+ if (!state) {
228
+ await ctx.session.synthetic({
229
+ sessionID,
230
+ text: "No factory run here yet — run /grill first.",
231
+ } as any)
232
+ return
233
+ }
234
+ await ctx.session.switchAgent({ sessionID, agent: "factory" } as any)
175
235
  await ctx.session.prompt({
176
236
  ...prompt,
177
237
  sessionID,
178
- text: `${factorySummary(state)}\nContinue the factory run from its current stage.${prompt.text?.trim() ? `\nHuman note: ${prompt.text}` : ""}`,
238
+ text: `${await runtime.factory.summary(state)}\nContinue the factory run from its current stage.${prompt.text?.trim() ? `\nHuman note: ${prompt.text}` : ""}`,
179
239
  delivery,
180
240
  } as any)
181
241
  },
@@ -241,7 +301,7 @@ export default Plugin.define({
241
301
  name: "status",
242
302
  description: "Show the factory state (Epistemic Swarm)",
243
303
  execute: async ({ sessionID }) => {
244
- await ctx.session.synthetic({ sessionID, text: factorySummary(await runtime.factory.read()) } as any)
304
+ await ctx.session.synthetic({ sessionID, text: await runtime.factory.summary() } as any)
245
305
  },
246
306
  })
247
307
  editor.add({
package/src/session.ts CHANGED
@@ -14,15 +14,17 @@ export function createSessionHooks(runtime: Runtime) {
14
14
  for (const name of ALL_ES_TOOLS) if (!visible.has(name)) delete event.tools[name]
15
15
  if (!FACTORY_AGENTS.has(event.agent)) return
16
16
  const state = await runtime.factory.read().catch(() => undefined)
17
- event.system.push({ type: "text", text: factorySummary(state) })
17
+ const summary = state ? await runtime.factory.summary(state) : factorySummary(undefined)
18
+ event.system.push({ type: "text", text: summary })
18
19
  }
19
20
 
20
21
  const compaction = async (event: { agent: string; system: any[] }) => {
21
22
  if (!FACTORY_AGENTS.has(event.agent)) return
22
23
  const state = await runtime.factory.read().catch(() => undefined)
24
+ const summary = state ? await runtime.factory.summary(state) : factorySummary(undefined)
23
25
  event.system.push({
24
26
  type: "text",
25
- text: `Preserve this factory state verbatim at the top of your summary; the run continues from it:\n${factorySummary(state)}`,
27
+ text: `Preserve this factory state verbatim at the top of your summary; the run continues from it:\n${summary}`,
26
28
  })
27
29
  }
28
30
 
package/src/tools.ts CHANGED
@@ -3,12 +3,15 @@
3
3
  // action so per-agent rules can hide it. Refusals become model-facing tool errors.
4
4
  import {
5
5
  brainstormTools,
6
+ codeAuditTools,
6
7
  designTools,
7
8
  esTools,
8
9
  harvestTools,
9
10
  lspTools,
10
11
  pendingApprovals,
12
+ researchOptions,
11
13
  researchTools,
14
+ scoutTools,
12
15
  } from "@heretek-ai/es-core"
13
16
  import { Error as ToolError } from "@opencode/plugin/promise/tool"
14
17
  import type { Runtime } from "./runtime.ts"
@@ -18,7 +21,7 @@ export function registerTools(editor: { add(tool: any): void }, runtime: Runtime
18
21
  const research = researchTools({
19
22
  root: runtime.root,
20
23
  policy: () => runtime.policy(),
21
- ...(runtime.options.searchProvider ? { provider: runtime.options.searchProvider } : {}),
24
+ ...researchOptions(runtime.config),
22
25
  })
23
26
  const brainstorm = brainstormTools({
24
27
  root: runtime.root,
@@ -30,8 +33,19 @@ export function registerTools(editor: { add(tool: any): void }, runtime: Runtime
30
33
  stateDir: runtime.stateDir,
31
34
  })
32
35
  const design = designTools({ root: runtime.root })
36
+ const scout = scoutTools({
37
+ root: runtime.root,
38
+ whitelist: runtime.config.licenseWhitelist,
39
+ stateDir: runtime.stateDir,
40
+ halted: async () => {
41
+ const state = await runtime.factory.read().catch(() => undefined)
42
+ return state?.stage === "HALTED" ? (state.halt?.reason ?? "halted") : undefined
43
+ },
44
+ })
33
45
  for (const def of [
34
46
  ...esTools({ ...runtime, lsp: runtime.lsp }),
47
+ ...codeAuditTools({ ...runtime, lsp: runtime.lsp }),
48
+ ...scout,
35
49
  ...lsp,
36
50
  ...research,
37
51
  ...brainstorm,