ucode-agent 1.18.0 → 1.19.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 +13 -4
- 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 +3 -1
- package/ucode.js +7 -1
package/package.json
CHANGED
package/src/core/loop.js
CHANGED
|
@@ -937,6 +937,7 @@ export class Agent {
|
|
|
937
937
|
|
|
938
938
|
this.busy = true;
|
|
939
939
|
this.abort = new AbortController();
|
|
940
|
+
this.endedSilently = false;
|
|
940
941
|
|
|
941
942
|
const turnStarted = Date.now();
|
|
942
943
|
let finished = false;
|
|
@@ -962,6 +963,7 @@ export class Agent {
|
|
|
962
963
|
this.stats.workMs += Date.now() - turnStarted;
|
|
963
964
|
this.stats.turns++;
|
|
964
965
|
if (!finished) this.closeInterrupted();
|
|
966
|
+
const ok = finished && !this.endedSilently;
|
|
965
967
|
this.busy = false;
|
|
966
968
|
this.abort = null;
|
|
967
969
|
this.ui.stopSpinner();
|
|
@@ -972,7 +974,7 @@ export class Agent {
|
|
|
972
974
|
if (finished) this.openWhenReady(turnStarted);
|
|
973
975
|
// Last, so "Done" is the last thing that happens rather than the last
|
|
974
976
|
// thing said before several more things happen.
|
|
975
|
-
this.ui.turnEnd?.({ ok
|
|
977
|
+
this.ui.turnEnd?.({ ok });
|
|
976
978
|
}
|
|
977
979
|
}
|
|
978
980
|
|
|
@@ -1169,14 +1171,21 @@ export class Agent {
|
|
|
1169
1171
|
this.push({
|
|
1170
1172
|
role: 'user',
|
|
1171
1173
|
content:
|
|
1172
|
-
'You
|
|
1173
|
-
'
|
|
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.',
|
|
1174
1177
|
});
|
|
1175
1178
|
continue;
|
|
1176
1179
|
}
|
|
1177
1180
|
|
|
1178
1181
|
this.push({ role: 'assistant', content: reply.text });
|
|
1179
|
-
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
|
+
}
|
|
1180
1189
|
return;
|
|
1181
1190
|
}
|
|
1182
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
|
@@ -912,7 +912,9 @@ export class Screen {
|
|
|
912
912
|
const a = this.activity;
|
|
913
913
|
this.activity = null;
|
|
914
914
|
if (!this.status.busy) this.stopTimer();
|
|
915
|
-
|
|
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 })}`);
|
|
916
918
|
this.paintStatus();
|
|
917
919
|
}
|
|
918
920
|
|
package/ucode.js
CHANGED
|
@@ -88,7 +88,13 @@ async function main() {
|
|
|
88
88
|
// have no way to know. So if there is something newer, it goes on now and
|
|
89
89
|
// this process hands over to it. The wait is a few seconds, once per
|
|
90
90
|
// release; every other launch pays one quick question to the registry.
|
|
91
|
-
|
|
91
|
+
//
|
|
92
|
+
// Only when a person is sitting there. Handing over means re-running this
|
|
93
|
+
// process, and a run whose input is a pipe has that input consumed by the
|
|
94
|
+
// handover — `cat prompt.txt | ucode` printed "updating…" and then did
|
|
95
|
+
// nothing at all. Piped runs take the background update and carry on.
|
|
96
|
+
if (!args.version && process.stdin.isTTY &&
|
|
97
|
+
process.argv[2] !== 'login' && process.argv[2] !== 'doctor') {
|
|
92
98
|
const { pendingUpdate, installNow } = await import('./src/core/updater.js');
|
|
93
99
|
const waiting = await pendingUpdate();
|
|
94
100
|
if (waiting) {
|