@bahulam/code 0.1.17 → 0.1.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bahulam/code",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Bahulam Code — abundance, in your terminal. CLI-first, reliability-first, sub-agents, 65.6% SWE-bench Verified.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema": "bahulam.model_defaults.v1",
3
+ "source": "codekepler-backend/app/services/model_defaults.py",
4
+ "defaults": {
5
+ "reasoning": "deepseek/deepseek-v4-flash",
6
+ "fast": "deepseek/deepseek-v4-flash",
7
+ "planning": "deepseek/deepseek-v4-pro"
8
+ }
9
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shipped npm-side model defaults.
3
+ *
4
+ * Source of truth is backend `app/services/model_defaults.py`; release sync
5
+ * writes `model-defaults-default.json` next to the shipped catalog.
6
+ */
7
+
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ export const FALLBACK_MODEL_DEFAULTS = Object.freeze({
13
+ reasoning: 'deepseek/deepseek-v4-flash',
14
+ fast: 'deepseek/deepseek-v4-flash',
15
+ planning: 'deepseek/deepseek-v4-pro',
16
+ });
17
+
18
+ const _dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const DEFAULTS_PATH = path.join(_dirname, 'model-defaults-default.json');
20
+
21
+ function clean(value) {
22
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
23
+ }
24
+
25
+ export function readShippedModelDefaults() {
26
+ try {
27
+ const data = JSON.parse(fs.readFileSync(DEFAULTS_PATH, 'utf-8'));
28
+ const defaults = data?.defaults && typeof data.defaults === 'object' ? data.defaults : {};
29
+ return {
30
+ reasoning: clean(defaults.reasoning) || FALLBACK_MODEL_DEFAULTS.reasoning,
31
+ fast: clean(defaults.fast) || FALLBACK_MODEL_DEFAULTS.fast,
32
+ planning: clean(defaults.planning) || FALLBACK_MODEL_DEFAULTS.planning,
33
+ };
34
+ } catch {
35
+ return { ...FALLBACK_MODEL_DEFAULTS };
36
+ }
37
+ }
38
+
39
+ export const SHIPPED_MODEL_DEFAULTS = readShippedModelDefaults();
40
+ export const DEFAULT_REASONING_MODEL = SHIPPED_MODEL_DEFAULTS.reasoning;
41
+ export const DEFAULT_FAST_MODEL = SHIPPED_MODEL_DEFAULTS.fast;
42
+ export const DEFAULT_PLANNING_MODEL = SHIPPED_MODEL_DEFAULTS.planning;
@@ -7,6 +7,7 @@
7
7
  import fs from 'fs';
8
8
  import path from 'path';
9
9
  import os from 'os';
10
+ import { DEFAULT_FAST_MODEL, DEFAULT_REASONING_MODEL } from './model-defaults.mjs';
10
11
 
11
12
  /**
12
13
  * Full settings schema with defaults.
@@ -30,9 +31,9 @@ export const SETTINGS_SCHEMA = {
30
31
  Stop: [],
31
32
  SessionStart: [],
32
33
  },
33
- model: 'claude-sonnet-4-6',
34
+ model: DEFAULT_REASONING_MODEL,
34
35
  subagentModel: null,
35
- fastModel: 'claude-haiku-4-5',
36
+ fastModel: DEFAULT_FAST_MODEL,
36
37
  fastMode: false,
37
38
  alwaysThinkingEnabled: false,
38
39
  autoCompactEnabled: true,
@@ -343,7 +343,7 @@ export async function runPreflight({ auth, cwd, version, silent = false } = {})
343
343
  const t = term();
344
344
  const write = (s) => { if (!silent) process.stderr.write(s); };
345
345
 
346
- const header = `${icons.search} ${paint.bold(paint.brand.primary('Bahulam Code v' + (version || '?')))} ${paint.text.dim('· initializing orbit')}`;
346
+ const header = `${icons.search} ${paint.bold(paint.brand.primary('Bahulam Code v' + (version || '?')))} ${paint.text.dim('· starting session')}`;
347
347
  write('\n' + header + '\n\n');
348
348
 
349
349
  const checks = [];
@@ -1335,8 +1335,8 @@ async function _checkForUpgradeAndAnnounce() {
1335
1335
  const [x, y, z] = asTuple(current);
1336
1336
  const newer = (a > x) || (a === x && b > y) || (a === x && b === y && c1 > z);
1337
1337
  if (!newer) return;
1338
- process.stderr.write(` ${c.brand('◆')} ${c.dim('New version available:')} ${c.bold(c.green(latest))} ${c.dim(`(current ${current})`)}\n`);
1339
- process.stderr.write(` ${c.dim(' Upgrade:')} ${c.dim('npm install -g ' + pkgName + '@latest')}\n\n`);
1338
+ process.stderr.write(` ${c.brand('◆')} ${c.dim('Update available:')} ${c.bold(c.green(`${pkgName}@${latest}`))} ${c.dim(`(installed ${current})`)}\n`);
1339
+ process.stderr.write(` ${c.dim(' Install:')} ${c.dim('npm install -g ' + pkgName + '@latest')}\n\n`);
1340
1340
  }
1341
1341
 
1342
1342
  // ── Prompt Chrome ──
@@ -1385,6 +1385,8 @@ function buildContextStrip() {
1385
1385
  // volume + elapsed. Historical rate calc was double-counting the cache tokens
1386
1386
  // vs OpenRouter's convention (see computeCacheTotals) which was misleading.
1387
1387
  const parts = [];
1388
+ const model = compactDockModel(activeDockModel());
1389
+ if (model) parts.push(c.dim(`model ${model}`));
1388
1390
  // ctx: last turn's cumulative input tokens — approximates the CURRENT
1389
1391
  // prompt size. Only shown once we've completed at least one turn (so
1390
1392
  // the initial banner doesn't read '0 ctx'). This is the number the
@@ -1397,13 +1399,27 @@ function buildContextStrip() {
1397
1399
  return parts.join(c.dim(' · '));
1398
1400
  }
1399
1401
 
1400
- // ── Dock meta line (model · cwd ⎇ branch · turn N) ─────────────────────
1402
+ // ── Dock meta line (cwd ⎇ branch · turn N) ─────────────────────────────
1401
1403
  //
1402
1404
  // The dock's meta row shows durable session context. Git branch is cached
1403
1405
  // so we don't shell out on every keystroke; refreshed at most every 5s.
1404
1406
 
1405
1407
  const _dockGitCache = { branch: null, at: 0, cwd: null };
1406
1408
 
1409
+ function activeDockModel() {
1410
+ return session.modelOverrides?.reasoning
1411
+ || session.model
1412
+ || session.modelLimits?.coder?.model
1413
+ || session.user?.default_reasoning_model
1414
+ || null;
1415
+ }
1416
+
1417
+ function compactDockModel(model) {
1418
+ const value = String(model || '').trim();
1419
+ if (!value) return '';
1420
+ return value.replace(/^(anthropic|openai|google|deepseek|xai|meta)\//, '');
1421
+ }
1422
+
1407
1423
  function _probeGitBranch(cwd) {
1408
1424
  const now = Date.now();
1409
1425
  if (_dockGitCache.cwd === cwd && (now - _dockGitCache.at) < 5000) {
@@ -1434,11 +1450,6 @@ function buildDockMeta() {
1434
1450
  parts.push(`turn ${session.turns}`);
1435
1451
  }
1436
1452
 
1437
- const totalTokens = session.inputTokens + session.outputTokens;
1438
- if (totalTokens > 0) {
1439
- parts.push(`${formatTokens(totalTokens)} tok`);
1440
- }
1441
-
1442
1453
  return parts.join(' · ');
1443
1454
  }
1444
1455
 
@@ -11,6 +11,7 @@
11
11
  import { createAgentLoop } from '../core/agent-loop.mjs';
12
12
  import { createToolRegistry } from './registry.mjs';
13
13
  import { createPermissionChecker } from '../permissions/checker.mjs';
14
+ import { DEFAULT_REASONING_MODEL } from '../config/model-defaults.mjs';
14
15
 
15
16
  export const AgentTool = {
16
17
  name: 'Agent',
@@ -59,7 +60,7 @@ export const AgentTool = {
59
60
  _nextBgId: 0,
60
61
 
61
62
  async call(input, options = {}) {
62
- const model = input.model || process.env.SUBAGENT_MODEL || 'claude-sonnet-4-6';
63
+ const model = input.model || process.env.SUBAGENT_MODEL || DEFAULT_REASONING_MODEL;
63
64
  const tools = createToolRegistry({
64
65
  pluginRegistry: options.pluginRegistry || null,
65
66
  stateEmit: options.stateEmit || null,
@@ -9,6 +9,7 @@ import { SessionManager } from '../core/session.mjs';
9
9
  import { CheckpointManager } from '../core/checkpoints.mjs';
10
10
  import { readEnv, listEnvVars } from '../config/env.mjs';
11
11
  import * as telemetry from '../telemetry/index.mjs';
12
+ import { DEFAULT_FAST_MODEL, DEFAULT_REASONING_MODEL } from '../config/model-defaults.mjs';
12
13
 
13
14
  const checkpoints = new CheckpointManager();
14
15
  let sessionManager = null;
@@ -134,12 +135,14 @@ export const COMMANDS = {
134
135
  '/fast': {
135
136
  description: 'Toggle fast mode (uses faster, cheaper model)',
136
137
  handler(args, state) {
137
- if (state.model?.includes('haiku')) {
138
- state.model = 'claude-sonnet-4-6';
139
- return 'Fast mode OFF — using claude-sonnet-4-6';
138
+ if (state.fastMode) {
139
+ state.fastMode = false;
140
+ state.model = DEFAULT_REASONING_MODEL;
141
+ return `Fast mode OFF — using ${DEFAULT_REASONING_MODEL}`;
140
142
  }
141
- state.model = 'claude-haiku-4-5';
142
- return 'Fast mode ON — using claude-haiku-4-5';
143
+ state.fastMode = true;
144
+ state.model = DEFAULT_FAST_MODEL;
145
+ return `Fast mode ON — using ${DEFAULT_FAST_MODEL}`;
143
146
  },
144
147
  },
145
148
 
@@ -704,11 +704,12 @@ export function clearInputPrompt() {
704
704
  export function renderDockInput(prefix, value, { context = '', tips = '', meta = '', cursor = null, fixedRows = null } = {}) {
705
705
  if (!mounted) return false;
706
706
  contentTrackingActive = false;
707
+ lastFrame = { ...lastFrame, context, tips, meta, prefix, value, cursor, overlayLines: null };
707
708
  const requestedRows = fixedRows == null
708
709
  ? computeInputRowsForBuffer(prefix, value)
709
710
  : Math.max(MIN_INPUT_ROWS, Math.min(inputRowsMax, Math.floor(Number(fixedRows) || MIN_INPUT_ROWS)));
710
711
  setInputRowsTo(requestedRows);
711
- renderFrame({ context, tips, meta, prefix, value, cursor, overlayLines: null });
712
+ renderFrame(lastFrame);
712
713
  const layout = layoutInput(prefix, value);
713
714
  drawInputLines(layout.lines);
714
715
  focusDockInput(prefix, value, cursor);