talon-agent 1.0.0 → 1.2.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/LICENSE +21 -0
- package/README.md +1 -0
- package/package.json +15 -11
- package/prompts/dream.md +7 -3
- package/prompts/heartbeat.md +30 -0
- package/prompts/identity.md +1 -0
- package/prompts/teams.md +3 -0
- package/prompts/telegram.md +1 -0
- package/src/__tests__/chat-settings.test.ts +108 -2
- package/src/__tests__/cleanup-registry.test.ts +58 -0
- package/src/__tests__/config.test.ts +118 -52
- package/src/__tests__/cron-store-extended.test.ts +661 -0
- package/src/__tests__/cron-store.test.ts +145 -11
- package/src/__tests__/daily-log.test.ts +224 -13
- package/src/__tests__/dispatcher.test.ts +424 -23
- package/src/__tests__/dream.test.ts +1028 -0
- package/src/__tests__/errors-extended.test.ts +428 -0
- package/src/__tests__/errors.test.ts +95 -3
- package/src/__tests__/fuzz.test.ts +87 -15
- package/src/__tests__/gateway-actions.test.ts +1174 -433
- package/src/__tests__/gateway-http.test.ts +210 -19
- package/src/__tests__/gateway-retry.test.ts +359 -0
- package/src/__tests__/gateway-withRetry-extended.test.ts +343 -0
- package/src/__tests__/graph.test.ts +830 -0
- package/src/__tests__/handlers-stream.test.ts +208 -0
- package/src/__tests__/handlers.test.ts +2539 -70
- package/src/__tests__/heartbeat.test.ts +364 -0
- package/src/__tests__/history-extended.test.ts +775 -0
- package/src/__tests__/history-persistence.test.ts +74 -19
- package/src/__tests__/history.test.ts +113 -79
- package/src/__tests__/integration.test.ts +43 -8
- package/src/__tests__/log-init.test.ts +129 -0
- package/src/__tests__/log.test.ts +23 -5
- package/src/__tests__/media-index.test.ts +317 -35
- package/src/__tests__/plugin.test.ts +314 -0
- package/src/__tests__/prompt-builder-extended.test.ts +296 -0
- package/src/__tests__/prompt-builder.test.ts +44 -9
- package/src/__tests__/sessions.test.ts +258 -4
- package/src/__tests__/storage-save-errors.test.ts +342 -0
- package/src/__tests__/teams-frontend.test.ts +526 -31
- package/src/__tests__/telegram-formatting.test.ts +82 -0
- package/src/__tests__/terminal-commands.test.ts +208 -1
- package/src/__tests__/terminal-renderer.test.ts +223 -0
- package/src/__tests__/time.test.ts +107 -0
- package/src/__tests__/workspace-migrate.test.ts +256 -0
- package/src/__tests__/workspace.test.ts +63 -1
- package/src/backend/claude-sdk/tools.ts +64 -18
- package/src/bootstrap.ts +14 -14
- package/src/cli.ts +440 -125
- package/src/core/cron.ts +20 -5
- package/src/core/dispatcher.ts +27 -9
- package/src/core/dream.ts +79 -24
- package/src/core/errors.ts +12 -2
- package/src/core/gateway-actions.ts +182 -46
- package/src/core/gateway.ts +93 -41
- package/src/core/heartbeat.ts +515 -0
- package/src/core/plugin.ts +1 -1
- package/src/core/prompt-builder.ts +1 -4
- package/src/core/pulse.ts +4 -3
- package/src/frontend/teams/actions.ts +3 -1
- package/src/frontend/teams/formatting.ts +47 -8
- package/src/frontend/teams/graph.ts +35 -11
- package/src/frontend/teams/index.ts +155 -57
- package/src/frontend/teams/tools.ts +4 -6
- package/src/frontend/telegram/actions.ts +358 -82
- package/src/frontend/telegram/admin.ts +162 -72
- package/src/frontend/telegram/callbacks.ts +16 -10
- package/src/frontend/telegram/commands.ts +37 -21
- package/src/frontend/telegram/formatting.ts +2 -4
- package/src/frontend/telegram/handlers.ts +262 -66
- package/src/frontend/telegram/index.ts +39 -14
- package/src/frontend/telegram/middleware.ts +14 -4
- package/src/frontend/telegram/userbot.ts +16 -4
- package/src/frontend/terminal/renderer.ts +1 -4
- package/src/index.ts +28 -4
- package/src/storage/chat-settings.ts +32 -9
- package/src/storage/cron-store.ts +53 -11
- package/src/storage/daily-log.ts +72 -19
- package/src/storage/history.ts +39 -21
- package/src/storage/media-index.ts +37 -12
- package/src/storage/sessions.ts +3 -2
- package/src/util/cleanup-registry.ts +34 -0
- package/src/util/config.ts +85 -23
- package/src/util/log.ts +47 -17
- package/src/util/paths.ts +10 -0
- package/src/util/time.ts +29 -6
- package/src/util/watchdog.ts +5 -1
- package/src/util/workspace.ts +51 -10
|
@@ -51,6 +51,11 @@ const { handleSharedAction } = await import("../core/gateway-actions.js");
|
|
|
51
51
|
const { resolveModelName } = await import("../storage/chat-settings.js");
|
|
52
52
|
const { Cron } = await import("croner");
|
|
53
53
|
|
|
54
|
+
// ── Configuration ───────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
const NUM_RUNS = Number(process.env.FAST_CHECK_NUM_RUNS) || 100;
|
|
57
|
+
const fcParams = { numRuns: NUM_RUNS };
|
|
58
|
+
|
|
54
59
|
// ── Fuzz tests ───────────────────────────────────────────────────────────────
|
|
55
60
|
|
|
56
61
|
describe("fuzz: classify()", () => {
|
|
@@ -63,6 +68,7 @@ describe("fuzz: classify()", () => {
|
|
|
63
68
|
expect(typeof result.retryable).toBe("boolean");
|
|
64
69
|
expect(typeof result.message).toBe("string");
|
|
65
70
|
}),
|
|
71
|
+
fcParams,
|
|
66
72
|
);
|
|
67
73
|
});
|
|
68
74
|
|
|
@@ -73,6 +79,7 @@ describe("fuzz: classify()", () => {
|
|
|
73
79
|
expect(result).toBeInstanceOf(TalonError);
|
|
74
80
|
expect(result.reason).toBeTruthy();
|
|
75
81
|
}),
|
|
82
|
+
fcParams,
|
|
76
83
|
);
|
|
77
84
|
});
|
|
78
85
|
|
|
@@ -93,6 +100,7 @@ describe("fuzz: classify()", () => {
|
|
|
93
100
|
expect(result.reason).toBeTruthy();
|
|
94
101
|
},
|
|
95
102
|
),
|
|
103
|
+
fcParams,
|
|
96
104
|
);
|
|
97
105
|
});
|
|
98
106
|
|
|
@@ -115,6 +123,7 @@ describe("fuzz: classify()", () => {
|
|
|
115
123
|
const result = classify(input);
|
|
116
124
|
expect(validReasons.has(result.reason)).toBe(true);
|
|
117
125
|
}),
|
|
126
|
+
fcParams,
|
|
118
127
|
);
|
|
119
128
|
});
|
|
120
129
|
|
|
@@ -126,18 +135,25 @@ describe("fuzz: classify()", () => {
|
|
|
126
135
|
expect(result.retryAfterMs).toBeGreaterThanOrEqual(0);
|
|
127
136
|
}
|
|
128
137
|
}),
|
|
138
|
+
fcParams,
|
|
129
139
|
);
|
|
130
140
|
});
|
|
131
141
|
});
|
|
132
142
|
|
|
133
143
|
// Real cron validator (the import is mocked, so we use Cron directly)
|
|
134
|
-
function realValidateCron(
|
|
144
|
+
function realValidateCron(
|
|
145
|
+
expr: string,
|
|
146
|
+
timezone?: string,
|
|
147
|
+
): { valid: boolean; error?: string } {
|
|
135
148
|
try {
|
|
136
149
|
const cron = new Cron(expr, { timezone: timezone ?? undefined });
|
|
137
150
|
cron.nextRun();
|
|
138
151
|
return { valid: true };
|
|
139
152
|
} catch (err) {
|
|
140
|
-
return {
|
|
153
|
+
return {
|
|
154
|
+
valid: false,
|
|
155
|
+
error: err instanceof Error ? err.message : String(err),
|
|
156
|
+
};
|
|
141
157
|
}
|
|
142
158
|
}
|
|
143
159
|
|
|
@@ -151,6 +167,7 @@ describe("fuzz: validateCronExpression()", () => {
|
|
|
151
167
|
expect(typeof result.error).toBe("string");
|
|
152
168
|
}
|
|
153
169
|
}),
|
|
170
|
+
fcParams,
|
|
154
171
|
);
|
|
155
172
|
});
|
|
156
173
|
|
|
@@ -160,6 +177,7 @@ describe("fuzz: validateCronExpression()", () => {
|
|
|
160
177
|
const result = realValidateCron(expr, tz);
|
|
161
178
|
expect(typeof result.valid).toBe("boolean");
|
|
162
179
|
}),
|
|
180
|
+
fcParams,
|
|
163
181
|
);
|
|
164
182
|
});
|
|
165
183
|
});
|
|
@@ -170,15 +188,24 @@ describe("fuzz: handleSharedAction() — unknown actions", () => {
|
|
|
170
188
|
fc.asyncProperty(fc.string(), async (actionName) => {
|
|
171
189
|
// Skip known action names to test only the default/unknown path
|
|
172
190
|
const knownActions = new Set([
|
|
173
|
-
"read_history",
|
|
174
|
-
"
|
|
175
|
-
"
|
|
191
|
+
"read_history",
|
|
192
|
+
"search_history",
|
|
193
|
+
"get_user_messages",
|
|
194
|
+
"list_known_users",
|
|
195
|
+
"list_media",
|
|
196
|
+
"web_search",
|
|
197
|
+
"fetch_url",
|
|
198
|
+
"create_cron_job",
|
|
199
|
+
"list_cron_jobs",
|
|
200
|
+
"edit_cron_job",
|
|
201
|
+
"delete_cron_job",
|
|
176
202
|
]);
|
|
177
203
|
if (knownActions.has(actionName)) return;
|
|
178
204
|
|
|
179
205
|
const result = await handleSharedAction({ action: actionName }, 123);
|
|
180
206
|
expect(result).toBeNull();
|
|
181
207
|
}),
|
|
208
|
+
fcParams,
|
|
182
209
|
);
|
|
183
210
|
});
|
|
184
211
|
|
|
@@ -192,9 +219,17 @@ describe("fuzz: handleSharedAction() — unknown actions", () => {
|
|
|
192
219
|
}),
|
|
193
220
|
async (body) => {
|
|
194
221
|
const knownActions = new Set([
|
|
195
|
-
"read_history",
|
|
196
|
-
"
|
|
197
|
-
"
|
|
222
|
+
"read_history",
|
|
223
|
+
"search_history",
|
|
224
|
+
"get_user_messages",
|
|
225
|
+
"list_known_users",
|
|
226
|
+
"list_media",
|
|
227
|
+
"web_search",
|
|
228
|
+
"fetch_url",
|
|
229
|
+
"create_cron_job",
|
|
230
|
+
"list_cron_jobs",
|
|
231
|
+
"edit_cron_job",
|
|
232
|
+
"delete_cron_job",
|
|
198
233
|
]);
|
|
199
234
|
if (knownActions.has(body.action)) return;
|
|
200
235
|
|
|
@@ -202,6 +237,7 @@ describe("fuzz: handleSharedAction() — unknown actions", () => {
|
|
|
202
237
|
expect(result).toBeNull();
|
|
203
238
|
},
|
|
204
239
|
),
|
|
240
|
+
fcParams,
|
|
205
241
|
);
|
|
206
242
|
});
|
|
207
243
|
});
|
|
@@ -216,7 +252,10 @@ describe("fuzz: fetch_url URL validation", () => {
|
|
|
216
252
|
|
|
217
253
|
fc.assert(
|
|
218
254
|
fc.asyncProperty(fc.string(), async (url) => {
|
|
219
|
-
const result = await handleSharedAction(
|
|
255
|
+
const result = await handleSharedAction(
|
|
256
|
+
{ action: "fetch_url", url },
|
|
257
|
+
123,
|
|
258
|
+
);
|
|
220
259
|
// Should either return an error result or handle gracefully
|
|
221
260
|
expect(result).not.toBeUndefined();
|
|
222
261
|
if (result !== null) {
|
|
@@ -226,6 +265,7 @@ describe("fuzz: fetch_url URL validation", () => {
|
|
|
226
265
|
}
|
|
227
266
|
}
|
|
228
267
|
}),
|
|
268
|
+
fcParams,
|
|
229
269
|
);
|
|
230
270
|
|
|
231
271
|
globalThis.fetch = originalFetch;
|
|
@@ -234,11 +274,22 @@ describe("fuzz: fetch_url URL validation", () => {
|
|
|
234
274
|
it("rejects all non-http/https protocols", () => {
|
|
235
275
|
fc.assert(
|
|
236
276
|
fc.asyncProperty(
|
|
237
|
-
fc.constantFrom(
|
|
277
|
+
fc.constantFrom(
|
|
278
|
+
"ftp",
|
|
279
|
+
"file",
|
|
280
|
+
"javascript",
|
|
281
|
+
"data",
|
|
282
|
+
"ws",
|
|
283
|
+
"wss",
|
|
284
|
+
"ssh",
|
|
285
|
+
),
|
|
238
286
|
fc.webUrl(),
|
|
239
287
|
async (protocol, path) => {
|
|
240
288
|
const url = `${protocol}://${path.replace(/^https?:\/\//, "")}`;
|
|
241
|
-
const result = await handleSharedAction(
|
|
289
|
+
const result = await handleSharedAction(
|
|
290
|
+
{ action: "fetch_url", url },
|
|
291
|
+
123,
|
|
292
|
+
);
|
|
242
293
|
if (result !== null) {
|
|
243
294
|
// Should not be ok for non-http protocols
|
|
244
295
|
// (some invalid URLs will get "Invalid URL" error at parse time)
|
|
@@ -246,6 +297,7 @@ describe("fuzz: fetch_url URL validation", () => {
|
|
|
246
297
|
}
|
|
247
298
|
},
|
|
248
299
|
),
|
|
300
|
+
fcParams,
|
|
249
301
|
);
|
|
250
302
|
});
|
|
251
303
|
});
|
|
@@ -257,6 +309,7 @@ describe("fuzz: resolveModelName()", () => {
|
|
|
257
309
|
const result = resolveModelName(input);
|
|
258
310
|
expect(typeof result).toBe("string");
|
|
259
311
|
}),
|
|
312
|
+
fcParams,
|
|
260
313
|
);
|
|
261
314
|
});
|
|
262
315
|
|
|
@@ -266,11 +319,22 @@ describe("fuzz: resolveModelName()", () => {
|
|
|
266
319
|
const result = resolveModelName(input);
|
|
267
320
|
expect(result).toBe(result.trim());
|
|
268
321
|
}),
|
|
322
|
+
fcParams,
|
|
269
323
|
);
|
|
270
324
|
});
|
|
271
325
|
|
|
272
326
|
it("known aliases always resolve to claude model names", () => {
|
|
273
|
-
const aliases = [
|
|
327
|
+
const aliases = [
|
|
328
|
+
"sonnet",
|
|
329
|
+
"opus",
|
|
330
|
+
"haiku",
|
|
331
|
+
"sonnet-4.6",
|
|
332
|
+
"opus-4.6",
|
|
333
|
+
"haiku-4.5",
|
|
334
|
+
"sonnet-4-6",
|
|
335
|
+
"opus-4-6",
|
|
336
|
+
"haiku-4-5",
|
|
337
|
+
];
|
|
274
338
|
fc.assert(
|
|
275
339
|
fc.property(
|
|
276
340
|
fc.constantFrom(...aliases),
|
|
@@ -280,6 +344,7 @@ describe("fuzz: resolveModelName()", () => {
|
|
|
280
344
|
expect(result).toMatch(/^claude-/);
|
|
281
345
|
},
|
|
282
346
|
),
|
|
347
|
+
fcParams,
|
|
283
348
|
);
|
|
284
349
|
});
|
|
285
350
|
|
|
@@ -288,15 +353,22 @@ describe("fuzz: resolveModelName()", () => {
|
|
|
288
353
|
fc.property(fc.string(), (input) => {
|
|
289
354
|
const trimmed = input.trim().toLowerCase();
|
|
290
355
|
const knownAliases = [
|
|
291
|
-
"sonnet",
|
|
292
|
-
"
|
|
293
|
-
"
|
|
356
|
+
"sonnet",
|
|
357
|
+
"opus",
|
|
358
|
+
"haiku",
|
|
359
|
+
"sonnet-4.6",
|
|
360
|
+
"opus-4.6",
|
|
361
|
+
"haiku-4.5",
|
|
362
|
+
"sonnet-4-6",
|
|
363
|
+
"opus-4-6",
|
|
364
|
+
"haiku-4-5",
|
|
294
365
|
];
|
|
295
366
|
if (knownAliases.includes(trimmed)) return;
|
|
296
367
|
|
|
297
368
|
const result = resolveModelName(input);
|
|
298
369
|
expect(result).toBe(input.trim());
|
|
299
370
|
}),
|
|
371
|
+
fcParams,
|
|
300
372
|
);
|
|
301
373
|
});
|
|
302
374
|
});
|