@metabob/minibob 0.1.2
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.
- package/ARCHITECTURE.md +255 -0
- package/CHANGELOG.md +112 -0
- package/README.md +380 -0
- package/bin/minibob.js +36 -0
- package/dist/acp-gossip.d.ts +72 -0
- package/dist/acp-gossip.d.ts.map +1 -0
- package/dist/acp-gossip.js +156 -0
- package/dist/acp-gossip.js.map +1 -0
- package/dist/acp.d.ts +62 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +292 -0
- package/dist/acp.js.map +1 -0
- package/dist/activity.d.ts +157 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +518 -0
- package/dist/activity.js.map +1 -0
- package/dist/agent-runtime.d.ts +104 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/boredom.d.ts +125 -0
- package/dist/boredom.d.ts.map +1 -0
- package/dist/boredom.js +244 -0
- package/dist/boredom.js.map +1 -0
- package/dist/cli/acp-server.d.ts +23 -0
- package/dist/cli/acp-server.d.ts.map +1 -0
- package/dist/cli/burrow.d.ts +26 -0
- package/dist/cli/burrow.d.ts.map +1 -0
- package/dist/cli/doctor.d.ts +22 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/goal.d.ts +22 -0
- package/dist/cli/goal.d.ts.map +1 -0
- package/dist/cli/index.d.ts +47 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/instance-registry.d.ts +78 -0
- package/dist/cli/instance-registry.d.ts.map +1 -0
- package/dist/cli/observe.d.ts +35 -0
- package/dist/cli/observe.d.ts.map +1 -0
- package/dist/cli/vessel.d.ts +14 -0
- package/dist/cli/vessel.d.ts.map +1 -0
- package/dist/composition-observer.d.ts +96 -0
- package/dist/composition-observer.d.ts.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/docker/Dockerfile +35 -0
- package/dist/environment.d.ts +72 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +142 -0
- package/dist/environment.js.map +1 -0
- package/dist/goal-processor.d.ts +165 -0
- package/dist/goal-processor.d.ts.map +1 -0
- package/dist/helm/minibob-cluster/Chart.yaml +13 -0
- package/dist/helm/minibob-cluster/templates/_helpers.tpl +60 -0
- package/dist/helm/minibob-cluster/templates/configmap.yaml +11 -0
- package/dist/helm/minibob-cluster/templates/deployment.yaml +108 -0
- package/dist/helm/minibob-cluster/templates/secret.yaml +10 -0
- package/dist/helm/minibob-cluster/templates/service.yaml +37 -0
- package/dist/helm/minibob-cluster/values-local.yaml +41 -0
- package/dist/helm/minibob-cluster/values-production.yaml +57 -0
- package/dist/helm/minibob-cluster/values-testing-cluster.yaml +43 -0
- package/dist/helm/minibob-cluster/values.yaml +127 -0
- package/dist/improviser.d.ts +74 -0
- package/dist/improviser.d.ts.map +1 -0
- package/dist/impulse-filter.d.ts +74 -0
- package/dist/impulse-filter.d.ts.map +1 -0
- package/dist/impulse.d.ts +92 -0
- package/dist/impulse.d.ts.map +1 -0
- package/dist/impulse.js +234 -0
- package/dist/impulse.js.map +1 -0
- package/dist/lib.d.ts +29 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +18561 -0
- package/dist/lib.js.map +98 -0
- package/dist/lifecycle-hooks.d.ts +99 -0
- package/dist/lifecycle-hooks.d.ts.map +1 -0
- package/dist/lifecycle-hooks.js +135 -0
- package/dist/lifecycle-hooks.js.map +1 -0
- package/dist/llm.d.ts +31 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +349 -0
- package/dist/llm.js.map +1 -0
- package/dist/mcp-activity-bridge.d.ts +66 -0
- package/dist/mcp-activity-bridge.d.ts.map +1 -0
- package/dist/mcp-activity-bridge.js +126 -0
- package/dist/mcp-activity-bridge.js.map +1 -0
- package/dist/mcp.d.ts +216 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +292 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory-agent.d.ts +92 -0
- package/dist/memory-agent.d.ts.map +1 -0
- package/dist/memory-agent.js +277 -0
- package/dist/memory-agent.js.map +1 -0
- package/dist/runtime-mapping.d.ts +97 -0
- package/dist/runtime-mapping.d.ts.map +1 -0
- package/dist/search-first-executor.d.ts +113 -0
- package/dist/search-first-executor.d.ts.map +1 -0
- package/dist/session.d.ts +48 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/template-extractor.d.ts +9 -0
- package/dist/template-extractor.d.ts.map +1 -0
- package/dist/template-generator.d.ts +12 -0
- package/dist/template-generator.d.ts.map +1 -0
- package/dist/tools.d.ts +58 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +771 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +503 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/understanding/analyzer.d.ts +55 -0
- package/dist/understanding/analyzer.d.ts.map +1 -0
- package/dist/understanding/explorer.d.ts +73 -0
- package/dist/understanding/explorer.d.ts.map +1 -0
- package/dist/understanding/index.d.ts +7 -0
- package/dist/understanding/index.d.ts.map +1 -0
- package/dist/understanding/types.d.ts +136 -0
- package/dist/understanding/types.d.ts.map +1 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +106 -0
- package/dist/validation.js.map +1 -0
- package/dist/vessel-bootstrap.d.ts +190 -0
- package/dist/vessel-bootstrap.d.ts.map +1 -0
- package/dist/vessel-registry.d.ts +229 -0
- package/dist/vessel-registry.d.ts.map +1 -0
- package/index.ts +1329 -0
- package/package.json +54 -0
- package/src/acp-gossip.ts +193 -0
- package/src/acp.ts +362 -0
- package/src/activity.ts +1464 -0
- package/src/agent-runtime.ts +365 -0
- package/src/boredom.ts +423 -0
- package/src/cli/acp-server.ts +377 -0
- package/src/cli/burrow.ts +896 -0
- package/src/cli/doctor.ts +526 -0
- package/src/cli/goal.ts +224 -0
- package/src/cli/index.ts +147 -0
- package/src/cli/instance-registry.ts +271 -0
- package/src/cli/observe.ts +682 -0
- package/src/cli/vessel.ts +287 -0
- package/src/components/SystemOverview.tsx +331 -0
- package/src/composition-observer.ts +449 -0
- package/src/config.ts +172 -0
- package/src/environment.ts +167 -0
- package/src/goal-processor.ts +654 -0
- package/src/improviser.ts +591 -0
- package/src/impulse-filter.ts +273 -0
- package/src/impulse.ts +311 -0
- package/src/lib.ts +147 -0
- package/src/lifecycle-hooks.ts +181 -0
- package/src/llm.ts +434 -0
- package/src/mcp-activity-bridge.ts +158 -0
- package/src/mcp.ts +747 -0
- package/src/memory-agent.ts +316 -0
- package/src/runtime-mapping.ts +527 -0
- package/src/search-first-executor.ts +666 -0
- package/src/session.ts +141 -0
- package/src/template-extractor.ts +256 -0
- package/src/template-generator.ts +130 -0
- package/src/tools.ts +924 -0
- package/src/types.ts +497 -0
- package/src/understanding/analyzer.ts +354 -0
- package/src/understanding/explorer.ts +488 -0
- package/src/understanding/index.ts +27 -0
- package/src/understanding/types.ts +153 -0
- package/src/validation.ts +125 -0
- package/src/vessel-bootstrap.ts +440 -0
- package/src/vessel-registry.ts +621 -0
- package/templates/core/edit-file.json +85 -0
- package/templates/understanding/diagnose-problem.json +32 -0
- package/templates/understanding/explore-codebase-v2.json +57 -0
- package/templates/understanding/explore-codebase.json +37 -0
package/src/boredom.ts
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* minibob Boredom Task System
|
|
3
|
+
*
|
|
4
|
+
* Enables autonomous operation by polling backend for tasks when idle.
|
|
5
|
+
* Transforms minibob from request-response to continuous autonomous agent.
|
|
6
|
+
*
|
|
7
|
+
* Two execution modes:
|
|
8
|
+
* 1. Template-based: Load and execute a specific template
|
|
9
|
+
* 2. Goal-based: Use SearchFirstExecutor for dynamic decomposition and reuse
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getMCPClient, isMCPEnabled } from "./mcp"
|
|
13
|
+
import { loadTemplateFromMCPOrLocal } from "./activity"
|
|
14
|
+
import { SearchFirstExecutor, type SearchFirstConfig } from "./search-first-executor"
|
|
15
|
+
import type { ActivityTemplate } from "./types"
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// BOREDOM TASK TYPES
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
export interface BoredomTask {
|
|
22
|
+
id: string
|
|
23
|
+
templateId?: string // Optional - if provided, use template execution
|
|
24
|
+
goal?: string // Optional - if provided, use search-first execution
|
|
25
|
+
priority: "critical" | "high" | "medium" | "low"
|
|
26
|
+
variables: Record<string, unknown>
|
|
27
|
+
reason?: string
|
|
28
|
+
createdAt: number
|
|
29
|
+
assignedTo?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface BoredomTaskResult {
|
|
33
|
+
taskId: string
|
|
34
|
+
success: boolean
|
|
35
|
+
executionId?: string
|
|
36
|
+
duration?: number
|
|
37
|
+
error?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// BOREDOM TASK EXECUTOR
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
export interface BoredomExecutorConfig {
|
|
45
|
+
pollInterval?: number
|
|
46
|
+
idleThreshold?: number
|
|
47
|
+
// Template-based execution callback
|
|
48
|
+
onExecuteTask: (template: ActivityTemplate, variables: Record<string, unknown>, reason?: string) => Promise<unknown>
|
|
49
|
+
// LLM config for goal-based execution
|
|
50
|
+
llmConfig: {
|
|
51
|
+
provider: "anthropic" | "openai"
|
|
52
|
+
apiKey: string
|
|
53
|
+
model: string
|
|
54
|
+
workingDirectory: string
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class BoredomTaskExecutor {
|
|
59
|
+
private isRunning = false
|
|
60
|
+
private pollInterval: number
|
|
61
|
+
private idleThreshold: number
|
|
62
|
+
private lastActivityTime: number
|
|
63
|
+
private onExecuteTask: (template: ActivityTemplate, variables: Record<string, unknown>, reason?: string) => Promise<unknown>
|
|
64
|
+
private searchFirstExecutor: SearchFirstExecutor
|
|
65
|
+
|
|
66
|
+
constructor(config: BoredomExecutorConfig) {
|
|
67
|
+
this.pollInterval = config.pollInterval ?? 30000 // 30 seconds
|
|
68
|
+
this.idleThreshold = config.idleThreshold ?? 60000 // 60 seconds idle before fetching tasks
|
|
69
|
+
this.lastActivityTime = Date.now()
|
|
70
|
+
this.onExecuteTask = config.onExecuteTask
|
|
71
|
+
|
|
72
|
+
// Initialize SearchFirstExecutor for goal-based tasks
|
|
73
|
+
this.searchFirstExecutor = new SearchFirstExecutor({
|
|
74
|
+
provider: config.llmConfig.provider,
|
|
75
|
+
apiKey: config.llmConfig.apiKey,
|
|
76
|
+
model: config.llmConfig.model,
|
|
77
|
+
workingDirectory: config.llmConfig.workingDirectory,
|
|
78
|
+
maxSteps: 4, // Conservative for autonomous execution
|
|
79
|
+
maxTokensPerStep: 4096, // Prevent token overflow
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Start the boredom task executor loop
|
|
85
|
+
*
|
|
86
|
+
* Only starts if:
|
|
87
|
+
* 1. MCP is enabled (backend connection available)
|
|
88
|
+
* 2. Cluster mode is enabled (3+ pods)
|
|
89
|
+
*
|
|
90
|
+
* Boredom system is disabled in local/Docker/single-pod environments to avoid resource waste.
|
|
91
|
+
*/
|
|
92
|
+
start(clusterMode = false): void {
|
|
93
|
+
if (this.isRunning) {
|
|
94
|
+
console.log("[Boredom] Already running")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!isMCPEnabled()) {
|
|
99
|
+
console.log("[Boredom] MCP not enabled, boredom tasks disabled")
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!clusterMode) {
|
|
104
|
+
console.log("[Boredom] Not in cluster mode, boredom tasks disabled")
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.isRunning = true
|
|
109
|
+
console.log(`[Boredom] Starting task executor (poll interval: ${this.pollInterval}ms, idle threshold: ${this.idleThreshold}ms)`)
|
|
110
|
+
|
|
111
|
+
this.loop().catch((error) => {
|
|
112
|
+
console.error("[Boredom] Fatal error in loop:", error)
|
|
113
|
+
this.isRunning = false
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Stop the boredom task executor
|
|
119
|
+
*/
|
|
120
|
+
stop(): void {
|
|
121
|
+
this.isRunning = false
|
|
122
|
+
console.log("[Boredom] Stopping task executor")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Mark activity (resets idle timer)
|
|
127
|
+
*/
|
|
128
|
+
markActivity(): void {
|
|
129
|
+
this.lastActivityTime = Date.now()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if vessel is idle
|
|
134
|
+
*/
|
|
135
|
+
isIdle(): boolean {
|
|
136
|
+
const idleTime = Date.now() - this.lastActivityTime
|
|
137
|
+
return idleTime >= this.idleThreshold
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Main executor loop
|
|
142
|
+
*/
|
|
143
|
+
private async loop(): Promise<void> {
|
|
144
|
+
while (this.isRunning) {
|
|
145
|
+
try {
|
|
146
|
+
// Wait for poll interval
|
|
147
|
+
await this.sleep(this.pollInterval)
|
|
148
|
+
|
|
149
|
+
// Skip if not idle
|
|
150
|
+
if (!this.isIdle()) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Fetch tasks from backend
|
|
155
|
+
const tasks = await this.fetchTasks()
|
|
156
|
+
if (tasks.length === 0) {
|
|
157
|
+
continue
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`[Boredom] Found ${tasks.length} available task(s)`)
|
|
161
|
+
|
|
162
|
+
// Execute highest priority task
|
|
163
|
+
const task = tasks[0]
|
|
164
|
+
if (!task) continue
|
|
165
|
+
await this.executeTask(task)
|
|
166
|
+
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("[Boredom] Error in loop:", error)
|
|
169
|
+
// Continue running despite errors
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Fetch available boredom tasks from backend
|
|
176
|
+
*/
|
|
177
|
+
private async fetchTasks(): Promise<BoredomTask[]> {
|
|
178
|
+
const mcp = getMCPClient()
|
|
179
|
+
if (!mcp) return []
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(`${(mcp as any).endpoint}/boredom-tasks`, {
|
|
183
|
+
method: "GET",
|
|
184
|
+
headers: {
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
if (!response.ok) {
|
|
190
|
+
if (response.status === 404) {
|
|
191
|
+
// Endpoint not implemented yet
|
|
192
|
+
return []
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`Failed to fetch tasks: ${response.status}`)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const data = await response.json() as { tasks?: BoredomTask[] }
|
|
198
|
+
const tasks = data.tasks ?? []
|
|
199
|
+
|
|
200
|
+
// Sort by priority
|
|
201
|
+
const sorted = tasks.sort((a, b) => {
|
|
202
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 }
|
|
203
|
+
return priorityOrder[a.priority] - priorityOrder[b.priority]
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// Convert debug/optimization templates to goal-based tasks
|
|
207
|
+
return sorted.map(task => this.convertToGoalTaskIfApplicable(task))
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error("[Boredom] Error fetching tasks:", error)
|
|
210
|
+
return []
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Convert template-based tasks to goal-based tasks when appropriate
|
|
216
|
+
*
|
|
217
|
+
* This enables backwards compatibility: the backend can still emit
|
|
218
|
+
* template-based tasks, but we convert debug/optimization tasks to
|
|
219
|
+
* use the SearchFirstExecutor for better reuse and decomposition.
|
|
220
|
+
*/
|
|
221
|
+
private convertToGoalTaskIfApplicable(task: BoredomTask): BoredomTask {
|
|
222
|
+
// If task already has a goal, use as-is
|
|
223
|
+
if (task.goal) {
|
|
224
|
+
return task
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Convert debug templates to goal-based tasks
|
|
228
|
+
if (task.templateId?.startsWith("debug-")) {
|
|
229
|
+
const vars = task.variables as {
|
|
230
|
+
failingTemplateId?: string
|
|
231
|
+
successRate?: number
|
|
232
|
+
totalExecutions?: number
|
|
233
|
+
recentFailures?: unknown[]
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log(`[Boredom] Converting debug task to goal-based: ${task.id}`)
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
...task,
|
|
240
|
+
templateId: undefined, // Remove template - use goal instead
|
|
241
|
+
goal: `Investigate why activity "${vars.failingTemplateId}" has ${vars.successRate ?? 0}% success rate after ${vars.totalExecutions ?? 0} executions. Identify the root cause and suggest fixes.`,
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Convert optimization templates to goal-based tasks
|
|
246
|
+
if (task.templateId?.startsWith("optimize-") || task.templateId?.startsWith("improve-")) {
|
|
247
|
+
console.log(`[Boredom] Converting optimization task to goal-based: ${task.id}`)
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
...task,
|
|
251
|
+
templateId: undefined,
|
|
252
|
+
goal: `Optimize activity "${(task.variables as { targetTemplateId?: string }).targetTemplateId}". Analyze performance metrics and suggest improvements.`,
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Keep other tasks as template-based
|
|
257
|
+
return task
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Execute a boredom task
|
|
262
|
+
*
|
|
263
|
+
* Two execution modes:
|
|
264
|
+
* 1. Goal-based: Use SearchFirstExecutor for dynamic step decomposition
|
|
265
|
+
* 2. Template-based: Load and execute a specific template (legacy)
|
|
266
|
+
*/
|
|
267
|
+
private async executeTask(task: BoredomTask): Promise<void> {
|
|
268
|
+
const result: BoredomTaskResult = {
|
|
269
|
+
taskId: task.id,
|
|
270
|
+
success: false,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const startTime = Date.now()
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
// Mode 1: Goal-based execution (preferred)
|
|
277
|
+
if (task.goal) {
|
|
278
|
+
console.log(`[Boredom] Executing goal-based task: ${task.id}`)
|
|
279
|
+
console.log(`[Boredom] Goal: ${task.goal}`)
|
|
280
|
+
|
|
281
|
+
const goalResult = await this.searchFirstExecutor.execute(
|
|
282
|
+
task.goal,
|
|
283
|
+
{
|
|
284
|
+
...task.variables,
|
|
285
|
+
taskId: task.id,
|
|
286
|
+
reason: task.reason,
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
result.success = goalResult.completed
|
|
291
|
+
result.duration = Date.now() - startTime
|
|
292
|
+
|
|
293
|
+
if (goalResult.completed) {
|
|
294
|
+
console.log(`[Boredom] ✓ Goal completed: ${task.id} in ${result.duration}ms`)
|
|
295
|
+
console.log(`[Boredom] Summary: ${goalResult.summary}`)
|
|
296
|
+
} else {
|
|
297
|
+
result.error = `Goal partially completed: ${goalResult.results.filter(r => r.status === "failed").map(r => r.error).join("; ")}`
|
|
298
|
+
console.log(`[Boredom] ⚠ Goal partially completed: ${task.id}`)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Mode 2: Template-based execution (legacy)
|
|
302
|
+
else if (task.templateId) {
|
|
303
|
+
console.log(`[Boredom] Executing template-based task: ${task.id} (template: ${task.templateId})`)
|
|
304
|
+
|
|
305
|
+
const template = await loadTemplateFromMCPOrLocal(task.templateId)
|
|
306
|
+
|
|
307
|
+
const execution = await this.onExecuteTask(
|
|
308
|
+
template,
|
|
309
|
+
task.variables,
|
|
310
|
+
task.reason ?? `Boredom task: ${task.id}`
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
result.success = true
|
|
314
|
+
result.executionId = (execution as any).id
|
|
315
|
+
result.duration = Date.now() - startTime
|
|
316
|
+
|
|
317
|
+
console.log(`[Boredom] ✓ Task completed: ${task.id} in ${result.duration}ms`)
|
|
318
|
+
}
|
|
319
|
+
// No goal or template - invalid task
|
|
320
|
+
else {
|
|
321
|
+
throw new Error("Task must have either 'goal' or 'templateId'")
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
} catch (error) {
|
|
325
|
+
result.success = false
|
|
326
|
+
result.error = error instanceof Error ? error.message : String(error)
|
|
327
|
+
result.duration = Date.now() - startTime
|
|
328
|
+
console.error(`[Boredom] ✗ Task failed: ${task.id}`, error)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Report result to backend
|
|
332
|
+
await this.reportResult(result)
|
|
333
|
+
|
|
334
|
+
// Mark activity to reset idle timer
|
|
335
|
+
this.markActivity()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Report task execution result to backend
|
|
340
|
+
*/
|
|
341
|
+
private async reportResult(result: BoredomTaskResult): Promise<void> {
|
|
342
|
+
const mcp = getMCPClient()
|
|
343
|
+
if (!mcp) return
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
const response = await fetch(`${(mcp as any).endpoint}/boredom-tasks/${result.taskId}/result`, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
headers: {
|
|
349
|
+
"Content-Type": "application/json",
|
|
350
|
+
},
|
|
351
|
+
body: JSON.stringify(result),
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
if (!response.ok) {
|
|
355
|
+
console.warn(`[Boredom] Failed to report result: ${response.status}`)
|
|
356
|
+
}
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error("[Boredom] Error reporting result:", error)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Sleep helper
|
|
364
|
+
*/
|
|
365
|
+
private sleep(ms: number): Promise<void> {
|
|
366
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// =============================================================================
|
|
371
|
+
// SINGLETON BOREDOM EXECUTOR
|
|
372
|
+
// =============================================================================
|
|
373
|
+
|
|
374
|
+
let boredomExecutor: BoredomTaskExecutor | null = null
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Initialize boredom task executor
|
|
378
|
+
*/
|
|
379
|
+
export function initializeBoredom(config: BoredomExecutorConfig): BoredomTaskExecutor {
|
|
380
|
+
if (boredomExecutor) {
|
|
381
|
+
console.warn("[Boredom] Already initialized")
|
|
382
|
+
return boredomExecutor
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
boredomExecutor = new BoredomTaskExecutor(config)
|
|
386
|
+
return boredomExecutor
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Get boredom executor instance
|
|
391
|
+
*/
|
|
392
|
+
export function getBoredomExecutor(): BoredomTaskExecutor | null {
|
|
393
|
+
return boredomExecutor
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Start boredom task execution
|
|
398
|
+
*
|
|
399
|
+
* @param clusterMode Whether cluster mode is enabled (required for boredom to start)
|
|
400
|
+
*/
|
|
401
|
+
export function startBoredom(clusterMode = false): void {
|
|
402
|
+
if (boredomExecutor) {
|
|
403
|
+
boredomExecutor.start(clusterMode)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Stop boredom task execution
|
|
409
|
+
*/
|
|
410
|
+
export function stopBoredom(): void {
|
|
411
|
+
if (boredomExecutor) {
|
|
412
|
+
boredomExecutor.stop()
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Mark activity (called when handling user requests)
|
|
418
|
+
*/
|
|
419
|
+
export function markBoredomActivity(): void {
|
|
420
|
+
if (boredomExecutor) {
|
|
421
|
+
boredomExecutor.markActivity()
|
|
422
|
+
}
|
|
423
|
+
}
|