@bridge4dev/runner 0.66.0 → 0.68.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.
@@ -19,7 +19,7 @@ import { applySession, gitBranches, gitCommit, gitDiff, gitLog, gitPush, gitRefs
19
19
  import { fsView } from './fsview.js';
20
20
  import { publishFile } from './file-publish.js';
21
21
  import { agentAuthStatuses, AuthRelay, clearAgentAuthFailure, noteAgentAuthFailure, } from './auth-relay.js';
22
- import { isAccountCommand, runAccountCommand } from './account-commands.js';
22
+ import { isAccountCommand, runAccountCommand, } from './account-commands.js';
23
23
  import { isSupervisedProcess, selfUpdate } from './self-update.js';
24
24
  import { installAgent } from './agent-install.js';
25
25
  import { autoUpdateRefusal, claimAgentAutoUpdate } from './agent-auto-update.js';
@@ -3337,7 +3337,11 @@ export class Supervisor {
3337
3337
  activeMs: Supervisor.spentMs(running),
3338
3338
  errorMessage: event.errorMessage ?? 'Agent turn failed',
3339
3339
  });
3340
+ return;
3340
3341
  }
3342
+ // The turn is over – so an account switch that arrived while it was running
3343
+ // can be carried out now, without costing that turn (#422 S4.1).
3344
+ this.applyPendingAccountSwitch(running);
3341
3345
  }
3342
3346
  /**
3343
3347
  * Hold a turn that produced nothing, in case it was never a turn (#300).
@@ -3578,6 +3582,93 @@ export class Supervisor {
3578
3582
  }
3579
3583
  running.session.stop('session_parked');
3580
3584
  }
3585
+ /**
3586
+ * Move the sessions running right now onto the account just made active (#422 S4.1).
3587
+ *
3588
+ * A process reads its account ONCE, at its start: `CLAUDE_CONFIG_DIR` for
3589
+ * Claude, the link `codex-home/auth.json` resolves to for Codex. Neither can
3590
+ * be changed under a live process, so «apply to the open sessions» is parking
3591
+ * them – the conversation is kept (`--resume` / `thread/resume`) and the next
3592
+ * message starts a process on the account the machine is now set to.
3593
+ *
3594
+ * A session in the middle of a turn is not killed for this: it is marked and
3595
+ * parked at the end of its turn (`applyPendingAccountSwitch`). Which is why
3596
+ * the answer counts the two separately – the window says so in words.
3597
+ */
3598
+ applyAccountToSessions(agent, accountId, label) {
3599
+ let switched = 0;
3600
+ let pending = 0;
3601
+ for (const running of this.sessions.values()) {
3602
+ if (agentByDbValue(running.descriptor.agent)?.wireKey !== agent)
3603
+ continue;
3604
+ const session = running.session;
3605
+ // Parked already: whatever starts it next reads the active account then.
3606
+ if (!session)
3607
+ continue;
3608
+ // Already there, or an adapter that does not name an account at all – a
3609
+ // switch has nothing to do to it, and a stale mark must not outlive it.
3610
+ if (!session.accountId || session.accountId === accountId) {
3611
+ delete running.accountSwitchPending;
3612
+ continue;
3613
+ }
3614
+ if (this.isParkable(running)) {
3615
+ delete running.accountSwitchPending;
3616
+ this.parkForAccount(running, label);
3617
+ switched += 1;
3618
+ }
3619
+ else {
3620
+ running.accountSwitchPending = { id: accountId, ...(label ? { label } : {}) };
3621
+ pending += 1;
3622
+ }
3623
+ }
3624
+ if (switched || pending) {
3625
+ log.info('supervisor: the account switch reached the open sessions', {
3626
+ agent,
3627
+ account: accountId,
3628
+ switched,
3629
+ pending,
3630
+ });
3631
+ }
3632
+ return { switched, pending };
3633
+ }
3634
+ /**
3635
+ * Park a session because the account under it changed – its own words.
3636
+ *
3637
+ * `quiet`, then a note of its own: «the runner switched to another session»
3638
+ * is simply untrue here, and this is the one thing the person needs to read
3639
+ * to know why their session is waiting for a message (the same reason #126
3640
+ * gave the rewind its own wording).
3641
+ */
3642
+ parkForAccount(running, label) {
3643
+ this.sendEvent(running, 'system_note', {
3644
+ // The word the dashboard reads to tell «parked» from «your turn» (#124) –
3645
+ // the state IS parked; only the reason differs.
3646
+ code: 'session_parked',
3647
+ text: label
3648
+ ? `The agent account on this server was switched to ${label} — send a message and this session carries on under it, with everything above kept.`
3649
+ : 'The agent account on this server was switched — send a message and this session carries on under the new one, with everything above kept.',
3650
+ });
3651
+ this.park(running, { quiet: true });
3652
+ }
3653
+ /**
3654
+ * The turn is over – carry out a switch that arrived while it was running.
3655
+ *
3656
+ * Still not parkable (a card is open, a question is waiting): the mark stays
3657
+ * and the next end of a turn tries again. Nothing here ever kills a turn.
3658
+ */
3659
+ applyPendingAccountSwitch(running) {
3660
+ const wanted = running.accountSwitchPending;
3661
+ if (!wanted)
3662
+ return;
3663
+ if (!running.session || running.session.accountId === wanted.id) {
3664
+ delete running.accountSwitchPending;
3665
+ return;
3666
+ }
3667
+ if (!this.isParkable(running))
3668
+ return;
3669
+ delete running.accountSwitchPending;
3670
+ this.parkForAccount(running, wanted.label);
3671
+ }
3581
3672
  /**
3582
3673
  * The agent is producing output while this runner still says a human is
3583
3674
  * expected — put `lastReported` back to RUNNING (ticket #185).
@@ -7494,7 +7585,11 @@ export class Supervisor {
7494
7585
  // #422: the five account commands – their own module, one door each
7495
7586
  // over `claude-homes.ts` (plan §8).
7496
7587
  if (isAccountCommand(frame.name)) {
7497
- return void reply(await runAccountCommand(frame.name, frame.args ?? {}, this.authRelay));
7588
+ return void reply(
7589
+ // The fourth argument is «apply to the sessions already running»
7590
+ // (#422 S4.1): the commands know the accounts, this class knows
7591
+ // the sessions, so the switch reaches them from here.
7592
+ await runAccountCommand(frame.name, frame.args ?? {}, this.authRelay, (...args) => this.applyAccountToSessions(...args)));
7498
7593
  }
7499
7594
  return void reply({ ok: false, error: `Unknown command` });
7500
7595
  }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const RUNNER_VERSION = "0.66.0";
1
+ export declare const RUNNER_VERSION = "0.68.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Kept in sync with package.json by the release script (manual for now).
2
- export const RUNNER_VERSION = '0.66.0';
2
+ export const RUNNER_VERSION = '0.68.0';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge4dev/runner",
3
- "version": "0.66.0",
3
+ "version": "0.68.0",
4
4
  "description": "DevBridge dev runner — connects a dev server to DevBridge and runs agent sessions (Claude Code / Codex)",
5
5
  "homepage": "https://bridge4.dev",
6
6
  "license": "MIT",