agent-working-memory 0.7.16 → 0.8.5
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 +156 -15
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +2 -16
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +2 -11
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/common.d.ts +18 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +254 -141
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/cursor.d.ts.map +1 -1
- package/dist/adapters/cursor.js +2 -15
- package/dist/adapters/cursor.js.map +1 -1
- package/dist/adapters/http.d.ts.map +1 -1
- package/dist/adapters/http.js +28 -34
- package/dist/adapters/http.js.map +1 -1
- package/dist/api/routes.d.ts +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +280 -113
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts +42 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +268 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli.js +45 -1
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.d.ts +24 -0
- package/dist/coordination/circuit-breaker.d.ts.map +1 -0
- package/dist/coordination/circuit-breaker.js +70 -0
- package/dist/coordination/circuit-breaker.js.map +1 -0
- package/dist/coordination/failure-modes.d.ts +21 -0
- package/dist/coordination/failure-modes.d.ts.map +1 -0
- package/dist/coordination/failure-modes.js +49 -0
- package/dist/coordination/failure-modes.js.map +1 -0
- package/dist/coordination/peer-decisions.js +7 -7
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +44 -1
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +18 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +9 -9
- package/dist/coordination/stale.d.ts +9 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +53 -9
- package/dist/coordination/stale.js.map +1 -1
- package/dist/core/embeddings.d.ts +16 -10
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +54 -38
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/ml-worker-entry.d.ts +17 -0
- package/dist/core/ml-worker-entry.d.ts.map +1 -0
- package/dist/core/ml-worker-entry.js +193 -0
- package/dist/core/ml-worker-entry.js.map +1 -0
- package/dist/core/ml-worker.d.ts +59 -0
- package/dist/core/ml-worker.d.ts.map +1 -0
- package/dist/core/ml-worker.js +253 -0
- package/dist/core/ml-worker.js.map +1 -0
- package/dist/core/query-expander.d.ts +13 -15
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +48 -54
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts +9 -19
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +33 -35
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +32 -5
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +201 -28
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +128 -0
- package/dist/core/write-pipeline.d.ts.map +1 -0
- package/dist/core/write-pipeline.js +405 -0
- package/dist/core/write-pipeline.js.map +1 -0
- package/dist/engine/activation.d.ts +2 -2
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +238 -115
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/confidence.d.ts +62 -0
- package/dist/engine/confidence.d.ts.map +1 -0
- package/dist/engine/confidence.js +100 -0
- package/dist/engine/confidence.js.map +1 -0
- package/dist/engine/connections.d.ts +47 -8
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +83 -22
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +46 -10
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +170 -50
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts +16 -2
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +143 -44
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts +2 -2
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +9 -9
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts +4 -4
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +15 -15
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/retraction.d.ts +93 -5
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +238 -19
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +3 -3
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +4 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +48 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +110 -155
- package/dist/mcp.js.map +1 -1
- package/dist/storage/factory.d.ts +55 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +135 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts +20 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -0
- package/dist/storage/pglite-schema.js +165 -0
- package/dist/storage/pglite-schema.js.map +1 -0
- package/dist/storage/pglite.d.ts +187 -0
- package/dist/storage/pglite.d.ts.map +1 -0
- package/dist/storage/pglite.js +1114 -0
- package/dist/storage/pglite.js.map +1 -0
- package/dist/storage/sqlite.d.ts +117 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +357 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.d.ts +63 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +27 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/types/engram.d.ts +92 -2
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +3 -1
- package/src/adapters/claude-code.ts +218 -234
- package/src/adapters/codex.ts +252 -262
- package/src/adapters/common.ts +411 -284
- package/src/adapters/cursor.ts +135 -150
- package/src/adapters/http.ts +93 -100
- package/src/adapters/index.ts +31 -31
- package/src/adapters/types.ts +75 -75
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +367 -120
- package/src/cli/migrate.ts +307 -0
- package/src/cli.ts +37 -1
- package/src/coordination/circuit-breaker.ts +83 -0
- package/src/coordination/events.ts +90 -90
- package/src/coordination/failure-modes.ts +50 -0
- package/src/coordination/peer-decisions.ts +105 -105
- package/src/coordination/plugin-loader.ts +60 -60
- package/src/coordination/plugin.ts +44 -44
- package/src/coordination/routes.ts +56 -1
- package/src/coordination/schema.ts +13 -0
- package/src/coordination/stale.ts +80 -11
- package/src/coordination/types.ts +311 -311
- package/src/coordination/write-mutex.ts +69 -69
- package/src/core/auto-tagger.ts +168 -168
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -0
- package/src/core/ml-worker.ts +281 -0
- package/src/core/query-expander.ts +122 -128
- package/src/core/reranker.ts +119 -125
- package/src/core/salience.ts +200 -33
- package/src/core/write-pipeline.ts +521 -0
- package/src/engine/activation.ts +235 -109
- package/src/engine/confidence.ts +120 -0
- package/src/engine/connections.ts +162 -103
- package/src/engine/consolidation-scheduler.ts +242 -125
- package/src/engine/consolidation.ts +138 -45
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -116
- package/src/engine/staging.ts +74 -74
- package/src/hooks/sidecar.ts +5 -5
- package/src/index.ts +42 -28
- package/src/mcp.ts +105 -152
- package/src/storage/factory.ts +147 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -0
- package/src/storage/pglite.ts +1363 -0
- package/src/storage/sqlite.ts +402 -7
- package/src/storage/store.ts +80 -0
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +101 -3
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* `awm migrate` — Convert a SQLite AWM database to a PGlite store.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* awm migrate --from <sqlite.db> --to <pglite-dir> [--dry-run]
|
|
8
|
+
*
|
|
9
|
+
* Reads each table from the source SQLite database and writes it to a fresh
|
|
10
|
+
* PGlite directory. Embedding BLOBs are converted from Float32Array bytes to
|
|
11
|
+
* pgvector text format (`[v1,v2,...]`). Integer booleans (0/1) become
|
|
12
|
+
* Postgres BOOLEAN. Timestamps are passed through as ISO strings.
|
|
13
|
+
*
|
|
14
|
+
* Tables migrated:
|
|
15
|
+
* - engrams + their derived FTS index (regenerated by PGlite trigger)
|
|
16
|
+
* - associations
|
|
17
|
+
* - agents
|
|
18
|
+
* - activation_events
|
|
19
|
+
* - staging_events
|
|
20
|
+
* - retrieval_feedback
|
|
21
|
+
* - episodes
|
|
22
|
+
* - conscious_state
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import Database from 'better-sqlite3';
|
|
26
|
+
import { PGlite } from '@electric-sql/pglite';
|
|
27
|
+
import { vector } from '@electric-sql/pglite/vector';
|
|
28
|
+
import { PGLITE_SCHEMA_DDL } from '../storage/pglite-schema.js';
|
|
29
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
30
|
+
|
|
31
|
+
function bufferToFloat32Array(buf: Buffer): Float32Array {
|
|
32
|
+
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
33
|
+
return new Float32Array(ab);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function vectorLiteral(values: number[] | null): string | null {
|
|
37
|
+
if (!values || values.length === 0) return null;
|
|
38
|
+
return '[' + values.join(',') + ']';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function intToBool(v: unknown): boolean {
|
|
42
|
+
return v === 1 || v === true || v === '1' || v === 'true';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface MigrateOptions {
|
|
46
|
+
from: string;
|
|
47
|
+
to: string;
|
|
48
|
+
dryRun?: boolean;
|
|
49
|
+
batchSize?: number;
|
|
50
|
+
verbose?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface MigrateStats {
|
|
54
|
+
engrams: number;
|
|
55
|
+
associations: number;
|
|
56
|
+
agents: number;
|
|
57
|
+
activationEvents: number;
|
|
58
|
+
stagingEvents: number;
|
|
59
|
+
retrievalFeedback: number;
|
|
60
|
+
episodes: number;
|
|
61
|
+
consciousState: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function migrate(opts: MigrateOptions): Promise<MigrateStats> {
|
|
65
|
+
const { from, to, dryRun = false, batchSize = 500, verbose = false } = opts;
|
|
66
|
+
|
|
67
|
+
if (!existsSync(from)) {
|
|
68
|
+
throw new Error(`Source SQLite database not found: ${from}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!dryRun && !existsSync(to)) {
|
|
72
|
+
mkdirSync(to, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const src = new Database(from, { readonly: true });
|
|
76
|
+
const dst = dryRun ? null : await PGlite.create(to, { extensions: { vector } });
|
|
77
|
+
|
|
78
|
+
if (dst) {
|
|
79
|
+
await dst.exec(PGLITE_SCHEMA_DDL);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const stats: MigrateStats = {
|
|
83
|
+
engrams: 0,
|
|
84
|
+
associations: 0,
|
|
85
|
+
agents: 0,
|
|
86
|
+
activationEvents: 0,
|
|
87
|
+
stagingEvents: 0,
|
|
88
|
+
retrievalFeedback: 0,
|
|
89
|
+
episodes: 0,
|
|
90
|
+
consciousState: 0,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// ── engrams ─────────────────────────────────────────────
|
|
94
|
+
const engramRows = src.prepare(`SELECT * FROM engrams`).all() as any[];
|
|
95
|
+
if (verbose) console.log(`engrams: ${engramRows.length} rows to migrate`);
|
|
96
|
+
if (dst && engramRows.length > 0) {
|
|
97
|
+
for (let i = 0; i < engramRows.length; i += batchSize) {
|
|
98
|
+
const batch = engramRows.slice(i, i + batchSize);
|
|
99
|
+
for (const r of batch) {
|
|
100
|
+
const embedding = r.embedding
|
|
101
|
+
? vectorLiteral(Array.from(bufferToFloat32Array(r.embedding)))
|
|
102
|
+
: null;
|
|
103
|
+
await dst.query(
|
|
104
|
+
`INSERT INTO engrams (
|
|
105
|
+
id, agent_id, concept, content, embedding, embedding_model,
|
|
106
|
+
confidence, salience, access_count, last_accessed, created_at,
|
|
107
|
+
salience_features, reason_codes, stage, ttl,
|
|
108
|
+
retracted, retracted_by, retracted_at, tags, memory_type,
|
|
109
|
+
memory_class, superseded_by, supersedes, episode_id,
|
|
110
|
+
task_status, task_priority, blocked_by, sequence, references_json
|
|
111
|
+
) VALUES (
|
|
112
|
+
$1, $2, $3, $4, $5::vector, $6, $7, $8, $9, $10, $11,
|
|
113
|
+
$12, $13, $14, $15, $16, $17, $18, $19, $20,
|
|
114
|
+
$21, $22, $23, $24, $25, $26, $27, $28, $29
|
|
115
|
+
)`,
|
|
116
|
+
[
|
|
117
|
+
r.id, r.agent_id, r.concept, r.content, embedding, r.embedding_model,
|
|
118
|
+
r.confidence, r.salience, r.access_count, r.last_accessed, r.created_at,
|
|
119
|
+
r.salience_features ?? '{}', r.reason_codes ?? '[]',
|
|
120
|
+
r.stage ?? 'active', r.ttl,
|
|
121
|
+
intToBool(r.retracted), r.retracted_by, r.retracted_at,
|
|
122
|
+
r.tags ?? '[]', r.memory_type ?? 'unclassified',
|
|
123
|
+
r.memory_class ?? 'working', r.superseded_by, r.supersedes, r.episode_id,
|
|
124
|
+
r.task_status, r.task_priority, r.blocked_by, r.sequence, r.references_json,
|
|
125
|
+
],
|
|
126
|
+
);
|
|
127
|
+
stats.engrams++;
|
|
128
|
+
}
|
|
129
|
+
if (verbose) process.stdout.write(` ${Math.min(i + batchSize, engramRows.length)}/${engramRows.length}\r`);
|
|
130
|
+
}
|
|
131
|
+
if (verbose) console.log(` engrams: done (${stats.engrams})`);
|
|
132
|
+
} else if (dryRun) {
|
|
133
|
+
stats.engrams = engramRows.length;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── associations ────────────────────────────────────────
|
|
137
|
+
const assocRows = src.prepare(`SELECT * FROM associations`).all() as any[];
|
|
138
|
+
if (verbose) console.log(`associations: ${assocRows.length} rows`);
|
|
139
|
+
if (dst && assocRows.length > 0) {
|
|
140
|
+
for (let i = 0; i < assocRows.length; i += batchSize) {
|
|
141
|
+
const batch = assocRows.slice(i, i + batchSize);
|
|
142
|
+
for (const r of batch) {
|
|
143
|
+
await dst.query(
|
|
144
|
+
`INSERT INTO associations (
|
|
145
|
+
id, from_engram_id, to_engram_id, weight, confidence, type,
|
|
146
|
+
activation_count, created_at, last_activated
|
|
147
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
|
|
148
|
+
[
|
|
149
|
+
r.id, r.from_engram_id, r.to_engram_id, r.weight, r.confidence,
|
|
150
|
+
r.type ?? 'hebbian', r.activation_count ?? 0,
|
|
151
|
+
r.created_at, r.last_activated,
|
|
152
|
+
],
|
|
153
|
+
);
|
|
154
|
+
stats.associations++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else if (dryRun) {
|
|
158
|
+
stats.associations = assocRows.length;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── agents ──────────────────────────────────────────────
|
|
162
|
+
const agentRows = src.prepare(`SELECT * FROM agents`).all() as any[];
|
|
163
|
+
if (verbose) console.log(`agents: ${agentRows.length} rows`);
|
|
164
|
+
if (dst && agentRows.length > 0) {
|
|
165
|
+
for (const r of agentRows) {
|
|
166
|
+
await dst.query(
|
|
167
|
+
`INSERT INTO agents (id, name, created_at, config) VALUES ($1,$2,$3,$4)
|
|
168
|
+
ON CONFLICT (id) DO NOTHING`,
|
|
169
|
+
[r.id, r.name, r.created_at, r.config ?? '{}'],
|
|
170
|
+
);
|
|
171
|
+
stats.agents++;
|
|
172
|
+
}
|
|
173
|
+
} else if (dryRun) {
|
|
174
|
+
stats.agents = agentRows.length;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── activation_events ───────────────────────────────────
|
|
178
|
+
const aeRows = src.prepare(`SELECT * FROM activation_events`).all() as any[];
|
|
179
|
+
if (verbose) console.log(`activation_events: ${aeRows.length} rows`);
|
|
180
|
+
if (dst && aeRows.length > 0) {
|
|
181
|
+
for (let i = 0; i < aeRows.length; i += batchSize) {
|
|
182
|
+
for (const r of aeRows.slice(i, i + batchSize)) {
|
|
183
|
+
await dst.query(
|
|
184
|
+
`INSERT INTO activation_events
|
|
185
|
+
(id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
186
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
|
187
|
+
[r.id, r.agent_id, r.timestamp, r.context, r.results_returned,
|
|
188
|
+
r.top_score, r.latency_ms, r.engram_ids ?? '[]'],
|
|
189
|
+
);
|
|
190
|
+
stats.activationEvents++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else if (dryRun) {
|
|
194
|
+
stats.activationEvents = aeRows.length;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── staging_events ──────────────────────────────────────
|
|
198
|
+
const seRows = src.prepare(`SELECT * FROM staging_events`).all() as any[];
|
|
199
|
+
if (verbose) console.log(`staging_events: ${seRows.length} rows`);
|
|
200
|
+
if (dst && seRows.length > 0) {
|
|
201
|
+
for (const r of seRows) {
|
|
202
|
+
await dst.query(
|
|
203
|
+
`INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
204
|
+
VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
205
|
+
[r.engram_id, r.agent_id, r.action, r.resonance_score, r.timestamp, r.age_ms],
|
|
206
|
+
);
|
|
207
|
+
stats.stagingEvents++;
|
|
208
|
+
}
|
|
209
|
+
} else if (dryRun) {
|
|
210
|
+
stats.stagingEvents = seRows.length;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── retrieval_feedback ──────────────────────────────────
|
|
214
|
+
const rfRows = src.prepare(`SELECT * FROM retrieval_feedback`).all() as any[];
|
|
215
|
+
if (verbose) console.log(`retrieval_feedback: ${rfRows.length} rows`);
|
|
216
|
+
if (dst && rfRows.length > 0) {
|
|
217
|
+
for (const r of rfRows) {
|
|
218
|
+
await dst.query(
|
|
219
|
+
`INSERT INTO retrieval_feedback
|
|
220
|
+
(id, activation_event_id, engram_id, useful, context, timestamp)
|
|
221
|
+
VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
222
|
+
[r.id, r.activation_event_id, r.engram_id, intToBool(r.useful), r.context, r.timestamp],
|
|
223
|
+
);
|
|
224
|
+
stats.retrievalFeedback++;
|
|
225
|
+
}
|
|
226
|
+
} else if (dryRun) {
|
|
227
|
+
stats.retrievalFeedback = rfRows.length;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── episodes ────────────────────────────────────────────
|
|
231
|
+
// The episodes table may not exist in older SQLite databases.
|
|
232
|
+
try {
|
|
233
|
+
const epRows = src.prepare(`SELECT * FROM episodes`).all() as any[];
|
|
234
|
+
if (verbose) console.log(`episodes: ${epRows.length} rows`);
|
|
235
|
+
if (dst && epRows.length > 0) {
|
|
236
|
+
for (const r of epRows) {
|
|
237
|
+
const embedding = r.embedding
|
|
238
|
+
? vectorLiteral(Array.from(bufferToFloat32Array(r.embedding)))
|
|
239
|
+
: null;
|
|
240
|
+
await dst.query(
|
|
241
|
+
`INSERT INTO episodes
|
|
242
|
+
(id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
243
|
+
VALUES ($1,$2,$3,$4::vector,$5,$6,$7,$8)`,
|
|
244
|
+
[r.id, r.agent_id, r.label, embedding, r.engram_count ?? 0,
|
|
245
|
+
r.start_time, r.end_time, r.created_at],
|
|
246
|
+
);
|
|
247
|
+
stats.episodes++;
|
|
248
|
+
}
|
|
249
|
+
} else if (dryRun) {
|
|
250
|
+
stats.episodes = epRows.length;
|
|
251
|
+
}
|
|
252
|
+
} catch {
|
|
253
|
+
if (verbose) console.log(`episodes: table not present in source (skipping)`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── conscious_state ─────────────────────────────────────
|
|
257
|
+
try {
|
|
258
|
+
const csRows = src.prepare(`SELECT * FROM conscious_state`).all() as any[];
|
|
259
|
+
if (verbose) console.log(`conscious_state: ${csRows.length} rows`);
|
|
260
|
+
if (dst && csRows.length > 0) {
|
|
261
|
+
for (const r of csRows) {
|
|
262
|
+
await dst.query(
|
|
263
|
+
`INSERT INTO conscious_state (
|
|
264
|
+
agent_id, last_write_id, last_recall_context, last_recall_ids,
|
|
265
|
+
last_activity_at, write_count_since_consolidation,
|
|
266
|
+
recall_count_since_consolidation, execution_state, checkpoint_at,
|
|
267
|
+
last_consolidation_at, last_mini_consolidation_at,
|
|
268
|
+
consolidation_cycle_count, updated_at
|
|
269
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
|
270
|
+
ON CONFLICT (agent_id) DO NOTHING`,
|
|
271
|
+
[
|
|
272
|
+
r.agent_id, r.last_write_id, r.last_recall_context,
|
|
273
|
+
r.last_recall_ids ?? '[]', r.last_activity_at,
|
|
274
|
+
r.write_count_since_consolidation ?? 0,
|
|
275
|
+
r.recall_count_since_consolidation ?? 0,
|
|
276
|
+
r.execution_state, r.checkpoint_at,
|
|
277
|
+
r.last_consolidation_at, r.last_mini_consolidation_at,
|
|
278
|
+
r.consolidation_cycle_count ?? 0, r.updated_at,
|
|
279
|
+
],
|
|
280
|
+
);
|
|
281
|
+
stats.consciousState++;
|
|
282
|
+
}
|
|
283
|
+
} else if (dryRun) {
|
|
284
|
+
stats.consciousState = csRows.length;
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
if (verbose) console.log(`conscious_state: table not present in source (skipping)`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
src.close();
|
|
291
|
+
if (dst) await dst.close();
|
|
292
|
+
|
|
293
|
+
return stats;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function printStats(stats: MigrateStats, dryRun: boolean): void {
|
|
297
|
+
const verb = dryRun ? 'would migrate' : 'migrated';
|
|
298
|
+
console.log(`\nMigration ${dryRun ? 'plan' : 'complete'}:`);
|
|
299
|
+
console.log(` ${verb} ${stats.engrams} engrams`);
|
|
300
|
+
console.log(` ${verb} ${stats.associations} associations`);
|
|
301
|
+
console.log(` ${verb} ${stats.agents} agents`);
|
|
302
|
+
console.log(` ${verb} ${stats.activationEvents} activation events`);
|
|
303
|
+
console.log(` ${verb} ${stats.stagingEvents} staging events`);
|
|
304
|
+
console.log(` ${verb} ${stats.retrievalFeedback} retrieval feedback records`);
|
|
305
|
+
console.log(` ${verb} ${stats.episodes} episodes`);
|
|
306
|
+
console.log(` ${verb} ${stats.consciousState} conscious_state rows`);
|
|
307
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -56,6 +56,8 @@ Usage:
|
|
|
56
56
|
awm merge --target <db> --source <db> [--source ...]
|
|
57
57
|
[--remap uuid=name] [--remap-all-uuids <name>]
|
|
58
58
|
[--dedupe] [--dry-run] Merge multiple memory DBs
|
|
59
|
+
awm migrate --from <sqlite.db> --to <pglite-dir> [--dry-run] [--verbose]
|
|
60
|
+
Migrate SQLite DB to PGlite
|
|
59
61
|
|
|
60
62
|
Setup targets:
|
|
61
63
|
claude-code (default) .mcp.json + CLAUDE.md + hooks
|
|
@@ -334,7 +336,7 @@ async function exportMemories() {
|
|
|
334
336
|
const agents = [...new Set(memories.map((m: any) => m.agent_id))];
|
|
335
337
|
|
|
336
338
|
const exportData = {
|
|
337
|
-
version: '0.
|
|
339
|
+
version: '0.8.5',
|
|
338
340
|
exported_at: new Date().toISOString(),
|
|
339
341
|
source_db: dbPath,
|
|
340
342
|
agent_filter: agentFilter,
|
|
@@ -680,6 +682,37 @@ async function mergeMemories() {
|
|
|
680
682
|
if (dryRun) console.log('(dry run — no data written)');
|
|
681
683
|
}
|
|
682
684
|
|
|
685
|
+
async function migrateCmd() {
|
|
686
|
+
let from = '';
|
|
687
|
+
let to = '';
|
|
688
|
+
let dryRun = false;
|
|
689
|
+
let verbose = false;
|
|
690
|
+
|
|
691
|
+
for (let i = 1; i < args.length; i++) {
|
|
692
|
+
const a = args[i];
|
|
693
|
+
if (a === '--from') { from = args[++i]; }
|
|
694
|
+
else if (a === '--to') { to = args[++i]; }
|
|
695
|
+
else if (a === '--dry-run') { dryRun = true; }
|
|
696
|
+
else if (a === '--verbose' || a === '-v') { verbose = true; }
|
|
697
|
+
else { console.error(`Unknown argument: ${a}`); process.exit(1); }
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (!from || !to) {
|
|
701
|
+
console.error('Usage: awm migrate --from <sqlite.db> --to <pglite-dir> [--dry-run] [--verbose]');
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const { migrate, printStats } = await import('./cli/migrate.js');
|
|
706
|
+
try {
|
|
707
|
+
console.log(`Migrating ${from} → ${to}${dryRun ? ' (dry run)' : ''}`);
|
|
708
|
+
const stats = await migrate({ from, to, dryRun, verbose });
|
|
709
|
+
printStats(stats, dryRun);
|
|
710
|
+
} catch (err) {
|
|
711
|
+
console.error(`Migration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
683
716
|
// ─── Dispatch ──────────────────────────────────────
|
|
684
717
|
|
|
685
718
|
switch (command) {
|
|
@@ -707,6 +740,9 @@ switch (command) {
|
|
|
707
740
|
case 'merge':
|
|
708
741
|
mergeMemories();
|
|
709
742
|
break;
|
|
743
|
+
case 'migrate':
|
|
744
|
+
await migrateCmd();
|
|
745
|
+
break;
|
|
710
746
|
case '--help':
|
|
711
747
|
case '-h':
|
|
712
748
|
case undefined:
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Per-worker CircuitBreaker for the coordination control layer.
|
|
5
|
+
* Prevents chronically-stale workers from poisoning the assignment queue.
|
|
6
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
7
|
+
*
|
|
8
|
+
* States:
|
|
9
|
+
* closed — normal operation
|
|
10
|
+
* open — worker blocked after FAILURE_THRESHOLD consecutive failures
|
|
11
|
+
* half_open — probe window (30s after open), allows one assignment attempt
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type Database from 'better-sqlite3';
|
|
15
|
+
|
|
16
|
+
export type CircuitState = 'closed' | 'open' | 'half_open';
|
|
17
|
+
|
|
18
|
+
const FAILURE_THRESHOLD = 5;
|
|
19
|
+
const HALF_OPEN_DELAY_MS = 30_000;
|
|
20
|
+
|
|
21
|
+
/** Record a worker failure. Opens the circuit when consecutive failures hit the threshold. */
|
|
22
|
+
export function recordFailure(db: Database.Database, agentId: string): void {
|
|
23
|
+
db.prepare(`
|
|
24
|
+
INSERT INTO coord_circuit_state (agent_id, consecutive_failures, last_transition_at)
|
|
25
|
+
VALUES (?, 1, datetime('now'))
|
|
26
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
27
|
+
consecutive_failures = consecutive_failures + 1,
|
|
28
|
+
state = CASE
|
|
29
|
+
WHEN consecutive_failures + 1 >= ${FAILURE_THRESHOLD} THEN 'open'
|
|
30
|
+
ELSE state
|
|
31
|
+
END,
|
|
32
|
+
opened_at = CASE
|
|
33
|
+
WHEN consecutive_failures + 1 >= ${FAILURE_THRESHOLD} AND (state != 'open' OR opened_at IS NULL)
|
|
34
|
+
THEN datetime('now')
|
|
35
|
+
ELSE opened_at
|
|
36
|
+
END,
|
|
37
|
+
last_transition_at = datetime('now')
|
|
38
|
+
`).run(agentId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Record a worker success. Resets to closed regardless of prior state. */
|
|
42
|
+
export function recordSuccess(db: Database.Database, agentId: string): void {
|
|
43
|
+
db.prepare(`
|
|
44
|
+
INSERT INTO coord_circuit_state (agent_id, state, consecutive_failures, last_transition_at)
|
|
45
|
+
VALUES (?, 'closed', 0, datetime('now'))
|
|
46
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
47
|
+
state = 'closed',
|
|
48
|
+
consecutive_failures = 0,
|
|
49
|
+
opened_at = NULL,
|
|
50
|
+
last_transition_at = datetime('now')
|
|
51
|
+
`).run(agentId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get current circuit state for a worker.
|
|
56
|
+
* If the circuit has been open for >30s, auto-transitions to half_open.
|
|
57
|
+
*/
|
|
58
|
+
export function getState(db: Database.Database, agentId: string): CircuitState {
|
|
59
|
+
const row = db.prepare(
|
|
60
|
+
`SELECT state, opened_at FROM coord_circuit_state WHERE agent_id = ?`
|
|
61
|
+
).get(agentId) as { state: string; opened_at: string | null } | undefined;
|
|
62
|
+
|
|
63
|
+
if (!row || row.state === 'closed') return 'closed';
|
|
64
|
+
if (row.state === 'half_open') return 'half_open';
|
|
65
|
+
|
|
66
|
+
// open — check if half-open window has elapsed
|
|
67
|
+
if (row.state === 'open' && row.opened_at) {
|
|
68
|
+
const openedAt = new Date(row.opened_at.endsWith('Z') ? row.opened_at : row.opened_at + 'Z').getTime();
|
|
69
|
+
if (Date.now() - openedAt > HALF_OPEN_DELAY_MS) {
|
|
70
|
+
db.prepare(
|
|
71
|
+
`UPDATE coord_circuit_state SET state = 'half_open', last_transition_at = datetime('now') WHERE agent_id = ?`
|
|
72
|
+
).run(agentId);
|
|
73
|
+
return 'half_open';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return 'open';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Returns true when the worker is eligible to receive an assignment. */
|
|
81
|
+
export function isAvailable(db: Database.Database, agentId: string): boolean {
|
|
82
|
+
return getState(db, agentId) !== 'open';
|
|
83
|
+
}
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* Typed internal event emitter for coordination.
|
|
5
|
-
* Lightweight Node EventEmitter wrapper with typed events.
|
|
6
|
-
* Used to decouple route handlers from side-effects (channel push, logging, etc).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { EventEmitter } from 'events';
|
|
10
|
-
|
|
11
|
-
// ─── Event Payloads ──────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
export interface AssignmentCreatedEvent {
|
|
14
|
-
assignmentId: string;
|
|
15
|
-
agentId: string;
|
|
16
|
-
task: string;
|
|
17
|
-
workspace?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface AssignmentUpdatedEvent {
|
|
21
|
-
assignmentId: string;
|
|
22
|
-
agentId: string | null;
|
|
23
|
-
status: string;
|
|
24
|
-
result?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface AssignmentCompletedEvent {
|
|
28
|
-
assignmentId: string;
|
|
29
|
-
agentId: string | null;
|
|
30
|
-
result: string | null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface AgentCheckinEvent {
|
|
34
|
-
agentId: string;
|
|
35
|
-
name: string;
|
|
36
|
-
role: string;
|
|
37
|
-
workspace?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface AgentCheckoutEvent {
|
|
41
|
-
agentId: string;
|
|
42
|
-
name: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface SessionStartedEvent {
|
|
46
|
-
agentId: string;
|
|
47
|
-
channelId: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface SessionClosedEvent {
|
|
51
|
-
agentId: string;
|
|
52
|
-
channelId: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ─── Event Map ───────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
export interface CoordinationEvents {
|
|
58
|
-
'assignment.created': [AssignmentCreatedEvent];
|
|
59
|
-
'assignment.updated': [AssignmentUpdatedEvent];
|
|
60
|
-
'assignment.completed': [AssignmentCompletedEvent];
|
|
61
|
-
'agent.checkin': [AgentCheckinEvent];
|
|
62
|
-
'agent.checkout': [AgentCheckoutEvent];
|
|
63
|
-
'session.started': [SessionStartedEvent];
|
|
64
|
-
'session.closed': [SessionClosedEvent];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ─── Typed Event Bus ─────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
export class CoordinationEventBus extends EventEmitter {
|
|
70
|
-
emit<K extends keyof CoordinationEvents>(event: K, ...args: CoordinationEvents[K]): boolean {
|
|
71
|
-
return super.emit(event, ...args);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
on<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
75
|
-
return super.on(event, listener as (...args: unknown[]) => void);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
once<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
79
|
-
return super.once(event, listener as (...args: unknown[]) => void);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
off<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
83
|
-
return super.off(event, listener as (...args: unknown[]) => void);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Create a new coordination event bus. */
|
|
88
|
-
export function createEventBus(): CoordinationEventBus {
|
|
89
|
-
return new CoordinationEventBus();
|
|
90
|
-
}
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Typed internal event emitter for coordination.
|
|
5
|
+
* Lightweight Node EventEmitter wrapper with typed events.
|
|
6
|
+
* Used to decouple route handlers from side-effects (channel push, logging, etc).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
|
|
11
|
+
// ─── Event Payloads ──────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface AssignmentCreatedEvent {
|
|
14
|
+
assignmentId: string;
|
|
15
|
+
agentId: string;
|
|
16
|
+
task: string;
|
|
17
|
+
workspace?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AssignmentUpdatedEvent {
|
|
21
|
+
assignmentId: string;
|
|
22
|
+
agentId: string | null;
|
|
23
|
+
status: string;
|
|
24
|
+
result?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AssignmentCompletedEvent {
|
|
28
|
+
assignmentId: string;
|
|
29
|
+
agentId: string | null;
|
|
30
|
+
result: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AgentCheckinEvent {
|
|
34
|
+
agentId: string;
|
|
35
|
+
name: string;
|
|
36
|
+
role: string;
|
|
37
|
+
workspace?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AgentCheckoutEvent {
|
|
41
|
+
agentId: string;
|
|
42
|
+
name: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SessionStartedEvent {
|
|
46
|
+
agentId: string;
|
|
47
|
+
channelId: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SessionClosedEvent {
|
|
51
|
+
agentId: string;
|
|
52
|
+
channelId: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── Event Map ───────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
export interface CoordinationEvents {
|
|
58
|
+
'assignment.created': [AssignmentCreatedEvent];
|
|
59
|
+
'assignment.updated': [AssignmentUpdatedEvent];
|
|
60
|
+
'assignment.completed': [AssignmentCompletedEvent];
|
|
61
|
+
'agent.checkin': [AgentCheckinEvent];
|
|
62
|
+
'agent.checkout': [AgentCheckoutEvent];
|
|
63
|
+
'session.started': [SessionStartedEvent];
|
|
64
|
+
'session.closed': [SessionClosedEvent];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Typed Event Bus ─────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
export class CoordinationEventBus extends EventEmitter {
|
|
70
|
+
emit<K extends keyof CoordinationEvents>(event: K, ...args: CoordinationEvents[K]): boolean {
|
|
71
|
+
return super.emit(event, ...args);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
on<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
75
|
+
return super.on(event, listener as (...args: unknown[]) => void);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
once<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
79
|
+
return super.once(event, listener as (...args: unknown[]) => void);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
off<K extends keyof CoordinationEvents>(event: K, listener: (...args: CoordinationEvents[K]) => void): this {
|
|
83
|
+
return super.off(event, listener as (...args: unknown[]) => void);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Create a new coordination event bus. */
|
|
88
|
+
export function createEventBus(): CoordinationEventBus {
|
|
89
|
+
return new CoordinationEventBus();
|
|
90
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* FailureMode taxonomy and mutation-hint map for the coordination control layer.
|
|
5
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export enum FailureMode {
|
|
9
|
+
AGENT_STALE = 'agent_stale',
|
|
10
|
+
TIMEOUT = 'timeout',
|
|
11
|
+
OUTPUT_INVALID = 'output_invalid',
|
|
12
|
+
TEST_FAIL = 'test_fail',
|
|
13
|
+
LINT_FAIL = 'lint_fail',
|
|
14
|
+
MERGE_CONFLICT = 'merge_conflict',
|
|
15
|
+
UNKNOWN = 'unknown',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Classify a failure result string into one of the known modes. */
|
|
19
|
+
export function classifyFailure(result: string | null): FailureMode {
|
|
20
|
+
if (!result) return FailureMode.UNKNOWN;
|
|
21
|
+
const r = result.toLowerCase();
|
|
22
|
+
if (r.includes('stale') || r.includes('disconnected')) return FailureMode.AGENT_STALE;
|
|
23
|
+
if (r.includes('timeout') || r.includes('timed out')) return FailureMode.TIMEOUT;
|
|
24
|
+
if (r.includes('json') || r.includes('schema') || r.includes('parse')) return FailureMode.OUTPUT_INVALID;
|
|
25
|
+
if (r.includes('test fail') || r.includes('vitest') || r.includes('jest')) return FailureMode.TEST_FAIL;
|
|
26
|
+
if (r.includes('lint') || r.includes('eslint') || r.includes('typecheck')) return FailureMode.LINT_FAIL;
|
|
27
|
+
if (r.includes('conflict')) return FailureMode.MERGE_CONFLICT;
|
|
28
|
+
return FailureMode.UNKNOWN;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Corrective guidance injected into the task description on retry.
|
|
33
|
+
* Each hint is written in the vocabulary the next worker will read.
|
|
34
|
+
*/
|
|
35
|
+
export const MUTATION_HINTS: Record<FailureMode, string> = {
|
|
36
|
+
[FailureMode.AGENT_STALE]:
|
|
37
|
+
'Previous worker disconnected before completion. Resume from last known state; check git status before re-running destructive commands.',
|
|
38
|
+
[FailureMode.TIMEOUT]:
|
|
39
|
+
'Previous attempt timed out. Break work into smaller commits; report progress every 5 minutes.',
|
|
40
|
+
[FailureMode.OUTPUT_INVALID]:
|
|
41
|
+
'Previous output failed validation. Return a single fenced code block; verify JSON parses before submitting.',
|
|
42
|
+
[FailureMode.TEST_FAIL]:
|
|
43
|
+
'Previous attempt left tests failing. Run vitest before completion; do NOT mark complete if any test fails.',
|
|
44
|
+
[FailureMode.LINT_FAIL]:
|
|
45
|
+
'Previous attempt had lint/typecheck errors. Run pnpm typecheck and pnpm lint before completion.',
|
|
46
|
+
[FailureMode.MERGE_CONFLICT]:
|
|
47
|
+
'Previous attempt left merge conflicts unresolved. git pull --rebase, resolve, then re-attempt.',
|
|
48
|
+
[FailureMode.UNKNOWN]:
|
|
49
|
+
'Previous attempt failed for an unclassified reason. Investigate the prior result before re-running.',
|
|
50
|
+
};
|