agent-dag 1.34.5 → 1.34.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,7 +40,7 @@
40
40
  document.documentElement.setAttribute("data-theme", stored === "light" ? "light" : "dark");
41
41
  })();
42
42
  </script>
43
- <script type="module" crossorigin src="/assets/index-CDgBtXXg.js"></script>
43
+ <script type="module" crossorigin src="/assets/index-G1Xt6iC6.js"></script>
44
44
  <link rel="stylesheet" crossorigin href="/assets/index-CRPobBZf.css">
45
45
  </head>
46
46
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dag",
3
- "version": "1.34.5",
3
+ "version": "1.34.6",
4
4
  "description": "Live deck of Claude Code and Codex agents — watch parallel subagents fork, call tools, and return on one calm canvas. Also available as npx ccdeck and npx agent-dag.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -785,12 +785,17 @@ function maybeResolveCodex(payload) {
785
785
  // object {timestamp, type, payload}; we map the relevant ones to the same
786
786
  // synthetic hook payloads the reducer already understands:
787
787
  // session_meta → SessionStart
788
- // event_msg/user_message → UserPromptSubmit
788
+ // event_msg/user_message → UserPromptSubmit (Codex ≤ 0.144)
789
+ // event_msg/item_completed/UserMessage → UserPromptSubmit (Codex ≥ 0.147)
789
790
  // response_item/function_call → PreToolUse
790
791
  // response_item/function_call_output → PostToolUse
791
792
  // event_msg/token_count → UsageObserved
792
793
  // event_msg/task_started (+window) → ModelObserved (context window)
794
+ // event_msg/task_complete → Stop (the turn finished)
795
+ // event_msg/turn_aborted → Stop (the turn was interrupted)
793
796
  // turn_context / response_item.model → model snapshot (ModelObserved on change)
797
+ // There is deliberately no SessionEnd here: Codex writes no session-close
798
+ // record, so the end of a SESSION is still inferred by sweepStaleSessions.
794
799
  // Events are emitted with source "codex" so pushEvent skips the Claude-only
795
800
  // transcript enrichment (which needs transcript_path / hook events) but still
796
801
  // broadcasts them exactly like a hook event, and persists them when this deck is
@@ -890,10 +895,27 @@ async function readCodexHeader(path) {
890
895
  return null;
891
896
  }
892
897
 
898
+ /**
899
+ * The human's prompt out of a 0.147-era `item_completed` item.
900
+ *
901
+ * `item.content` is an array of parts — every UserMessage observed carries a
902
+ * single `{ type: "text", text, text_elements }` — so the parts are joined
903
+ * rather than indexed, and a part with no string `text` contributes nothing
904
+ * instead of printing "undefined" into the prompt the card shows.
905
+ */
906
+ function codexItemText(item) {
907
+ const parts = Array.isArray(item && item.content) ? item.content : [];
908
+ return parts.map(p => (p && typeof p.text === "string" ? p.text : "")).join("");
909
+ }
910
+
893
911
  // Map one parsed rollout object to a synthetic hook payload (or null to skip).
894
912
  // Mutates codexSessionModel and returns { payload, modelEvent } where
895
913
  // modelEvent is an optional ModelObserved to emit first when the model changed.
896
- function codexObjToPayload(obj, sid, cwd) {
914
+ //
915
+ // Exported for the tests: this is the whole of the Codex translation, and the
916
+ // lifecycle it produces is worth pinning against the real reducer without
917
+ // standing up a watcher, a temp home and a 1.5s poll to get at it.
918
+ export function codexObjToPayload(obj, sid, cwd) {
897
919
  const type = obj && obj.type;
898
920
  const pl = (obj && obj.payload) || {};
899
921
  const base = { session_id: sid, cwd, provider: "codex" };
@@ -913,12 +935,49 @@ function codexObjToPayload(obj, sid, cwd) {
913
935
  const prompt = typeof pl.message === "string" ? pl.message : "";
914
936
  return { ...base, hook_event_name: "UserPromptSubmit", prompt, model };
915
937
  }
938
+ // Codex 0.147 stopped writing `user_message` and writes the same submission
939
+ // as an `item_completed` carrying a `UserMessage` item instead. The two are
940
+ // mutually exclusive per CLI version — across the rollouts sampled here the
941
+ // 0.144 files have `user_message` and no `item_completed` at all, and the
942
+ // 0.147 files have exactly one `UserMessage` item per turn and no
943
+ // `user_message` — so handling both names emits one prompt per turn either
944
+ // way rather than two on either version. The item is the human's typed text
945
+ // only: the AGENTS.md preamble Codex prepends is written as a bare
946
+ // `response_item` with role "user" and never gets an item of its own.
947
+ //
948
+ // This matters far beyond the prompt text. `UserPromptSubmit` is what puts
949
+ // a settled root back to `active` (reducer.ts), so on 0.147 it is the ONLY
950
+ // thing that reopens a session for its second turn — without it the `Stop`
951
+ // below would trade "live forever" for "done forever", which is not better.
952
+ if (pl.type === "item_completed" && pl.item && pl.item.type === "UserMessage") {
953
+ return { ...base, hook_event_name: "UserPromptSubmit", prompt: codexItemText(pl.item), model };
954
+ }
916
955
  if (pl.type === "token_count" && pl.info && pl.info.total_token_usage) {
917
956
  return { ...base, hook_event_name: "UsageObserved", usage: pl.info.total_token_usage, model };
918
957
  }
919
958
  if (pl.type === "task_started" && typeof pl.model_context_window === "number") {
920
959
  return { ...base, hook_event_name: "ModelObserved", model, model_context_window: pl.model_context_window };
921
960
  }
961
+ // The end of a turn, which is the only end Codex ever announces. Both names
962
+ // are one outcome as far as the deck is concerned — the turn is over and
963
+ // nothing is running — so both settle the root the way Claude's own Stop
964
+ // hook does. They are also exhaustive: across the rollouts sampled here
965
+ // every `task_started` is answered by exactly one of the two (54 completes
966
+ // + 1 abort for 55 starts), so no turn is left open by this mapping and
967
+ // none is closed twice. `turn_aborted` is the Esc key, and it is the case
968
+ // that matters most on the deck: pressing Esc is precisely when the user is
969
+ // watching to confirm the thing stopped.
970
+ //
971
+ // Per TURN, not per session, and that is correct: Codex writes no
972
+ // session-close record at all — a rollout simply stops growing when the
973
+ // terminal goes away — so `sweepStaleSessions` remains the only thing that
974
+ // ends a Codex *session*, and it must. What changes is that it now only
975
+ // ever sees the sessions it was written for: the ones that died without
976
+ // finishing. A turn that really ended is settled here, at the moment it
977
+ // ended, with no `reaped` flag, because it was a finish and not a guess.
978
+ if (pl.type === "task_complete" || pl.type === "turn_aborted") {
979
+ return { ...base, hook_event_name: "Stop", model };
980
+ }
922
981
  return null;
923
982
  }
924
983
  if (type === "response_item") {