@runuai/host 0.9.146 → 0.9.147
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/lib/agent-cli.ts +17 -2
- package/lib/stopped-task-status-queue.ts +24 -6
- package/package.json +1 -1
- package/src/main.ts +16 -6
- package/src/protocol.ts +15 -3
package/lib/agent-cli.ts
CHANGED
|
@@ -305,7 +305,22 @@ async function main() {
|
|
|
305
305
|
case "memory search": {
|
|
306
306
|
const q = pos.join(" ") || flags.q || "";
|
|
307
307
|
const author = flags.as ? "&author=" + encodeURIComponent(flags.as) : "";
|
|
308
|
-
|
|
308
|
+
const limit = flags.limit ? "&limit=" + encodeURIComponent(String(flags.limit)) : "";
|
|
309
|
+
const res = await api(
|
|
310
|
+
"GET",
|
|
311
|
+
"/api/agent/memory?q=" + encodeURIComponent(q) + author + limit,
|
|
312
|
+
);
|
|
313
|
+
out(res.memories);
|
|
314
|
+
// Truncation goes to stderr so it cannot corrupt piped JSON, and it is
|
|
315
|
+
// stated rather than implied: recalling across tasks means reading the
|
|
316
|
+
// WHOLE store, and a short page that says nothing reads as all there is.
|
|
317
|
+
// NOTE: this file is a template literal. No backticks, no \${} in here.
|
|
318
|
+
if (res.truncated) {
|
|
319
|
+
console.error(
|
|
320
|
+
"uai: showing " + res.memories.length + " of " + res.total +
|
|
321
|
+
" — re-run with --limit all for every row.",
|
|
322
|
+
);
|
|
323
|
+
}
|
|
309
324
|
break;
|
|
310
325
|
}
|
|
311
326
|
case "memory save": {
|
|
@@ -453,7 +468,7 @@ async function main() {
|
|
|
453
468
|
" uai task list",
|
|
454
469
|
" uai task create --name <n> --prompt <p> [--projects id,id] [--team id] [--agents handle,handle]",
|
|
455
470
|
' uai task close --status finished|canceled [--reason "final report"]',
|
|
456
|
-
" uai memory search <query>",
|
|
471
|
+
" uai memory search <query> [--as handle] [--limit N|all]",
|
|
457
472
|
" uai memory save <text> [--project id] [--tags a,b]",
|
|
458
473
|
" uai memory delete <id>",
|
|
459
474
|
" uai todo list",
|
|
@@ -4,8 +4,21 @@
|
|
|
4
4
|
* mirror remains the authority at flush time so a later Resume wins over an
|
|
5
5
|
* older queued stop.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Why the host stopped the task, when the host knows.
|
|
9
|
+
*
|
|
10
|
+
* `"manual"` is ADR-028's local-UI Stop — a person at the machine pressed it.
|
|
11
|
+
* Everything else the host reports through this frame is automatic (writable
|
|
12
|
+
* -container quarantine during a runtime activation or a shared-volume
|
|
13
|
+
* repair), and carries no reason. The cloud needs the difference because
|
|
14
|
+
* ADR-144 lets a collaborator resume a task that stopped on its own but not
|
|
15
|
+
* one a human deliberately paused, and until now both arrived as the same
|
|
16
|
+
* three fields.
|
|
17
|
+
*/
|
|
18
|
+
export type StoppedTaskReason = "manual";
|
|
19
|
+
|
|
7
20
|
export class StoppedTaskStatusQueue {
|
|
8
|
-
private readonly pending = new
|
|
21
|
+
private readonly pending = new Map<string, StoppedTaskReason | undefined>();
|
|
9
22
|
|
|
10
23
|
constructor(
|
|
11
24
|
private readonly statusForTask: (
|
|
@@ -13,8 +26,13 @@ export class StoppedTaskStatusQueue {
|
|
|
13
26
|
) => string | null | undefined,
|
|
14
27
|
) {}
|
|
15
28
|
|
|
16
|
-
enqueue(taskId: string): void {
|
|
17
|
-
|
|
29
|
+
enqueue(taskId: string, reason?: StoppedTaskReason): void {
|
|
30
|
+
// A reason, once known, STICKS across a re-enqueue. The same task can be
|
|
31
|
+
// queued twice before a flush — an operator Stop, then a quarantine sweep
|
|
32
|
+
// that finds its container already down — and the automatic pass arriving
|
|
33
|
+
// second does not make the human's decision retroactively automatic.
|
|
34
|
+
// Dropping it would silently hand the task back to every collaborator.
|
|
35
|
+
this.pending.set(taskId, reason ?? this.pending.get(taskId));
|
|
18
36
|
}
|
|
19
37
|
|
|
20
38
|
/** A failed taskUp may have stopped a legacy runtime before recreation lost
|
|
@@ -35,9 +53,9 @@ export class StoppedTaskStatusQueue {
|
|
|
35
53
|
return true;
|
|
36
54
|
}
|
|
37
55
|
|
|
38
|
-
flush(publish: (taskId: string) => void): void {
|
|
39
|
-
for (const taskId of [...this.pending]) {
|
|
40
|
-
if (this.statusForTask(taskId) === "stopped") publish(taskId);
|
|
56
|
+
flush(publish: (taskId: string, reason?: StoppedTaskReason) => void): void {
|
|
57
|
+
for (const [taskId, reason] of [...this.pending]) {
|
|
58
|
+
if (this.statusForTask(taskId) === "stopped") publish(taskId, reason);
|
|
41
59
|
this.pending.delete(taskId);
|
|
42
60
|
}
|
|
43
61
|
}
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -28,7 +28,10 @@ import {
|
|
|
28
28
|
getHostTask,
|
|
29
29
|
hasInFlightHostTaskLifecycle,
|
|
30
30
|
} from "../lib/runtime-state";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
StoppedTaskStatusQueue,
|
|
33
|
+
type StoppedTaskReason,
|
|
34
|
+
} from "../lib/stopped-task-status-queue";
|
|
32
35
|
import {
|
|
33
36
|
getOrchestrator,
|
|
34
37
|
quarantineWritableRuntimeContainers,
|
|
@@ -866,7 +869,11 @@ async function startLocalUi(): Promise<void> {
|
|
|
866
869
|
invalidateMachineTunnelRoute(taskId);
|
|
867
870
|
try {
|
|
868
871
|
const result = await getOrchestrator().stopTask(taskId);
|
|
869
|
-
|
|
872
|
+
// A PERSON pressed Stop on this machine's own UI — ADR-028. Labelled
|
|
873
|
+
// as such because the cloud otherwise cannot tell it apart from the
|
|
874
|
+
// quarantine sweeps below, which reach the bridge through this same
|
|
875
|
+
// frame, and ADR-144 turns that difference into who may undo it.
|
|
876
|
+
if (result.ok) queueStoppedTaskStatus(taskId, "manual");
|
|
870
877
|
return result;
|
|
871
878
|
} finally {
|
|
872
879
|
invalidateMachineTunnelRoute(taskId);
|
|
@@ -1042,15 +1049,18 @@ function sendCapabilities(): void {
|
|
|
1042
1049
|
|
|
1043
1050
|
/** Keep a rolling-isolation/local-UI stop from remaining cloud-ghost-running
|
|
1044
1051
|
* merely because the bridge was between connections at the transition. */
|
|
1045
|
-
function queueStoppedTaskStatus(
|
|
1046
|
-
|
|
1052
|
+
function queueStoppedTaskStatus(
|
|
1053
|
+
taskId: string,
|
|
1054
|
+
reason?: StoppedTaskReason,
|
|
1055
|
+
): void {
|
|
1056
|
+
pendingRuntimeIsolationStatuses.enqueue(taskId, reason);
|
|
1047
1057
|
flushStoppedTaskStatuses();
|
|
1048
1058
|
}
|
|
1049
1059
|
|
|
1050
1060
|
function flushStoppedTaskStatuses(socket: WebSocket | null = ws): void {
|
|
1051
1061
|
if (!socket || socket.readyState !== WebSocket.OPEN) return;
|
|
1052
|
-
pendingRuntimeIsolationStatuses.flush((taskId) => {
|
|
1053
|
-
send(socket, { kind: "task.status", taskId, status: "stopped" });
|
|
1062
|
+
pendingRuntimeIsolationStatuses.flush((taskId, reason) => {
|
|
1063
|
+
send(socket, { kind: "task.status", taskId, status: "stopped", reason });
|
|
1054
1064
|
});
|
|
1055
1065
|
}
|
|
1056
1066
|
|
package/src/protocol.ts
CHANGED
|
@@ -1812,9 +1812,21 @@ export type HostToCloud =
|
|
|
1812
1812
|
// ADR-103: `seq` is the outbox sequence number (monotonic within a bootId).
|
|
1813
1813
|
// Absent from pre-103 hosts; the cloud dedups replay overlap by it.
|
|
1814
1814
|
| { kind: "event"; event: HostEvent; seq?: number }
|
|
1815
|
-
// Host-pushed task lifecycle
|
|
1816
|
-
//
|
|
1817
|
-
|
|
1815
|
+
// Host-pushed task lifecycle: the cloud mirrors the status (currently
|
|
1816
|
+
// "stopped"). TWO different events arrive through this one frame — ADR-028's
|
|
1817
|
+
// local-UI Stop, where the host operator paused the task's containers, and
|
|
1818
|
+
// the automatic writable-container quarantine a runtime activation or
|
|
1819
|
+
// shared-volume repair performs. `reason: "manual"` is the first; its
|
|
1820
|
+
// absence is the second.
|
|
1821
|
+
//
|
|
1822
|
+
// The cloud cannot infer it. ADR-144 lets a collaborator resume a task that
|
|
1823
|
+
// stopped on its own and not one a human deliberately paused, and with both
|
|
1824
|
+
// stops indistinguishable on the wire the deliberate one was reversible by
|
|
1825
|
+
// anyone on the task. OPTIONAL so an older host stays compatible: it sends
|
|
1826
|
+
// no reason, the cloud records none, and that host's operator Stop keeps the
|
|
1827
|
+
// pre-ADR-144 behaviour rather than mislabelling its quarantines as manual
|
|
1828
|
+
// and blocking the recovery path they exist to enable.
|
|
1829
|
+
| { kind: "task.status"; taskId: string; status: string; reason?: string }
|
|
1818
1830
|
| { kind: "ping"; ts: number }
|
|
1819
1831
|
// ADR-133: a sidecar asks for (or refreshes) its task-scoped AWS lease.
|
|
1820
1832
|
| { kind: "lease.request"; opId: string; taskId: string }
|