agent-dag 3.8.4 → 3.8.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-DkJt3T9U.js"></script>
43
+ <script type="module" crossorigin src="/assets/index-BnUiIfx6.js"></script>
44
44
  <link rel="stylesheet" crossorigin href="/assets/index-ByAgTqB8.css">
45
45
  </head>
46
46
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dag",
3
- "version": "3.8.4",
3
+ "version": "3.8.6",
4
4
  "description": "Live deck of Claude Code and Codex agents — watch tool calls, token spend and every Claude Code subagent on one calm canvas. Run it with npx ccdeck.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,18 @@
31
31
  "about a defect in the type, and the suite refuses both it and no space at",
32
32
  "all."
33
33
  ],
34
+ "3.8.6": [
35
+ {
36
+ "title": "\ud83d\uddc2\ufe0f Two decks can no longer both claim the same log",
37
+ "body": "When several decks share one events log they elect a single writer for it, so nothing is recorded twice. That election compared the path as typed \u2014 so two decks reaching the SAME file by different spellings each thought they were alone, both wrote every event, and each believed the log was theirs to clear.\n\nDifferent spellings are ordinary, not exotic: a shortened Windows home directory, a mapped or substituted drive, a junction, or on macOS `/tmp` against `/private/tmp`. The visible symptoms were duplicated tool calls after a restart, and Clear wiping history belonging to another deck.\n\nThe path is now canonicalised the same way the watched folder already was. It works on a first run too, before the log file exists."
38
+ }
39
+ ],
40
+ "3.8.5": [
41
+ {
42
+ "title": "\u26d4 Cancelling a sign-in no longer adds the account anyway",
43
+ "body": "Press Escape while adding a Claude account and the deck killed the sign-in \u2014 and then, sometimes, registered the account regardless, switched to it, and told you it had worked.\n\nThe deck asks the tools it runs whether they finished cleanly. A program that is well behaved about being cancelled tidies up and exits with a success code, and the deck was reading that as \"it finished\" rather than \"I stopped it\". `claude auth login` is exactly that kind of program.\n\nA cancelled run now reports as cancelled. Everything that was not cancelled is unaffected."
44
+ }
45
+ ],
34
46
  "3.8.4": [
35
47
  {
36
48
  "title": "\ud83e\uddf7 Your own hooks can no longer be eaten by two decks starting together",
@@ -800,7 +800,21 @@ export function runInteractive(cmd, args, { timeout = 300_000, maxOutput = 256 <
800
800
  const s = settle; settle = null;
801
801
  clearTimeout(timer);
802
802
  clearTimeout(graceTimer);
803
- s({ ok: code === 0 && !err && !timedOut, code: err?.code ?? code ?? -1, killed, timedOut, stdout, stderr });
803
+ // `!killed`, and it was missing (#787). A tool that handles SIGTERM by
804
+ // exiting 0 — the well-behaved kind, which `run`'s own header names — came
805
+ // back `{ ok: true, killed: true }`, and a caller reading `ok` could not
806
+ // tell a clean run from one it had just cancelled. `claude auth login` is
807
+ // exactly that shape: it traps SIGTERM to restore the terminal.
808
+ //
809
+ // What that cost: pressing Escape during a sign-in killed the child, and
810
+ // spawnLogin's handler then saw `r.ok` true and ran `registerSignedIn` — a
811
+ // `cswap add` for the account the user had just cancelled, racing
812
+ // cancelLogin's own restore, with the dialog flipping to `done`.
813
+ //
814
+ // The memo guard eleven lines below already distrusted a kill for the same
815
+ // reason (`if (code === 0 && !killed && !timedOut)`), so the two halves of
816
+ // this function disagreed about what a killed exit means.
817
+ s({ ok: code === 0 && !err && !timedOut && !killed, code: err?.code ?? code ?? -1, killed, timedOut, stdout, stderr });
804
818
  };
805
819
 
806
820
  // The deadline states the outcome and only then kills — the order `run` uses
@@ -21,7 +21,8 @@
21
21
  // reason it re-derives the Claude config dir inline. The two copies are pinned
22
22
  // equal by a test, as challengeProof's pair already is.
23
23
  import { open } from "node:fs/promises";
24
- import { win32, posix } from "node:path";
24
+ import { realpathSync } from "node:fs";
25
+ import { basename, dirname, join, resolve, win32, posix } from "node:path";
25
26
 
26
27
  /**
27
28
  * Does this platform's filesystem treat two spellings that differ only in case
@@ -33,6 +34,44 @@ import { win32, posix } from "node:path";
33
34
  * not, and folding case there would be a bug of its own: /srv/a/events.jsonl and
34
35
  * /srv/A/events.jsonl are two real files, each of which needs a writer.
35
36
  */
37
+ /**
38
+ * The one spelling of an events log, so two decks pointed at one file land in
39
+ * one group (#793).
40
+ *
41
+ * `resolve` alone was what shipped, and it settles relative-vs-absolute and
42
+ * nothing else. The election below then only case-folds — so two spellings of
43
+ * one file read as two files, which is the exact thing `bin/deck.js`'s comment
44
+ * over this value says must not happen. On Windows it needs no odd user action:
45
+ * `claudeConfigDir()` derives from `homedir()`, and a shell whose `USERPROFILE`
46
+ * is 8.3-shortened yields a different default string than one with the long
47
+ * form. `subst` and mapped drives and junctions do it too, and on macOS so does
48
+ * `/tmp` against `/private/tmp`.
49
+ *
50
+ * What it cost was not merely a duplicate group. BOTH decks were then elected,
51
+ * so every hook event was appended twice — the duplicate-tools-after-restart
52
+ * symptom the election exists to end — and `logSharing()` compares the same
53
+ * string, so both answered `mine: true` and `POST /api/clear` truncated a file
54
+ * this deck does not own. That is the #698 history loss the ownership gate was
55
+ * added to prevent.
56
+ *
57
+ * THE DIRECTORY IS CANONICALISED EVEN WHEN THE FILE IS NOT THERE, which is the
58
+ * half a plain realpath misses. On a first run, or against a `--history` naming
59
+ * a file the deck will create, `realpath` throws ENOENT — and falling back to
60
+ * the resolved string would leave the two spellings different for exactly the
61
+ * run that creates the file. The parent exists (or is about to be created under
62
+ * one canonical name), so it is canonicalised and the basename rejoined.
63
+ *
64
+ * `canonicalWorkspace` in index.mjs is the same rule for the other path this
65
+ * deck publishes, with three comments naming 8.3 expansion as its reason. This
66
+ * is that rule reaching the value two lines away from it.
67
+ */
68
+ export function canonicalLogPath(raw) {
69
+ if (typeof raw !== "string" || raw.trim() === "") return "";
70
+ const abs = resolve(raw);
71
+ try { return realpathSync.native(abs); } catch { /* not created yet */ }
72
+ try { return join(realpathSync.native(dirname(abs)), basename(abs)); } catch { return abs; }
73
+ }
74
+
36
75
  export const foldsCase = (platform = process.platform) =>
37
76
  platform === "win32" || platform === "darwin";
38
77