@ebowwa/daemons 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +264 -0
- package/dist/bin/discord-cli.js +124118 -0
- package/dist/bin/manager.js +143 -0
- package/dist/bin/telegram-cli.js +124114 -0
- package/dist/index.js +125340 -0
- package/package.json +94 -0
- package/src/agent.ts +111 -0
- package/src/channels/base.ts +573 -0
- package/src/channels/discord.ts +306 -0
- package/src/channels/index.ts +169 -0
- package/src/channels/telegram.ts +315 -0
- package/src/daemon.ts +534 -0
- package/src/hooks.ts +97 -0
- package/src/index.ts +111 -0
- package/src/memory.ts +369 -0
- package/src/skills/coding/commit.ts +202 -0
- package/src/skills/coding/execute-subtask.ts +136 -0
- package/src/skills/coding/fix-issues.ts +126 -0
- package/src/skills/coding/index.ts +26 -0
- package/src/skills/coding/plan-task.ts +158 -0
- package/src/skills/coding/quality-check.ts +155 -0
- package/src/skills/index.ts +65 -0
- package/src/skills/registry.ts +380 -0
- package/src/skills/shared/index.ts +21 -0
- package/src/skills/shared/reflect.ts +156 -0
- package/src/skills/shared/review.ts +201 -0
- package/src/skills/shared/trajectory.ts +319 -0
- package/src/skills/trading/analyze-market.ts +144 -0
- package/src/skills/trading/check-risk.ts +176 -0
- package/src/skills/trading/execute-trade.ts +185 -0
- package/src/skills/trading/generate-signal.ts +160 -0
- package/src/skills/trading/index.ts +26 -0
- package/src/skills/trading/monitor-position.ts +179 -0
- package/src/skills/types.ts +235 -0
- package/src/skills/workflows.ts +340 -0
- package/src/state.ts +77 -0
- package/src/tools.ts +134 -0
- package/src/types.ts +314 -0
- package/src/workflow.ts +341 -0
- package/src/workflows/coding.ts +580 -0
- package/src/workflows/index.ts +61 -0
- package/src/workflows/trading.ts +608 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Skill Registry
|
|
3
|
+
*
|
|
4
|
+
* Central registry for all available skills.
|
|
5
|
+
* Skills register themselves on import, or can be registered manually.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Skill,
|
|
10
|
+
SkillConfig,
|
|
11
|
+
SkillContext,
|
|
12
|
+
SkillResult,
|
|
13
|
+
SkillExecutionOptions,
|
|
14
|
+
SkillExecutionMeta,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Skill Registry - manages all available skills
|
|
19
|
+
*/
|
|
20
|
+
export class SkillRegistry {
|
|
21
|
+
private skills: Map<string, Skill> = new Map();
|
|
22
|
+
private executionHistory: SkillExecutionMeta[] = [];
|
|
23
|
+
private maxHistorySize = 1000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a skill
|
|
27
|
+
*/
|
|
28
|
+
register(skill: Skill): void {
|
|
29
|
+
if (this.skills.has(skill.id)) {
|
|
30
|
+
console.warn(`[SkillRegistry] Overwriting existing skill: ${skill.id}`);
|
|
31
|
+
}
|
|
32
|
+
this.skills.set(skill.id, skill);
|
|
33
|
+
console.log(`[SkillRegistry] Registered skill: ${skill.id}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register multiple skills
|
|
38
|
+
*/
|
|
39
|
+
registerAll(skills: Skill[]): void {
|
|
40
|
+
for (const skill of skills) {
|
|
41
|
+
this.register(skill);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get a skill by ID
|
|
47
|
+
*/
|
|
48
|
+
get(id: string): Skill | undefined {
|
|
49
|
+
return this.skills.get(id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a skill exists
|
|
54
|
+
*/
|
|
55
|
+
has(id: string): boolean {
|
|
56
|
+
return this.skills.has(id);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* List all registered skills
|
|
61
|
+
*/
|
|
62
|
+
list(): Skill[] {
|
|
63
|
+
return Array.from(this.skills.values());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* List skill IDs only
|
|
68
|
+
*/
|
|
69
|
+
listIds(): string[] {
|
|
70
|
+
return Array.from(this.skills.keys());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* List skills by tag
|
|
75
|
+
*/
|
|
76
|
+
listByTag(tag: string): Skill[] {
|
|
77
|
+
return this.list().filter((s) => s.tags?.includes(tag));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get skills info for AI/discovery
|
|
82
|
+
*/
|
|
83
|
+
getInfo(): Array<{
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
description: string;
|
|
87
|
+
tags?: string[];
|
|
88
|
+
parallelizable?: boolean;
|
|
89
|
+
}> {
|
|
90
|
+
return this.list().map((s) => ({
|
|
91
|
+
id: s.id,
|
|
92
|
+
name: s.name,
|
|
93
|
+
description: s.description,
|
|
94
|
+
tags: s.tags,
|
|
95
|
+
parallelizable: s.parallelizable,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Execute a skill with retry logic
|
|
101
|
+
*/
|
|
102
|
+
async execute(
|
|
103
|
+
skillId: string,
|
|
104
|
+
context: SkillContext,
|
|
105
|
+
params?: unknown,
|
|
106
|
+
options: SkillExecutionOptions = {}
|
|
107
|
+
): Promise<SkillResult> {
|
|
108
|
+
const skill = this.skills.get(skillId);
|
|
109
|
+
if (!skill) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: `Skill not found: ${skillId}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate parameters if schema provided
|
|
117
|
+
if (skill.paramsSchema && !options.skipValidation) {
|
|
118
|
+
const parsed = skill.paramsSchema.safeParse(params);
|
|
119
|
+
if (!parsed.success) {
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
error: `Invalid params for ${skillId}: ${parsed.error.message}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check required context keys
|
|
128
|
+
if (skill.requires) {
|
|
129
|
+
const missing = skill.requires.filter((key) => !(key in context.custom));
|
|
130
|
+
if (missing.length > 0) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: `Missing required context for ${skillId}: ${missing.join(", ")}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Execute with retry
|
|
139
|
+
const maxRetries = options.maxRetries ?? skill.maxRetries ?? 3;
|
|
140
|
+
const baseDelay = skill.retryBaseDelay ?? 1000;
|
|
141
|
+
let lastError: string | undefined;
|
|
142
|
+
|
|
143
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
144
|
+
const meta: SkillExecutionMeta = {
|
|
145
|
+
skillId,
|
|
146
|
+
startTime: new Date().toISOString(),
|
|
147
|
+
attempt,
|
|
148
|
+
success: false,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Merge default params with provided params
|
|
153
|
+
const mergedParams = skill.defaultParams
|
|
154
|
+
? { ...skill.defaultParams, ...(params as Record<string, unknown>) }
|
|
155
|
+
: params;
|
|
156
|
+
|
|
157
|
+
const result = await this.executeWithTimeout(
|
|
158
|
+
skill,
|
|
159
|
+
mergedParams,
|
|
160
|
+
context,
|
|
161
|
+
options.timeout
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
meta.endTime = new Date().toISOString();
|
|
165
|
+
meta.duration =
|
|
166
|
+
new Date(meta.endTime).getTime() - new Date(meta.startTime).getTime();
|
|
167
|
+
meta.success = result.success;
|
|
168
|
+
meta.error = result.error;
|
|
169
|
+
|
|
170
|
+
this.recordExecution(meta);
|
|
171
|
+
|
|
172
|
+
if (result.success) {
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lastError = result.error;
|
|
177
|
+
|
|
178
|
+
// Don't retry on certain errors
|
|
179
|
+
if (
|
|
180
|
+
result.error?.includes("not allowed") ||
|
|
181
|
+
result.error?.includes("not found")
|
|
182
|
+
) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Exponential backoff before retry
|
|
187
|
+
if (attempt < maxRetries) {
|
|
188
|
+
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
189
|
+
console.log(
|
|
190
|
+
`[SkillRegistry] Retrying ${skillId} in ${delay}ms (attempt ${attempt}/${maxRetries})`
|
|
191
|
+
);
|
|
192
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
meta.endTime = new Date().toISOString();
|
|
196
|
+
meta.duration =
|
|
197
|
+
new Date(meta.endTime).getTime() - new Date(meta.startTime).getTime();
|
|
198
|
+
meta.success = false;
|
|
199
|
+
meta.error = error instanceof Error ? error.message : String(error);
|
|
200
|
+
lastError = meta.error;
|
|
201
|
+
|
|
202
|
+
this.recordExecution(meta);
|
|
203
|
+
|
|
204
|
+
if (attempt < maxRetries) {
|
|
205
|
+
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
206
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
error: lastError || `Skill failed after ${maxRetries} attempts`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Execute multiple skills in parallel
|
|
219
|
+
*/
|
|
220
|
+
async executeParallel(
|
|
221
|
+
skillIds: string[],
|
|
222
|
+
context: SkillContext,
|
|
223
|
+
params?: Record<string, unknown>
|
|
224
|
+
): Promise<Map<string, SkillResult>> {
|
|
225
|
+
const results = new Map<string, SkillResult>();
|
|
226
|
+
|
|
227
|
+
// Validate all skills exist and are parallelizable
|
|
228
|
+
for (const id of skillIds) {
|
|
229
|
+
const skill = this.skills.get(id);
|
|
230
|
+
if (!skill) {
|
|
231
|
+
results.set(id, { success: false, error: `Skill not found: ${id}` });
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (!skill.parallelizable) {
|
|
235
|
+
results.set(id, {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `Skill ${id} is not parallelizable`,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Filter out already-failed skills
|
|
243
|
+
const toExecute = skillIds.filter((id) => !results.has(id));
|
|
244
|
+
|
|
245
|
+
// Execute in parallel
|
|
246
|
+
const promises = toExecute.map(async (id) => {
|
|
247
|
+
const result = await this.execute(id, context, params?.[id]);
|
|
248
|
+
return { id, result };
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const settled = await Promise.allSettled(promises);
|
|
252
|
+
|
|
253
|
+
for (const item of settled) {
|
|
254
|
+
if (item.status === "fulfilled") {
|
|
255
|
+
results.set(item.value.id, item.value.result);
|
|
256
|
+
} else {
|
|
257
|
+
// This shouldn't happen as errors are caught in execute()
|
|
258
|
+
const id = toExecute[settled.indexOf(item)];
|
|
259
|
+
results.set(id, {
|
|
260
|
+
success: false,
|
|
261
|
+
error: item.reason?.message || "Unknown error",
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return results;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Execute with optional timeout
|
|
271
|
+
*/
|
|
272
|
+
private async executeWithTimeout(
|
|
273
|
+
skill: Skill,
|
|
274
|
+
params: unknown,
|
|
275
|
+
context: SkillContext,
|
|
276
|
+
timeout?: number
|
|
277
|
+
): Promise<SkillResult> {
|
|
278
|
+
if (!timeout) {
|
|
279
|
+
return skill.execute(params, context);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return new Promise((resolve) => {
|
|
283
|
+
const timer = setTimeout(() => {
|
|
284
|
+
resolve({
|
|
285
|
+
success: false,
|
|
286
|
+
error: `Skill ${skill.id} timed out after ${timeout}ms`,
|
|
287
|
+
});
|
|
288
|
+
}, timeout);
|
|
289
|
+
|
|
290
|
+
skill
|
|
291
|
+
.execute(params, context)
|
|
292
|
+
.then((result) => {
|
|
293
|
+
clearTimeout(timer);
|
|
294
|
+
resolve(result);
|
|
295
|
+
})
|
|
296
|
+
.catch((error) => {
|
|
297
|
+
clearTimeout(timer);
|
|
298
|
+
resolve({
|
|
299
|
+
success: false,
|
|
300
|
+
error: error instanceof Error ? error.message : String(error),
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Record execution in history
|
|
308
|
+
*/
|
|
309
|
+
private recordExecution(meta: SkillExecutionMeta): void {
|
|
310
|
+
this.executionHistory.push(meta);
|
|
311
|
+
|
|
312
|
+
// Trim old entries
|
|
313
|
+
if (this.executionHistory.length > this.maxHistorySize) {
|
|
314
|
+
this.executionHistory = this.executionHistory.slice(-this.maxHistorySize);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get execution history
|
|
320
|
+
*/
|
|
321
|
+
getHistory(limit = 100): SkillExecutionMeta[] {
|
|
322
|
+
return this.executionHistory.slice(-limit);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get execution stats for a skill
|
|
327
|
+
*/
|
|
328
|
+
getStats(skillId: string): {
|
|
329
|
+
total: number;
|
|
330
|
+
success: number;
|
|
331
|
+
failed: number;
|
|
332
|
+
avgDuration: number;
|
|
333
|
+
} {
|
|
334
|
+
const executions = this.executionHistory.filter((m) => m.skillId === skillId);
|
|
335
|
+
const total = executions.length;
|
|
336
|
+
const success = executions.filter((m) => m.success).length;
|
|
337
|
+
const durations = executions
|
|
338
|
+
.filter((m) => m.duration !== undefined)
|
|
339
|
+
.map((m) => m.duration!);
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
total,
|
|
343
|
+
success,
|
|
344
|
+
failed: total - success,
|
|
345
|
+
avgDuration:
|
|
346
|
+
durations.length > 0
|
|
347
|
+
? durations.reduce((a, b) => a + b, 0) / durations.length
|
|
348
|
+
: 0,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Clear registry (useful for testing)
|
|
354
|
+
*/
|
|
355
|
+
clear(): void {
|
|
356
|
+
this.skills.clear();
|
|
357
|
+
this.executionHistory = [];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Global skill registry instance
|
|
363
|
+
*/
|
|
364
|
+
export const skillRegistry = new SkillRegistry();
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Helper to create and register a skill
|
|
368
|
+
*/
|
|
369
|
+
export function defineSkill<P = unknown, D extends Record<string, unknown> = Record<string, unknown>>(
|
|
370
|
+
config: SkillConfig<P>,
|
|
371
|
+
execute: (params: P | undefined, context: SkillContext) => Promise<SkillResult<D>>
|
|
372
|
+
): Skill<P, D> {
|
|
373
|
+
const skill: Skill<P, D> = {
|
|
374
|
+
...config,
|
|
375
|
+
execute,
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
skillRegistry.register(skill);
|
|
379
|
+
return skill;
|
|
380
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Shared Skills
|
|
3
|
+
*
|
|
4
|
+
* Skills shared across all workflows.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Import to auto-register
|
|
8
|
+
import "./reflect.js";
|
|
9
|
+
import "./review.js";
|
|
10
|
+
import "./trajectory.js";
|
|
11
|
+
|
|
12
|
+
// Re-export for explicit imports
|
|
13
|
+
export { reflectSkill } from "./reflect.js";
|
|
14
|
+
export { reviewSkill } from "./review.js";
|
|
15
|
+
export { trajectorySkill } from "./trajectory.js";
|
|
16
|
+
|
|
17
|
+
// Re-export types
|
|
18
|
+
export type { ReflectData } from "./reflect.js";
|
|
19
|
+
export type { ReviewData } from "./review.js";
|
|
20
|
+
export type { TrajectoryData, LearnedPattern } from "./trajectory.js";
|
|
21
|
+
export { TrajectoryParams } from "./trajectory.js";
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon - Reflect Skill
|
|
3
|
+
*
|
|
4
|
+
* TL;DR checkpoint for summarizing progress after N tool calls.
|
|
5
|
+
* Used by all workflows for periodic reflection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { Skill, SkillContext, SkillResult } from "../types.js";
|
|
10
|
+
import { skillRegistry } from "../registry.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reflect skill parameters
|
|
14
|
+
*/
|
|
15
|
+
const ReflectParams = z.object({
|
|
16
|
+
/** Force reflection even if not at checkpoint */
|
|
17
|
+
force: z.boolean().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reflection result data (matches GLMReflectionSummary)
|
|
22
|
+
*/
|
|
23
|
+
interface ReflectData {
|
|
24
|
+
/** Checkpoint number */
|
|
25
|
+
checkpoint: number;
|
|
26
|
+
/** Summary text */
|
|
27
|
+
summary: string;
|
|
28
|
+
/** What was accomplished */
|
|
29
|
+
accomplishments: string[];
|
|
30
|
+
/** What to do next */
|
|
31
|
+
nextSteps: string[];
|
|
32
|
+
/** Any issues/blockers */
|
|
33
|
+
issues: string[];
|
|
34
|
+
/** Timestamp */
|
|
35
|
+
timestamp: string;
|
|
36
|
+
/** Tools used in this chunk */
|
|
37
|
+
toolsUsed: number;
|
|
38
|
+
/** Phase when reflection occurred */
|
|
39
|
+
phase: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Reflect skill definition
|
|
44
|
+
*/
|
|
45
|
+
export const reflectSkill: Skill<void, ReflectData> = {
|
|
46
|
+
id: "/reflect",
|
|
47
|
+
name: "Reflect",
|
|
48
|
+
description:
|
|
49
|
+
"TL;DR checkpoint for summarizing progress. Generates a brief summary of what was done and what's next.",
|
|
50
|
+
tags: ["shared", "checkpoint", "meta"],
|
|
51
|
+
parallelizable: false,
|
|
52
|
+
maxRetries: 1,
|
|
53
|
+
|
|
54
|
+
async execute(_params, context): Promise<SkillResult<ReflectData>> {
|
|
55
|
+
const checkpoint = context.state.reflection.checkpointCount + 1;
|
|
56
|
+
const previousPhase = context.state.slam.previousPhase || "unknown";
|
|
57
|
+
|
|
58
|
+
console.log(`[/reflect] Checkpoint #${checkpoint}`);
|
|
59
|
+
console.log(`[/reflect] Tools used this chunk: ${context.state.reflection.toolCount}`);
|
|
60
|
+
|
|
61
|
+
const prompt = `REFLECTION CHECKPOINT #${checkpoint}
|
|
62
|
+
|
|
63
|
+
You have used ${context.state.reflection.toolCount} tools. Pause and reflect.
|
|
64
|
+
|
|
65
|
+
Task: ${context.state.prompt}
|
|
66
|
+
Current Phase: ${previousPhase}
|
|
67
|
+
Iteration: ${context.state.iteration}
|
|
68
|
+
|
|
69
|
+
Progress so far:
|
|
70
|
+
- Subtasks completed: ${context.state.slam.completedSubtasks.length}/${context.state.slam.subtasks.length}
|
|
71
|
+
- Files changed: ${context.state.filesChanged.length > 0 ? context.state.filesChanged.join(", ") : "none"}
|
|
72
|
+
|
|
73
|
+
Generate a TL;DR summary in this JSON format:
|
|
74
|
+
{
|
|
75
|
+
"summary": "1-2 sentence summary of progress",
|
|
76
|
+
"accomplishments": ["what was done"],
|
|
77
|
+
"nextSteps": ["what needs to be done next"],
|
|
78
|
+
"issues": ["any blockers or problems"]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Be concise. Focus on what matters for continuing the task.`;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const response = await context.agent.execute(prompt);
|
|
85
|
+
|
|
86
|
+
// Parse JSON from response
|
|
87
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
88
|
+
let data: ReflectData;
|
|
89
|
+
|
|
90
|
+
if (jsonMatch) {
|
|
91
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
92
|
+
data = {
|
|
93
|
+
checkpoint,
|
|
94
|
+
summary: parsed.summary || "Progress made",
|
|
95
|
+
accomplishments: parsed.accomplishments || [],
|
|
96
|
+
nextSteps: parsed.nextSteps || [],
|
|
97
|
+
issues: parsed.issues || [],
|
|
98
|
+
timestamp: new Date().toISOString(),
|
|
99
|
+
toolsUsed: context.state.reflection.toolCount,
|
|
100
|
+
phase: previousPhase,
|
|
101
|
+
};
|
|
102
|
+
} else {
|
|
103
|
+
// Fallback if no JSON found
|
|
104
|
+
data = {
|
|
105
|
+
checkpoint,
|
|
106
|
+
summary: response.substring(0, 200),
|
|
107
|
+
accomplishments: [],
|
|
108
|
+
nextSteps: [],
|
|
109
|
+
issues: [],
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
toolsUsed: context.state.reflection.toolCount,
|
|
112
|
+
phase: previousPhase,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(`[/reflect] Summary: ${data.summary}`);
|
|
117
|
+
if (data.accomplishments.length > 0) {
|
|
118
|
+
console.log(`[/reflect] Accomplishments: ${data.accomplishments.join(", ")}`);
|
|
119
|
+
}
|
|
120
|
+
if (data.nextSteps.length > 0) {
|
|
121
|
+
console.log(`[/reflect] Next: ${data.nextSteps.join(", ")}`);
|
|
122
|
+
}
|
|
123
|
+
if (data.issues.length > 0) {
|
|
124
|
+
console.log(`[/reflect] Issues: ${data.issues.join(", ")}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Execute reflection hook
|
|
128
|
+
await context.executeHook("onReflection", context.state, data);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
success: true,
|
|
132
|
+
data,
|
|
133
|
+
stateUpdates: {
|
|
134
|
+
reflection: {
|
|
135
|
+
...context.state.reflection,
|
|
136
|
+
toolCount: 0, // Reset counter
|
|
137
|
+
checkpointCount: checkpoint,
|
|
138
|
+
lastReflection: data.timestamp,
|
|
139
|
+
summaries: [...context.state.reflection.summaries, data],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: error instanceof Error ? error.message : String(error),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Auto-register
|
|
153
|
+
skillRegistry.register(reflectSkill);
|
|
154
|
+
|
|
155
|
+
export { ReflectParams };
|
|
156
|
+
export type { ReflectData };
|