@drisp/cli 0.5.11 → 0.5.13

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.
@@ -216,7 +216,6 @@ function cleanupStaleSockets(sockDir) {
216
216
 
217
217
  // src/harnesses/claude/runtime/interactionRules.ts
218
218
  var DEFAULT_TIMEOUT_MS = 4e3;
219
- var PERMISSION_TIMEOUT_MS = 3e5;
220
219
  var DEFAULT_HINTS = {
221
220
  expectsDecision: false,
222
221
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
@@ -225,12 +224,12 @@ var DEFAULT_HINTS = {
225
224
  var RULES = {
226
225
  "permission.request": {
227
226
  expectsDecision: true,
228
- defaultTimeoutMs: PERMISSION_TIMEOUT_MS,
227
+ defaultTimeoutMs: null,
229
228
  canBlock: true
230
229
  },
231
230
  "tool.pre": {
232
231
  expectsDecision: true,
233
- defaultTimeoutMs: PERMISSION_TIMEOUT_MS,
232
+ defaultTimeoutMs: null,
234
233
  canBlock: true
235
234
  },
236
235
  "tool.post": {
@@ -390,7 +389,7 @@ var RULES = {
390
389
  },
391
390
  "elicitation.request": {
392
391
  expectsDecision: true,
393
- defaultTimeoutMs: PERMISSION_TIMEOUT_MS,
392
+ defaultTimeoutMs: null,
394
393
  canBlock: true
395
394
  },
396
395
  "elicitation.result": {
@@ -5369,13 +5368,12 @@ function describeAction(a) {
5369
5368
  }
5370
5369
 
5371
5370
  // src/harnesses/codex/runtime/interactionRules.ts
5372
- var APPROVAL_TIMEOUT_MS = 3e5;
5373
5371
  var DEFAULT_TIMEOUT_MS2 = 4e3;
5374
5372
  function getCodexInteractionHints(expectsDecision) {
5375
5373
  if (expectsDecision) {
5376
5374
  return {
5377
5375
  expectsDecision: true,
5378
- defaultTimeoutMs: APPROVAL_TIMEOUT_MS,
5376
+ defaultTimeoutMs: null,
5379
5377
  canBlock: true
5380
5378
  };
5381
5379
  }
@@ -7860,7 +7858,8 @@ function createTranscriptReader() {
7860
7858
  }
7861
7859
 
7862
7860
  // src/core/feed/internals/runLifecycle.ts
7863
- function createRunLifecycle() {
7861
+ function createRunLifecycle(boundary) {
7862
+ const { makeEvent, resetPerRunState } = boundary;
7864
7863
  let currentSession = null;
7865
7864
  let currentRun = null;
7866
7865
  let seq = 0;
@@ -7869,17 +7868,79 @@ function createRunLifecycle() {
7869
7868
  const sessId = currentSession?.session_id ?? "unknown";
7870
7869
  return `${sessId}:R${runSeq}`;
7871
7870
  }
7871
+ function allocateSeq() {
7872
+ return ++seq;
7873
+ }
7874
+ function getCurrentRun() {
7875
+ return currentRun;
7876
+ }
7877
+ function closeRun(ts, status) {
7878
+ if (!currentRun) return null;
7879
+ currentRun.status = status;
7880
+ currentRun.ended_at = ts;
7881
+ const closed = currentRun;
7882
+ currentRun = null;
7883
+ return closed;
7884
+ }
7885
+ function openNewRun(ts, sessionId, triggerType, promptPreview) {
7886
+ runSeq++;
7887
+ currentRun = {
7888
+ run_id: getRunId(),
7889
+ session_id: sessionId,
7890
+ started_at: ts,
7891
+ trigger: { type: triggerType, prompt_preview: promptPreview },
7892
+ status: "running",
7893
+ actors: { root_agent_id: "agent:root", subagent_ids: [] },
7894
+ counters: {
7895
+ tool_uses: 0,
7896
+ tool_failures: 0,
7897
+ permission_requests: 0,
7898
+ blocks: 0
7899
+ }
7900
+ };
7901
+ return currentRun;
7902
+ }
7903
+ function closeRunIntoEvent(runtimeEvent, status) {
7904
+ const closed = closeRun(runtimeEvent.timestamp, status);
7905
+ if (!closed) return null;
7906
+ return makeEvent(
7907
+ "run.end",
7908
+ "info",
7909
+ "system",
7910
+ { status, counters: { ...closed.counters } },
7911
+ runtimeEvent
7912
+ );
7913
+ }
7914
+ function beginRun(runtimeEvent, triggerType = "other", promptPreview) {
7915
+ if (currentRun && triggerType === "other") return [];
7916
+ const results = [];
7917
+ const closeEvt = closeRunIntoEvent(runtimeEvent, "completed");
7918
+ if (closeEvt) results.push(closeEvt);
7919
+ resetPerRunState();
7920
+ openNewRun(
7921
+ runtimeEvent.timestamp,
7922
+ runtimeEvent.sessionId,
7923
+ triggerType,
7924
+ promptPreview
7925
+ );
7926
+ results.push(
7927
+ makeEvent(
7928
+ "run.start",
7929
+ "info",
7930
+ "system",
7931
+ { trigger: { type: triggerType, prompt_preview: promptPreview } },
7932
+ runtimeEvent
7933
+ )
7934
+ );
7935
+ return results;
7936
+ }
7872
7937
  return {
7873
- allocateSeq() {
7874
- return ++seq;
7875
- },
7938
+ allocateSeq,
7876
7939
  getRunId,
7877
7940
  getSession() {
7878
7941
  return currentSession;
7879
7942
  },
7880
- getCurrentRun() {
7881
- return currentRun;
7882
- },
7943
+ getCurrentRun,
7883
7944
  setSession(session) {
7884
7945
  currentSession = session;
7885
7946
  },
@@ -7892,32 +7953,10 @@ function createRunLifecycle() {
7892
7953
  incrementCounter(name) {
7893
7954
  if (currentRun) currentRun.counters[name]++;
7894
7955
  },
7895
- closeRun(ts, status) {
7896
- if (!currentRun) return null;
7897
- currentRun.status = status;
7898
- currentRun.ended_at = ts;
7899
- const closed = currentRun;
7900
- currentRun = null;
7901
- return closed;
7902
- },
7903
- openNewRun(ts, sessionId, triggerType, promptPreview) {
7904
- runSeq++;
7905
- currentRun = {
7906
- run_id: getRunId(),
7907
- session_id: sessionId,
7908
- started_at: ts,
7909
- trigger: { type: triggerType, prompt_preview: promptPreview },
7910
- status: "running",
7911
- actors: { root_agent_id: "agent:root", subagent_ids: [] },
7912
- counters: {
7913
- tool_uses: 0,
7914
- tool_failures: 0,
7915
- permission_requests: 0,
7916
- blocks: 0
7917
- }
7918
- };
7919
- return currentRun;
7920
- },
7956
+ closeRun,
7957
+ openNewRun,
7958
+ closeRunIntoEvent,
7959
+ beginRun,
7921
7960
  restoreFrom(bootstrap) {
7922
7961
  for (const e of bootstrap.feedEvents) {
7923
7962
  if (e.seq > seq) seq = e.seq;
@@ -8037,6 +8076,34 @@ function createToolCorrelation() {
8037
8076
  };
8038
8077
  }
8039
8078
 
8079
+ // src/core/feed/internals/projection.ts
8080
+ function readString(...values) {
8081
+ for (const value of values) {
8082
+ if (typeof value === "string") return value;
8083
+ }
8084
+ return void 0;
8085
+ }
8086
+ function readBoolean(...values) {
8087
+ for (const value of values) {
8088
+ if (typeof value === "boolean") return value;
8089
+ }
8090
+ return void 0;
8091
+ }
8092
+ function readObject(...values) {
8093
+ for (const value of values) {
8094
+ if (typeof value === "object" && value !== null) {
8095
+ return value;
8096
+ }
8097
+ }
8098
+ return {};
8099
+ }
8100
+ function readSuggestionArray(...values) {
8101
+ for (const value of values) {
8102
+ if (Array.isArray(value)) return value;
8103
+ }
8104
+ return void 0;
8105
+ }
8106
+
8040
8107
  // src/core/feed/internals/agentMessageStream.ts
8041
8108
  function normalizeAgentMessage(message) {
8042
8109
  return message.replace(/\r\n/g, "\n").trimEnd();
@@ -8044,6 +8111,9 @@ function normalizeAgentMessage(message) {
8044
8111
  function agentMessageKey(actorId, scope) {
8045
8112
  return `${actorId}\0${scope}`;
8046
8113
  }
8114
+ function isStopEvent(kind) {
8115
+ return kind === "stop.request" || kind === "subagent.stop";
8116
+ }
8047
8117
  function createAgentMessageStream(eventBuilder, transcriptReader) {
8048
8118
  const pendingMessages = /* @__PURE__ */ new Map();
8049
8119
  const lastAgentMessageByActorScope = /* @__PURE__ */ new Map();
@@ -8070,6 +8140,25 @@ function createAgentMessageStream(eventBuilder, transcriptReader) {
8070
8140
  lastAgentMessageByActorScope.set(key, normalized);
8071
8141
  return event;
8072
8142
  }
8143
+ function emitTranscriptMessages(transcriptPath, runtimeEvent, actorId, scope) {
8144
+ const msgs = transcriptReader.readNewAssistantMessages(transcriptPath);
8145
+ const out = [];
8146
+ for (const msg of msgs) {
8147
+ const ev = emit({
8148
+ runtimeEvent,
8149
+ actorId,
8150
+ scope,
8151
+ message: msg.text,
8152
+ source: "transcript",
8153
+ model: msg.model
8154
+ });
8155
+ if (ev) out.push(ev);
8156
+ }
8157
+ return out;
8158
+ }
8159
+ function drainTranscript(transcriptPath) {
8160
+ transcriptReader.readNewAssistantMessages(transcriptPath);
8161
+ }
8073
8162
  return {
8074
8163
  emit,
8075
8164
  appendPendingDelta(itemId, delta, defaultActorId, defaultScope) {
@@ -8125,25 +8214,37 @@ function createAgentMessageStream(eventBuilder, transcriptReader) {
8125
8214
  }
8126
8215
  return out;
8127
8216
  },
8128
- emitTranscriptMessages(transcriptPath, runtimeEvent, actorId, scope) {
8129
- const msgs = transcriptReader.readNewAssistantMessages(transcriptPath);
8130
- const out = [];
8131
- for (const msg of msgs) {
8132
- const ev = emit({
8133
- runtimeEvent,
8134
- actorId,
8135
- scope,
8136
- message: msg.text,
8137
- source: "transcript",
8138
- model: msg.model
8139
- });
8140
- if (ev) out.push(ev);
8141
- }
8142
- return out;
8217
+ emitTranscriptMessages,
8218
+ replayBeforeEvent(runtimeEvent, actorId, scope) {
8219
+ const transcriptPath = runtimeEvent.context.transcriptPath;
8220
+ if (!transcriptPath || isStopEvent(runtimeEvent.kind)) return [];
8221
+ return emitTranscriptMessages(
8222
+ transcriptPath,
8223
+ runtimeEvent,
8224
+ actorId,
8225
+ scope
8226
+ );
8143
8227
  },
8144
- drainTranscript(transcriptPath) {
8145
- transcriptReader.readNewAssistantMessages(transcriptPath);
8228
+ emitStopFallback(runtimeEvent, opts) {
8229
+ const transcriptPath = runtimeEvent.context.transcriptPath;
8230
+ if (transcriptPath) drainTranscript(transcriptPath);
8231
+ if (opts.priorResults.some((r) => r.kind === "agent.message")) return [];
8232
+ const message = readString(
8233
+ runtimeEvent.data["last_assistant_message"]
8234
+ );
8235
+ if (!message) return [];
8236
+ const parent = opts.priorResults.find((r) => r.kind === opts.parentKind);
8237
+ const ev = emit({
8238
+ runtimeEvent,
8239
+ actorId: opts.actorId,
8240
+ scope: opts.scope,
8241
+ message,
8242
+ source: "hook",
8243
+ cause: parent ? { parent_event_id: parent.event_id } : void 0
8244
+ });
8245
+ return ev ? [ev] : [];
8146
8246
  },
8247
+ drainTranscript,
8147
8248
  appendReasoningSummary(itemId, index, chunk) {
8148
8249
  const key = `${itemId ?? ""}:${index ?? 0}`;
8149
8250
  const next = `${reasoningSummaryByKey.get(key) ?? ""}${chunk}`;
@@ -8242,6 +8343,129 @@ function createTaskLifecycleTracker() {
8242
8343
  };
8243
8344
  }
8244
8345
 
8346
+ // src/core/feed/internals/taskStateTracker.ts
8347
+ function extractTodoItems(toolInput) {
8348
+ const input = toolInput;
8349
+ return Array.isArray(input?.todos) ? input.todos : [];
8350
+ }
8351
+ function mapPlanStepStatus(status) {
8352
+ switch (status) {
8353
+ case "inProgress":
8354
+ return "in_progress";
8355
+ case "completed":
8356
+ return "completed";
8357
+ case void 0:
8358
+ default:
8359
+ return "pending";
8360
+ }
8361
+ }
8362
+ function createTaskStateTracker() {
8363
+ const rootPlan = createRootPlanTracker();
8364
+ const taskLifecycle = createTaskLifecycleTracker();
8365
+ function applyToolPre(input) {
8366
+ const { toolName, toolInput, actorId } = input;
8367
+ if (toolName === "TodoWrite" && actorId === "agent:root") {
8368
+ rootPlan.set(extractTodoItems(toolInput));
8369
+ }
8370
+ if (toolName === "TaskUpdate") {
8371
+ const taskId = readString(toolInput["taskId"], toolInput["task_id"]);
8372
+ const status = coerceTaskStatus(toolInput["status"]);
8373
+ if (taskId && status) {
8374
+ taskLifecycle.updateStatus({ taskId, status });
8375
+ }
8376
+ }
8377
+ }
8378
+ function applyToolPost(input) {
8379
+ const { toolName, toolInput, toolResponse } = input;
8380
+ if (toolName === "TaskCreate") {
8381
+ const response = readObject(toolResponse);
8382
+ const task = readObject(response["task"]);
8383
+ const taskId = readString(task["id"], task["task_id"]);
8384
+ const subject = readString(task["subject"], toolInput["subject"]);
8385
+ if (taskId && subject) {
8386
+ taskLifecycle.upsertCreated({
8387
+ taskId,
8388
+ subject,
8389
+ description: readString(toolInput["description"]),
8390
+ activeForm: readString(toolInput["activeForm"])
8391
+ });
8392
+ }
8393
+ }
8394
+ if (toolName === "TaskUpdate") {
8395
+ const response = readObject(toolResponse);
8396
+ const taskId = readString(
8397
+ response["taskId"],
8398
+ response["task_id"],
8399
+ toolInput["taskId"],
8400
+ toolInput["task_id"]
8401
+ );
8402
+ const status = coerceTaskStatus(
8403
+ readObject(response["statusChange"])["to"] ?? toolInput["status"]
8404
+ );
8405
+ if (taskId && status) {
8406
+ taskLifecycle.updateStatus({ taskId, status });
8407
+ }
8408
+ }
8409
+ }
8410
+ function applyTaskCreatedEvent(data) {
8411
+ const taskId = readString(data["task_id"]);
8412
+ const subject = readString(data["task_subject"]);
8413
+ const description = readString(data["task_description"]);
8414
+ if (taskId && subject) {
8415
+ taskLifecycle.upsertCreated({ taskId, subject, description });
8416
+ }
8417
+ }
8418
+ function applyTaskCompletedEvent(data) {
8419
+ const taskId = readString(data["task_id"]);
8420
+ const subject = readString(data["task_subject"]);
8421
+ if (taskId) {
8422
+ taskLifecycle.markCompleted({ taskId, subject });
8423
+ }
8424
+ }
8425
+ return {
8426
+ current() {
8427
+ return [...rootPlan.current(), ...taskLifecycle.current()];
8428
+ },
8429
+ applyToolPre,
8430
+ applyToolPost,
8431
+ applyPlanDelta(planSteps) {
8432
+ if (!Array.isArray(planSteps) || planSteps.length === 0) return false;
8433
+ const next = planSteps.map((step) => ({
8434
+ content: typeof step.step === "string" ? step.step : "",
8435
+ status: mapPlanStepStatus(step.status)
8436
+ }));
8437
+ if (!rootPlan.differs(next)) return false;
8438
+ rootPlan.set(next);
8439
+ return true;
8440
+ },
8441
+ applyTaskCreatedEvent,
8442
+ applyTaskCompletedEvent,
8443
+ restore(feedEvents) {
8444
+ for (const e of feedEvents) {
8445
+ if (e.kind === "tool.pre") {
8446
+ const data = e.data;
8447
+ applyToolPre({
8448
+ toolName: data.tool_name ?? "",
8449
+ toolInput: readObject(data.tool_input),
8450
+ actorId: e.actor_id
8451
+ });
8452
+ } else if (e.kind === "tool.post") {
8453
+ const data = e.data;
8454
+ applyToolPost({
8455
+ toolName: data.tool_name ?? "",
8456
+ toolInput: readObject(data.tool_input),
8457
+ toolResponse: data.tool_response
8458
+ });
8459
+ } else if (e.kind === "task.created") {
8460
+ applyTaskCreatedEvent(e.data);
8461
+ } else if (e.kind === "task.completed") {
8462
+ applyTaskCompletedEvent(e.data);
8463
+ }
8464
+ }
8465
+ }
8466
+ };
8467
+ }
8468
+
8245
8469
  // src/core/feed/internals/subagentTracker.ts
8246
8470
  function createSubagentTracker() {
8247
8471
  const stack = [];
@@ -8284,44 +8508,57 @@ function createSubagentTracker() {
8284
8508
  };
8285
8509
  }
8286
8510
 
8287
- // src/core/feed/internals/projection.ts
8288
- function readString(...values) {
8289
- for (const value of values) {
8290
- if (typeof value === "string") return value;
8291
- }
8292
- return void 0;
8293
- }
8294
- function readBoolean(...values) {
8295
- for (const value of values) {
8296
- if (typeof value === "boolean") return value;
8297
- }
8298
- return void 0;
8299
- }
8300
- function readObject(...values) {
8301
- for (const value of values) {
8302
- if (typeof value === "object" && value !== null) {
8303
- return value;
8304
- }
8305
- }
8306
- return {};
8307
- }
8308
- function readSuggestionArray(...values) {
8309
- for (const value of values) {
8310
- if (Array.isArray(value)) return value;
8311
- }
8312
- return void 0;
8313
- }
8314
-
8315
8511
  // src/core/feed/todo.ts
8316
8512
  function isSubagentTool(toolName) {
8317
8513
  return toolName === "Task" || toolName === "Agent";
8318
8514
  }
8319
8515
 
8320
- // src/core/feed/internals/toolProjection.ts
8321
- function extractTodoItems(toolInput) {
8322
- const input = toolInput;
8323
- return Array.isArray(input?.todos) ? input.todos : [];
8516
+ // src/core/feed/internals/subagentLifecycle.ts
8517
+ function createSubagentLifecycle(args) {
8518
+ const { actors, runLifecycle } = args;
8519
+ const tracker = createSubagentTracker();
8520
+ const actorIdFor = (agentId) => `subagent:${agentId}`;
8521
+ return {
8522
+ observeToolInput(toolName, toolInput) {
8523
+ if (!isSubagentTool(toolName)) return;
8524
+ if (typeof toolInput["description"] === "string") {
8525
+ tracker.recordPendingDescription(toolInput["description"]);
8526
+ } else {
8527
+ tracker.clearPendingDescription();
8528
+ }
8529
+ },
8530
+ startSubagent({ agentId, agentType, fallbackDescription }) {
8531
+ const description = tracker.consumePendingDescription() ?? fallbackDescription;
8532
+ if (agentId) {
8533
+ actors.ensureSubagent(agentId, agentType ?? "unknown");
8534
+ const currentRun = runLifecycle.getCurrentRun();
8535
+ if (currentRun) currentRun.actors.subagent_ids.push(agentId);
8536
+ tracker.pushActor(actorIdFor(agentId));
8537
+ if (description) tracker.setDescription(agentId, description);
8538
+ }
8539
+ return { actorId: "agent:root", description: description ?? void 0 };
8540
+ },
8541
+ stopSubagent(agentId) {
8542
+ if (agentId) tracker.popActor(actorIdFor(agentId));
8543
+ return {
8544
+ actorId: actorIdFor(agentId ?? "unknown"),
8545
+ description: tracker.description(agentId ?? "")
8546
+ };
8547
+ },
8548
+ currentActor() {
8549
+ return tracker.peek() ?? "agent:root";
8550
+ },
8551
+ currentScope() {
8552
+ return tracker.currentScope();
8553
+ },
8554
+ actorIdFor,
8555
+ clear() {
8556
+ tracker.clear();
8557
+ }
8558
+ };
8324
8559
  }
8560
+
8561
+ // src/core/feed/internals/toolProjection.ts
8325
8562
  function resolveToolUseId(event, record) {
8326
8563
  return event.toolUseId ?? record["tool_use_id"];
8327
8564
  }
@@ -8337,8 +8574,7 @@ function createToolProjection(args) {
8337
8574
  makeEvent,
8338
8575
  runLifecycle,
8339
8576
  toolCorrelation,
8340
- rootPlan,
8341
- taskLifecycle,
8577
+ taskState,
8342
8578
  subagents,
8343
8579
  resolveToolActor
8344
8580
  } = args;
@@ -8435,23 +8671,12 @@ function createToolProjection(args) {
8435
8671
  webSearchStarted(event, data, toolUseId, preEvent.event_id)
8436
8672
  );
8437
8673
  }
8438
- if (toolName === "TodoWrite" && preEvent.actor_id === "agent:root") {
8439
- rootPlan.set(extractTodoItems(toolInput));
8440
- }
8441
- if (toolName === "TaskUpdate") {
8442
- const taskId = readString(toolInput["taskId"], toolInput["task_id"]);
8443
- const status = coerceTaskStatus(toolInput["status"]);
8444
- if (taskId && status) {
8445
- taskLifecycle.updateStatus({ taskId, status });
8446
- }
8447
- }
8448
- if (isSubagentTool(toolName)) {
8449
- if (typeof toolInput["description"] === "string") {
8450
- subagents.recordPendingDescription(toolInput["description"]);
8451
- } else {
8452
- subagents.clearPendingDescription();
8453
- }
8454
- }
8674
+ taskState.applyToolPre({
8675
+ toolName,
8676
+ toolInput,
8677
+ actorId: preEvent.actor_id
8678
+ });
8679
+ subagents.observeToolInput(toolName, toolInput);
8455
8680
  return results;
8456
8681
  }
8457
8682
  if (event.kind === "tool.post") {
@@ -8471,35 +8696,11 @@ function createToolProjection(args) {
8471
8696
  toolUseCause(toolUseId, parentId)
8472
8697
  );
8473
8698
  results.push(postEvent);
8474
- if (toolName === "TaskCreate") {
8475
- const response = readObject(data["tool_response"]);
8476
- const task = readObject(response["task"]);
8477
- const taskId = readString(task["id"], task["task_id"]);
8478
- const subject = readString(task["subject"], toolInput["subject"]);
8479
- if (taskId && subject) {
8480
- taskLifecycle.upsertCreated({
8481
- taskId,
8482
- subject,
8483
- description: readString(toolInput["description"]),
8484
- activeForm: readString(toolInput["activeForm"])
8485
- });
8486
- }
8487
- }
8488
- if (toolName === "TaskUpdate") {
8489
- const response = readObject(data["tool_response"]);
8490
- const taskId = readString(
8491
- response["taskId"],
8492
- response["task_id"],
8493
- toolInput["taskId"],
8494
- toolInput["task_id"]
8495
- );
8496
- const status = coerceTaskStatus(
8497
- readObject(response["statusChange"])["to"] ?? toolInput["status"]
8498
- );
8499
- if (taskId && status) {
8500
- taskLifecycle.updateStatus({ taskId, status });
8501
- }
8502
- }
8699
+ taskState.applyToolPost({
8700
+ toolName,
8701
+ toolInput,
8702
+ toolResponse: data.tool_response
8703
+ });
8503
8704
  if (toolName === "WebSearch") {
8504
8705
  results.push(
8505
8706
  webSearchCompleted(event, data, toolUseId, postEvent.event_id)
@@ -8940,29 +9141,27 @@ function createDecisionProjection(args) {
8940
9141
 
8941
9142
  // src/core/feed/internals/subagentProjection.ts
8942
9143
  function createSubagentProjection(args) {
8943
- const { ensureRunArray, makeEvent, runLifecycle, actors, subagents } = args;
9144
+ const { ensureRunArray, makeEvent, subagents } = args;
8944
9145
  return {
8945
9146
  mapSubagentEvent(event, data) {
8946
9147
  const results = ensureRunArray(event);
8947
9148
  const agentId = event.agentId ?? readString(data["agent_id"]);
8948
9149
  const agentType = event.agentType ?? readString(data["agent_type"]);
8949
9150
  if (event.kind === "subagent.start") {
8950
- if (agentId) {
8951
- actors.ensureSubagent(agentId, agentType ?? "unknown");
8952
- const currentRun = runLifecycle.getCurrentRun();
8953
- if (currentRun) currentRun.actors.subagent_ids.push(agentId);
8954
- subagents.pushActor(`subagent:${agentId}`);
8955
- }
8956
- const description = subagents.consumePendingDescription() ?? readString(data["prompt"]);
9151
+ const { actorId: actorId2, description: description2 } = subagents.startSubagent({
9152
+ agentId,
9153
+ agentType,
9154
+ fallbackDescription: readString(data["prompt"])
9155
+ });
8957
9156
  results.push(
8958
9157
  makeEvent(
8959
9158
  "subagent.start",
8960
9159
  "info",
8961
- "agent:root",
9160
+ actorId2,
8962
9161
  {
8963
9162
  agent_id: agentId ?? "",
8964
9163
  agent_type: agentType ?? "",
8965
- description: description ?? void 0,
9164
+ description: description2 ?? void 0,
8966
9165
  tool: readString(data["tool"]),
8967
9166
  sender_thread_id: readString(data["sender_thread_id"]),
8968
9167
  receiver_thread_id: readString(data["receiver_thread_id"]),
@@ -8972,23 +9171,21 @@ function createSubagentProjection(args) {
8972
9171
  event
8973
9172
  )
8974
9173
  );
8975
- if (agentId && description)
8976
- subagents.setDescription(agentId, description);
8977
9174
  return results;
8978
9175
  }
8979
- if (agentId) subagents.popActor(`subagent:${agentId}`);
9176
+ const { actorId, description } = subagents.stopSubagent(agentId);
8980
9177
  results.push(
8981
9178
  makeEvent(
8982
9179
  "subagent.stop",
8983
9180
  "info",
8984
- `subagent:${agentId ?? "unknown"}`,
9181
+ actorId,
8985
9182
  {
8986
9183
  agent_id: agentId ?? "",
8987
9184
  agent_type: agentType ?? "",
8988
9185
  stop_hook_active: readBoolean(data["stop_hook_active"]) ?? false,
8989
9186
  agent_transcript_path: readString(data["agent_transcript_path"]),
8990
9187
  last_assistant_message: readString(data["last_assistant_message"]),
8991
- description: subagents.description(agentId ?? ""),
9188
+ description,
8992
9189
  tool: readString(data["tool"]),
8993
9190
  status: readString(data["status"]),
8994
9191
  sender_thread_id: readString(data["sender_thread_id"]),
@@ -9169,17 +9366,6 @@ function createFileConfigProjection(args) {
9169
9366
  }
9170
9367
 
9171
9368
  // src/core/feed/internals/runSessionProjection.ts
9172
- function mapPlanStepStatus(status) {
9173
- switch (status) {
9174
- case "inProgress":
9175
- return "in_progress";
9176
- case "completed":
9177
- return "completed";
9178
- case void 0:
9179
- default:
9180
- return "pending";
9181
- }
9182
- }
9183
9369
  function createRunSessionProjection(args) {
9184
9370
  const {
9185
9371
  ensureRunArray,
@@ -9187,7 +9373,7 @@ function createRunSessionProjection(args) {
9187
9373
  closeRunIntoEvent,
9188
9374
  runLifecycle,
9189
9375
  agentMessageStream,
9190
- rootPlan,
9376
+ taskState,
9191
9377
  resolveToolActor,
9192
9378
  currentScope
9193
9379
  } = args;
@@ -9338,28 +9524,19 @@ function createRunSessionProjection(args) {
9338
9524
  }
9339
9525
  if (event.kind === "plan.delta") {
9340
9526
  const planSteps = data["plan"];
9341
- if (Array.isArray(planSteps) && planSteps.length > 0) {
9342
- const next = planSteps.map(
9343
- (step) => ({
9344
- content: typeof step.step === "string" ? step.step : "",
9345
- status: mapPlanStepStatus(step.status)
9346
- })
9527
+ if (taskState.applyPlanDelta(planSteps)) {
9528
+ results.push(
9529
+ makeEvent(
9530
+ "todo.update",
9531
+ "info",
9532
+ "system",
9533
+ {
9534
+ todo_id: "plan",
9535
+ patch: { status: "doing" }
9536
+ },
9537
+ event
9538
+ )
9347
9539
  );
9348
- if (rootPlan.differs(next)) {
9349
- rootPlan.set(next);
9350
- results.push(
9351
- makeEvent(
9352
- "todo.update",
9353
- "info",
9354
- "system",
9355
- {
9356
- todo_id: "plan",
9357
- patch: { status: "doing" }
9358
- },
9359
- event
9360
- )
9361
- );
9362
- }
9363
9540
  }
9364
9541
  results.push(
9365
9542
  makeEvent(
@@ -9431,7 +9608,7 @@ function createRunSessionProjection(args) {
9431
9608
 
9432
9609
  // src/core/feed/internals/statusProjection.ts
9433
9610
  function createStatusProjection(args) {
9434
- const { ensureRunArray, makeEvent, taskLifecycle } = args;
9611
+ const { ensureRunArray, makeEvent, taskState } = args;
9435
9612
  return {
9436
9613
  mapStatusEvent(event, data) {
9437
9614
  const results = ensureRunArray(event);
@@ -9454,13 +9631,7 @@ function createStatusProjection(args) {
9454
9631
  const taskId = readString(data["task_id"]) ?? "";
9455
9632
  const subject = readString(data["task_subject"]) ?? "";
9456
9633
  const description = readString(data["task_description"]);
9457
- if (taskId && subject) {
9458
- taskLifecycle.upsertCreated({
9459
- taskId,
9460
- subject,
9461
- description
9462
- });
9463
- }
9634
+ taskState.applyTaskCreatedEvent(data);
9464
9635
  results.push(
9465
9636
  makeEvent(
9466
9637
  "task.created",
@@ -9481,7 +9652,7 @@ function createStatusProjection(args) {
9481
9652
  if (event.kind === "task.completed") {
9482
9653
  const taskId = readString(data["task_id"]) ?? "";
9483
9654
  const subject = readString(data["task_subject"]);
9484
- if (taskId) taskLifecycle.markCompleted({ taskId, subject });
9655
+ taskState.applyTaskCompletedEvent(data);
9485
9656
  results.push(
9486
9657
  makeEvent(
9487
9658
  "task.completed",
@@ -9551,14 +9722,21 @@ var STATUS_EVENT_KINDS = /* @__PURE__ */ new Set([
9551
9722
  "task.created"
9552
9723
  ]);
9553
9724
  function createFeedMapper(bootstrap) {
9554
- const runLifecycle = createRunLifecycle();
9725
+ const runLifecycle = createRunLifecycle({
9726
+ makeEvent,
9727
+ resetPerRunState: () => {
9728
+ toolCorrelation.resetForNewRun();
9729
+ decisionCorrelation.resetForNewRun();
9730
+ agentMessageStream.resetForNewRun();
9731
+ subagents.clear();
9732
+ }
9733
+ });
9555
9734
  const decisionCorrelation = createDecisionCorrelation();
9556
9735
  const toolCorrelation = createToolCorrelation();
9557
9736
  const transcriptReader = createTranscriptReader();
9558
9737
  const actors = new ActorRegistry();
9559
- const rootPlan = createRootPlanTracker();
9560
- const taskLifecycle = createTaskLifecycleTracker();
9561
- const subagents = createSubagentTracker();
9738
+ const taskState = createTaskStateTracker();
9739
+ const subagents = createSubagentLifecycle({ actors, runLifecycle });
9562
9740
  function makeEvent(kind, level, actorId, data, runtimeEvent, cause) {
9563
9741
  const s = runLifecycle.allocateSeq();
9564
9742
  const runId = runLifecycle.getRunId();
@@ -9593,117 +9771,21 @@ function createFeedMapper(bootstrap) {
9593
9771
  makeEvent,
9594
9772
  transcriptReader
9595
9773
  );
9596
- function replayTaskLifecycleToolEvent(e) {
9597
- if (e.kind !== "tool.pre" && e.kind !== "tool.post") return;
9598
- const data = e.data;
9599
- const toolInput = readObject(data.tool_input);
9600
- if (data.tool_name === "TaskCreate" && e.kind === "tool.post") {
9601
- const response = readObject(data.tool_response);
9602
- const task = readObject(response["task"]);
9603
- const taskId = readString(task["id"], task["task_id"]);
9604
- const subject = readString(task["subject"], toolInput["subject"]);
9605
- if (taskId && subject) {
9606
- taskLifecycle.upsertCreated({
9607
- taskId,
9608
- subject,
9609
- description: readString(toolInput["description"]),
9610
- activeForm: readString(toolInput["activeForm"])
9611
- });
9612
- }
9613
- }
9614
- if (data.tool_name === "TaskUpdate") {
9615
- const response = readObject(data.tool_response);
9616
- const status = coerceTaskStatus(
9617
- readObject(response["statusChange"])["to"] ?? toolInput["status"]
9618
- );
9619
- const taskId = readString(
9620
- response["taskId"],
9621
- response["task_id"],
9622
- toolInput["taskId"],
9623
- toolInput["task_id"]
9624
- );
9625
- if (taskId && status) {
9626
- taskLifecycle.updateStatus({ taskId, status });
9627
- }
9628
- }
9629
- }
9630
9774
  if (bootstrap) {
9631
9775
  runLifecycle.restoreFrom(bootstrap);
9632
- for (const e of bootstrap.feedEvents) {
9633
- if (e.kind === "tool.pre" && e.actor_id === "agent:root" && e.data.tool_name === "TodoWrite") {
9634
- rootPlan.set(
9635
- extractTodoItems(e.data.tool_input)
9636
- );
9637
- }
9638
- replayTaskLifecycleToolEvent(e);
9639
- if (e.kind === "task.created") {
9640
- const data = e.data;
9641
- if (data.task_id && data.task_subject) {
9642
- taskLifecycle.upsertCreated({
9643
- taskId: data.task_id,
9644
- subject: data.task_subject,
9645
- description: data.task_description
9646
- });
9647
- }
9648
- }
9649
- if (e.kind === "task.completed") {
9650
- const data = e.data;
9651
- if (data.task_id) {
9652
- taskLifecycle.markCompleted({
9653
- taskId: data.task_id,
9654
- subject: data.task_subject
9655
- });
9656
- }
9657
- }
9658
- }
9659
- }
9660
- function closeRunIntoEvent(runtimeEvent, status) {
9661
- const closed = runLifecycle.closeRun(runtimeEvent.timestamp, status);
9662
- if (!closed) return null;
9663
- return makeEvent(
9664
- "run.end",
9665
- "info",
9666
- "system",
9667
- { status, counters: { ...closed.counters } },
9668
- runtimeEvent
9669
- );
9670
- }
9671
- function ensureRunArray(runtimeEvent, triggerType = "other", promptPreview) {
9672
- if (runLifecycle.getCurrentRun() && triggerType === "other") return [];
9673
- const results = [];
9674
- const closeEvt = closeRunIntoEvent(runtimeEvent, "completed");
9675
- if (closeEvt) results.push(closeEvt);
9676
- toolCorrelation.resetForNewRun();
9677
- decisionCorrelation.resetForNewRun();
9678
- agentMessageStream.resetForNewRun();
9679
- subagents.clear();
9680
- runLifecycle.openNewRun(
9681
- runtimeEvent.timestamp,
9682
- runtimeEvent.sessionId,
9683
- triggerType,
9684
- promptPreview
9685
- );
9686
- results.push(
9687
- makeEvent(
9688
- "run.start",
9689
- "info",
9690
- "system",
9691
- { trigger: { type: triggerType, prompt_preview: promptPreview } },
9692
- runtimeEvent
9693
- )
9694
- );
9695
- return results;
9776
+ taskState.restore(bootstrap.feedEvents);
9696
9777
  }
9778
+ const ensureRunArray = runLifecycle.beginRun;
9779
+ const closeRunIntoEvent = runLifecycle.closeRunIntoEvent;
9697
9780
  function resolveToolActor() {
9698
- return subagents.peek() ?? "agent:root";
9781
+ return subagents.currentActor();
9699
9782
  }
9700
9783
  const toolProjection = createToolProjection({
9701
9784
  ensureRunArray,
9702
9785
  makeEvent,
9703
9786
  runLifecycle,
9704
9787
  toolCorrelation,
9705
- rootPlan,
9706
- taskLifecycle,
9788
+ taskState,
9707
9789
  subagents,
9708
9790
  resolveToolActor
9709
9791
  });
@@ -9721,8 +9803,6 @@ function createFeedMapper(bootstrap) {
9721
9803
  const subagentProjection = createSubagentProjection({
9722
9804
  ensureRunArray,
9723
9805
  makeEvent,
9724
- runLifecycle,
9725
- actors,
9726
9806
  subagents
9727
9807
  });
9728
9808
  const fileConfigProjection = createFileConfigProjection({
@@ -9732,7 +9812,7 @@ function createFeedMapper(bootstrap) {
9732
9812
  const statusProjection = createStatusProjection({
9733
9813
  ensureRunArray,
9734
9814
  makeEvent,
9735
- taskLifecycle
9815
+ taskState
9736
9816
  });
9737
9817
  const currentScope = () => subagents.currentScope();
9738
9818
  const runSessionProjection = createRunSessionProjection({
@@ -9741,7 +9821,7 @@ function createFeedMapper(bootstrap) {
9741
9821
  closeRunIntoEvent,
9742
9822
  runLifecycle,
9743
9823
  agentMessageStream,
9744
- rootPlan,
9824
+ taskState,
9745
9825
  resolveToolActor,
9746
9826
  currentScope
9747
9827
  });
@@ -9749,33 +9829,13 @@ function createFeedMapper(bootstrap) {
9749
9829
  const d = event.data;
9750
9830
  const eventKind2 = event.kind;
9751
9831
  const results = [];
9752
- function emitFallbackMessage(parentKind, actorId, scope) {
9753
- if (results.some((r) => r.kind === "agent.message")) return;
9754
- const msg = readString(d["last_assistant_message"]);
9755
- if (!msg) return;
9756
- const parentEvt = results.find((r) => r.kind === parentKind);
9757
- const ev = agentMessageStream.emit({
9758
- runtimeEvent: event,
9759
- actorId,
9760
- scope,
9761
- message: msg,
9762
- source: "hook",
9763
- cause: parentEvt ? { parent_event_id: parentEvt.event_id } : void 0
9764
- });
9765
- if (ev) results.push(ev);
9766
- }
9767
- const transcriptPath = event.context.transcriptPath;
9768
- const isStopEvent = eventKind2 === "stop.request" || eventKind2 === "subagent.stop";
9769
- if (transcriptPath && !isStopEvent) {
9770
- results.push(
9771
- ...agentMessageStream.emitTranscriptMessages(
9772
- transcriptPath,
9773
- event,
9774
- resolveToolActor(),
9775
- currentScope()
9776
- )
9777
- );
9778
- }
9832
+ results.push(
9833
+ ...agentMessageStream.replayBeforeEvent(
9834
+ event,
9835
+ resolveToolActor(),
9836
+ currentScope()
9837
+ )
9838
+ );
9779
9839
  if (RUN_SESSION_EVENT_KINDS.has(eventKind2)) {
9780
9840
  results.push(...runSessionProjection.mapRunSessionEvent(event, d));
9781
9841
  } else if (TOOL_EVENT_KINDS.has(eventKind2)) {
@@ -9810,13 +9870,25 @@ function createFeedMapper(bootstrap) {
9810
9870
  results.push(unknownEvt);
9811
9871
  }
9812
9872
  if (eventKind2 === "stop.request") {
9813
- if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
9814
- emitFallbackMessage("stop.request", "agent:root", "root");
9873
+ results.push(
9874
+ ...agentMessageStream.emitStopFallback(event, {
9875
+ actorId: "agent:root",
9876
+ scope: "root",
9877
+ parentKind: "stop.request",
9878
+ priorResults: results
9879
+ })
9880
+ );
9815
9881
  }
9816
9882
  if (eventKind2 === "subagent.stop") {
9817
9883
  const agentId = readString(d["agent_id"]) ?? "unknown";
9818
- if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
9819
- emitFallbackMessage("subagent.stop", `subagent:${agentId}`, "subagent");
9884
+ results.push(
9885
+ ...agentMessageStream.emitStopFallback(event, {
9886
+ actorId: subagents.actorIdFor(agentId),
9887
+ scope: "subagent",
9888
+ parentKind: "subagent.stop",
9889
+ priorResults: results
9890
+ })
9891
+ );
9820
9892
  }
9821
9893
  return results;
9822
9894
  }
@@ -9829,7 +9901,7 @@ function createFeedMapper(bootstrap) {
9829
9901
  getSession: () => runLifecycle.getSession(),
9830
9902
  getCurrentRun: () => runLifecycle.getCurrentRun(),
9831
9903
  getActors: () => actors.all(),
9832
- getTasks: () => [...rootPlan.current(), ...taskLifecycle.current()],
9904
+ getTasks: () => taskState.current(),
9833
9905
  allocateSeq: () => runLifecycle.allocateSeq()
9834
9906
  };
9835
9907
  }
@@ -12431,8 +12503,32 @@ var VERBOSE_ONLY_KINDS = /* @__PURE__ */ new Set([
12431
12503
  "worktree.remove",
12432
12504
  "turn.diff",
12433
12505
  "usage.update",
12434
- "reasoning.summary"
12506
+ "reasoning.summary",
12507
+ // Codex protocol bookkeeping that churns the feed without conveying
12508
+ // meaningful agent progress. `usage.update` (above) still feeds header
12509
+ // token/context metrics via the raw event stream — only the rendered row
12510
+ // is suppressed in the default (non-verbose) feed.
12511
+ "thread.status",
12512
+ "server.request.resolved"
12435
12513
  ]);
12514
+ var VERBOSE_ONLY_NOTIFICATION_TYPES = /* @__PURE__ */ new Set([
12515
+ "account.rate_limits_updated",
12516
+ "account.updated",
12517
+ "item.agentMessage.started",
12518
+ "raw_response_item.completed",
12519
+ "command_exec.output_delta",
12520
+ "fuzzy_file_search.updated",
12521
+ "fuzzy_file_search.completed",
12522
+ "app.list_updated",
12523
+ "thread_name",
12524
+ "thread.archived",
12525
+ "thread.unarchived",
12526
+ "thread.realtime.transcript_delta",
12527
+ "thread.realtime.output_audio_delta"
12528
+ ]);
12529
+ function isVerboseOnlyNotification(event) {
12530
+ return event.kind === "notification" && typeof event.data.notification_type === "string" && VERBOSE_ONLY_NOTIFICATION_TYPES.has(event.data.notification_type);
12531
+ }
12436
12532
  function toRunStatus(event) {
12437
12533
  switch (event.data.status) {
12438
12534
  case "completed":
@@ -13221,6 +13317,9 @@ function shouldSkipEvent(event, verbose) {
13221
13317
  if (!verbose && event.kind === "stop.request" && !event.data.stop_hook_active) {
13222
13318
  return true;
13223
13319
  }
13320
+ if (!verbose && isVerboseOnlyNotification(event)) {
13321
+ return true;
13322
+ }
13224
13323
  return false;
13225
13324
  }
13226
13325
  function mergedToolUseId(event, postByToolUseId) {
@@ -13880,7 +13979,7 @@ async function connect(opts) {
13880
13979
  const entry = pending.get(requestId);
13881
13980
  if (!entry) return;
13882
13981
  pending.delete(requestId);
13883
- clearTimeout(entry.timer);
13982
+ if (entry.timer) clearTimeout(entry.timer);
13884
13983
  entry.resolve(parsed);
13885
13984
  return;
13886
13985
  }
@@ -13904,7 +14003,7 @@ async function connect(opts) {
13904
14003
  )
13905
14004
  );
13906
14005
  for (const [, p] of pending) {
13907
- clearTimeout(p.timer);
14006
+ if (p.timer) clearTimeout(p.timer);
13908
14007
  p.reject(
13909
14008
  new GatewayProtocolError("connection closed", "connection_closed")
13910
14009
  );
@@ -13964,14 +14063,14 @@ async function connect(opts) {
13964
14063
  kind,
13965
14064
  payload
13966
14065
  };
13967
- const effectiveTimeoutMs = reqOpts?.timeoutMs ?? timeoutMs;
14066
+ const effectiveTimeoutMs = reqOpts?.timeoutMs === void 0 ? timeoutMs : reqOpts.timeoutMs;
13968
14067
  const responsePromise = new Promise(
13969
14068
  (resolve, reject) => {
13970
- const timer = setTimeout(() => {
14069
+ const timer = effectiveTimeoutMs === null ? void 0 : setTimeout(() => {
13971
14070
  pending.delete(requestId);
13972
14071
  reject(new GatewayProtocolError(`request ${kind} timed out`));
13973
14072
  }, effectiveTimeoutMs);
13974
- pending.set(requestId, { resolve, reject, timer });
14073
+ pending.set(requestId, { resolve, reject, ...timer ? { timer } : {} });
13975
14074
  }
13976
14075
  );
13977
14076
  connection.send(envelope);
@@ -14227,7 +14326,6 @@ function writeGatewayClientConfig(config, env = process.env) {
14227
14326
  }
14228
14327
 
14229
14328
  // src/app/channels/sessionBridge.ts
14230
- var RELAY_REQUEST_TIMEOUT_MS = 6 * 6e4;
14231
14329
  var RECONNECT_BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
14232
14330
  var SessionBridge = class {
14233
14331
  opts;
@@ -14379,9 +14477,9 @@ var SessionBridge = class {
14379
14477
  toolName: req.toolName,
14380
14478
  description: req.description,
14381
14479
  inputPreview: req.inputPreview,
14382
- ...req.ttlMs !== void 0 ? { ttlMs: req.ttlMs } : {}
14480
+ ttlMs: req.ttlMs ?? null
14383
14481
  };
14384
- const overallTimeoutMs = req.ttlMs === null ? null : req.ttlMs ?? RELAY_REQUEST_TIMEOUT_MS;
14482
+ const overallTimeoutMs = req.ttlMs ?? null;
14385
14483
  return this.requestWithReconnect("relay.permission.request", payload, overallTimeoutMs);
14386
14484
  }
14387
14485
  async relayQuestion(req) {
@@ -14390,9 +14488,9 @@ var SessionBridge = class {
14390
14488
  channelRequestId,
14391
14489
  title: req.title,
14392
14490
  questions: req.questions,
14393
- ...req.ttlMs !== void 0 ? { ttlMs: req.ttlMs } : {}
14491
+ ttlMs: req.ttlMs ?? null
14394
14492
  };
14395
- const overallTimeoutMs = req.ttlMs === null ? null : req.ttlMs ?? RELAY_REQUEST_TIMEOUT_MS;
14493
+ const overallTimeoutMs = req.ttlMs ?? null;
14396
14494
  return this.requestWithReconnect("relay.question.request", payload, overallTimeoutMs);
14397
14495
  }
14398
14496
  /**
@@ -14400,8 +14498,9 @@ var SessionBridge = class {
14400
14498
  * survives a transient WS disconnect: on `connection closed`, wait for
14401
14499
  * the bridge's reconnect to settle, then re-issue the same payload. The
14402
14500
  * server attaches the replay to the existing pending entry by
14403
- * `channelRequestId`, so adapters are not re-prompted. Bounded by the
14404
- * caller-provided overall timeout so a long outage still surfaces.
14501
+ * `channelRequestId`, so adapters are not re-prompted. If the caller
14502
+ * provided an explicit TTL, reconnect retries are bounded by that deadline;
14503
+ * otherwise human-in-the-loop relays wait until answered or cancelled.
14405
14504
  */
14406
14505
  async requestWithReconnect(kind, payload, overallTimeoutMs) {
14407
14506
  const deadline = overallTimeoutMs === null ? null : Date.now() + overallTimeoutMs;
@@ -14413,9 +14512,7 @@ var SessionBridge = class {
14413
14512
  }
14414
14513
  try {
14415
14514
  return await client.request(kind, payload, {
14416
- // When unbounded, pass the largest safe setTimeout value so the
14417
- // inner request never self-cancels in any human time scale.
14418
- timeoutMs: remaining ?? 2147483647
14515
+ timeoutMs: remaining
14419
14516
  });
14420
14517
  } catch (err) {
14421
14518
  if (err instanceof GatewayProtocolError && err.code === "connection_closed" && !this.stopped && (deadline === null || Date.now() < deadline)) {
@@ -15039,8 +15136,8 @@ async function runExec(options) {
15039
15136
  const runtimeFactory = options.runtimeFactory ?? createRuntime;
15040
15137
  const sessionStoreFactory = options.sessionStoreFactory ?? createSessionStore;
15041
15138
  const athenaSessionId = options.athenaSessionId ?? crypto2.randomUUID();
15042
- const ownsFeedPublisher = !options.dashboardFeedPublisher;
15043
- const dashboardFeedPublisher = options.dashboardFeedPublisher ?? createPairedFeedPublisher();
15139
+ const ownedFeedPublisher = options.dashboardFeedPublisher ? null : createPairedFeedPublisher();
15140
+ const dashboardFeedPublisher = options.dashboardFeedPublisher ?? ownedFeedPublisher;
15044
15141
  const dashboardOrigin = options.dashboardOrigin ?? "local";
15045
15142
  const output = createExecOutputWriter({
15046
15143
  json,
@@ -15409,9 +15506,7 @@ async function runExec(options) {
15409
15506
  }
15410
15507
  await bridge?.stop();
15411
15508
  store.close();
15412
- if (ownsFeedPublisher) {
15413
- dashboardFeedPublisher.close();
15414
- }
15509
+ ownedFeedPublisher?.close();
15415
15510
  }
15416
15511
  const resolvedFinalMessage = resolveFinalMessage({
15417
15512
  streamMessage: streamFinalMessage,
@@ -16419,8 +16514,26 @@ function parseRemoteRunSpec(value) {
16419
16514
  callbackToken: typeof callbackToken === "string" && callbackToken.length > 0 ? callbackToken : void 0
16420
16515
  };
16421
16516
  }
16422
- function isRemoteAssignmentAdmissible(frame) {
16423
- return parseRemoteRunSpec(frame.runSpec) !== null;
16517
+ function validateDashboardAssignment(frame) {
16518
+ const spec = parseRemoteRunSpec(frame.runSpec);
16519
+ if (!spec) {
16520
+ return {
16521
+ kind: "rejected",
16522
+ rejection: {
16523
+ reason: "malformed_assignment",
16524
+ message: "remote assignment missing prompt"
16525
+ }
16526
+ };
16527
+ }
16528
+ return {
16529
+ kind: "valid",
16530
+ assignment: {
16531
+ runId: frame.runId,
16532
+ runnerId: frame.runnerId ?? "legacy",
16533
+ spec,
16534
+ frame
16535
+ }
16536
+ };
16424
16537
  }
16425
16538
  function workflowNameFromRef(ref) {
16426
16539
  if (!ref) return void 0;
@@ -16501,7 +16614,7 @@ function mergeRunSpecEnvIntoWorkflow(workflow, env) {
16501
16614
  };
16502
16615
  }
16503
16616
  async function executeRemoteAssignment({
16504
- frame,
16617
+ assignment,
16505
16618
  client,
16506
16619
  projectDir,
16507
16620
  log = () => {
@@ -16524,11 +16637,11 @@ async function executeRemoteAssignment({
16524
16637
  const deferredFailedCompletion = {
16525
16638
  current: null
16526
16639
  };
16527
- const spec = parseRemoteRunSpec(frame.runSpec);
16640
+ const { spec, runId, frame } = assignment;
16528
16641
  const runEventPublisher = await createRemoteRunEventPublisher({
16529
- runId: frame.runId,
16530
- callbackWsUrl: spec?.callbackWsUrl,
16531
- callbackToken: spec?.callbackToken,
16642
+ runId,
16643
+ callbackWsUrl: spec.callbackWsUrl,
16644
+ callbackToken: spec.callbackToken,
16532
16645
  client,
16533
16646
  log,
16534
16647
  now,
@@ -16543,10 +16656,6 @@ async function executeRemoteAssignment({
16543
16656
  };
16544
16657
  send("progress", { message: "assignment received" });
16545
16658
  try {
16546
- if (!spec) {
16547
- send("error", { message: "remote assignment missing prompt" });
16548
- return;
16549
- }
16550
16659
  let artifactUploadSpec;
16551
16660
  try {
16552
16661
  artifactUploadSpec = parseArtifactUploadSpec(frame.runSpec);
@@ -16627,7 +16736,7 @@ async function executeRemoteAssignment({
16627
16736
  prompt: spec.prompt,
16628
16737
  projectDir,
16629
16738
  harness: runtimeConfig.harness,
16630
- athenaSessionId: spec.athenaSessionId ?? spec.sessionId ?? `athena-${frame.runId}`,
16739
+ athenaSessionId: spec.athenaSessionId ?? spec.sessionId ?? `athena-${runId}`,
16631
16740
  adapterResumeSessionId: spec.adapterResumeSessionId,
16632
16741
  isolationConfig: runtimeConfig.isolationConfig,
16633
16742
  pluginMcpConfig: runtimeConfig.pluginMcpConfig,
@@ -16644,8 +16753,8 @@ async function executeRemoteAssignment({
16644
16753
  ...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {},
16645
16754
  ...dashboardFeedPublisher ? { dashboardFeedPublisher } : {},
16646
16755
  ...artifactUploadSpec ? {
16647
- beforeTerminalCompletion: async ({ result: result2, runId }) => {
16648
- const artifactRunId = runId ?? frame.runId;
16756
+ beforeTerminalCompletion: async ({ result: result2, runId: runId2 }) => {
16757
+ const artifactRunId = runId2 ?? assignment.runId;
16649
16758
  const { feedEvent } = await captureAndUploadArtifacts({
16650
16759
  spec: artifactUploadSpec,
16651
16760
  projectDir,
@@ -17055,54 +17164,55 @@ function createDashboardPairedExecution(options) {
17055
17164
  });
17056
17165
  log("warn", `run ${runId} rejected: ${rejection.message}`);
17057
17166
  }
17058
- function handleDecision(frame) {
17167
+ function submitDashboardDecision(submission) {
17059
17168
  decisionInbox.enqueue({
17060
- athenaSessionId: frame.athenaSessionId,
17061
- requestId: frame.requestId,
17062
- decision: frame.decision,
17169
+ athenaSessionId: submission.athenaSessionId,
17170
+ requestId: submission.requestId,
17171
+ decision: submission.decision,
17063
17172
  receivedAt: now()
17064
17173
  });
17065
17174
  client.sendDecisionAck({
17066
- athenaSessionId: frame.athenaSessionId,
17067
- requestId: frame.requestId
17175
+ athenaSessionId: submission.athenaSessionId,
17176
+ requestId: submission.requestId
17068
17177
  });
17069
17178
  }
17070
- function handleCancel(frame) {
17071
- const entry = active.get(frame.runId);
17072
- if (!entry) return;
17179
+ function cancelRun(runId) {
17180
+ const entry = active.get(runId);
17181
+ if (!entry) return false;
17073
17182
  entry.record.status = "cancelled";
17074
17183
  entry.controller.abort();
17184
+ return true;
17075
17185
  }
17076
- function handleAssignment(frame, input = {}) {
17077
- if (active.has(frame.runId)) {
17186
+ function handleAssignment(assignment, input = {}) {
17187
+ const { runId, runnerId } = assignment;
17188
+ if (active.has(runId)) {
17078
17189
  const rejection = {
17079
17190
  reason: "duplicate",
17080
- message: `duplicate active assignment ${frame.runId}`
17191
+ message: `duplicate active assignment ${runId}`
17081
17192
  };
17082
- rejectAssignment(frame.runId, rejection);
17193
+ rejectAssignment(runId, rejection);
17083
17194
  return { kind: "rejected", rejection };
17084
17195
  }
17085
- const runnerKey = frame.runnerId;
17086
- const bucket = activeByRunner.get(runnerKey) ?? /* @__PURE__ */ new Set();
17196
+ const bucket = activeByRunner.get(runnerId) ?? /* @__PURE__ */ new Set();
17087
17197
  if (bucket.size >= maxConcurrentRuns) {
17088
17198
  const rejection = {
17089
17199
  reason: "local_capacity",
17090
- message: `runtime daemon at concurrency cap (${maxConcurrentRuns}) for runner ${runnerKey ?? "<legacy>"}`
17200
+ message: `runtime daemon at concurrency cap (${maxConcurrentRuns}) for runner ${runnerId}`
17091
17201
  };
17092
- rejectAssignment(frame.runId, rejection);
17202
+ rejectAssignment(runId, rejection);
17093
17203
  return { kind: "rejected", rejection };
17094
17204
  }
17095
17205
  const controller = new AbortController();
17096
17206
  const record = {
17097
- runId: frame.runId,
17207
+ runId,
17098
17208
  startedAt: now(),
17099
17209
  status: "running"
17100
17210
  };
17101
17211
  recordRun(record);
17102
- bucket.add(frame.runId);
17103
- activeByRunner.set(runnerKey, bucket);
17212
+ bucket.add(runId);
17213
+ activeByRunner.set(runnerId, bucket);
17104
17214
  const promise = executor({
17105
- frame,
17215
+ assignment,
17106
17216
  client,
17107
17217
  projectDir: input.projectDir ?? projectDir,
17108
17218
  log,
@@ -17118,40 +17228,33 @@ function createDashboardPairedExecution(options) {
17118
17228
  record.error = err instanceof Error ? err.message : String(err);
17119
17229
  log(
17120
17230
  "error",
17121
- `run ${frame.runId} failed: ${err instanceof Error ? err.message : String(err)}`
17231
+ `run ${runId} failed: ${err instanceof Error ? err.message : String(err)}`
17122
17232
  );
17123
17233
  }).finally(() => {
17124
17234
  record.endedAt = now();
17125
17235
  completedRuns += 1;
17126
- active.delete(frame.runId);
17127
- const remaining = activeByRunner.get(runnerKey);
17236
+ active.delete(runId);
17237
+ const remaining = activeByRunner.get(runnerId);
17128
17238
  if (remaining) {
17129
- remaining.delete(frame.runId);
17130
- if (remaining.size === 0) activeByRunner.delete(runnerKey);
17239
+ remaining.delete(runId);
17240
+ if (remaining.size === 0) activeByRunner.delete(runnerId);
17131
17241
  }
17132
17242
  });
17133
- active.set(frame.runId, { controller, promise, record, runnerKey });
17243
+ active.set(runId, { controller, promise, record, runnerKey: runnerId });
17134
17244
  return { kind: "accepted" };
17135
17245
  }
17136
17246
  return {
17137
17247
  // `job_assignment` is intentionally not handled here: the runtime daemon
17138
17248
  // routes assignments through `DashboardAssignmentIntake`, which gates
17139
17249
  // admission on attachment readiness and then calls `admitAssignment`
17140
- // directly. `handleFrame` owns only the frames that flow straight through.
17141
- handleFrame(frame) {
17142
- if (frame.type === "dashboard_decision") {
17143
- handleDecision(frame);
17144
- return true;
17145
- }
17146
- if (frame.type === "cancel") {
17147
- handleCancel(frame);
17148
- return true;
17149
- }
17150
- return false;
17151
- },
17152
- admitAssignment(frame, input) {
17153
- return handleAssignment(frame, input);
17250
+ // directly. Run-control frames (`dashboard_decision`, `cancel`) are
17251
+ // translated by `routeDashboardRunFrame` into `submitDashboardDecision`
17252
+ // and `cancelRun` calls.
17253
+ admitAssignment(assignment, input) {
17254
+ return handleAssignment(assignment, input);
17154
17255
  },
17256
+ cancelRun,
17257
+ submitDashboardDecision,
17155
17258
  rejectAssignment,
17156
17259
  snapshot() {
17157
17260
  return {
@@ -17182,22 +17285,12 @@ function createDashboardPairedExecution(options) {
17182
17285
  import fs23 from "fs";
17183
17286
  import os13 from "os";
17184
17287
  import path21 from "path";
17185
- function resolveRemoteWorkspace(frame, options = {}) {
17186
- const spec = parseRemoteRunSpec(frame.runSpec);
17187
- if (!spec) {
17188
- return {
17189
- kind: "rejected",
17190
- rejection: {
17191
- reason: "workspace_unresolved",
17192
- message: "remote assignment missing prompt"
17193
- }
17194
- };
17195
- }
17288
+ function resolveRemoteWorkspace(assignment, options = {}) {
17289
+ const { spec, runId, runnerId } = assignment;
17196
17290
  if (spec.projectDir) {
17197
17291
  return validateProjectDir(spec.projectDir, options.env);
17198
17292
  }
17199
17293
  const sessionId = spec.athenaSessionId ?? spec.sessionId;
17200
- const runnerId = frame.runnerId ?? "legacy";
17201
17294
  const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
17202
17295
  const stateDir = daemonStatePaths(options.env).dir;
17203
17296
  const projectDir = sessionId ? path21.join(
@@ -17213,7 +17306,7 @@ function resolveRemoteWorkspace(frame, options = {}) {
17213
17306
  deploymentSlug,
17214
17307
  sanitizePathSegment(runnerId),
17215
17308
  "runs",
17216
- sanitizePathSegment(frame.runId)
17309
+ sanitizePathSegment(runId)
17217
17310
  );
17218
17311
  try {
17219
17312
  fs23.mkdirSync(projectDir, { recursive: true, mode: 448 });
@@ -17296,23 +17389,21 @@ function sanitizePathSegment(value) {
17296
17389
  function createDashboardAssignmentIntake(options) {
17297
17390
  const log = options.log ?? (() => {
17298
17391
  });
17299
- const resolveWorkspace = options.resolveWorkspace ?? ((frame) => resolveRemoteWorkspace(frame));
17392
+ const resolveWorkspace = options.resolveWorkspace ?? ((assignment, context2) => resolveRemoteWorkspace(assignment, { dashboardUrl: context2.dashboardUrl }));
17300
17393
  const pending = [];
17301
- let ready = false;
17302
- function handle(frame) {
17303
- if (!isRemoteAssignmentAdmissible(frame)) {
17304
- const rejection = {
17305
- reason: "malformed_assignment",
17306
- message: "remote assignment missing prompt"
17307
- };
17308
- options.execution.rejectAssignment(frame.runId, rejection);
17394
+ let context = null;
17395
+ function handle(frame, readyContext) {
17396
+ const validation = validateDashboardAssignment(frame);
17397
+ if (validation.kind === "rejected") {
17398
+ options.execution.rejectAssignment(frame.runId, validation.rejection);
17309
17399
  options.client.sendAssignmentRejected({
17310
17400
  runId: frame.runId,
17311
- ...rejection
17401
+ ...validation.rejection
17312
17402
  });
17313
17403
  return;
17314
17404
  }
17315
- const workspace = resolveWorkspace(frame);
17405
+ const assignment = validation.assignment;
17406
+ const workspace = resolveWorkspace(assignment, readyContext);
17316
17407
  if (workspace.kind === "rejected") {
17317
17408
  options.execution.rejectAssignment(frame.runId, workspace.rejection);
17318
17409
  options.client.sendAssignmentRejected({
@@ -17321,7 +17412,7 @@ function createDashboardAssignmentIntake(options) {
17321
17412
  });
17322
17413
  return;
17323
17414
  }
17324
- const outcome = options.execution.admitAssignment(frame, {
17415
+ const outcome = options.execution.admitAssignment(assignment, {
17325
17416
  projectDir: workspace.projectDir
17326
17417
  });
17327
17418
  if (outcome.kind === "accepted") {
@@ -17334,15 +17425,15 @@ function createDashboardAssignmentIntake(options) {
17334
17425
  });
17335
17426
  }
17336
17427
  function drain() {
17337
- if (!ready) return;
17428
+ if (!context) return;
17338
17429
  while (pending.length > 0) {
17339
17430
  const frame = pending.shift();
17340
- if (frame) handle(frame);
17431
+ if (frame) handle(frame, context);
17341
17432
  }
17342
17433
  }
17343
17434
  return {
17344
17435
  receive(frame) {
17345
- if (!ready) {
17436
+ if (!context) {
17346
17437
  pending.push(frame);
17347
17438
  log(
17348
17439
  "debug",
@@ -17350,18 +17441,35 @@ function createDashboardAssignmentIntake(options) {
17350
17441
  );
17351
17442
  return;
17352
17443
  }
17353
- handle(frame);
17444
+ handle(frame, context);
17354
17445
  },
17355
- markReady() {
17356
- ready = true;
17446
+ markReady(nextContext) {
17447
+ context = nextContext;
17357
17448
  drain();
17358
17449
  },
17359
17450
  markNotReady() {
17360
- ready = false;
17451
+ context = null;
17361
17452
  }
17362
17453
  };
17363
17454
  }
17364
17455
 
17456
+ // src/app/dashboard/dashboardFrameRouter.ts
17457
+ function routeDashboardRunFrame(execution, frame) {
17458
+ if (frame.type === "dashboard_decision") {
17459
+ execution.submitDashboardDecision({
17460
+ athenaSessionId: frame.athenaSessionId,
17461
+ requestId: frame.requestId,
17462
+ decision: frame.decision
17463
+ });
17464
+ return true;
17465
+ }
17466
+ if (frame.type === "cancel") {
17467
+ execution.cancelRun(frame.runId);
17468
+ return true;
17469
+ }
17470
+ return false;
17471
+ }
17472
+
17365
17473
  // src/app/dashboard/runtimeDaemon.ts
17366
17474
  var DEFAULT_RECONNECT_DELAYS_MS2 = [1e3, 2e3, 5e3, 1e4, 3e4];
17367
17475
  var DEFAULT_MAX_CONCURRENT_RUNS2 = 1;
@@ -17465,7 +17573,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
17465
17573
  },
17466
17574
  execution: pairedExecution,
17467
17575
  log,
17468
- resolveWorkspace: (frame) => resolveRemoteWorkspace(frame, { dashboardUrl: currentDashboardUrl })
17576
+ resolveWorkspace: (assignment, context) => resolveRemoteWorkspace(assignment, { dashboardUrl: context.dashboardUrl })
17469
17577
  });
17470
17578
  function nextReconnectDelay() {
17471
17579
  if (reconnectDelays.length === 0) return 0;
@@ -17584,7 +17692,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
17584
17692
  assignmentIntake.receive(frame);
17585
17693
  return;
17586
17694
  }
17587
- pairedExecution.handleFrame(frame);
17695
+ routeDashboardRunFrame(pairedExecution, frame);
17588
17696
  });
17589
17697
  next.onClose((reason) => {
17590
17698
  if (stopped || client !== next) return;
@@ -17614,9 +17722,12 @@ async function runDashboardRuntimeDaemon(options = {}) {
17614
17722
  );
17615
17723
  }
17616
17724
  if (stopped || client !== next) return;
17617
- assignmentIntake.markReady();
17618
17725
  currentInstanceId = token.instanceId;
17619
17726
  currentDashboardUrl = config.dashboardUrl;
17727
+ assignmentIntake.markReady({
17728
+ dashboardUrl: config.dashboardUrl,
17729
+ instanceId: token.instanceId
17730
+ });
17620
17731
  reconnectAttempt = 0;
17621
17732
  scheduleRefresh(token.expiresInSec);
17622
17733
  pairedFeedPublisher.attachTransport(next);
@@ -18076,4 +18187,4 @@ export {
18076
18187
  startUdsServer,
18077
18188
  sendUdsRequest
18078
18189
  };
18079
- //# sourceMappingURL=chunk-2QMUBFZZ.js.map
18190
+ //# sourceMappingURL=chunk-BUNMENOT.js.map