trantor 0.18.53 → 0.18.54

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.
Files changed (62) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +2 -0
  3. package/bin/crew/herdr.mjs +18 -1
  4. package/bin/crew/open.mjs +13 -6
  5. package/bin/crew/state.mjs +7 -1
  6. package/bin/crew-runner.mjs +97 -318
  7. package/bin/slop-gate.mjs +69 -41
  8. package/hooks/agent-notify.mjs +4 -13
  9. package/hooks/file-claim.mjs +6 -22
  10. package/hooks/handoff-now.mjs +5 -9
  11. package/hooks/heartbeat.mjs +16 -51
  12. package/hooks/inbox-deliver.mjs +8 -36
  13. package/hooks/lib/api.mjs +17 -66
  14. package/hooks/lib/handoff.mjs +69 -217
  15. package/hooks/lib/inbox-ledger.mjs +3 -11
  16. package/hooks/lib/resources.mjs +6 -16
  17. package/hooks/lib/update-check.mjs +4 -12
  18. package/hooks/overseer-warn.mjs +4 -16
  19. package/hooks/precompact.mjs +5 -10
  20. package/hooks/prompt-focus.mjs +6 -12
  21. package/hooks/sessionstart.mjs +38 -125
  22. package/hooks/statusline.mjs +4 -9
  23. package/hooks/stop-inbox.mjs +9 -49
  24. package/hooks/subagent-cost.mjs +2 -6
  25. package/hooks/subagent-start.mjs +4 -18
  26. package/hooks/todo-sync.mjs +2 -5
  27. package/hub/reaper.mjs +7 -2
  28. package/lib/autonomy.mjs +6 -30
  29. package/lib/balances.mjs +12 -48
  30. package/lib/classify-failure.mjs +6 -36
  31. package/lib/duty-nudges.mjs +2 -13
  32. package/lib/enroll.mjs +2 -12
  33. package/lib/identity.mjs +4 -22
  34. package/lib/integrate.mjs +6 -20
  35. package/lib/overseer.mjs +2 -15
  36. package/lib/project.mjs +22 -72
  37. package/lib/provider-keys.mjs +2 -5
  38. package/lib/providers.mjs +6 -27
  39. package/lib/redact.mjs +4 -24
  40. package/lib/same-project.mjs +2 -15
  41. package/lib/scrub.mjs +4 -13
  42. package/lib/seat-why.mjs +2 -9
  43. package/lib/seats.mjs +3 -18
  44. package/lib/signed-fetch.mjs +2 -8
  45. package/lib/splitbrain.mjs +4 -24
  46. package/lib/state/apply.mjs +7 -26
  47. package/lib/state/assemble.mjs +10 -33
  48. package/lib/state/cost.mjs +10 -32
  49. package/lib/state/derive.mjs +14 -52
  50. package/lib/state/driver.mjs +26 -108
  51. package/lib/state/gate.mjs +13 -71
  52. package/lib/state/migrate.mjs +3 -6
  53. package/lib/state/promote.mjs +7 -27
  54. package/lib/state/schema.mjs +12 -33
  55. package/lib/state/store.mjs +35 -121
  56. package/lib/state/validate.mjs +7 -31
  57. package/lib/store-contract.mjs +17 -40
  58. package/lib/store-pg.mjs +27 -32
  59. package/lib/subagent-manifest.mjs +2 -17
  60. package/lib/subagent-scan.mjs +0 -0
  61. package/lib/turn-policy.mjs +10 -40
  62. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.18.53",
3
+ "version": "0.18.54",
4
4
  "description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + crew orchestration for independent AI coding agents (Claude, Codex, Gemini, Kimi, DeepSeek)",
5
5
  "mcpServers": {
6
6
  "relay": {
package/README.md CHANGED
@@ -368,6 +368,8 @@ Waking works the same way from the other end: hover a sleeping project in the ap
368
368
  **Wake** hosts its session as an in-app pane — `trantor open` under the hood, opened in the
369
369
  project's own checkout wherever it is called from, handoff-beats-resume, and the same kickoff
370
370
  prompt so the woken session catches up (handoff, board, memory) and recaps on its own.
371
+ A reboot can leave herdr with a pane in its layout and no terminal behind it; `trantor open`,
372
+ the app's Resume and `trantor prune` all treat such a pane as dead and host a fresh one.
371
373
 
372
374
  Why crews never exhaust the orchestrator: bus messages are **by reference** (~70 tokens),
373
375
  work products stay in each agent's own context — the orchestrator burns at coordination
@@ -37,11 +37,27 @@ export function splitPane(ctx, pane, direction, cwd = "", execute = herdrCall) {
37
37
  return parsed?.result?.pane?.pane_id || "";
38
38
  }
39
39
 
40
+ // A pane herdr LISTS is not always a pane herdr can RUN (#7247: a restart kept w2:p8 in the
41
+ // layout, so list/get/rename answered, while read/process-info/run said pane_not_found). Rename
42
+ // is a layout question; process-info is the terminal question, and only a terminal hosts a session.
43
+ export function paneAlive(ctx, pane) {
44
+ if (!pane) return false;
45
+ const result = herdrCall(ctx, ["pane", "process-info", "--pane", pane]);
46
+ const parsed = parseJsonOutput(result.stdout);
47
+ return Boolean(result.ok && parsed && !parsed.error && parsed.result?.process_info);
48
+ }
49
+
40
50
  export function workspacePane(ctx, workspace, preferredCwd) {
41
51
  const parsed = parseJsonOutput(herdrCall(ctx, ["pane", "list"]).stdout);
42
52
  const panes = Array.isArray(parsed) ? parsed : parsed?.panes || parsed?.result?.panes || [];
43
53
  const matches = panes.filter(pane => (pane.workspace_id || pane.workspace || "") === workspace);
44
- return matches.find(pane => (pane.cwd || "") === preferredCwd)?.pane_id || matches[0]?.pane_id || matches[0]?.id || "";
54
+ const preferred = matches.filter(pane => (pane.cwd || "") === preferredCwd);
55
+ const ordered = [...preferred, ...matches.filter(pane => !preferred.includes(pane))];
56
+ for (const pane of ordered) {
57
+ const id = pane.pane_id || pane.id || "";
58
+ if (id && paneAlive(ctx, id)) return id;
59
+ }
60
+ return "";
45
61
  }
46
62
 
47
63
  export function closeWorkspace(ctx, id) {
@@ -179,5 +195,6 @@ export function createHerdrAdapter(ctx) {
179
195
  closeWorkspace: id => closeWorkspace(ctx, id),
180
196
  closePane: id => closePane(ctx, id),
181
197
  liveWorkspaces: () => workspaceList(ctx),
198
+ paneAlive: pane => paneAlive(ctx, pane),
182
199
  };
183
200
  }
package/bin/crew/open.mjs CHANGED
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { basename, join } from "node:path";
5
5
  import { spawn, execSync } from "node:child_process";
6
6
  import { call, parseJsonOutput } from "./core.mjs";
7
- import { createWorkspace, herdrCall, reportAgent, splitPane, workspaceList, workspacePane } from "./herdr.mjs";
7
+ import { createWorkspace, herdrCall, paneAlive, reportAgent, splitPane, workspaceList, workspacePane } from "./herdr.mjs";
8
8
  import { dropState, readRows, recordState } from "./state.mjs";
9
9
  import { resolveOrchestratorDir } from "./worktrees.mjs";
10
10
 
@@ -103,6 +103,14 @@ function tracked(ctx, live) {
103
103
  function reattach(ctx, workspace, pane, id) {
104
104
  if (!pane) return false;
105
105
  if (ctx.dry) { console.log(`herdr:${workspace || "%DRYWS"}/${pane}`); return true; }
106
+ // Two probes, because herdr answers them from different places: a pane a restart left in the
107
+ // layout without a terminal still answers rename, and only process-info says it cannot host
108
+ // anything. Either way the row is dead and the open path hosts a fresh pane.
109
+ if (!paneAlive(ctx, pane)) {
110
+ dropState(ctx, ctx.project, "orch");
111
+ console.error(`— tracked orchestrator pane ${pane} has no terminal behind it (herdr restarted without it): hosting a fresh pane —`);
112
+ return false;
113
+ }
106
114
  const renamed = herdrCall(ctx, ["pane", "rename", pane, `orchestrator · ${ctx.project}`]);
107
115
  if (!renamed.ok) { dropState(ctx, ctx.project, "orch"); return false; }
108
116
  if (!paneHasAgent(ctx, pane)) {
@@ -166,11 +174,10 @@ export function maybeBuildGraft(dir) {
166
174
  const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
167
175
  if (!/^graft\/?$/m.test(cur)) writeFileSync(gi, cur + (cur && !cur.endsWith("\n") ? "\n" : "") + "graft/\n");
168
176
  } catch {}
169
- // The build is backgrounded, so we cannot gate the pane on its exit code — but a failure has to
170
- // land SOMEWHERE. It used to go to stdio:"ignore", which meant a graft whose native parsers were
171
- // never built (npm v12 skips node-gyp by default; see deploy/setup.sh) failed in total silence
172
- // while the line below still claimed an index was being built.
173
- //
177
+ // The build is backgrounded, so its exit code cannot gate the pane, but a failure has to land
178
+ // SOMEWHERE: under stdio:"ignore" a graft without native parsers (see deploy/setup.sh) failed in
179
+ // silence while the line below still claimed an index was being built.
180
+
174
181
  // The log goes to the OS temp dir rather than the project: nothing to gitignore, nothing to clean
175
182
  // up, and no exit handler — after unref() the parent usually dies first, so an on-exit cleanup
176
183
  // would be a promise we cannot keep. It is simply overwritten by the next open of this project.
@@ -139,7 +139,13 @@ export function prune(ctx, adapters) {
139
139
  if (row.kind === "cmuxws") return !cmuxLive.proven || cmuxLive.ids.has(row.handle);
140
140
  if (row.kind === "cmux") return !cmuxLive.proven || cmuxLive.names.has(`trantor:${row.project}`);
141
141
  if (row.kind === "herdrws") return !herdrLive.proven || herdrLive.ids.has(row.handle);
142
- if (["herdr", "orch"].includes(row.kind)) return !herdrLive.proven || herdrLive.names.has(`trantor:${row.project}`);
142
+ // Workspace first (the cmux 0.17.61 lesson: pane ids there were not a liveness fact), then
143
+ // the one per-pane fact herdr does state plainly: a pane whose terminal is gone answers
144
+ // process-info with pane_not_found. A pane that merely hosts a shell stays.
145
+ if (["herdr", "orch"].includes(row.kind)) {
146
+ if (!herdrLive.proven) return true;
147
+ return herdrLive.names.has(`trantor:${row.project}`) && (!adapters.herdr.paneAlive || adapters.herdr.paneAlive(row.handle));
148
+ }
143
149
  return true;
144
150
  });
145
151
  writeRows(ctx, kept);