pi-agent-replicate 0.0.0-stage → 1.4.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.
Files changed (82) hide show
  1. package/AGENTS.md +27 -0
  2. package/CHANGELOG.md +152 -0
  3. package/CONTEXT.md +63 -0
  4. package/LICENSE +21 -0
  5. package/README.md +68 -2
  6. package/RELEASING.md +132 -0
  7. package/assets/banner.png +0 -0
  8. package/config.json.example +6 -0
  9. package/docs/README.md +23 -0
  10. package/docs/adr/0003-installable-role-packs.md +29 -0
  11. package/docs/adr/0008-adopt-pi-only-subagent-execution.md +22 -0
  12. package/docs/adr/0009-durable-recursive-subagent-ownership.md +23 -0
  13. package/docs/adr/0010-public-subagent-tree-registry.md +16 -0
  14. package/docs/configuration.md +14 -0
  15. package/docs/development.md +27 -0
  16. package/docs/public-subagent-tree-api.md +58 -0
  17. package/docs/retained-roles.md +71 -0
  18. package/docs/settled-lifecycle-contract.md +71 -0
  19. package/docs/worktree-subagents.md +58 -0
  20. package/examples/role-pack/extension.ts +18 -0
  21. package/examples/role-pack/package.json +16 -0
  22. package/examples/role-pack/roles/example-reviewer.md +12 -0
  23. package/index.d.ts +116 -0
  24. package/index.js +1064 -0
  25. package/index.ts +155 -0
  26. package/package.json +70 -4
  27. package/pi-extension/subagents/activity.js +575 -0
  28. package/pi-extension/subagents/activity.ts +980 -0
  29. package/pi-extension/subagents/completion.js +184 -0
  30. package/pi-extension/subagents/completion.ts +229 -0
  31. package/pi-extension/subagents/delivery-drain.js +15 -0
  32. package/pi-extension/subagents/delivery-drain.ts +20 -0
  33. package/pi-extension/subagents/delivery-files.js +86 -0
  34. package/pi-extension/subagents/delivery-files.ts +105 -0
  35. package/pi-extension/subagents/delivery-log.js +51 -0
  36. package/pi-extension/subagents/delivery-log.ts +51 -0
  37. package/pi-extension/subagents/delivery-payload.ts +55 -0
  38. package/pi-extension/subagents/delivery.ts +219 -0
  39. package/pi-extension/subagents/execution-owner.js +30 -0
  40. package/pi-extension/subagents/execution-owner.ts +38 -0
  41. package/pi-extension/subagents/herdr.js +645 -0
  42. package/pi-extension/subagents/herdr.ts +927 -0
  43. package/pi-extension/subagents/index.ts +3400 -0
  44. package/pi-extension/subagents/launch.js +774 -0
  45. package/pi-extension/subagents/launch.ts +1124 -0
  46. package/pi-extension/subagents/lifecycle.js +477 -0
  47. package/pi-extension/subagents/lifecycle.ts +582 -0
  48. package/pi-extension/subagents/lineage.js +806 -0
  49. package/pi-extension/subagents/lineage.ts +841 -0
  50. package/pi-extension/subagents/replica-background.js +32 -0
  51. package/pi-extension/subagents/replica-background.ts +38 -0
  52. package/pi-extension/subagents/replica-branch.ts +45 -0
  53. package/pi-extension/subagents/replica-directory-settings.ts +51 -0
  54. package/pi-extension/subagents/replica-history.js +22 -0
  55. package/pi-extension/subagents/replica-history.ts +21 -0
  56. package/pi-extension/subagents/replica-work.js +326 -0
  57. package/pi-extension/subagents/replica-work.ts +336 -0
  58. package/pi-extension/subagents/replica-worker.js +74 -0
  59. package/pi-extension/subagents/replica-worker.ts +71 -0
  60. package/pi-extension/subagents/replication.ts +99 -0
  61. package/pi-extension/subagents/result-delivery-queue.ts +33 -0
  62. package/pi-extension/subagents/role-catalog.js +123 -0
  63. package/pi-extension/subagents/role-catalog.ts +24 -0
  64. package/pi-extension/subagents/runtime-routing.js +154 -0
  65. package/pi-extension/subagents/runtime-routing.ts +239 -0
  66. package/pi-extension/subagents/session-binding.js +346 -0
  67. package/pi-extension/subagents/session-binding.ts +339 -0
  68. package/pi-extension/subagents/session-work.js +219 -0
  69. package/pi-extension/subagents/session-work.ts +167 -0
  70. package/pi-extension/subagents/session.js +318 -0
  71. package/pi-extension/subagents/session.ts +448 -0
  72. package/pi-extension/subagents/settled-contract.js +47 -0
  73. package/pi-extension/subagents/settled-contract.ts +175 -0
  74. package/pi-extension/subagents/source-state.js +180 -0
  75. package/pi-extension/subagents/source-state.ts +184 -0
  76. package/pi-extension/subagents/status.ts +493 -0
  77. package/pi-extension/subagents/subagent-done.js +805 -0
  78. package/pi-extension/subagents/subagent-done.ts +878 -0
  79. package/pi-extension/subagents/terminal.js +111 -0
  80. package/pi-extension/subagents/terminal.ts +208 -0
  81. package/pi-extension/subagents/watchdog.js +122 -0
  82. package/pi-extension/subagents/watchdog.ts +171 -0
@@ -0,0 +1,575 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ const ACTIVITY_UPDATE_THROTTLE_MS = 500;
4
+ const MAX_WRITE_FAILURES = 3;
5
+ const KNOWN_PHASES = new Set([
6
+ "starting",
7
+ "active",
8
+ "waiting",
9
+ "done"
10
+ ]);
11
+ const KNOWN_SCOPES = new Set([
12
+ "agent",
13
+ "turn",
14
+ "provider",
15
+ "streaming",
16
+ "tool",
17
+ "compaction"
18
+ ]);
19
+ const KNOWN_OUTCOMES = new Set([
20
+ "clean",
21
+ "empty",
22
+ "error",
23
+ "intentional-abort",
24
+ "unexpected-abort"
25
+ ]);
26
+ const KNOWN_COMPACTION_REASONS = new Set([
27
+ "manual",
28
+ "threshold",
29
+ "overflow"
30
+ ]);
31
+ const KNOWN_EVENTS = new Set([
32
+ "session_start",
33
+ "session_before_compact",
34
+ "session_compact",
35
+ "session_compact_failed",
36
+ "input",
37
+ "before_agent_start",
38
+ "agent_start",
39
+ "agent_end",
40
+ "agent_settled",
41
+ "turn_start",
42
+ "turn_end",
43
+ "before_provider_request",
44
+ "after_provider_response",
45
+ "message_update",
46
+ "tool_execution_start",
47
+ "tool_call",
48
+ "tool_execution_update",
49
+ "tool_result",
50
+ "tool_execution_end",
51
+ "caller_ping",
52
+ "subagent_done",
53
+ "session_shutdown"
54
+ ]);
55
+ const MAX_ACTIVITY_STRING_LENGTH = 200;
56
+ export function getSubagentActivityFile(artifactDir, runningChildId) {
57
+ return join(artifactDir, "subagent-activity", `${runningChildId}.json`);
58
+ }
59
+ export function getSubagentSettledEventsFile(artifactDir, runningChildId) {
60
+ return join(artifactDir, "subagent-activity", `${runningChildId}.settled.jsonl`);
61
+ }
62
+ function isSettledActivityEvent(value, childId) {
63
+ const object = requireObject(value);
64
+ return !!object && object.schema === 1 && object.childId === childId && Number.isInteger(object.sequence) && Number.isFinite(object.recordedAt) && typeof object.outcome === "string" && KNOWN_OUTCOMES.has(object.outcome) && (object.turnIndex == null || Number.isInteger(object.turnIndex)) && (object.assistantId == null || typeof object.assistantId === "string") && (object.stopReason == null || typeof object.stopReason === "string") && (object.errorMessage == null || typeof object.errorMessage === "string") && (object.empty == null || typeof object.empty === "boolean");
65
+ }
66
+ export function orderSettledActivityEvents(events) {
67
+ return [
68
+ ...events
69
+ ].sort((left, right)=>left.sequence - right.sequence || (left.assistantId ?? "").localeCompare(right.assistantId ?? ""));
70
+ }
71
+ export function readSubagentSettledEventsFile(eventsFile, expectedRunningChildId) {
72
+ if (!existsSync(eventsFile)) return [];
73
+ let text;
74
+ try {
75
+ text = readFileSync(eventsFile, "utf8");
76
+ } catch {
77
+ return [];
78
+ }
79
+ const lines = text.split("\n");
80
+ const events = [];
81
+ for(let index = 0; index < lines.length; index += 1){
82
+ const line = lines[index]?.trim();
83
+ if (!line) continue;
84
+ try {
85
+ const parsed = JSON.parse(line);
86
+ if (isSettledActivityEvent(parsed, expectedRunningChildId)) events.push(parsed);
87
+ } catch {
88
+ if (index === lines.length - 1) break;
89
+ }
90
+ }
91
+ return events;
92
+ }
93
+ function requireObject(value) {
94
+ if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
95
+ return value;
96
+ }
97
+ function validateFiniteNumber(object, fieldName) {
98
+ return Number.isFinite(object[fieldName]) ? null : `${fieldName} must be finite`;
99
+ }
100
+ function validateOptionalFiniteNumber(object, fieldName) {
101
+ const value = object[fieldName];
102
+ return value == null || Number.isFinite(value) ? null : `${fieldName} must be finite when present`;
103
+ }
104
+ function validateInteger(object, fieldName) {
105
+ return Number.isInteger(object[fieldName]) ? null : `${fieldName} must be an integer`;
106
+ }
107
+ function validateOptionalInteger(object, fieldName) {
108
+ const value = object[fieldName];
109
+ return value == null || Number.isInteger(value) ? null : `${fieldName} must be an integer when present`;
110
+ }
111
+ function validateBoolean(object, fieldName) {
112
+ return typeof object[fieldName] === "boolean" ? null : `${fieldName} must be a boolean`;
113
+ }
114
+ function validateOptionalBoolean(object, fieldName) {
115
+ const value = object[fieldName];
116
+ return value == null || typeof value === "boolean" ? null : `${fieldName} must be a boolean when present`;
117
+ }
118
+ function validateOptionalActivityString(object, fieldName) {
119
+ const value = object[fieldName];
120
+ if (value == null) return null;
121
+ if (typeof value !== "string") return `${fieldName} must be a string when present`;
122
+ if (/\r|\n/.test(value)) return `${fieldName} must not contain newlines`;
123
+ return value.length <= MAX_ACTIVITY_STRING_LENGTH ? null : `${fieldName} is too long`;
124
+ }
125
+ function invalidActivity(error) {
126
+ return {
127
+ ok: false,
128
+ reason: "invalid",
129
+ error
130
+ };
131
+ }
132
+ function validateActivity(value, expectedRunningChildId) {
133
+ const object = requireObject(value);
134
+ if (!object) return invalidActivity("activity must be an object");
135
+ if (object.version !== 1) return invalidActivity("unsupported activity version");
136
+ if (typeof object.runningChildId !== "string") return invalidActivity("runningChildId must be a string");
137
+ if (object.runningChildId !== expectedRunningChildId) return {
138
+ ok: false,
139
+ reason: "wrong-id"
140
+ };
141
+ if (typeof object.latestEvent !== "string" || !KNOWN_EVENTS.has(object.latestEvent)) {
142
+ return invalidActivity("unknown latestEvent");
143
+ }
144
+ if (typeof object.phase !== "string" || !KNOWN_PHASES.has(object.phase)) {
145
+ return invalidActivity("unknown activity phase");
146
+ }
147
+ if (object.activeScope != null && (typeof object.activeScope !== "string" || !KNOWN_SCOPES.has(object.activeScope))) {
148
+ return invalidActivity("unknown activeScope");
149
+ }
150
+ if (object.compactionReason != null && (typeof object.compactionReason !== "string" || !KNOWN_COMPACTION_REASONS.has(object.compactionReason))) {
151
+ return invalidActivity("unknown compactionReason");
152
+ }
153
+ if (object.settledOutcome != null && (typeof object.settledOutcome !== "string" || !KNOWN_OUTCOMES.has(object.settledOutcome))) {
154
+ return invalidActivity("unknown settledOutcome");
155
+ }
156
+ const settledEmptyError = object.settledEmpty != null && typeof object.settledEmpty !== "boolean" ? "settledEmpty must be a boolean when present" : null;
157
+ if (settledEmptyError) return invalidActivity(settledEmptyError);
158
+ const validationError = [
159
+ validateFiniteNumber(object, "createdAt"),
160
+ validateFiniteNumber(object, "updatedAt"),
161
+ validateInteger(object, "sequence"),
162
+ validateBoolean(object, "agentActive"),
163
+ validateBoolean(object, "turnActive"),
164
+ validateBoolean(object, "providerActive"),
165
+ validateBoolean(object, "toolActive"),
166
+ validateOptionalBoolean(object, "compactionActive"),
167
+ validateOptionalBoolean(object, "compactionWillRetry"),
168
+ validateOptionalBoolean(object, "compactionAborted"),
169
+ validateOptionalActivityString(object, "compactionErrorMessage"),
170
+ validateOptionalFiniteNumber(object, "activeSince"),
171
+ validateOptionalFiniteNumber(object, "waitingSince"),
172
+ validateOptionalInteger(object, "turnIndex"),
173
+ validateOptionalFiniteNumber(object, "toolStartedAt"),
174
+ validateOptionalFiniteNumber(object, "toolEndedAt"),
175
+ validateOptionalActivityString(object, "messageEventType"),
176
+ validateOptionalActivityString(object, "toolCallId"),
177
+ validateOptionalActivityString(object, "toolName"),
178
+ validateOptionalActivityString(object, "settledAssistantId"),
179
+ validateOptionalActivityString(object, "settledStopReason"),
180
+ validateOptionalActivityString(object, "settledErrorMessage")
181
+ ].find((error)=>error != null);
182
+ if (validationError) return invalidActivity(validationError);
183
+ return {
184
+ ok: true,
185
+ activity: object
186
+ };
187
+ }
188
+ export function readSubagentActivityFile(activityFile, expectedRunningChildId) {
189
+ if (!existsSync(activityFile)) return {
190
+ ok: false,
191
+ reason: "missing"
192
+ };
193
+ let parsed;
194
+ try {
195
+ parsed = JSON.parse(readFileSync(activityFile, "utf8"));
196
+ } catch (error) {
197
+ const message = error instanceof Error ? error.message : String(error);
198
+ return {
199
+ ok: false,
200
+ reason: "invalid",
201
+ error: message
202
+ };
203
+ }
204
+ return validateActivity(parsed, expectedRunningChildId);
205
+ }
206
+ export function writeSubagentActivityFile(activityFile, activity) {
207
+ const dir = dirname(activityFile);
208
+ mkdirSync(dir, {
209
+ recursive: true
210
+ });
211
+ const tempFile = join(dir, `${activity.runningChildId}.json.${process.pid}.${activity.sequence}.tmp`);
212
+ try {
213
+ writeFileSync(tempFile, `${JSON.stringify(activity)}\n`, "utf8");
214
+ renameSync(tempFile, activityFile);
215
+ } catch (error) {
216
+ try {
217
+ unlinkSync(tempFile);
218
+ } catch (cleanupError) {
219
+ void cleanupError;
220
+ }
221
+ throw error;
222
+ }
223
+ }
224
+ function createNoopRecorder() {
225
+ return {
226
+ sessionStart () {},
227
+ compactionStart () {},
228
+ compactionSucceeded () {},
229
+ compactionFailed () {},
230
+ input () {},
231
+ beforeAgentStart () {},
232
+ agentStart () {},
233
+ agentEndWaiting () {},
234
+ agentEndDone () {},
235
+ agentSettled () {},
236
+ turnStart () {},
237
+ turnEnd () {},
238
+ beforeProviderRequest () {},
239
+ afterProviderResponse () {},
240
+ messageUpdate () {},
241
+ toolExecutionStart () {},
242
+ toolCall () {},
243
+ toolExecutionUpdate () {},
244
+ toolResult () {},
245
+ toolExecutionEnd () {},
246
+ callerPing () {},
247
+ subagentDone () {},
248
+ sessionShutdown () {}
249
+ };
250
+ }
251
+ function clearActiveState(activity) {
252
+ activity.agentActive = false;
253
+ activity.turnActive = false;
254
+ activity.providerActive = false;
255
+ activity.toolActive = false;
256
+ activity.compactionActive = false;
257
+ delete activity.activeScope;
258
+ delete activity.activeSince;
259
+ }
260
+ function refreshActiveScope(activity) {
261
+ if (activity.compactionActive) {
262
+ activity.phase = "active";
263
+ activity.activeScope = "compaction";
264
+ return;
265
+ }
266
+ if (activity.toolActive) {
267
+ activity.phase = "active";
268
+ activity.activeScope = "tool";
269
+ return;
270
+ }
271
+ if (activity.providerActive) {
272
+ activity.phase = "active";
273
+ activity.activeScope = "provider";
274
+ return;
275
+ }
276
+ if (activity.turnActive) {
277
+ activity.phase = "active";
278
+ activity.activeScope = "turn";
279
+ return;
280
+ }
281
+ if (activity.agentActive) {
282
+ activity.phase = "active";
283
+ activity.activeScope = "agent";
284
+ return;
285
+ }
286
+ delete activity.activeScope;
287
+ delete activity.activeSince;
288
+ }
289
+ function markActive(activity, scope, now, resetActiveSince = false) {
290
+ activity.phase = "active";
291
+ activity.activeScope = scope;
292
+ if (activity.activeSince == null || resetActiveSince) activity.activeSince = now;
293
+ delete activity.waitingSince;
294
+ }
295
+ function finishCompactionActivity(activity, observedAt) {
296
+ activity.compactionActive = false;
297
+ refreshActiveScope(activity);
298
+ if (activity.activeScope) return;
299
+ activity.phase = "waiting";
300
+ activity.waitingSince = observedAt;
301
+ }
302
+ export function createSubagentActivityRecorder(params) {
303
+ const runningChildId = params.runningChildId?.trim();
304
+ const activityFile = params.activityFile?.trim();
305
+ const settledEventsFile = params.settledEventsFile?.trim();
306
+ if (!runningChildId || !activityFile) return createNoopRecorder();
307
+ const activityPath = activityFile;
308
+ const now = params.now ?? (()=>Date.now());
309
+ const createdAt = now();
310
+ const activity = {
311
+ version: 1,
312
+ runningChildId,
313
+ createdAt,
314
+ updatedAt: createdAt,
315
+ sequence: 0,
316
+ latestEvent: "session_start",
317
+ phase: "starting",
318
+ agentActive: false,
319
+ turnActive: false,
320
+ providerActive: false,
321
+ toolActive: false,
322
+ compactionActive: false
323
+ };
324
+ let disabled = false;
325
+ let failureCount = 0;
326
+ let lastFlushAt = 0;
327
+ let pendingFlush = null;
328
+ function clearPendingFlush() {
329
+ if (!pendingFlush) return;
330
+ clearTimeout(pendingFlush);
331
+ pendingFlush = null;
332
+ }
333
+ function disable() {
334
+ disabled = true;
335
+ clearPendingFlush();
336
+ }
337
+ function flushNow() {
338
+ if (disabled) return;
339
+ try {
340
+ writeSubagentActivityFile(activityPath, activity);
341
+ lastFlushAt = now();
342
+ failureCount = 0;
343
+ } catch {
344
+ failureCount += 1;
345
+ if (failureCount >= MAX_WRITE_FAILURES) disable();
346
+ }
347
+ }
348
+ function scheduleFlush() {
349
+ if (disabled || pendingFlush) return;
350
+ const remainingMs = Math.max(0, ACTIVITY_UPDATE_THROTTLE_MS - (now() - lastFlushAt));
351
+ if (remainingMs === 0) {
352
+ flushNow();
353
+ return;
354
+ }
355
+ pendingFlush = setTimeout(()=>{
356
+ pendingFlush = null;
357
+ flushNow();
358
+ }, remainingMs);
359
+ }
360
+ function record(latestEvent, update, flush) {
361
+ if (disabled) return;
362
+ if (flush === "immediate") clearPendingFlush();
363
+ const observedAt = now();
364
+ activity.latestEvent = latestEvent;
365
+ activity.updatedAt = observedAt;
366
+ activity.sequence += 1;
367
+ update(activity, observedAt);
368
+ if (flush === "immediate") flushNow();
369
+ else scheduleFlush();
370
+ }
371
+ function markDone(latestEvent) {
372
+ record(latestEvent, (current)=>{
373
+ current.phase = "done";
374
+ clearActiveState(current);
375
+ delete current.waitingSince;
376
+ }, "immediate");
377
+ disable();
378
+ }
379
+ return {
380
+ sessionStart () {
381
+ record("session_start", (current)=>{
382
+ current.phase = "starting";
383
+ clearActiveState(current);
384
+ delete current.waitingSince;
385
+ }, "immediate");
386
+ },
387
+ compactionStart (details) {
388
+ record("session_before_compact", (current, observedAt)=>{
389
+ current.compactionActive = true;
390
+ current.compactionReason = details.reason;
391
+ current.compactionWillRetry = details.willRetry;
392
+ delete current.compactionAborted;
393
+ delete current.compactionErrorMessage;
394
+ markActive(current, "compaction", observedAt, true);
395
+ }, "immediate");
396
+ },
397
+ compactionSucceeded (details) {
398
+ record("session_compact", (current, observedAt)=>{
399
+ current.compactionReason = details.reason;
400
+ current.compactionWillRetry = details.willRetry;
401
+ delete current.compactionAborted;
402
+ delete current.compactionErrorMessage;
403
+ finishCompactionActivity(current, observedAt);
404
+ }, "immediate");
405
+ },
406
+ compactionFailed (details) {
407
+ record("session_compact_failed", (current, observedAt)=>{
408
+ current.compactionReason = details.reason;
409
+ current.compactionWillRetry = details.willRetry;
410
+ current.compactionAborted = details.aborted;
411
+ current.compactionErrorMessage = details.errorMessage?.slice(0, MAX_ACTIVITY_STRING_LENGTH);
412
+ finishCompactionActivity(current, observedAt);
413
+ }, "immediate");
414
+ },
415
+ input () {
416
+ record("input", ()=>{}, "immediate");
417
+ },
418
+ beforeAgentStart () {
419
+ record("before_agent_start", (current, observedAt)=>{
420
+ current.agentActive = true;
421
+ markActive(current, "agent", observedAt);
422
+ }, "immediate");
423
+ },
424
+ agentStart () {
425
+ record("agent_start", (current, observedAt)=>{
426
+ current.agentActive = true;
427
+ markActive(current, "agent", observedAt);
428
+ }, "immediate");
429
+ },
430
+ agentEndWaiting () {
431
+ record("agent_end", (current, observedAt)=>{
432
+ clearActiveState(current);
433
+ current.phase = "waiting";
434
+ current.waitingSince = observedAt;
435
+ }, "immediate");
436
+ },
437
+ agentEndDone () {
438
+ markDone("agent_end");
439
+ },
440
+ agentSettled (details) {
441
+ const autoExit = details.autoExit === true && (details.outcome === "clean" || details.outcome === "empty");
442
+ record("agent_settled", (current, observedAt)=>{
443
+ clearActiveState(current);
444
+ current.phase = autoExit ? "done" : "waiting";
445
+ if (autoExit) delete current.waitingSince;
446
+ else current.waitingSince = observedAt;
447
+ current.settledOutcome = details.outcome;
448
+ current.settledAssistantId = details.assistantId;
449
+ current.settledStopReason = details.stopReason;
450
+ current.settledErrorMessage = details.errorMessage;
451
+ current.settledEmpty = details.empty;
452
+ if (details.turnIndex != null) current.turnIndex = details.turnIndex;
453
+ }, "immediate");
454
+ if (settledEventsFile && details.publish !== false) {
455
+ try {
456
+ mkdirSync(dirname(settledEventsFile), {
457
+ recursive: true
458
+ });
459
+ const event = {
460
+ schema: 1,
461
+ childId: runningChildId,
462
+ sequence: activity.sequence,
463
+ recordedAt: activity.updatedAt,
464
+ outcome: details.outcome,
465
+ ...details.turnIndex == null ? {} : {
466
+ turnIndex: details.turnIndex
467
+ },
468
+ ...details.assistantId ? {
469
+ assistantId: details.assistantId
470
+ } : {},
471
+ ...details.stopReason ? {
472
+ stopReason: details.stopReason
473
+ } : {},
474
+ ...details.errorMessage ? {
475
+ errorMessage: details.errorMessage.slice(0, MAX_ACTIVITY_STRING_LENGTH)
476
+ } : {},
477
+ ...details.empty == null ? {} : {
478
+ empty: details.empty
479
+ }
480
+ };
481
+ appendFileSync(settledEventsFile, `${JSON.stringify(event)}\n`, "utf8");
482
+ } catch {}
483
+ }
484
+ if (autoExit) disable();
485
+ },
486
+ turnStart (turnIndex) {
487
+ record("turn_start", (current, observedAt)=>{
488
+ current.agentActive = true;
489
+ current.turnActive = true;
490
+ if (turnIndex != null) current.turnIndex = turnIndex;
491
+ markActive(current, current.toolActive || current.providerActive ? current.activeScope ?? "turn" : "turn", observedAt);
492
+ }, "immediate");
493
+ },
494
+ turnEnd (turnIndex) {
495
+ record("turn_end", (current)=>{
496
+ current.turnActive = false;
497
+ current.providerActive = false;
498
+ current.toolActive = false;
499
+ if (turnIndex != null) current.turnIndex = turnIndex;
500
+ refreshActiveScope(current);
501
+ }, "immediate");
502
+ },
503
+ beforeProviderRequest () {
504
+ record("before_provider_request", (current, observedAt)=>{
505
+ current.providerActive = true;
506
+ markActive(current, "provider", observedAt, true);
507
+ }, "immediate");
508
+ },
509
+ afterProviderResponse () {
510
+ record("after_provider_response", (current)=>{
511
+ current.providerActive = false;
512
+ refreshActiveScope(current);
513
+ }, "immediate");
514
+ },
515
+ messageUpdate (messageEventType) {
516
+ record("message_update", (current, observedAt)=>{
517
+ current.agentActive = true;
518
+ current.turnActive = true;
519
+ current.messageEventType = messageEventType;
520
+ if (!current.toolActive) markActive(current, "streaming", observedAt);
521
+ }, "throttled");
522
+ },
523
+ toolExecutionStart (toolCallId, toolName) {
524
+ record("tool_execution_start", (current, observedAt)=>{
525
+ current.toolActive = true;
526
+ current.toolCallId = toolCallId;
527
+ current.toolName = toolName;
528
+ current.toolStartedAt = observedAt;
529
+ markActive(current, "tool", observedAt, true);
530
+ }, "immediate");
531
+ },
532
+ toolCall (toolCallId, toolName) {
533
+ record("tool_call", (current, observedAt)=>{
534
+ current.toolActive = true;
535
+ current.toolCallId = toolCallId ?? current.toolCallId;
536
+ current.toolName = toolName ?? current.toolName;
537
+ markActive(current, "tool", observedAt);
538
+ }, "immediate");
539
+ },
540
+ toolExecutionUpdate (toolCallId, toolName) {
541
+ record("tool_execution_update", (current, observedAt)=>{
542
+ current.toolActive = true;
543
+ current.toolCallId = toolCallId ?? current.toolCallId;
544
+ current.toolName = toolName ?? current.toolName;
545
+ markActive(current, "tool", observedAt);
546
+ }, "throttled");
547
+ },
548
+ toolResult (toolCallId, toolName) {
549
+ record("tool_result", (current)=>{
550
+ current.toolCallId = toolCallId ?? current.toolCallId;
551
+ current.toolName = toolName ?? current.toolName;
552
+ refreshActiveScope(current);
553
+ }, "immediate");
554
+ },
555
+ toolExecutionEnd (toolCallId, toolName) {
556
+ record("tool_execution_end", (current, observedAt)=>{
557
+ current.toolActive = false;
558
+ current.toolCallId = toolCallId ?? current.toolCallId;
559
+ current.toolName = toolName ?? current.toolName;
560
+ current.toolEndedAt = observedAt;
561
+ refreshActiveScope(current);
562
+ }, "immediate");
563
+ },
564
+ callerPing () {
565
+ markDone("caller_ping");
566
+ },
567
+ subagentDone () {
568
+ markDone("subagent_done");
569
+ },
570
+ sessionShutdown (reason) {
571
+ if (reason === "quit") markDone("session_shutdown");
572
+ else disable();
573
+ }
574
+ };
575
+ }