@assuredloop/orca-bot-kit 0.22.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ an `obk` command and a set of skills. You are meant to manage the fleet by
10
10
  talking to Bot Father, the first bot, whose skills call the CLI for you — not by
11
11
  editing files.
12
12
 
13
- The design is in [`docs/prd.md`](docs/prd.md); the decisions with lasting
13
+ The design is in [`docs/prd/prd.md`](docs/prd/prd.md); the decisions with lasting
14
14
  consequences are in [`docs/adr/`](docs/adr/); what we know about Orca and the
15
15
  two harnesses is in [`docs/tech-notes.md`](docs/tech-notes.md).
16
16
 
@@ -364,6 +364,22 @@ conversation it was having, rather than starting a new one. Clear a session —
364
364
  keeps the old one, and gives the session its start prompt again, because that
365
365
  prompt is what tells one session's duty from another's.
366
366
 
367
+ Ask the kit to clear or compact a session, rather than typing into its tab:
368
+
369
+ ```sh
370
+ obk session clear --bots /path/to/my-bots --bot api-bot --session daily
371
+ obk session compact --bots /path/to/my-bots --bot api-bot --session daily
372
+ ```
373
+
374
+ Each waits up to 30 s for the session to be idle, and types nothing into one
375
+ that is busy or has a question on its screen. It types the harness's own
376
+ command, checks the screen before it presses return, and then confirms the
377
+ result: for a clear, the new conversation in the book; for a compact, the
378
+ compaction in the harness's own record, within 5 minutes, or it says it is not
379
+ confirmed yet. A harness that does not offer `/compact` is told so, and nothing
380
+ is entered. A cleared session reads its `AGENTS.md` again, so a clear also
381
+ brings it onto changed rules.
382
+
367
383
  A session paused before its first turn can have an id in the book and no
368
384
  conversation behind it: the harness has nothing on record for that id. There is
369
385
  nothing to resume, so it starts again with its duty, and the book moves the id
@@ -649,7 +665,7 @@ editor's runner, skips and says how to run it.
649
665
  JavaScript this branch changed against `main` — committed, still in the working
650
666
  tree, or not tracked yet — and reports the mutants the tests do not kill. It is
651
667
  an audit, not a step of every pull request
652
- ([PRD 7.3](docs/prd.md#73-decided-rules-inside-the-skills)): the everyday
668
+ ([PRD 7.3](docs/prd/prd.md#prd-21-73-decided-rules-inside-the-skills)): the everyday
653
669
  check is the implementer's own hand check, and the tool runs over the whole
654
670
  suite at a milestone or when the owner asks, or once, narrowed to the changed
655
671
  logic, for a change at the core of the kit. A surviving mutant is a change to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assuredloop/orca-bot-kit",
3
- "version": "0.22.0",
3
+ "version": "0.24.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -368,8 +368,9 @@ already up even when a skill change would, and a session's model, effort or
368
368
  approval only changes when it is started again (`obk restart`, on the user's
369
369
  word, and then read its tabs as above). Say which you changed, and what that
370
370
  means for the sessions running now. `obk health` names each running session
371
- still on the older rules or on other settings, and what brings it up to date: on
372
- Claude Code a `/clear` reads the rules again, and a start does on both harnesses.
371
+ still on the older rules or on other settings, and what brings it up to date: a
372
+ clear reads the rules again (`obk session clear`), and so does a start, on both
373
+ harnesses.
373
374
  Where everything needs restarting, remind
374
375
  them to do it from Bot Father's ops tab.
375
376
 
package/src/book.js CHANGED
@@ -152,8 +152,13 @@ function takeLock(home) {
152
152
  }
153
153
  }
154
154
 
155
- /** The lock on `file`, waited for `waitMs` at most: see `takeLock` for why it is this one. */
156
- function lockOn(file, waitMs) {
155
+ /**
156
+ * The lock on `file`, waited for `waitMs` at most: see `takeLock` for why it is
157
+ * this one. `how` is SQLite's: `IMMEDIATE`, one holder at a time; `SHARED`,
158
+ * any number at once, none while an `EXCLUSIVE` is held or waited for; and
159
+ * `EXCLUSIVE`, alone, once every `SHARED` holder has let go.
160
+ */
161
+ function lockOn(file, waitMs, how = 'IMMEDIATE') {
157
162
  mkdirSync(path.dirname(file), { recursive: true });
158
163
 
159
164
  const db = new DatabaseSync(file);
@@ -161,7 +166,13 @@ function lockOn(file, waitMs) {
161
166
  // A writer that arrives while another is working waits for it rather than
162
167
  // failing at once, and gives up saying so rather than waiting for ever.
163
168
  db.exec(`PRAGMA busy_timeout = ${waitMs}`);
164
- db.exec('BEGIN IMMEDIATE');
169
+ if (how === 'SHARED') {
170
+ // A reading transaction holds its shared lock from its first read.
171
+ db.exec('BEGIN');
172
+ db.prepare('SELECT count(*) FROM sqlite_master').get();
173
+ } else {
174
+ db.exec(`BEGIN ${how}`);
175
+ }
165
176
  } catch (error) {
166
177
  db.close();
167
178
  throw error;
@@ -233,6 +244,86 @@ export function takeMailboxTurn(home, session) {
233
244
  }
234
245
  }
235
246
 
247
+ /**
248
+ * A session's turn to have its Codex thread named (#480), beside its mailbox
249
+ * turn: `{ release }`, or undefined at once when another naming holds it, so
250
+ * that two turn ends close together type the name once.
251
+ */
252
+ export const takeNameTurn = (home, session) => turnOn(home, session, 'name', 0);
253
+
254
+ /**
255
+ * A session's typing turn (#480, the architect's ruling), for what the kit
256
+ * types into the session's tab one key at a time: no line of the kit's may
257
+ * land in between and send what is there with its own return. Two locks
258
+ * beside its mailbox turn: `typing`, the gate, held for the whole typing, and
259
+ * `lines`, taken alone once the lines already on their way have gone in.
260
+ * Returns `{ release }`, or undefined when it did not come within `waitMs`,
261
+ * or the lines in flight did not finish within LINES_WAIT_MS.
262
+ */
263
+ export function takeTypingTurn(home, session, waitMs) {
264
+ const gate = turnOn(home, session, 'typing', waitMs);
265
+ if (gate === undefined) return undefined;
266
+ let lines;
267
+ try {
268
+ lines = turnOn(home, session, 'lines', LINES_WAIT_MS, 'EXCLUSIVE');
269
+ } finally {
270
+ if (lines === undefined) gate.release();
271
+ }
272
+ if (lines === undefined) return undefined;
273
+ return {
274
+ release() {
275
+ try {
276
+ lines.release();
277
+ } finally {
278
+ gate.release();
279
+ }
280
+ },
281
+ };
282
+ }
283
+
284
+ /**
285
+ * A session's turn for one whole line, sent with its return in one go, as the
286
+ * mail nudge is: through the typing turn's gate, waited for `waitMs` at most,
287
+ * and then held beside any other line, so whole lines never wait on each
288
+ * other, only on typing one key at a time. Returns `{ release }`, or undefined
289
+ * when the gate did not come within `waitMs`. Every whole line the kit types
290
+ * takes it: the nudge, the list line, the skills reload and the grooming line (#482).
291
+ */
292
+ export function takeLineTurn(home, session, waitMs) {
293
+ const gate = turnOn(home, session, 'typing', waitMs);
294
+ if (gate === undefined) return undefined;
295
+ try {
296
+ return turnOn(home, session, 'lines', 0, 'SHARED');
297
+ } finally {
298
+ gate.release();
299
+ }
300
+ }
301
+
302
+ /**
303
+ * How long a kit path waits for a session's typing turn before it types
304
+ * nothing (#480, #482): a naming holds it for seconds.
305
+ */
306
+ export const TYPING_WAIT_MS = 5000;
307
+
308
+ /** What a kit path reports when the session's typing turn did not come within TYPING_WAIT_MS. */
309
+ export const TYPING_HELD = `the kit is typing into it, and it was still at it after ${TYPING_WAIT_MS / 1000} s, so nothing was typed`;
310
+
311
+ /**
312
+ * How long a typing turn waits for the lines already on their way: a nudge
313
+ * holds its line's turn while Orca waits up to 5 s to see it start a turn.
314
+ */
315
+ const LINES_WAIT_MS = 10_000;
316
+
317
+ /** A turn of the kit's own for one session, on a file beside its mailbox turn's. */
318
+ function turnOn(home, session, kind, waitMs, how) {
319
+ try {
320
+ return lockOn(mailboxLockFile(home, session).replace(/\.mailbox\.lock$/, `.${kind}.lock`), waitMs, how);
321
+ } catch (error) {
322
+ if (error.errcode === SQLITE_BUSY) return undefined;
323
+ throw error;
324
+ }
325
+ }
326
+
236
327
  /**
237
328
  * How long a step, a check or an `up` waits for a session's turn. Longer than a
238
329
  * step can hold it — three Orca calls of twenty seconds each at most — and short