@assuredloop/orca-bot-kit 0.26.0 → 0.26.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 +10 -4
- package/package.json +1 -1
- package/skills/obk-bot-building/SKILL.md +5 -2
- package/src/bot.js +2 -2
- package/src/cli.js +13 -8
- package/src/commands.js +16 -6
- package/src/health.js +1 -1
- package/src/launch.js +1 -1
- package/src/permissions.js +22 -6
- package/src/temp.js +1 -1
package/README.md
CHANGED
|
@@ -141,15 +141,19 @@ words, `dangerously-skip`
|
|
|
141
141
|
([ADR 0015](docs/adr/0015-three-approval-levels-auto-by-default.md)). Only
|
|
142
142
|
`obk permission approval` changes it, after your yes: `session add` and
|
|
143
143
|
`session change` take no approval, and refuse an extra argument that sets one
|
|
144
|
-
([ADR
|
|
144
|
+
([ADR 0041](docs/adr/0041-permission-changes-and-risky-operations-have-commands-of-their-own.md)).
|
|
145
145
|
|
|
146
146
|
Every bot gets the kit's default permission rules, written by the kit with
|
|
147
147
|
nobody asked: its own commands for this bots folder, reading a long message,
|
|
148
148
|
`git add` and `git commit`, and Orca's `orca orchestration check --run`
|
|
149
149
|
([ADR 0036](docs/adr/0036-the-kit-writes-every-bots-default-permission-rules.md)).
|
|
150
|
-
`obk permission`, `obk retire`, `obk pause`
|
|
151
|
-
and
|
|
152
|
-
|
|
150
|
+
`obk permission`, `obk retire`, `obk pause`, `obk restart`, `obk bot create`
|
|
151
|
+
and `obk init` are not in the set, and keep your yes
|
|
152
|
+
([ADR 0041](docs/adr/0041-permission-changes-and-risky-operations-have-commands-of-their-own.md)).
|
|
153
|
+
A rule beyond the set is written by `obk permission allow`, after your yes to
|
|
154
|
+
the exact rule. A bot that still holds the `obk restart` or `obk bot create`
|
|
155
|
+
rule, which kit 0.26.0 gave every bot, keeps it: `obk rules build` and `obk up`
|
|
156
|
+
name it, with the `obk permission disallow` command that takes it back.
|
|
153
157
|
|
|
154
158
|
Neither command touches Orca. `obk up` is what makes it all real there: an Orca
|
|
155
159
|
project per bot, a tab per session, each started at the bot home with its own
|
|
@@ -676,6 +680,8 @@ except the mailbox Runs Orca gives no way to delete, which the run names; it
|
|
|
676
680
|
never closes a tab it did not open. The trust its harnesses write into your
|
|
677
681
|
`~/.codex/config.toml` and `~/.claude.json` for the run's own throwaway folders
|
|
678
682
|
is taken out again after the run, and named; nothing else in them is changed.
|
|
683
|
+
An Orca project the run leaves in one of its own throwaway folders is named,
|
|
684
|
+
with its setup id, and fails the run; the command removes none itself.
|
|
679
685
|
|
|
680
686
|
They run on the machine the command is typed on, so the command will not start
|
|
681
687
|
them by itself. On its own it says what it is about to drive — whose machine,
|
package/package.json
CHANGED
|
@@ -213,8 +213,11 @@ its commits. The kit writes this default set itself, with nobody asked:
|
|
|
213
213
|
does not have yet to its `bot.yaml` and its settings, or for Codex its
|
|
214
214
|
`.codex/rules/obk.rules`, and say which rules they wrote. The set leaves out
|
|
215
215
|
the commands that keep the user's yes: `obk permission`, `obk retire`, `obk
|
|
216
|
-
pause`
|
|
217
|
-
|
|
216
|
+
pause`, `obk restart`, `obk bot create` and `obk init`. A bot that still holds
|
|
217
|
+
the `obk restart` or `obk bot create` rule from kit 0.26.0 keeps it, and `obk
|
|
218
|
+
rules build` and `obk up` name it with the `obk permission disallow` command
|
|
219
|
+
that takes it back; run that command only after the user's yes. Never write a
|
|
220
|
+
permission rule into a settings or rules file by hand.
|
|
218
221
|
|
|
219
222
|
The charter decides what else the bot needs. What it lets the bot do without
|
|
220
223
|
asking, such as merging pull requests, auto mode or Codex's sandbox can still
|
package/src/bot.js
CHANGED
|
@@ -162,7 +162,7 @@ export function readBot(home, name = path.basename(home)) {
|
|
|
162
162
|
// The roles its temporary sessions can be made in, judged by tempRoles.
|
|
163
163
|
...(bot.temp_roles === undefined ? {} : { temp_roles: bot.temp_roles }),
|
|
164
164
|
// The widest approval its temporary sessions may be made at, the user's yes
|
|
165
|
-
// kept by `permission approval --temps` (ADR
|
|
165
|
+
// kept by `permission approval --temps` (ADR 0041).
|
|
166
166
|
...(bot.temp_approval === undefined ? {} : { temp_approval: bot.temp_approval }),
|
|
167
167
|
// A paused bot is one `obk up` leaves closed; its sessions keep their book.
|
|
168
168
|
...(bot.paused === true ? { paused: true } : {}),
|
|
@@ -362,7 +362,7 @@ export function changeSession(bots, bot, name, settings) {
|
|
|
362
362
|
|
|
363
363
|
/**
|
|
364
364
|
* Record the widest approval the bot's temporary sessions may be made at,
|
|
365
|
-
* `level`, as `temp_approval` (ADR
|
|
365
|
+
* `level`, as `temp_approval` (ADR 0041). Returns { bot, home, level }.
|
|
366
366
|
*/
|
|
367
367
|
export function setTempApproval(bots, bot, level) {
|
|
368
368
|
editBot(bots, bot, `let ${bot}'s temporary sessions be made at ${level}`, (doc) => doc.set('temp_approval', level), (was) => ({ ...was, temp_approval: level }));
|
package/src/cli.js
CHANGED
|
@@ -909,7 +909,7 @@ const commands = {
|
|
|
909
909
|
throw new Error(`bot change does not change a bot's harness: its sessions' conversations belong to the harness they ran on. To move to ${values.harness}, give it a session on ${values.harness} with obk session add, or retire the bot with obk retire and create a new one.`);
|
|
910
910
|
}
|
|
911
911
|
// Permission changes have commands of their own, which keep the user's yes
|
|
912
|
-
// (ADR
|
|
912
|
+
// (ADR 0041): the old spelling refuses, and changes nothing.
|
|
913
913
|
const moved = [['allow', 'permission allow'], ['disallow', 'permission disallow']].filter(([flag]) => values[flag] !== undefined);
|
|
914
914
|
if (moved.length > 0) {
|
|
915
915
|
throw new Error(`bot change no longer takes ${moved.map(([flag]) => `--${flag}`).join(' or ')}: permission rules change through ${moved.map(([, now]) => `${shellWord(ownCli())} ${now} --bots ${shellWord(bots)} --bot ${values.bot} --rule <rule>`).join(' and ')}, after the user's yes. Nothing was changed.`);
|
|
@@ -1116,7 +1116,7 @@ const commands = {
|
|
|
1116
1116
|
...(values.effort === undefined ? {} : { effort: values.effort }),
|
|
1117
1117
|
...(values['extra-arg'] === undefined ? {} : { extra_args: values['extra-arg'] }),
|
|
1118
1118
|
};
|
|
1119
|
-
// A run's launch may not widen its approval (ADR
|
|
1119
|
+
// A run's launch may not widen its approval (ADR 0041).
|
|
1120
1120
|
if (values['run-on'] === 'codex') refuseApprovalArgs('codex', values['extra-arg']);
|
|
1121
1121
|
const groom = grooming(bots, { at: values.at, ask, run });
|
|
1122
1122
|
return { answer: { bots, groom }, lines: groomLines(groom, bots) };
|
|
@@ -1407,7 +1407,7 @@ function workDirFound(values, bot, home, session) {
|
|
|
1407
1407
|
|
|
1408
1408
|
/**
|
|
1409
1409
|
* Refuse what would change a session's approval through `session add` or
|
|
1410
|
-
* `session change` (ADR
|
|
1410
|
+
* `session change` (ADR 0041): `--approval`, and an extra argument that sets
|
|
1411
1411
|
* it on the session's harness. Only `permission approval` changes it.
|
|
1412
1412
|
*/
|
|
1413
1413
|
function refuseApproval(bots, values, command) {
|
|
@@ -1787,17 +1787,22 @@ function charterRulesLines(bots, bot, rules) {
|
|
|
1787
1787
|
|
|
1788
1788
|
/**
|
|
1789
1789
|
* What became of each bot's permission rules: each of the kit's default rules
|
|
1790
|
-
* the run added to the bot, word for word (ADR 0036),
|
|
1791
|
-
*
|
|
1790
|
+
* the run added to the bot, word for word (ADR 0036), each rule of a command no
|
|
1791
|
+
* longer in the set that the bot still holds, with the command that takes it
|
|
1792
|
+
* back (ADR 0041), and the file the allowed ones were written into. Nothing for
|
|
1793
|
+
* a bot with nothing added, held or written.
|
|
1792
1794
|
*/
|
|
1793
1795
|
function permissionsLines(permissions, bots) {
|
|
1794
1796
|
// A bot on both harnesses has an entry for each file, with the same
|
|
1795
|
-
// defaults: they are listed once, with the first.
|
|
1797
|
+
// defaults and held rules: they are listed once, with the first.
|
|
1796
1798
|
const listed = new Set();
|
|
1799
|
+
const disallow = (bot, rule) => `${shellWord(ownCli())} permission disallow --bots ${shellWord(bots)} --bot ${shellWord(bot)} --rule ${shellWord(rule)}`;
|
|
1797
1800
|
return permissions.flatMap((entry) => [
|
|
1798
1801
|
...(entry.trouble === undefined ? [] : [`${'refused'.padEnd(9)} ${entry.trouble}`]),
|
|
1799
|
-
...(
|
|
1800
|
-
|
|
1802
|
+
...(listed.has(entry.bot) || !listed.add(entry.bot) ? [] : [
|
|
1803
|
+
...(entry.defaults ?? []).map((rule) => `${'allowed'.padEnd(9)} ${entry.bot} ${rule}`),
|
|
1804
|
+
...(entry.held ?? []).map((rule) => `${'holds'.padEnd(9)} ${entry.bot} ${rule}, which is no longer one of the kit's defaults. To take it back: ${disallow(entry.bot, rule)}`),
|
|
1805
|
+
]),
|
|
1801
1806
|
...(entry.written.length === 0
|
|
1802
1807
|
? []
|
|
1803
1808
|
: [`${'wrote'.padEnd(9)} ${path.relative(bots, entry.file)} ${entry.written.length} permission rule${entry.written.length === 1 ? '' : 's'} from its allow list`]),
|
package/src/commands.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The kit's commands, each with the flags it cannot do without, which `obk`
|
|
2
2
|
// reads to parse a command line; and the two lists every command is on one of:
|
|
3
3
|
// those every bot is allowed by default (#527, ADR 0036), and those kept back
|
|
4
|
-
// for the user's yes (ADR
|
|
4
|
+
// for the user's yes (ADR 0041). A new command is on neither until someone
|
|
5
5
|
// puts it on one, and a test holds every command to that choice.
|
|
6
6
|
|
|
7
7
|
/** The commands, and the flags each one cannot do without. */
|
|
@@ -56,8 +56,8 @@ export const COMMANDS = {
|
|
|
56
56
|
* runs whatever its arguments say never is.
|
|
57
57
|
*/
|
|
58
58
|
export const DEFAULT_COMMANDS = [
|
|
59
|
-
'up', '
|
|
60
|
-
'bot
|
|
59
|
+
'up', 'unpause', 'health', 'groom', 'roster', 'usage',
|
|
60
|
+
'bot change', 'rules build',
|
|
61
61
|
'skills add', 'skills remove', 'skills build', 'skills fetch', 'skills update', 'source add',
|
|
62
62
|
'session add', 'session change', 'session clear', 'session compact',
|
|
63
63
|
'message to', 'message send', 'message check',
|
|
@@ -68,7 +68,17 @@ export const DEFAULT_COMMANDS = [
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* The commands kept out of the default permission set, which keep the user's
|
|
71
|
-
* yes (ADR
|
|
72
|
-
* session, or make the
|
|
71
|
+
* yes (ADR 0041): the ones that change permissions, close a long-lived
|
|
72
|
+
* session, make the bots folder, or make a bot, which the owner leaves to Bot
|
|
73
|
+
* Father (#548).
|
|
73
74
|
*/
|
|
74
|
-
export const KEPT_BACK = [
|
|
75
|
+
export const KEPT_BACK = [
|
|
76
|
+
'init', 'retire', 'pause', 'restart', 'bot create',
|
|
77
|
+
'permission allow', 'permission disallow', 'permission approval',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The commands kit 0.26.0 gave every bot by default and that are kept back now
|
|
82
|
+
* (#548). A bot may still hold their rules, which the kit names and leaves.
|
|
83
|
+
*/
|
|
84
|
+
export const NO_LONGER_DEFAULT = ['restart', 'bot create'];
|
package/src/health.js
CHANGED
|
@@ -213,7 +213,7 @@ function sessionSettings(home, bot) {
|
|
|
213
213
|
/** What is wrong with the roles the bot's temporary sessions are made in (#465). */
|
|
214
214
|
function rolesTrouble(home, bot) {
|
|
215
215
|
const where = path.join(home, 'bot.yaml');
|
|
216
|
-
// The widest approval they may be made at, the user's yes (ADR
|
|
216
|
+
// The widest approval they may be made at, the user's yes (ADR 0041).
|
|
217
217
|
const level = bot.temp_approval;
|
|
218
218
|
const approval = level === undefined || APPROVALS.includes(level) ? [] : [{
|
|
219
219
|
where,
|
package/src/launch.js
CHANGED
|
@@ -45,7 +45,7 @@ export const approvalRank = (level) => ['ask', 'auto', 'dangerously-skip'].index
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* The harness flags that set approval or permissions, which only
|
|
48
|
-
* `obk permission approval` may change, not an extra argument (ADR
|
|
48
|
+
* `obk permission approval` may change, not an extra argument (ADR 0041).
|
|
49
49
|
* Codex's `-a` and `-s` take their value glued on too.
|
|
50
50
|
*/
|
|
51
51
|
const APPROVAL_FLAGS = {
|
package/src/permissions.js
CHANGED
|
@@ -24,7 +24,7 @@ import { accessSync, constants, existsSync, mkdirSync, readdirSync, readFileSync
|
|
|
24
24
|
import path from 'node:path';
|
|
25
25
|
|
|
26
26
|
import { allowRules, leadsOutside } from './bot.js';
|
|
27
|
-
import { DEFAULT_COMMANDS } from './commands.js';
|
|
27
|
+
import { DEFAULT_COMMANDS, NO_LONGER_DEFAULT } from './commands.js';
|
|
28
28
|
import { readSettings } from './hooks.js';
|
|
29
29
|
import { harnessOf, ownCli, shellWord } from './launch.js';
|
|
30
30
|
|
|
@@ -37,7 +37,7 @@ const CODEX_FILE = `${CODEX_RULES}/obk.rules`;
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* The rules every bot is given (ADR 0036): each of the kit's default commands,
|
|
40
|
-
* and none kept back for the user's yes (ADR
|
|
40
|
+
* and none kept back for the user's yes (ADR 0041), narrowed to this bots folder and
|
|
41
41
|
* spelled as the kit prints them, which is what a bot runs; reading a long
|
|
42
42
|
* message; committing; and the check Orca's mail notice tells a session to
|
|
43
43
|
* run. `//` is Claude's form for an absolute path.
|
|
@@ -54,6 +54,18 @@ export function defaultRules(bots, cli = ownCli()) {
|
|
|
54
54
|
];
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* The rules of the commands that were defaults once and are kept back now
|
|
59
|
+
* (#548), spelled as `defaultRules` spelled them, that the bot's `allow`
|
|
60
|
+
* holds. The kit cannot tell its own old entry from a user's yes to the same
|
|
61
|
+
* rule, so it names them and leaves them (ADR 0041).
|
|
62
|
+
*/
|
|
63
|
+
export function heldRules(bots, home, bot, cli = ownCli()) {
|
|
64
|
+
const allowed = allowOf(home, bot);
|
|
65
|
+
return NO_LONGER_DEFAULT.map((command) => `Bash(${shellWord(cli)} ${command} --bots ${shellWord(bots)}:*)`)
|
|
66
|
+
.filter((rule) => allowed.includes(rule));
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
/** Command wrappers: programs that run the command their arguments name. */
|
|
58
70
|
const WRAPPERS = new Set(['env', 'xargs', 'sudo', 'eval', 'exec', 'nohup', 'timeout', 'nice', 'command', 'time', 'watch']);
|
|
59
71
|
|
|
@@ -261,26 +273,30 @@ export function refuseNoCodexForm(bot, rules) {
|
|
|
261
273
|
* Add the default rules the bot is not allowed yet to its `allow` (ADR 0036),
|
|
262
274
|
* then write what it is allowed into the files of the harnesses it runs on:
|
|
263
275
|
* one entry per file, Claude's first, each with `defaults`, the rules this run
|
|
264
|
-
* added
|
|
276
|
+
* added, and `held`, the rules of commands no longer in the set that the bot
|
|
277
|
+
* still holds (#548). The harness files are written first and bot.yaml last, as
|
|
265
278
|
* `permission allow` writes them (#383). With `keepGoing`, a file the kit may
|
|
266
279
|
* not write is an entry with its `trouble` rather than a throw.
|
|
267
280
|
*/
|
|
268
281
|
export function writePermissions(bots, home, bot, { keepGoing = false } = {}) {
|
|
269
282
|
const writers = [[runsOnClaude, FILE], [runsOnCodex, CODEX_FILE]].filter(([runs]) => runs(bot));
|
|
270
283
|
let defaults = [];
|
|
284
|
+
// Read before anything is written, so a file's trouble still names them.
|
|
285
|
+
let held = [];
|
|
271
286
|
try {
|
|
287
|
+
held = heldRules(bots, home, bot);
|
|
272
288
|
defaults = writers.length === 0 ? [] : missingDefaults(bots, home, bot);
|
|
273
289
|
if (defaults.length > 0) {
|
|
274
290
|
const { allow } = allowRules(bots, bot.name, defaults, { write: false });
|
|
275
291
|
const entries = allowIn(bots, home, { ...bot, allow })();
|
|
276
292
|
allowRules(bots, bot.name, defaults);
|
|
277
|
-
return entries.map((entry) => ({ ...entry, defaults }));
|
|
293
|
+
return entries.map((entry) => ({ ...entry, defaults, held }));
|
|
278
294
|
}
|
|
279
295
|
return writers.map(([runs]) => (runs === runsOnClaude ? planClaude : planCodex)(bots, home, bot).write())
|
|
280
|
-
.map((entry) => ({ ...entry, defaults }));
|
|
296
|
+
.map((entry) => ({ ...entry, defaults, held }));
|
|
281
297
|
} catch (error) {
|
|
282
298
|
if (!keepGoing) throw error;
|
|
283
|
-
return writers.map(([, file]) => ({ bot: bot.name, file: path.join(home, file), written: [], defaults: [], trouble: error.message }));
|
|
299
|
+
return writers.map(([, file]) => ({ bot: bot.name, file: path.join(home, file), written: [], defaults: [], held, trouble: error.message }));
|
|
284
300
|
}
|
|
285
301
|
}
|
|
286
302
|
|
package/src/temp.js
CHANGED
|
@@ -178,7 +178,7 @@ function settingsFor(bot, maker, option, given) {
|
|
|
178
178
|
/**
|
|
179
179
|
* Refuse an approval wider than the widest of the maker's own and the bot's
|
|
180
180
|
* `temp_approval`, which only the user's yes through `permission approval
|
|
181
|
-
* --temps` widens (ADR
|
|
181
|
+
* --temps` widens (ADR 0041).
|
|
182
182
|
*/
|
|
183
183
|
function refuseWiderApproval(bots, caller, bot, maker, asked) {
|
|
184
184
|
if (asked === undefined) return;
|