0agent 1.0.38 → 1.0.39

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/chat.js +67 -24
  2. package/package.json +1 -1
package/bin/chat.js CHANGED
@@ -23,33 +23,64 @@ class Spinner {
23
23
  this._msg = msg;
24
24
  this._frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
25
25
  this._i = 0; this._timer = null; this._active = false;
26
+ this._sessionMode = false; // true during agent sessions — no \r animation
26
27
  }
28
+
29
+ // Animated spinner — safe only at startup when user cannot type yet.
30
+ // Uses \r which conflicts with readline when user is typing.
27
31
  start(msg) {
28
32
  if (this._active) return;
29
33
  if (msg) this._msg = msg;
30
34
  this._active = true;
35
+ this._sessionMode = false;
31
36
  this._timer = setInterval(() => {
32
37
  process.stdout.write(`\r \x1b[36m${this._frames[this._i++ % this._frames.length]}\x1b[0m \x1b[2m${this._msg}\x1b[0m `);
33
38
  }, 80);
34
39
  }
40
+
41
+ // Session mode — prints a one-time static status line, NO \r animation.
42
+ // readline owns the cursor; call rl.prompt(true) after this to show › .
43
+ startSession(msg) {
44
+ if (this._active) return;
45
+ if (msg) this._msg = msg;
46
+ this._active = true;
47
+ this._sessionMode = true;
48
+ process.stdout.write(` \x1b[2m⠋ ${this._msg}...\x1b[0m\n`);
49
+ }
50
+
35
51
  update(msg) { this._msg = msg; }
52
+
36
53
  stop(clearIt = true) {
37
54
  if (!this._active) return;
38
- clearInterval(this._timer); this._timer = null; this._active = false;
39
- if (clearIt) process.stdout.write('\r\x1b[2K');
55
+ if (this._timer) { clearInterval(this._timer); this._timer = null; }
56
+ const wasSession = this._sessionMode;
57
+ this._active = false;
58
+ this._sessionMode = false;
59
+ // Only clear the \r-based animation line; session mode output flows naturally
60
+ if (clearIt && !wasSession) {
61
+ process.stdout.write('\r\x1b[2K');
62
+ }
40
63
  }
64
+
41
65
  get active() { return this._active; }
42
66
 
43
- /**
44
- * Pause spinner, run fn() (which may print lines), then resume.
45
- * Prevents spinner from overwriting printed content.
46
- */
67
+ // Pause to print something cleanly, then resume.
47
68
  pauseFor(fn) {
48
- const wasActive = this._active;
49
- const savedMsg = this._msg;
50
- if (wasActive) this.stop(true);
69
+ const wasActive = this._active;
70
+ const wasSession = this._sessionMode;
71
+ const savedMsg = this._msg;
72
+ this.stop(!wasSession); // clear animated spinner; session mode: just deactivate
51
73
  fn();
52
- if (wasActive) this.start(savedMsg);
74
+ if (wasActive) {
75
+ if (wasSession) {
76
+ // Re-mark active without printing again — readline is showing the prompt
77
+ this._active = true;
78
+ this._sessionMode = true;
79
+ this._msg = savedMsg;
80
+ } else {
81
+ this.start(savedMsg);
82
+ }
83
+ }
53
84
  }
54
85
  }
55
86
 
@@ -192,13 +223,20 @@ function handleWsEvent(event) {
192
223
  case 'session.step': {
193
224
  spinner.stop();
194
225
  if (streaming) { process.stdout.write('\n'); streaming = false; }
226
+ // Clear current readline line, print step, then restore › prompt
227
+ process.stdout.write('\r\x1b[2K');
195
228
  console.log(` ${fmt(C.dim, '›')} ${event.step}`);
196
- spinner.start(event.step.slice(0, 50)); // resume with current step label
229
+ spinner.startSession(event.step.slice(0, 50));
230
+ rl.prompt(true); // restore › so user can keep typing
197
231
  break;
198
232
  }
199
233
  case 'session.token': {
200
234
  spinner.stop();
201
- if (!streaming) { process.stdout.write('\n '); streaming = true; }
235
+ if (!streaming) {
236
+ // Clear › prompt line before streaming response
237
+ process.stdout.write('\r\x1b[2K\n ');
238
+ streaming = true;
239
+ }
202
240
  process.stdout.write(event.token);
203
241
  lineBuffer += event.token;
204
242
  break;
@@ -383,9 +421,10 @@ async function runTask(input) {
383
421
  });
384
422
  const s = await res.json();
385
423
  sessionId = s.session_id ?? s.id;
386
- // Show affordance so user knows they can queue messages while agent works
387
- process.stdout.write(`\n \x1b[2m(type and press Enter to queue next message)\x1b[0m\n`);
388
- spinner.start('Thinking');
424
+ // Start session-mode status (no \r animation) then restore so user can type
425
+ process.stdout.write('\n');
426
+ spinner.startSession('Thinking');
427
+ rl.prompt(true); // keep › visible — user can queue next message while agent works
389
428
 
390
429
  // Polling fallback — runs concurrently with WS events.
391
430
  // Catches completion when WS is disconnected (e.g. daemon just restarted).
@@ -416,8 +455,10 @@ async function runTask(input) {
416
455
  const steps = session.steps ?? [];
417
456
  for (let j = lastPolledStep; j < steps.length; j++) {
418
457
  spinner.stop();
458
+ process.stdout.write('\r\x1b[2K');
419
459
  console.log(` \x1b[2m›\x1b[0m ${steps[j].description}`);
420
- spinner.start(steps[j].description.slice(0, 50));
460
+ spinner.startSession(steps[j].description.slice(0, 50));
461
+ rl.prompt(true);
421
462
  }
422
463
  lastPolledStep = steps.length;
423
464
 
@@ -968,14 +1009,16 @@ rl.on('line', async (input) => {
968
1009
  if (pendingResolve) {
969
1010
  messageQueue.push(line);
970
1011
  const qLen = messageQueue.length;
971
- spinner.pauseFor(() => {
972
- process.stdout.write(
973
- ` ${fmt(C.magenta, '')} ${fmt(C.bold, `[queued #${qLen}]`)} ${fmt(C.dim, line.slice(0, 70))}\n`
974
- );
975
- if (qLen > 1) {
976
- process.stdout.write(` ${fmt(C.dim, `${qLen} tasks waiting`)}\n`);
977
- }
978
- });
1012
+ // No spinner.pauseFor() needed — session mode has no \r animation
1013
+ // Just print the queued confirmation and restore the › prompt
1014
+ process.stdout.write('\r\x1b[2K');
1015
+ process.stdout.write(
1016
+ ` ${fmt(C.magenta, '↳')} ${fmt(C.bold, `[queued #${qLen}]`)} ${fmt(C.dim, line.slice(0, 70))}\n`
1017
+ );
1018
+ if (qLen > 1) {
1019
+ process.stdout.write(` ${fmt(C.dim, `${qLen} tasks waiting`)}\n`);
1020
+ }
1021
+ rl.prompt(true); // keep › visible
979
1022
  return;
980
1023
  }
981
1024
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0agent",
3
- "version": "1.0.38",
3
+ "version": "1.0.39",
4
4
  "description": "A persistent, learning AI agent that runs on your machine. An agent that learns.",
5
5
  "private": false,
6
6
  "license": "Apache-2.0",