ucode-agent 1.17.0 → 1.19.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 +1 -1
- package/src/core/loop.js +26 -11
- package/src/tools/index.js +5 -1
- package/src/ui/activity.js +6 -3
- package/src/ui/plain.js +1 -1
- package/src/ui/screen.js +6 -2
package/package.json
CHANGED
package/src/core/loop.js
CHANGED
|
@@ -458,13 +458,19 @@ function systemPrompt({ cwd, skills, mode, check, map, memory }) {
|
|
|
458
458
|
'already contains it. Do not re-check work the checks have already reported on.',
|
|
459
459
|
'Fast is not sloppy: it is the same work with the waiting taken out.',
|
|
460
460
|
'',
|
|
461
|
-
'
|
|
462
|
-
'
|
|
463
|
-
'
|
|
464
|
-
'
|
|
465
|
-
'
|
|
466
|
-
'
|
|
467
|
-
'
|
|
461
|
+
'SPEAK AS "I", NEVER "WE". You are doing this, not a committee: "I will build',
|
|
462
|
+
'Tide as a single HTML file", not "we have created the file".',
|
|
463
|
+
'',
|
|
464
|
+
'Say three or four things over a whole build, not one per step:',
|
|
465
|
+
' - Open with what you are going to make and how, in one line, before anything',
|
|
466
|
+
' else: "I will build Tide as a single HTML file - markup, one stylesheet, one',
|
|
467
|
+
' module - with the tasks kept in localStorage."',
|
|
468
|
+
' - One line when you move between the big pieces of work: "The layout is done,',
|
|
469
|
+
' now the animations."',
|
|
470
|
+
' - One line at the end saying what it does and how to try it.',
|
|
471
|
+
'That is all. A line before every tool call is not narration, it is noise: the',
|
|
472
|
+
'steps already show on screen, and repeating them in words buries the few',
|
|
473
|
+
'sentences worth reading.',
|
|
468
474
|
'',
|
|
469
475
|
'FIRST, EVERY TIME: write one short line saying what you are about to do, then',
|
|
470
476
|
'make the tool calls. Never open a turn with a tool call and no words. Examples:',
|
|
@@ -931,6 +937,7 @@ export class Agent {
|
|
|
931
937
|
|
|
932
938
|
this.busy = true;
|
|
933
939
|
this.abort = new AbortController();
|
|
940
|
+
this.endedSilently = false;
|
|
934
941
|
|
|
935
942
|
const turnStarted = Date.now();
|
|
936
943
|
let finished = false;
|
|
@@ -956,6 +963,7 @@ export class Agent {
|
|
|
956
963
|
this.stats.workMs += Date.now() - turnStarted;
|
|
957
964
|
this.stats.turns++;
|
|
958
965
|
if (!finished) this.closeInterrupted();
|
|
966
|
+
const ok = finished && !this.endedSilently;
|
|
959
967
|
this.busy = false;
|
|
960
968
|
this.abort = null;
|
|
961
969
|
this.ui.stopSpinner();
|
|
@@ -966,7 +974,7 @@ export class Agent {
|
|
|
966
974
|
if (finished) this.openWhenReady(turnStarted);
|
|
967
975
|
// Last, so "Done" is the last thing that happens rather than the last
|
|
968
976
|
// thing said before several more things happen.
|
|
969
|
-
this.ui.turnEnd?.({ ok
|
|
977
|
+
this.ui.turnEnd?.({ ok });
|
|
970
978
|
}
|
|
971
979
|
}
|
|
972
980
|
|
|
@@ -1163,14 +1171,21 @@ export class Agent {
|
|
|
1163
1171
|
this.push({
|
|
1164
1172
|
role: 'user',
|
|
1165
1173
|
content:
|
|
1166
|
-
'You
|
|
1167
|
-
'
|
|
1174
|
+
'You stopped without saying anything. If the thing I asked for is not ' +
|
|
1175
|
+
'built yet, carry on and build it. If it is, tell me in one or two ' +
|
|
1176
|
+
'sentences what it does and how to try it. No preamble, no diffs.',
|
|
1168
1177
|
});
|
|
1169
1178
|
continue;
|
|
1170
1179
|
}
|
|
1171
1180
|
|
|
1172
1181
|
this.push({ role: 'assistant', content: reply.text });
|
|
1173
|
-
if (!reply.text?.trim())
|
|
1182
|
+
if (!reply.text?.trim()) {
|
|
1183
|
+
// Silence after being asked to speak is not a finished turn. Saying
|
|
1184
|
+
// "Done" here is the worst thing available: the user believes it,
|
|
1185
|
+
// looks, and finds the thing they asked for was never built.
|
|
1186
|
+
this.ui.note('the model stopped without saying anything — the work may be unfinished');
|
|
1187
|
+
this.endedSilently = true;
|
|
1188
|
+
}
|
|
1174
1189
|
return;
|
|
1175
1190
|
}
|
|
1176
1191
|
|
package/src/tools/index.js
CHANGED
|
@@ -639,7 +639,11 @@ export function describe(name, args = {}) {
|
|
|
639
639
|
case 'deploy':
|
|
640
640
|
return `Deploying ${clip(args.folder || '.', 30)} to Vercel`;
|
|
641
641
|
case 'create_app':
|
|
642
|
-
|
|
642
|
+
// Say which starter it actually is. Hardcoding one of them meant a plain
|
|
643
|
+
// HTML app announced itself as Next.js, which is a line that is simply
|
|
644
|
+
// untrue on screen while the opposite happens on disk.
|
|
645
|
+
return `Creating ${clip(args.name || args.folder, 30)} from the ` +
|
|
646
|
+
`${args.template === 'plain-html' ? 'HTML' : 'Next.js'} starter`;
|
|
643
647
|
case 'look_at_app':
|
|
644
648
|
return `Looking at ${clip(args.url, 40)} on a phone and a desktop`;
|
|
645
649
|
case 'web_search':
|
package/src/ui/activity.js
CHANGED
|
@@ -192,9 +192,12 @@ export function fitActivity({ glyph, label = '', meta = [], hint = '', paint = d
|
|
|
192
192
|
* theme; the rest dim, because it is a footnote to the answer above it rather
|
|
193
193
|
* than something to read first.
|
|
194
194
|
*/
|
|
195
|
-
export function doneLine(ms, steps) {
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
export function doneLine(ms, steps, { ok = true } = {}) {
|
|
196
|
+
// The step count is bookkeeping: it tells the reader nothing about whether
|
|
197
|
+
// the thing they asked for exists. /stats has it for anyone who wants it.
|
|
198
|
+
void steps;
|
|
199
|
+
if (!ok) return `${theme.warn('!')} ${dim(`Stopped after ${formatDuration(ms)} without finishing`)}`;
|
|
200
|
+
return `${theme.ok('✓')} ${dim(`Done in ${formatDuration(ms)}`)}`;
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
/** The step count, brighter for a moment right after it goes up. */
|
package/src/ui/plain.js
CHANGED
|
@@ -262,7 +262,7 @@ export class Plain {
|
|
|
262
262
|
turnEnd({ ok = true } = {}) {
|
|
263
263
|
const t = this.turn;
|
|
264
264
|
this.turn = null;
|
|
265
|
-
if (t &&
|
|
265
|
+
if (t && Date.now() - t.start >= 2000) this.write(` ${doneLine(Date.now() - t.start, t.steps, { ok })}`);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// -- input ---------------------------------------------------------------
|
package/src/ui/screen.js
CHANGED
|
@@ -571,7 +571,9 @@ export class Screen {
|
|
|
571
571
|
if (line.length < 12) return; // "Okay." tells nobody anything
|
|
572
572
|
|
|
573
573
|
this.openedWith = line;
|
|
574
|
-
this
|
|
574
|
+
// Full strength: this is the model talking, and it is the thing on the page
|
|
575
|
+
// worth reading. The dimmed lines around it are the machinery.
|
|
576
|
+
this.push(' ' + chalk.white(clip(line, Math.max(30, this.width() - 6))));
|
|
575
577
|
}
|
|
576
578
|
|
|
577
579
|
thinkingEnd() {
|
|
@@ -910,7 +912,9 @@ export class Screen {
|
|
|
910
912
|
const a = this.activity;
|
|
911
913
|
this.activity = null;
|
|
912
914
|
if (!this.status.busy) this.stopTimer();
|
|
913
|
-
|
|
915
|
+
// A turn that stopped without finishing says so. Dropping the line entirely
|
|
916
|
+
// left the transcript looking like the work was still going.
|
|
917
|
+
if (a && Date.now() - a.start >= 2000) this.push(` ${doneLine(Date.now() - a.start, a.steps, { ok })}`);
|
|
914
918
|
this.paintStatus();
|
|
915
919
|
}
|
|
916
920
|
|