@fickydev/pigent 0.1.16 → 0.1.18
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/CHANGELOG.md +12 -1
- package/TODO.md +3 -3
- package/drizzle/migrations/0006_flippant_bruce_banner.sql +1 -0
- package/drizzle/migrations/meta/0006_snapshot.json +711 -0
- package/drizzle/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/src/agents/AgentRegistry.ts +4 -4
- package/src/agents/BotCommandHandler.ts +2 -2
- package/src/config/loadConfig.ts +5 -3
- package/src/config/schemas.ts +6 -1
- package/src/daemon/Scheduler.ts +21 -0
- package/src/db/schema.ts +1 -0
- package/src/pi/PiAgentRunner.ts +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.1.18 - 2026-05-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added per-agent skill loading. Agent `skills[]` (relative to agent dir) and profile `defaultSkills[]` (relative to profile dir) are now resolved and passed to Pi SDK via `DefaultResourceLoader.additionalSkillPaths`. Skills are discovered and injected into the system prompt by the Pi SDK.
|
|
8
|
+
- Added `LoadedProfileConfig` type tracking profile `baseDir` for path resolution.
|
|
9
|
+
|
|
10
|
+
## 0.1.17 - 2026-05-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added per-task `maxRunsPerHour` rate limit (default 12) to the scheduler. Skips execution when the cap is reached within the rolling hour window. Configurable per task in `pigent.yaml` task configs. DB-created tasks use the default unless the column is set.
|
|
4
15
|
|
|
5
16
|
## 0.1.16 - 2026-05-18
|
|
6
17
|
|
package/TODO.md
CHANGED
|
@@ -144,8 +144,8 @@
|
|
|
144
144
|
- [x] Reuse Pi session files across messages
|
|
145
145
|
- [x] Self-heal missing runtime `agent_sessions` columns after partial/mismatched migration state
|
|
146
146
|
- [x] Make README more end-user facing
|
|
147
|
-
- [
|
|
148
|
-
- [
|
|
147
|
+
- [x] Confirm per-agent system prompt injection
|
|
148
|
+
- [x] Confirm per-agent skills loading
|
|
149
149
|
- [ ] Confirm per-agent extensions loading
|
|
150
150
|
- [x] Prototype one prompt through Pi SDK
|
|
151
151
|
- [x] Decide CLI fallback strategy if SDK gaps appear
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
- [x] Add `/task remove <id>` command
|
|
167
167
|
- [x] Scheduler loads tasks from both config file + DB
|
|
168
168
|
- [x] Share session lock between Scheduler and AgentRunner (prevent concurrent Pi runs for same agent+chat)
|
|
169
|
-
- [
|
|
169
|
+
- [x] Add max task runs per hour rate limit
|
|
170
170
|
- [ ] Add `/task status` or similar command
|
|
171
171
|
|
|
172
172
|
## Policy And Safety
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE `task_configs` ADD `max_runs_per_hour` integer;
|
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "0b04695e-7837-47b0-b25d-14479ebd7fd8",
|
|
5
|
+
"prevId": "d1b9c948-a4d8-4767-ae22-80aefb5ba800",
|
|
6
|
+
"tables": {
|
|
7
|
+
"agent_sessions": {
|
|
8
|
+
"name": "agent_sessions",
|
|
9
|
+
"columns": {
|
|
10
|
+
"id": {
|
|
11
|
+
"name": "id",
|
|
12
|
+
"type": "text",
|
|
13
|
+
"primaryKey": true,
|
|
14
|
+
"notNull": true,
|
|
15
|
+
"autoincrement": false
|
|
16
|
+
},
|
|
17
|
+
"agent_id": {
|
|
18
|
+
"name": "agent_id",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true,
|
|
22
|
+
"autoincrement": false
|
|
23
|
+
},
|
|
24
|
+
"channel": {
|
|
25
|
+
"name": "channel",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": true,
|
|
29
|
+
"autoincrement": false
|
|
30
|
+
},
|
|
31
|
+
"chat_id": {
|
|
32
|
+
"name": "chat_id",
|
|
33
|
+
"type": "text",
|
|
34
|
+
"primaryKey": false,
|
|
35
|
+
"notNull": true,
|
|
36
|
+
"autoincrement": false
|
|
37
|
+
},
|
|
38
|
+
"thread_id": {
|
|
39
|
+
"name": "thread_id",
|
|
40
|
+
"type": "text",
|
|
41
|
+
"primaryKey": false,
|
|
42
|
+
"notNull": false,
|
|
43
|
+
"autoincrement": false
|
|
44
|
+
},
|
|
45
|
+
"user_id": {
|
|
46
|
+
"name": "user_id",
|
|
47
|
+
"type": "text",
|
|
48
|
+
"primaryKey": false,
|
|
49
|
+
"notNull": false,
|
|
50
|
+
"autoincrement": false
|
|
51
|
+
},
|
|
52
|
+
"pi_session_id": {
|
|
53
|
+
"name": "pi_session_id",
|
|
54
|
+
"type": "text",
|
|
55
|
+
"primaryKey": false,
|
|
56
|
+
"notNull": false,
|
|
57
|
+
"autoincrement": false
|
|
58
|
+
},
|
|
59
|
+
"pi_session_path": {
|
|
60
|
+
"name": "pi_session_path",
|
|
61
|
+
"type": "text",
|
|
62
|
+
"primaryKey": false,
|
|
63
|
+
"notNull": false,
|
|
64
|
+
"autoincrement": false
|
|
65
|
+
},
|
|
66
|
+
"instructions_hash": {
|
|
67
|
+
"name": "instructions_hash",
|
|
68
|
+
"type": "text",
|
|
69
|
+
"primaryKey": false,
|
|
70
|
+
"notNull": false,
|
|
71
|
+
"autoincrement": false
|
|
72
|
+
},
|
|
73
|
+
"model": {
|
|
74
|
+
"name": "model",
|
|
75
|
+
"type": "text",
|
|
76
|
+
"primaryKey": false,
|
|
77
|
+
"notNull": false,
|
|
78
|
+
"autoincrement": false
|
|
79
|
+
},
|
|
80
|
+
"thinking_level": {
|
|
81
|
+
"name": "thinking_level",
|
|
82
|
+
"type": "text",
|
|
83
|
+
"primaryKey": false,
|
|
84
|
+
"notNull": false,
|
|
85
|
+
"autoincrement": false
|
|
86
|
+
},
|
|
87
|
+
"active": {
|
|
88
|
+
"name": "active",
|
|
89
|
+
"type": "integer",
|
|
90
|
+
"primaryKey": false,
|
|
91
|
+
"notNull": true,
|
|
92
|
+
"autoincrement": false,
|
|
93
|
+
"default": true
|
|
94
|
+
},
|
|
95
|
+
"ended_at": {
|
|
96
|
+
"name": "ended_at",
|
|
97
|
+
"type": "integer",
|
|
98
|
+
"primaryKey": false,
|
|
99
|
+
"notNull": false,
|
|
100
|
+
"autoincrement": false
|
|
101
|
+
},
|
|
102
|
+
"created_at": {
|
|
103
|
+
"name": "created_at",
|
|
104
|
+
"type": "integer",
|
|
105
|
+
"primaryKey": false,
|
|
106
|
+
"notNull": true,
|
|
107
|
+
"autoincrement": false
|
|
108
|
+
},
|
|
109
|
+
"updated_at": {
|
|
110
|
+
"name": "updated_at",
|
|
111
|
+
"type": "integer",
|
|
112
|
+
"primaryKey": false,
|
|
113
|
+
"notNull": true,
|
|
114
|
+
"autoincrement": false
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"indexes": {
|
|
118
|
+
"agent_sessions_active_key_idx": {
|
|
119
|
+
"name": "agent_sessions_active_key_idx",
|
|
120
|
+
"columns": [
|
|
121
|
+
"agent_id",
|
|
122
|
+
"channel",
|
|
123
|
+
"chat_id",
|
|
124
|
+
"thread_id",
|
|
125
|
+
"active"
|
|
126
|
+
],
|
|
127
|
+
"isUnique": false
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"foreignKeys": {},
|
|
131
|
+
"compositePrimaryKeys": {},
|
|
132
|
+
"uniqueConstraints": {},
|
|
133
|
+
"checkConstraints": {}
|
|
134
|
+
},
|
|
135
|
+
"agents": {
|
|
136
|
+
"name": "agents",
|
|
137
|
+
"columns": {
|
|
138
|
+
"id": {
|
|
139
|
+
"name": "id",
|
|
140
|
+
"type": "text",
|
|
141
|
+
"primaryKey": true,
|
|
142
|
+
"notNull": true,
|
|
143
|
+
"autoincrement": false
|
|
144
|
+
},
|
|
145
|
+
"name": {
|
|
146
|
+
"name": "name",
|
|
147
|
+
"type": "text",
|
|
148
|
+
"primaryKey": false,
|
|
149
|
+
"notNull": true,
|
|
150
|
+
"autoincrement": false
|
|
151
|
+
},
|
|
152
|
+
"profile": {
|
|
153
|
+
"name": "profile",
|
|
154
|
+
"type": "text",
|
|
155
|
+
"primaryKey": false,
|
|
156
|
+
"notNull": true,
|
|
157
|
+
"autoincrement": false
|
|
158
|
+
},
|
|
159
|
+
"workspace": {
|
|
160
|
+
"name": "workspace",
|
|
161
|
+
"type": "text",
|
|
162
|
+
"primaryKey": false,
|
|
163
|
+
"notNull": true,
|
|
164
|
+
"autoincrement": false
|
|
165
|
+
},
|
|
166
|
+
"config_json": {
|
|
167
|
+
"name": "config_json",
|
|
168
|
+
"type": "text",
|
|
169
|
+
"primaryKey": false,
|
|
170
|
+
"notNull": true,
|
|
171
|
+
"autoincrement": false
|
|
172
|
+
},
|
|
173
|
+
"system_prompt": {
|
|
174
|
+
"name": "system_prompt",
|
|
175
|
+
"type": "text",
|
|
176
|
+
"primaryKey": false,
|
|
177
|
+
"notNull": true,
|
|
178
|
+
"autoincrement": false,
|
|
179
|
+
"default": "''"
|
|
180
|
+
},
|
|
181
|
+
"created_at": {
|
|
182
|
+
"name": "created_at",
|
|
183
|
+
"type": "integer",
|
|
184
|
+
"primaryKey": false,
|
|
185
|
+
"notNull": true,
|
|
186
|
+
"autoincrement": false
|
|
187
|
+
},
|
|
188
|
+
"updated_at": {
|
|
189
|
+
"name": "updated_at",
|
|
190
|
+
"type": "integer",
|
|
191
|
+
"primaryKey": false,
|
|
192
|
+
"notNull": true,
|
|
193
|
+
"autoincrement": false
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"indexes": {},
|
|
197
|
+
"foreignKeys": {},
|
|
198
|
+
"compositePrimaryKeys": {},
|
|
199
|
+
"uniqueConstraints": {},
|
|
200
|
+
"checkConstraints": {}
|
|
201
|
+
},
|
|
202
|
+
"heartbeats": {
|
|
203
|
+
"name": "heartbeats",
|
|
204
|
+
"columns": {
|
|
205
|
+
"id": {
|
|
206
|
+
"name": "id",
|
|
207
|
+
"type": "text",
|
|
208
|
+
"primaryKey": true,
|
|
209
|
+
"notNull": true,
|
|
210
|
+
"autoincrement": false
|
|
211
|
+
},
|
|
212
|
+
"agent_id": {
|
|
213
|
+
"name": "agent_id",
|
|
214
|
+
"type": "text",
|
|
215
|
+
"primaryKey": false,
|
|
216
|
+
"notNull": true,
|
|
217
|
+
"autoincrement": false
|
|
218
|
+
},
|
|
219
|
+
"session_id": {
|
|
220
|
+
"name": "session_id",
|
|
221
|
+
"type": "text",
|
|
222
|
+
"primaryKey": false,
|
|
223
|
+
"notNull": false,
|
|
224
|
+
"autoincrement": false
|
|
225
|
+
},
|
|
226
|
+
"status": {
|
|
227
|
+
"name": "status",
|
|
228
|
+
"type": "text",
|
|
229
|
+
"primaryKey": false,
|
|
230
|
+
"notNull": true,
|
|
231
|
+
"autoincrement": false
|
|
232
|
+
},
|
|
233
|
+
"prompt": {
|
|
234
|
+
"name": "prompt",
|
|
235
|
+
"type": "text",
|
|
236
|
+
"primaryKey": false,
|
|
237
|
+
"notNull": true,
|
|
238
|
+
"autoincrement": false
|
|
239
|
+
},
|
|
240
|
+
"result": {
|
|
241
|
+
"name": "result",
|
|
242
|
+
"type": "text",
|
|
243
|
+
"primaryKey": false,
|
|
244
|
+
"notNull": false,
|
|
245
|
+
"autoincrement": false
|
|
246
|
+
},
|
|
247
|
+
"error": {
|
|
248
|
+
"name": "error",
|
|
249
|
+
"type": "text",
|
|
250
|
+
"primaryKey": false,
|
|
251
|
+
"notNull": false,
|
|
252
|
+
"autoincrement": false
|
|
253
|
+
},
|
|
254
|
+
"started_at": {
|
|
255
|
+
"name": "started_at",
|
|
256
|
+
"type": "integer",
|
|
257
|
+
"primaryKey": false,
|
|
258
|
+
"notNull": false,
|
|
259
|
+
"autoincrement": false
|
|
260
|
+
},
|
|
261
|
+
"finished_at": {
|
|
262
|
+
"name": "finished_at",
|
|
263
|
+
"type": "integer",
|
|
264
|
+
"primaryKey": false,
|
|
265
|
+
"notNull": false,
|
|
266
|
+
"autoincrement": false
|
|
267
|
+
},
|
|
268
|
+
"created_at": {
|
|
269
|
+
"name": "created_at",
|
|
270
|
+
"type": "integer",
|
|
271
|
+
"primaryKey": false,
|
|
272
|
+
"notNull": true,
|
|
273
|
+
"autoincrement": false
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"indexes": {},
|
|
277
|
+
"foreignKeys": {},
|
|
278
|
+
"compositePrimaryKeys": {},
|
|
279
|
+
"uniqueConstraints": {},
|
|
280
|
+
"checkConstraints": {}
|
|
281
|
+
},
|
|
282
|
+
"messages": {
|
|
283
|
+
"name": "messages",
|
|
284
|
+
"columns": {
|
|
285
|
+
"id": {
|
|
286
|
+
"name": "id",
|
|
287
|
+
"type": "text",
|
|
288
|
+
"primaryKey": true,
|
|
289
|
+
"notNull": true,
|
|
290
|
+
"autoincrement": false
|
|
291
|
+
},
|
|
292
|
+
"agent_id": {
|
|
293
|
+
"name": "agent_id",
|
|
294
|
+
"type": "text",
|
|
295
|
+
"primaryKey": false,
|
|
296
|
+
"notNull": true,
|
|
297
|
+
"autoincrement": false
|
|
298
|
+
},
|
|
299
|
+
"session_id": {
|
|
300
|
+
"name": "session_id",
|
|
301
|
+
"type": "text",
|
|
302
|
+
"primaryKey": false,
|
|
303
|
+
"notNull": false,
|
|
304
|
+
"autoincrement": false
|
|
305
|
+
},
|
|
306
|
+
"channel": {
|
|
307
|
+
"name": "channel",
|
|
308
|
+
"type": "text",
|
|
309
|
+
"primaryKey": false,
|
|
310
|
+
"notNull": true,
|
|
311
|
+
"autoincrement": false
|
|
312
|
+
},
|
|
313
|
+
"direction": {
|
|
314
|
+
"name": "direction",
|
|
315
|
+
"type": "text",
|
|
316
|
+
"primaryKey": false,
|
|
317
|
+
"notNull": true,
|
|
318
|
+
"autoincrement": false
|
|
319
|
+
},
|
|
320
|
+
"sender_id": {
|
|
321
|
+
"name": "sender_id",
|
|
322
|
+
"type": "text",
|
|
323
|
+
"primaryKey": false,
|
|
324
|
+
"notNull": false,
|
|
325
|
+
"autoincrement": false
|
|
326
|
+
},
|
|
327
|
+
"chat_id": {
|
|
328
|
+
"name": "chat_id",
|
|
329
|
+
"type": "text",
|
|
330
|
+
"primaryKey": false,
|
|
331
|
+
"notNull": false,
|
|
332
|
+
"autoincrement": false
|
|
333
|
+
},
|
|
334
|
+
"thread_id": {
|
|
335
|
+
"name": "thread_id",
|
|
336
|
+
"type": "text",
|
|
337
|
+
"primaryKey": false,
|
|
338
|
+
"notNull": false,
|
|
339
|
+
"autoincrement": false
|
|
340
|
+
},
|
|
341
|
+
"content": {
|
|
342
|
+
"name": "content",
|
|
343
|
+
"type": "text",
|
|
344
|
+
"primaryKey": false,
|
|
345
|
+
"notNull": true,
|
|
346
|
+
"autoincrement": false
|
|
347
|
+
},
|
|
348
|
+
"raw_json": {
|
|
349
|
+
"name": "raw_json",
|
|
350
|
+
"type": "text",
|
|
351
|
+
"primaryKey": false,
|
|
352
|
+
"notNull": false,
|
|
353
|
+
"autoincrement": false
|
|
354
|
+
},
|
|
355
|
+
"created_at": {
|
|
356
|
+
"name": "created_at",
|
|
357
|
+
"type": "integer",
|
|
358
|
+
"primaryKey": false,
|
|
359
|
+
"notNull": true,
|
|
360
|
+
"autoincrement": false
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
"indexes": {},
|
|
364
|
+
"foreignKeys": {},
|
|
365
|
+
"compositePrimaryKeys": {},
|
|
366
|
+
"uniqueConstraints": {},
|
|
367
|
+
"checkConstraints": {}
|
|
368
|
+
},
|
|
369
|
+
"runtime_kv": {
|
|
370
|
+
"name": "runtime_kv",
|
|
371
|
+
"columns": {
|
|
372
|
+
"key": {
|
|
373
|
+
"name": "key",
|
|
374
|
+
"type": "text",
|
|
375
|
+
"primaryKey": true,
|
|
376
|
+
"notNull": true,
|
|
377
|
+
"autoincrement": false
|
|
378
|
+
},
|
|
379
|
+
"value": {
|
|
380
|
+
"name": "value",
|
|
381
|
+
"type": "text",
|
|
382
|
+
"primaryKey": false,
|
|
383
|
+
"notNull": true,
|
|
384
|
+
"autoincrement": false
|
|
385
|
+
},
|
|
386
|
+
"updated_at": {
|
|
387
|
+
"name": "updated_at",
|
|
388
|
+
"type": "integer",
|
|
389
|
+
"primaryKey": false,
|
|
390
|
+
"notNull": true,
|
|
391
|
+
"autoincrement": false
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"indexes": {},
|
|
395
|
+
"foreignKeys": {},
|
|
396
|
+
"compositePrimaryKeys": {},
|
|
397
|
+
"uniqueConstraints": {},
|
|
398
|
+
"checkConstraints": {}
|
|
399
|
+
},
|
|
400
|
+
"task_configs": {
|
|
401
|
+
"name": "task_configs",
|
|
402
|
+
"columns": {
|
|
403
|
+
"id": {
|
|
404
|
+
"name": "id",
|
|
405
|
+
"type": "text",
|
|
406
|
+
"primaryKey": true,
|
|
407
|
+
"notNull": true,
|
|
408
|
+
"autoincrement": false
|
|
409
|
+
},
|
|
410
|
+
"agent": {
|
|
411
|
+
"name": "agent",
|
|
412
|
+
"type": "text",
|
|
413
|
+
"primaryKey": false,
|
|
414
|
+
"notNull": true,
|
|
415
|
+
"autoincrement": false
|
|
416
|
+
},
|
|
417
|
+
"interval_ms": {
|
|
418
|
+
"name": "interval_ms",
|
|
419
|
+
"type": "integer",
|
|
420
|
+
"primaryKey": false,
|
|
421
|
+
"notNull": true,
|
|
422
|
+
"autoincrement": false
|
|
423
|
+
},
|
|
424
|
+
"prompt": {
|
|
425
|
+
"name": "prompt",
|
|
426
|
+
"type": "text",
|
|
427
|
+
"primaryKey": false,
|
|
428
|
+
"notNull": true,
|
|
429
|
+
"autoincrement": false
|
|
430
|
+
},
|
|
431
|
+
"channel": {
|
|
432
|
+
"name": "channel",
|
|
433
|
+
"type": "text",
|
|
434
|
+
"primaryKey": false,
|
|
435
|
+
"notNull": true,
|
|
436
|
+
"autoincrement": false,
|
|
437
|
+
"default": "'telegram'"
|
|
438
|
+
},
|
|
439
|
+
"chat_id": {
|
|
440
|
+
"name": "chat_id",
|
|
441
|
+
"type": "text",
|
|
442
|
+
"primaryKey": false,
|
|
443
|
+
"notNull": true,
|
|
444
|
+
"autoincrement": false
|
|
445
|
+
},
|
|
446
|
+
"enabled": {
|
|
447
|
+
"name": "enabled",
|
|
448
|
+
"type": "integer",
|
|
449
|
+
"primaryKey": false,
|
|
450
|
+
"notNull": true,
|
|
451
|
+
"autoincrement": false,
|
|
452
|
+
"default": true
|
|
453
|
+
},
|
|
454
|
+
"max_runs_per_hour": {
|
|
455
|
+
"name": "max_runs_per_hour",
|
|
456
|
+
"type": "integer",
|
|
457
|
+
"primaryKey": false,
|
|
458
|
+
"notNull": false,
|
|
459
|
+
"autoincrement": false
|
|
460
|
+
},
|
|
461
|
+
"created_at": {
|
|
462
|
+
"name": "created_at",
|
|
463
|
+
"type": "integer",
|
|
464
|
+
"primaryKey": false,
|
|
465
|
+
"notNull": true,
|
|
466
|
+
"autoincrement": false
|
|
467
|
+
},
|
|
468
|
+
"updated_at": {
|
|
469
|
+
"name": "updated_at",
|
|
470
|
+
"type": "integer",
|
|
471
|
+
"primaryKey": false,
|
|
472
|
+
"notNull": true,
|
|
473
|
+
"autoincrement": false
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
"indexes": {},
|
|
477
|
+
"foreignKeys": {},
|
|
478
|
+
"compositePrimaryKeys": {},
|
|
479
|
+
"uniqueConstraints": {},
|
|
480
|
+
"checkConstraints": {}
|
|
481
|
+
},
|
|
482
|
+
"task_runs": {
|
|
483
|
+
"name": "task_runs",
|
|
484
|
+
"columns": {
|
|
485
|
+
"id": {
|
|
486
|
+
"name": "id",
|
|
487
|
+
"type": "text",
|
|
488
|
+
"primaryKey": true,
|
|
489
|
+
"notNull": true,
|
|
490
|
+
"autoincrement": false
|
|
491
|
+
},
|
|
492
|
+
"agent_id": {
|
|
493
|
+
"name": "agent_id",
|
|
494
|
+
"type": "text",
|
|
495
|
+
"primaryKey": false,
|
|
496
|
+
"notNull": true,
|
|
497
|
+
"autoincrement": false
|
|
498
|
+
},
|
|
499
|
+
"task_id": {
|
|
500
|
+
"name": "task_id",
|
|
501
|
+
"type": "text",
|
|
502
|
+
"primaryKey": false,
|
|
503
|
+
"notNull": true,
|
|
504
|
+
"autoincrement": false
|
|
505
|
+
},
|
|
506
|
+
"prompt": {
|
|
507
|
+
"name": "prompt",
|
|
508
|
+
"type": "text",
|
|
509
|
+
"primaryKey": false,
|
|
510
|
+
"notNull": true,
|
|
511
|
+
"autoincrement": false
|
|
512
|
+
},
|
|
513
|
+
"session_id": {
|
|
514
|
+
"name": "session_id",
|
|
515
|
+
"type": "text",
|
|
516
|
+
"primaryKey": false,
|
|
517
|
+
"notNull": false,
|
|
518
|
+
"autoincrement": false
|
|
519
|
+
},
|
|
520
|
+
"status": {
|
|
521
|
+
"name": "status",
|
|
522
|
+
"type": "text",
|
|
523
|
+
"primaryKey": false,
|
|
524
|
+
"notNull": true,
|
|
525
|
+
"autoincrement": false
|
|
526
|
+
},
|
|
527
|
+
"result": {
|
|
528
|
+
"name": "result",
|
|
529
|
+
"type": "text",
|
|
530
|
+
"primaryKey": false,
|
|
531
|
+
"notNull": false,
|
|
532
|
+
"autoincrement": false
|
|
533
|
+
},
|
|
534
|
+
"error": {
|
|
535
|
+
"name": "error",
|
|
536
|
+
"type": "text",
|
|
537
|
+
"primaryKey": false,
|
|
538
|
+
"notNull": false,
|
|
539
|
+
"autoincrement": false
|
|
540
|
+
},
|
|
541
|
+
"started_at": {
|
|
542
|
+
"name": "started_at",
|
|
543
|
+
"type": "integer",
|
|
544
|
+
"primaryKey": false,
|
|
545
|
+
"notNull": false,
|
|
546
|
+
"autoincrement": false
|
|
547
|
+
},
|
|
548
|
+
"finished_at": {
|
|
549
|
+
"name": "finished_at",
|
|
550
|
+
"type": "integer",
|
|
551
|
+
"primaryKey": false,
|
|
552
|
+
"notNull": false,
|
|
553
|
+
"autoincrement": false
|
|
554
|
+
},
|
|
555
|
+
"created_at": {
|
|
556
|
+
"name": "created_at",
|
|
557
|
+
"type": "integer",
|
|
558
|
+
"primaryKey": false,
|
|
559
|
+
"notNull": true,
|
|
560
|
+
"autoincrement": false
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
"indexes": {},
|
|
564
|
+
"foreignKeys": {},
|
|
565
|
+
"compositePrimaryKeys": {},
|
|
566
|
+
"uniqueConstraints": {},
|
|
567
|
+
"checkConstraints": {}
|
|
568
|
+
},
|
|
569
|
+
"telegram_chat_agents": {
|
|
570
|
+
"name": "telegram_chat_agents",
|
|
571
|
+
"columns": {
|
|
572
|
+
"id": {
|
|
573
|
+
"name": "id",
|
|
574
|
+
"type": "text",
|
|
575
|
+
"primaryKey": true,
|
|
576
|
+
"notNull": true,
|
|
577
|
+
"autoincrement": false
|
|
578
|
+
},
|
|
579
|
+
"chat_id": {
|
|
580
|
+
"name": "chat_id",
|
|
581
|
+
"type": "text",
|
|
582
|
+
"primaryKey": false,
|
|
583
|
+
"notNull": true,
|
|
584
|
+
"autoincrement": false
|
|
585
|
+
},
|
|
586
|
+
"agent_id": {
|
|
587
|
+
"name": "agent_id",
|
|
588
|
+
"type": "text",
|
|
589
|
+
"primaryKey": false,
|
|
590
|
+
"notNull": true,
|
|
591
|
+
"autoincrement": false
|
|
592
|
+
},
|
|
593
|
+
"enabled": {
|
|
594
|
+
"name": "enabled",
|
|
595
|
+
"type": "integer",
|
|
596
|
+
"primaryKey": false,
|
|
597
|
+
"notNull": true,
|
|
598
|
+
"autoincrement": false,
|
|
599
|
+
"default": true
|
|
600
|
+
},
|
|
601
|
+
"custom_instructions": {
|
|
602
|
+
"name": "custom_instructions",
|
|
603
|
+
"type": "text",
|
|
604
|
+
"primaryKey": false,
|
|
605
|
+
"notNull": true,
|
|
606
|
+
"autoincrement": false,
|
|
607
|
+
"default": "''"
|
|
608
|
+
},
|
|
609
|
+
"created_at": {
|
|
610
|
+
"name": "created_at",
|
|
611
|
+
"type": "integer",
|
|
612
|
+
"primaryKey": false,
|
|
613
|
+
"notNull": true,
|
|
614
|
+
"autoincrement": false
|
|
615
|
+
},
|
|
616
|
+
"updated_at": {
|
|
617
|
+
"name": "updated_at",
|
|
618
|
+
"type": "integer",
|
|
619
|
+
"primaryKey": false,
|
|
620
|
+
"notNull": true,
|
|
621
|
+
"autoincrement": false
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
"indexes": {
|
|
625
|
+
"telegram_chat_agents_chat_agent_unique": {
|
|
626
|
+
"name": "telegram_chat_agents_chat_agent_unique",
|
|
627
|
+
"columns": [
|
|
628
|
+
"chat_id",
|
|
629
|
+
"agent_id"
|
|
630
|
+
],
|
|
631
|
+
"isUnique": true
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
"foreignKeys": {},
|
|
635
|
+
"compositePrimaryKeys": {},
|
|
636
|
+
"uniqueConstraints": {},
|
|
637
|
+
"checkConstraints": {}
|
|
638
|
+
},
|
|
639
|
+
"telegram_chats": {
|
|
640
|
+
"name": "telegram_chats",
|
|
641
|
+
"columns": {
|
|
642
|
+
"chat_id": {
|
|
643
|
+
"name": "chat_id",
|
|
644
|
+
"type": "text",
|
|
645
|
+
"primaryKey": true,
|
|
646
|
+
"notNull": true,
|
|
647
|
+
"autoincrement": false
|
|
648
|
+
},
|
|
649
|
+
"title": {
|
|
650
|
+
"name": "title",
|
|
651
|
+
"type": "text",
|
|
652
|
+
"primaryKey": false,
|
|
653
|
+
"notNull": false,
|
|
654
|
+
"autoincrement": false
|
|
655
|
+
},
|
|
656
|
+
"default_agent_id": {
|
|
657
|
+
"name": "default_agent_id",
|
|
658
|
+
"type": "text",
|
|
659
|
+
"primaryKey": false,
|
|
660
|
+
"notNull": false,
|
|
661
|
+
"autoincrement": false
|
|
662
|
+
},
|
|
663
|
+
"instructions": {
|
|
664
|
+
"name": "instructions",
|
|
665
|
+
"type": "text",
|
|
666
|
+
"primaryKey": false,
|
|
667
|
+
"notNull": true,
|
|
668
|
+
"autoincrement": false,
|
|
669
|
+
"default": "''"
|
|
670
|
+
},
|
|
671
|
+
"enabled": {
|
|
672
|
+
"name": "enabled",
|
|
673
|
+
"type": "integer",
|
|
674
|
+
"primaryKey": false,
|
|
675
|
+
"notNull": true,
|
|
676
|
+
"autoincrement": false,
|
|
677
|
+
"default": true
|
|
678
|
+
},
|
|
679
|
+
"created_at": {
|
|
680
|
+
"name": "created_at",
|
|
681
|
+
"type": "integer",
|
|
682
|
+
"primaryKey": false,
|
|
683
|
+
"notNull": true,
|
|
684
|
+
"autoincrement": false
|
|
685
|
+
},
|
|
686
|
+
"updated_at": {
|
|
687
|
+
"name": "updated_at",
|
|
688
|
+
"type": "integer",
|
|
689
|
+
"primaryKey": false,
|
|
690
|
+
"notNull": true,
|
|
691
|
+
"autoincrement": false
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
"indexes": {},
|
|
695
|
+
"foreignKeys": {},
|
|
696
|
+
"compositePrimaryKeys": {},
|
|
697
|
+
"uniqueConstraints": {},
|
|
698
|
+
"checkConstraints": {}
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
"views": {},
|
|
702
|
+
"enums": {},
|
|
703
|
+
"_meta": {
|
|
704
|
+
"schemas": {},
|
|
705
|
+
"tables": {},
|
|
706
|
+
"columns": {}
|
|
707
|
+
},
|
|
708
|
+
"internal": {
|
|
709
|
+
"indexes": {}
|
|
710
|
+
}
|
|
711
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { LoadedAgentConfig, LoadedConfig,
|
|
1
|
+
import type { LoadedAgentConfig, LoadedConfig, LoadedProfileConfig } from "../config/schemas";
|
|
2
2
|
import type { Repositories } from "../db/repositories";
|
|
3
3
|
|
|
4
4
|
export class AgentRegistry {
|
|
5
5
|
private readonly agentsById: Map<string, LoadedAgentConfig>;
|
|
6
|
-
private readonly profilesById: Map<string,
|
|
6
|
+
private readonly profilesById: Map<string, LoadedProfileConfig>;
|
|
7
7
|
|
|
8
8
|
constructor(
|
|
9
9
|
private readonly config: LoadedConfig,
|
|
@@ -23,7 +23,7 @@ export class AgentRegistry {
|
|
|
23
23
|
return [...this.agentsById.values()];
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
listProfiles():
|
|
26
|
+
listProfiles(): LoadedProfileConfig[] {
|
|
27
27
|
return [...this.profilesById.values()];
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ export class AgentRegistry {
|
|
|
31
31
|
return this.agentsById.get(id) ?? null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
getProfile(id: string):
|
|
34
|
+
getProfile(id: string): LoadedProfileConfig | null {
|
|
35
35
|
return this.profilesById.get(id) ?? null;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { InboundMessage, InlineKeyboardButton } from "../channels/types";
|
|
3
|
-
import type { LoadedAgentConfig,
|
|
3
|
+
import type { LoadedAgentConfig, LoadedProfileConfig, ModelChoiceConfig } from "../config/schemas";
|
|
4
4
|
import type { AgentSessionRow } from "../db/schema";
|
|
5
5
|
import type { Repositories } from "../db/repositories";
|
|
6
6
|
import { PiAgentRunner, type PiContextUsage } from "../pi/PiAgentRunner";
|
|
@@ -169,7 +169,7 @@ export class BotCommandHandler {
|
|
|
169
169
|
].join("\n");
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
private async loadPiStatus(session: AgentSessionRow, agent: LoadedAgentConfig, profile:
|
|
172
|
+
private async loadPiStatus(session: AgentSessionRow, agent: LoadedAgentConfig, profile: LoadedProfileConfig | null) {
|
|
173
173
|
try {
|
|
174
174
|
return await this.piRunner.status({ agent, profile, session });
|
|
175
175
|
} catch {
|
package/src/config/loadConfig.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import YAML from "yaml";
|
|
5
5
|
import {
|
|
6
6
|
AgentConfigSchema,
|
|
7
7
|
type LoadedAgentConfig,
|
|
8
8
|
type LoadedConfig,
|
|
9
|
+
type LoadedProfileConfig,
|
|
9
10
|
ProfileConfigSchema,
|
|
10
11
|
RootConfigSchema,
|
|
11
12
|
} from "./schemas";
|
|
@@ -60,7 +61,7 @@ async function loadAgents(rootDir: string): Promise<LoadedAgentConfig[]> {
|
|
|
60
61
|
async function loadProfiles(rootDir: string) {
|
|
61
62
|
const profilesDir = join(rootDir, "profiles");
|
|
62
63
|
const entries = await readdir(profilesDir, { withFileTypes: true }).catch(() => []);
|
|
63
|
-
const profiles = [];
|
|
64
|
+
const profiles: LoadedProfileConfig[] = [];
|
|
64
65
|
|
|
65
66
|
for (const entry of entries) {
|
|
66
67
|
if (!entry.isFile()) continue;
|
|
@@ -68,7 +69,8 @@ async function loadProfiles(rootDir: string) {
|
|
|
68
69
|
|
|
69
70
|
const configPath = join(profilesDir, entry.name);
|
|
70
71
|
const parsed = await readYamlFile(configPath);
|
|
71
|
-
|
|
72
|
+
const profile = ProfileConfigSchema.parse(parsed);
|
|
73
|
+
profiles.push({ ...profile, baseDir: dirname(configPath) });
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
return profiles;
|
package/src/config/schemas.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const TaskConfigSchema = z.object({
|
|
|
18
18
|
prompt: z.string().min(1).default("If no useful action is needed, reply exactly: NOOP."),
|
|
19
19
|
channel: z.string().default("telegram"),
|
|
20
20
|
chatId: z.string().optional(),
|
|
21
|
+
maxRunsPerHour: z.number().int().nonnegative().default(12),
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
export const SchedulerConfigSchema = z.object({
|
|
@@ -102,9 +103,13 @@ export type LoadedAgentConfig = AgentConfig & {
|
|
|
102
103
|
systemPrompt: string;
|
|
103
104
|
};
|
|
104
105
|
|
|
106
|
+
export type LoadedProfileConfig = ProfileConfig & {
|
|
107
|
+
baseDir: string;
|
|
108
|
+
};
|
|
109
|
+
|
|
105
110
|
export type LoadedConfig = {
|
|
106
111
|
agents: LoadedAgentConfig[];
|
|
107
|
-
profiles:
|
|
112
|
+
profiles: LoadedProfileConfig[];
|
|
108
113
|
telegramChats: TelegramChatConfig[];
|
|
109
114
|
modelChoices: ModelChoiceConfig[];
|
|
110
115
|
scheduler: SchedulerConfig;
|
package/src/daemon/Scheduler.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type { AgentRunner } from "../agents/AgentRunner";
|
|
|
7
7
|
import type { ChannelAdapter } from "../channels/types";
|
|
8
8
|
import { isTaskDue } from "./taskDue";
|
|
9
9
|
|
|
10
|
+
const ONE_HOUR_MS = 3_600_000;
|
|
11
|
+
|
|
10
12
|
export class Scheduler {
|
|
11
13
|
private running = false;
|
|
12
14
|
private tickTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -70,6 +72,7 @@ export class Scheduler {
|
|
|
70
72
|
prompt: saved.prompt,
|
|
71
73
|
channel: saved.channel,
|
|
72
74
|
chatId: saved.chatId,
|
|
75
|
+
maxRunsPerHour: saved.maxRunsPerHour ?? 12,
|
|
73
76
|
});
|
|
74
77
|
|
|
75
78
|
if (!this.running && this.tasks.length === 1) {
|
|
@@ -108,6 +111,7 @@ export class Scheduler {
|
|
|
108
111
|
prompt: row.prompt,
|
|
109
112
|
channel: row.channel,
|
|
110
113
|
chatId: row.chatId,
|
|
114
|
+
maxRunsPerHour: row.maxRunsPerHour ?? 12,
|
|
111
115
|
}));
|
|
112
116
|
|
|
113
117
|
return [...configTasks, ...dbTaskConfigs];
|
|
@@ -129,6 +133,23 @@ export class Scheduler {
|
|
|
129
133
|
const lastRun = await this.repositories.taskRuns.findLatest(task.agent, task.id);
|
|
130
134
|
if (!isTaskDue(lastRun?.createdAt ?? null, task.intervalMs)) return;
|
|
131
135
|
|
|
136
|
+
// Enforce hourly rate limit
|
|
137
|
+
const maxPerHour = task.maxRunsPerHour ?? 12;
|
|
138
|
+
const recentCount = await this.repositories.taskRuns.countRecentNotified(
|
|
139
|
+
task.agent,
|
|
140
|
+
task.id,
|
|
141
|
+
Date.now() - ONE_HOUR_MS,
|
|
142
|
+
);
|
|
143
|
+
if (recentCount >= maxPerHour) {
|
|
144
|
+
logger.warn("task rate limited — max runs per hour reached", {
|
|
145
|
+
taskId: task.id,
|
|
146
|
+
agent: task.agent,
|
|
147
|
+
recentCount,
|
|
148
|
+
maxPerHour,
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
132
153
|
const agent = this.registry.getAgent(task.agent);
|
|
133
154
|
if (!agent) {
|
|
134
155
|
logger.warn("task references unknown agent, skipping", { taskId: task.id, agent: task.agent });
|
package/src/db/schema.ts
CHANGED
|
@@ -116,6 +116,7 @@ export const taskConfigs = sqliteTable("task_configs", {
|
|
|
116
116
|
channel: text("channel").notNull().default("telegram"),
|
|
117
117
|
chatId: text("chat_id").notNull(),
|
|
118
118
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
119
|
+
maxRunsPerHour: integer("max_runs_per_hour"),
|
|
119
120
|
createdAt: integer("created_at").notNull(),
|
|
120
121
|
updatedAt: integer("updated_at").notNull(),
|
|
121
122
|
});
|
package/src/pi/PiAgentRunner.ts
CHANGED
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { mkdir } from "node:fs/promises";
|
|
10
10
|
import { resolve } from "node:path";
|
|
11
|
-
import type { LoadedAgentConfig,
|
|
11
|
+
import type { LoadedAgentConfig, LoadedProfileConfig } from "../config/schemas";
|
|
12
12
|
import type { AgentSessionRow } from "../db/schema";
|
|
13
13
|
import { resolveModelSelection } from "./PiModelResolver";
|
|
14
14
|
import { loadOrCreatePiSession } from "./PiSessionFactory";
|
|
15
15
|
|
|
16
16
|
export type PiAgentRunInput = {
|
|
17
17
|
agent: LoadedAgentConfig;
|
|
18
|
-
profile:
|
|
18
|
+
profile: LoadedProfileConfig | null;
|
|
19
19
|
session: AgentSessionRow;
|
|
20
20
|
prompt: string;
|
|
21
21
|
};
|
|
@@ -80,11 +80,13 @@ export class PiAgentRunner {
|
|
|
80
80
|
compaction: { enabled: false },
|
|
81
81
|
});
|
|
82
82
|
const agentDir = getAgentDir();
|
|
83
|
+
const skillPaths = resolveSkillPaths(input.agent, input.profile);
|
|
83
84
|
const resourceLoader = new DefaultResourceLoader({
|
|
84
85
|
cwd: workspace,
|
|
85
86
|
agentDir,
|
|
86
87
|
settingsManager,
|
|
87
88
|
systemPromptOverride: () => systemPrompt,
|
|
89
|
+
additionalSkillPaths: skillPaths,
|
|
88
90
|
});
|
|
89
91
|
await resourceLoader.reload();
|
|
90
92
|
|
|
@@ -109,7 +111,23 @@ export class PiAgentRunner {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
function
|
|
114
|
+
function resolveSkillPaths(agent: LoadedAgentConfig, profile: LoadedProfileConfig | null): string[] {
|
|
115
|
+
const paths: string[] = [];
|
|
116
|
+
|
|
117
|
+
if (profile) {
|
|
118
|
+
for (const s of profile.defaultSkills) {
|
|
119
|
+
paths.push(resolve(profile.baseDir, s));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const s of agent.skills) {
|
|
124
|
+
paths.push(resolve(agent.baseDir, s));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return paths;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function composeSystemPrompt(agent: LoadedAgentConfig, profile: LoadedProfileConfig | null): string {
|
|
113
131
|
return [
|
|
114
132
|
profile?.instructions,
|
|
115
133
|
agent.systemPrompt,
|