neuron-inspector 0.7.0 → 0.8.0

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 (99) hide show
  1. package/dist/compound-tools.js +2 -136
  2. package/dist/compound-tools.js.map +1 -1
  3. package/dist/monitor.js +208 -7
  4. package/dist/monitor.js.map +1 -1
  5. package/dist/recipe-tools.js +6 -39
  6. package/dist/recipe-tools.js.map +1 -1
  7. package/dist/recipes.d.ts +0 -4
  8. package/dist/recipes.js +3 -49
  9. package/dist/recipes.js.map +1 -1
  10. package/dist/server.js +53 -166
  11. package/dist/server.js.map +1 -1
  12. package/dist/tools.js +0 -26
  13. package/dist/tools.js.map +1 -1
  14. package/package.json +3 -4
  15. package/recipes/linkedin-outreach/learnings.md +1 -17
  16. package/recipes/planner/agent.md +1 -66
  17. package/recipes/planner/learnings.md +1 -49
  18. package/recipes/social-ops/learnings.md +13 -33
  19. package/dist/__tests__/monitor.test.d.ts +0 -1
  20. package/dist/__tests__/monitor.test.js +0 -286
  21. package/dist/__tests__/monitor.test.js.map +0 -1
  22. package/dist/__tests__/recipes.test.d.ts +0 -1
  23. package/dist/__tests__/recipes.test.js +0 -415
  24. package/dist/__tests__/recipes.test.js.map +0 -1
  25. package/dist/__tests__/scheduler.test.d.ts +0 -1
  26. package/dist/__tests__/scheduler.test.js +0 -316
  27. package/dist/__tests__/scheduler.test.js.map +0 -1
  28. package/dist/__tests__/session-state.test.d.ts +0 -1
  29. package/dist/__tests__/session-state.test.js +0 -335
  30. package/dist/__tests__/session-state.test.js.map +0 -1
  31. package/dist/collections/__tests__/assertions.test.d.ts +0 -1
  32. package/dist/collections/__tests__/assertions.test.js +0 -203
  33. package/dist/collections/__tests__/assertions.test.js.map +0 -1
  34. package/dist/collections/__tests__/dynamic-vars.test.d.ts +0 -1
  35. package/dist/collections/__tests__/dynamic-vars.test.js +0 -169
  36. package/dist/collections/__tests__/dynamic-vars.test.js.map +0 -1
  37. package/dist/collections/__tests__/execution.test.d.ts +0 -1
  38. package/dist/collections/__tests__/execution.test.js +0 -271
  39. package/dist/collections/__tests__/execution.test.js.map +0 -1
  40. package/dist/collections/__tests__/integration.test.d.ts +0 -1
  41. package/dist/collections/__tests__/integration.test.js +0 -478
  42. package/dist/collections/__tests__/integration.test.js.map +0 -1
  43. package/dist/collections/__tests__/resolver.test.d.ts +0 -1
  44. package/dist/collections/__tests__/resolver.test.js +0 -282
  45. package/dist/collections/__tests__/resolver.test.js.map +0 -1
  46. package/dist/collections/__tests__/scenario.test.d.ts +0 -1
  47. package/dist/collections/__tests__/scenario.test.js +0 -367
  48. package/dist/collections/__tests__/scenario.test.js.map +0 -1
  49. package/dist/collections/__tests__/scripts.test.d.ts +0 -1
  50. package/dist/collections/__tests__/scripts.test.js +0 -245
  51. package/dist/collections/__tests__/scripts.test.js.map +0 -1
  52. package/dist/collections/__tests__/store.test.d.ts +0 -1
  53. package/dist/collections/__tests__/store.test.js +0 -313
  54. package/dist/collections/__tests__/store.test.js.map +0 -1
  55. package/dist/collections/assertions.d.ts +0 -16
  56. package/dist/collections/assertions.js +0 -107
  57. package/dist/collections/assertions.js.map +0 -1
  58. package/dist/collections/dynamic-vars.d.ts +0 -75
  59. package/dist/collections/dynamic-vars.js +0 -187
  60. package/dist/collections/dynamic-vars.js.map +0 -1
  61. package/dist/collections/executor.d.ts +0 -100
  62. package/dist/collections/executor.js +0 -301
  63. package/dist/collections/executor.js.map +0 -1
  64. package/dist/collections/http.d.ts +0 -9
  65. package/dist/collections/http.js +0 -194
  66. package/dist/collections/http.js.map +0 -1
  67. package/dist/collections/index.d.ts +0 -12
  68. package/dist/collections/index.js +0 -27
  69. package/dist/collections/index.js.map +0 -1
  70. package/dist/collections/postman.d.ts +0 -10
  71. package/dist/collections/postman.js +0 -288
  72. package/dist/collections/postman.js.map +0 -1
  73. package/dist/collections/resolver.d.ts +0 -74
  74. package/dist/collections/resolver.js +0 -330
  75. package/dist/collections/resolver.js.map +0 -1
  76. package/dist/collections/scripts.d.ts +0 -20
  77. package/dist/collections/scripts.js +0 -225
  78. package/dist/collections/scripts.js.map +0 -1
  79. package/dist/collections/store.d.ts +0 -68
  80. package/dist/collections/store.js +0 -507
  81. package/dist/collections/store.js.map +0 -1
  82. package/dist/collections/tools.d.ts +0 -8
  83. package/dist/collections/tools.js +0 -1276
  84. package/dist/collections/tools.js.map +0 -1
  85. package/dist/collections/types.d.ts +0 -226
  86. package/dist/collections/types.js +0 -3
  87. package/dist/collections/types.js.map +0 -1
  88. package/dist/killswitch.d.ts +0 -8
  89. package/dist/killswitch.js +0 -304
  90. package/dist/killswitch.js.map +0 -1
  91. package/dist/resilience.d.ts +0 -112
  92. package/dist/resilience.js +0 -319
  93. package/dist/resilience.js.map +0 -1
  94. package/recipes/autopilot/agent.md +0 -316
  95. package/recipes/autopilot/learnings.md +0 -33
  96. package/recipes/autopilot/recipe.yaml +0 -96
  97. package/recipes/inbox-responder/agent.md +0 -268
  98. package/recipes/inbox-responder/learnings.md +0 -25
  99. package/recipes/inbox-responder/recipe.yaml +0 -82
@@ -1,316 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
4
- import * as os from "node:os";
5
- import YAML from "yaml";
6
- let testDir;
7
- let originalHome;
8
- beforeEach(() => {
9
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), "neuron-scheduler-test-"));
10
- originalHome = process.env.HOME || "";
11
- process.env.HOME = testDir;
12
- });
13
- afterEach(() => {
14
- process.env.HOME = originalHome;
15
- fs.rmSync(testDir, { recursive: true, force: true });
16
- });
17
- function schedulesPath() {
18
- return path.join(testDir, ".neuron", "schedules.yaml");
19
- }
20
- function loadSchedules() {
21
- if (!fs.existsSync(schedulesPath())) {
22
- return { schedules: [] };
23
- }
24
- const content = fs.readFileSync(schedulesPath(), "utf-8");
25
- return YAML.parse(content);
26
- }
27
- function saveSchedule(schedule) {
28
- const neuronDir = path.join(testDir, ".neuron");
29
- if (!fs.existsSync(neuronDir)) {
30
- fs.mkdirSync(neuronDir, { recursive: true });
31
- }
32
- const data = loadSchedules();
33
- data.schedules.push(schedule);
34
- fs.writeFileSync(schedulesPath(), YAML.stringify(data), "utf-8");
35
- }
36
- describe("Schedule storage", () => {
37
- it("creates schedules file when saving first schedule", () => {
38
- const schedule = {
39
- id: "test_123",
40
- slug: "web-researcher",
41
- interval_minutes: 60,
42
- enabled: true,
43
- created_at: "2026-01-01T00:00:00Z",
44
- run_count: 0,
45
- };
46
- saveSchedule(schedule);
47
- expect(fs.existsSync(schedulesPath())).toBe(true);
48
- const data = loadSchedules();
49
- expect(data.schedules).toHaveLength(1);
50
- expect(data.schedules[0].id).toBe("test_123");
51
- });
52
- it("loads existing schedules correctly", () => {
53
- saveSchedule({
54
- id: "sched_1",
55
- slug: "recipe-a",
56
- interval_minutes: 30,
57
- enabled: true,
58
- created_at: "2026-01-01T00:00:00Z",
59
- run_count: 0,
60
- });
61
- saveSchedule({
62
- id: "sched_2",
63
- slug: "recipe-b",
64
- cron_hour: 9,
65
- enabled: false,
66
- created_at: "2026-01-01T01:00:00Z",
67
- run_count: 0,
68
- });
69
- const data = loadSchedules();
70
- expect(data.schedules).toHaveLength(2);
71
- expect(data.schedules[0].slug).toBe("recipe-a");
72
- expect(data.schedules[1].slug).toBe("recipe-b");
73
- });
74
- });
75
- describe("Schedule creation", () => {
76
- it("generates unique IDs", () => {
77
- const schedule1 = {
78
- id: `sched_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
79
- slug: "test",
80
- interval_minutes: 60,
81
- enabled: true,
82
- created_at: new Date().toISOString(),
83
- run_count: 0,
84
- };
85
- const schedule2 = {
86
- id: `sched_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
87
- slug: "test",
88
- interval_minutes: 60,
89
- enabled: true,
90
- created_at: new Date().toISOString(),
91
- run_count: 0,
92
- };
93
- expect(schedule1.id).toMatch(/^sched_\d+_[a-z0-9]+$/);
94
- expect(schedule2.id).toMatch(/^sched_\d+_[a-z0-9]+$/);
95
- expect(schedule1.id).not.toBe(schedule2.id);
96
- });
97
- it("validates interval vs cron exclusivity", () => {
98
- const invalidSchedule = {
99
- id: "test_invalid",
100
- slug: "test",
101
- interval_minutes: 60,
102
- cron_hour: 9,
103
- enabled: true,
104
- created_at: new Date().toISOString(),
105
- run_count: 0,
106
- };
107
- expect(() => {
108
- if (invalidSchedule.interval_minutes !== undefined && invalidSchedule.cron_hour !== undefined) {
109
- throw new Error("Cannot specify both interval_minutes and cron_hour");
110
- }
111
- }).toThrow(/both interval_minutes and cron_hour/);
112
- });
113
- it("requires at least one timing parameter", () => {
114
- const invalidSchedule = {
115
- id: "test_invalid",
116
- slug: "test",
117
- enabled: true,
118
- created_at: new Date().toISOString(),
119
- run_count: 0,
120
- };
121
- expect(() => {
122
- if (invalidSchedule.interval_minutes === undefined && invalidSchedule.cron_hour === undefined) {
123
- throw new Error("Must specify either interval_minutes or cron_hour");
124
- }
125
- }).toThrow(/Must specify either/);
126
- });
127
- });
128
- describe("Cron schedule calculation", () => {
129
- function computeNextCronRun(hour, days) {
130
- const now = new Date();
131
- let next = new Date(now);
132
- next.setHours(hour, 0, 0, 0);
133
- if (next <= now) {
134
- next.setDate(next.getDate() + 1);
135
- }
136
- if (days && days.length > 0) {
137
- const maxIterations = 7;
138
- let iterations = 0;
139
- while (!days.includes(next.getDay()) && iterations < maxIterations) {
140
- next.setDate(next.getDate() + 1);
141
- iterations++;
142
- }
143
- }
144
- return next;
145
- }
146
- it("schedules for today if time hasn't passed", () => {
147
- const now = new Date();
148
- const futureHour = (now.getHours() + 2) % 24;
149
- const next = computeNextCronRun(futureHour);
150
- if (futureHour > now.getHours()) {
151
- expect(next.getDate()).toBe(now.getDate());
152
- }
153
- expect(next.getHours()).toBe(futureHour);
154
- expect(next.getMinutes()).toBe(0);
155
- expect(next.getSeconds()).toBe(0);
156
- });
157
- it("schedules for tomorrow if time has passed", () => {
158
- const now = new Date();
159
- const pastHour = (now.getHours() - 1 + 24) % 24;
160
- const next = computeNextCronRun(pastHour);
161
- if (pastHour < now.getHours()) {
162
- const tomorrow = new Date(now);
163
- tomorrow.setDate(tomorrow.getDate() + 1);
164
- expect(next.getDate()).toBe(tomorrow.getDate());
165
- }
166
- expect(next.getHours()).toBe(pastHour);
167
- });
168
- it("respects day-of-week constraints", () => {
169
- const next = computeNextCronRun(9, [1, 3, 5]); // Monday, Wednesday, Friday
170
- expect([1, 3, 5]).toContain(next.getDay());
171
- expect(next.getHours()).toBe(9);
172
- });
173
- it("finds next valid day within a week", () => {
174
- const next = computeNextCronRun(9, [0]); // Only Sunday
175
- expect(next.getDay()).toBe(0);
176
- expect(next.getHours()).toBe(9);
177
- const now = new Date();
178
- const daysDiff = Math.floor((next.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
179
- expect(daysDiff).toBeLessThanOrEqual(7);
180
- });
181
- });
182
- describe("Schedule toggle", () => {
183
- it("enables and disables schedules", () => {
184
- saveSchedule({
185
- id: "toggle_test",
186
- slug: "test",
187
- interval_minutes: 60,
188
- enabled: true,
189
- created_at: new Date().toISOString(),
190
- run_count: 0,
191
- });
192
- let data = loadSchedules();
193
- expect(data.schedules[0].enabled).toBe(true);
194
- data.schedules[0].enabled = false;
195
- fs.writeFileSync(schedulesPath(), YAML.stringify(data), "utf-8");
196
- data = loadSchedules();
197
- expect(data.schedules[0].enabled).toBe(false);
198
- });
199
- });
200
- describe("Schedule removal", () => {
201
- it("removes schedule by ID", () => {
202
- saveSchedule({
203
- id: "remove_1",
204
- slug: "keep-me",
205
- interval_minutes: 60,
206
- enabled: true,
207
- created_at: new Date().toISOString(),
208
- run_count: 0,
209
- });
210
- saveSchedule({
211
- id: "remove_2",
212
- slug: "delete-me",
213
- interval_minutes: 30,
214
- enabled: true,
215
- created_at: new Date().toISOString(),
216
- run_count: 0,
217
- });
218
- let data = loadSchedules();
219
- expect(data.schedules).toHaveLength(2);
220
- data.schedules = data.schedules.filter((s) => s.id !== "remove_2");
221
- fs.writeFileSync(schedulesPath(), YAML.stringify(data), "utf-8");
222
- data = loadSchedules();
223
- expect(data.schedules).toHaveLength(1);
224
- expect(data.schedules[0].id).toBe("remove_1");
225
- });
226
- it("throws error for nonexistent schedule", () => {
227
- const data = loadSchedules();
228
- const idx = data.schedules.findIndex((s) => s.id === "nonexistent");
229
- expect(() => {
230
- if (idx === -1) {
231
- throw new Error("Schedule nonexistent not found");
232
- }
233
- }).toThrow(/not found/);
234
- });
235
- });
236
- describe("Schedule metadata", () => {
237
- it("tracks run count", () => {
238
- saveSchedule({
239
- id: "count_test",
240
- slug: "test",
241
- interval_minutes: 60,
242
- enabled: true,
243
- created_at: "2026-01-01T00:00:00Z",
244
- run_count: 0,
245
- });
246
- let data = loadSchedules();
247
- expect(data.schedules[0].run_count).toBe(0);
248
- data.schedules[0].run_count += 1;
249
- data.schedules[0].last_run_at = new Date().toISOString();
250
- fs.writeFileSync(schedulesPath(), YAML.stringify(data), "utf-8");
251
- data = loadSchedules();
252
- expect(data.schedules[0].run_count).toBe(1);
253
- expect(data.schedules[0].last_run_at).toBeDefined();
254
- });
255
- it("stores variables and rules", () => {
256
- saveSchedule({
257
- id: "vars_test",
258
- slug: "test",
259
- interval_minutes: 60,
260
- variables: {
261
- username: "testuser",
262
- count: 10,
263
- },
264
- rules: ["Be polite", "Rate limit: 100/hour"],
265
- enabled: true,
266
- created_at: new Date().toISOString(),
267
- run_count: 0,
268
- });
269
- const data = loadSchedules();
270
- expect(data.schedules[0].variables).toEqual({
271
- username: "testuser",
272
- count: 10,
273
- });
274
- expect(data.schedules[0].rules).toEqual(["Be polite", "Rate limit: 100/hour"]);
275
- });
276
- it("updates next_run_at timestamp", () => {
277
- const nextRun = new Date();
278
- nextRun.setHours(nextRun.getHours() + 1);
279
- saveSchedule({
280
- id: "next_run_test",
281
- slug: "test",
282
- interval_minutes: 60,
283
- enabled: true,
284
- created_at: new Date().toISOString(),
285
- next_run_at: nextRun.toISOString(),
286
- run_count: 0,
287
- });
288
- const data = loadSchedules();
289
- expect(data.schedules[0].next_run_at).toBeDefined();
290
- expect(new Date(data.schedules[0].next_run_at).getTime()).toBeGreaterThan(Date.now());
291
- });
292
- });
293
- describe("Pending runs", () => {
294
- it("creates pending run entries", () => {
295
- const neuronDir = path.join(testDir, ".neuron");
296
- fs.mkdirSync(neuronDir, { recursive: true });
297
- const pendingRunsPath = path.join(neuronDir, "pending-runs.yaml");
298
- const pending = {
299
- pending: [
300
- {
301
- slug: "web-researcher",
302
- variables: { url: "https://example.com" },
303
- rules: ["Be thorough"],
304
- scheduled_at: new Date().toISOString(),
305
- schedule_id: "sched_123",
306
- },
307
- ],
308
- };
309
- fs.writeFileSync(pendingRunsPath, YAML.stringify(pending), "utf-8");
310
- const loaded = YAML.parse(fs.readFileSync(pendingRunsPath, "utf-8"));
311
- expect(loaded.pending).toHaveLength(1);
312
- expect(loaded.pending[0].slug).toBe("web-researcher");
313
- expect(loaded.pending[0].schedule_id).toBe("sched_123");
314
- });
315
- });
316
- //# sourceMappingURL=scheduler.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scheduler.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,335 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
4
- import * as os from "node:os";
5
- import { handleSessionTool as _handleSessionTool } from "../session-state.js";
6
- // Typed wrapper — all session tool results are plain objects
7
- const handleSessionTool = (name, args) => _handleSessionTool(name, args);
8
- let testDir;
9
- let originalHome;
10
- beforeEach(() => {
11
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), "neuron-session-test-"));
12
- originalHome = process.env.HOME || "";
13
- process.env.HOME = testDir;
14
- // Pre-create sessions directory to avoid async mkdir issues in module
15
- const sessionsDir = path.join(testDir, ".neuron", "sessions");
16
- fs.mkdirSync(sessionsDir, { recursive: true });
17
- });
18
- afterEach(() => {
19
- process.env.HOME = originalHome;
20
- fs.rmSync(testDir, { recursive: true, force: true });
21
- });
22
- function sessionPath(sessionId) {
23
- return path.join(testDir, ".neuron", "sessions", `${sessionId}.yaml`);
24
- }
25
- describe("neuron_session_save", () => {
26
- it("creates new session with initial state", async () => {
27
- const result = await handleSessionTool("neuron_session_save", {
28
- session_id: "instagram-scrape",
29
- cursor: 0,
30
- working_set: ["user1", "user2", "user3"],
31
- completed: [],
32
- progress: { total: 3, done: 0 },
33
- metadata: { target: "@testuser" },
34
- });
35
- expect(result).toMatchObject({
36
- status: "saved",
37
- session_id: "instagram-scrape",
38
- });
39
- expect(fs.existsSync(sessionPath("instagram-scrape"))).toBe(true);
40
- });
41
- it("merges with existing session state", async () => {
42
- await handleSessionTool("neuron_session_save", {
43
- session_id: "merge-test",
44
- cursor: 0,
45
- working_set: ["a", "b", "c"],
46
- completed: [],
47
- progress: { total: 3 },
48
- });
49
- await handleSessionTool("neuron_session_save", {
50
- session_id: "merge-test",
51
- cursor: 1,
52
- completed: ["a"],
53
- progress: { done: 1 },
54
- });
55
- const loaded = await handleSessionTool("neuron_session_load", {
56
- session_id: "merge-test",
57
- });
58
- expect(loaded).toMatchObject({
59
- cursor: 1,
60
- working_set: ["a", "b", "c"],
61
- completed: ["a"],
62
- progress: { total: 3, done: 1 },
63
- });
64
- });
65
- it("extracts recipe slug from session_id", async () => {
66
- await handleSessionTool("neuron_session_save", {
67
- session_id: "instagram-scrape-batch1",
68
- cursor: 0,
69
- working_set: [],
70
- completed: [],
71
- });
72
- const loaded = await handleSessionTool("neuron_session_load", {
73
- session_id: "instagram-scrape-batch1",
74
- });
75
- expect(loaded).toHaveProperty("recipe_slug", "instagram-scrape");
76
- });
77
- it("creates sessions directory if missing", async () => {
78
- // Remove the pre-created sessions dir for this test
79
- const sessionsDir = path.join(testDir, ".neuron", "sessions");
80
- fs.rmSync(sessionsDir, { recursive: true, force: true });
81
- expect(fs.existsSync(sessionsDir)).toBe(false);
82
- await handleSessionTool("neuron_session_save", {
83
- session_id: "test",
84
- cursor: 0,
85
- working_set: [],
86
- completed: [],
87
- });
88
- // Session file should exist now (directory will be created)
89
- expect(fs.existsSync(sessionPath("test"))).toBe(true);
90
- });
91
- });
92
- describe("neuron_session_load", () => {
93
- it("returns null for nonexistent session", async () => {
94
- const result = await handleSessionTool("neuron_session_load", {
95
- session_id: "nonexistent",
96
- });
97
- expect(result).toBeNull();
98
- });
99
- it("loads existing session state", async () => {
100
- await handleSessionTool("neuron_session_save", {
101
- session_id: "load-test",
102
- cursor: 5,
103
- working_set: ["a", "b", "c"],
104
- completed: ["x", "y"],
105
- progress: { done: 2, total: 5 },
106
- metadata: { source: "api" },
107
- });
108
- const loaded = await handleSessionTool("neuron_session_load", {
109
- session_id: "load-test",
110
- });
111
- expect(loaded).toMatchObject({
112
- session_id: "load-test",
113
- cursor: 5,
114
- working_set: ["a", "b", "c"],
115
- completed: ["x", "y"],
116
- progress: { done: 2, total: 5 },
117
- metadata: { source: "api" },
118
- });
119
- });
120
- it("includes timestamps", async () => {
121
- await handleSessionTool("neuron_session_save", {
122
- session_id: "timestamp-test",
123
- cursor: 0,
124
- working_set: [],
125
- completed: [],
126
- });
127
- const loaded = await handleSessionTool("neuron_session_load", {
128
- session_id: "timestamp-test",
129
- });
130
- expect(loaded).toHaveProperty("created_at");
131
- expect(loaded).toHaveProperty("updated_at");
132
- expect(new Date(loaded.created_at).getTime()).toBeGreaterThan(0);
133
- });
134
- });
135
- describe("neuron_session_list", () => {
136
- it("returns empty array when no sessions exist", async () => {
137
- const result = await handleSessionTool("neuron_session_list", {});
138
- expect(result).toEqual({ sessions: [] });
139
- });
140
- it("lists all active sessions with summaries", async () => {
141
- await handleSessionTool("neuron_session_save", {
142
- session_id: "session-1",
143
- cursor: 0,
144
- working_set: ["a", "b", "c"],
145
- completed: [],
146
- progress: { total: 3 },
147
- });
148
- await handleSessionTool("neuron_session_save", {
149
- session_id: "session-2",
150
- cursor: 2,
151
- working_set: ["x", "y"],
152
- completed: ["p", "q"],
153
- progress: { total: 4 },
154
- });
155
- const result = await handleSessionTool("neuron_session_list", {});
156
- expect(result.sessions).toHaveLength(2);
157
- expect(result.sessions[0]).toMatchObject({
158
- session_id: "session-1",
159
- working_set_size: 3,
160
- completed_size: 0,
161
- checkpoints: 0,
162
- });
163
- expect(result.sessions[1]).toMatchObject({
164
- session_id: "session-2",
165
- working_set_size: 2,
166
- completed_size: 2,
167
- checkpoints: 0,
168
- });
169
- });
170
- it("includes recipe slug in listings", async () => {
171
- await handleSessionTool("neuron_session_save", {
172
- session_id: "instagram-batch1",
173
- cursor: 0,
174
- working_set: [],
175
- completed: [],
176
- });
177
- const result = await handleSessionTool("neuron_session_list", {});
178
- expect(result.sessions[0]).toHaveProperty("recipe_slug", "instagram");
179
- });
180
- });
181
- describe("neuron_session_delete", () => {
182
- it("deletes session file", async () => {
183
- await handleSessionTool("neuron_session_save", {
184
- session_id: "delete-me",
185
- cursor: 0,
186
- working_set: [],
187
- completed: [],
188
- });
189
- expect(fs.existsSync(sessionPath("delete-me"))).toBe(true);
190
- const result = await handleSessionTool("neuron_session_delete", {
191
- session_id: "delete-me",
192
- });
193
- expect(result).toEqual({
194
- status: "deleted",
195
- session_id: "delete-me",
196
- });
197
- expect(fs.existsSync(sessionPath("delete-me"))).toBe(false);
198
- });
199
- it("returns not_found for nonexistent session", async () => {
200
- const result = await handleSessionTool("neuron_session_delete", {
201
- session_id: "nonexistent",
202
- });
203
- expect(result).toEqual({
204
- status: "not_found",
205
- session_id: "nonexistent",
206
- });
207
- });
208
- });
209
- describe("neuron_session_checkpoint", () => {
210
- it("increments cursor and moves item to completed", async () => {
211
- await handleSessionTool("neuron_session_save", {
212
- session_id: "checkpoint-test",
213
- cursor: 0,
214
- working_set: ["a", "b", "c"],
215
- completed: [],
216
- });
217
- const result = await handleSessionTool("neuron_session_checkpoint", {
218
- session_id: "checkpoint-test",
219
- completed_item: { user: "a", status: "done" },
220
- });
221
- expect(result).toMatchObject({
222
- status: "checkpoint_saved",
223
- session_id: "checkpoint-test",
224
- cursor: 1,
225
- completed_count: 1,
226
- checkpoints: 1,
227
- });
228
- const loaded = await handleSessionTool("neuron_session_load", {
229
- session_id: "checkpoint-test",
230
- });
231
- expect(loaded.cursor).toBe(1);
232
- expect(loaded.completed).toHaveLength(1);
233
- expect(loaded.completed[0]).toEqual({ user: "a", status: "done" });
234
- expect(loaded.checkpoints).toBe(1);
235
- });
236
- it("tracks multiple checkpoints", async () => {
237
- await handleSessionTool("neuron_session_save", {
238
- session_id: "multi-checkpoint",
239
- cursor: 0,
240
- working_set: ["a", "b", "c"],
241
- completed: [],
242
- });
243
- await handleSessionTool("neuron_session_checkpoint", {
244
- session_id: "multi-checkpoint",
245
- completed_item: "a",
246
- });
247
- await handleSessionTool("neuron_session_checkpoint", {
248
- session_id: "multi-checkpoint",
249
- completed_item: "b",
250
- });
251
- await handleSessionTool("neuron_session_checkpoint", {
252
- session_id: "multi-checkpoint",
253
- completed_item: "c",
254
- });
255
- const loaded = await handleSessionTool("neuron_session_load", {
256
- session_id: "multi-checkpoint",
257
- });
258
- expect(loaded.cursor).toBe(3);
259
- expect(loaded.completed).toEqual(["a", "b", "c"]);
260
- expect(loaded.checkpoints).toBe(3);
261
- });
262
- it("throws error for nonexistent session", async () => {
263
- await expect(handleSessionTool("neuron_session_checkpoint", {
264
- session_id: "nonexistent",
265
- completed_item: "test",
266
- })).rejects.toThrow(/not found/);
267
- });
268
- it("updates timestamp on checkpoint", async () => {
269
- await handleSessionTool("neuron_session_save", {
270
- session_id: "timestamp-checkpoint",
271
- cursor: 0,
272
- working_set: ["a"],
273
- completed: [],
274
- });
275
- const before = await handleSessionTool("neuron_session_load", {
276
- session_id: "timestamp-checkpoint",
277
- });
278
- await new Promise((resolve) => setTimeout(resolve, 10));
279
- await handleSessionTool("neuron_session_checkpoint", {
280
- session_id: "timestamp-checkpoint",
281
- completed_item: "a",
282
- });
283
- const after = await handleSessionTool("neuron_session_load", {
284
- session_id: "timestamp-checkpoint",
285
- });
286
- expect(new Date(after.updated_at).getTime()).toBeGreaterThan(new Date(before.updated_at).getTime());
287
- });
288
- it("handles non-numeric cursors gracefully", async () => {
289
- await handleSessionTool("neuron_session_save", {
290
- session_id: "string-cursor",
291
- cursor: "page-1",
292
- working_set: ["a", "b"],
293
- completed: [],
294
- });
295
- await handleSessionTool("neuron_session_checkpoint", {
296
- session_id: "string-cursor",
297
- completed_item: "a",
298
- });
299
- const loaded = await handleSessionTool("neuron_session_load", {
300
- session_id: "string-cursor",
301
- });
302
- expect(loaded.cursor).toBe("page-1");
303
- expect(loaded.completed).toEqual(["a"]);
304
- });
305
- });
306
- describe("Session resumption flow", () => {
307
- it("supports typical scraping workflow", async () => {
308
- const sessionId = "instagram-scrape-test";
309
- const users = ["user1", "user2", "user3", "user4", "user5"];
310
- await handleSessionTool("neuron_session_save", {
311
- session_id: sessionId,
312
- cursor: 0,
313
- working_set: users,
314
- completed: [],
315
- progress: { total: users.length, done: 0 },
316
- metadata: { target: "@testaccount" },
317
- });
318
- for (let i = 0; i < 3; i++) {
319
- await handleSessionTool("neuron_session_checkpoint", {
320
- session_id: sessionId,
321
- completed_item: { username: users[i], scraped_at: new Date().toISOString() },
322
- });
323
- }
324
- const state = await handleSessionTool("neuron_session_load", {
325
- session_id: sessionId,
326
- });
327
- expect(state.cursor).toBe(3);
328
- expect(state.completed).toHaveLength(3);
329
- expect(state.working_set).toHaveLength(5);
330
- expect(state.checkpoints).toBe(3);
331
- const remaining = state.working_set.slice(state.cursor);
332
- expect(remaining).toEqual(["user4", "user5"]);
333
- });
334
- });
335
- //# sourceMappingURL=session-state.test.js.map