ucode-agent 1.25.0 → 1.26.1

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": "ucode-agent",
3
- "version": "1.25.0",
3
+ "version": "1.26.1",
4
4
  "description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA and Cohere models.",
5
5
  "type": "module",
6
6
  "main": "ucode.js",
package/src/core/loop.js CHANGED
@@ -435,13 +435,13 @@ function systemPrompt({ cwd, skills, mode, check, map, memory }) {
435
435
  '',
436
436
  '## How to work',
437
437
  '',
438
- 'AN APP THAT IS AWKWARD TO USE IS NOT FINISHED. Before you call it done, look at',
439
- 'what you built as someone using it for the first time: is the button inside its',
440
- 'field or sitting on top of it, is there room to breathe between things, does the',
441
- 'empty state say anything, can every control be reached from the keyboard, does',
442
- 'it hold together at 360px wide. Controls that overlap or crowd each other are a',
443
- 'bug in the same way a crash is - fix it before you say a word about being done.',
444
- '',
438
+ 'AN APP THAT IS AWKWARD TO USE IS NOT FINISHED. Before you call it done, look at',
439
+ 'what you built as someone using it for the first time: is the button inside its',
440
+ 'field or sitting on top of it, is there room to breathe between things, does the',
441
+ 'empty state say anything, can every control be reached from the keyboard, does',
442
+ 'it hold together at 360px wide. Controls that overlap or crowd each other are a',
443
+ 'bug in the same way a crash is - fix it before you say a word about being done.',
444
+ '',
445
445
  'Before building anything, turn the request into a list of what it must do — every',
446
446
  'feature named, and the ones any user would expect whether or not they were named',
447
447
  '(an empty state, an error state, the keyboard doing the obvious thing, working on',
@@ -493,18 +493,19 @@ function systemPrompt({ cwd, skills, mode, check, map, memory }) {
493
493
  '',
494
494
  'YOUR FIRST WORDS, BEFORE ANY TOOL CALL, EVERY TIME. Nothing else appears on',
495
495
  'screen while you work, so if you say nothing the user watches a blank page.',
496
- 'Open with one line: I will build Tide for you - a tasks app in a single HTML',
497
- 'file. Then one line each time you start a new piece of the work: Now the',
498
- 'components. / Now making the filter row. / Onto the animations. Then one',
499
- 'line at the end saying it is done. Do not explain where the file is or how to',
500
- 'open it - they asked for it, they know where it went, and being told reads as',
501
- 'being talked down to.',
502
496
  '',
503
- 'Those lines are the whole of what the user reads. They are not thinking out',
504
- 'loud: never "I need to", never "Let me", never "The user wants", never a plan',
505
- 'of which files you will touch. Say what you are doing, the way you would to',
506
- 'someone watching over your shoulder.',
497
+ 'Open with one plain sentence naming the thing and the shape of it:',
498
+ ' I will build Tide for you - a tasks app in a single HTML file, dark with a',
499
+ ' teal accent.',
500
+ 'Then one line whenever you start a new piece of the work: Now the filter row.',
501
+ 'Then one line at the end saying it is done.',
507
502
  '',
503
+ 'Plain words only. No methodology, no "component-based approach", no',
504
+ '"structured design", no listing the qualities the work will have. Never',
505
+ '"I need to", never "Let me", never a plan of which files you will touch, never',
506
+ 'the request repeated back. Say it the way you would to someone watching over',
507
+ 'your shoulder, who can already see the screen.',
508
+ '',
508
509
 
509
510
  '',
510
511
  'Before you guess at an API, ask: type_of gives the exact signature from the',
@@ -1015,6 +1016,7 @@ export class Agent {
1015
1016
  let argRetries = 0;
1016
1017
  let continuations = 0;
1017
1018
  let askedToVerify = false;
1019
+ let squeezed = false;
1018
1020
  let askedToSpeak = false;
1019
1021
  let fixRounds = 0;
1020
1022
  this.failovers = 0;
@@ -1100,6 +1102,23 @@ export class Agent {
1100
1102
  continue;
1101
1103
  }
1102
1104
 
1105
+ // An oversized conversation is a recoverable thing, not a dead end.
1106
+ //
1107
+ // The provider rejects the request, ucode prints the error and the
1108
+ // half-built app stops there; typing "continue" sends the same
1109
+ // oversized conversation again and fails the same way. Folding it and
1110
+ // trying once is what the user would be told to do, so it happens
1111
+ // without asking.
1112
+ if (err.kind === 'bad_request' && !squeezed && !this.abort.signal.aborted) {
1113
+ squeezed = true;
1114
+ const before = this.working.length;
1115
+ await this.maybeFold({ force: true });
1116
+ if (this.working.length < before) {
1117
+ this.ui.note('the conversation had grown too large — folded it and carried on');
1118
+ continue;
1119
+ }
1120
+ }
1121
+
1103
1122
  // Busy, slow or down: move to the next model and carry on, rather
1104
1123
  // than ending a half-built app with an error.
1105
1124
  if (passing(err) && !this.abort.signal.aborted && (await this.failover(err))) continue;
@@ -1841,10 +1860,16 @@ export class Agent {
1841
1860
  total.turns += 1;
1842
1861
  }
1843
1862
 
1844
- /** Fold older turns into a summary when the window gets tight. */
1845
- async maybeFold() {
1863
+ /**
1864
+ * Fold older turns into a summary when the window gets tight.
1865
+ *
1866
+ * `force` is for when the provider has already said the conversation is too
1867
+ * large: its word beats our estimate of the same thing, and the estimate is
1868
+ * what let it get here.
1869
+ */
1870
+ async maybeFold({ force = false } = {}) {
1846
1871
  const limit = contextLimit();
1847
- if (!tooBig(this.working, limit)) return;
1872
+ if (!force && !tooBig(this.working, limit)) return;
1848
1873
 
1849
1874
  this.ui.startSpinner('context is filling up — summarizing earlier turns');
1850
1875
  try {
package/src/ui/screen.js CHANGED
@@ -340,11 +340,19 @@ export class Screen {
340
340
  * is what makes the transcript read as a sequence of things done rather
341
341
  * than a set of running totals.
342
342
  */
343
- endRun() {
344
- if (this.run) this.paintRun({ live: false });
345
- this.run = null;
346
- this.segment = new Map();
347
- }
343
+ /**
344
+ * Stop adding to the current run, but keep the lines already on screen.
345
+ *
346
+ * A kind of work gets one line for the whole turn. Starting a fresh set
347
+ * whenever the model spoke meant "Creating Tide from the HTML starter" five
348
+ * times down the page and "Reading files" four, each saying the same thing
349
+ * about a different moment. One line that counts up says all of it and
350
+ * costs one row.
351
+ */
352
+ endRun() { this.run = null; }
353
+
354
+ /** A new turn starts with a clean page's worth of lines. */
355
+ newSegment() { this.run = null; this.segment = new Map(); }
348
356
 
349
357
  /** Redraw the run's single line from what it has accumulated. */
350
358
  /**
@@ -869,6 +877,7 @@ export class Screen {
869
877
 
870
878
  /** A turn begins: the timer and step count run until turnEnd(). */
871
879
  turnStart() {
880
+ this.newSegment();
872
881
  this.activity = { start: Date.now(), steps: 0, movedAt: 0 };
873
882
  this.startTimer();
874
883
  }