bobs-workshop 0.3.3 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +199 -210
  3. package/bin/bobs-workshop.js +109 -0
  4. package/config/agents.json +27 -0
  5. package/dist/plugins/bobs-workshop.js +34 -0
  6. package/dist/tools/background-agent/cancel.d.ts +3 -0
  7. package/dist/tools/background-agent/cancel.d.ts.map +1 -0
  8. package/dist/tools/background-agent/cancel.js +52 -0
  9. package/dist/tools/background-agent/concurrency.d.ts +15 -0
  10. package/dist/tools/background-agent/concurrency.d.ts.map +1 -0
  11. package/dist/tools/background-agent/concurrency.js +61 -0
  12. package/dist/tools/background-agent/index.d.ts +8 -0
  13. package/dist/tools/background-agent/index.d.ts.map +1 -0
  14. package/dist/tools/background-agent/index.js +7 -0
  15. package/dist/tools/background-agent/launch.d.ts +6 -0
  16. package/dist/tools/background-agent/launch.d.ts.map +1 -0
  17. package/dist/tools/background-agent/launch.js +33 -0
  18. package/dist/tools/background-agent/list.d.ts +7 -0
  19. package/dist/tools/background-agent/list.d.ts.map +1 -0
  20. package/dist/tools/background-agent/list.js +40 -0
  21. package/dist/tools/background-agent/manager.d.ts +29 -0
  22. package/dist/tools/background-agent/manager.d.ts.map +1 -0
  23. package/dist/tools/background-agent/manager.js +388 -0
  24. package/dist/tools/background-agent/output.d.ts +3 -0
  25. package/dist/tools/background-agent/output.d.ts.map +1 -0
  26. package/dist/tools/background-agent/output.js +41 -0
  27. package/dist/tools/background-agent/types.d.ts +46 -0
  28. package/dist/tools/background-agent/types.d.ts.map +1 -0
  29. package/dist/tools/background-agent/types.js +1 -0
  30. package/dist/tools/index.d.ts +9 -0
  31. package/dist/tools/index.d.ts.map +1 -0
  32. package/dist/tools/index.js +8 -0
  33. package/dist/tools/manual/index.d.ts +3 -0
  34. package/dist/tools/manual/index.d.ts.map +1 -0
  35. package/dist/tools/manual/index.js +2 -0
  36. package/dist/tools/manual/manual-update.d.ts +4 -0
  37. package/dist/tools/manual/manual-update.d.ts.map +1 -0
  38. package/dist/tools/manual/manual-update.js +190 -0
  39. package/dist/tools/manual/verify-manual.d.ts +4 -0
  40. package/dist/tools/manual/verify-manual.d.ts.map +1 -0
  41. package/dist/tools/manual/verify-manual.js +51 -0
  42. package/package.json +34 -66
  43. package/postinstall.js +193 -0
  44. package/src/agents/alice.md +466 -0
  45. package/src/agents/bob-rev.md +493 -0
  46. package/src/agents/bob-send.md +277 -0
  47. package/src/agents/bob.md +442 -0
  48. package/src/agents/trace.md +451 -0
  49. package/src/plugins/bobs-workshop.ts +45 -0
  50. package/src/skills/api-patterns/SKILL.md +376 -0
  51. package/src/skills/architecture/SKILL.md +271 -0
  52. package/src/skills/bobs-workshop/performance/icon.svg +3 -0
  53. package/src/skills/brainstorming/SKILL.md +210 -0
  54. package/src/skills/clean-code/SKILL.md +151 -0
  55. package/src/skills/code-review-checklist/SKILL.md +220 -0
  56. package/src/skills/database-design/SKILL.md +271 -0
  57. package/src/skills/exploration/SKILL.md +257 -0
  58. package/src/skills/frontend-ui-ux/SKILL.md +78 -0
  59. package/src/skills/git-master/SKILL.md +1105 -0
  60. package/src/skills/performance/SKILL.md +144 -0
  61. package/src/skills/performance/icon.svg +3 -0
  62. package/src/skills/plan-writing/SKILL.md +225 -0
  63. package/src/skills/security/SKILL.md +410 -0
  64. package/src/skills/simplification/SKILL.md +238 -0
  65. package/src/skills/systematic-debugging/SKILL.md +175 -0
  66. package/src/skills/testing-patterns/SKILL.md +305 -0
  67. package/src/skills/verification/SKILL.md +286 -0
  68. package/src/tools/background-agent/cancel.ts +67 -0
  69. package/src/tools/background-agent/concurrency.ts +71 -0
  70. package/src/tools/background-agent/index.ts +7 -0
  71. package/src/tools/background-agent/launch.ts +39 -0
  72. package/src/tools/background-agent/list.ts +50 -0
  73. package/src/tools/background-agent/manager.ts +466 -0
  74. package/src/tools/background-agent/output.ts +57 -0
  75. package/src/tools/background-agent/types.ts +55 -0
  76. package/src/tools/index.ts +8 -0
  77. package/src/tools/manual/index.ts +2 -0
  78. package/src/tools/manual/manual-update.ts +197 -0
  79. package/src/tools/manual/verify-manual.ts +60 -0
  80. package/uninstall.js +64 -0
  81. package/Claude.md +0 -162
  82. package/bin/bobs-mcp-server.js +0 -11
  83. package/bin/bobs-mcp.js +0 -130
  84. package/dist/api/taskLogger.js +0 -106
  85. package/dist/api/taskLogger.js.map +0 -1
  86. package/dist/cli/checker.js +0 -401
  87. package/dist/cli/checker.js.map +0 -1
  88. package/dist/cli/cleanup.js +0 -131
  89. package/dist/cli/cleanup.js.map +0 -1
  90. package/dist/cli/debug.js +0 -157
  91. package/dist/cli/debug.js.map +0 -1
  92. package/dist/cli/health.js +0 -97
  93. package/dist/cli/health.js.map +0 -1
  94. package/dist/cli/setup.js +0 -81
  95. package/dist/cli/setup.js.map +0 -1
  96. package/dist/cli/workshop.js +0 -42
  97. package/dist/cli/workshop.js.map +0 -1
  98. package/dist/dashboard/server.js +0 -1203
  99. package/dist/dashboard/server.js.map +0 -1
  100. package/dist/index.js +0 -960
  101. package/dist/index.js.map +0 -1
  102. package/dist/prompts/architect.js +0 -221
  103. package/dist/prompts/architect.js.map +0 -1
  104. package/dist/prompts/debugger.js +0 -257
  105. package/dist/prompts/debugger.js.map +0 -1
  106. package/dist/prompts/engineer.js +0 -249
  107. package/dist/prompts/engineer.js.map +0 -1
  108. package/dist/prompts/orchestrator.js +0 -304
  109. package/dist/prompts/orchestrator.js.map +0 -1
  110. package/dist/prompts/reviewer.js +0 -289
  111. package/dist/prompts/reviewer.js.map +0 -1
  112. package/dist/services/activitySummarizer.js +0 -388
  113. package/dist/services/activitySummarizer.js.map +0 -1
  114. package/dist/services/changeValidator.js +0 -396
  115. package/dist/services/changeValidator.js.map +0 -1
  116. package/dist/services/claudeOrchestrator.js +0 -343
  117. package/dist/services/claudeOrchestrator.js.map +0 -1
  118. package/dist/services/fileMonitor.js +0 -250
  119. package/dist/services/fileMonitor.js.map +0 -1
  120. package/dist/services/implementationSummarizer.js +0 -306
  121. package/dist/services/implementationSummarizer.js.map +0 -1
  122. package/dist/services/liveMonitor.js +0 -315
  123. package/dist/services/liveMonitor.js.map +0 -1
  124. package/dist/services/mcpAuditLogger.js +0 -104
  125. package/dist/services/mcpAuditLogger.js.map +0 -1
  126. package/dist/services/mcpLogger.js +0 -223
  127. package/dist/services/mcpLogger.js.map +0 -1
  128. package/dist/services/tmuxManager.js +0 -541
  129. package/dist/services/tmuxManager.js.map +0 -1
  130. package/dist/tools/approvalTools.js +0 -244
  131. package/dist/tools/approvalTools.js.map +0 -1
  132. package/dist/tools/autoDebugger.js +0 -147
  133. package/dist/tools/autoDebugger.js.map +0 -1
  134. package/dist/tools/cleanupService.js +0 -221
  135. package/dist/tools/cleanupService.js.map +0 -1
  136. package/dist/tools/dashboardTools.js +0 -342
  137. package/dist/tools/dashboardTools.js.map +0 -1
  138. package/dist/tools/developmentNudges.js +0 -336
  139. package/dist/tools/developmentNudges.js.map +0 -1
  140. package/dist/tools/gitTools.js +0 -741
  141. package/dist/tools/gitTools.js.map +0 -1
  142. package/dist/tools/orchestratorTools.js +0 -832
  143. package/dist/tools/orchestratorTools.js.map +0 -1
  144. package/dist/tools/searchCache.js +0 -64
  145. package/dist/tools/searchCache.js.map +0 -1
  146. package/dist/tools/searchTools.js +0 -1107
  147. package/dist/tools/searchTools.js.map +0 -1
  148. package/dist/tools/semgrep-patterns.js +0 -296
  149. package/dist/tools/semgrep-patterns.js.map +0 -1
  150. package/dist/tools/specTools.js +0 -332
  151. package/dist/tools/specTools.js.map +0 -1
  152. package/dist/tools/structural/__tests__/orchestrator.test.js +0 -61
  153. package/dist/tools/structural/__tests__/orchestrator.test.js.map +0 -1
  154. package/dist/tools/structural/cache.js +0 -226
  155. package/dist/tools/structural/cache.js.map +0 -1
  156. package/dist/tools/structural/engines/python/index.js +0 -118
  157. package/dist/tools/structural/engines/python/index.js.map +0 -1
  158. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js +0 -97
  159. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js.map +0 -1
  160. package/dist/tools/structural/engines/typescript/analyzer.js +0 -433
  161. package/dist/tools/structural/engines/typescript/analyzer.js.map +0 -1
  162. package/dist/tools/structural/engines/typescript/index.js +0 -381
  163. package/dist/tools/structural/engines/typescript/index.js.map +0 -1
  164. package/dist/tools/structural/engines/typescript/utils.js +0 -279
  165. package/dist/tools/structural/engines/typescript/utils.js.map +0 -1
  166. package/dist/tools/structural/index.js +0 -248
  167. package/dist/tools/structural/index.js.map +0 -1
  168. package/dist/tools/structural/types.js +0 -18
  169. package/dist/tools/structural/types.js.map +0 -1
  170. package/dist/tools/tmuxTools.js +0 -100
  171. package/dist/tools/tmuxTools.js.map +0 -1
  172. package/dist/tools/workRecorder.js +0 -215
  173. package/dist/tools/workRecorder.js.map +0 -1
  174. package/dist/tools/worktreeTools.js +0 -705
  175. package/dist/tools/worktreeTools.js.map +0 -1
  176. package/dist/utils/__tests__/integration.test.js +0 -57
  177. package/dist/utils/__tests__/integration.test.js.map +0 -1
  178. package/dist/utils/__tests__/serverDetection.test.js +0 -151
  179. package/dist/utils/__tests__/serverDetection.test.js.map +0 -1
  180. package/dist/utils/errorHandling.js +0 -336
  181. package/dist/utils/errorHandling.js.map +0 -1
  182. package/dist/utils/processManager.js +0 -172
  183. package/dist/utils/processManager.js.map +0 -1
  184. package/dist/utils/reliability.js +0 -263
  185. package/dist/utils/reliability.js.map +0 -1
  186. package/dist/utils/responseFormatter.js +0 -250
  187. package/dist/utils/responseFormatter.js.map +0 -1
  188. package/dist/utils/serverDetection.js +0 -133
  189. package/dist/utils/serverDetection.js.map +0 -1
  190. package/dist/utils/specMigration.js +0 -105
  191. package/dist/utils/specMigration.js.map +0 -1
  192. package/dist/validation/schemas.js +0 -299
  193. package/dist/validation/schemas.js.map +0 -1
  194. package/public/.well-known/mcp/manifest.json +0 -473
  195. package/public/index.html +0 -3157
  196. package/public/index.html.backup +0 -2805
  197. package/public/index.html.backup2 +0 -1292
  198. package/scripts/cleanup-system-logs.ts +0 -121
  199. package/scripts/init-workspace.js +0 -63
  200. package/scripts/install-search-tools.js +0 -116
@@ -0,0 +1,388 @@
1
+ import { ConcurrencyManager } from "./concurrency";
2
+ const TASK_TTL_MS = 30 * 60 * 1000; // 30 minutes
3
+ const MIN_STABILITY_TIME_MS = 10 * 1000; // 10 seconds before stability detection
4
+ export class BackgroundManager {
5
+ tasks;
6
+ client;
7
+ directory;
8
+ pollingInterval;
9
+ concurrencyManager;
10
+ shutdownTriggered = false;
11
+ static instance = null;
12
+ static getInstance(ctx) {
13
+ if (!BackgroundManager.instance) {
14
+ BackgroundManager.instance = new BackgroundManager(ctx);
15
+ }
16
+ return BackgroundManager.instance;
17
+ }
18
+ constructor(ctx) {
19
+ this.tasks = new Map();
20
+ this.client = ctx.client;
21
+ this.directory = ctx.directory;
22
+ this.concurrencyManager = new ConcurrencyManager();
23
+ }
24
+ async launch(input) {
25
+ console.log("[background-agent] Launching task:", {
26
+ agent: input.agent,
27
+ model: input.model,
28
+ description: input.description,
29
+ });
30
+ if (!input.agent || input.agent.trim() === "") {
31
+ throw new Error("Agent parameter is required");
32
+ }
33
+ const task = {
34
+ id: `bg_${crypto.randomUUID().slice(0, 8)}`,
35
+ status: "pending",
36
+ queuedAt: new Date(),
37
+ description: input.description,
38
+ prompt: input.prompt,
39
+ agent: input.agent,
40
+ parentSessionID: input.parentSessionID,
41
+ model: input.model,
42
+ skills: input.skills,
43
+ };
44
+ this.tasks.set(task.id, task);
45
+ // Trigger processing
46
+ this.startTask(task, input).catch((error) => {
47
+ console.error("[background-agent] Error starting task:", error);
48
+ task.status = "error";
49
+ task.error = error instanceof Error ? error.message : String(error);
50
+ task.completedAt = new Date();
51
+ });
52
+ return task;
53
+ }
54
+ async startTask(task, input) {
55
+ const concurrencyKey = input.model || input.agent;
56
+ await this.concurrencyManager.acquire(concurrencyKey);
57
+ try {
58
+ const createResult = await this.client.session.create({
59
+ body: {
60
+ parentID: input.parentSessionID,
61
+ title: `Background: ${input.description}`,
62
+ },
63
+ query: {
64
+ directory: this.directory,
65
+ },
66
+ });
67
+ if (createResult.error) {
68
+ throw new Error(`Failed to create background session: ${createResult.error}`);
69
+ }
70
+ const sessionID = createResult.data.id;
71
+ task.status = "running";
72
+ task.startedAt = new Date();
73
+ task.sessionID = sessionID;
74
+ task.progress = {
75
+ toolCalls: 0,
76
+ lastUpdate: new Date(),
77
+ };
78
+ this.startPolling();
79
+ console.log("[background-agent] Launching task:", {
80
+ taskId: task.id,
81
+ sessionID,
82
+ agent: input.agent,
83
+ });
84
+ // Build skill content
85
+ let skillContent = input.skillContent || "";
86
+ if (input.skills && input.skills.length > 0) {
87
+ const { join } = await import("node:path");
88
+ const { readFileSync, existsSync } = await import("node:fs");
89
+ for (const skillName of input.skills) {
90
+ const skillPath = join(this.directory, ".opencode", "skill", "bobs-workshop", skillName, "SKILL.md");
91
+ if (existsSync(skillPath)) {
92
+ let skillFile = readFileSync(skillPath, "utf8");
93
+ // Sanitize content to prevent JSON parsing issues with kimi model
94
+ skillFile = skillFile
95
+ .replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F]/g, '') // Remove control chars except \n, \r, \t
96
+ .replace(/\u2028/g, '\n') // Replace line separator with newline
97
+ .replace(/\u2029/g, '\n'); // Replace paragraph separator with newline
98
+ skillContent += `\n\n---\n## Skill: ${skillName}\n\n${skillFile}`;
99
+ }
100
+ }
101
+ }
102
+ // Add MANUAL content if provided
103
+ if (input.manual_path) {
104
+ const { existsSync, readFileSync } = await import("node:fs");
105
+ if (existsSync(input.manual_path)) {
106
+ let manualContent = readFileSync(input.manual_path, "utf8");
107
+ // Sanitize content to prevent JSON parsing issues with kimi model
108
+ // Remove control characters and ensure proper escaping
109
+ manualContent = manualContent
110
+ .replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F]/g, '') // Remove control chars except \n, \r, \t
111
+ .replace(/\u2028/g, '\n') // Replace line separator with newline
112
+ .replace(/\u2029/g, '\n'); // Replace paragraph separator with newline
113
+ skillContent = skillContent
114
+ ? `${skillContent}\n\n## MANUAL Context:\n${manualContent}`
115
+ : manualContent;
116
+ }
117
+ }
118
+ // Fire-and-forget prompt
119
+ this.client.session.prompt({
120
+ path: { id: sessionID },
121
+ body: {
122
+ agent: input.agent,
123
+ ...(input.model ? { model: input.model } : {}),
124
+ system: skillContent || undefined,
125
+ tools: {
126
+ task: false,
127
+ delegate_task: false,
128
+ },
129
+ parts: [{ type: "text", text: input.prompt }],
130
+ },
131
+ }).catch((error) => {
132
+ console.error("[background-agent] Prompt error:", error);
133
+ const existingTask = this.tasks.get(task.id);
134
+ if (existingTask && existingTask.status === "running") {
135
+ existingTask.status = "error";
136
+ existingTask.error = error instanceof Error ? error.message : String(error);
137
+ existingTask.completedAt = new Date();
138
+ if (concurrencyKey) {
139
+ this.concurrencyManager.release(concurrencyKey);
140
+ }
141
+ }
142
+ });
143
+ }
144
+ catch (error) {
145
+ this.concurrencyManager.release(concurrencyKey);
146
+ throw error;
147
+ }
148
+ }
149
+ getTask(id) {
150
+ return this.tasks.get(id);
151
+ }
152
+ getAllTasks() {
153
+ return Array.from(this.tasks.values());
154
+ }
155
+ getRunningTasks() {
156
+ return Array.from(this.tasks.values()).filter((t) => t.status === "running");
157
+ }
158
+ handleEvent(event) {
159
+ if (event.type === "session.idle") {
160
+ const sessionID = event.properties?.sessionID;
161
+ if (!sessionID)
162
+ return;
163
+ const task = Array.from(this.tasks.values()).find((t) => t.sessionID === sessionID);
164
+ if (!task || task.status !== "running")
165
+ return;
166
+ const startedAt = task.startedAt;
167
+ if (!startedAt)
168
+ return;
169
+ const elapsedMs = Date.now() - startedAt.getTime();
170
+ const MIN_IDLE_TIME_MS = 5000; // 5 seconds
171
+ if (elapsedMs < MIN_IDLE_TIME_MS) {
172
+ console.log("[background-agent] Ignoring early session.idle");
173
+ return;
174
+ }
175
+ this.tryCompleteTask(task, "session.idle");
176
+ }
177
+ if (event.type === "session.deleted") {
178
+ const info = event.properties?.info;
179
+ if (!info?.id)
180
+ return;
181
+ const task = Array.from(this.tasks.values()).find((t) => t.sessionID === info.id);
182
+ if (!task)
183
+ return;
184
+ if (task.status === "running") {
185
+ task.status = "cancelled";
186
+ task.completedAt = new Date();
187
+ task.error = "Session deleted";
188
+ }
189
+ this.tasks.delete(task.id);
190
+ }
191
+ }
192
+ async tryCompleteTask(task, source) {
193
+ if (task.status !== "running") {
194
+ return false;
195
+ }
196
+ // Validate session has output before completing
197
+ if (!task.sessionID)
198
+ return false;
199
+ const hasOutput = await this.validateSessionHasOutput(task.sessionID);
200
+ if (!hasOutput) {
201
+ console.log("[background-agent] No valid output yet, waiting");
202
+ return false;
203
+ }
204
+ task.status = "completed";
205
+ task.completedAt = new Date();
206
+ // Release concurrency
207
+ if (task.model) {
208
+ this.concurrencyManager.release(task.model);
209
+ }
210
+ else {
211
+ this.concurrencyManager.release(task.agent);
212
+ }
213
+ console.log(`[background-agent] Task completed via ${source}:`, task.id);
214
+ // Auto-remove after 5 minutes
215
+ setTimeout(() => {
216
+ this.tasks.delete(task.id);
217
+ console.log("[background-agent] Removed completed task from memory:", task.id);
218
+ }, 5 * 60 * 1000);
219
+ return true;
220
+ }
221
+ async validateSessionHasOutput(sessionID) {
222
+ try {
223
+ const response = await this.client.session.messages({
224
+ path: { id: sessionID },
225
+ });
226
+ const messages = (response.data ?? []);
227
+ const hasAssistantOrToolMessage = messages.some((m) => m.info?.role === "assistant" || m.info?.role === "tool");
228
+ if (!hasAssistantOrToolMessage)
229
+ return false;
230
+ const hasContent = messages.some((m) => {
231
+ if (m.info?.role !== "assistant" && m.info?.role !== "tool")
232
+ return false;
233
+ const parts = m.parts ?? [];
234
+ return parts.some((p) => (p.type === "text" && p.text && p.text.trim().length > 0) ||
235
+ (p.type === "reasoning" && p.text && p.text.trim().length > 0) ||
236
+ p.type === "tool" ||
237
+ (p.type === "tool_result" && p.content !== undefined));
238
+ });
239
+ return hasContent;
240
+ }
241
+ catch (error) {
242
+ console.error("[background-agent] Error validating session output:", error);
243
+ return true; // Allow completion on error
244
+ }
245
+ }
246
+ startPolling() {
247
+ if (this.pollingInterval)
248
+ return;
249
+ this.pollingInterval = setInterval(() => {
250
+ this.pollRunningTasks();
251
+ }, 2000);
252
+ this.pollingInterval.unref();
253
+ }
254
+ stopPolling() {
255
+ if (this.pollingInterval) {
256
+ clearInterval(this.pollingInterval);
257
+ this.pollingInterval = undefined;
258
+ }
259
+ }
260
+ async pollRunningTasks() {
261
+ const now = Date.now();
262
+ // Prune stale tasks
263
+ for (const [taskId, task] of this.tasks.entries()) {
264
+ const timestamp = task.status === "pending"
265
+ ? task.queuedAt?.getTime()
266
+ : task.startedAt?.getTime();
267
+ if (!timestamp)
268
+ continue;
269
+ const age = now - timestamp;
270
+ if (age > TASK_TTL_MS) {
271
+ const errorMessage = task.status === "pending"
272
+ ? "Task timed out while queued (30 minutes)"
273
+ : "Task timed out after 30 minutes";
274
+ console.log("[background-agent] Pruning stale task:", { taskId, age });
275
+ task.status = "error";
276
+ task.error = errorMessage;
277
+ task.completedAt = new Date();
278
+ this.tasks.delete(taskId);
279
+ }
280
+ }
281
+ // Check running tasks
282
+ const statusResult = await this.client.session.status();
283
+ const allStatuses = (statusResult.data ?? {});
284
+ for (const task of this.tasks.values()) {
285
+ if (task.status !== "running")
286
+ continue;
287
+ const sessionID = task.sessionID;
288
+ if (!sessionID)
289
+ continue;
290
+ try {
291
+ const sessionStatus = allStatuses[sessionID];
292
+ if (sessionStatus?.type === "idle") {
293
+ const hasValidOutput = await this.validateSessionHasOutput(sessionID);
294
+ if (!hasValidOutput) {
295
+ continue;
296
+ }
297
+ // Re-check status after async operation
298
+ if (task.status !== "running")
299
+ continue;
300
+ await this.tryCompleteTask(task, "polling (idle status)");
301
+ continue;
302
+ }
303
+ // Stability detection
304
+ const messagesResult = await this.client.session.messages({
305
+ path: { id: sessionID },
306
+ });
307
+ if (!messagesResult.error && messagesResult.data) {
308
+ const messages = messagesResult.data;
309
+ const assistantMsgs = messages.filter((m) => m.info?.role === "assistant");
310
+ let toolCalls = 0;
311
+ let lastTool;
312
+ for (const msg of assistantMsgs) {
313
+ const parts = msg.parts ?? [];
314
+ for (const part of parts) {
315
+ if (part.type === "tool_use" || part.tool) {
316
+ toolCalls++;
317
+ lastTool = part.tool || part.name || "unknown";
318
+ }
319
+ }
320
+ }
321
+ if (!task.progress) {
322
+ task.progress = { toolCalls: 0, lastUpdate: new Date() };
323
+ }
324
+ task.progress.toolCalls = toolCalls;
325
+ task.progress.lastTool = lastTool;
326
+ task.progress.lastUpdate = new Date();
327
+ const currentMsgCount = messages.length;
328
+ const startedAt = task.startedAt;
329
+ if (!startedAt)
330
+ continue;
331
+ const elapsedMs = Date.now() - startedAt.getTime();
332
+ if (elapsedMs >= MIN_STABILITY_TIME_MS) {
333
+ if (task.lastMsgCount === currentMsgCount) {
334
+ task.stablePolls = (task.stablePolls ?? 0) + 1;
335
+ if ((task.stablePolls ?? 0) >= 3) {
336
+ const recheckStatus = await this.client.session.status();
337
+ const recheckData = (recheckStatus.data ?? {});
338
+ const currentStatus = recheckData[sessionID];
339
+ if (currentStatus?.type !== "idle") {
340
+ task.stablePolls = 0;
341
+ continue;
342
+ }
343
+ const hasValidOutput = await this.validateSessionHasOutput(sessionID);
344
+ if (!hasValidOutput) {
345
+ continue;
346
+ }
347
+ if (task.status !== "running")
348
+ continue;
349
+ await this.tryCompleteTask(task, "stability detection");
350
+ continue;
351
+ }
352
+ }
353
+ else {
354
+ task.stablePolls = 0;
355
+ }
356
+ }
357
+ task.lastMsgCount = currentMsgCount;
358
+ }
359
+ }
360
+ catch (error) {
361
+ console.error("[background-agent] Poll error:", error);
362
+ }
363
+ }
364
+ // Stop polling if no running tasks
365
+ if (!this.getRunningTasks().length) {
366
+ this.stopPolling();
367
+ }
368
+ }
369
+ shutdown() {
370
+ if (this.shutdownTriggered)
371
+ return;
372
+ this.shutdownTriggered = true;
373
+ console.log("[background-agent] Shutting down BackgroundManager");
374
+ this.stopPolling();
375
+ // Release concurrency for all running tasks
376
+ for (const task of this.tasks.values()) {
377
+ if (task.model) {
378
+ this.concurrencyManager.release(task.model);
379
+ }
380
+ else {
381
+ this.concurrencyManager.release(task.agent);
382
+ }
383
+ }
384
+ this.concurrencyManager.clear();
385
+ this.tasks.clear();
386
+ console.log("[background-agent] Shutdown complete");
387
+ }
388
+ }
@@ -0,0 +1,3 @@
1
+ declare const BackgroundOutputTool: any;
2
+ export default BackgroundOutputTool;
3
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/tools/background-agent/output.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,oBAAoB,EAAE,GAkD1B,CAAC;AAEH,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { BackgroundManager } from "./manager.js";
3
+ const BackgroundOutputTool = tool({
4
+ description: "Collect output from a specific background agent session",
5
+ args: {
6
+ task_id: tool.schema.string().describe("Background task ID to retrieve output from"),
7
+ },
8
+ async execute(args, context) {
9
+ const ctx = context;
10
+ const manager = BackgroundManager.getInstance(ctx);
11
+ const task = manager.getTask(args.task_id);
12
+ if (!task) {
13
+ return `❌ Task ${args.task_id} not found`;
14
+ }
15
+ if (task.status === "pending") {
16
+ return `⏳ Task ${args.task_id} is still queued...`;
17
+ }
18
+ if (task.status === "running") {
19
+ return `🔄 Task ${args.task_id} still running...`;
20
+ }
21
+ if (task.status === "error") {
22
+ return `❌ Task ${args.task_id} failed: ${task.error}`;
23
+ }
24
+ if (!task.sessionID) {
25
+ return `❌ Task ${args.task_id} has no session`;
26
+ }
27
+ const messagesResult = await ctx.client.session.messages({
28
+ path: { id: task.sessionID },
29
+ });
30
+ if (messagesResult.error) {
31
+ return `❌ Failed to retrieve output: ${messagesResult.error}`;
32
+ }
33
+ const messages = messagesResult.data;
34
+ const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
35
+ const output = assistantMessages
36
+ .map((m) => m.parts?.map((p) => p.text).join("\n"))
37
+ .join("\n\n---\n\n");
38
+ return `📥 Output from ${args.task_id}:\n\n${output}`;
39
+ },
40
+ });
41
+ export default BackgroundOutputTool;
@@ -0,0 +1,46 @@
1
+ export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled";
2
+ export interface TaskProgress {
3
+ toolCalls: number;
4
+ lastTool?: string;
5
+ lastUpdate: Date;
6
+ lastMessage?: string;
7
+ lastMessageAt?: Date;
8
+ }
9
+ export interface BackgroundTask {
10
+ id: string;
11
+ sessionID?: string;
12
+ parentSessionID: string;
13
+ description: string;
14
+ prompt: string;
15
+ agent: string;
16
+ status: BackgroundTaskStatus;
17
+ queuedAt?: Date;
18
+ startedAt?: Date;
19
+ completedAt?: Date;
20
+ error?: string;
21
+ progress?: TaskProgress;
22
+ model?: string;
23
+ skills?: string[];
24
+ lastMsgCount?: number;
25
+ stablePolls?: number;
26
+ }
27
+ export interface LaunchInput {
28
+ description: string;
29
+ prompt: string;
30
+ agent: string;
31
+ parentSessionID: string;
32
+ model?: string;
33
+ skills?: string[];
34
+ skillContent?: string;
35
+ manual_path?: string;
36
+ }
37
+ export interface ModelConfig {
38
+ providerID: string;
39
+ modelID: string;
40
+ }
41
+ export interface ResumeInput {
42
+ sessionId: string;
43
+ prompt: string;
44
+ parentSessionID: string;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/tools/background-agent/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,SAAS,GACT,WAAW,GACX,OAAO,GACP,WAAW,CAAC;AAEhB,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export { default as background_agent } from "./background-agent/launch.js";
2
+ export { default as list_background_tasks } from "./background-agent/list.js";
3
+ export { default as background_output } from "./background-agent/output.js";
4
+ export { default as background_cancel } from "./background-agent/cancel.js";
5
+ export { default as manual_update } from "./manual/manual-update.js";
6
+ export { default as verify_manual } from "./manual/verify-manual.js";
7
+ export * from "./background-agent/index.js";
8
+ export * from "./manual/index.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACrE,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { default as background_agent } from "./background-agent/launch.js";
2
+ export { default as list_background_tasks } from "./background-agent/list.js";
3
+ export { default as background_output } from "./background-agent/output.js";
4
+ export { default as background_cancel } from "./background-agent/cancel.js";
5
+ export { default as manual_update } from "./manual/manual-update.js";
6
+ export { default as verify_manual } from "./manual/verify-manual.js";
7
+ export * from "./background-agent/index.js";
8
+ export * from "./manual/index.js";
@@ -0,0 +1,3 @@
1
+ export { default as manual_update } from "./manual-update.js";
2
+ export { default as verify_manual } from "./verify-manual.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/manual/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as manual_update } from "./manual-update.js";
2
+ export { default as verify_manual } from "./verify-manual.js";
@@ -0,0 +1,4 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ declare const ManualUpdateTool: ToolDefinition;
3
+ export default ManualUpdateTool;
4
+ //# sourceMappingURL=manual-update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual-update.d.ts","sourceRoot":"","sources":["../../../src/tools/manual/manual-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA6IrE,QAAA,MAAM,gBAAgB,EAAE,cAqDtB,CAAC;AAEH,eAAe,gBAAgB,CAAC"}