agen-vektor 0.3.6 → 0.3.7

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/dist/cli/index.js CHANGED
@@ -247,7 +247,10 @@ async function runTui(opts) {
247
247
  const out = app.render();
248
248
  process.stdout.write(terminal_1.ANSI.hideCursor + out);
249
249
  };
250
- const onResize = () => doRender();
250
+ const onResize = () => {
251
+ app.markDirty();
252
+ doRender();
253
+ };
251
254
  process.stdout.on('resize', onResize);
252
255
  process.on('SIGINT', () => {
253
256
  // In raw mode Ctrl+C arrives as a key event (0x03), but some terminals
@@ -258,6 +261,12 @@ async function runTui(opts) {
258
261
  else
259
262
  process.exit(0);
260
263
  });
264
+ process.on('SIGTERM', () => {
265
+ if (app)
266
+ app.requestExit();
267
+ else
268
+ process.exit(0);
269
+ });
261
270
  process.on('SIGWINCH', onResize);
262
271
  const keyHandler = (ev) => {
263
272
  void app.handleKey(ev);
@@ -269,11 +278,17 @@ async function runTui(opts) {
269
278
  (0, keyboard_1.keyStream)(keyHandler);
270
279
  app.start();
271
280
  doRender();
272
- // Main tick loop
281
+ app.consumeRender();
282
+ // Main tick loop — repaint only when something changed. Repainting the
283
+ // whole frame every tick caused constant flicker and made keystrokes
284
+ // (Enter, Ctrl+C) feel laggy on slow/mobile connections.
273
285
  while (!exit) {
274
286
  await app.tick();
275
- doRender();
276
- await sleep(50);
287
+ if (app.needsRender()) {
288
+ doRender();
289
+ app.consumeRender();
290
+ }
291
+ await sleep(80);
277
292
  }
278
293
  }
279
294
  finally {
package/dist/tui/app.js CHANGED
@@ -79,6 +79,8 @@ class App {
79
79
  abortController = null;
80
80
  spinner = new theme_1.Spinner();
81
81
  runStart = null;
82
+ /** set on every mutation so the render loop only repaints when needed */
83
+ dirty = true;
82
84
  constructor(agent, opts) {
83
85
  this.agent = agent;
84
86
  this.opts = opts;
@@ -88,7 +90,20 @@ class App {
88
90
  }
89
91
  }
90
92
  // ─── Public API ───────────────────────────────────────────────
93
+ /** Mark the screen as needing a repaint. */
94
+ markDirty() {
95
+ this.dirty = true;
96
+ }
97
+ /** True when the screen must be repainted (dirty, or streaming). */
98
+ needsRender() {
99
+ return this.dirty || this.running;
100
+ }
101
+ /** Clear the dirty flag after a repaint. */
102
+ consumeRender() {
103
+ this.dirty = false;
104
+ }
91
105
  start() {
106
+ this.markDirty();
92
107
  this.connected = true;
93
108
  this.status = 'Ready';
94
109
  if (this.opts.continueSession && this.sessionName) {
@@ -106,6 +121,7 @@ class App {
106
121
  }
107
122
  }
108
123
  async handleKey(ev) {
124
+ this.markDirty();
109
125
  if (this.modal.type !== 'none') {
110
126
  await this.handleModalKey(ev);
111
127
  return;
@@ -119,6 +135,10 @@ class App {
119
135
  this.running = false;
120
136
  this.addSystem('⏹ Stopped. Press Ctrl+C again to quit.');
121
137
  }
138
+ else if (ev.name === 'ctrl_d') {
139
+ // Ctrl+D stops and quits in one go (handy on mobile)
140
+ this.requestExit();
141
+ }
122
142
  return;
123
143
  }
124
144
  switch (ev.name) {
@@ -204,11 +224,13 @@ class App {
204
224
  }
205
225
  async tick() {
206
226
  if (this.pendingPrompt) {
227
+ this.markDirty();
207
228
  const p = this.pendingPrompt;
208
229
  this.pendingPrompt = '';
209
230
  await this.submit(p);
210
231
  }
211
232
  if (this.planToExecute) {
233
+ this.markDirty();
212
234
  const plan = this.planToExecute;
213
235
  this.planToExecute = null;
214
236
  await this.executeWithPlan(plan);
@@ -222,6 +244,7 @@ class App {
222
244
  * Used by Ctrl+C and SIGINT so the TUI never needs to be killed.
223
245
  */
224
246
  requestExit() {
247
+ this.markDirty();
225
248
  if (this.running) {
226
249
  this.abortController?.abort();
227
250
  this.running = false;
@@ -230,6 +253,7 @@ class App {
230
253
  }
231
254
  // ─── Submission ───────────────────────────────────────────────
232
255
  async submit(text) {
256
+ this.markDirty();
233
257
  if (text.startsWith('/')) {
234
258
  await this.handleCommand(text);
235
259
  return;
@@ -284,15 +308,18 @@ class App {
284
308
  }) - 1;
285
309
  this.agent.setCallbacks({
286
310
  onDelta: (delta) => {
311
+ this.markDirty();
287
312
  const m = this.messages[msgIndex];
288
313
  if (m)
289
314
  m.content += delta;
290
315
  },
291
316
  onStatus: (status) => {
317
+ this.markDirty();
292
318
  this.status = status;
293
319
  this.statusColor = (0, theme_1.statusColor)(status);
294
320
  },
295
321
  onToolCall: (tool, args) => {
322
+ this.markDirty();
296
323
  let preview = '';
297
324
  try {
298
325
  const parsed = JSON.parse(args);
@@ -306,6 +333,7 @@ class App {
306
333
  this.messages.push({ kind: 'tool', tool, content: `→ ${preview}` });
307
334
  },
308
335
  onToolResult: (tool, result) => {
336
+ this.markDirty();
309
337
  const last = this.messages[this.messages.length - 1];
310
338
  if (last && last.kind === 'tool' && last.tool === tool) {
311
339
  last.content = result.split('\n').slice(0, 12).join('\n');
@@ -314,6 +342,7 @@ class App {
314
342
  }
315
343
  },
316
344
  onError: (err) => {
345
+ this.markDirty();
317
346
  const m = this.messages[msgIndex];
318
347
  if (m) {
319
348
  m.kind = 'error';
@@ -322,6 +351,7 @@ class App {
322
351
  }
323
352
  },
324
353
  onFinal: (result) => {
354
+ this.markDirty();
325
355
  const m = this.messages[msgIndex];
326
356
  if (m) {
327
357
  m.content = result.content || m.content;
@@ -386,6 +416,7 @@ class App {
386
416
  }));
387
417
  }
388
418
  async executeWithPlan(plan) {
419
+ this.markDirty();
389
420
  this.running = true;
390
421
  this.status = 'Executing plan';
391
422
  this.statusColor = terminal_1.ANSI.yellow;
@@ -478,6 +509,21 @@ class App {
478
509
  this.addSystem(`Current session: ${this.sessionName || '(none)'}`);
479
510
  }
480
511
  break;
512
+ case '/connect':
513
+ // Wizard for a custom OpenAI-compatible API: base URL + token
514
+ this.agent.config.provider = 'custom';
515
+ this.agent.rebuildProvider();
516
+ (0, config_1.saveConfig)(this.agent.config);
517
+ this.addSystem('Custom provider — masukkan base URL API (mis. https://…/v1), lalu token.');
518
+ this.modal = { type: 'custom-url', value: this.agent.config.apiUrl || '' };
519
+ break;
520
+ case '/agent':
521
+ this.agent.setMode(this.agent.config.permissionMode === 'yolo' ? 'ask' : 'yolo');
522
+ (0, config_1.saveConfig)(this.agent.config);
523
+ this.addSystem(this.agent.config.permissionMode === 'yolo'
524
+ ? 'Agent mode: YOLO — prompt permission untuk aksi ask-level dilewati.'
525
+ : 'Agent mode: ask — prompt permission aktif.');
526
+ break;
481
527
  default:
482
528
  this.addSystem(`Unknown command: ${name} (type /help for commands)`);
483
529
  break;
@@ -689,10 +735,12 @@ class App {
689
735
  }
690
736
  // ─── Message helpers ──────────────────────────────────────────
691
737
  addSystem(text) {
738
+ this.markDirty();
692
739
  this.messages.push({ kind: 'system', content: text });
693
740
  }
694
741
  /** Show a permission modal (called by the permission callback). */
695
742
  setPermissionModal(req) {
743
+ this.markDirty();
696
744
  this.modal = {
697
745
  type: 'permission',
698
746
  req,
@@ -706,6 +754,7 @@ class App {
706
754
  this.permissionResolver = fn;
707
755
  }
708
756
  addTool(tool, content) {
757
+ this.markDirty();
709
758
  this.messages.push({ kind: 'tool', tool, content });
710
759
  }
711
760
  // ─── Rendering ────────────────────────────────────────────────
@@ -799,11 +848,12 @@ class App {
799
848
  ' PgUp/Dn scroll chat',
800
849
  ' ? this help',
801
850
  ' Ctrl+C stop agent / quit',
851
+ ' Ctrl+D stop + quit (mobile)',
802
852
  ' Ctrl+L clear conversation',
803
853
  '',
804
- 'Commands: /setup /model /provider /session',
805
- '/settings /status /clear /continue /diff',
806
- '/git /compact /yolo /exit',
854
+ 'Commands: /connect /setup /model /provider',
855
+ '/session /settings /status /clear /continue',
856
+ '/diff /git /compact /agent /exit',
807
857
  ],
808
858
  }, rows, cols);
809
859
  case 'commands':
@@ -811,7 +861,8 @@ class App {
811
861
  title: 'Commands',
812
862
  body: [
813
863
  '/help show help',
814
- '/setup setup provider (base URL + token)',
864
+ '/connect set custom API base URL + token',
865
+ '/setup setup provider',
815
866
  '/model select model',
816
867
  '/provider select provider',
817
868
  '/session manage sessions',
@@ -822,7 +873,7 @@ class App {
822
873
  '/diff show diff',
823
874
  '/git git status',
824
875
  '/compact compact context',
825
- '/yolo toggle permission mode',
876
+ '/agent toggle permission mode (ask/yolo)',
826
877
  '/exit quit',
827
878
  ],
828
879
  }, rows, cols);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agen-vektor",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "VectorHead (agen-vektor) — AI Coding Agent CLI/TUI for Linux & Termux. Multi-provider, tool calling, session, permission system.",
5
5
  "type": "commonjs",
6
6
  "bin": {