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 +1 -1
- package/src/core/loop.js +45 -20
- package/src/ui/screen.js +14 -5
package/package.json
CHANGED
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
|
-
'
|
|
504
|
-
'
|
|
505
|
-
'
|
|
506
|
-
'
|
|
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
|
-
/**
|
|
1845
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
}
|