@assuredloop/orca-bot-kit 0.9.0 → 0.11.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 +8 -0
- package/SETUP.md +22 -16
- package/package.json +1 -1
- package/rules/limits.md +4 -0
- package/rules/mail.md +1 -2
- package/skills/obk-bot-building/SKILL.md +54 -13
- package/src/book.js +60 -4
- package/src/bot.js +40 -1
- package/src/cli.js +161 -34
- package/src/health.js +2 -0
- package/src/hooks.js +2 -2
- package/src/message.js +83 -11
- package/src/orca-reload.applescript +45 -0
- package/src/orca-runtime.cjs +28 -0
- package/src/orca.js +123 -26
- package/src/permissions.js +485 -0
- package/src/retire.js +12 -25
- package/src/rules.js +16 -1
- package/src/up.js +59 -18
- package/src/orca-window.cjs +0 -21
package/src/up.js
CHANGED
|
@@ -8,10 +8,11 @@ import { existsSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs';
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { setTimeout as pause } from 'node:timers/promises';
|
|
10
10
|
|
|
11
|
-
import { forgetClaimed, forgetSession, readBook, sessionIdsIn, tabIdsIn, updateBook, withUnclaimed } from './book.js';
|
|
11
|
+
import { forgetClaimed, forgetSession, MAILBOX_WAIT_MS, readBook, sessionIdsIn, tabIdsIn, takeMailboxTurn, updateBook, withUnclaimed } from './book.js';
|
|
12
12
|
import { botDir, botNames, displayName, readBot } from './bot.js';
|
|
13
13
|
import { conversationsIn, hasConversation, heldAsUserTurn, transcriptsIn } from './conversations.js';
|
|
14
14
|
import { installHook } from './hooks.js';
|
|
15
|
+
import { writePermissions } from './permissions.js';
|
|
15
16
|
import { addressOf, harnessOf, isAddressOf, isShortPrompt, launchCommand, mailboxStep, reachesMail, sessionTrouble, startPrompt, workDirOf } from './launch.js';
|
|
16
17
|
import { asFolderProject, coordinatorOf, findProject, harnessInTab, makeMailbox, makeProject, openTab, QUESTION_ON_SCREEN, retitleTab, tabs, TERMINAL_ENV, TIMED_OUT, tellWindow, typeIntoTab, useMailbox } from './orca.js';
|
|
17
18
|
import { TAB_ENV } from './record.js';
|
|
@@ -58,7 +59,7 @@ export async function bringUp(bots, { bot: onlyBot, session: onlySession } = {})
|
|
|
58
59
|
return false;
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
const { running, rules, skills } = prepareBots(bots, names, onlySession);
|
|
62
|
+
const { running, rules, skills, permissions } = prepareBots(bots, names, onlySession);
|
|
62
63
|
|
|
63
64
|
const report = [];
|
|
64
65
|
const projects = [];
|
|
@@ -83,6 +84,7 @@ export async function bringUp(bots, { bot: onlyBot, session: onlySession } = {})
|
|
|
83
84
|
trouble: `${entry.trouble} Its sessions were not started: a bot comes up with its rules or not at all.`,
|
|
84
85
|
})),
|
|
85
86
|
skills,
|
|
87
|
+
permissions,
|
|
86
88
|
paused,
|
|
87
89
|
projects,
|
|
88
90
|
};
|
|
@@ -90,7 +92,7 @@ export async function bringUp(bots, { bot: onlyBot, session: onlySession } = {})
|
|
|
90
92
|
|
|
91
93
|
/**
|
|
92
94
|
* Everything the bots need in place before a session starts, and which of them
|
|
93
|
-
* can be started at all: `{ running, rules, skills }`.
|
|
95
|
+
* can be started at all: `{ running, rules, skills, permissions }`.
|
|
94
96
|
*
|
|
95
97
|
* Separate from the tabs because the order matters twice over. A session reads
|
|
96
98
|
* its rules, its skills and its hooks as it comes up, so all three have to be
|
|
@@ -150,7 +152,12 @@ export function prepareBots(bots, names, onlySession) {
|
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
// And the permission rules the user allowed, beside the hook and for the same
|
|
156
|
+
// reason: a session that comes up before its rules are there is asked about
|
|
157
|
+
// what the user already said yes to (#344).
|
|
158
|
+
const permissions = running.flatMap(({ bot, home }) => writePermissions(bots, home, bot));
|
|
159
|
+
|
|
160
|
+
return { running, rules, skills, permissions };
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
/**
|
|
@@ -289,20 +296,31 @@ async function bringUpSession(bots, home, live, session, bot, title) {
|
|
|
289
296
|
// does not (#272).
|
|
290
297
|
const rules = rulesStamp(home);
|
|
291
298
|
let held;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
// Not while the session's mailbox step or a check in its old tab is at work:
|
|
300
|
+
// the book moving under one leaves the mailbox bound to, or read in, a tab the
|
|
301
|
+
// book no longer names (#321). Waited for a bounded time and then written all
|
|
302
|
+
// the same, because this tab is open and its harness has to start; the step
|
|
303
|
+
// in it then waits its own turn and says what it left. Let go before the line
|
|
304
|
+
// is typed, since that step takes it.
|
|
305
|
+
const turn = takeMailboxTurn(home, session.name);
|
|
306
|
+
try {
|
|
307
|
+
await updateBook(home, (current) => {
|
|
308
|
+
// What the harness has in this folder that nobody claims goes on the record,
|
|
309
|
+
// for a person or Bot Father to settle — added to whatever was already noted,
|
|
310
|
+
// because this run's scan cannot see what an earlier one found. The kit never
|
|
311
|
+
// settles it itself.
|
|
312
|
+
let entry = { ...current.sessions[session.name], tab: made.tabId, launched, rules };
|
|
313
|
+
if (rules === undefined) delete entry.rules;
|
|
314
|
+
held = entry.session;
|
|
315
|
+
// Before the line is typed, so the hook finds no id here and takes the one
|
|
316
|
+
// it reports for a start rather than a clear, which would tell the duty twice.
|
|
317
|
+
if (which.noConversation !== undefined && held === which.noConversation) entry = forgetSession(entry, 'no conversation');
|
|
318
|
+
current.sessions[session.name] = withUnclaimed(entry, which.unclaimed ?? []);
|
|
319
|
+
forgetClaimed(current);
|
|
320
|
+
});
|
|
321
|
+
} finally {
|
|
322
|
+
turn?.release();
|
|
323
|
+
}
|
|
306
324
|
|
|
307
325
|
// The old tab's hook can name another conversation while the tab is being
|
|
308
326
|
// opened. A fresh start chosen before that is chosen again from the id the
|
|
@@ -517,6 +535,20 @@ export async function ownMailbox(bots, botName, sessionName) {
|
|
|
517
535
|
throw new Error(`session mailbox binds ${who}'s mailbox to the Orca tab it runs in, and this is not one: ${TERMINAL_ENV} is not set. The kit runs it in the session's own tab when it starts the session.`);
|
|
518
536
|
}
|
|
519
537
|
|
|
538
|
+
// One step, check or tab write for this session at a time, and the book read
|
|
539
|
+
// once the turn is had (#321). A step that does not get it leaves everything
|
|
540
|
+
// as it is and says so, and the launch line goes on to the harness.
|
|
541
|
+
const turn = takeMailboxTurn(home, sessionName);
|
|
542
|
+
if (turn === undefined) throw noTurn(home, who, sessionName);
|
|
543
|
+
try {
|
|
544
|
+
return await mailboxInTurn(home, botName, sessionName, who);
|
|
545
|
+
} finally {
|
|
546
|
+
turn.release();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** What `ownMailbox` does once it has the session's turn. */
|
|
551
|
+
async function mailboxInTurn(home, botName, sessionName, who) {
|
|
520
552
|
// Only in the tab the book names for the session. Two runs of `up` at once
|
|
521
553
|
// can leave a session two tabs, and the book one of them: a mailbox made or
|
|
522
554
|
// bound in the other would be bound to a tab no message is meant for.
|
|
@@ -593,6 +625,15 @@ export async function ownMailbox(bots, botName, sessionName) {
|
|
|
593
625
|
*/
|
|
594
626
|
const STEP_WAIT = { timeoutMs: 20_000 };
|
|
595
627
|
|
|
628
|
+
/** A step that waited its turn out: nothing was made or bound, and where the mailbox was left. */
|
|
629
|
+
function noTurn(home, who, sessionName) {
|
|
630
|
+
const held = readBook(home).sessions[sessionName]?.mailbox;
|
|
631
|
+
const left = typeof held === 'string'
|
|
632
|
+
? `${who}'s mailbox ${held} is left bound to ${boundTo(held)}; an \`obk message check\` in ${who}'s own tab binds it there.`
|
|
633
|
+
: `None is written down for ${who}; it gets one the next time the kit starts it.`;
|
|
634
|
+
return new Error(`waited ${MAILBOX_WAIT_MS / 1000} seconds for ${who}'s mailbox while another obk was making, binding or reading it, and it did not finish. Nothing was made or bound in this tab. ${left}`);
|
|
635
|
+
}
|
|
636
|
+
|
|
596
637
|
/** Whether a session that can have a mailbox has none in its book. */
|
|
597
638
|
const lacksMailbox = (home, session, harness) => reachesMail(session, harness)
|
|
598
639
|
&& typeof readBook(home).sessions[session.name]?.mailbox !== 'string';
|
package/src/orca-window.cjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Run by Orca's own binary as plain Node, the way Orca's `bin/orca` runs its
|
|
2
|
-
// CLI: `ELECTRON_RUN_AS_NODE=1 <Orca> orca-window.cjs <client> <projectId> <wait>`.
|
|
3
|
-
//
|
|
4
|
-
// It asks Orca's runtime, through Orca's own client, for `project.update` with
|
|
5
|
-
// no changes on one project. That is the call that makes Orca's window read its
|
|
6
|
-
// projects again, and Orca's CLI does not offer it (#224, ADR 0023). It exits 0
|
|
7
|
-
// only when the runtime took the call; anything else is a non-zero exit, and
|
|
8
|
-
// the kit reads every non-zero exit the same way.
|
|
9
|
-
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
const [client, projectId, wait] = process.argv.slice(2);
|
|
13
|
-
|
|
14
|
-
process.exitCode = 1;
|
|
15
|
-
const { RuntimeClient } = require(client);
|
|
16
|
-
new RuntimeClient(undefined, Number(wait))
|
|
17
|
-
.call('project.update', { projectId, updates: {} })
|
|
18
|
-
.then((answer) => {
|
|
19
|
-
if (answer?.ok === true) process.exitCode = 0;
|
|
20
|
-
}, () => {})
|
|
21
|
-
.finally(() => process.exit());
|