novaprime 1.3.4 → 1.4.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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/agent.js +18 -6
  3. package/src/ui.js +21 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novaprime",
3
- "version": "1.3.4",
3
+ "version": "1.4.0",
4
4
  "description": "NovaPrime — an AI coding assistant in your terminal, powered by GLM.",
5
5
  "bin": {
6
6
  "novaprime": "bin/novaprime.js"
package/src/agent.js CHANGED
@@ -2,7 +2,7 @@
2
2
  const os = require('os');
3
3
  const ora = require('ora');
4
4
  const tools = require('./tools');
5
- const { c, aiLabel, error, warn } = require('./ui');
5
+ const { c, aiLabel, routeReport, error, warn } = require('./ui');
6
6
  const { Renderer } = require('./render');
7
7
 
8
8
  const SYSTEM_PROMPT =
@@ -50,8 +50,17 @@ async function streamMessage(server, key, messages) {
50
50
  return { error: msg };
51
51
  }
52
52
 
53
+ // live model-switch report: show what the free Flash brain decided (only on a fresh prompt)
54
+ try {
55
+ const hdr = res.headers.get('x-novaprime-route');
56
+ if (hdr) {
57
+ const report = JSON.parse(decodeURIComponent(hdr));
58
+ if (report && report.fresh) { stopSpin(); routeReport(report); ensureSpin(c.muted('thinking')); }
59
+ }
60
+ } catch (_) {}
61
+
53
62
  const blocks = [];
54
- let stopReason = null, labelShown = false, buffer = '';
63
+ let stopReason = null, labelShown = false, buffer = '', usedModel = null;
55
64
  const renderer = new Renderer();
56
65
  const decoder = new TextDecoder();
57
66
  const reader = res.body.getReader();
@@ -67,22 +76,25 @@ async function streamMessage(server, key, messages) {
67
76
  if (!dataLine) continue;
68
77
  let json; try { json = JSON.parse(dataLine.slice(5).trim()); } catch (_) { continue; }
69
78
 
70
- if (json.type === 'content_block_start') {
79
+ if (json.type === 'message_start') {
80
+ usedModel = (json.message && json.message.model) || usedModel;
81
+ if (spinning) spinner.text = c.muted('thinking · ' + (usedModel || ''));
82
+ } else if (json.type === 'content_block_start') {
71
83
  const cb = json.content_block || {};
72
84
  blocks[json.index] = cb.type === 'tool_use'
73
85
  ? { type: 'tool_use', id: cb.id, name: cb.name, _json: '' }
74
86
  : { type: 'text', text: '' };
75
- if (cb.type === 'tool_use') ensureSpin(c.muted('preparing ' + cb.name + '…'));
87
+ if (cb.type === 'tool_use') ensureSpin(c.muted('writing with ' + (usedModel || 'model') + '…'));
76
88
  } else if (json.type === 'content_block_delta') {
77
89
  const b = blocks[json.index]; if (!b) continue;
78
90
  if (json.delta.type === 'text_delta') {
79
91
  stopSpin();
80
- if (!labelShown) { aiLabel(); labelShown = true; }
92
+ if (!labelShown) { aiLabel(usedModel); labelShown = true; }
81
93
  b.text += json.delta.text;
82
94
  renderer.feed(json.delta.text);
83
95
  } else if (json.delta.type === 'input_json_delta') {
84
96
  b._json += json.delta.partial_json;
85
- ensureSpin(c.muted((b.name === 'run_command' ? 'preparing command' : 'writing ' + (b.name || 'file')) + '… ' + b._json.length + ' chars'));
97
+ ensureSpin(c.muted('writing with ' + (usedModel || 'model') + '… ' + b._json.length + ' chars'));
86
98
  }
87
99
  } else if (json.type === 'message_delta') {
88
100
  if (json.delta && json.delta.stop_reason) stopReason = json.delta.stop_reason;
package/src/ui.js CHANGED
@@ -98,7 +98,26 @@ function inputBoxOpen() {
98
98
  }
99
99
  function inputBoxClose() { process.stdout.write('\n'); }
100
100
 
101
- function aiLabel() { process.stdout.write('\n' + c.violet('● ') + c.violet.bold('Nova Prime') + '\n'); }
101
+ function aiLabel(model) { process.stdout.write('\n' + c.violet('● ') + c.violet.bold('Nova Prime') + (model ? c.dim(' · ' + model) : '') + '\n'); }
102
+
103
+ // Live "model switch" report — shows what the free Flash brain decided for this request.
104
+ function routeReport(r) {
105
+ if (!r) return;
106
+ const sep = c.dim(' → ');
107
+ const tag = c.violet('⚡ ') + c.muted('Flash analyzed');
108
+ let mid, model;
109
+ if (r.decision === 'build') {
110
+ mid = c.amber('new / heavy build');
111
+ model = c.white.bold('switched to ' + (r.chosenLabel || r.chosenModel));
112
+ } else if (r.escalated) {
113
+ mid = c.amber(r.reason || 'escalated');
114
+ model = c.white.bold('using ' + (r.chosenLabel || r.chosenModel));
115
+ } else {
116
+ mid = c.green('quick task');
117
+ model = c.body(r.chosenLabel || r.chosenModel) + (r.free ? c.dim(' · free') : '');
118
+ }
119
+ console.log('\n' + tag + sep + mid + sep + model);
120
+ }
102
121
 
103
122
  function info(msg) { console.log(c.muted(msg)); }
104
123
  function hint(msg) { console.log(c.dim(msg)); }
@@ -107,4 +126,4 @@ function error(msg) { console.log(c.red(' ✕ ') + c.red(msg)); }
107
126
  function ok(msg) { console.log(c.green(' ✓ ') + c.body(msg)); }
108
127
  function tool(name, detail) { console.log(c.dim(' · ' + name + (detail ? ' ' + detail : ''))); }
109
128
 
110
- module.exports = { chalk, boxen, c, banner, maskKey, aiLabel, inputTop, inputBottom, inputPrompt, inputBoxOpen, inputBoxClose, info, hint, warn, error, ok, tool };
129
+ module.exports = { chalk, boxen, c, banner, maskKey, aiLabel, routeReport, inputTop, inputBottom, inputPrompt, inputBoxOpen, inputBoxClose, info, hint, warn, error, ok, tool };