@mclawnet/swarm 0.1.12 → 0.1.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.
Files changed (57) hide show
  1. package/dist/__tests__/inbox-store-notbefore.test.d.ts +2 -0
  2. package/dist/__tests__/inbox-store-notbefore.test.d.ts.map +1 -0
  3. package/dist/__tests__/inbox-store-notbefore.test.js +47 -0
  4. package/dist/__tests__/inbox-store-notbefore.test.js.map +1 -0
  5. package/dist/__tests__/inbox-store-swarmid-validation.test.d.ts +2 -0
  6. package/dist/__tests__/inbox-store-swarmid-validation.test.d.ts.map +1 -0
  7. package/dist/__tests__/inbox-store-swarmid-validation.test.js +32 -0
  8. package/dist/__tests__/inbox-store-swarmid-validation.test.js.map +1 -0
  9. package/dist/__tests__/role-loader-editor.test.js +40 -10
  10. package/dist/__tests__/role-loader-editor.test.js.map +1 -1
  11. package/dist/__tests__/template-loader-editor.test.js +28 -13
  12. package/dist/__tests__/template-loader-editor.test.js.map +1 -1
  13. package/dist/__tests__/wakeup-end-to-end.test.d.ts +2 -0
  14. package/dist/__tests__/wakeup-end-to-end.test.d.ts.map +1 -0
  15. package/dist/__tests__/wakeup-end-to-end.test.js +80 -0
  16. package/dist/__tests__/wakeup-end-to-end.test.js.map +1 -0
  17. package/dist/__tests__/wakeup-scheduler-restore-fires.test.d.ts +2 -0
  18. package/dist/__tests__/wakeup-scheduler-restore-fires.test.d.ts.map +1 -0
  19. package/dist/__tests__/wakeup-scheduler-restore-fires.test.js +33 -0
  20. package/dist/__tests__/wakeup-scheduler-restore-fires.test.js.map +1 -0
  21. package/dist/__tests__/wakeup-scheduler-restore.test.d.ts +2 -0
  22. package/dist/__tests__/wakeup-scheduler-restore.test.d.ts.map +1 -0
  23. package/dist/__tests__/wakeup-scheduler-restore.test.js +62 -0
  24. package/dist/__tests__/wakeup-scheduler-restore.test.js.map +1 -0
  25. package/dist/__tests__/wakeup-scheduler.test.d.ts +2 -0
  26. package/dist/__tests__/wakeup-scheduler.test.d.ts.map +1 -0
  27. package/dist/__tests__/wakeup-scheduler.test.js +65 -0
  28. package/dist/__tests__/wakeup-scheduler.test.js.map +1 -0
  29. package/dist/__tests__/watch-manager.test.d.ts +2 -0
  30. package/dist/__tests__/watch-manager.test.d.ts.map +1 -0
  31. package/dist/__tests__/watch-manager.test.js +203 -0
  32. package/dist/__tests__/watch-manager.test.js.map +1 -0
  33. package/dist/inbox-store.d.ts +7 -0
  34. package/dist/inbox-store.d.ts.map +1 -1
  35. package/dist/inbox-store.js +8 -1
  36. package/dist/inbox-store.js.map +1 -1
  37. package/dist/index.d.ts +3 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/roles/role-loader.d.ts +30 -5
  42. package/dist/roles/role-loader.d.ts.map +1 -1
  43. package/dist/roles/role-loader.js +40 -32
  44. package/dist/roles/role-loader.js.map +1 -1
  45. package/dist/templates/template-loader.d.ts +23 -9
  46. package/dist/templates/template-loader.d.ts.map +1 -1
  47. package/dist/templates/template-loader.js +33 -26
  48. package/dist/templates/template-loader.js.map +1 -1
  49. package/dist/wakeup-scheduler.d.ts +36 -0
  50. package/dist/wakeup-scheduler.d.ts.map +1 -0
  51. package/dist/wakeup-scheduler.js +107 -0
  52. package/dist/wakeup-scheduler.js.map +1 -0
  53. package/dist/watch-manager.d.ts +32 -0
  54. package/dist/watch-manager.d.ts.map +1 -0
  55. package/dist/watch-manager.js +153 -0
  56. package/dist/watch-manager.js.map +1 -0
  57. package/package.json +3 -3
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { WakeupScheduler } from "../wakeup-scheduler.js";
3
+ describe("WakeupScheduler", () => {
4
+ it("calls relay.deliver after notBefore elapses", async () => {
5
+ vi.useFakeTimers();
6
+ const deliver = vi.fn().mockResolvedValue(undefined);
7
+ const sched = new WakeupScheduler({ deliver });
8
+ sched.register("s1", "w0", Date.now() + 5_000);
9
+ expect(deliver).not.toHaveBeenCalled();
10
+ await vi.advanceTimersByTimeAsync(5_001);
11
+ expect(deliver).toHaveBeenCalledWith("s1", "w0");
12
+ sched.dispose();
13
+ vi.useRealTimers();
14
+ });
15
+ it("dedupes register on identical key — second register does not cause extra delivery", async () => {
16
+ vi.useFakeTimers();
17
+ const deliver = vi.fn().mockResolvedValue(undefined);
18
+ const sched = new WakeupScheduler({ deliver });
19
+ const t = Date.now() + 10_000;
20
+ sched.register("s1", "w0", t);
21
+ sched.register("s1", "w0", t);
22
+ expect(sched.size()).toBe(1);
23
+ await vi.advanceTimersByTimeAsync(10_001);
24
+ expect(deliver).toHaveBeenCalledTimes(1);
25
+ sched.dispose();
26
+ vi.useRealTimers();
27
+ });
28
+ it("swallows deliver errors and continues (best-effort, no unhandled rejection)", async () => {
29
+ vi.useFakeTimers();
30
+ const deliver = vi.fn().mockRejectedValue(new Error("relay boom"));
31
+ const sched = new WakeupScheduler({ deliver });
32
+ sched.register("s1", "w0", Date.now() + 1_000);
33
+ await vi.advanceTimersByTimeAsync(1_001);
34
+ // Yield once more so the .catch handler attached to the rejected promise runs.
35
+ await Promise.resolve();
36
+ expect(deliver).toHaveBeenCalledOnce();
37
+ expect(sched.size()).toBe(0); // timer cleaned up despite rejection
38
+ sched.dispose();
39
+ vi.useRealTimers();
40
+ });
41
+ it("calls deliver immediately if notBefore is in the past", async () => {
42
+ vi.useFakeTimers();
43
+ const deliver = vi.fn().mockResolvedValue(undefined);
44
+ const sched = new WakeupScheduler({ deliver });
45
+ sched.register("s1", "w0", Date.now() - 1);
46
+ await vi.advanceTimersByTimeAsync(1);
47
+ expect(deliver).toHaveBeenCalled();
48
+ sched.dispose();
49
+ vi.useRealTimers();
50
+ });
51
+ it("countForInstance returns active timer count for (s,i)", () => {
52
+ vi.useFakeTimers();
53
+ const sched = new WakeupScheduler({ deliver: vi.fn() });
54
+ const now = Date.now();
55
+ expect(sched.countForInstance("s1", "q")).toBe(0);
56
+ sched.register("s1", "q", now + 1000);
57
+ sched.register("s1", "q", now + 2000);
58
+ sched.register("s1", "other", now + 3000);
59
+ expect(sched.countForInstance("s1", "q")).toBe(2);
60
+ expect(sched.countForInstance("s1", "other")).toBe(1);
61
+ sched.dispose();
62
+ vi.useRealTimers();
63
+ });
64
+ });
65
+ //# sourceMappingURL=wakeup-scheduler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeup-scheduler.test.js","sourceRoot":"","sources":["../../src/__tests__/wakeup-scheduler.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=watch-manager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-manager.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/watch-manager.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,203 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mkdtempSync, mkdirSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { WatchManager } from "../watch-manager.js";
6
+ // Build a fake fs.watch that records listeners and exposes an emit() helper.
7
+ function makeFakeWatch() {
8
+ const listeners = [];
9
+ const closed = [];
10
+ const watchFn = ((_root, _opts, cb) => {
11
+ listeners.push(cb);
12
+ const idx = closed.push(false) - 1;
13
+ return {
14
+ close: () => { closed[idx] = true; },
15
+ on: () => { },
16
+ };
17
+ });
18
+ return {
19
+ watchFn,
20
+ emit: (name) => listeners.forEach(l => l("change", name)),
21
+ closedCount: () => closed.filter(Boolean).length,
22
+ listenerCount: () => listeners.length,
23
+ };
24
+ }
25
+ describe("WatchManager", () => {
26
+ it("triggers onTrigger when a matching file changes (after debounce)", async () => {
27
+ vi.useFakeTimers();
28
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
29
+ const onTrigger = vi.fn();
30
+ const fake = makeFakeWatch();
31
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
32
+ mgr.register({
33
+ swarmId: "s1", instanceId: "q", watchId: "w1",
34
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: ["node_modules/**"],
35
+ debounceMs: 200, onTrigger,
36
+ });
37
+ fake.emit("a.ts");
38
+ await vi.advanceTimersByTimeAsync(300);
39
+ expect(onTrigger).toHaveBeenCalledTimes(1);
40
+ expect(onTrigger).toHaveBeenCalledWith("a.ts");
41
+ mgr.dispose();
42
+ vi.useRealTimers();
43
+ });
44
+ it("ignores files matching ignoreGlobs", async () => {
45
+ vi.useFakeTimers();
46
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
47
+ mkdirSync(join(root, "node_modules"), { recursive: true });
48
+ const onTrigger = vi.fn();
49
+ const fake = makeFakeWatch();
50
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
51
+ mgr.register({
52
+ swarmId: "s1", instanceId: "q", watchId: "w1",
53
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: ["node_modules/**"],
54
+ debounceMs: 100, onTrigger,
55
+ });
56
+ fake.emit("node_modules/x.ts");
57
+ await vi.advanceTimersByTimeAsync(200);
58
+ expect(onTrigger).not.toHaveBeenCalled();
59
+ mgr.dispose();
60
+ vi.useRealTimers();
61
+ });
62
+ it("drops events that don't match includeGlobs", async () => {
63
+ vi.useFakeTimers();
64
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
65
+ const onTrigger = vi.fn();
66
+ const fake = makeFakeWatch();
67
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
68
+ mgr.register({
69
+ swarmId: "s1", instanceId: "q", watchId: "w1",
70
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
71
+ debounceMs: 100, onTrigger,
72
+ });
73
+ fake.emit("README.md");
74
+ await vi.advanceTimersByTimeAsync(200);
75
+ expect(onTrigger).not.toHaveBeenCalled();
76
+ mgr.dispose();
77
+ vi.useRealTimers();
78
+ });
79
+ it("debounces bursts into one trigger", async () => {
80
+ vi.useFakeTimers();
81
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
82
+ const onTrigger = vi.fn();
83
+ const fake = makeFakeWatch();
84
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
85
+ mgr.register({
86
+ swarmId: "s1", instanceId: "q", watchId: "w1",
87
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
88
+ debounceMs: 200, onTrigger,
89
+ });
90
+ for (let i = 0; i < 5; i++) {
91
+ fake.emit(`f${i}.ts`);
92
+ await vi.advanceTimersByTimeAsync(50);
93
+ }
94
+ await vi.advanceTimersByTimeAsync(300);
95
+ expect(onTrigger).toHaveBeenCalledTimes(1);
96
+ mgr.dispose();
97
+ vi.useRealTimers();
98
+ });
99
+ it("unwatchByInstance clears all watches for that instance", () => {
100
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
101
+ const fake = makeFakeWatch();
102
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
103
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
104
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w2", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
105
+ mgr.register({ swarmId: "s1", instanceId: "other", watchId: "w3", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
106
+ expect(mgr.size()).toBe(3);
107
+ mgr.unwatchByInstance("s1", "q");
108
+ expect(mgr.size()).toBe(1);
109
+ expect(fake.closedCount()).toBe(2);
110
+ mgr.dispose();
111
+ expect(fake.closedCount()).toBe(3);
112
+ });
113
+ it("skips register when projectRoot is missing", () => {
114
+ const mgr = new WatchManager();
115
+ mgr.register({
116
+ swarmId: "s1", instanceId: "q", watchId: "w1",
117
+ projectRoot: "/no/such/dir/xyz123", includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100,
118
+ onTrigger: () => { },
119
+ });
120
+ expect(mgr.size()).toBe(0);
121
+ });
122
+ // M5 hotfix I3: server.ts's unregister wrapper uses countForInstance to
123
+ // prune its (s,i)->workDir registry once the last watch for an instance
124
+ // is gone (countForInstance(s,i) === 0).
125
+ it("countForInstance reflects last-removed transition for prune signalling", () => {
126
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
127
+ const fake = makeFakeWatch();
128
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
129
+ expect(mgr.countForInstance("s1", "q")).toBe(0);
130
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
131
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w2", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
132
+ expect(mgr.countForInstance("s1", "q")).toBeGreaterThan(0);
133
+ // unrelated instance must not bleed
134
+ expect(mgr.countForInstance("s1", "other")).toBe(0);
135
+ mgr.unregister("s1", "q", "w1");
136
+ expect(mgr.countForInstance("s1", "q")).toBe(1); // w2 still there
137
+ mgr.unregister("s1", "q", "w2");
138
+ expect(mgr.countForInstance("s1", "q")).toBe(0);
139
+ mgr.dispose();
140
+ });
141
+ it("countForInstance returns active watch count for (s,i)", () => {
142
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
143
+ const fake = makeFakeWatch();
144
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
145
+ expect(mgr.countForInstance("s1", "q")).toBe(0);
146
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "a", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
147
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "b", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
148
+ mgr.register({ swarmId: "s1", instanceId: "other", watchId: "c", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
149
+ expect(mgr.countForInstance("s1", "q")).toBe(2);
150
+ expect(mgr.countForInstance("s1", "other")).toBe(1);
151
+ mgr.dispose();
152
+ });
153
+ it("re-register replaces the existing handle and closes the old watcher", () => {
154
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
155
+ const fake = makeFakeWatch();
156
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
157
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
158
+ mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
159
+ expect(mgr.size()).toBe(1);
160
+ expect(fake.listenerCount()).toBe(2);
161
+ expect(fake.closedCount()).toBe(1);
162
+ mgr.dispose();
163
+ });
164
+ // Race guard: an event arriving after dispose() must not schedule a new timer
165
+ // nor call onTrigger. fs.watch close is best-effort and the listener may still
166
+ // fire briefly after close on some platforms.
167
+ it("ignores events that arrive after dispose (race guard)", async () => {
168
+ vi.useFakeTimers();
169
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
170
+ const onTrigger = vi.fn();
171
+ const fake = makeFakeWatch();
172
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
173
+ mgr.register({
174
+ swarmId: "s1", instanceId: "q", watchId: "w1",
175
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
176
+ debounceMs: 100, onTrigger,
177
+ });
178
+ mgr.dispose();
179
+ fake.emit("late.ts");
180
+ await vi.advanceTimersByTimeAsync(200);
181
+ expect(onTrigger).not.toHaveBeenCalled();
182
+ vi.useRealTimers();
183
+ });
184
+ it("ignores timer fire after unregister (race guard, in-flight timer)", async () => {
185
+ vi.useFakeTimers();
186
+ const root = mkdtempSync(join(tmpdir(), "wm-"));
187
+ const onTrigger = vi.fn();
188
+ const fake = makeFakeWatch();
189
+ const mgr = new WatchManager({ watchFn: fake.watchFn });
190
+ mgr.register({
191
+ swarmId: "s1", instanceId: "q", watchId: "w1",
192
+ projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
193
+ debounceMs: 200, onTrigger,
194
+ });
195
+ fake.emit("a.ts"); // schedules timer
196
+ await vi.advanceTimersByTimeAsync(100); // mid-debounce
197
+ mgr.unregister("s1", "q", "w1"); // clears timer + map entry
198
+ await vi.advanceTimersByTimeAsync(300);
199
+ expect(onTrigger).not.toHaveBeenCalled();
200
+ vi.useRealTimers();
201
+ });
202
+ });
203
+ //# sourceMappingURL=watch-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-manager.test.js","sourceRoot":"","sources":["../../src/__tests__/watch-manager.test.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,12 @@ export interface InboxMessage {
7
7
  timestamp: number;
8
8
  delivered: boolean;
9
9
  deliveredAt?: number;
10
+ /**
11
+ * Optional unix-ms timestamp. The message is "scheduled" — readUndelivered
12
+ * filters it out until Date.now() >= notBefore. Used by the wakeup_after
13
+ * MCP tool to let an agent self-defer work without external triggers.
14
+ */
15
+ notBefore?: number;
10
16
  }
11
17
  export declare class InboxStore {
12
18
  private workDir;
@@ -15,6 +21,7 @@ export declare class InboxStore {
15
21
  constructor(workDir: string, swarmId: string, homeOverride?: string | undefined);
16
22
  private home;
17
23
  private inboxesDir;
24
+ private validateSwarmId;
18
25
  private validateInstanceId;
19
26
  inboxFile(instanceId: string): string;
20
27
  readAll(instanceId: string): Promise<InboxMessage[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"inbox-store.d.ts","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,UAAU;IAQnB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,YAAY,CAAC;gBAFb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,YAAA;IAG/B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAM1B,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAK/B,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUpD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAI5D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBtE"}
1
+ {"version":3,"file":"inbox-store.d.ts","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAQnB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,YAAY,CAAC;gBAFb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,YAAA;IAG/B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,kBAAkB;IAM1B,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAK/B,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUpD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO5D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBtE"}
@@ -26,8 +26,14 @@ export class InboxStore {
26
26
  return this.homeOverride ?? process.env.CLAWNET_HOME ?? homedir();
27
27
  }
28
28
  inboxesDir() {
29
+ this.validateSwarmId(this.swarmId);
29
30
  return join(projectRoot(this.workDir, this.home()), "swarms", this.swarmId, "inboxes");
30
31
  }
32
+ validateSwarmId(id) {
33
+ if (!id || /[\\/]|\.\./.test(id) || id.includes("\0")) {
34
+ throw new Error(`InboxStore: invalid swarmId: ${JSON.stringify(id)}`);
35
+ }
36
+ }
31
37
  validateInstanceId(id) {
32
38
  if (!id || /[\\/]|\.\./.test(id) || id.includes("\0")) {
33
39
  throw new Error(`InboxStore: invalid instanceId: ${JSON.stringify(id)}`);
@@ -66,7 +72,8 @@ export class InboxStore {
66
72
  }
67
73
  }
68
74
  async readUndelivered(instanceId) {
69
- return (await this.readAll(instanceId)).filter(m => !m.delivered);
75
+ const now = Date.now();
76
+ return (await this.readAll(instanceId)).filter(m => !m.delivered && (m.notBefore === undefined || m.notBefore <= now));
70
77
  }
71
78
  async markDelivered(instanceId, ids) {
72
79
  if (ids.length === 0)
@@ -1 +1 @@
1
- {"version":3,"file":"inbox-store.js","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbox-store.js","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,9 +11,11 @@ export type { InboxMessage } from "./inbox-store.js";
11
11
  export { pickStrongestStatus } from "./plan-sync.js";
12
12
  export { InboxRelay } from "./inbox-relay.js";
13
13
  export { InboxWatcher, type InboxRelayLike as InboxWatcherRelayLike } from "./inbox-watcher.js";
14
+ export { WakeupScheduler, type WakeupRelayLike } from "./wakeup-scheduler.js";
15
+ export { WatchManager, type WatchRegistration, type WatchManagerOptions } from "./watch-manager.js";
14
16
  export type { SessionAdapter, HubAdapter, SwarmInstance, RoleInstance, SwarmAction, SwarmMessage, SwarmCreateOptions, RouteEndpoint, SwarmStatus, PlanStatus, } from "./types.js";
15
17
  export type { RoleDefinition, DelegationMap, DelegationEntry } from "./roles/types.js";
16
- export { initTemplates, listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
18
+ export { listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
17
19
  export type { TemplateDefinition, TemplateRoleSpec } from "./templates/types.js";
18
20
  export type { TemplateSource, TemplateListItem } from "./templates/template-loader.js";
19
21
  export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhG,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EACjD,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvF,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpG,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,YAAY,EAClC,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvF,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -8,7 +8,9 @@ export { InboxStore } from "./inbox-store.js";
8
8
  export { pickStrongestStatus } from "./plan-sync.js";
9
9
  export { InboxRelay } from "./inbox-relay.js";
10
10
  export { InboxWatcher } from "./inbox-watcher.js";
11
- export { initTemplates, listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
11
+ export { WakeupScheduler } from "./wakeup-scheduler.js";
12
+ export { WatchManager } from "./watch-manager.js";
13
+ export { listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
12
14
  export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
13
15
  export { PROJECT_FILE_WHITELIST, isWhitelistedRelPath, listProjectFiles, readProjectFile, writeProjectFile, ProjectFilesError, } from "./project-files.js";
14
16
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,CAAC;AAiBhG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EACjD,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAwB,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAoD,MAAM,oBAAoB,CAAC;AAiBpG,OAAO,EAAE,aAAa,EAAE,YAAY,EAClC,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
@@ -10,25 +10,50 @@ import type { RoleDefinition } from "./types.js";
10
10
  */
11
11
  export declare function parseRoleFile(content: string): RoleDefinition;
12
12
  /**
13
- * Ensure user role directory exists and preset templates are copied.
14
- * Returns the user roles directory path.
13
+ * Ensure user role directory exists.
14
+ *
15
+ * Presets live inside the npm package (read-only). User-authored roles live in
16
+ * `~/.clawnet/roles/<name>/CLAUDE.md`. We deliberately do NOT seed presets into
17
+ * the user dir: a preset and a user role with the same name is forbidden at
18
+ * save time, so the union over the two dirs is conflict-free.
15
19
  */
16
20
  export declare function initRoles(): string;
17
21
  /** Load a role definition by name. Reads from ~/.clawnet/roles/{name}/CLAUDE.md first, falls back to preset. */
18
22
  export declare function loadRole(roleName: string): RoleDefinition;
19
23
  /** List all available role names (from user dir + presets). */
20
24
  export declare function listRoles(): string[];
21
- export type RoleSource = "preset" | "user" | "override";
25
+ export type RoleSource = "preset" | "user";
22
26
  export interface RoleListItem {
23
27
  name: string;
24
28
  source: RoleSource;
29
+ /**
30
+ * True when a user-authored role with this name hides a built-in preset of
31
+ * the same name (legacy state — saveRole now refuses such writes). Lets the
32
+ * UI warn the user they're masking a built-in.
33
+ */
34
+ shadowsPreset?: boolean;
25
35
  }
26
- /** List all roles with source tag (preset / user / override). */
36
+ /**
37
+ * List all roles with source tag.
38
+ *
39
+ * Presets live in the npm package; user roles in `~/.clawnet/roles/`. Names
40
+ * must be globally unique going forward (enforced at save time). If a stale
41
+ * user dir collides with a preset (pre-refactor seeded copy, manual file),
42
+ * the user copy wins and the entry is flagged with `shadowsPreset` so the UI
43
+ * can surface the conflict.
44
+ */
27
45
  export declare function listRolesWithSource(): RoleListItem[];
28
46
  export declare function getRoleSource(name: string): RoleSource | null;
29
47
  /** Get raw markdown content. User file wins over preset. */
30
48
  export declare function getRoleRaw(name: string): string | null;
31
- /** Validate by parsing first; throws on bad frontmatter or name mismatch. */
49
+ /**
50
+ * Save a user role.
51
+ *
52
+ * Validates by parsing first; throws on bad frontmatter or name mismatch.
53
+ * Refuses to write if the requested name collides with a built-in preset —
54
+ * presets are immutable, so "edit a preset" is implemented by Copy with a
55
+ * new name.
56
+ */
32
57
  export declare function saveRole(name: string, raw: string): RoleSource;
33
58
  /**
34
59
  * Delete a user role directory. Preset is never touched.
@@ -1 +1 @@
1
- {"version":3,"file":"role-loader.d.ts","sourceRoot":"","sources":["../../src/roles/role-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,YAAY,CAAC;AAiCjF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAyJ7D;AAgBD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAoBlC;AAED,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAazD;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAqBpC;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA2BpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAQ7D;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtD;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAe9D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,CAWtE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,uqKA2CnC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,CA8CR"}
1
+ {"version":3,"file":"role-loader.d.ts","sourceRoot":"","sources":["../../src/roles/role-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,YAAY,CAAC;AAiCjF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAyJ7D;AAgBD;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAazD;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAqBpC;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA8BpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAK7D;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtD;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAoB9D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,CAWtE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,uqKA2CnC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,CA8CR"}
@@ -195,26 +195,16 @@ function pushDelegationEntry(map, key, entry) {
195
195
  }
196
196
  }
197
197
  /**
198
- * Ensure user role directory exists and preset templates are copied.
199
- * Returns the user roles directory path.
198
+ * Ensure user role directory exists.
199
+ *
200
+ * Presets live inside the npm package (read-only). User-authored roles live in
201
+ * `~/.clawnet/roles/<name>/CLAUDE.md`. We deliberately do NOT seed presets into
202
+ * the user dir: a preset and a user role with the same name is forbidden at
203
+ * save time, so the union over the two dirs is conflict-free.
200
204
  */
201
205
  export function initRoles() {
202
206
  const USER_ROLES_DIR = userRolesDir();
203
207
  mkdirSync(USER_ROLES_DIR, { recursive: true });
204
- if (!existsSync(PRESET_DIR))
205
- return USER_ROLES_DIR;
206
- for (const file of readdirSync(PRESET_DIR)) {
207
- if (!file.endsWith(".md"))
208
- continue;
209
- const roleName = file.replace(/\.md$/, "");
210
- const userRoleDir = join(USER_ROLES_DIR, roleName);
211
- const userFile = join(userRoleDir, "CLAUDE.md");
212
- if (!existsSync(userFile)) {
213
- mkdirSync(userRoleDir, { recursive: true });
214
- const content = readFileSync(join(PRESET_DIR, file), "utf-8");
215
- writeFileSync(userFile, content, "utf-8");
216
- }
217
- }
218
208
  return USER_ROLES_DIR;
219
209
  }
220
210
  /** Load a role definition by name. Reads from ~/.clawnet/roles/{name}/CLAUDE.md first, falls back to preset. */
@@ -250,7 +240,15 @@ export function listRoles() {
250
240
  }
251
241
  return [...roles].sort();
252
242
  }
253
- /** List all roles with source tag (preset / user / override). */
243
+ /**
244
+ * List all roles with source tag.
245
+ *
246
+ * Presets live in the npm package; user roles in `~/.clawnet/roles/`. Names
247
+ * must be globally unique going forward (enforced at save time). If a stale
248
+ * user dir collides with a preset (pre-refactor seeded copy, manual file),
249
+ * the user copy wins and the entry is flagged with `shadowsPreset` so the UI
250
+ * can surface the conflict.
251
+ */
254
252
  export function listRolesWithSource() {
255
253
  const presetNames = existsSync(PRESET_DIR)
256
254
  ? readdirSync(PRESET_DIR)
@@ -266,25 +264,25 @@ export function listRolesWithSource() {
266
264
  }
267
265
  }
268
266
  }
269
- const presetSet = new Set(presetNames);
270
267
  const userSet = new Set(userNames);
271
- const all = new Set([...presetSet, ...userSet]);
272
- return [...all].sort().map((name) => {
273
- const inUser = userSet.has(name);
274
- const inPreset = presetSet.has(name);
275
- const source = inUser && inPreset ? "override" : inUser ? "user" : "preset";
276
- return { name, source };
277
- });
268
+ const presetSet = new Set(presetNames);
269
+ const items = [];
270
+ for (const name of userNames) {
271
+ items.push(presetSet.has(name)
272
+ ? { name, source: "user", shadowsPreset: true }
273
+ : { name, source: "user" });
274
+ }
275
+ for (const name of presetNames) {
276
+ if (!userSet.has(name))
277
+ items.push({ name, source: "preset" });
278
+ }
279
+ return items.sort((a, b) => a.name.localeCompare(b.name));
278
280
  }
279
281
  export function getRoleSource(name) {
280
282
  validateName(name);
281
- const inUser = existsSync(join(userRolesDir(), name, "CLAUDE.md"));
282
- const inPreset = existsSync(join(PRESET_DIR, `${name}.md`));
283
- if (inUser && inPreset)
284
- return "override";
285
- if (inUser)
283
+ if (existsSync(join(userRolesDir(), name, "CLAUDE.md")))
286
284
  return "user";
287
- if (inPreset)
285
+ if (existsSync(join(PRESET_DIR, `${name}.md`)))
288
286
  return "preset";
289
287
  return null;
290
288
  }
@@ -299,9 +297,19 @@ export function getRoleRaw(name) {
299
297
  return readFileSync(presetFile, "utf-8");
300
298
  return null;
301
299
  }
302
- /** Validate by parsing first; throws on bad frontmatter or name mismatch. */
300
+ /**
301
+ * Save a user role.
302
+ *
303
+ * Validates by parsing first; throws on bad frontmatter or name mismatch.
304
+ * Refuses to write if the requested name collides with a built-in preset —
305
+ * presets are immutable, so "edit a preset" is implemented by Copy with a
306
+ * new name.
307
+ */
303
308
  export function saveRole(name, raw) {
304
309
  validateName(name);
310
+ if (existsSync(join(PRESET_DIR, `${name}.md`))) {
311
+ throw new Error(`Cannot save role '${name}': name conflicts with a built-in preset. Choose another name.`);
312
+ }
305
313
  const parsed = parseRoleFile(raw);
306
314
  if (parsed.name && parsed.name !== name && parsed.name !== "unknown") {
307
315
  throw new Error(`Frontmatter name '${parsed.name}' does not match requested '${name}'`);