@nledford/omp-coord 0.3.0 → 0.3.1

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.
@@ -7,7 +7,7 @@
7
7
  "name": "omp-coord",
8
8
  "source": "./",
9
9
  "description": "Shared subagent caps, build and test queues, claims, pause, a decision log and messages across omp sessions working in one project on one machine.",
10
- "version": "0.3.0",
10
+ "version": "0.3.1",
11
11
  "author": { "name": "Nathaniel Ledford" },
12
12
  "homepage": "https://github.com/nledford/omp-coord",
13
13
  "repository": "https://github.com/nledford/omp-coord",
package/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ Add each user-visible change under Unreleased as it lands, in one of: Added, Cha
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.1] - 2026-10-27
10
+
11
+ ### Fixed
12
+
13
+ - `omp-coord interrupted` and `/coord interrupted` no longer list an agent that was revived (`write agent://<id>`) since its session left, from a session in the same folder, nor its nested helpers, and leave out a session with none left; before, a session restarted after a crash kept being told to revive agents that had long finished. A new agent that only reuses the name doesn't count as a revival.
14
+
9
15
  ## [0.3.0] - 2026-10-27
10
16
 
11
17
  ### Changed
@@ -76,7 +82,8 @@ Add each user-visible change under Unreleased as it lands, in one of: Added, Cha
76
82
  - An SQLite event log per scope, with retention.
77
83
  - Packaging as an omp plugin, with a marketplace catalog; queued commands run on omp's own Bun.
78
84
 
79
- [Unreleased]: https://github.com/nledford/omp-coord/compare/v0.3.0...HEAD
85
+ [Unreleased]: https://github.com/nledford/omp-coord/compare/v0.3.1...HEAD
86
+ [0.3.1]: https://github.com/nledford/omp-coord/compare/v0.3.0...v0.3.1
80
87
  [0.3.0]: https://github.com/nledford/omp-coord/compare/v0.2.3...v0.3.0
81
88
  [0.2.3]: https://github.com/nledford/omp-coord/compare/v0.2.2...v0.2.3
82
89
  [0.2.2]: https://github.com/nledford/omp-coord/compare/v0.2.1...v0.2.2
@@ -36,6 +36,6 @@ With `resources.ports`, each session gets its own block of ports, in `OMP_COORD_
36
36
 
37
37
  ## After a crash
38
38
 
39
- `/coord interrupted` (or `omp-coord interrupted`, which first drops sessions whose process is gone) lists, per session that left, the agents that were mid-run and the folder the session ran in. omp keeps their transcripts; revive each from a session in that folder with `write agent://<id>`.
39
+ `/coord interrupted` (or `omp-coord interrupted`, which first drops sessions whose process is gone) lists, per session that left, the agents that were mid-run and the folder the session ran in. omp keeps their transcripts; revive each from a session in that folder with `write agent://<id>`. An agent revived since from a session in that folder, and its nested helpers, are left out; a new agent that only reuses the name is not a revival.
40
40
 
41
41
  `/coord agents` (or `omp-coord status --agents`) lists who runs: each agent's session, id, type, parent, pool and age, so a message to `B/<agent id>` goes to the agent you mean. `/coord stats` sums waits, refusals, peaks and job outcomes per day; `omp-coord doctor` checks the scope's health, and `doctor --fix` drops sessions and jobs whose process is gone.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nledford/omp-coord",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "files": [
5
5
  "src",
6
6
  "docs",
@@ -11,18 +11,34 @@ export interface InterruptedSession {
11
11
  /**
12
12
  * Sessions that left (exited, or were found dead) at or after `since` while some of their agents were in a run:
13
13
  * those runs started and never ended. Revive them with `write agent://<id>` from a session in the same folder.
14
+ *
15
+ * An agent revived since, from a session in the same folder, is no longer listed, and neither are its nested
16
+ * helpers (they aren't restarted; reviving the parent covers them); a session with nothing left is left out. A
17
+ * revival is a run that started without a spawn approval (`revived` in the event) by an id that session hadn't run
18
+ * before: a new agent that reuses the name was spawned, so it doesn't hide the old one. A session whose folder is
19
+ * unknown (its join event pruned) never hides anything.
14
20
  */
15
21
  export function interrupted(events: BoardEvent[], since: number): InterruptedSession[] {
16
22
  const cwd = new Map<string, string>();
17
23
  const running = new Map<string, Map<string, { id: string; type: string; startedAt: number }>>();
24
+ const seen = new Map<string, Set<string>>();
25
+ // The latest revival per folder and agent id.
26
+ const revivedAt = new Map<string, number>();
18
27
  const result: InterruptedSession[] = [];
19
28
  for (const e of events) {
20
29
  if (e.sessionId === null) continue;
21
30
  const runs = running.get(e.sessionId) ?? new Map();
22
31
  running.set(e.sessionId, runs);
23
32
  if (e.type === "session_joined" && typeof e.data.cwd === "string") cwd.set(e.sessionId, e.data.cwd);
24
- if (e.type === "run_started")
25
- runs.set(String(e.data.agentId), { id: String(e.data.agentId), type: String(e.data.agentType), startedAt: e.at });
33
+ if (e.type === "run_started") {
34
+ const id = String(e.data.agentId);
35
+ runs.set(id, { id, type: String(e.data.agentType), startedAt: e.at });
36
+ const own = seen.get(e.sessionId) ?? new Set<string>();
37
+ seen.set(e.sessionId, own);
38
+ const folder = cwd.get(e.sessionId);
39
+ if (folder !== undefined && e.data.revived === true && !own.has(id)) revivedAt.set(`${folder}\u0000${id}`, e.at);
40
+ own.add(id);
41
+ }
26
42
  if (e.type === "run_ended") runs.delete(String(e.data.agentId));
27
43
  if (e.type === "session_left") {
28
44
  if (e.at >= since && runs.size)
@@ -30,7 +46,17 @@ export function interrupted(events: BoardEvent[], since: number): InterruptedSes
30
46
  running.delete(e.sessionId);
31
47
  }
32
48
  }
33
- return result;
49
+ const revivedSince = (folder: string | undefined, id: string, leftAt: number): boolean =>
50
+ folder !== undefined && (revivedAt.get(`${folder}\u0000${id}`) ?? 0) > leftAt;
51
+ return result
52
+ .map(s => ({
53
+ ...s,
54
+ agents: s.agents.filter(a => {
55
+ const top = a.id.split(".")[0] ?? a.id;
56
+ return !revivedSince(s.cwd, a.id, s.leftAt) && !revivedSince(s.cwd, top, s.leftAt);
57
+ }),
58
+ }))
59
+ .filter(s => s.agents.length > 0);
34
60
  }
35
61
 
36
62
  export interface QueueDay {