@assuredloop/orca-bot-kit 0.8.0 → 0.9.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 CHANGED
@@ -529,7 +529,8 @@ name. Everything else goes through Orca's mailbox, which the kit does carry.
529
529
 
530
530
  That includes a Claude session still running under a name from before the
531
531
  token, `<bot>.<session>`: `message to` answers the mailbox road for it, never
532
- that name, until the session next starts a new conversation.
532
+ that name, until the kit's launch line next starts it (`obk restart`, or
533
+ `obk up` after its tab is gone), which gives it a token name of its own.
533
534
 
534
535
  A session's mailbox is made the first time the kit starts it, and written in
535
536
  the book beside its tab. It is an Orca Run rather than the session's tab,
@@ -598,7 +599,9 @@ cannot be read as one that passed. `-- --yes` is how you say you meant it. When
598
599
  Orca is not answering, the command says which Orca it asked and skips, rather
599
600
  than report a kit that is not broken. Name files after it to run only those;
600
601
  each must be one of the `*.test.js` files under `test/system/`, links followed,
601
- and a name that is not is refused before anything runs.
602
+ and a name that is not is refused before anything runs. This command is the
603
+ only way in: a system test loaded any other way, with `node --test` or an
604
+ editor's runner, skips and says how to run it.
602
605
 
603
606
  `npm run mutate` runs [StrykerJS](https://stryker-mutator.io) over the
604
607
  JavaScript this branch changed against `main` — committed, still in the working
package/SETUP.md CHANGED
@@ -37,7 +37,7 @@ node --version # 24.21.0 or newer
37
37
  git --version
38
38
  /Applications/Orca.app/Contents/Resources/bin/orca status --json
39
39
  claude --version # at least one of these
40
- codex --version
40
+ codex --version # 0.156.1 or newer
41
41
  ```
42
42
 
43
43
  The Orca line must come back with `ok: true` and `result.runtime.reachable:
@@ -55,6 +55,10 @@ first release measured here that loads it without an experimental warning.
55
55
  If neither harness is installed, say so and stop. If only one is, that is fine,
56
56
  and it is the one Bot Father will run on.
57
57
 
58
+ If Codex is older than 0.156.1, say so and ask them to update it before a bot
59
+ runs on Codex. The kit starts Codex with `--no-daemon`, and older Codex refuses
60
+ that flag.
61
+
58
62
  ## 2. Make `obk` runnable
59
63
 
60
64
  **First ask whether it already is:**
@@ -206,8 +210,9 @@ empty book and nothing says why.
206
210
  Codex's trust question applies to the **repository root**, which for a bot means
207
211
  the whole bots folder rather than the one bot. That is what they are agreeing to.
208
212
 
209
- Codex's `/new` question (0.156.1 on) is always answered with the current
210
- checkout, which is the bot home. The kit never makes a git worktree, so
213
+ Codex's `/new` question (seen on 0.156.1; 0.157.1 did not show it in the runs
214
+ seen so far) is always answered with the current checkout, which is the bot
215
+ home. The kit never makes a git worktree, so
211
216
  `2. New worktree` is never the answer.
212
217
 
213
218
  **Anything you do not recognise: type nothing.** Tell them what is on the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assuredloop/orca-bot-kit",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -260,9 +260,10 @@ table: a harness that has added an option has moved them. Codex's hooks
260
260
  question matters most. The kit's hook is how the book learns which
261
261
  conversation the session is running, and until it is answered the
262
262
  conversation has not started. Codex's trust applies to the repository root,
263
- which is the whole bots folder. Codex's `/new` asks where the new
264
- conversation runs; the answer is always the current checkout, the bot home,
265
- and never `2. New worktree`: the kit never makes a git worktree.
263
+ which is the whole bots folder. Codex's `/new` may ask where the new
264
+ conversation runs (0.156.1 did; 0.157.1 has not so far); the answer is
265
+ always the current checkout, the bot home, and never `2. New worktree`:
266
+ the kit never makes a git worktree.
266
267
 
267
268
  Where the kit says no session came up, the shell swallowed the launch line,
268
269
  usually while it was asking its own question. Answer the shell, then close
package/src/launch.js CHANGED
@@ -99,6 +99,48 @@ export const setsNetworkAccess = (session) =>
99
99
  export const reachesMail = (session, harness) =>
100
100
  harness !== 'codex' || !turnedOff(session);
101
101
 
102
+ /** Codex's flag for running without its shared background server (0.156.1 on). */
103
+ const NO_DAEMON = '--no-daemon';
104
+
105
+ // A string is shell text, so it is read as the shell will split it: `'--no-daemon'`
106
+ // is the flag, and `'/tmp/a --no-daemon b'` is one path that merely says it.
107
+ const setsNoDaemon = (session) => {
108
+ const extra = session.extra_args;
109
+ const words = Array.isArray(extra) ? extra.map(String) : set(extra) ? shellWords(String(extra)) : [];
110
+ return words.includes(NO_DAEMON);
111
+ };
112
+
113
+ /**
114
+ * The words a shell makes of `text`: split on blanks outside quotes, quotes
115
+ * removed, a backslash keeping the next character. Nothing is expanded.
116
+ */
117
+ function shellWords(text) {
118
+ const words = [];
119
+ let word;
120
+ let quote;
121
+ for (let at = 0; at < text.length; at += 1) {
122
+ const char = text[at];
123
+ if (quote === "'") {
124
+ if (char === "'") quote = undefined;
125
+ else word += char;
126
+ } else if (quote === '"') {
127
+ if (char === '"') quote = undefined;
128
+ else if (char === '\\' && '"\\$`'.includes(text[at + 1] ?? ' ')) word += text[(at += 1)];
129
+ else word += char;
130
+ } else if (/\s/.test(char)) {
131
+ if (word !== undefined) words.push(word);
132
+ word = undefined;
133
+ } else {
134
+ word ??= '';
135
+ if (char === "'" || char === '"') quote = char;
136
+ else if (char === '\\') word += text[(at += 1)] ?? '';
137
+ else word += char;
138
+ }
139
+ }
140
+ if (word !== undefined) words.push(word);
141
+ return words;
142
+ }
143
+
102
144
  const turnedOff = (session) =>
103
145
  extraWords(session.extra_args).some((word) => word.includes(`${NETWORK_ACCESS}=false`));
104
146
 
@@ -174,6 +216,11 @@ export function launchCommand(session, { harness, home, workDir, prompt, promptF
174
216
  // take every other flag exactly as a fresh session does.
175
217
  if (resume !== undefined && harness === 'codex') words.push('resume');
176
218
  words.push(...APPROVAL[harness][set(session.approval) ? session.approval : DEFAULT_APPROVAL]);
219
+ // Codex 0.157 shares one background server between sessions by default, and
220
+ // a resume through it was seen to fail; without it Codex runs as 0.156 did
221
+ // (tech notes, section 3, #330). Codex refuses the flag twice, so the user's
222
+ // own wins.
223
+ if (harness === 'codex' && !setsNoDaemon(session)) words.push(NO_DAEMON);
177
224
 
178
225
  if (harness === 'claude') {
179
226
  // The name is the address other Claude sessions write to, so it goes on
package/src/up.js CHANGED
@@ -377,13 +377,13 @@ function launchFor(bots, bot, session, harness, home, workDir, resume, held) {
377
377
  // through the tab's shell a character at a time.
378
378
  const promptFile = prompt === undefined || isShortPrompt(prompt) ? undefined : promptPath(bots, bot.name, session.name);
379
379
  // A new conversation is given a new name. A resume goes on under the one the
380
- // kit gave it, and under no `-n` at all when the book `held` none of the
381
- // kit's own: a resume keeps whatever name the conversation has, which is
382
- // proven, and whether `-n` renames it is not (#286). Such a session is
383
- // written to through its mailbox until it next starts fresh.
380
+ // kit gave it, and gets a new one when the book `held` none of the kit's own:
381
+ // `--resume <id> -n <name>` renames the conversation, proven live (#319), so
382
+ // a session still under the shared `<bot>.<session>` moves to an address of
383
+ // its own here.
384
384
  const address = harness !== 'claude' ? undefined
385
- : resume === undefined ? addressOf(bot.name, session.name)
386
- : isAddressOf(bot.name, session.name, held) ? held : undefined;
385
+ : resume !== undefined && isAddressOf(bot.name, session.name, held) ? held
386
+ : addressOf(bot.name, session.name);
387
387
  const line = launchCommand(session, {
388
388
  harness,
389
389
  home,