codex-agent-view 0.4.4 → 0.4.6

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.
@@ -5,6 +5,7 @@ const DEFAULT_LIMITS = Object.freeze({
5
5
  maxAgentsPerSession: 100,
6
6
  maxDiagnostics: 100,
7
7
  maxSessions: 50,
8
+ staleAfterMs: 5 * 60 * 1000,
8
9
  });
9
10
 
10
11
  function positiveInteger(value, name) {
@@ -19,6 +20,8 @@ function createSession(event) {
19
20
  session_id: event.session_id,
20
21
  workspace_label: null,
21
22
  workspace_label_observed_at_ms: null,
23
+ task_summary: null,
24
+ task_summary_observed_at_ms: null,
22
25
  first_seen_at_ms: event.received_at_ms,
23
26
  last_seen_at_ms: event.received_at_ms,
24
27
  agents: new Map(),
@@ -42,6 +45,22 @@ function createSession(event) {
42
45
  };
43
46
  }
44
47
 
48
+ function applyTaskSummary(session, event) {
49
+ if (event.type !== "turn_started") {
50
+ return;
51
+ }
52
+ if (
53
+ session.task_summary_observed_at_ms !== null &&
54
+ event.received_at_ms < session.task_summary_observed_at_ms
55
+ ) {
56
+ return;
57
+ }
58
+ session.task_summary_observed_at_ms = event.received_at_ms;
59
+ if (session.task_summary === null && "task_summary" in event) {
60
+ session.task_summary = event.task_summary;
61
+ }
62
+ }
63
+
45
64
  function applyWorkspaceLabel(session, event) {
46
65
  if (!("workspace_label" in event)) {
47
66
  return;
@@ -56,7 +75,30 @@ function applyWorkspaceLabel(session, event) {
56
75
  session.workspace_label_observed_at_ms = event.received_at_ms;
57
76
  }
58
77
 
59
- function deriveSessionStatus(session) {
78
+ function hasActiveState(session) {
79
+ return (
80
+ session.permission.status === "waiting_for_user" ||
81
+ session.root_turn.status === "running" ||
82
+ [...session.agents.values()].some(({ status }) => status === "running") ||
83
+ [...session.tools.values()].some(({ status }) => status === "running")
84
+ );
85
+ }
86
+
87
+ function isStaleActiveSession(session, nowMs, staleAfterMs) {
88
+ return (
89
+ !session.lifecycle.end_observed &&
90
+ hasActiveState(session) &&
91
+ nowMs - session.last_seen_at_ms >= staleAfterMs
92
+ );
93
+ }
94
+
95
+ function deriveSessionStatus(session, nowMs, staleAfterMs) {
96
+ if (session.lifecycle.end_observed) {
97
+ return "completed";
98
+ }
99
+ if (isStaleActiveSession(session, nowMs, staleAfterMs)) {
100
+ return "completion_not_observed";
101
+ }
60
102
  if (session.permission.status === "waiting_for_user") {
61
103
  return "waiting_for_user";
62
104
  }
@@ -67,12 +109,92 @@ function deriveSessionStatus(session) {
67
109
  ) {
68
110
  return "running";
69
111
  }
70
- if (session.lifecycle.end_observed) {
112
+ if (session.root_turn.status === "completed") {
71
113
  return "completed";
72
114
  }
73
115
  return "observed";
74
116
  }
75
117
 
118
+ function settleRunningState(session, status, options = {}) {
119
+ const turnId = options.turnId;
120
+ const settledAgentIds = new Set();
121
+ const settledToolUseIds = new Set();
122
+ for (const agent of session.agents.values()) {
123
+ if (
124
+ agent.status === "running" &&
125
+ (turnId === undefined || agent.turn_id === turnId)
126
+ ) {
127
+ agent.status = status;
128
+ settledAgentIds.add(agent.agent_id);
129
+ }
130
+ }
131
+ for (const tool of session.tools.values()) {
132
+ if (
133
+ tool.status === "running" &&
134
+ (turnId === undefined || tool.turn_id === turnId)
135
+ ) {
136
+ tool.status = status;
137
+ settledToolUseIds.add(tool.tool_use_id);
138
+ }
139
+ }
140
+ for (const activity of session.recent_activities) {
141
+ if (
142
+ activity.status === "running" &&
143
+ ((activity.type === "subagent_started" &&
144
+ settledAgentIds.has(activity.agent_id)) ||
145
+ (activity.type === "tool_started" &&
146
+ settledToolUseIds.has(activity.tool_use_id)))
147
+ ) {
148
+ activity.status = status;
149
+ }
150
+ }
151
+ }
152
+
153
+ function clearPermission(session, activityStatus) {
154
+ const permission = session.permission;
155
+ if (permission.status !== "waiting_for_user") {
156
+ return;
157
+ }
158
+ for (const activity of session.recent_activities) {
159
+ if (
160
+ activity.type === "permission_requested" &&
161
+ activity.status === "waiting_for_user" &&
162
+ activity.turn_id === permission.turn_id &&
163
+ activity.tool_name === permission.tool_name
164
+ ) {
165
+ activity.status = activityStatus;
166
+ break;
167
+ }
168
+ }
169
+ session.permission = { status: "idle" };
170
+ }
171
+
172
+ function settleRootTurnActivity(session, status) {
173
+ for (const activity of session.recent_activities) {
174
+ if (
175
+ activity.type === "turn_started" &&
176
+ activity.status === "running" &&
177
+ activity.turn_id === session.root_turn.turn_id
178
+ ) {
179
+ activity.status = status;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+
185
+ function resetTransientState(session) {
186
+ session.agents.clear();
187
+ session.tools.clear();
188
+ session.root_turn = {
189
+ status: "idle",
190
+ turn_id: null,
191
+ started_at_ms: null,
192
+ stopped_at_ms: null,
193
+ has_out_of_order_events: false,
194
+ };
195
+ session.permission = { status: "idle" };
196
+ }
197
+
76
198
  function touchMapEntry(map, key, value) {
77
199
  map.delete(key);
78
200
  map.set(key, value);
@@ -91,7 +213,14 @@ function addActivity(session, event, status, limit) {
91
213
  received_at_ms: event.received_at_ms,
92
214
  };
93
215
 
94
- for (const field of ["turn_id", "agent_id", "agent_type", "tool_name", "tool_use_id"]) {
216
+ for (const field of [
217
+ "turn_id",
218
+ "agent_id",
219
+ "agent_type",
220
+ "tool_name",
221
+ "tool_use_id",
222
+ "session_start_source",
223
+ ]) {
95
224
  if (field in event) {
96
225
  activity[field] = event[field];
97
226
  }
@@ -107,11 +236,35 @@ function applySessionEvent(session, event, limits) {
107
236
  const lifecycle = session.lifecycle;
108
237
  if (event.type === "session_started") {
109
238
  const resumedAfterEnd = lifecycle.end_observed;
239
+ const startsNewEpoch =
240
+ resumedAfterEnd ||
241
+ event.session_start_source === "resume" ||
242
+ event.session_start_source === "clear";
243
+ if (event.session_start_source === "compact") {
244
+ if (resumedAfterEnd) {
245
+ return "stale";
246
+ }
247
+ lifecycle.start_observed = true;
248
+ lifecycle.started_at_ms ??= event.received_at_ms;
249
+ addActivity(session, event, "observed", limits.maxActivitiesPerSession);
250
+ return "applied";
251
+ }
252
+ if (lifecycle.start_observed && !startsNewEpoch) {
253
+ return "duplicate";
254
+ }
255
+ if (startsNewEpoch) {
256
+ if (!resumedAfterEnd) {
257
+ settleRootTurnActivity(session, "completion_not_observed");
258
+ clearPermission(session, "completion_not_observed");
259
+ settleRunningState(session, "completion_not_observed");
260
+ }
261
+ resetTransientState(session);
262
+ }
110
263
  lifecycle.start_observed = true;
111
- lifecycle.started_at_ms ??= event.received_at_ms;
264
+ lifecycle.started_at_ms = event.received_at_ms;
112
265
  lifecycle.end_observed = false;
113
266
  lifecycle.ended_at_ms = null;
114
- lifecycle.has_out_of_order_events ||= resumedAfterEnd;
267
+ lifecycle.has_out_of_order_events = false;
115
268
  addActivity(session, event, "observed", limits.maxActivitiesPerSession);
116
269
  return "applied";
117
270
  }
@@ -122,9 +275,11 @@ function applySessionEvent(session, event, limits) {
122
275
  lifecycle.end_observed = true;
123
276
  lifecycle.ended_at_ms = event.received_at_ms;
124
277
  lifecycle.has_out_of_order_events = !lifecycle.start_observed;
278
+ settleRootTurnActivity(session, "completed");
125
279
  session.root_turn.status = "completed";
126
280
  session.root_turn.stopped_at_ms ??= event.received_at_ms;
127
- session.permission = { status: "idle" };
281
+ clearPermission(session, "interrupted");
282
+ settleRunningState(session, "interrupted");
128
283
  addActivity(session, event, "completed", limits.maxActivitiesPerSession);
129
284
  return "applied";
130
285
  }
@@ -132,9 +287,10 @@ function applySessionEvent(session, event, limits) {
132
287
  function applyTurnEvent(session, event, limits) {
133
288
  const turn = session.root_turn;
134
289
  if (event.type === "turn_started") {
135
- if (turn.turn_id === event.turn_id && turn.status === "running") {
136
- return "duplicate";
290
+ if (turn.turn_id === event.turn_id) {
291
+ return turn.status === "running" ? "duplicate" : "stale";
137
292
  }
293
+ settleRunningState(session, "completion_not_observed");
138
294
  session.root_turn = {
139
295
  status: "running",
140
296
  turn_id: event.turn_id,
@@ -142,7 +298,12 @@ function applyTurnEvent(session, event, limits) {
142
298
  stopped_at_ms: null,
143
299
  has_out_of_order_events: false,
144
300
  };
145
- session.permission = { status: "idle" };
301
+ if (
302
+ session.permission.status !== "waiting_for_user" ||
303
+ session.permission.turn_id !== event.turn_id
304
+ ) {
305
+ clearPermission(session, "completion_not_observed");
306
+ }
146
307
  addActivity(session, event, "running", limits.maxActivitiesPerSession);
147
308
  return "applied";
148
309
  }
@@ -151,6 +312,9 @@ function applyTurnEvent(session, event, limits) {
151
312
  return "duplicate";
152
313
  }
153
314
  const startObserved = turn.turn_id === event.turn_id && turn.started_at_ms !== null;
315
+ if (startObserved) {
316
+ settleRootTurnActivity(session, "completed");
317
+ }
154
318
  session.root_turn = {
155
319
  status: "completed",
156
320
  turn_id: event.turn_id,
@@ -158,7 +322,15 @@ function applyTurnEvent(session, event, limits) {
158
322
  stopped_at_ms: event.received_at_ms,
159
323
  has_out_of_order_events: !startObserved,
160
324
  };
161
- session.permission = { status: "idle" };
325
+ if (
326
+ session.permission.status === "waiting_for_user" &&
327
+ session.permission.turn_id === event.turn_id
328
+ ) {
329
+ clearPermission(session, "completion_not_observed");
330
+ }
331
+ settleRunningState(session, "completion_not_observed", {
332
+ turnId: event.turn_id,
333
+ });
162
334
  addActivity(
163
335
  session,
164
336
  event,
@@ -174,6 +346,7 @@ function applySubagentEvent(session, event, limits) {
174
346
  agent = {
175
347
  agent_id: event.agent_id,
176
348
  agent_type: event.agent_type,
349
+ turn_id: event.turn_id,
177
350
  status: "unknown",
178
351
  started_at_ms: null,
179
352
  stopped_at_ms: null,
@@ -207,6 +380,7 @@ function applySubagentEvent(session, event, limits) {
207
380
  }
208
381
 
209
382
  agent.agent_type = event.agent_type;
383
+ agent.turn_id = event.turn_id;
210
384
  agent.last_seen_at_ms = Math.max(agent.last_seen_at_ms, event.received_at_ms);
211
385
  touchMapEntry(session.agents, event.agent_id, agent);
212
386
  trimMap(session.agents, limits.maxAgentsPerSession);
@@ -264,7 +438,7 @@ function applyToolEvent(session, event, limits) {
264
438
  permission.turn_id === event.turn_id &&
265
439
  event.received_at_ms >= permission.requested_at_ms
266
440
  ) {
267
- session.permission = { status: "idle" };
441
+ clearPermission(session, "completed");
268
442
  }
269
443
  }
270
444
 
@@ -310,9 +484,29 @@ function applyPermissionEvent(session, event, limits) {
310
484
  }
311
485
 
312
486
  function applyEvent(session, event, limits) {
313
- if (event.type === "session_started" || event.type === "session_ended") {
487
+ if (event.type === "session_started") {
488
+ return applySessionEvent(session, event, limits);
489
+ }
490
+ if (
491
+ session.lifecycle.end_observed &&
492
+ event.type !== "tool_completed" &&
493
+ event.type !== "subagent_stopped"
494
+ ) {
495
+ return event.type === "session_ended" ? "duplicate" : "stale";
496
+ }
497
+ if (event.type === "session_ended") {
314
498
  return applySessionEvent(session, event, limits);
315
499
  }
500
+ if (
501
+ session.root_turn.status === "completed" &&
502
+ session.root_turn.turn_id === event.turn_id &&
503
+ (event.type === "turn_started" ||
504
+ event.type === "permission_requested" ||
505
+ event.type === "tool_started" ||
506
+ event.type === "subagent_started")
507
+ ) {
508
+ return "stale";
509
+ }
316
510
  if (event.type === "turn_started" || event.type === "turn_stopped") {
317
511
  return applyTurnEvent(session, event, limits);
318
512
  }
@@ -325,21 +519,51 @@ function applyEvent(session, event, limits) {
325
519
  return applyPermissionEvent(session, event, limits);
326
520
  }
327
521
 
328
- function snapshotSession(session) {
522
+ function snapshotSession(session, nowMs, staleAfterMs) {
523
+ const staleActive = isStaleActiveSession(session, nowMs, staleAfterMs);
329
524
  return {
330
525
  session_id: session.session_id,
331
526
  workspace_label: session.workspace_label,
332
- status: deriveSessionStatus(session),
527
+ task_summary: session.task_summary,
528
+ status: deriveSessionStatus(session, nowMs, staleAfterMs),
333
529
  first_seen_at_ms: session.first_seen_at_ms,
334
530
  last_seen_at_ms: session.last_seen_at_ms,
335
531
  agents: [...session.agents.values()]
336
- .map(({ start_observed, stop_observed, ...agent }) => ({ ...agent }))
532
+ .map(({ start_observed, stop_observed, ...agent }) => ({
533
+ ...agent,
534
+ ...(staleActive && agent.status === "running"
535
+ ? { status: "completion_not_observed" }
536
+ : {}),
537
+ }))
337
538
  .sort((left, right) => right.last_seen_at_ms - left.last_seen_at_ms),
338
- root_turn: { ...session.root_turn },
539
+ tools: [...session.tools.values()]
540
+ .map(({ start_observed, completion_observed, ...tool }) => ({
541
+ ...tool,
542
+ ...(staleActive && tool.status === "running"
543
+ ? { status: "completion_not_observed" }
544
+ : {}),
545
+ }))
546
+ .sort((left, right) => right.last_seen_at_ms - left.last_seen_at_ms),
547
+ root_turn: {
548
+ ...session.root_turn,
549
+ ...(staleActive && session.root_turn.status === "running"
550
+ ? { status: "completion_not_observed" }
551
+ : {}),
552
+ },
339
553
  recent_activities: session.recent_activities.map((activity) => ({
340
554
  ...activity,
555
+ ...(staleActive &&
556
+ (activity.status === "running" ||
557
+ activity.status === "waiting_for_user")
558
+ ? { status: "completion_not_observed" }
559
+ : {}),
341
560
  })),
342
- permission: { ...session.permission },
561
+ permission: {
562
+ ...session.permission,
563
+ ...(staleActive && session.permission.status === "waiting_for_user"
564
+ ? { status: "completion_not_observed" }
565
+ : {}),
566
+ },
343
567
  };
344
568
  }
345
569
 
@@ -362,6 +586,10 @@ export function createMonitorStore(options = {}) {
362
586
  options.maxSessions ?? DEFAULT_LIMITS.maxSessions,
363
587
  "maxSessions",
364
588
  ),
589
+ staleAfterMs: positiveInteger(
590
+ options.staleAfterMs ?? DEFAULT_LIMITS.staleAfterMs,
591
+ "staleAfterMs",
592
+ ),
365
593
  };
366
594
  const now = options.now ?? Date.now;
367
595
  if (typeof now !== "function") {
@@ -409,6 +637,7 @@ export function createMonitorStore(options = {}) {
409
637
  }
410
638
 
411
639
  applyWorkspaceLabel(session, event);
640
+ applyTaskSummary(session, event);
412
641
 
413
642
  session.first_seen_at_ms = Math.min(
414
643
  session.first_seen_at_ms,
@@ -425,12 +654,15 @@ export function createMonitorStore(options = {}) {
425
654
  }
426
655
 
427
656
  function getSnapshot() {
657
+ const snapshotAtMs = now();
428
658
  return {
429
659
  schema_version: 1,
430
660
  source_of_truth: "hook",
431
661
  updated_at_ms: updatedAtMs,
432
662
  sessions: [...sessions.values()]
433
- .map(snapshotSession)
663
+ .map((session) =>
664
+ snapshotSession(session, snapshotAtMs, limits.staleAfterMs),
665
+ )
434
666
  .sort((left, right) => right.last_seen_at_ms - left.last_seen_at_ms),
435
667
  diagnostics: diagnostics.map((diagnostic) => ({ ...diagnostic })),
436
668
  };
@@ -11,11 +11,38 @@ const NORMALIZED_EVENT_TYPES = Object.freeze({
11
11
  });
12
12
 
13
13
  const SESSION_EVENT_TYPES = new Set(["session_started", "session_ended"]);
14
+ const SESSION_START_SOURCES = new Set([
15
+ "startup",
16
+ "resume",
17
+ "clear",
18
+ "compact",
19
+ ]);
14
20
 
15
21
  const MAX_IDENTIFIER_LENGTH = 512;
16
22
  const MAX_LABEL_LENGTH = 256;
17
23
  const MAX_WORKSPACE_LABEL_LENGTH = 120;
24
+ const MAX_PROMPT_INSPECTION_LENGTH = 4_096;
25
+ const MAX_TASK_SUMMARY_LENGTH = 180;
18
26
  const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/;
27
+ const CONTROL_CHARACTERS_GLOBAL = /[\u0000-\u001f\u007f-\u009f]/g;
28
+
29
+ const URL =
30
+ /\b[A-Z][A-Z0-9+.-]*:\/\/[^\s<>"'`]+|\bwww\.[^\s<>"'`]+/giu;
31
+ const EMAIL = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu;
32
+ const LABELED_CREDENTIAL =
33
+ /\b(?:[A-Z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth(?:orization)?|bearer|credential|key|password|passwd|private[_-]?key|refresh[_-]?token|secret|token)\b\s*(?:=|:)\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)/giu;
34
+ const BEARER_CREDENTIAL = /\bBearer\s+[A-Za-z0-9._~+\/-]+=*/giu;
35
+ const PREFIXED_SECRET =
36
+ /\b(?:AKIA[0-9A-Z]{16}|AIza[A-Za-z0-9_-]{20,}|github_pat_[A-Za-z0-9_]{12,}|gh[pousr]_[A-Za-z0-9_]{12,}|glpat-[A-Za-z0-9_-]{12,}|npm_[A-Za-z0-9_]{12,}|sk-[A-Za-z0-9_-]{12,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{12,}|whsec_[A-Za-z0-9]{12,}|xox[baprs]-[A-Za-z0-9-]{12,})\b/gu;
37
+ const JWT = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu;
38
+ const PRIVATE_KEY_BLOCK =
39
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/giu;
40
+ const WINDOWS_ABSOLUTE_PATH =
41
+ /(^|[\s("'`=:[{])[A-Za-z]:[\\/][^\s<>"'`)\]},;]*/gu;
42
+ const UNC_ABSOLUTE_PATH =
43
+ /(^|[\s("'`=:[{])\\\\[^\s<>"'`)\]},;]+(?:\\[^\s<>"'`)\]},;]+)+/gu;
44
+ const POSIX_ABSOLUTE_PATH =
45
+ /(^|[\s("'`=:[{])\/(?!\/)[^\s<>"'`)\]},;]*(?:\/[^\s<>"'`)\]},;]+)*/gu;
19
46
 
20
47
  function isObject(value) {
21
48
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -86,6 +113,52 @@ function optionalWorkspaceLabel(payload) {
86
113
  return label;
87
114
  }
88
115
 
116
+ function replaceAbsolutePaths(value) {
117
+ return value
118
+ .replace(UNC_ABSOLUTE_PATH, (_match, prefix) => `${prefix}[path]`)
119
+ .replace(WINDOWS_ABSOLUTE_PATH, (_match, prefix) => `${prefix}[path]`)
120
+ .replace(POSIX_ABSOLUTE_PATH, (_match, prefix) => `${prefix}[path]`);
121
+ }
122
+
123
+ /**
124
+ * Derive a short, display-safe hint from an untrusted UserPromptSubmit prompt.
125
+ * The caller must discard the raw prompt after this synchronous derivation.
126
+ */
127
+ export function deriveTaskSummary(value) {
128
+ if (typeof value !== "string" || value.length === 0) {
129
+ return null;
130
+ }
131
+
132
+ let summary = value
133
+ .slice(0, MAX_PROMPT_INSPECTION_LENGTH)
134
+ .replace(PRIVATE_KEY_BLOCK, "[credential]")
135
+ .replace(CONTROL_CHARACTERS_GLOBAL, " ")
136
+ .replace(URL, "[link]")
137
+ .replace(EMAIL, "[email]")
138
+ .replace(LABELED_CREDENTIAL, "[credential]")
139
+ .replace(BEARER_CREDENTIAL, "[credential]")
140
+ .replace(PREFIXED_SECRET, "[credential]")
141
+ .replace(JWT, "[credential]");
142
+ summary = replaceAbsolutePaths(summary).replace(/\s+/gu, " ").trim();
143
+
144
+ if (!summary || !summary.replace(/\[(?:credential|email|link|path)\]/gu, "").trim()) {
145
+ return null;
146
+ }
147
+
148
+ const characters = Array.from(summary);
149
+ if (characters.length <= MAX_TASK_SUMMARY_LENGTH) {
150
+ return summary;
151
+ }
152
+
153
+ const bounded = characters
154
+ .slice(0, MAX_TASK_SUMMARY_LENGTH - 1)
155
+ .join("")
156
+ .trimEnd();
157
+ const lastSpace = bounded.lastIndexOf(" ");
158
+ const readableBoundary = lastSpace >= Math.floor(MAX_TASK_SUMMARY_LENGTH * 0.6);
159
+ return `${readableBoundary ? bounded.slice(0, lastSpace) : bounded}…`;
160
+ }
161
+
89
162
  /**
90
163
  * Validate an untrusted Codex hook payload and retain only monitor-safe fields.
91
164
  * Raw prompts, tool input/output, paths, and assistant messages are never copied.
@@ -122,11 +195,29 @@ export function normalizeHookPayload(payload, options = {}) {
122
195
  }
123
196
 
124
197
  const event = commonEvent(payload, type, receivedAtMs);
198
+ if (
199
+ type === "session_started" &&
200
+ typeof payload.source === "string" &&
201
+ SESSION_START_SOURCES.has(payload.source)
202
+ ) {
203
+ event.session_start_source = payload.source;
204
+ }
125
205
  const workspaceLabel = optionalWorkspaceLabel(payload);
126
206
  if (workspaceLabel) {
127
207
  event.workspace_label = workspaceLabel;
128
208
  }
129
209
 
210
+ if (type === "turn_started") {
211
+ const taskSummary = deriveTaskSummary(
212
+ typeof payload.task_summary === "string"
213
+ ? payload.task_summary
214
+ : payload.prompt,
215
+ );
216
+ if (taskSummary) {
217
+ event.task_summary = taskSummary;
218
+ }
219
+ }
220
+
130
221
  if (type === "subagent_started" || type === "subagent_stopped") {
131
222
  for (const [field, maxLength] of [
132
223
  ["agent_id", MAX_IDENTIFIER_LENGTH],