@pi-unipi/subagents 0.2.2 → 0.2.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 (64) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/badge-generation.test.ts +299 -0
  3. package/src/index.ts +34 -9
  4. package/src/types.ts +11 -0
  5. package/dist/__tests__/config.test.d.ts +0 -11
  6. package/dist/__tests__/config.test.d.ts.map +0 -1
  7. package/dist/__tests__/config.test.js +0 -196
  8. package/dist/__tests__/config.test.js.map +0 -1
  9. package/dist/__tests__/esc-propagation.test.d.ts +0 -10
  10. package/dist/__tests__/esc-propagation.test.d.ts.map +0 -1
  11. package/dist/__tests__/esc-propagation.test.js +0 -140
  12. package/dist/__tests__/esc-propagation.test.js.map +0 -1
  13. package/dist/__tests__/file-lock.test.d.ts +0 -12
  14. package/dist/__tests__/file-lock.test.d.ts.map +0 -1
  15. package/dist/__tests__/file-lock.test.js +0 -187
  16. package/dist/__tests__/file-lock.test.js.map +0 -1
  17. package/dist/__tests__/workflow-integration.test.d.ts +0 -12
  18. package/dist/__tests__/workflow-integration.test.d.ts.map +0 -1
  19. package/dist/__tests__/workflow-integration.test.js +0 -261
  20. package/dist/__tests__/workflow-integration.test.js.map +0 -1
  21. package/dist/agent-manager.d.ts +0 -75
  22. package/dist/agent-manager.d.ts.map +0 -1
  23. package/dist/agent-manager.js +0 -268
  24. package/dist/agent-manager.js.map +0 -1
  25. package/dist/agent-runner.d.ts +0 -51
  26. package/dist/agent-runner.d.ts.map +0 -1
  27. package/dist/agent-runner.js +0 -254
  28. package/dist/agent-runner.js.map +0 -1
  29. package/dist/config.d.ts +0 -24
  30. package/dist/config.d.ts.map +0 -1
  31. package/dist/config.js +0 -132
  32. package/dist/config.js.map +0 -1
  33. package/dist/conversation-viewer.d.ts +0 -40
  34. package/dist/conversation-viewer.d.ts.map +0 -1
  35. package/dist/conversation-viewer.js +0 -276
  36. package/dist/conversation-viewer.js.map +0 -1
  37. package/dist/custom-agents.d.ts +0 -14
  38. package/dist/custom-agents.d.ts.map +0 -1
  39. package/dist/custom-agents.js +0 -106
  40. package/dist/custom-agents.js.map +0 -1
  41. package/dist/file-lock.d.ts +0 -42
  42. package/dist/file-lock.d.ts.map +0 -1
  43. package/dist/file-lock.js +0 -91
  44. package/dist/file-lock.js.map +0 -1
  45. package/dist/index.d.ts +0 -10
  46. package/dist/index.d.ts.map +0 -1
  47. package/dist/index.js +0 -653
  48. package/dist/index.js.map +0 -1
  49. package/dist/model-resolver.d.ts +0 -19
  50. package/dist/model-resolver.d.ts.map +0 -1
  51. package/dist/model-resolver.js +0 -61
  52. package/dist/model-resolver.js.map +0 -1
  53. package/dist/prompts.d.ts +0 -13
  54. package/dist/prompts.d.ts.map +0 -1
  55. package/dist/prompts.js +0 -31
  56. package/dist/prompts.js.map +0 -1
  57. package/dist/types.d.ts +0 -96
  58. package/dist/types.d.ts.map +0 -1
  59. package/dist/types.js +0 -36
  60. package/dist/types.js.map +0 -1
  61. package/dist/widget.d.ts +0 -55
  62. package/dist/widget.d.ts.map +0 -1
  63. package/dist/widget.js +0 -404
  64. package/dist/widget.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/subagents",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Subagents for UniPi — parallel execution, file locking, workflow integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Test: Badge Generation Flow
3
+ *
4
+ * Tests the full badge name generation flow to identify and verify
5
+ * fixes for "Generating session name..." getting stuck.
6
+ *
7
+ * BUG 1 — Tool mismatch:
8
+ * Background agent was told to "Call the set_session_name tool" but the tool
9
+ * doesn't exist in the agent's session (only builtin tools available).
10
+ * FIX: Changed prompt to output title directly, parse in onComplete callback.
11
+ *
12
+ * BUG 2 — Wrong event bus:
13
+ * Cross-module events emitted via pi.events.emit() but listeners used pi.on()
14
+ * (extension lifecycle events) — completely different event bus.
15
+ * FIX: Changed all cross-module listeners to pi.events.on().
16
+ */
17
+
18
+ import { describe, it } from "node:test";
19
+ import assert from "node:assert/strict";
20
+ import { readFileSync, existsSync } from "node:fs";
21
+ import { join } from "node:path";
22
+
23
+ const ROOT = join(import.meta.dirname, "../../../..");
24
+
25
+ // ─── Helpers ────────────────────────────────────────────────────────
26
+
27
+ function readSource(relativePath: string): string {
28
+ const fullPath = join(ROOT, relativePath);
29
+ if (!existsSync(fullPath)) throw new Error(`File not found: ${fullPath}`);
30
+ return readFileSync(fullPath, "utf-8");
31
+ }
32
+
33
+ // ─── Test: Tool availability in spawned agent ──────────────────────
34
+
35
+ describe("Badge generation — tool availability", () => {
36
+ it("agent-runner uses only builtin tools, NOT extension-registered tools", () => {
37
+ const src = readSource("packages/subagents/src/agent-runner.ts");
38
+
39
+ const builtinMatch = src.match(
40
+ /const BUILTIN_TOOL_NAMES\s*=\s*(\[.*?\])/s,
41
+ );
42
+ assert.ok(builtinMatch, "BUILTIN_TOOL_NAMES should be defined");
43
+
44
+ const builtinTools: string[] = eval(builtinMatch[1]);
45
+ assert.deepStrictEqual(builtinTools, [
46
+ "read", "bash", "edit", "write", "grep", "find", "ls",
47
+ ]);
48
+ });
49
+
50
+ it("set_session_name is NOT in the agent's tool list", () => {
51
+ const src = readSource("packages/subagents/src/agent-runner.ts");
52
+ const builtinMatch = src.match(
53
+ /const BUILTIN_TOOL_NAMES\s*=\s*(\[.*?\])/s,
54
+ );
55
+ const tools: string[] = eval(builtinMatch![1]);
56
+ assert.ok(!tools.includes("set_session_name"));
57
+ });
58
+ });
59
+
60
+ // ─── Test: Prompt no longer references non-existent tool ───────────
61
+
62
+ describe("Badge generation — prompt fix", () => {
63
+ it("prompt includes conversation context inline", () => {
64
+ const src = readSource("packages/subagents/src/index.ts");
65
+
66
+ assert.ok(
67
+ src.includes("Conversation:"),
68
+ "Prompt should include conversation context inline",
69
+ );
70
+
71
+ assert.ok(
72
+ src.includes("Reply with ONLY the title"),
73
+ "Prompt should ask agent to reply with only the title",
74
+ );
75
+
76
+ assert.ok(
77
+ !src.includes("Call the set_session_name tool"),
78
+ "Prompt should NOT tell agent to call set_session_name",
79
+ );
80
+ });
81
+ });
82
+
83
+ // ─── Test: onComplete extracts name from result ────────────────────
84
+
85
+ describe("Badge generation — onComplete callback", () => {
86
+ it("onComplete extracts name from agent result and calls pi.setSessionName", () => {
87
+ const src = readSource("packages/subagents/src/index.ts");
88
+
89
+ assert.ok(
90
+ src.includes('record.description === "Generate session name"'),
91
+ "Should detect badge generation agents by description",
92
+ );
93
+
94
+ assert.ok(
95
+ src.includes("pi.setSessionName(name)"),
96
+ "Should call pi.setSessionName with extracted name",
97
+ );
98
+ });
99
+ });
100
+
101
+ // ─── Test: Agent configuration ────────────────────────────────────
102
+
103
+ describe("Badge generation — agent configuration", () => {
104
+ it("badge generation uses 'name-gen' agent type (not 'explore')", () => {
105
+ const src = readSource("packages/subagents/src/index.ts");
106
+
107
+ assert.ok(
108
+ src.includes('manager.spawn(pi, sessionCtx, "name-gen", prompt'),
109
+ "Badge generation should spawn a 'name-gen' agent",
110
+ );
111
+
112
+ assert.ok(
113
+ !src.includes('manager.spawn(pi, sessionCtx, "explore"'),
114
+ "Should NOT use 'explore' — that type can be overridden by user custom agents",
115
+ );
116
+ });
117
+
118
+ it("'name-gen' type is defined in BUILTIN_CONFIGS with empty tools", () => {
119
+ const src = readSource("packages/subagents/src/types.ts");
120
+
121
+ assert.ok(
122
+ src.includes('"name-gen"'),
123
+ "types.ts should define name-gen agent config",
124
+ );
125
+
126
+ assert.ok(
127
+ src.includes('builtinToolNames: []'),
128
+ "name-gen should have empty tool list",
129
+ );
130
+ });
131
+
132
+ it("background agent is isolated (no extensions, no skills, minimal system prompt)", () => {
133
+ const src = readSource("packages/subagents/src/index.ts");
134
+
135
+ assert.ok(
136
+ src.includes("isolated: true"),
137
+ "Should spawn with isolated: true to avoid loading extensions/skills",
138
+ );
139
+ });
140
+
141
+ it("background agent maxTurns is 1 (single response)", () => {
142
+ const src = readSource("packages/subagents/src/index.ts");
143
+
144
+ assert.ok(
145
+ src.includes("maxTurns: 1"),
146
+ "Badge generation agent should have maxTurns: 1",
147
+ );
148
+ });
149
+ });
150
+
151
+ // ─── Test: Cross-module event bus — the critical fix ───────────────
152
+
153
+ describe("Badge generation — event bus (CRITICAL FIX)", () => {
154
+ it("emitEvent uses pi.events.emit (not pi.on)", () => {
155
+ const src = readSource("packages/core/utils.ts");
156
+
157
+ assert.ok(
158
+ src.includes("pi.events.emit(eventName, payload)"),
159
+ "emitEvent should use pi.events.emit()",
160
+ );
161
+ });
162
+
163
+ it("subagents listens via pi.events.on (NOT pi.on)", () => {
164
+ const src = readSource("packages/subagents/src/index.ts");
165
+
166
+ // Must use pi.events.on for cross-module events
167
+ assert.ok(
168
+ src.includes("pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST"),
169
+ "Subagents should listen via pi.events.on",
170
+ );
171
+
172
+ // Should NOT use pi.on for custom events
173
+ const piOnMatch = src.match(/pi\.on\(UNIPI_EVENTS\.BADGE_GENERATE_REQUEST/g);
174
+ assert.ok(!piOnMatch, "Should NOT use pi.on() for cross-module events");
175
+ });
176
+
177
+ it("utility BADGE_GENERATE_REQUEST listener is removed (input handler already shows overlay)", () => {
178
+ const src = readSource("packages/utility/src/index.ts");
179
+
180
+ // Should NOT have a separate BADGE_GENERATE_REQUEST listener
181
+ // The input handler already shows the overlay and emits the event
182
+ assert.ok(
183
+ !src.includes("pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST"),
184
+ "Utility should NOT have a separate BADGE_GENERATE_REQUEST listener",
185
+ );
186
+ });
187
+
188
+ it("workflow listens for MODULE_READY via pi.events.on (NOT pi.on)", () => {
189
+ const src = readSource("packages/workflow/index.ts");
190
+
191
+ assert.ok(
192
+ src.includes("pi.events.on(UNIPI_EVENTS.MODULE_READY"),
193
+ "Workflow should listen via pi.events.on",
194
+ );
195
+
196
+ const piOnMatch = src.match(/pi\.on\(UNIPI_EVENTS\.MODULE_READY/g);
197
+ assert.ok(!piOnMatch, "Should NOT use pi.on() for cross-module events");
198
+ });
199
+
200
+ it("pi.on() is ONLY used for known lifecycle events", () => {
201
+ const subagentsSrc = readSource("packages/subagents/src/index.ts");
202
+ const utilitySrc = readSource("packages/utility/src/index.ts");
203
+
204
+ // These are valid lifecycle events that should use pi.on()
205
+ const validLifecycleEvents = [
206
+ "session_start", "session_shutdown", "input",
207
+ "tool_call", "tool_execution_start",
208
+ ];
209
+
210
+ // Check that pi.on() is only used with lifecycle events
211
+ const piOnPattern = /pi\.on\("([^"]+)"/g;
212
+ let match;
213
+ while ((match = piOnPattern.exec(subagentsSrc)) !== null) {
214
+ assert.ok(
215
+ validLifecycleEvents.includes(match[1]),
216
+ `subagents: pi.on("${match[1]}") should be a lifecycle event, use pi.events.on() for custom events`,
217
+ );
218
+ }
219
+ while ((match = piOnPattern.exec(utilitySrc)) !== null) {
220
+ assert.ok(
221
+ validLifecycleEvents.includes(match[1]),
222
+ `utility: pi.on("${match[1]}") should be a lifecycle event`,
223
+ );
224
+ }
225
+ });
226
+ });
227
+
228
+ // ─── Test: Event flow ──────────────────────────────────────────────
229
+
230
+ describe("Badge generation — event flow", () => {
231
+ it("utility emits BADGE_GENERATE_REQUEST on first input", () => {
232
+ const src = readSource("packages/utility/src/index.ts");
233
+
234
+ assert.ok(src.includes("BADGE_GENERATE_REQUEST"));
235
+ assert.ok(src.includes('source: "input-hook"'));
236
+ });
237
+
238
+ it("BADGE_GENERATE_REQUEST event is defined in core", () => {
239
+ const src = readSource("packages/core/events.ts");
240
+ assert.ok(src.includes("BADGE_GENERATE_REQUEST"));
241
+ });
242
+ });
243
+
244
+ // ─── Test: Model resolution ────────────────────────────────────────
245
+
246
+ describe("Badge generation — model resolution", () => {
247
+ it("reads generationModel from badge.json instead of hardcoding", () => {
248
+ const src = readSource("packages/subagents/src/index.ts");
249
+
250
+ assert.ok(!src.includes('"openai/gpt-oss-20b"'));
251
+ assert.ok(src.includes(".unipi/config/badge.json"));
252
+ assert.ok(src.includes("parsed.generationModel"));
253
+ });
254
+ });
255
+
256
+ // ─── Summary ───────────────────────────────────────────────────────
257
+
258
+ describe("Badge generation — ROOT CAUSE SUMMARY", () => {
259
+ it("BUG 1 FIXED: prompt no longer references non-existent tool", () => {
260
+ const src = readSource("packages/subagents/src/index.ts");
261
+
262
+ assert.ok(!src.includes("Call the set_session_name tool"),
263
+ "FIXED: prompt no longer tells agent to call set_session_name");
264
+ assert.ok(src.includes("Reply with ONLY the title"),
265
+ "FIXED: prompt asks agent to reply with only the title");
266
+ });
267
+
268
+ it("BUG 1 FIXED: onComplete extracts name and sets it directly", () => {
269
+ const src = readSource("packages/subagents/src/index.ts");
270
+
271
+ assert.ok(src.includes('record.description === "Generate session name"'),
272
+ "FIXED: onComplete detects badge generation agents");
273
+ assert.ok(src.includes("pi.setSessionName(name)"),
274
+ "FIXED: onComplete calls pi.setSessionName directly");
275
+ });
276
+
277
+ it("BUG 2 FIXED: cross-module events use pi.events.on, not pi.on", () => {
278
+ const subagentsSrc = readSource("packages/subagents/src/index.ts");
279
+ const utilitySrc = readSource("packages/utility/src/index.ts");
280
+ const workflowSrc = readSource("packages/workflow/index.ts");
281
+
282
+ // Subagents: correct event bus
283
+ assert.ok(
284
+ subagentsSrc.includes("pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST"),
285
+ "subagents: must use pi.events.on for BADGE_GENERATE_REQUEST",
286
+ );
287
+
288
+ // Utility: no duplicate listener (input handler already handles it)
289
+ assert.ok(
290
+ !utilitySrc.includes("pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST"),
291
+ "utility: no duplicate BADGE_GENERATE_REQUEST listener", );
292
+
293
+ // Workflow: correct event bus
294
+ assert.ok(
295
+ workflowSrc.includes("pi.events.on(UNIPI_EVENTS.MODULE_READY"),
296
+ "workflow: must use pi.events.on for MODULE_READY",
297
+ );
298
+ });
299
+ });
package/src/index.ts CHANGED
@@ -148,6 +148,19 @@ export default function (pi: ExtensionAPI) {
148
148
  // Build notification details
149
149
  const details = buildNotificationDetails(record, agentActivity.get(record.id));
150
150
 
151
+ // Badge generation: extract name from agent result and set directly.
152
+ // Mark resultConsumed BEFORE the notification check so the main agent
153
+ // never sees this subagent.
154
+ if (record.description === "Generate session name" && record.result && record.status === "completed") {
155
+ const name = record.result.split("\n")[0]?.trim().slice(0, 50) ?? "";
156
+ if (name && !name.startsWith("Error") && !name.includes("error")) {
157
+ try {
158
+ pi.setSessionName(name);
159
+ } catch { /* best effort */ }
160
+ }
161
+ record.resultConsumed = true;
162
+ }
163
+
151
164
  // Send styled notification via message renderer
152
165
  const status = getStatusLabel(record.status, record.error);
153
166
  const durationMs = record.completedAt ? record.completedAt - record.startedAt : 0;
@@ -339,20 +352,31 @@ export default function (pi: ExtensionAPI) {
339
352
  });
340
353
 
341
354
  // Listen for badge generation requests — spawn background agent
342
- pi.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST as any, async (event: any) => {
355
+ pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST, async (event: any) => {
343
356
  if (!sessionCtx) return;
344
357
 
345
358
  const summary = event?.conversationSummary ?? "";
346
359
  const prompt = summary
347
- ? `Generate a concise session title (MAX 5 WORDS) for this conversation:\n\n"${summary}"\n\nCall the set_session_name tool with the name. Do not explain.`
348
- : `Generate a concise session title (MAX 5 WORDS) for the current session. Call the set_session_name tool. Do not explain.`;
349
-
350
- // Try with openai/gpt-oss-20b, fallback to inherit
351
- const modelInput = "openai/gpt-oss-20b";
360
+ ? `Based on this conversation, generate a concise session title (MAX 5 WORDS). Reply with ONLY the title. No quotes, no explanation, no punctuation.\n\nConversation:\n${summary}`
361
+ : `Generate a concise session title (MAX 5 WORDS) for this session. Reply with ONLY the title. No quotes, no explanation, no punctuation.`;
362
+
363
+ // Try with configured model, fallback to inherit
364
+ let modelInput: string | undefined = undefined;
365
+ try {
366
+ const fs = await import("node:fs");
367
+ const path = await import("node:path");
368
+ const configPath = path.resolve(process.cwd(), ".unipi/config/badge.json");
369
+ if (fs.existsSync(configPath)) {
370
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
371
+ if (typeof parsed.generationModel === "string" && parsed.generationModel !== "inherit") {
372
+ modelInput = parsed.generationModel;
373
+ }
374
+ }
375
+ } catch { /* ignore — inherit parent model */ }
352
376
  let resolvedModel: any = undefined;
353
377
 
354
378
  // Check if model is available
355
- if (sessionCtx.modelRegistry) {
379
+ if (modelInput && sessionCtx.modelRegistry) {
356
380
  const { resolveModel } = await import("./model-resolver.js");
357
381
  const result = resolveModel(modelInput, sessionCtx.modelRegistry);
358
382
  if (typeof result !== "string") {
@@ -361,11 +385,12 @@ export default function (pi: ExtensionAPI) {
361
385
  // If result is a string (error), resolvedModel stays undefined → inherit parent
362
386
  }
363
387
 
364
- manager.spawn(pi, sessionCtx, "explore", prompt, {
388
+ manager.spawn(pi, sessionCtx, "name-gen", prompt, {
365
389
  description: "Generate session name",
366
390
  model: resolvedModel,
367
391
  isBackground: true,
368
- maxTurns: 3,
392
+ isolated: true,
393
+ maxTurns: 1,
369
394
  });
370
395
  });
371
396
 
package/src/types.ts CHANGED
@@ -44,6 +44,17 @@ export const BUILTIN_CONFIGS: Record<string, AgentConfig> = {
44
44
  promptMode: "append",
45
45
  source: "builtin",
46
46
  },
47
+ "name-gen": {
48
+ name: "name-gen",
49
+ displayName: "Name Generator",
50
+ description: "Minimal agent for generating session names from conversation context.",
51
+ builtinToolNames: [],
52
+ extensions: false,
53
+ skills: false,
54
+ systemPrompt: "You are a session name generator. Generate concise titles from conversation context. Reply with ONLY the title.",
55
+ promptMode: "replace",
56
+ source: "builtin",
57
+ },
47
58
  } as const;
48
59
 
49
60
  /** Memory scope for persistent agent memory. */
@@ -1,11 +0,0 @@
1
- /**
2
- * Test: Config auto-generation and corruption recovery
3
- *
4
- * Verifies:
5
- * - Missing config → auto-generated with defaults
6
- * - Corrupted config → renamed to .json.bak, fresh generated
7
- * - Workspace config overrides global config
8
- * - Atomic writes prevent corruption
9
- */
10
- export {};
11
- //# sourceMappingURL=config.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -1,196 +0,0 @@
1
- /**
2
- * Test: Config auto-generation and corruption recovery
3
- *
4
- * Verifies:
5
- * - Missing config → auto-generated with defaults
6
- * - Corrupted config → renamed to .json.bak, fresh generated
7
- * - Workspace config overrides global config
8
- * - Atomic writes prevent corruption
9
- */
10
- import { describe, it, beforeEach, afterEach } from "node:test";
11
- import assert from "node:assert/strict";
12
- import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, renameSync } from "node:fs";
13
- import { join } from "node:path";
14
- import { tmpdir } from "node:os";
15
- // Inline config implementation for testing
16
- const DEFAULT_CONFIG = {
17
- maxConcurrent: 4,
18
- enabled: true,
19
- types: {
20
- explore: { enabled: true },
21
- work: { enabled: true },
22
- },
23
- };
24
- function loadConfigFromPath(filePath) {
25
- if (!existsSync(filePath))
26
- return null;
27
- try {
28
- const content = readFileSync(filePath, "utf-8");
29
- const parsed = JSON.parse(content);
30
- if (typeof parsed !== "object" || parsed === null)
31
- return null;
32
- return parsed;
33
- }
34
- catch {
35
- return null;
36
- }
37
- }
38
- function repairCorrupted(filePath) {
39
- const backupPath = filePath + ".bak";
40
- try {
41
- renameSync(filePath, backupPath);
42
- }
43
- catch {
44
- // If rename fails, just overwrite
45
- }
46
- writeConfigAtomic(filePath, DEFAULT_CONFIG);
47
- return DEFAULT_CONFIG;
48
- }
49
- function writeConfigAtomic(filePath, config) {
50
- const tmpPath = filePath + ".tmp";
51
- writeFileSync(tmpPath, JSON.stringify(config, null, 2), "utf-8");
52
- renameSync(tmpPath, filePath);
53
- }
54
- function initConfig(globalDir, workspaceDir) {
55
- const globalPath = join(globalDir, "subagents.json");
56
- const workspacePath = join(workspaceDir, "subagents.json");
57
- // Load or create global config
58
- let globalConfig = loadConfigFromPath(globalPath);
59
- if (globalConfig === null) {
60
- globalConfig = repairCorrupted(globalPath);
61
- }
62
- // Load workspace override if exists
63
- const workspaceConfig = loadConfigFromPath(workspacePath);
64
- if (workspaceConfig) {
65
- // Merge: workspace overrides global on any field present
66
- return {
67
- ...globalConfig,
68
- ...workspaceConfig,
69
- types: {
70
- ...globalConfig.types,
71
- ...workspaceConfig.types,
72
- },
73
- };
74
- }
75
- return globalConfig;
76
- }
77
- describe("Config Management", () => {
78
- let testDir;
79
- let globalDir;
80
- let workspaceDir;
81
- beforeEach(() => {
82
- // Create temp directories for testing
83
- testDir = join(tmpdir(), `subagents-test-${Date.now()}`);
84
- globalDir = join(testDir, "global");
85
- workspaceDir = join(testDir, "workspace");
86
- mkdirSync(globalDir, { recursive: true });
87
- mkdirSync(workspaceDir, { recursive: true });
88
- });
89
- afterEach(() => {
90
- // Cleanup
91
- rmSync(testDir, { recursive: true, force: true });
92
- });
93
- describe("Missing config", () => {
94
- it("should auto-generate with defaults when no config exists", () => {
95
- const config = initConfig(globalDir, workspaceDir);
96
- assert.deepEqual(config, DEFAULT_CONFIG);
97
- assert.equal(existsSync(join(globalDir, "subagents.json")), true);
98
- // Verify the generated file
99
- const content = readFileSync(join(globalDir, "subagents.json"), "utf-8");
100
- const parsed = JSON.parse(content);
101
- assert.deepEqual(parsed, DEFAULT_CONFIG);
102
- });
103
- it("should create global config even if workspace exists", () => {
104
- // Write workspace config only
105
- const workspaceConfig = { maxConcurrent: 8, enabled: true, types: {} };
106
- writeFileSync(join(workspaceDir, "subagents.json"), JSON.stringify(workspaceConfig));
107
- const config = initConfig(globalDir, workspaceDir);
108
- // Global should be created
109
- assert.equal(existsSync(join(globalDir, "subagents.json")), true);
110
- // Config should merge
111
- assert.equal(config.maxConcurrent, 8); // workspace overrides
112
- });
113
- });
114
- describe("Corrupted config", () => {
115
- it("should backup corrupted config and generate fresh", () => {
116
- const configPath = join(globalDir, "subagents.json");
117
- const backupPath = configPath + ".bak";
118
- // Write corrupted JSON
119
- writeFileSync(configPath, "{ invalid json !!!");
120
- const config = initConfig(globalDir, workspaceDir);
121
- // Should have created backup
122
- assert.equal(existsSync(backupPath), true, "Backup should exist");
123
- // Backup should contain corrupted content
124
- assert.equal(readFileSync(backupPath, "utf-8"), "{ invalid json !!!");
125
- // New config should be defaults
126
- assert.deepEqual(config, DEFAULT_CONFIG);
127
- // New file should be valid JSON
128
- const content = readFileSync(configPath, "utf-8");
129
- assert.deepEqual(JSON.parse(content), DEFAULT_CONFIG);
130
- });
131
- it("should handle completely empty file", () => {
132
- const configPath = join(globalDir, "subagents.json");
133
- writeFileSync(configPath, "");
134
- const config = initConfig(globalDir, workspaceDir);
135
- assert.deepEqual(config, DEFAULT_CONFIG);
136
- assert.equal(existsSync(configPath + ".bak"), true);
137
- });
138
- it("should handle non-object JSON", () => {
139
- const configPath = join(globalDir, "subagents.json");
140
- writeFileSync(configPath, '"just a string"');
141
- const config = initConfig(globalDir, workspaceDir);
142
- // JSON.parse succeeds but returns string, not object
143
- // loadConfigFromPath checks typeof === "object"
144
- assert.deepEqual(config, DEFAULT_CONFIG);
145
- });
146
- });
147
- describe("Workspace override", () => {
148
- it("should merge workspace config with global", () => {
149
- const globalConfig = { maxConcurrent: 4, enabled: true, types: { explore: { enabled: true } } };
150
- const workspaceConfig = { maxConcurrent: 8, types: { work: { enabled: false } } };
151
- writeFileSync(join(globalDir, "subagents.json"), JSON.stringify(globalConfig));
152
- writeFileSync(join(workspaceDir, "subagents.json"), JSON.stringify(workspaceConfig));
153
- const config = initConfig(globalDir, workspaceDir);
154
- assert.equal(config.maxConcurrent, 8); // workspace overrides
155
- assert.equal(config.enabled, true); // global preserved
156
- assert.deepEqual(config.types.explore, { enabled: true }); // global preserved
157
- assert.deepEqual(config.types.work, { enabled: false }); // workspace added
158
- });
159
- it("should override specific fields only", () => {
160
- const globalConfig = {
161
- maxConcurrent: 4,
162
- enabled: true,
163
- types: { explore: { enabled: true }, work: { enabled: true } },
164
- };
165
- const workspaceConfig = { enabled: false };
166
- writeFileSync(join(globalDir, "subagents.json"), JSON.stringify(globalConfig));
167
- writeFileSync(join(workspaceDir, "subagents.json"), JSON.stringify(workspaceConfig));
168
- const config = initConfig(globalDir, workspaceDir);
169
- assert.equal(config.maxConcurrent, 4); // global preserved
170
- assert.equal(config.enabled, false); // workspace overrides
171
- assert.deepEqual(config.types, { explore: { enabled: true }, work: { enabled: true } }); // global preserved
172
- });
173
- it("should handle empty workspace config", () => {
174
- const globalConfig = { maxConcurrent: 4, enabled: true, types: {} };
175
- writeFileSync(join(globalDir, "subagents.json"), JSON.stringify(globalConfig));
176
- writeFileSync(join(workspaceDir, "subagents.json"), "{}");
177
- const config = initConfig(globalDir, workspaceDir);
178
- assert.deepEqual(config, globalConfig);
179
- });
180
- });
181
- describe("Atomic writes", () => {
182
- it("should write config atomically", () => {
183
- const configPath = join(globalDir, "subagents.json");
184
- const config = { maxConcurrent: 8, enabled: false, types: {} };
185
- writeConfigAtomic(configPath, config);
186
- // Should have main file
187
- assert.equal(existsSync(configPath), true);
188
- // Should not have temp file
189
- assert.equal(existsSync(configPath + ".tmp"), false);
190
- // Content should be valid
191
- const content = readFileSync(configPath, "utf-8");
192
- assert.deepEqual(JSON.parse(content), config);
193
- });
194
- });
195
- });
196
- //# sourceMappingURL=config.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"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"}
@@ -1,10 +0,0 @@
1
- /**
2
- * Test: ESC propagation — all children abort on parent ESC
3
- *
4
- * Verifies:
5
- * - forwardAbortSignal wires parent signal to child session
6
- * - abortAll stops all running agents
7
- * - All agents stop within reasonable time
8
- */
9
- export {};
10
- //# sourceMappingURL=esc-propagation.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"esc-propagation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/esc-propagation.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}