dual-brain 0.3.34 → 0.3.36

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.
Files changed (2) hide show
  1. package/bin/dual-brain.mjs +109 -9
  2. package/package.json +1 -1
@@ -6,6 +6,7 @@ import { join, dirname, basename, extname } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { execSync, spawnSync as _spawnSyncTop } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
9
+ import { randomUUID } from 'node:crypto';
9
10
 
10
11
  import {
11
12
  ensureProfile, loadProfile, saveProfile, runOnboarding,
@@ -68,6 +69,22 @@ function _codexResumeArgs(sessionId, cwd) {
68
69
  ];
69
70
  }
70
71
 
72
+ function _codexNewArgs(cwd) {
73
+ const workspace = cwd || process.cwd();
74
+ if (getEffectiveBypassPermissions(workspace)) {
75
+ return ['--dangerously-bypass-approvals-and-sandbox'];
76
+ }
77
+ const settings = loadSessionSettings(workspace);
78
+ const approvalMode = getEffectiveAutomode(loadProfile(workspace), workspace) ? 'never' : 'on-request';
79
+ return [
80
+ ..._codexModelEffortArgs(
81
+ _modelMatchesProvider(settings.headModel, 'codex') ? settings.headModel : null,
82
+ settings.effort,
83
+ ),
84
+ ..._codexApprovalArgs(workspace, approvalMode),
85
+ ];
86
+ }
87
+
71
88
  function _isReplitWorkspace(cwd) {
72
89
  const workspace = cwd || process.cwd();
73
90
  return !!(
@@ -1552,6 +1569,10 @@ async function cmdRuntimeSwitch(args = []) {
1552
1569
  ? runtimeLaunchArgsForPending(sess, cwd, pending)
1553
1570
  : ['handoff', '--to', provider];
1554
1571
 
1572
+ const activeForSwitch = confirmed ? readActiveConversation(cwd) : null;
1573
+ const activeMatches = activeForSwitch?.sessionId === sess.id;
1574
+ let restartSignaled = false;
1575
+
1555
1576
  console.log('');
1556
1577
  console.log(`Runtime switch ${confirmed ? 'confirmed' : 'prepared'} for ${pending?.sessionName || sess.id}`);
1557
1578
  console.log(`Provider: ${provider}`);
@@ -1561,10 +1582,16 @@ async function cmdRuntimeSwitch(args = []) {
1561
1582
  console.log(`Reason: ${reason}`);
1562
1583
  console.log(`Launch: ${provider} ${launchArgs.join(' ')}`);
1563
1584
  if (confirmed) {
1564
- const activeForSwitch = readActiveConversation(cwd);
1565
- if (activeForSwitch?.sessionId === sess.id) {
1566
- console.log('Active supervisor detected: HEAD will restart with these settings.');
1567
- signalActiveConversation(cwd, sess.id);
1585
+ if (activeMatches) {
1586
+ restartSignaled = signalActiveConversation(cwd, sess.id);
1587
+ if (restartSignaled) {
1588
+ const terminalNote = activeForSwitch.terminalId && activeForSwitch.terminalId !== getTerminalId()
1589
+ ? ` in ${activeForSwitch.terminalId}`
1590
+ : '';
1591
+ console.log(`Active supervisor detected${terminalNote}: restart signal sent.`);
1592
+ } else {
1593
+ console.log('Active supervisor was detected, but the restart signal could not be delivered.');
1594
+ }
1568
1595
  } else if (!args.includes('--apply')) {
1569
1596
  console.log('No active supervisor detected: saved for the next dual-brain resume/switch, not live yet.');
1570
1597
  }
@@ -1573,7 +1600,12 @@ async function cmdRuntimeSwitch(args = []) {
1573
1600
 
1574
1601
  if (args.includes('--apply')) {
1575
1602
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
1576
- console.log('Non-interactive shell detected: saved for the active dual-brain terminal to apply.');
1603
+ if (restartSignaled) {
1604
+ console.log('Non-interactive shell detected: command applied by signaling the active dual-brain terminal.');
1605
+ } else {
1606
+ console.log('Non-interactive shell detected: saved for the next dual-brain resume/switch.');
1607
+ console.log('This command cannot mutate an unattached API/chat surface that is already running under another provider.');
1608
+ }
1577
1609
  } else {
1578
1610
  const applied = await processPendingRuntimeSwitch(cwd);
1579
1611
  if (!applied) console.log('Could not apply live here; resume the session through dual-brain to use these settings.');
@@ -4780,11 +4812,79 @@ async function mainScreen(rl, ask) {
4780
4812
 
4781
4813
  async function newSessionScreen(rl, ask) {
4782
4814
  const cwd = process.cwd();
4783
- const input = (await ask('\n What do you want to do? ')).trim();
4784
- if (!input) { return { next: 'main' }; }
4815
+ const profile = loadProfile(cwd);
4816
+ const settings = loadSessionSettings(cwd);
4817
+ settings.automode = true;
4818
+ settings.bypassPermissions = false;
4785
4819
 
4786
- // All work routes through pipeline detect → decide → dispatch with mandatory gates.
4787
- await cmdGo([input], { cwd });
4820
+ let provider = _modelMatchesProvider(settings.headModel, 'claude') ? 'claude' : 'codex';
4821
+ if (!_modelMatchesProvider(settings.headModel, provider)) {
4822
+ const policy = _headPolicyFor(provider, profile, settings);
4823
+ settings.headModel = policy.model;
4824
+ settings.effort = policy.effort;
4825
+ }
4826
+ saveSessionSettings(cwd, settings);
4827
+
4828
+ const renderStart = () => {
4829
+ const policy = _headPolicyFor(provider, profile, settings);
4830
+ if (!_modelMatchesProvider(settings.headModel, provider)) {
4831
+ settings.headModel = policy.model;
4832
+ settings.effort = policy.effort;
4833
+ saveSessionSettings(cwd, settings);
4834
+ }
4835
+ process.stdout.write('\n');
4836
+ process.stdout.write(' New HEAD Conversation\n\n');
4837
+ process.stdout.write(' Recommended Balanced Session\n');
4838
+ process.stdout.write(` Provider: ${provider === 'codex' ? 'GPT/Codex' : 'Claude'}\n`);
4839
+ process.stdout.write(` Model: ${settings.headModel || policy.model} (${settings.effort || policy.effort || 'default'})\n`);
4840
+ process.stdout.write(' Mode: Smart Auto\n');
4841
+ process.stdout.write(` Permissions: ${provider === 'codex' ? 'never ask + Replit sandbox boundary' : 'auto'}\n\n`);
4842
+ process.stdout.write(' Enter start recommended p start with pasted prompt b back\n\n');
4843
+ };
4844
+
4845
+ renderStart();
4846
+ let choice = (await ask(' Choice: ')).trim().toLowerCase();
4847
+ let initialPrompt = '';
4848
+ if (choice === 'b' || choice === 'q') return { next: 'main' };
4849
+ if (choice === 'p' || choice === 'prompt') {
4850
+ initialPrompt = (await ask(' Initial prompt: ')).trim();
4851
+ }
4852
+
4853
+ const policy = _headPolicyFor(provider, profile, settings);
4854
+ if (!_modelMatchesProvider(settings.headModel, provider)) settings.headModel = policy.model;
4855
+ if (!settings.effort) settings.effort = policy.effort;
4856
+ settings.automode = true;
4857
+ settings.bypassPermissions = false;
4858
+ saveSessionSettings(cwd, settings);
4859
+
4860
+ const session = {
4861
+ id: randomUUID(),
4862
+ tool: provider,
4863
+ smartName: 'New HEAD Conversation',
4864
+ firstPrompt: initialPrompt || 'New dual-brain HEAD conversation',
4865
+ };
4866
+ const launchArgs = provider === 'codex'
4867
+ ? _codexNewArgs(cwd)
4868
+ : ['--session-id', session.id, ..._claudeNewArgs(cwd)];
4869
+ if (initialPrompt) launchArgs.push(initialPrompt);
4870
+
4871
+ process.stdout.write('\n');
4872
+ process.stdout.write(' Starting HEAD in Smart Auto...\n');
4873
+ process.stdout.write(` Provider: ${provider === 'codex' ? 'GPT/Codex' : 'Claude'}\n`);
4874
+ process.stdout.write(` Model: ${settings.headModel || 'default'}${settings.effort ? ` (${settings.effort})` : ''}\n`);
4875
+ process.stdout.write(` Launch: ${provider} ${launchArgs.join(' ')}\n\n`);
4876
+
4877
+ writeActiveConversation(cwd, session, provider, {
4878
+ model: settings.headModel || null,
4879
+ effort: settings.effort || null,
4880
+ automode: true,
4881
+ bypassPermissions: false,
4882
+ });
4883
+ try {
4884
+ await launchSupervisedHead(provider, launchArgs, cwd, session);
4885
+ } finally {
4886
+ clearActiveConversation(cwd, session.id);
4887
+ }
4788
4888
 
4789
4889
  return { next: 'main' };
4790
4890
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.3.34",
3
+ "version": "0.3.36",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {