@bastani/atomic 0.9.57 → 0.9.58-alpha.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/CHANGELOG.md +52 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +39 -5
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/index.bundle.mjs +2 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +42 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +885 -228
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +933 -175
- package/dist/builtin/workflows/src/index.js +630 -162
- package/dist/builtin/workflows/src/local-worker-entry.mjs +933 -175
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +1 -1
- package/dist/builtin/workflows/src/shared/workflow-infrastructure-error.d.ts +13 -0
- package/dist/builtin/workflows/src/workers/public-options.d.ts +10 -0
- package/dist/core/agent-session-persistent-custom-messages.d.ts +5 -0
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +16 -0
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-tasks.d.ts.map +1 -1
- package/dist/core/agent-session-tasks.js +20 -0
- package/dist/core/agent-session-tasks.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +4 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +4 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +2 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +6 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-schema.d.ts +9 -0
- package/dist/core/settings-schema.d.ts.map +1 -1
- package/dist/core/settings-schema.js +11 -0
- package/dist/core/settings-schema.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tasks/agent-adapter.d.ts +10 -1
- package/dist/core/tasks/agent-adapter.d.ts.map +1 -1
- package/dist/core/tasks/agent-adapter.js +20 -0
- package/dist/core/tasks/agent-adapter.js.map +1 -1
- package/dist/core/tasks/contracts.d.ts +6 -0
- package/dist/core/tasks/contracts.d.ts.map +1 -1
- package/dist/core/tasks/contracts.js.map +1 -1
- package/dist/core/tasks/heartbeat-notice.d.ts +64 -0
- package/dist/core/tasks/heartbeat-notice.d.ts.map +1 -0
- package/dist/core/tasks/heartbeat-notice.js +98 -0
- package/dist/core/tasks/heartbeat-notice.js.map +1 -0
- package/dist/core/tasks/heartbeat.d.ts +75 -0
- package/dist/core/tasks/heartbeat.d.ts.map +1 -0
- package/dist/core/tasks/heartbeat.js +440 -0
- package/dist/core/tasks/heartbeat.js.map +1 -0
- package/dist/core/tasks/supervisor.d.ts +2 -0
- package/dist/core/tasks/supervisor.d.ts.map +1 -1
- package/dist/core/tasks/supervisor.js +32 -4
- package/dist/core/tasks/supervisor.js.map +1 -1
- package/dist/core/tasks/task-view-source.d.ts +72 -0
- package/dist/core/tasks/task-view-source.d.ts.map +1 -0
- package/dist/core/tasks/task-view-source.js +155 -0
- package/dist/core/tasks/task-view-source.js.map +1 -0
- package/dist/core/tools/bash-pty-native.d.ts +2 -0
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +1 -0
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts +3 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -8
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +7 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.d.ts +3 -0
- package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.js +4 -0
- package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-types.d.ts +11 -0
- package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-types.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +2 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +15 -8
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +12 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/task-detail.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-detail.js +5 -0
- package/dist/modes/interactive/components/task-detail.js.map +1 -1
- package/dist/modes/interactive/components/task-heartbeat-message.d.ts +14 -0
- package/dist/modes/interactive/components/task-heartbeat-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/task-heartbeat-message.js +76 -0
- package/dist/modes/interactive/components/task-heartbeat-message.js.map +1 -0
- package/dist/modes/interactive/components/task-inspector.d.ts +6 -3
- package/dist/modes/interactive/components/task-inspector.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-inspector.js +53 -100
- package/dist/modes/interactive/components/task-inspector.js.map +1 -1
- package/dist/modes/interactive/skill-command-autocomplete.d.ts +22 -2
- package/dist/modes/interactive/skill-command-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/skill-command-autocomplete.js +28 -9
- package/dist/modes/interactive/skill-command-autocomplete.js.map +1 -1
- package/docs/background-tasks.md +65 -1
- package/docs/settings.md +12 -0
- package/docs/subagents.md +2 -0
- package/docs/tools.md +2 -1
- package/docs/workflows/operations.md +5 -3
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/schemas/settings.schema.json +24 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { taskOutputText } from "./command-output.js";
|
|
2
|
+
import { DEFAULT_TASK_HEARTBEAT_MS, formatTaskHeartbeat, isValidTaskHeartbeatMs, singleLine, TASK_HEARTBEAT_MESSAGE_TYPE, } from "./heartbeat-notice.js";
|
|
3
|
+
import { taskOwnTranscriptEntries, } from "./supervisor.js";
|
|
4
|
+
export * from "./heartbeat-notice.js";
|
|
5
|
+
/** Tasks described in full by one notice; further due tasks are only listed. */
|
|
6
|
+
export const TASK_HEARTBEAT_DETAIL_LIMIT = 8;
|
|
7
|
+
/** Further due tasks listed by name in one notice; the rest are counted. */
|
|
8
|
+
export const TASK_HEARTBEAT_LIST_LIMIT = 20;
|
|
9
|
+
/** Boundaries due within this window (capped at a quarter cadence) join the notice that is going out. */
|
|
10
|
+
export const TASK_HEARTBEAT_COALESCE_MS = 60_000;
|
|
11
|
+
/** While the owner has not read the previous notice, due tasks wait and are rechecked this often. */
|
|
12
|
+
export const TASK_HEARTBEAT_RECHECK_MS = 30_000;
|
|
13
|
+
/** How often a running shell's output is sampled to date its last output. */
|
|
14
|
+
export const TASK_HEARTBEAT_OUTPUT_SAMPLE_MS = 30_000;
|
|
15
|
+
const OUTPUT_TAIL_BYTES = 2048;
|
|
16
|
+
const OUTPUT_TAIL_LINES = 6;
|
|
17
|
+
const LINE_CHARS = 160;
|
|
18
|
+
const TITLE_CHARS = 120;
|
|
19
|
+
/** Longest delay a host timer accepts before it overflows to an immediate fire. */
|
|
20
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
21
|
+
/** The owner's default cadence, or the built-in one when its setting cannot be read or is out of range. */
|
|
22
|
+
export function defaultTaskHeartbeatMs(delivery) {
|
|
23
|
+
try {
|
|
24
|
+
const configured = delivery.defaultIntervalMs();
|
|
25
|
+
return isValidTaskHeartbeatMs(configured) ? configured : DEFAULT_TASK_HEARTBEAT_MS;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return DEFAULT_TASK_HEARTBEAT_MS;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const systemClock = {
|
|
32
|
+
now: () => Date.now(),
|
|
33
|
+
setTimeout: (run, delayMs) => {
|
|
34
|
+
const handle = setTimeout(run, delayMs);
|
|
35
|
+
handle.unref();
|
|
36
|
+
return handle;
|
|
37
|
+
},
|
|
38
|
+
clearTimeout: (handle) => clearTimeout(handle),
|
|
39
|
+
};
|
|
40
|
+
function activityKey(task) {
|
|
41
|
+
return JSON.stringify([task.currentAction, task.metrics, task.attention, task.model]);
|
|
42
|
+
}
|
|
43
|
+
/** Whether the newest message in a child's history is an assistant turn whose tool calls have not returned yet. */
|
|
44
|
+
function endsInToolCall(entries) {
|
|
45
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
46
|
+
const entry = entries[index];
|
|
47
|
+
if (entry?.type !== "message")
|
|
48
|
+
continue;
|
|
49
|
+
const message = entry.message;
|
|
50
|
+
return message.role === "assistant" && message.content.some((block) => block.type === "toolCall");
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/** First boundary on the `startedAt + n × intervalMs` series strictly after `after`. */
|
|
55
|
+
function nextBoundary(startedAt, intervalMs, after) {
|
|
56
|
+
const n = Math.max(1, Math.floor((after - startedAt) / intervalMs) + 1);
|
|
57
|
+
const boundary = startedAt + n * intervalMs;
|
|
58
|
+
return boundary > after ? boundary : startedAt + (n + 1) * intervalMs;
|
|
59
|
+
}
|
|
60
|
+
function byBoundary(a, b) {
|
|
61
|
+
const left = a.nextAt ?? Number.POSITIVE_INFINITY;
|
|
62
|
+
const right = b.nextAt ?? Number.POSITIVE_INFINITY;
|
|
63
|
+
if (left !== right)
|
|
64
|
+
return left - right;
|
|
65
|
+
return a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Periodic "is this still making progress?" notices for one task owner's running shells and subagents.
|
|
69
|
+
*
|
|
70
|
+
* It follows the workflow heartbeat rules: boundaries are anchored at the task's start and never at the previous
|
|
71
|
+
* delivery, missed boundaries are skipped rather than replayed, and only one notice per owner is outstanding. Unlike
|
|
72
|
+
* a workflow run, an owner can have many tasks, so tasks due together share one bounded notice, and a task due while
|
|
73
|
+
* the owner has not read the previous notice waits for it rather than losing its turn.
|
|
74
|
+
*/
|
|
75
|
+
export class TaskHeartbeatMonitor {
|
|
76
|
+
options;
|
|
77
|
+
clock;
|
|
78
|
+
tracked = new Map();
|
|
79
|
+
subscription;
|
|
80
|
+
timer;
|
|
81
|
+
/** The time the current timer wakes for, so an unchanged schedule keeps its timer. */
|
|
82
|
+
timerAt;
|
|
83
|
+
busy = false;
|
|
84
|
+
disposed = false;
|
|
85
|
+
recheckAt;
|
|
86
|
+
nextSampleAt;
|
|
87
|
+
constructor(options) {
|
|
88
|
+
this.options = options;
|
|
89
|
+
this.clock = options.clock ?? systemClock;
|
|
90
|
+
}
|
|
91
|
+
/** Begin watching the owner. Idempotent. */
|
|
92
|
+
start() {
|
|
93
|
+
if (this.subscription || this.disposed)
|
|
94
|
+
return;
|
|
95
|
+
const watched = this.options.supervisor.watchOwnerTasks(this.options.owner);
|
|
96
|
+
if (!watched.ok)
|
|
97
|
+
return;
|
|
98
|
+
this.subscription = watched.value;
|
|
99
|
+
this.subscription.onReconcile = (snapshot, events) => this.reconcile(snapshot, events);
|
|
100
|
+
this.reconcile(this.subscription.snapshot, []);
|
|
101
|
+
}
|
|
102
|
+
dispose() {
|
|
103
|
+
if (this.disposed)
|
|
104
|
+
return;
|
|
105
|
+
this.disposed = true;
|
|
106
|
+
this.subscription?.dispose();
|
|
107
|
+
this.subscription = undefined;
|
|
108
|
+
this.tracked.clear();
|
|
109
|
+
this.disarm();
|
|
110
|
+
}
|
|
111
|
+
reconcile(snapshot, events) {
|
|
112
|
+
if (this.disposed)
|
|
113
|
+
return;
|
|
114
|
+
if (snapshot.state !== "open") {
|
|
115
|
+
if (snapshot.state === "closed")
|
|
116
|
+
this.dispose();
|
|
117
|
+
else {
|
|
118
|
+
this.tracked.clear();
|
|
119
|
+
this.disarm();
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const at = this.clock.now();
|
|
124
|
+
const reported = new Set();
|
|
125
|
+
for (const event of events)
|
|
126
|
+
if (event.payload.kind === "task-activity")
|
|
127
|
+
reported.add(event.payload.ref.taskId);
|
|
128
|
+
const running = new Set();
|
|
129
|
+
for (const task of snapshot.tasks) {
|
|
130
|
+
// Queued, stopping, paused and finished tasks do not heartbeat.
|
|
131
|
+
if (task.execution.kind !== "running")
|
|
132
|
+
continue;
|
|
133
|
+
const id = task.ref.taskId;
|
|
134
|
+
running.add(id);
|
|
135
|
+
const key = activityKey(task);
|
|
136
|
+
const entry = this.tracked.get(id);
|
|
137
|
+
if (entry) {
|
|
138
|
+
entry.record = task;
|
|
139
|
+
if (key !== entry.activityKey || reported.has(id)) {
|
|
140
|
+
entry.activityKey = key;
|
|
141
|
+
entry.lastActivityAt = at;
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const intervalMs = this.resolveInterval(task);
|
|
146
|
+
this.tracked.set(id, {
|
|
147
|
+
taskId: id,
|
|
148
|
+
record: task,
|
|
149
|
+
startedAt: at,
|
|
150
|
+
intervalMs,
|
|
151
|
+
...(intervalMs > 0 ? { nextAt: at + intervalMs } : {}),
|
|
152
|
+
...(reported.has(id) ? { lastActivityAt: at } : {}),
|
|
153
|
+
activityKey: key,
|
|
154
|
+
outputEnd: 0n,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
for (const id of [...this.tracked.keys()])
|
|
158
|
+
if (!running.has(id))
|
|
159
|
+
this.tracked.delete(id);
|
|
160
|
+
if (!this.hasSampledShells())
|
|
161
|
+
this.nextSampleAt = undefined;
|
|
162
|
+
else
|
|
163
|
+
this.nextSampleAt ??= at + TASK_HEARTBEAT_OUTPUT_SAMPLE_MS;
|
|
164
|
+
this.arm();
|
|
165
|
+
}
|
|
166
|
+
resolveInterval(task) {
|
|
167
|
+
// Owners with a heartbeat binding fix each task's cadence on its record at admission.
|
|
168
|
+
if (task.heartbeatMs !== undefined)
|
|
169
|
+
return isValidTaskHeartbeatMs(task.heartbeatMs) ? task.heartbeatMs : 0;
|
|
170
|
+
const delivery = this.options.delivery();
|
|
171
|
+
return delivery ? defaultTaskHeartbeatMs(delivery) : DEFAULT_TASK_HEARTBEAT_MS;
|
|
172
|
+
}
|
|
173
|
+
hasSampledShells() {
|
|
174
|
+
for (const entry of this.tracked.values())
|
|
175
|
+
if (entry.record.kind === "command" && entry.intervalMs > 0)
|
|
176
|
+
return true;
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
disarm() {
|
|
180
|
+
if (this.timer === undefined)
|
|
181
|
+
return;
|
|
182
|
+
this.clock.clearTimeout(this.timer);
|
|
183
|
+
this.timer = undefined;
|
|
184
|
+
this.timerAt = undefined;
|
|
185
|
+
}
|
|
186
|
+
arm() {
|
|
187
|
+
if (this.disposed || this.busy) {
|
|
188
|
+
this.disarm();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// A blocked owner cannot receive anything before its recheck, however many tasks come due meanwhile.
|
|
192
|
+
let wakeAt = this.recheckAt ?? Number.POSITIVE_INFINITY;
|
|
193
|
+
if (this.recheckAt === undefined)
|
|
194
|
+
for (const entry of this.tracked.values())
|
|
195
|
+
if (entry.nextAt !== undefined && entry.nextAt < wakeAt)
|
|
196
|
+
wakeAt = entry.nextAt;
|
|
197
|
+
if (this.nextSampleAt !== undefined && this.nextSampleAt < wakeAt)
|
|
198
|
+
wakeAt = this.nextSampleAt;
|
|
199
|
+
// Activity reconciles many times a second; it rarely moves the next wake-up.
|
|
200
|
+
if (this.timer !== undefined && this.timerAt === wakeAt)
|
|
201
|
+
return;
|
|
202
|
+
this.disarm();
|
|
203
|
+
if (!Number.isFinite(wakeAt))
|
|
204
|
+
return;
|
|
205
|
+
const delay = Math.min(Math.max(1, Math.ceil(wakeAt - this.clock.now())), MAX_TIMER_DELAY_MS);
|
|
206
|
+
this.timerAt = wakeAt;
|
|
207
|
+
this.timer = this.clock.setTimeout(() => {
|
|
208
|
+
this.timer = undefined;
|
|
209
|
+
this.timerAt = undefined;
|
|
210
|
+
void this.wake();
|
|
211
|
+
}, delay);
|
|
212
|
+
}
|
|
213
|
+
async wake() {
|
|
214
|
+
if (this.disposed || this.busy)
|
|
215
|
+
return;
|
|
216
|
+
this.busy = true;
|
|
217
|
+
try {
|
|
218
|
+
const at = this.clock.now();
|
|
219
|
+
if (this.nextSampleAt !== undefined && this.nextSampleAt <= at) {
|
|
220
|
+
await this.sampleShellOutput(at);
|
|
221
|
+
this.nextSampleAt = this.hasSampledShells() ? at + TASK_HEARTBEAT_OUTPUT_SAMPLE_MS : undefined;
|
|
222
|
+
}
|
|
223
|
+
await this.beat(at);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// A failed check skips this boundary; the next one is already scheduled.
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
this.busy = false;
|
|
230
|
+
this.arm();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async beat(at) {
|
|
234
|
+
if (this.recheckAt !== undefined && this.recheckAt > at)
|
|
235
|
+
return;
|
|
236
|
+
this.recheckAt = undefined;
|
|
237
|
+
const entries = [...this.tracked.values()];
|
|
238
|
+
if (!entries.some((entry) => entry.nextAt !== undefined && entry.nextAt <= at))
|
|
239
|
+
return;
|
|
240
|
+
const delivery = this.options.delivery();
|
|
241
|
+
let unread = false;
|
|
242
|
+
try {
|
|
243
|
+
unread = delivery?.hasUnreadHeartbeat() === true;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
unread = false;
|
|
247
|
+
}
|
|
248
|
+
if (unread) {
|
|
249
|
+
// One outstanding notice per owner: due tasks stay owed and join the next notice instead of stacking.
|
|
250
|
+
this.recheckAt = at + TASK_HEARTBEAT_RECHECK_MS;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const batch = entries
|
|
254
|
+
.filter((entry) => entry.nextAt !== undefined &&
|
|
255
|
+
entry.nextAt <= at + Math.min(TASK_HEARTBEAT_COALESCE_MS, entry.intervalMs / 4))
|
|
256
|
+
.sort(byBoundary);
|
|
257
|
+
for (const entry of batch)
|
|
258
|
+
entry.nextAt = nextBoundary(entry.startedAt, entry.intervalMs, Math.max(at, entry.nextAt ?? at));
|
|
259
|
+
if (!delivery)
|
|
260
|
+
return;
|
|
261
|
+
const details = await this.describe(batch, at);
|
|
262
|
+
// A task that finished while its notice was being prepared is no longer mentioned.
|
|
263
|
+
const live = (taskId) => this.tracked.has(taskId);
|
|
264
|
+
details.tasks = details.tasks.filter((task) => live(task.taskId));
|
|
265
|
+
details.more = details.more.filter((task) => live(task.taskId));
|
|
266
|
+
details.moreCount = batch.slice(TASK_HEARTBEAT_DETAIL_LIMIT).filter((entry) => live(entry.taskId)).length;
|
|
267
|
+
if (details.tasks.length === 0 || this.disposed)
|
|
268
|
+
return;
|
|
269
|
+
await delivery.deliver({
|
|
270
|
+
customType: TASK_HEARTBEAT_MESSAGE_TYPE,
|
|
271
|
+
content: formatTaskHeartbeat(details),
|
|
272
|
+
display: true,
|
|
273
|
+
details,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async describe(batch, at) {
|
|
277
|
+
const detailed = batch.slice(0, TASK_HEARTBEAT_DETAIL_LIMIT);
|
|
278
|
+
const rest = batch.slice(TASK_HEARTBEAT_DETAIL_LIMIT);
|
|
279
|
+
const tasks = [];
|
|
280
|
+
for (const entry of detailed)
|
|
281
|
+
tasks.push(await this.describeTask(entry, at));
|
|
282
|
+
return {
|
|
283
|
+
scheduledAt: at,
|
|
284
|
+
tasks,
|
|
285
|
+
more: rest.slice(0, TASK_HEARTBEAT_LIST_LIMIT).map((entry) => ({
|
|
286
|
+
taskId: entry.taskId,
|
|
287
|
+
kind: entry.record.kind,
|
|
288
|
+
title: singleLine(entry.record.title, TITLE_CHARS),
|
|
289
|
+
})),
|
|
290
|
+
moreCount: rest.length,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async describeTask(entry, at) {
|
|
294
|
+
const task = entry.record;
|
|
295
|
+
const lease = this.lease(entry.taskId);
|
|
296
|
+
const described = {
|
|
297
|
+
taskId: entry.taskId,
|
|
298
|
+
kind: task.kind,
|
|
299
|
+
title: singleLine(task.title, TITLE_CHARS),
|
|
300
|
+
...(task.agentName === undefined ? {} : { agentName: singleLine(task.agentName, TITLE_CHARS) }),
|
|
301
|
+
...(task.kind === "agent" && task.model !== undefined ? { model: task.model } : {}),
|
|
302
|
+
elapsedMs: Math.max(0, at - entry.startedAt),
|
|
303
|
+
intervalMs: entry.intervalMs,
|
|
304
|
+
};
|
|
305
|
+
if (task.kind === "command" && lease) {
|
|
306
|
+
const end = await this.observeOutput(entry, lease, at);
|
|
307
|
+
if (end !== undefined)
|
|
308
|
+
described.output = { bytes: Number(end), tail: await this.outputTail(lease, end) };
|
|
309
|
+
}
|
|
310
|
+
if (entry.lastActivityAt !== undefined)
|
|
311
|
+
described.lastActivityAgoMs = Math.max(0, at - entry.lastActivityAt);
|
|
312
|
+
if (task.kind === "agent") {
|
|
313
|
+
const entries = lease ? taskOwnTranscriptEntries(lease) : undefined;
|
|
314
|
+
if (task.currentAction)
|
|
315
|
+
described.currentAction = {
|
|
316
|
+
tool: singleLine(task.currentAction.tool, 60),
|
|
317
|
+
text: singleLine(task.currentAction.text, LINE_CHARS),
|
|
318
|
+
// The record keeps the last reported action after its tool returns. The child's own history says
|
|
319
|
+
// whether a call is still outstanding: its last message is an assistant turn that called tools.
|
|
320
|
+
running: entries !== undefined && endsInToolCall(entries),
|
|
321
|
+
};
|
|
322
|
+
if (task.metrics?.toolCount !== undefined)
|
|
323
|
+
described.toolCount = task.metrics.toolCount;
|
|
324
|
+
if (task.metrics?.tokenCount !== undefined)
|
|
325
|
+
described.tokenCount = task.metrics.tokenCount;
|
|
326
|
+
if (entries)
|
|
327
|
+
described.turnCount = entries.filter((item) => item.type === "message" && item.message.role === "assistant").length;
|
|
328
|
+
}
|
|
329
|
+
if (task.attention.kind === "input-needed")
|
|
330
|
+
described.attention = `waiting for input: ${singleLine(task.attention.prompt, LINE_CHARS)}`;
|
|
331
|
+
else if (task.attention.kind === "no-recent-activity")
|
|
332
|
+
described.attention = "no recent activity";
|
|
333
|
+
return described;
|
|
334
|
+
}
|
|
335
|
+
lease(taskId) {
|
|
336
|
+
const found = this.options.supervisor.lookupTask(this.options.owner, taskId);
|
|
337
|
+
return found.ok ? found.value : undefined;
|
|
338
|
+
}
|
|
339
|
+
async sampleShellOutput(at) {
|
|
340
|
+
for (const entry of [...this.tracked.values()]) {
|
|
341
|
+
if (entry.record.kind !== "command" || entry.intervalMs === 0)
|
|
342
|
+
continue;
|
|
343
|
+
const lease = this.lease(entry.taskId);
|
|
344
|
+
if (lease)
|
|
345
|
+
await this.observeOutput(entry, lease, at);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/** Current retained output size; growth since the last look dates the shell's last output. */
|
|
349
|
+
async observeOutput(entry, lease, at) {
|
|
350
|
+
const end = await findOutputEnd(this.options.supervisor, lease, entry.outputEnd);
|
|
351
|
+
if (end === undefined)
|
|
352
|
+
return undefined;
|
|
353
|
+
if (end > entry.outputEnd) {
|
|
354
|
+
entry.outputEnd = end;
|
|
355
|
+
entry.lastActivityAt = at;
|
|
356
|
+
}
|
|
357
|
+
return end;
|
|
358
|
+
}
|
|
359
|
+
async outputTail(lease, end) {
|
|
360
|
+
if (end === 0n)
|
|
361
|
+
return [];
|
|
362
|
+
const start = end > BigInt(OUTPUT_TAIL_BYTES) ? end - BigInt(OUTPUT_TAIL_BYTES) : 0n;
|
|
363
|
+
const page = await this.options.supervisor.readTaskOutput(lease, {
|
|
364
|
+
start: String(start),
|
|
365
|
+
maximumBytes: OUTPUT_TAIL_BYTES,
|
|
366
|
+
});
|
|
367
|
+
if (!page.ok)
|
|
368
|
+
return [];
|
|
369
|
+
const lines = taskOutputText(page.value).split("\n");
|
|
370
|
+
// The window can open mid-line; that fragment is not a line of output.
|
|
371
|
+
if (start > 0n)
|
|
372
|
+
lines.shift();
|
|
373
|
+
return lines
|
|
374
|
+
.map((line) => {
|
|
375
|
+
// A CRLF ending is not a redraw; a carriage return inside a line redraws it, so its last state counts.
|
|
376
|
+
const ended = line.replace(/\r+$/, "");
|
|
377
|
+
return singleLine(ended.slice(ended.lastIndexOf("\r") + 1), LINE_CHARS).trimEnd();
|
|
378
|
+
})
|
|
379
|
+
.filter((line) => line.trim().length > 0)
|
|
380
|
+
.slice(-OUTPUT_TAIL_LINES);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Retained output size of a running shell, found with one-byte reads: a read past the end returns nothing. Gallops
|
|
385
|
+
* from the last known size, then bisects, so a large burst of output costs a few dozen tiny reads, not a full scan.
|
|
386
|
+
*/
|
|
387
|
+
async function findOutputEnd(supervisor, lease, from) {
|
|
388
|
+
const probe = async (offset) => {
|
|
389
|
+
const page = await supervisor.readTaskOutput(lease, { start: String(offset), maximumBytes: 1 });
|
|
390
|
+
if (!page.ok)
|
|
391
|
+
return undefined;
|
|
392
|
+
if (page.value.nextOffset !== undefined)
|
|
393
|
+
return { before: true, end: BigInt(page.value.nextOffset) };
|
|
394
|
+
let end = offset;
|
|
395
|
+
for (const chunk of page.value.chunks)
|
|
396
|
+
if (BigInt(chunk.offsets.end) > end)
|
|
397
|
+
end = BigInt(chunk.offsets.end);
|
|
398
|
+
for (const range of page.value.omittedRanges)
|
|
399
|
+
if (BigInt(range.end) > end)
|
|
400
|
+
end = BigInt(range.end);
|
|
401
|
+
// No byte beyond this page: the output ends at its last byte, or at or before `offset` when it is empty.
|
|
402
|
+
return { before: end > offset, end };
|
|
403
|
+
};
|
|
404
|
+
let low = from;
|
|
405
|
+
const first = await probe(low);
|
|
406
|
+
if (!first)
|
|
407
|
+
return undefined;
|
|
408
|
+
if (!first.before)
|
|
409
|
+
return first.end > from ? first.end : from;
|
|
410
|
+
let step = 65536n;
|
|
411
|
+
let high = low + step;
|
|
412
|
+
for (;;) {
|
|
413
|
+
const result = await probe(high);
|
|
414
|
+
if (!result)
|
|
415
|
+
return undefined;
|
|
416
|
+
if (!result.before) {
|
|
417
|
+
if (result.end > high)
|
|
418
|
+
return result.end;
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
low = high;
|
|
422
|
+
step *= 2n;
|
|
423
|
+
high = low + step;
|
|
424
|
+
}
|
|
425
|
+
// The output ends after `low` and at or before `high`.
|
|
426
|
+
while (high - low > 1n) {
|
|
427
|
+
const middle = low + (high - low) / 2n;
|
|
428
|
+
const result = await probe(middle);
|
|
429
|
+
if (!result)
|
|
430
|
+
return undefined;
|
|
431
|
+
if (result.before)
|
|
432
|
+
low = middle;
|
|
433
|
+
else if (result.end > middle)
|
|
434
|
+
return result.end;
|
|
435
|
+
else
|
|
436
|
+
high = middle;
|
|
437
|
+
}
|
|
438
|
+
return high;
|
|
439
|
+
}
|
|
440
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../../src/core/tasks/heartbeat.ts"],"names":[],"mappings":"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","sourcesContent":["import { taskOutputText } from \"./command-output.js\";\nimport type { NativeEvent, OwnerSnapshot, TaskId, TaskRecord } from \"./contracts.js\";\nimport {\n\tDEFAULT_TASK_HEARTBEAT_MS,\n\tformatTaskHeartbeat,\n\tisValidTaskHeartbeatMs,\n\tsingleLine,\n\tTASK_HEARTBEAT_MESSAGE_TYPE,\n\ttype TaskHeartbeatDetails,\n\ttype TaskHeartbeatMessage,\n\ttype TaskHeartbeatTask,\n} from \"./heartbeat-notice.js\";\nimport {\n\ttype OwnerLease,\n\ttype TaskLease,\n\ttype TaskSubscription,\n\ttype TaskSupervisor,\n\ttaskOwnTranscriptEntries,\n} from \"./supervisor.js\";\n\nexport * from \"./heartbeat-notice.js\";\n\n/** Tasks described in full by one notice; further due tasks are only listed. */\nexport const TASK_HEARTBEAT_DETAIL_LIMIT = 8;\n/** Further due tasks listed by name in one notice; the rest are counted. */\nexport const TASK_HEARTBEAT_LIST_LIMIT = 20;\n/** Boundaries due within this window (capped at a quarter cadence) join the notice that is going out. */\nexport const TASK_HEARTBEAT_COALESCE_MS = 60_000;\n/** While the owner has not read the previous notice, due tasks wait and are rechecked this often. */\nexport const TASK_HEARTBEAT_RECHECK_MS = 30_000;\n/** How often a running shell's output is sampled to date its last output. */\nexport const TASK_HEARTBEAT_OUTPUT_SAMPLE_MS = 30_000;\nconst OUTPUT_TAIL_BYTES = 2048;\nconst OUTPUT_TAIL_LINES = 6;\nconst LINE_CHARS = 160;\nconst TITLE_CHARS = 120;\n/** Longest delay a host timer accepts before it overflows to an immediate fire. */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/** Where an owner's heartbeats go. The host binds its own chat; nothing here is model-provided. */\nexport interface TaskHeartbeatDelivery {\n\t/** Cadence for tasks launched without their own `heartbeatMs`; `0` disables. */\n\tdefaultIntervalMs(): number;\n\t/** Whether a heartbeat this owner already sent is still waiting, unread, in its chat. */\n\thasUnreadHeartbeat(): boolean;\n\tdeliver(message: TaskHeartbeatMessage): Promise<void>;\n}\n\n/** The owner's default cadence, or the built-in one when its setting cannot be read or is out of range. */\nexport function defaultTaskHeartbeatMs(delivery: Pick<TaskHeartbeatDelivery, \"defaultIntervalMs\">): number {\n\ttry {\n\t\tconst configured = delivery.defaultIntervalMs();\n\t\treturn isValidTaskHeartbeatMs(configured) ? configured : DEFAULT_TASK_HEARTBEAT_MS;\n\t} catch {\n\t\treturn DEFAULT_TASK_HEARTBEAT_MS;\n\t}\n}\n\nexport interface TaskHeartbeatClock {\n\tnow(): number;\n\tsetTimeout(run: () => void, delayMs: number): object;\n\tclearTimeout(handle: object): void;\n}\n\nconst systemClock: TaskHeartbeatClock = {\n\tnow: () => Date.now(),\n\tsetTimeout: (run, delayMs) => {\n\t\tconst handle = setTimeout(run, delayMs);\n\t\thandle.unref();\n\t\treturn handle;\n\t},\n\tclearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport interface TaskHeartbeatMonitorOptions {\n\tsupervisor: TaskSupervisor;\n\towner: OwnerLease;\n\t/** Read on every use, so a replaced session's binding takes over the same owner. */\n\tdelivery: () => TaskHeartbeatDelivery | undefined;\n\tclock?: TaskHeartbeatClock;\n}\n\ntype Tracked = {\n\ttaskId: TaskId;\n\trecord: TaskRecord;\n\t/** First observed running: the anchor of the cadence. */\n\tstartedAt: number;\n\t/** Frozen when the task is first observed; `0` disables. */\n\tintervalMs: number;\n\t/** Next boundary `startedAt + n × intervalMs`; absent when disabled. */\n\tnextAt?: number;\n\tlastActivityAt?: number;\n\tactivityKey: string;\n\t/** Shells: retained output bytes observed so far. */\n\toutputEnd: bigint;\n};\n\nfunction activityKey(task: TaskRecord): string {\n\treturn JSON.stringify([task.currentAction, task.metrics, task.attention, task.model]);\n}\n\n/** Whether the newest message in a child's history is an assistant turn whose tool calls have not returned yet. */\nfunction endsInToolCall(entries: NonNullable<ReturnType<typeof taskOwnTranscriptEntries>>): boolean {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type !== \"message\") continue;\n\t\tconst message = entry.message;\n\t\treturn message.role === \"assistant\" && message.content.some((block) => block.type === \"toolCall\");\n\t}\n\treturn false;\n}\n\n/** First boundary on the `startedAt + n × intervalMs` series strictly after `after`. */\nfunction nextBoundary(startedAt: number, intervalMs: number, after: number): number {\n\tconst n = Math.max(1, Math.floor((after - startedAt) / intervalMs) + 1);\n\tconst boundary = startedAt + n * intervalMs;\n\treturn boundary > after ? boundary : startedAt + (n + 1) * intervalMs;\n}\n\nfunction byBoundary(a: Tracked, b: Tracked): number {\n\tconst left = a.nextAt ?? Number.POSITIVE_INFINITY;\n\tconst right = b.nextAt ?? Number.POSITIVE_INFINITY;\n\tif (left !== right) return left - right;\n\treturn a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;\n}\n\n/**\n * Periodic \"is this still making progress?\" notices for one task owner's running shells and subagents.\n *\n * It follows the workflow heartbeat rules: boundaries are anchored at the task's start and never at the previous\n * delivery, missed boundaries are skipped rather than replayed, and only one notice per owner is outstanding. Unlike\n * a workflow run, an owner can have many tasks, so tasks due together share one bounded notice, and a task due while\n * the owner has not read the previous notice waits for it rather than losing its turn.\n */\nexport class TaskHeartbeatMonitor {\n\tprivate readonly options: TaskHeartbeatMonitorOptions;\n\tprivate readonly clock: TaskHeartbeatClock;\n\tprivate readonly tracked = new Map<TaskId, Tracked>();\n\tprivate subscription?: TaskSubscription;\n\tprivate timer?: object;\n\t/** The time the current timer wakes for, so an unchanged schedule keeps its timer. */\n\tprivate timerAt?: number;\n\tprivate busy = false;\n\tprivate disposed = false;\n\tprivate recheckAt?: number;\n\tprivate nextSampleAt?: number;\n\n\tconstructor(options: TaskHeartbeatMonitorOptions) {\n\t\tthis.options = options;\n\t\tthis.clock = options.clock ?? systemClock;\n\t}\n\n\t/** Begin watching the owner. Idempotent. */\n\tstart(): void {\n\t\tif (this.subscription || this.disposed) return;\n\t\tconst watched = this.options.supervisor.watchOwnerTasks(this.options.owner);\n\t\tif (!watched.ok) return;\n\t\tthis.subscription = watched.value;\n\t\tthis.subscription.onReconcile = (snapshot, events) => this.reconcile(snapshot, events);\n\t\tthis.reconcile(this.subscription.snapshot, []);\n\t}\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.subscription?.dispose();\n\t\tthis.subscription = undefined;\n\t\tthis.tracked.clear();\n\t\tthis.disarm();\n\t}\n\n\tprivate reconcile(snapshot: OwnerSnapshot, events: readonly NativeEvent[]): void {\n\t\tif (this.disposed) return;\n\t\tif (snapshot.state !== \"open\") {\n\t\t\tif (snapshot.state === \"closed\") this.dispose();\n\t\t\telse {\n\t\t\t\tthis.tracked.clear();\n\t\t\t\tthis.disarm();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst at = this.clock.now();\n\t\tconst reported = new Set<TaskId>();\n\t\tfor (const event of events) if (event.payload.kind === \"task-activity\") reported.add(event.payload.ref.taskId);\n\t\tconst running = new Set<TaskId>();\n\t\tfor (const task of snapshot.tasks) {\n\t\t\t// Queued, stopping, paused and finished tasks do not heartbeat.\n\t\t\tif (task.execution.kind !== \"running\") continue;\n\t\t\tconst id = task.ref.taskId;\n\t\t\trunning.add(id);\n\t\t\tconst key = activityKey(task);\n\t\t\tconst entry = this.tracked.get(id);\n\t\t\tif (entry) {\n\t\t\t\tentry.record = task;\n\t\t\t\tif (key !== entry.activityKey || reported.has(id)) {\n\t\t\t\t\tentry.activityKey = key;\n\t\t\t\t\tentry.lastActivityAt = at;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst intervalMs = this.resolveInterval(task);\n\t\t\tthis.tracked.set(id, {\n\t\t\t\ttaskId: id,\n\t\t\t\trecord: task,\n\t\t\t\tstartedAt: at,\n\t\t\t\tintervalMs,\n\t\t\t\t...(intervalMs > 0 ? { nextAt: at + intervalMs } : {}),\n\t\t\t\t...(reported.has(id) ? { lastActivityAt: at } : {}),\n\t\t\t\tactivityKey: key,\n\t\t\t\toutputEnd: 0n,\n\t\t\t});\n\t\t}\n\t\tfor (const id of [...this.tracked.keys()]) if (!running.has(id)) this.tracked.delete(id);\n\t\tif (!this.hasSampledShells()) this.nextSampleAt = undefined;\n\t\telse this.nextSampleAt ??= at + TASK_HEARTBEAT_OUTPUT_SAMPLE_MS;\n\t\tthis.arm();\n\t}\n\n\tprivate resolveInterval(task: TaskRecord): number {\n\t\t// Owners with a heartbeat binding fix each task's cadence on its record at admission.\n\t\tif (task.heartbeatMs !== undefined) return isValidTaskHeartbeatMs(task.heartbeatMs) ? task.heartbeatMs : 0;\n\t\tconst delivery = this.options.delivery();\n\t\treturn delivery ? defaultTaskHeartbeatMs(delivery) : DEFAULT_TASK_HEARTBEAT_MS;\n\t}\n\n\tprivate hasSampledShells(): boolean {\n\t\tfor (const entry of this.tracked.values())\n\t\t\tif (entry.record.kind === \"command\" && entry.intervalMs > 0) return true;\n\t\treturn false;\n\t}\n\n\tprivate disarm(): void {\n\t\tif (this.timer === undefined) return;\n\t\tthis.clock.clearTimeout(this.timer);\n\t\tthis.timer = undefined;\n\t\tthis.timerAt = undefined;\n\t}\n\n\tprivate arm(): void {\n\t\tif (this.disposed || this.busy) {\n\t\t\tthis.disarm();\n\t\t\treturn;\n\t\t}\n\t\t// A blocked owner cannot receive anything before its recheck, however many tasks come due meanwhile.\n\t\tlet wakeAt = this.recheckAt ?? Number.POSITIVE_INFINITY;\n\t\tif (this.recheckAt === undefined)\n\t\t\tfor (const entry of this.tracked.values())\n\t\t\t\tif (entry.nextAt !== undefined && entry.nextAt < wakeAt) wakeAt = entry.nextAt;\n\t\tif (this.nextSampleAt !== undefined && this.nextSampleAt < wakeAt) wakeAt = this.nextSampleAt;\n\t\t// Activity reconciles many times a second; it rarely moves the next wake-up.\n\t\tif (this.timer !== undefined && this.timerAt === wakeAt) return;\n\t\tthis.disarm();\n\t\tif (!Number.isFinite(wakeAt)) return;\n\t\tconst delay = Math.min(Math.max(1, Math.ceil(wakeAt - this.clock.now())), MAX_TIMER_DELAY_MS);\n\t\tthis.timerAt = wakeAt;\n\t\tthis.timer = this.clock.setTimeout(() => {\n\t\t\tthis.timer = undefined;\n\t\t\tthis.timerAt = undefined;\n\t\t\tvoid this.wake();\n\t\t}, delay);\n\t}\n\n\tprivate async wake(): Promise<void> {\n\t\tif (this.disposed || this.busy) return;\n\t\tthis.busy = true;\n\t\ttry {\n\t\t\tconst at = this.clock.now();\n\t\t\tif (this.nextSampleAt !== undefined && this.nextSampleAt <= at) {\n\t\t\t\tawait this.sampleShellOutput(at);\n\t\t\t\tthis.nextSampleAt = this.hasSampledShells() ? at + TASK_HEARTBEAT_OUTPUT_SAMPLE_MS : undefined;\n\t\t\t}\n\t\t\tawait this.beat(at);\n\t\t} catch {\n\t\t\t// A failed check skips this boundary; the next one is already scheduled.\n\t\t} finally {\n\t\t\tthis.busy = false;\n\t\t\tthis.arm();\n\t\t}\n\t}\n\n\tprivate async beat(at: number): Promise<void> {\n\t\tif (this.recheckAt !== undefined && this.recheckAt > at) return;\n\t\tthis.recheckAt = undefined;\n\t\tconst entries = [...this.tracked.values()];\n\t\tif (!entries.some((entry) => entry.nextAt !== undefined && entry.nextAt <= at)) return;\n\t\tconst delivery = this.options.delivery();\n\t\tlet unread = false;\n\t\ttry {\n\t\t\tunread = delivery?.hasUnreadHeartbeat() === true;\n\t\t} catch {\n\t\t\tunread = false;\n\t\t}\n\t\tif (unread) {\n\t\t\t// One outstanding notice per owner: due tasks stay owed and join the next notice instead of stacking.\n\t\t\tthis.recheckAt = at + TASK_HEARTBEAT_RECHECK_MS;\n\t\t\treturn;\n\t\t}\n\t\tconst batch = entries\n\t\t\t.filter(\n\t\t\t\t(entry) =>\n\t\t\t\t\tentry.nextAt !== undefined &&\n\t\t\t\t\tentry.nextAt <= at + Math.min(TASK_HEARTBEAT_COALESCE_MS, entry.intervalMs / 4),\n\t\t\t)\n\t\t\t.sort(byBoundary);\n\t\tfor (const entry of batch)\n\t\t\tentry.nextAt = nextBoundary(entry.startedAt, entry.intervalMs, Math.max(at, entry.nextAt ?? at));\n\t\tif (!delivery) return;\n\t\tconst details = await this.describe(batch, at);\n\t\t// A task that finished while its notice was being prepared is no longer mentioned.\n\t\tconst live = (taskId: string) => this.tracked.has(taskId as TaskId);\n\t\tdetails.tasks = details.tasks.filter((task) => live(task.taskId));\n\t\tdetails.more = details.more.filter((task) => live(task.taskId));\n\t\tdetails.moreCount = batch.slice(TASK_HEARTBEAT_DETAIL_LIMIT).filter((entry) => live(entry.taskId)).length;\n\t\tif (details.tasks.length === 0 || this.disposed) return;\n\t\tawait delivery.deliver({\n\t\t\tcustomType: TASK_HEARTBEAT_MESSAGE_TYPE,\n\t\t\tcontent: formatTaskHeartbeat(details),\n\t\t\tdisplay: true,\n\t\t\tdetails,\n\t\t});\n\t}\n\n\tprivate async describe(batch: readonly Tracked[], at: number): Promise<TaskHeartbeatDetails> {\n\t\tconst detailed = batch.slice(0, TASK_HEARTBEAT_DETAIL_LIMIT);\n\t\tconst rest = batch.slice(TASK_HEARTBEAT_DETAIL_LIMIT);\n\t\tconst tasks: TaskHeartbeatTask[] = [];\n\t\tfor (const entry of detailed) tasks.push(await this.describeTask(entry, at));\n\t\treturn {\n\t\t\tscheduledAt: at,\n\t\t\ttasks,\n\t\t\tmore: rest.slice(0, TASK_HEARTBEAT_LIST_LIMIT).map((entry) => ({\n\t\t\t\ttaskId: entry.taskId,\n\t\t\t\tkind: entry.record.kind,\n\t\t\t\ttitle: singleLine(entry.record.title, TITLE_CHARS),\n\t\t\t})),\n\t\t\tmoreCount: rest.length,\n\t\t};\n\t}\n\n\tprivate async describeTask(entry: Tracked, at: number): Promise<TaskHeartbeatTask> {\n\t\tconst task = entry.record;\n\t\tconst lease = this.lease(entry.taskId);\n\t\tconst described: TaskHeartbeatTask = {\n\t\t\ttaskId: entry.taskId,\n\t\t\tkind: task.kind,\n\t\t\ttitle: singleLine(task.title, TITLE_CHARS),\n\t\t\t...(task.agentName === undefined ? {} : { agentName: singleLine(task.agentName, TITLE_CHARS) }),\n\t\t\t...(task.kind === \"agent\" && task.model !== undefined ? { model: task.model } : {}),\n\t\t\telapsedMs: Math.max(0, at - entry.startedAt),\n\t\t\tintervalMs: entry.intervalMs,\n\t\t};\n\t\tif (task.kind === \"command\" && lease) {\n\t\t\tconst end = await this.observeOutput(entry, lease, at);\n\t\t\tif (end !== undefined) described.output = { bytes: Number(end), tail: await this.outputTail(lease, end) };\n\t\t}\n\t\tif (entry.lastActivityAt !== undefined) described.lastActivityAgoMs = Math.max(0, at - entry.lastActivityAt);\n\t\tif (task.kind === \"agent\") {\n\t\t\tconst entries = lease ? taskOwnTranscriptEntries(lease) : undefined;\n\t\t\tif (task.currentAction)\n\t\t\t\tdescribed.currentAction = {\n\t\t\t\t\ttool: singleLine(task.currentAction.tool, 60),\n\t\t\t\t\ttext: singleLine(task.currentAction.text, LINE_CHARS),\n\t\t\t\t\t// The record keeps the last reported action after its tool returns. The child's own history says\n\t\t\t\t\t// whether a call is still outstanding: its last message is an assistant turn that called tools.\n\t\t\t\t\trunning: entries !== undefined && endsInToolCall(entries),\n\t\t\t\t};\n\t\t\tif (task.metrics?.toolCount !== undefined) described.toolCount = task.metrics.toolCount;\n\t\t\tif (task.metrics?.tokenCount !== undefined) described.tokenCount = task.metrics.tokenCount;\n\t\t\tif (entries)\n\t\t\t\tdescribed.turnCount = entries.filter(\n\t\t\t\t\t(item) => item.type === \"message\" && item.message.role === \"assistant\",\n\t\t\t\t).length;\n\t\t}\n\t\tif (task.attention.kind === \"input-needed\")\n\t\t\tdescribed.attention = `waiting for input: ${singleLine(task.attention.prompt, LINE_CHARS)}`;\n\t\telse if (task.attention.kind === \"no-recent-activity\") described.attention = \"no recent activity\";\n\t\treturn described;\n\t}\n\n\tprivate lease(taskId: TaskId): TaskLease | undefined {\n\t\tconst found = this.options.supervisor.lookupTask(this.options.owner, taskId);\n\t\treturn found.ok ? found.value : undefined;\n\t}\n\n\tprivate async sampleShellOutput(at: number): Promise<void> {\n\t\tfor (const entry of [...this.tracked.values()]) {\n\t\t\tif (entry.record.kind !== \"command\" || entry.intervalMs === 0) continue;\n\t\t\tconst lease = this.lease(entry.taskId);\n\t\t\tif (lease) await this.observeOutput(entry, lease, at);\n\t\t}\n\t}\n\n\t/** Current retained output size; growth since the last look dates the shell's last output. */\n\tprivate async observeOutput(entry: Tracked, lease: TaskLease, at: number): Promise<bigint | undefined> {\n\t\tconst end = await findOutputEnd(this.options.supervisor, lease, entry.outputEnd);\n\t\tif (end === undefined) return undefined;\n\t\tif (end > entry.outputEnd) {\n\t\t\tentry.outputEnd = end;\n\t\t\tentry.lastActivityAt = at;\n\t\t}\n\t\treturn end;\n\t}\n\n\tprivate async outputTail(lease: TaskLease, end: bigint): Promise<string[]> {\n\t\tif (end === 0n) return [];\n\t\tconst start = end > BigInt(OUTPUT_TAIL_BYTES) ? end - BigInt(OUTPUT_TAIL_BYTES) : 0n;\n\t\tconst page = await this.options.supervisor.readTaskOutput(lease, {\n\t\t\tstart: String(start),\n\t\t\tmaximumBytes: OUTPUT_TAIL_BYTES,\n\t\t});\n\t\tif (!page.ok) return [];\n\t\tconst lines = taskOutputText(page.value).split(\"\\n\");\n\t\t// The window can open mid-line; that fragment is not a line of output.\n\t\tif (start > 0n) lines.shift();\n\t\treturn lines\n\t\t\t.map((line) => {\n\t\t\t\t// A CRLF ending is not a redraw; a carriage return inside a line redraws it, so its last state counts.\n\t\t\t\tconst ended = line.replace(/\\r+$/, \"\");\n\t\t\t\treturn singleLine(ended.slice(ended.lastIndexOf(\"\\r\") + 1), LINE_CHARS).trimEnd();\n\t\t\t})\n\t\t\t.filter((line) => line.trim().length > 0)\n\t\t\t.slice(-OUTPUT_TAIL_LINES);\n\t}\n}\n\n/**\n * Retained output size of a running shell, found with one-byte reads: a read past the end returns nothing. Gallops\n * from the last known size, then bisects, so a large burst of output costs a few dozen tiny reads, not a full scan.\n */\nasync function findOutputEnd(supervisor: TaskSupervisor, lease: TaskLease, from: bigint): Promise<bigint | undefined> {\n\tconst probe = async (offset: bigint): Promise<{ before: boolean; end: bigint } | undefined> => {\n\t\tconst page = await supervisor.readTaskOutput(lease, { start: String(offset), maximumBytes: 1 });\n\t\tif (!page.ok) return undefined;\n\t\tif (page.value.nextOffset !== undefined) return { before: true, end: BigInt(page.value.nextOffset) };\n\t\tlet end = offset;\n\t\tfor (const chunk of page.value.chunks) if (BigInt(chunk.offsets.end) > end) end = BigInt(chunk.offsets.end);\n\t\tfor (const range of page.value.omittedRanges) if (BigInt(range.end) > end) end = BigInt(range.end);\n\t\t// No byte beyond this page: the output ends at its last byte, or at or before `offset` when it is empty.\n\t\treturn { before: end > offset, end };\n\t};\n\tlet low = from;\n\tconst first = await probe(low);\n\tif (!first) return undefined;\n\tif (!first.before) return first.end > from ? first.end : from;\n\tlet step = 65_536n;\n\tlet high = low + step;\n\tfor (;;) {\n\t\tconst result = await probe(high);\n\t\tif (!result) return undefined;\n\t\tif (!result.before) {\n\t\t\tif (result.end > high) return result.end;\n\t\t\tbreak;\n\t\t}\n\t\tlow = high;\n\t\tstep *= 2n;\n\t\thigh = low + step;\n\t}\n\t// The output ends after `low` and at or before `high`.\n\twhile (high - low > 1n) {\n\t\tconst middle = low + (high - low) / 2n;\n\t\tconst result = await probe(middle);\n\t\tif (!result) return undefined;\n\t\tif (result.before) low = middle;\n\t\telse if (result.end > middle) return result.end;\n\t\telse high = middle;\n\t}\n\treturn high;\n}\n"]}
|
|
@@ -67,6 +67,8 @@ export type TrustedTaskHost = {
|
|
|
67
67
|
authorizeLaunch(intent: C.AgentIntent): void;
|
|
68
68
|
resourceAdmission?: import("../sdk-types.ts").ResourceAdmission;
|
|
69
69
|
onTaskSettled?(ref: C.NativeTaskRef, receipt: C.SettlementReceipt): void;
|
|
70
|
+
/** Heartbeat cadence for a launch that names none, fixed on its record at admission; omitted, none is recorded. */
|
|
71
|
+
defaultHeartbeatMs?(): number | undefined;
|
|
70
72
|
createRunner(context: FakeRunnerContext, intent: C.AgentIntent): FakeExecution;
|
|
71
73
|
};
|
|
72
74
|
/** Reads only history bound by the admitted runner, never a caller-selected session. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/supervisor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAEzC,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,YAAY,CAAC,GAAG;IACxF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACjD,6EAA6E;IAC7E,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACrE,mBAAmB,CAAC,IAAI,YAAY,GAAG,SAAS,CAAC;CACjD,CAAC;AAEF,eAAO,MAAM,4BAA4B,QAAQ,CAAC;AAClD,qFAAqF;AACrF,cAAM,UAAU;;IAEf,MAAM,IAAI,KAAK,CAEd;CACD;AACD,cAAM,cAAe,SAAQ,UAAU;IACtC,QAAQ,CAAC,IAAI,UAAU;CACvB;AACD,cAAM,eAAgB,SAAQ,UAAU;IACvC,QAAQ,CAAC,IAAI,WAAW;CACxB;AACD,cAAM,cAAe,SAAQ,UAAU;IACtC,QAAQ,CAAC,IAAI,UAAU;CACvB;AACD,cAAM,cAAe,SAAQ,UAAU;IACtC,QAAQ,CAAC,IAAI,UAAU;CACvB;AACD,cAAM,sBAAuB,SAAQ,UAAU;IAC9C,QAAQ,CAAC,IAAI,kBAAkB;CAC/B;AACD,cAAM,eAAgB,SAAQ,UAAU;IACvC,QAAQ,CAAC,IAAI,WAAW;CACxB;AACD,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;AACzC,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;AACzC,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC;AACvC,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC;AACvC,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,GAAG,EAAE,CAAC,CAAC,aAAa,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IACnF,+EAA+E;IAC/E,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpD,oGAAoG;IACpG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACtE,mFAAmF;IACnF,sBAAsB,IAAI,IAAI,CAAC;CAC/B,CAAC;AACF,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAC3F,MAAM,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC;IACpB,oEAAoE;IACpE,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC,qBAAqB,CAAA;KAAE,CAAC;IAC3C,2EAA2E;IAC3E,sBAAsB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,aAAa,GAAG,IAAI,CAAC;IACvD,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;IAC7C,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,EAAE,iBAAiB,CAAC;IAChE,aAAa,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,iBAAiB,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC,CAAC,WAAW,GAAG,aAAa,CAAC;CAC/E,CAAC;
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -197,10 +197,26 @@ function observation(value) {
|
|
|
197
197
|
// Native records own lifecycle and mutable execution metadata. Routing provenance is
|
|
198
198
|
// immutable host metadata, retained for the same environment lifetime as task records.
|
|
199
199
|
const routerSelections = new Map();
|
|
200
|
+
// Launch heartbeat cadences, keyed by owner and launch operation rather than task id: a command's
|
|
201
|
+
// record can be observed before its native start resolves, and it must already carry its cadence.
|
|
202
|
+
const launchHeartbeats = new Map();
|
|
203
|
+
function launchKey(ownerId, operation) {
|
|
204
|
+
return `${ownerId}\0${operation}`;
|
|
205
|
+
}
|
|
206
|
+
/** Record a launch's cadence before native admission; returns the key to forget if admission fails. */
|
|
207
|
+
function rememberLaunchHeartbeat(ownerId, operation, heartbeatMs) {
|
|
208
|
+
const key = launchKey(ownerId, operation);
|
|
209
|
+
// A replayed operation keeps the cadence of the launch that admitted it.
|
|
210
|
+
if (heartbeatMs === undefined || launchHeartbeats.has(key))
|
|
211
|
+
return undefined;
|
|
212
|
+
launchHeartbeats.set(key, heartbeatMs);
|
|
213
|
+
return key;
|
|
214
|
+
}
|
|
200
215
|
function record(value) {
|
|
201
216
|
if (value.kind !== "agent" && value.kind !== "command")
|
|
202
217
|
throw new Error(`Invalid native task kind: ${value.kind}`);
|
|
203
218
|
const routerSelection = routerSelections.get(value.ref.taskId);
|
|
219
|
+
const heartbeatMs = launchHeartbeats.get(launchKey(value.ref.ownerId, value.launchOperationId));
|
|
204
220
|
return {
|
|
205
221
|
ref: reference(value.ref),
|
|
206
222
|
launchOperationId: value.launchOperationId,
|
|
@@ -213,6 +229,7 @@ function record(value) {
|
|
|
213
229
|
...(value.model === undefined ? {} : { model: value.model }),
|
|
214
230
|
...(value.thinking === undefined ? {} : { thinking: value.thinking }),
|
|
215
231
|
...(routerSelection ? { routerSelection } : {}),
|
|
232
|
+
...(heartbeatMs === undefined ? {} : { heartbeatMs }),
|
|
216
233
|
execution: execution(value.execution),
|
|
217
234
|
observation: observation(value.observation),
|
|
218
235
|
...(value.wasBackground === undefined ? {} : { wasBackground: value.wasBackground }),
|
|
@@ -580,6 +597,7 @@ export class TaskSupervisor {
|
|
|
580
597
|
const owner = new OwnerCapability();
|
|
581
598
|
Object.freeze(owner);
|
|
582
599
|
const ownerState = {
|
|
600
|
+
id,
|
|
583
601
|
native: lease,
|
|
584
602
|
host: state,
|
|
585
603
|
tasks: new Map(),
|
|
@@ -701,9 +719,11 @@ export class TaskSupervisor {
|
|
|
701
719
|
let release;
|
|
702
720
|
try {
|
|
703
721
|
release = await (currentResourceAdmission() ?? state.host.binding.resourceAdmission)?.acquire("process", signal ? AbortSignal.any([signal, state.closing.signal]) : state.closing.signal);
|
|
722
|
+
const { heartbeatMs, ...nativeIntent } = intent;
|
|
723
|
+
const heartbeatKey = rememberLaunchHeartbeat(state.id, operation, heartbeatMs ?? state.host.binding.defaultHeartbeatMs?.());
|
|
704
724
|
let started;
|
|
705
725
|
try {
|
|
706
|
-
started = await this.#native.startCommandTask(state.native,
|
|
726
|
+
started = await this.#native.startCommandTask(state.native, nativeIntent, operation);
|
|
707
727
|
}
|
|
708
728
|
catch (error) {
|
|
709
729
|
release = undefined;
|
|
@@ -716,8 +736,11 @@ export class TaskSupervisor {
|
|
|
716
736
|
throw new Error(`Command cleanup unconfirmed; process permit retained: ${started.error.code}: ${started.error.message}`);
|
|
717
737
|
}
|
|
718
738
|
const admitted = mapped(started, (lease) => lease, startErrors);
|
|
719
|
-
if (!admitted.ok)
|
|
739
|
+
if (!admitted.ok) {
|
|
740
|
+
if (heartbeatKey !== undefined)
|
|
741
|
+
launchHeartbeats.delete(heartbeatKey);
|
|
720
742
|
return admitted;
|
|
743
|
+
}
|
|
721
744
|
const ref = this.#native.taskReference(admitted.value);
|
|
722
745
|
if (!ref.ok)
|
|
723
746
|
throw new Error(ref.error.message);
|
|
@@ -808,9 +831,14 @@ export class TaskSupervisor {
|
|
|
808
831
|
async startAgentTask(owner, intent, operation, schedule) {
|
|
809
832
|
const state = this.#owner(owner);
|
|
810
833
|
state.host.binding.authorizeLaunch(intent);
|
|
811
|
-
const
|
|
812
|
-
|
|
834
|
+
const { heartbeatMs, ...nativeIntent } = intent;
|
|
835
|
+
const heartbeatKey = rememberLaunchHeartbeat(state.id, operation, heartbeatMs ?? state.host.binding.defaultHeartbeatMs?.());
|
|
836
|
+
const admitted = mapped(this.#native.startAgentTask(state.native, nativeIntent, operation), (lease) => lease, startErrors);
|
|
837
|
+
if (!admitted.ok) {
|
|
838
|
+
if (heartbeatKey !== undefined)
|
|
839
|
+
launchHeartbeats.delete(heartbeatKey);
|
|
813
840
|
return admitted;
|
|
841
|
+
}
|
|
814
842
|
const ref = this.#native.taskReference(admitted.value);
|
|
815
843
|
if (!ref.ok)
|
|
816
844
|
throw new Error(ref.error.message);
|