@fitchmultz/pi-subagents 0.50.0 → 0.51.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/CHANGELOG.md +51 -2
- package/README.md +36 -5
- package/dist/agent/contracts.js +6 -24
- package/dist/agent/host.js +3 -16
- package/dist/agent/index.js +207 -173
- package/dist/agent/journal-tail.js +115 -156
- package/dist/agent/launch.js +20 -8
- package/dist/agent/legacy.js +65 -23
- package/dist/agent/notifications.js +138 -47
- package/dist/agent/observation-state.js +246 -0
- package/dist/agent/observation-view.js +258 -0
- package/dist/agent/observation.js +346 -0
- package/dist/agent/observe-card.js +243 -0
- package/dist/agent/observe-chrome.js +85 -0
- package/dist/agent/observe-client.js +285 -0
- package/dist/agent/observe-layout.js +65 -0
- package/dist/agent/observe-reading.js +84 -0
- package/dist/agent/observe-screen.js +400 -96
- package/dist/agent/observe-transcript.js +382 -0
- package/dist/agent/observe.js +250 -43
- package/dist/agent/process.js +198 -25
- package/dist/agent/receipts.js +92 -20
- package/dist/agent/runner.js +125 -20
- package/dist/agent/shell.js +2 -1
- package/dist/agent/storage.js +29 -12
- package/dist/agent/tool-schemas.js +20 -0
- package/dist/agent/typebox.js +10 -5
- package/dist/agent/usage.js +0 -115
- package/dist/agent/view.js +46 -16
- package/dist/agent/wake.js +99 -0
- package/dist/build-info.js +1 -1
- package/dist/intercom-client.d.ts +26 -0
- package/dist/intercom-client.js +45 -0
- package/dist/pi-intercom/agent-dir.d.ts +1 -0
- package/dist/pi-intercom/bridge-session.js +2 -1
- package/dist/pi-intercom/broker/broker.js +27 -21
- package/dist/pi-intercom/broker/client-last.d.ts +13 -0
- package/dist/pi-intercom/broker/client-last.js +17 -5
- package/dist/pi-intercom/broker/client.d.ts +60 -0
- package/dist/pi-intercom/broker/client.js +47 -16
- package/dist/pi-intercom/broker/connection.d.ts +29 -0
- package/dist/pi-intercom/broker/framing.d.ts +17 -0
- package/dist/pi-intercom/broker/framing.js +10 -7
- package/dist/pi-intercom/broker/guard.js +23 -0
- package/dist/pi-intercom/broker/last.d.ts +26 -0
- package/dist/pi-intercom/broker/last.js +57 -32
- package/dist/pi-intercom/broker/paths.d.ts +13 -0
- package/dist/pi-intercom/broker/paths.js +35 -12
- package/dist/pi-intercom/broker/pid.js +5 -1
- package/dist/pi-intercom/broker/server-connection.js +19 -0
- package/dist/pi-intercom/broker/snapshot-reader.d.ts +9 -0
- package/dist/pi-intercom/broker/snapshots.d.ts +29 -0
- package/dist/pi-intercom/broker/targets.d.ts +9 -0
- package/dist/pi-intercom/connection.js +10 -2
- package/dist/pi-intercom/identity.js +6 -5
- package/dist/pi-intercom/inbound-delivery.js +16 -60
- package/dist/pi-intercom/inbound-journal.js +83 -6
- package/dist/pi-intercom/inbound-record.js +18 -1
- package/dist/pi-intercom/inbound.js +12 -38
- package/dist/pi-intercom/index.js +3 -0
- package/dist/pi-intercom/last-message.js +4 -16
- package/dist/pi-intercom/lifecycle.js +88 -3
- package/dist/pi-intercom/outbound.js +5 -15
- package/dist/pi-intercom/reply-tracker.js +7 -6
- package/dist/pi-intercom/reply-wait.js +2 -2
- package/dist/pi-intercom/session-targets.d.ts +24 -0
- package/dist/pi-intercom/tool-registration.js +4 -1
- package/dist/pi-intercom/tool-results.js +5 -7
- package/dist/pi-intercom/tool-schemas.js +12 -26
- package/dist/pi-intercom/types.d.ts +212 -0
- package/dist/pi-intercom/types.js +54 -3
- package/dist/pi-intercom/validation.d.ts +12 -0
- package/dist/pi-intercom/validation.js +3 -0
- package/dist/shared/bounded-json.js +151 -0
- package/dist/shared/compact-cards.js +66 -0
- package/dist/shared/journal-frames.js +3 -6
- package/dist/shared/json-projection.js +267 -129
- package/dist/shared/native-journal.js +123 -56
- package/dist/shared/post-exit-stdio-guard.js +120 -7
- package/dist/shared/tui-action.js +29 -0
- package/dist/shared/unknown.d.ts +9 -0
- package/docs/code-quality.md +2 -0
- package/docs/development.md +40 -1
- package/docs/intercom-bridge.md +9 -4
- package/docs/intercom.md +226 -6
- package/docs/performance.md +87 -11
- package/docs/reference.md +157 -20
- package/docs/retired-history-maintenance.md +252 -0
- package/package.json +28 -15
- package/scripts/archive-retired-history.mjs +464 -0
- package/scripts/build.mjs +48 -28
- package/scripts/measure-size.mjs +1 -1
- package/scripts/native-package-smoke.mjs +153 -12
- package/scripts/package-smoke.mjs +80 -11
- package/scripts/prepare.mjs +2 -2
- package/scripts/real-pi-live.mjs +377 -0
- package/scripts/real-pi-processes.mjs +6 -1
- package/scripts/real-pi-smoke.mjs +6 -282
- package/scripts/run-tests.mjs +4 -2
- package/scripts/typescript.mjs +35 -0
- package/skills/pi-intercom/SKILL.md +2 -1
- package/skills/pi-subagents/SKILL.md +23 -5
- package/src/agent/contracts.ts +8 -34
- package/src/agent/host.ts +19 -20
- package/src/agent/index.ts +224 -190
- package/src/agent/journal-tail.ts +141 -153
- package/src/agent/launch.ts +33 -9
- package/src/agent/legacy.ts +85 -31
- package/src/agent/notifications.ts +192 -67
- package/src/agent/observation-state.ts +277 -0
- package/src/agent/observation-view.ts +267 -0
- package/src/agent/observation.ts +405 -0
- package/src/agent/observe-card.ts +304 -0
- package/src/agent/observe-chrome.ts +170 -0
- package/src/agent/observe-client.ts +318 -0
- package/src/agent/observe-layout.ts +79 -0
- package/src/agent/observe-reading.ts +97 -0
- package/src/agent/observe-screen.ts +443 -129
- package/src/agent/observe-transcript.ts +434 -0
- package/src/agent/observe.ts +304 -71
- package/src/agent/process.ts +240 -34
- package/src/agent/receipts.ts +114 -24
- package/src/agent/runner.ts +125 -27
- package/src/agent/shell.ts +2 -1
- package/src/agent/storage.ts +27 -11
- package/src/agent/tool-schemas.ts +33 -0
- package/src/agent/typebox.ts +12 -7
- package/src/agent/usage.ts +0 -130
- package/src/agent/view.ts +70 -20
- package/src/agent/wake.ts +107 -0
- package/src/intercom-client.ts +108 -0
- package/src/pi-intercom/bridge-session.ts +2 -2
- package/src/pi-intercom/broker/broker.ts +27 -23
- package/src/pi-intercom/broker/client-last.ts +25 -12
- package/src/pi-intercom/broker/client.ts +55 -33
- package/src/pi-intercom/broker/framing.ts +11 -8
- package/src/pi-intercom/broker/guard.ts +33 -0
- package/src/pi-intercom/broker/last.ts +86 -33
- package/src/pi-intercom/broker/paths.ts +39 -12
- package/src/pi-intercom/broker/pid.ts +6 -1
- package/src/pi-intercom/broker/server-connection.ts +25 -0
- package/src/pi-intercom/connection.ts +21 -10
- package/src/pi-intercom/identity.ts +8 -5
- package/src/pi-intercom/inbound-delivery.ts +20 -66
- package/src/pi-intercom/inbound-journal.ts +103 -7
- package/src/pi-intercom/inbound-record.ts +21 -4
- package/src/pi-intercom/inbound.ts +18 -49
- package/src/pi-intercom/index.ts +3 -0
- package/src/pi-intercom/last-message.ts +10 -19
- package/src/pi-intercom/lifecycle.ts +117 -14
- package/src/pi-intercom/outbound.ts +7 -16
- package/src/pi-intercom/reply-tracker.ts +8 -6
- package/src/pi-intercom/reply-wait.ts +3 -2
- package/src/pi-intercom/runtime-types.ts +0 -1
- package/src/pi-intercom/tool-registration.ts +4 -1
- package/src/pi-intercom/tool-results.ts +10 -9
- package/src/pi-intercom/tool-schemas.ts +12 -44
- package/src/pi-intercom/types.ts +115 -4
- package/src/pi-intercom/validation.ts +4 -0
- package/src/shared/bounded-json.ts +162 -0
- package/src/shared/compact-cards.ts +118 -0
- package/src/shared/journal-frames.ts +3 -19
- package/src/shared/json-projection.ts +267 -147
- package/src/shared/native-journal.ts +157 -75
- package/src/shared/post-exit-stdio-guard.ts +165 -7
- package/src/shared/tui-action.ts +39 -0
- package/tsconfig.client.json +8 -0
- package/tsconfig.json +1 -0
- package/src/shared/stream-json.d.ts +0 -10
package/dist/agent/observe.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import { SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Container, TruncatedText } from "@earendil-works/pi-tui";
|
|
5
|
+
import { actionText } from "../shared/tui-action.js";
|
|
2
6
|
import { getPiAgentDir } from "../pi-intercom/agent-dir.js";
|
|
3
7
|
import { isChild, errorText } from "./host.js";
|
|
4
8
|
import { launchAt, list, ownerDirectory, runDirectory, read } from "./storage.js";
|
|
5
9
|
import { successorSchema } from "./contracts.js";
|
|
6
10
|
import { readCanonicalResult } from "./usage.js";
|
|
7
|
-
import { actor,
|
|
8
|
-
import {
|
|
11
|
+
import { actor, observeAlive } from "./process.js";
|
|
12
|
+
import { plainText } from "./journal-tail.js";
|
|
13
|
+
import { ObservationClient } from "./observe-client.js";
|
|
9
14
|
import { adoptedJournal } from "./saved.js";
|
|
10
15
|
const STATUS_KEY = "agent-observer";
|
|
11
16
|
class Observer {
|
|
@@ -13,9 +18,16 @@ class Observer {
|
|
|
13
18
|
owner = "";
|
|
14
19
|
base = "";
|
|
15
20
|
timer;
|
|
21
|
+
scanning;
|
|
22
|
+
rescan = false;
|
|
16
23
|
close;
|
|
17
24
|
refresh;
|
|
18
25
|
rows = new Map();
|
|
26
|
+
peers = new Map();
|
|
27
|
+
watcher;
|
|
28
|
+
widgetVisible = false;
|
|
29
|
+
widgetSignature = "";
|
|
30
|
+
widgetRender;
|
|
19
31
|
launches = new Map();
|
|
20
32
|
finished = new Set();
|
|
21
33
|
pending = [];
|
|
@@ -27,10 +39,22 @@ class Observer {
|
|
|
27
39
|
}
|
|
28
40
|
dispose() {
|
|
29
41
|
clearInterval(this.timer);
|
|
42
|
+
this.timer = undefined;
|
|
43
|
+
this.scanning = undefined;
|
|
44
|
+
this.rescan = false;
|
|
45
|
+
this.watcher?.close();
|
|
46
|
+
this.watcher = undefined;
|
|
47
|
+
for (const peer of this.peers.values()) {
|
|
48
|
+
peer.client.close();
|
|
49
|
+
}
|
|
50
|
+
this.peers.clear();
|
|
51
|
+
this.widgetVisible = false;
|
|
52
|
+
this.widgetSignature = "";
|
|
53
|
+
this.widgetRender = undefined;
|
|
30
54
|
this.close?.();
|
|
31
55
|
this.close = undefined;
|
|
32
56
|
this.refresh = undefined;
|
|
33
|
-
this.ctx?.ui.
|
|
57
|
+
this.ctx?.ui.setWidget(STATUS_KEY, undefined);
|
|
34
58
|
this.ctx = undefined;
|
|
35
59
|
this.rows.clear();
|
|
36
60
|
this.launches.clear();
|
|
@@ -49,57 +73,75 @@ class Observer {
|
|
|
49
73
|
this.ctx = ctx;
|
|
50
74
|
this.owner = ctx.sessionManager.getSessionId();
|
|
51
75
|
this.base = ownerDirectory(path.resolve(getPiAgentDir()), this.owner);
|
|
76
|
+
fs.mkdirSync(this.base, { recursive: true, mode: 0o700 });
|
|
77
|
+
try {
|
|
78
|
+
const watcher = fs.watch(this.base, { recursive: true }, (_event, file) => {
|
|
79
|
+
if (this.watcher === watcher &&
|
|
80
|
+
this.timer === undefined &&
|
|
81
|
+
(file === null || /(?:launch|result|successor|process)\.json$/.test(file))) {
|
|
82
|
+
this.scan();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.watcher = watcher;
|
|
86
|
+
watcher.on("error", () => {
|
|
87
|
+
if (this.watcher !== watcher) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
watcher.close();
|
|
91
|
+
this.watcher = undefined;
|
|
92
|
+
this.scan();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* Sparse native execution events still wake inventory without an idle poll. */
|
|
97
|
+
}
|
|
52
98
|
this.scan();
|
|
53
|
-
this.timer = setInterval(() => this.scan(), 1000);
|
|
54
|
-
this.timer.unref();
|
|
55
99
|
}
|
|
56
100
|
launch(id) {
|
|
57
101
|
const launch = this.launches.get(id) ?? launchAt(this.base, id, this.owner);
|
|
58
102
|
this.launches.set(id, launch);
|
|
59
103
|
return launch;
|
|
60
104
|
}
|
|
61
|
-
|
|
62
|
-
const cached = roots.get(launch.rootId);
|
|
63
|
-
if (cached !== undefined) {
|
|
64
|
-
return cached;
|
|
65
|
-
}
|
|
66
|
-
const identity = actor(this.base, launch);
|
|
67
|
-
const live = identity !== undefined && alive(identity);
|
|
68
|
-
roots.set(launch.rootId, live);
|
|
69
|
-
return live;
|
|
70
|
-
}
|
|
105
|
+
/** Reads durable metadata now; unfinished rows commit after their root's batched liveness. */
|
|
71
106
|
load(id, roots) {
|
|
72
107
|
if (this.finished.has(id)) {
|
|
73
|
-
return;
|
|
108
|
+
return undefined;
|
|
74
109
|
}
|
|
75
110
|
const launch = this.launch(id);
|
|
76
111
|
const result = readCanonicalResult(this.base, launch);
|
|
77
112
|
const finished = result !== undefined || this.hasSuccessor(id);
|
|
78
|
-
const live = !finished && this.live(launch, roots);
|
|
79
113
|
if (finished) {
|
|
80
114
|
this.finished.add(id);
|
|
81
115
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
live
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
else if (!roots.has(launch.rootId)) {
|
|
117
|
+
roots.set(launch.rootId, actor(this.base, launch));
|
|
118
|
+
}
|
|
119
|
+
return (live) => {
|
|
120
|
+
const running = !finished && live.get(launch.rootId) === true;
|
|
121
|
+
this.rows.set(id, {
|
|
122
|
+
id,
|
|
123
|
+
name: plainText(launch.name ?? id, 120),
|
|
124
|
+
status: result?.status ?? (running ? "running" : "interrupted"),
|
|
125
|
+
live: running,
|
|
126
|
+
createdAt: launch.createdAt,
|
|
127
|
+
activity: this.peers.get(id)?.client.activity,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
89
130
|
}
|
|
90
131
|
hasSuccessor(id) {
|
|
91
132
|
return (read(path.join(runDirectory(this.base, id), "successor.json"), successorSchema) !== undefined);
|
|
92
133
|
}
|
|
93
134
|
loadVisible(id, roots) {
|
|
94
135
|
try {
|
|
95
|
-
this.load(id, roots);
|
|
136
|
+
return this.load(id, roots);
|
|
96
137
|
}
|
|
97
138
|
catch (error) {
|
|
98
139
|
this.rows.set(id, { id, name: id, status: "unavailable", live: false, createdAt: 0 });
|
|
99
140
|
this.error = plainText(errorText(error));
|
|
141
|
+
return undefined;
|
|
100
142
|
}
|
|
101
143
|
}
|
|
102
|
-
|
|
144
|
+
loadBatch(roots) {
|
|
103
145
|
try {
|
|
104
146
|
const ids = list(this.base);
|
|
105
147
|
const present = new Set(ids);
|
|
@@ -118,14 +160,161 @@ class Observer {
|
|
|
118
160
|
this.pending = ids.filter((id) => !this.finished.has(id));
|
|
119
161
|
}
|
|
120
162
|
this.error = "";
|
|
121
|
-
|
|
122
|
-
this.pending.splice(0, 50).forEach((id) => this.loadVisible(id, roots));
|
|
163
|
+
return this.pending.splice(0, 50).flatMap((id) => this.loadVisible(id, roots) ?? []);
|
|
123
164
|
}
|
|
124
165
|
catch (error) {
|
|
125
166
|
this.error = plainText(errorText(error));
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** One scan at a time; triggers during a scan coalesce into one follow-up. */
|
|
171
|
+
scan() {
|
|
172
|
+
if (this.scanning !== undefined) {
|
|
173
|
+
this.rescan = true;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const scanning = this.update()
|
|
177
|
+
.catch((error) => {
|
|
178
|
+
this.error = plainText(errorText(error));
|
|
179
|
+
})
|
|
180
|
+
.finally(() => {
|
|
181
|
+
if (this.scanning !== scanning) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.scanning = undefined;
|
|
185
|
+
if (this.rescan) {
|
|
186
|
+
this.rescan = false;
|
|
187
|
+
this.scan();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
this.scanning = scanning;
|
|
191
|
+
}
|
|
192
|
+
async update() {
|
|
193
|
+
const ctx = this.ctx;
|
|
194
|
+
const roots = new Map();
|
|
195
|
+
const commits = this.loadBatch(roots);
|
|
196
|
+
const claims = [...roots].flatMap(([root, identity]) => (identity ? [{ root, identity }] : []));
|
|
197
|
+
const verdicts = await observeAlive(claims.map(({ identity }) => identity));
|
|
198
|
+
if (this.ctx !== ctx) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const live = new Map(claims.map(({ root }, index) => [root, verdicts[index]]));
|
|
202
|
+
for (const commit of commits) {
|
|
203
|
+
commit(live);
|
|
126
204
|
}
|
|
127
|
-
this.
|
|
205
|
+
this.activity();
|
|
206
|
+
this.widget();
|
|
128
207
|
this.refresh?.();
|
|
208
|
+
const periodic = this.pending.length > 0 || [...this.rows.values()].some((row) => row.live);
|
|
209
|
+
if (periodic && !this.timer) {
|
|
210
|
+
this.timer = setInterval(() => this.scan(), 1000);
|
|
211
|
+
this.timer.unref();
|
|
212
|
+
}
|
|
213
|
+
else if (!periodic) {
|
|
214
|
+
clearInterval(this.timer);
|
|
215
|
+
this.timer = undefined;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
activity() {
|
|
219
|
+
const visible = this.strip()
|
|
220
|
+
.filter((entry) => entry.live)
|
|
221
|
+
.slice(0, 4);
|
|
222
|
+
const ids = new Set(visible.map((entry) => entry.id));
|
|
223
|
+
for (const [id, peer] of this.peers) {
|
|
224
|
+
if (!ids.has(id) || peer.client.closed) {
|
|
225
|
+
peer.client.close();
|
|
226
|
+
this.peers.delete(id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
for (const row of visible) {
|
|
230
|
+
if (this.peers.has(row.id)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const launch = this.launch(row.id);
|
|
235
|
+
const identity = actor(this.base, launch);
|
|
236
|
+
if (!identity) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const client = new ObservationClient({ token: identity.token, rootId: launch.rootId, sessionId: launch.sessionId }, "activity", () => {
|
|
240
|
+
if (this.peers.get(row.id)?.client !== client) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const current = this.rows.get(row.id);
|
|
244
|
+
if (current) {
|
|
245
|
+
current.activity = client.connected ? client.activity : "Reconnecting";
|
|
246
|
+
}
|
|
247
|
+
this.widget();
|
|
248
|
+
});
|
|
249
|
+
this.peers.set(row.id, { token: identity.token, client });
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// Older or not-yet-ready actors still have a known run status, but no live phase.
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
widget() {
|
|
257
|
+
const ctx = this.ctx;
|
|
258
|
+
if (!ctx) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const rows = this.strip();
|
|
262
|
+
if (rows.length === 0 && this.rows.size === this.total && this.error.length === 0) {
|
|
263
|
+
if (this.widgetVisible) {
|
|
264
|
+
ctx.ui.setWidget(STATUS_KEY, undefined);
|
|
265
|
+
}
|
|
266
|
+
this.widgetVisible = false;
|
|
267
|
+
this.widgetSignature = "";
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const signature = JSON.stringify([this.status(), rows.slice(0, 4), rows.length]);
|
|
271
|
+
if (signature === this.widgetSignature) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
this.widgetSignature = signature;
|
|
275
|
+
if (this.widgetVisible) {
|
|
276
|
+
this.widgetRender?.();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.widgetVisible = true;
|
|
280
|
+
ctx.ui.setWidget(STATUS_KEY, (tui, theme) => {
|
|
281
|
+
this.widgetRender = () => {
|
|
282
|
+
tui.requestRender();
|
|
283
|
+
};
|
|
284
|
+
const content = new Container();
|
|
285
|
+
const open = (id) => {
|
|
286
|
+
if (ctx !== this.ctx) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
void this.show(id, ctx).catch((error) => {
|
|
290
|
+
ctx.ui.notify(errorText(error), "error");
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
return {
|
|
294
|
+
render: (width) => {
|
|
295
|
+
const strip = this.strip();
|
|
296
|
+
const visible = strip.slice(0, 4);
|
|
297
|
+
content.clear();
|
|
298
|
+
content.addChild(actionText(theme.fg("accent", this.status()), () => open("")));
|
|
299
|
+
for (const row of visible) {
|
|
300
|
+
const glyph = row.live ? "●" : "○";
|
|
301
|
+
const color = row.live ? "accent" : "warning";
|
|
302
|
+
content.addChild(actionText(`${theme.fg(color, glyph)} ${theme.bold(row.name)} · ${row.activity ?? row.status}`, () => open(row.id)));
|
|
303
|
+
}
|
|
304
|
+
if (strip.length > visible.length) {
|
|
305
|
+
content.addChild(actionText(theme.fg("dim", `+${strip.length - visible.length} more · /agents`), () => open("")));
|
|
306
|
+
}
|
|
307
|
+
if (this.error.length > 0) {
|
|
308
|
+
content.addChild(new TruncatedText(theme.fg("warning", this.error), 0, 0));
|
|
309
|
+
}
|
|
310
|
+
return content.render(width);
|
|
311
|
+
},
|
|
312
|
+
handleMouse: (event) => content.handleMouse(event),
|
|
313
|
+
invalidate: () => {
|
|
314
|
+
// Render reads current rows, expansion and theme on every frame.
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}, { placement: "aboveEditor" });
|
|
129
318
|
}
|
|
130
319
|
status() {
|
|
131
320
|
const active = [...this.rows.values()].filter((row) => row.live).length;
|
|
@@ -134,7 +323,9 @@ class Observer {
|
|
|
134
323
|
return `Agents: ${active}${checking ? "+" : ""} active${checking ? " (checking…)" : ""} · ${this.total} runs${error} · /agents`;
|
|
135
324
|
}
|
|
136
325
|
inventory() {
|
|
137
|
-
return [...this.rows.values()]
|
|
326
|
+
return [...this.rows.values()]
|
|
327
|
+
.filter((row) => row.status !== "completed")
|
|
328
|
+
.sort((a, b) => {
|
|
138
329
|
const order = Number(b.live) - Number(a.live);
|
|
139
330
|
if (order !== 0) {
|
|
140
331
|
return order;
|
|
@@ -143,6 +334,10 @@ class Observer {
|
|
|
143
334
|
return created !== 0 ? created : a.id.localeCompare(b.id);
|
|
144
335
|
});
|
|
145
336
|
}
|
|
337
|
+
/** The always-visible strip shows active work and unreadable runs; /agents keeps history. */
|
|
338
|
+
strip() {
|
|
339
|
+
return this.inventory().filter((row) => row.live || row.status === "unavailable");
|
|
340
|
+
}
|
|
146
341
|
watch(id) {
|
|
147
342
|
const matches = list(this.base).filter((candidate) => candidate.startsWith(id));
|
|
148
343
|
const exact = matches.includes(id);
|
|
@@ -171,11 +366,14 @@ class Observer {
|
|
|
171
366
|
!adoptedJournal(this.base, launch)) {
|
|
172
367
|
throw new Error("Child journal path is outside its saved conversation.");
|
|
173
368
|
}
|
|
369
|
+
const identity = actor(this.base, launch);
|
|
174
370
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
371
|
+
launch,
|
|
372
|
+
identity: identity && {
|
|
373
|
+
token: identity.token,
|
|
374
|
+
rootId: launch.rootId,
|
|
375
|
+
sessionId: launch.sessionId,
|
|
376
|
+
},
|
|
179
377
|
};
|
|
180
378
|
}
|
|
181
379
|
async show(args, ctx) {
|
|
@@ -193,6 +391,7 @@ class Observer {
|
|
|
193
391
|
const screen = new ObserverScreen({
|
|
194
392
|
data: {
|
|
195
393
|
inventory: () => this.inventory(),
|
|
394
|
+
checking: () => this.rows.size < this.total,
|
|
196
395
|
row: (id) => this.rows.get(id),
|
|
197
396
|
status: () => this.status(),
|
|
198
397
|
error: () => this.error,
|
|
@@ -203,24 +402,32 @@ class Observer {
|
|
|
203
402
|
keys,
|
|
204
403
|
done: () => done(true),
|
|
205
404
|
selected: args.trim(),
|
|
405
|
+
hideThinking: SettingsManager.create(ctx.cwd, getPiAgentDir()).getHideThinkingBlock(),
|
|
206
406
|
});
|
|
207
407
|
this.close = () => done(true);
|
|
208
408
|
this.refresh = () => screen.update();
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
screen.dispose();
|
|
215
|
-
this.close = undefined;
|
|
216
|
-
this.refresh = undefined;
|
|
217
|
-
},
|
|
409
|
+
const dispose = screen.dispose.bind(screen);
|
|
410
|
+
screen.dispose = () => {
|
|
411
|
+
dispose();
|
|
412
|
+
this.close = undefined;
|
|
413
|
+
this.refresh = undefined;
|
|
218
414
|
};
|
|
415
|
+
return screen;
|
|
416
|
+
}, {
|
|
417
|
+
overlay: true,
|
|
418
|
+
overlayOptions: { width: "100%", maxHeight: "100%", anchor: "top-left", margin: 0 },
|
|
219
419
|
});
|
|
220
420
|
}
|
|
221
421
|
register() {
|
|
222
422
|
this.pi.on("session_start", (_event, ctx) => this.attach(ctx));
|
|
223
423
|
this.pi.on("session_shutdown", () => this.dispose());
|
|
424
|
+
this.pi.on("tool_execution_end", (event) => {
|
|
425
|
+
if (this.ctx &&
|
|
426
|
+
this.watcher === undefined &&
|
|
427
|
+
(event.toolName === "delegate" || event.toolName === "agent_runs")) {
|
|
428
|
+
this.scan();
|
|
429
|
+
}
|
|
430
|
+
});
|
|
224
431
|
this.pi.registerCommand("agents", {
|
|
225
432
|
description: "List this parent's children; /agents <run-id or unique 8+ prefix> watches native activity read-only",
|
|
226
433
|
handler: (args, ctx) => this.show(args, ctx),
|