@flame0510/project-aether 1.5.0 → 1.5.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.
@@ -2,9 +2,10 @@
2
2
  * Persisted state of agent updates: the `agent_upgrades` table (lib/db-bootstrap.mjs).
3
3
  *
4
4
  * Kept apart from lib/agent-update.ts so the provider sync can ask which agents are
5
- * being updated without importing the update action, which imports the sync.
5
+ * being updated without importing the update action, which imports the sync. The
6
+ * machinery is lib/agent-job-state.ts; this file holds the table's types and names.
6
7
  */
7
- import { openDb } from '@/lib/db';
8
+ import { createJobState } from '@/lib/agent-job-state';
8
9
 
9
10
  export type UpdateStatus =
10
11
  | 'pending'
@@ -38,65 +39,36 @@ export interface AgentUpdateRow {
38
39
 
39
40
  const FINAL: readonly UpdateStatus[] = ['done', 'failed', 'interrupted', 'rolled_back', 'rollback_failed'];
40
41
 
41
- export function insertUpdate(agentId: string, fromVersion: string | null, toVersion: string): number {
42
- const db = openDb(false);
43
- try {
44
- const now = Date.now();
45
- const info = db.prepare(
46
- `INSERT INTO agent_upgrades (agent_id, status, from_version, to_version, started_at, updated_at)
47
- VALUES (?, 'pending', ?, ?, ?, ?)`,
48
- ).run(agentId, fromVersion, toVersion, now, now);
49
- return Number(info.lastInsertRowid);
50
- } finally {
51
- db.close();
52
- }
53
- }
42
+ const state = createJobState({
43
+ table: 'agent_upgrades',
44
+ initialStatus: 'pending',
45
+ activeStatuses: ACTIVE_STATUSES,
46
+ finalStatuses: FINAL,
47
+ insertColumns: ['from_version', 'to_version'],
48
+ writableColumns: ['backup_file', 'baseline_json', 'verify_json'],
49
+ });
54
50
 
55
51
  type Writable = Partial<Pick<AgentUpdateRow, 'status' | 'backup_file' | 'baseline_json' | 'verify_json' | 'error'>>;
56
52
 
53
+ export function insertUpdate(agentId: string, fromVersion: string | null, toVersion: string): number {
54
+ return state.insert(agentId, fromVersion, toVersion);
55
+ }
56
+
57
57
  export function updateRow(id: number, fields: Writable): void {
58
- const entries = Object.entries(fields).filter(([, v]) => v !== undefined);
59
- const now = Date.now();
60
- const sets = [...entries.map(([k]) => `${k} = ?`), 'updated_at = ?'];
61
- const values: unknown[] = [...entries.map(([, v]) => v), now];
62
- if (fields.status && FINAL.includes(fields.status)) {
63
- sets.push('finished_at = ?');
64
- values.push(now);
65
- } else if (fields.status) {
66
- sets.push('finished_at = NULL');
67
- }
68
- const db = openDb(false);
69
- try {
70
- db.prepare(`UPDATE agent_upgrades SET ${sets.join(', ')} WHERE id = ?`).run(...values, id);
71
- } finally {
72
- db.close();
73
- }
58
+ state.update(id, fields);
74
59
  }
75
60
 
76
61
  export function latestUpdate(agentId: string): AgentUpdateRow | null {
77
- const db = openDb(true);
78
- try {
79
- return (db.prepare('SELECT * FROM agent_upgrades WHERE agent_id = ? ORDER BY id DESC LIMIT 1').get(agentId) as AgentUpdateRow | undefined) ?? null;
80
- } finally {
81
- db.close();
82
- }
62
+ return state.latest<AgentUpdateRow>(agentId);
83
63
  }
84
64
 
85
65
  export function isUpdateActive(agentId: string): boolean {
86
- const row = latestUpdate(agentId);
87
- return !!row && ACTIVE_STATUSES.includes(row.status);
66
+ return state.isActive(agentId);
88
67
  }
89
68
 
90
69
  /** AGENT_IDs with an update in an active status. */
91
70
  export function activeUpdateAgentIds(): Set<string> {
92
- const db = openDb(true);
93
- try {
94
- const placeholders = ACTIVE_STATUSES.map(() => '?').join(', ');
95
- const rows = db.prepare(`SELECT DISTINCT agent_id FROM agent_upgrades WHERE status IN (${placeholders})`).all(...ACTIVE_STATUSES) as { agent_id: string }[];
96
- return new Set(rows.map((r) => r.agent_id));
97
- } finally {
98
- db.close();
99
- }
71
+ return state.activeAgentIds();
100
72
  }
101
73
 
102
74
  /**
@@ -105,18 +77,10 @@ export function activeUpdateAgentIds(): Set<string> {
105
77
  * between Rollback and a new Update.
106
78
  */
107
79
  export function markInterruptedUpdates(): number {
108
- const db = openDb(false);
109
- try {
110
- const placeholders = ACTIVE_STATUSES.map(() => '?').join(', ');
111
- const now = Date.now();
112
- const info = db.prepare(
113
- `UPDATE agent_upgrades
114
- SET error = COALESCE(error, 'Rev4a restarted while this step was running: ' || status),
115
- status = 'interrupted', updated_at = ?, finished_at = ?
116
- WHERE status IN (${placeholders})`,
117
- ).run(now, now, ...ACTIVE_STATUSES);
118
- return info.changes;
119
- } finally {
120
- db.close();
121
- }
80
+ return state.markInterrupted();
81
+ }
82
+
83
+ /** Keep the newest `keepPerAgent` updates of each agent; the rest is history. */
84
+ export function pruneUpdates(keepPerAgent: number): number {
85
+ return state.prune(keepPerAgent);
122
86
  }
@@ -287,6 +287,12 @@ export interface ApprovedPairing {
287
287
  export interface PairingState {
288
288
  pending: PendingPairing[];
289
289
  approved: ApprovedPairing[];
290
+ /**
291
+ * Why the approved list is empty, when that is not simply "none": the pairing store
292
+ * could not be read (the writer is not in this OpenClaw build, or the read failed).
293
+ * Callers must show it instead of "No approved senders", which would be a guess.
294
+ */
295
+ error?: string | null;
290
296
  }
291
297
 
292
298
  /**
@@ -428,8 +434,13 @@ export async function getPairings(agentId: string, channel: string = 'telegram')
428
434
  if (store.status === 'ok') {
429
435
  for (const entry of store.entries ?? []) approved.push({ senderId: String(entry) });
430
436
  }
437
+ // 'absent' is a real answer (no store yet, so no approved senders); anything else means
438
+ // the read did not succeed and an empty list must not be presented as "none".
439
+ const error = store.status === 'ok' || store.status === 'absent'
440
+ ? null
441
+ : (store.error ?? 'The approved senders could not be read');
431
442
 
432
- return { pending, approved };
443
+ return { pending, approved, error };
433
444
  }
434
445
 
435
446
  /** Approve a pending pairing code */
@@ -103,6 +103,18 @@
103
103
  "input": 0.9,
104
104
  "output": 3.73
105
105
  },
106
+ "glm/glm-5.3": {
107
+ "input": 1.4,
108
+ "output": 4.4
109
+ },
110
+ "glm/glm-5.3-flash": {
111
+ "input": 0.15,
112
+ "output": 0.5
113
+ },
114
+ "glm/glm-5.3-flashx": {
115
+ "input": 0.37,
116
+ "output": 1.25
117
+ },
106
118
  "glm/glm-5.2": {
107
119
  "input": 1.4,
108
120
  "output": 4.4
@@ -164,8 +176,8 @@
164
176
  "output": 0
165
177
  },
166
178
  "openrouter/deepseek/deepseek-v4-flash": {
167
- "input": 0.0659,
168
- "output": 0.1319
179
+ "input": 0.08708,
180
+ "output": 0.17416
169
181
  },
170
182
  "openrouter/google/gemini-2.5-flash": {
171
183
  "input": 0.3,
@@ -180,8 +192,8 @@
180
192
  "output": 15
181
193
  },
182
194
  "openrouter/deepseek/deepseek-v4-pro": {
183
- "input": 1.6,
184
- "output": 3.2
195
+ "input": 0.95526,
196
+ "output": 1.91052
185
197
  },
186
198
  "openrouter/qwen/qwen3.7-max": {
187
199
  "input": 1.475,
@@ -204,12 +216,12 @@
204
216
  "output": 1.125
205
217
  },
206
218
  "openrouter/qwen/qwen3.6-27b": {
207
- "input": 0.3,
208
- "output": 2
219
+ "input": 0.32,
220
+ "output": 2.7
209
221
  },
210
222
  "openrouter/qwen/qwen3.6-35b-a3b": {
211
- "input": 0.1,
212
- "output": 0.9
223
+ "input": 0.15,
224
+ "output": 1
213
225
  },
214
226
  "openrouter/qwen/qwen3-coder": {
215
227
  "input": 0.3,
@@ -248,8 +260,8 @@
248
260
  "output": 1.2
249
261
  },
250
262
  "openrouter/moonshotai/kimi-k2.7-code": {
251
- "input": 0.71,
252
- "output": 3.5
263
+ "input": 0.7062,
264
+ "output": 3.3
253
265
  },
254
266
  "openrouter/moonshotai/kimi-k2.6": {
255
267
  "input": 0.95,
@@ -264,12 +276,24 @@
264
276
  "output": 2.5
265
277
  },
266
278
  "openrouter/moonshotai/kimi-k3": {
267
- "input": 2.6481,
268
- "output": 13.2827
279
+ "input": 3,
280
+ "output": 15
281
+ },
282
+ "openrouter/z-ai/glm-5.3": {
283
+ "input": 0.5614,
284
+ "output": 1.7644
285
+ },
286
+ "openrouter/z-ai/glm-5.3-flash": {
287
+ "input": 0.15,
288
+ "output": 0.5
289
+ },
290
+ "openrouter/z-ai/glm-5.3-flashx": {
291
+ "input": 0.37,
292
+ "output": 1.25
269
293
  },
270
294
  "openrouter/z-ai/glm-5.2": {
271
- "input": 0.6,
272
- "output": 2
295
+ "input": 0.6496,
296
+ "output": 2.0416
273
297
  },
274
298
  "openrouter/z-ai/glm-5.1": {
275
299
  "input": 0.966,
@@ -304,8 +328,8 @@
304
328
  "output": 0.3
305
329
  },
306
330
  "openrouter/tencent/hy3": {
307
- "input": 0.0825,
308
- "output": 0.33
331
+ "input": 0.132,
332
+ "output": 0.528
309
333
  },
310
334
  "openrouter/tencent/hy3-preview": {
311
335
  "input": 0.18,
@@ -344,8 +368,8 @@
344
368
  "output": 1.9
345
369
  },
346
370
  "openrouter/meta-llama/llama-4-maverick": {
347
- "input": 0.2,
348
- "output": 0.696
371
+ "input": 0.1875,
372
+ "output": 0.6525
349
373
  },
350
374
  "openrouter/meta-llama/llama-4-scout": {
351
375
  "input": 0.1,
@@ -380,12 +404,12 @@
380
404
  "output": 3.2
381
405
  },
382
406
  "openrouter/nvidia/nemotron-3-super-120b-a12b": {
383
- "input": 0.085,
384
- "output": 0.4
407
+ "input": 0.08,
408
+ "output": 0.45
385
409
  },
386
410
  "openrouter/nvidia/nemotron-3-ultra-550b-a55b": {
387
- "input": 0.625,
388
- "output": 3.125
411
+ "input": 0.6,
412
+ "output": 2.4
389
413
  },
390
414
  "openrouter/nousresearch/hermes-4-405b": {
391
415
  "input": 1,
@@ -486,5 +510,217 @@
486
510
  "openrouter/deepseek/deepseek-v4-flash-vision-exp": {
487
511
  "input": 0.22,
488
512
  "output": 0.66
513
+ },
514
+ "openrouter/openai/gpt-5-image": {
515
+ "input": 10.0,
516
+ "output": 10.0
517
+ },
518
+ "openrouter/openai/gpt-5-image-mini": {
519
+ "input": 2.5,
520
+ "output": 2.0
521
+ },
522
+ "openrouter/openai/gpt-5-mini": {
523
+ "input": 0.25,
524
+ "output": 2.0
525
+ },
526
+ "openrouter/openai/gpt-5-nano": {
527
+ "input": 0.05,
528
+ "output": 0.4
529
+ },
530
+ "openrouter/openai/gpt-5-pro": {
531
+ "input": 15.0,
532
+ "output": 120.0
533
+ },
534
+ "openrouter/openai/gpt-5.2": {
535
+ "input": 1.75,
536
+ "output": 14.0
537
+ },
538
+ "openrouter/openai/gpt-5.2-chat": {
539
+ "input": 1.75,
540
+ "output": 14.0
541
+ },
542
+ "openrouter/openai/gpt-5.2-codex": {
543
+ "input": 1.75,
544
+ "output": 14.0
545
+ },
546
+ "openrouter/openai/gpt-5.2-pro": {
547
+ "input": 21.0,
548
+ "output": 168.0
549
+ },
550
+ "openrouter/openai/gpt-5.3-codex": {
551
+ "input": 1.75,
552
+ "output": 14.0
553
+ },
554
+ "openrouter/openai/gpt-5.4": {
555
+ "input": 2.5,
556
+ "output": 15.0
557
+ },
558
+ "openrouter/openai/gpt-5.4-image-2": {
559
+ "input": 8.0,
560
+ "output": 15.0
561
+ },
562
+ "openrouter/openai/gpt-5.4-nano": {
563
+ "input": 0.2,
564
+ "output": 1.25
565
+ },
566
+ "openrouter/openai/gpt-5.4-pro": {
567
+ "input": 30.0,
568
+ "output": 180.0
569
+ },
570
+ "openrouter/openai/gpt-5.5": {
571
+ "input": 5.0,
572
+ "output": 30.0
573
+ },
574
+ "openrouter/openai/gpt-5.5-pro": {
575
+ "input": 30.0,
576
+ "output": 180.0
577
+ },
578
+ "openrouter/openai/gpt-5.6-luna-pro": {
579
+ "input": 0.2,
580
+ "output": 1.2
581
+ },
582
+ "openrouter/openai/gpt-5.6-sol-pro": {
583
+ "input": 2.0,
584
+ "output": 10.0
585
+ },
586
+ "openrouter/openai/gpt-5.6-terra": {
587
+ "input": 2.0,
588
+ "output": 12.0
589
+ },
590
+ "openrouter/openai/gpt-5.6-terra-pro": {
591
+ "input": 2.0,
592
+ "output": 12.0
593
+ },
594
+ "openrouter/openai/gpt-6-astra": {
595
+ "input": 10.0,
596
+ "output": 50.0
597
+ },
598
+ "openrouter/openai/gpt-6-astra-pro": {
599
+ "input": 10.0,
600
+ "output": 50.0
601
+ },
602
+ "openrouter/openai/gpt-6-luna": {
603
+ "input": 0.1,
604
+ "output": 0.5
605
+ },
606
+ "openrouter/openai/gpt-6-luna-pro": {
607
+ "input": 0.1,
608
+ "output": 0.5
609
+ },
610
+ "openrouter/openai/gpt-6-sol": {
611
+ "input": 2.0,
612
+ "output": 10.0
613
+ },
614
+ "openrouter/openai/gpt-6-sol-pro": {
615
+ "input": 2.0,
616
+ "output": 10.0
617
+ },
618
+ "openrouter/anthropic/claude-fable-5.1": {
619
+ "input": 10.0,
620
+ "output": 50.0
621
+ },
622
+ "openrouter/anthropic/claude-opus-4.5": {
623
+ "input": 5.0,
624
+ "output": 25.0
625
+ },
626
+ "openrouter/anthropic/claude-opus-4.6": {
627
+ "input": 5.0,
628
+ "output": 25.0
629
+ },
630
+ "openrouter/anthropic/claude-opus-4.8": {
631
+ "input": 5.0,
632
+ "output": 25.0
633
+ },
634
+ "openrouter/anthropic/claude-opus-5.5": {
635
+ "input": 4.0,
636
+ "output": 20.0
637
+ },
638
+ "openrouter/google/gemini-3.5-flash": {
639
+ "input": 1.5,
640
+ "output": 9.0
641
+ },
642
+ "openrouter/google/gemini-3.5-flash-lite": {
643
+ "input": 0.3,
644
+ "output": 2.5
645
+ },
646
+ "openrouter/google/gemini-3.7-flash": {
647
+ "input": 0.75,
648
+ "output": 3.75
649
+ },
650
+ "openrouter/google/gemini-3.8-flash": {
651
+ "input": 0.75,
652
+ "output": 3.75
653
+ },
654
+ "openrouter/qwen/qwen3.5-122b-a10b": {
655
+ "input": 0.26,
656
+ "output": 2.08
657
+ },
658
+ "openrouter/qwen/qwen3.5-27b": {
659
+ "input": 0.195,
660
+ "output": 1.56
661
+ },
662
+ "openrouter/qwen/qwen3.5-35b-a3b": {
663
+ "input": 0.3125,
664
+ "output": 1.25
665
+ },
666
+ "openrouter/qwen/qwen3.5-397b-a17b": {
667
+ "input": 0.55,
668
+ "output": 3.5
669
+ },
670
+ "openrouter/qwen/qwen3.5-9b": {
671
+ "input": 0.1,
672
+ "output": 0.15
673
+ },
674
+ "openrouter/qwen/qwen3.5-flash-02-23": {
675
+ "input": 0.065,
676
+ "output": 0.26
677
+ },
678
+ "openrouter/qwen/qwen3.5-plus-02-15": {
679
+ "input": 0.26,
680
+ "output": 1.56
681
+ },
682
+ "openrouter/qwen/qwen3.5-plus-20260420": {
683
+ "input": 0.3,
684
+ "output": 1.8
685
+ },
686
+ "openrouter/qwen/qwen3.8-2.4t-a95b": {
687
+ "input": 2.0,
688
+ "output": 6.0
689
+ },
690
+ "openrouter/qwen/qwen3.8-27b": {
691
+ "input": 0.42,
692
+ "output": 3.0
693
+ },
694
+ "openrouter/qwen/qwen3.8-flash": {
695
+ "input": 0.15,
696
+ "output": 0.47
697
+ },
698
+ "openrouter/qwen/qwen3.8-max-0902": {
699
+ "input": 2.0,
700
+ "output": 6.0
701
+ },
702
+ "openrouter/qwen/qwen3.8-omni-flash": {
703
+ "input": 0.15,
704
+ "output": 0.47
705
+ },
706
+ "openrouter/deepseek/deepseek-v4-flash-0731": {
707
+ "input": 0.04,
708
+ "output": 0.64
709
+ },
710
+ "openrouter/deepseek/deepseek-v4-pro-0813": {
711
+ "input": 0.462,
712
+ "output": 1.386
713
+ },
714
+ "openrouter/moonshotai/kimi-k2.5": {
715
+ "input": 0.45,
716
+ "output": 2.25
717
+ },
718
+ "openrouter/x-ai/grok-4.6": {
719
+ "input": 2.0,
720
+ "output": 6.0
721
+ },
722
+ "openrouter/x-ai/grok-4.7": {
723
+ "input": 1.6,
724
+ "output": 4.8
489
725
  }
490
726
  }