@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 +18 -2
- package/package.json +1 -1
- package/skills/obk-bot-building/SKILL.md +3 -2
- package/src/book.js +94 -3
- package/src/clear.js +554 -0
- package/src/cli.js +61 -0
- package/src/groom.js +17 -10
- package/src/health.js +4 -7
- package/src/hooks.js +67 -31
- package/src/lines.js +7 -4
- package/src/message.js +15 -1
- package/src/name.js +138 -0
- package/src/orca.js +6 -4
- package/src/record.js +5 -5
- package/src/skills.js +10 -1
- package/src/temp.js +17 -10
- package/src/up.js +49 -26
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
|
@@ -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:
|
|
372
|
-
|
|
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
|
-
/**
|
|
156
|
-
|
|
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
|
-
|
|
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
|