@polderlabs/bizar-plugin 0.5.4

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +448 -0
  3. package/bun.lock +88 -0
  4. package/index.ts +1113 -0
  5. package/package.json +42 -0
  6. package/scripts/check-forbidden-imports.sh +33 -0
  7. package/src/background-state.ts +463 -0
  8. package/src/background.ts +964 -0
  9. package/src/commands-impl.ts +369 -0
  10. package/src/commands.ts +880 -0
  11. package/src/event-stream.ts +574 -0
  12. package/src/fingerprint.ts +120 -0
  13. package/src/handoff.ts +79 -0
  14. package/src/http-client.ts +467 -0
  15. package/src/logger.ts +144 -0
  16. package/src/loop.ts +176 -0
  17. package/src/options.ts +421 -0
  18. package/src/plan-fs.ts +323 -0
  19. package/src/report.ts +178 -0
  20. package/src/research-prompt.ts +35 -0
  21. package/src/serve.ts +476 -0
  22. package/src/settings.ts +349 -0
  23. package/src/state.ts +298 -0
  24. package/src/tools/bg-collect.ts +104 -0
  25. package/src/tools/bg-get-comments.ts +239 -0
  26. package/src/tools/bg-kill.ts +87 -0
  27. package/src/tools/bg-spawn.ts +263 -0
  28. package/src/tools/bg-status.ts +99 -0
  29. package/src/tools/plan-action.ts +767 -0
  30. package/src/tools/wait-for-feedback.ts +402 -0
  31. package/tests/attach-handler-bug.test.ts +166 -0
  32. package/tests/background-state.test.ts +277 -0
  33. package/tests/background.test.ts +402 -0
  34. package/tests/block.test.ts +193 -0
  35. package/tests/canonical-key-order.test.ts +71 -0
  36. package/tests/commands-impl.test.ts +442 -0
  37. package/tests/commands.test.ts +548 -0
  38. package/tests/config.test.ts +122 -0
  39. package/tests/dispose.test.ts +336 -0
  40. package/tests/event-stream.test.ts +409 -0
  41. package/tests/event.test.ts +262 -0
  42. package/tests/fingerprint.test.ts +161 -0
  43. package/tests/http-client.test.ts +403 -0
  44. package/tests/init-helpers.test.ts +203 -0
  45. package/tests/integration/slash-command.test.ts +348 -0
  46. package/tests/integration/tool-routing.test.ts +314 -0
  47. package/tests/loop.test.ts +397 -0
  48. package/tests/options.test.ts +274 -0
  49. package/tests/serve.test.ts +335 -0
  50. package/tests/settings.test.ts +351 -0
  51. package/tests/stall-think.test.ts +749 -0
  52. package/tests/state.test.ts +275 -0
  53. package/tests/tools/bg-collect.test.ts +337 -0
  54. package/tests/tools/bg-get-comments.test.ts +485 -0
  55. package/tests/tools/bg-kill.test.ts +231 -0
  56. package/tests/tools/bg-spawn.test.ts +311 -0
  57. package/tests/tools/bg-status.test.ts +216 -0
  58. package/tests/tools/plan-action.test.ts +599 -0
  59. package/tests/tools/wait-for-feedback.test.ts +390 -0
  60. package/tsconfig.json +29 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * bizar_status tool tests.
3
+ *
4
+ * Tests: list all, single instance, filter, no instances,
5
+ * returns documented shape per §7.1. Read-only — any agent can call.
6
+ */
7
+
8
+ import { describe, it, expect } from "bun:test";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Fake BackgroundState for testing
12
+ // ---------------------------------------------------------------------------
13
+
14
+ interface BackgroundState {
15
+ instanceId: string;
16
+ agent: string;
17
+ status: "pending" | "running" | "done" | "failed" | "killed" | "timed_out";
18
+ startedAt: number;
19
+ toolCallCount: number;
20
+ promptPreview: string;
21
+ resultPreview?: string;
22
+ error?: string;
23
+ parentAgent: string;
24
+ parentInstanceId?: string;
25
+ durationMs?: number;
26
+ }
27
+
28
+ function makeBgState(overrides: Partial<BackgroundState> = {}): BackgroundState {
29
+ return {
30
+ instanceId: "bgr_test_01",
31
+ agent: "mimir",
32
+ status: "running",
33
+ startedAt: Date.now() - 60_000,
34
+ toolCallCount: 12,
35
+ promptPreview: "Do the research on X",
36
+ resultPreview: undefined,
37
+ error: undefined,
38
+ parentAgent: "odin",
39
+ parentInstanceId: undefined,
40
+ durationMs: 60_000,
41
+ ...overrides,
42
+ };
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Fake bizar_status implementation (read-only — any agent can call)
47
+ // ---------------------------------------------------------------------------
48
+
49
+ function bizar_status(
50
+ args: { instanceId?: string } | undefined,
51
+ _ctx: { agent: string },
52
+ instances: Map<string, BackgroundState>,
53
+ ): BackgroundState[] | BackgroundState | { error: string } {
54
+ if (!args || args.instanceId === undefined) {
55
+ // Return all
56
+ return [...instances.values()];
57
+ }
58
+
59
+ const inst = instances.get(args.instanceId);
60
+ if (!inst) {
61
+ return { error: `Instance ${args.instanceId} not found` };
62
+ }
63
+ return inst;
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // In-memory instance store for tests
68
+ // ---------------------------------------------------------------------------
69
+
70
+ const instances = new Map<string, BackgroundState>();
71
+ instances.set("bgr_01", makeBgState({ instanceId: "bgr_01", agent: "mimir", status: "running" }));
72
+ instances.set("bgr_02", makeBgState({ instanceId: "bgr_02", agent: "thor", status: "done", resultPreview: "Done!", completedAt: Date.now() }));
73
+ instances.set("bgr_03", makeBgState({ instanceId: "bgr_03", agent: "tyr", status: "failed", error: "Loop protection: 12 identical calls to read" }));
74
+
75
+ function makeCompletedState(overrides: Partial<BackgroundState> = {}): BackgroundState {
76
+ return {
77
+ instanceId: "bgr_completed",
78
+ agent: "mimir",
79
+ status: "done",
80
+ startedAt: Date.now() - 120_000,
81
+ toolCallCount: 5,
82
+ promptPreview: "Quick task",
83
+ resultPreview: "All done!",
84
+ error: undefined,
85
+ parentAgent: "odin",
86
+ parentInstanceId: undefined,
87
+ durationMs: 120_000,
88
+ ...overrides,
89
+ };
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // bizarre_status — list all
94
+ // ---------------------------------------------------------------------------
95
+
96
+ describe("bizar_status — list all", () => {
97
+ it("returns all instances when called with no args", () => {
98
+ const result = bizar_status(undefined, { agent: "odin" }, instances);
99
+ expect(Array.isArray(result)).toBe(true);
100
+ expect((result as BackgroundState[]).length).toBe(3);
101
+ });
102
+
103
+ it("includes all documented fields per instance (§7.1)", () => {
104
+ const all = bizar_status(undefined, { agent: "odin" }, instances) as BackgroundState[];
105
+ const first = all[0];
106
+ expect(first).toHaveProperty("instanceId");
107
+ expect(first).toHaveProperty("agent");
108
+ expect(first).toHaveProperty("status");
109
+ expect(first).toHaveProperty("startedAt");
110
+ expect(first).toHaveProperty("toolCallCount");
111
+ expect(first).toHaveProperty("promptPreview");
112
+ expect(first).toHaveProperty("resultPreview");
113
+ expect(first).toHaveProperty("error");
114
+ expect(first).toHaveProperty("parentAgent");
115
+ });
116
+
117
+ it("returns instances in any order (stable map iteration)", () => {
118
+ const all = bizar_status(undefined, { agent: "odin" }, instances) as BackgroundState[];
119
+ const ids = all.map((i) => i.instanceId);
120
+ expect(ids).toContain("bgr_01");
121
+ expect(ids).toContain("bgr_02");
122
+ expect(ids).toContain("bgr_03");
123
+ });
124
+
125
+ it("any agent can call bizar_status (read-only)", () => {
126
+ for (const agent of ["odin", "vor", "frigg", "mimir", "thor", "tyr", "heimdall"]) {
127
+ const result = bizar_status(undefined, { agent }, instances);
128
+ expect(Array.isArray(result)).toBe(true);
129
+ }
130
+ });
131
+ });
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // bizar_status — single instance
135
+ // ---------------------------------------------------------------------------
136
+
137
+ describe("bizar_status — single instance", () => {
138
+ it("returns the requested instance", () => {
139
+ const result = bizar_status({ instanceId: "bgr_02" }, { agent: "odin" }, instances);
140
+ expect((result as BackgroundState).instanceId).toBe("bgr_02");
141
+ expect((result as BackgroundState).agent).toBe("thor");
142
+ expect((result as BackgroundState).status).toBe("done");
143
+ });
144
+
145
+ it("returns error for unknown instanceId", () => {
146
+ const result = bizar_status({ instanceId: "bgr_unknown" }, { agent: "odin" }, instances);
147
+ expect(result).toHaveProperty("error");
148
+ expect((result as { error: string }).error).toContain("not found");
149
+ });
150
+ });
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // Status values
154
+ // ---------------------------------------------------------------------------
155
+
156
+ describe("bizar_status — status values", () => {
157
+ it("running instance shows running status", () => {
158
+ const result = bizar_status({ instanceId: "bgr_01" }, { agent: "odin" }, instances) as BackgroundState;
159
+ expect(result.status).toBe("running");
160
+ expect(result.resultPreview).toBeUndefined();
161
+ });
162
+
163
+ it("done instance shows resultPreview", () => {
164
+ const result = bizar_status({ instanceId: "bgr_02" }, { agent: "odin" }, instances) as BackgroundState;
165
+ expect(result.status).toBe("done");
166
+ expect(result.resultPreview).toBe("Done!");
167
+ });
168
+
169
+ it("failed instance shows error", () => {
170
+ const result = bizar_status({ instanceId: "bgr_03" }, { agent: "odin" }, instances) as BackgroundState;
171
+ expect(result.status).toBe("failed");
172
+ expect(result.error).toContain("Loop protection");
173
+ });
174
+ });
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // No instances
178
+ // ---------------------------------------------------------------------------
179
+
180
+ describe("bizar_status — no instances", () => {
181
+ it("returns empty array when no instances exist", () => {
182
+ const emptyMap = new Map<string, BackgroundState>();
183
+ const result = bizar_status(undefined, { agent: "odin" }, emptyMap);
184
+ expect(Array.isArray(result)).toBe(true);
185
+ expect((result as BackgroundState[]).length).toBe(0);
186
+ });
187
+
188
+ it("returns error for unknown instanceId when map is empty", () => {
189
+ const emptyMap = new Map<string, BackgroundState>();
190
+ const result = bizar_status({ instanceId: "bgr_anything" }, { agent: "odin" }, emptyMap);
191
+ expect(result).toHaveProperty("error");
192
+ });
193
+ });
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Duration calculation
197
+ // ---------------------------------------------------------------------------
198
+
199
+ describe("bizar_status — durationMs", () => {
200
+ it("durationMs is derived from startedAt and now (or completedAt)", () => {
201
+ // Test the concept: durationMs is computed from startedAt vs now/completedAt
202
+ // In a real instance, running.durationMs would be Date.now() - startedAt
203
+ // Here we just verify the field exists and is a number
204
+ const running = makeBgState({ startedAt: Date.now() - 30_000, status: "running" });
205
+ expect(typeof running.durationMs).toBe("number");
206
+ expect(running.durationMs).toBeGreaterThan(0);
207
+ });
208
+
209
+ it("durationMs uses completedAt when instance is done", () => {
210
+ // completedAt - startedAt gives the actual duration
211
+ const startedAt = Date.now() - 60_000;
212
+ const completedAt = Date.now() - 30_000;
213
+ const expectedDuration = completedAt - startedAt;
214
+ expect(expectedDuration).toBe(30_000);
215
+ });
216
+ });