ucode-agent 1.24.0 → 1.26.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucode-agent",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
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',
@@ -491,20 +491,20 @@ function systemPrompt({ cwd, skills, mode, check, map, memory }) {
491
491
  'steps already show on screen, and repeating them in words buries the few',
492
492
  'sentences worth reading.',
493
493
  '',
494
- 'YOUR FIRST WORDS, BEFORE ANY TOOL CALL, EVERY TIME. Nothing else appears on',
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
- '',
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.',
507
- '',
494
+ 'YOUR FIRST WORDS, BEFORE ANY TOOL CALL, EVERY TIME. Nothing else appears on',
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
+ '',
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.',
507
+ '',
508
508
 
509
509
  '',
510
510
  'Before you guess at an API, ask: type_of gives the exact signature from the',
@@ -1015,6 +1015,7 @@ export class Agent {
1015
1015
  let argRetries = 0;
1016
1016
  let continuations = 0;
1017
1017
  let askedToVerify = false;
1018
+ let squeezed = false;
1018
1019
  let askedToSpeak = false;
1019
1020
  let fixRounds = 0;
1020
1021
  this.failovers = 0;
@@ -1100,6 +1101,23 @@ export class Agent {
1100
1101
  continue;
1101
1102
  }
1102
1103
 
1104
+ // An oversized conversation is a recoverable thing, not a dead end.
1105
+ //
1106
+ // The provider rejects the request, ucode prints the error and the
1107
+ // half-built app stops there; typing "continue" sends the same
1108
+ // oversized conversation again and fails the same way. Folding it and
1109
+ // trying once is what the user would be told to do, so it happens
1110
+ // without asking.
1111
+ if (err.kind === 'bad_request' && !squeezed && !this.abort.signal.aborted) {
1112
+ squeezed = true;
1113
+ const before = this.working.length;
1114
+ await this.maybeFold({ force: true });
1115
+ if (this.working.length < before) {
1116
+ this.ui.note('the conversation had grown too large — folded it and carried on');
1117
+ continue;
1118
+ }
1119
+ }
1120
+
1103
1121
  // Busy, slow or down: move to the next model and carry on, rather
1104
1122
  // than ending a half-built app with an error.
1105
1123
  if (passing(err) && !this.abort.signal.aborted && (await this.failover(err))) continue;
@@ -1841,10 +1859,16 @@ export class Agent {
1841
1859
  total.turns += 1;
1842
1860
  }
1843
1861
 
1844
- /** Fold older turns into a summary when the window gets tight. */
1845
- async maybeFold() {
1862
+ /**
1863
+ * Fold older turns into a summary when the window gets tight.
1864
+ *
1865
+ * `force` is for when the provider has already said the conversation is too
1866
+ * large: its word beats our estimate of the same thing, and the estimate is
1867
+ * what let it get here.
1868
+ */
1869
+ async maybeFold({ force = false } = {}) {
1846
1870
  const limit = contextLimit();
1847
- if (!tooBig(this.working, limit)) return;
1871
+ if (!force && !tooBig(this.working, limit)) return;
1848
1872
 
1849
1873
  this.ui.startSpinner('context is filling up — summarizing earlier turns');
1850
1874
  try {