@drisp/cli 0.5.10 → 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
  }
@@ -7167,15 +7165,10 @@ function loadPlugin(pluginDir) {
7167
7165
  }
7168
7166
  const mcpConfigPath = path12.join(pluginDir, ".mcp.json");
7169
7167
  const hasMcpConfig = fs14.existsSync(mcpConfigPath);
7170
- const entries = fs14.readdirSync(skillsDir, { withFileTypes: true });
7171
7168
  const commands2 = [];
7172
- for (const entry of entries) {
7173
- if (!entry.isDirectory()) continue;
7174
- const skillPath = path12.join(skillsDir, entry.name, "SKILL.md");
7175
- if (!fs14.existsSync(skillPath)) continue;
7176
- const content = fs14.readFileSync(skillPath, "utf-8");
7177
- const parsed = parseFrontmatter(content);
7178
- if (!parsed.frontmatter["user-invocable"]) continue;
7169
+ const loadSkillFile = (skillPath) => {
7170
+ const parsed = parseFrontmatter(fs14.readFileSync(skillPath, "utf-8"));
7171
+ if (!parsed.frontmatter["user-invocable"]) return;
7179
7172
  commands2.push(
7180
7173
  skillToCommand(
7181
7174
  parsed.frontmatter,
@@ -7183,6 +7176,20 @@ function loadPlugin(pluginDir) {
7183
7176
  hasMcpConfig ? mcpConfigPath : void 0
7184
7177
  )
7185
7178
  );
7179
+ };
7180
+ for (const entry of fs14.readdirSync(skillsDir, { withFileTypes: true })) {
7181
+ if (!entry.isDirectory()) continue;
7182
+ const entryDir = path12.join(skillsDir, entry.name);
7183
+ const directSkill = path12.join(entryDir, "SKILL.md");
7184
+ if (fs14.existsSync(directSkill)) {
7185
+ loadSkillFile(directSkill);
7186
+ continue;
7187
+ }
7188
+ for (const nested of fs14.readdirSync(entryDir, { withFileTypes: true })) {
7189
+ if (!nested.isDirectory()) continue;
7190
+ const nestedSkill = path12.join(entryDir, nested.name, "SKILL.md");
7191
+ if (fs14.existsSync(nestedSkill)) loadSkillFile(nestedSkill);
7192
+ }
7186
7193
  }
7187
7194
  return commands2;
7188
7195
  }
@@ -7851,7 +7858,8 @@ function createTranscriptReader() {
7851
7858
  }
7852
7859
 
7853
7860
  // src/core/feed/internals/runLifecycle.ts
7854
- function createRunLifecycle() {
7861
+ function createRunLifecycle(boundary) {
7862
+ const { makeEvent, resetPerRunState } = boundary;
7855
7863
  let currentSession = null;
7856
7864
  let currentRun = null;
7857
7865
  let seq = 0;
@@ -7860,17 +7868,79 @@ function createRunLifecycle() {
7860
7868
  const sessId = currentSession?.session_id ?? "unknown";
7861
7869
  return `${sessId}:R${runSeq}`;
7862
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
+ }
7863
7937
  return {
7864
- allocateSeq() {
7865
- return ++seq;
7866
- },
7938
+ allocateSeq,
7867
7939
  getRunId,
7868
7940
  getSession() {
7869
7941
  return currentSession;
7870
7942
  },
7871
- getCurrentRun() {
7872
- return currentRun;
7873
- },
7943
+ getCurrentRun,
7874
7944
  setSession(session) {
7875
7945
  currentSession = session;
7876
7946
  },
@@ -7883,32 +7953,10 @@ function createRunLifecycle() {
7883
7953
  incrementCounter(name) {
7884
7954
  if (currentRun) currentRun.counters[name]++;
7885
7955
  },
7886
- closeRun(ts, status) {
7887
- if (!currentRun) return null;
7888
- currentRun.status = status;
7889
- currentRun.ended_at = ts;
7890
- const closed = currentRun;
7891
- currentRun = null;
7892
- return closed;
7893
- },
7894
- openNewRun(ts, sessionId, triggerType, promptPreview) {
7895
- runSeq++;
7896
- currentRun = {
7897
- run_id: getRunId(),
7898
- session_id: sessionId,
7899
- started_at: ts,
7900
- trigger: { type: triggerType, prompt_preview: promptPreview },
7901
- status: "running",
7902
- actors: { root_agent_id: "agent:root", subagent_ids: [] },
7903
- counters: {
7904
- tool_uses: 0,
7905
- tool_failures: 0,
7906
- permission_requests: 0,
7907
- blocks: 0
7908
- }
7909
- };
7910
- return currentRun;
7911
- },
7956
+ closeRun,
7957
+ openNewRun,
7958
+ closeRunIntoEvent,
7959
+ beginRun,
7912
7960
  restoreFrom(bootstrap) {
7913
7961
  for (const e of bootstrap.feedEvents) {
7914
7962
  if (e.seq > seq) seq = e.seq;
@@ -8028,6 +8076,34 @@ function createToolCorrelation() {
8028
8076
  };
8029
8077
  }
8030
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
+
8031
8107
  // src/core/feed/internals/agentMessageStream.ts
8032
8108
  function normalizeAgentMessage(message) {
8033
8109
  return message.replace(/\r\n/g, "\n").trimEnd();
@@ -8035,6 +8111,9 @@ function normalizeAgentMessage(message) {
8035
8111
  function agentMessageKey(actorId, scope) {
8036
8112
  return `${actorId}\0${scope}`;
8037
8113
  }
8114
+ function isStopEvent(kind) {
8115
+ return kind === "stop.request" || kind === "subagent.stop";
8116
+ }
8038
8117
  function createAgentMessageStream(eventBuilder, transcriptReader) {
8039
8118
  const pendingMessages = /* @__PURE__ */ new Map();
8040
8119
  const lastAgentMessageByActorScope = /* @__PURE__ */ new Map();
@@ -8061,6 +8140,25 @@ function createAgentMessageStream(eventBuilder, transcriptReader) {
8061
8140
  lastAgentMessageByActorScope.set(key, normalized);
8062
8141
  return event;
8063
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
+ }
8064
8162
  return {
8065
8163
  emit,
8066
8164
  appendPendingDelta(itemId, delta, defaultActorId, defaultScope) {
@@ -8116,25 +8214,37 @@ function createAgentMessageStream(eventBuilder, transcriptReader) {
8116
8214
  }
8117
8215
  return out;
8118
8216
  },
8119
- emitTranscriptMessages(transcriptPath, runtimeEvent, actorId, scope) {
8120
- const msgs = transcriptReader.readNewAssistantMessages(transcriptPath);
8121
- const out = [];
8122
- for (const msg of msgs) {
8123
- const ev = emit({
8124
- runtimeEvent,
8125
- actorId,
8126
- scope,
8127
- message: msg.text,
8128
- source: "transcript",
8129
- model: msg.model
8130
- });
8131
- if (ev) out.push(ev);
8132
- }
8133
- 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
+ );
8134
8227
  },
8135
- drainTranscript(transcriptPath) {
8136
- 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] : [];
8137
8246
  },
8247
+ drainTranscript,
8138
8248
  appendReasoningSummary(itemId, index, chunk) {
8139
8249
  const key = `${itemId ?? ""}:${index ?? 0}`;
8140
8250
  const next = `${reasoningSummaryByKey.get(key) ?? ""}${chunk}`;
@@ -8233,6 +8343,129 @@ function createTaskLifecycleTracker() {
8233
8343
  };
8234
8344
  }
8235
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
+
8236
8469
  // src/core/feed/internals/subagentTracker.ts
8237
8470
  function createSubagentTracker() {
8238
8471
  const stack = [];
@@ -8275,44 +8508,57 @@ function createSubagentTracker() {
8275
8508
  };
8276
8509
  }
8277
8510
 
8278
- // src/core/feed/internals/projection.ts
8279
- function readString(...values) {
8280
- for (const value of values) {
8281
- if (typeof value === "string") return value;
8282
- }
8283
- return void 0;
8284
- }
8285
- function readBoolean(...values) {
8286
- for (const value of values) {
8287
- if (typeof value === "boolean") return value;
8288
- }
8289
- return void 0;
8290
- }
8291
- function readObject(...values) {
8292
- for (const value of values) {
8293
- if (typeof value === "object" && value !== null) {
8294
- return value;
8295
- }
8296
- }
8297
- return {};
8298
- }
8299
- function readSuggestionArray(...values) {
8300
- for (const value of values) {
8301
- if (Array.isArray(value)) return value;
8302
- }
8303
- return void 0;
8304
- }
8305
-
8306
8511
  // src/core/feed/todo.ts
8307
8512
  function isSubagentTool(toolName) {
8308
8513
  return toolName === "Task" || toolName === "Agent";
8309
8514
  }
8310
8515
 
8311
- // src/core/feed/internals/toolProjection.ts
8312
- function extractTodoItems(toolInput) {
8313
- const input = toolInput;
8314
- 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
+ };
8315
8559
  }
8560
+
8561
+ // src/core/feed/internals/toolProjection.ts
8316
8562
  function resolveToolUseId(event, record) {
8317
8563
  return event.toolUseId ?? record["tool_use_id"];
8318
8564
  }
@@ -8328,8 +8574,7 @@ function createToolProjection(args) {
8328
8574
  makeEvent,
8329
8575
  runLifecycle,
8330
8576
  toolCorrelation,
8331
- rootPlan,
8332
- taskLifecycle,
8577
+ taskState,
8333
8578
  subagents,
8334
8579
  resolveToolActor
8335
8580
  } = args;
@@ -8426,23 +8671,12 @@ function createToolProjection(args) {
8426
8671
  webSearchStarted(event, data, toolUseId, preEvent.event_id)
8427
8672
  );
8428
8673
  }
8429
- if (toolName === "TodoWrite" && preEvent.actor_id === "agent:root") {
8430
- rootPlan.set(extractTodoItems(toolInput));
8431
- }
8432
- if (toolName === "TaskUpdate") {
8433
- const taskId = readString(toolInput["taskId"], toolInput["task_id"]);
8434
- const status = coerceTaskStatus(toolInput["status"]);
8435
- if (taskId && status) {
8436
- taskLifecycle.updateStatus({ taskId, status });
8437
- }
8438
- }
8439
- if (isSubagentTool(toolName)) {
8440
- if (typeof toolInput["description"] === "string") {
8441
- subagents.recordPendingDescription(toolInput["description"]);
8442
- } else {
8443
- subagents.clearPendingDescription();
8444
- }
8445
- }
8674
+ taskState.applyToolPre({
8675
+ toolName,
8676
+ toolInput,
8677
+ actorId: preEvent.actor_id
8678
+ });
8679
+ subagents.observeToolInput(toolName, toolInput);
8446
8680
  return results;
8447
8681
  }
8448
8682
  if (event.kind === "tool.post") {
@@ -8462,35 +8696,11 @@ function createToolProjection(args) {
8462
8696
  toolUseCause(toolUseId, parentId)
8463
8697
  );
8464
8698
  results.push(postEvent);
8465
- if (toolName === "TaskCreate") {
8466
- const response = readObject(data["tool_response"]);
8467
- const task = readObject(response["task"]);
8468
- const taskId = readString(task["id"], task["task_id"]);
8469
- const subject = readString(task["subject"], toolInput["subject"]);
8470
- if (taskId && subject) {
8471
- taskLifecycle.upsertCreated({
8472
- taskId,
8473
- subject,
8474
- description: readString(toolInput["description"]),
8475
- activeForm: readString(toolInput["activeForm"])
8476
- });
8477
- }
8478
- }
8479
- if (toolName === "TaskUpdate") {
8480
- const response = readObject(data["tool_response"]);
8481
- const taskId = readString(
8482
- response["taskId"],
8483
- response["task_id"],
8484
- toolInput["taskId"],
8485
- toolInput["task_id"]
8486
- );
8487
- const status = coerceTaskStatus(
8488
- readObject(response["statusChange"])["to"] ?? toolInput["status"]
8489
- );
8490
- if (taskId && status) {
8491
- taskLifecycle.updateStatus({ taskId, status });
8492
- }
8493
- }
8699
+ taskState.applyToolPost({
8700
+ toolName,
8701
+ toolInput,
8702
+ toolResponse: data.tool_response
8703
+ });
8494
8704
  if (toolName === "WebSearch") {
8495
8705
  results.push(
8496
8706
  webSearchCompleted(event, data, toolUseId, postEvent.event_id)
@@ -8931,29 +9141,27 @@ function createDecisionProjection(args) {
8931
9141
 
8932
9142
  // src/core/feed/internals/subagentProjection.ts
8933
9143
  function createSubagentProjection(args) {
8934
- const { ensureRunArray, makeEvent, runLifecycle, actors, subagents } = args;
9144
+ const { ensureRunArray, makeEvent, subagents } = args;
8935
9145
  return {
8936
9146
  mapSubagentEvent(event, data) {
8937
9147
  const results = ensureRunArray(event);
8938
9148
  const agentId = event.agentId ?? readString(data["agent_id"]);
8939
9149
  const agentType = event.agentType ?? readString(data["agent_type"]);
8940
9150
  if (event.kind === "subagent.start") {
8941
- if (agentId) {
8942
- actors.ensureSubagent(agentId, agentType ?? "unknown");
8943
- const currentRun = runLifecycle.getCurrentRun();
8944
- if (currentRun) currentRun.actors.subagent_ids.push(agentId);
8945
- subagents.pushActor(`subagent:${agentId}`);
8946
- }
8947
- 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
+ });
8948
9156
  results.push(
8949
9157
  makeEvent(
8950
9158
  "subagent.start",
8951
9159
  "info",
8952
- "agent:root",
9160
+ actorId2,
8953
9161
  {
8954
9162
  agent_id: agentId ?? "",
8955
9163
  agent_type: agentType ?? "",
8956
- description: description ?? void 0,
9164
+ description: description2 ?? void 0,
8957
9165
  tool: readString(data["tool"]),
8958
9166
  sender_thread_id: readString(data["sender_thread_id"]),
8959
9167
  receiver_thread_id: readString(data["receiver_thread_id"]),
@@ -8963,23 +9171,21 @@ function createSubagentProjection(args) {
8963
9171
  event
8964
9172
  )
8965
9173
  );
8966
- if (agentId && description)
8967
- subagents.setDescription(agentId, description);
8968
9174
  return results;
8969
9175
  }
8970
- if (agentId) subagents.popActor(`subagent:${agentId}`);
9176
+ const { actorId, description } = subagents.stopSubagent(agentId);
8971
9177
  results.push(
8972
9178
  makeEvent(
8973
9179
  "subagent.stop",
8974
9180
  "info",
8975
- `subagent:${agentId ?? "unknown"}`,
9181
+ actorId,
8976
9182
  {
8977
9183
  agent_id: agentId ?? "",
8978
9184
  agent_type: agentType ?? "",
8979
9185
  stop_hook_active: readBoolean(data["stop_hook_active"]) ?? false,
8980
9186
  agent_transcript_path: readString(data["agent_transcript_path"]),
8981
9187
  last_assistant_message: readString(data["last_assistant_message"]),
8982
- description: subagents.description(agentId ?? ""),
9188
+ description,
8983
9189
  tool: readString(data["tool"]),
8984
9190
  status: readString(data["status"]),
8985
9191
  sender_thread_id: readString(data["sender_thread_id"]),
@@ -9160,17 +9366,6 @@ function createFileConfigProjection(args) {
9160
9366
  }
9161
9367
 
9162
9368
  // src/core/feed/internals/runSessionProjection.ts
9163
- function mapPlanStepStatus(status) {
9164
- switch (status) {
9165
- case "inProgress":
9166
- return "in_progress";
9167
- case "completed":
9168
- return "completed";
9169
- case void 0:
9170
- default:
9171
- return "pending";
9172
- }
9173
- }
9174
9369
  function createRunSessionProjection(args) {
9175
9370
  const {
9176
9371
  ensureRunArray,
@@ -9178,7 +9373,7 @@ function createRunSessionProjection(args) {
9178
9373
  closeRunIntoEvent,
9179
9374
  runLifecycle,
9180
9375
  agentMessageStream,
9181
- rootPlan,
9376
+ taskState,
9182
9377
  resolveToolActor,
9183
9378
  currentScope
9184
9379
  } = args;
@@ -9329,28 +9524,19 @@ function createRunSessionProjection(args) {
9329
9524
  }
9330
9525
  if (event.kind === "plan.delta") {
9331
9526
  const planSteps = data["plan"];
9332
- if (Array.isArray(planSteps) && planSteps.length > 0) {
9333
- const next = planSteps.map(
9334
- (step) => ({
9335
- content: typeof step.step === "string" ? step.step : "",
9336
- status: mapPlanStepStatus(step.status)
9337
- })
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
+ )
9338
9539
  );
9339
- if (rootPlan.differs(next)) {
9340
- rootPlan.set(next);
9341
- results.push(
9342
- makeEvent(
9343
- "todo.update",
9344
- "info",
9345
- "system",
9346
- {
9347
- todo_id: "plan",
9348
- patch: { status: "doing" }
9349
- },
9350
- event
9351
- )
9352
- );
9353
- }
9354
9540
  }
9355
9541
  results.push(
9356
9542
  makeEvent(
@@ -9422,7 +9608,7 @@ function createRunSessionProjection(args) {
9422
9608
 
9423
9609
  // src/core/feed/internals/statusProjection.ts
9424
9610
  function createStatusProjection(args) {
9425
- const { ensureRunArray, makeEvent, taskLifecycle } = args;
9611
+ const { ensureRunArray, makeEvent, taskState } = args;
9426
9612
  return {
9427
9613
  mapStatusEvent(event, data) {
9428
9614
  const results = ensureRunArray(event);
@@ -9445,13 +9631,7 @@ function createStatusProjection(args) {
9445
9631
  const taskId = readString(data["task_id"]) ?? "";
9446
9632
  const subject = readString(data["task_subject"]) ?? "";
9447
9633
  const description = readString(data["task_description"]);
9448
- if (taskId && subject) {
9449
- taskLifecycle.upsertCreated({
9450
- taskId,
9451
- subject,
9452
- description
9453
- });
9454
- }
9634
+ taskState.applyTaskCreatedEvent(data);
9455
9635
  results.push(
9456
9636
  makeEvent(
9457
9637
  "task.created",
@@ -9472,7 +9652,7 @@ function createStatusProjection(args) {
9472
9652
  if (event.kind === "task.completed") {
9473
9653
  const taskId = readString(data["task_id"]) ?? "";
9474
9654
  const subject = readString(data["task_subject"]);
9475
- if (taskId) taskLifecycle.markCompleted({ taskId, subject });
9655
+ taskState.applyTaskCompletedEvent(data);
9476
9656
  results.push(
9477
9657
  makeEvent(
9478
9658
  "task.completed",
@@ -9542,14 +9722,21 @@ var STATUS_EVENT_KINDS = /* @__PURE__ */ new Set([
9542
9722
  "task.created"
9543
9723
  ]);
9544
9724
  function createFeedMapper(bootstrap) {
9545
- 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
+ });
9546
9734
  const decisionCorrelation = createDecisionCorrelation();
9547
9735
  const toolCorrelation = createToolCorrelation();
9548
9736
  const transcriptReader = createTranscriptReader();
9549
9737
  const actors = new ActorRegistry();
9550
- const rootPlan = createRootPlanTracker();
9551
- const taskLifecycle = createTaskLifecycleTracker();
9552
- const subagents = createSubagentTracker();
9738
+ const taskState = createTaskStateTracker();
9739
+ const subagents = createSubagentLifecycle({ actors, runLifecycle });
9553
9740
  function makeEvent(kind, level, actorId, data, runtimeEvent, cause) {
9554
9741
  const s = runLifecycle.allocateSeq();
9555
9742
  const runId = runLifecycle.getRunId();
@@ -9584,117 +9771,21 @@ function createFeedMapper(bootstrap) {
9584
9771
  makeEvent,
9585
9772
  transcriptReader
9586
9773
  );
9587
- function replayTaskLifecycleToolEvent(e) {
9588
- if (e.kind !== "tool.pre" && e.kind !== "tool.post") return;
9589
- const data = e.data;
9590
- const toolInput = readObject(data.tool_input);
9591
- if (data.tool_name === "TaskCreate" && e.kind === "tool.post") {
9592
- const response = readObject(data.tool_response);
9593
- const task = readObject(response["task"]);
9594
- const taskId = readString(task["id"], task["task_id"]);
9595
- const subject = readString(task["subject"], toolInput["subject"]);
9596
- if (taskId && subject) {
9597
- taskLifecycle.upsertCreated({
9598
- taskId,
9599
- subject,
9600
- description: readString(toolInput["description"]),
9601
- activeForm: readString(toolInput["activeForm"])
9602
- });
9603
- }
9604
- }
9605
- if (data.tool_name === "TaskUpdate") {
9606
- const response = readObject(data.tool_response);
9607
- const status = coerceTaskStatus(
9608
- readObject(response["statusChange"])["to"] ?? toolInput["status"]
9609
- );
9610
- const taskId = readString(
9611
- response["taskId"],
9612
- response["task_id"],
9613
- toolInput["taskId"],
9614
- toolInput["task_id"]
9615
- );
9616
- if (taskId && status) {
9617
- taskLifecycle.updateStatus({ taskId, status });
9618
- }
9619
- }
9620
- }
9621
9774
  if (bootstrap) {
9622
9775
  runLifecycle.restoreFrom(bootstrap);
9623
- for (const e of bootstrap.feedEvents) {
9624
- if (e.kind === "tool.pre" && e.actor_id === "agent:root" && e.data.tool_name === "TodoWrite") {
9625
- rootPlan.set(
9626
- extractTodoItems(e.data.tool_input)
9627
- );
9628
- }
9629
- replayTaskLifecycleToolEvent(e);
9630
- if (e.kind === "task.created") {
9631
- const data = e.data;
9632
- if (data.task_id && data.task_subject) {
9633
- taskLifecycle.upsertCreated({
9634
- taskId: data.task_id,
9635
- subject: data.task_subject,
9636
- description: data.task_description
9637
- });
9638
- }
9639
- }
9640
- if (e.kind === "task.completed") {
9641
- const data = e.data;
9642
- if (data.task_id) {
9643
- taskLifecycle.markCompleted({
9644
- taskId: data.task_id,
9645
- subject: data.task_subject
9646
- });
9647
- }
9648
- }
9649
- }
9650
- }
9651
- function closeRunIntoEvent(runtimeEvent, status) {
9652
- const closed = runLifecycle.closeRun(runtimeEvent.timestamp, status);
9653
- if (!closed) return null;
9654
- return makeEvent(
9655
- "run.end",
9656
- "info",
9657
- "system",
9658
- { status, counters: { ...closed.counters } },
9659
- runtimeEvent
9660
- );
9661
- }
9662
- function ensureRunArray(runtimeEvent, triggerType = "other", promptPreview) {
9663
- if (runLifecycle.getCurrentRun() && triggerType === "other") return [];
9664
- const results = [];
9665
- const closeEvt = closeRunIntoEvent(runtimeEvent, "completed");
9666
- if (closeEvt) results.push(closeEvt);
9667
- toolCorrelation.resetForNewRun();
9668
- decisionCorrelation.resetForNewRun();
9669
- agentMessageStream.resetForNewRun();
9670
- subagents.clear();
9671
- runLifecycle.openNewRun(
9672
- runtimeEvent.timestamp,
9673
- runtimeEvent.sessionId,
9674
- triggerType,
9675
- promptPreview
9676
- );
9677
- results.push(
9678
- makeEvent(
9679
- "run.start",
9680
- "info",
9681
- "system",
9682
- { trigger: { type: triggerType, prompt_preview: promptPreview } },
9683
- runtimeEvent
9684
- )
9685
- );
9686
- return results;
9776
+ taskState.restore(bootstrap.feedEvents);
9687
9777
  }
9778
+ const ensureRunArray = runLifecycle.beginRun;
9779
+ const closeRunIntoEvent = runLifecycle.closeRunIntoEvent;
9688
9780
  function resolveToolActor() {
9689
- return subagents.peek() ?? "agent:root";
9781
+ return subagents.currentActor();
9690
9782
  }
9691
9783
  const toolProjection = createToolProjection({
9692
9784
  ensureRunArray,
9693
9785
  makeEvent,
9694
9786
  runLifecycle,
9695
9787
  toolCorrelation,
9696
- rootPlan,
9697
- taskLifecycle,
9788
+ taskState,
9698
9789
  subagents,
9699
9790
  resolveToolActor
9700
9791
  });
@@ -9712,8 +9803,6 @@ function createFeedMapper(bootstrap) {
9712
9803
  const subagentProjection = createSubagentProjection({
9713
9804
  ensureRunArray,
9714
9805
  makeEvent,
9715
- runLifecycle,
9716
- actors,
9717
9806
  subagents
9718
9807
  });
9719
9808
  const fileConfigProjection = createFileConfigProjection({
@@ -9723,7 +9812,7 @@ function createFeedMapper(bootstrap) {
9723
9812
  const statusProjection = createStatusProjection({
9724
9813
  ensureRunArray,
9725
9814
  makeEvent,
9726
- taskLifecycle
9815
+ taskState
9727
9816
  });
9728
9817
  const currentScope = () => subagents.currentScope();
9729
9818
  const runSessionProjection = createRunSessionProjection({
@@ -9732,7 +9821,7 @@ function createFeedMapper(bootstrap) {
9732
9821
  closeRunIntoEvent,
9733
9822
  runLifecycle,
9734
9823
  agentMessageStream,
9735
- rootPlan,
9824
+ taskState,
9736
9825
  resolveToolActor,
9737
9826
  currentScope
9738
9827
  });
@@ -9740,33 +9829,13 @@ function createFeedMapper(bootstrap) {
9740
9829
  const d = event.data;
9741
9830
  const eventKind2 = event.kind;
9742
9831
  const results = [];
9743
- function emitFallbackMessage(parentKind, actorId, scope) {
9744
- if (results.some((r) => r.kind === "agent.message")) return;
9745
- const msg = readString(d["last_assistant_message"]);
9746
- if (!msg) return;
9747
- const parentEvt = results.find((r) => r.kind === parentKind);
9748
- const ev = agentMessageStream.emit({
9749
- runtimeEvent: event,
9750
- actorId,
9751
- scope,
9752
- message: msg,
9753
- source: "hook",
9754
- cause: parentEvt ? { parent_event_id: parentEvt.event_id } : void 0
9755
- });
9756
- if (ev) results.push(ev);
9757
- }
9758
- const transcriptPath = event.context.transcriptPath;
9759
- const isStopEvent = eventKind2 === "stop.request" || eventKind2 === "subagent.stop";
9760
- if (transcriptPath && !isStopEvent) {
9761
- results.push(
9762
- ...agentMessageStream.emitTranscriptMessages(
9763
- transcriptPath,
9764
- event,
9765
- resolveToolActor(),
9766
- currentScope()
9767
- )
9768
- );
9769
- }
9832
+ results.push(
9833
+ ...agentMessageStream.replayBeforeEvent(
9834
+ event,
9835
+ resolveToolActor(),
9836
+ currentScope()
9837
+ )
9838
+ );
9770
9839
  if (RUN_SESSION_EVENT_KINDS.has(eventKind2)) {
9771
9840
  results.push(...runSessionProjection.mapRunSessionEvent(event, d));
9772
9841
  } else if (TOOL_EVENT_KINDS.has(eventKind2)) {
@@ -9801,13 +9870,25 @@ function createFeedMapper(bootstrap) {
9801
9870
  results.push(unknownEvt);
9802
9871
  }
9803
9872
  if (eventKind2 === "stop.request") {
9804
- if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
9805
- 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
+ );
9806
9881
  }
9807
9882
  if (eventKind2 === "subagent.stop") {
9808
9883
  const agentId = readString(d["agent_id"]) ?? "unknown";
9809
- if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
9810
- 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
+ );
9811
9892
  }
9812
9893
  return results;
9813
9894
  }
@@ -9820,7 +9901,7 @@ function createFeedMapper(bootstrap) {
9820
9901
  getSession: () => runLifecycle.getSession(),
9821
9902
  getCurrentRun: () => runLifecycle.getCurrentRun(),
9822
9903
  getActors: () => actors.all(),
9823
- getTasks: () => [...rootPlan.current(), ...taskLifecycle.current()],
9904
+ getTasks: () => taskState.current(),
9824
9905
  allocateSeq: () => runLifecycle.allocateSeq()
9825
9906
  };
9826
9907
  }
@@ -12422,8 +12503,32 @@ var VERBOSE_ONLY_KINDS = /* @__PURE__ */ new Set([
12422
12503
  "worktree.remove",
12423
12504
  "turn.diff",
12424
12505
  "usage.update",
12425
- "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"
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"
12426
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
+ }
12427
12532
  function toRunStatus(event) {
12428
12533
  switch (event.data.status) {
12429
12534
  case "completed":
@@ -13212,6 +13317,9 @@ function shouldSkipEvent(event, verbose) {
13212
13317
  if (!verbose && event.kind === "stop.request" && !event.data.stop_hook_active) {
13213
13318
  return true;
13214
13319
  }
13320
+ if (!verbose && isVerboseOnlyNotification(event)) {
13321
+ return true;
13322
+ }
13215
13323
  return false;
13216
13324
  }
13217
13325
  function mergedToolUseId(event, postByToolUseId) {
@@ -13871,7 +13979,7 @@ async function connect(opts) {
13871
13979
  const entry = pending.get(requestId);
13872
13980
  if (!entry) return;
13873
13981
  pending.delete(requestId);
13874
- clearTimeout(entry.timer);
13982
+ if (entry.timer) clearTimeout(entry.timer);
13875
13983
  entry.resolve(parsed);
13876
13984
  return;
13877
13985
  }
@@ -13895,7 +14003,7 @@ async function connect(opts) {
13895
14003
  )
13896
14004
  );
13897
14005
  for (const [, p] of pending) {
13898
- clearTimeout(p.timer);
14006
+ if (p.timer) clearTimeout(p.timer);
13899
14007
  p.reject(
13900
14008
  new GatewayProtocolError("connection closed", "connection_closed")
13901
14009
  );
@@ -13955,14 +14063,14 @@ async function connect(opts) {
13955
14063
  kind,
13956
14064
  payload
13957
14065
  };
13958
- const effectiveTimeoutMs = reqOpts?.timeoutMs ?? timeoutMs;
14066
+ const effectiveTimeoutMs = reqOpts?.timeoutMs === void 0 ? timeoutMs : reqOpts.timeoutMs;
13959
14067
  const responsePromise = new Promise(
13960
14068
  (resolve, reject) => {
13961
- const timer = setTimeout(() => {
14069
+ const timer = effectiveTimeoutMs === null ? void 0 : setTimeout(() => {
13962
14070
  pending.delete(requestId);
13963
14071
  reject(new GatewayProtocolError(`request ${kind} timed out`));
13964
14072
  }, effectiveTimeoutMs);
13965
- pending.set(requestId, { resolve, reject, timer });
14073
+ pending.set(requestId, { resolve, reject, ...timer ? { timer } : {} });
13966
14074
  }
13967
14075
  );
13968
14076
  connection.send(envelope);
@@ -14218,7 +14326,6 @@ function writeGatewayClientConfig(config, env = process.env) {
14218
14326
  }
14219
14327
 
14220
14328
  // src/app/channels/sessionBridge.ts
14221
- var RELAY_REQUEST_TIMEOUT_MS = 6 * 6e4;
14222
14329
  var RECONNECT_BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
14223
14330
  var SessionBridge = class {
14224
14331
  opts;
@@ -14370,9 +14477,9 @@ var SessionBridge = class {
14370
14477
  toolName: req.toolName,
14371
14478
  description: req.description,
14372
14479
  inputPreview: req.inputPreview,
14373
- ...req.ttlMs !== void 0 ? { ttlMs: req.ttlMs } : {}
14480
+ ttlMs: req.ttlMs ?? null
14374
14481
  };
14375
- const overallTimeoutMs = req.ttlMs === null ? null : req.ttlMs ?? RELAY_REQUEST_TIMEOUT_MS;
14482
+ const overallTimeoutMs = req.ttlMs ?? null;
14376
14483
  return this.requestWithReconnect("relay.permission.request", payload, overallTimeoutMs);
14377
14484
  }
14378
14485
  async relayQuestion(req) {
@@ -14381,9 +14488,9 @@ var SessionBridge = class {
14381
14488
  channelRequestId,
14382
14489
  title: req.title,
14383
14490
  questions: req.questions,
14384
- ...req.ttlMs !== void 0 ? { ttlMs: req.ttlMs } : {}
14491
+ ttlMs: req.ttlMs ?? null
14385
14492
  };
14386
- const overallTimeoutMs = req.ttlMs === null ? null : req.ttlMs ?? RELAY_REQUEST_TIMEOUT_MS;
14493
+ const overallTimeoutMs = req.ttlMs ?? null;
14387
14494
  return this.requestWithReconnect("relay.question.request", payload, overallTimeoutMs);
14388
14495
  }
14389
14496
  /**
@@ -14391,8 +14498,9 @@ var SessionBridge = class {
14391
14498
  * survives a transient WS disconnect: on `connection closed`, wait for
14392
14499
  * the bridge's reconnect to settle, then re-issue the same payload. The
14393
14500
  * server attaches the replay to the existing pending entry by
14394
- * `channelRequestId`, so adapters are not re-prompted. Bounded by the
14395
- * 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.
14396
14504
  */
14397
14505
  async requestWithReconnect(kind, payload, overallTimeoutMs) {
14398
14506
  const deadline = overallTimeoutMs === null ? null : Date.now() + overallTimeoutMs;
@@ -14404,9 +14512,7 @@ var SessionBridge = class {
14404
14512
  }
14405
14513
  try {
14406
14514
  return await client.request(kind, payload, {
14407
- // When unbounded, pass the largest safe setTimeout value so the
14408
- // inner request never self-cancels in any human time scale.
14409
- timeoutMs: remaining ?? 2147483647
14515
+ timeoutMs: remaining
14410
14516
  });
14411
14517
  } catch (err) {
14412
14518
  if (err instanceof GatewayProtocolError && err.code === "connection_closed" && !this.stopped && (deadline === null || Date.now() < deadline)) {
@@ -15030,7 +15136,8 @@ async function runExec(options) {
15030
15136
  const runtimeFactory = options.runtimeFactory ?? createRuntime;
15031
15137
  const sessionStoreFactory = options.sessionStoreFactory ?? createSessionStore;
15032
15138
  const athenaSessionId = options.athenaSessionId ?? crypto2.randomUUID();
15033
- const dashboardFeedPublisher = options.dashboardFeedPublisher ?? createPairedFeedPublisher();
15139
+ const ownedFeedPublisher = options.dashboardFeedPublisher ? null : createPairedFeedPublisher();
15140
+ const dashboardFeedPublisher = options.dashboardFeedPublisher ?? ownedFeedPublisher;
15034
15141
  const dashboardOrigin = options.dashboardOrigin ?? "local";
15035
15142
  const output = createExecOutputWriter({
15036
15143
  json,
@@ -15399,6 +15506,7 @@ async function runExec(options) {
15399
15506
  }
15400
15507
  await bridge?.stop();
15401
15508
  store.close();
15509
+ ownedFeedPublisher?.close();
15402
15510
  }
15403
15511
  const resolvedFinalMessage = resolveFinalMessage({
15404
15512
  streamMessage: streamFinalMessage,
@@ -16406,8 +16514,26 @@ function parseRemoteRunSpec(value) {
16406
16514
  callbackToken: typeof callbackToken === "string" && callbackToken.length > 0 ? callbackToken : void 0
16407
16515
  };
16408
16516
  }
16409
- function isRemoteAssignmentAdmissible(frame) {
16410
- 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
+ };
16411
16537
  }
16412
16538
  function workflowNameFromRef(ref) {
16413
16539
  if (!ref) return void 0;
@@ -16488,7 +16614,7 @@ function mergeRunSpecEnvIntoWorkflow(workflow, env) {
16488
16614
  };
16489
16615
  }
16490
16616
  async function executeRemoteAssignment({
16491
- frame,
16617
+ assignment,
16492
16618
  client,
16493
16619
  projectDir,
16494
16620
  log = () => {
@@ -16511,11 +16637,11 @@ async function executeRemoteAssignment({
16511
16637
  const deferredFailedCompletion = {
16512
16638
  current: null
16513
16639
  };
16514
- const spec = parseRemoteRunSpec(frame.runSpec);
16640
+ const { spec, runId, frame } = assignment;
16515
16641
  const runEventPublisher = await createRemoteRunEventPublisher({
16516
- runId: frame.runId,
16517
- callbackWsUrl: spec?.callbackWsUrl,
16518
- callbackToken: spec?.callbackToken,
16642
+ runId,
16643
+ callbackWsUrl: spec.callbackWsUrl,
16644
+ callbackToken: spec.callbackToken,
16519
16645
  client,
16520
16646
  log,
16521
16647
  now,
@@ -16530,10 +16656,6 @@ async function executeRemoteAssignment({
16530
16656
  };
16531
16657
  send("progress", { message: "assignment received" });
16532
16658
  try {
16533
- if (!spec) {
16534
- send("error", { message: "remote assignment missing prompt" });
16535
- return;
16536
- }
16537
16659
  let artifactUploadSpec;
16538
16660
  try {
16539
16661
  artifactUploadSpec = parseArtifactUploadSpec(frame.runSpec);
@@ -16614,7 +16736,7 @@ async function executeRemoteAssignment({
16614
16736
  prompt: spec.prompt,
16615
16737
  projectDir,
16616
16738
  harness: runtimeConfig.harness,
16617
- athenaSessionId: spec.athenaSessionId ?? spec.sessionId ?? `athena-${frame.runId}`,
16739
+ athenaSessionId: spec.athenaSessionId ?? spec.sessionId ?? `athena-${runId}`,
16618
16740
  adapterResumeSessionId: spec.adapterResumeSessionId,
16619
16741
  isolationConfig: runtimeConfig.isolationConfig,
16620
16742
  pluginMcpConfig: runtimeConfig.pluginMcpConfig,
@@ -16631,8 +16753,8 @@ async function executeRemoteAssignment({
16631
16753
  ...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {},
16632
16754
  ...dashboardFeedPublisher ? { dashboardFeedPublisher } : {},
16633
16755
  ...artifactUploadSpec ? {
16634
- beforeTerminalCompletion: async ({ result: result2, runId }) => {
16635
- const artifactRunId = runId ?? frame.runId;
16756
+ beforeTerminalCompletion: async ({ result: result2, runId: runId2 }) => {
16757
+ const artifactRunId = runId2 ?? assignment.runId;
16636
16758
  const { feedEvent } = await captureAndUploadArtifacts({
16637
16759
  spec: artifactUploadSpec,
16638
16760
  projectDir,
@@ -17042,54 +17164,55 @@ function createDashboardPairedExecution(options) {
17042
17164
  });
17043
17165
  log("warn", `run ${runId} rejected: ${rejection.message}`);
17044
17166
  }
17045
- function handleDecision(frame) {
17167
+ function submitDashboardDecision(submission) {
17046
17168
  decisionInbox.enqueue({
17047
- athenaSessionId: frame.athenaSessionId,
17048
- requestId: frame.requestId,
17049
- decision: frame.decision,
17169
+ athenaSessionId: submission.athenaSessionId,
17170
+ requestId: submission.requestId,
17171
+ decision: submission.decision,
17050
17172
  receivedAt: now()
17051
17173
  });
17052
17174
  client.sendDecisionAck({
17053
- athenaSessionId: frame.athenaSessionId,
17054
- requestId: frame.requestId
17175
+ athenaSessionId: submission.athenaSessionId,
17176
+ requestId: submission.requestId
17055
17177
  });
17056
17178
  }
17057
- function handleCancel(frame) {
17058
- const entry = active.get(frame.runId);
17059
- if (!entry) return;
17179
+ function cancelRun(runId) {
17180
+ const entry = active.get(runId);
17181
+ if (!entry) return false;
17060
17182
  entry.record.status = "cancelled";
17061
17183
  entry.controller.abort();
17184
+ return true;
17062
17185
  }
17063
- function handleAssignment(frame, input = {}) {
17064
- if (active.has(frame.runId)) {
17186
+ function handleAssignment(assignment, input = {}) {
17187
+ const { runId, runnerId } = assignment;
17188
+ if (active.has(runId)) {
17065
17189
  const rejection = {
17066
17190
  reason: "duplicate",
17067
- message: `duplicate active assignment ${frame.runId}`
17191
+ message: `duplicate active assignment ${runId}`
17068
17192
  };
17069
- rejectAssignment(frame.runId, rejection);
17193
+ rejectAssignment(runId, rejection);
17070
17194
  return { kind: "rejected", rejection };
17071
17195
  }
17072
- const runnerKey = frame.runnerId;
17073
- const bucket = activeByRunner.get(runnerKey) ?? /* @__PURE__ */ new Set();
17196
+ const bucket = activeByRunner.get(runnerId) ?? /* @__PURE__ */ new Set();
17074
17197
  if (bucket.size >= maxConcurrentRuns) {
17075
17198
  const rejection = {
17076
17199
  reason: "local_capacity",
17077
- message: `runtime daemon at concurrency cap (${maxConcurrentRuns}) for runner ${runnerKey ?? "<legacy>"}`
17200
+ message: `runtime daemon at concurrency cap (${maxConcurrentRuns}) for runner ${runnerId}`
17078
17201
  };
17079
- rejectAssignment(frame.runId, rejection);
17202
+ rejectAssignment(runId, rejection);
17080
17203
  return { kind: "rejected", rejection };
17081
17204
  }
17082
17205
  const controller = new AbortController();
17083
17206
  const record = {
17084
- runId: frame.runId,
17207
+ runId,
17085
17208
  startedAt: now(),
17086
17209
  status: "running"
17087
17210
  };
17088
17211
  recordRun(record);
17089
- bucket.add(frame.runId);
17090
- activeByRunner.set(runnerKey, bucket);
17212
+ bucket.add(runId);
17213
+ activeByRunner.set(runnerId, bucket);
17091
17214
  const promise = executor({
17092
- frame,
17215
+ assignment,
17093
17216
  client,
17094
17217
  projectDir: input.projectDir ?? projectDir,
17095
17218
  log,
@@ -17105,40 +17228,33 @@ function createDashboardPairedExecution(options) {
17105
17228
  record.error = err instanceof Error ? err.message : String(err);
17106
17229
  log(
17107
17230
  "error",
17108
- `run ${frame.runId} failed: ${err instanceof Error ? err.message : String(err)}`
17231
+ `run ${runId} failed: ${err instanceof Error ? err.message : String(err)}`
17109
17232
  );
17110
17233
  }).finally(() => {
17111
17234
  record.endedAt = now();
17112
17235
  completedRuns += 1;
17113
- active.delete(frame.runId);
17114
- const remaining = activeByRunner.get(runnerKey);
17236
+ active.delete(runId);
17237
+ const remaining = activeByRunner.get(runnerId);
17115
17238
  if (remaining) {
17116
- remaining.delete(frame.runId);
17117
- if (remaining.size === 0) activeByRunner.delete(runnerKey);
17239
+ remaining.delete(runId);
17240
+ if (remaining.size === 0) activeByRunner.delete(runnerId);
17118
17241
  }
17119
17242
  });
17120
- active.set(frame.runId, { controller, promise, record, runnerKey });
17243
+ active.set(runId, { controller, promise, record, runnerKey: runnerId });
17121
17244
  return { kind: "accepted" };
17122
17245
  }
17123
17246
  return {
17124
- handleFrame(frame) {
17125
- if (frame.type === "dashboard_decision") {
17126
- handleDecision(frame);
17127
- return true;
17128
- }
17129
- if (frame.type === "cancel") {
17130
- handleCancel(frame);
17131
- return true;
17132
- }
17133
- if (frame.type === "job_assignment") {
17134
- handleAssignment(frame);
17135
- return true;
17136
- }
17137
- return false;
17138
- },
17139
- admitAssignment(frame, input) {
17140
- return handleAssignment(frame, input);
17247
+ // `job_assignment` is intentionally not handled here: the runtime daemon
17248
+ // routes assignments through `DashboardAssignmentIntake`, which gates
17249
+ // admission on attachment readiness and then calls `admitAssignment`
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);
17141
17255
  },
17256
+ cancelRun,
17257
+ submitDashboardDecision,
17142
17258
  rejectAssignment,
17143
17259
  snapshot() {
17144
17260
  return {
@@ -17169,22 +17285,12 @@ function createDashboardPairedExecution(options) {
17169
17285
  import fs23 from "fs";
17170
17286
  import os13 from "os";
17171
17287
  import path21 from "path";
17172
- function resolveRemoteWorkspace(frame, options = {}) {
17173
- const spec = parseRemoteRunSpec(frame.runSpec);
17174
- if (!spec) {
17175
- return {
17176
- kind: "rejected",
17177
- rejection: {
17178
- reason: "workspace_unresolved",
17179
- message: "remote assignment missing prompt"
17180
- }
17181
- };
17182
- }
17288
+ function resolveRemoteWorkspace(assignment, options = {}) {
17289
+ const { spec, runId, runnerId } = assignment;
17183
17290
  if (spec.projectDir) {
17184
17291
  return validateProjectDir(spec.projectDir, options.env);
17185
17292
  }
17186
17293
  const sessionId = spec.athenaSessionId ?? spec.sessionId;
17187
- const runnerId = frame.runnerId ?? "legacy";
17188
17294
  const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
17189
17295
  const stateDir = daemonStatePaths(options.env).dir;
17190
17296
  const projectDir = sessionId ? path21.join(
@@ -17200,7 +17306,7 @@ function resolveRemoteWorkspace(frame, options = {}) {
17200
17306
  deploymentSlug,
17201
17307
  sanitizePathSegment(runnerId),
17202
17308
  "runs",
17203
- sanitizePathSegment(frame.runId)
17309
+ sanitizePathSegment(runId)
17204
17310
  );
17205
17311
  try {
17206
17312
  fs23.mkdirSync(projectDir, { recursive: true, mode: 448 });
@@ -17283,23 +17389,21 @@ function sanitizePathSegment(value) {
17283
17389
  function createDashboardAssignmentIntake(options) {
17284
17390
  const log = options.log ?? (() => {
17285
17391
  });
17286
- const resolveWorkspace = options.resolveWorkspace ?? ((frame) => resolveRemoteWorkspace(frame));
17392
+ const resolveWorkspace = options.resolveWorkspace ?? ((assignment, context2) => resolveRemoteWorkspace(assignment, { dashboardUrl: context2.dashboardUrl }));
17287
17393
  const pending = [];
17288
- let ready = false;
17289
- function handle(frame) {
17290
- if (!isRemoteAssignmentAdmissible(frame)) {
17291
- const rejection = {
17292
- reason: "malformed_assignment",
17293
- message: "remote assignment missing prompt"
17294
- };
17295
- 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);
17296
17399
  options.client.sendAssignmentRejected({
17297
17400
  runId: frame.runId,
17298
- ...rejection
17401
+ ...validation.rejection
17299
17402
  });
17300
17403
  return;
17301
17404
  }
17302
- const workspace = resolveWorkspace(frame);
17405
+ const assignment = validation.assignment;
17406
+ const workspace = resolveWorkspace(assignment, readyContext);
17303
17407
  if (workspace.kind === "rejected") {
17304
17408
  options.execution.rejectAssignment(frame.runId, workspace.rejection);
17305
17409
  options.client.sendAssignmentRejected({
@@ -17308,7 +17412,7 @@ function createDashboardAssignmentIntake(options) {
17308
17412
  });
17309
17413
  return;
17310
17414
  }
17311
- const outcome = options.execution.admitAssignment(frame, {
17415
+ const outcome = options.execution.admitAssignment(assignment, {
17312
17416
  projectDir: workspace.projectDir
17313
17417
  });
17314
17418
  if (outcome.kind === "accepted") {
@@ -17321,15 +17425,15 @@ function createDashboardAssignmentIntake(options) {
17321
17425
  });
17322
17426
  }
17323
17427
  function drain() {
17324
- if (!ready) return;
17428
+ if (!context) return;
17325
17429
  while (pending.length > 0) {
17326
17430
  const frame = pending.shift();
17327
- if (frame) handle(frame);
17431
+ if (frame) handle(frame, context);
17328
17432
  }
17329
17433
  }
17330
17434
  return {
17331
17435
  receive(frame) {
17332
- if (!ready) {
17436
+ if (!context) {
17333
17437
  pending.push(frame);
17334
17438
  log(
17335
17439
  "debug",
@@ -17337,18 +17441,35 @@ function createDashboardAssignmentIntake(options) {
17337
17441
  );
17338
17442
  return;
17339
17443
  }
17340
- handle(frame);
17444
+ handle(frame, context);
17341
17445
  },
17342
- markReady() {
17343
- ready = true;
17446
+ markReady(nextContext) {
17447
+ context = nextContext;
17344
17448
  drain();
17345
17449
  },
17346
17450
  markNotReady() {
17347
- ready = false;
17451
+ context = null;
17348
17452
  }
17349
17453
  };
17350
17454
  }
17351
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
+
17352
17473
  // src/app/dashboard/runtimeDaemon.ts
17353
17474
  var DEFAULT_RECONNECT_DELAYS_MS2 = [1e3, 2e3, 5e3, 1e4, 3e4];
17354
17475
  var DEFAULT_MAX_CONCURRENT_RUNS2 = 1;
@@ -17452,7 +17573,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
17452
17573
  },
17453
17574
  execution: pairedExecution,
17454
17575
  log,
17455
- resolveWorkspace: (frame) => resolveRemoteWorkspace(frame, { dashboardUrl: currentDashboardUrl })
17576
+ resolveWorkspace: (assignment, context) => resolveRemoteWorkspace(assignment, { dashboardUrl: context.dashboardUrl })
17456
17577
  });
17457
17578
  function nextReconnectDelay() {
17458
17579
  if (reconnectDelays.length === 0) return 0;
@@ -17571,7 +17692,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
17571
17692
  assignmentIntake.receive(frame);
17572
17693
  return;
17573
17694
  }
17574
- pairedExecution.handleFrame(frame);
17695
+ routeDashboardRunFrame(pairedExecution, frame);
17575
17696
  });
17576
17697
  next.onClose((reason) => {
17577
17698
  if (stopped || client !== next) return;
@@ -17594,15 +17715,19 @@ async function runDashboardRuntimeDaemon(options = {}) {
17594
17715
  accessToken: token.accessToken
17595
17716
  });
17596
17717
  } catch (err) {
17597
- client = null;
17598
- assignmentIntake.markNotReady();
17599
17718
  attachmentReconciler.markStale(token.instanceId);
17600
- next.close("attachment reconciliation failed");
17601
- throw err;
17719
+ log(
17720
+ "warn",
17721
+ `runtime daemon: attachment reconciliation failed; continuing with push-only mirror: ${err instanceof Error ? err.message : String(err)}`
17722
+ );
17602
17723
  }
17724
+ if (stopped || client !== next) return;
17603
17725
  currentInstanceId = token.instanceId;
17604
17726
  currentDashboardUrl = config.dashboardUrl;
17605
- assignmentIntake.markReady();
17727
+ assignmentIntake.markReady({
17728
+ dashboardUrl: config.dashboardUrl,
17729
+ instanceId: token.instanceId
17730
+ });
17606
17731
  reconnectAttempt = 0;
17607
17732
  scheduleRefresh(token.expiresInSec);
17608
17733
  pairedFeedPublisher.attachTransport(next);
@@ -18062,4 +18187,4 @@ export {
18062
18187
  startUdsServer,
18063
18188
  sendUdsRequest
18064
18189
  };
18065
- //# sourceMappingURL=chunk-RN5AVH3D.js.map
18190
+ //# sourceMappingURL=chunk-BUNMENOT.js.map